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

__author__ = "Peace Lee"
__copyright__ = "Copyright 2015-2024, Guider"
__module__ = "guider"
__credits__ = "Peace Lee"
__license__ = "GPLv2"
__version__ = "3.9.8"
__revision__ = "240418"
__maintainer__ = "Peace Lee"
__email__ = "iipeace5@gmail.com"
__repository__ = "https://github.com/iipeace/guider"


# import essential packages #
import sys

try:
    import os
    import re
    import gc
    import time
    import errno
    import signal
    import atexit
    import struct
    from copy import deepcopy

    # from ctypes import *  # for lint check
except ImportError:
    err = sys.exc_info()[1]
    sys.exit("[ERROR] failed to import essential package: %s" % err.args[0])

# convert an unsupported type #
try:
    long  # pylint: disable=used-before-assignment
except:
    long = int

# prevent MemoryError in python2 #
try:
    xrange  # pylint: disable=used-before-assignment
except:
    xrange = range

# enable JIT compiler #
try:
    if "JITCOMPILE" in os.environ:
        from numba import jit  # pylint: disable=import-error
    else:

        def jit(func):
            def decorated():
                func()

            return decorated

except:
    err = sys.exc_info()[1]
    sys.exit("[ERROR] failed to import numba package: %s" % err.args[0])


class ConfigMgr(object):
    """Manager for config"""

    # logo, made by http://www.figlet.org, consider also jp2a #
    logo = """
   _____       _     _
  / ____|     (_)   | |
 | |  __ _   _ _  __| | ___ _ __
 | | |_ | | | | |/ _` |/ _ \ '__|
 | |__| | |_| | | (_| |  __/ |
  \_____|\__,_|_|\__,_|\___|_|  ver_%s_%s on python_%s.%s
""" % (
        __version__,
        __revision__,
        sys.version_info[0],
        sys.version_info[1],
    )

    # color #
    if (
        sys.platform.startswith("linux")
        or sys.platform.startswith("darwin")
        or sys.platform.startswith("freebsd")
    ) and not "REMOTERUN" in os.environ:
        ENDC = "\033[0m"
    else:
        ENDC = ""

    # config info #
    confData = {}

    # support architecture #
    supportArch = ["arm", "aarch64", "riscv64", "x86", "x64"]

    # ANSI color #
    COLOR_LIST = {
        "DEFAULT": "\033[0m",
        "BOLD": "\033[1m",
        "ITALIC": "\033[3m",
        "UNDERLINE": "\033[4m",
        "LIGHT": "\033[5m",
        "BLINK": "\033[6m",
        "REVERSE": "\033[7m",
        "SCRATCH": "\033[9m",
        "BLACK": "\033[30m",
        "RED": "\033[31m",
        "GREEN": "\033[32m",
        "YELLOW": "\033[33m",
        "BLUE": "\033[34m",
        "PINK": "\033[35m",
        "CYAN": "\033[36m",
        "WHITE": "\033[37m",
        "DEFCOLOR": "\033[39m",
        "BGBLACK": "\033[40m",
        "BGRED": "\033[41m",
        "BGGREEN": "\033[42m",
        "BGYELLOW": "\033[43m",
        "BGBLUE": "\033[44m",
        "BGPINK": "\033[45m",
        "BGCYAN": "\033[46m",
        "BGWHITE": "\033[47m",
        "FAIL": "\033[91m",
        "OKGREEN": "\033[92m",
        "SPECIAL": "\033[93m",
        "OKBLUE": "\033[94m",
        "WARNING": "\033[95m",
        "LIGHTGREEN": "\033[1;32m",
    }

    # cgroup entity #
    CGROUP_VALUE = [
        "tasks",
        "cgroup.procs",
        "cpu.shares",
        "cpu.cfs_period_us",
        "cpu.cfs_quota_us",
        "cpu.rt_period_us",
        "cpu.rt_runtime_us",
        "cpu.stat",
        "cpu.uclamp.min",
        "cpu.uclamp.max",
        "cpu.uclamp.latency_sensitive",
        "cpuacct.usage",
        "cpuset.cpus",
        "cpuset.cpus.effective",
        "cpus",
        "memory.limit_in_bytes",
        "memory.memsw.limit_in_bytes",
        "blkio.weight",
        "blkio.bfq.weight",
        "blkio.weight_device",
        "blkio.io_wait_time",
        "blkio.throttle.io_service_bytes",
        "cpu.pressure",
        "memory.pressure",
        "io.pressure",
    ]

    # cgroup stat #
    CGROUP_STAT = {
        "blkio.throttle.io_service_bytes_recursive": None,
        "cgroup.procs": None,
        "cpu.stat": None,
        "cpuacct.usage": None,
        "io.stat": None,
        "memory.current": None,
        "memory.usage_in_bytes": None,
        "tasks": None,
        "cpu.pressure": None,
        "memory.pressure": None,
        "io.pressure": None,
    }

    # state of process #
    PROC_STAT_TYPE = {
        "R": "running",
        "S": "sleep",
        "D": "disk",
        "T": "stopped",
        "t": "traced",
        "X": "dead",
        "x": "dead",
        "Z": "zombie",
        "K": "wakekill",
        "W": "waking",
        "P": "parked",
        "I": "idle",
    }

    # diskstat statistics #
    DISKSTAT = [
        "major",
        "minor",
        "name",
        "readComplete",
        "readMerge",
        "sectorRead",
        "readTime",
        "writeComplete",
        "writeMerge",
        "sectorWrite",
        "writeTime",
        "currentIO",
        "ioTime",
        "ioWTime",
        "discComplete",
        "discMerged",
        "sectorDisc",
        "discTime",  # 4.18+
        "flushComplete",
        "flushTime",  # 5.5+
    ]

    BGRECLAIMSTAT = [
        "pgsteal_kswapd",
        "pgsteal_kswapd_normal",
        "pgsteal_kswapd_high",
        "pgsteal_kswapd_dma",
        "pgsteal_kswapd_dma32",
        "pgsteal_kswapd_movable",
    ]

    FGRECLAIMSTAT = [
        "pgsteal_direct",
        "pgsteal_direct_normal",
        "pgsteal_direct_high",
        "pgsteal_direct_dma",
        "pgsteal_direct_dma32",
        "pgsteal_direct_movable",
    ]

    # socketcall attributes #
    SOCKETCALL = {
        1: "socket",
        2: "bind",
        3: "connect",
        4: "listen",
        5: "accept",
        6: "getsockname",
        7: "getpeername",
        8: "socketpair",
        9: "send",
        10: "recv",
        11: "sendto",
        12: "recvfrom",
        13: "shutdown",
        14: "setsockopt",
        15: "getsockopt",
        16: "sendmsg",
        17: "recvmsg",
        18: "accept4",
        19: "recvmmsg",
        20: "sendmmsg",
    }

    # socket family #
    SOCKET_FAMILY = {
        0: "AF_UNSPEC",
        1: "AF_FILE",
        2: "AF_INET",
        3: "AF_AX25",
        4: "AF_IPX",
        5: "AF_APPLETALK",
        6: "AF_NETROM",
        7: "AF_BRIDGE",
        8: "AF_ATMPVC",
        9: "AF_X25",
        10: "AF_INET6",
        11: "AF_ROSE",
        12: "AF_DECnet",
        13: "AF_NETBEUI",
        14: "AF_SECURITY",
        15: "AF_KEY",
        16: "AF_NETLINK",
        17: "AF_PACKET",
        18: "AF_ASH",
        19: "AF_ECONET",
        20: "AF_ATMSVC",
        22: "AF_SNA",
        23: "AF_IRDA",
        24: "AF_PPPOX",
        25: "AF_WANPIPE",
        31: "AF_BLUETOOTH",
    }

    # DLOPEN type #
    DLOPEN_TYPE = {
        1: "RTLD_LAZY",
        2: "RTLD_NOW",
        3: "RTLD_BINDING_MASK",
        4: "RTLD_NOLOAD",
        8: "RTLD_DEEPBIND",
    }

    # socket type #
    SOCKET_TYPE = {
        1: "SOCK_STREAM",
        2: "SOCK_DGRAM",
        3: "SOCK_RAW",
        4: "SOCK_RDM",
        5: "SOCK_SEQPACKET",
        10: "SOCK_PACKET",
        2048: "SOCK_NONBLOCK",
        524288: "SOCK_CLOEXEC",
    }

    # log level #
    LOG_LEVEL = {
        0: "EMERG",
        1: "ALERT",
        2: "CRIT",
        3: "ERR",
        4: "WARNING",
        5: "NOTICE",
        6: "INFO",
        7: "DEBUG",
    }

    # MSG type #
    MSG_TYPE = {
        0x1: "MSG_OOB",
        0x2: "MSG_PEEK",
        0x4: "MSG_DONTROUTE|MSG_TRYHARD",
        0x8: "MSG_CTRUNC",
        0x10: "MSG_PROBE",
        0x20: "MSG_TRUNC",
        0x40: "MSG_DONTWAIT",
        0x80: "MSG_EOR",
        0x100: "MSG_WAITALL",
        0x200: "MSG_FIN",
        0x400: "MSG_SYN",
        0x800: "MSG_CONFIRM",
        0x1000: "MSG_RST",
        0x2000: "MSG_ERRQUEUE",
        0x4000: "MSG_NOSIGNAL",
        0x8000: "MSG_MORE",
        0x40000000: "MSG_CMSG_CLOEXEC",
        0x80000000: "MSG_CMSG_COMPAT",
    }

    # control message type #
    CMSG_TYPE = {
        0x01: "SCM_RIGHTS",  # rw: access rights (array of int)
        0x02: "SCM_CREDENTIALS",  # rw: struct ucred
        0x03: "SCM_SECURITY",
    }

    # mmap prot type #
    ARCH_CODE = {
        0x1001: "ARCH_SET_GS",
        0x1002: "ARCH_SET_FS",
        0x1003: "ARCH_GET_FS",
        0x1004: "ARCH_GET_GS",
        0x1011: "ARCH_GET_CPUID",
        0x1012: "ARCH_SET_CPUID",
        0x1021: "ARCH_GET_XCOMP_SUPP",
        0x1022: "ARCH_GET_XCOMP_PERM",
        0x1023: "ARCH_REQ_XCOMP_PERM",
        0x1024: "ARCH_GET_XCOMP_GUEST_PERM",
        0x1025: "ARCH_REQ_XCOMP_GUEST_PERM",
        0x2001: "ARCH_MAP_VDSO_X32",
        0x2002: "ARCH_MAP_VDSO_32",
        0x2003: "ARCH_MAP_VDSO_64",
        0x3001: "ARCH_CET_STATUS",
        0x3002: "ARCH_CET_DISABLE",
        0x3003: "ARCH_CET_LOCK",
        0x3004: "ARCH_CET_ALLOC_SHSTK",
    }

    # mmap prot type #
    MAP_TYPE = {
        0x0000: "MAP_FILE",
        0x0001: "MAP_SHARED",
        0x0002: "MAP_PRIVATE",
        # 0x0003: "MAP_SHARED_VALIDATE",
        # 0x000f: "MAP_TYPE",
        0x0010: "MAP_FIXED",
        0x0020: "MAP_ANONYMOUS",
        0x0100: "MAP_GROWSDOWN",
        0x0800: "MAP_DENYWRITE",
        0x1000: "MAP_EXECUTABLE",
        0x2000: "MAP_LOCKED",
        0x4000: "MAP_NORESERVE",
        0x8000: "MAP_POPULATE",
        0x10000: "MAP_NONBLOCK",
        0x20000: "MAP_STACK",
        0x40000: "MAP_HUGETLB",
        0x80000: "MAP_SYNC",
    }

    # FAT type #
    FAT_TYPE = {
        -100: "AT_FDCWD",
        0x100: "AT_SYMLINK_NOFOLLOW",
        0x200: "AT_REMOVEDIR",
        0x400: "AT_SYMLINK_FOLLOW",
        0x800: "AT_NO_AUTOMOUNT",
        0x1000: "AT_EMPTY_PATH",
    }
    FAT_TYPE_REVERSE = {}

    # mmap prot type #
    PROT_TYPE = {
        0x0: "PROT_NONE",  # Page can not be accessed
        0x1: "PROT_READ",  # Page can be read
        0x2: "PROT_WRITE",  # Page can be written
        0x4: "PROT_EXEC",  # Page can be executed
    }

    # perm type #
    PERM_TYPE = {0x0: "F_OK", 0x1: "X_OK", 0x2: "W_OK", 0x4: "R_OK"}

    # seek type #
    SEEK_TYPE = {0x0: "SEEK_SET", 0x1: "SEEK_CUR", 0x2: "SEEK_END"}

    # prctl flags type #
    PRCTL_TYPE = {
        0: "PR_SET_VMA_ANON_NAME",
        1: "PR_SET_PDEATHSIG",
        2: "PR_GET_PDEATHSIG",
        3: "PR_GET_DUMPABLE",
        4: "PR_SET_DUMPABLE",
        5: "PR_GET_UNALIGN",
        6: "PR_SET_UNALIGN",
        7: "PR_GET_KEEPCAPS",
        8: "PR_SET_KEEPCAPS",
        9: "PR_GET_FPEMU",
        10: "PR_SET_FPEMU",
        11: "PR_GET_FPEXC",
        12: "PR_SET_FPEXC",
        13: "PR_GET_TIMING",
        14: "PR_SET_TIMING",
        15: "PR_SET_NAME",
        16: "PR_GET_NAME",
        19: "PR_GET_ENDIAN",
        20: "PR_SET_ENDIAN",
        21: "PR_GET_SECCOMP",
        22: "PR_SET_SECCOMP",
        23: "PR_CAPBSET_READ",
        24: "PR_CAPBSET_DROP",
        25: "PR_GET_TSC 25",
        26: "PR_SET_TSC 26",
        27: "PR_GET_SECUREBITS",
        28: "PR_SET_SECUREBITS",
        29: "PR_SET_TIMERSLACK",
        30: "PR_GET_TIMERSLACK",
        31: "PR_TASK_PERF_EVENTS_DISABLE",
        32: "PR_TASK_PERF_EVENTS_ENABLE",
        33: "PR_MCE_KILL",
        0x53564D41: "PR_SET_VMA",
    }

    # mount flags type #
    MOUNT_TYPE = {
        "MS_RDONLY": 1,  # Mount read-only
        "MS_NOSUID": 2,  # Ignore suid and sgid bits
        "MS_NODEV": 4,  # Disallow access to device special files
        "MS_NOEXEC": 8,  # Disallow program execution
        "MS_SYNCHRONOUS": 16,  # Writes are synced at once
        "MS_REMOUNT": 32,  # Alter flags of a mounted FS
        "MS_MANDLOCK": 64,  # Allow mandatory locks on an FS
        "MS_WRITE": 128,  # Write on file/directory/symlink
        "MS_APPEND": 256,  # Append-only file
        "MS_IMMUTABLE": 512,  # Immutable file
        "MS_NOATIME": 1024,  # Do not update access times
        "MS_NODIRATIME": 2048,  # Do not update directory access times
        "MS_BIND": 4096,  # Bind directory at different place
        "MS_REC": 16384,
        "MS_VERBOSE": 32768,  # War is peace. Verbosity is silence
        "MS_SILENT": 32768,
        "MS_POSIXACL": (1 << 16),  # VFS does not apply the umask
        "MS_UNBINDABLE": (1 << 17),  # change to unbindable
        "MS_PRIVATE": (1 << 18),  # change to private
        "MS_SLAVE": (1 << 19),  # change to slave
        "MS_SHARED": (1 << 20),  # change to shared
        "MS_RELATIME": (1 << 21),  # Update atime relative to mtime/ctime
        "MS_KERNMOUNT": (1 << 22),  # this is a kern_mount call
        "MS_I_VERSION": (1 << 23),  # Update inode I_version field
        "MS_STRICTATIME": (1 << 24),  # Always perform atime updates
        "MS_LAZYTIME": (1 << 25),  # Update the on-disk [acm]times lazily
        "MS_NOSEC": (1 << 28),
        "MS_BORN": (1 << 29),
        "MS_ACTIVE": (1 << 30),
        "MS_NOUSER": (1 << 31),
    }
    MOUNT_TYPE_REVERSE = {}

    # umount flags type #
    UMOUNT_TYPE = {
        "MNT_FORCE": 0x00000001,  # Attempt to forcibily umount
        "MNT_DETACH": 0x00000002,  # Just detach from the tree
        "MNT_EXPIRE": 0x00000004,  # Mark for expiry
        "MNT_NOFOLLOW": 0x00000008,  # Don't follow symlink on umount
        "MNT_UNUSED": 0x80000000,  # Flag guaranteed to be unused
    }
    UMOUNT_TYPE_REVERSE = {}

    # clone flags type #
    CLONE_TYPE = {
        0x000000FF: "CSIGNAL",
        0x00000100: "CLONE_VM",
        0x00000200: "CLONE_FS",
        0x00000400: "CLONE_FILES",
        0x00000800: "CLONE_SIGHAND",
        0x00002000: "CLONE_PTRACE",
        0x00004000: "CLONE_VFORK",
        0x00008000: "CLONE_PARENT",
        0x00010000: "CLONE_THREAD",
        0x00020000: "CLONE_NEWNS",
        0x00040000: "CLONE_SYSVSEM",
        0x00080000: "CLONE_SETTLS",
        0x00100000: "CLONE_PARENT_SETTID",
        0x00200000: "CLONE_CHILD_CLEARTID",
        0x00400000: "CLONE_DETACHED",
        0x00800000: "CLONE_UNTRACED",
        0x01000000: "CLONE_CHILD_SETTID",
        0x02000000: "CLONE_STOPPED",
        0x04000000: "CLONE_NEWUTS",
        0x08000000: "CLONE_NEWIPC",
        0x10000000: "CLONE_NEWUSER",
        0x20000000: "CLONE_NEWPID",
        0x40000000: "CLONE_NEWNET",
        0x80000000: "CLONE_IO",
    }

    # open flags type #
    OPEN_TYPE = {
        0o0: "O_RDONLY",
        0o1: "O_WRONLY",
        0o2: "O_RDWR",
        0o100: "O_CREAT",
        0o200: "O_EXCL",
        0o400: "O_NOCTTY",
        0o1000: "O_TRUNC",
        0o2000: "O_APPEND",
        0o4000: "O_NONBLOCK",
        0o10000: "O_SYNC",
        0o20000: "O_ASYNC",
        0o40000: "O_DIRECT",
        0o100000: "O_LARGEFILE",
        0o200000: "O_DIRECTORY",
        0o400000: "O_NOFOLLOW",
        0o1000000: "O_NOATIME",
        0o2000000: "O_CLOEXEC",
        0o10000000: "O_PATH",
        0o20200000: "O_TMPFILE",
    }

    # epoll op type #
    EPOLL_CMD_TYPE = [
        "EPOLL_CTL_NONE",  # None
        "EPOLL_CTL_ADD",  # Add a file descriptor to the interface
        "EPOLL_CTL_DEL",  # Remove a file descriptor from the interface
        "EPOLL_CTL_MOD",  # Change file descriptor epoll_event structure
    ]

    # epoll event type #
    EPOLL_EVENT_TYPE = {
        0x001: "EPOLLIN",
        0x002: "EPOLLPRI",
        0x004: "EPOLLOUT",
        0x040: "EPOLLRDNORM",
        0x080: "EPOLLRDBAND",
        0x100: "EPOLLWRNORM",
        0x200: "EPOLLWRBAND",
        0x400: "EPOLLMSG",
        0x008: "EPOLLERR",
        0x010: "EPOLLHUP",
        0x2000: "EPOLLRDHUP",
        (1 << 29): "EPOLLWAKEUP",
        (1 << 30): "EPOLLONESHOT",
        (1 << 31): "EPOLLET",
    }

    # clock type #
    CLOCK_TYPE = {
        "CLOCK_REALTIME": 0,  # Identifier for system-wide realtime clock
        "CLOCK_MONOTONIC": 1,  # Monotonic system-wide clock
        "CLOCK_PROCESS_CPUTIME_ID": 2,  # High-resolution timer from the CPU
        "CLOCK_THREAD_CPUTIME_ID": 3,  # Thread-specific CPU-time clock
        "CLOCK_BOOTTIME": 7,  # Monotonic system-wide clock that includes time spent in suspension
        "CLOCK_REALTIME_ALARM": 8,  # Like CLOCK_REALTIME but also wakes suspended system
        "CLOCK_BOOTTIME_ALARM": 9,  # CLOCK_BOOTTIME but also wakes suspended system
        "CLOCK_TAI": 11,  # Like CLOCK_REALTIME but in International Atomic Time
    }

    # fadvise type #
    FADV_TYPE = {
        0: "FADV_NORMAL",  # No further special treatment
        1: "FADV_RANDOM",  # Expect random page references
        2: "FADV_SEQUENTIAL",  # Expect sequential page references
        3: "FADV_WILLNEED",  # Will need these pages
        4: "FADV_DONTNEED",  # Don't need these pages
        5: "FADV_NOREUSE",  # Data will be accessed once
        # 6: "FADV_DONTNEED",  # Don't need these pages
        # 7: "FADV_NOREUSE",  # Data will be accessed once
    }
    FADV_TYPE_REVERSE = {}

    # madvise type #
    MADV_TYPE = {
        0: "MADV_NORMAL",  # No further special treatment
        1: "MADV_RANDOM",  # Expect random page references
        2: "MADV_SEQUENTIAL",  # Expect sequential page references
        3: "MADV_WILLNEED",  # Will need these pages
        4: "MADV_DONTNEED",  # Don't need these pages
        8: "MADV_FREE",  # Free pages only if memory pressure
        9: "MADV_REMOVE",  # Remove these pages and resources
        10: "MADV_DONTFORK",  # Do not inherit across fork
        11: "MADV_DOFORK",  # Do inherit across fork
        12: "MADV_MERGEABLE",  # KSM may merge identical pages
        13: "MADV_UNMERGEABLE",  # KSM may not merge identical pages
        14: "MADV_HUGEPAGE",  # Worth backing with hugepages
        15: "MADV_NOHUGEPAGE",  # Not worth backing with hugepages
        16: "MADV_DONTDUMP",  # Explicitly exclude from the core dump,
        17: "MADV_DODUMP",  # Clear the MADV_DONTDUMP flag
        18: "MADV_WIPEONFORK",  # Zero memory on fork, child only
        19: "MADV_KEEPONFORK",  # Undo MADV_WIPEONFORK
        20: "MADV_COLD",  # Deactivatie these pages
        21: "MADV_PAGEOUT",  # Reclaim these pages
        100: "MADV_HWPOISON",  # Poison a page for testing
    }
    MADV_TYPE_REVERSE = {}

    # fanotify types #
    FAN_EVENT_TYPE = {
        0x00000001: "FAN_ACCESS",  # File was accessed #
        0x00000002: "FAN_MODIFY",  # File was modified #
        0x00000004: "FAN_ATTRIB",  # Metadata changed #
        0x00000008: "FAN_CLOSE_WRITE",  # Writtable file closed #
        0x00000010: "FAN_CLOSE_NOWRITE",  # Unwrittable file closed #
        0x00000020: "FAN_OPEN",  # File was opened #
        0x00000040: "FAN_MOVED_FROM",  # File was moved from X #
        0x00000080: "FAN_MOVED_TO",  # File was moved to Y #
        0x00000100: "FAN_CREATE",  # Subfile was created #
        0x00000200: "FAN_DELETE",  # Subfile was deleted #
        0x00000400: "FAN_DELETE_SELF",  # Self was deleted #
        0x00000800: "FAN_MOVE_SELF",  # Self was moved #
        0x00001000: "FAN_OPEN_EXEC",  # File was opened for exec #
        0x00004000: "FAN_Q_OVERFLOW",  # Event queued overflowed #
        0x00010000: "FAN_OPEN_PERM",  # File open in perm check #
        0x00020000: "FAN_ACCESS_PERM",  # File accessed in perm check #
        0x00040000: "FAN_OPEN_EXEC_PERM",  # File open/exec in perm check #
        0x40000000: "FAN_ONDIR",  # event occurred against dir #
        0x08000000: "FAN_EVENT_ON_CHILD",  # interested in child events #
    }
    FAN_EVENT_TYPE_REVERSE = {}

    FAN_INIT_TYPE = {
        0x00000000: "FAN_CLASS_NOTIF",
        0x00000001: "FAN_CLOEXEC",
        0x00000002: "FAN_NONBLOCK",
        0x00000004: "FAN_CLASS_CONTENT",
        0x00000008: "FAN_CLASS_PRE_CONTENT",
        0x00000010: "FAN_UNLIMITED_QUEUE",
        0x00000020: "FAN_UNLIMITED_MARKS",
        0x00000040: "FAN_ENABLE_AUDIT",
        0x00000100: "FAN_REPORT_TID",
        0x00000200: "FAN_REPORT_FID",
        0x00000800: "FAN_REPORT_NAME",
        0x00001000: "FAN_REPORT_TARGET_FID",
    }
    FAN_INIT_TYPE_REVERSE = {}
    """
    FAN_REPORT_DFID_NAME = FAN_REPORT_NAME | FAN_REPORT_DIR_FID
    FAN_REPORT_DFID_NAME_TARGET = \
        FAN_REPORT_DFID_NAME | FAN_REPORT_FID | FAN_REPORT_TARGETFID
    """

    FAN_MARK_TYPE = {
        0x00000000: "FAN_MARK_INODE",
        0x00000001: "FAN_MARK_ADD",
        0x00000002: "FAN_MARK_REMOVE",
        0x00000004: "FAN_MARK_DONT_FOLLOW",
        0x00000008: "FAN_MARK_ONLYDIR",
        0x00000010: "FAN_MARK_MOUNT",
        0x00000020: "FAN_MARK_IGNORED_MASK",
        0x00000040: "FAN_MARK_IGNORED_SURV_MODIFY",
        0x00000080: "FAN_MARK_FLUSH",
        0x00000100: "FAN_MARK_FILESYSTEM",
    }

    FAN_PERM_TYPE = {
        0x01: "FAN_ALLOW",
        0x02: "FAN_DENY",
        0x10: "FAN_AUDIT",
    }

    # netlink type #
    NETLINK_TYPE = {
        "NETLINK_ROUTE": 0,
        "NETLINK_UNUSED": 1,
        "NETLINK_USERSOCK": 2,
        "NETLINK_FIREWALL": 3,
        "NETLINK_SOCK_DIAG": 4,
        "NETLINK_NFLOG": 5,
        "NETLINK_XFRM": 6,
        "NETLINK_SELINUX": 7,
        "NETLINK_ISCSI": 8,
        "NETLINK_AUDIT": 9,
        "NETLINK_FIB_LOOKUP": 10,
        "NETLINK_CONNECTOR": 11,
        "NETLINK_NETFILTER": 12,
        "NETLINK_IP6_FW": 13,
        "NETLINK_DNRTMSG": 14,
        "NETLINK_KOBJECT_UEVENT": 15,
        "NETLINK_GENERIC": 16,
        "NETLINK_SCSITRANSPORT": 18,
        "NETLINK_ECRYPTFS": 19,
        "NETLINK_RDMA": 20,
        "NETLINK_CRYPTO": 21,
    }

    # entry type #
    INOTIFY_TYPE = {
        "IN_ACCESS": 0x00000001,  # File was accessed */
        "IN_MODIFY": 0x00000002,  # File was modified */
        "IN_ATTRIB": 0x00000004,  # Metadata changed */
        "IN_CLOSE_WRITE": 0x00000008,  # Writtable file was closed */
        "IN_CLOSE_NOWRITE": 0x00000010,  # Unwrittable file closed */
        "IN_CLOSE": 0x00000008 | 0x00000010,  # file closed */
        "IN_OPEN": 0x00000020,  # File was opened */
        "IN_MOVED_FROM": 0x00000040,  # File was moved from X */
        "IN_MOVED_TO": 0x00000080,  # File was moved to Y */
        "IN_MOVED": 0x00000040 | 0x00000080,  # File was moved */
        "IN_CREATE": 0x00000100,  # Subfile was created */
        "IN_DELETE": 0x00000200,  # Subfile was deleted */
        "IN_DELETE_SELF": 0x00000400,  # Self was deleted */
        "IN_MOVE_SELF": 0x00000800,  # Self was moved */
        "IN_UNMOUNT": 0x00002000,  # Backing fs was unmounted */
        "IN_Q_OVERFLOW": 0x00004000,  # Event queued overflowed */
        "IN_IGNORED": 0x00008000,  # File was ignored */
        "IN_ONLYDIR": 0x01000000,  # only watch the path if it is a directory */
        "IN_DONT_FOLLOW": 0x02000000,  # don't follow a sym link */
        "IN_EXCL_UNLINK": 0x04000000,  # exclude events on unlinked objects */
        "IN_MASK_CREATE": 0x10000000,  # only create watches */
        "IN_MASK_ADD": 0x20000000,  # add to the mask of an already existing watch */
        "IN_ISDIR": 0x40000000,  # event occurred against dir */
        "IN_ONESHOT": 0x80000000,  # only send event once */
    }

    # entry type #
    AT_TYPE = {
        "AT_IGNORE": 1,  # Entry should be ignored
        "AT_EXECFD": 2,  # File descriptor of program
        "AT_PHDR": 3,  # Program headers for program
        "AT_PHENT": 4,  # Size of program header entry
        "AT_PHNUM": 5,  # Number of program headers
        "AT_PAGESZ": 6,  # System page size
        "AT_BASE": 7,  # Base address of interpreter
        "AT_FLAGS": 8,  # Flags
        "AT_ENTRY": 9,  # Entry point of program
        "AT_NOTELF": 10,  # Program is not ELF
        "AT_UID": 11,  # Real uid
        "AT_EUID": 12,  # Effective uid
        "AT_GID": 13,  # Real gid
        "AT_EGID": 14,  # Effective gid
        "AT_CLKTCK": 17,  # Frequency of times()
        "AT_PLATFORM": 15,  # String identifying platform
        "AT_HWCAP": 16,  # Machine-dependent hints about
        "AT_FPUCW": 18,  # Used FPU control word
        "AT_DCACHEBSIZE": 19,  # Data cache block size
        "AT_ICACHEBSIZE": 20,  # Instruction cache block size
        "AT_UCACHEBSIZE": 21,  # Unified cache block size
        "AT_IGNOREPPC": 22,  # Entry should be ignored
        "AT_SECURE": 23,  # Boolean, was exec setuid-like?
        "AT_BASE_PLATFORM": 24,  # String identifying real platforms
        "AT_RANDOM": 25,  # Address of 16 random bytes
        "AT_HWCAP2": 26,  # More machine-dependent hints about
        "AT_EXECFN": 31,  # Filename of executable
        "AT_SYSINFO": 32,
        "AT_SYSINFO_EHDR": 33,
        "AT_L1I_CACHESHAPE": 34,
        "AT_L1D_CACHESHAPE": 35,
        "AT_L2_CACHESHAPE": 36,
        "AT_L3_CACHESHAPE": 37,
        "AT_L1I_CACHESIZE": 40,
        "AT_L1I_CACHEGEOMETRY": 41,
        "AT_L1D_CACHESIZE": 42,
        "AT_L1D_CACHEGEOMETRY": 43,
        "AT_L2_CACHESIZE": 44,
        "AT_L2_CACHEGEOMETRY": 45,
        "AT_L3_CACHESIZE": 46,
        "AT_L3_CACHEGEOMETRY": 47,
        "AT_MINSIGSTKSZ": 51,  # Stack needed for signal delivery
    }

    # syscall prototypes #
    SYSCALL_REFBUF = {
        "write": 0,
        "read": 0,
        "send": 0,
        "sendto": 0,
        "recv": 0,
        "recvfrom": 0,
        "pread": 0,
        "pread64": 0,
        "pwrite": 0,
        "pwrite64": 0,
    }

    # BPF syscall commands #
    BPF_CMD = [
        "BPF_MAP_CREATE",
        "BPF_MAP_LOOKUP_ELEM",
        "BPF_MAP_UPDATE_ELEM",
        "BPF_MAP_DELETE_ELEM",
        "BPF_MAP_GET_NEXT_KEY",
        "BPF_PROG_LOAD",
        "BPF_OBJ_PIN",
        "BPF_OBJ_GET",
        "BPF_PROG_ATTACH",
        "BPF_PROG_DETACH",
        "BPF_PROG_TEST_RUN",
        "BPF_PROG_GET_NEXT_ID",
        "BPF_MAP_GET_NEXT_ID",
        "BPF_PROG_GET_FD_BY_ID",
        "BPF_MAP_GET_FD_BY_ID",
        "BPF_OBJ_GET_INFO_BY_FD",
    ]

    BPF_MAP_TYPE = [
        "BPF_MAP_TYPE_UNSPEC",
        "BPF_MAP_TYPE_HASH",
        "BPF_MAP_TYPE_ARRAY",
        "BPF_MAP_TYPE_PROG_ARRAY",
        "BPF_MAP_TYPE_PERF_EVENT_ARRAY",
        "BPF_MAP_TYPE_PERCPU_HASH",
        "BPF_MAP_TYPE_PERCPU_ARRAY",
        "BPF_MAP_TYPE_STACK_TRACE",
        "BPF_MAP_TYPE_CGROUP_ARRAY",
        "BPF_MAP_TYPE_LRU_HASH",
        "BPF_MAP_TYPE_LRU_PERCPU_HASH",
        "BPF_MAP_TYPE_LPM_TRIE",
        "BPF_MAP_TYPE_ARRAY_OF_MAPS",
        "BPF_MAP_TYPE_HASH_OF_MAPS",
        "BPF_MAP_TYPE_DEVMAP",
        "BPF_MAP_TYPE_SOCKMAP",
    ]

    BPF_PROG_TYPE = [
        "BPF_PROG_TYPE_UNSPEC",
        "BPF_PROG_TYPE_SOCKET_FILTER",
        "BPF_PROG_TYPE_KPROBE",
        "BPF_PROG_TYPE_SCHED_CLS",
        "BPF_PROG_TYPE_SCHED_ACT",
        "BPF_PROG_TYPE_TRACEPOINT",
        "BPF_PROG_TYPE_XDP",
        "BPF_PROG_TYPE_PERF_EVENT",
        "BPF_PROG_TYPE_CGROUP_SKB",
        "BPF_PROG_TYPE_CGROUP_SOCK",
        "BPF_PROG_TYPE_LWT_IN",
        "BPF_PROG_TYPE_LWT_OUT",
        "BPF_PROG_TYPE_LWT_XMIT",
        "BPF_PROG_TYPE_SOCK_OPS",
        "BPF_PROG_TYPE_SK_SKB",
    ]

    BPF_ATTACH_TYPE = [
        "BPF_CGROUP_INET_INGRESS",
        "BPF_CGROUP_INET_EGRESS",
        "BPF_CGROUP_INET_SOCK_CREATE",
        "BPF_CGROUP_SOCK_OPS",
        "BPF_SK_SKB_STREAM_PARSER",
        "BPF_SK_SKB_STREAM_VERDICT",
        "__MAX_BPF_ATTACH_TYPE",
    ]

    # SAVE RESOURCE COMMANDS #
    RESSET = (
        "SAVECPU",
        "SAVETOTALCPU",
        "SAVEMEM",
        "SAVEMEMPROC",
        "SAVEBLOCK",
        "SAVETOTALBLOCK",
        "SAVEPID",
        "SAVERUNTIME",
        "SAVEPRIO",
        "SAVESWAP",
    )

    # regset types #
    NT_TYPE = {
        "NT_PRSTATUS": 1,
        "NT_PRFPREG": 2,
        "NT_PRPSINFO": 3,
        "NT_TASKSTRUCT": 4,
        "NT_AUXV": 6,
        "NT_SIGINFO": 0x53494749,
        "NT_FILE": 0x46494C45,
        "NT_PRXFPREG": 0x46E62B7F,
        "NT_ARM_VFP": 0x400,
        "NT_ARM_TLS": 0x401,
        "NT_ARM_HW_BREAK": 0x402,
        "NT_ARM_HW_WATCH": 0x403,
        "NT_ARM_SYSTEM_CALL": 0x404,
    }

    # unpack types #
    UNPACK_TYPE = {
        "U1": "B",
        "U2": "H",
        "U4": "I",
        "U8": "Q",
        "S1": "b",
        "S2": "h",
        "S4": "i",
        "S8": "q",
        "float": "f",
        "double": "d",
    }

    # capabiliities #
    capabilities = [
        "chown",
        "dac_override",
        "dac_read_search",
        "fowner",
        "fsetid",
        "kill",
        "setgid",
        "setuid",
        "setpcap",
        "linux_immutable",
        "net_bind_service",
        "net_broadcast",
        "net_admin",
        "net_raw",
        "ipc_lock",
        "ipc_owner",
        "sys_module",
        "sys_rawio",
        "sys_chroot",
        "sys_ptrace",
        "sys_pacct",
        "sys_admin",
        "sys_boot",
        "sys_nice",
        "sys_resource",
        "sys_time",
        "sys_tty_config",
        "mknod",
        "lease",
        "audit_write",
        "audit_control",
        "setfcap",
        "mac_override",
        "mac_admin",
        "syslog",
        "wake_alarm",
        "block_suspend",
        "audit_read",
    ]

    # defferable syscall prototypes #
    SYSCALL_DEFFERABLE = {
        "clock_gettime": 0,
        "clone": 0,
        "clone3": 0,
        "getgroups": 0,
        "getgroups16": 0,
        "gethostname": 0,
        "getitimer": 0,
        "getpeername": 0,
        "getsockname": 0,
        "gettimeofday": 0,
        "io_uring_setup": 0,
        "pread": 0,
        "pread64": 0,
        "process_vm_readv": 0,
        "read": 0,
        "readlink": 0,
        "readv": 0,
        "recv": 0,
        "recvfrom": 0,
        "recvmmsg": 0,
        "recvmsg": 0,
    }

    # defferable syscall return prototypes #
    SYSCALL_DEFFERABLE_RET = {
        "ppoll": 0,
    }

    # syscall prototypes #
    SYSCALL_PROTOTYPES = {
        "accept": (
            "long",
            (
                ("int", "sockfd"),
                ("struct sockaddr *", "addr"),
                ("int *", "addrlen"),
            ),
        ),
        "accept4": (
            "long",
            (
                ("int", "sockfd"),
                ("struct sockaddr *", "addr"),
                ("int *", "addrlen"),
                ("int", "flags"),
            ),
        ),
        "access": ("long", (("const char *", "filename"), ("int", "mode"))),
        "acct": ("long", (("const char *", "name"),)),
        "add_key": (
            "long",
            (
                ("const char *", "_type"),
                ("const char *", "_description"),
                ("const void *", "_payload"),
                ("size_t", "plen"),
                ("key_serial_t", "destringid"),
            ),
        ),
        "adjtimex": ("long", (("struct timex *", "txc_p"),)),
        "alarm": ("long", (("unsigned int", "seconds"),)),
        "bdflush": ("long", (("int", "func"), ("long", "data"))),
        "bind": (
            "long",
            (
                ("int", "sockfd"),
                ("struct sockaddr *", "addr"),
                ("int", "addrlen"),
            ),
        ),
        "bpf": (
            "long",
            (
                ("int", "cmd"),
                ("union bpf_attr *", "attr"),
                ("unsigned int", "size"),
            ),
        ),
        "brk": ("long", (("unsigned long", "brk"),)),
        "capget": (
            "long",
            (("cap_user_header_t", "header"), ("cap_user_data_t", "dataptr")),
        ),
        "capset": (
            "long",
            (
                ("cap_user_header_t", "header"),
                ("const cap_user_data_t", "data"),
            ),
        ),
        "chdir": ("long", (("const char *", "filename"),)),
        "chmod": ("long", (("const char *", "filename"), ("umode_t", "mode"))),
        "chown": (
            "long",
            (
                ("const char *", "filename"),
                ("uid_t", "user"),
                ("gid_t", "group"),
            ),
        ),
        "chown16": (
            "long",
            (
                ("const char *", "filename"),
                ("old_uid_t", "user"),
                ("old_gid_t", "group"),
            ),
        ),
        "chroot": ("long", (("const char *", "filename"),)),
        "clock_adjtime": (
            "long",
            (("clockid_t", "which_clock"), ("struct timex *", "tx")),
        ),
        "clock_getres": (
            "long",
            (("clockid_t", "which_clock"), ("struct timespec *", "tp")),
        ),
        "clock_gettime": (
            "long",
            (("clockid_t", "which_clock"), ("struct timespec *", "tp")),
        ),
        "clock_nanosleep": (
            "long",
            (
                ("clockid_t", "which_clock"),
                ("int", "flags"),
                ("const struct timespec *", "rqtp"),
                ("struct timespec *", "rmtp"),
            ),
        ),
        "clock_settime": (
            "long",
            (("clockid_t", "which_clock"), ("const struct timespec *", "tp")),
        ),
        "clone": (
            "long",
            (
                ("unsigned long", "flags"),
                ("unsigned long", "child_stack"),
                ("int *", "ptid"),
                ("int *", "ctid"),
                ("unsigned long", "regs"),
            ),
        ),
        "clone3": (
            "long",
            (("struct clone_args *", "uargs"), ("size_t", "size")),
        ),
        "close": ("long", (("unsigned int", "fd"),)),
        "close_range": (
            "long",
            (
                ("unsigned int", "fd"),
                ("unsigned int", "max_fd"),
                ("unsigned int", "flags"),
            ),
        ),
        "connect": (
            "long",
            (
                ("int", "sockfd"),
                ("struct sockaddr *", "addr"),
                ("int", "addrlen"),
            ),
        ),
        "copy_file_range": (
            "long",
            (
                ("int", "fd_in"),
                ("loff_t *", "off_in"),
                ("int", "fd_out"),
                ("loff_t *", "off_out"),
                ("size_t", "len"),
                ("unsigned int", "flags"),
            ),
        ),
        "creat": ("long", (("const char *", "pathname"), ("umode_t", "mode"))),
        "creat_module": ("long", (("const char *", "name"), ("int", "size"))),
        "delete_module": (
            "long",
            (("const char *", "name_user"), ("unsigned int", "flags")),
        ),
        "dup": ("long", (("unsigned int", "fildes"),)),
        "dup2": (
            "long",
            (("unsigned int", "oldfd"), ("unsigned int", "newfd")),
        ),
        "dup3": (
            "long",
            (
                ("unsigned int", "oldfd"),
                ("unsigned int", "newfd"),
                ("int", "flags"),
            ),
        ),
        "epoll_create": ("long", (("int", "size"),)),
        "epoll_create1": ("long", (("int", "flags"),)),
        "epoll_ctl": (
            "long",
            (
                ("int", "epfd"),
                ("int", "op"),
                ("int", "fd"),
                ("struct epoll_event *", "event"),
            ),
        ),
        "epoll_pwait": (
            "long",
            (
                ("int", "epfd"),
                ("struct epoll_event *", "events"),
                ("int", "maxevents"),
                ("int", "timeout"),
                ("const sigset_t *", "sigmask"),
                ("size_t", "sigsetsize"),
            ),
        ),
        "epoll_wait": (
            "long",
            (
                ("int", "epfd"),
                ("struct epoll_event *", "events"),
                ("int", "maxevents"),
                ("int", "timeout"),
            ),
        ),
        "eventfd": ("long", (("unsigned int", "count"),)),
        "eventfd2": ("long", (("unsigned int", "count"), ("int", "flags"))),
        "execve": (
            "long",
            (
                ("const char *", "filename"),
                ("const char * const *", "argv"),
                ("const char * const *", "envp"),
            ),
        ),
        "execveat": (
            "long",
            (
                ("int", "dfd"),
                ("const char *", "filename"),
                ("const char * const *", "argv"),
                ("const char * const *", "envp"),
                ("int", "flags"),
            ),
        ),
        "exit": ("long", (("int", "error_code"),)),
        "exit_group": ("long", (("int", "error_code"),)),
        "faccessat": (
            "long",
            (("int", "dfd"), ("const char *", "filename"), ("int", "mode")),
        ),
        "faccessat2": (
            "long",
            (
                ("int", "dfd"),
                ("const char *", "filename"),
                ("int", "mode"),
                ("int", "flags"),
            ),
        ),
        "fadvise64": (
            "long",
            (
                ("int", "fd"),
                ("loff_t", "offset"),
                ("size_t", "len"),
                ("int", "advice"),
            ),
        ),
        "fadvise64_64": (
            "long",
            (
                ("int", "fd"),
                ("loff_t", "offset"),
                ("loff_t", "len"),
                ("int", "advice"),
            ),
        ),
        "fallocate": (
            "long",
            (
                ("int", "fd"),
                ("int", "mode"),
                ("loff_t", "offset"),
                ("loff_t", "len"),
            ),
        ),
        "fanotify_init": (
            "long",
            (("unsigned int", "flags"), ("unsigned int", "event_f_flags")),
        ),
        "fanotify_mark": (
            "long",
            (
                ("int", "fanotify_fd"),
                ("unsigned int", "flags"),
                ("u64", "mask"),
                ("int", "dirfd"),
                ("const char *", "pathname"),
            ),
        ),
        "fchdir": ("long", (("unsigned int", "fd"),)),
        "fchmod": ("long", (("unsigned int", "fd"), ("umode_t", "mode"))),
        "fchmodat": (
            "long",
            (
                ("int", "dfd"),
                ("const char *", "filename"),
                ("umode_t", "mode"),
            ),
        ),
        "fchown": (
            "long",
            (("unsigned int", "fd"), ("uid_t", "user"), ("gid_t", "group")),
        ),
        "fchown16": (
            "long",
            (
                ("unsigned int", "fd"),
                ("old_uid_t", "user"),
                ("old_gid_t", "group"),
            ),
        ),
        "fchownat": (
            "long",
            (
                ("int", "dfd"),
                ("const char *", "filename"),
                ("uid_t", "user"),
                ("gid_t", "group"),
                ("int", "flag"),
            ),
        ),
        "fcntl": (
            "long",
            (
                ("unsigned int", "fd"),
                ("unsigned int", "cmd"),
                ("unsigned long", "arg"),
            ),
        ),
        "fcntl64": (
            "long",
            (
                ("unsigned int", "fd"),
                ("unsigned int", "cmd"),
                ("unsigned long", "arg"),
            ),
        ),
        "fdatasync": ("long", (("unsigned int", "fd"),)),
        "fgetxattr": (
            "long",
            (
                ("int", "fd"),
                ("const char *", "name"),
                ("void *", "value"),
                ("size_t", "size"),
            ),
        ),
        "finit_module": (
            "long",
            (("int", "fd"), ("const char *", "uargs"), ("int", "flags")),
        ),
        "flistxattr": (
            "long",
            (("int", "fd"), ("const char *", "list"), ("size_t", "size")),
        ),
        "flock": ("long", (("unsigned int", "fd"), ("unsigned int", "cmd"))),
        "fork": ("long", ()),
        "fremovexattr": ("long", (("int", "fd"), ("const char *", "name"))),
        "fsconfig": (
            "long",
            (
                ("int", "fs_fd"),
                ("unsigned int", "cmd"),
                ("const char *", "key"),
                ("const void *", "value"),
                ("int", "aux"),
            ),
        ),
        "fsetxattr": (
            "long",
            (
                ("int", "fd"),
                ("const char *", "name"),
                ("const void *", "value"),
                ("size_t", "size"),
                ("int", "flags"),
            ),
        ),
        "fsmount": (
            "long",
            (
                ("int", "fs_fd"),
                ("unsigned int", "flags"),
                ("unsigned int", "ms_flags"),
            ),
        ),
        "fsopen": (
            "long",
            (("const char *", "fs_name"), ("unsigned int", "flags")),
        ),
        "fspick": (
            "long",
            (
                ("int", "dfd"),
                ("const char *", "path"),
                ("unsigned int", "flags"),
            ),
        ),
        "fstat": (
            "long",
            (
                ("unsigned int", "fd"),
                ("struct __old_kernel_stat *", "statbuf"),
            ),
        ),
        "fstat64": (
            "long",
            (("unsigned long", "fd"), ("struct stat64 *", "statbuf")),
        ),
        "fstatat64": (
            "long",
            (
                ("int", "dfd"),
                ("const char *", "filename"),
                ("struct stat64 *", "statbuf"),
                ("int", "flag"),
            ),
        ),
        "fstatfs": (
            "long",
            (("unsigned int", "fd"), ("struct statfs *", "buf")),
        ),
        "fstatfs64": (
            "long",
            (
                ("unsigned int", "fd"),
                ("size_t", "sz"),
                ("struct statfs64 *", "buf"),
            ),
        ),
        "fsync": ("long", (("unsigned int", "fd"),)),
        "ftruncate": (
            "long",
            (("unsigned int", "fd"), ("unsigned long", "length")),
        ),
        "ftruncate64": (
            "long",
            (("unsigned int", "fd"), ("loff_t", "length")),
        ),
        "futex": (
            "long",
            (
                ("u32 *", "uaddr"),
                ("int", "op"),
                ("u32", "val"),
                ("struct timespec *", "utime"),
                ("u32 *", "uaddr2"),
                ("u32", "val3"),
            ),
        ),
        "futimesat": (
            "long",
            (
                ("int", "dfd"),
                ("const char *", "filename"),
                ("struct old_timeval *", "utimes"),
            ),
        ),
        "get_kernel_syms": ("long", (("struct kernel_sym *", "table"))),
        "get_mempolicy": (
            "long",
            (
                ("int *", "policy"),
                ("unsigned long *", "nmask"),
                ("unsigned long", "maxnode"),
                ("unsigned long", "addr"),
                ("unsigned long", "flags"),
            ),
        ),
        "get_robust_list": (
            "long",
            (
                ("int", "pid"),
                ("struct robust_list_head * *", "head_ptr"),
                ("size_t *", "len_ptr"),
            ),
        ),
        "get_thread_area": ("long", (("struct user_desc *", "u_info"))),
        "getcpu": (
            "long",
            (
                ("unsigned *", "cpu"),
                ("unsigned *", "node"),
                ("struct getcpu_cache *", "cache"),
            ),
        ),
        "getcwd": (
            "long",
            (("char *", "pathname"), ("unsigned long", "size")),
        ),
        "getdents": (
            "long",
            (
                ("unsigned int", "fd"),
                ("struct linux_dirent *", "dirent"),
                ("unsigned int", "count"),
            ),
        ),
        "getdents64": (
            "long",
            (
                ("unsigned int", "fd"),
                ("struct linux_dirent64 *", "dirent"),
                ("unsigned int", "count"),
            ),
        ),
        "getegid": ("long", ()),
        "getegid16": ("long", ()),
        "geteuid": ("long", ()),
        "geteuid16": ("long", ()),
        "getgid": ("long", ()),
        "getgid16": ("long", ()),
        "getgroups": (
            "long",
            (("int", "gidsetsize"), ("gid_t *", "grouplist")),
        ),
        "getgroups16": (
            "long",
            (("int", "gidsetsize"), ("old_gid_t *", "grouplist")),
        ),
        "gethostname": ("long", (("char *", "name"), ("int", "len"))),
        "getitimer": (
            "long",
            (("int", "which"), ("struct itimerval *", "value")),
        ),
        "getpeername": (
            "long",
            (
                ("int", "sockfd"),
                ("struct sockaddr *", "addr"),
                ("int *", "addrlen"),
            ),
        ),
        "getpgid": ("long", (("pid_t", "pid"),)),
        "getpgrp": ("long", ()),
        "getpid": ("long", ()),
        "getppid": ("long", ()),
        "getpriority": ("long", (("int", "which"), ("int", "who"))),
        "getrandom": (
            "long",
            (
                ("void *", "buf"),
                ("size_t", "count"),
                ("unsigned int", "flags"),
            ),
        ),
        "getresgid": (
            "long",
            (("gid_t *", "rgid"), ("gid_t *", "egid"), ("gid_t *", "sgid")),
        ),
        "getresgid16": (
            "long",
            (
                ("old_gid_t *", "rgid"),
                ("old_gid_t *", "egid"),
                ("old_gid_t *", "sgid"),
            ),
        ),
        "getresuid": (
            "long",
            (("uid_t *", "ruid"), ("uid_t *", "euid"), ("uid_t *", "suid")),
        ),
        "getresuid16": (
            "long",
            (
                ("old_uid_t *", "ruid"),
                ("old_uid_t *", "euid"),
                ("old_uid_t *", "suid"),
            ),
        ),
        "getrlimit": (
            "long",
            (("unsigned int", "resource"), ("struct rlimit *", "rlim")),
        ),
        "ugetrlimit": (
            "long",
            (("unsigned int", "resource"), ("struct rlimit *", "rlim")),
        ),
        "getrusage": ("long", (("int", "who"), ("struct rusage *", "ru"))),
        "getsid": ("long", (("pid_t", "pid"),)),
        "getsockname": (
            "long",
            (
                ("int", "sockfd"),
                ("struct sockaddr *", "addr"),
                ("int *", "addrlen"),
            ),
        ),
        "getsockopt": (
            "long",
            (
                ("int", "fd"),
                ("int", "level"),
                ("int", "optname"),
                ("void *", "optval"),
                ("int *", "optlen"),
            ),
        ),
        "gettid": ("long", ()),
        "gettimeofday": (
            "long",
            (("struct timeval *", "tv"), ("struct timezone *", "tz")),
        ),
        "getuid": ("long", ()),
        "getuid16": ("long", ()),
        "getxattr": (
            "long",
            (
                ("const char *", "path"),
                ("const char *", "name"),
                ("void *", "value"),
                ("size_t", "size"),
            ),
        ),
        "init_module": (
            "long",
            (
                ("void *", "umod"),
                ("unsigned long", "len"),
                ("const char *", "uargs"),
            ),
        ),
        "inotify_add_watch": (
            "long",
            (("int", "fd"), ("const char *", "path"), ("u32", "mask")),
        ),
        "inotify_init": ("long", ()),
        "inotify_init1": ("long", (("int", "flags"),)),
        "inotify_rm_watch": ("long", (("int", "fd"), ("__s32", "wd"))),
        "io_cancel": (
            "long",
            (
                ("aio_context_t", "ctx_id"),
                ("struct iocb *", "iocb"),
                ("struct io_event *", "result"),
            ),
        ),
        "io_destroy": ("long", (("aio_context_t", "ctx"),)),
        "io_getevents": (
            "long",
            (
                ("aio_context_t", "ctx_id"),
                ("long", "min_nr"),
                ("long", "nr"),
                ("struct io_event *", "events"),
                ("struct timespec *", "timeout"),
            ),
        ),
        "io_pgetevents": (
            "long",
            (
                ("aio_context_t", "ctx_id"),
                ("long", "min_nr"),
                ("long", "nr"),
                ("struct io_event *", "events"),
                ("struct timespec *", "timeout"),
                ("const struct __aio_sigset *", "sig"),
            ),
        ),
        "io_setup": (
            "long",
            (("unsigned", "nr_reqs"), ("aio_context_t *", "ctx")),
        ),
        "io_submit": (
            "long",
            (
                ("aio_context_t", "ctx_id"),
                ("long", "nr"),
                ("struct iocb * *", "iocbpp"),
            ),
        ),
        "io_uring_enter": (
            "long",
            (
                ("unsigned int", "fd"),
                ("u32", "to_submit"),
                ("u32", "min_complete"),
                ("u32", "flags"),
                ("const sigset_t *", "sig"),
                ("size_t", "sigsz"),
            ),
        ),
        "io_uring_register": (
            "long",
            (
                ("unsigned int", "fd"),
                ("unsigned int", "op"),
                ("void *", "arg"),
                ("unsigned int", "nr_args"),
            ),
        ),
        "io_uring_setup": (
            "long",
            (("u32", "entries"), ("struct io_uring_params *", "p")),
        ),
        "ioctl": (
            "long",
            (
                ("unsigned int", "fd"),
                ("unsigned int", "cmd"),
                ("unsigned long", "arg"),
            ),
        ),
        "ioperm": (
            "long",
            (
                ("unsigned long", "from"),
                ("unsigned long", "num"),
                ("int", "on"),
            ),
        ),
        "iopl": (
            "long",
            (("int", "level")),
        ),
        "ioprio_get": ("long", (("int", "which"), ("int", "who"))),
        "ioprio_set": (
            "long",
            (("int", "which"), ("int", "who"), ("int", "ioprio")),
        ),
        "ipc": (
            "long",
            (
                ("unsigned int", "call"),
                ("int", "first"),
                ("unsigned long", "second"),
                ("unsigned long", "third"),
                ("void *", "ptr"),
                ("long", "fifth"),
            ),
        ),
        "kcmp": (
            "long",
            (
                ("pid_t", "pid1"),
                ("pid_t", "pid2"),
                ("int", "type"),
                ("unsigned long", "idx1"),
                ("unsigned long", "idx2"),
            ),
        ),
        "kexec_file_load": (
            "long",
            (
                ("int", "kernel_fd"),
                ("int", "initrd_fd"),
                ("unsigned long", "cmdline_len"),
                ("const char *", "cmdline_ptr"),
                ("unsigned long", "flags"),
            ),
        ),
        "kexec_load": (
            "long",
            (
                ("unsigned long", "entry"),
                ("unsigned long", "nr_segments"),
                ("struct kexec_segment *", "segments"),
                ("unsigned long", "flags"),
            ),
        ),
        "keyctl": (
            "long",
            (
                ("int", "cmd"),
                ("unsigned long", "arg2"),
                ("unsigned long", "arg3"),
                ("unsigned long", "arg4"),
                ("unsigned long", "arg5"),
            ),
        ),
        "kill": ("long", (("int", "pid"), ("int", "sig"))),
        "lchown": (
            "long",
            (
                ("const char *", "filename"),
                ("uid_t", "user"),
                ("gid_t", "group"),
            ),
        ),
        "lchown16": (
            "long",
            (
                ("const char *", "filename"),
                ("old_uid_t", "user"),
                ("old_gid_t", "group"),
            ),
        ),
        "lgetxattr": (
            "long",
            (
                ("const char *", "path"),
                ("const char *", "name"),
                ("void *", "value"),
                ("size_t", "size"),
            ),
        ),
        "link": (
            "long",
            (("const char *", "oldname"), ("const char *", "newname")),
        ),
        "linkat": (
            "long",
            (
                ("int", "olddfd"),
                ("const char *", "oldname"),
                ("int", "newdfd"),
                ("const char *", "newname"),
                ("int", "flags"),
            ),
        ),
        "listen": ("long", (("int", "sockfd"), ("int", "backlog"))),
        "listxattr": (
            "long",
            (
                ("const char *", "path"),
                ("char char *", "list"),
                ("size_t", "size"),
            ),
        ),
        "llistxattr": (
            "long",
            (
                ("const char *", "path"),
                ("char char *", "list"),
                ("size_t", "size"),
            ),
        ),
        "llseek": (
            "long",
            (
                ("unsigned int", "fd"),
                ("unsigned long", "offset_high"),
                ("unsigned long", "offset_low"),
                ("loff_t *", "result"),
                ("unsigned int", "whence"),
            ),
        ),
        "lookup_dcookie": (
            "long",
            (("u64", "cookie64"), ("char *", "buf"), ("size_t", "len")),
        ),
        "lremovexattr": (
            "long",
            (("const char *", "path"), ("const char *", "name")),
        ),
        "lseek": (
            "long",
            (
                ("unsigned int", "fd"),
                ("off_t", "offset"),
                ("unsigned int", "whence"),
            ),
        ),
        "lsetxattr": (
            "long",
            (
                ("const char *", "path"),
                ("const char *", "name"),
                ("const void *", "value"),
                ("size_t", "size"),
                ("int", "flags"),
            ),
        ),
        "lstat": (
            "long",
            (
                ("const char *", "filename"),
                ("struct __old_kernel_stat *", "statbuf"),
            ),
        ),
        "lstat64": (
            "long",
            (("const char *", "filename"), ("struct stat64 *", "statbuf")),
        ),
        "madvise": (
            "long",
            (
                ("unsigned long", "addr"),
                ("size_t", "length"),
                ("int", "advice"),
            ),
        ),
        "mbind": (
            "long",
            (
                ("unsigned long", "start"),
                ("unsigned long", "len"),
                ("unsigned long", "mode"),
                ("const unsigned long *", "nmask"),
                ("unsigned long", "maxnode"),
                ("unsigned", "flags"),
            ),
        ),
        "membarrier": ("long", (("int", "cmd"), ("int", "flags"))),
        "memfd_create": (
            "long",
            (("const char *", "uname_ptr"), ("unsigned int", "flags")),
        ),
        "migrate_pages": (
            "long",
            (
                ("pid_t", "pid"),
                ("unsigned long", "maxnode"),
                ("const unsigned long *", "from"),
                ("const unsigned long *", "to"),
            ),
        ),
        "mincore": (
            "long",
            (
                ("unsigned long", "start"),
                ("size_t", "len"),
                ("unsigned char *", "vec"),
            ),
        ),
        "mkdir": ("long", (("const char *", "pathname"), ("umode_t", "mode"))),
        "mkdirat": (
            "long",
            (
                ("int", "dfd"),
                ("const char *", "pathname"),
                ("umode_t", "mode"),
            ),
        ),
        "mknod": (
            "long",
            (
                ("const char *", "filename"),
                ("umode_t", "mode"),
                ("unsigned", "dev"),
            ),
        ),
        "mknodat": (
            "long",
            (
                ("int", "dfd"),
                ("const char *", "filename"),
                ("umode_t", "mode"),
                ("unsigned", "dev"),
            ),
        ),
        "mlock": ("long", (("unsigned long", "start"), ("size_t", "len"))),
        "mlock2": (
            "long",
            (("unsigned long", "start"), ("size_t", "len"), ("int", "flags")),
        ),
        "mlockall": ("long", (("int", "flags"),)),
        "mmap_pgoff": (
            "long",
            (
                ("unsigned long", "addr"),
                ("unsigned long", "len"),
                ("unsigned long", "prot"),
                ("unsigned long", "flags"),
                ("unsigned long", "fd"),
                ("unsigned long", "pgoff"),
            ),
        ),
        "mmap": (
            "long",
            (
                ("unsigned long", "addr"),
                ("unsigned long", "len"),
                ("unsigned long", "prot"),
                ("unsigned long", "flags"),
                ("unsigned long", "fd"),
                ("unsigned long", "pgoff"),
            ),
        ),
        "mmap2": (
            "long",
            (
                ("unsigned long", "addr"),
                ("unsigned long", "len"),
                ("unsigned long", "prot"),
                ("unsigned long", "flags"),
                ("unsigned long", "fd"),
                ("unsigned long", "pgoff"),
            ),
        ),
        "move_mount": (
            "long",
            (
                ("int", "from_dfd"),
                ("const char *", "from_path"),
                ("int", "to_dfd"),
                ("const char *", "to_path"),
                ("unsigned int", "ms_flags"),
            ),
        ),
        "arch_prctl": ("int", (("int", "code"), ("unsigned long", "addr"))),
        "mount": (
            "long",
            (
                ("const char *", "dev_name"),
                ("const char *", "dir_name"),
                ("const char *", "type"),
                ("unsigned long", "flags"),
                ("void *", "data"),
            ),
        ),
        "move_pages": (
            "long",
            (
                ("pid_t", "pid"),
                ("unsigned long", "nr_pages"),
                ("const void * *", "pages"),
                ("const int *", "nodes"),
                ("int *", "status"),
                ("int", "flags"),
            ),
        ),
        "mprotect": (
            "long",
            (
                ("unsigned long", "start"),
                ("size_t", "len"),
                ("unsigned long", "prot"),
            ),
        ),
        "mq_getsetattr": (
            "long",
            (
                ("mqd_t", "mqdes"),
                ("const struct mq_attr *", "mqstat"),
                ("struct mq_attr *", "omqstat"),
            ),
        ),
        "mq_notify": (
            "long",
            (("mqd_t", "mqdes"), ("const struct sigevent *", "notification")),
        ),
        "mq_open": (
            "long",
            (
                ("const char *", "name"),
                ("int", "oflag"),
                ("umode_t", "mode"),
                ("struct mq_attr *", "attr"),
            ),
        ),
        "mq_timedreceive": (
            "long",
            (
                ("mqd_t", "mqdes"),
                ("char *", "msg_ptr"),
                ("size_t", "msg_len"),
                ("unsigned int *", "msg_prio"),
                ("const struct timespec *", "abs_timeout"),
            ),
        ),
        "mq_timedsend": (
            "long",
            (
                ("mqd_t", "mqdes"),
                ("const char *", "msg_ptr"),
                ("size_t", "msg_len"),
                ("unsigned int", "msg_prio"),
                ("const struct timespec *", "abs_timeout"),
            ),
        ),
        "mq_unlink": ("long", (("const char *", "name"),)),
        "mremap": (
            "long",
            (
                ("unsigned long", "addr"),
                ("unsigned long", "old_len"),
                ("unsigned long", "new_len"),
                ("unsigned long", "flags"),
                ("unsigned long", "new_addr"),
            ),
        ),
        "msgctl": (
            "long",
            (("int", "msqid"), ("int", "cmd"), ("struct msqid_ds *", "buf")),
        ),
        "msgget": ("long", (("key_t", "key"), ("int", "msgflg"))),
        "msgrcv": (
            "long",
            (
                ("int", "msqid"),
                ("struct msgbuf *", "msgp"),
                ("size_t", "msgsz"),
                ("long", "msgtyp"),
                ("int", "msgflg"),
            ),
        ),
        "msgsnd": (
            "long",
            (
                ("int", "msqid"),
                ("struct msgbuf *", "msgp"),
                ("size_t", "msgsz"),
                ("int", "msgflg"),
            ),
        ),
        "msync": (
            "long",
            (("unsigned long", "start"), ("size_t", "len"), ("int", "flags")),
        ),
        "munlock": ("long", (("unsigned long", "start"), ("size_t", "len"))),
        "munlockall": ("long", ()),
        "munmap": ("long", (("unsigned long", "addr"), ("size_t", "len"))),
        "name_to_handle_at": (
            "long",
            (
                ("int", "dfd"),
                ("const char *", "name"),
                ("struct file_handle *", "handle"),
                ("int *", "mnt_id"),
                ("int", "flag"),
            ),
        ),
        "nanosleep": (
            "long",
            (("struct timespec *", "rqtp"), ("struct timespec *", "rmtp")),
        ),
        "newfstat": (
            "long",
            (("unsigned int", "fd"), ("struct stat *", "statbuf")),
        ),
        "newfstatat": (
            "long",
            (
                ("int", "dfd"),
                ("const char *", "filename"),
                ("struct stat *", "statbuf"),
                ("int", "flag"),
            ),
        ),
        "newlstat": (
            "long",
            (("const char *", "filename"), ("struct stat *", "statbuf")),
        ),
        "newstat": (
            "long",
            (("const char *", "filename"), ("struct stat *", "statbuf")),
        ),
        "newuname": ("long", (("struct new_utsname *", "name"),)),
        "ni_syscall": ("long", ()),
        "nice": ("long", (("int", "increment"),)),
        "old_getrlimit": (
            "long",
            (("unsigned int", "resource"), ("struct rlimit *", "rlim")),
        ),
        "old_mmap": ("long", (("struct mmap_arg_struct *", "arg"),)),
        "old_msgctl": (
            "long",
            (("int", "msqid"), ("int", "cmd"), ("struct msqid_ds *", "buf")),
        ),
        "old_readdir": (
            "long",
            (
                ("unsigned int", "fd"),
                ("struct old_linux_dirent *", "dirp"),
                ("unsigned int", "count"),
            ),
        ),
        "old_select": ("long", (("struct sel_arg_struct *", "arg"),)),
        "old_semctl": (
            "long",
            (
                ("int", "semid"),
                ("int", "semnum"),
                ("int", "cmd"),
                ("unsigned long", "arg"),
            ),
        ),
        "old_shmctl": (
            "long",
            (("int", "shmid"), ("int", "cmd"), ("struct shmid_ds *", "buf")),
        ),
        "oldumount": ("long", (("char char *", "name"),)),
        "olduname": ("long", (("struct oldold_utsname *", "buf"),)),
        "open": (
            "long",
            (
                ("const char *", "filename"),
                ("int", "flags"),
                ("umode_t", "mode"),
            ),
        ),
        "open_by_handle_at": (
            "long",
            (
                ("int", "mountdirfd"),
                ("struct file_handle *", "handle"),
                ("int", "flags"),
            ),
        ),
        "open_tree": (
            "long",
            (("int", "dfd"), ("const char *", "path"), ("unsigned", "flags")),
        ),
        "openat": (
            "long",
            (
                ("int", "dfd"),
                ("const char *", "filename"),
                ("int", "flags"),
                ("umode_t", "mode"),
            ),
        ),
        "openat2": (
            "long",
            (
                ("int", "dfd"),
                ("const char *", "filename"),
                ("struct open_how *", "how"),
                ("size_t", "size"),
            ),
        ),
        "pause": ("long", ()),
        "pciconfig_iobase": (
            "long",
            (
                ("long", "which"),
                ("unsigned long", "bus"),
                ("unsigned long", "devfn"),
            ),
        ),
        "pciconfig_read": (
            "long",
            (
                ("unsigned long", "bus"),
                ("unsigned long", "dfn"),
                ("unsigned long", "off"),
                ("unsigned long", "len"),
                ("void *", "buf"),
            ),
        ),
        "pciconfig_write": (
            "long",
            (
                ("unsigned long", "bus"),
                ("unsigned long", "dfn"),
                ("unsigned long", "off"),
                ("unsigned long", "len"),
                ("void *", "buf"),
            ),
        ),
        "perf_event_open": (
            "long",
            (
                ("struct perf_event_attr *", "attr_uptr"),
                ("pid_t", "pid"),
                ("int", "cpu"),
                ("int", "group_fd"),
                ("unsigned long", "flags"),
            ),
        ),
        "personality": ("long", (("unsigned int", "personality"),)),
        "pidfd_getfd": (
            "long",
            (("int", "pidfd"), ("int", "fd"), ("unsigned int", "flags")),
        ),
        "pidfd_open": ("long", (("pid_t", "pid"), ("unsigned int", "flags"))),
        "pidfd_send_signal": (
            "long",
            (
                ("int", "pidfd"),
                ("int", "sig"),
                ("siginfo_t *", "info"),
                ("unsigned int", "flags"),
            ),
        ),
        "pipe": ("long", (("int *", "fildes"),)),
        "pipe2": ("long", (("int *", "fildes"), ("int", "flags"))),
        "pivot_root": (
            "long",
            (("const char *", "new_root"), ("const char *", "put_old")),
        ),
        "pkey_alloc": (
            "long",
            (("unsigned long", "flags"), ("unsigned long", "init_val")),
        ),
        "pkey_free": ("long", (("int", "pkey"),)),
        "pkey_mprotect": (
            "long",
            (
                ("unsigned long", "start"),
                ("size_t", "len"),
                ("unsigned long", "prot"),
                ("int", "pkey"),
            ),
        ),
        "poll": (
            "long",
            (
                ("struct pollfd *", "ufds"),
                ("unsigned int", "nfds"),
                ("int", "timeout"),
            ),
        ),
        "ppoll": (
            "long",
            (
                ("struct pollfd *", "fds"),
                ("unsigned int", "nfds"),
                ("struct timespec *", "tmo_p"),
                ("const sigset_t *", "sigmask"),
            ),
        ),
        "prctl": (
            "long",
            (
                ("int", "option"),
                ("unsigned long", "arg2"),
                ("unsigned long", "arg3"),
                ("unsigned long", "arg4"),
                ("unsigned long", "arg5"),
            ),
        ),
        "pread64": (
            "long",
            (
                ("unsigned int", "fd"),
                ("void *", "buf"),
                ("size_t", "count"),
                ("loff_t", "pos"),
            ),
        ),
        "preadv": (
            "long",
            (
                ("unsigned long", "fd"),
                ("const struct iovec *", "vec"),
                ("unsigned long", "vlen"),
                ("unsigned long", "pos_l"),
                ("unsigned long", "pos_h"),
            ),
        ),
        "preadv2": (
            "long",
            (
                ("unsigned long", "fd"),
                ("const struct iovec *", "vec"),
                ("unsigned long", "vlen"),
                ("unsigned long", "pos_l"),
                ("unsigned long", "pos_h"),
                ("rwf_t", "flags"),
            ),
        ),
        "prlimit64": (
            "long",
            (
                ("pid_t", "pid"),
                ("unsigned int", "resource"),
                ("const struct rlimit64 *", "new_rlim"),
                ("struct rlimit64 *", "old_rlim"),
            ),
        ),
        "process_vm_readv": (
            "long",
            (
                ("pid_t", "pid"),
                ("const struct iovec *", "lvec"),
                ("unsigned long", "liovcnt"),
                ("const struct iovec *", "rvec"),
                ("unsigned long", "riovcnt"),
                ("unsigned long", "flags"),
            ),
        ),
        "process_vm_writev": (
            "long",
            (
                ("pid_t", "pid"),
                ("const struct iovec *", "lvec"),
                ("unsigned long", "liovcnt"),
                ("const struct iovec *", "rvec"),
                ("unsigned long", "riovcnt"),
                ("unsigned long", "flags"),
            ),
        ),
        "pselect6": (
            "long",
            (
                ("int", "nfds"),
                ("fd_set *", "readfds"),
                ("fd_set *", "writefds"),
                ("fd_set *", "exceptfds"),
                ("struct timespec *", "timeout"),
                ("void *", "sigmask"),
            ),
        ),
        "ptrace": (
            "long",
            (
                ("long", "request"),
                ("long", "pid"),
                ("unsigned long", "addr"),
                ("unsigned long", "data"),
            ),
        ),
        "pwrite64": (
            "long",
            (
                ("unsigned int", "fd"),
                ("const char *", "buf"),
                ("size_t", "count"),
                ("loff_t", "pos"),
            ),
        ),
        "pwritev": (
            "long",
            (
                ("unsigned long", "fd"),
                ("const struct iovec *", "vec"),
                ("unsigned long", "vlen"),
                ("unsigned long", "pos_l"),
                ("unsigned long", "pos_h"),
            ),
        ),
        "pwritev2": (
            "long",
            (
                ("unsigned long", "fd"),
                ("const struct iovec *", "vec"),
                ("unsigned long", "vlen"),
                ("unsigned long", "pos_l"),
                ("unsigned long", "pos_h"),
                ("rwf_t", "flags"),
            ),
        ),
        "quotactl": (
            "long",
            (
                ("unsigned int", "cmd"),
                ("const char *", "special"),
                ("qid_t", "id"),
                ("void *", "addr"),
            ),
        ),
        "query_module": (
            "long",
            (
                ("const char *", "name"),
                ("int", "which"),
                ("void *", "buf"),
                ("int", "bufsize"),
                ("int *", "ret"),
            ),
        ),
        "read": (
            "long",
            (("unsigned int", "fd"), ("void *", "buf"), ("size_t", "count")),
        ),
        "readahead": (
            "long",
            (("int", "fd"), ("loff_t", "offset"), ("size_t", "count")),
        ),
        "readlink": (
            "long",
            (("const char *", "path"), ("char *", "buf"), ("int", "bufsiz")),
        ),
        "readlinkat": (
            "long",
            (
                ("int", "dfd"),
                ("const char *", "path"),
                ("char *", "buf"),
                ("int", "bufsiz"),
            ),
        ),
        "readv": (
            "long",
            (
                ("unsigned long", "fd"),
                ("const struct iovec *", "vec"),
                ("unsigned long", "vlen"),
            ),
        ),
        "reboot": (
            "long",
            (
                ("int", "magic1"),
                ("int", "magic2"),
                ("unsigned int", "cmd"),
                ("void *", "arg"),
            ),
        ),
        "recv": (
            "long",
            (
                ("int", "sockfd"),
                ("void *", "buf"),
                ("size_t", "len"),
                ("unsigned", "flags"),
            ),
        ),
        "recvfrom": (
            "long",
            (
                ("int", "sockfd"),
                ("void *", "buf"),
                ("size_t", "len"),
                ("unsigned", "flags"),
                ("struct sockaddr *", "src_addr"),
                ("int *", "addrlen"),
            ),
        ),
        "recvmmsg": (
            "long",
            (
                ("int", "fd"),
                ("struct mmsghdr *", "msg"),
                ("unsigned int", "vlen"),
                ("unsigned", "flags"),
                ("struct timespec *", "timeout"),
            ),
        ),
        "recvmsg": (
            "long",
            (
                ("int", "fd"),
                ("struct user_msghdr *", "msg"),
                ("unsigned", "flags"),
            ),
        ),
        "remap_file_pages": (
            "long",
            (
                ("unsigned long", "start"),
                ("unsigned long", "size"),
                ("unsigned long", "prot"),
                ("unsigned long", "pgoff"),
                ("unsigned long", "flags"),
            ),
        ),
        "removexattr": (
            "long",
            (("const char *", "path"), ("const char *", "name")),
        ),
        "rename": (
            "long",
            (("const char *", "oldname"), ("const char *", "newname")),
        ),
        "renameat": (
            "long",
            (
                ("int", "olddfd"),
                ("const char *", "oldname"),
                ("int", "newdfd"),
                ("const char *", "newname"),
            ),
        ),
        "renameat2": (
            "long",
            (
                ("int", "olddfd"),
                ("const char *", "oldname"),
                ("int", "newdfd"),
                ("const char *", "newname"),
                ("unsigned int", "flags"),
            ),
        ),
        "request_key": (
            "long",
            (
                ("const char *", "_type"),
                ("const char *", "_description"),
                ("const char *", "_callout_info"),
                ("key_serial_t", "destringid"),
            ),
        ),
        "restart_syscall": ("long", ()),
        "rmdir": ("long", (("const char *", "pathname"),)),
        "rseq": (
            "long",
            (
                ("struct rseq *", "rseq"),
                ("uint32_t", "rseq_len"),
                ("int", "flags"),
                ("uint32_t", "sig"),
            ),
        ),
        "rt_sigaction": (
            "long",
            (
                ("int", "signum"),
                ("const struct sigaction *", "act"),
                ("struct sigaction *", "oldact"),
                ("size_t", ""),
            ),
        ),
        "rt_sigpending": (
            "long",
            (("sigset_t *", "set"), ("size_t", "sigsetsize")),
        ),
        "rt_sigreturn": ("long", ()),
        "rt_sigprocmask": (
            "long",
            (
                ("int", "how"),
                ("sigset_t *", "set"),
                ("sigset_t *", "oset"),
                ("size_t", "sigsetsize"),
            ),
        ),
        "rt_sigqueueinfo": (
            "long",
            (("int", "pid"), ("int", "sig"), ("siginfo_t *", "uinfo")),
        ),
        "rt_sigsuspend": (
            "long",
            (("sigset_t *", "unewset"), ("size_t", "sigsetsize")),
        ),
        "rt_sigtimedwait": (
            "long",
            (
                ("const sigset_t *", "uthese"),
                ("siginfo_t *", "uinfo"),
                ("const struct timespec *", "uts"),
                ("size_t", "sigsetsize"),
            ),
        ),
        "rt_tgsigqueueinfo": (
            "long",
            (
                ("pid_t", "tgid"),
                ("pid_t", "pid"),
                ("int", "sig"),
                ("siginfo_t *", "uinfo"),
            ),
        ),
        "sched_get_priority_max": ("long", (("int", "policy"),)),
        "sched_get_priority_min": ("long", (("int", "policy"),)),
        "sched_getaffinity": (
            "long",
            (
                ("pid_t", "pid"),
                ("unsigned int", "len"),
                ("unsigned long *", "user_mask_ptr"),
            ),
        ),
        "sched_getattr": (
            "long",
            (
                ("pid_t", "pid"),
                ("struct sched_attr *", "attr"),
                ("unsigned int", "size"),
                ("unsigned int", "flags"),
            ),
        ),
        "sched_getparam": (
            "long",
            (("pid_t", "pid"), ("struct sched_param *", "param")),
        ),
        "sched_getscheduler": ("long", (("pid_t", "pid"),)),
        "sched_rr_get_interval": (
            "long",
            (("pid_t", "pid"), ("struct timespec *", "interval")),
        ),
        "sched_setaffinity": (
            "long",
            (
                ("pid_t", "pid"),
                ("unsigned int", "len"),
                ("unsigned long *", "user_mask_ptr"),
            ),
        ),
        "sched_setattr": (
            "long",
            (
                ("pid_t", "pid"),
                ("struct sched_attr *", "attr"),
                ("unsigned int", "flags"),
            ),
        ),
        "sched_setparam": (
            "long",
            (("pid_t", "pid"), ("struct sched_param *", "param")),
        ),
        "sched_setscheduler": (
            "long",
            (
                ("pid_t", "pid"),
                ("int", "policy"),
                ("struct sched_param *", "param"),
            ),
        ),
        "sched_yield": ("long", ()),
        "seccomp": (
            "long",
            (
                ("unsigned int", "op"),
                ("unsigned int", "flags"),
                ("const char *", "uargs"),
            ),
        ),
        "select": (
            "long",
            (
                ("int", "n"),
                ("fd_set *", "inp"),
                ("fd_set *", "outp"),
                ("fd_set *", "exp"),
                ("struct timeval *", "tvp"),
            ),
        ),
        "newselect": (
            "long",
            (
                ("int", "n"),
                ("fd_set *", "inp"),
                ("fd_set *", "outp"),
                ("fd_set *", "exp"),
                ("struct timeval *", "tvp"),
            ),
        ),
        "semctl": (
            "long",
            (
                ("int", "semid"),
                ("int", "semnum"),
                ("int", "cmd"),
                ("unsigned long", "arg"),
            ),
        ),
        "semget": (
            "long",
            (("key_t", "key"), ("int", "nsems"), ("int", "semflg")),
        ),
        "semop": (
            "long",
            (
                ("int", "semid"),
                ("struct sembuf *", "sops"),
                ("unsigned", "nsops"),
            ),
        ),
        "semtimedop": (
            "long",
            (
                ("int", "semid"),
                ("struct sembuf *", "sops"),
                ("unsigned", "nsops"),
                ("const struct timespec *", "timeout"),
            ),
        ),
        "send": (
            "long",
            (
                ("int", "sockfd"),
                ("void *", "buf"),
                ("size_t", "len"),
                ("unsigned", "flags"),
            ),
        ),
        "sendfile": (
            "long",
            (
                ("int", "out_fd"),
                ("int", "in_fd"),
                ("off_t *", "offset"),
                ("size_t", "count"),
            ),
        ),
        "sendfile64": (
            "long",
            (
                ("int", "out_fd"),
                ("int", "in_fd"),
                ("loff_t *", "offset"),
                ("size_t", "count"),
            ),
        ),
        "sendmmsg": (
            "long",
            (
                ("int", "fd"),
                ("struct mmsghdr *", "msg"),
                ("unsigned int", "vlen"),
                ("unsigned", "flags"),
            ),
        ),
        "sendmsg": (
            "long",
            (
                ("int", "fd"),
                ("struct user_msghdr *", "msg"),
                ("unsigned", "flags"),
            ),
        ),
        "sendto": (
            "long",
            (
                ("int", "sockfd"),
                ("void *", "buf"),
                ("size_t", "len"),
                ("unsigned", "flags"),
                ("struct sockaddr *", "destAddr"),
                ("int", "addrlen"),
            ),
        ),
        "set_mempolicy": (
            "long",
            (
                ("int", "mode"),
                ("const unsigned long *", "nmask"),
                ("unsigned long", "maxnode"),
            ),
        ),
        "set_robust_list": (
            "long",
            (("struct robust_list_head *", "head"), ("size_t", "len")),
        ),
        "set_tid_address": ("long", (("int *", "tidptr"),)),
        "setdomainname": ("long", (("const char *", "name"), ("int", "len"))),
        "setfsgid": ("long", (("gid_t", "gid"),)),
        "setfsgid16": ("long", (("old_gid_t", "gid"),)),
        "setfsuid": ("long", (("uid_t", "uid"),)),
        "setfsuid16": ("long", (("old_uid_t", "uid"),)),
        "setgid": ("long", (("gid_t", "gid"),)),
        "setgid16": ("long", (("old_gid_t", "gid"),)),
        "setgroups": (
            "long",
            (("int", "gidsetsize"), ("gid_t *", "grouplist")),
        ),
        "setgroups16": (
            "long",
            (("int", "gidsetsize"), ("old_gid_t *", "grouplist")),
        ),
        "sethostname": ("long", (("const char *", "name"), ("int", "len"))),
        "setitimer": (
            "long",
            (
                ("int", "which"),
                ("struct itimerval *", "value"),
                ("struct itimerval *", "ovalue"),
            ),
        ),
        "setns": ("long", (("int", "fd"), ("int", "nstype"))),
        "setpgid": ("long", (("pid_t", "pid"), ("pid_t", "pgid"))),
        "setpriority": (
            "long",
            (("int", "which"), ("int", "who"), ("int", "niceval")),
        ),
        "setregid": ("long", (("gid_t", "rgid"), ("gid_t", "egid"))),
        "setregid16": ("long", (("old_gid_t", "rgid"), ("old_gid_t", "egid"))),
        "setresgid": (
            "long",
            (("gid_t", "rgid"), ("gid_t", "egid"), ("gid_t", "sgid")),
        ),
        "setresgid16": (
            "long",
            (
                ("old_gid_t", "rgid"),
                ("old_gid_t", "egid"),
                ("old_gid_t", "sgid"),
            ),
        ),
        "setresuid": (
            "long",
            (("uid_t", "ruid"), ("uid_t", "euid"), ("uid_t", "suid")),
        ),
        "setresuid16": (
            "long",
            (
                ("old_uid_t", "ruid"),
                ("old_uid_t", "euid"),
                ("old_uid_t", "suid"),
            ),
        ),
        "setreuid": ("long", (("uid_t", "ruid"), ("uid_t", "euid"))),
        "setreuid16": ("long", (("old_uid_t", "ruid"), ("old_uid_t", "euid"))),
        "setrlimit": (
            "long",
            (("unsigned int", "resource"), ("struct rlimit *", "rlim")),
        ),
        "setsid": ("long", ()),
        "setsockopt": (
            "long",
            (
                ("int", "fd"),
                ("int", "level"),
                ("int", "optname"),
                ("const void *", "optval"),
                ("int", "optlen"),
            ),
        ),
        "settimeofday": (
            "long",
            (("struct timeval *", "tv"), ("struct timezone *", "tz")),
        ),
        "setuid": ("long", (("uid_t", "uid"),)),
        "setuid16": ("long", (("old_uid_t", "uid"),)),
        "setxattr": (
            "long",
            (
                ("const char *", "path"),
                ("const char *", "name"),
                ("const void *", "value"),
                ("size_t", "size"),
                ("int", "flags"),
            ),
        ),
        "sgetmask": ("long", ()),
        "shmat": (
            "long",
            (("int", "shmid"), ("const void *", "shmaddr"), ("int", "shmflg")),
        ),
        "shmctl": (
            "long",
            (("int", "shmid"), ("int", "cmd"), ("struct shmid_ds *", "buf")),
        ),
        "shmdt": ("long", (("const void *", "shmaddr"),)),
        "shmget": (
            "long",
            (("key_t", "key"), ("size_t", "size"), ("int", "flag")),
        ),
        "shutdown": ("long", (("int", "sockfd"), ("int", "how"))),
        "sigaction": (
            "long",
            (
                ("int", "signum"),
                ("const struct old_sigaction *", "act"),
                ("struct old_sigaction *", "oldact"),
            ),
        ),
        "sigaltstack": (
            "long",
            (
                ("const struct sigaltstack *", "uss"),
                ("struct sigaltstack *", "uoss"),
            ),
        ),
        "signal": ("long", (("int", "sig"), ("__sighandler_t", "handler"))),
        "signalfd": (
            "long",
            (
                ("int", "ufd"),
                ("sigset_t *", "user_mask"),
                ("size_t", "sizemask"),
            ),
        ),
        "signalfd4": (
            "long",
            (
                ("int", "ufd"),
                ("sigset_t *", "user_mask"),
                ("size_t", "sizemask"),
                ("int", "flags"),
            ),
        ),
        "sigpending": ("long", (("old_sigset_t *", "set"),)),
        "sigprocmask": (
            "long",
            (
                ("int", "how"),
                ("old_sigset_t *", "set"),
                ("old_sigset_t *", "oset"),
            ),
        ),
        "sigsuspend": (
            "long",
            (("int", "unused1"), ("int", "unused2"), ("old_sigset_t", "mask")),
        ),
        "socket": (
            "long",
            (("int", "domain"), ("int", "type"), ("int", "protocol")),
        ),
        "socketcall": ("long", (("int", "call"), ("unsigned long *", "args"))),
        "socketpair": (
            "long",
            (
                ("int", "domain"),
                ("int", "type"),
                ("int", "protocol"),
                ("int *", "sv"),
            ),
        ),
        "splice": (
            "long",
            (
                ("int", "fd_in"),
                ("loff_t *", "off_in"),
                ("int", "fd_out"),
                ("loff_t *", "off_out"),
                ("size_t", "len"),
                ("unsigned int", "flags"),
            ),
        ),
        "spu_create": (
            "long",
            (
                ("const char *", "name"),
                ("unsigned int", "flags"),
                ("umode_t", "mode"),
                ("int", "fd"),
            ),
        ),
        "spu_run": (
            "long",
            (("int", "fd"), ("__u32 *", "unpc"), ("__u32 *", "ustatus")),
        ),
        "ssetmask": ("long", (("int", "newmask"),)),
        "stat": (
            "long",
            (
                ("const char *", "filename"),
                ("struct __old_kernel_stat *", "statbuf"),
            ),
        ),
        "stat64": (
            "long",
            (("const char *", "filename"), ("struct stat64 *", "statbuf")),
        ),
        "statfs": (
            "long",
            (("const char *", "path"), ("struct statfs *", "buf")),
        ),
        "statfs64": (
            "long",
            (
                ("const char *", "path"),
                ("size_t", "sz"),
                ("struct statfs64 *", "buf"),
            ),
        ),
        "statx": (
            "long",
            (
                ("int", "dfd"),
                ("const char *", "path"),
                ("unsigned", "flags"),
                ("unsigned", "mask"),
                ("struct statx *", "statxbuf"),
            ),
        ),
        "stime": ("long", (("old_time_t *", "tptr"),)),
        "swapoff": ("long", (("const char *", "specialfile"),)),
        "swapon": (
            "long",
            (("const char *", "specialfile"), ("int", "swap_flags")),
        ),
        "symlink": (
            "long",
            (("const char *", "old"), ("const char *", "new")),
        ),
        "symlinkat": (
            "long",
            (
                ("const char *", "oldname"),
                ("int", "newdfd"),
                ("const char *", "newname"),
            ),
        ),
        "sync": ("long", ()),
        "sync_file_range": (
            "long",
            (
                ("int", "fd"),
                ("loff_t", "offset"),
                ("loff_t", "nbytes"),
                ("unsigned int", "flags"),
            ),
        ),
        "sync_file_range2": (
            "long",
            (
                ("int", "fd"),
                ("unsigned int", "flags"),
                ("loff_t", "offset"),
                ("loff_t", "nbytes"),
            ),
        ),
        "syncfs": ("long", (("int", "fd"),)),
        "sysctl": ("long", (("struct __sysctl_args *", "args"),)),
        "sysfs": (
            "long",
            (
                ("int", "option"),
                ("unsigned long", "arg1"),
                ("unsigned long", "arg2"),
            ),
        ),
        "sysinfo": ("long", (("struct sysinfo *", "info"),)),
        "syslog": (
            "long",
            (("int", "type"), ("char *", "buf"), ("int", "len")),
        ),
        "tee": (
            "long",
            (
                ("int", "fdin"),
                ("int", "fdout"),
                ("size_t", "len"),
                ("unsigned int", "flags"),
            ),
        ),
        "tgkill": ("long", (("int", "tgid"), ("int", "pid"), ("int", "sig"))),
        "time": ("long", (("old_time_t *", "tloc"),)),
        "timer_create": (
            "long",
            (
                ("clockid_t", "which_clock"),
                ("struct sigevent *", "timer_event_spec"),
                ("timer_t *", "created_timer_id"),
            ),
        ),
        "timer_delete": ("long", (("timer_t", "timer_id"),)),
        "timer_getoverrun": ("long", (("timer_t", "timer_id"),)),
        "timer_gettime": (
            "long",
            (("timer_t", "timer_id"), ("struct itimerspec *", "setting")),
        ),
        "timer_settime": (
            "long",
            (
                ("timer_t", "timer_id"),
                ("int", "flags"),
                ("const struct itimerspec *", "new_setting"),
                ("struct itimerspec *", "old_setting"),
            ),
        ),
        "timerfd_create": ("long", (("int", "clockid"), ("int", "flags"))),
        "timerfd_gettime": (
            "long",
            (("int", "ufd"), ("struct itimerspec *", "otmr")),
        ),
        "timerfd_settime": (
            "long",
            (
                ("int", "ufd"),
                ("int", "flags"),
                ("const struct itimerspec *", "utmr"),
                ("struct itimerspec *", "otmr"),
            ),
        ),
        "times": ("long", (("struct tms *", "tbuf"),)),
        "tkill": ("long", (("int", "pid"), ("int", "sig"))),
        "truncate": ("long", (("const char *", "path"), ("long", "length"))),
        "truncate64": (
            "long",
            (("const char *", "path"), ("loff_t", "length")),
        ),
        "umask": ("long", (("int", "mask"),)),
        "umount": ("long", (("const char *", "name"), ("int", "flags"))),
        "umount2": ("long", (("const char *", "name"), ("int", "flags"))),
        "uname": ("long", (("struct old_utsname *", "buf"),)),
        "unlink": ("long", (("const char *", "pathname"),)),
        "unlinkat": (
            "long",
            (("int", "dfd"), ("const char *", "pathname"), ("int", "flag")),
        ),
        "unshare": ("long", (("unsigned long", "unshare_flags"),)),
        "uselib": ("long", (("const char *", "library"),)),
        "userfaultfd": ("long", (("int", "flags"),)),
        "ustat": ("long", (("unsigned", "dev"), ("struct ustat *", "ubuf"))),
        "utime": (
            "long",
            (("const char *", "filename"), ("struct utimbuf *", "times")),
        ),
        "utimensat": (
            "long",
            (
                ("int", "dfd"),
                ("const char *", "filename"),
                ("struct timespec *", "utimes"),
                ("int", "flags"),
            ),
        ),
        "utimes": (
            "long",
            (("const char *", "filename"), ("struct old_timeval *", "utimes")),
        ),
        "vfork": ("long", ()),
        "vhangup": ("long", ()),
        "vmsplice": (
            "long",
            (
                ("int", "fd"),
                ("const struct iovec *", "iov"),
                ("unsigned long", "nr_segs"),
                ("unsigned int", "flags"),
            ),
        ),
        "wait4": (
            "long",
            (
                ("pid_t", "pid"),
                ("int *", "stat_addr"),
                ("int", "options"),
                ("struct rusage *", "ru"),
            ),
        ),
        "waitid": (
            "long",
            (
                ("int", "which"),
                ("pid_t", "pid"),
                ("struct siginfo *", "infop"),
                ("int", "options"),
                ("struct rusage *", "ru"),
            ),
        ),
        "waitpid": (
            "long",
            (("pid_t", "pid"), ("int *", "stat_addr"), ("int", "options")),
        ),
        "write": (
            "long",
            (
                ("unsigned int", "fd"),
                ("const char *", "buf"),
                ("size_t", "count"),
            ),
        ),
        "writev": (
            "long",
            (
                ("unsigned long", "fd"),
                ("const struct iovec *", "vec"),
                ("unsigned long", "vlen"),
            ),
        ),
    }

    """
    update syscalls from https://github.com/strace/src/linux/ARCH/syscallent.h
    1. %s/\[.*= //g
    2. %s/{.*),\s*"/\'sys_/g
    3. %s/".*,/\',/g
    4. paste the processed list
    4. call UtilMgr.printSyscalls(ConfigMgr.SYSCALL_X86)
    """

    # common 32bit syscalls from 403 ~ 423 #
    SYSCALL_COMMON32 = [
        "sys_clock_gettime64",
        "sys_clock_settime64",
        "sys_clock_adjtime64",
        "sys_clock_getres_time64",
        "sys_clock_nanosleep_time64",
        "sys_timer_gettime64",
        "sys_timer_settime64",
        "sys_timerfd_gettime64",
        "sys_timerfd_settime64",
        "sys_utimensat_time64",
        "sys_pselect6_time64",
        "sys_ppoll_time64",
        "sys_unused",
        "sys_io_pgetevents_time64",
        "sys_recvmmsg_time64",
        "sys_mq_timedsend_time64",
        "sys_mq_timedreceive_time64",
        "sys_semtimedop_time64",
        "sys_rt_sigtimedwait_time64",
        "sys_futex_time64",
        "sys_sched_rr_get_interval_time64",
    ]

    # common syscalls from 424 ~ 450 #
    SYSCALL_COMMON = [
        "sys_pidfd_send_signal",
        "sys_io_uring_setup",
        "sys_io_uring_enter",
        "sys_io_uring_register",
        "sys_open_tree",
        "sys_move_mount",
        "sys_fsopen",
        "sys_fsconfig",
        "sys_fsmount",
        "sys_fspick",
        "sys_pidfd_open",
        "sys_clone3",
        "sys_close_range",
        "sys_openat2",
        "sys_pidfd_getfd",
        "sys_faccessat2",
        "sys_process_madvise",
        "sys_epoll_pwait2",
        "sys_mount_setattr",
        "sys_quotactl_fd",
        "sys_landlock_create_ruleset",
        "sys_landlock_add_rule",
        "sys_landlock_restrict_self",
        "sys_memfd_secret",
        "sys_process_mrelease",
        "sys_futex_waitv",
        "sys_set_mempolicy_home_node",
    ]

    # syscall for ARM #
    SYSCALL_ARM = (
        [
            "sys_restart_syscall",
            "sys_exit",
            "sys_fork",
            "sys_read",
            "sys_write",
            "sys_open",
            "sys_close",
            "sys_waitpid",
            "sys_creat",
            "sys_link",
            "sys_unlink",
            "sys_execve",
            "sys_chdir",
            "sys_time",
            "sys_mknod",
            "sys_chmod",
            "sys_lchown",
            "sys_break",
            "sys_oldstat",
            "sys_lseek",
            "sys_getpid",
            "sys_mount",
            "sys_umount",
            "sys_setuid",
            "sys_getuid",
            "sys_stime",
            "sys_ptrace",
            "sys_alarm",
            "sys_oldfstat",
            "sys_pause",
            "sys_utime",
            "sys_stty",
            "sys_gtty",
            "sys_access",
            "sys_nice",
            "sys_ftime",
            "sys_sync",
            "sys_kill",
            "sys_rename",
            "sys_mkdir",
            "sys_rmdir",
            "sys_dup",
            "sys_pipe",
            "sys_times",
            "sys_prof",
            "sys_brk",
            "sys_setgid",
            "sys_getgid",
            "sys_signal",
            "sys_geteuid",
            "sys_getegid",
            "sys_acct",
            "sys_umount2",
            "sys_lock",
            "sys_ioctl",
            "sys_fcntl",
            "sys_mpx",
            "sys_setpgid",
            "sys_ulimit",
            "sys_oldolduname",
            "sys_umask",
            "sys_chroot",
            "sys_ustat",
            "sys_dup2",
            "sys_getppid",
            "sys_getpgrp",
            "sys_setsid",
            "sys_sigaction",
            "sys_sgetmask",
            "sys_ssetmask",
            "sys_setreuid",
            "sys_setregid",
            "sys_sigsuspend",
            "sys_sigpending",
            "sys_sethostname",
            "sys_setrlimit",
            "sys_getrlimit",
            "sys_getrusage",
            "sys_gettimeofday",
            "sys_settimeofday",
            "sys_getgroups",
            "sys_setgroups",
            "sys_select",
            "sys_symlink",
            "sys_oldlstat",
            "sys_readlink",
            "sys_uselib",
            "sys_swapon",
            "sys_reboot",
            "sys_readdir",
            "sys_mmap",
            "sys_munmap",
            "sys_truncate",
            "sys_ftruncate",
            "sys_fchmod",
            "sys_fchown",
            "sys_getpriority",
            "sys_setpriority",
            "sys_profil",
            "sys_statfs",
            "sys_fstatfs",
            "sys_ioperm",
            "sys_socketcall",
            "sys_syslog",
            "sys_setitimer",
            "sys_getitimer",
            "sys_stat",
            "sys_lstat",
            "sys_fstat",
            "sys_olduname",
            "sys_iopl",
            "sys_vhangup",
            "sys_idle",
            "sys_syscall",
            "sys_wait4",
            "sys_swapoff",
            "sys_sysinfo",
            "sys_ipc",
            "sys_fsync",
            "sys_sigreturn",
            "sys_clone",
            "sys_setdomainname",
            "sys_uname",
            "sys_modify_ldt",
            "sys_adjtimex",
            "sys_mprotect",
            "sys_sigprocmask",
            "sys_create_module",
            "sys_init_module",
            "sys_delete_module",
            "sys_get_kernel_syms",
            "sys_quotactl",
            "sys_getpgid",
            "sys_fchdir",
            "sys_bdflush",
            "sys_sysfs",
            "sys_personality",
            "sys_afs_syscall",
            "sys_setfsuid",
            "sys_setfsgid",
            "sys_llseek",
            "sys_getdents",
            "sys_newselect",
            "sys_flock",
            "sys_msync",
            "sys_readv",
            "sys_writev",
            "sys_getsid",
            "sys_fdatasync",
            "sys_sysctl",
            "sys_mlock",
            "sys_munlock",
            "sys_mlockall",
            "sys_munlockall",
            "sys_sched_setparam",
            "sys_sched_getparam",
            "sys_sched_setscheduler",
            "sys_sched_getscheduler",
            "sys_sched_yield",
            "sys_sched_get_priority_max",
            "sys_sched_get_priority_min",
            "sys_sched_rr_get_interval",
            "sys_nanosleep",
            "sys_mremap",
            "sys_setresuid",
            "sys_getresuid",
            "sys_vm86",
            "sys_query_module",
            "sys_poll",
            "sys_nfsservctl",
            "sys_setresgid",
            "sys_getresgid",
            "sys_prctl",
            "sys_rt_sigreturn",
            "sys_rt_sigaction",
            "sys_rt_sigprocmask",
            "sys_rt_sigpending",
            "sys_rt_sigtimedwait",
            "sys_rt_sigqueueinfo",
            "sys_rt_sigsuspend",
            "sys_pread64",
            "sys_pwrite64",
            "sys_chown",
            "sys_getcwd",
            "sys_capget",
            "sys_capset",
            "sys_sigaltstack",
            "sys_sendfile",
            "sys_getpmsg",
            "sys_putpmsg",
            "sys_vfork",
            "sys_ugetrlimit",
            "sys_mmap2",
            "sys_truncate64",
            "sys_ftruncate64",
            "sys_stat64",
            "sys_lstat64",
            "sys_fstat64",
            "sys_lchown32",
            "sys_getuid32",
            "sys_getgid32",
            "sys_geteuid32",
            "sys_getegid32",
            "sys_setreuid32",
            "sys_setregid32",
            "sys_getgroups32",
            "sys_setgroups32",
            "sys_fchown32",
            "sys_setresuid32",
            "sys_getresuid32",
            "sys_setresgid32",
            "sys_getresgid32",
            "sys_chown32",
            "sys_setuid32",
            "sys_setgid32",
            "sys_setfsuid32",
            "sys_setfsgid32",
            "sys_getdents64",
            "sys_pivot_root",
            "sys_mincore",
            "sys_madvise",
            "sys_fcntl64",
            "sys_tux",
            "sys_unused",
            "sys_gettid",
            "sys_readahead",
            "sys_setxattr",
            "sys_lsetxattr",
            "sys_fsetxattr",
            "sys_getxattr",
            "sys_lgetxattr",
            "sys_fgetxattr",
            "sys_listxattr",
            "sys_llistxattr",
            "sys_flistxattr",
            "sys_removexattr",
            "sys_lremovexattr",
            "sys_fremovexattr",
            "sys_tkill",
            "sys_sendfile64",
            "sys_futex",
            "sys_sched_setaffinity",
            "sys_sched_getaffinity",
            "sys_io_setup",
            "sys_io_destroy",
            "sys_io_getevents",
            "sys_io_submit",
            "sys_io_cancel",
            "sys_exit_group",
            "sys_lookup_dcookie",
            "sys_epoll_create",
            "sys_epoll_ctl",
            "sys_epoll_wait",
            "sys_remap_file_pages",
            "sys_set_thread_area",
            "sys_get_thread_area",
            "sys_set_tid_address",
            "sys_timer_create",
            "sys_timer_settime",
            "sys_timer_gettime",
            "sys_timer_getoverrun",
            "sys_timer_delete",
            "sys_clock_settime",
            "sys_clock_gettime",
            "sys_clock_getres",
            "sys_clock_nanosleep",
            "sys_statfs64",
            "sys_fstatfs64",
            "sys_tgkill",
            "sys_utimes",
            "sys_fadvise64_64",
            "sys_pciconfig_iobase",
            "sys_pciconfig_read",
            "sys_pciconfig_write",
            "sys_mq_open",
            "sys_mq_unlink",
            "sys_mq_timedsend",
            "sys_mq_timedreceive",
            "sys_mq_notify",
            "sys_mq_getsetattr",
            "sys_waitid",
            "sys_socket",
            "sys_bind",
            "sys_connect",
            "sys_listen",
            "sys_accept",
            "sys_getsockname",
            "sys_getpeername",
            "sys_socketpair",
            "sys_send",
            "sys_sendto",
            "sys_recv",
            "sys_recvfrom",
            "sys_shutdown",
            "sys_setsockopt",
            "sys_getsockopt",
            "sys_sendmsg",
            "sys_recvmsg",
            "sys_semop",
            "sys_semget",
            "sys_semctl",
            "sys_msgsnd",
            "sys_msgrcv",
            "sys_msgget",
            "sys_msgctl",
            "sys_shmat",
            "sys_shmdt",
            "sys_shmget",
            "sys_shmctl",
            "sys_add_key",
            "sys_request_key",
            "sys_keyctl",
            "sys_semtimedop",
            "sys_vserver",
            "sys_ioprio_set",
            "sys_ioprio_get",
            "sys_inotify_init",
            "sys_inotify_add_watch",
            "sys_inotify_rm_watch",
            "sys_mbind",
            "sys_get_mempolicy",
            "sys_set_mempolicy",
            "sys_openat",
            "sys_mkdirat",
            "sys_mknodat",
            "sys_fchownat",
            "sys_futimesat",
            "sys_fstatat64",
            "sys_unlinkat",
            "sys_renameat",
            "sys_linkat",
            "sys_symlinkat",
            "sys_readlinkat",
            "sys_fchmodat",
            "sys_faccessat",
            "sys_pselect6",
            "sys_ppoll",
            "sys_unshare",
            "sys_set_robust_list",
            "sys_get_robust_list",
            "sys_splice",
            "sys_sync_file_range2",
            "sys_tee",
            "sys_vmsplice",
            "sys_move_pages",
            "sys_getcpu",
            "sys_epoll_pwait",
            "sys_kexec_load",
            "sys_utimensat",
            "sys_signalfd",
            "sys_timerfd_create",
            "sys_eventfd",
            "sys_fallocate",
            "sys_timerfd_settime",
            "sys_timerfd_gettime",
            "sys_signalfd4",
            "sys_eventfd2",
            "sys_epoll_create1",
            "sys_dup3",
            "sys_pipe2",
            "sys_inotify_init1",
            "sys_preadv",
            "sys_pwritev",
            "sys_rt_tgsigqueueinfo",
            "sys_perf_event_open",
            "sys_recvmmsg",
            "sys_accept4",
            "sys_fanotify_init",
            "sys_fanotify_mark",
            "sys_prlimit64",
            "sys_name_to_handle_at",
            "sys_open_by_handle_at",
            "sys_clock_adjtime",
            "sys_syncfs",
            "sys_sendmmsg",
            "sys_setns",
            "sys_process_vm_readv",
            "sys_process_vm_writev",
            "sys_kcmp",
            "sys_finit_module",
            "sys_sched_setattr",
            "sys_sched_getattr",
            "sys_renameat2",
            "sys_seccomp",
            "sys_getrandom",
            "sys_memfd_create",
            "sys_bpf",
            "sys_execveat",
            "sys_userfaultfd",
            "sys_membarrier",
            "sys_mlock2",
            "sys_copy_file_range",
            "sys_preadv2",
            "sys_pwritev2",
            "sys_pkey_mprotect",
            "sys_pkey_alloc",
            "sys_pkey_free",
            "sys_statx",
            "sys_rseq",
            "sys_io_pgetevents",
            "sys_migrate_pages",
            "sys_kexec_file_load",
            "sys_unused",
        ]
        + SYSCALL_COMMON32
        + SYSCALL_COMMON
    )

    # syscall for AARCH64 #
    SYSCALL_AARCH64 = (
        [
            "sys_io_setup",
            "sys_io_destroy",
            "sys_io_submit",
            "sys_io_cancel",
            "sys_io_getevents",
            "sys_setxattr",
            "sys_lsetxattr",
            "sys_fsetxattr",
            "sys_getxattr",
            "sys_lgetxattr",
            "sys_fgetxattr",
            "sys_listxattr",
            "sys_llistxattr",
            "sys_flistxattr",
            "sys_removexattr",
            "sys_lremovexattr",
            "sys_fremovexattr",
            "sys_getcwd",
            "sys_lookup_dcookie",
            "sys_eventfd2",
            "sys_epoll_create1",
            "sys_epoll_ctl",
            "sys_epoll_pwait",
            "sys_dup",
            "sys_dup3",
            "sys_fcntl",
            "sys_inotify_init1",
            "sys_inotify_add_watch",
            "sys_inotify_rm_watch",
            "sys_ioctl",
            "sys_ioprio_set",
            "sys_ioprio_get",
            "sys_flock",
            "sys_mknodat",
            "sys_mkdirat",
            "sys_unlinkat",
            "sys_symlinkat",
            "sys_linkat",
            "sys_renameat",
            "sys_umount2",
            "sys_mount",
            "sys_pivot_root",
            "sys_nfsservctl",
            "sys_statfs",
            "sys_fstatfs",
            "sys_truncate",
            "sys_ftruncate",
            "sys_fallocate",
            "sys_faccessat",
            "sys_chdir",
            "sys_fchdir",
            "sys_chroot",
            "sys_fchmod",
            "sys_fchmodat",
            "sys_fchownat",
            "sys_fchown",
            "sys_openat",
            "sys_close",
            "sys_vhangup",
            "sys_pipe2",
            "sys_quotactl",
            "sys_getdents64",
            "sys_lseek",
            "sys_read",
            "sys_write",
            "sys_readv",
            "sys_writev",
            "sys_pread64",
            "sys_pwrite64",
            "sys_preadv",
            "sys_pwritev",
            "sys_sendfile",
            "sys_pselect6",
            "sys_ppoll",
            "sys_signalfd4",
            "sys_vmsplice",
            "sys_splice",
            "sys_tee",
            "sys_readlinkat",
            "sys_newfstatat",
            "sys_fstat",
            "sys_sync",
            "sys_fsync",
            "sys_fdatasync",
            "sys_sync_file_range",
            "sys_timerfd_create",
            "sys_timerfd_settime",
            "sys_timerfd_gettime",
            "sys_utimensat",
            "sys_acct",
            "sys_capget",
            "sys_capset",
            "sys_personality",
            "sys_exit",
            "sys_exit_group",
            "sys_waitid",
            "sys_set_tid_address",
            "sys_unshare",
            "sys_futex",
            "sys_set_robust_list",
            "sys_get_robust_list",
            "sys_nanosleep",
            "sys_getitimer",
            "sys_setitimer",
            "sys_kexec_load",
            "sys_init_module",
            "sys_delete_module",
            "sys_timer_create",
            "sys_timer_gettime",
            "sys_timer_getoverrun",
            "sys_timer_settime",
            "sys_timer_delete",
            "sys_clock_settime",
            "sys_clock_gettime",
            "sys_clock_getres",
            "sys_clock_nanosleep",
            "sys_syslog",
            "sys_ptrace",
            "sys_sched_setparam",
            "sys_sched_setscheduler",
            "sys_sched_getscheduler",
            "sys_sched_getparam",
            "sys_sched_setaffinity",
            "sys_sched_getaffinity",
            "sys_sched_yield",
            "sys_sched_get_priority_max",
            "sys_sched_get_priority_min",
            "sys_sched_rr_get_interval",
            "sys_restart_syscall",
            "sys_kill",
            "sys_tkill",
            "sys_tgkill",
            "sys_sigaltstack",
            "sys_rt_sigsuspend",
            "sys_rt_sigaction",
            "sys_rt_sigprocmask",
            "sys_rt_sigpending",
            "sys_rt_sigtimedwait",
            "sys_rt_sigqueueinfo",
            "sys_rt_sigreturn",
            "sys_setpriority",
            "sys_getpriority",
            "sys_reboot",
            "sys_setregid",
            "sys_setgid",
            "sys_setreuid",
            "sys_setuid",
            "sys_setresuid",
            "sys_getresuid",
            "sys_setresgid",
            "sys_getresgid",
            "sys_setfsuid",
            "sys_setfsgid",
            "sys_times",
            "sys_setpgid",
            "sys_getpgid",
            "sys_getsid",
            "sys_setsid",
            "sys_getgroups",
            "sys_setgroups",
            "sys_uname",
            "sys_sethostname",
            "sys_setdomainname",
            "sys_getrlimit",
            "sys_setrlimit",
            "sys_getrusage",
            "sys_umask",
            "sys_prctl",
            "sys_getcpu",
            "sys_gettimeofday",
            "sys_settimeofday",
            "sys_adjtimex",
            "sys_getpid",
            "sys_getppid",
            "sys_getuid",
            "sys_geteuid",
            "sys_getgid",
            "sys_getegid",
            "sys_gettid",
            "sys_sysinfo",
            "sys_mq_open",
            "sys_mq_unlink",
            "sys_mq_timedsend",
            "sys_mq_timedreceive",
            "sys_mq_notify",
            "sys_mq_getsetattr",
            "sys_msgget",
            "sys_msgctl",
            "sys_msgrcv",
            "sys_msgsnd",
            "sys_semget",
            "sys_semctl",
            "sys_semtimedop",
            "sys_semop",
            "sys_shmget",
            "sys_shmctl",
            "sys_shmat",
            "sys_shmdt",
            "sys_socket",
            "sys_socketpair",
            "sys_bind",
            "sys_listen",
            "sys_accept",
            "sys_connect",
            "sys_getsockname",
            "sys_getpeername",
            "sys_sendto",
            "sys_recvfrom",
            "sys_setsockopt",
            "sys_getsockopt",
            "sys_shutdown",
            "sys_sendmsg",
            "sys_recvmsg",
            "sys_readahead",
            "sys_brk",
            "sys_munmap",
            "sys_mremap",
            "sys_add_key",
            "sys_request_key",
            "sys_keyctl",
            "sys_clone",
            "sys_execve",
            "sys_mmap",
            "sys_fadvise64",
            "sys_swapon",
            "sys_swapoff",
            "sys_mprotect",
            "sys_msync",
            "sys_mlock",
            "sys_munlock",
            "sys_mlockall",
            "sys_munlockall",
            "sys_mincore",
            "sys_madvise",
            "sys_remap_file_pages",
            "sys_mbind",
            "sys_get_mempolicy",
            "sys_set_mempolicy",
            "sys_migrate_pages",
            "sys_move_pages",
            "sys_rt_tgsigqueueinfo",
            "sys_perf_event_open",
            "sys_accept4",
            "sys_recvmmsg",
            "sys_reserved",
            "sys_reserved",
            "sys_reserved",
            "sys_reserved",
            "sys_reserved",
            "sys_reserved",
            "sys_reserved",
            "sys_reserved",
            "sys_reserved",
            "sys_reserved",
            "sys_reserved",
            "sys_reserved",
            "sys_reserved",
            "sys_reserved",
            "sys_reserved",
            "sys_reserved",
            "sys_wait4",
            "sys_prlimit64",
            "sys_fanotify_init",
            "sys_fanotify_mark",
            "sys_name_to_handle_at",
            "sys_open_by_handle_at",
            "sys_clock_adjtime",
            "sys_syncfs",
            "sys_setns",
            "sys_sendmmsg",
            "sys_process_vm_readv",
            "sys_process_vm_writev",
            "sys_kcmp",
            "sys_finit_module",
            "sys_sched_setattr",
            "sys_sched_getattr",
            "sys_renameat2",
            "sys_seccomp",
            "sys_getrandom",
            "sys_memfd_create",
            "sys_bpf",
            "sys_execveat",
            "sys_userfaultfd",
            "sys_membarrier",
            "sys_mlock2",
            "sys_copy_file_range",
            "sys_preadv2",
            "sys_pwritev2",
            "sys_pkey_mprotect",
            "sys_pkey_alloc",
            "sys_pkey_free",
            "sys_statx",
            "sys_io_pgetevents",
            "sys_rseq",
            "sys_kexec_file_load",
        ]
        + ["sys_null" for idx in xrange(295, 424, 1)]
        + SYSCALL_COMMON
    )

    # syscall for x86 #
    SYSCALL_X86 = (
        [
            "sys_restart_syscall",
            "sys_exit",
            "sys_fork",
            "sys_read",
            "sys_write",
            "sys_open",
            "sys_close",
            "sys_waitpid",
            "sys_creat",
            "sys_link",
            "sys_unlink",
            "sys_execve",
            "sys_chdir",
            "sys_time",
            "sys_mknod",
            "sys_chmod",
            "sys_lchown",
            "sys_break",
            "sys_oldstat",
            "sys_lseek",
            "sys_getpid",
            "sys_mount",
            "sys_umount",
            "sys_setuid",
            "sys_getuid",
            "sys_stime",
            "sys_ptrace",
            "sys_alarm",
            "sys_oldfstat",
            "sys_pause",
            "sys_utime",
            "sys_stty",
            "sys_gtty",
            "sys_access",
            "sys_nice",
            "sys_ftime",
            "sys_sync",
            "sys_kill",
            "sys_rename",
            "sys_mkdir",
            "sys_rmdir",
            "sys_dup",
            "sys_pipe",
            "sys_times",
            "sys_prof",
            "sys_brk",
            "sys_setgid",
            "sys_getgid",
            "sys_signal",
            "sys_geteuid",
            "sys_getegid",
            "sys_acct",
            "sys_umount2",
            "sys_lock",
            "sys_ioctl",
            "sys_fcntl",
            "sys_mpx",
            "sys_setpgid",
            "sys_ulimit",
            "sys_oldolduname",
            "sys_umask",
            "sys_chroot",
            "sys_ustat",
            "sys_dup2",
            "sys_getppid",
            "sys_getpgrp",
            "sys_setsid",
            "sys_sigaction",
            "sys_sgetmask",
            "sys_ssetmask",
            "sys_setreuid",
            "sys_setregid",
            "sys_sigsuspend",
            "sys_sigpending",
            "sys_sethostname",
            "sys_setrlimit",
            "sys_getrlimit",
            "sys_getrusage",
            "sys_gettimeofday",
            "sys_settimeofday",
            "sys_getgroups",
            "sys_setgroups",
            "sys_select",
            "sys_symlink",
            "sys_oldlstat",
            "sys_readlink",
            "sys_uselib",
            "sys_swapon",
            "sys_reboot",
            "sys_readdir",
            "sys_mmap",
            "sys_munmap",
            "sys_truncate",
            "sys_ftruncate",
            "sys_fchmod",
            "sys_fchown",
            "sys_getpriority",
            "sys_setpriority",
            "sys_profil",
            "sys_statfs",
            "sys_fstatfs",
            "sys_ioperm",
            "sys_socketcall",
            "sys_syslog",
            "sys_setitimer",
            "sys_getitimer",
            "sys_stat",
            "sys_lstat",
            "sys_fstat",
            "sys_olduname",
            "sys_iopl",
            "sys_vhangup",
            "sys_idle",
            "sys_vm86old",
            "sys_wait4",
            "sys_swapoff",
            "sys_sysinfo",
            "sys_ipc",
            "sys_fsync",
            "sys_sigreturn",
            "sys_clone",
            "sys_setdomainname",
            "sys_uname",
            "sys_modify_ldt",
            "sys_adjtimex",
            "sys_mprotect",
            "sys_sigprocmask",
            "sys_create_module",
            "sys_init_module",
            "sys_delete_module",
            "sys_get_kernel_syms",
            "sys_quotactl",
            "sys_getpgid",
            "sys_fchdir",
            "sys_bdflush",
            "sys_sysfs",
            "sys_personality",
            "sys_afs_syscall",
            "sys_setfsuid",
            "sys_setfsgid",
            "sys_llseek",
            "sys_getdents",
            "sys_newselect",
            "sys_flock",
            "sys_msync",
            "sys_readv",
            "sys_writev",
            "sys_getsid",
            "sys_fdatasync",
            "sys_sysctl",
            "sys_mlock",
            "sys_munlock",
            "sys_mlockall",
            "sys_munlockall",
            "sys_sched_setparam",
            "sys_sched_getparam",
            "sys_sched_setscheduler",
            "sys_sched_getscheduler",
            "sys_sched_yield",
            "sys_sched_get_priority_max",
            "sys_sched_get_priority_min",
            "sys_sched_rr_get_interval",
            "sys_nanosleep",
            "sys_mremap",
            "sys_setresuid",
            "sys_getresuid",
            "sys_vm86",
            "sys_query_module",
            "sys_poll",
            "sys_nfsservctl",
            "sys_setresgid",
            "sys_getresgid",
            "sys_prctl",
            "sys_rt_sigreturn",
            "sys_rt_sigaction",
            "sys_rt_sigprocmask",
            "sys_rt_sigpending",
            "sys_rt_sigtimedwait",
            "sys_rt_sigqueueinfo",
            "sys_rt_sigsuspend",
            "sys_pread64",
            "sys_pwrite64",
            "sys_chown",
            "sys_getcwd",
            "sys_capget",
            "sys_capset",
            "sys_sigaltstack",
            "sys_sendfile",
            "sys_getpmsg",
            "sys_putpmsg",
            "sys_vfork",
            "sys_ugetrlimit",
            "sys_mmap2",
            "sys_truncate64",
            "sys_ftruncate64",
            "sys_stat64",
            "sys_lstat64",
            "sys_fstat64",
            "sys_lchown32",
            "sys_getuid32",
            "sys_getgid32",
            "sys_geteuid32",
            "sys_getegid32",
            "sys_setreuid32",
            "sys_setregid32",
            "sys_getgroups32",
            "sys_setgroups32",
            "sys_fchown32",
            "sys_setresuid32",
            "sys_getresuid32",
            "sys_setresgid32",
            "sys_getresgid32",
            "sys_chown32",
            "sys_setuid32",
            "sys_setgid32",
            "sys_setfsuid32",
            "sys_setfsgid32",
            "sys_pivot_root",
            "sys_mincore",
            "sys_madvise",
            "sys_getdents64",
            "sys_fcntl64",
            "sys_null",
            "sys_null",
            "sys_gettid",
            "sys_readahead",
            "sys_setxattr",
            "sys_lsetxattr",
            "sys_fsetxattr",
            "sys_getxattr",
            "sys_lgetxattr",
            "sys_fgetxattr",
            "sys_listxattr",
            "sys_llistxattr",
            "sys_flistxattr",
            "sys_removexattr",
            "sys_lremovexattr",
            "sys_fremovexattr",
            "sys_tkill",
            "sys_sendfile64",
            "sys_futex",
            "sys_sched_setaffinity",
            "sys_sched_getaffinity",
            "sys_set_thread_area",
            "sys_get_thread_area",
            "sys_io_setup",
            "sys_io_destroy",
            "sys_io_getevents",
            "sys_io_submit",
            "sys_io_cancel",
            "sys_fadvise64",
            "sys_set_zone_reclaim",
            "sys_exit_group",
            "sys_lookup_dcookie",
            "sys_epoll_create",
            "sys_epoll_ctl",
            "sys_epoll_wait",
            "sys_remap_file_pages",
            "sys_set_tid_address",
            "sys_timer_create",
            "sys_timer_settime",
            "sys_timer_gettime",
            "sys_timer_getoverrun",
            "sys_timer_delete",
            "sys_clock_settime",
            "sys_clock_gettime",
            "sys_clock_getres",
            "sys_clock_nanosleep",
            "sys_statfs64",
            "sys_fstatfs64",
            "sys_tgkill",
            "sys_utimes",
            "sys_fadvise64_64",
            "sys_vserver",
            "sys_mbind",
            "sys_get_mempolicy",
            "sys_set_mempolicy",
            "sys_mq_open",
            "sys_mq_unlink",
            "sys_mq_timedsend",
            "sys_mq_timedreceive",
            "sys_mq_notify",
            "sys_mq_getsetattr",
            "sys_kexec_load",
            "sys_waitid",
            "sys_setaltroot",
            "sys_add_key",
            "sys_request_key",
            "sys_keyctl",
            "sys_ioprio_set",
            "sys_ioprio_get",
            "sys_inotify_init",
            "sys_inotify_add_watch",
            "sys_inotify_rm_watch",
            "sys_migrate_pages",
            "sys_openat",
            "sys_mkdirat",
            "sys_mknodat",
            "sys_fchownat",
            "sys_futimesat",
            "sys_fstatat64",
            "sys_unlinkat",
            "sys_renameat",
            "sys_linkat",
            "sys_symlinkat",
            "sys_readlinkat",
            "sys_fchmodat",
            "sys_faccessat",
            "sys_pselect6",
            "sys_ppoll",
            "sys_unshare",
            "sys_set_robust_list",
            "sys_get_robust_list",
            "sys_splice",
            "sys_sync_file_range",
            "sys_tee",
            "sys_vmsplice",
            "sys_move_pages",
            "sys_getcpu",
            "sys_epoll_pwait",
            "sys_utimensat",
            "sys_signalfd",
            "sys_timerfd_create",
            "sys_eventfd",
            "sys_fallocate",
            "sys_timerfd_settime",
            "sys_timerfd_gettime",
            "sys_signalfd4",
            "sys_eventfd2",
            "sys_epoll_create1",
            "sys_dup3",
            "sys_pipe2",
            "sys_inotify_init1",
            "sys_preadv",
            "sys_pwritev",
            "sys_rt_tgsigqueueinfo",
            "sys_perf_event_open",
            "sys_recvmmsg",
            "sys_fanotify_init",
            "sys_fanotify_mark",
            "sys_prlimit64",
            "sys_name_to_handle_at",
            "sys_open_by_handle_at",
            "sys_clock_adjtime",
            "sys_syncfs",
            "sys_sendmmsg",
            "sys_setns",
            "sys_process_vm_readv",
            "sys_process_vm_writev",
            "sys_kcmp",
            "sys_finit_module",
            "sys_sched_setattr",
            "sys_sched_getattr",
            "sys_renameat2",
            "sys_seccomp",
            "sys_getrandom",
            "sys_memfd_create",
            "sys_bpf",
            "sys_execveat",
            "sys_socket",
            "sys_socketpair",
            "sys_bind",
            "sys_connect",
            "sys_listen",
            "sys_accept4",
            "sys_getsockopt",
            "sys_setsockopt",
            "sys_getsockname",
            "sys_getpeername",
            "sys_sendto",
            "sys_sendmsg",
            "sys_recvfrom",
            "sys_recvmsg",
            "sys_shutdown",
            "sys_userfaultfd",
            "sys_membarrier",
            "sys_mlock2",
            "sys_copy_file_range",
            "sys_preadv2",
            "sys_pwritev2",
            "sys_pkey_mprotect",
            "sys_pkey_alloc",
            "sys_pkey_free",
            "sys_statx",
            "sys_arch_prctl",
            "sys_io_pgetevents",
            "sys_rseq",
            "sys_reserved",
            "sys_reserved",
            "sys_reserved",
            "sys_reserved",
            "sys_reserved",
            "sys_reserved",
            "sys_semget",
            "sys_semctl",
            "sys_shmget",
            "sys_shmctl",
            "sys_shmat",
            "sys_shmdt",
            "sys_msgget",
            "sys_msgsnd",
            "sys_msgrcv",
            "sys_msgctl",
        ]
        + SYSCALL_COMMON32
        + SYSCALL_COMMON
    )

    # syscall for x86_64 #
    SYSCALL_X64 = (
        [
            "sys_read",
            "sys_write",
            "sys_open",
            "sys_close",
            "sys_stat",
            "sys_fstat",
            "sys_lstat",
            "sys_poll",
            "sys_lseek",
            "sys_mmap",
            "sys_mprotect",
            "sys_munmap",
            "sys_brk",
            "sys_rt_sigaction",
            "sys_rt_sigprocmask",
            "sys_rt_sigreturn",
            "sys_ioctl",
            "sys_pread64",
            "sys_pwrite64",
            "sys_readv",
            "sys_writev",
            "sys_access",
            "sys_pipe",
            "sys_select",
            "sys_sched_yield",
            "sys_mremap",
            "sys_msync",
            "sys_mincore",
            "sys_madvise",
            "sys_shmget",
            "sys_shmat",
            "sys_shmctl",
            "sys_dup",
            "sys_dup2",
            "sys_pause",
            "sys_nanosleep",
            "sys_getitimer",
            "sys_alarm",
            "sys_setitimer",
            "sys_getpid",
            "sys_sendfile",
            "sys_socket",
            "sys_connect",
            "sys_accept",
            "sys_sendto",
            "sys_recvfrom",
            "sys_sendmsg",
            "sys_recvmsg",
            "sys_shutdown",
            "sys_bind",
            "sys_listen",
            "sys_getsockname",
            "sys_getpeername",
            "sys_socketpair",
            "sys_setsockopt",
            "sys_getsockopt",
            "sys_clone",
            "sys_fork",
            "sys_vfork",
            "sys_execve",
            "sys_exit",
            "sys_wait4",
            "sys_kill",
            "sys_uname",
            "sys_semget",
            "sys_semop",
            "sys_semctl",
            "sys_shmdt",
            "sys_msgget",
            "sys_msgsnd",
            "sys_msgrcv",
            "sys_msgctl",
            "sys_fcntl",
            "sys_flock",
            "sys_fsync",
            "sys_fdatasync",
            "sys_truncate",
            "sys_ftruncate",
            "sys_getdents",
            "sys_getcwd",
            "sys_chdir",
            "sys_fchdir",
            "sys_rename",
            "sys_mkdir",
            "sys_rmdir",
            "sys_creat",
            "sys_link",
            "sys_unlink",
            "sys_symlink",
            "sys_readlink",
            "sys_chmod",
            "sys_fchmod",
            "sys_chown",
            "sys_fchown",
            "sys_lchown",
            "sys_umask",
            "sys_gettimeofday",
            "sys_getrlimit",
            "sys_getrusage",
            "sys_sysinfo",
            "sys_times",
            "sys_ptrace",
            "sys_getuid",
            "sys_syslog",
            "sys_getgid",
            "sys_setuid",
            "sys_setgid",
            "sys_geteuid",
            "sys_getegid",
            "sys_setpgid",
            "sys_getppid",
            "sys_getpgrp",
            "sys_setsid",
            "sys_setreuid",
            "sys_setregid",
            "sys_getgroups",
            "sys_setgroups",
            "sys_setresuid",
            "sys_getresuid",
            "sys_setresgid",
            "sys_getresgid",
            "sys_getpgid",
            "sys_setfsuid",
            "sys_setfsgid",
            "sys_getsid",
            "sys_capget",
            "sys_capset",
            "sys_rt_sigpending",
            "sys_rt_sigtimedwait",
            "sys_rt_sigqueueinfo",
            "sys_rt_sigsuspend",
            "sys_sigaltstack",
            "sys_utime",
            "sys_mknod",
            "sys_uselib",
            "sys_personality",
            "sys_ustat",
            "sys_statfs",
            "sys_fstatfs",
            "sys_sysfs",
            "sys_getpriority",
            "sys_setpriority",
            "sys_sched_setparam",
            "sys_sched_getparam",
            "sys_sched_setscheduler",
            "sys_sched_getscheduler",
            "sys_sched_get_priority_max",
            "sys_sched_get_priority_min",
            "sys_sched_rr_get_interval",
            "sys_mlock",
            "sys_munlock",
            "sys_mlockall",
            "sys_munlockall",
            "sys_vhangup",
            "sys_modify_ldt",
            "sys_pivot_root",
            "sys_sysctl",
            "sys_prctl",
            "sys_arch_prctl",
            "sys_adjtimex",
            "sys_setrlimit",
            "sys_chroot",
            "sys_sync",
            "sys_acct",
            "sys_settimeofday",
            "sys_mount",
            "sys_umount2",
            "sys_swapon",
            "sys_swapoff",
            "sys_reboot",
            "sys_sethostname",
            "sys_setdomainname",
            "sys_iopl",
            "sys_ioperm",
            "sys_create_module",
            "sys_init_module",
            "sys_delete_module",
            "sys_get_kernel_syms",
            "sys_query_module",
            "sys_quotactl",
            "sys_nfsservctl",
            "sys_getpmsg",
            "sys_putpmsg",
            "sys_afs_syscall",
            "sys_tuxcall",
            "sys_security",
            "sys_gettid",
            "sys_readahead",
            "sys_setxattr",
            "sys_lsetxattr",
            "sys_fsetxattr",
            "sys_getxattr",
            "sys_lgetxattr",
            "sys_fgetxattr",
            "sys_listxattr",
            "sys_llistxattr",
            "sys_flistxattr",
            "sys_removexattr",
            "sys_lremovexattr",
            "sys_fremovexattr",
            "sys_tkill",
            "sys_time",
            "sys_futex",
            "sys_sched_setaffinity",
            "sys_sched_getaffinity",
            "sys_set_thread_area",
            "sys_io_setup",
            "sys_io_destroy",
            "sys_io_getevents",
            "sys_io_submit",
            "sys_io_cancel",
            "sys_get_thread_area",
            "sys_lookup_dcookie",
            "sys_epoll_create",
            "sys_epoll_ctl_old",
            "sys_epoll_wait_old",
            "sys_remap_file_pages",
            "sys_getdents64",
            "sys_set_tid_address",
            "sys_restart_syscall",
            "sys_semtimedop",
            "sys_fadvise64",
            "sys_timer_create",
            "sys_timer_settime",
            "sys_timer_gettime",
            "sys_timer_getoverrun",
            "sys_timer_delete",
            "sys_clock_settime",
            "sys_clock_gettime",
            "sys_clock_getres",
            "sys_clock_nanosleep",
            "sys_exit_group",
            "sys_epoll_wait",
            "sys_epoll_ctl",
            "sys_tgkill",
            "sys_utimes",
            "sys_vserver",
            "sys_mbind",
            "sys_set_mempolicy",
            "sys_get_mempolicy",
            "sys_mq_open",
            "sys_mq_unlink",
            "sys_mq_timedsend",
            "sys_mq_timedreceive",
            "sys_mq_notify",
            "sys_mq_getsetattr",
            "sys_kexec_load",
            "sys_waitid",
            "sys_add_key",
            "sys_request_key",
            "sys_keyctl",
            "sys_ioprio_set",
            "sys_ioprio_get",
            "sys_inotify_init",
            "sys_inotify_add_watch",
            "sys_inotify_rm_watch",
            "sys_migrate_pages",
            "sys_openat",
            "sys_mkdirat",
            "sys_mknodat",
            "sys_fchownat",
            "sys_futimesat",
            "sys_newfstatat",
            "sys_unlinkat",
            "sys_renameat",
            "sys_linkat",
            "sys_symlinkat",
            "sys_readlinkat",
            "sys_fchmodat",
            "sys_faccessat",
            "sys_pselect6",
            "sys_ppoll",
            "sys_unshare",
            "sys_set_robust_list",
            "sys_get_robust_list",
            "sys_splice",
            "sys_tee",
            "sys_sync_file_range",
            "sys_vmsplice",
            "sys_move_pages",
            "sys_utimensat",
            "sys_epoll_pwait",
            "sys_signalfd",
            "sys_timerfd_create",
            "sys_eventfd",
            "sys_fallocate",
            "sys_timerfd_settime",
            "sys_timerfd_gettime",
            "sys_accept4",
            "sys_signalfd4",
            "sys_eventfd2",
            "sys_epoll_create1",
            "sys_dup3",
            "sys_pipe2",
            "sys_inotify_init1",
            "sys_preadv",
            "sys_pwritev",
            "sys_rt_tgsigqueueinfo",
            "sys_perf_event_open",
            "sys_recvmmsg",
            "sys_fanotify_init",
            "sys_fanotify_mark",
            "sys_prlimit64",
            "sys_name_to_handle_at",
            "sys_open_by_handle_at",
            "sys_clock_adjtime",
            "sys_syncfs",
            "sys_sendmmsg",
            "sys_setns",
            "sys_getcpu",
            "sys_process_vm_readv",
            "sys_process_vm_writev",
            "sys_kcmp",
            "sys_finit_module",
            "sys_sched_setattr",
            "sys_sched_getattr",
            "sys_renameat2",
            "sys_seccomp",
            "sys_getrandom",
            "sys_memfd_create",
            "sys_kexec_file_load",
            "sys_bpf",
            "sys_execveat",
            "sys_userfaultfd",
            "sys_membarrier",
            "sys_mlock2",
            "sys_copy_file_range",
            "sys_preadv2",
            "sys_pwritev2",
            "sys_pkey_mprotect",
            "sys_pkey_alloc",
            "sys_pkey_free",
            "sys_statx",
            "sys_io_pgetevents",
            "sys_rseq",
        ]
        + ["sys_null" for idx in xrange(335, 424, 1)]
        + SYSCALL_COMMON
    )

    # syscall for riscv64 #
    SYSCALL_RISCV64 = (
        [
            "sys_io_setup",
            "sys_io_destroy",
            "sys_io_submit",
            "sys_io_cancel",
            "sys_io_getevents",
            "sys_setxattr",
            "sys_lsetxattr",
            "sys_fsetxattr",
            "sys_getxattr",
            "sys_lgetxattr",
            "sys_fgetxattr",
            "sys_listxattr",
            "sys_llistxattr",
            "sys_flistxattr",
            "sys_removexattr",
            "sys_lremovexattr",
            "sys_fremovexattr",
            "sys_getcwd",
            "sys_lookup_dcookie",
            "sys_eventfd2",
            "sys_epoll_create1",
            "sys_epoll_ctl",
            "sys_epoll_pwait",
            "sys_dup",
            "sys_dup3",
            "sys_fcntl",
            "sys_inotify_init1",
            "sys_inotify_add_watch",
            "sys_inotify_rm_watch",
            "sys_ioctl",
            "sys_ioprio_set",
            "sys_ioprio_get",
            "sys_flock",
            "sys_mknodat",
            "sys_mkdirat",
            "sys_unlinkat",
            "sys_symlinkat",
            "sys_linkat",
            "sys_renameat",
            "sys_umount2",
            "sys_mount",
            "sys_pivot_root",
            "sys_nfsservctl",
            "sys_statfs",
            "sys_fstatfs",
            "sys_truncate",
            "sys_ftruncate",
            "sys_fallocate",
            "sys_faccessat",
            "sys_chdir",
            "sys_fchdir",
            "sys_chroot",
            "sys_fchmod",
            "sys_fchmodat",
            "sys_fchownat",
            "sys_fchown",
            "sys_openat",
            "sys_close",
            "sys_vhangup",
            "sys_pipe2",
            "sys_quotactl",
            "sys_getdents64",
            "sys_lseek",
            "sys_read",
            "sys_write",
            "sys_readv",
            "sys_writev",
            "sys_pread64",
            "sys_pwrite64",
            "sys_preadv",
            "sys_pwritev",
            "sys_sendfile",
            "sys_pselect6",
            "sys_ppoll",
            "sys_signalfd4",
            "sys_vmsplice",
            "sys_splice",
            "sys_tee",
            "sys_readlinkat",
            "sys_newfstatat",
            "sys_fstat",
            "sys_sync",
            "sys_fsync",
            "sys_fdatasync",
            "sys_sync_file_range",
            "sys_timerfd_create",
            "sys_timerfd_settime",
            "sys_timerfd_gettime",
            "sys_utimensat",
            "sys_acct",
            "sys_capget",
            "sys_capset",
            "sys_personality",
            "sys_exit",
            "sys_exit_group",
            "sys_waitid",
            "sys_set_tid_address",
            "sys_unshare",
            "sys_futex",
            "sys_set_robust_list",
            "sys_get_robust_list",
            "sys_nanosleep",
            "sys_getitimer",
            "sys_setitimer",
            "sys_kexec_load",
            "sys_init_module",
            "sys_delete_module",
            "sys_timer_create",
            "sys_timer_gettime",
            "sys_timer_getoverrun",
            "sys_timer_settime",
            "sys_timer_delete",
            "sys_clock_settime",
            "sys_clock_gettime",
            "sys_clock_getres",
            "sys_clock_nanosleep",
            "sys_syslog",
            "sys_ptrace",
            "sys_sched_setparam",
            "sys_sched_setscheduler",
            "sys_sched_getscheduler",
            "sys_sched_getparam",
            "sys_sched_setaffinity",
            "sys_sched_getaffinity",
            "sys_sched_yield",
            "sys_sched_get_priority_max",
            "sys_sched_get_priority_min",
            "sys_sched_rr_get_interval",
            "sys_restart_syscall",
            "sys_kill",
            "sys_tkill",
            "sys_tgkill",
            "sys_sigaltstack",
            "sys_rt_sigsuspend",
            "sys_rt_sigaction",
            "sys_rt_sigprocmask",
            "sys_rt_sigpending",
            "sys_rt_sigtimedwait",
            "sys_rt_sigqueueinfo",
            "sys_rt_sigreturn",
            "sys_setpriority",
            "sys_getpriority",
            "sys_reboot",
            "sys_setregid",
            "sys_setgid",
            "sys_setreuid",
            "sys_setuid",
            "sys_setresuid",
            "sys_getresuid",
            "sys_setresgid",
            "sys_getresgid",
            "sys_setfsuid",
            "sys_setfsgid",
            "sys_times",
            "sys_setpgid",
            "sys_getpgid",
            "sys_getsid",
            "sys_setsid",
            "sys_getgroups",
            "sys_setgroups",
            "sys_uname",
            "sys_sethostname",
            "sys_setdomainname",
            "sys_getrlimit",
            "sys_setrlimit",
            "sys_getrusage",
            "sys_umask",
            "sys_prctl",
            "sys_getcpu",
            "sys_gettimeofday",
            "sys_settimeofday",
            "sys_adjtimex",
            "sys_getpid",
            "sys_getppid",
            "sys_getuid",
            "sys_geteuid",
            "sys_getgid",
            "sys_getegid",
            "sys_gettid",
            "sys_sysinfo",
            "sys_mq_open",
            "sys_mq_unlink",
            "sys_mq_timedsend",
            "sys_mq_timedreceive",
            "sys_mq_notify",
            "sys_mq_getsetattr",
            "sys_msgget",
            "sys_msgctl",
            "sys_msgrcv",
            "sys_msgsnd",
            "sys_semget",
            "sys_semctl",
            "sys_semtimedop",
            "sys_semop",
            "sys_shmget",
            "sys_shmctl",
            "sys_shmat",
            "sys_shmdt",
            "sys_socket",
            "sys_socketpair",
            "sys_bind",
            "sys_listen",
            "sys_accept",
            "sys_connect",
            "sys_getsockname",
            "sys_getpeername",
            "sys_sendto",
            "sys_recvfrom",
            "sys_setsockopt",
            "sys_getsockopt",
            "sys_shutdown",
            "sys_sendmsg",
            "sys_recvmsg",
            "sys_readahead",
            "sys_brk",
            "sys_munmap",
            "sys_mremap",
            "sys_add_key",
            "sys_request_key",
            "sys_keyctl",
            "sys_clone",
            "sys_execve",
            "sys_mmap",
            "sys_fadvise64",
            "sys_swapon",
            "sys_swapoff",
            "sys_mprotect",
            "sys_msync",
            "sys_mlock",
            "sys_munlock",
            "sys_mlockall",
            "sys_munlockall",
            "sys_mincore",
            "sys_madvise",
            "sys_remap_file_pages",
            "sys_mbind",
            "sys_get_mempolicy",
            "sys_set_mempolicy",
            "sys_migrate_pages",
            "sys_move_pages",
            "sys_rt_tgsigqueueinfo",
            "sys_perf_event_open",
            "sys_accept4",
            "sys_riscv_flush_icache",
            "sys_recvmmsg",
            "sys_wait4",
            "sys_prlimit64",
            "sys_fanotify_init",
            "sys_fanotify_mark",
            "sys_name_to_handle_at",
            "sys_open_by_handle_at",
            "sys_clock_adjtime",
            "sys_syncfs",
            "sys_setns",
            "sys_sendmmsg",
            "sys_process_vm_readv",
            "sys_process_vm_writev",
            "sys_kcmp",
            "sys_finit_module",
            "sys_sched_setattr",
            "sys_sched_getattr",
            "sys_renameat2",
            "sys_seccomp",
            "sys_getrandom",
            "sys_memfd_create",
            "sys_bpf",
            "sys_execveat",
            "sys_userfaultfd",
            "sys_membarrier",
            "sys_mlock2",
            "sys_copy_file_range",
            "sys_preadv2",
            "sys_pwritev2",
            "sys_pkey_mprotect",
            "sys_pkey_alloc",
            "sys_pkey_free",
            "sys_statx",
            "sys_io_pgetevents",
            "sys_rseq",
            "sys_kexec_file_load",
        ]
        + ["sys_null" for idx in xrange(295, 424, 1)]
        + SYSCALL_COMMON
    )

    # default syscall list #
    sysList = []

    # default register list #
    regList = []

    # registers sorted by number #
    REGS_X86 = [
        "eax",
        "ecx",
        "edx",
        "ebx",
        "esp",
        "ebp",  # 5 #
        "esi",
        "edi",
        "eip",
        "eflags",
        "<none>",  # 10 #
        "st0",
        "st1",
        "st2",
        "st3",
        "st4",  # 15 #
        "st5",
        "st6",
        "st7",
        "<none>",
        "<none>",  # 20 #
        "xmm0",
        "xmm1",
        "xmm2",
        "xmm3",
        "xmm4",  # 25 #
        "xmm5",
        "xmm6",
        "xmm7",
        "mm0",
        "mm1",  # 30 #
        "mm2",
        "mm3",
        "mm4",
        "mm5",
        "mm6",  # 35 #
        "mm7",
        "fcw",
        "fsw",
        "mxcsr",
        "es",  # 40 #
        "cs",
        "ss",
        "ds",
        "fs",
        "gs",  # 45 #
        "<none>",
        "<none>",
        "tr",
        "ldtr",
    ]

    REGS_X64 = [
        "rax",
        "rdx",
        "rcx",
        "rbx",
        "rsi",
        "rdi",  # 5 #
        "rbp",
        "rsp",
        "r8",
        "r9",
        "r10",  # 10 #
        "r11",
        "r12",
        "r13",
        "r14",
        "r15",  # 15 #
        "rip",
        "xmm0",
        "xmm1",
        "xmm2",
        "xmm3",  # 20 #
        "xmm4",
        "xmm5",
        "xmm6",
        "xmm7",
        "xmm8",  # 25 #
        "xmm9",
        "xmm10",
        "xmm11",
        "xmm12",
        "xmm13",  # 30 #
        "xmm14",
        "xmm15",
        "st0",
        "st1",
        "st2",  # 35 #
        "st3",
        "st4",
        "st5",
        "st6",
        "st7",  # 40 #
        "mm0",
        "mm1",
        "mm2",
        "mm3",
        "mm4",  # 45 #
        "mm5",
        "mm6",
        "mm7",
        "rflags",
        "es",  # 50 #
        "cs",
        "ss",
        "ds",
        "fs",
        "gs",  # 55 #
        "<none>",
        "<none>",
        "fs.base",
        "gs.base",
        "<none>",  # 60 #
        "<none>",
        "tr",
        "ldtr",
        "mxcsr",
        "fcw",  # 65 #
        "fsw",
    ]

    REGS_RISCV64 = [
        "pc",
        "ra",
        "sp",
        "gp",
        "tp",
        "t0",
        "t1",
        "t2",
        "s0",
        "s1",
        "a0",
        "a1",
        "a2",
        "a3",
        "a4",
        "a5",
        "a6",
        "a7",
        "s2",
        "s3",
        "s4",
        "s5",
        "s6",
        "s7",
        "s8",
        "s9",
        "s10",
        "s11",
        "t3",
        "t4",
        "t5",
        "t6",
    ]

    REGS_ARM = ["r%d" % idx for idx in xrange(16)]

    REGS_AARCH64 = (
        ["x%d" % idx for idx in xrange(31)]
        + ["sp", "pc", "ELP_mode", "RA_SIGN_STATE", "<none>", "<none>"]
        + ["reserved" for idx in xrange(37, 46, 1)]
        + ["VG", "FFR"]
        + ["p%d" % idx for idx in xrange(16)]
        + ["v%d" % idx for idx in xrange(32)]
        + ["z%d" % idx for idx in xrange(32)]
    )

    pcRegIndex = {
        "arm": REGS_ARM.index("r14"),
        "aarch64": REGS_AARCH64.index("pc"),
        "x86": REGS_X86.index("eip"),
        "x64": REGS_X64.index("rip"),
        "riscv64": REGS_RISCV64.index("pc"),
    }

    # syscall register #
    SYSREG_LIST = {
        "powerpc": "gpr0",
        "arm": "r7",
        "aarch64": "x8",
        "x64": "orig_rax",
        "x86": "orig_eax",
        "riscv64": "a7",
    }

    # return register #
    RET_LIST = {
        "powerpc": "result",
        "arm": "r0",
        "aarch64": "x0",
        "x64": "rax",
        "x86": "eax",
        "riscv64": "a0",
    }

    # signal for Linux #
    SIG_LIST = [
        "0",
        "SIGHUP",
        "SIGINT",
        "SIGQUIT",
        "SIGILL",  # 4
        "SIGTRAP",
        "SIGABRT",
        "SIGBUS",
        "SIGFPE",  # 8
        "SIGKILL",
        "SIGUSR1",
        "SIGSEGV",
        "SIGUSR2",  # 12
        "SIGPIPE",
        "SIGALRM",
        "SIGTERM",
        "SIGSTKFLT",  # 16
        "SIGCHLD",
        "SIGCONT",
        "SIGSTOP",
        "SIGTSTP",  # 20
        "SIGTTIN",
        "SIGTTOU",
        "SIGURG",
        "SIGXCPU",  # 24
        "SIGXFSZ",
        "SIGVTALRM",
        "SIGPROF",
        "SIGWINCH",  # 28
        "SIGIO",
        "SIGPWR",
        "SIGSYS",
        "SIG32",  # 32
        "SIG33",
    ] + ["SIGRT%d" % idx for idx in xrange(32)]
    SIGKILL = SIG_LIST.index("SIGKILL")

    # signal for MacOS #
    SIG_LIST_MACOS = [
        "0",
        "SIGHUP",
        "SIGINT",
        "SIGQUIT",
        "SIGILL",  # 4
        "SIGTRAP",
        "SIGABRT",
        "SIGEMT",
        "SIGFPE",  # 8
        "SIGKILL",
        "SIGBUS",
        "SIGSEGV",
        "SIGSYS",  # 12
        "SIGPIPE",
        "SIGALRM",
        "SIGTERM",
        "SIGURG",  # 16
        "SIGSTOP",
        "SIGTSTP",
        "SIGCONT",
        "SIGCHLD",  # 20
        "SIGTTIN",
        "SIGTTOU",
        "SIGIO",
        "SIGXCPU",  # 24
        "SIGXFSZ",
        "SIGVTALRM",
        "SIGPROF",
        "SIGWINCH",  # 28
        "SIGINFO",
        "SIGUSR1",
        "SIGUSR2",  # 31
    ]

    # SIGCHLD si_codes #
    SIGCHLD_CODE = [
        "N/A",
        "CLD_EXITED",  # /* child has exited */
        "CLD_KILLED",  # /* child was killed */
        "CLD_DUMPED",  # /* child terminated abnormally */
        "CLD_TRAPPED",  # /* traced child has trapped */
        "CLD_STOPPED",  # /* child has stopped */
        "CLD_CONTINUED",  # /* stopped child has continued */
    ]

    # SIGSEGV si_codes #
    SIGSEGV_CODE = [
        "N/A",
        "SEGV_MAPERR",  # /* address not mapped to object */
        "SEGV_ACCERR",  # /* invalid permissions for mapped object */
        "SEGV_BNDERR",  # /* failed address bound checks */
        "SEGV_PSTKOVF",  # /* paragraph stack overflow */
        "SEGV_ACCADI",  # /* ADI not enabled for mapped object */
        "SEGV_ADIDERR",  # /* Disrupting MCD error */
        "SEGV_ADIPERR",  # /* Precise MCD exception */
        "SEGV_MTEAERR",  # /* Asynchronous ARM MTE error */
        "SEGV_MTESERR",  # /* Synchronous ARM MTE exception */
    ]

    # SIGILL si_codes #
    SIGILL_CODE = [
        "N/A",
        "ILL_ILLOPC",  # /* illegal opcode */
        "ILL_ILLOPN",  # /* illegal operand */
        "ILL_ILLADR",  # /* illegal addressing mode */
        "ILL_ILLTRP",  # /* illegal trap */
        "ILL_PRVOPC",  # /* privileged opcode */
        "ILL_PRVREG",  # /* privileged register */
        "ILL_COPROC",  # /* coprocessor error */
        "ILL_BADSTK",  # /* internal stack error */
        "ILL_BADIADDR",  # /* unimplemented instruction address */
        "__ILL_BREAK",  # /* illegal break */
        "__ILL_BNDMOD",  # /* bundle-update (modification) in progress */
    ]

    # SIGTRAP si_codes #
    SIGTRAP_CODE = [
        "N/A",
        "TRAP_BRKPT",  # /* process breakpoint */
        "TRAP_TRACE",  # /* process trace trap */
        "TRAP_BRANCH",  # /* process taken branch trap */
        "TRAP_HWBKPT",  # /* hardware breakpoint/watchpoint */
        "TRAP_UNK",  # /* undiagnosed trap */
    ]

    # SIGFPE si_codes #
    SIGFPE_CODE = [
        "N/A",
        "FPE_INTDIV",  # /* integer divide by zero */
        "FPE_INTOVF",  # /* integer overflow */
        "FPE_FLTDIV",  # /* floating point divide by zero */
        "FPE_FLTOVF",  # /* floating point overflow */
        "FPE_FLTUND",  # /* floating point underflow */
        "FPE_FLTRES",  # /* floating point inexact result */
        "FPE_FLTINV",  # /* floating point invalid operation */
        "FPE_FLTSUB",  # /* subscript out of range */
        "__FPE_DECOVF",  # /* decimal overflow */
        "__FPE_DECDIV",  # /* decimal division by zero */
        "__FPE_DECERR",  # /* packed decimal error */
        "__FPE_INVASC",  # /* invalid ASCII digit */
        "__FPE_INVDEC",  # /* invalid decimal digit */
        "FPE_FLTUNK",  # /* undiagnosed floating-point exception */
        "FPE_CONDTRAP",  # /* trap on condition */
    ]

    # si_codes #
    SI_CODE = {
        0: "SI_USER",  # /* sent by kill, sigsend, raise */
        0x80: "SI_KERNEL",  # /* sent by the kernel from somewhere */
        -1: "SI_QUEUE",  # /* sent by sigqueue */
        -2: "SI_TIMER",  # /* sent by timer expiration */
        -3: "SI_MESGQ",  # /* sent by real time mesq state change */
        -4: "SI_ASYNCIO",  # /* sent by AIO completion */
        -5: "SI_SIGIO",  # /* sent by queued SIGIO */
        -6: "SI_TKILL",  # /* sent by tkill system call */
        -7: "SI_DETHREAD",  # /* sent by execve() killing subsidiary threads */
        -60: "SI_ASYNCNL",  # /* sent by glibc async name lookup completion */
    }

    # stat fields from http://linux.die.net/man/5/proc #
    STAT_ATTR = [
        "CORE",
        "USER",
        "NICE",
        "SYSTEM",
        "IDLE",
        "IOWAIT",
        "IRQ",
        "SOFTIRQ",
        "STEAL",
        "GUEST",
        "GUESTNICE",
    ]

    # proc stat fields from http://linux.die.net/man/5/proc #
    PROCSTAT_ATTR = [
        "PID",
        "COMM",
        "STATE",
        "PPID",
        "PGRP",
        "SESSIONID",  # 5
        "NRTTY",
        "TPGID",
        "FLAGS",
        "MINFLT",
        "CMINFLT",  # 10
        "MAJFLT",
        "CMAJFLT",
        "UTIME",
        "STIME",
        "CUTIME",  # 15
        "CSTIME",
        "PRIORITY",
        "NICE",
        "NRTHREAD",
        "ITERALVAL",  # 20
        "STARTTIME",
        "VSIZE",
        "RSS",
        "RSSLIM",
        "STARTCODE",  # 25
        "ENDCODE",
        "STARTSTACK",
        "SP",
        "PC",
        "SIGNAL",  # 30
        "BLOCKED",
        "SIGIGNORE",
        "SIGCATCH",
        "WCHEN",
        "NSWAP",  # 35
        "CNSWAP",
        "EXITSIGNAL",
        "PROCESSOR",
        "RTPRIORITY",  # 39
        "POLICY",
        "DELAYBLKTICK",
        "GUESTTIME",
        "CGUESTTIME",
        "STARTDATA",  # 44
        "ENDDATA",
        "STARTBRK",
        "ARGSTART",
        "ARGEND",  # 48
        "ENVSTART",
        "ENVEND",
        "EXITCODE",  # 51
    ]

    # sched policy #
    SCHED_POLICY = [
        "C",  # 0: CFS #
        "F",  # 1: FIFO #
        "R",  # 2: RR #
        "B",  # 3: BATCH #
        "N",  # 4: NONE #
        "I",  # 5: IDLE #
        "D",  # 6: DEADLINE #
    ]

    # sched policy for Windows #
    SCHED_POLICY_WINDOWS = {
        "ABOVE_NORMAL_PRIORITY_CLASS": "ANOR",
        "BELOW_NORMAL_PRIORITY_CLASS": "BNOR",
        "HIGH_PRIORITY_CLASS": "HIGH",
        "IDLE_PRIORITY_CLASS": "IDLE",
        "NORMAL_PRIORITY_CLASS": "NORM",
        "PROCESS_MODE_BACKGROUND_BEGIN": "BGBE",
        "PROCESS_MODE_BACKGROUND_END": "BGEN",
        "REALTIME_PRIORITY_CLASS": "RT",
    }

    # I/O sched class #
    IOSCHED_CLASS = [
        "NONE",
        "IOPRIO_CLASS_RT",
        "IOPRIO_CLASS_BE",
        "IOPRIO_CLASS_IDLE",
    ]

    # I/O sched target #
    IOSCHED_TARGET = [
        "NONE",
        "IOPRIO_WHO_PROCESS",
        "IOPRIO_WHO_PGRP",
        "IOPRIO_WHO_USER",
    ]

    # sa_family_t #
    SA_FAMILY_TYPE = {
        "0": "AF_UNSPEC",
        "1": "AF_LOCAL",  # POSIX name for AF_UNIX
        "1": "AF_UNIX",  # Unix domain sockets
        "2": "AF_INET",  # Internet IP Protocol
        "3": "AF_AX25",  # Amateur Radio AX.25
        "4": "AF_IPX",  # Novell IPX
        "5": "AF_APPLETALK",  # AppleTalk DDP
        "6": "AF_NETROM",  # Amateur Radio NET/ROM
        "7": "AF_BRIDGE",  # Multiprotocol bridge
        "8": "AF_ATMPVC",  # ATM PVCs
        "9": "AF_X25",  # Reserved for X.25 project
        "10": "AF_INET6",  # IP version 6
        "11": "AF_ROSE",  # Amateur Radio X.25 PLP
        "12": "AF_DECnet",  # Reserved for DECnet project
        "13": "AF_NETBEUI",  # Reserved for 802.2LLC project
        "14": "AF_SECURITY",  # Security callback pseudo AF
        "15": "AF_KEY",  # PF_KEY key management API
        "16": "AF_ROUTE",  # Alias to emulate 4.4BSD
        "16": "AF_NETLINK",
        "17": "AF_PACKET",  # Packet family
        "18": "AF_ASH",  # Ash
        "19": "AF_ECONET",  # Acorn Econet
        "20": "AF_ATMSVC",  # ATM SVCs
        "21": "AF_RDS",  # RDS sockets
        "22": "AF_SNA",  # Linux SNA Project (nutters!)
        "23": "AF_IRDA",  # IRDA sockets
        "24": "AF_PPPOX",  # PPPoX sockets
        "25": "AF_WANPIPE",  # Wanpipe API Sockets
        "26": "AF_LLC",  # Linux LLC
        "29": "AF_CAN",  # Controller Area Network
        "30": "AF_TIPC",  # TIPC sockets
        "31": "AF_BLUETOOTH",  # Bluetooth sockets
        "32": "AF_IUCV",  # IUCV sockets
        "33": "AF_RXRPC",  # RxRPC sockets
        "34": "AF_ISDN",  # mISDN sockets
        "35": "AF_PHONET",  # Phonet sockets
        "36": "AF_IEEE802154",  # IEEE802154 sockets
        "37": "AF_MAX",  # For now..
    }

    # statm fields #
    STATM_TYPE = [
        "TOTAL",  # 0 #
        "RSS",  # 1 #
        "SHR",  # 2 #
        "TEXT",  # 3 #
        "DATA",  # 4 #
        "LIB",  # 5 #
        "DIRTY",  # 6 #
    ]

    # error types #
    ERR_TYPE = [
        "EPERM",  # Operation not permitted #
        "ENOENT",  # No such file or directory #
        "ESRCH",  # No such process #
        "EINTR",  # Interrupted system call #
        "EIO",  # I/O error #
        "ENXIO",  # No such device or address #
        "E2BIG",  # Argument list too long #
        "ENOEXEC",  # Exec format error #
        "EBADF",  # Bad file number #
        "ECHILD",  # No child processes #
        "EAGAIN",  # Try again #
        "ENOMEM",  # Out of memory #
        "EACCES",  # Permission denied #
        "EFAULT",  # Bad address #
        "ENOTBLK",  # Block device required #
        "EBUSY",  # Device or resource busy #
        "EEXIST",  # File exists #
        "EXDEV",  # Cross-device link #
        "ENODEV",  # No such device #
        "ENOTDIR",  # Not a directory #
        "EISDIR",  # Is a directory #
        "EINVAL",  # Invalid argument #
        "ENFILE",  # File table overflow #
        "EMFILE",  # Too many open files #
        "ENOTTY",  # Not a typewriter #
        "ETXTBSY",  # Text file busy #
        "EFBIG",  # File too large #
        "ENOSPC",  # No space left on device #
        "ESPIPE",  # Illegal seek #
        "EROFS",  # Read-only file system #
        "EMLINK",  # Too many links #
        "EPIPE",  # Broken pipe #
        "EDOM",  # Math argument out of domain of func #
        "ERANGE",  # Math result not representable #
        "EDEADLK",  # Resource deadlock would occur #
        "ENAMETOOLONG",  # File name too long #
        "ENOLCK",  # No record locks available #
        "ENOSYS",  # Function not implemented #
        "ENOTEMPTY",  # Directory not empty #
        "ELOOP",  # Too many symbolic links encountered #
        "EWOULDBLOCK",  # Operation would block #
        "ENOMSG",  # No message of desired type #
        "EIDRM",  # Identifier removed #
        "ECHRNG",  # Channel number out of range #
        "EL2NSYNC",  # Level 2 not synchronized #
        "EL3HLT",  # Level 3 halted #
        "EL3RST",  # Level 3 reset #
        "ELNRNG",  # Link number out of range #
        "EUNATCH",  # Protocol driver not attached #
        "ENOCSI",  # No CSI structure available #
        "EL2HLT",  # Level 2 halted #
        "EBADE",  # Invalid exchange #
        "EBADR",  # Invalid request descriptor #
        "EXFULL",  # Exchange full #
        "ENOANO",  # No anode #
        "EBADRQC",  # Invalid request code #
        "EBADSLT ",  # Invalid slot #
        "EDEADLOCK",
        "EBFONT",  # Bad font file format #
        "ENOSTR",  # Device not a stream #
        "ENODATA",  # No data available #
        "ETIME",  # Timer expired #
        "ENOSR",  # Out of streams resources #
        "ENONET",  # Machine is not on the network #
        "ENOPKG",  # Package not installed #
        "EREMOTE",  # Object is remote #
        "ENOLINK",  # Link has been severed #
        "EADV",  # Advertise error #
        "ESRMNT",  # Srmount error #
        "ECOMM",  # Communication error on send #
        "EPROTO",  # Protocol error #
        "EMULTIHOP",  # Multihop attempted #
        "EDOTDOT",  # RFS specific error #
        "EBADMSG",  # Not a data message #
        "EOVERFLOW",  # Value too large for defined data type #
        "ENOTUNIQ",  # Name not unique on network #
        "EBADFD",  # File descriptor in bad state #
        "EREMCHG",  # Remote address changed #
        "ELIBACC",  # Can not access a needed shared library #
        "ELIBBAD",  # Accessing a corrupted shared library #
        "ELIBSCN",  # .lib section in a.out corrupted #
        "ELIBMAX",  # Attempting to link in too many shared libraries #
        "ELIBEXEC",  # Cannot exec a shared library directly #
        "EILSEQ",  # Illegal byte sequence #
        "ERESTART",  # Interrupted system call should be restarted #
        "ESTRPIPE",  # Streams pipe error #
        "EUSERS",  # Too many users #
        "ENOTSOCK",  # Socket operation on non-socket #
        "EDESTADDRREQ",  # Destination address required #
        "EMSGSIZE",  # Message too long #
        "EPROTOTYPE",  # Protocol wrong type for socket #
        "EPROTOTYPE",  # Protocol wrong type for socket #
        "ENOPROTOOPT",  # Protocol not available #
        "EPROTONOSUPPORT",  # Protocol not supported #
        "ESOCKTNOSUPPORT",  # Socket type not supported #
        "EOPNOTSUPP",  # Operation not supported on transport endpoint #
        "EPFNOSUPPORT",  # Protocol family not supported #
        "EAFNOSUPPORT ",  # Address family not supported by protocol #
        "EADDRINUSE ",  # Address already in use #
        "EADDRNOTAVAIL",  # Cannot assign requested address #
        "ENETDOWN",  # Network is down #
        "ENETUNREACH",  # Network is unreachable #
        "ENETRESET",  # Network dropped connection because of reset #
        "ECONNABORTED",  # Software caused connection abort #
        "ECONNRESET",  # Connection reset by peer #
        "ENOBUFS",  # No buffer space available #
        "EISCONN",  # Transport endpoint is already connected #
        "ENOTCONN",  # Transport endpoint is not connected #
        "ESHUTDOWN",  # Cannot send after transport endpoint shutdown #
        "ETOOMANYREFS",  # Too many references: cannot splice #
        "ETIMEDOUT",  # Connection timed out #
        "ECONNREFUSED",  # Connection refused #
        "EHOSTDOWN",  # Host is down #
        "EHOSTUNREACH",  # No route to host #
        "EALREADY",  # Operation already in progress #
        "EINPROGRESS",  # Operation now in progress #
        "ESTALE",  # Stale NFS file handle #
        "EUCLEAN",  # Structure needs cleaning #
        "ENOTNAM",  # Not a XENIX named type file #
        "ENAVAIL",  # No XENIX semaphores available #
        "EISNAM",  # Is a named type file #
        "EREMOTEIO",  # Remote I/O error #
        "EDQUOT",  # Quota exceeded #
        "ENOMEDIUM",  # No medium found #
        "EMEDIUMTYPE",  # Wrong medium type #
        "ECANCELED",  # Operation Canceled #
        "ENOKEY",  # Required key not available #
        "EKEYEXPIRED",  # Key has expired #
        "EKEYREVOKED",  # Key has been revoked #
        "EKEYREJECTED",  # Key was rejected by service #
        "EOWNERDEAD",  # Owner died #
        "ENOTRECOVERABLE",  # State not recoverable #
        "ERFKILL",  # Operation not possible due to RF-kill #
        "EHWPOISON",  # Memory page has hardware error #
    ]

    # rlimit types #
    RLIMIT_TYPE = [
        "RLIMIT_CPU",
        "RLIMIT_FSIZE",
        "RLIMIT_DATA",
        "RLIMIT_STACK",
        "RLIMIT_CORE",
        "RLIMIT_RSS",
        "RLIMIT_NPROC",
        "RLIMIT_NOFILE",
        "RLIMIT_MEMLOCK",
        "RLIMIT_AS",
        "RLIMIT_LOCKS",
        "RLIMIT_SIGPENDING",
        "RLIMIT_MSGQUEUE",
        "RLIMIT_NICE",
        "RLIMIT_RTPRIO",
        "RLIMIT_RTTIME",
        "RLIMIT_NLIMITS",
    ]

    # UDP/TCP format #
    PROTOCOL_ATTR = [
        "sl",
        "local_address",
        "rem_address",
        "st",
        "tx_rx_queue",
        "tr_tm->when",
        "retrnsmt",
        "uid",
        "timeout",
        "inode",
        "ref",
        "pointer",
        "drops",
    ]

    # UDS format #
    UDS_ATTR = [
        "Num",
        "RefCount",
        "Protocol",
        "Flags",
        "Type",
        "St",
        "Inode",
        "Path",
    ]

    TCP_STAT = [
        "N/A",
        "ESTABLISHED",
        "SYN_SENT",
        "SYN_RECV",
        "FIN_WAIT1",
        "FIN_WAIT2",
        "TIME_WAIT",
        "CLOSE",
        "CLOSE_WAIT",
        "LAST_ACK",
        "LISTEN",
        "CLOSING",
    ]

    # futex operation flags #
    FUTEX_TYPE = [
        "FUTEX_WAIT",
        "FUTEX_WAKE",
        "FUTEX_FD",
        "FUTEX_REQUEUE",
        "FUTEX_CMP_REQUEUE",
        "FUTEX_WAKE_OP",
        "FUTEX_LOCK_PI",
        "FUTEX_UNLOCK_PI",
        "FUTEX_TRYLOCK_PI",
        "FUTEX_WAIT_BITSET",
        "FUTEX_WAKE_BITSET",
        "FUTEX_WAIT_REQUEUE_PI",
        "FUTEX_CMP_REQUEUE_PI",
    ]

    # fcntl command flags #
    FCNTL_TYPE = {
        0: "F_DUPFD",
        1: "F_GETFD",
        2: "F_SETFD",
        3: "F_GETFL",
        4: "F_SETFL",
        5: "F_GETOWN",
        6: "F_SETOWN",
        7: "F_GETLK",
        8: "F_SETLK",
        9: "F_SETLKW",
        10: "F_SETOWN",
        11: "F_GETOWN",
        12: "F_SETSIG",
        13: "F_GETSIG",
        14: "F_GETLK64",
        15: "F_SETLK64",
        16: "F_SETLKW64",
        17: "F_SETOWNEX",
        18: "F_GETOWNEX",
        19: "F_GETOWNERUIDS",
        1024: "F_SETLEASE",
        1025: "F_GETLEASE",
        1026: "F_NOTIFY",
        1029: "F_CANCELLK",
        1030: "F_DUPFD_CLOEXEC",
        1031: "F_SETPIPE_SZ",
        1032: "F_GETPIPE_SZ",
        1033: "F_ADD_SEALS",
        1034: "F_GET_SEALS",
        1035: "F_GET_RW_HINT",
        1036: "F_SET_RW_HINT",
        1037: "F_GET_FILE_RW_HINT",
        1038: "F_SET_FILE_RW_HINT",
    }

    # ipc call flags #
    IPC_TYPE = {
        1: "SEMOP",
        2: "SEMGET",
        3: "SEMCTL",
        4: "SEMTIMEDOP",
        11: "MSGSND",
        12: "MSGRCV",
        13: "MSGGET",
        14: "MSGCTL",
        21: "SHMAT",
        22: "SHMDT",
        23: "SHMGET",
        24: "SHMCTL",
    }

    # ioring_enter flags #
    IORING_ENTER_FLAG = {
        1: "IORING_ENTER_GETEVENTS",
        2: "IORING_ENTER_SQ_WAKEUP",
        4: "IORING_ENTER_SQ_WAIT",
        8: "IORING_ENTER_EXT_ARG",
        16: "IORING_ENTER_REGISTERED_RING",
    }

    # ioring_register opcode #
    IORING_REGISTER_OPCODE = [
        "IORING_REGISTER_BUFFERS",
        "IORING_UNREGISTER_BUFFERS",
        "IORING_REGISTER_FILES",
        "IORING_UNREGISTER_FILES",
        "IORING_REGISTER_EVENTFD",
        "IORING_UNREGISTER_EVENTFD",
        "IORING_REGISTER_FILES_UPDATE",
        "IORING_REGISTER_EVENTFD_ASYNC",
        "IORING_REGISTER_PROBE",
        "IORING_REGISTER_PERSONALITY",
        "IORING_UNREGISTER_PERSONALITY",
        "IORING_REGISTER_RESTRICTIONS",
        "IORING_REGISTER_ENABLE_RINGS",
        "IORING_REGISTER_FILES2",
        "IORING_REGISTER_FILES_UPDATE2",
        "IORING_REGISTER_BUFFERS2",
        "IORING_REGISTER_BUFFERS_UPDATE",
        "IORING_REGISTER_IOWQ_AFF",
        "IORING_UNREGISTER_IOWQ_AFF",
        "IORING_REGISTER_IOWQ_MAX_WORKERS",
        "IORING_REGISTER_RING_FDS",
        "IORING_UNREGISTER_RING_FDS",
        "IORING_REGISTER_PBUF_RING",
        "IORING_UNREGISTER_PBUF_RING",
        "IORING_REGISTER_SYNC_CANCEL",
        "IORING_REGISTER_FILE_ALLOC_RANGE",
    ]

    # ioring_param features #
    IORING_PARAM_FEATURES = {
        0x1: "IORING_FEAT_SINGLE_MMAP",
        0x2: "IORING_FEAT_NODROP",
        0x4: "IORING_FEAT_SUBMIT_STABLE",
        0x8: "IORING_FEAT_RW_CUR_POS",
        0x16: "IORING_FEAT_CUR_PERSONALITY",
        0x32: "IORING_FEAT_FAST_POLL",
        0x64: "IORING_FEAT_POLL_32BITS",
        0x128: "IORING_FEAT_SQPOLL_NONFIXED",
        0x256: "IORING_FEAT_EXT_ARG",
        0x512: "IORING_FEAT_NATIVE_WORKERS",
        0x1024: "IORING_FEAT_RSRC_TAGS",
        0x2048: "IORING_FEAT_CQE_SKIP",
        0x4096: "IORING_FEAT_LINKED_FILE",
    }

    # ioctl flags #
    IOCTL_TYPE = {
        "TCGETS": 0x5401,
        "TCSETS": 0x5402,
        "TCSETSW": 0x5403,
        "TCSETSF": 0x5404,
        "TCGETA": 0x5405,
        "TCSETA": 0x5406,
        "TCSETAW": 0x5407,
        "TCSETAF": 0x5408,
        "TCSBRK": 0x5409,
        "TCXONC": 0x540A,
        "TCFLSH": 0x540B,
        "TIOCEXCL": 0x540C,
        "TIOCNXCL": 0x540D,
        "TIOCSCTTY": 0x540E,
        "TIOCGPGRP": 0x540F,
        "TIOCSPGRP": 0x5410,
        "TIOCOUTQ": 0x5411,
        "TIOCSTI": 0x5412,
        "TIOCGWINSZ": 0x5413,
        "TIOCSWINSZ": 0x5414,
        "TIOCMGET": 0x5415,
        "TIOCMBIS": 0x5416,
        "TIOCMBIC": 0x5417,
        "TIOCMSET": 0x5418,
        "TIOCGSOFTCAR": 0x5419,
        "TIOCSSOFTCAR": 0x541A,
        "FIONREAD": 0x541B,
        "TIOCLINUX": 0x541C,
        "TIOCCONS": 0x541D,
        "TIOCGSERIAL": 0x541E,
        "TIOCSSERIAL": 0x541F,
        "TIOCPKT": 0x5420,
        "FIONBIO": 0x5421,
        "TIOCNOTTY": 0x5422,
        "TIOCSETD": 0x5423,
        "TIOCGETD": 0x5424,
        "TCSBRKP": 0x5425,
        "TIOCSBRK": 0x5427,
        "TIOCCBRK": 0x5428,
        "TIOCGSID": 0x5429,
        "TIOCGRS485": 0x542E,
        "TIOCSRS485": 0x542F,
        "TCGETX": 0x5432,
        "TCSETX": 0x5433,
        "TCSETXF": 0x5434,
        "TCSETXW": 0x5435,
        "FIONCLEX": 0x5450,
        "FIOCLEX": 0x5451,
        "FIOASYNC": 0x5452,
        "TIOCSERCONFIG": 0x5453,
        "TIOCSERGWILD": 0x5454,
        "TIOCSERSWILD": 0x5455,
        "TIOCGLCKTRMIOS": 0x5456,
        "TIOCSLCKTRMIOS": 0x5457,
        "TIOCSERGSTRUCT": 0x5458,
        "TIOCSERGETLSR": 0x5459,
        "TIOCSERGETMULTI": 0x545A,
        "TIOCSERSETMULTI": 0x545B,
        "TIOCMIWAIT": 0x545C,
        "TIOCGICOUNT": 0x545D,
    }
    IOCTL_TYPE_REVERSE = {}

    # ptrace request type #
    PTRACE_TYPE = [
        "PTRACE_TRACEME",  # 0
        "PTRACE_PEEKTEXT",
        "PTRACE_PEEKDATA",
        "PTRACE_PEEKUSR",
        "PTRACE_POKETEXT",
        "PTRACE_POKEDATA",
        "PTRACE_POKEUSR",
        "PTRACE_CONT",
        "PTRACE_KILL",
        "PTRACE_SINGLESTEP",  # 9
        "",
        "",
        "PTRACE_GETREGS",  # 12
        "PTRACE_SETREGS",  # 13
        "PTRACE_GETFPREGS",  # 14
        "PTRACE_SETFPREGS",  # 15
        "PTRACE_ATTACH",  # 16
        "PTRACE_DETACH",  # 17
        "PTRACE_GETFPXREGS",  # 18
        "PTRACE_SETFPXREGS",  # 19
        "",
        "",
        "",
        "PTRACE_SET_SYSCALL",  # 23
        "PTRACE_SYSCALL",  # 24
        "",
        "",
        "",
        "",
        "",
        "",
        "PTRACE_SYSEMU",  # 31
        "PTRACE_SYSEMU_SINGLESTEP",  # 32
    ]

    # ptrace event types #
    PTRACE_EVENT_TYPE = (
        [
            "PTRACE_EVENT_NONE",
            "PTRACE_EVENT_FORK",
            "PTRACE_EVENT_VFORK",
            "PTRACE_EVENT_CLONE",
            "PTRACE_EVENT_EXEC",
            "PTRACE_EVENT_VFORK_DONE",
            "PTRACE_EVENT_EXIT",
            "PTRACE_EVENT_SECCOMP",
        ]
        + ["NONE" for idx in xrange(120)]
        + ["PTRACE_EVENT_STOP"]
    )

    # perf event types #
    PERF_EVENT_TYPE = [
        "PERF_TYPE_HARDWARE",
        "PERF_TYPE_SOFTWARE",
        "PERF_TYPE_TRACEPOINT",
        "PERF_TYPE_HW_CACHE",
        "PERF_TYPE_RAW",
        "PERF_TYPE_BREAKPOINT",
    ]
    PERF_HW_EVENT_TYPE = [
        "PERF_COUNT_HW_CPU_CYCLES",
        "PERF_COUNT_HW_INSTRUCTIONS",
        "PERF_COUNT_HW_CACHE_REFERENCES",
        "PERF_COUNT_HW_CACHE_MISSES",
        "PERF_COUNT_HW_BRANCH_INSTRUCTIONS",
        "PERF_COUNT_HW_BRANCH_MISSES",
        "PERF_COUNT_HW_BUS_CYCLES",
        "PERF_COUNT_HW_STALLED_CYCLES_FRONTEND",
        "PERF_COUNT_HW_STALLED_CYCLES_BACKEND",
        "PERF_COUNT_HW_REF_CPU_CYCLES",
    ]
    PERF_SW_EVENT_TYPE = [
        "PERF_COUNT_SW_CPU_CLOCK",
        "PERF_COUNT_SW_TASK_CLOCK",
        "PERF_COUNT_SW_PAGE_FAULTS",
        "PERF_COUNT_SW_CONTEXT_SWITCHES",
        "PERF_COUNT_SW_CPU_MIGRATIONS",
        "PERF_COUNT_SW_PAGE_FAULTS_MIN",
        "PERF_COUNT_SW_PAGE_FAULTS_MAJ",
        "PERF_COUNT_SW_ALIGNMENT_FAULTS",
        "PERF_COUNT_SW_EMULATION_FAULTS",
        "PERF_COUNT_SW_DUMMY",
        "PERF_COUNT_SW_BPF_OUTPUT",
        "PERF_COUNT_SW_CGROUP_SWITCHES",
    ]
    PERF_CACHE_EVENT_TYPE = [
        "PERF_COUNT_HW_CACHE_L1D",
        "PERF_COUNT_HW_CACHE_L1I",
        "PERF_COUNT_HW_CACHE_LL",
        "PERF_COUNT_HW_CACHE_DTLB",
        "PERF_COUNT_HW_CACHE_ITLB",
        "PERF_COUNT_HW_CACHE_BPU",
        "PERF_COUNT_HW_CACHE_NODE",
    ]
    PERF_CACHE_EVENT_OP = [
        "PERF_COUNT_HW_CACHE_OP_READ",
        "PERF_COUNT_HW_CACHE_OP_WRITE",
        "PERF_COUNT_HW_CACHE_OP_PREFETCH",
    ]
    PERF_CACHE_EVENT_OPRES = [
        "PERF_COUNT_HW_CACHE_RESULT_ACCESS",
        "PERF_COUNT_HW_CACHE_RESULT_MISS",
    ]
    PERF_EVENT_SAMPLE = {
        "PERF_SAMPLE_IP": 1 << 0,
        "PERF_SAMPLE_TID": 1 << 1,
        "PERF_SAMPLE_TIME": 1 << 2,
        "PERF_SAMPLE_ADDR": 1 << 3,
        "PERF_SAMPLE_READ": 1 << 4,
        "PERF_SAMPLE_CALLCHAIN": 1 << 5,
        "PERF_SAMPLE_ID": 1 << 6,
        "PERF_SAMPLE_CPU": 1 << 7,
        "PERF_SAMPLE_PERIOD": 1 << 8,
        "PERF_SAMPLE_STREAM_ID": 1 << 9,
        "PERF_SAMPLE_RAW": 1 << 10,
        "PERF_SAMPLE_BRANCH_STACK": 1 << 11,
        "PERF_SAMPLE_REGS_USER": 1 << 12,
        "PERF_SAMPLE_STACK_USER": 1 << 13,
        "PERF_SAMPLE_WEIGHT": 1 << 14,
        "PERF_SAMPLE_DATA_SRC": 1 << 15,
        "PERF_SAMPLE_IDENTIFIER": 1 << 16,
        "PERF_SAMPLE_TRANSACTION": 1 << 17,
        "PERF_SAMPLE_REGS_INTR": 1 << 18,
        "PERF_SAMPLE_PHYS_ADDR": 1 << 19,
        "PERF_SAMPLE_AUX": 1 << 20,
        "PERF_SAMPLE_CGROUP": 1 << 21,
        "PERF_SAMPLE_DATA_PAGE_SIZE": 1 << 22,
        "PERF_SAMPLE_CODE_PAGE_SIZE": 1 << 23,
        "PERF_SAMPLE_WEIGHT_STRUCT": 1 << 24,
    }
    PERF_EVENT_READ_FORMAT = {
        "PERF_FORMAT_TOTAL_TIME_ENABLED": 1 << 0,
        "PERF_FORMAT_TOTAL_TIME_RUNNING": 1 << 1,
        "PERF_FORMAT_ID": 1 << 2,
        "PERF_FORMAT_GROUP": 1 << 3,
        "PERF_FORMAT_LOST": 1 << 4,
        "PERF_FORMAT_MAX": 1 << 5,
    }
    PERF_FLAG = {
        "PERF_FLAG_FD_NO_GROUP": 1 << 0,
        "PERF_FLAG_FD_OUTPUT": 1 << 1,
        "PERF_FLAG_PID_CGROUP": 1 << 2,
        "PERF_FLAG_FD_CLOEXEC": 1 << 3,
    }
    PERF_BRANCH_SAMPLE_SHIFT = [
        "PERF_SAMPLE_BRANCH_USER_SHIFT",  # user branches #
        "PERF_SAMPLE_BRANCH_KERNEL_SHIFT",  # kernel branches #
        "PERF_SAMPLE_BRANCH_HV_SHIFT",  # hypervisor branches #
        "PERF_SAMPLE_BRANCH_ANY_SHIFT",  # any branch types #
        "PERF_SAMPLE_BRANCH_ANY_CALL_SHIFT",  # any call branch #
        "PERF_SAMPLE_BRANCH_ANY_RETURN_SHIFT",  # any return branch #
        "PERF_SAMPLE_BRANCH_IND_CALL_SHIFT",  # indirect calls #
        "PERF_SAMPLE_BRANCH_ABORT_TX_SHIFT",  # transaction aborts #
        "PERF_SAMPLE_BRANCH_IN_TX_SHIFT",  # in transaction #
        "PERF_SAMPLE_BRANCH_NO_TX_SHIFT",  # not in transaction #
        "PERF_SAMPLE_BRANCH_COND_SHIFT",  # conditional branches #
        "PERF_SAMPLE_BRANCH_CALL_STACK_SHIFT",  # call/ret stack #
        "PERF_SAMPLE_BRANCH_IND_JUMP_SHIFT",  # indirect jumps #
        "PERF_SAMPLE_BRANCH_CALL_SHIFT",  # direct call #
        "PERF_SAMPLE_BRANCH_NO_FLAGS_SHIFT",  # no flags #
        "PERF_SAMPLE_BRANCH_NO_CYCLES_SHIFT",  # no cycles #
        "PERF_SAMPLE_BRANCH_TYPE_SAVE_SHIFT",  # save branch type #
        "PERF_SAMPLE_BRANCH_HW_INDEX_SHIFT",  # save low level index of raw branch records #
        "PERF_SAMPLE_BRANCH_PRIV_SAVE_SHIFT",  # save privilege mode #
    ]

    @staticmethod
    def getMmapId():
        if SysMgr.arch == "arm":
            return SysMgr.getNrSyscall("sys_mmap2")
        else:
            return SysMgr.getNrSyscall("sys_mmap")

    def __init__(self, mode):
        pass

    def __del__(self):
        pass


class UtilMgr(object):
    """Manager for utilities"""

    """
    [ TIPS ]
    - vim replacement
        - PROBLEM: replace all "type(???) is long" with "isinstance(???, (int, long))"
        - SOLUTION: %s/type(\(.*\)) is long/isinstance(\1, (int, long)/g
    """

    curTime = 0
    progressCnt = 0
    progressStr = 0
    progressChar = {0: "|", 1: "/", 2: "-", 3: "\\"}

    @staticmethod
    def startMemProf(reset=True):
        tracemalloc = SysMgr.getPkg("tracemalloc")
        if reset:
            tracemalloc.clear_traces()
        tracemalloc.start()

    @staticmethod
    def stopMemProf():
        SysMgr.getPkg("tracemalloc").stop()

    @staticmethod
    def getMemProf():
        return SysMgr.getPkg("tracemalloc").take_snapshot()

    @staticmethod
    def printMemProf(num=10, prev=None, backtrace=False):
        tracemalloc = SysMgr.getPkg("tracemalloc")
        snapshot = tracemalloc.take_snapshot()

        if prev:
            top_stats = snapshot.compare_to(prev, "lineno")
        else:
            top_stats = snapshot.statistics("lineno")

        print("[ Top %s%s ]" % ("Diff " if prev else "", num))
        for idx, stat in enumerate(top_stats[:num]):
            print("[%s] %s" % (idx, stat))
            if backtrace:
                for line in stat.traceback.format():
                    print(line)

    @staticmethod
    def printCtypeArray(addr, size, length=-1, isInt=True):
        # get ctypes object #
        SysMgr.importPkgItems("ctypes")

        # print as a bytearray #
        if isInt:
            res = list(map(long, string_at(addr, size)[:length]))
        else:
            res = string_at(addr, size)[:length]

        print(res)

    @staticmethod
    def convHtmlChar(string):
        chars = {"<": "&lt;", ">": "&gt;", "&": "&amp;"}

        for key, val in chars.items():
            string = string.replace(key, val)

        return string

    @staticmethod
    def makeReverseDict(orig, new):
        if not new:
            for name, value in orig.items():
                new[value] = name

    @staticmethod
    def getRange(start, end, step):
        def mrange(start, end, step):
            r = start
            while r < end:
                yield r
                r += step

        return [x for x in mrange(start, end, step)]

    @staticmethod
    def removeColor(string):
        if not SysMgr.ansiObj:
            ansi = r"(?:\x1B[@-_]|[\x80-\x9F])[0-?]*[ -/]*[@-~]"
            SysMgr.ansiObj = re.compile(ansi)
        return SysMgr.ansiObj.sub("", string)

    @staticmethod
    def convFloat2Str(val):
        val = ("%f" % val).rstrip("0")
        if val.endswith("."):
            val += "0"
        return val

    @staticmethod
    def convStr2Bytes(string):
        # build string #
        res = ""
        for byte in string:
            if isinstance(byte, str):
                byte = long(repr(struct.unpack("B", byte)[0]))
            bstr = "%x" % byte
            res = "{0:s}{1:0>2} ".format(res, bstr)
        return res

    @staticmethod
    def saveTime():
        UtilMgr.printTime(update=True, verb=False)

    @staticmethod
    def convLineStr(val, title, indent, maxLen, startIndent=True):
        resList = [val[i : i + maxLen] for i in xrange(0, len(val), maxLen)]

        res = ""
        cur = indent if startIndent else 1
        for string in resList:
            res += "{0:{cur}} {1:<1}\n".format(title, string, cur=cur)
            title = ""
            cur = indent

        return res.rstrip()

    @staticmethod
    def removeItems(target, val):
        while 1:
            try:
                if not val in target:
                    break
                target.remove(val)
            except SystemExit:
                sys.exit(0)
            except:
                pass

        return target

    @staticmethod
    def convStackStr(stack, indent, maxLen):
        newStack = ""
        nowLen = indent
        for call in stack.split(" <- "):
            if newStack and nowLen + len(call) + 4 > maxLen:
                newStack += "\n" + " " * indent
                nowLen = indent
            newStack += " <- " + call
            nowLen += 4 + len(call)
        return newStack

    @staticmethod
    def getInputNames(inputFile):
        if not inputFile:
            fileName = "guider"
            inputName = "N/A"
            inputList = []
        elif isinstance(inputFile, list):
            if SysMgr.outPath:
                fileName = SysMgr.outPath
            elif len(inputFile) == 1:
                fileName = inputFile[0]
            else:
                fileName = "merged"
            inputName = ", ".join(inputFile)
            inputList = inputFile
        else:
            fileName = inputFile
            inputName = inputFile
            inputList = [inputFile]

        return fileName, inputName, inputList

    @staticmethod
    def getSizeFilterFunc(var="SIZEFILTER"):
        # define size filter function #
        def _sizeChecker(val):
            return True

        # check size filter #
        if not var in SysMgr.environList:
            return _sizeChecker

        # get filter values #
        try:
            filters = SysMgr.environList[var][0]
            filterCode = filters[0]
            filterValue = UtilMgr.convUnit2Size(filters[1:])

            SysMgr.printInfo("applied %s '%s'" % (var, filters))
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to apply %s '%s'" % (var, filters), True)
            sys.exit(-1)

        # return function #
        if filterCode == "<":

            def _sizeChecker(val):
                if val < filterValue:
                    return True
                else:
                    return False

        elif filterCode == ">":

            def _sizeChecker(val):
                if val > filterValue:
                    return True
                else:
                    return False

        elif filterCode == "=":

            def _sizeChecker(val):
                if val == filterValue:
                    return True
                else:
                    return False

        else:
            SysMgr.printErr(
                "failed to apply %s '%s' because wrong operation"
                % (var, filters)
            )
            sys.exit(-1)

        return _sizeChecker

    @staticmethod
    def getPageAddrs(addr, length):
        PAGESIZE = SysMgr.PAGESIZE
        mod = addr % PAGESIZE
        start = addr - mod
        end = addr + length
        addr = start
        length = long((end - start + PAGESIZE - 1) / PAGESIZE) * PAGESIZE
        return start, end, addr, length

    @staticmethod
    def deepcopy(data):
        json = SysMgr.getPkg("marshal", False)
        if not json:
            json = SysMgr.getPkg("json", False)
        if json:
            return json.loads(json.dumps(data))
        else:
            return deepcopy(data)

    @staticmethod
    def printTime(name=None, update=True, verb=True):
        # get current time #
        now = time.time()
        if not UtilMgr.curTime and update:
            UtilMgr.curTime = now
            return

        diff = now - UtilMgr.curTime

        # update timestamp #
        if update:
            UtilMgr.curTime = now

        if not verb:
            return

        # add name #
        if name:
            prefix = "/ %s" % name
        else:
            prefix = ""

        # print time diff #
        string = "[ELAPSED] %f sec %s" % (diff, prefix)
        SysMgr.printWarn(string, True)

    @staticmethod
    def getCommonPart(strings):
        try:
            return os.path.commonprefix(strings)
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to get common part of %s" % strings)
            sys.exit(-1)

    @staticmethod
    def callPyFunc(path, fname, *args):
        try:
            # get function pointer #
            fullname = "%s_%s" % (path, fname)
            if fullname in SysMgr.externList:
                func = SysMgr.externList[fullname]
            elif sys.version_info < (3, 0, 0):
                execfile(path)  # pylint: disable=undefined-variable
                SysMgr.externList[fullname] = locals()[fname]
                func = SysMgr.externList[fullname]
            else:
                exec(open(path).read())
                SysMgr.externList[fullname] = locals()[fname]
                func = SysMgr.externList[fullname]

            # call the function and return #
            return func(args)
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                "failed to call '%s()' from '%s'" % (func, path), True
            )
            sys.exit(-1)

    @staticmethod
    def printAllAttrs(objList, name, rep=None, nameFilter=[]):
        if not objList:
            return

        # get first item #
        obj = objList[0]

        # get attrs #
        attrs = sorted([attr for attr in dir(obj) if not attr.startswith("_")])

        # get extract flag #
        doExtract = "EXTRACT" in SysMgr.environList
        defPath = os.path.dirname(SysMgr.outPath) if SysMgr.outPath else "."

        # get printtree flag #
        printTree = "PRINTTREE" in SysMgr.environList
        treeList = {}

        def _applyTreeItem(treeList, items, val):
            if not isinstance(treeList, dict):
                return

            name = items.pop(0)

            if items:
                treeList.setdefault(name, {})
                _applyTreeItem(treeList[name], items, val)
            elif "file_size" in val:
                treeList[name] = val["file_size"]
            elif "size" in val:
                treeList[name] = val["size"]

        def _printTreeItems(data, indent=0):
            for key, value in data.items():
                if key:
                    if isinstance(value, long):
                        size = " (%s)" % UtilMgr.convSize2Unit(value)
                    else:
                        key = "[%s]" % key
                        size = ""

                    SysMgr.printPipe(
                        "  " * indent
                        + ("|-" if indent else "")
                        + str(key)
                        + size
                    )

                if isinstance(value, dict):
                    _printTreeItems(value, indent + 1)

        # print objects #
        for obj in objList:
            try:
                itemDict = {}
                for attr in attrs:
                    if not hasattr(obj, attr):
                        continue

                    val = getattr(obj, attr)
                    if not isinstance(val, (str, long, bytes)):
                        continue

                    itemDict[attr] = getattr(obj, attr)

                repName = (
                    getattr(obj, rep) if rep and hasattr(obj, rep) else ""
                )
                if nameFilter and not UtilMgr.isValidStr(repName, nameFilter):
                    continue

                if printTree:
                    _applyTreeItem(treeList, repName.split("/"), itemDict)
                    continue

                origRepName = repName
                if repName:
                    repName = "(%s)" % repName

                # print values #
                SysMgr.printPipe(
                    "\n[%s Info] %s\n%s" % (name, repName, twoLine)
                )
                for n, v in sorted(itemDict.items()):
                    if not v:
                        continue

                    try:
                        if isinstance(v, str):
                            v.decode("UTF-8")
                    except UnicodeDecodeError:
                        v = "BYTES"
                    except SystemExit:
                        sys.exit(0)
                    except:
                        pass

                    if isinstance(v, long):
                        hexstr = " (%s)" % hex(v)
                    else:
                        hexstr = ""

                    SysMgr.printPipe(
                        "{0:<20} {1:1}".format(n, str(v) + hexstr)
                    )
                SysMgr.printPipe(oneLine + "\n")

                # extract to file #
                if doExtract:
                    try:
                        SysMgr.doDecompress(args=[name], ffilter=[origRepName])
                    except SystemExit:
                        sys.exit(0)
                    except:
                        SysMgr.printErr(
                            "failed to extract '%s'(%s) to '%s'"
                            % (
                                origRepName,
                                name,
                                os.path.join(defPath, origRepName),
                            ),
                            True,
                        )
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr("failed to print values for %s" % obj, True)

        _printTreeItems(treeList)

    @staticmethod
    def getTarFileList(path, perm="r|*", verb=True):
        tarfile = SysMgr.getPkg("tarfile", isExit=False)
        if not tarfile:
            return []

        try:
            with tarfile.open(path, perm) as fd:
                return fd.getmembers()
        except SystemExit:
            sys.exit(0)
        except:
            if not verb:
                return []
            SysMgr.printErr("failed to get tar info from '%s'" % path, True)
            return []

    @staticmethod
    def extractZipFile(path, target):
        zipfile = SysMgr.getPkg("zipfile", isExit=False)
        if not zipfile:
            return None

        try:
            with zipfile.ZipFile(path, "r") as fd:
                return fd.read(target)
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to get zip info from '%s'" % path, True)
            return None

    @staticmethod
    def getZipFileMap(path, onlyElf=False):
        # get zipfile object #
        try:
            zfd = SysMgr.getPkg("zipfile", False).ZipFile(path, "r")
            if not zfd:
                return {}
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                "failed to get ZipFile object of '%s'" % path, True
            )
            return {}

        filemap = {}
        for item in zfd.filelist:
            fd = zfd.open(item.filename)

            if onlyElf:
                if not UtilMgr.isElfFile(fd=fd):
                    continue

            filemap[item.filename] = {
                "offset": fd._orig_compress_start,
                "size": item.file_size,
                "compsize": item.compress_size,
            }

        return filemap

    @staticmethod
    def getZipFileList(path):
        zipfile = SysMgr.getPkg("zipfile", isExit=False)
        if not zipfile:
            return []

        try:
            with zipfile.ZipFile(path, "r") as fd:
                return fd.infolist()
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to get zip info from '%s'" % path, True)
            return []

    @staticmethod
    def isElfFile(path=None, fd=None):
        try:
            if path:
                fd = open(path, "rb")
            return struct.unpack("4B", fd.read(4)) == (
                0x7F,
                ord("E"),
                ord("L"),
                ord("F"),
            )
        except IOError:
            SysMgr.printOpenErr(path)
            return False
        except SystemExit:
            sys.exit(0)
        except:
            return False

    @staticmethod
    def isArtFile(path):
        return SysMgr.readFile(path, size=3, verb=False, byte=True) == b"art"

    @staticmethod
    def isOatFile(path):
        # OAT header starts from oatdata symbol adderss (0x1000) #
        return (
            SysMgr.readFile(path, size=3, verb=False, byte=True, offset=0x1000)
            == b"oat"
        )

    @staticmethod
    def checkMagicNum(path, fd, _check):
        try:
            if fd:
                pos = fd.tell()
                ret = _check(fd)
                fd.seek(pos)
                return ret

            with open(path, "rb") as fd:
                return _check(fd)
        except SystemExit:
            sys.exit(0)
        except:
            return False

    @staticmethod
    def isBzipFile(path, fd=None):
        def _check(fd):
            return struct.unpack("BB", fd.read(2)) == (0x42, 0x5A)

        return UtilMgr.checkMagicNum(path, fd, _check)

    @staticmethod
    def isXzFile(path, fd=None):
        def _check(fd):
            # check .gz type #
            res = struct.unpack("6B", fd.read(6)) == (
                0xFD,
                0x37,
                0x7A,
                0x58,
                0x5A,
                0x00,
            )
            if res:
                return True
            fd.seek(0)
            # check .lzma type #
            return struct.unpack("5B", fd.read(5)) == (
                0x5D,
                0x00,
                0x00,
                0x80,
                0x00,
            )

        return UtilMgr.checkMagicNum(path, fd, _check)

    @staticmethod
    def isPKZipFile(path, fd=None):
        def _check(fd):
            return struct.unpack("4B", fd.read(4)) == (
                0x50,
                0x4B,
                0x07,
                0x08,
            )

        return UtilMgr.checkMagicNum(path, fd, _check)

    @staticmethod
    def isZipFile(path, fd=None):
        def _check(fd):
            return struct.unpack("4B", fd.read(4)) == (
                0x50,
                0x4B,
                0x03,
                0x04,
            )

        return UtilMgr.checkMagicNum(path, fd, _check)

    @staticmethod
    def isTarFile(path, fd=None):
        def _check(fd):
            fd.seek(257)
            return struct.unpack("5B", fd.read(5)) == (
                0x75,
                0x73,
                0x74,
                0x61,
                0x72,
            )

        return UtilMgr.checkMagicNum(path, fd, _check)

    @staticmethod
    def isZlibFile(path, fd=None):
        def _check(fd):
            return fd.read(4) == b"ZLIB"

        return UtilMgr.checkMagicNum(path, fd, _check)

    @staticmethod
    def isGzipFile(path, fd=None):
        def _check(fd):
            return struct.unpack("BB", fd.read(2)) == (0x1F, 0x8B)

        return UtilMgr.checkMagicNum(path, fd, _check)

    @staticmethod
    def sort(l, val=None, reverse=False):
        convert = lambda text: float(text) if text.isdigit() else text
        alphanum = lambda key: [
            convert(c) for c in re.split("([-+]?[0-9]+\.?[0-9]*)", key)
        ]

        if isinstance(l, list):
            l.sort(key=alphanum, reverse=reverse)
            return l
        elif isinstance(l, dict):
            return sorted(
                l.items(),
                key=lambda e: alphanum(e[1][val] if val else e[0]),
                reverse=reverse,
            )
        else:
            SysMgr.printWarn(
                "failed to sort items because '%s' is not supported" % type(l),
                True,
            )
            return l

    @staticmethod
    def compareSyscallSuperset():
        syscallList = (
            ConfigMgr.SYSCALL_COMMON
            + ConfigMgr.SYSCALL_COMMON32
            + ConfigMgr.SYSCALL_X86
            + ConfigMgr.SYSCALL_X64
            + ConfigMgr.SYSCALL_ARM
            + ConfigMgr.SYSCALL_AARCH64
            + ConfigMgr.SYSCALL_RISCV64
        )
        syscallList = set(syscallList)

        protoList = set(
            ["sys_%s" % name for name in ConfigMgr.SYSCALL_PROTOTYPES]
        )

        # print final diff list #
        SysMgr.printPipe("--- NO PROTOTYPE ---")
        for name in sorted(list(syscallList - protoList)):
            SysMgr.printPipe(name)

        SysMgr.printPipe("\n--- NO DEFINITION ---")
        for name in sorted(list(protoList - syscallList)):
            SysMgr.printPipe(name)

    @staticmethod
    def isBitEnabled(num, bits):
        if not bits:
            return None

        try:
            num = long(num)
        except SystemExit:
            sys.exit(0)
        except:
            num = long(num, 16)

        try:
            bits = long(bits)
        except SystemExit:
            sys.exit(0)
        except:
            bits = long(bits, 16)

        if bits & (1 << num - 1):
            return True
        else:
            return False

    @staticmethod
    def convxrange(targetList):
        result = []

        for item in targetList:
            try:
                nums = item.split(":")
                if len(nums) == 1:
                    result.append(item)
                elif len(nums) == 2:
                    for num in xrange(long(nums[0]), long(nums[1]) + 1):
                        result.append(str(num))
                else:
                    raise Exception()
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr("wrong number range '%s'" % item, True)
                sys.exit(-1)

        return result

    @staticmethod
    def unionItem(targetList):
        result = []
        dictionary = {}

        for item in targetList:
            if item in dictionary:
                continue

            result.append(item)
            dictionary[item] = None

        return result

    @staticmethod
    def cleanItem(targetList, union=True):
        targetType = type(targetList)

        if targetType is str:
            targetStr = ""
            for val in targetList:
                if val:
                    targetStr += val
            return targetStr
        elif targetType is list:
            # remove redundant values #
            if union:
                targetList = UtilMgr.unionItem(targetList)

            # remove empty values #
            newList = []
            for val in targetList:
                val = val.strip()
                if val:
                    newList.append(val)

            return newList
        else:
            return targetList

    @staticmethod
    def genRangeDict(prefix, startIdx, endIdx, valStart):
        dictList = {}
        for index in xrange(startIdx, endIdx + 1):
            name = "%s%s" % (prefix, index)
            value = valStart + index - startIdx
            dictList[name] = value
        return dictList

    @staticmethod
    def printHist(table, title, unit, printer=None, newline=""):
        if not table:
            return

        convNum = UtilMgr.convNum
        if not printer:
            printer = SysMgr.printPipe

        # pop stats #
        try:
            statmin = table.pop("min", None)
            statmax = table.pop("max", None)
            statcnt = table.pop("cnt", None)
            statsum = table.pop("sum", None)
        except SystemExit:
            sys.exit(0)
        except:
            pass

        lineLen = 43
        printer(
            "\n[%s Histogram] (unit:%s)\n%s%s"
            % (title, unit, twoLine[:lineLen], newline[:lineLen])
        )
        printer(
            "{0:^21} | {1:^18}|\n{2:1}{3:1}".format(
                "Range", "Count", oneLine[:lineLen], newline[:lineLen]
            )
        )

        for digit, cnt in sorted(table.items()):
            srange = long(pow(2, digit - 1))

            if srange == 0:
                erange = 1
            else:
                erange = long((srange << 1) - 1)

            printer(
                "{0:10}-{1:>10} | {2:>10}({3:5.1f}%)|{4:1}".format(
                    convNum(srange),
                    convNum(erange),
                    convNum(cnt),
                    (cnt / float(statcnt)) * 100,
                    newline[:lineLen],
                )
            )

        try:
            printer(
                (
                    "{0:1}\n{1:^21} | {2:>18}|\n{3:^21} | {4:>18}|\n"
                    "{5:^21} | {6:>18}|\n{7:^21} | {8:>18}|\n{0:1}\n"
                ).format(
                    oneLine[:lineLen],
                    "Min",
                    convNum(statmin),
                    "Max",
                    convNum(statmax),
                    "Cnt",
                    convNum(statcnt),
                    "Sum",
                    convNum(statsum),
                )
            )
        except SystemExit:
            sys.exit(0)
        except:
            pass

    @staticmethod
    def ungzip(path):
        gzip = SysMgr.getPkg("gzip")
        return gzip.open(path, "rb").read()

    @staticmethod
    def unzip(fd, name):
        # get pkg #
        zlib = SysMgr.getPkg("zlib", False)
        if not zlib:
            return None, None

        # check format #
        magic = fd.read(4)
        if magic != b"ZLIB":
            SysMgr.printWarn(
                "wrong zlib magic number '%s' for %s section" % (magic, name)
            )
            return None, None

        compSize = struct.unpack(">Q", fd.read(8))[0]

        uncompBytes = b""
        decompressor = zlib.decompressobj()
        PAGESIZE = SysMgr.PAGESIZE

        # define getsize func #
        if hasattr(fd, "getsize"):

            def _getsize(fd):
                return fd.getsize()

        else:

            def _getsize(fd):
                return os.fstat(fd.fileno()).st_size

        while 1:
            remain = _getsize(fd) - fd.tell()
            size = PAGESIZE if remain >= PAGESIZE else remain
            chunk = fd.read(size)
            if not chunk:
                break
            uncompBytes += decompressor.decompress(chunk)

        uncompBytes += decompressor.flush()

        if len(uncompBytes) != compSize:
            SysMgr.printWarn(
                (
                    "failed to decompress %s section because "
                    "decompressed size is wrong [record: %s, actual: %s]"
                )
                % (name, compSize, len(uncompBytes))
            )

        return uncompBytes, len(uncompBytes)

    @staticmethod
    def convList2Histo(items, dtype="float", mult=1):
        def _getRangeIdx(value):
            if value <= 1:
                return 0

            digit = 0
            while 1:
                digit += 1
                value = value >> 1
                if value == 0:
                    return digit

        if not items:
            return None

        # convert type #
        if dtype == "float":
            if not isinstance(items[0], float):
                items = list(map(float, items))
        elif dtype == "long":
            if not isinstance(items[0], long):
                items = list(map(long, items))
        else:
            return None

        # convert unit #
        items = list(map(lambda x: long(x * mult), items))

        # get stats #
        histDict = {
            "max": max(items),
            "min": min(items),
            "cnt": len(items),
            "sum": sum(items),
        }

        for value in items:
            digit = _getRangeIdx(value)
            histDict.setdefault(digit, 0)
            histDict[digit] += 1

        return histDict

    @staticmethod
    def getSigList():
        sigList = dict(
            (k, v)
            for v, k in reversed(sorted(signal.__dict__.items()))
            if v.startswith("SIG") and not v.startswith("SIG_")
        )
        return sigList

    @staticmethod
    def getEnvironNum(
        name, isExit=True, default=None, verb=True, isInt=False, isFloat=False
    ):
        try:
            # get first value #
            value = SysMgr.environList[name][0]

            # return by type for compatibility #
            if isInt:
                return int(value)
            elif isFloat:
                return float(value)
            else:
                return long(value)
        except SystemExit:
            sys.exit(0)
        except:
            if verb:
                SysMgr.printErr(
                    "failed to get the number of %s variable" % name, True
                )

            if isExit:
                sys.exit(-1)

            return default

    @staticmethod
    def parseCommand(option):
        if not UtilMgr.isString(option):
            return option

        stringList = {}

        # process strings in "" #
        strings = re.findall('"(.*?)"', option)
        if strings:
            # create an dictionary for strings #
            for idx, item in enumerate(strings):
                if not item:
                    continue

                val = "#%s#" % idx
                stringList.setdefault(item.strip('"'), val)

            # replace strings #
            for string, value in stringList.items():
                option = option.replace('"%s"' % string, value)

        # split the option string #
        option = option.split(" ")
        for string, value in stringList.items():
            for idx, item in enumerate(deepcopy(option)):
                if value in item:
                    option[idx] = item.replace(value, string)

        return option

    @staticmethod
    def convList2Dict(optList, sep=":", cap=False):
        newDict = {}
        for item in optList:
            try:
                # get values #
                values = item.split(sep, 1)
                if len(values) == 1:
                    key = values[0]
                    value = "SET"
                else:
                    key, value = values

                # change to capital #
                if cap:
                    key = key.upper()

                # set values #
                if key in newDict:
                    newDict[key].append(value)
                else:
                    newDict[key] = [value]
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printWarn(
                    "failed to parse %s by separator %s" % (item, sep)
                )
                continue

        return newDict

    @staticmethod
    def getStdev(data):
        def _variance(data, ddof=0):
            n = len(data)
            mean = sum(data) / n
            v = n - ddof
            diffs = [(x - mean) ** 2 for x in data]
            try:
                return __builtins__.sum(diffs) / v
            except SystemExit:
                sys.exit(0)
            except:
                return sum(diffs) / v

        var = _variance(data)
        math = SysMgr.getPkg("math")
        stdDev = math.sqrt(var)
        return stdDev

    @staticmethod
    def splitString(string):
        string = string.replace("\,", "$%")

        clist = string.split(",")

        for idx, item in enumerate(list(clist)):
            clist[idx] = item.replace("$%", ",").strip()

        return clist

    @staticmethod
    def getDrawOutputPath(inputPath, name, suffix=False):
        # set output path #
        if SysMgr.outPath:
            outputPath = SysMgr.outPath

            # check dir #
            if os.path.isdir(outputPath):
                outputFileName = (
                    "%s.svg" % os.path.splitext(os.path.basename(inputPath))[0]
                )
                outputPath = os.path.join(outputPath, outputFileName)
            elif suffix:
                dirName = os.path.dirname(os.path.realpath(inputPath))
                fileName = "%s_%s.svg" % (
                    os.path.splitext(os.path.basename(inputPath))[0],
                    name,
                )
                outputPath = os.path.join(dirName, fileName)
        else:
            outputPath = UtilMgr.prepareForImageFile(inputPath, name)

        return os.path.realpath(outputPath)

    @staticmethod
    def printSyscalls(systable):
        bufstring = ""
        for idx, syscall in enumerate(systable):
            if idx % 4 == 0:
                bufstring += "\n"
            bufstring = "%s'%s', " % (bufstring, syscall)

        sys.exit("%s\ntotal: %s" % (bufstring, len(systable)))

    @staticmethod
    def isValidStrAll(string, key=None, inc=False, ignCap=False):
        if not string or not key:
            return False

        final = True
        for item in key:
            if not UtilMgr.isValidStr(string, [item], inc, ignCap):
                final = False
                break

        return final

    @staticmethod
    def isValidStr(string, key=None, inc=False, ignCap=False):
        if not string:
            return False

        if not key:
            if not SysMgr.filterGroup:
                return True
            key = SysMgr.filterGroup

        if ignCap:
            string = string.lower()

        for cond in list(key):
            if not cond:
                continue

            cond = str(cond)

            if ignCap:
                cond = cond.lower()

            if inc:
                if cond in string:
                    return True
            else:
                if cond == "*":
                    return True
                elif (
                    cond[0] == "*"
                    and cond[-1] == "*"
                    and cond.strip("*") in string
                ):
                    return True
                elif cond[0] == "*" and string.endswith(cond[1:]):
                    return True
                elif cond[-1] == "*" and string.startswith(cond[:-1]):
                    return True
                elif cond == string:
                    return True

        return False

    @staticmethod
    def convWord2Str(word):
        try:
            return struct.pack("L", word)
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to convert word %s to string" % word, True)
            return None

    @staticmethod
    def lstrip(string, wordList):
        """
        str.lstrip() removes all heading characters including all characters
        in the string argument.
        ex) '12345'.lstrip('321') -> '45'
        ex) '12345'.lstrip('31') -> '2345'
        """

        if not isinstance(wordList, list):
            wordList = [wordList]

        for word in wordList:
            if string.startswith(word):
                string = string[len(word) :]

        return string

    @staticmethod
    def rstrip(string, wordList):
        """
        str.rstrip() removes all ending characters including all characters
        in the string argument.
        ex) '12345'.rstrip('543') -> '12'
        ex) '12345'.rstrip('53') -> '1234'
        """

        if not isinstance(wordList, list):
            wordList = [wordList]

        for word in wordList:
            if string.endswith(word):
                return string[: -len(word)]

        return string

    @staticmethod
    def strip(string, wordList):
        ret = UtilMgr.lstrip(string, wordList)
        ret = UtilMgr.rstrip(ret, wordList)
        return ret

    @staticmethod
    def getInodes(
        path,
        inodeFilter=[],
        nameFilter=[],
        exceptFilter=[],
        fileAttr=None,
        verb=True,
    ):
        inodeList = {}

        for r, d, f in os.walk(path):
            # get full path for upper dir #
            fdir = os.path.realpath(r)
            if not FileAnalyzer.isValidFile(fdir):
                continue

            # print progress #
            UtilMgr.printProgress()

            for name in f + d:
                # get full path for file ##
                fpath = os.path.join(fdir, name)

                # check name filter #
                if nameFilter and not UtilMgr.isValidStr(name, nameFilter):
                    continue
                elif exceptFilter and UtilMgr.isValidStr(fpath, exceptFilter):
                    continue

                # check inode filter #
                try:
                    fstat = os.stat(fpath)
                    if not fstat:
                        continue
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printWarn(
                        "failed to get stat for %s" % fpath, reason=True
                    )
                    continue

                # get inode #
                inode = str(fstat.st_ino)
                if inodeFilter and not inode in inodeFilter:
                    continue

                # make device ID #
                major = os.major(fstat.st_dev)
                minor = os.minor(fstat.st_dev)
                devid = "%s:%s" % (major, minor)
                fpath = os.path.join(fdir, name)

                # register inode #
                inodeList.setdefault(devid, {})
                inodeList[devid][inode] = fpath

                # register file attribute #
                if isinstance(fileAttr, dict):
                    fileAttr[fpath] = fstat

        return inodeList

    @staticmethod
    def gerPermutation(inputList, union=False):
        visited = [0 for _ in xrange(len(inputList))]
        returnList = []

        def dfs(cnt, items):
            if cnt == len(inputList):
                returnList.append(items[:])
                return

            for i, val in enumerate(inputList):
                # check union #
                if union and visited[i] == 1:
                    continue

                # add item and check visit flag #
                items.append(val)
                visited[i] = 1

                dfs(cnt + 1, items)

                # remove item and uncheck visit flag #
                items.pop()
                visited[i] = 0

        dfs(0, [])

        return returnList

    @staticmethod
    def getPath(path):
        # get file info #
        filename = os.path.basename(path)
        dirname = os.path.dirname(path)
        if not dirname:
            dirname = "."
        return dirname, filename

    @staticmethod
    def getFiles(path, name=None, incFile=True, incDir=False, recursive=True):
        flist = []

        for r, d, f in os.walk(path):
            for inc, target in ((incFile, f), (incDir, d)):
                target = target if inc else []
                for s in target:
                    if name and not UtilMgr.isValidStr(s, name):
                        continue
                    flist.append(os.path.join(r, s))

            if not recursive:
                break

        return flist

    @staticmethod
    def convStr2Time(timestr, timefmt=None, isSec=True, verb=True):
        datetime = SysMgr.getPkg("datetime", False)
        if not datetime:
            return None

        if not timefmt:
            timefmt = "%Y-%m-%dT%H:%M:%SZ"

        try:
            timeObj = datetime.datetime.strptime(timestr, timefmt)
            if isSec:
                # seconds from UTC 1970-01-01 00:00:00 #
                return (
                    timeObj - datetime.datetime.fromtimestamp(0)
                ).total_seconds()
            else:
                return timeObj
        except SystemExit:
            sys.exit(0)
        except:
            if verb:
                SysMgr.printWarn(
                    "failed to convert '%s' to time" % timestr, True, True
                )
            return None

    @staticmethod
    def getEpoch2Start(utc=False):
        datetime = SysMgr.getPkg("datetime", False)
        if not datetime:
            return None

        # get epoch time #
        if utc:
            epoch = datetime.datetime.utcnow().timestamp()
        else:
            epoch = datetime.datetime.now().timestamp()

        return epoch - SysMgr.getUptime()

    @staticmethod
    def getClockTime(ctype="CLOCK_MONOTONIC", dlt=False):
        # get ctypes object #
        SysMgr.importPkgItems("ctypes")

        class timespec(Structure):
            _fields_ = [("tv_sec", c_long), ("tv_nsec", c_long)]

        # load libc #
        SysMgr.loadLibcObj(exit=True)

        # define arg types for free() #
        SysMgr.libcObj.clock_gettime.argtypes = [c_int, POINTER(timespec)]

        # create an object #
        t = timespec()

        # get time #
        ret = SysMgr.libcObj.clock_gettime(
            ConfigMgr.CLOCK_TYPE[ctype], pointer(t)
        )
        if ret != 0:
            SysMgr.printErr("failed to get clock %s time" % ctype, True)
            return -1

        # return time #
        if dlt:
            return (
                c_uint32(t.tv_sec * 10000).value / 10000
                + c_uint32(t.tv_nsec).value / 1000000000
            )
        else:
            return t.tv_sec + t.tv_nsec * 1e-9

    @staticmethod
    def getTime(utc=False):
        datetime = SysMgr.getPkg("datetime", False)
        if not datetime:
            return None

        if utc:
            timeobj = datetime.datetime.utcnow()
        else:
            timeobj = datetime.datetime.now()
        return timeobj.strftime("%Y-%m-%dT%H:%M:%SZ")

    @staticmethod
    def getFileList(flist, sort=False, exceptDir=False, verb=True):
        if not flist or not isinstance(flist, list):
            return []

        rlist = []
        for item in flist:
            item = item.strip()
            if item.startswith("-"):
                break

            # apply regex for path #
            ilist = UtilMgr.convPath(item)
            if not ilist:
                if verb:
                    SysMgr.printWarn(
                        "failed to find any file related to '%s'" % item, True
                    )
            elif UtilMgr.isString(ilist):
                rlist.append(ilist)
            elif isinstance(ilist, list):
                rlist += ilist

        if not rlist:
            return

        # check redundant files #
        if len(rlist) != len(set(rlist)):
            if verb:
                SysMgr.printWarn(
                    "detected redundant files in [ %s ]" % ", ".join(rlist),
                    True,
                )

        # exclude dir #
        if exceptDir:
            newlist = []
            for fname in rlist:
                if os.path.isdir(fname):
                    continue
                newlist.append(fname)
            rlist = newlist

        if not rlist:
            return

        # get filter list #
        targetList, exceptList = SysMgr.getTargetFileList()
        isValidStr = UtilMgr.isValidStr

        # apply filter #
        if targetList or exceptList:
            newlist = []
            for fname in rlist:
                if targetList and not isValidStr(fname, targetList):
                    continue
                elif exceptList and isValidStr(fname, exceptList):
                    continue
                newlist.append(fname)
            rlist = newlist

        # apply list filter #
        start = end = None
        ffilter = SysMgr.environList.get("FILELIST", [":"])[0].split(":", 1)
        try:
            if len(ffilter) == 1:
                ffilter.append(None)
            start, end = [long(f) if f else None for f in ffilter]
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to get FILELIST variables", True)
            sys.exit(0)

        # remove redundant files #
        if sort:
            return sorted(rlist)[start:end]
        else:
            return rlist[start:end]

    @staticmethod
    def decodeArg(value):
        try:
            text = repr(value.decode())
        except SystemExit:
            sys.exit(0)
        except:
            text = value

        # define start index by encoding type #
        if isinstance(text, bytes):
            start = 2
        else:
            start = 1

        return text[start:].rstrip("'")

    @staticmethod
    def convBin2Str(path, pos=False):
        try:
            if sys.version_info < (3, 0):
                fd = open(path, "rb")
            else:
                fd = open(path, encoding="latin-1")

            content = fd.read()
            strList = list(re.findall("[^\x00-\x1F\x7F-\xFF]{4,}", content))

            # add position #
            if pos:
                lastPos = 0
                dictList = {}
                for item in strList:
                    dictList.setdefault(item, content.find(item, lastPos))
                    lastPos = dictList[item]
                return dictList

            return strList
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to convert '%s' to strings" % path, True)
            return False

    @staticmethod
    def bisect_left(a, x, lo=0, hi=None):
        # copied from python standard library bisect.py #
        if lo < 0:
            raise ValueError("lo must be non-negative")
        if not hi:
            hi = len(a)
        while lo < hi:
            mid = (lo + hi) // 2
            if a[mid] <= x:
                lo = mid + 1
            else:
                hi = mid
        return lo

    @staticmethod
    def getFlagBit(vlist, flist):
        num = 0

        for flag in flist:
            try:
                num |= vlist[flag]
            except:
                SysMgr.printErr(
                    "failed to get %s in [%s]" % (flag, "|".join(list(vlist)))
                )
                sys.exit(-1)

        return num

    @staticmethod
    def getFlagList(value, flist, num="hex"):
        rlist = []
        numVal = long(value)

        for name, bits in flist.items():
            if numVal & bits:
                rlist.append(name)
        return rlist

    @staticmethod
    def getFlagString(value, flist, num="hex", zero=False):
        string = ""
        numVal = long(value)

        # get sorted dictionary vaue #
        if sys.version_info >= (3, 7, 0):
            targetList = flist
        else:
            targetList = sorted(list(flist))

        for bit in targetList:
            try:
                if numVal - bit < 0:
                    break
                elif numVal & bit:
                    string = "%s%s|" % (string, flist[bit])
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printWarn(
                    "failed to get flag info for %s" % value, reason=True
                )

        # check value for 0 index in limited variable range #
        if 0 in flist:
            if zero:
                string = "%s|%s" % (flist[0], string)
            elif num == "hex" and numVal & 0xF == 0:
                string = "%s|%s" % (flist[0], string)
            elif num == "oct" and numVal & 0o7 == 0:
                string = "%s|%s" % (flist[0], string)
            elif num == "bin" and numVal & 1 == 0:
                string = "%s|%s" % (flist[0], string)

        if string:
            return string[:-1]
        else:
            return value

    @staticmethod
    def encodeBase64(value):
        base64 = SysMgr.getPkg("base64", False)

        try:
            return base64.b64encode(value)
        except SystemExit:
            sys.exit(0)
        except:
            return value

    @staticmethod
    def readLEB128(fd):
        data = None
        while 1:
            char = fd.read(1)
            if ord(char) & 0x80 == 0:
                break
            elif not data:
                data = char
            else:
                data += char
        return data

    @staticmethod
    def decodeSLEB128(obj):
        size = 1
        value = 0

        # get size #
        for i, b in enumerate(obj):
            b = b if isinstance(b, long) else ord(b)
            value += (b & 0x7F) << (i * 7)
            if (b & 0x80) == 0:
                break
            size += 1

        # decode data #
        obj = obj[:size]
        if ord(obj[-1]) & 0x40:
            # negative -> sign extend
            value |= -(1 << (7 * len(obj)))

        return value, size

    @staticmethod
    def decodeULEB128(obj):
        size = 1
        value = 0

        # get size #
        for b in obj:
            val = b if isinstance(b, long) else ord(b)
            if (val & 0x80) == 0:
                break
            size += 1

        # decode data #
        for b in reversed(obj[:size]):
            b = b if isinstance(b, long) else ord(b)
            value = (value << 7) + (b & 0x7F)

        return value, size

    @staticmethod
    def isCompressed(fname=None, fd=None):
        # file name #
        if fname:
            try:
                fd = open(fname, "rb")
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printOpenErr(fname)
                sys.exit(-1)

        # file descriptor #
        if fd:
            data = fd.read(2)
            fd.seek(0, 0)
            if struct.unpack("BB", data) == (0x1F, 0x8B):
                return True
            else:
                return False

        # no value #
        return False

    @staticmethod
    def conv2BitStr(content):
        return bin(content)

    @staticmethod
    def decodeBase64(value):
        base64 = SysMgr.getPkg("base64", False)

        try:
            return base64.b64decode(value)
        except SystemExit:
            sys.exit(0)
        except:
            return value

    @staticmethod
    def encodeStr(value):
        try:
            return value.encode()
        except SystemExit:
            sys.exit(0)
        except:
            try:
                return value.encode("utf8", "surrogateescape")
            except SystemExit:
                sys.exit(0)
            except:
                return value

    @staticmethod
    def isString(value):
        if isinstance(value, str):
            return True
        elif sys.version_info >= (3, 0, 0):
            if isinstance(value, bytes):
                return True
        # pylint: disable-next=undefined-variable
        elif isinstance(value, unicode):
            return True
        return False

    @staticmethod
    def isFloat(value):
        if isinstance(value, float):
            return True
        elif isinstance(value, str):
            try:
                float(value)
                return True
            except SystemExit:
                sys.exit(0)
            except:
                return False
        else:
            return False

    @staticmethod
    def isNumber(value):
        if isinstance(value, (int, long)):
            return True
        elif isinstance(value, str):
            if value.isdigit():
                return True

            try:
                if value.startswith("0x") and long(value, 16):
                    return True
                else:
                    return False
            except SystemExit:
                sys.exit(0)
            except:
                return False
        else:
            return False

    @staticmethod
    def getTextLines(fname, verb=False, retfd=False, load=True):
        buf = []

        if verb:
            # get output size #
            fsize = UtilMgr.getFileSizeStr(fname)

            # set job type #
            if load:
                job = "loading"
            else:
                job = "checking"

            SysMgr.printStat(r"start %s '%s'%s..." % (job, fname, fsize))

        # open compressed file #
        fd = SysMgr.getCompFd(fname)

        # open normal file #
        try:
            if not fd:
                fd = open(fname, "r", encoding="utf-8")
        except SystemExit:
            sys.exit(0)
        except:
            fd = open(fname, "r")

        # just return fd #
        if retfd:
            return fd

        # get total size #
        try:
            totalSize = os.stat(fname).st_size
        except SystemExit:
            sys.exit(0)
        except:
            totalSize = 0

        # read data from a file #
        while 1:
            try:
                data = fd.readline()
                if not data:
                    break

                buf.append(data)

                if verb:
                    UtilMgr.printProgress(fd.tell(), totalSize)
            except SystemExit:
                sys.exit(0)
            except:
                break

        if verb:
            UtilMgr.deleteProgress()

        try:
            fd.close()
        except:
            pass

        return buf

    @staticmethod
    def convPath(value, retStr=False, isExit=False, separator=" ", warn=True):
        def _convType(retStr, res):
            # str #
            if retStr:
                if res:
                    return sorted(separator.join(res))
                else:
                    return ""
            # list #
            else:
                if res:
                    return sorted(res)
                else:
                    return []

        # strip path #
        value = value.strip()

        # get relative path option #
        isRelative = "RELPATH" in SysMgr.environList

        # all files including hidden files from specific directory recursively #
        if value.endswith("/**"):
            glob = None
            value = UtilMgr.rstrip(value, "/**")

            res = []
            for r, d, f in os.walk(value):
                if not isRelative:
                    r = os.path.realpath(r)
                res.append(r)
                for item in f:
                    res.append(os.path.join(r, item))

            return _convType(retStr, res)

        # all files including hidden files from specific directory recursively #
        glob = SysMgr.getPkg("glob", False)
        if glob:
            """
            # sort option #
            glob(value, key=os.path.getctime)
            glob(value, key=os.path.getatime)
            glob(value, key=os.path.getmtime)
            glob(value, key=os.path.getsize)
            """

            # check recursive path #
            if "**" in value:
                if sys.version_info >= (3, 11):
                    res = glob.glob(value, recursive=True, include_hidden=True)
                elif sys.version_info >= (3, 5):
                    res = glob.glob(value, recursive=True)
                else:
                    res = glob.glob(value)
            else:
                res = glob.glob(value)

            if not res and isExit:
                SysMgr.printErr("failed to find a file matching '%s'" % value)
                sys.exit(-1)

            return _convType(retStr, res)
        elif warn:
            if "*" in value:
                SysMgr.printWarn(
                    "failed to handle * character in the path "
                    "because there is no glob package",
                    True,
                )

        # str #
        if retStr:
            return value
        # list #
        else:
            return [value]

    @staticmethod
    def convStr2Word(bstring):
        try:
            return struct.unpack("L", bstring)[0]
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to convert string %s to word" % bstring)
            return None

    @staticmethod
    def convOverlayPath(path, overlayfsList):
        fileList = []
        itemList = ["lowerdir", "upperdir"]

        for pos, info in overlayfsList.items():
            if not pos in path:
                continue

            for target in itemList:
                for item in info[target]:
                    fullpath = item + path
                    if os.path.exists(fullpath):
                        fileList.append(fullpath)

        # return recent path #
        if fileList:
            return fileList[-1]
        else:
            return path

    @staticmethod
    def convStr2Num(string, verb=True, onlyHex=False):
        try:
            if isinstance(string, (int, long)):
                return string
            elif not onlyHex and string.isdigit():
                string = long(string)
            else:
                string = long(string, 16)
        except SystemExit:
            sys.exit(0)
        except:
            if verb:
                SysMgr.printErr(
                    "failed to convert %s to number" % string, True
                )
            return None

        return string

    @staticmethod
    def convNumIgn(number, isFloat=False, floatDigit=1):
        return str(number)

    @staticmethod
    def convNum(number, isFloat=False, floatDigit=1):
        try:
            if number == 0:
                return "0"
            elif isFloat:
                numstr = format(round(float(number), floatDigit), ",")
                first, second = numstr.split(".", 1)
                return first + "." + second[:floatDigit]
            else:
                return format(long(number), ",")
        except SystemExit:
            sys.exit(0)
        except:
            return number

    @staticmethod
    def convCpuColor(value, string=None, size=1, align="right"):
        if string is None:
            string = value

        if value >= SysMgr.cpuPerHighThr:
            return UtilMgr.convColor(string, "RED", size, align)
        elif value > 0:
            return UtilMgr.convColor(string, "YELLOW", size, align)
        else:
            return str(string)

    @staticmethod
    def convColor(string, color="LIGHT", size=1, align="right", force=False):
        # check skip condition #
        if not color:
            return str(string)
        elif force and not SysMgr.isWindows:
            pass
        elif not SysMgr.colorEnable:
            return str(string)
        elif SysMgr.outPath or SysMgr.jsonEnable:
            SysMgr.colorEnable = False
            return str(string)

        # direction #
        if align == "right":
            direct = ">"
        else:
            direct = "<"
        string = "{0:{direct}{size}}".format(
            str(string), direct=direct, size=size
        )

        # add color characters #
        try:
            return "%s%s%s" % (
                ConfigMgr.COLOR_LIST[color],
                string,
                ConfigMgr.ENDC,
            )
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn(
                "failed to convert color for %s" % color, reason=True
            )
            return str(string)

    @staticmethod
    def convSize2UnitIgn(size, isInt=False, unit=None):
        return str(size)

    @staticmethod
    def convSize2Unit(size, isInt=False, unit=None):
        sizeKB = 1024
        sizeMB = 1048576
        sizeGB = 1073741824
        sizeTB = 1099511627776
        sizePB = 1125899906842624
        sizeEB = 1152921504606846976
        # sizeZB = 1180591620717411303424
        # sizeYB = 120892581961462917470617

        # convert to ABS value #
        try:
            sizeAbs = abs(size)
        except SystemExit:
            sys.exit(0)
        except:
            return "?"

        if unit:
            factor = 1 if isInt else 1.0

            upperUnit = unit.upper()
            if upperUnit == "K":
                val = size / sizeKB * factor
            elif upperUnit == "M":
                val = size / sizeMB * factor
            elif upperUnit == "G":
                val = size / sizeGB * factor
            elif upperUnit == "T":
                val = size / sizeTB * factor
            elif upperUnit == "P":
                val = size / sizePB * factor
            elif upperUnit == "E":
                val = size / sizeEB * factor
            else:
                SysMgr.printErr("no support size unit '%s'" % unit)
                sys.exit(-1)

            if isInt:
                num = UtilMgr.convNum(long(val))
            else:
                num = UtilMgr.convNum(round(float(val), 1), isFloat=True)

            return "%s%s" % (num, upperUnit)

        # Int type #
        if isInt:
            try:
                if sizeAbs >= sizeEB:
                    return "%dE" % (size >> 60)
                elif sizeAbs >= sizePB:
                    return "%dP" % (size >> 50)
                elif sizeAbs >= sizeTB:
                    return "%dT" % (size >> 40)
                elif sizeAbs >= sizeGB:
                    return "%dG" % (size >> 30)
                elif sizeAbs >= sizeMB:
                    return "%dM" % (size >> 20)
                elif sizeAbs >= sizeKB:
                    return "%dK" % (size >> 10)
                else:
                    return "%d" % size
            except SystemExit:
                sys.exit(0)
            except:
                return "?"
        # Float type #
        else:
            try:
                if sizeAbs >= sizeEB:
                    return "%.1fE" % ((size >> 50) / 1024.0)
                elif sizeAbs >= sizePB:
                    return "%.1fP" % ((size >> 40) / 1024.0)
                elif sizeAbs >= sizeTB:
                    return "%.1fT" % ((size >> 30) / 1024.0)
                elif sizeAbs >= sizeGB:
                    return "%.1fG" % ((size >> 20) / 1024.0)
                elif sizeAbs >= sizeMB:
                    return "%.1fM" % ((size >> 10) / 1024.0)
                elif sizeAbs >= sizeKB:
                    return "%.1fK" % (size / 1024.0)
                else:
                    return "%d" % (size)
            except SystemExit:
                sys.exit(0)
            except:
                return "?"

    @staticmethod
    def convTime2Unit(sec):
        sec = float(sec)
        conv = UtilMgr.convNum

        try:
            if sec == long(sec):
                return "%ssec" % conv(sec)

            msSec = round(sec * 1000, 3)
            msSecInt = long(msSec)
            if msSec and msSec == msSecInt:
                return "%sms" % msSecInt

            usSec = round(sec * 1000000, 6)
            usSecInt = long(usSec)
            if usSec and usSec == usSecInt:
                return "%sus" % usSecInt

            nsSec = round(sec * 1000000000, 9)
            nsSecInt = long(nsSec)
            if nsSec and nsSec == nsSecInt:
                return "%sns" % nsSecInt

            return sec
        except SystemExit:
            sys.exit(0)
        except:
            return "?"

    @staticmethod
    def convTime2Sec(time):
        # convert time to seconds #
        try:
            sec = 0

            nums = time.strip().split(":")
            if len(nums) == 3:
                y = d = 0
                times = nums
            elif len(nums) == 4:
                y = 0
                d = int(nums[0].rstrip("d"))
                times = nums[1:]
            elif len(nums) == 5:
                y = int(nums[0].rstrip("y"))
                d = int(nums[0].rstrip("d"))
                times = nums[1:]
            else:
                raise Exception()

            # convert type #
            h, m, s = list(map(int, times))

            # convert items to seconds #
            sec += s
            sec += m * 60
            sec += h * 3600
            if d:
                sec += d * 86400
            if y:
                sec += y * 31536000
        except SystemExit:
            sys.exit(0)
        except:
            pass

        return sec

    @staticmethod
    def convTime(time, remainder=False):
        try:
            if not remainder:
                raise Exception("integer")
            remain = ".%02d" % long(time * 100 % 100)
        except SystemExit:
            sys.exit(0)
        except:
            remain = ""

        # convert seconds to time #
        try:
            m, s = divmod(time, 60)
            h, m = divmod(m, 60)

            # hour #
            if h >= 24:
                d, h = divmod(h, 24)

                # year #
                if d >= 365:
                    y, d = divmod(d, 365)
                    d = "%dy:%dd:" % (y, d)
                else:
                    d = "%dd:" % d
            else:
                d = ""

            ctime = "%s%02d:%02d:%02d%s" % (d, h, m, s, remain)
        except SystemExit:
            sys.exit(0)
        except:
            ctime = "%s%02s:%02s:%02s%s" % ("", "?", "?", "?", remain)

        return ctime.strip()

    @staticmethod
    def prepareForImageFile(logFile, itype="", outFile=None):
        # build output file name #
        if outFile:
            outputFile = outFile
        else:
            # convert file object to string #
            if hasattr(logFile, "name"):
                logFile = logFile.name

            if SysMgr.outPath:
                outputFile = os.path.normpath(SysMgr.outPath)
            else:
                outputFile = os.path.normpath(logFile)

            # convert output path #
            if os.path.isdir(outputFile):
                filename = os.path.basename(logFile)
                filename = os.path.splitext(filename)[0]
                name = "%s/%s" % (outputFile, filename)
            else:
                name = os.path.splitext(outputFile)[0]

            outputFile = "%s%s.%s" % (
                name,
                ("_" + itype) if itype and not name.endswith(itype) else "",
                SysMgr.drawFormat,
            )

        try:
            # backup an exist image file #
            if os.path.isfile(outputFile):
                name, ext = os.path.splitext(outputFile)

                oldPath = "%s_old%s" % (name, ext)
                if os.path.isfile(oldPath):
                    os.remove(oldPath)

                os.rename(outputFile, oldPath)

                SysMgr.printInfo(
                    "renamed '%s' to '%s' for backup" % (outputFile, oldPath)
                )
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                "failed to backup '%s' to '%s'" % (outputFile, oldPath), True
            )

        return outputFile

    @staticmethod
    def convUnit2Time(data, isFloat=False, verb=True):
        # check type #
        if isFloat:
            unit = float
        else:
            unit = long

        # check negative #
        if data.startswith("-"):
            negative = True
            data = data[1:]
        else:
            negative = False

        # convert to seconds #
        if str(data).isdigit():
            ret = unit(data)
        elif isFloat and str(data).replace(".", "").isdigit():
            ret = unit(data)
        elif data.upper().endswith("S"):
            ret = unit(data[:-1])
        elif data.upper().endswith("M"):
            ret = unit(data[:-1]) * 60
        elif data.upper().endswith("H"):
            ret = unit(data[:-1]) * 3600
        elif data.upper().endswith("D"):
            ret = unit(data[:-1]) * 86400
        elif data.upper().endswith("W"):
            ret = unit(data[:-1]) * 604800
        else:
            ret = 0
            if verb:
                SysMgr.printErr("failed to convert '%s' to seconds" % data)

        if negative:
            ret = -(ret)

        return ret

    @staticmethod
    def convUnit2Sec(data, verb=True):
        if str(data).isdigit():
            ret = long(data)
        elif data.upper().endswith("MS"):
            ret = long(data[:-2]) / 1000.0
        elif data.upper().endswith("US"):
            ret = long(data[:-2]) / 1000000.0
        elif data.upper().endswith("NS"):
            ret = long(data[:-2]) / 1000000000.0
        elif data.upper().endswith("PS"):
            ret = long(data[:-2]) / 1000000000000.0
        else:
            ret = 0
            if verb:
                SysMgr.printErr("failed to convert '%s' to seconds" % data)

        return ret

    @staticmethod
    def convUnit2Size(value, verb=True):
        sizeKB = 1024
        sizeMB = 1048576
        sizeGB = 1073741824
        sizeTB = 1099511627776
        sizePB = 1125899906842624
        sizeEB = 1152921504606846976
        # sizeZB = 1180591620717411303424
        # sizeYB = 120892581961462917470617

        if isinstance(value, (long, float)):
            return value

        value = str(value)

        # check type #
        origValue = value
        if value.startswith("-"):
            value = value.lstrip("-")
            factor = -1
        else:
            factor = 1

        if str(value).isdigit():
            return long(value) * factor

        # convert unit character to capital #
        value = value.upper()

        try:
            if value.endswith("K"):
                return long(float(value[:-1]) * sizeKB) * factor
            if value.endswith("KB"):
                return long(float(value[:-2]) * sizeKB) * factor

            if value.endswith("M"):
                return long(float(value[:-1]) * sizeMB) * factor
            if value.endswith("MB"):
                return long(float(value[:-2]) * sizeMB) * factor

            if value.endswith("G"):
                return long(float(value[:-1]) * sizeGB) * factor
            if value.endswith("GB"):
                return long(float(value[:-2]) * sizeGB) * factor

            if value.endswith("T"):
                return long(float(value[:-1]) * sizeTB) * factor
            if value.endswith("TB"):
                return long(float(value[:-2]) * sizeTB) * factor

            if value.endswith("P"):
                return long(float(value[:-1]) * sizePB) * factor
            if value.endswith("PB"):
                return long(float(value[:-2]) * sizePB) * factor

            if value.endswith("E"):
                return long(float(value[:-1]) * sizeEB) * factor
            if value.endswith("EB"):
                return long(float(value[:-2]) * sizeEB) * factor

            if verb:
                SysMgr.printErr("failed to convert '%s' to bytes" % origValue)

            raise ValueError()
        except SystemExit:
            sys.exit(0)
        except ValueError:
            raise Exception("wrong number unit")
        except:
            return origValue

    @staticmethod
    def saveStr2File(string, path):
        # open the file #
        try:
            with open(path, "w") as fd:
                fd.truncate()
                fd.write(string)
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                "failed to write JSON format data to %s" % path, True
            )
            sys.exit(-1)

    @staticmethod
    def writeJsonObject(jsonObj, fd=None, trunc=False, path=None):
        if fd:
            try:
                if trunc:
                    fd.seek(0, 0)
                    fd.truncate()
            except SystemExit:
                sys.exit(0)
            except:
                pass

            try:
                fd.write(jsonObj)
                fd.flush()
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printWarn(
                    "failed to write JSON format data", reason=True
                )
            return

        # check write option #
        if trunc:
            perm = "w"
        else:
            perm = "a"

        # open the file #
        try:
            with open(path, perm) as fd:
                fd.write(jsonObj)
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                "failed to write JSON format data to %s" % path, True
            )
            sys.exit(-1)

    @staticmethod
    def saveObject2File(obj, path):
        if not obj:
            return False

        pickle = SysMgr.getPicklePkg(False)
        if not pickle:
            return False

        # compress by gzip #
        if "COMPCACHE" in SysMgr.environList:
            compressor = SysMgr.getPkg("gzip", False)
        else:
            compressor = False

        # original object #
        try:
            if compressor:
                with compressor.open(path, "wb") as fd:
                    pickle.dump(obj, fd, -1)
            else:
                with open(path, "wb") as fd:
                    pickle.dump(obj, fd, -1)
            os.chmod(path, 0o777)
            return True
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn(
                "failed to save %s object to %s"
                % (obj.__class__.__name__, path),
                reason=True,
            )
            return False

    @staticmethod
    def loadObjectFromFile(path):
        # check object exists #
        if not os.path.isfile(path):
            return None

        # load pickle object #
        pickle = SysMgr.getPicklePkg(False)
        if not pickle:
            return None

        try:
            # try to load compressed cache #
            with SysMgr.getPkg("gzip", False).open(path, "rb") as fd:
                return pickle.load(fd)
        except SystemExit:
            sys.exit(0)
        except:
            try:
                with open(path, "rb") as fd:
                    return pickle.load(fd)
            except SystemExit:
                sys.exit(0)
            except:
                return None

    @staticmethod
    def printProgress(current=0, dest=0):
        if (
            not SysMgr.printEnable
            or not SysMgr.logEnable
            or dest == sys.maxsize
        ):
            return

        # just output #
        if not current and not dest:
            if UtilMgr.progressCnt >= len(UtilMgr.progressChar) - 1:
                UtilMgr.progressCnt = 0
            else:
                UtilMgr.progressCnt += 1

            mod = UtilMgr.progressCnt

            progressStr = ".... %s%s" % (UtilMgr.progressChar[mod], "\b" * 6)
        else:
            try:
                div = round((current / float(dest)) * 100, 1)
            except SystemExit:
                sys.exit(0)
            except:
                div = 0

            percent = long(div)

            mod = percent & 3

            progressStr = "%3d%% %s%s" % (
                percent,
                UtilMgr.progressChar[mod],
                "\b" * 6,
            )

        # handle reentrant call exception #
        try:
            if progressStr == UtilMgr.progressStr:
                return

            UtilMgr.progressStr = progressStr
            sys.stdout.write(progressStr)
            sys.stdout.flush()
        except SystemExit:
            sys.exit(0)
        except:
            return

    @staticmethod
    def writeFlamegraph(path, samples, title, depth=20):
        # flamegraph from https://github.com/rbspy/rbspy/tree/master/src/ui/flamegraph.rs #
        # fixed font size: 12, bar height: 15 #
        barHeight = 17
        titleHeight = (title.count("<tspan ") + 1) * barHeight
        height = barHeight * depth + 50 + titleHeight
        width = "1"
        flameCode = (
            (
                """<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" width="%s" height="%s" onload="init(evt)" viewBox="0 0 %s %s"
        """
                % (width, height, width, height)
            )
            + """
        xmlns="http://www.w3.org/2000/svg"
        xmlns:xlink="http://www.w3.org/1999/xlink">
        <!--Flame graph stack visualization. See https://github.com/brendangregg/FlameGraph for latest version, and http://www.brendangregg.com/flamegraphs.html for examples.-->
        <!--NOTES: -->
        <defs>
                <linearGradient id="background" y1="0" y2="1" x1="0" x2="0">
                        <stop stop-color="#eeeeee" offset="5%"/>
                        <stop stop-color="#eeeeb0" offset="95%"/>
                </linearGradient>
        </defs>
        <style type="text/css">
            text { font-family:"Verdana"; font-size:12px; fill:rgb(0,0,0); }
            #title { text-anchor:middle; font-size:17px; }
            #search { opacity:0.1; cursor:pointer; }
            #search:hover, #search.show { opacity:1; }
            #subtitle { text-anchor:left; font-color:rgb(160,160,160); }
            #unzoom { cursor:pointer; }
            #frames > *:hover { stroke:black; stroke-width:0.5; cursor:pointer; }
            .hide { display:none; }
            .parent { opacity:0.5; }
        </style>
        <script type="text/ecmascript">
                <![CDATA[var nametype = 'Function:';
var fontsize = 12;
var fontwidth = 0.59;
var xpad = 10;
var inverted = true;
var searchcolor = 'rgb(255,0,0)';
var searchstroke = 'rgb(0,0,0)';
var fluiddrawing = true;
var truncate_text_right = true;]]>
                <![CDATA["use strict";
var details, searchbtn, unzoombtn, matchedtxt, svg, searching, frames;
function init(evt) {
    details = document.getElementById("details").firstChild;
    searchbtn = document.getElementById("search");
    unzoombtn = document.getElementById("unzoom");
    matchedtxt = document.getElementById("matched");
    svg = document.getElementsByTagName("svg")[0];
    frames = document.getElementById("frames");
    searching = 0;

    // Use GET parameters to restore a flamegraph's state.
    var restore_state = function() {
        var params = get_params();
        if (params.x && params.y)
            zoom(find_group(document.querySelector('[x="' + params.x + '"][y="' + params.y + '"]')));
        if (params.s)
            search(params.s);
    };

    if (fluiddrawing) {
        // Make width dynamic so the SVG fits its parent's width.
        svg.removeAttribute("width");
        // Edge requires us to have a viewBox that gets updated with size changes
        var isEdge = /Edge\/\d./i.test(navigator.userAgent);
        if (!isEdge) {
          svg.removeAttribute("viewBox");
        }
        var update_for_width_change = function() {
            if (isEdge) {
                svg.attributes.viewBox.value = "0 0 " + svg.width.baseVal.value + " " + svg.height.baseVal.value;
            }

            // Keep consistent padding on left and right of frames container.
            frames.attributes.width.value = svg.width.baseVal.value - xpad * 2;

            // Text truncation needs to be adjusted for the current width.
            var el = frames.children;
            for(var i = 0; i < el.length; i++) {
                update_text(el[i]);
            }

            // Keep search elements at a fixed distance from right edge.
            var svgWidth = svg.width.baseVal.value;
            searchbtn.attributes.x.value = svgWidth - xpad - 100;
            matchedtxt.attributes.x.value = svgWidth - xpad - 100;
        };
        window.addEventListener('resize', function() {
            update_for_width_change();
        });
        // This needs to be done asynchronously for Safari to work.
        setTimeout(function() {
            unzoom();
            update_for_width_change();
            restore_state();
        }, 0);
    } else {
        restore_state();
    }
}
// event listeners
window.addEventListener("click", function(e) {
    var target = find_group(e.target);
    if (target) {
        if (target.nodeName == "a") {
            if (e.ctrlKey === false) return;
            e.preventDefault();
        }
        if (target.classList.contains("parent")) unzoom();
        zoom(target);

        // set parameters for zoom state
        var el = target.querySelector("rect");
        if (el && el.attributes && el.attributes.y && el.attributes._orig_x) {
            var params = get_params()
            params.x = el.attributes._orig_x.value;
            params.y = el.attributes.y.value;
            history.replaceState(null, null, parse_params(params));
        }
    }
    else if (e.target.id == "unzoom") {
        unzoom();

        // remove zoom state
        var params = get_params();
        if (params.x) delete params.x;
        if (params.y) delete params.y;
        history.replaceState(null, null, parse_params(params));
    }
    else if (e.target.id == "search") search_prompt();
}, false)
// mouse-over for info
// show
window.addEventListener("mouseover", function(e) {
    var target = find_group(e.target);
    if (target && details) {
        details.nodeValue = nametype + " " + g_to_text(target);
    }
}, false)
// clear
window.addEventListener("mouseout", function(e) {
    var target = find_group(e.target);
    if (target && details) {
        details.nodeValue = ' ';
    }
}, false)
// F3 / ctrl-F for search, ESC for reset search or zoom
window.addEventListener("keydown",function (e) {
    if (e.keyCode === 114 || (e.ctrlKey && e.keyCode === 70)) {
        e.preventDefault();
        search_prompt();
    } else if (e.keyCode === 27 || (e.ctrlKey && e.keyCode === 90)) {
        e.preventDefault();
        reset_search();
        unzoom()
    }
}, false)
// functions
function get_params() {
    var params = {};
    var paramsarr = window.location.search.substr(1).split('&');
    for (var i = 0; i < paramsarr.length; ++i) {
        var tmp = paramsarr[i].split("=");
        if (!tmp[0] || !tmp[1]) continue;
        params[tmp[0]] = decodeURIComponent(tmp[1]);
    }
    return params;
}
function parse_params(params) {
    var uri = "?";
    for (var key in params) {
        uri += key + '=' + encodeURIComponent(params[key]) + '&';
    }
    if (uri.slice(-1) == "&")
        uri = uri.substring(0, uri.length - 1);
    if (uri == '?')
        uri = window.location.href.split('?')[0];
    return uri;
}
function find_child(node, selector) {
    var children = node.querySelectorAll(selector);
    if (children.length) return children[0];
    return;
}
function find_group(node) {
    var parent = node.parentElement;
    if (!parent) return;
    if (parent.id == "frames") return node;
    return find_group(parent);
}
function orig_save(e, attr, val) {
    if (e.attributes["_orig_" + attr] != undefined) return;
    if (e.attributes[attr] == undefined) return;
    if (val == undefined) val = e.attributes[attr].value;
    e.setAttribute("_orig_" + attr, val);
}
function orig_load(e, attr) {
    if (e.attributes["_orig_"+attr] == undefined) return;
    e.attributes[attr].value = e.attributes["_orig_" + attr].value;
    e.removeAttribute("_orig_" + attr);
}
function g_to_text(e) {
    var text = find_child(e, "title").firstChild.nodeValue;
    return (text)
}
function g_to_func(e) {
    var func = g_to_text(e);
    // if there's any manipulation we want to do to the function
    // name before it's searched, do it here before returning.
    return (func);
}
function update_text(e) {
    var r = find_child(e, "rect");
    var t = find_child(e, "text");
    var w = parseFloat(r.attributes.width.value) * frames.attributes.width.value / 100 - 3;
    var txt = find_child(e, "title").textContent.replace(/&lt;/g,'<').replace(/&gt;/g,'>').replace(/&amp;/g,'&');
    t.attributes.x.value = format_percent((parseFloat(r.attributes.x.value) + (100 * 3 / frames.attributes.width.value)));
    // Smaller than this size won't fit anything
    if (w < 2 * fontsize * fontwidth) {
        t.textContent = "";
        return;
    }
    t.textContent = txt;
    // Fit in full text width
    if (/^ *\$/.test(txt) || t.getComputedTextLength() < w)
        return;
    if (truncate_text_right) {
        // Truncate the right side of the text.
        for (var x = txt.length - 2; x > 0; x--) {
            if (t.getSubStringLength(0, x + 2) <= w) {
                t.textContent = txt.substring(0, x) + "..";
                return;
            }
        }
    } else {
        // Truncate the left side of the text.
        for (var x = 2; x < txt.length; x++) {
            if (t.getSubStringLength(x - 2, txt.length) <= w) {
                t.textContent = ".." + txt.substring(x, txt.length);
                return;
            }
        }
    }
    t.textContent = "";
}
// zoom
function zoom_reset(e) {
    if (e.attributes != undefined) {
        orig_load(e, "x");
        orig_load(e, "width");
    }
    if (e.childNodes == undefined) return;
    for(var i = 0, c = e.childNodes; i < c.length; i++) {
        zoom_reset(c[i]);
    }
}
function zoom_child(e, x, ratio) {
    if (e.attributes != undefined) {
        if (e.attributes.x != undefined) {
            orig_save(e, "x");
            e.attributes.x.value = format_percent((parseFloat(e.attributes.x.value) - x) * ratio);
            if (e.tagName == "text") {
                e.attributes.x.value = format_percent(parseFloat(find_child(e.parentNode, "rect[x]").attributes.x.value) + (100 * 3 / frames.attributes.width.value));
            }
        }
        if (e.attributes.width != undefined) {
            orig_save(e, "width");
            e.attributes.width.value = format_percent(parseFloat(e.attributes.width.value) * ratio);
        }
    }
    if (e.childNodes == undefined) return;
    for(var i = 0, c = e.childNodes; i < c.length; i++) {
        zoom_child(c[i], x, ratio);
    }
}
function zoom_parent(e) {
    if (e.attributes) {
        if (e.attributes.x != undefined) {
            orig_save(e, "x");
            e.attributes.x.value = "0.0%";
        }
        if (e.attributes.width != undefined) {
            orig_save(e, "width");
            e.attributes.width.value = "100.0%";
        }
    }
    if (e.childNodes == undefined) return;
    for(var i = 0, c = e.childNodes; i < c.length; i++) {
        zoom_parent(c[i]);
    }
}
function zoom(node) {
    var attr = find_child(node, "rect").attributes;
    var width = parseFloat(attr.width.value);
    var xmin = parseFloat(attr.x.value);
    var xmax = xmin + width;
    var ymin = parseFloat(attr.y.value);
    var ratio = 100 / width;
    // XXX: Workaround for JavaScript float issues (fix me)
    var fudge = 0.001;
    unzoombtn.classList.remove("hide");
    var el = frames.children;
    for (var i = 0; i < el.length; i++) {
        var e = el[i];
        var a = find_child(e, "rect").attributes;
        var ex = parseFloat(a.x.value);
        var ew = parseFloat(a.width.value);
        // Is it an ancestor
        if (!inverted) {
            var upstack = parseFloat(a.y.value) > ymin;
        } else {
            var upstack = parseFloat(a.y.value) < ymin;
        }
        if (upstack) {
            // Direct ancestor
            if (ex <= xmin && (ex+ew+fudge) >= xmax) {
                e.classList.add("parent");
                zoom_parent(e);
                update_text(e);
            }
            // not in current path
            else
                e.classList.add("hide");
        }
        // Children maybe
        else {
            // no common path
            if (ex < xmin || ex + fudge >= xmax) {
                e.classList.add("hide");
            }
            else {
                zoom_child(e, xmin, ratio);
                update_text(e);
            }
        }
    }
}
function unzoom() {
    unzoombtn.classList.add("hide");
    var el = frames.children;
    for(var i = 0; i < el.length; i++) {
        el[i].classList.remove("parent");
        el[i].classList.remove("hide");
        zoom_reset(el[i]);
        update_text(el[i]);
    }
}
// search
function reset_search() {
    var el = document.querySelectorAll("#frames rect");
    for (var i = 0; i < el.length; i++) {
        orig_load(el[i], "fill")
        el[i].setAttribute("stroke-width", "0")
    }
    var params = get_params();
    delete params.s;
    history.replaceState(null, null, parse_params(params));
}
function search_prompt() {
    if (!searching) {
        var term = prompt("Enter a search term (regexp " +
            "allowed, eg: ^ext4_)", "");
        if (term != null) {
            search(term)
        }
    } else {
        reset_search();
        searching = 0;
        searchbtn.classList.remove("show");
        if (searchbtn.firstChild) {
            searchbtn.firstChild.nodeValue = "Search (F3)"
        }
        matchedtxt.classList.add("hide");
        if (matchedtxt.firstChild) {
            matchedtxt.firstChild.nodeValue = ""
        }
    }
}
function search(term) {
    var re = new RegExp(term);
    var el = frames.children;
    var matches = new Object();
    var maxwidth = 0;
    for (var i = 0; i < el.length; i++) {
        var e = el[i];
        var func = g_to_func(e);
        var rect = find_child(e, "rect");
        if (func == null || rect == null)
            continue;
        // Save max width. Only works as we have a root frame
        var w = parseFloat(rect.attributes.width.value);
        if (w > maxwidth)
            maxwidth = w;
        if (func.match(re)) {
            // highlight
            var x = parseFloat(rect.attributes.x.value);
            orig_save(rect, "fill");
            rect.setAttribute("fill", searchcolor);
            rect.setAttribute("stroke", searchstroke);
            rect.setAttribute("stroke-width", "3");
            // remember matches
            if (matches[x] == undefined) {
                matches[x] = w;
            } else {
                if (w > matches[x]) {
                    // overwrite with parent
                    matches[x] = w;
                }
            }
            searching = 1;
        }
    }
    if (!searching)
        return;
    var params = get_params();
    params.s = term;
    history.replaceState(null, null, parse_params(params));

    searchbtn.classList.add("show");
    if (searchbtn.firstChild) {
        searchbtn.firstChild.nodeValue = "Reset Search (F3)";
    }
    // calculate percent matched, excluding vertical overlap
    var count = 0;
    var lastx = -1;
    var lastw = 0;
    var keys = Array();
    for (k in matches) {
        if (matches.hasOwnProperty(k))
            keys.push(k);
    }
    // sort the matched frames by their x location
    // ascending, then width descending
    keys.sort(function(a, b){
        return a - b;
    });
    // Step through frames saving only the biggest bottom-up frames
    // thanks to the sort order. This relies on the tree property
    // where children are always smaller than their parents.
    var fudge = 0.0001;    // JavaScript floating point
    for (var k in keys) {
        var x = parseFloat(keys[k]);
        var w = matches[keys[k]];
        if (x >= lastx + lastw - fudge) {
            count += w;
            lastx = x;
            lastw = w;
        }
    }
    // display matched percent
    matchedtxt.classList.remove("hide");
    var pct = 100 * count / maxwidth;
    if (pct != 100) pct = pct.toFixed(1);
    if (matchedtxt.firstChild) {
        matchedtxt.firstChild.nodeValue = "Matched: " + pct + "%";
    }
}
function format_percent(n) {
    return n.toFixed(4) + "%";
}
]]>
        </script>
"""
        )
        attrCode = """
    <rect x="0" y="0" width="100%%" height="%s" fill="url(#background)"/>
    <text id="title" x="50.0000%%" y="24.00">Guider Flame Graph</text>
    <text id="subtitle" x="0.0000%%" y="50.00">%s</text>
    <text id="details" x="10" y="213.00"></text>
    <text id="unzoom" class="hide" x="10" y="24.00">Reset Zoom (ESC)</text>
    <text id="search" x="1090" y="24.00">Search (F3)</text>
    <text id="matched" x="1090" y="213.00"></text>
    <svg id="frames" x="10" y="%s" width="%s">
""" % (
            height,
            "\r\n%s" % title if title else "",
            titleHeight,
            width,
        )

        # complete code for flamegraph #
        finalCode = flameCode + attrCode + samples + "\n</svg></svg>"

        # write flamegraph to file #
        try:
            with open(path, "w") as fd:
                fd.write(finalCode)

            os.chmod(path, 0o777)

            # get output size #
            fsize = UtilMgr.getFileSizeStr(path)

            SysMgr.printInfo(
                "saved a flame graph into '%s'%s successfully" % (path, fsize)
            )
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to write a flame graph to %s" % path, True)
            sys.exit(-1)

    @staticmethod
    def getFileSizeStr(path, string=True):
        fsize = UtilMgr.getFileSize(path)
        if fsize and fsize != "0":
            fsize = " [%s]" % fsize
        else:
            fsize = ""
        return fsize

    @staticmethod
    def getFileSize(path, string=True):
        try:
            fsize = long(os.path.getsize(path))
            if string:
                return UtilMgr.convSize2Unit(fsize)
            else:
                return fsize
        except SystemExit:
            sys.exit(0)
        except:
            if UtilMgr.isString(path) and os.path.exists(path):
                SysMgr.printWarn("failed to get file size for '%s'" % path)

            if string:
                return "?"
            else:
                return 0

    @staticmethod
    def printFile(path):
        try:
            with open(path, "r") as fd:
                for line in fd:
                    print(line.rstrip())
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to print %s" % path, reason=True)

    @staticmethod
    def deleteProgress():
        if not SysMgr.printEnable or not SysMgr.logEnable:
            return

        sys.stdout.write(" " * 6)
        sys.stdout.write("\b" * 6)

        # handle reentrant call exception #
        try:
            sys.stdout.flush()
        except SystemExit:
            sys.exit(0)
        except:
            return

    @staticmethod
    def which(cmd):
        if not "PATH" in os.environ:
            return None

        pathList = []
        for path in os.environ["PATH"].split(os.pathsep):
            if os.path.exists(os.path.join(path, cmd)):
                pathList.append(os.path.join(path, cmd))

        if pathList:
            return pathList
        else:
            return None

    @staticmethod
    def convDict2Str(dictObj, pretty=True, indent=2, ignore=False):
        try:
            jsonStr = SysMgr.getPkg("json").dumps(
                dictObj,
                indent=indent,
                ensure_ascii=False,
                default=(lambda o: "<not serializable>") if ignore else None,
            )

            if pretty:
                return jsonStr
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn("failed to convert dict to string", reason=True)

            """
            # for debugging #
            SysMgr.printWarn(
                "failed to convert %s to string" % [dictObj], reason=True)
            """

            return None

        # when encode flag is disabled, remove whitespace [\t\n\r\f\v] #
        if not SysMgr.encodeEnable:
            jsonStr = re.sub("[\t\n\r\f\v]", "", jsonStr) + "\n"

        return jsonStr

    @staticmethod
    def convUlong2Long(retval):
        retval = retval & 0xFFFFFFFFFFFFFFFF
        if retval & 0x8000000000000000:
            retval = retval - 0x10000000000000000
        return retval

    @staticmethod
    def convStr2Dict(strObj, verb=False):
        try:
            return SysMgr.getPkg("json").loads(strObj)
        except SystemExit:
            sys.exit(0)
        except:
            try:
                strObj = strObj.replace("'", '"')
                return SysMgr.getPkg("json").loads(strObj)
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printWarn(
                    "failed to convert %s to dict" % [strObj],
                    always=verb,
                    reason=True,
                )
                return None


class NetworkMgr(object):
    """Manager for remote communication"""

    # define valid request list #
    REQUEST_LIST = {
        "BROADCAST": None,
        "CLEAR": None,
        "DIR": None,
        "DOWNLOAD": None,
        "JOBS": None,
        "LIST": None,
        "NEW": None,
        "NOTIFY": None,
        "PING": None,
        "RESTART": None,
        "RUN": None,
        "UPLOAD": None,
        "UPSTREAM": None,
    }

    def __init__(
        self,
        mode,
        ip,
        port,
        tcp=False,
        uds=False,
        netlink=False,
        blocking=True,
        anyPort=False,
        bind=True,
        reuse=True,
    ):
        self.mode = mode
        self.ip = None
        self.port = None
        self.socket = None
        self.request = None
        self.status = None
        self.ignore = 0
        self.fileno = -1
        self.time = None
        self.sendSize = 32767
        self.recvSize = 32767
        self.tcp = tcp
        self.netlink = netlink
        self.connected = False

        # get socket object #
        socket = SysMgr.getPkg("socket")

        try:
            from socket import (
                socket,
                AF_INET,
                SOCK_DGRAM,
                SOCK_STREAM,
                SOL_SOCKET,
                SO_REUSEADDR,
                SO_SNDBUF,
                SO_RCVBUF,
                SOL_TCP,
                SO_RCVTIMEO,
                SO_SNDTIMEO,
                SOCK_RAW,
            )
        except:
            SysMgr.printWarn("failed to import socket", True, reason=True)
            return None

        try:
            # set socket type #
            if tcp:
                self.socket = socket(AF_INET, SOCK_STREAM)
            elif uds:
                # pylint: disable-next=no-name-in-module
                from socket import AF_UNIX

                self.socket = socket(AF_UNIX, SOCK_STREAM)
            elif netlink:
                try:
                    # pylint: disable-next=no-name-in-module
                    from socket import AF_NETLINK

                    self.socket = socket(
                        AF_NETLINK,
                        SOCK_RAW,
                        ConfigMgr.NETLINK_TYPE["NETLINK_GENERIC"],
                    )
                except:
                    SysMgr.printWarn(
                        "failed to create NETLINK socket", True, reason=True
                    )
                    return None
            else:
                self.socket = socket(AF_INET, SOCK_DGRAM)

            self.fileno = self.socket.fileno()

            # increase socket buffer size to 1MB #
            self.socket.setsockopt(SOL_SOCKET, SO_SNDBUF, 1 << 20)
            self.socket.setsockopt(SOL_SOCKET, SO_RCVBUF, 1 << 20)

            # get buffer size #
            self.sendSize = self.socket.getsockopt(SOL_SOCKET, SO_SNDBUF)
            self.recvSize = self.socket.getsockopt(SOL_SOCKET, SO_RCVBUF)

            # set REUSEADDR flag #
            if reuse:
                self.socket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)

            # set REUSEPORT #
            """
            from socket import SO_REUSEPORT
            self.socket.setsockopt(SOL_SOCKET, SO_REUSEPORT, 0)
            """

            # set NODELAY #
            """
            self.setNoDelay()
            """

            # set SENDTIMEOUT #
            """
            sec = 1
            usec = 0
            timeval = struct.pack('ll', sec, usec)
            self.socket.setsockopt(SOL_SOCKET, SO_SNDTIMEO, timeval)
            """

            if uds:
                return None

            # convert IP #
            if ip == "*":
                ip = "0.0.0.0"
            elif str(ip).upper() == "LOCALHOST":
                ip = "127.0.0.1"

            # set IP & PORT #
            self.ip = ip
            self.port = port

            if mode == "server":
                # IP #
                if ip is None:
                    self.ip = "0.0.0.0"

                # PORT #
                if anyPort:
                    self.port = 0
                elif port is None:
                    self.port = SysMgr.defaultServPort

                # bind #
                if bind:
                    try:
                        self.socket.bind((self.ip, self.port))
                    except OSError as e:
                        if e.errno == errno.EADDRINUSE:
                            self.port = 0
                            self.socket.bind((self.ip, self.port))
                        else:
                            raise e
                    except SystemExit:
                        sys.exit(0)
                    except:
                        self.socket.bind((self.ip, self.port))

                    # get bind port #
                    self.port = self.socket.getsockname()[1]

            if not blocking:
                self.socket.setblocking(0)
        except:
            err = SysMgr.getErrMsg()
            if err.startswith("13") and not SysMgr.isRoot() and port < 1024:
                feedback = ", use port bigger than 1024"
            else:
                feedback = ""

            # check mode to print error message #
            if (
                SysMgr.warnEnable
                or SysMgr.checkMode("server")
                or SysMgr.checkMode("cli")
            ):
                SysMgr.printErr(
                    "failed to create a socket for %s:%s as server because %s%s"
                    % (self.ip, self.port, err, feedback)
                )

            """
            if error "99 Cannot assign requested address" occurs:
                add "net.ipv4.ip_nonlocal_bind = 1" in /etc/sysctl.conf
                execute sysctl -p /etc/sysctl.conf
            """

            self.ip = None
            self.port = None

            return None

    def __str__(self):
        return "%s object at %x, IP: %s, PORT: %s" % (
            self.__class__,
            id(self),
            self.ip,
            self.port,
        )

    def listen(self, nrQueue=5):
        return self.socket.listen(nrQueue)

    def accept(self):
        return self.socket.accept()

    def bind(self, ip, port):
        return self.socket.bind((ip, port))

    def write(self, message):
        return self.send(message, write=True)

    def close(self):
        if self.socket:
            ret = self.socket.close()
        else:
            ret = False

        self.socket = None

        return ret

    def flush(self):
        pass

    def timeout(self, sec=3):
        if "TIMEOUT" in SysMgr.environList:
            sec = UtilMgr.getEnvironNum("TIMEOUT")

        self.socket.settimeout(sec)

    @staticmethod
    def sendFile(sock, ip, port, src, des):
        # verify path #
        target = src
        sdir, name = UtilMgr.getPath(target)

        # get file list #
        targetList = UtilMgr.getFiles(sdir, [name])
        if not targetList:
            SysMgr.printWarn("failed to find %s to transfer" % target, True)

            return False

        for target in targetList:
            target = SysMgr.convFullPath(target)

            SysMgr.printInfo(
                "start uploading %s[%s] to %s:%s... "
                % (target, UtilMgr.getFileSize(target), sock.ip, sock.port),
                suffix=False,
            )

            # send file info #
            totalSize = os.stat(target).st_size
            fileInfo = "%s#%s" % (target, totalSize)
            sock.send(UtilMgr.encodeStr(fileInfo))

            # read for ACK #
            while 1:
                ret = sock.recv(3)
                if ret is None:
                    continue
                elif ret is False:
                    raise Exception("no response")
                else:
                    break

            # send a file #
            with open(target, "rb") as fd:
                buf = fd.read(sock.sendSize)
                curSize = 0
                while buf:
                    # send a chunk #
                    sock.send(buf)
                    curSize += len(buf)

                    # read for ACK #
                    while 1:
                        ret = sock.recv(3)
                        if ret is None:
                            continue
                        elif ret is False:
                            raise Exception("no response")
                        else:
                            break

                    # read a chunk #
                    buf = fd.read(sock.sendSize)

            # read for ACK #
            while 1:
                ret = sock.recv(3)
                if ret is None:
                    continue
                elif ret is False:
                    raise Exception("no response")
                else:
                    break

            SysMgr.printInfo(
                "uploaded %s[%s] to %s:%s successfully"
                % (target, UtilMgr.getFileSize(target), sock.ip, sock.port)
            )

        return True

    @staticmethod
    def recvData(sockObj, ip, port=0):
        connMan = NetworkMgr("server", ip, port, tcp=True, bind=True)
        if not connMan.ip:
            return

        # send connection info #
        try:
            sockObj.send(("%s:%s" % (connMan.ip, connMan.port)).encode())
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                "failed to send connection info to %s:%s"
                % (sockObj.ip, sockObj.port),
                True,
            )
            return

        # listen #
        try:
            connMan.listen()
            connMan.timeout()
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to listen to prepare for connection", True)
            sys.exit(-1)

        # accept #
        try:
            sock, addr = connMan.accept()
        except SystemExit:
            sys.exit(0)
        except IOError as err:
            if err.errno != errno.EINTR:
                SysMgr.printWarn(
                    "failed to accept the connection request", reason=True
                )
                return
        except SysMgr.getPkg("socket").timeout:
            return
        except:
            SysMgr.printWarn(
                "failed to accept the connection request", reason=True
            )
            return

        SysMgr.printInfo(
            "connected to the new client (%s:%s)" % (addr[0], addr[1])
        )

        try:
            # get payload size #
            size = sock.recv(SysMgr.PAGESIZE)
            size = long(size.decode())

            # send ACK #
            sock.send("ACK".encode())
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                "failed to get payload size from %s:%s" % (addr[0], addr[1]),
                True,
            )
            return

        # receive data #
        try:
            buf = b""
            while 1:
                data = sock.recv(SysMgr.PAGESIZE)
                if data:
                    buf += data
                else:
                    break
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                "failed to receive data from %s:%s" % (addr[0], addr[1]), True
            )
            return

        return buf

    @staticmethod
    def recvFile(sock, ip, port, src, des):
        # get select object #
        selectObj = SysMgr.getPkg("select")

        while 1:
            curSize = 0
            totalSize = None

            # receive file info #
            while 1:
                fileInfo = sock.recv(sock.recvSize)
                if not fileInfo:
                    continue

                # check termination condition #
                name, size = fileInfo.decode().split("#", 1)
                if name == "@DONE":
                    return True
                elif not size:
                    continue

                # send ACK for file info #
                totalSize = long(size)
                sock.send("ACK".encode())
                break

            # set destination path #
            fname = os.path.basename(name)
            if des:
                if os.path.isdir(des):
                    target = os.path.join(des, fname)
                else:
                    target = des
            else:
                target = name

            # make dirs #
            dirPos = target.rfind("/")
            if dirPos >= 0:
                SysMgr.mkdirs(target[:dirPos])

            # convert path #
            target = SysMgr.convFullPath(target)
            if os.path.isdir(target):
                target = os.path.join(target, fname)

            SysMgr.printInfo(
                "start downloading %s[%s]@%s:%s to %s..."
                % (name, UtilMgr.convSize2Unit(totalSize), ip, port, target),
                suffix=False,
            )

            # receive a file #
            with open(target, "wb") as fd:
                # change permission #
                os.chmod(target, 0o777)

                sent = 0
                while 1:
                    selectObj.select([sock.socket], [], [], 3)

                    # receive a chunk #
                    buf = sock.recv(sock.recvSize)
                    if not buf:
                        break

                    # update stat and write a chunk to file #
                    curSize += len(buf)
                    sent += len(buf)
                    fd.write(buf)

                    # check received size #
                    if curSize >= totalSize:
                        sock.send("ACK".encode())
                        break

                    # send ACK for a complete chunk #
                    if sent == sock.recvSize:
                        sock.send("ACK".encode())
                        sent = 0

                    # print progress #
                    UtilMgr.printProgress(curSize, totalSize)

            # remove progress #
            UtilMgr.deleteProgress()

            # check received size #
            if curSize < totalSize:
                raise Exception("broken connection")

            SysMgr.printInfo(
                "downloaded %s[%s]@%s:%s to %s successfully\n"
                % (target, UtilMgr.getFileSize(target), ip, port, name),
                suffix=False,
            )

            # send ACK #
            sock.send("ACK".encode())

        return True

    def customBind(self):
        # get bind info #
        ipList = portList = []
        if "CLIIP" in SysMgr.environList:
            ipList = SysMgr.environList["CLIIP"]
        if "CLIPORT" in SysMgr.environList:
            portList = SysMgr.environList["CLIPORT"]

        # check bind address #
        if not ipList and not portList:
            return True

        # set default IP #
        if not ipList:
            ipList = ["0"]

        # convert PORT list #
        if portList:
            newPortList = []
            for item in portList:
                if not "-" in item:
                    newPortList.append(long(item))
                    continue

                try:
                    start, end = item.split("-")
                    if end == "":
                        end = 65535
                    for idx in xrange(long(start), long(end) + 1):
                        newPortList.append(idx)
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr("failed to bind client socket", True)
                    return False

            portList = list(set(newPortList))
        else:
            portList = [0]

        # disable REUSEADDR flag #
        socket = SysMgr.getPkg("socket")
        self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 0)

        # bind #
        isBound = False
        for ip in ipList:
            for port in portList:
                try:
                    self.bind(ip, port)
                    SysMgr.printWarn(
                        "succeed to bind client socket to %s:%s" % (ip, port)
                    )
                    isBound = True
                    break
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printWarn(
                        "failed to bind client socket to %s:%s" % (ip, port),
                        reason=True,
                    )

        # check result #
        if isBound:
            return True
        else:
            SysMgr.printErr("failed to bind local address for client socket")
            return False

    def connect(self, addr=None):
        if addr is None:
            addr = (self.ip, self.port)

        # bind to specific address #
        if not self.customBind():
            raise Exception("bind failure")

        self.socket.connect(addr)

        self.connected = True

    def handleServerRequest(self, req, onlySocket=False):
        def _onDownload(req):
            # parse path #
            reqList = req.split("|", 1)[1]
            path = UtilMgr.cleanItem(reqList.split("@"), False)
            if len(path) == 2:
                origPath, desPath = path
                if origPath and not desPath:
                    origPath = desPath
            else:
                origPath = path[0]
                desPath = None

            res = False

            # receive file #
            try:
                res = NetworkMgr.recvFile(
                    self, self.ip, self.port, origPath, desPath
                )
            except:
                res = False
                SysMgr.printErr(
                    "failed to download %s from %s in %s:%s"
                    % (origPath, desPath, self.ip, self.port),
                    True,
                )
            finally:
                try:
                    self.close()
                except:
                    pass
                finally:
                    return res

        def _onUpstream(req):
            pass

        def _onDir(req):
            SysMgr.printInfo(UtilMgr.lstrip(req, "DIR:"))
            return True

        def _onNotify(req):
            return True

        def _onUpload(req):
            # parse path #
            reqList = req.split("|", 1)[1]
            path = UtilMgr.cleanItem(reqList.split("@"), False)
            if len(path) == 2:
                origPath, targetPath = path
                if origPath and not targetPath:
                    origPath = targetPath
            else:
                origPath = targetPath = path[0]

            res = False

            # transfer file #
            try:
                res = NetworkMgr.sendFile(
                    self, self.ip, self.port, origPath, targetPath
                )
            except:
                res = False
                SysMgr.printErr(
                    "failed to upload %s to %s in %s:%s"
                    % (origPath, targetPath, self.ip, self.port),
                    True,
                )
            finally:
                try:
                    self.send("@DONE#0".encode())
                    self.close()
                except:
                    pass
                finally:
                    return res

        def _onList(req):
            SysMgr.printInfo(UtilMgr.lstrip(req, "LIST:"))
            return True

        def _onClear(req):
            SysMgr.printInfo(UtilMgr.lstrip(req, "CLEAR:"))
            return True

        def _onJobs(req):
            SysMgr.printInfo(UtilMgr.lstrip(req, "JOBS:"))
            return True

        def _onRun(req, onlySocket):
            # parse command #
            command = req.split("|", 1)[1]

            # parse addr #
            addr = "%s:%s" % (self.ip, self.port)

            if not onlySocket:
                SysMgr.printInfo("executed '%s' from %s\n" % (command, addr))

            # return just the connected socket #
            if onlySocket:
                return self

            # get select object #
            selectObj = SysMgr.getPkg("select")

            # set print flag #
            printFlag = SysMgr.getPrintFlag()

            if printFlag:
                print(oneLine)

            # run mainloop #
            isPrint = False
            while 1:
                try:
                    selectObj.select([self.socket], [], [])

                    # receive packet #
                    output = self.getData()
                    if not output:
                        break

                    if printFlag:
                        print(output[:-1])
                    isPrint = True
                except:
                    break

            # print output from server #
            if not isPrint:
                print("no response")

            if printFlag:
                print(oneLine)

            # close connection #
            try:
                self.close()
            except:
                pass

        # get select object to check reply #
        SysMgr.getPkg("select")

        # unmarshalling #
        if isinstance(req, tuple):
            try:
                req = req[0].decode()
            except:
                req = req[0]

            # check request #
            if not req:
                return

            # handle request #
            reqUpper = req.upper()

            if reqUpper.startswith("DOWNLOAD"):
                return _onDownload(req)

            elif reqUpper.startswith("UPLOAD"):
                return _onUpload(req)

            elif reqUpper.startswith("DIR"):
                return _onDir(req)

            elif reqUpper.startswith("RUN"):
                return _onRun(req, onlySocket)

            elif reqUpper.startswith("LIST:"):
                return _onList(req)

            elif reqUpper.startswith("CLEAR"):
                return _onClear(req)

            elif reqUpper.startswith("JOBS"):
                return _onJobs(req)

            elif reqUpper.startswith("NOTIFY"):
                return _onNotify(req)

            elif reqUpper.startswith("ERROR"):
                err = req.split("|", 1)[1]
                errToken = err.find("':")
                errMsg = "%s' from %s" % (err[:errToken], err[errToken + 2 :])
                SysMgr.printErr(errMsg)

            else:
                SysMgr.printErr("failed to recognize '%s' request" % req)

        elif not req:
            SysMgr.printErr("no response from server")

        else:
            SysMgr.printErr("received wrong reply '%s'" % req)

    def send(self, message, write=False):
        if None in (self.ip, self.port):
            SysMgr.printWarn(
                "failed to use IP address for client because it is not set"
            )
            return False
        elif not self.socket:
            SysMgr.printErr(
                "failed to use socket for client because it is not set"
            )
            return False

        # encode message #
        if UtilMgr.isString(message):
            message = UtilMgr.encodeStr(message)

        # convert list to bytes #
        if isinstance(message, list):
            if not message:
                message = "".encode()
            elif message[0][-1] == "\n":
                message = "".join(message).encode()
            else:
                message = "\n".join(message).encode()

        try:
            # send data #
            if self.tcp or self.netlink:
                ret = self.socket.send(message)
            elif not write and SysMgr.localServObj:
                ret = SysMgr.localServObj.socket.sendto(
                    message, (self.ip, self.port)
                )
            else:
                ret = self.socket.sendto(message, (self.ip, self.port))

            # check result #
            if ret < 0:
                raise Exception("send error")
            elif self.status != "ALWAYS":
                self.status = "SENT"
            return True
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                "failed to send data to %s:%d as server"
                % (self.ip, self.port),
                True,
            )
            return False

    def sendto(self, message, ip, port):
        if not ip or not port:
            SysMgr.printWarn(
                "failed to use IP address for client because it is not set"
            )
            return False
        elif not self.socket:
            SysMgr.printErr(
                "failed to use socket for client because it is not set"
            )
            return False

        # encode message #
        if UtilMgr.isString(message):
            message = UtilMgr.encodeStr(message)

        try:
            self.socket.sendto(message, (ip, port))
            return True
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                "failed to send data to %s:%d as client"
                % (self.ip, self.port),
                True,
            )
            return False

    def recv(self, size=0):
        if None in (self.ip, self.port):
            SysMgr.printWarn(
                "failed to use IP address for server because it is not set"
            )
            return False
        elif not self.socket:
            SysMgr.printErr(
                "failed to use socket for client because it is not set"
            )
            return False

        # set recv size #
        if size == 0:
            size = self.recvSize

        try:
            return self.socket.recv(size)
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn(
                "failed to receive data from %s:%d as client"
                % (self.ip, self.port),
                reason=True,
            )
            return False

    def getData(self, noTimeout=True):
        try:
            data = b""

            # receive and composite packets #
            while 1:
                output = self.recvfrom(noTimeout=noTimeout)

                # handle error #
                if not output:
                    continue

                # handle timeout #
                if not noTimeout and (not output[0] and not output[1]):
                    if data:
                        return data
                    else:
                        return None

                # get only data #
                output = output[0]

                # composite packets #
                data += output

                if not output:
                    break

                # decode data #
                try:
                    output = output.decode()
                except:
                    pass

                if len(output) < self.recvSize and output[-1] == "\n":
                    break
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                "failed to get data from %s:%d as client"
                % (self.ip, self.port),
                True,
            )
            return None

        # decode data #
        try:
            retstr = data.decode()
            return retstr
        except:
            return data

    def setNoDelay(self):
        from socket import socket, SOL_TCP, TCP_NODELAY

        # set NODELAY for NAGLE #
        self.socket.setsockopt(SOL_TCP, TCP_NODELAY, 1)

    def recvfrom(self, size=0, noTimeout=False, verb=True):
        if None in (self.ip, self.port):
            SysMgr.printWarn(
                "failed to use IP address for server because it is not set"
            )
            return False
        elif not self.socket:
            SysMgr.printErr(
                "failed to use socket for client because it is not set"
            )
            return False

        # get socket object #
        socket = SysMgr.getPkg("socket", False)
        if not socket:
            return

        # set recv size #
        if size == 0:
            size = self.recvSize

        while 1:
            try:
                message, address = self.socket.recvfrom(size)
                return (message, address)
            except socket.timeout:
                if noTimeout:
                    continue

                SysMgr.printWarn(
                    "failed to receive data from %s:%d as client because of %s"
                    % (self.ip, self.port, "timeout")
                )
                return None
            except KeyboardInterrupt:
                sys.exit(0)
            except SystemExit:
                sys.exit(0)
            except:
                if verb:
                    SysMgr.printWarn(
                        "failed to receive data from %s:%d as client"
                        % (self.ip, self.port),
                        reason=True,
                    )
                return None

    @staticmethod
    def getDataType(data):
        if not data:
            return "None"

        data = data.lstrip()

        if data.startswith("{"):
            return "JSON"
        elif (
            "[INFO" in data[:10]
            or "[ERROR" in data[:10]
            or "[WARN" in data[:10]
            or "[STEP" in data[:10]
        ):
            return "LOG"
        else:
            return "CONSOLE"

    @staticmethod
    def requestCmd(connObj, cmd):
        if not connObj:
            return

        # send request to server #
        connObj.send(cmd)

        # receive reply from server #
        reply = connObj.recvfrom()

        # handle reply from server #
        try:
            connObj.handleServerRequest(reply)
        except:
            return

    @staticmethod
    def requestPing(addr=None, verb=True, cmd=None):
        if cmd:
            cmd = cmd.upper()
            cmd = "%s:%s" % (cmd, cmd)
        else:
            cmd = "PING:PING"

        return NetworkMgr.execRemoteCmd(cmd, addr=addr, verb=verb)

    @staticmethod
    def getCmdPipe(connObj, cmd):
        if not cmd:
            return None

        # add command prefix #
        reqCmd = cmd.split(":", 1)[0].upper()
        if reqCmd in NetworkMgr.REQUEST_LIST:
            pass
        elif not cmd.startswith("run:"):
            cmd = "run:%s" % cmd

        # send request to server #
        connObj.send(cmd)

        # receive reply from server #
        reply = connObj.recvfrom()
        try:
            if not reply:
                raise Exception()

            msg = reply[0].decode()
            if not msg:
                pass
            elif msg == "PONG":
                return True
            elif msg == "NO_SERV_NODE":
                SysMgr.printErr(
                    "no service node to %s:%s" % (connObj.ip, connObj.port)
                )
                return True
            elif msg.startswith("MSG:"):
                # print message in the packet #
                SysMgr.printInfo(UtilMgr.lstrip(msg, "MSG:"))

                # send ACK to prevent receiving two packegs at once #
                connObj.send("ACK")

                # wait for a request again #
                reply = connObj.recvfrom()
            elif msg.startswith("LIST:"):
                reply = (msg,)
            elif msg.startswith("NOTIFY:"):
                return True
        except:
            pass

        # handle reply from server #
        try:
            return connObj.handleServerRequest(reply, onlySocket=True)
        except:
            return None

    @staticmethod
    def execRemoteCmd(command, addr=None, verb=True):
        # get new connection #
        connObj = NetworkMgr.getServerConn(addr, verb)
        if not connObj:
            return None

        # launch remote command #
        pipe = NetworkMgr.getCmdPipe(connObj, command)
        return pipe

    @staticmethod
    def getServerConn(addr=None, verb=True):
        def _printErr():
            SysMgr.printErr("no running server or wrong server address")

        # set server address in local #
        if addr:
            # classify IP and PORT #
            service, ip, port = NetworkMgr.parseAddr(addr)
            if service == ip == port == None:
                _printErr()
                return None
            else:
                NetworkMgr.setRemoteServer("%s:%s" % (ip, port), tcp=True)
        elif SysMgr.isLinux and not SysMgr.remoteServObj:
            try:
                addr = SysMgr.getProcNetAddrs(__module__)
            except:
                addr = None

            if not addr:
                return None

            # classify IP and PORT #
            service, ip, port = NetworkMgr.parseAddr(addr)
            if service == ip == port == None:
                _printErr()
                return None
            else:
                NetworkMgr.setRemoteServer(addr, tcp=True)
        # set server address again #
        elif SysMgr.remoteServObj:
            servObj = SysMgr.remoteServObj
            ip = servObj.ip
            port = servObj.port
            NetworkMgr.setRemoteServer("%s:%s" % (ip, port), tcp=True)

        # check server address #
        if not SysMgr.remoteServObj:
            _printErr()
            return None

        # bind local socket for UDP #
        try:
            if not SysMgr.remoteServObj.tcp and SysMgr.localServObj:
                lip = SysMgr.localServObj.ip
                lport = SysMgr.localServObj.port
                SysMgr.remoteServObj.socket.bind((lip, lport))
        except:
            SysMgr.printErr(
                "failed to bind socket to %s:%s for connection" % (lip, lport),
                True,
            )

        # do connect to server #
        try:
            connObj = SysMgr.remoteServObj

            if not "NOTIMEOUT" in SysMgr.environList:
                connObj.timeout()

            # set immediately exit flag to prevent hang on connect #
            origFlag = SysMgr.exitFlag
            SysMgr.exitFlag = True

            # connect with handling CLOSE_WAIT #
            while 1:
                try:
                    connObj.connect()
                    break
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printWarn(
                        "failed to connect to %s:%s" % (ip, port),
                        reason=True,
                        always=verb,
                    )

                    # handle error #
                    _, err, to = sys.exc_info()
                    if err.args and err.args[0] == 99:
                        time.sleep(0.1)
                        continue
                    else:
                        raise Exception(err.args[0])
                finally:
                    SysMgr.exitFlag = origFlag

            return connObj
        except SystemExit:
            sys.exit(0)
        except:
            if verb:
                SysMgr.printErr("failed to set socket for connection", True)
            return None

    @staticmethod
    def parseAddr(value):
        service = None
        ip = None
        port = None

        if not UtilMgr.isString(value):
            return (service, ip, port)

        # get request and address #
        cmdList = value.split("@")
        if len(cmdList) >= 2:
            service = cmdList[0]
            addr = cmdList[1]
        else:
            addr = value

        # get IP and PORT #
        addrList = addr.split(":")
        arg = addrList[0]
        if len(addrList) >= 2:
            try:
                # set IP #
                if arg and str(arg).upper() == "LOCALHOST":
                    ip = "127.0.0.1"
                else:
                    ip = arg

                # set PORT #
                if addrList[1]:
                    port = long(addrList[1])
            except:
                pass
        else:
            try:
                # set IP #
                if "." in arg:
                    ip = arg
                elif str(arg).upper() == "LOCALHOST":
                    ip = "127.0.0.1"
                # set PORT #
                else:
                    port = long(arg)
            except:
                pass

        return (service, ip, port)

    @staticmethod
    def setRemoteServer(value, tcp=False, verb=True):
        # receive mode #
        if not value:
            SysMgr.remoteServObj = "NONE"
            return

        # request mode #
        service, ip, port = NetworkMgr.parseAddr(value)

        # set PRINT as default #
        if not service:
            service = "PRINT"

        if not ip:
            ip = NetworkMgr.getPublicIp()

        if port is None:
            port = SysMgr.defaultServPort

        # check server addresses #
        if (
            SysMgr.localServObj
            and SysMgr.localServObj.ip == ip
            and SysMgr.localServObj.port == port
        ):
            SysMgr.printErr(
                (
                    "wrong value for remote connection, "
                    "local address and remote address are same (%s:%s)"
                )
                % (ip, port)
            )
            sys.exit(-1)

        # check request #
        if not ip or not port or not SysMgr.isValidRequest(service):
            reqList = ""
            for req in TaskAnalyzer.requestType:
                reqList += req + "|"

            SysMgr.printErr(
                "wrong input address, input [%s]@IP:PORT as remote address"
                % reqList[:-1]
            )
            sys.exit(-1)

        # create a socket #
        networkObject = NetworkMgr("client", ip, port, tcp=tcp)
        if not networkObject.ip:
            sys.exit(-1)
        else:
            networkObject.request = service
            SysMgr.remoteServObj = networkObject

        # set protocol #
        if tcp:
            proto = "TCP"
        else:
            proto = "UDP"

        if verb:
            SysMgr.printInfo(
                "use %s:%d(%s) as remote address" % (ip, port, proto)
            )

        return SysMgr.remoteServObj

    @staticmethod
    def setRemoteNetwork(service, ip, port):
        # set default service #
        if not service:
            service = "PRINT"

        errMsg = (
            "wrong value for remote server, input in the format [%s]@IP:PORT"
        ) % "|".join(TaskAnalyzer.requestType)

        if not ip or not SysMgr.isValidRequest(service):
            SysMgr.printErr(errMsg)
            sys.exit(-1)

        if port is None:
            port = SysMgr.defaultServPort

        # create a new object #
        netObj = NetworkMgr("client", ip, port)
        if not netObj.ip:
            sys.exit(-1)
        else:
            netObj.status = "ALWAYS"
            netObj.request = service
            naddr = "%s:%s" % (ip, str(port))

            if service == "PRINT":
                SysMgr.addrListForPrint[naddr] = netObj
            elif service.startswith("REPORT"):
                SysMgr.reportEnable = True
                SysMgr.addrListForReport[naddr] = netObj
            else:
                SysMgr.printErr(errMsg)

        SysMgr.printInfo(
            "use %s:%d as remote address to request %s" % (ip, port, service)
        )

    @staticmethod
    def setServerNetwork(
        ip,
        port,
        force=False,
        blocking=False,
        tcp=False,
        anyPort=False,
        reuse=True,
        weakPort=False,
        verb=False,
    ):
        if SysMgr.localServObj and not force:
            SysMgr.printWarn(
                "ignored to set server network because its already set", verb
            )
            return

        # get internet available IP first #
        if not ip:
            ip = NetworkMgr.getPublicIp()

        # set default port #
        if port is None:
            if SysMgr.checkMode("cli"):
                port = SysMgr.defaultCliPort
            else:
                port = SysMgr.defaultServPort

        # print available IP list #
        try:
            iplist = sorted(NetworkMgr.getUsingIps())
            if iplist:
                SysMgr.printWarn(
                    "available IP list [ %s ]" % ", ".join(iplist), verb
                )
        except:
            pass

        # check server setting #
        localObj = SysMgr.localServObj
        if (
            localObj
            and localObj.socket
            and localObj.ip == ip
            and localObj.port == port
        ):
            if blocking:
                localObj.socket.setblocking(1)
            else:
                localObj.socket.setblocking(0)
            return

        # create a new server setting #
        networkObject = NetworkMgr(
            "server",
            ip,
            port,
            tcp=tcp,
            blocking=blocking,
            anyPort=anyPort,
            reuse=reuse,
        )
        if not networkObject.ip and weakPort:
            networkObject = NetworkMgr(
                "server",
                ip,
                port,
                tcp=tcp,
                blocking=blocking,
                anyPort=True,
                reuse=reuse,
            )
        if not networkObject.ip:
            SysMgr.printWarn("failed to set server IP", verb)
            return

        # set protocol #
        if tcp:
            proto = "TCP"
        else:
            proto = "UDP"

        localObj = SysMgr.localServObj = networkObject

        # print adderss info #
        if not SysMgr.masterPid:
            if SysMgr.parentPid:
                printer = SysMgr.printWarn
            else:
                printer = SysMgr.printInfo
            printer(
                "use %s:%d(%s) as local address"
                % (localObj.ip, localObj.port, proto)
            )

        return networkObject

    @staticmethod
    def prepareServerConn(cliAddr, servAddr):
        # set local address #
        if not cliAddr:
            NetworkMgr.setServerNetwork(None, None, anyPort=True)
        else:
            service, ip, port = NetworkMgr.parseAddr(cliAddr)

            NetworkMgr.setServerNetwork(ip, port)

        # set remote address #
        if servAddr:
            NetworkMgr.setRemoteServer(servAddr)

        # set client address #
        if SysMgr.localServObj:
            cliIp = SysMgr.localServObj.ip
            cliPort = SysMgr.localServObj.port
        else:
            cliIp = None
            cliPort = None

        # set server address #
        if SysMgr.remoteServObj.ip:
            servIp = SysMgr.remoteServObj.ip
            servPort = SysMgr.remoteServObj.port
        else:
            servIp = None
            servPort = None

        return (cliIp, cliPort), (servIp, servPort)

    @staticmethod
    def getDevByIp(ip):
        # get device data #
        data = SysMgr.getNetDevData()
        if not data:
            return

        # create a new socket #
        try:
            fcntl = SysMgr.getPkg("fcntl", False)
            socket = SysMgr.getPkg("socket", False)
            sobj = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        except SystemExit:
            sys.exit(0)
        except:
            return

        # scan IP device and address #
        for line in data:
            try:
                dev, stats = line.split(":")

                dev = dev.strip()

                res = fcntl.ioctl(
                    sobj.fileno(),
                    0x8915,  # SIOCGIFADDR
                    struct.pack("256s", dev[:15].encode("utf-8")),
                )

                ipaddr = socket.inet_ntoa(res[20:24])
                if ipaddr == ip:
                    return dev
            except SystemExit:
                sys.exit(0)
            except:
                pass

    @staticmethod
    def getMainMacAddr():
        dirPath = "/sys/class/net"

        try:
            devices = os.listdir(dirPath)
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printOpenErr(dirPath)
            return

        # return main IP and device #
        try:
            ip = NetworkMgr.getPublicIp()
            if ip:
                dev = NetworkMgr.getDevByIp(ip)
                if dev:
                    return (dev, ip)
        except SystemExit:
            sys.exit(0)
        except:
            pass

        for dev in devices:
            # skip virtual device #
            if dev == "lo" or dev.startswith("docker"):
                continue

            target = "%s/%s/address" % (dirPath, dev)
            try:
                with open(target, "r") as fd:
                    addr = fd.readline()[:-1]
                    return (dev, addr)
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printOpenErr(target)

        return ("None", "None")

    @staticmethod
    def getUsingIps():
        if not SysMgr.isLinux:
            return

        connPaths = [
            "%s/net/udp" % SysMgr.procPath,
            "%s/net/tcp" % SysMgr.procPath,
        ]

        validList = {}
        cacheList = {}

        for path in connPaths:
            try:
                with open(path, "r") as fd:
                    ipList = fd.read().split("\n")

                # remove title #
                ipList.pop(0)

                for line in ipList:
                    if not line:
                        continue
                    items = line.split(None, 2)
                    addr = items[1].split(":")[0]
                    if addr in cacheList:
                        continue

                    cacheList[addr] = None
                    ip = SysMgr.convCIDR(addr)
                    validList[ip] = None
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printOpenWarn(path)

        return list(validList)

    @staticmethod
    def getGateways():
        gateways = {}

        ips = NetworkMgr.getRoutedIps()

        for item in ips:
            try:
                ip = item[1]
                if ip in ("0.0.0.0", "127.0.0.1") or not ip.endswith(".1"):
                    continue

                gw = "%s.1" % ip[: ip.rfind(".")]
                gateways[gw] = None
            except SystemExit:
                sys.exit(0)
            except:
                pass

        return list(gateways)

    @staticmethod
    def getMainIp():
        ipList = NetworkMgr.getUsingIps()

        # remove IP for all IPv4 addresses #
        try:
            ipList.remove("0.0.0.0")
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # return main IP #
        if not ipList:
            return None
        elif "127.0.0.1" in ipList:
            return "127.0.0.1"
        else:
            return list(sorted(ipList, reverse=True))[0]

    @staticmethod
    def getRoutedIps():
        validList = []
        routePath = "%s/net/route" % SysMgr.procPath
        try:
            with open(routePath, "r") as fd:
                ipList = fd.readlines()

            # remove title #
            ipList.pop(0)

            for line in ipList:
                items = line.split()
                validList.append([items[0], SysMgr.convCIDR(items[1])])

            return validList
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printOpenWarn(routePath)
            return validList

    @staticmethod
    def getHostName():
        try:
            return SysMgr.getPkg("socket").gethostname()
        except:
            return None

    @staticmethod
    def getPublicIp(force=False):
        if SysMgr.ipAddr and not force:
            return SysMgr.ipAddr

        # get socket object #
        socket = SysMgr.getPkg("socket", False)
        if not socket:
            return

        from socket import socket, AF_INET, SOCK_DGRAM, SOCK_STREAM

        ret = None

        try:
            s = socket(AF_INET, SOCK_STREAM)
            s.settimeout(0.3)

            # connect to google public IP #
            s.connect(("8.8.8.8", 53))

            ret = s.getsockname()[0]
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn("failed to get public IP address")

        if not ret:
            ret = NetworkMgr.getMainIp()

        SysMgr.ipAddr = ret

        return ret

    def __del__(self):
        try:
            self.close()
        except:
            pass


class Timeline(object):
    """SVG viewer for timeline segments"""

    class Segment(object):
        def __init__(self, group, time_start, time_end, extra):
            self.group = group
            self.time_start = time_start
            self.time_end = time_end
            self.text = None
            self.id = None
            self.color = None
            self.state = None
            self.info = None
            self._init_extra(extra)

        def _init_extra(self, extra):
            if "text" in extra:
                self.text = extra["text"]

            if "id" in extra:
                self.id = extra["id"]

            if "color" in extra:
                self.color = extra["color"]

            if "state" in extra:
                self.state = extra["state"]

            if "info" in extra:
                self.info = extra["info"]

    class Config(object):
        def _conv_palette(self, palette):
            plist = []
            for palette_entry in palette:
                """
                rgb = [
                    int(rgb_value)
                    for rgb_value in re.findall("\d+", palette_entry)
                ]
                """
                color = "rgb%s" % palette_entry
                plist.append(color)
            return plist

        def __init__(self):
            self.WIDTH = 1500
            self.HEIGHT = 770
            self.TIME_AXIS_HEIGHT = 1
            self.TICKS = 100

            # set item filter #
            self.FILTER = SysMgr.environList.get("FILTER", [])
            if self.FILTER:
                SysMgr.printInfo(
                    "only specific items [ %s ] are drawn"
                    % ", ".join(self.FILTER)
                )

            # set label filter #
            if "LABELMIN" in SysMgr.environList:
                self.LABEL_SIZE_MIN = UtilMgr.getEnvironNum(
                    "LABELMIN", isInt=True
                )
                SysMgr.printInfo(
                    "only time segments bigger than %s are printed"
                    % UtilMgr.convNum(self.LABEL_SIZE_MIN)
                )
            else:
                self.LABEL_SIZE_MIN = 0

            # set font size #
            self.FONT_SIZE = UtilMgr.getEnvironNum(
                "FONTSIZE", False, 3, False, isInt=True
            )

            # set group font size #
            self.GROUP_FONT_SIZE = UtilMgr.getEnvironNum(
                "GROUPFONTSIZE", False, 0, False, isInt=True
            )

            palette = [
                "(0,150,136)",
                "(0,188,212)",
                "(0,0,128)",
                "(0,0,139)",
                "(0,0,205)",
                "(0,0,255)",
                "(0,100,0)",
                "(0,128,0)",
                "(0,128,128)",
                "(0,139,139)",
                "(0,191,255)",
                "(0,206,209)",
                "(0,250,154)",
                "(0,255,0)",
                "(0,255,127)",
                "(0,255,255)",
                "(100,149,237)",
                "(102,205,170)",
                "(103,58,183)",
                "(106,90,205)",
                "(107,142,35)",
                "(121,85,72)",
                "(123,104,238)",
                "(124,252,0)",
                "(127,255,0)",
                "(127,255,212)",
                "(128,0,0)",
                "(128,0,128)",
                "(128,128,0)",
                "(128,128,128)",
                "(135,206,235)",
                "(135,206,250)",
                "(138,43,226)",
                "(139,195,74)",
                "(139,0,0)",
                "(139,0,139)",
                "(139,69,19)",
                "(143,188,143)",
                "(144,238,144)",
                "(147,112,219)",
                "(148,0,211)",
                "(152,251,152)",
                "(153,50,204)",
                "(154,205,50)",
                "(156,39,176)",
                "(158,158,158)",
                "(160,82,45)",
                "(165,42,42)",
                "(173,216,230)",
                "(173,255,47)",
                "(175,238,238)",
                "(176,224,230)",
                "(178,34,34)",
                "(184,134,11)",
                "(186,85,211)",
                "(188,143,143)",
                "(189,183,107)",
                "(192,192,192)",
                "(199,21,133)",
                "(205,133,63)",
                "(205,220,57)",
                "(205,92,92)",
                "(210,105,30)",
                "(210,180,140)",
                "(216,191,216)",
                "(218,112,214)",
                "(218,165,32)",
                "(219,112,147)",
                "(220,20,60)",
                "(221,160,221)",
                "(222,184,135)",
                "(224,255,255)",
                "(233,150,122)",
                "(233,30,99)",
                "(238,130,238)",
                "(238,232,170)",
                "(240,128,128)",
                "(240,230,140)",
                "(244,67,54)",
                "(244,164,96)",
                "(245,222,179)",
                "(245,245,220)",
                "(25,25,112)",
                "(250,128,114)",
                "(250,235,215)",
                "(250,250,210)",
                "(255,0,0)",
                "(255,0,255)",
                "(255,105,180)",
                "(255,127,80)",
                "(255,140,0)",
                "(255,152,0)",
                "(255,160,122)",
                "(255,165,0)",
                "(255,182,193)",
                "(255,192,203)",
                "(255,193,7)",
                "(255,20,147)",
                "(255,215,0)",
                "(255,228,196)",
                "(255,235,205)",
                "(255,235,59)",
                "(255,248,220)",
                "(255,250,205)",
                "(255,255,0)",
                "(255,255,224)",
                "(255,69,0)",
                "(255,87,34)",
                "(255,99,71)",
                "(3,169,244)",
                "(30,144,255)",
                "(32,178,170)",
                "(33,150,243)",
                "(34,139,34)",
                "(46,139,87)",
                "(47,79,79)",
                "(50,205,50)",
                "(60,179,113)",
                "(63,81,181)",
                "(64,224,208)",
                "(65,105,225)",
                "(70,130,180)",
                "(72,209,204)",
                "(72,61,139)",
                "(75,0,130)",
                "(76,175,80)",
                "(85,107,47)",
                "(95,158,160)",
                "(96,125,139)",
            ]

            self.PALETTE = self._conv_palette(palette)

            # shuffle list #
            random = SysMgr.getPkg("random", False)
            if random:
                random.shuffle(self.PALETTE)

        @staticmethod
        def _load(fileName=None, data=None):
            if fileName:
                with open(fileName) as fd:
                    data = fd.read()
                    data = UtilMgr.convStr2Dict(data)
            elif not data:
                SysMgr.printErr("no path or data for timeline config")
                sys.exit(-1)

            config = Timeline.Config()
            config.WIDTH = data.get("width", 20000)
            config.HEIGHT = data.get("height", 4000)
            config.FONT_SIZE = data.get("font_size", 3)
            config.TICKS = data.get("time_ticks", 5)
            config.TIME_AXIS_HEIGHT = data.get("time_axis_height", 5)
            config.PALETTE = config._conv_palette(data.get("palette", []))
            config.TIMEUNIT = data.get("time_unit")
            config.TIMEFACTOR = data.get("time_factor", 1)
            if config.LABEL_SIZE_MIN == 0:
                config.LABEL_SIZE_MIN = data.get("label_size_min", 5)

            return config

    def __init__(self, title, segments, time_unit, fontsize, config, tasks=[]):
        self.title = title
        self.segments = segments
        self.time_unit = time_unit
        self.config = config

        self.time_start = min(
            segments, key=lambda segment: segment.time_start
        ).time_start
        self.time_end = max(
            segments, key=lambda segment: segment.time_end
        ).time_end
        self.segment_groups = sorted(
            list(map(long, set(s.group for s in self.segments)))
        )
        self.segment_groups = list(map(str, self.segment_groups))
        self.groups = len(self.segment_groups)
        self.scaled_height = self.config.HEIGHT / self.groups
        self.group_list = list(self.segment_groups)
        self.group_stat = [
            {
                "cnt": 0,
                "du_min": 0,
                "du_max": 0,
                "du_list": [],
                "inter_min": 0,
                "inter_max": 0,
                "inter_list": [],
                "prev": 0,
            }
            for _ in xrange(len(self.group_list))
        ]

        try:
            self.ratio = self.config.WIDTH / float(
                self.time_end - self.time_start
            )
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                "failed to recognize timeline because start and end are same"
            )
            sys.exit(-1)

        self.last_group_segment = {}
        self.last_group_time = {}
        self.height_group_pos = {}
        self.last_iogroup_segment = {}
        self.last_iogroup_time = {}
        self.height_iogroup_pos = {}

        # apply task converter #
        try:
            self.tasks = {}
            if tasks:
                for task in tasks:
                    if task in Timeline.conv_table:
                        new = Timeline.conv_table[task]
                        self.tasks[new] = tasks[task]
                    elif isinstance(tasks, dict):
                        self.tasks[task] = tasks[task]
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to change tasks in timeline", True)
            sys.exit(-1)

        # time factor #
        if hasattr(self.config, "TIMEFACTOR"):
            self.time_factor = self.config.TIMEFACTOR
        else:
            self.time_factor = 1

        # task color #
        if self.tasks:
            self.color_map = self._build_task_color_map()
        else:
            self.color_map = self._build_color_map()

        # update font size #
        if fontsize and self.config:
            self.config.FONT_SIZE = fontsize

    def _build_task_color_map(self):
        color_map = {}
        palette = self.config.PALETTE
        for i, group in enumerate(self.tasks):
            color_map[group] = palette[i % len(palette)]
        return color_map

    def _build_color_map(self):
        color_map = {}
        palette = self.config.PALETTE
        segment_groups = self.segment_groups
        for i, group in enumerate(segment_groups):
            color_map[group] = palette[i % len(palette)]
        return color_map

    def _draw_grid(self, dwg):
        for x in xrange(0, self.config.WIDTH, self.config.TICKS):
            dwg.add(
                dwg.line(
                    (x, 0),
                    (x, self.config.HEIGHT),
                    stroke="black",
                    stroke_width=0.1,
                )
            )

    def _draw_group_stat(self, dwg):
        # set group font size #
        if self.config.GROUP_FONT_SIZE > 0:
            fontsize = self.config.GROUP_FONT_SIZE
        else:
            fontsize = self.scaled_height / 4

        idx = 0
        convNum = UtilMgr.convNum
        for y_tick in xrange(self.config.HEIGHT):
            try:
                stats = self.group_stat[idx]
                idx += 1

                # count #
                count = stats["cnt"]
                if count == 0:
                    continue

                # build string #
                statstr = "Cnt: %s" % convNum(count)

                # interval #
                if count > 1:
                    try:
                        inter = stats["inter_list"]
                        inter_avg = long(sum(inter) / (len(inter)))
                    except SystemExit:
                        sys.exit(0)
                    except:
                        inter_avg = 0
                    inter_std = UtilMgr.getStdev(inter)

                    statstr += (
                        ", IntMin: %s, IntMax: %s, IntAvg: %s, IntStd: %s"
                        % (
                            convNum(stats["inter_min"]),
                            convNum(stats["inter_max"]),
                            convNum(inter_avg),
                            convNum(inter_std),
                        )
                    )

                # duration #
                if stats["du_min"] > 0:
                    try:
                        du = stats["du_list"]
                        du_avg = long(sum(du) / (len(du)))
                    except SystemExit:
                        sys.exit(0)
                    except:
                        du_avg = 0
                    du_std = UtilMgr.getStdev(du)

                    statstr += (
                        ", DuMin: %s, DuMax: %s, DuAvg: %s, DuStd: %s"
                        % (
                            convNum(stats["du_min"]),
                            convNum(stats["du_max"]),
                            convNum(du_avg),
                            convNum(du_std),
                        )
                    )
            except SystemExit:
                sys.exit(0)
            except:
                continue

            y_tick *= self.scaled_height
            dwg.add(
                dwg.text(
                    statstr,
                    (2, y_tick + self.scaled_height / 2),
                    font_size=fontsize / 2,
                    fill="rgb(200,200,200)",
                )
            )

    def _draw_group_axis(self, dwg, yval):
        dwg.add(
            dwg.rect(
                (0, 0),
                (self.config.TIME_AXIS_HEIGHT, self.config.HEIGHT),
                fill="black",
            )
        )

        # set group font size #
        if self.config.GROUP_FONT_SIZE > 0:
            fontsize = self.config.GROUP_FONT_SIZE
        else:
            fontsize = self.scaled_height / 2

        idx = 0
        groupList = list(self.segment_groups)
        for y_tick in xrange(self.config.HEIGHT):
            try:
                name = groupList[idx]
                idx += 1
            except:
                continue

            y_tick *= self.scaled_height

            dwg.add(
                dwg.line(
                    (0, y_tick),
                    (self.config.WIDTH, y_tick),
                    stroke="black",
                    stroke_width=2,
                )
            )

            dwg.add(
                dwg.text(
                    name,
                    (2, y_tick + self.scaled_height),
                    font_size=fontsize,
                    fill="rgb(200,200,200)",
                )
            )

            # add info #
            addinfo = yval[name] if yval and name in yval else ""
            name = str(name)
            if not addinfo and self.tasks and name in self.tasks:
                addinfo = self.tasks[name]
            if addinfo:
                x = 2 * self.scaled_height * len(name) / 5
                dwg.add(
                    dwg.text(
                        addinfo,
                        (x, y_tick + self.scaled_height),
                        font_size=fontsize / 2,
                        fill="rgb(200,200,200)",
                    )
                )

    def _draw_time_axis(self, dwg, start=0, annotation=None):
        dwg.add(
            dwg.rect(
                (0, self.config.HEIGHT),
                (self.config.WIDTH, self.config.TIME_AXIS_HEIGHT),
                fill="black",
            )
        )

        y_time_tick = self.config.HEIGHT + self.config.TIME_AXIS_HEIGHT / 2
        ratio = 1 / self.ratio

        # set time delta #
        start = float(start) / self.time_factor

        # create the drawing group #
        g = dwg.add(dwg.g())
        if annotation:
            g.set_desc(annotation)

        for x_tick_time in xrange(0, self.config.WIDTH, self.config.TICKS):
            abs_x_tick_time = (x_tick_time * ratio) + start
            tick_time = "{:10}".format(long(abs_x_tick_time))
            g.add(
                dwg.text(
                    "%s %s" % (UtilMgr.convNum(tick_time), self.time_unit),
                    (x_tick_time, y_time_tick + self.config.FONT_SIZE * 5),
                    font_size=self.config.FONT_SIZE * 5,
                    color="rgb(255,255,255)",
                )
            )

    def _draw_background(self, dwg):
        dwg.add(
            dwg.rect(
                (0, 0),
                (self.config.WIDTH, self.config.HEIGHT),
                fill="rgb(245,245,245)",
            )
        )

        # set title #
        if self.title:
            title = self.title
        else:
            title = "Guider Timeline"

        # set font size for title #
        fontsize = self.config.FONT_SIZE * 10

        dwg.add(
            dwg.text(
                title,
                (
                    (self.config.WIDTH / 2) - (len(title) * fontsize / 4),
                    fontsize,
                ),
                font_size=fontsize,
                font_weight="bolder",
                fill="rgb(200,200,200)",
            )
        )

    def _draw_segments(self, dwg, start=0):
        # set time delta #
        start = float(start) / self.time_factor

        for idx, segment in enumerate(
            sorted(self.segments, key=lambda e: len(e.state))
        ):
            UtilMgr.printProgress(idx, len(self.segments))
            self._draw_segment(segment, dwg, start)

        if self.segments:
            UtilMgr.deleteProgress()

    def _draw_segment(self, segment, dwg, start=0):
        x0 = float(segment.time_start - self.time_start) * self.ratio
        x1 = float(segment.time_end - self.time_start) * self.ratio
        group_idx = self.group_list.index(segment.group)
        y0 = self.scaled_height * float(group_idx % self.groups)
        y1 = self.scaled_height * float((group_idx % self.groups) + 1)
        scaled_width = x1 - x0
        scaled_top_height = y0 + (self.scaled_height / 7)
        scaled_bottom_height = y1 - (self.scaled_height * 0.25)
        duration = segment.time_end - segment.time_start
        time_end = segment.time_end + start - self.time_start
        strokeSize = 3.5

        # check filter #
        if self.config.FILTER and not UtilMgr.isValidStr(
            segment.text, self.config.FILTER
        ):
            return

        # get color ID #
        if segment.color:
            color = segment.color
        else:
            if segment.id:
                colorid = segment.id
            else:
                colorid = group_idx

            # get real color via ID #
            try:
                color = self.color_map[str(colorid)]
            except SystemExit:
                sys.exit(0)
            except:
                try:
                    cidx = long(colorid) % len(self.color_map)
                    color = self.color_map[list(self.color_map)[cidx]]
                    self.color_map[str(colorid)] = color
                except SystemExit:
                    sys.exit(0)
                except:
                    color = self.color_map[list(self.color_map)[0]]

        # check stroke option #
        if self.stroke_text and UtilMgr.isValidStr(
            segment.text, self.stroke_text
        ):
            stroke = "rgb(255,0,0)"
            stroke_width = 1
        else:
            stroke = "none"
            stroke_width = 0

        # update count stat #
        stats = self.group_stat[group_idx]
        stats["cnt"] += 1

        # update interval stats #
        if stats["prev"] > 0:
            interval = segment.time_start - stats["prev"]
        else:
            interval = 0
        stats["prev"] = segment.time_start
        if stats["inter_min"] == 0 or stats["inter_min"] > interval:
            stats["inter_min"] = interval
        if stats["inter_max"] == 0 or stats["inter_max"] < interval:
            stats["inter_max"] = interval
        stats["inter_list"].append(interval)

        # apply stroke by interval #
        if self.stroke_interval > 0 and self.stroke_interval <= interval:
            stroke = "rgb(255,0,0)"
            stroke_width = 1

        # update duration stats #
        if stats["du_min"] == 0 or stats["du_min"] > duration:
            stats["du_min"] = duration
        if stats["du_max"] == 0 or stats["du_max"] < duration:
            stats["du_max"] = duration
        stats["du_list"].append(duration)

        # apply stroke by duration #
        if self.stroke_duration > 0 and self.stroke_duration <= duration:
            stroke = "rgb(255,0,0)"
            stroke_width = 1

        # draw bold line for core off #
        if segment.state == "OFF":
            dwg.add(
                dwg.line(
                    (x0, y0), (x1, y0), stroke="gray", stroke_width=strokeSize
                )
            )
            return

        # draw circle and text for event #
        if segment.state and segment.state.startswith("EVENT"):
            if segment.state == "EVENT_MARK":
                font_size = self.config.FONT_SIZE
            elif segment.state == "EVENT_USER":
                font_size = self.config.FONT_SIZE * 3
            else:
                font_size = self.config.FONT_SIZE

            dwg.add(
                dwg.circle(
                    center=(x0, y0),
                    r=font_size / 2,
                    stroke="darkgreen",
                    fill="darkgreen",
                )
            )

            # draw text #
            dwg.add(
                dwg.text(
                    segment.text,
                    (x0 + font_size, y0),
                    fill="darkgreen",
                    stroke="none",
                    font_size=font_size,
                    font_weight="bolder",
                )
            )

            return

        # define shortcut and duration string #
        convNum = UtilMgr.convNum
        durationstr = convNum(duration)

        if hasattr(segment, "info") and segment.info:
            info = "\n%s" % segment.info
        else:
            info = ""

        # create the drawing group #
        g = dwg.add(dwg.g())
        g.set_desc(
            "%s %s / ~%s (%s)%s"
            % (
                segment.text,
                durationstr,
                convNum(time_end),
                self.time_unit,
                info,
            )
        )

        # draw line for block_read status #
        if segment.state == "RD":
            g.add(
                dwg.rect(
                    (x0, scaled_bottom_height),
                    (scaled_width, self.scaled_height * 0.25),
                    rx=1,
                    ry=1,
                    fill="purple",
                    fill_opacity=0.5,
                    stroke=stroke,
                    stroke_width=stroke_width,
                )
            )
        # draw line for block_write status #
        elif segment.state == "WR":
            g.add(
                dwg.rect(
                    (x0, scaled_bottom_height),
                    (scaled_width, self.scaled_height * 0.25),
                    rx=1,
                    ry=1,
                    fill="darkcyan",
                    fill_opacity=0.5,
                    stroke=stroke,
                    stroke_width=stroke_width,
                )
            )
        # draw line for syscall status #
        elif segment.state == "SYSCALL":
            g.add(
                dwg.rect(
                    (x0, y0),
                    (scaled_width, self.scaled_height * 0.5),
                    rx=1,
                    ry=1,
                    fill=color,
                    fill_opacity=0.5,
                    stroke=stroke,
                    stroke_width=stroke_width,
                )
            )
        # draw line for sched status #
        else:
            # draw CPU timeslice #
            g.add(
                dwg.rect(
                    (x0, y0),
                    (scaled_width, self.scaled_height),
                    rx=1,
                    ry=1,
                    fill=color,
                    fill_opacity=0.5,
                    stroke=stroke,
                    stroke_width=stroke_width,
                )
            )

            # draw preempted status #
            if segment.state == "R":
                g.add(
                    dwg.line(
                        (x1, y0),
                        (x1, scaled_top_height),
                        stroke="red",
                        stroke_width=strokeSize,
                    )
                )
            # draw wait status #
            elif segment.state == "D":
                g.add(
                    dwg.line(
                        (x1, y0),
                        (x1, scaled_top_height),
                        stroke="black",
                        stroke_width=strokeSize,
                    )
                )

        # check label flag #
        if (
            not SysMgr.showAll
            and not "LABEL" in SysMgr.environList
            and not "LABELMIN" in SysMgr.environList
        ):
            return
        # check duration #
        elif not duration or duration < self.config.LABEL_SIZE_MIN:
            return

        # convert duration to text #
        duration = "~%s" % convNum(durationstr)

        # I/O #
        if segment.state in ("RD", "WR"):
            # initialize group data #
            self.last_iogroup_segment.setdefault(group_idx, None)
            self.last_iogroup_time.setdefault(group_idx, x0)

            # define data #
            last_iogroup_segment = self.last_iogroup_segment[group_idx]
            last_iogroup_time = self.last_iogroup_time[group_idx]

            # set text attributes for block_read #
            if segment.state == "RD":
                if (
                    last_iogroup_segment == segment.text
                    and x0 - last_iogroup_time < self.config.TICKS
                ):
                    segment_label = durationstr
                    font_size = self.config.FONT_SIZE - 1
                else:
                    segment_label = "[R] %s %s" % (segment.text, durationstr)
                    font_size = self.config.FONT_SIZE - 0.7
                color = "rgb(128,0,128)"
            # set text attributes for block_write #
            elif segment.state == "WR":
                if (
                    last_iogroup_segment == segment.text
                    and x0 - last_iogroup_time < self.config.TICKS
                ):
                    segment_label = durationstr
                    font_size = self.config.FONT_SIZE - 1
                else:
                    segment_label = "[W] %s %s" % (segment.text, durationstr)
                    font_size = self.config.FONT_SIZE - 0.7
                color = "rgb(0,139,139)"

            # update group info #
            self.last_iogroup_segment[group_idx] = segment.text
            self.last_iogroup_time[group_idx] = x0
            self.height_iogroup_pos.setdefault(group_idx, 0)

            # set text position #
            scaled_pos = self.scaled_height * 0.75
            self.height_iogroup_pos[group_idx] += self.config.FONT_SIZE
            height_pos = self.height_iogroup_pos[group_idx]
            if height_pos + scaled_pos >= self.scaled_height:
                height_pos = self.height_iogroup_pos[
                    group_idx
                ] = self.config.FONT_SIZE
        # CPU & SYSCALL #
        else:
            # SYSCALL #
            if segment.state == "SYSCALL":
                prefix = "[S] "
            else:
                prefix = ""

            # initialize group data #
            self.last_group_segment.setdefault(group_idx, None)
            self.last_group_time.setdefault(group_idx, x0)

            # set text attributes for same task #
            if (
                self.last_group_segment[group_idx] == segment.text
                and x0 - self.last_group_time[group_idx] < self.config.TICKS
            ):
                segment_label = durationstr
                color = "rgb(50,50,50)"
                font_size = self.config.FONT_SIZE - 1
            # set text attributes for new task #
            else:
                segment_label = "%s%s %s" % (prefix, segment.text, durationstr)
                color = "rgb(255,0,0)"
                font_size = self.config.FONT_SIZE - 0.5

            # update group info #
            self.last_group_segment[group_idx] = segment.text
            self.last_group_time[group_idx] = x0
            self.height_group_pos.setdefault(group_idx, 0)

            # set text position #
            scaled_pos = self.scaled_height * 0.15
            self.height_group_pos[group_idx] += self.config.FONT_SIZE
            height_pos = self.height_group_pos[group_idx]
            if height_pos + scaled_pos * 2.5 >= self.scaled_height:
                height_pos = self.height_group_pos[group_idx] = 0

        # set text position #
        xpos = x0
        ypos = y0 + scaled_pos + height_pos

        # draw text #
        dwg.add(
            dwg.text(
                segment_label,
                (xpos, ypos),
                fill=color,
                stroke="none",
                font_size=font_size,
                font_weight="normal",
                transform="rotate(0,%s,%s)" % (xpos, ypos),
            )
        )

    def draw(self, dwg, start=0, annotation=None, yval=None):
        self._draw_background(dwg)
        self._draw_grid(dwg)
        self._draw_group_axis(dwg, yval)
        self._draw_time_axis(dwg, start, annotation=annotation)
        self._draw_segments(dwg, start)
        self._draw_group_stat(dwg)

    @staticmethod
    def load(
        fileName=None,
        data=None,
        config=None,
        tasks=None,
        begin=0,
        end=0,
        duration=0,
        durationMin=0,
    ):
        if fileName:
            with open(fileName) as json_file:
                # get json object #
                json = SysMgr.getPkg("json")

                data = json.load(json_file)
        elif not data:
            SysMgr.printErr("no path or data for timeline input")
            sys.exit(-1)

        # get default timeunit #
        time_unit = ""
        if "time_unit" in data:
            time_unit = data["time_unit"]
            time_unit = time_unit.lower()

        # get title #
        if "title" in data:
            title = data["title"]
        else:
            title = ""

        # get font size #
        if "names" in data and data["names"]:
            if tasks:
                tasks.update(data["names"])
            else:
                tasks = data["names"]

        # get font size #
        if "font_size" in data:
            fontsize = data["font_size"]
        else:
            fontsize = None

        # get configured timeunit #
        time_factor = 1.0
        if hasattr(config, "TIMEUNIT") and config.TIMEUNIT:
            new_time_unit = config.TIMEUNIT.lower()

            if time_unit == new_time_unit:
                pass
            elif time_unit == "us":
                if new_time_unit == "ms":
                    time_factor = 1 / 1000.0
                elif new_time_unit == "sec":
                    time_factor = 1 / 1000000.0
                else:
                    SysMgr.printErr(
                        "no support '%s' unit for timeline" % new_time_unit
                    )
            elif time_unit == "ms":
                if new_time_unit == "us":
                    time_factor = 1000.0
                elif new_time_unit == "sec":
                    time_factor = 1 / 1000.0
                else:
                    SysMgr.printErr(
                        "no support '%s' unit for timeline" % new_time_unit
                    )
            else:
                SysMgr.printErr(
                    "no support '%s' unit for timeline" % new_time_unit
                )

            time_unit = new_time_unit
            config.TIMEFACTOR = time_factor

        # print time unit #
        if time_unit:
            SysMgr.printInfo("apply '%s' in time unit" % time_unit)

        # load segments #
        segments = Timeline._load_segments(
            data, time_factor, begin, end, duration, durationMin
        )
        if not segments:
            return None

        return Timeline(title, segments, time_unit, fontsize, config, tasks)

    @staticmethod
    def _load_segments(
        data, time_factor=1, begin=0, end=0, duration=0, durationMin=0
    ):
        segments = []
        for segment_data in sorted(
            data["segments"], key=lambda e: e["time_start"]
        ):
            # convert group #
            group = str(segment_data["group"])
            group_num = long(group)
            if group in Timeline.conv_table:
                group = Timeline.conv_table[group]

            # apply core filter ;) #
            if SysMgr.perCoreDrawList:
                if not group_num in SysMgr.perCoreDrawList:
                    continue

            # verify time #
            if segment_data["time_start"] > segment_data["time_end"]:
                SysMgr.printWarn(
                    "time_start is bigger than time_end for %s" % segment_data
                )
                continue

            # apply common duration #
            time_start = long(segment_data["time_start"] * time_factor)
            if duration > 0:
                time_end = time_start + duration
            else:
                time_end = long(segment_data["time_end"] * time_factor)

            # apply minimum duration #
            if durationMin > 0:
                diff = time_end - time_start
                if durationMin > diff:
                    time_end = time_start + durationMin

            # check time #
            if 0 < end < time_end:
                time_end = end
                if time_start > end:
                    continue
            if 0 < begin < time_start:
                if time_end < begin:
                    continue
                time_start = begin

            # add segment #
            segments.append(
                Timeline.Segment(group, time_start, time_end, segment_data)
            )

        return segments


class Ext4Analyzer(object):
    """Analyzer for ext4"""

    FILE_TYPE = {
        0: "unkn",
        1: "file",
        2: "dir",
        3: "chr",
        4: "blk",
        5: "fifo",
        6: "sock",
        7: "sym",
    }

    def __init__(self, path, verb=False):
        # get ctypes object #
        SysMgr.importPkgItems("ctypes")

        # init variables #
        self.volume = None
        self.inodeList = {}
        self.failDirList = {}

        # start loading classes #
        # refer to https://github.com/cubinator/ext4 #
        class Ext4Error(Exception):
            """
            Base class for all custom errors
            """

            pass

        class BlockMapError(Ext4Error):
            # a requested file_block is not mapped to disk
            pass

        class EndOfStreamError(Ext4Error):
            # BlockReader reads beyond the end of the volume's underlying stream
            pass

        class MagicError(Ext4Error):
            # a structures magic value is wrong and ignoreMagic is False
            pass

        class ext4_struct(LittleEndianStructure):
            """
            Simplifies access to *_lo and *_hi fields
            """

            def __getattr__(self, name):
                """
                Enables reading *_lo and *_hi fields together.
                """
                try:
                    # Combining *_lo and *_hi fields
                    lo_field = LittleEndianStructure.__getattribute__(
                        type(self), name + "_lo"
                    )
                    size = lo_field.size

                    lo = lo_field.__get__(self)
                    hi = LittleEndianStructure.__getattribute__(
                        self, name + "_hi"
                    )

                    return (hi << (8 * size)) | lo
                except AttributeError:
                    return LittleEndianStructure.__getattribute__(self, name)

            def __setattr__(self, name, value):
                """
                Enables setting *_lo and *_hi fields together.
                """
                try:
                    # Combining *_lo and *_hi fields
                    lo_field = (
                        lo_field
                    ) = LittleEndianStructure.__getattribute__(
                        type(self), name + "_lo"
                    )
                    size = lo_field.size

                    lo_field.__set__(self, value & ((1 << (8 * size)) - 1))
                    LittleEndianStructure.__setattr__(
                        self, name + "_hi", value >> (8 * size)
                    )
                except AttributeError:
                    LittleEndianStructure.__setattr__(self, name, value)

        class ext4_dir_entry_2(ext4_struct):
            _fields_ = [
                ("inode", c_uint),  # 0x0
                ("rec_len", c_ushort),  # 0x4
                ("name_len", c_ubyte),  # 0x6
                ("file_type", c_ubyte)  # 0x7
                # Variable length field "name" missing at 0x8
            ]

            @classmethod
            def _from_buffer_copy(cls, raw, offset=0, platform64=True):
                # pylint: disable=no-self-argument
                struct = ext4_dir_entry_2.from_buffer_copy(raw, offset)
                struct.name = raw[
                    offset + 0x8 : offset + 0x8 + struct.name_len
                ]
                return struct

        class ext4_extent(ext4_struct):
            _fields_ = [
                ("ee_block", c_uint),  # 0x0000
                ("ee_len", c_ushort),  # 0x0004
                ("ee_start_hi", c_ushort),  # 0x0006
                ("ee_start_lo", c_uint),  # 0x0008
            ]

        class ext4_extent_header(ext4_struct):
            _fields_ = [
                ("eh_magic", c_ushort),  # 0x0000, Must be 0xF30A
                ("eh_entries", c_ushort),  # 0x0002
                ("eh_max", c_ushort),  # 0x0004
                ("eh_depth", c_ushort),  # 0x0006
                ("eh_generation", c_uint),  # 0x0008
            ]

        class ext4_extent_idx(ext4_struct):
            _fields_ = [
                ("ei_block", c_uint),  # 0x0000
                ("ei_leaf_lo", c_uint),  # 0x0004
                ("ei_leaf_hi", c_ushort),  # 0x0008
                ("ei_unused", c_ushort),  # 0x000A
            ]

        class ext4_group_descriptor(ext4_struct):
            _fields_ = [
                ("bg_block_bitmap_lo", c_uint),  # 0x0000
                ("bg_inode_bitmap_lo", c_uint),  # 0x0004
                ("bg_inode_table_lo", c_uint),  # 0x0008
                ("bg_free_blocks_count_lo", c_ushort),  # 0x000C
                ("bg_free_inodes_count_lo", c_ushort),  # 0x000E
                ("bg_used_dirs_count_lo", c_ushort),  # 0x0010
                ("bg_flags", c_ushort),  # 0x0012
                ("bg_exclude_bitmap_lo", c_uint),  # 0x0014
                ("bg_block_bitmap_csum_lo", c_ushort),  # 0x0018
                ("bg_inode_bitmap_csum_lo", c_ushort),  # 0x001A
                ("bg_itable_unused_lo", c_ushort),  # 0x001C
                ("bg_checksum", c_ushort),  # 0x001E
                # 64-bit fields
                ("bg_block_bitmap_hi", c_uint),  # 0x0020
                ("bg_inode_bitmap_hi", c_uint),  # 0x0024
                ("bg_inode_table_hi", c_uint),  # 0x0028
                ("bg_free_blocks_count_hi", c_ushort),  # 0x002C
                ("bg_free_inodes_count_hi", c_ushort),  # 0x002E
                ("bg_used_dirs_count_hi", c_ushort),  # 0x0030
                ("bg_itable_unused_hi", c_ushort),  # 0x0032
                ("bg_exclude_bitmap_hi", c_uint),  # 0x0034
                ("bg_block_bitmap_csum_hi", c_ushort),  # 0x0038
                ("bg_inode_bitmap_csum_hi", c_ushort),  # 0x003A
                ("bg_reserved", c_uint),  # 0x003C
            ]

            @classmethod
            def _from_buffer_copy(cls, raw, offset=0, platform64=True):
                # pylint: disable=no-self-argument
                struct = ext4_group_descriptor.from_buffer_copy(raw, offset)

                if not platform64:
                    struct.bg_block_bitmap_hi = 0
                    struct.bg_inode_bitmap_hi = 0
                    struct.bg_inode_table_hi = 0
                    struct.bg_free_blocks_count_hi = 0
                    struct.bg_free_inodes_count_hi = 0
                    struct.bg_used_dirs_count_hi = 0
                    struct.bg_itable_unused_hi = 0
                    struct.bg_exclude_bitmap_hi = 0
                    struct.bg_block_bitmap_csum_hi = 0
                    struct.bg_inode_bitmap_csum_hi = 0
                    struct.bg_reserved = 0

                return struct

        class ext4_inode(ext4_struct):
            """
            Every field passing 128 bytes is "additional data",
            whose size is specified by i_extra_isize.
            """

            EXT2_GOOD_OLD_INODE_SIZE = 128

            # i_mode
            S_IXOTH = 0x1  # Others can execute
            S_IWOTH = 0x2  # Others can write
            S_IROTH = 0x4  # Others can read
            S_IXGRP = 0x8  # Group can execute
            S_IWGRP = 0x10  # Group can write
            S_IRGRP = 0x20  # Group can read
            S_IXUSR = 0x40  # Owner can execute
            S_IWUSR = 0x80  # Owner can write
            S_IRUSR = 0x100  # Owner can read
            S_ISVTX = 0x200  # Sticky bit (only owner can delete)
            S_ISGID = 0x400  # Set GID
            S_ISUID = 0x800  # Set UID
            S_IFIFO = 0x1000  # FIFO device (named pipe)
            S_IFCHR = 0x2000  # Character device
            S_IFDIR = 0x4000  # Directory
            S_IFBLK = 0x6000  # Block device
            S_IFREG = 0x8000  # Regular file
            S_IFLNK = 0xA000  # Symbolic link
            S_IFSOCK = 0xC000  # Socket

            # i_flags
            EXT4_INDEX_FL = 0x1000  # Uses hash trees
            EXT4_EXTENTS_FL = 0x80000  # Uses extents
            EXT4_EA_INODE_FL = 0x200000  # Inode stores large xattr
            EXT4_INLINE_DATA_FL = 0x10000000  # Has inline data

            _fields_ = [
                ("i_mode", c_ushort),  # 0x0000
                ("i_uid_lo", c_ushort),  # 0x0002
                ("i_size_lo", c_uint),  # 0x0004
                ("i_atime", c_uint),  # 0x0008
                ("i_ctime", c_uint),  # 0x000C
                ("i_mtime", c_uint),  # 0x0010
                ("i_dtime", c_uint),  # 0x0014
                ("i_gid_lo", c_ushort),  # 0x0018
                ("i_links_count", c_ushort),  # 0x001A
                ("i_blocks_lo", c_uint),  # 0x001C
                ("i_flags", c_uint),  # 0x0020
                ("osd1", c_uint),  # 0x0024
                ("i_block", c_uint * 15),  # 0x0028
                ("i_generation", c_uint),  # 0x0064
                ("i_file_acl_lo", c_uint),  # 0x0068
                ("i_size_hi", c_uint),  # 0x006C
                ("i_obso_faddr", c_uint),  # 0x0070
                ("i_osd2_blocks_high", c_ushort),  # 0x0074
                ("i_file_acl_hi", c_ushort),  # 0x0076
                ("i_uid_hi", c_ushort),  # 0x0078
                ("i_gid_hi", c_ushort),  # 0x007A
                ("i_osd2_checksum_lo", c_ushort),  # 0x007C
                ("i_osd2_reserved", c_ushort),  # 0x007E
                ("i_extra_isize", c_ushort),  # 0x0080
                ("i_checksum_hi", c_ushort),  # 0x0082
                ("i_ctime_extra", c_uint),  # 0x0084
                ("i_mtime_extra", c_uint),  # 0x0088
                ("i_atime_extra", c_uint),  # 0x008C
                ("i_crtime", c_uint),  # 0x0090
                ("i_crtime_extra", c_uint),  # 0x0094
                ("i_version_hi", c_uint),  # 0x0098
                ("i_projid", c_uint),  # 0x009C
            ]

        class ext4_superblock(ext4_struct):
            # Default value for s_desc_size, if INCOMPAT_64BIT is not set
            EXT2_MIN_DESC_SIZE = 0x20
            # Default value for s_desc_size, if INCOMPAT_64BIT is set
            EXT2_MIN_DESC_SIZE_64BIT = 0x40

            # s_feature_incompat
            # Uses 64-bit features
            INCOMPAT_64BIT = 0x80
            # Directory entries record file type (instead of inode flags)
            INCOMPAT_FILETYPE = 0x2

            _fields_ = [
                ("s_inodes_count", c_uint),  # 0x0000
                ("s_blocks_count_lo", c_uint),  # 0x0004
                ("s_r_blocks_count_lo", c_uint),  # 0x0008
                ("s_free_blocks_count_lo", c_uint),  # 0x000C
                ("s_free_inodes_count", c_uint),  # 0x0010
                ("s_first_data_block", c_uint),  # 0x0014
                ("s_log_block_size", c_uint),  # 0x0018
                ("s_log_cluster_size", c_uint),  # 0x001C
                ("s_blocks_per_group", c_uint),  # 0x0020
                ("s_clusters_per_group", c_uint),  # 0x0024
                ("s_inodes_per_group", c_uint),  # 0x0028
                ("s_mtime", c_uint),  # 0x002C
                ("s_wtime", c_uint),  # 0x0030
                ("s_mnt_count", c_ushort),  # 0x0034
                ("s_max_mnt_count", c_ushort),  # 0x0036
                ("s_magic", c_ushort),  # 0x0038
                ("s_state", c_ushort),  # 0x003A
                ("s_errors", c_ushort),  # 0x003C
                ("s_minor_rev_level", c_ushort),  # 0x003E
                ("s_lastcheck", c_uint),  # 0x0040
                ("s_checkinterval", c_uint),  # 0x0044
                ("s_creator_os", c_uint),  # 0x0048
                ("s_rev_level", c_uint),  # 0x004C
                ("s_def_resuid", c_ushort),  # 0x0050
                ("s_def_resgid", c_ushort),  # 0x0052
                ("s_first_ino", c_uint),  # 0x0054
                ("s_inode_size", c_ushort),  # 0x0058
                ("s_block_group_nr", c_ushort),  # 0x005A
                ("s_feature_compat", c_uint),  # 0x005C
                ("s_feature_incompat", c_uint),  # 0x0060
                ("s_feature_ro_compat", c_uint),  # 0x0064
                ("s_uuid", c_ubyte * 16),  # 0x0068
                ("s_volume_name", c_char * 16),  # 0x0078
                ("s_last_mounted", c_char * 64),  # 0x0088
                ("s_algorithm_usage_bitmap", c_uint),  # 0x00C8
                ("s_prealloc_blocks", c_ubyte),  # 0x00CC
                ("s_prealloc_dir_blocks", c_ubyte),  # 0x00CD
                ("s_reserved_gdt_blocks", c_ushort),  # 0x00CE
                ("s_journal_uuid", c_ubyte * 16),  # 0x00D0
                ("s_journal_inum", c_uint),  # 0x00E0
                ("s_journal_dev", c_uint),  # 0x00E4
                ("s_last_orphan", c_uint),  # 0x00E8
                ("s_hash_seed", c_uint * 4),  # 0x00EC
                ("s_def_hash_version", c_ubyte),  # 0x00FC
                ("s_jnl_backup_type", c_ubyte),  # 0x00FD
                ("s_desc_size", c_ushort),  # 0x00FE
                ("s_default_mount_opts", c_uint),  # 0x0100
                ("s_first_meta_bg", c_uint),  # 0x0104
                ("s_mkfs_time", c_uint),  # 0x0108
                ("s_jnl_blocks", c_uint * 17),  # 0x010C
                # 64-bit fields
                ("s_blocks_count_hi", c_uint),  # 0x0150
                ("s_r_blocks_count_hi", c_uint),  # 0x0154
                ("s_free_blocks_count_hi", c_uint),  # 0x0158
                ("s_min_extra_isize", c_ushort),  # 0x015C
                ("s_want_extra_isize", c_ushort),  # 0x015E
                ("s_flags", c_uint),  # 0x0160
                ("s_raid_stride", c_ushort),  # 0x0164
                ("s_mmp_interval", c_ushort),  # 0x0166
                ("s_mmp_block", c_ulonglong),  # 0x0168
                ("s_raid_stripe_width", c_uint),  # 0x0170
                ("s_log_groups_per_flex", c_ubyte),  # 0x0174
                ("s_checksum_type", c_ubyte),  # 0x0175
                ("s_reserved_pad", c_ushort),  # 0x0176
                ("s_kbytes_written", c_ulonglong),  # 0x0178
                ("s_snapshot_inum", c_uint),  # 0x0180
                ("s_snapshot_id", c_uint),  # 0x0184
                ("s_snapshot_r_blocks_count", c_ulonglong),  # 0x0188
                ("s_snapshot_list", c_uint),  # 0x0190
                ("s_error_count", c_uint),  # 0x0194
                ("s_first_error_time", c_uint),  # 0x0198
                ("s_first_error_ino", c_uint),  # 0x019C
                ("s_first_error_block", c_ulonglong),  # 0x01A0
                ("s_first_error_func", c_ubyte * 32),  # 0x01A8
                ("s_first_error_line", c_uint),  # 0x01C8
                ("s_last_error_time", c_uint),  # 0x01CC
                ("s_last_error_ino", c_uint),  # 0x01D0
                ("s_last_error_line", c_uint),  # 0x01D4
                ("s_last_error_block", c_ulonglong),  # 0x01D8
                ("s_last_error_func", c_ubyte * 32),  # 0x01E0
                ("s_mount_opts", c_ubyte * 64),  # 0x0200
                ("s_usr_quota_inum", c_uint),  # 0x0240
                ("s_grp_quota_inum", c_uint),  # 0x0244
                ("s_overhead_blocks", c_uint),  # 0x0248
                ("s_backup_bgs", c_uint * 2),  # 0x024C
                ("s_encrypt_algos", c_ubyte * 4),  # 0x0254
                ("s_encrypt_pw_salt", c_ubyte * 16),  # 0x0258
                ("s_lpf_ino", c_uint),  # 0x0268
                ("s_prj_quota_inum", c_uint),  # 0x026C
                ("s_checksum_seed", c_uint),  # 0x0270
                ("s_reserved", c_uint * 98),  # 0x0274
                ("s_checksum", c_uint),  # 0x03FC
            ]

            @classmethod
            def _from_buffer_copy(cls, raw, platform64=True):
                # pylint: disable=no-self-argument
                struct = ext4_superblock.from_buffer_copy(raw)

                if not platform64:
                    struct.s_blocks_count_hi = 0
                    struct.s_r_blocks_count_hi = 0
                    struct.s_free_blocks_count_hi = 0
                    struct.s_min_extra_isize = 0
                    struct.s_want_extra_isize = 0
                    struct.s_flags = 0
                    struct.s_raid_stride = 0
                    struct.s_mmp_interval = 0
                    struct.s_mmp_block = 0
                    struct.s_raid_stripe_width = 0
                    struct.s_log_groups_per_flex = 0
                    struct.s_checksum_type = 0
                    struct.s_reserved_pad = 0
                    struct.s_kbytes_written = 0
                    struct.s_snapshot_inum = 0
                    struct.s_snapshot_id = 0
                    struct.s_snapshot_r_blocks_count = 0
                    struct.s_snapshot_list = 0
                    struct.s_error_count = 0
                    struct.s_first_error_time = 0
                    struct.s_first_error_ino = 0
                    struct.s_first_error_block = 0
                    struct.s_first_error_func = 0
                    struct.s_first_error_line = 0
                    struct.s_last_error_time = 0
                    struct.s_last_error_ino = 0
                    struct.s_last_error_line = 0
                    struct.s_last_error_block = 0
                    struct.s_last_error_func = 0
                    struct.s_mount_opts = 0
                    struct.s_usr_quota_inum = 0
                    struct.s_grp_quota_inum = 0
                    struct.s_overhead_blocks = 0
                    struct.s_backup_bgs = 0
                    struct.s_encrypt_algos = 0
                    struct.s_encrypt_pw_salt = 0
                    struct.s_lpf_ino = 0
                    struct.s_prj_quota_inum = 0
                    struct.s_checksum_seed = 0
                    struct.s_reserved = 0
                    struct.s_checksum = 0

                if struct.s_desc_size != 0:
                    return struct

                if (
                    struct.s_feature_incompat & ext4_superblock.INCOMPAT_64BIT
                ) == 0:
                    struct.s_desc_size = ext4_superblock.EXT2_MIN_DESC_SIZE
                else:
                    struct.s_desc_size = (
                        ext4_superblock.EXT2_MIN_DESC_SIZE_64BIT
                    )

                return struct

        class ext4_xattr_entry(ext4_struct):
            _fields_ = [
                ("e_name_len", c_ubyte),  # 0x00
                ("e_name_index", c_ubyte),  # 0x01
                ("e_value_offs", c_ushort),  # 0x02
                ("e_value_inum", c_uint),  # 0x04
                ("e_value_size", c_uint),  # 0x08
                ("e_hash", c_uint)  # 0x0C
                # Variable length field "e_name" missing at 0x10
            ]

            @classmethod
            def _from_buffer_copy(cls, raw, offset=0, platform64=True):
                # pylint: disable=no-self-argument
                struct = ext4_xattr_entry.from_buffer_copy(raw, offset)
                struct.e_name = raw[
                    offset + 0x10 : offset + 0x10 + struct.e_name_len
                ]
                return struct

            @property
            def _size(self):
                # 4-byte alignment
                return 4 * ((sizeof(type(self)) + self.e_name_len + 3) // 4)

        class ext4_xattr_header(ext4_struct):
            _fields_ = [
                ("h_magic", c_uint),  # 0x0, Must be 0xEA020000
                ("h_refcount", c_uint),  # 0x4
                ("h_blocks", c_uint),  # 0x8
                ("h_hash", c_uint),  # 0xC
                ("h_checksum", c_uint),  # 0x10
                ("h_reserved", c_uint * 3),  # 0x14
            ]

        class ext4_xattr_ibody_header(ext4_struct):
            _fields_ = [("h_magic", c_uint)]  # 0x0, Must be 0xEA020000

        class InodeType:
            UNKNOWN = 0x0  # Unknown file type
            FILE = 0x1  # Regular file
            DIRECTORY = 0x2  # Directory
            CHARACTER_DEVICE = 0x3  # Character device
            BLOCK_DEVICE = 0x4  # Block device
            FIFO = 0x5  # FIFO
            SOCKET = 0x6  # Socket
            SYMBOLIC_LINK = 0x7  # Symbolic link
            CHECKSUM = 0xDE  # Checksum entry

        class MappingEntry:
            """
            Helper class: This class maps blkCnt file blocks
            indexed by fileBlkIdx to the associated disk blocks indexed
            by diskBlkIdx.
            """

            def __init__(self, fileBlkIdx, diskBlkIdx, blkCnt=1):
                """
                Initialize a MappingEntry instance with given fileBlkIdx,
                diskBlkIdx and blkCnt.
                """
                self.fileBlkIdx = fileBlkIdx
                self.diskBlkIdx = diskBlkIdx
                self.blkCnt = blkCnt

            def __iter__(self):
                """
                Can be used to convert an MappingEntry into a tuple
                (fileBlkIdx, diskBlkIdx, blkCnt).
                """
                yield self.fileBlkIdx
                yield self.diskBlkIdx
                yield self.blkCnt

            def __repr__(self):
                return "%s(%s, %s, %s)" % (
                    type(self).__name__,
                    self.fileBlkIdx,
                    self.diskBlkIdx,
                    self.blkCnt,
                )

            def copy(self):
                return MappingEntry(
                    self.fileBlkIdx, self.diskBlkIdx, self.blkCnt
                )

            def create_mapping(*entries):
                # pylint: disable=no-method-argument,no-self-argument
                """
                Converts a list of 2-tuples
                (diskBlkIdx, blkCnt) into a list of MappingEntry instances
                """
                fileBlkIdx = 0
                result = [None] * len(entries)

                for i, entry in enumerate(entries):
                    diskBlkIdx, blkCnt = entry
                    result[i] = MappingEntry(fileBlkIdx, diskBlkIdx, blkCnt)
                    fileBlkIdx += blkCnt

                return result

            @classmethod
            def optimize(cls, entries):
                # pylint: disable=no-self-argument
                """
                Sorts and stiches together a list of MappingEntry instances
                """
                entries = list(entries)
                entries.sort(key=lambda entry: entry.fileBlkIdx)

                idx = 0
                while idx < len(entries):
                    while (
                        (idx + 1 < len(entries))
                        and (
                            entries[idx].fileBlkIdx + entries[idx].blkCnt
                            == entries[idx + 1].fileBlkIdx
                        )
                        and (
                            entries[idx].diskBlkIdx + entries[idx].blkCnt
                            == entries[idx + 1].diskBlkIdx
                        )
                    ):
                        tmp = entries.pop(idx + 1)
                        entries[idx].blkCnt += tmp.blkCnt

                    idx += 1

        class Volume:
            """
            Provides functionality for reading ext4 volumes
            """

            ROOT_INODE = 2

            def __init__(
                self, stream, offset=0, ignoreFlag=False, ignoreMagic=False
            ):
                """
                Initializes a new ext4 reader at a given offset in stream.
                If ignoreMagic is True, no exception will be thrown,
                when a structure with wrong magic number is found.
                Analogously passing True to ignoreFlag suppresses Exception
                caused by wrong flags.
                """
                self.ignoreFlag = ignoreFlag
                self.ignoreMagic = ignoreMagic
                self.offset = offset
                # Initial value needed for Volume.read_struct
                self.platform64 = True
                self.stream = stream

                # Superblock
                self.superblock = self.read_struct(ext4_superblock, 0x400)
                self.platform64 = (
                    self.superblock.s_feature_incompat
                    & ext4_superblock.INCOMPAT_64BIT
                ) != 0

                if not ignoreMagic and self.superblock.s_magic != 0xEF53:
                    raise MagicError(
                        (
                            "Invalid magic value in superblock: "
                            "0x%04X (expected 0xEF53)"
                        )
                        % self.superblock.s_magic
                    )

                # Group descriptors
                self.group_descriptors = [None] * (
                    self.superblock.s_inodes_count
                    // self.superblock.s_inodes_per_group
                )

                # First block after superblock
                group_desc_table_offset = (
                    0x400 // self.block_size + 1
                ) * self.block_size
                for group_desc_idx in xrange(len(self.group_descriptors)):
                    group_desc_offset = (
                        group_desc_table_offset
                        + group_desc_idx * self.superblock.s_desc_size
                    )
                    self.group_descriptors[group_desc_idx] = self.read_struct(
                        ext4_group_descriptor, group_desc_offset
                    )

            def __repr__(self):
                return "%s(volume_name = %s, uuid = %s, last_mounted = %s)" % (
                    type(self).__name__,
                    self.superblock.s_volume_name,
                    self.uuid,
                    self.superblock.s_last_mounted,
                )

            @property
            def block_size(self):
                """
                Returns the volume's block size in bytes.
                """
                return 1 << (10 + self.superblock.s_log_block_size)

            def get_inode(self, inode_idx):
                """
                Returns an Inode instance representing the inode specified
                by its index inode_idx.
                """
                group_idx, inode_table_entry_idx = self.get_inode_group(
                    inode_idx
                )

                inode_table_offset = (
                    self.group_descriptors[group_idx].bg_inode_table
                    * self.block_size
                )
                inode_offset = (
                    inode_table_offset
                    + inode_table_entry_idx * self.superblock.s_inode_size
                )

                return Inode(self, inode_offset, inode_idx)

            def get_inode_group(self, inode_idx):
                """
                Returns a tuple (group_idx, inode_table_entry_idx)
                """
                group_idx = (
                    inode_idx - 1
                ) // self.superblock.s_inodes_per_group
                inode_table_entry_idx = (
                    inode_idx - 1
                ) % self.superblock.s_inodes_per_group
                return (group_idx, inode_table_entry_idx)

            def read(self, offset, byte_len):
                """
                Returns byte_len bytes at offset within this volume.
                """
                if self.offset + offset != self.stream.tell():
                    self.stream.seek(self.offset + offset, os.SEEK_SET)

                return self.stream.read(byte_len)

            def read_struct(self, structure, offset, platform64=None):
                """
                Interprets the bytes at offset as structure and returns
                the interpretd instance
                """
                raw = self.read(offset, sizeof(structure))

                if hasattr(structure, "_from_buffer_copy"):
                    return structure._from_buffer_copy(
                        raw,
                        platform64=platform64
                        if platform64 != None
                        else self.platform64,
                    )
                else:
                    return structure.from_buffer_copy(raw)

            @property
            def root(self):
                """
                Returns the volume's root inode
                """
                return self.get_inode(Volume.ROOT_INODE)

            @property
            def uuid(self):
                """
                Returns the volume's UUID in the format
                XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX.
                """
                uuid = self.superblock.s_uuid
                uuid = [uuid[:4], uuid[4:6], uuid[6:8], uuid[8:10], uuid[10:]]
                return "-".join(
                    "".join("%02X" % c for c in part) for part in uuid
                )

        class Inode:
            """
            Provides functionality for parsing inodes and accessing raw data
            """

            def __init__(self, volume, offset, inode_idx):
                """
                Initializes a new inode parser at the specified offset
                within the specified volume. file_type is the file type
                of the inode as given by the directory entry
                referring to this inode.
                """
                self.inode_idx = inode_idx
                self.offset = offset
                self.volume = volume
                self.inode = volume.read_struct(ext4_inode, offset)

            def __len__(self):
                """
                Returns the length in bytes of the content
                referenced by this inode.
                """
                return int(self.inode.i_size)

            def __repr__(self):
                if self.inode_idx != None:
                    return (
                        "%s(inode_idx = %s, offset = 0x%X, volume_uuid = %s"
                    ) % (
                        type(self).__name__,
                        self.inode_idx,
                        self.offset,
                        self.volume.uuid,
                    )
                else:
                    return "%s(offset = 0x%X, volume_uuid = %s)" % (
                        type(self).__name__,
                        self.offset,
                        self.volume.uuid,
                    )

            def _parse_xattrs(self, raw_data, offset, prefix_override={}):
                """
                Generator: Parses raw_data (bytes) as ext4_xattr_entry
                structures and their referenced xattr values and yields
                tuples (xattr_name, xattr_value) where xattr_name (str)
                is the attribute name including its prefix and
                xattr_value (bytes) is the raw attribute value.
                raw_data must start with the first ext4_xattr_entry
                structure and offset specifies the offset to the "block start"
                for ext4_xattr_entry.e_value_offs.
                prefix_overrides allows specifying attributes apart from
                the default prefixes. The default prefix dictionary is
                updated with prefix_overrides.
                """
                prefixes = {
                    0: "",
                    1: "user.",
                    2: "system.posix_acl_access",
                    3: "system.posix_acl_default",
                    4: "trusted.",
                    6: "security.",
                    7: "system.",
                    8: "system.richacl",
                }
                prefixes.update(prefixes)

                # Iterator over ext4_xattr_entry structures
                i = 0
                while i < len(raw_data):
                    xattr_entry = ext4_xattr_entry._from_buffer_copy(
                        raw_data, i, platform64=self.volume.platform64
                    )

                    if (
                        xattr_entry.e_name_len
                        | xattr_entry.e_name_index
                        | xattr_entry.e_value_offs
                        | xattr_entry.e_value_inum
                    ) == 0:
                        # End of ext4_xattr_entry list
                        break

                    if not xattr_entry.e_name_index in prefixes:
                        raise Ext4Error(
                            "Unknown attribute prefix %d in inode %d"
                            % (xattr_entry.e_name_idx, self.inode_idx)
                        )

                    xattr_name = prefixes[
                        xattr_entry.e_name_index
                    ] + xattr_entry.e_name.decode("iso-8859-2")

                    if xattr_entry.e_value_inum != 0:
                        # external xattr
                        xattr_inode = self.volume.get_inode(
                            xattr_entry.e_value_inum
                        )

                        if (
                            not self.volume.ignoreFlag
                            and (
                                xattr_inode.inode.i_flags
                                & ext4_inode.EXT4_EA_INODE_FL
                            )
                            != 0
                        ):
                            raise Ext4Error(
                                (
                                    "Inode %d associated with the extended "
                                    "attribute %s of inode %d is not marked as "
                                    "large extended attribute value."
                                )
                                % (
                                    xattr_inode.inode_idx,
                                    xattr_name,
                                    self.inode_idx,
                                )
                            )

                        # TODO Use xattr_entry.e_value_size or xattr_inode.inode.i_size?
                        xattr_value = xattr_inode.open_read().read()
                    else:
                        # internal xattr
                        start = xattr_entry.e_value_offs + offset
                        end = (
                            xattr_entry.e_value_offs
                            + offset
                            + xattr_entry.e_value_size
                        )
                        xattr_value = raw_data[start:end]

                    yield (xattr_name, xattr_value)

                    i += xattr_entry._size

            def get_inode(self, *relative_path):
                """
                Returns the inode specified by the path relative_path
                (list of entry names) relative to this inode. "." and ".."
                usually are supported too, however in special cases
                (e.g. manually crafted volumes) they might not be supported
                due to them being real on-disk directory entries that
                might be missing or pointing somewhere else.
                decode_name is directly passed to open_dir.
                NOTE: Whitespaces will not be trimmed off the path's parts
                and "\\0" and "\\0\\0" as well as b"\\0" and b"\\0\\0" are
                seen as different names
                (unless decode_name actually trims the name).
                NOTE: Along the path file_type != FILETYPE_DIR will be
                ignored, however i_flags will not be ignored.
                """
                if not self.is_dir:
                    raise Ext4Error(
                        "Inode %d is not a directory" % self.inode_idx
                    )

                current_inode = self
                decode_name = None

                for i, part in enumerate(relative_path):
                    if not self.volume.ignoreFlag and not current_inode.is_dir:
                        current_path = "/".join(relative_path[:i])
                        raise Ext4Error(
                            "%s (Inode %d) is not a directory."
                            % (current_path, self.inode_idx)
                        )

                    file_name, inode_idx, file_type = next(
                        filter(
                            lambda entry: entry[0] == part,
                            current_inode.open_dir(decode_name),
                        ),
                        (None, None, None),
                    )

                    if inode_idx is None:
                        current_path = "/".join(relative_path[:i])
                        raise FileNotFoundError(
                            "%s not found in %s (Inode %d)"
                            % (part, current_path, current_inode.inode_idx)
                        )

                    current_inode = current_inode.volume.get_inode(inode_idx)

                return current_inode

            @property
            def is_dir(self):
                """
                Indicates whether the inode is marked as a directory.
                """
                return (self.inode.i_mode & ext4_inode.S_IFDIR) != 0

            @property
            def is_file(self):
                """
                Indicates whether the inode is marker as a regular file.
                """
                return (self.inode.i_mode & ext4_inode.S_IFREG) != 0

            @property
            def is_in_use(self):
                """
                Indicates whether the inode's associated bit
                in the inode bitmap is set.
                """
                group_idx, bitmap_bit = self.volume.get_inode_group(
                    self.inode_idx
                )

                inode_usage_bitmap_offset = (
                    self.volume.group_descriptors[group_idx].bg_inode_bitmap
                    * self.volume.block_size
                )
                inode_usage_byte = self.volume.read(
                    inode_usage_bitmap_offset + bitmap_bit // 8, 1
                )[0]

                return ((inode_usage_byte >> (7 - bitmap_bit % 8)) & 1) != 0

            @property
            def mode_str(self):
                """
                Returns the inode's permissions in form of a unix string
                (e.g. "-rwxrw-rw" or "drwxr-xr--").
                """
                special_flag = lambda letter, execute, special: {
                    (False, False): "-",
                    (False, True): letter.upper(),
                    (True, False): "x",
                    (True, True): letter.lower(),
                }[(execute, special)]

                try:
                    device_type = {
                        ext4_inode.S_IFIFO: "p",
                        ext4_inode.S_IFCHR: "c",
                        ext4_inode.S_IFDIR: "d",
                        ext4_inode.S_IFBLK: "b",
                        ext4_inode.S_IFREG: "-",
                        ext4_inode.S_IFLNK: "l",
                        ext4_inode.S_IFSOCK: "s",
                    }[self.inode.i_mode & 0xF000]
                except KeyError:
                    device_type = "?"

                return "".join(
                    [
                        device_type,
                        "r"
                        if (self.inode.i_mode & ext4_inode.S_IRUSR) != 0
                        else "-",
                        "w"
                        if (self.inode.i_mode & ext4_inode.S_IWUSR) != 0
                        else "-",
                        special_flag(
                            "s",
                            (self.inode.i_mode & ext4_inode.S_IXUSR) != 0,
                            (self.inode.i_mode & ext4_inode.S_ISUID) != 0,
                        ),
                        "r"
                        if (self.inode.i_mode & ext4_inode.S_IRGRP) != 0
                        else "-",
                        "w"
                        if (self.inode.i_mode & ext4_inode.S_IWGRP) != 0
                        else "-",
                        special_flag(
                            "s",
                            (self.inode.i_mode & ext4_inode.S_IXGRP) != 0,
                            (self.inode.i_mode & ext4_inode.S_ISGID) != 0,
                        ),
                        "r"
                        if (self.inode.i_mode & ext4_inode.S_IROTH) != 0
                        else "-",
                        "w"
                        if (self.inode.i_mode & ext4_inode.S_IWOTH) != 0
                        else "-",
                        special_flag(
                            "t",
                            (self.inode.i_mode & ext4_inode.S_IXOTH) != 0,
                            (self.inode.i_mode & ext4_inode.S_ISVTX) != 0,
                        ),
                    ]
                )

            def open_dir(self, decode_name=None, path=None):
                """
                Generator: Yields the directory entries as tuples
                (decode_name(name), inode, file_type) in their on-disk order,
                where name is the raw on-disk directory entry name (bytes).
                file_type is one of the Inode.IT_* constants. For
                special cases (e.g. invalid utf8 characters in entry names)
                you can try a different decoder
                (e.g. decode_name = lambda raw: raw).
                Default of decode_name = lambda raw: raw.decode("utf8")
                """
                # Parse args
                if decode_name is None:
                    # decode_name = lambda raw: raw.decode("utf8")
                    decode_name = lambda raw: raw.decode("latin-1")

                if not self.volume.ignoreFlag and not self.is_dir:
                    raise Ext4Error(
                        "Inode (%d) is not a directory (%s)"
                        % (self.inode_idx, path)
                    )

                # Hash trees are compatible with linear arrays
                if (self.inode.i_flags & ext4_inode.EXT4_INDEX_FL) != 0:
                    SysMgr.printWarn(
                        "hash trees are not implemented yet for %s" % path
                    )
                    return
                    # TODO: implement hash tree parser #
                    raise NotImplementedError(
                        "Hash trees are not implemented yet for %s" % path
                    )

                # Read raw directory content
                raw_data = self.open_read().read()
                offset = 0

                while offset < len(raw_data):
                    dirent = ext4_dir_entry_2._from_buffer_copy(
                        raw_data, offset, platform64=self.volume.platform64
                    )

                    if dirent.file_type != InodeType.CHECKSUM:
                        yield (
                            decode_name(dirent.name),
                            dirent.inode,
                            dirent.file_type,
                        )

                    offset += dirent.rec_len

            def open_read(self):
                """
                Returns an BlockReader instance for reading this inode's
                raw content.
                """
                if (self.inode.i_flags & ext4_inode.EXT4_EXTENTS_FL) != 0:
                    # Obtain mapping from extents
                    mapping = []  # List of MappingEntry instances

                    nodes = []
                    nodes.append(self.offset + ext4_inode.i_block.offset)

                    while nodes:
                        header_offset = nodes.pop(0)
                        header = self.volume.read_struct(
                            ext4_extent_header, header_offset
                        )

                        if (
                            not self.volume.ignoreMagic
                            and header.eh_magic != 0xF30A
                        ):
                            raise MagicError(
                                (
                                    "Invalid magic value in extent header at "
                                    "offset 0x%X of inode %d: 0x%04X "
                                    "(expected 0xF30A)"
                                )
                                % (
                                    header_offset,
                                    self.inode_idx,
                                    header.eh_magic,
                                )
                            )

                        if header.eh_depth != 0:
                            indices = self.volume.read_struct(
                                ext4_extent_idx * header.eh_entries,
                                header_offset + sizeof(ext4_extent_header),
                            )
                            for idx in indices:
                                nodes.append(
                                    idx.ei_leaf * self.volume.block_size
                                )
                        else:
                            extents = self.volume.read_struct(
                                ext4_extent * header.eh_entries,
                                header_offset + sizeof(ext4_extent_header),
                            )
                            for extent in extents:
                                mapping.append(
                                    MappingEntry(
                                        extent.ee_block,
                                        extent.ee_start,
                                        extent.ee_len,
                                    )
                                )

                    MappingEntry.optimize(mapping)
                    return BlockReader(self.volume, len(self), mapping)
                else:
                    # Inode uses inline data
                    i_block = self.volume.read(
                        self.offset + ext4_inode.i_block.offset,
                        ext4_inode.i_block.size,
                    )
                    return SysMgr.getPkg("io").BytesIO(
                        i_block[: self.inode.i_size]
                    )

            def xattrs(
                self,
                check_inline=True,
                check_block=True,
                force_inline=False,
                prefix_override={},
            ):
                """
                Generator: Yields the inode's extended attributes as tuples
                (name, value) in their on-disk order, where name (str)
                is the on-disk attribute name including its resolved name
                prefix and value (bytes) is the raw attribute value.
                check_inline and check_block control where to read attributes
                (the inode's inline data and/or the external data block
                pointed to by i_file_acl) and if check_inline as well as
                force_inline are set to True, the inode's inline data
                will not be verified to contain actual extended attributes
                and instead is just interpretd as such. prefix_overrides
                is directly passed to Inode._parse_xattrs.
                """
                # Inline xattrs
                inline_data_offset = (
                    self.offset
                    + ext4_inode.EXT2_GOOD_OLD_INODE_SIZE
                    + self.inode.i_extra_isize
                )
                inline_data_length = (
                    self.offset
                    + self.volume.superblock.s_inode_size
                    - inline_data_offset
                )

                if check_inline and inline_data_length > sizeof(
                    ext4_xattr_ibody_header
                ):
                    inline_data = self.volume.read(
                        inline_data_offset, inline_data_length
                    )
                    xattrs_header = ext4_xattr_ibody_header.from_buffer_copy(
                        inline_data
                    )

                    """
                    TODO: Find way to detect inline xattrs without checking
                    the h_magic field to enable error detection with
                    the h_magic field.
                    """
                    if force_inline or xattrs_header.h_magic == 0xEA020000:
                        # The ext4_xattr_entry following the header is aligned on a 4-byte boundary
                        offset = 4 * (
                            (sizeof(ext4_xattr_ibody_header) + 3) // 4
                        )
                        for xattr_name, xattr_value in self._parse_xattrs(
                            inline_data[offset:],
                            0,
                            prefix_override=prefix_override,
                        ):
                            yield (xattr_name, xattr_value)

                # xattr block(s)
                if check_block and self.inode.i_file_acl != 0:
                    xattrs_block_start = (
                        self.inode.i_file_acl * self.volume.block_size
                    )
                    xattrs_block = self.volume.read(
                        xattrs_block_start, self.volume.block_size
                    )

                    xattrs_header = ext4_xattr_header.from_buffer_copy(
                        xattrs_block
                    )
                    if (
                        not self.volume.ignoreMagic
                        and xattrs_header.h_magic != 0xEA020000
                    ):
                        raise MagicError(
                            (
                                "Invalid magic value in xattrs block header at "
                                "offset 0x%X of inode %d: 0x%X "
                                "(expected 0xEA020000)"
                            )
                            % (
                                xattrs_block_start,
                                self.inode_idx,
                                xattrs_header.h_magic,
                            )
                        )

                    if xattrs_header.h_blocks != 1:
                        raise Ext4Error(
                            "Invalid number of xattr blocks at offset "
                            "0x%X of inode %d: %d (expected 1)"
                            % (
                                xattrs_block_start,
                                self.inode_idx,
                                xattrs_header.h_blocks,
                            )
                        )

                    # The ext4_xattr_entry following the header is aligned on a 4-byte boundary
                    offset = 4 * ((sizeof(ext4_xattr_header) + 3) // 4)
                    for xattr_name, xattr_value in self._parse_xattrs(
                        xattrs_block[offset:],
                        -offset,
                        prefix_override=prefix_override,
                    ):
                        yield (xattr_name, xattr_value)

        class BlockReader:
            """
            Maps disk blocks into a linear byte stream.
            NOTE: This class does not implement buffering or caching.
            """

            # OSError
            EINVAL = 22

            def __init__(self, volume, byte_size, block_map):
                """
                Initializes a new block reader on the specified volume.
                mapping must be a list of MappingEntry instances. If
                you prefer a way to use 2-tuples (diskBlkIdx, blkCnt)
                with inferred file_block_index entries, see
                MappingEntry.create_mapping.
                """
                self.byte_size = byte_size
                self.volume = volume
                self.cursor = 0
                block_map = list(map(MappingEntry.copy, block_map))

                # Optimize mapping (stich together)
                MappingEntry.optimize(block_map)
                self.block_map = block_map

            def __repr__(self):
                return (
                    "%s(byte_size = %s, block_map = %s, volume_uuid = %s)"
                    % (
                        type(self).__name__,
                        self.byte_size,
                        self.block_map,
                        self.volume.uuid,
                    )
                )

            def get_block_mapping(self, fileBlkIdx):
                """
                Returns the disk block index of the file block specified
                by fileBlkIdx.
                """
                diskBlkIdx = None

                # Find disk block
                for entry in self.block_map:
                    if (
                        entry.fileBlkIdx
                        <= fileBlkIdx
                        < entry.fileBlkIdx + entry.blkCnt
                    ):
                        block_diff = fileBlkIdx - entry.fileBlkIdx
                        diskBlkIdx = entry.diskBlkIdx + block_diff
                        break

                return diskBlkIdx

            def read(self, byte_len=-1):
                """
                Reades up to byte_len bytes from the block device beginning
                at the cursor's current position. This operation will
                not exceed the inode's size. If -1 is passed for byte_len,
                the inode is read to the end.
                """
                # Parse args
                if byte_len < -1:
                    raise ValueError("byte_len must be non-negative or -1")

                bytes_remaining = self.byte_size - self.cursor
                byte_len = (
                    bytes_remaining
                    if byte_len == -1
                    else max(0, min(byte_len, bytes_remaining))
                )

                if byte_len == 0:
                    return b""

                # Reading blocks
                start_block_idx = self.cursor // self.volume.block_size
                end_block_idx = (
                    self.cursor + byte_len - 1
                ) // self.volume.block_size
                end_of_stream_check = byte_len

                blocks = [
                    self.read_block(i)
                    for i in xrange(
                        start_block_idx, end_block_idx - start_block_idx + 1
                    )
                ]

                startOffset = self.cursor % self.volume.block_size
                if startOffset != 0:
                    blocks[0] = blocks[0][startOffset:]
                byte_len = (
                    byte_len + startOffset - self.volume.block_size - 1
                ) % self.volume.block_size + 1
                blocks[-1] = blocks[-1][:byte_len]

                result = b"".join(blocks)

                # Check read
                if len(result) != end_of_stream_check:
                    raise EndOfStreamError(
                        (
                            "The volume's underlying stream ended "
                            "%s bytes before EOF."
                        )
                        % (UtilMgr.convNum(byte_len - len(result)))
                    )

                self.cursor += len(result)
                return result

            def read_block(self, fileBlkIdx):
                """
                Reads one block from disk
                (return a zero-block if the file block is not mapped)
                """
                diskBlkIdx = self.get_block_mapping(fileBlkIdx)

                if diskBlkIdx != None:
                    return self.volume.read(
                        diskBlkIdx * self.volume.block_size,
                        self.volume.block_size,
                    )
                else:
                    return bytes([0] * self.volume.block_size)

            def seek(self, seek, seek_mode=os.SEEK_SET):
                """
                Moves the internal cursor along the file
                (not the disk) and behaves like BufferedReader.seek
                """
                if seek_mode == os.SEEK_CUR:
                    seek += self.cursor
                elif seek_mode == os.SEEK_END:
                    seek += self.byte_size
                elif seek_mode == os.SEEK_SET:
                    seek += 0

                if seek < 0:
                    # Exception behavior copied from IOBase.seek
                    raise OSError(BlockReader.EINVAL, "Invalid argument")

                self.cursor = seek
                return seek

            def tell(self):
                """
                Returns the internal cursor's current file offset.
                """
                return self.cursor

        # open target file #
        try:
            self.fd = open(path, "rb")
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn("failed to open %s" % path, reason=True)
            raise Exception("open failure")

        # init volume object #
        try:
            self.volume = Volume(self.fd)
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn(
                "failed to init EXT4 object for %s'" % path, reason=True
            )

        # check volume object #
        if not self.volume:
            raise Exception("volume failure")

    def getInodeInfo(self, inode=None, path=None):
        if not inode and not path:
            SysMgr.printErr("no input for inode or path to get file meta info")
            return None

        if inode:
            inode = long(inode)
            inodeObj = self.volume.get_inode(inode)
            return inodeObj

        if path:
            parent = self.volume.root
            for item in path.split("/"):
                if not item:
                    continue
                parent = parent.get_inode(item)
            return parent

    def getInodeList(self, start=None, path=None, filters=[], verb=False):
        # define traverse function #
        def _traverseItems(self, start, path, filters, verb):
            if start:
                if isinstance(start, str):
                    path = start.strip()
                    parent = self.volume.root
                    for item in start.split("/"):
                        if not item:
                            continue
                        parent = parent.get_inode(item)
                    start = parent
            else:
                start = self.volume.root
                path = "/"

            # print progress #
            UtilMgr.printProgress()

            # open directory #
            FILE_TYPE = Ext4Analyzer.FILE_TYPE
            try:
                dirnode = start.open_dir(path=path)
                if not dirnode:
                    self.failDirList.setdefault(path, None)
            except SystemExit:
                sys.exit(0)
            except:
                self.failDirList.setdefault(path, None)
                SysMgr.printWarn("failed to open '%s' directory" % None, True)
                return

            # traverse all items #
            for fname, inode, ftype in dirnode:
                if fname in (".", ".."):
                    continue
                elif inode in self.inodeList:
                    continue

                # define attribute #
                ftype = FILE_TYPE[ftype] if ftype in FILE_TYPE else "?"
                inodeObj = self.volume.get_inode(inode)
                fpath = os.path.join(path, fname)

                # check condition #
                if not UtilMgr.isValidStr(
                    str(inode), filters
                ) and not UtilMgr.isValidStr(fpath, filters):
                    pass
                else:
                    self.inodeList[inode] = {
                        "name": fname,
                        "type": ftype,
                        "size": len(inodeObj),
                        "path": path,
                    }

                    if verb:
                        SysMgr.printWarn(
                            "%s [inode: %s] [type: %s] [size: %s]"
                            % (
                                fpath,
                                inode,
                                ftype,
                                UtilMgr.convSize2Unit(len(inodeObj)),
                            ),
                            always=True,
                            newline=False,
                        )

                if ftype == "dir":
                    _traverseItems(self, inodeObj, fpath, filters, verb)
                    continue

        # start traversing all items #
        _traverseItems(self, start, path, filters, verb)

        # return inode list #
        return self.inodeList


'''
class GlMgr(object):
    """Manager for GL"""

    instance = None

    @staticmethod
    def init():
        SysMgr.importPkgItems("ctypes")

        try:
            # load libglesobj library #
            if not SysMgr.libglesObj:
                SysMgr.libglesObj = SysMgr.loadLib(SysMgr.libglesPath)
        except:
            SysMgr.printErr("failed to load GLES object")
            sys.exit(-1)

        gl = GlMgr.instance = SysMgr.libglesObj
        gl.glActiveShaderProgram.argtypes = [c_uint32, c_uint32]
        gl.glActiveShaderProgram.restype = None
        gl.glActiveTexture.argtypes = [c_uint]
        gl.glActiveTexture.restype = None
        gl.glAttachShader.argtypes = [c_uint32, c_uint32]
        gl.glAttachShader.restype = None
        gl.glBeginQuery.argtypes = [c_uint, c_uint32]
        gl.glBeginQuery.restype = None
        gl.glBeginTransformFeedback.argtypes = [c_uint]
        gl.glBeginTransformFeedback.restype = None
        gl.glBindAttribLocation.argtypes = [
            c_uint32,
            c_uint32,
            POINTER(c_char),
        ]
        gl.glBindAttribLocation.restype = None
        gl.glBindBuffer.argtypes = [c_uint, c_uint32]
        gl.glBindBuffer.restype = None
        gl.glBindBufferBase.argtypes = [c_uint, c_uint32, c_uint32]
        gl.glBindBufferBase.restype = None
        gl.glBindBufferRange.argtypes = [
            c_uint,
            c_uint32,
            c_uint32,
            c_size_t,
            c_ssize_t,
        ]
        gl.glBindBufferRange.restype = None
        gl.glBindFramebuffer.argtypes = [c_uint, c_uint32]
        gl.glBindFramebuffer.restype = None
        gl.glBindImageTexture.argtypes = [
            c_uint32,
            c_uint32,
            c_int32,
            c_ubyte,
            c_int32,
            c_uint,
            c_uint,
        ]
        gl.glBindImageTexture.restype = None
        gl.glBindProgramPipeline.argtypes = [c_uint32]
        gl.glBindProgramPipeline.restype = None
        gl.glBindRenderbuffer.argtypes = [c_uint, c_uint32]
        gl.glBindRenderbuffer.restype = None
        gl.glBindSampler.argtypes = [c_uint32, c_uint32]
        gl.glBindSampler.restype = None
        gl.glBindTexture.argtypes = [c_uint, c_uint]
        gl.glBindTexture.restype = None
        gl.glBindTransformFeedback.argtypes = [c_uint, c_uint32]
        gl.glBindTransformFeedback.restype = None
        gl.glBindVertexArray.argtypes = [c_uint32]
        gl.glBindVertexArray.restype = None
        gl.glBindVertexBuffer.argtypes = [
            c_uint32,
            c_uint32,
            c_size_t,
            c_size_t,
        ]
        gl.glBindVertexBuffer.restype = None
        gl.glBlendColor.argtypes = [c_float, c_float, c_float, c_float]
        gl.glBlendColor.restype = None
        gl.glBlendEquation.argtypes = [c_uint]
        gl.glBlendEquation.restype = None
        gl.glBlendEquationSeparate.argtypes = [c_uint, c_uint]
        gl.glBlendEquationSeparate.restype = None
        gl.glBlendEquationSeparatei.argtypes = [c_uint32, c_uint, c_uint]
        gl.glBlendEquationSeparatei.restype = None
        gl.glBlendEquationi.argtypes = [c_uint32, c_uint]
        gl.glBlendEquationi.restype = None
        gl.glBlendFunc.argtypes = [c_uint, c_uint]
        gl.glBlendFunc.restype = None
        gl.glBlendFuncSeparate.argtypes = [c_uint, c_uint, c_uint, c_uint]
        gl.glBlendFuncSeparate.restype = None
        gl.glBlendFuncSeparatei.argtypes = [
            c_uint32,
            c_uint,
            c_uint,
            c_uint,
            c_uint,
        ]
        gl.glBlendFuncSeparatei.restype = None
        gl.glBlendFunci.argtypes = [c_uint32, c_uint, c_uint]
        gl.glBlendFunci.restype = None
        gl.glBlitFramebuffer.argtypes = [
            c_int32,
            c_int32,
            c_int32,
            c_int32,
            c_int32,
            c_int32,
            c_int32,
            c_int32,
            c_uint,
            c_uint,
        ]
        gl.glBlitFramebuffer.restype = None
        gl.glBufferData.argtypes = [c_uint, c_ssize_t, c_void_p, c_uint]
        gl.glBufferData.restype = None
        gl.glBufferSubData.argtypes = [c_uint, c_size_t, c_ssize_t, c_void_p]
        gl.glBufferSubData.restype = None
        gl.glCheckFramebufferStatus.argtypes = [c_uint]
        gl.glCheckFramebufferStatus.restype = c_uint
        gl.glClear.argtypes = [c_uint]
        gl.glClear.restype = None
        gl.glClearBufferfi.argtypes = [c_uint, c_int, c_float, c_int32]
        gl.glClearBufferfi.restype = None
        gl.glClearBufferfv.argtypes = [c_uint, c_int, POINTER(c_float)]
        gl.glClearBufferfv.restype = None
        gl.glClearBufferiv.argtypes = [c_uint, c_int, POINTER(c_int32)]
        gl.glClearBufferiv.restype = None
        gl.glClearBufferuiv.argtypes = [c_uint, c_int, POINTER(c_uint32)]
        gl.glClearBufferuiv.restype = None
        gl.glClearColor.argtypes = [c_float, c_float, c_float, c_float]
        gl.glClearColor.restype = None
        gl.glClearDepthf.argtypes = [c_float]
        gl.glClearDepthf.restype = None
        gl.glClearStencil.argtypes = [c_int]
        gl.glClearStencil.restype = None
        gl.glColorMask.argtypes = [c_ubyte, c_ubyte, c_ubyte, c_ubyte]
        gl.glColorMask.restype = None
        gl.glColorMaski.argtypes = [
            c_uint32,
            c_ubyte,
            c_ubyte,
            c_ubyte,
            c_ubyte,
        ]
        gl.glColorMaski.restype = None
        gl.glCompileShader.argtypes = [c_uint32]
        gl.glCompileShader.restype = None
        gl.glCompressedTexImage2D.argtypes = [
            c_uint,
            c_long,
            c_uint,
            c_size_t,
            c_size_t,
            c_long,
            c_size_t,
            c_void_p,
        ]
        gl.glCompressedTexImage2D.restype = None
        gl.glCompressedTexImage3D.argtypes = [
            c_uint,
            c_long,
            c_uint,
            c_size_t,
            c_size_t,
            c_size_t,
            c_long,
            c_size_t,
            c_void_p,
        ]
        gl.glCompressedTexImage3D.restype = None
        gl.glCompressedTexSubImage2D.argtypes = [
            c_uint,
            c_long,
            c_long,
            c_long,
            c_size_t,
            c_size_t,
            c_uint,
            c_size_t,
            c_void_p,
        ]
        gl.glCompressedTexSubImage2D.restype = None
        gl.glCompressedTexSubImage3D.argtypes = [
            c_uint,
            c_long,
            c_long,
            c_long,
            c_long,
            c_size_t,
            c_size_t,
            c_size_t,
            c_uint,
            c_size_t,
            c_void_p,
        ]
        gl.glCompressedTexSubImage3D.restype = None
        gl.glCopyBufferSubData.argtypes = [
            c_uint,
            c_uint,
            c_size_t,
            c_size_t,
            c_ssize_t,
        ]
        gl.glCopyBufferSubData.restype = None
        gl.glCopyImageSubData.argtypes = [
            c_uint32,
            c_uint,
            c_int32,
            c_int32,
            c_int32,
            c_int32,
            c_uint32,
            c_uint,
            c_int32,
            c_int32,
            c_int32,
            c_int32,
            c_size_t,
            c_size_t,
            c_size_t,
        ]
        gl.glCopyImageSubData.restype = None
        gl.glCopyTexImage2D.argtypes = [
            c_uint,
            c_long,
            c_uint,
            c_int,
            c_int,
            c_size_t,
            c_size_t,
            c_long,
        ]
        gl.glCopyTexImage2D.restype = None
        gl.glCopyTexSubImage2D.argtypes = [
            c_uint,
            c_long,
            c_long,
            c_long,
            c_int,
            c_int,
            c_size_t,
            c_size_t,
        ]
        gl.glCopyTexSubImage2D.restype = None
        gl.glCopyTexSubImage3D.argtypes = [
            c_uint,
            c_long,
            c_long,
            c_long,
            c_long,
            c_int,
            c_int,
            c_size_t,
            c_size_t,
        ]
        gl.glCopyTexSubImage3D.restype = None
        gl.glCreateProgram.argtypes = None
        gl.glCreateProgram.restype = c_uint32
        gl.glCreateShader.argtypes = [c_uint]
        gl.glCreateShader.restype = c_uint32
        gl.glCreateShaderProgramv.argtypes = [
            c_uint,
            c_size_t,
            POINTER(c_char_p),
        ]
        gl.glCreateShaderProgramv.restype = c_uint32
        gl.glCullFace.argtypes = [c_uint]
        gl.glCullFace.restype = None
        gl.glDebugMessageControl.argtypes = [
            c_uint,
            c_uint,
            c_uint,
            c_size_t,
            POINTER(c_uint32),
            c_ubyte,
        ]
        gl.glDebugMessageControl.restype = None
        gl.glDebugMessageInsert.argtypes = [
            c_uint,
            c_uint,
            c_uint32,
            c_uint,
            c_size_t,
            POINTER(c_char),
        ]
        gl.glDebugMessageInsert.restype = None
        gl.glDeleteBuffers.argtypes = [c_size_t, POINTER(c_ulong)]
        gl.glDeleteBuffers.restype = None
        gl.glDeleteFramebuffers.argtypes = [c_size_t, POINTER(c_uint32)]
        gl.glDeleteFramebuffers.restype = None
        gl.glDeleteProgram.argtypes = [c_uint32]
        gl.glDeleteProgram.restype = None
        gl.glDeleteProgramPipelines.argtypes = [c_size_t, POINTER(c_uint32)]
        gl.glDeleteProgramPipelines.restype = None
        gl.glDeleteQueries.argtypes = [c_size_t, POINTER(c_uint32)]
        gl.glDeleteQueries.restype = None
        gl.glDeleteRenderbuffers.argtypes = [c_size_t, POINTER(c_uint32)]
        gl.glDeleteRenderbuffers.restype = None
        gl.glDeleteSamplers.argtypes = [c_size_t, POINTER(c_uint32)]
        gl.glDeleteSamplers.restype = None
        gl.glDeleteShader.argtypes = [c_uint32]
        gl.glDeleteShader.restype = None
        gl.glDeleteTextures.argtypes = [c_size_t, POINTER(c_uint)]
        gl.glDeleteTextures.restype = None
        gl.glDeleteTransformFeedbacks.argtypes = [c_size_t, POINTER(c_uint32)]
        gl.glDeleteTransformFeedbacks.restype = None
        gl.glDeleteVertexArrays.argtypes = [c_size_t, POINTER(c_uint32)]
        gl.glDeleteVertexArrays.restype = None
        gl.glDepthFunc.argtypes = [c_uint]
        gl.glDepthFunc.restype = None
        gl.glDepthMask.argtypes = [c_ubyte]
        gl.glDepthMask.restype = None
        gl.glDepthRangef.argtypes = [c_float, c_float]
        gl.glDepthRangef.restype = None
        gl.glDetachShader.argtypes = [c_uint32, c_uint32]
        gl.glDetachShader.restype = None
        gl.glDisable.argtypes = [c_uint]
        gl.glDisable.restype = None
        gl.glDisableVertexAttribArray.argtypes = [c_uint32]
        gl.glDisableVertexAttribArray.restype = None
        gl.glDisablei.argtypes = [c_uint, c_uint32]
        gl.glDisablei.restype = None
        gl.glDispatchCompute.argtypes = [c_uint32, c_uint32, c_uint32]
        gl.glDispatchCompute.restype = None
        gl.glDispatchComputeIndirect.argtypes = [c_size_t]
        gl.glDispatchComputeIndirect.restype = None
        gl.glDrawArrays.argtypes = [c_uint, c_int32, c_size_t]
        gl.glDrawArrays.restype = None
        gl.glDrawArraysIndirect.argtypes = [c_uint, c_void_p]
        gl.glDrawArraysIndirect.restype = None
        gl.glDrawArraysInstanced.argtypes = [
            c_uint,
            c_int32,
            c_size_t,
            c_size_t,
        ]
        gl.glDrawArraysInstanced.restype = None
        gl.glDrawBuffers.argtypes = [c_size_t, POINTER(c_uint)]
        gl.glDrawBuffers.restype = None
        gl.glDrawElements.argtypes = [c_uint, c_size_t, c_uint, c_void_p]
        gl.glDrawElements.restype = None
        gl.glDrawElementsBaseVertex.argtypes = [
            c_uint,
            c_size_t,
            c_uint,
            c_void_p,
            c_int32,
        ]
        gl.glDrawElementsBaseVertex.restype = None
        gl.glDrawElementsIndirect.argtypes = [c_uint, c_uint, c_void_p]
        gl.glDrawElementsIndirect.restype = None
        gl.glDrawElementsInstanced.argtypes = [
            c_uint,
            c_size_t,
            c_uint,
            c_void_p,
            c_size_t,
        ]
        gl.glDrawElementsInstanced.restype = None
        gl.glDrawElementsInstancedBaseVertex.argtypes = [
            c_uint,
            c_size_t,
            c_uint,
            c_void_p,
            c_size_t,
            c_int32,
        ]
        gl.glDrawElementsInstancedBaseVertex.restype = None
        gl.glDrawRangeElements.argtypes = [
            c_uint,
            c_uint32,
            c_uint32,
            c_size_t,
            c_uint,
            c_void_p,
        ]
        gl.glDrawRangeElements.restype = None
        gl.glDrawRangeElementsBaseVertex.argtypes = [
            c_uint,
            c_uint32,
            c_uint32,
            c_size_t,
            c_uint,
            c_void_p,
            c_int32,
        ]
        gl.glDrawRangeElementsBaseVertex.restype = None
        gl.glEnable.argtypes = [c_uint]
        gl.glEnable.restype = None
        gl.glEnableVertexAttribArray.argtypes = [c_uint32]
        gl.glEnableVertexAttribArray.restype = None
        gl.glEnablei.argtypes = [c_uint, c_uint32]
        gl.glEnablei.restype = None
        gl.glEndQuery.argtypes = [c_uint]
        gl.glEndQuery.restype = None
        gl.glEndTransformFeedback.argtypes = None
        gl.glEndTransformFeedback.restype = None
        gl.glFinish.argtypes = None
        gl.glFinish.restype = None
        gl.glFlush.argtypes = None
        gl.glFlush.restype = None
        gl.glFlushMappedBufferRange.argtypes = [c_uint, c_size_t, c_ssize_t]
        gl.glFlushMappedBufferRange.restype = None
        gl.glFramebufferParameteri.argtypes = [c_uint, c_uint, c_int32]
        gl.glFramebufferParameteri.restype = None
        gl.glFramebufferRenderbuffer.argtypes = [
            c_uint,
            c_uint,
            c_uint,
            c_uint32,
        ]
        gl.glFramebufferRenderbuffer.restype = None
        gl.glFramebufferTexture.argtypes = [c_uint, c_uint, c_uint32, c_int32]
        gl.glFramebufferTexture.restype = None
        gl.glFramebufferTexture2D.argtypes = [
            c_uint,
            c_uint,
            c_uint,
            c_uint32,
            c_int32,
        ]
        gl.glFramebufferTexture2D.restype = None
        gl.glFramebufferTextureLayer.argtypes = [
            c_uint,
            c_uint,
            c_uint,
            c_long,
            c_long,
        ]
        gl.glFramebufferTextureLayer.restype = None
        gl.glFrontFace.argtypes = [c_uint]
        gl.glFrontFace.restype = None
        gl.glGenBuffers.argtypes = [c_size_t, POINTER(c_uint32)]
        gl.glGenBuffers.restype = None
        gl.glGenFramebuffers.argtypes = [c_size_t, POINTER(c_uint32)]
        gl.glGenFramebuffers.restype = None
        gl.glGenProgramPipelines.argtypes = [c_size_t, POINTER(c_uint32)]
        gl.glGenProgramPipelines.restype = None
        gl.glGenQueries.argtypes = [c_size_t, POINTER(c_uint32)]
        gl.glGenQueries.restype = None
        gl.glGenRenderbuffers.argtypes = [c_size_t, POINTER(c_uint32)]
        gl.glGenRenderbuffers.restype = None
        gl.glGenSamplers.argtypes = [c_size_t, POINTER(c_uint32)]
        gl.glGenSamplers.restype = None
        gl.glGenTextures.argtypes = [c_size_t, POINTER(c_uint)]
        gl.glGenTextures.restype = None
        gl.glGenTransformFeedbacks.argtypes = [c_size_t, POINTER(c_uint32)]
        gl.glGenTransformFeedbacks.restype = None
        gl.glGenVertexArrays.argtypes = [c_size_t, POINTER(c_uint32)]
        gl.glGenVertexArrays.restype = None
        gl.glGenerateMipmap.argtypes = [c_uint]
        gl.glGenerateMipmap.restype = None
        gl.glGetActiveAttrib.argtypes = [
            c_uint32,
            c_uint32,
            c_size_t,
            POINTER(c_size_t),
            POINTER(c_int32),
            POINTER(c_uint),
            POINTER(c_char),
        ]
        gl.glGetActiveAttrib.restype = None
        gl.glGetActiveUniform.argtypes = [
            c_uint32,
            c_uint32,
            c_size_t,
            POINTER(c_size_t),
            POINTER(c_int32),
            POINTER(c_uint),
            POINTER(c_char),
        ]
        gl.glGetActiveUniform.restype = None
        gl.glGetActiveUniformBlockName.argtypes = [
            c_uint32,
            c_uint32,
            c_size_t,
            POINTER(c_size_t),
            POINTER(c_char),
        ]
        gl.glGetActiveUniformBlockName.restype = None
        gl.glGetActiveUniformBlockiv.argtypes = [
            c_uint32,
            c_uint32,
            c_uint,
            POINTER(c_int32),
        ]
        gl.glGetActiveUniformBlockiv.restype = None
        gl.glGetActiveUniformsiv.argtypes = [
            c_uint32,
            c_size_t,
            POINTER(c_uint32),
            c_uint,
            POINTER(c_int32),
        ]
        gl.glGetActiveUniformsiv.restype = None
        gl.glGetAttachedShaders.argtypes = [
            c_uint32,
            c_size_t,
            POINTER(c_size_t),
            POINTER(c_uint32),
        ]
        gl.glGetAttachedShaders.restype = None
        gl.glGetAttribLocation.argtypes = [c_uint32, POINTER(c_char)]
        gl.glGetAttribLocation.restype = c_int32
        gl.glGetBooleanv.argtypes = [c_uint, POINTER(c_ubyte)]
        gl.glGetBooleanv.restype = None
        gl.glGetBufferParameteri64v.argtypes = [
            c_uint,
            c_uint,
            POINTER(c_int64),
        ]
        gl.glGetBufferParameteri64v.restype = None
        gl.glGetBufferParameteriv.argtypes = [c_uint, c_uint, POINTER(c_int32)]
        gl.glGetBufferParameteriv.restype = None
        gl.glGetBufferPointerv.argtypes = [c_uint, c_uint, POINTER(c_void_p)]
        gl.glGetBufferPointerv.restype = None
        gl.glGetDebugMessageLog.argtypes = [
            c_uint32,
            c_size_t,
            POINTER(c_uint),
            POINTER(c_uint),
            POINTER(c_uint32),
            POINTER(c_uint),
            POINTER(c_size_t),
            POINTER(c_char),
        ]
        gl.glGetDebugMessageLog.restype = c_uint32
        gl.glGetError.argtypes = None
        gl.glGetError.restype = c_uint
        gl.glGetFloatv.argtypes = [c_uint, POINTER(c_float)]
        gl.glGetFloatv.restype = None
        gl.glGetFragDataLocation.argtypes = [c_uint32, POINTER(c_char)]
        gl.glGetFragDataLocation.restype = c_int32
        gl.glGetFramebufferAttachmentParameteriv.argtypes = [
            c_uint,
            c_uint,
            c_uint,
            POINTER(c_int32),
        ]
        gl.glGetFramebufferAttachmentParameteriv.restype = None
        gl.glGetFramebufferParameteriv.argtypes = [
            c_uint,
            c_uint,
            POINTER(c_int32),
        ]
        gl.glGetFramebufferParameteriv.restype = None
        gl.glGetInteger64v.argtypes = [c_uint, POINTER(c_int64)]
        gl.glGetInteger64v.restype = None
        gl.glGetIntegerv.argtypes = [c_uint, POINTER(c_int32)]
        gl.glGetIntegerv.restype = None
        gl.glGetInternalformativ.argtypes = [
            c_uint,
            c_uint,
            c_uint,
            c_size_t,
            POINTER(c_int32),
        ]
        gl.glGetInternalformativ.restype = None
        gl.glGetMultisamplefv.argtypes = [c_uint, c_uint32, POINTER(c_float)]
        gl.glGetMultisamplefv.restype = None
        gl.glGetObjectLabel.argtypes = [
            c_uint,
            c_uint32,
            c_size_t,
            POINTER(c_size_t),
            POINTER(c_char),
        ]
        gl.glGetObjectLabel.restype = None
        gl.glGetObjectPtrLabel.argtypes = [
            c_void_p,
            c_size_t,
            POINTER(c_size_t),
            POINTER(c_char),
        ]
        gl.glGetObjectPtrLabel.restype = None
        gl.glGetPointerv.argtypes = [c_uint, POINTER(c_void_p)]
        gl.glGetPointerv.restype = None
        gl.glGetProgramBinary.argtypes = [
            c_uint32,
            c_size_t,
            POINTER(c_size_t),
            POINTER(c_uint),
            c_void_p,
        ]
        gl.glGetProgramBinary.restype = None
        gl.glGetProgramInfoLog.argtypes = [
            c_uint32,
            c_size_t,
            POINTER(c_size_t),
            POINTER(c_char),
        ]
        gl.glGetProgramInfoLog.restype = None
        gl.glGetProgramInterfaceiv.argtypes = [
            c_uint32,
            c_uint,
            c_uint,
            POINTER(c_int32),
        ]
        gl.glGetProgramInterfaceiv.restype = None
        gl.glGetProgramPipelineInfoLog.argtypes = [
            c_uint32,
            c_size_t,
            POINTER(c_size_t),
            POINTER(c_char),
        ]
        gl.glGetProgramPipelineInfoLog.restype = None
        gl.glGetProgramPipelineiv.argtypes = [
            c_uint32,
            c_uint,
            POINTER(c_int32),
        ]
        gl.glGetProgramPipelineiv.restype = None
        gl.glGetProgramResourceIndex.argtypes = [
            c_uint32,
            c_uint,
            POINTER(c_char),
        ]
        gl.glGetProgramResourceIndex.restype = c_uint32
        gl.glGetProgramResourceLocation.argtypes = [
            c_uint32,
            c_uint,
            POINTER(c_char),
        ]
        gl.glGetProgramResourceLocation.restype = c_int32
        gl.glGetProgramResourceName.argtypes = [
            c_uint32,
            c_uint,
            c_uint32,
            c_size_t,
            POINTER(c_size_t),
            POINTER(c_char),
        ]
        gl.glGetProgramResourceName.restype = None
        gl.glGetProgramResourceiv.argtypes = [
            c_uint32,
            c_uint,
            c_uint32,
            c_size_t,
            POINTER(c_uint),
            c_size_t,
            POINTER(c_size_t),
            POINTER(c_int32),
        ]
        gl.glGetProgramResourceiv.restype = None
        gl.glGetProgramiv.argtypes = [c_uint32, c_uint, POINTER(c_int32)]
        gl.glGetProgramiv.restype = None
        gl.glGetQueryObjectuiv.argtypes = [c_uint32, c_uint, POINTER(c_uint32)]
        gl.glGetQueryObjectuiv.restype = None
        gl.glGetQueryiv.argtypes = [c_uint, c_uint, POINTER(c_int32)]
        gl.glGetQueryiv.restype = None
        gl.glGetRenderbufferParameteriv.argtypes = [
            c_uint,
            c_uint,
            POINTER(c_int32),
        ]
        gl.glGetRenderbufferParameteriv.restype = None
        gl.glGetSamplerParameterIiv.argtypes = [
            c_uint32,
            c_uint,
            POINTER(c_int32),
        ]
        gl.glGetSamplerParameterIiv.restype = None
        gl.glGetSamplerParameterIuiv.argtypes = [
            c_uint32,
            c_uint,
            POINTER(c_uint32),
        ]
        gl.glGetSamplerParameterIuiv.restype = None
        gl.glGetSamplerParameterfv.argtypes = [
            c_uint32,
            c_uint,
            POINTER(c_float),
        ]
        gl.glGetSamplerParameterfv.restype = None
        gl.glGetSamplerParameteriv.argtypes = [
            c_uint32,
            c_uint,
            POINTER(c_int32),
        ]
        gl.glGetSamplerParameteriv.restype = None
        gl.glGetShaderInfoLog.argtypes = [
            c_uint32,
            c_size_t,
            POINTER(c_size_t),
            POINTER(c_char),
        ]
        gl.glGetShaderInfoLog.restype = None
        gl.glGetShaderPrecisionFormat.argtypes = [
            c_uint,
            c_uint,
            POINTER(c_int32),
            POINTER(c_int32),
        ]
        gl.glGetShaderPrecisionFormat.restype = None
        gl.glGetShaderSource.argtypes = [
            c_uint32,
            c_size_t,
            POINTER(c_size_t),
            POINTER(c_char),
        ]
        gl.glGetShaderSource.restype = None
        gl.glGetShaderiv.argtypes = [c_uint32, c_uint, POINTER(c_int32)]
        gl.glGetShaderiv.restype = None
        gl.glGetString.argtypes = [c_uint]
        gl.glGetString.restype = c_char_p
        gl.glGetStringi.argtypes = [c_uint, c_uint32]
        gl.glGetStringi.restype = c_char_p
        gl.glGetTexLevelParameterfv.argtypes = [
            c_uint,
            c_long,
            c_uint,
            POINTER(c_float),
        ]
        gl.glGetTexLevelParameterfv.restype = None
        gl.glGetTexLevelParameteriv.argtypes = [
            c_uint,
            c_long,
            c_uint,
            POINTER(c_int32),
        ]
        gl.glGetTexLevelParameteriv.restype = None
        gl.glGetTexParameterIiv.argtypes = [c_uint, c_uint, POINTER(c_int32)]
        gl.glGetTexParameterIiv.restype = None
        gl.glGetTexParameterIuiv.argtypes = [c_uint, c_uint, POINTER(c_uint32)]
        gl.glGetTexParameterIuiv.restype = None
        gl.glGetTexParameterfv.argtypes = [c_uint, c_uint, POINTER(c_float)]
        gl.glGetTexParameterfv.restype = None
        gl.glGetTexParameteriv.argtypes = [c_uint, c_uint, POINTER(c_int32)]
        gl.glGetTexParameteriv.restype = None
        gl.glGetTransformFeedbackVarying.argtypes = [
            c_uint32,
            c_uint32,
            c_size_t,
            POINTER(c_size_t),
            POINTER(c_size_t),
            POINTER(c_uint),
            POINTER(c_char),
        ]
        gl.glGetTransformFeedbackVarying.restype = None
        gl.glGetUniformBlockIndex.argtypes = [c_uint32, POINTER(c_char)]
        gl.glGetUniformBlockIndex.restype = c_uint32
        gl.glGetUniformIndices.argtypes = [
            c_uint32,
            c_size_t,
            POINTER(c_char_p),
            POINTER(c_uint32),
        ]
        gl.glGetUniformIndices.restype = None
        gl.glGetUniformLocation.argtypes = [c_uint32, POINTER(c_char)]
        gl.glGetUniformLocation.restype = c_int32
        gl.glGetUniformfv.argtypes = [c_uint32, c_int32, POINTER(c_float)]
        gl.glGetUniformfv.restype = None
        gl.glGetUniformiv.argtypes = [c_uint32, c_int32, POINTER(c_int32)]
        gl.glGetUniformiv.restype = None
        gl.glGetUniformuiv.argtypes = [c_uint32, c_int32, POINTER(c_uint32)]
        gl.glGetUniformuiv.restype = None
        gl.glGetVertexAttribIiv.argtypes = [c_uint32, c_uint, POINTER(c_int32)]
        gl.glGetVertexAttribIiv.restype = None
        gl.glGetVertexAttribIuiv.argtypes = [
            c_uint32,
            c_uint,
            POINTER(c_uint32),
        ]
        gl.glGetVertexAttribIuiv.restype = None
        gl.glGetVertexAttribPointerv.argtypes = [
            c_uint32,
            c_uint,
            POINTER(c_void_p),
        ]
        gl.glGetVertexAttribPointerv.restype = None
        gl.glGetVertexAttribfv.argtypes = [c_uint32, c_uint, POINTER(c_float)]
        gl.glGetVertexAttribfv.restype = None
        gl.glGetVertexAttribiv.argtypes = [c_uint32, c_uint, POINTER(c_int32)]
        gl.glGetVertexAttribiv.restype = None
        gl.glHint.argtypes = [c_uint, c_uint]
        gl.glHint.restype = None
        gl.glInvalidateFramebuffer.argtypes = [
            c_uint,
            c_size_t,
            POINTER(c_uint),
        ]
        gl.glInvalidateFramebuffer.restype = None
        gl.glInvalidateSubFramebuffer.argtypes = [
            c_uint,
            c_size_t,
            POINTER(c_uint),
            c_int32,
            c_int32,
            c_size_t,
            c_size_t,
        ]
        gl.glInvalidateSubFramebuffer.restype = None
        gl.glIsBuffer.argtypes = [c_uint32]
        gl.glIsBuffer.restype = c_ubyte
        gl.glIsEnabled.argtypes = [c_uint]
        gl.glIsEnabled.restype = c_ubyte
        gl.glIsEnabledi.argtypes = [c_uint, c_uint32]
        gl.glIsEnabledi.restype = c_ubyte
        gl.glIsFramebuffer.argtypes = [c_uint32]
        gl.glIsFramebuffer.restype = c_ubyte
        gl.glIsProgram.argtypes = [c_uint32]
        gl.glIsProgram.restype = c_ubyte
        gl.glIsProgramPipeline.argtypes = [c_uint32]
        gl.glIsProgramPipeline.restype = c_ubyte
        gl.glIsQuery.argtypes = [c_uint32]
        gl.glIsQuery.restype = c_ubyte
        gl.glIsRenderbuffer.argtypes = [c_uint32]
        gl.glIsRenderbuffer.restype = c_ubyte
        gl.glIsSampler.argtypes = [c_uint32]
        gl.glIsSampler.restype = c_ubyte
        gl.glIsShader.argtypes = [c_uint32]
        gl.glIsShader.restype = c_ubyte
        gl.glIsTexture.argtypes = [c_uint]
        gl.glIsTexture.restype = c_ubyte
        gl.glIsTransformFeedback.argtypes = [c_uint32]
        gl.glIsTransformFeedback.restype = c_ubyte
        gl.glIsVertexArray.argtypes = [c_uint32]
        gl.glIsVertexArray.restype = c_ubyte
        gl.glLineWidth.argtypes = [c_float]
        gl.glLineWidth.restype = None
        gl.glLinkProgram.argtypes = [c_uint32]
        gl.glLinkProgram.restype = None
        gl.glMapBufferRange.argtypes = [c_uint, c_size_t, c_ssize_t, c_uint]
        gl.glMapBufferRange.restype = c_void_p
        gl.glMemoryBarrier.argtypes = [c_uint]
        gl.glMemoryBarrier.restype = None
        gl.glMinSampleShading.argtypes = [c_float]
        gl.glMinSampleShading.restype = None
        gl.glObjectLabel.argtypes = [
            c_uint,
            c_uint32,
            c_size_t,
            POINTER(c_char),
        ]
        gl.glObjectLabel.restype = None
        gl.glObjectPtrLabel.argtypes = [c_void_p, c_size_t, POINTER(c_char)]
        gl.glObjectPtrLabel.restype = None
        gl.glPatchParameteri.argtypes = [c_uint, c_int32]
        gl.glPatchParameteri.restype = None
        gl.glPauseTransformFeedback.argtypes = None
        gl.glPauseTransformFeedback.restype = None
        gl.glPixelStorei.argtypes = [c_uint, c_long]
        gl.glPixelStorei.restype = None
        gl.glPolygonOffset.argtypes = [c_float, c_float]
        gl.glPolygonOffset.restype = None
        gl.glPopDebugGroup.argtypes = None
        gl.glPopDebugGroup.restype = None
        gl.glProgramBinary.argtypes = [c_uint32, c_uint, c_void_p, c_size_t]
        gl.glProgramBinary.restype = None
        gl.glProgramParameteri.argtypes = [c_uint32, c_uint, c_int32]
        gl.glProgramParameteri.restype = None
        gl.glProgramUniform1f.argtypes = [c_uint32, c_int32, c_float]
        gl.glProgramUniform1f.restype = None
        gl.glProgramUniform1fv.argtypes = [
            c_uint32,
            c_int32,
            c_size_t,
            POINTER(c_float),
        ]
        gl.glProgramUniform1fv.restype = None
        gl.glProgramUniform1i.argtypes = [c_uint32, c_int32, c_int32]
        gl.glProgramUniform1i.restype = None
        gl.glProgramUniform1iv.argtypes = [
            c_uint32,
            c_int32,
            c_size_t,
            POINTER(c_int32),
        ]
        gl.glProgramUniform1iv.restype = None
        gl.glProgramUniform1ui.argtypes = [c_uint32, c_int32, c_uint32]
        gl.glProgramUniform1ui.restype = None
        gl.glProgramUniform1uiv.argtypes = [
            c_uint32,
            c_int32,
            c_size_t,
            POINTER(c_uint32),
        ]
        gl.glProgramUniform1uiv.restype = None
        gl.glProgramUniform2f.argtypes = [c_uint32, c_int32, c_float, c_float]
        gl.glProgramUniform2f.restype = None
        gl.glProgramUniform2fv.argtypes = [
            c_uint32,
            c_int32,
            c_size_t,
            POINTER(c_float),
        ]
        gl.glProgramUniform2fv.restype = None
        gl.glProgramUniform2i.argtypes = [c_uint32, c_int32, c_int32, c_int32]
        gl.glProgramUniform2i.restype = None
        gl.glProgramUniform2iv.argtypes = [
            c_uint32,
            c_int32,
            c_size_t,
            POINTER(c_int32),
        ]
        gl.glProgramUniform2iv.restype = None
        gl.glProgramUniform2ui.argtypes = [
            c_uint32,
            c_int32,
            c_uint32,
            c_uint32,
        ]
        gl.glProgramUniform2ui.restype = None
        gl.glProgramUniform2uiv.argtypes = [
            c_uint32,
            c_int32,
            c_size_t,
            POINTER(c_uint32),
        ]
        gl.glProgramUniform2uiv.restype = None
        gl.glProgramUniform3f.argtypes = [
            c_uint32,
            c_int32,
            c_float,
            c_float,
            c_float,
        ]
        gl.glProgramUniform3f.restype = None
        gl.glProgramUniform3fv.argtypes = [
            c_uint32,
            c_int32,
            c_size_t,
            POINTER(c_float),
        ]
        gl.glProgramUniform3fv.restype = None
        gl.glProgramUniform3i.argtypes = [
            c_uint32,
            c_int32,
            c_int32,
            c_int32,
            c_int32,
        ]
        gl.glProgramUniform3i.restype = None
        gl.glProgramUniform3iv.argtypes = [
            c_uint32,
            c_int32,
            c_size_t,
            POINTER(c_int32),
        ]
        gl.glProgramUniform3iv.restype = None
        gl.glProgramUniform3ui.argtypes = [
            c_uint32,
            c_int32,
            c_uint32,
            c_uint32,
            c_uint32,
        ]
        gl.glProgramUniform3ui.restype = None
        gl.glProgramUniform3uiv.argtypes = [
            c_uint32,
            c_int32,
            c_size_t,
            POINTER(c_uint32),
        ]
        gl.glProgramUniform3uiv.restype = None
        gl.glProgramUniform4f.argtypes = [
            c_uint32,
            c_int32,
            c_float,
            c_float,
            c_float,
            c_float,
        ]
        gl.glProgramUniform4f.restype = None
        gl.glProgramUniform4fv.argtypes = [
            c_uint32,
            c_int32,
            c_size_t,
            POINTER(c_float),
        ]
        gl.glProgramUniform4fv.restype = None
        gl.glProgramUniform4i.argtypes = [
            c_uint32,
            c_int32,
            c_int32,
            c_int32,
            c_int32,
            c_int32,
        ]
        gl.glProgramUniform4i.restype = None
        gl.glProgramUniform4iv.argtypes = [
            c_uint32,
            c_int32,
            c_size_t,
            POINTER(c_int32),
        ]
        gl.glProgramUniform4iv.restype = None
        gl.glProgramUniform4ui.argtypes = [
            c_uint32,
            c_int32,
            c_uint32,
            c_uint32,
            c_uint32,
            c_uint32,
        ]
        gl.glProgramUniform4ui.restype = None
        gl.glProgramUniform4uiv.argtypes = [
            c_uint32,
            c_int32,
            c_size_t,
            POINTER(c_uint32),
        ]
        gl.glProgramUniform4uiv.restype = None
        gl.glProgramUniformMatrix2fv.argtypes = [
            c_uint32,
            c_int32,
            c_size_t,
            c_ubyte,
            POINTER(c_float),
        ]
        gl.glProgramUniformMatrix2fv.restype = None
        gl.glProgramUniformMatrix2x3fv.argtypes = [
            c_uint32,
            c_int32,
            c_size_t,
            c_ubyte,
            POINTER(c_float),
        ]
        gl.glProgramUniformMatrix2x3fv.restype = None
        gl.glProgramUniformMatrix2x4fv.argtypes = [
            c_uint32,
            c_int32,
            c_size_t,
            c_ubyte,
            POINTER(c_float),
        ]
        gl.glProgramUniformMatrix2x4fv.restype = None
        gl.glProgramUniformMatrix3fv.argtypes = [
            c_uint32,
            c_int32,
            c_size_t,
            c_ubyte,
            POINTER(c_float),
        ]
        gl.glProgramUniformMatrix3fv.restype = None
        gl.glProgramUniformMatrix3x2fv.argtypes = [
            c_uint32,
            c_int32,
            c_size_t,
            c_ubyte,
            POINTER(c_float),
        ]
        gl.glProgramUniformMatrix3x2fv.restype = None
        gl.glProgramUniformMatrix3x4fv.argtypes = [
            c_uint32,
            c_int32,
            c_size_t,
            c_ubyte,
            POINTER(c_float),
        ]
        gl.glProgramUniformMatrix3x4fv.restype = None
        gl.glProgramUniformMatrix4fv.argtypes = [
            c_uint32,
            c_int32,
            c_size_t,
            c_ubyte,
            POINTER(c_float),
        ]
        gl.glProgramUniformMatrix4fv.restype = None
        gl.glProgramUniformMatrix4x2fv.argtypes = [
            c_uint32,
            c_int32,
            c_size_t,
            c_ubyte,
            POINTER(c_float),
        ]
        gl.glProgramUniformMatrix4x2fv.restype = None
        gl.glProgramUniformMatrix4x3fv.argtypes = [
            c_uint32,
            c_int32,
            c_size_t,
            c_ubyte,
            POINTER(c_float),
        ]
        gl.glProgramUniformMatrix4x3fv.restype = None
        gl.glPushDebugGroup.argtypes = [
            c_uint,
            c_uint32,
            c_size_t,
            POINTER(c_char),
        ]
        gl.glPushDebugGroup.restype = None
        gl.glReadBuffer.argtypes = [c_uint]
        gl.glReadBuffer.restype = None
        gl.glReadPixels.argtypes = [
            c_int,
            c_int,
            c_size_t,
            c_size_t,
            c_uint,
            c_uint,
            c_void_p,
        ]
        gl.glReadPixels.restype = None
        gl.glReleaseShaderCompiler.argtypes = None
        gl.glReleaseShaderCompiler.restype = None
        gl.glRenderbufferStorage.argtypes = [
            c_uint,
            c_uint,
            c_size_t,
            c_size_t,
        ]
        gl.glRenderbufferStorage.restype = None
        gl.glRenderbufferStorageMultisample.argtypes = [
            c_uint,
            c_size_t,
            c_uint,
            c_size_t,
            c_size_t,
        ]
        gl.glRenderbufferStorageMultisample.restype = None
        gl.glResumeTransformFeedback.argtypes = None
        gl.glResumeTransformFeedback.restype = None
        gl.glSampleCoverage.argtypes = [c_float, c_ubyte]
        gl.glSampleCoverage.restype = None
        gl.glSampleMaski.argtypes = [c_uint32, c_uint]
        gl.glSampleMaski.restype = None
        gl.glSamplerParameterIiv.argtypes = [
            c_uint32,
            c_uint,
            POINTER(c_int32),
        ]
        gl.glSamplerParameterIiv.restype = None
        gl.glSamplerParameterIuiv.argtypes = [
            c_uint32,
            c_uint,
            POINTER(c_uint32),
        ]
        gl.glSamplerParameterIuiv.restype = None
        gl.glSamplerParameterf.argtypes = [c_uint32, c_uint, c_float]
        gl.glSamplerParameterf.restype = None
        gl.glSamplerParameterfv.argtypes = [c_uint32, c_uint, POINTER(c_float)]
        gl.glSamplerParameterfv.restype = None
        gl.glSamplerParameteri.argtypes = [c_uint32, c_uint, c_int32]
        gl.glSamplerParameteri.restype = None
        gl.glSamplerParameteriv.argtypes = [c_uint32, c_uint, POINTER(c_int32)]
        gl.glSamplerParameteriv.restype = None
        gl.glScissor.argtypes = [c_int, c_int, c_size_t, c_size_t]
        gl.glScissor.restype = None
        gl.glShaderBinary.argtypes = [
            c_size_t,
            POINTER(c_uint32),
            c_uint,
            c_void_p,
            c_size_t,
        ]
        gl.glShaderBinary.restype = None
        gl.glShaderSource.argtypes = [
            c_uint32,
            c_size_t,
            POINTER(c_char_p),
            POINTER(c_int32),
        ]
        gl.glShaderSource.restype = None
        gl.glStencilFunc.argtypes = [c_uint, c_int, c_uint]
        gl.glStencilFunc.restype = None
        gl.glStencilFuncSeparate.argtypes = [c_uint, c_uint, c_int, c_uint]
        gl.glStencilFuncSeparate.restype = None
        gl.glStencilMask.argtypes = [c_uint]
        gl.glStencilMask.restype = None
        gl.glStencilMaskSeparate.argtypes = [c_uint, c_uint]
        gl.glStencilMaskSeparate.restype = None
        gl.glStencilOp.argtypes = [c_uint, c_uint, c_uint]
        gl.glStencilOp.restype = None
        gl.glStencilOpSeparate.argtypes = [c_uint, c_uint, c_uint, c_uint]
        gl.glStencilOpSeparate.restype = None
        gl.glTexBuffer.argtypes = [c_uint, c_uint, c_uint32]
        gl.glTexBuffer.restype = None
        gl.glTexBufferRange.argtypes = [
            c_uint,
            c_uint,
            c_uint32,
            c_size_t,
            c_ssize_t,
        ]
        gl.glTexBufferRange.restype = None
        gl.glTexImage2D.argtypes = [
            c_uint,
            c_long,
            c_int,
            c_size_t,
            c_size_t,
            c_long,
            c_uint,
            c_uint,
            c_void_p,
        ]
        gl.glTexImage2D.restype = None
        gl.glTexParameterIiv.argtypes = [c_uint, c_uint, POINTER(c_int32)]
        gl.glTexParameterIiv.restype = None
        gl.glTexParameterIuiv.argtypes = [c_uint, c_uint, POINTER(c_uint32)]
        gl.glTexParameterIuiv.restype = None
        gl.glTexParameterf.argtypes = [c_uint, c_uint, c_float]
        gl.glTexParameterf.restype = None
        gl.glTexParameterfv.argtypes = [c_uint, c_uint, POINTER(c_float)]
        gl.glTexParameterfv.restype = None
        gl.glTexParameteri.argtypes = [c_uint, c_uint, c_long]
        gl.glTexParameteri.restype = None
        gl.glTexParameteriv.argtypes = [c_uint, c_uint, POINTER(c_long)]
        gl.glTexParameteriv.restype = None
        gl.glTexStorage2D.argtypes = [
            c_uint,
            c_size_t,
            c_uint,
            c_size_t,
            c_size_t,
        ]
        gl.glTexStorage2D.restype = None
        gl.glTexStorage2DMultisample.argtypes = [
            c_uint,
            c_size_t,
            c_uint,
            c_size_t,
            c_size_t,
            c_ubyte,
        ]
        gl.glTexStorage2DMultisample.restype = None
        gl.glTexStorage3D.argtypes = [
            c_uint,
            c_size_t,
            c_uint,
            c_size_t,
            c_size_t,
            c_size_t,
        ]
        gl.glTexStorage3D.restype = None
        gl.glTexStorage3DMultisample.argtypes = [
            c_uint,
            c_size_t,
            c_uint,
            c_size_t,
            c_size_t,
            c_size_t,
            c_ubyte,
        ]
        gl.glTexStorage3DMultisample.restype = None
        gl.glTexSubImage2D.argtypes = [
            c_uint,
            c_long,
            c_long,
            c_long,
            c_size_t,
            c_size_t,
            c_uint,
            c_uint,
            c_void_p,
        ]
        gl.glTexSubImage2D.restype = None
        gl.glTexSubImage3D.argtypes = [
            c_uint,
            c_long,
            c_long,
            c_long,
            c_long,
            c_size_t,
            c_size_t,
            c_size_t,
            c_uint,
            c_uint,
            c_void_p,
        ]
        gl.glTexSubImage3D.restype = None
        gl.glTransformFeedbackVaryings.argtypes = [
            c_uint32,
            c_size_t,
            POINTER(c_char_p),
            c_uint,
        ]
        gl.glTransformFeedbackVaryings.restype = None
        gl.glUniform1f.argtypes = [c_int32, c_float]
        gl.glUniform1f.restype = None
        gl.glUniform1fv.argtypes = [c_int32, c_size_t, POINTER(c_float)]
        gl.glUniform1fv.restype = None
        gl.glUniform1i.argtypes = [c_int32, c_int32]
        gl.glUniform1i.restype = None
        gl.glUniform1iv.argtypes = [c_int32, c_size_t, POINTER(c_int32)]
        gl.glUniform1iv.restype = None
        gl.glUniform1ui.argtypes = [c_int32, c_uint32]
        gl.glUniform1ui.restype = None
        gl.glUniform1uiv.argtypes = [c_int32, c_size_t, POINTER(c_uint32)]
        gl.glUniform1uiv.restype = None
        gl.glUniform2f.argtypes = [c_int32, c_float, c_float]
        gl.glUniform2f.restype = None
        gl.glUniform2fv.argtypes = [c_int32, c_size_t, POINTER(c_float)]
        gl.glUniform2fv.restype = None
        gl.glUniform2i.argtypes = [c_int32, c_int32, c_int32]
        gl.glUniform2i.restype = None
        gl.glUniform2iv.argtypes = [c_int32, c_size_t, POINTER(c_int32)]
        gl.glUniform2iv.restype = None
        gl.glUniform2ui.argtypes = [c_int32, c_uint32, c_uint32]
        gl.glUniform2ui.restype = None
        gl.glUniform2uiv.argtypes = [c_int32, c_size_t, POINTER(c_uint32)]
        gl.glUniform2uiv.restype = None
        gl.glUniform3f.argtypes = [c_int32, c_float, c_float, c_float]
        gl.glUniform3f.restype = None
        gl.glUniform3fv.argtypes = [c_int32, c_size_t, POINTER(c_float)]
        gl.glUniform3fv.restype = None
        gl.glUniform3i.argtypes = [c_int32, c_int32, c_int32, c_int32]
        gl.glUniform3i.restype = None
        gl.glUniform3iv.argtypes = [c_int32, c_size_t, POINTER(c_int32)]
        gl.glUniform3iv.restype = None
        gl.glUniform3ui.argtypes = [c_int32, c_uint32, c_uint32, c_uint32]
        gl.glUniform3ui.restype = None
        gl.glUniform3uiv.argtypes = [c_int32, c_size_t, POINTER(c_uint32)]
        gl.glUniform3uiv.restype = None
        gl.glUniform4f.argtypes = [c_int32, c_float, c_float, c_float, c_float]
        gl.glUniform4f.restype = None
        gl.glUniform4fv.argtypes = [c_int32, c_size_t, POINTER(c_float)]
        gl.glUniform4fv.restype = None
        gl.glUniform4i.argtypes = [c_int32, c_int32, c_int32, c_int32, c_int32]
        gl.glUniform4i.restype = None
        gl.glUniform4iv.argtypes = [c_int32, c_size_t, POINTER(c_int32)]
        gl.glUniform4iv.restype = None
        gl.glUniform4ui.argtypes = [
            c_int32,
            c_uint32,
            c_uint32,
            c_uint32,
            c_uint32,
        ]
        gl.glUniform4ui.restype = None
        gl.glUniform4uiv.argtypes = [c_int32, c_size_t, POINTER(c_uint32)]
        gl.glUniform4uiv.restype = None
        gl.glUniformBlockBinding.argtypes = [c_uint32, c_uint32, c_uint32]
        gl.glUniformBlockBinding.restype = None
        gl.glUniformMatrix2fv.argtypes = [
            c_int32,
            c_size_t,
            c_ubyte,
            POINTER(c_float),
        ]
        gl.glUniformMatrix2fv.restype = None
        gl.glUniformMatrix2x3fv.argtypes = [
            c_int32,
            c_size_t,
            c_ubyte,
            POINTER(c_float),
        ]
        gl.glUniformMatrix2x3fv.restype = None
        gl.glUniformMatrix2x4fv.argtypes = [
            c_int32,
            c_size_t,
            c_ubyte,
            POINTER(c_float),
        ]
        gl.glUniformMatrix2x4fv.restype = None
        gl.glUniformMatrix3fv.argtypes = [
            c_int32,
            c_size_t,
            c_ubyte,
            POINTER(c_float),
        ]
        gl.glUniformMatrix3fv.restype = None
        gl.glUniformMatrix3x2fv.argtypes = [
            c_int32,
            c_size_t,
            c_ubyte,
            POINTER(c_float),
        ]
        gl.glUniformMatrix3x2fv.restype = None
        gl.glUniformMatrix3x4fv.argtypes = [
            c_int32,
            c_size_t,
            c_ubyte,
            POINTER(c_float),
        ]
        gl.glUniformMatrix3x4fv.restype = None
        gl.glUniformMatrix4fv.argtypes = [
            c_int32,
            c_size_t,
            c_ubyte,
            POINTER(c_float),
        ]
        gl.glUniformMatrix4fv.restype = None
        gl.glUniformMatrix4x2fv.argtypes = [
            c_int32,
            c_size_t,
            c_ubyte,
            POINTER(c_float),
        ]
        gl.glUniformMatrix4x2fv.restype = None
        gl.glUniformMatrix4x3fv.argtypes = [
            c_int32,
            c_size_t,
            c_ubyte,
            POINTER(c_float),
        ]
        gl.glUniformMatrix4x3fv.restype = None
        gl.glUnmapBuffer.argtypes = [c_uint]
        gl.glUnmapBuffer.restype = c_ubyte
        gl.glUseProgram.argtypes = [c_uint32]
        gl.glUseProgram.restype = None
        gl.glUseProgramStages.argtypes = [c_uint32, c_uint, c_uint32]
        gl.glUseProgramStages.restype = None
        gl.glValidateProgram.argtypes = [c_uint32]
        gl.glValidateProgram.restype = None
        gl.glValidateProgramPipeline.argtypes = [c_uint32]
        gl.glValidateProgramPipeline.restype = None
        gl.glVertexAttribBinding.argtypes = [c_uint32, c_uint32]
        gl.glVertexAttribBinding.restype = None
        gl.glVertexAttribDivisor.argtypes = [c_uint32, c_uint32]
        gl.glVertexAttribDivisor.restype = None
        gl.glVertexAttribFormat.argtypes = [
            c_uint32,
            c_int32,
            c_uint,
            c_ubyte,
            c_uint32,
        ]
        gl.glVertexAttribFormat.restype = None
        gl.glVertexAttribIFormat.argtypes = [
            c_uint32,
            c_int32,
            c_uint,
            c_uint32,
        ]
        gl.glVertexAttribIFormat.restype = None
        gl.glVertexAttribIPointer.argtypes = [
            c_uint32,
            c_int32,
            c_uint,
            c_size_t,
            c_void_p,
        ]
        gl.glVertexAttribIPointer.restype = None
        gl.glVertexAttribPointer.argtypes = [
            c_uint32,
            c_int32,
            c_uint,
            c_ubyte,
            c_size_t,
            c_void_p,
        ]
        gl.glVertexAttribPointer.restype = None
        gl.glVertexBindingDivisor.argtypes = [c_uint32, c_uint32]
        gl.glVertexBindingDivisor.restype = None
        gl.glViewport.argtypes = [c_int, c_int, c_size_t, c_size_t]
        gl.glViewport.restype = None
'''


class PageAnalyzer(object):
    """Analyzer for kernel page"""

    # page flags from kernel/include/uapi/linux/kernel-page-flags.h #
    flagList = [
        "KPF_LOCKED",  # 0
        "KPF_ERROR",  # 1
        "KPF_REFERENCED",  # 2
        "KPF_UPTODATE",  # 3
        "KPF_DIRTY",  # 4
        "KPF_LRU",  # 5
        "KPF_ACTIVE",  # 6
        "KPF_SLAB",  # 7
        "KPF_WRITEBACK",  # 8
        "KPF_RECLAIM",  # 9
        "KPF_BUDDY",  # 10
        "KPF_MMAP",  # 11
        "KPF_ANON",  # 12
        "KPF_SWAPCACHE",  # 13
        "KPF_SWAPBACKED",  # 14
        "KPF_COMPOUND_HEAD",  # 15
        "KPF_COMPOUND_TAIL",  # 16
        "KPF_HUGE",  # 17
        "KPF_UNEVICTABLE",  # 18
        "KPF_HWPOISON",  # 19
        "KPF_NOPAGE",  # 20
        "KPF_KSM",  # 21
        "KPF_THP",  # 22
        "KPF_BALLOON",  # 23
        "KPF_ZERO_PAGE",  # 24
        "KPF_IDLE",  # 25
        "KPF_PAGETABLE",  # 26
        "",  # 27
        "",  # 28
        "",  # 29
        "",  # 30
        "",  # 31
        "KPF_RESERVED",  # 32
        "KPF_MLOCKED",  # 33
        "KPF_MAPPEDTODISK",  # 34
        "KPF_PRIVATE",  # 35
        "KPF_PRIVATE_2",  # 36
        "KPF_OWNER_PRIVATE",  # 37
        "KPF_ARCH",  # 38
        "KPF_UNCACHED",  # 39
        "KPF_SOFTDIRTY",  # 40
        "KPF_ARCH_2",  # 41
        "KPF_ARCH_3",  # 42
    ]

    mapFileCache = {}

    @staticmethod
    def getPageInfo(
        pid,
        vaddr,
        markIdle=False,
        checkIdle=False,
        retList=False,
        verb=True,
        progress=False,
        cmdset=[],
        showAttr=True,
    ):
        try:
            if not pid:
                raise Exception("no pid")
            elif not isinstance(pid, list):
                raise Exception("wrong pid")

            pids = []
            for item in pid:
                ret = SysMgr.getTids(item, isThread=False)
                if ret:
                    pids += ret
            if not pids:
                raise Exception("no task")
        except SystemExit:
            sys.exit(0)
        except:
            if pid:
                targetStr = " for '%s'" % ", ".join(pid)
            else:
                targetStr = ""

            SysMgr.printErr("failed to find task%s" % targetStr, reason=True)

            sys.exit(-1)

        # check root permission #
        SysMgr.checkRootPerm()

        # get target addresses #
        if vaddr:
            vaddrOrig = UtilMgr.cleanItem(vaddr.split(","), False)
        else:
            vaddrOrig = None

        # update verb #
        if verb and not SysMgr.printEnable:
            verb = False

        # set print function #
        if verb:
            _printPipe = SysMgr.printPipe
        else:
            _printPipe = lambda _: True

        # check idle page marking #
        if "MARKIDLE" in SysMgr.environList:
            markIdle = True
            if not SysMgr.checkIdlePageCond():
                sys.exit(-1)

        # check idle page checking #
        checkIdle = (
            "CHECKIDLE" in SysMgr.environList if not checkIdle else checkIdle
        )

        # check bitmap saving #
        saveBitmapFlag = "SAVEBITMAP" in SysMgr.environList

        PAGESIZE = SysMgr.PAGESIZE
        skipAttr = "SKIPATTR" in SysMgr.environList
        if "SKIPATTRSTR" in SysMgr.environList:
            showAttr = False

        # define shortcut variables #
        convNum = UtilMgr.convNum
        convSize = UtilMgr.convSize2Unit
        errMsg = (
            "failed to recognize addresses %s, "
            "input the address such as 102400 or 0x1234a-0x123ff"
        )

        idlePageList = {}

        for pidx, pid in enumerate(sorted(pids)):
            # read whole bitmap for idle pages #
            if checkIdle:
                bitmap = SysMgr.getIdleMap()
            else:
                bitmap = None

            # get process name #
            comm = SysMgr.getComm(pid)
            vaddrs = vaddrOrig
            if not vaddrs:
                PageAnalyzer.printMemoryArea(
                    pid,
                    comm=comm,
                    showall=SysMgr.showAll,
                    lastLine=True,
                    cache=True,
                )
                continue

            if retList:
                idlePageList.setdefault(pid, [])

            # get target address ranges #
            targetList = []
            for vaddr in vaddrs:
                if not vaddr:
                    continue

                # get address range #
                if vaddr in ("heap", "stack"):
                    vaddr = "[%s]" % vaddr

                    # get addresses by name #
                    vrange = FileAnalyzer.getMapAddr(pid, vaddr)
                    if not vrange:
                        SysMgr.printErr(
                            "failed to find memory address for '%s'" % vaddr
                        )
                        continue

                    targetList.append(vrange)
                else:
                    # get addresses by file #
                    vranges = FileAnalyzer.getMapAddr(pid, vaddr, retList=True)
                    if vranges:
                        targetList += vranges
                    else:
                        vrange = UtilMgr.cleanItem(vaddr.split("-"), False)
                        if len(vrange) > 1 and vrange[1].startswith("+"):
                            try:
                                start = long(vrange[0])
                            except SystemExit:
                                sys.exit(0)
                            except:
                                start = long(vrange[0], 16)

                            try:
                                var = UtilMgr.convUnit2Size(vrange[1])
                            except SystemExit:
                                sys.exit(0)
                            except:
                                var = long(vrange[1], 16)
                            vrange[1] = hex(start + var)

                        targetList.append(vrange)

            procPresent = 0
            procSwapped = 0
            procSoftdirty = 0
            procExmapped = 0
            procFile = 0
            procRef = 0
            procIdle = 0
            procTotalFlags = 0
            pageTable = []

            if cmdset:
                # create a debugger object #
                dbgObj = Debugger(pid)
                dbgObj.initValues()

            # print page info in target address ranges #
            for vcnt, vrange in enumerate(targetList):
                # print progress #
                if progress:
                    UtilMgr.printProgress(vcnt, len(targetList))

                rangeCnt = len(vrange)

                # check input count #
                if rangeCnt > 2:
                    SysMgr.printErr(errMsg % vrange, True)
                    sys.exit(-1)

                # check input format #
                try:
                    addrs = long(vrange[0], base=16)
                    addre = addrs
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr(errMsg % vrange, True)
                    sys.exit(-1)

                try:
                    if rangeCnt == 2:
                        addre = long(vrange[1], base=16)
                        offset = 0
                    else:
                        offset = PAGESIZE

                    # check range #
                    if addrs > addre:
                        if verb:
                            # print memory area #
                            PageAnalyzer.printMemoryArea(
                                pid,
                                comm=comm,
                                showall=True,
                                lastLine=True,
                                cache=True,
                            )

                        # print error message #
                        SysMgr.printErr(
                            (
                                "failed to recognize addresses, "
                                "the first address (%s) is bigger than "
                                "the second address (%s)"
                            )
                            % (hex(addrs), hex(addre))
                        )
                        sys.exit(-1)
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr(errMsg % vrange, True)
                    sys.exit(-1)

                size = addre - addrs

                _printPipe(
                    "\n[Mem Info] [Proc: %s(%s)] [AREA: %s] [HELP: %s]"
                    % (comm, pid, vaddr, "kernel/Documentation/vm/pagemap.txt")
                )

                # print memory area info #
                if verb:
                    PageAnalyzer.printMemoryArea(
                        pid, addrs, addre, lastLine=True, cache=True
                    )

                # execute commands #
                if cmdset:
                    cmds = []
                    # convert START and SIZE values #
                    for item in cmdset:
                        item = item.replace("START", str(addrs))
                        item = item.replace("END", str(addre))
                        item = item.replace("SIZE", str(size))
                        cmds.append(item)

                    # execute remote commands #
                    dbgObj.executeCmd(cmds, force=True)
                    SysMgr.printPipe("\n")

                # check skip condition #
                if skipAttr:
                    continue

                # print menu #
                _printPipe(
                    (
                        "{0:^19}|{1:^16}|{2:>5}|{3:>5}|{4:>5}|{5:>5}|"
                        "{6:>5}|{7:>5}|{8:>5}| {9} ({10})\n{11}"
                    ).format(
                        "VADDR",
                        "PFN",
                        "PRES",
                        "SWAP",
                        "FILE",
                        "SHR",
                        "SDRT",
                        "EXMAP",
                        "IDLE",
                        "ATTRIBUTES",
                        "BITS",
                        oneLine,
                    )
                )

                # init total variables #
                totalPresent = 0
                totalSwapped = 0
                totalSoftdirty = 0
                totalExmapped = 0
                totalFile = 0
                totalRef = 0
                totalIdle = 0
                totalFlags = 0

                # read pagemap #
                addreNew = addre + offset
                length = addreNew - addrs
                pagemap = PageAnalyzer.getPagemap(pid, addrs, length)

                # create a new pagetable for this area #
                if retList or (saveBitmapFlag and checkIdle):
                    pageSubTable = [1] * long(length / PAGESIZE)

                idx = 0
                for addr in xrange(addrs, addreNew, PAGESIZE):
                    # get page frame info #
                    try:
                        entry = struct.unpack("Q", pagemap[idx : idx + 8])[0]
                    except SystemExit:
                        sys.exit(0)
                    except:
                        entry = 0
                    finally:
                        idx += 8

                    # get page frame number #
                    pfn = PageAnalyzer.getPfn(entry)

                    # no page #
                    isIdle = "-"
                    # mark idle pages #
                    if markIdle:
                        if SysMgr.handleIdlePage(pfn, write=True):
                            isIdle = True
                            totalIdle += 1
                            procIdle += 1
                    # check idle pages #
                    elif checkIdle:
                        isIdle = SysMgr.handleIdlePage(
                            pfn, write=False, bitmap=bitmap
                        )
                        if isIdle is None:
                            isIdle = "-"
                        elif isIdle:
                            totalIdle += 1
                            procIdle += 1

                            # change a bit in a pagetable #
                            if retList or saveBitmapFlag:
                                try:
                                    pageSubTable[long(idx / 8) - 1] = 0
                                except:
                                    SysMgr.printWarn(
                                        "failed to save an idle page status",
                                        always=True,
                                        reason=True,
                                    )

                    # check skip condition #
                    if not verb:
                        continue

                    isPresent = PageAnalyzer.isPresent(entry)
                    if isPresent:
                        totalPresent += 1
                        procPresent += 1

                    isSwapped = PageAnalyzer.isSwapped(entry)
                    if isSwapped:
                        totalSwapped += 1
                        procSwapped += 1

                    isSoftdirty = PageAnalyzer.isSoftdirty(entry)
                    if isSoftdirty:
                        totalSoftdirty += 1
                        procSoftdirty += 1

                    isExmapped = PageAnalyzer.isExmapped(entry)
                    if isExmapped:
                        totalExmapped += 1
                        procExmapped += 1

                    isFile = PageAnalyzer.isFilePage(entry)
                    if isFile:
                        totalFile += 1
                        procFile += 1

                    refCnt = PageAnalyzer.getPageCount(pfn)
                    if refCnt > 1:
                        refCnt = 1
                        totalRef += 1
                        procRef += 1
                    else:
                        refCnt = 0

                    if showAttr:
                        kflags = PageAnalyzer.getPageFlags(pfn)
                        totalFlags |= kflags
                        if kflags:
                            bflags = hex(kflags).rstrip("L")
                            sflags = PageAnalyzer.getFlagTypes(bflags)
                            bflags = "(%s)" % bflags
                        else:
                            sflags = bflags = ""
                    else:
                        sflags = bflags = ""

                    if SysMgr.showAll:
                        SysMgr.addPrint(
                            (
                                "{0:>18} |{1:>15} |{2:>5}|{3:>5}|{4:>5}|"
                                "{5:>5}|{6:>5}|{7:>5}|{8:>5}| {9} {10}\n"
                            ).format(
                                hex(addr).rstrip("L"),
                                hex(pfn).rstrip("L") if pfn else "",
                                isPresent,
                                isSwapped,
                                isFile,
                                convSize(refCnt, isInt=True),
                                isSoftdirty,
                                isExmapped,
                                isIdle,
                                sflags,
                                bflags,
                            ),
                            force=True,
                            isList=True,
                        )

                procTotalFlags |= totalFlags
                totalFlagsStr = hex(totalFlags).rstrip("L")

                # print total stats #
                _printPipe(
                    (
                        "{0:>18} |{1:>15} |{2:>5}|{3:>5}|{4:>5}|"
                        "{5:>5}|{6:>5}|{7:>5}|{8:>5}| {9} ({10})\n{11:1}"
                    ).format(
                        "[TOTAL]",
                        "",
                        convSize(totalPresent, isInt=True),
                        convSize(totalSwapped, isInt=True),
                        convSize(totalFile, isInt=True),
                        convSize(totalRef, isInt=True),
                        convSize(totalSoftdirty, isInt=True),
                        convSize(totalExmapped, isInt=True),
                        convSize(totalIdle, isInt=True),
                        PageAnalyzer.getFlagTypes(totalFlagsStr),
                        totalFlagsStr,
                        oneLine,
                    )
                )

                # print all items #
                if verb:
                    SysMgr.doPrint(newline=False, clear=True, isList=True)
                    if SysMgr.showAll:
                        _printPipe("%s\n" % oneLine)
                else:
                    SysMgr.clearPrint()

                # add a new area to list #
                if retList:
                    idlePageList[pid].append([addrs, addreNew, pageSubTable])

                # merge pagetables #
                if saveBitmapFlag and checkIdle:
                    pageTable += pageSubTable

            if progress:
                UtilMgr.deleteProgress()

            if cmdset:
                # destroy debugger object #
                dbgObj.detach()
                del dbgObj

            # check skip condition #
            if skipAttr:
                continue

            # print process memory info #
            _printPipe(
                "\n[Mem Info] [Proc: %s(%s)] [Area: %s)\n%s"
                % (SysMgr.getComm(pid), pid, convNum(len(targetList)), twoLine)
            )

            _printPipe(
                (
                    "{0:^10}|{1:^8}|{2:^8}|{3:^8}|{4:^8}|"
                    "{5:^8}|{6:^8}|{7:^8}| {8} ({9})\n{10}"
                ).format(
                    "[TOTAL]",
                    "PRESENT",
                    "SWAP",
                    "FILE",
                    "SHR",
                    "SDRT",
                    "EXMAP",
                    "IDLE",
                    "ATTRIBUTES",
                    "BITS",
                    oneLine,
                )
            )

            procTotalFlagsStr = hex(procTotalFlags).rstrip("L")

            # print total page stats #
            _printPipe(
                (
                    "{0:^9} |{1:>7} |{2:>7} |{3:>7} |"
                    "{4:>7} |{5:>7} |{6:>7} |{7:>7} | {8}"
                ).format(
                    "PAGE",
                    convNum(procPresent),
                    convNum(procSwapped),
                    convNum(procFile),
                    convSize(procRef, isInt=True),
                    convNum(procSoftdirty),
                    convNum(procExmapped),
                    convNum(procIdle) if checkIdle or markIdle else "-",
                    PageAnalyzer.getFlagTypes(procTotalFlagsStr),
                )
            )

            # print total size stats #
            _printPipe(
                (
                    "{0:^9} |{1:>7} |{2:>7} |{3:>7} |"
                    "{4:>7} |{5:>7} |{6:>7} |{7:>7} | {8}\n{9}"
                ).format(
                    "SIZE",
                    convSize(procPresent << 12),
                    convSize(procSwapped << 12),
                    convSize(procFile << 12),
                    convSize(procRef << 12, isInt=True),
                    convSize(procSoftdirty << 12),
                    convSize(procExmapped << 12),
                    convSize(procIdle << 12) if checkIdle or markIdle else "-",
                    procTotalFlagsStr,
                    oneLine,
                )
            )

            # save idle bitmap to the file #
            if saveBitmapFlag and checkIdle:
                if SysMgr.printFd:
                    filename = SysMgr.printFd.name
                    name, ext = os.path.splitext(filename)
                    filename = "%s_idlemap%s%s" % (name, pid, ext)
                else:
                    filename = "idlemap.out"

                # backup #
                SysMgr.backupFile(filename)

                # save bitmap to the file #
                try:
                    with open(filename, "wb") as fd:
                        btable = bytes(pageTable)
                        fd.write(btable)
                    os.chmod(filename, 0o777)
                except:
                    SysMgr.printOpenErr(filename)
                    continue

                # get output size #
                fsize = UtilMgr.getFileSizeStr(filename)

                SysMgr.printStat(
                    "saved the bitmap data to '%s'%s successfully"
                    % (filename, fsize)
                )

            # print split line between proceses #
            if pidx < len(pids) - 1:
                _printPipe("\n\n" + splitLine + "\n\n")

        return idlePageList

    @staticmethod
    def printMemoryArea(
        pid,
        start=-1,
        end=-1,
        comm=None,
        lastLine=False,
        showall=True,
        cache=False,
    ):
        buf = []
        count = 0
        switch = False
        fpath = "%s/%s/maps" % (SysMgr.procPath, pid)

        if start == end == -1 and not showall:
            summaryFlag = True
        else:
            summaryFlag = False

        # read all map info #
        try:
            # summary #
            if summaryFlag:
                buf = FileAnalyzer.getProcMapInfo(pid, saveAll=True)
            else:
                # check cache #
                if cache:
                    buf = PageAnalyzer.mapFileCache.get(fpath, [])

                if not buf:
                    with open(fpath, "r") as fd:
                        buf = fd.readlines()

                # save cache #
                if cache:
                    PageAnalyzer.mapFileCache[fpath] = buf
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printOpenErr(fpath)
            sys.exit(-1)

        convSize = UtilMgr.convSize2Unit

        if start == end == -1:
            if not comm:
                comm = SysMgr.getComm(pid)

            # get mem stat #
            mlist = SysMgr.getMemStat(pid)
            mstat = SysMgr.convMemStat(mlist)
            vss = convSize(mstat["vss"])
            rss = convSize(mstat["rss"])
            SysMgr.printPipe(
                "\n[Mem Info] [Proc: %s(%s)] [VSS: %s] [RSS: %s]"
                % (comm, pid, vss, rss)
            )

        startHex = hex(start)
        endHex = hex(end)
        allHex = hex(-1)

        # print menu #
        menuStr = ""
        menuBuf = menuList = [
            "AREA",
            "PERM",
            "%8s" % "OFFSET",
            "%6s" % "DEV",
            "%12s" % "INODE",
            "%5s" % "SIZE",
        ]

        for idx, value in enumerate(menuBuf):
            if idx < 6:
                if idx == 0:
                    text = "{0:^38}".format(menuList[idx])
                else:
                    text = menuList[idx]
            else:
                break

            value = " " * (len(value) - len(text) + 1)
            menuStr = "%s%s%s" % (menuStr, text, value)

        menuStr += "TARGET"
        SysMgr.printPipe("%s\n%s\n%s" % (twoLine, menuStr, oneLine))

        # print summarized map info #
        if summaryFlag:
            for fname, info in sorted(
                buf.items(), key=lambda e: e[1]["vstart"]
            ):
                # skip non-contiguous segments #
                if SysMgr.magicStr in fname:
                    continue

                # convert path from overlayfs mount point #
                overlayInfo = SysMgr.getOverlayfsInfo(pid)
                if overlayInfo:
                    fname = UtilMgr.convOverlayPath(fname, overlayInfo)

                try:
                    soffset = hex(info["vstart"]).rstrip("L")
                    eoffset = hex(info["vend"]).rstrip("L")
                    size = convSize(
                        long(eoffset, 16) - long(soffset, 16), True
                    )

                    if not fname.startswith("/"):
                        fname = "[%s]" % fname

                    SysMgr.printPipe(
                        "%18s %18s %4s %8s %6s %12s %5s %s"
                        % (
                            soffset,
                            eoffset,
                            info["perm"],
                            info["offset"],
                            info["devid"],
                            info["inode"],
                            size,
                            fname,
                        )
                    )

                    count += 1

                except SystemExit:
                    sys.exit(0)
                except:
                    pass

            if count == 0:
                SysMgr.printPipe("no involved memory area")

            if lastLine:
                SysMgr.printPipe(oneLine)

            return

        # get anon size #
        amap = TaskAnalyzer.getProcAnonMap(pid, cache=cache)

        # print all map info #
        for line in buf:
            tmplist = line.split()
            vma = tmplist[0]
            soffset, eoffset = vma.split("-")

            soffset = long(soffset, base=16)
            eoffset = long(eoffset, base=16)

            if startHex == endHex == allHex:
                switch = False
            elif "-" in line:
                if soffset <= start < eoffset:
                    switch = True
                elif not switch:
                    continue
                elif end < eoffset:
                    break

            try:
                target = line[:-1].split(None, 5)

                target[4] = "%12s" % target[4]

                soffsetHex = hex(soffset)
                eoffsetHex = hex(eoffset)

                if not soffsetHex.startswith("0x"):
                    soffsetHex = "0x%s" % soffsetHex

                if not eoffsetHex.startswith("0x"):
                    eoffsetHex = "0x%s" % eoffsetHex

                size = convSize(eoffset - soffset, True)

                # convert path from overlayfs mount point #
                fname = target[5] if len(target) > 5 else " "
                overlayInfo = SysMgr.getOverlayfsInfo(pid)
                if overlayInfo:
                    fname = UtilMgr.convOverlayPath(fname, overlayInfo)

                # add anon size #
                if fname == " " and vma in amap and amap[vma]["size"]:
                    fname = "anon(%s)" % UtilMgr.convNum(
                        long(amap[vma]["size"]) << 10
                    )

                SysMgr.printPipe(
                    "%18s %18s %4s %8s %6s %12s %5s %s"
                    % (
                        soffsetHex,
                        eoffsetHex,
                        target[1],
                        target[2],
                        target[3],
                        target[4],
                        size,
                        fname,
                    )
                )
            except SystemExit:
                sys.exit(0)
            except:
                pass

            count += 1

            if switch and end <= eoffset:
                break

        if count == 0:
            SysMgr.printPipe("no involved memory area")

        if lastLine:
            SysMgr.printPipe(oneLine)

    @staticmethod
    def getFlagTypes(flags):
        nrFlags = long(flags, 16)
        maxBit = nrFlags.bit_length()
        sflags = [
            PageAnalyzer.flagList[idx][4:]
            for idx in xrange(maxBit)
            if nrFlags & (1 << idx)
        ]
        return "|".join(sflags)

    @staticmethod
    def readEntry(path, offset, size=8):
        try:
            f = SysMgr.getCachedFd(path)
            f.seek(offset, 0)

            data = f.read(size)
            if not data:
                return 0

            return struct.unpack("Q" if size == 8 else "I", data)[0]
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                "failed to read %s byte from %s of %s" % (size, offset, path),
                reason=True,
            )
            sys.exit(-1)

    @staticmethod
    def getPagemapPath(pid):
        path = "{0}/{1}/pagemap".format(SysMgr.procPath, pid)
        if not os.path.isfile(path):
            SysMgr.printErr("failed to find the process with PID %s" % pid)
            sys.exit(-1)
        return path

    @staticmethod
    def getPagemap(pid, addr, size):
        path = PageAnalyzer.getPagemapPath(pid)
        offset = long(addr / SysMgr.PAGESIZE) * 8
        size = long(size / SysMgr.PAGESIZE) * 8

        try:
            f = SysMgr.getCachedFd(path)
            f.seek(offset, 0)
            return f.read(size)
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                "failed to read %s byte from %s of %s" % (size, offset, path),
                reason=True,
            )
            sys.exit(-1)

    @staticmethod
    def getPagemapEntry(pid, addr):
        path = PageAnalyzer.getPagemapPath(pid)
        offset = long(addr / SysMgr.PAGESIZE) * 8
        return PageAnalyzer.readEntry(path, offset)

    @staticmethod
    def getPfn(entry):
        return entry & 0x7FFFFFFFFFFFFF

    @staticmethod
    def isSoftdirty(entry):
        return (entry & (1 << 55)) != 0

    @staticmethod
    def isExmapped(entry):
        return (entry & (1 << 56)) != 0

    @staticmethod
    def isWriteProtected(entry):
        return (entry & (1 << 57)) != 0

    @staticmethod
    def isSwapped(entry):
        return (entry & (1 << 62)) != 0

    @staticmethod
    def isFilePage(entry):
        return (entry & (1 << 61)) != 0

    @staticmethod
    def isPresent(entry):
        return (entry & (1 << 63)) != 0

    @staticmethod
    def getPageCount(pfn):
        path = SysMgr.procPath + "/kpagecount"
        return PageAnalyzer.readEntry(path, pfn * 8)

    @staticmethod
    def getPageFlags(pfn):
        path = SysMgr.procPath + "/kpageflags"
        return PageAnalyzer.readEntry(path, pfn * 8)


class FunctionAnalyzer(object):
    """Analyzer for function profiling"""

    symStackIdxTable = [
        "CPU_TICK",
        "STACK",
        "PAGE_ALLOC",
        "PAGE_FREE",
        "BLK_READ",
        "ARGUMENT",
        "HEAP_EXPAND",
        "HEAP_REDUCE",
        "IGNORE",
        "BLK_WRITE",
        "LOCK_TRY",
        "UNLOCK",
        "SYSCALL",
        "CUSTOM",
    ]

    def __init__(self, logFile):
        self.cpuEnabled = False
        self.memEnabled = False
        self.heapEnabled = False
        self.breadEnabled = False
        self.bwriteEnabled = False
        self.sigEnabled = False
        self.lockEnabled = False
        self.sysEnabled = False

        self.sort = "sym"
        self.connObj = None

        self.finishTime = "0"
        self.lastTime = "0"
        self.totalTime = 0
        self.totalTick = 0
        self.prevTime = "0"
        self.prevTid = "0"

        self.lastCore = None
        self.coreCtx = {}
        self.nowCtx = None
        self.nowEvent = None
        self.savedEvent = None
        self.nestedEvent = None
        self.nowCnt = 0
        self.savedCnt = 0
        self.nestedCnt = 0
        self.nowArg = 0
        self.savedArg = 0
        self.nestedArg = 0

        self.duplicatedPos = 0
        self.periodicEventCnt = 0
        self.periodicContEventCnt = 0
        self.periodicEventInterval = 0
        self.heapExpEventCnt = 0
        self.heapExpSize = 0
        self.heapRedEventCnt = 0
        self.heapRedSize = 0
        self.pageAllocEventCnt = 0
        self.pageAllocCnt = 0
        self.pageFreeEventCnt = 0
        self.pageFreeCnt = 0
        self.pageUnknownFreeCnt = 0
        self.pageUsageCnt = 0
        self.blockRdEventCnt = 0
        self.blockRdUsageCnt = 0
        self.blockWrEventCnt = 0
        self.blockWrUsageCnt = 0
        self.lockTryEventCnt = 0
        self.unlockEventCnt = 0
        self.customCnt = 0
        self.customTotal = 0
        self.syscallCnt = 0

        self.customEventTable = {}
        self.ignoreTable = {}
        self.mapData = []
        self.pageTable = {}
        self.oldPageTable = {}
        self.heapTable = {}
        self.oldHeapTable = {}
        self.posData = {}
        self.userSymData = {}
        self.userFileData = {}
        self.kerSymData = {}
        self.threadData = {}
        self.syscallTable = {}
        self.customCallData = []
        self.lockCallData = []
        self.sysCallData = []
        self.userCallData = []
        self.kernelCallData = []
        """
        userCallData = kernelCallData = \
            [pos, stack, event, eventCnt, eventArg]
        """

        self.init_threadData = {
            "comm": "?",
            "tgid": "-" * 5,
            "target": False,
            "cpuTick": 0,
            "die": False,
            "new": False,
            "nrPages": 0,
            "userPages": 0,
            "cachePages": 0,
            "kernelPages": 0,
            "heapSize": 0,
            "eventCnt": 0,
            "nrWrBlocks": 0,
            "customCnt": 0,
            "nrUnknownFreePages": 0,
            "nrKnownFreePages": 0,
            "nrRdBlocks": 0,
            "nrLockTry": 0,
            "nrUnlock": 0,
            "customTotal": 0,
            "nrSyscall": 0,
            "syscallTable": None,
            "lastNrSyscall": -1,
        }

        self.init_posData = {
            "symbol": "",
            "binary": "",
            "origBin": "",
            "offset": hex(0),
            "posCnt": 0,
            "userPageCnt": 0,
            "cachePageCnt": 0,
            "kernelPageCnt": 0,
            "totalCnt": 0,
            "blockRdCnt": 0,
            "blockWrCnt": 0,
            "pageCnt": 0,
            "heapSize": 0,
            "unknownPageFreeCnt": 0,
            "src": "",
            "customCnt": 0,
            "customTotal": 0,
            "lockTryCnt": 0,
            "unlockCnt": 0,
            "syscallCnt": 0,
        }

        self.init_symData = {
            "pos": "",
            "origBin": "",
            "tickCnt": 0,
            "blockRdCnt": 0,
            "pageCnt": 0,
            "unknownPageFreeCnt": 0,
            "stack": None,
            "symStack": None,
            "userPageCnt": 0,
            "cachePageCnt": 0,
            "kernelPageCnt": 0,
            "heapSize": 0,
            "blockWrCnt": 0,
            "customCnt": 0,
            "customTotal": 0,
            "pagePair": None,
            "pagePairCnt": 0,
            "pagePairTotal": 0.0,
            "pagePairMin": 0.0,
            "pagePairMax": 0.0,
            "pagePairAvr": 0.0,
            "pageRemainMin": 0.0,
            "pageRemainMax": 0.0,
            "pageRemainAvr": 0.0,
            "pageRemainTotal": 0.0,
            "lockTryCnt": 0,
            "unlockCnt": 0,
            "syscallCnt": 0,
            "totalTickCnt": 0,
        }

        self.init_ctxData = {
            "nestedEvent": None,
            "savedEvent": None,
            "nowEvent": None,
            "nested": 0,
            "recStat": False,
            "nestedCnt": 0,
            "savedCnt": 0,
            "nowCnt": 0,
            "nestedArg": None,
            "savedArg": None,
            "prevMode": None,
            "curMode": None,
            "userLastPos": "",
            "userStack": None,
            "kerLastPos": "",
            "kerStack": None,
            "prevKerLastPos": "",
            "prevKerStack": None,
            "nowArg": None,
            "prevTid": None,
            "prevTime": None,
        }

        self.init_pageLinkData = {
            "sym": "0",
            "subStackAddr": 0,
            "ksym": "0",
            "ksubStackAddr": 0,
            "type": "0",
            "time": "0",
        }

        self.init_heapSegData = {
            "tid": "0",
            "size": 0,
            "sym": "0",
            "subStackAddr": 0,
            "ksym": "0",
            "ksubStackAddr": 0,
            "time": 0.0,
            "core": "0",
        }

        self.init_pageData = {
            "tid": "0",
            "page": "0",
            "flags": "0",
            "type": "0",
            "time": "0",
        }

        self.init_glueData = {
            "count": 0,
            "size": 0,
            "timeList": None,
            "valueList": None,
        }

        self.init_subStackPageInfo = [0, 0, 0]
        # subStackPageInfo = [userPageCnt, cachePageCnt, kernelPageCnt]

        # read trace data #
        lines = TaskAnalyzer.readTraceData(logFile)

        # save trace data and stop analysis #
        if SysMgr.outputFile:
            SysMgr.saveTraceData(lines)
            sys.exit(0)

        # Check target thread setting #
        if not SysMgr.filterGroup:
            SysMgr.filterGroup.insert(0, "")
            self.target = []
        else:
            self.target = SysMgr.filterGroup

        # Check root path #
        if SysMgr.userEnable:
            if SysMgr.rootPath == "":
                rootPath = "/"
            else:
                rootPath = SysMgr.rootPath
            SysMgr.printInfo("use '%s' as the sysroot path" % rootPath)

        # Register None pos #
        self.posData["0"] = dict(self.init_posData)

        # get process tree #
        SysMgr.getProcTreeInfo()

        # start parsing logs #
        SysMgr.totalLine = len(lines)
        SysMgr.printStat("start analyzing data... [ STOP(Ctrl+c) ]")

        # parse logs #
        self.parseLines(lines, SysMgr.filterGroup)

        # Check whether data of target thread is collected or nothing #
        if (
            not self.userCallData
            and not self.kernelCallData
            and len(self.target) > 0
        ):
            if not self.target:
                SysMgr.printErr("no collected stack data")
            else:
                targetStr = ", ".join(self.target)
                SysMgr.printErr(
                    "no collected stack related to '%s'" % targetStr
                )
            sys.exit(-1)
        elif (
            SysMgr.userEnable
            and len(self.userCallData) == 1
            and self.userCallData[0][0] == "0"
        ):
            SysMgr.userEnable = False
            if self.target == []:
                SysMgr.printWarn("no collected user stack data", True)
            else:
                targetStr = ", ".join(self.target)
                SysMgr.printWarn(
                    "no collected user stack related to '%s'" % targetStr, True
                )

        # Get symbols from call address #
        SysMgr.printStat("start resolving symbols... [ STOP(Ctrl+c) ]")
        self.getSymbols()

        # Merge callstacks by symbol and address #
        SysMgr.printStat("start summarizing functions... [ STOP(Ctrl+c) ]")
        self.mergeStacks()

    def __del__(self):
        pass

    def handleHeapExpand(self, sym, ksym, stackAddr, kstackAddr, size, arg):
        addr = arg[0]
        time = arg[1]
        core = arg[2]
        tid = arg[3]

        self.userSymData[sym]["heapSize"] += size
        self.kerSymData[ksym]["heapSize"] += size

        self.heapTable.setdefault(addr, dict(self.init_heapSegData))
        self.heapTable[addr]["size"] = size
        self.heapTable[addr]["sym"] = sym
        self.heapTable[addr]["ksym"] = ksym
        self.heapTable[addr]["subStackAddr"] = stackAddr
        self.heapTable[addr]["ksubStackAddr"] = kstackAddr
        self.heapTable[addr]["time"] = time
        self.heapTable[addr]["core"] = core
        self.heapTable[addr]["tid"] = tid

    def handleHeapReduce(self, size, arg):
        addr = arg[0]

        subStackIndex = FunctionAnalyzer.symStackIdxTable.index("STACK")
        heapExpIndex = FunctionAnalyzer.symStackIdxTable.index("HEAP_EXPAND")

        try:
            sym = self.heapTable[addr]["sym"]
            ksym = self.heapTable[addr]["ksym"]
            stackAddr = self.heapTable[addr]["subStackAddr"]
            kstackAddr = self.heapTable[addr]["ksubStackAddr"]

            self.userSymData[sym]["heapSize"] -= size
            self.kerSymData[ksym]["heapSize"] -= size
        except:
            SysMgr.printWarn("failed to find heap segment to be freed")
            return

        # Set user stack list #
        if self.sort == "sym":
            targetStack = self.userSymData[sym]["symStack"]
        elif self.sort == "pos":
            targetStack = self.userSymData[sym]["stack"]

        # Find user stack of symbol allocated this segment #
        for val in targetStack:
            if id(val[subStackIndex]) == stackAddr:
                # Increase heap count of subStack #
                val[heapExpIndex] -= size
                break

        # Set kernel stack list #
        kernelTargetStack = self.kerSymData[ksym]["stack"]

        # Find kernel stack of symbol allocated this segment #
        for val in kernelTargetStack:
            if id(val[subStackIndex]) == kstackAddr:
                # Increase heap count of subStack #
                val[heapExpIndex] -= size
                break

        self.heapTable.pop(addr)

    def handlePageFree(
        self,
        sym,
        ksym,
        stackAddr,
        kstackAddr,
        pageFreeCnt,
        pageType,
        pfn,
        atime,
    ):
        subStackIndex = FunctionAnalyzer.symStackIdxTable.index("STACK")
        pageAllocIndex = FunctionAnalyzer.symStackIdxTable.index("PAGE_ALLOC")
        pageFreeIndex = FunctionAnalyzer.symStackIdxTable.index("PAGE_FREE")
        argIndex = FunctionAnalyzer.symStackIdxTable.index("ARGUMENT")

        for cnt in xrange(pageFreeCnt):
            pfnv = pfn + cnt
            subStackPageInfoIdx = 0

            try:
                # Decrease page count of symbol allocated page #
                # TODO: fix bug about wrong count of pos #
                allocSym = self.pageTable[pfnv]["sym"]
                allocStackAddr = self.pageTable[pfnv]["subStackAddr"]
                allocKernelSym = self.pageTable[pfnv]["ksym"]
                allocKernelStackAddr = self.pageTable[pfnv]["ksubStackAddr"]
                allocTime = self.pageTable[pfnv]["time"]

                self.pageUsageCnt -= 1
                self.userSymData[allocSym]["pageCnt"] -= 1
                self.kerSymData[allocKernelSym]["pageCnt"] -= 1

                if pageType == "USER":
                    self.userSymData[allocSym]["userPageCnt"] -= 1
                    self.kerSymData[allocKernelSym]["userPageCnt"] -= 1
                    subStackPageInfoIdx = 0
                elif pageType == "CACHE":
                    self.userSymData[allocSym]["cachePageCnt"] -= 1
                    self.kerSymData[allocKernelSym]["cachePageCnt"] -= 1
                    subStackPageInfoIdx = 1
                elif pageType == "KERNEL":
                    self.userSymData[allocSym]["kernelPageCnt"] -= 1
                    self.kerSymData[allocKernelSym]["kernelPageCnt"] -= 1
                    subStackPageInfoIdx = 2

                # get page lifetime #
                lifeTime = float(atime) - float(allocTime)

                # Set user page lifetime #
                if lifeTime > self.userSymData[allocSym]["pagePairMax"]:
                    self.userSymData[allocSym]["pagePairMax"] = lifeTime
                if (
                    self.userSymData[allocSym]["pagePairMin"] == 0
                    or lifeTime < self.userSymData[allocSym]["pagePairMin"]
                ):
                    self.userSymData[allocSym]["pagePairMin"] = lifeTime
                self.userSymData[allocSym]["pagePairTotal"] += lifeTime

                # Set kernel page lifetime #
                if lifeTime > self.kerSymData[allocKernelSym]["pagePairMax"]:
                    self.kerSymData[allocKernelSym]["pagePairMax"] = lifeTime
                if (
                    self.kerSymData[allocKernelSym]["pagePairMin"] == 0
                    or lifeTime
                    < self.kerSymData[allocKernelSym]["pagePairMin"]
                ):
                    self.kerSymData[allocKernelSym]["pagePairMin"] = lifeTime
                self.kerSymData[allocKernelSym]["pagePairTotal"] += lifeTime

                # Set user stack list #
                if self.sort == "sym":
                    targetStack = self.userSymData[allocSym]["symStack"]
                elif self.sort == "pos":
                    targetStack = self.userSymData[allocSym]["stack"]

                # Find user stack allocated this page #
                for val in targetStack:
                    if id(val[subStackIndex]) != allocStackAddr:
                        continue

                    val[pageAllocIndex] -= 1
                    val[argIndex][subStackPageInfoIdx] -= 1

                    # Set user stack list to free this page #
                    if self.sort == "sym":
                        subTargetStack = self.userSymData[sym]["symStack"]
                    elif self.sort == "pos":
                        subTargetStack = self.userSymData[sym]["stack"]

                    # Find user stack to free this page #
                    for sval in subTargetStack:
                        if id(sval[subStackIndex]) != stackAddr:
                            continue

                        if not self.userSymData[allocSym]["pagePair"]:
                            self.userSymData[allocSym]["pagePair"] = {}

                        allocCall = ""
                        freeCall = ""

                        try:
                            tsym = val[subStackIndex][0]
                            allocCall = "%s [%s]" % (
                                val[subStackIndex][0],
                                self.userSymData[tsym]["origBin"],
                            )
                            for usym in val[subStackIndex][1:]:
                                allocCall = "%s <- %s [%s]" % (
                                    allocCall,
                                    usym,
                                    self.userSymData[sym]["origBin"],
                                )
                        except:
                            if allocCall == "":
                                allocCall = "None"

                        try:
                            freeCall = "%s [%s]" % (
                                sym,
                                self.userSymData[sym]["origBin"],
                            )
                            for usym in sval[subStackIndex][1:]:
                                freeCall = "%s <- %s[%s]" % (
                                    freeCall,
                                    usym,
                                    self.userSymData[sym]["origBin"],
                                )
                        except:
                            if freeCall == "":
                                freeCall = "None"

                        pairId = "%s#%s" % (allocCall, freeCall)

                        try:
                            self.userSymData[allocSym]["pagePair"][pairId]
                        except:
                            self.userSymData[allocSym]["pagePair"][
                                pairId
                            ] = dict(self.init_glueData)

                        self.userSymData[allocSym]["pagePairCnt"] += 1
                        allocator = self.userSymData[allocSym]["pagePair"][
                            pairId
                        ]
                        allocator["size"] += 1

                        if not allocator["valueList"]:
                            allocator["valueList"] = {}
                        try:
                            allocator["valueList"][pageType] += 1
                        except:
                            allocator["valueList"][pageType] = 1

                        break
                    break

                # Set kernel stack list #
                kernelTargetStack = self.kerSymData[allocKernelSym]["stack"]

                # Find kernel stack allocated this page #
                for val in kernelTargetStack:
                    if id(val[subStackIndex]) != allocKernelStackAddr:
                        continue

                    val[pageAllocIndex] -= 1
                    val[argIndex][subStackPageInfoIdx] -= 1

                    # Set kernel stack list to free this page #
                    subTargetStack = self.kerSymData[ksym]["stack"]

                    # Find kernel stack to free this page #
                    for sval in subTargetStack:
                        if id(sval[subStackIndex]) != kstackAddr:
                            continue

                        kernelData = self.kerSymData[allocKernelSym]

                        if not kernelData["pagePair"]:
                            kernelData["pagePair"] = {}

                        allocCall = ""
                        freeCall = ""

                        try:
                            allocCall = (
                                "%s"
                                % self.posData[val[subStackIndex][0]]["symbol"]
                            )
                            for addr in val[subStackIndex][1:]:
                                allocCall = "%s <- %s" % (
                                    allocCall,
                                    self.posData[addr]["symbol"],
                                )
                        except:
                            if allocCall == "":
                                allocCall = "None"

                        try:
                            freeCall = "%s" % ksym
                            for addr in sval[subStackIndex]:
                                freeCall = "%s <- %s" % (
                                    freeCall,
                                    self.posData[addr]["symbol"],
                                )
                        except:
                            if freeCall == "":
                                freeCall = "None"

                        pairId = "%s#%s" % (allocCall, freeCall)

                        try:
                            kernelData["pagePair"][pairId]
                        except:
                            kernelData["pagePair"][pairId] = dict(
                                self.init_glueData
                            )

                        self.kerSymData[allocKernelSym]["pagePairCnt"] += 1
                        allocator = kernelData["pagePair"][pairId]
                        allocator["size"] += 1

                        if not allocator["valueList"]:
                            allocator["valueList"] = {}
                        try:
                            allocator["valueList"][pageType] += 1
                        except:
                            allocator["valueList"][pageType] = 1

                        break

                    break

                self.pageTable.pop(pfnv, None)
            except SystemExit:
                sys.exit(0)
            except:
                # this page is allocated before starting profile #

                self.pageUnknownFreeCnt += 1
                self.userSymData[sym]["unknownPageFreeCnt"] += 1
                self.kerSymData[ksym]["unknownPageFreeCnt"] += 1

                # Set user stack list #
                if self.sort == "sym":
                    targetStack = self.userSymData[sym]["symStack"]
                elif self.sort == "pos":
                    targetStack = self.userSymData[sym]["stack"]

                # Find subStack allocated this page #
                for val in targetStack:
                    if id(val[subStackIndex]) == stackAddr:
                        val[pageFreeIndex] += 1
                        break

                # Set kernel stack list #
                kernelTargetStack = self.kerSymData[ksym]["stack"]

                # Find subStack allocated this page #
                for val in kernelTargetStack:
                    if id(val[subStackIndex]) == kstackAddr:
                        val[pageFreeIndex] += 1
                        break

                continue

    def handlePageAlloc(
        self,
        sym,
        ksym,
        stackAddr,
        kstackAddr,
        pageAllocCnt,
        pageType,
        pfn,
        atime,
    ):
        subStackPageInfoIdx = 0

        subStackIndex = FunctionAnalyzer.symStackIdxTable.index("STACK")
        pageAllocIndex = FunctionAnalyzer.symStackIdxTable.index("PAGE_ALLOC")
        argIndex = FunctionAnalyzer.symStackIdxTable.index("ARGUMENT")

        # Increase counts of page to be allocated #
        self.userSymData[sym]["pageCnt"] += pageAllocCnt
        self.kerSymData[ksym]["pageCnt"] += pageAllocCnt

        if pageType == "USER":
            self.userSymData[sym]["userPageCnt"] += pageAllocCnt
            self.kerSymData[ksym]["userPageCnt"] += pageAllocCnt
            subStackPageInfoIdx = 0
        elif pageType == "CACHE":
            self.userSymData[sym]["cachePageCnt"] += pageAllocCnt
            self.kerSymData[ksym]["cachePageCnt"] += pageAllocCnt
            subStackPageInfoIdx = 1
        elif pageType == "KERNEL":
            self.userSymData[sym]["kernelPageCnt"] += pageAllocCnt
            self.kerSymData[ksym]["kernelPageCnt"] += pageAllocCnt
            subStackPageInfoIdx = 2

        # Set user stack list #
        if self.sort == "sym":
            targetStack = self.userSymData[sym]["symStack"]
        elif self.sort == "pos":
            targetStack = self.userSymData[sym]["stack"]

        # Find user stack of symbol allocated this page #
        for val in targetStack:
            if id(val[subStackIndex]) == stackAddr:
                # Increase page count of subStack #
                val[argIndex][subStackPageInfoIdx] += pageAllocCnt
                break

        # Set kernel stack list #
        kernelTargetStack = self.kerSymData[ksym]["stack"]

        # Find kernel stack of symbol allocated this page #
        for val in kernelTargetStack:
            if id(val[subStackIndex]) == kstackAddr:
                # Increase page count of subStack #
                val[argIndex][subStackPageInfoIdx] += pageAllocCnt
                break

        # Make PTE in page table #
        for cnt in xrange(pageAllocCnt):
            pfnv = pfn + cnt
            subStackPageInfoIdx = 0

            try:
                # Check whether this page is already allocated #
                allocSym = self.pageTable[pfnv]["sym"]

                allocStackAddr = self.pageTable[pfnv]["subStackAddr"]
                allocKernelSym = self.pageTable[pfnv]["ksym"]
                allocKernelStackAddr = self.pageTable[pfnv]["ksubStackAddr"]

                # Decrease counts of page already allocated but no free log #
                self.pageUsageCnt -= 1
                self.userSymData[allocSym]["pageCnt"] -= 1
                self.kerSymData[allocKernelSym]["pageCnt"] -= 1

                origPageType = self.pageTable[pfnv]["type"]
                if origPageType == "USER":
                    self.userSymData[allocSym]["userPageCnt"] -= 1
                    self.kerSymData[allocKernelSym]["userPageCnt"] -= 1
                    subStackPageInfoIdx = 0
                elif origPageType == "CACHE":
                    self.userSymData[allocSym]["cachePageCnt"] -= 1
                    self.kerSymData[allocKernelSym]["cachePageCnt"] -= 1
                    subStackPageInfoIdx = 1
                elif origPageType == "KERNEL":
                    self.userSymData[allocSym]["kernelPageCnt"] -= 1
                    self.kerSymData[allocKernelSym]["kernelPageCnt"] -= 1
                    subStackPageInfoIdx = 2

                # Set user stack list #
                if self.sort == "sym":
                    targetStack = self.userSymData[allocSym]["symStack"]
                elif self.sort == "pos":
                    targetStack = self.userSymData[allocSym]["stack"]

                # Find user stack of symbol allocated this page #
                for val in targetStack:
                    if id(val[subStackIndex]) == allocStackAddr:
                        # Decrease allocated page count of substack #
                        val[pageAllocIndex] -= 1
                        val[argIndex][subStackPageInfoIdx] -= 1
                        break

                # Set kernel stack list #
                kernelTargetStack = self.kerSymData[allocKernelSym]["stack"]

                # Find kernel stack of symbol allocated this page #
                for val in kernelTargetStack:
                    if id(val[subStackIndex]) == allocKernelStackAddr:
                        # Decrease allocated page count of substack #
                        val[pageAllocIndex] -= 1
                        val[argIndex][subStackPageInfoIdx] -= 1
                        break
            except SystemExit:
                sys.exit(0)
            except:
                self.pageTable[pfnv] = dict(self.init_pageLinkData)

            self.pageTable[pfnv]["sym"] = sym
            self.pageTable[pfnv]["ksym"] = ksym
            self.pageTable[pfnv]["type"] = pageType
            self.pageTable[pfnv]["subStackAddr"] = stackAddr
            self.pageTable[pfnv]["ksubStackAddr"] = kstackAddr
            self.pageTable[pfnv]["time"] = atime

    def mergeStacks(self):
        sym = ""
        ksym = ""
        stackAddr = 0
        kstackAddr = 0
        lineCnt = -1
        lastIdx = len(self.userCallData)

        # Backup page table used previously and Initialize it #
        self.oldPageTable = self.pageTable
        self.pageTable = {}

        # Backup heap table used previously and Initialize it #
        self.oldHeapTable = self.heapTable
        self.heapTable = {}

        subStackIndex = FunctionAnalyzer.symStackIdxTable.index("STACK")
        argIndex = FunctionAnalyzer.symStackIdxTable.index("ARGUMENT")

        # Merge call data by symbol or address #
        for val in self.userCallData:
            lineCnt += 1
            UtilMgr.printProgress(lineCnt, lastIdx - 1)

            pos = val[0]
            stack = val[1]
            event = val[2]
            eventCnt = val[3]
            arg = val[4]

            """
            Do not merge PAGE_FREE count
            because it will be merged with unknownPageFreeCnt
            """
            if event == "PAGE_FREE":
                savedEventCnt = eventCnt
                eventCnt = 0

            try:
                eventIndex = FunctionAnalyzer.symStackIdxTable.index(event)
            except:
                eventIndex = FunctionAnalyzer.symStackIdxTable.index("IGNORE")

            kernelPos = self.kernelCallData[lineCnt][0]
            kernelStack = self.kernelCallData[lineCnt][1]
            subStackPageInfo = list(self.init_subStackPageInfo)

            targetStack = []
            kernelTargetStack = []

            # Resolve user symbol #
            try:
                # No symbol related to last pos #
                if self.posData[pos]["symbol"] == "":
                    self.posData[pos]["symbol"] = pos
                    sym = pos
                else:
                    sym = self.posData[pos]["symbol"]
            except:
                continue

            # Resolve kernel symbol #
            try:
                # No symbol related to last pos #
                if self.posData[kernelPos]["symbol"] == "":
                    self.posData[kernelPos]["symbol"] = kernelPos
                    ksym = kernelPos
                else:
                    ksym = self.posData[kernelPos]["symbol"]
            except:
                continue

            # Make user file table of last pos in stack #
            try:
                path = self.posData[pos]["binary"]
                self.userFileData[path]
            except:
                self.userFileData[path] = dict(self.init_symData)

            # Make user symbol table of last pos in stack #
            try:
                self.userSymData[sym]
            except:
                self.userSymData[sym] = dict(self.init_symData)
                self.userSymData[sym]["stack"] = []
                self.userSymData[sym]["symStack"] = []
                self.userSymData[sym]["pos"] = pos
                self.userSymData[sym]["origBin"] = self.posData[pos]["origBin"]

            # Make kernel symbol table of last pos in stack #
            try:
                self.kerSymData[ksym]
            except:
                self.kerSymData[ksym] = dict(self.init_symData)
                self.kerSymData[ksym]["stack"] = []
                self.kerSymData[ksym]["pos"] = kernelPos

            # Set target user stack #
            if self.sort == "sym":
                tempSymStack = []
                # Make temporary symbol stack to merge stacks by symbol #
                for addr in stack:
                    tempSym = self.posData[addr]["symbol"]

                    # Ignore this function if there is no symbol #
                    if (
                        not SysMgr.showAll
                        and self.posData[addr]["origBin"] == "??"
                        and (
                            tempSym == addr
                            or tempSym == self.posData[addr]["offset"]
                            or addr == "00c0ffee"
                        )
                    ):
                        continue

                    # No symbol data #
                    if tempSym == "":
                        if self.posData[addr]["origBin"] == "??":
                            tempSym = "%x" % long(
                                self.posData[addr]["pos"], 16
                            )
                        else:
                            tempSym = "%x" % long(
                                self.posData[addr]["offset"], 16
                            )

                    try:
                        self.userSymData[tempSym]
                    except:
                        self.userSymData[tempSym] = dict(self.init_symData)
                        self.userSymData[tempSym]["stack"] = []
                        self.userSymData[tempSym]["symStack"] = []
                        self.userSymData[tempSym]["pos"] = addr
                        self.userSymData[tempSym]["origBin"] = self.posData[
                            addr
                        ]["origBin"]

                    tempSymStack.append(tempSym)

                # Switch input stack to symbol stack #
                stack = tempSymStack
                targetStack = self.userSymData[sym]["symStack"]
            elif self.sort == "pos":
                targetStack = self.userSymData[sym]["stack"]

            # First user stack related to this symbol #
            if not targetStack:
                tempList = [0] * len(FunctionAnalyzer.symStackIdxTable)
                tempList[eventIndex] = eventCnt
                tempList[subStackIndex] = stack
                tempList[argIndex] = list(subStackPageInfo)
                targetStack.append(tempList)

                stackAddr = id(stack)
            else:
                found = False

                # Find same stack by pos in stack list #
                for stackInfo in targetStack:
                    stackSet = set(stack)
                    subStackSet = set(stackInfo[subStackIndex])

                    # Found same stack #
                    if stackSet == subStackSet:
                        found = True
                        stackInfo[eventIndex] += eventCnt
                        stackAddr = id(stackInfo[subStackIndex])
                        break

                # New stack related to this symbol #
                if not found:
                    tempList = [0] * len(FunctionAnalyzer.symStackIdxTable)
                    tempList[eventIndex] = eventCnt
                    tempList[subStackIndex] = stack
                    tempList[argIndex] = list(subStackPageInfo)
                    targetStack.append(tempList)

                    stackAddr = id(stack)

            # Set target kernel stack #
            kernelTargetStack = self.kerSymData[ksym]["stack"]

            # First stack related to this symbol #
            if not kernelTargetStack:
                tempList = [0] * len(FunctionAnalyzer.symStackIdxTable)
                tempList[eventIndex] = eventCnt
                tempList[subStackIndex] = kernelStack
                tempList[argIndex] = list(subStackPageInfo)
                kernelTargetStack.append(tempList)

                kstackAddr = id(kernelStack)
            else:
                found = False
                for stackInfo in kernelTargetStack:
                    kerStackSet = set(kernelStack)
                    kerSubStackSet = set(stackInfo[subStackIndex])

                    # Found same stack in stack list #
                    if kerStackSet == kerSubStackSet:
                        found = True
                        stackInfo[eventIndex] += eventCnt
                        kstackAddr = id(stackInfo[subStackIndex])
                        break

                # New stack related to this symbol #
                if not found:
                    tempList = [0] * len(FunctionAnalyzer.symStackIdxTable)
                    tempList[eventIndex] = eventCnt
                    tempList[subStackIndex] = kernelStack
                    tempList[argIndex] = list(subStackPageInfo)
                    kernelTargetStack.append(tempList)

                    kstackAddr = id(kernelStack)

            # Recover PAGE_FREE count to merge with unknownPageFreeCnt #
            if event == "PAGE_FREE":
                eventCnt = savedEventCnt

            # memory allocation event #
            if event == "PAGE_ALLOC":
                pageType = arg[0]
                pfn = arg[1]
                atime = arg[2]

                self.handlePageAlloc(
                    sym,
                    ksym,
                    stackAddr,
                    kstackAddr,
                    eventCnt,
                    pageType,
                    pfn,
                    atime,
                )

            # memory free event #
            elif event == "PAGE_FREE":
                pageType = arg[0]
                pfn = arg[1]
                atime = arg[2]

                self.handlePageFree(
                    sym,
                    ksym,
                    stackAddr,
                    kstackAddr,
                    eventCnt,
                    pageType,
                    pfn,
                    atime,
                )

            # heap expand event #
            elif event == "HEAP_EXPAND":
                self.handleHeapExpand(
                    sym, ksym, stackAddr, kstackAddr, eventCnt, arg
                )

            # heap expand event #
            elif event == "HEAP_REDUCE":
                self.handleHeapReduce(eventCnt, arg)

            # block read event #
            elif event == "BLK_READ":
                self.userSymData[sym]["blockRdCnt"] += eventCnt
                self.kerSymData[ksym]["blockRdCnt"] += eventCnt

            # block write event #
            elif event == "BLK_WRITE":
                self.userSymData[sym]["blockWrCnt"] += eventCnt
                self.kerSymData[ksym]["blockWrCnt"] += eventCnt

            # lock try event #
            elif event == "LOCK_TRY":
                self.userSymData[sym]["lockTryCnt"] += eventCnt
                self.kerSymData[ksym]["lockTryCnt"] += eventCnt
                self.userFileData[path]["lockTryCnt"] += eventCnt

            # unlock event #
            elif event == "UNLOCK":
                self.userSymData[sym]["unlockCnt"] += eventCnt
                self.kerSymData[ksym]["unlockCnt"] += eventCnt
                self.userFileData[path]["unlockCnt"] += eventCnt

            # periodic event such as CPU tick #
            elif event == "CPU_TICK":
                self.userSymData[sym]["tickCnt"] += 1
                self.kerSymData[ksym]["tickCnt"] += 1
                self.userFileData[path]["tickCnt"] += 1

            # syscall event #
            elif event == "SYSCALL":
                self.userSymData[sym]["syscallCnt"] += 1
                self.kerSymData[ksym]["syscallCnt"] += 1
                self.userFileData[path]["syscallCnt"] += 1

            # periodic event such as CPU tick #
            elif event == "CUSTOM":
                if eventCnt > 0:
                    self.userSymData[sym]["customTotal"] += 1
                    self.kerSymData[ksym]["customTotal"] += 1
                    self.userFileData[path]["customTotal"] += 1

                self.userSymData[sym]["customCnt"] += eventCnt
                self.kerSymData[ksym]["customCnt"] += eventCnt
                self.userFileData[path]["customCnt"] += eventCnt

            # etc event #
            elif event == "IGNORE":
                try:
                    self.ignoreTable[arg]["ignCnt"] += 1
                except:
                    self.ignoreTable[arg] = {"ignCnt": long(1)}

            else:
                SysMgr.printWarn("failed to recognize event %s" % event)

        UtilMgr.deleteProgress()

        # Print summary about ignored events #
        self.printIgnoreEvents()

    def printIgnoreEvents(self):
        for idx, value in self.ignoreTable.items():
            SysMgr.printWarn(
                "ignore %s event %d times" % (idx, value["ignCnt"])
            )

    def getBinFromServer(self, remoteObj, src, des):
        if not remoteObj or remoteObj == "NONE":
            SysMgr.printErr(
                "wrong remote address, input in the format {IP:PORT}"
            )
            sys.exit(-1)

        # set download command #
        req = "DOWNLOAD:%s@%s" % (src, des)

        # get connection with server #
        self.connObj = NetworkMgr.getServerConn()
        if not self.connObj:
            return None

        # request download command #
        NetworkMgr.requestCmd(self.connObj, req)

    def getSymbols(self):
        binPath = ""
        offsetList = []
        curIdx = 0
        nrNoFile = 0
        lastIdx = len(self.posData)

        # Set alarm handler to handle hanged addr2line #
        signal.signal(signal.SIGALRM, SysMgr.timerHandler)

        # Get symbols and source pos #
        for idx, value in sorted(
            self.posData.items(), key=lambda e: e[1]["binary"], reverse=True
        ):
            curIdx += 1

            UtilMgr.printProgress(curIdx, lastIdx)

            # Handle thumbcode #
            if idx == "00c0ffee":
                value["binary"] = "??"
                value["origBin"] = "??"
                value["symbol"] = "ThumbCode"
                continue

            # Handle address #
            if value["binary"] == "":
                # user pos without offset #
                if value["symbol"] in ("", "??"):
                    # TODO: find binary path and symbol of pos #
                    value["binary"] = "??"
                    value["origBin"] = "??"
                    value["symbol"] = idx
                continue

            # Get symbols from address list of previous binary #
            if binPath != value["binary"]:
                if binPath != "":
                    # Get symbols #
                    if self.getFileSymbolInfo(binPath, offsetList) == -1:
                        nrNoFile += 1
                    offsetList = []

                if value["offset"] == hex(0):
                    offsetList.append(idx)
                else:
                    offsetList.append(value["offset"])

                # Set new binPath to find symbol from address #
                binPath = value["binary"]

                # Get binary from server #
                if not os.path.isfile(binPath) and SysMgr.remoteServObj:
                    self.getBinFromServer(
                        SysMgr.remoteServObj, value["origBin"], binPath
                    )
            # add address to offsetList #
            else:
                # not relocatable binary #
                if value["offset"] == hex(0):
                    offsetList.append(idx)
                # relocatable binary #
                else:
                    offsetList.append(value["offset"])

        if self.posData:
            UtilMgr.deleteProgress()

        # Get symbols and source path from last binary #
        if binPath != "":
            if self.getFileSymbolInfo(binPath, offsetList) == -1:
                nrNoFile += 1

        if nrNoFile > 0:
            SysMgr.printWarn(
                "failed to find total %s binaries to analyze functions"
                % nrNoFile,
                True,
            )

    def getFileSymbolInfo(self, binPath, offsetList, onlyFunc=True):
        def _updateSymbol(addr, symbol, src, relocated):
            if not addr:
                return -1
            elif symbol == "??":
                symbol = addr

            # Check whether the file is relocatable or not #
            if not relocated:
                try:
                    savedSymbol = self.posData[addr]["symbol"]
                except:
                    return -1

                """
                Check whether saved symbol found by
                previous addr2line is right #
                """
                if (
                    not savedSymbol
                    or savedSymbol in ("", addr)
                    or savedSymbol[0] == "$"
                ):
                    self.posData[addr]["symbol"] = symbol

                    if SysMgr.showAll:
                        self.posData[addr]["src"] = src
                    else:
                        fileIdx = src.rfind("/")
                        if fileIdx >= 0:
                            self.posData[addr]["src"] = src[fileIdx + 1 :]

                return

            inBinArea = False
            for idx, value in sorted(
                self.posData.items(),
                key=lambda e: e[1]["binary"],
                reverse=True,
            ):
                if value["binary"] == binPath:
                    inBinArea = True

                    if value["offset"] == addr:
                        savedSymbol = self.posData[idx]["symbol"]

                        if (
                            not savedSymbol
                            or savedSymbol in ("", addr)
                            or savedSymbol[0] == "$"
                        ):
                            self.posData[idx]["symbol"] = symbol

                            if SysMgr.showAll:
                                self.posData[idx]["src"] = src
                            else:
                                fileIdx = src.rfind("/")
                                if fileIdx >= 0:
                                    self.posData[idx]["src"] = src[
                                        fileIdx + 1 :
                                    ]

                            break
                elif inBinArea:
                    break

        # Recognize binary type #
        relocated = ElfAnalyzer.isRelocFile(binPath)

        # No file exist #
        if not os.path.isfile(binPath):
            for addr in offsetList:
                try:
                    if not relocated:
                        self.posData[addr]["symbol"] = "NoFile"
                        self.posData[addr]["src"] = "NoFile"
                    else:
                        for idx, value in sorted(
                            self.posData.items(),
                            key=lambda e: e[1]["binary"],
                            reverse=True,
                        ):
                            if value["binary"] == binPath and value[
                                "offset"
                            ] == hex(long(addr, 16)):
                                self.posData[idx]["symbol"] = "NoFile"
                                self.posData[idx]["src"] = "NoFile"
                                break
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printWarn("failed to find address %s" % addr)
            return -1

        # check user-mode enabled #
        if not SysMgr.userEnable:
            return None

        # check addr2line path #
        if not "ADDR2LINE" in SysMgr.environList:
            try:
                symbolList = []
                binObj = ElfAnalyzer.getObject(binPath)
                if not binObj:
                    return None

                for offset in offsetList:
                    symbol, size = binObj.getSymbolByOffset(
                        offset, onlyFunc=onlyFunc
                    )

                    symbolList.append("??")

                    _updateSymbol(offset, symbol, "??", relocated)
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr("failed to get symbol from %s" % binPath, True)

            return None

            # get addr2line path #
            addr2linePath = UtilMgr.which("addr2line")
            if not addr2linePath:
                SysMgr.printErr(
                    (
                        "failed to find addr2line to analyze functions, "
                        "use -q option with ADDR2LINE to set binary path"
                    )
                )
                sys.exit(-1)

            SysMgr.printInfo(
                "use '%s' as the addr2line path" % ", ".join(addr2linePath)
            )
        else:
            for path in SysMgr.environList["ADDR2LINE"]:
                if not os.path.isfile(path):
                    SysMgr.printErr(
                        "failed to find %s to use addr2line" % path
                    )
                    sys.exit(-1)

        # get subprocess object #
        subprocess = SysMgr.getPkg("subprocess")

        for path in SysMgr.environList["ADDR2LINE"]:
            # Set addr2line command #
            args = [path, "-C", "-f", "-a", "-e", binPath]

            # Limit the number of arguments to be passed because of ARG_MAX #
            # ARG_MAX = $(getconf PAGE_SIZE)*32 = 131072 #
            listLen = len(offsetList)
            maxArgLine = 256
            offset = 0
            timeout = 10

            # Get symbol by address of every maxArgLine elements in list #
            while offset < listLen:
                # Launch addr2line #
                try:
                    workload = offsetList[offset : offset + maxArgLine - 1]
                    proc = subprocess.Popen(
                        args + workload,
                        stdout=subprocess.PIPE,
                        stderr=subprocess.PIPE,
                    )
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr(
                        "failed to execute %s to pick symbols from binary"
                        % path
                    )
                    sys.exit(-1)

                # Increase offset count in address list #
                offset += maxArgLine

                try:
                    # Set alarm to handle hanged addr2line #
                    signal.alarm(timeout)

                    # Wait for addr2line to finish its job #
                    proc.wait()

                    # Cancel alarm after addr2line respond #
                    signal.alarm(0)
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printWarn(
                        "no response of addr2line for %s" % binPath
                    )
                    continue

                while 1:
                    # Get return of addr2line #
                    addr = (
                        proc.stdout.readline().decode().replace("\n", "")[2:]
                    )
                    try:
                        addr = hex(long(addr, 16)).rstrip("L")
                    except SystemExit:
                        sys.exit(0)
                    except:
                        pass

                    symbol = proc.stdout.readline().decode().replace("\n", "")
                    src = proc.stdout.readline().decode().replace("\n", "")

                    err = proc.stderr.readline().decode().replace("\n", "")
                    if err:
                        SysMgr.printWarn(err[err.find(":") + 2 :])

                    if _updateSymbol(addr, symbol, src, relocated):
                        break

    def initStacks(self):
        self.nowCtx["userLastPos"] = "0"
        self.nowCtx["userStack"] = []
        self.nowCtx["kerLastPos"] = "0"
        self.nowCtx["kerStack"] = []

    def swapEvents(self):
        tempEvent = self.nowCtx["nowEvent"]
        self.nowCtx["nowEvent"] = self.nowCtx["savedEvent"]
        self.nowCtx["savedEvent"] = tempEvent

        tempCnt = self.nowCtx["nowCnt"]
        self.nowCtx["nowCnt"] = self.nowCtx["savedCnt"]
        self.nowCtx["savedCnt"] = tempCnt

        tempArg = self.nowCtx["nowArg"]
        self.nowCtx["nowArg"] = self.nowCtx["savedArg"]
        self.nowCtx["savedArg"] = tempArg

    def saveFullStack(
        self,
        kernelPos,
        kernelStack,
        userPos,
        userStack,
        targetEvent,
        targetCnt,
        targetArg,
    ):
        # Save userstack #
        self.userCallData.append(
            [userPos, userStack, targetEvent, targetCnt, targetArg]
        )

        # Save kernelstack #
        self.kernelCallData.append(
            [kernelPos, kernelStack, targetEvent, targetCnt, targetArg]
        )

        # Save custom event stacks #
        if SysMgr.showAll and targetEvent == "CUSTOM":
            self.customCallData.append(
                [
                    targetArg[0],
                    targetArg[1],
                    self.userCallData[-1],
                    self.kernelCallData[-1],
                ]
            )

        # Save lock event stacks #
        if SysMgr.showAll and targetEvent in ("LOCK_TRY", "UNLOCK"):
            self.lockCallData.append(
                [
                    targetArg[0],
                    targetArg[1:],
                    self.userCallData[-1],
                    self.kernelCallData[-1],
                ]
            )

        # Save syscall event stacks #
        if SysMgr.showAll and targetEvent == "SYSCALL":
            self.sysCallData.append(
                [
                    targetArg[0],
                    targetArg[1:],
                    self.userCallData[-1],
                    self.kernelCallData[-1],
                ]
            )

    def saveEventStack(self, targetEvent, targetCnt, targetArg):
        kpos = self.nowCtx["kerLastPos"]
        upos = self.nowCtx["userLastPos"]

        # save count data #
        if targetEvent == "CPU_TICK":
            self.periodicEventCnt += 1

        elif targetEvent == "PAGE_ALLOC":
            self.pageAllocEventCnt += 1
            self.pageAllocCnt += targetCnt
            self.pageUsageCnt += targetCnt
            self.posData[kpos]["pageCnt"] += targetCnt
            self.posData[upos]["pageCnt"] += targetCnt

            pageType = targetArg[0]
            pfn = targetArg[1]
            time = targetArg[2]
            targetArg = [pageType, pfn, time]

        elif targetEvent == "PAGE_FREE":
            self.pageFreeEventCnt += 1
            self.pageFreeCnt += targetCnt

            pageType = targetArg[0]
            pfn = targetArg[1]
            time = targetArg[2]
            targetArg = [pageType, pfn, time]

        elif targetEvent == "BLK_READ":
            self.blockRdEventCnt += 1
            self.blockRdUsageCnt += targetCnt
            self.posData[kpos]["blockRdCnt"] += targetCnt
            self.posData[upos]["blockRdCnt"] += targetCnt

        elif targetEvent == "BLK_WRITE":
            self.blockWrEventCnt += 1
            self.blockWrUsageCnt += targetCnt
            self.posData[kpos]["blockWrCnt"] += targetCnt
            self.posData[upos]["blockWrCnt"] += targetCnt

        elif targetEvent == "LOCK_TRY":
            self.lockTryEventCnt += 1
            self.posData[kpos]["lockTryCnt"] += targetCnt
            self.posData[upos]["lockTryCnt"] += targetCnt

        elif targetEvent == "UNLOCK":
            self.unlockEventCnt += 1
            self.posData[kpos]["unlockCnt"] += targetCnt
            self.posData[upos]["unlockCnt"] += targetCnt

        elif targetEvent == "HEAP_EXPAND":
            self.heapExpEventCnt += 1
            self.heapExpSize += targetCnt
            self.posData[kpos]["heapSize"] += targetCnt
            self.posData[upos]["heapSize"] += targetCnt

        elif targetEvent == "HEAP_REDUCE":
            self.posData[kpos]["heapSize"] += targetCnt
            self.posData[upos]["heapSize"] += targetCnt

        elif targetEvent == "SYSCALL":
            nrSyscall = targetArg[0]
            self.syscallCnt += 1

            try:
                self.syscallTable[nrSyscall] += 0
            except:
                self.syscallTable[nrSyscall] = 1

            self.posData[kpos]["syscallCnt"] += targetCnt
            self.posData[upos]["syscallCnt"] += targetCnt

        elif targetEvent == "CUSTOM":
            if targetCnt > 0:
                self.customTotal += 1
                self.customCnt += targetCnt

                self.posData[kpos]["customTotal"] += 1
                self.posData[upos]["customTotal"] += 1

                self.posData[kpos]["customCnt"] += targetCnt
                self.posData[upos]["customCnt"] += targetCnt

        else:
            pass

        # cut stacks by depth #
        if SysMgr.funcDepth > 0:
            ksize = len(self.nowCtx["kerStack"])
            if ksize >= SysMgr.funcDepth:
                self.nowCtx["kerLastPos"] = self.nowCtx["kerStack"][
                    -SysMgr.funcDepth
                ]
                self.nowCtx["kerStack"] = self.nowCtx["kerStack"][
                    -SysMgr.funcDepth + 1 :
                ]

            usize = len(self.nowCtx["userStack"])
            if usize >= SysMgr.funcDepth:
                self.nowCtx["userLastPos"] = self.nowCtx["userStack"][
                    -SysMgr.funcDepth
                ]
                self.nowCtx["userStack"] = self.nowCtx["userStack"][
                    -SysMgr.funcDepth + 1 :
                ]

            if SysMgr.funcDepth == 1:
                self.nowCtx["kerStack"] = []
                self.nowCtx["userStack"] = []

        try:
            # save both stacks #
            self.saveFullStack(
                self.nowCtx["kerLastPos"],
                self.nowCtx["kerStack"],
                self.nowCtx["userLastPos"],
                self.nowCtx["userStack"],
                targetEvent,
                targetCnt,
                targetArg,
            )
        except:
            SysMgr.printErr("failed to save full stacks", True)
            sys.exit(-1)

    def saveCallStack(self):
        nowCtx = self.nowCtx

        # stack of kernel thread #
        if (
            not SysMgr.userRecordEnable
            or nowCtx["prevMode"] != nowCtx["curMode"] == "kernel"
        ):
            if not nowCtx["userStack"] and len(nowCtx["kerStack"]) > 0:
                # Set userLastPos to None #
                self.nowCtx["userLastPos"] = "0"
                self.nowCtx["userStack"].append("0")
            if not nowCtx["kerStack"] and len(nowCtx["userStack"]) > 0:
                # Set kerLastPos to None #
                self.nowCtx["kerLastPos"] = "0"
                self.nowCtx["kerStack"].append("0")

        # complicated situation ;( #
        elif nowCtx["prevMode"] == nowCtx["curMode"]:
            # previous user stack loss or nested interval #
            if nowCtx["curMode"] == "kernel":
                # nested interval #
                if nowCtx["nowEvent"] == "CPU_TICK":
                    # Backup kernel stack #
                    self.nowCtx["prevKerLastPos"] = nowCtx["kerLastPos"]
                    self.nowCtx["prevKerStack"] = nowCtx["kerStack"]

                    # Initialize both stacks #
                    self.initStacks()
                # previous user stack loss #
                else:
                    # Set userLastPos to None #
                    self.nowCtx["userLastPos"] = "0"
                    self.nowCtx["userStack"].append("0")
            # nested interval #
            elif nowCtx["curMode"] == "user":
                """
                CORE/0 EVENT0
                CORE/0 <kernel>
                CORE/0 <user>

                CORE/0 EVENT1
                CORE/0 <kernel>
                    CORE/0 EVENT2
                    CORE/0 <kernel>
                    CORE/0 <user>
                CORE/0 <user>
                """
                # Swap nowEvent and savedEvent #
                self.swapEvents()

        """
        Save both stacks of previous event before
        starting to record new kernel stack #
        """
        if (len(nowCtx["userStack"]) > 0 and nowCtx["userLastPos"] != "") and (
            len(nowCtx["kerStack"]) > 0 and nowCtx["kerLastPos"] != ""
        ):
            # Remove pc in each stacks #
            del self.nowCtx["kerStack"][0], self.nowCtx["userStack"][0]

            # Check whether there is nested event or not #
            if nowCtx["nested"] > 0:
                """
                CORE/0 EVENT0
                CORE/0 <kernel>
                CORE/0 <user>

                CORE/0 EVENT1
                    CORE/0 EVENT2
                    CORE/0 <kernel>
                    CORE/0 <user>
                CORE/0 <kernel>
                CORE/0 <user>
                """
                targetEvent = nowCtx["nestedEvent"]
                targetCnt = nowCtx["nestedCnt"]
                targetArg = nowCtx["nestedArg"]

                # Swap nowEvent and savedEvent #
                self.swapEvents()
            else:
                targetEvent = nowCtx["savedEvent"]
                targetCnt = nowCtx["savedCnt"]
                targetArg = nowCtx["savedArg"]

            # Save full stack of previous event #
            self.saveEventStack(targetEvent, targetCnt, targetArg)

            # Recover previous kernel stack after handling nested event #
            if (
                nowCtx["prevMode"] == nowCtx["curMode"] == "user"
                and nowCtx["prevKerLastPos"] != "0"
            ):
                self.nowCtx["kerLastPos"] = nowCtx["prevKerLastPos"]
                self.nowCtx["kerStack"] = nowCtx["prevKerStack"]
                self.nowCtx["prevKerLastPos"] = "0"
                self.nowCtx["prevKerStack"] = []
            else:
                self.nowCtx["kerLastPos"] = ""
                self.nowCtx["kerStack"] = []

            # Initialize user stack #
            self.nowCtx["userLastPos"] = ""
            self.nowCtx["userStack"] = []
            self.nowCtx["nestedEvent"] = ""
            self.nowCtx["nestedCnt"] = 0

        # On stack recording switch #
        self.nowCtx["recStat"] = True

    def savePosData(self, pos, path, offset):
        if self.nowCtx["nested"] > 0:
            targetEvent = self.nowCtx["savedEvent"]
        else:
            targetEvent = self.nowCtx["nowEvent"]

        # Register pos #
        try:
            self.posData[pos]
            if (
                path
                and path[0] == "/"
                and path != self.posData[pos]["origBin"]
            ):
                self.duplicatedPos += 1
                """
                SysMgr.printWarn(
                    "duplicated address %s in both '%s' and '%s'" % \
                    (pos, path, self.posData[pos]['origBin']))
                """
        except:
            self.posData[pos] = dict(self.init_posData)

        # user mode #
        if self.nowCtx["curMode"] == "user":
            # Set path #
            if path:
                self.posData[pos]["origBin"] = path
                self.posData[pos]["binary"] = SysMgr.rootPath + path
                self.posData[pos]["binary"] = os.path.normpath(
                    self.posData[pos]["binary"]
                )

                # Set offset #
                if offset:
                    if ElfAnalyzer.isRelocFile(path):
                        self.posData[pos]["offset"] = offset

            # Save pos #
            if not self.nowCtx["userStack"]:
                self.nowCtx["userLastPos"] = pos

                if targetEvent == "CPU_TICK":
                    self.posData[pos]["posCnt"] += 1
                elif targetEvent == "LOCK_TRY":
                    self.posData[pos]["lockTryCnt"] += 1
                elif targetEvent == "UNLOCK":
                    self.posData[pos]["unlockCnt"] += 1

            self.nowCtx["userStack"].append(pos)
        # kernel mode #
        elif self.nowCtx["curMode"] == "kernel":
            # Save pos #
            if not self.nowCtx["kerStack"]:
                self.nowCtx["kerLastPos"] = pos

                if targetEvent == "CPU_TICK":
                    self.posData[pos]["posCnt"] += 1
                elif targetEvent == "LOCK_TRY":
                    self.posData[pos]["lockTryCnt"] += 1
                elif targetEvent == "UNLOCK":
                    self.posData[pos]["unlockCnt"] += 1

            # Skip pos because it is usercall or no symbol #
            elif not SysMgr.showAll and not path:
                return

            self.posData[pos]["symbol"] = path

            self.nowCtx["kerStack"].append(pos)

        # wrong mode #
        else:
            SysMgr.printWarn("wrong current mode %s" % self.nowCtx["curMode"])

        # Increase total call count #
        if self.nowEvent == "CPU_TICK":
            self.posData[pos]["totalCnt"] += 1

    def allocHeapSeg(self, tid, size):
        try:
            self.heapTable[tid + "-ready"]["size"] = size
            self.heapTable[tid + "-ready"]["tid"] = tid
            self.threadData[tid]["heapSize"] -= size
            SysMgr.printWarn(
                "overwrite heap segment of %s(%s) at %s"
                % (self.threadData[tid]["comm"], tid, SysMgr.dbgEventLine)
            )
        except:
            self.heapTable[tid + "-ready"] = dict(self.init_heapSegData)
            self.heapTable[tid + "-ready"]["size"] = size
            self.heapTable[tid + "-ready"]["tid"] = tid

    def freeHeapSeg(self, addr):
        try:
            self.heapRedEventCnt += 1
            self.heapRedSize += self.heapTable[addr]["size"]

            # get tid #
            try:
                tid = self.heapTable[addr]["tid"]
            except:
                return

            self.threadData[tid]["heapSize"] -= self.heapTable[addr]["size"]

            self.heapTable.pop(addr, None)
        except:
            SysMgr.printWarn(
                "failed to free heap segment %s of %s(%s) at %s"
                % (
                    addr,
                    self.threadData[tid]["comm"],
                    tid,
                    SysMgr.dbgEventLine,
                )
            )

    def setHeapSegAddr(self, tid, addr):
        try:
            self.heapTable[addr] = dict(self.heapTable["%s-ready" % tid])
            del self.heapTable["%s-ready" % tid]
        except:
            SysMgr.printWarn(
                "failed to set address of heap segment %s of %s(%s) at %s"
                % (
                    addr,
                    self.threadData[tid]["comm"],
                    tid,
                    SysMgr.dbgEventLine,
                )
            )

    def parseLines(self, lines, desc):
        curIdx = 0
        lastIdx = len(lines)

        # make custom event table #
        if SysMgr.customCmd:
            for cmd in SysMgr.customCmd:
                cmd = cmd.split(":")

                if len(cmd) > 1:
                    self.customEventTable[cmd[0]] = cmd[1]
                else:
                    self.customEventTable[cmd[0]] = None

        # make kernel event table #
        if SysMgr.kernelCmd:
            for cmd in SysMgr.kernelCmd:
                cmd = cmd.split(":")
                self.customEventTable[cmd[0] + "_enter"] = None
                self.customEventTable[cmd[0] + "_exit"] = None

        # make user event table #
        if SysMgr.userCmd:
            for cmd in SysMgr.userCmd:
                cmd = cmd.split(":")
                self.customEventTable[cmd[0] + "_enter"] = None
                self.customEventTable[cmd[0] + "_exit"] = None

        # get pid filter by comm in advance #
        plist = {}
        if SysMgr.groupProcEnable:
            for key, value in self.getTargetList(lines).items():
                for item in desc:
                    if item in value["comm"]:
                        plist[value["tgid"]] = 0

        # start parsing logs #
        for liter in lines:
            curIdx += 1
            SysMgr.logSize += len(liter)
            SysMgr.curLine += 1
            SysMgr.dbgEventLine += 1

            ret = self.parse(liter, desc, plist)

            UtilMgr.printProgress(curIdx, lastIdx)

            # Skip lines before first meaningful event #
            if not self.lastCore:
                continue

            # Set context of current core #
            self.nowCtx = self.coreCtx[self.lastCore]

            # Save full stack to callData table #
            if ret is True:
                self.saveCallStack()
            elif ret is False:
                """
                Ignore this log because its not event or
                stack info related to target thread #
                """
                self.nowCtx["recStat"] = False
                continue
            # Save pos into target stack #
            elif self.nowCtx["recStat"]:
                # decode return value #
                (pos, path, offset) = ret

                self.savePosData(pos, path, offset)

        if lines:
            UtilMgr.deleteProgress()

        # update finish time #
        if self.finishTime == "0":
            self.finishTime = self.lastTime

        # Save stack of last events per core #
        for idx in list(self.coreCtx):
            self.lastCore = idx
            self.nowCtx = self.coreCtx[idx]

            # Recover previous mode #
            if SysMgr.userEnable:
                self.nowCtx["prevMode"] = "user"
            self.nowCtx["curMode"] = "kernel"

            self.saveEventParam("IGNORE", 0, 0)
            self.nowCtx["nested"] -= 1
            self.saveCallStack()

        if self.duplicatedPos > 0:
            SysMgr.printWarn(
                "found %d addresses duplicated" % self.duplicatedPos
            )

    def getCustomEventValue(self, args, cond):
        if not cond:
            return 1

        # set condition #
        if ">" in cond:
            condVal = cond[cond.find(">") + 1 :]
            condOp = ">"
            condStr = cond[: cond.find(">")]
        elif "<" in cond:
            condVal = cond[cond.find("<") + 1 :]
            condOp = "<"
            condStr = cond[: cond.find("<")]
        elif "==" in cond:
            condVal = cond[cond.find("==") + 2 :]
            condOp = "=="
            condStr = cond[: cond.find("==")]
        else:
            condStr = cond
            condOp = None
            condVal = None

        m = re.match(r"^.+%s=(?P<value>\S+)" % condStr, args)
        if not m:
            return 0

        d = m.groupdict()

        value = d["value"]

        if not condOp and value:
            try:
                return long(value)
            except:
                return 0
        elif condOp == ">":
            try:
                if long(value) > long(condVal):
                    return long(value)
            except:
                pass

            return 0
        elif condOp == "<":
            try:
                if long(value) < long(condVal):
                    return long(value)
            except:
                pass

            return 0
        elif condOp == "==":
            if value == condVal:
                return 1
            else:
                return 0
        else:
            return 0

    def saveEventParam(self, event, count, arg):
        # save context #
        self.nowCtx["nestedEvent"] = self.nowCtx["savedEvent"]
        self.nowCtx["savedEvent"] = self.nowCtx["nowEvent"]
        self.nowCtx["nowEvent"] = event

        self.nowCtx["nestedCnt"] = self.nowCtx["savedCnt"]
        self.nowCtx["savedCnt"] = self.nowCtx["nowCnt"]
        self.nowCtx["nowCnt"] = count

        self.nowCtx["nestedArg"] = self.nowCtx["savedArg"]
        self.nowCtx["savedArg"] = self.nowCtx["nowArg"]
        self.nowCtx["nowArg"] = arg

        self.nowCtx["nested"] += 1

        if self.nowCtx["nested"] > 2:
            # self.printDbgInfo()
            SysMgr.printWarn(
                (
                    "failed to analyze stack data "
                    "because of corruption (overflow) at %s\n"
                    "\tso report the results may differ from actual"
                )
                % SysMgr.dbgEventLine,
                True,
            )

    def printDbgInfo(self):
        data = self.nowCtx

        print(
            "[%s]" % self.lastCore,
            "(now) %s/%s/%s"
            % (data["nowEvent"], data["nowCnt"], data["nowArg"]),
            "(saved) %s/%s/%s"
            % (data["savedEvent"], data["savedCnt"], data["savedArg"]),
            "(nested) %s/%s/%s"
            % (data["nestedEvent"], data["nestedCnt"], data["nestedArg"]),
            "(user) %s/%s" % (data["userLastPos"], len(data["userStack"])),
            "(kernel) %s/%s" % (data["kerLastPos"], len(data["kerStack"])),
            "(backup) %s/%s"
            % (data["prevKerLastPos"], len(data["prevKerStack"])),
            "at %s" % SysMgr.dbgEventLine,
        )

    def parseEventInfo(self, tid, func, args, time, core):
        # check core filter #
        if (
            SysMgr.perCoreList
            and long(core) not in SysMgr.perCoreList
            and func[0] != "<"
        ):
            self.saveEventParam("IGNORE", 0, func[:-1])
            return False

        # check fixed event list #
        if self.customEventTable and (
            func[:-1] in self.customEventTable
            or len(
                [
                    event
                    for event in self.customEventTable
                    if event.endswith(func[:-1])
                ]
            )
            > 0
        ):
            isFixedEvent = False
        else:
            isFixedEvent = True

        # CPU tick event #
        # TODO: find shorter periodic event for sampling #
        if isFixedEvent and func == "hrtimer_start:":
            if "tick_sched_timer" in args:
                self.cpuEnabled = True

                self.saveEventParam("CPU_TICK", 1, 0)
            else:
                self.saveEventParam("IGNORE", 0, func[:-1])

            return False

        # memory allocation event #
        elif isFixedEvent and func == "mm_page_alloc:":
            m = re.match(
                (
                    r"^\s*page=\s*(?P<page>\S+)\s+pfn=(?P<pfn>[0-9]+)\s+"
                    r"order=(?P<order>[0-9]+)\s+migratetype=(?P<mt>[0-9]+)\s+"
                    r"gfp_flags=(?P<flags>\S+)"
                ),
                args,
            )
            if m:
                d = m.groupdict()

                # check whether it is huge page #
                if d["page"] == "(null)":
                    page = "huge"
                else:
                    page = d["page"]

                pfn = long(d["pfn"])
                flags = d["flags"]
                pageCnt = pow(2, long(d["order"]))

                # Increase page count of thread #
                self.threadData[tid]["nrPages"] += pageCnt

                # Increase page counts of thread #
                pageType = None
                if (
                    "NOFS" in flags
                    or "GFP_WRITE" in flags
                    or "0x1000000" in flags
                ):
                    pageType = "CACHE"
                    self.threadData[tid]["cachePages"] += pageCnt
                elif "USER" in flags:
                    pageType = "USER"
                    self.threadData[tid]["userPages"] += pageCnt
                else:
                    pageType = "KERNEL"
                    self.threadData[tid]["kernelPages"] += pageCnt

                # Make PTE in page table #
                for cnt in xrange(pageCnt):
                    pfnv = pfn + cnt

                    try:
                        """
                        Decrease page count of it's owner \
                        because this page was already allocated but no free log
                        """

                        ownerTid = self.pageTable[pfnv]["tid"]
                        self.threadData[ownerTid]["nrPages"] -= 1

                        origPageType = self.pageTable[pfnv]["type"]
                        if origPageType == "USER":
                            self.threadData[ownerTid]["userPages"] -= 1
                        elif origPageType == "CACHE":
                            self.threadData[ownerTid]["cachePages"] -= 1
                        elif origPageType == "KERNEL":
                            self.threadData[ownerTid]["kernelPages"] -= 1
                    except:
                        self.pageTable[pfnv] = dict(self.init_pageData)

                    self.pageTable[pfnv]["tid"] = tid
                    self.pageTable[pfnv]["page"] = page
                    self.pageTable[pfnv]["flags"] = flags
                    self.pageTable[pfnv]["type"] = pageType
                    self.pageTable[pfnv]["time"] = time

                self.memEnabled = True

                self.saveEventParam(
                    "PAGE_ALLOC", pageCnt, [pageType, pfn, time]
                )
            else:
                self.saveEventParam("IGNORE", 0, func[:-1])

                SysMgr.printWarn(
                    "failed to recognize event %s at %d"
                    % (func[:-1], SysMgr.dbgEventLine)
                )

            return False

        # memory free event #
        elif isFixedEvent and func in (
            "mm_page_free:",
            "mm_page_free_direct:",
        ):
            m = re.match(
                (
                    r"^\s*page=(?P<page>\S+)\s+pfn=(?P<pfn>[0-9]+)\s+"
                    r"order=(?P<order>[0-9]+)"
                ),
                args,
            )
            if m:
                d = m.groupdict()

                page = d["page"]
                pfn = long(d["pfn"])
                pageCnt = pow(2, long(d["order"]))

                # Update page table #
                origPageType = None
                for cnt in xrange(pageCnt):
                    pfnv = pfn + cnt

                    try:
                        owner = self.pageTable[pfnv]["tid"]
                        origPageType = self.pageTable[pfnv]["type"]

                        self.threadData[owner]["nrPages"] -= 1

                        if origPageType == "CACHE":
                            self.threadData[owner]["cachePages"] -= 1
                        elif origPageType == "USER":
                            self.threadData[owner]["userPages"] -= 1
                        elif origPageType == "KERNEL":
                            self.threadData[owner]["kernelPages"] -= 1

                        self.threadData[tid]["nrKnownFreePages"] += 1

                        self.pageTable.pop(pfnv)
                    except:
                        # this page was allocated before starting profile #

                        self.threadData[tid]["nrUnknownFreePages"] += 1
                        continue

                self.memEnabled = True

                self.saveEventParam(
                    "PAGE_FREE", pageCnt, [origPageType, pfn, time]
                )

                return False

            SysMgr.printWarn(
                "failed to recognize event %s at %d"
                % (func[:-1], SysMgr.dbgEventLine)
            )

            self.saveEventParam("IGNORE", 0, func[:-1])

            return False

        # syscall / heap / lock events #
        elif isFixedEvent and func == "sys_enter:":
            m = re.match(r"^\s*NR (?P<nr>[0-9]+) (?P<args>.+)", args)
            if m:
                b = m.groupdict()

                num = long(b["nr"])

                self.threadData[tid]["lastNrSyscall"] = num

                # syscall event #
                if SysMgr.sysEnable:
                    self.sysEnabled = True

                    syscallList = SysMgr.syscallList

                    if not syscallList or num in syscallList:
                        args = b["args"][1:-1]

                        self.threadData[tid]["nrSyscall"] += 1

                        # set syscall table #
                        if not self.threadData[tid]["syscallTable"]:
                            self.threadData[tid]["syscallTable"] = {}

                        try:
                            self.threadData[tid]["syscallTable"][num] += 1
                        except:
                            self.threadData[tid]["syscallTable"][num] = 1

                        self.saveEventParam(
                            "SYSCALL", 1, [num, args, time, core, tid]
                        )

                        return False

                # heap increasement event #
                elif num == ConfigMgr.getMmapId():
                    self.heapEnabled = True

                    try:
                        size = long(b["args"].split(",")[1], 16)

                        # just brk call to check data segment address #
                        if size == 0:
                            pass

                        self.threadData[tid]["heapSize"] += size
                    except:
                        self.saveEventParam("IGNORE", 0, func[:-1])

                        return False

                    # make heap segment tid-ready #
                    self.allocHeapSeg(tid, size)

                    self.saveEventParam("IGNORE", 0, func[:-1])

                    return False

                # heap decreasement event #
                elif num == SysMgr.getNrSyscall("sys_munmap"):
                    self.heapEnabled = True

                    try:
                        addr = long(b["args"][1:].split(",")[0], 16)
                        size = self.heapTable[addr]["size"]

                        # remove heap segment #
                        self.freeHeapSeg(addr)

                        self.saveEventParam(
                            "HEAP_REDUCE", size, [addr, time, core, tid]
                        )

                        return False
                    except:
                        pass

                # lock event #
                elif num == SysMgr.getNrSyscall("sys_futex"):
                    n = re.match(
                        (
                            r"^\s*(?P<uaddr>\S+), (?P<op>\S+), "
                            r"(?P<val>\S+), (?P<timer>\S+),"
                        ),
                        b["args"],
                    )
                    if n:
                        l = n.groupdict()

                        FUTEX_CMD_MASK = ~(128 | 256)
                        # FUTEX_PRIVATE_FLAG: 128, FUTEX_CLOCK_REALTIME: 256 #
                        maskedOp = long(l["op"], base=16) & FUTEX_CMD_MASK

                        addr = l["uaddr"][1:]

                        try:
                            event = ConfigMgr.FUTEX_TYPE[maskedOp]
                        except:
                            event = "LOCK"

                        # lock_pi #
                        if event in ("FUTEX_LOCK_PI", "FUTEX_TRYLOCK_PI"):
                            self.lockEnabled = True
                            self.threadData[tid]["nrLockTry"] += 1
                            self.saveEventParam(
                                "LOCK_TRY", 1, [event, addr, time, core, tid]
                            )
                            return False
                        # wait #
                        elif event in (
                            "FUTEX_WAIT",
                            "FUTEX_WAIT_REQUEUE_PI",
                            "FUTEX_WAIT_BITSET",
                        ):
                            self.lockEnabled = True
                            self.threadData[tid]["nrLockTry"] += 1
                            self.saveEventParam(
                                "LOCK_TRY", 1, [event, addr, time, core, tid]
                            )
                            return False
                        # unlock #
                        elif event == "FUTEX_UNLOCK_PI":
                            self.lockEnabled = True
                            self.threadData[tid]["nrUnlock"] += 1
                            self.saveEventParam(
                                "UNLOCK", 1, [event, addr, time, core, tid]
                            )
                            return False

            else:
                SysMgr.printWarn(
                    "failed to recognize event %s at %d"
                    % (func[:-1], SysMgr.dbgEventLine)
                )

            self.saveEventParam("IGNORE", 0, func[:-1])

            return False

        # syscall / heap events #
        elif isFixedEvent and func == "sys_exit:":
            m = re.match(r"^\s*NR (?P<nr>\S+) = (?P<ret>.+)", args)
            if m:
                b = m.groupdict()

                nr = long(b["nr"])

                # handle wrong syscall number #
                if nr < 0:
                    if self.threadData[tid]["lastNrSyscall"] >= 0:
                        nr = self.threadData[tid]["lastNrSyscall"]

                # heap increasement event #
                if nr == ConfigMgr.getMmapId():
                    self.heapEnabled = True

                    addr = long(b["ret"])

                    # rename heap segment from tid-ready to addr #
                    self.setHeapSegAddr(tid, addr)

                    try:
                        size = self.heapTable[addr]["size"]

                        self.saveEventParam(
                            "HEAP_EXPAND", size, [addr, time, core, tid]
                        )

                        return False
                    except:
                        pass

                # heap decreasement event #
                elif nr == SysMgr.getNrSyscall("sys_brk"):
                    self.heapEnabled = True

                    addr = long(b["ret"])

                    try:
                        pid = self.threadData[tid]["tgid"]
                        if pid.startswith("-"):
                            pid = SysMgr.savedProcTree[tid]
                        self.threadData[pid]
                    except:
                        pid = tid

                    try:
                        self.threadData[pid]["lastBrk"]

                        if addr > self.threadData[pid]["lastBrk"]:
                            size = addr - self.threadData[pid]["lastBrk"]

                            self.threadData[pid]["heapSize"] += size

                            self.saveEventParam(
                                "HEAP_EXPAND", size, [addr, time, core, tid]
                            )

                            return False
                    except:
                        self.threadData[pid]["lastBrk"] = addr
            else:
                SysMgr.printWarn(
                    "failed to recognize event %s at %d"
                    % (func[:-1], SysMgr.dbgEventLine)
                )

            self.saveEventParam("IGNORE", 0, func[:-1])

            return False

        # block request event #
        elif isFixedEvent and func == "block_bio_queue:":
            m = re.match(
                (
                    r"^\s*(?P<major>[0-9]+),(?P<minor>[0-9]+)\s*"
                    r"(?P<operation>\S+)\s*(?P<address>\S+)\s+\+\s+"
                    r"(?P<size>[0-9]+)"
                ),
                args,
            )
            if m:
                b = m.groupdict()

                """
                The operation to specific virtual devices such like
                device-mapper is remapped to another device by block_bio_queue
                event. It will probably make the workload look multiple times.
                """

                opt = b["operation"]

                if opt[0] == "R":
                    self.breadEnabled = True

                    blockRdCnt = long(b["size"])
                    self.threadData[tid]["nrRdBlocks"] += blockRdCnt

                    self.saveEventParam("BLK_READ", blockRdCnt, 0)

                    return False
                elif opt == "WS":
                    self.bwriteEnabled = True

                    blockWrCnt = long(b["size"])
                    self.threadData[tid]["nrWrBlocks"] += blockWrCnt

                    self.saveEventParam("BLK_WRITE", blockWrCnt, 0)

                    return False

            SysMgr.printWarn(
                "failed to recognize event %s at %d"
                % (func[:-1], SysMgr.dbgEventLine)
            )

            self.saveEventParam("IGNORE", 0, func[:-1])

            return False

        # block write request event #
        elif isFixedEvent and func == "writeback_dirty_page:":
            m = re.match(
                (
                    r"^\s*bdi\s+(?P<major>[0-9]+):(?P<minor>[0-9]+):\s*"
                    r"ino=(?P<ino>\S+)\s+index=(?P<index>\S+)"
                ),
                args,
            )
            if m:
                b = m.groupdict()
                self.bwriteEnabled = True

                self.threadData[tid]["nrWrBlocks"] += 8

                self.saveEventParam("BLK_WRITE", 8, 0)

                return False

            self.saveEventParam("IGNORE", 0, func[:-1])

            return False

        # block write request event #
        elif isFixedEvent and func == "wbc_writepage:":
            m = re.match(
                (
                    r"^\s*bdi\s+(?P<major>[0-9]+):(?P<minor>[0-9]+):\s*"
                    r"towrt=(?P<towrt>\S+)\s+skip=(?P<skip>\S+)"
                ),
                args,
            )
            if m:
                d = m.groupdict()

                if d["skip"] == "0":
                    self.bwriteEnabled = True

                    self.threadData[tid]["nrWrBlocks"] += 8

                    self.saveEventParam("BLK_WRITE", 8, 0)

                    return False

            self.saveEventParam("IGNORE", 0, func[:-1])

            return False

        # segmentation fault generation event #
        elif isFixedEvent and func == "signal_generate:":
            m = re.match(
                (
                    r"^\s*sig=(?P<sig>[0-9]+) errno=(?P<err>[0-9]+) "
                    r"code=(?P<code>.*) comm=(?P<comm>.*) "
                    r"pid=(?P<pid>[0-9]+)"
                ),
                args,
            )
            if m:
                b = m.groupdict()

                if b["sig"] == str(signal.SIGSEGV):
                    self.sigEnabled = True

                    self.saveEventParam("SIGSEGV_GEN", 0, 0)

                    return False

            SysMgr.printWarn(
                "failed to recognize event %s at %d"
                % (func[:-1], SysMgr.dbgEventLine)
            )

            self.saveEventParam("IGNORE", 0, func[:-1])

            return False

        elif isFixedEvent and func == "signal_deliver:":
            m = re.match(
                (
                    r"^\s*sig=(?P<sig>[0-9]+) errno=(?P<err>[0-9]+) "
                    r"code=(?P<code>.*) sa_handler=(?P<handler>.*) "
                    r"sa_flags=(?P<flags>.*)"
                ),
                args,
            )
            if m:
                b = m.groupdict()

                if b["sig"] == str(signal.SIGSEGV):
                    self.sigEnabled = True

                    self.saveEventParam("SIGSEGV_DLV", 0, 0)
                else:
                    self.saveEventParam("IGNORE", 0, func[:-1])
            else:
                self.saveEventParam("IGNORE", 0, func[:-1])

            return False

        elif isFixedEvent and func == "locks_get_lock_context:":
            m = re.match(
                (
                    r"^\s*dev=(?P<dev>.+)\s+ino=(?P<ino>.+)"
                    r"\s+type=(?P<type>.+)\s+ctx=(?P<ctx>.+)"
                ),
                args,
            )
            if m:
                d = m.groupdict()
                if d["type"] == "F_UNLCK":
                    self.saveEventParam("IGNORE", 0, func[:-1])
                else:
                    self.lockEnabled = True

                    self.threadData[tid]["nrLockTry"] += 1

                    self.saveEventParam(
                        "LOCK_TRY", 1, ["FLOCK", d["ino"], time, core, tid]
                    )
            else:
                self.saveEventParam("IGNORE", 0, func[:-1])

            return False

        # Start to record user stack #
        elif func == "<user":
            self.nowCtx["prevMode"] = self.nowCtx["curMode"]
            self.nowCtx["curMode"] = "user"

            return True

        # Start to record kernel stack #
        elif func == "<stack":
            self.nowCtx["prevMode"] = self.nowCtx["curMode"]
            self.nowCtx["curMode"] = "kernel"
            self.nowCtx["nested"] -= 1

            if self.nowCtx["nested"] < 0:
                # self.printDbgInfo()
                SysMgr.printWarn(
                    (
                        "failed to analyze stack data "
                        "because of corruption (underflow) at %s\n"
                        "\tso report the results may differ from actual"
                    )
                    % SysMgr.dbgEventLine,
                    True,
                )

            return True

        # custom event #
        elif not isFixedEvent:
            try:
                if not [
                    event
                    for event in self.customEventTable
                    if event.endswith(func[:-1])
                ]:
                    cond = self.customEventTable[func[:-1]] = None
                else:
                    cond = self.customEventTable[func[:-1]]

                # set event filter #
                customCnt = self.getCustomEventValue(args, cond)

                if customCnt > 0:
                    self.threadData[tid]["customTotal"] += customCnt

                self.saveEventParam(
                    "CUSTOM", customCnt, [func[:-1], [args, time, core, tid]]
                )
            except:
                self.saveEventParam("IGNORE", 0, func[:-1])

            return False

        # Ignore event #
        else:
            self.saveEventParam("IGNORE", 0, func[:-1])

            return False

    def getTargetList(self, tlist):
        threadData = {}

        for liter in tlist:
            m = SysMgr.getTraceItem(liter)
            if m:
                d = m.groupdict()

                # Make thread entity #
                thread = d["thread"]
                try:
                    threadData[thread]["comm"] = d["comm"]
                except:
                    threadData[thread] = {}
                    threadData[thread]["comm"] = d["comm"]

                # set tgid #
                try:
                    tgid = d["tgid"]
                except:
                    try:
                        tgid = SysMgr.savedProcTree[thread]
                    except:
                        continue

                threadData[thread]["tgid"] = tgid

        return threadData

    def parse(self, string, desc, plist=[]):
        m = SysMgr.getTraceItem(string)
        if m:
            d = m.groupdict()

            self.lastTime = d["time"]

            if (
                SysMgr.countEnable
                and SysMgr.repeatCnt * SysMgr.intervalEnable
                <= float(d["time"]) - float(SysMgr.startTime)
            ):
                self.lastCore = None
                return False

            # Make thread entity #
            thread = d["thread"]
            try:
                self.threadData[thread]["comm"] = d["comm"]
            except:
                self.threadData[thread] = dict(self.init_threadData)
                self.threadData[thread]["comm"] = d["comm"]

            # set tgid #
            tgid = None
            try:
                if d["tgid"].startswith("-"):
                    raise Exception("no tgid")
                tgid = d["tgid"]
            except:
                try:
                    tgid = SysMgr.savedProcTree[thread]
                except:
                    pass
            if tgid:
                self.threadData[thread]["tgid"] = tgid

            # increase event count #
            self.threadData[thread]["eventCnt"] += 1

            # set current core #
            self.lastCore = d["core"]

            # Make core entity #
            try:
                self.coreCtx[self.lastCore]
            except:
                self.coreCtx[self.lastCore] = dict(self.init_ctxData)
                self.coreCtx[self.lastCore]["userStack"] = []
                self.coreCtx[self.lastCore]["kerStack"] = []
                self.coreCtx[self.lastCore]["prevKerStack"] = []

            # set context of current core #
            self.nowCtx = self.coreCtx[self.lastCore]

            # Check core filter #
            if (
                SysMgr.perCoreList
                and long(d["core"]) not in SysMgr.perCoreList
                and not d["func"].startswith("tracing_mark_write")
                and d["func"] != "0:"
            ):
                pass

            # Calculate a total of CPU usage #
            elif (
                d["func"] == "hrtimer_start:"
                and "tick_sched_timer" in d["etc"]
            ):
                self.totalTick += 1
                self.threadData[thread]["cpuTick"] += 1

                # Set global interval #
                if self.nowCtx["prevTid"]:
                    diff = float(d["time"]) - float(self.nowCtx["prevTime"])
                    self.periodicEventInterval += diff
                    self.periodicContEventCnt += 1

                self.nowCtx["prevTid"] = thread
                self.nowCtx["prevTime"] = d["time"]

                # Set max core to calculate CPU usage of thread #
                if SysMgr.maxCore < long(d["core"]):
                    SysMgr.maxCore = long(d["core"])

            # Mark die flag of thread that is not able to be profiled #
            elif d["func"] == "sched_process_exit:":
                m = re.match(
                    r"^\s*comm=(?P<comm>.*)\s+pid=(?P<pid>[0-9]+)", d["etc"]
                )
                if m:
                    p = m.groupdict()

                    pid = p["pid"]

                    try:
                        self.threadData[pid]
                    except:
                        self.threadData[pid] = dict(self.init_threadData)
                        self.threadData[pid]["comm"] = p["comm"]

                    self.threadData[pid]["die"] = True

            # Make thread name #
            elif d["func"] == "sched_process_fork:":
                m = re.match(
                    (
                        r"^\s*comm=(?P<comm>.*)\s+pid=(?P<pid>[0-9]+)\s+"
                        r"child_comm=(?P<child_comm>.*)\s+"
                        r"child_pid=(?P<child_pid>[0-9]+)"
                    ),
                    d["etc"],
                )
                if m:
                    p = m.groupdict()

                    cpid = p["child_pid"]
                    ccomm = p["child_comm"]

                    try:
                        self.threadData[cpid]
                    except:
                        self.threadData[cpid] = dict(self.init_threadData)
                        self.threadData[cpid]["comm"] = ccomm
                        self.threadData[cpid]["new"] = True
                        self.threadData[cpid]["ptid"] = thread

            # Make thread name #
            elif d["func"] == "task_newtask:":
                m = re.match(
                    r"^\s*pid=(?P<pid>[0-9]+)\s+comm=(?P<comm>\S+)", d["etc"]
                )
                if m:
                    p = m.groupdict()

                    pid = p["pid"]

                    try:
                        self.threadData[pid]
                    except:
                        self.threadData[pid] = dict(self.init_threadData)
                        self.threadData[pid]["comm"] = p["comm"]
                        self.threadData[pid]["new"] = True
                        self.threadData[pid]["ptid"] = thread

            # Save user event #
            elif (
                d["func"].startswith("tracing_mark_write") or d["func"] == "0:"
            ):
                m = re.match(r"^.+EVENT_(?P<event>\S+)", d["etc"])
                if m:
                    gd = m.groupdict()

                    EventAnalyzer.addEvent(d["time"], gd["event"])

                    if gd["event"] == "STOP":
                        self.finishTime = float(d["time"])

                # Return False because there is no stack data with this event #
                return False

            # apply filter #
            if SysMgr.isExceptTarget(thread, self.threadData, plist=plist):
                return False
            else:
                self.threadData[thread]["target"] = True

            return self.parseEventInfo(
                thread, d["func"], d["etc"], d["time"], d["core"]
            )

        # Parse call stack #
        else:
            # exist path, offset, pos #
            m = re.match(
                (
                    r" => (?P<path>.+)\[\+0x(?P<offset>.\S*)\] "
                    r"\<(?P<pos>.\S+)\>"
                ),
                string,
            )
            if m:
                d = m.groupdict()
                return (d["pos"], d["path"], hex(long(d["offset"], 16)))

            # exist only pos #
            pos = string.find("=>  <")
            if pos > -1:
                return (string[pos + 5 : len(string) - 2], None, None)

            # no user stack tracing supported #
            if "??" in string:
                if SysMgr.userEnable and SysMgr.userEnableWarn:
                    SysMgr.printWarn(
                        (
                            "enable CONFIG_USER_STACKTRACE_SUPPORT kernel"
                            " option if it is not enabled"
                        ),
                        True,
                    )
                    SysMgr.userEnableWarn = False
                return ("0", None, None)

            # exist symbol, pos #
            m = re.match(r" => (?P<symbol>.+) \<(?P<pos>.\S+)\>", string)
            if m:
                d = m.groupdict()
                return (d["pos"], d["symbol"], None)

            # garbage log #
            return False

    def getBinInfo(self, addr):
        for data in self.mapData:
            if not (
                long(data["startAddr"], 16) <= long(addr, 16)
                and long(data["endAddr"], 16) >= long(addr, 16)
            ):
                continue

            if ElfAnalyzer.isRelocFile(data["binName"]):
                # Return full path and offset in mapping table #
                return SysMgr.rootPath + data["binName"], hex(
                    long(addr, 16) - long(data["startAddr"], 16)
                )
            else:
                return SysMgr.rootPath + data["binName"], hex(long(addr, 16))

        SysMgr.printWarn(
            "failed to get the binary info of %s in mapping table" % addr
        )

    def printSyscallSummary(self):
        # no effective syscall event #
        if self.syscallCnt == 0:
            return

        convNum = UtilMgr.convNum

        SysMgr.clearPrint()
        SysMgr.printPipe(
            "[Function Syscall Info] [Cnt: %s]\n%s"
            % (convNum(self.syscallCnt), twoLine)
        )
        SysMgr.printPipe(
            "{0:>16}({1:>7}/{2:>7}) {3:>30}({4:>3}) {5:>12}\n{6:1}".format(
                "Name", "TID", "PID", "Syscall", "SID", "Count", twoLine
            )
        )

        outputCnt = 0
        for key, value in sorted(
            self.threadData.items(),
            key=lambda e: e[1]["nrSyscall"],
            reverse=True,
        ):
            threadInfo = ""
            syscallInfo = ""

            if key.startswith("0["):
                continue

            try:
                if value["syscallTable"]:
                    threadInfo = "%16s(%7s/%7s)" % (
                        value["comm"],
                        key,
                        value["tgid"],
                    )
                else:
                    continue
            except:
                continue

            for sysId, val in sorted(
                value["syscallTable"].items(), key=lambda e: e[1], reverse=True
            ):
                if val == 0:
                    continue

                try:
                    syscall = ConfigMgr.sysList[sysId][4:]
                except:
                    SysMgr.printErr(
                        "failed to get syscall name by number %s" % sysId
                    )
                    syscall = sysId

                syscallInfo = ("{0:1} {1:>30}({2:>3}) {3:>12}\n").format(
                    syscallInfo + (" " * len(threadInfo)),
                    syscall,
                    sysId,
                    convNum(val),
                )

            if syscallInfo != "":
                outputCnt += 1
                SysMgr.printPipe(threadInfo)
                SysMgr.printPipe(syscallInfo + oneLine)

        if outputCnt == 0:
            SysMgr.printPipe("    None\n%s" % oneLine)

        SysMgr.printPipe("\n\n")

    def printUsage(self):
        targetCnt = 0
        self.totalTime = float(self.finishTime) - float(SysMgr.startTime)

        convSize = UtilMgr.convSize2Unit
        convNum = UtilMgr.convNum

        SysMgr.printLogo(big=True)

        # print system information #
        SysMgr.printInfoBuffer()

        # choose syscall / heap menu in table #
        if self.sysEnabled:
            cmenu = "SYSTEM"
            cmenu2 = "CALLS"
        else:
            cmenu = "HEAP"
            cmenu2 = "EVENTS"

        # Print thread list #
        SysMgr.printPipe(
            "[%s] [%s: %0.3f] [%s: %0.3f] [Threads: %d] [LogSize: %s]\n%s"
            % (
                "Function Thread Info",
                "Elapsed",
                round(self.totalTime, 7),
                "Start",
                round(float(SysMgr.startTime), 7),
                len(self.threadData),
                convSize(SysMgr.logSize),
                twoLine,
            )
        )
        SysMgr.printPipe(
            (
                "{0:_^46}|{1:_^7}|{2:_^54}|{3:_^8}|"
                "{4:_^18}|{5:_^6}|{6:_^8}|"
            ).format("Thread", "CPU", "PAGE", cmenu, "BLOCK", "LOCK", "CUSTOM")
        )
        SysMgr.printPipe(
            (
                "{0:^16}|{0:^7}|{0:^7}|{0:^6}|{0:^6}|{0:^7}|"
                "{0:^9}{0:^8}{0:^8}{0:^12}|{0:^8}|{0:^7}|{0:^8}|"
                "{0:^8}|{0:^9}|{0:^6}|{0:^8}|"
            ).format(" ")
        )

        SysMgr.printPipe(
            (
                "{0:_^16}|{1:_^7}|{2:_^7}|{3:_^6}|{4:_^6}|"
                "{5:_^7}|{6:_^9}({7:_^8}/{8:_^8}/{9:_^8})|{10:_^8}|"
                "{11:_^7}|{12:_^8}|{13:_^8}|{14:_^9}|{15:_^6}|{16:_^8}|"
            ).format(
                "Name",
                "TID",
                "PID",
                "PICK",
                "LIFE",
                "PER",
                "ALLOC",
                "USER",
                "BUF",
                "KERN",
                "FREE",
                "UFREE",
                cmenu2,
                "READ",
                "WRITE",
                "TRY",
                "EVENTS",
            )
        )
        SysMgr.printPipe(twoLine)

        # set sort value #
        if SysMgr.sort == "m":
            sortedThreadData = sorted(
                self.threadData.items(),
                key=lambda e: e[1]["nrPages"],
                reverse=True,
            )
        elif SysMgr.sort == "b":
            sortedThreadData = sorted(
                self.threadData.items(),
                key=lambda e: e[1]["nrRdBlocks"],
                reverse=True,
            )
        elif SysMgr.sort == "L":
            sortedThreadData = sorted(
                self.threadData.items(),
                key=lambda e: e[1]["nrLockTry"],
                reverse=True,
            )
        elif SysMgr.sort == "h":
            sortedThreadData = sorted(
                self.threadData.items(),
                key=lambda e: e[1]["heapSize"],
                reverse=True,
            )
        elif SysMgr.sort == "s":
            sortedThreadData = sorted(
                self.threadData.items(),
                key=lambda e: e[1]["nrSyscall"],
                reverse=True,
            )
        else:
            # set CPU usage as default #
            sortedThreadData = sorted(
                self.threadData.items(),
                key=lambda e: e[1]["cpuTick"],
                reverse=True,
            )

        for idx, value in sortedThreadData:
            targetMark = ""

            # skip no event count thread #
            if value["eventCnt"] == 0:
                continue

            # check target thread #
            if value["target"]:
                targetCnt += 1
                if targetCnt == 2:
                    SysMgr.printWarn("multiple target threads are selected")
                targetMark = "*"

            # get CPU usage #
            if self.totalTick > 0:
                cpuPer = "%.1f%%" % (
                    (float(value["cpuTick"]) / float(self.totalTick)) * 100
                )
            else:
                cpuPer = "0.0%%"

            # set break condition #
            if SysMgr.sort == "m":
                breakCond = value["nrPages"]
            elif SysMgr.sort == "b":
                breakCond = value["nrRdBlocks"]
            else:
                breakCond = long(cpuPer[: cpuPer.rfind(".")])

            # check condition for stop #
            if breakCond < 1 and not SysMgr.showAll:
                pass

            # set lifecycle flags #
            if value["new"]:
                life = "N"
            else:
                life = " "
            if value["die"]:
                life = "%sD" % life

            if self.cpuEnabled:
                # remove percentage if no tick #
                if float(value["cpuTick"]) == 0:
                    cpuPer = "-"
                else:
                    cpuPer = cpuPer
            else:
                cpuPer = "-"

            if self.sysEnabled:
                cval = "%s" % convNum(value["nrSyscall"])
            elif self.heapEnabled:
                cval = "%s" % convSize(value["heapSize"])
            else:
                cval = "-"

            if self.memEnabled:
                allocMem = "%s" % convSize(value["nrPages"] << 12)
                userMem = "%s" % convSize(value["userPages"] << 12)
                cacheMem = "%s" % convSize(value["cachePages"] << 12)
                kernelMem = "%s" % convSize(value["kernelPages"] << 12)
                knownFreeMem = "%s" % convSize(value["nrKnownFreePages"] << 12)
                unknownFreeMem = "%s" % convSize(
                    value["nrUnknownFreePages"] << 12
                )
            else:
                allocMem = "-"
                userMem = "-"
                cacheMem = "-"
                kernelMem = "-"
                knownFreeMem = "-"
                unknownFreeMem = "-"

            if self.breadEnabled:
                readBlock = "%s" % convSize(value["nrRdBlocks"] << 9)
            else:
                readBlock = "-"

            if self.bwriteEnabled:
                writeBlock = "%s" % convSize(value["nrWrBlocks"] << 9)
            else:
                writeBlock = "-"

            if self.lockEnabled:
                nrLock = convNum(value["nrLockTry"])
            else:
                nrLock = "-"

            if self.customTotal > 0:
                nrCustom = convNum(value["customTotal"])
            else:
                nrCustom = "-"

            # update comm #
            if value["comm"] == "<...>" and idx in SysMgr.commCache:
                comm = SysMgr.commCache[idx]
            else:
                comm = value["comm"]

            SysMgr.printPipe(
                (
                    "{0:>16}|{1:>7}|{2:>7}|{3:^6}|{4:^6}|"
                    "{5:>7}|{6:>9}({7:>8}/{8:>8}/{9:>8})|{10:>7}|{11:>8}|"
                    "{12:>8}|{13:>8}|{14:>9}|{15:>6}|{16:>8}|"
                ).format(
                    comm[:16],
                    idx,
                    value["tgid"],
                    targetMark,
                    life,
                    cpuPer,
                    allocMem,
                    userMem,
                    cacheMem,
                    kernelMem,
                    knownFreeMem,
                    unknownFreeMem,
                    cval,
                    readBlock,
                    writeBlock,
                    nrLock,
                    nrCustom,
                )
            )

        if targetCnt == 0:
            SysMgr.printPipe("    None")

        SysMgr.printPipe("%s\n\n\n" % oneLine)

        # Exit because there is no target #
        if not self.target:
            SysMgr.printWarn(
                "no specific thread targeted, input value for TID"
            )

        # Print syscall usage of threads #
        self.printSyscallSummary()

        # Print resource usage of functions #
        self.printCpuUsage()
        self.printMemUsage()
        self.printHeapUsage()
        self.printBlockRdUsage()
        self.printBlockWrUsage()
        self.printLockUsage()
        self.printSyscallUsage()
        self.printCustomUsage()

    def makeKernelSymList(self, subStack, ilen):
        symbolStack = ""
        stackIdx = 0
        tlen = ilen

        if not subStack:
            return " None"

        try:
            for pos in subStack:
                if self.posData[pos]["symbol"] == "":
                    symbolSet = " <- %s" % hex(long(pos, 16))
                elif not self.posData[pos]["symbol"] and SysMgr.showAll:
                    symbolSet = " <- %s" % hex(long(pos, 16))
                else:
                    symbolSet = " <- %s" % str(self.posData[pos]["symbol"])

                lpos = tlen + len(symbolStack[stackIdx:]) + len(symbolSet)
                if symbolStack != "" and lpos > SysMgr.lineLength:
                    stackIdx = len(symbolStack)
                    symbolStack = "%s\n%s" % (symbolStack, " " * ilen)
                    tlen = 0

                symbolStack += symbolSet
        except SystemExit:
            sys.exit(0)
        except:
            pass

        return symbolStack

    def makeUserSymList(self, subStack, ilen):
        symbolStack = ""
        stackIdx = 0
        tlen = ilen

        if self.sort == "sym":
            for sym in subStack:
                if not sym or sym == "0":
                    symbolSet = ""
                elif self.userSymData[sym]["origBin"] == "??":
                    symbolSet = " <- %s" % sym
                else:
                    symbolSet = " <- %s [%s]" % (
                        sym,
                        self.userSymData[sym]["origBin"],
                    )

                lpos = tlen + len(symbolStack[stackIdx:]) + len(symbolSet)
                if symbolStack != "" and lpos > SysMgr.lineLength:
                    stackIdx = len(symbolStack)
                    symbolStack = "%s\n%s" % (symbolStack, " " * ilen)
                    tlen = 0

                symbolStack += symbolSet
        elif self.sort == "pos":
            for pos in subStack:
                if not pos:
                    symbolStack += " <- None"
                # No symbol so that just print pos #
                elif self.posData[pos]["symbol"] == "":
                    symbolStack = "%s <- %s [%s]" % (
                        symbolStack,
                        hex(long(pos, 16)),
                        self.posData[pos]["origBin"],
                    )
                # Print symbol #
                else:
                    symbolStack = "%s <- %s [%s]" % (
                        symbolStack,
                        self.posData[pos]["symbol"],
                        self.posData[pos]["origBin"],
                    )

        if not symbolStack:
            return "    None"
        else:
            return symbolStack

    def printSyscallUsage(self):
        # no effective syscall event #
        if self.syscallCnt == 0 or not SysMgr.userEnable:
            return

        subStackIndex = FunctionAnalyzer.symStackIdxTable.index("STACK")
        eventIndex = FunctionAnalyzer.symStackIdxTable.index("SYSCALL")
        convNum = UtilMgr.convNum

        # Print syscall event #
        SysMgr.clearPrint()
        SysMgr.printPipe(
            "[Function Syscall Info] [Cnt: %s] (USER)\n%s"
            % (convNum(self.syscallCnt), twoLine)
        )

        SysMgr.printPipe(
            "{0:_^9}|{1:_^47}|{2:_^49}|{3:_^46}\n{4:1}".format(
                "Usage", "Function", "Binary", "Source", twoLine
            )
        )

        for idx, value in sorted(
            self.userSymData.items(),
            key=lambda e: e[1]["syscallCnt"],
            reverse=True,
        ):
            if value["syscallCnt"] == 0:
                break

            SysMgr.printPipe(
                "{0:>7}  |{1:^47}| {2:48}| {3:37}".format(
                    convNum(value["syscallCnt"]),
                    idx,
                    self.posData[value["pos"]]["origBin"],
                    self.posData[value["pos"]]["src"],
                )
            )

            # Set target stack #
            targetStack = self.getTargetStack(value, eventIndex)

            # Merge and Print symbols in stack #
            for stack in targetStack:
                eventCnt = stack[eventIndex]
                subStack = list(stack[subStackIndex])

                if eventCnt == 0:
                    break

                if not subStack:
                    continue
                else:
                    ilen = len("\t" * 16) + 3
                    symbolStack = self.makeUserSymList(subStack, ilen)

                SysMgr.printPipe(
                    "\t\t +{0:>7} |{1:32}".format(
                        convNum(eventCnt), symbolStack
                    )
                )

            SysMgr.printPipe(oneLine)

        SysMgr.printPipe()

        # Print syscall file #
        SysMgr.printPipe(
            "[Function Syscall File Info] [Cnt: %s] (USER)\n%s"
            % (twoLine, convNum(self.syscallCnt))
        )

        SysMgr.printPipe(
            "{0:_^9}|{1:_^144}\n{2:1}".format("Usage", "Binary", twoLine)
        )

        for idx, value in sorted(
            self.userFileData.items(),
            key=lambda e: e[1]["syscallCnt"],
            reverse=True,
        ):
            if value["syscallCnt"] == 0:
                break

            SysMgr.printPipe(
                "{0:>8} | {1:<142}\n{2:1}".format(
                    convNum(value["syscallCnt"]), idx, oneLine
                )
            )

        if self.periodicEventCnt == 0:
            SysMgr.printPipe("    None\n%s" % oneLine)

        SysMgr.printPipe()

        # Print syscall history #
        if not SysMgr.showAll or not self.sysCallData:
            SysMgr.printPipe("\n\n")
            return

        SysMgr.clearPrint()
        SysMgr.printPipe(
            "[Function Syscall History] [Cnt: %s]\n%s"
            % (convNum(self.syscallCnt), twoLine)
        )

        SysMgr.printPipe(
            "{0:_^32}|{1:_^17}({2:_^7})|{3:_^8}|{4:_^17}|\n{5:1}".format(
                "Event", "COMM", "TID", "CORE", "TIME", twoLine
            )
        )

        # sort by time #
        for call in self.sysCallData:
            event = ConfigMgr.sysList[call[0]][4:]
            args, time, core, tid = call[1][:4]

            title = "{0:^32}| {1:>16}({2:>7})| {3:>6} | {4:>15} |".format(
                event, self.threadData[tid]["comm"], tid, core, time
            )
            SysMgr.printPipe("%s\n%s" % (title, len(title) * "-"))

            # Make argument info #
            argsInfo = " %s" % args

            # Make user call info #
            ilen = 32
            nowLen = ilen
            try:
                last = call[2][0]
                stack = call[2][1]
                userCall = " %s[%s]" % (
                    self.posData[last]["symbol"],
                    self.posData[last]["binary"],
                )
                nowLen += len(userCall)
                for subcall in stack:
                    try:
                        symbol = self.posData[subcall]["symbol"]
                        binary = self.posData[subcall]["binary"]
                        nextCall = " <- %s[%s]" % (symbol, binary)
                        if SysMgr.lineLength > nowLen + len(nextCall):
                            userCall += nextCall
                            nowLen += len(nextCall)
                        else:
                            userCall = "%s\n%s %s" % (
                                userCall,
                                " " * ilen,
                                nextCall,
                            )
                            nowLen = ilen + len(nextCall)
                    except:
                        pass
            except SystemExit:
                sys.exit(0)
            except:
                pass

            SysMgr.printPipe(
                "{0:>32}| {1:<121}".format("[Args] ", argsInfo.strip())
            )
            SysMgr.printPipe(
                "{0:>32}|{1:<121}\n{2:1}".format("[User] ", userCall, oneLine)
            )

        SysMgr.printPipe("\n\n")

    def printCustomUsage(self):
        # no effective custom event #
        if self.customTotal == 0:
            return

        subStackIndex = FunctionAnalyzer.symStackIdxTable.index("STACK")
        eventIndex = FunctionAnalyzer.symStackIdxTable.index("CUSTOM")
        convNum = UtilMgr.convNum

        # Make custom event list #
        customList = ", ".join(list(self.customEventTable))

        # Print custom event in user space #
        if SysMgr.userEnable:
            SysMgr.clearPrint()
            SysMgr.printPipe(
                "[Function %s Info] [Cnt: %s] [Total: %s] (USER)\n%s"
                % (
                    customList,
                    convNum(self.customTotal),
                    convNum(self.customCnt),
                    twoLine,
                )
            )

            SysMgr.printPipe(
                "{0:_^9}|{1:_^47}|{2:_^49}|{3:_^46}\n{4:1}".format(
                    "Usage", "Function", "Binary", "Source", twoLine
                )
            )

            for idx, value in sorted(
                self.userSymData.items(),
                key=lambda e: e[1]["customCnt"],
                reverse=True,
            ):
                if value["customCnt"] == 0:
                    break

                SysMgr.printPipe(
                    "{0:>7}  |{1:^47}| {2:48}| {3:37}".format(
                        convNum(value["customCnt"]),
                        idx,
                        self.posData[value["pos"]]["origBin"],
                        self.posData[value["pos"]]["src"],
                    )
                )

                # Set target stack #
                targetStack = self.getTargetStack(value, eventIndex)

                # Merge and Print symbols in stack #
                for stack in targetStack:
                    eventCnt = stack[eventIndex]
                    subStack = list(stack[subStackIndex])

                    if eventCnt == 0:
                        break

                    if not subStack:
                        continue
                    else:
                        ilen = len("\t" * 16) + 3
                        symbolStack = self.makeUserSymList(subStack, ilen)

                    SysMgr.printPipe(
                        "\t\t +{0:>7} |{1:32}".format(
                            convNum(eventCnt), symbolStack
                        )
                    )

                SysMgr.printPipe(oneLine)

            SysMgr.printPipe("\n")

            # Print custom event file in user space #
            SysMgr.printPipe(
                "[Function %s File Info] [Cnt: %s] [Total: %s] (USER)\n%s"
                % (
                    customList,
                    convNum(self.customTotal),
                    convNum(self.customCnt),
                    twoLine,
                )
            )

            SysMgr.printPipe(
                "{0:_^9}|{1:_^144}\n{2:1}".format("Usage", "Binary", twoLine)
            )

            for idx, value in sorted(
                self.userFileData.items(),
                key=lambda e: e[1]["customCnt"],
                reverse=True,
            ):
                if value["customCnt"] == 0:
                    break

                SysMgr.printPipe(
                    "{0:>8} | {1:<142}\n{2:1}".format(
                        convNum(value["customCnt"]), idx, oneLine
                    )
                )

            if self.periodicEventCnt == 0:
                SysMgr.printPipe("    None\n%s" % oneLine)

            SysMgr.printPipe("\n")

        # Print custom event in kernel space #
        SysMgr.clearPrint()
        SysMgr.printPipe(
            "[Function %s Info] [Cnt: %s] [Total: %s] (KERNEL)\n%s"
            % (
                customList,
                convNum(self.customTotal),
                convNum(self.customCnt),
                twoLine,
            )
        )

        SysMgr.printPipe(
            "{0:_^9}|{1:_^144}\n{2:1}".format("Usage", "Function", twoLine)
        )

        # Print custom usage of stacks #
        for idx, value in sorted(
            self.kerSymData.items(),
            key=lambda e: e[1]["customCnt"],
            reverse=True,
        ):
            if value["customCnt"] == 0:
                break

            SysMgr.printPipe(
                "{0:>7}  |{1:^134}".format(convNum(value["customCnt"]), idx)
            )

            # Sort stacks by usage #
            value["stack"] = sorted(
                value["stack"], key=lambda x: x[eventIndex], reverse=True
            )

            # Print stacks by symbol #
            for stack in value["stack"]:
                eventCnt = stack[eventIndex]
                subStack = list(stack[subStackIndex])

                if eventCnt == 0:
                    break

                if not subStack:
                    continue
                elif (
                    len(subStack) == 1
                    and not SysMgr.showAll
                    and (
                        not self.posData[subStack[0]]["symbol"]
                        or self.posData[subStack[0]]["symbol"] == "NoFile"
                    )
                ):
                    # Pass unmeaningful part #
                    continue
                else:
                    ilen = len("\t" * 16) + 3
                    symbolStack = self.makeKernelSymList(subStack, ilen)

                SysMgr.printPipe(
                    "\t\t +{0:>7} |{1:32}".format(
                        convNum(eventCnt), symbolStack
                    )
                )

            SysMgr.printPipe(oneLine)

            SysMgr.printPipe("\n")

        # Print custom call history #
        if not SysMgr.showAll or not self.customCallData:
            SysMgr.printPipe("\n\n")
            return

        SysMgr.clearPrint()
        SysMgr.printPipe(
            "[Function %s History] [Cnt: %s] [Total: %s]\n%s"
            % (
                customList,
                convNum(self.customTotal),
                convNum(self.customCnt),
                twoLine,
            )
        )

        SysMgr.printPipe(
            "{0:_^32}|{1:_^17}({2:_^7})|{3:_^8}|{4:_^17}|\n{5:1}".format(
                "Event", "COMM", "TID", "CORE", "TIME", twoLine
            )
        )

        # sort by time #
        for call in sorted(self.customCallData, key=lambda e: e[1][1]):
            event = call[0]
            args = call[1][0]
            time = call[1][1]
            core = call[1][2]
            tid = call[1][3]

            title = "{0:^32}| {1:>16}({2:>7})| {3:>6} | {4:>15} |".format(
                event, self.threadData[tid]["comm"], tid, core, time
            )
            SysMgr.printPipe("%s\n%s" % (title, len(title) * "-"))

            # Make argument info #
            argsInfo = " %s" % args

            # Make user call info #
            ilen = 32
            nowLen = ilen
            try:
                last = call[2][0]
                stack = call[2][1]
                userCall = " %s[%s]" % (
                    self.posData[last]["symbol"],
                    self.posData[last]["binary"],
                )
                nowLen += len(userCall)
                for subcall in stack:
                    try:
                        symbol = self.posData[subcall]["symbol"]
                        binary = self.posData[subcall]["binary"]
                        nextCall = " <- %s[%s]" % (symbol, binary)
                        if SysMgr.lineLength > nowLen + len(nextCall):
                            userCall += nextCall
                            nowLen += len(nextCall)
                        else:
                            userCall = "%s\n%s %s" % (
                                userCall,
                                " " * ilen,
                                nextCall,
                            )
                            nowLen = ilen + len(nextCall)
                    except:
                        pass
            except SystemExit:
                sys.exit(0)
            except:
                pass

            # Make kernel call info #
            ilen = 32
            nowLen = ilen
            try:
                last = call[3][0]
                stack = call[3][1]
                kernelCall = " %s" % (self.posData[last]["symbol"])
                nowLen += len(kernelCall)
                for subcall in stack:
                    try:
                        nextCall = " <- %s" % (self.posData[subcall]["symbol"])
                        if SysMgr.lineLength > nowLen + len(nextCall):
                            kernelCall += nextCall
                            nowLen += len(nextCall)
                        else:
                            kernelCall = "%s\n%s %s" % (
                                kernelCall,
                                " " * ilen,
                                nextCall,
                            )
                            nowLen = ilen + len(nextCall)
                    except:
                        pass
            except SystemExit:
                sys.exit(0)
            except:
                pass

            SysMgr.printPipe(
                "{0:>32}| {1:<121}".format("[Args] ", argsInfo.strip())
            )
            SysMgr.printPipe("{0:>32}|{1:<121}".format("[User] ", userCall))
            SysMgr.printPipe(
                "{0:>32}|{1:<121}\n{2:1}".format(
                    "[Kernel] ", kernelCall, oneLine
                )
            )

        SysMgr.printPipe("\n\n")

    def printCpuUsage(self):
        # no CPU event #
        if not self.cpuEnabled or self.periodicEventCnt == 0:
            return

        subStackIndex = FunctionAnalyzer.symStackIdxTable.index("STACK")
        cpuTickIndex = FunctionAnalyzer.symStackIdxTable.index("CPU_TICK")
        tCnt = UtilMgr.convNum(self.periodicEventCnt)

        # average tick interval #
        if self.periodicContEventCnt > 0:
            self.periodicEventInterval /= self.periodicContEventCnt

        # Print CPU usage in user space #
        if SysMgr.userEnable:
            SysMgr.clearPrint()
            title = "Function CPU-Tick Info"
            SysMgr.printPipe(
                "[%s] [Cnt: %s] [Interval: %dms] (USER)"
                % (title, tCnt, self.periodicEventInterval * 1000)
            )

            # Print call stack #
            SysMgr.printPipe(
                "{3:1}\n{0:_^9}|{1:_^47}|{2:_^96}\n{3:1}".format(
                    "Usage", "Function", "Binary", twoLine
                )
            )

            for idx, value in sorted(
                self.userSymData.items(),
                key=lambda e: e[1]["tickCnt"],
                reverse=True,
            ):
                if value["tickCnt"] == 0:
                    break

                cpuPer = (
                    value["tickCnt"] / float(self.periodicEventCnt)
                ) * 100
                if cpuPer < 1 and not SysMgr.showAll:
                    break

                SysMgr.printPipe(
                    "{0:7.1f}% |{1:^47}| {2:48}".format(
                        cpuPer, idx, self.posData[value["pos"]]["origBin"]
                    )
                )

                # Increase total CPU usage per symbol #
                value["totalTickCnt"] += value["tickCnt"]

                # Set target stack #
                targetStack = self.getTargetStack(value)

                # Merge and Print symbols in stack #
                for stack in targetStack:
                    cpuCnt = stack[cpuTickIndex]
                    subStack = list(stack[subStackIndex])

                    if cpuCnt == 0:
                        break

                    if not subStack:
                        continue
                    else:
                        # Increase total tick count of symbols in stack #
                        for sym in subStack:
                            self.userSymData[sym]["totalTickCnt"] += 1

                        cpuPer = (cpuCnt / float(value["tickCnt"])) * 100
                        if cpuPer < 1 and not SysMgr.showAll:
                            break

                        ilen = len("\t" * 16)
                        symbolStack = self.makeUserSymList(subStack, ilen)

                    SysMgr.printPipe(
                        "\t +{0:7.1f}% |{1:32}".format(cpuPer, symbolStack)
                    )

                SysMgr.printPipe(oneLine)

            if self.periodicEventCnt == 0:
                SysMgr.printPipe("    None\n%s" % oneLine)

            SysMgr.printPipe("\n")

            # Print per-symbol #
            title = "Function CPU-Tick Symbol Info"
            SysMgr.printPipe(
                "[%s] [Cnt: %s] [Interval: %dms] (USER)\n%s"
                % (title, tCnt, self.periodicEventInterval * 1000, twoLine)
            )

            SysMgr.printPipe(
                "{0:_^9}|{1:_^47}|{2:_^96}\n{3:1}".format(
                    "Usage", "Function", "Binary", twoLine
                )
            )

            for idx, value in sorted(
                self.userSymData.items(),
                key=lambda e: e[1]["totalTickCnt"],
                reverse=True,
            ):
                if value["totalTickCnt"] == 0:
                    break

                cpuPer = (
                    value["totalTickCnt"] / float(self.periodicEventCnt)
                ) * 100
                if cpuPer < 1 and not SysMgr.showAll:
                    break

                SysMgr.printPipe(
                    "{0:7.1f}% |{1:^47}| {2:48}\n{3:1}".format(
                        cpuPer,
                        idx,
                        self.posData[value["pos"]]["origBin"],
                        oneLine,
                    )
                )

            if self.periodicEventCnt == 0:
                SysMgr.printPipe("    None\n%s" % oneLine)

            SysMgr.printPipe("\n")

            # Print tick per-file #
            title = "Function CPU-Tick File Info"
            SysMgr.printPipe(
                "[%s] [Cnt: %s] [Interval: %dms] (USER)\n%s"
                % (title, tCnt, self.periodicEventInterval * 1000, twoLine)
            )

            SysMgr.printPipe(
                "{0:_^9}|{1:_^144}\n{2:1}".format("Usage", "Binary", twoLine)
            )

            for idx, value in sorted(
                self.userFileData.items(),
                key=lambda e: e[1]["tickCnt"],
                reverse=True,
            ):
                if value["tickCnt"] == 0:
                    break

                cpuPer = (
                    value["tickCnt"] / float(self.periodicEventCnt)
                ) * 100
                if cpuPer < 1 and not SysMgr.showAll:
                    break

                SysMgr.printPipe(
                    "{0:7.1f}% | {1:<142}\n{2:1}".format(cpuPer, idx, oneLine)
                )

            if self.periodicEventCnt == 0:
                SysMgr.printPipe("    None\n%s" % oneLine)

            SysMgr.printPipe("\n")

        # Print CPU usage in kernel space #
        title = "Function CPU-Tick Info"
        SysMgr.clearPrint()
        titleStr = "[%s] [Cnt: %s] [Interval: %dms] (KERNEL)" % (
            title,
            tCnt,
            self.periodicEventInterval * 1000,
        )
        SysMgr.printPipe(
            "{3:1}\n{2:1}\n{0:_^9}|{1:_^144}\n{2:1}".format(
                "Usage", "Function", twoLine, titleStr
            )
        )

        # Make exception list to remove a redundant part of stack #
        exceptList = {}
        for pos, value in self.posData.items():
            if value["symbol"] in (
                "__irq_usr",
                "__irq_svc",
                "el1_irq",
                "gic_handle_irq",
                "apic_timer_interrupt",
                "asm_sysvec_hyperv_stimer0",
            ):
                exceptList.setdefault(pos, {})

        # Print CPU usage of stacks #
        for idx, value in sorted(
            self.kerSymData.items(),
            key=lambda e: e[1]["tickCnt"],
            reverse=True,
        ):
            if value["tickCnt"] == 0:
                break

            """
            disable to print last symbol because it is only one

            tickCnt = float(value['tickCnt'])
            eventCnt = float(self.periodicEventCnt)
            cpuPer = round(tickCnt / eventCnt * 100, 1)

            if cpuPer < 1 and not SysMgr.showAll:
                break

            SysMgr.printPipe("{0:7}% |{1:^134}".format(cpuPer, idx))
            """

            # Sort stacks by usage #
            value["stack"].sort(reverse=True)

            # Define merge list #
            mergedSymbolChain = {}

            # Merge by symbol chain #
            for stack in value["stack"]:
                cpuCnt = stack[cpuTickIndex]
                subStack = list(stack[subStackIndex])

                if cpuCnt == 0:
                    break
                else:
                    # Find index of the backmost exception value #
                    maxIdx = -1
                    for pos in list(exceptList):
                        try:
                            ridx = subStack.index(pos)
                            if ridx >= 0 and ridx > maxIdx:
                                maxIdx = ridx
                        except:
                            pass

                    # Remove a redundant part #
                    if maxIdx >= 0:
                        maxIdx += 1
                        if maxIdx == len(subStack):
                            subStack = []
                        else:
                            subStack = subStack[maxIdx:]

                if not subStack:
                    symbolStack = " <- USER"
                elif (
                    len(subStack) == 1
                    and not SysMgr.showAll
                    and (
                        not self.posData[subStack[0]]["symbol"]
                        or self.posData[subStack[0]]["symbol"] == "NoFile"
                    )
                ):
                    # Pass unmeaningful part #
                    continue
                else:
                    ilen = 10
                    symbolStack = self.makeKernelSymList(subStack, ilen)

                try:
                    mergedSymbolChain[symbolStack] += cpuCnt
                except:
                    mergedSymbolChain[symbolStack] = cpuCnt

            # define sample list for flame graph #
            callList = {}
            drawflame = "DRAWFLAME" in SysMgr.environList

            # Print stacks by symbol #
            for chain, tick in sorted(
                mergedSymbolChain.items(), key=lambda e: e[1], reverse=True
            ):
                cpuPer = (tick / float(value["tickCnt"])) * 100
                if cpuPer < 1 and not SysMgr.showAll:
                    break

                # add a sample for flame graph #
                if drawflame:
                    callList[chain.lstrip("<- ")] = tick

                SysMgr.printPipe("{0:7.1f}% |{1:32}".format(cpuPer, chain))

            SysMgr.printPipe(oneLine)

            if self.periodicEventCnt == 0:
                SysMgr.printPipe("    None\n%s" % oneLine)

            # draw flame graph #
            if drawflame:
                Debugger.drawFlame(
                    callList=callList,
                    title=titleStr,
                    outFile=SysMgr.outPath + ".cpu.kernel",
                )

        SysMgr.printPipe("\n\n")

    def printUnknownMemFreeInfo(self):
        # check memory event #
        if not self.memEnabled:
            return

        SysMgr.printPipe("\n")

        title = "Function Free-Only-Page Info"
        subStackIndex = FunctionAnalyzer.symStackIdxTable.index("STACK")
        pageFreeIndex = FunctionAnalyzer.symStackIdxTable.index("PAGE_FREE")
        convSize = UtilMgr.convSize2Unit
        size = convSize(self.pageUnknownFreeCnt << 12)

        if SysMgr.userEnable:
            # Print memory reduce by page free in user space #
            SysMgr.clearPrint()
            SysMgr.printPipe(
                "[%s] [Size: %s] (USER)\n%s" % (title, size, twoLine)
            )

            SysMgr.printPipe(
                "{0:_^9}|{1:_^47}|{2:_^49}|{3:_^46}\n{4:1}".format(
                    "Free", "Function", "Binary", "Source", twoLine
                )
            )

            for idx, value in sorted(
                self.userSymData.items(),
                key=lambda e: e[1]["unknownPageFreeCnt"],
                reverse=True,
            ):
                if value["unknownPageFreeCnt"] == 0:
                    break

                SysMgr.printPipe(
                    "{0:>8} |{1:^47}| {2:48}| {3:37}".format(
                        convSize(value["unknownPageFreeCnt"] << 12),
                        idx,
                        self.posData[value["pos"]]["origBin"],
                        self.posData[value["pos"]]["src"],
                    )
                )

                # Set target stack #
                targetStack = self.getTargetStack(value, pageFreeIndex)

                # Merge and Print symbols in stack #
                for stack in targetStack:
                    pageFreeCnt = stack[pageFreeIndex]
                    subStack = list(stack[subStackIndex])

                    if pageFreeCnt == 0:
                        break

                    if not subStack:
                        continue
                    else:
                        ilen = len("\t" * 16)
                        symbolStack = self.makeUserSymList(subStack, ilen)

                    SysMgr.printPipe(
                        "\t+ {0:>8} |{1:32}".format(
                            convSize(pageFreeCnt << 12), symbolStack
                        )
                    )

                SysMgr.printPipe(oneLine)

            if self.pageUnknownFreeCnt == 0:
                SysMgr.printPipe("    None\n%s" % oneLine)

            SysMgr.printPipe("\n")

        # Print memory reduce by page free in kernel space #
        SysMgr.clearPrint()
        SysMgr.printPipe(
            "[%s] [Size: %s] (KERNEL)\n%s" % (title, size, twoLine)
        )

        SysMgr.printPipe(
            "{0:_^9}|{1:_^144}\n{2:1}".format("FREE", "Function", twoLine)
        )

        for idx, value in sorted(
            self.kerSymData.items(),
            key=lambda e: e[1]["unknownPageFreeCnt"],
            reverse=True,
        ):
            if value["unknownPageFreeCnt"] == 0:
                break

            SysMgr.printPipe(
                "{0:>8} |{1:^144}".format(
                    convSize(value["unknownPageFreeCnt"] << 12), idx
                )
            )

            # Sort stacks by usage #
            value["stack"] = sorted(
                value["stack"], key=lambda x: x[pageFreeIndex], reverse=True
            )

            # Print stacks by symbol #
            for stack in value["stack"]:
                pageFreeCnt = stack[pageFreeIndex]
                subStack = list(stack[subStackIndex])

                if pageFreeCnt == 0:
                    continue

                if not subStack:
                    symbolStack = "    None"
                else:
                    ilen = len("\t" * 16)
                    symbolStack = self.makeKernelSymList(subStack, ilen)

                SysMgr.printPipe(
                    "\t+ {0:>8} |{1:32}".format(
                        convSize(pageFreeCnt << 12), symbolStack
                    )
                )

            SysMgr.printPipe(oneLine)

        if self.pageUnknownFreeCnt == 0:
            SysMgr.printPipe("    None\n%s" % oneLine)

    def printKnownMemFreeInfo(self):
        title = "Function Alloc-Free-Page Info"
        lineLength = SysMgr.lineLength
        diff = self.pageAllocCnt - self.pageUsageCnt
        convSize = UtilMgr.convSize2Unit
        size = convSize(diff << 12)

        # Print page alloc-free pair in user space #
        if SysMgr.userEnable:
            SysMgr.clearPrint()
            SysMgr.printPipe(
                "[%s] [Total: %s] (USER)\n%s" % (title, size, twoLine)
            )

            SysMgr.printPipe(
                (
                    "{0:^7}({1:^6}/{2:^6}/{3:^6})|"
                    "{4:_^47}|{5:_^40}|{6:_^35}\n{7:1}"
                ).format(
                    "Usage",
                    "Usr",
                    "Buf",
                    "Ker",
                    "Function",
                    "LifeTime",
                    "Binary",
                    twoLine,
                )
            )

            for idx, value in sorted(
                self.userSymData.items(),
                key=lambda e: e[1]["pagePairCnt"],
                reverse=True,
            ):
                if value["pagePairCnt"] == 0:
                    break

                typeList = {"USER": 0, "KERNEL": 0, "CACHE": 0}

                for pairId, item in value["pagePair"].items():
                    for ptype, cnt in item["valueList"].items():
                        try:
                            typeList[ptype] += cnt
                        except:
                            pass

                try:
                    avrTime = float(
                        value["pagePairTotal"] / value["pagePairCnt"]
                    )
                except:
                    avrTime = 0

                lifeTime = " AVR: %.3f / MIN: %.3f / MAX: %.3f" % (
                    avrTime,
                    value["pagePairMin"],
                    value["pagePairMax"],
                )

                SysMgr.printPipe(
                    (
                        "{0:>7}({1:>6}/{2:>6}/{3:>6})|{4:^47}|{5:40}| {6:1}"
                    ).format(
                        convSize(value["pagePairCnt"] << 12),
                        convSize(typeList["USER"] << 12),
                        convSize(typeList["CACHE"] << 12),
                        convSize(typeList["KERNEL"] << 12),
                        idx,
                        lifeTime,
                        self.posData[value["pos"]]["origBin"],
                    )
                )

                for pairId, item in sorted(
                    value["pagePair"].items(),
                    key=lambda e: e[1]["size"],
                    reverse=True,
                ):
                    try:
                        userPages = item["valueList"]["USER"]
                    except:
                        userPages = 0
                    try:
                        cachePages = item["valueList"]["CACHE"]
                    except:
                        cachePages = 0
                    try:
                        kernelPages = item["valueList"]["KERNEL"]
                    except:
                        kernelPages = 0

                    # get user alloc and free call #
                    allocCall, freeCall = pairId.split("#")

                    printBuf = "{0:4}+ {1:>7}({2:>6}/{3:>6}/{4:>6})| ".format(
                        " ",
                        convSize(item["size"] << 12),
                        convSize(userPages << 12),
                        convSize(cachePages << 12),
                        convSize(kernelPages << 12),
                    )

                    ilen = len(printBuf)
                    tlen = ilen

                    for seq, call in enumerate(allocCall.split(" <- ")):
                        if seq > 0 and tlen + len(call) > lineLength:
                            printBuf = "%s\n%s" % (printBuf, " " * ilen)
                            tlen = ilen
                        printBuf = "%s<- %s " % (printBuf, call)
                        tlen += len(call) + 4

                    SysMgr.printPipe(printBuf)

                    printBuf = "{0:5}{1:>30}|".format(" ", "[FREE]")
                    ilen = len(printBuf)
                    tlen = ilen

                    for index, call in enumerate(freeCall.split(" <- ")):
                        clen = len(call) + 4

                        if index == 0:
                            clen -= 4

                        if index > 0 and tlen + clen > lineLength:
                            printBuf = "%s\n%s" % (printBuf, " " * ilen)
                            tlen = ilen

                        if index == 0:
                            printBuf = "%s %s" % (printBuf, call)
                        else:
                            printBuf = "%s <- %s" % (printBuf, call)

                        tlen += clen

                    SysMgr.printPipe(printBuf)

                SysMgr.printPipe(oneLine)

            if self.pageAllocCnt - self.pageUsageCnt <= 0:
                SysMgr.printPipe("    None\n%s" % oneLine)

            SysMgr.printPipe("\n")

        # Print page alloc-free pair in kernel space #
        SysMgr.clearPrint()
        SysMgr.printPipe(
            "[%s] [Total: %s] (KERNEL)\n%s" % (title, size, twoLine)
        )

        SysMgr.printPipe(
            "{0:^7}({1:^6}/{2:^6}/{3:^6})|{4:_^47}|{5:_^76}\n{6:1}".format(
                "Usage", "Usr", "Buf", "Ker", "Function", "LifeTime", twoLine
            )
        )

        # Print mem usage of stacks #
        for idx, value in sorted(
            self.kerSymData.items(),
            key=lambda e: e[1]["pagePairCnt"],
            reverse=True,
        ):
            if value["pagePairCnt"] == 0:
                break

            typeList = {"USER": 0, "KERNEL": 0, "CACHE": 0}

            for pairId, item in value["pagePair"].items():
                for ptype, cnt in item["valueList"].items():
                    try:
                        typeList[ptype] += cnt
                    except:
                        pass

            try:
                avrTime = float(value["pagePairTotal"] / value["pagePairCnt"])
            except:
                avrTime = 0

            lifeTime = " AVR: %.3f / MIN: %.3f / MAX: %.3f" % (
                avrTime,
                value["pagePairMin"],
                value["pagePairMax"],
            )

            SysMgr.printPipe(
                "{0:>7}({1:>6}/{2:>6}/{3:>6})|{4:^47}|{5:^75}".format(
                    convSize(value["pagePairCnt"] << 12),
                    convSize(typeList["USER"] << 12),
                    convSize(typeList["CACHE"] << 12),
                    convSize(typeList["KERNEL"] << 12),
                    idx,
                    lifeTime,
                )
            )

            for pairId, item in sorted(
                value["pagePair"].items(),
                key=lambda e: e[1]["size"],
                reverse=True,
            ):
                try:
                    userPages = item["valueList"]["USER"]
                except:
                    userPages = 0
                try:
                    cachePages = item["valueList"]["CACHE"]
                except:
                    cachePages = 0
                try:
                    kernelPages = item["valueList"]["KERNEL"]
                except:
                    kernelPages = 0

                # get kernel alloc and free call #
                allocCall, freeCall = pairId.split("#")

                printBuf = "{0:4}+ {1:>7}({2:>6}/{3:>6}/{4:>6})| ".format(
                    " ",
                    convSize(item["size"] << 12),
                    convSize(userPages << 12),
                    convSize(cachePages << 12),
                    convSize(kernelPages << 12),
                )

                ilen = len(printBuf)
                tlen = ilen

                for seq, call in enumerate(allocCall.split(" <- ")):
                    if seq > 0 and tlen + len(call) > lineLength:
                        printBuf = "%s\n%s" % (printBuf, " " * ilen)
                        tlen = ilen
                    printBuf = "%s<- %s " % (printBuf, call)
                    tlen += len(call) + 4

                SysMgr.printPipe(printBuf)

                printBuf = "{0:5}{1:>30}|".format(" ", "[FREE]")
                ilen = len(printBuf)
                tlen = ilen

                for index, call in enumerate(freeCall.split(" <- ")):
                    clen = len(call) + 4

                    if index == 0:
                        clen -= 4

                    if index > 0 and tlen + clen > lineLength:
                        printBuf = "%s\n%s" % (printBuf, " " * ilen)
                        tlen = ilen

                    if index == 0:
                        printBuf = "%s %s" % (printBuf, call)
                    else:
                        printBuf = "%s <- %s" % (printBuf, call)

                    tlen += clen

                SysMgr.printPipe(printBuf)

            SysMgr.printPipe(oneLine)

        if self.pageAllocCnt - self.pageUsageCnt <= 0:
            SysMgr.printPipe("    None\n%s" % oneLine)

        SysMgr.printPipe()

    def printMemUsage(self):
        # check memory event #
        if not self.memEnabled:
            return

        title = "Function Alloc-Only-Page Info"
        subStackIndex = FunctionAnalyzer.symStackIdxTable.index("STACK")
        pageAllocIndex = FunctionAnalyzer.symStackIdxTable.index("PAGE_ALLOC")
        argIndex = FunctionAnalyzer.symStackIdxTable.index("ARGUMENT")

        convSize = UtilMgr.convSize2Unit
        userSize = convSize(self.pageUsageCnt << 12)
        allocSize = convSize(self.pageAllocCnt << 12)
        freeSize = convSize(self.pageFreeCnt << 12)
        allocCnt = UtilMgr.convNum(self.pageAllocEventCnt)
        freeCnt = UtilMgr.convNum(self.pageFreeEventCnt)

        # Calculate page lifetime #
        for pfn, item in self.pageTable.items():
            if not item:
                continue

            # calculate time #
            time = float(item["time"])
            if time > 0:
                lifeTime = float(self.finishTime) - time
            else:
                lifeTime = 0

            # Set user page lifetime #
            self.userSymData[item["sym"]]["pageRemainTotal"] += lifeTime
            if (
                self.userSymData[item["sym"]]["pageRemainMin"] == 0
                or self.userSymData[item["sym"]]["pageRemainMin"] > lifeTime
            ):
                self.userSymData[item["sym"]]["pageRemainMin"] = lifeTime
            if self.userSymData[item["sym"]]["pageRemainMax"] < lifeTime:
                self.userSymData[item["sym"]]["pageRemainMax"] = lifeTime

            # Set kernel page lifetime #
            self.kerSymData[item["ksym"]]["pageRemainTotal"] += lifeTime
            if (
                self.kerSymData[item["ksym"]]["pageRemainMin"] == 0
                or self.kerSymData[item["ksym"]]["pageRemainMin"] > lifeTime
            ):
                self.kerSymData[item["ksym"]]["pageRemainMin"] = lifeTime
            if self.kerSymData[item["ksym"]]["pageRemainMax"] < lifeTime:
                self.kerSymData[item["ksym"]]["pageRemainMax"] = lifeTime

        # Print memory usage by page allocation in user space #
        if SysMgr.userEnable:
            SysMgr.clearPrint()
            SysMgr.printPipe(
                "[%s] [Total: %s] [Alloc: %s(%s)] [Free: %s(%s)] (USER)\n%s"
                % (
                    title,
                    userSize,
                    allocSize,
                    allocCnt,
                    freeSize,
                    freeCnt,
                    twoLine,
                )
            )

            SysMgr.printPipe(
                (
                    "{0:^7}({1:^6}/{2:^6}/{3:^6})|"
                    "{4:_^47}|{5:_^40}|{6:_^35}\n{7:1}"
                ).format(
                    "Usage",
                    "Usr",
                    "Buf",
                    "Ker",
                    "Function",
                    "LifeTime",
                    "Binary",
                    twoLine,
                )
            )

            for idx, value in sorted(
                self.userSymData.items(),
                key=lambda e: e[1]["pageCnt"],
                reverse=True,
            ):
                if value["pageCnt"] == 0:
                    break

                try:
                    avrTime = float(
                        value["pageRemainTotal"] / value["pageCnt"]
                    )
                except:
                    avrTime = 0

                lifeTime = " AVR: %.3f / MIN: %.3f / MAX: %.3f" % (
                    avrTime,
                    value["pageRemainMin"],
                    value["pageRemainMax"],
                )

                SysMgr.printPipe(
                    (
                        "{0:>7}({1:>6}/{2:>6}/{3:>6})|{4:^47}|{5:40}| {6:1}"
                    ).format(
                        convSize(value["pageCnt"] << 12),
                        convSize(value["userPageCnt"] << 12),
                        convSize(value["cachePageCnt"] << 12),
                        convSize(value["kernelPageCnt"] << 12),
                        idx,
                        lifeTime,
                        self.posData[value["pos"]]["origBin"],
                    )
                )

                # Set target stack #
                targetStack = self.getTargetStack(value, pageAllocIndex)

                # Merge and Print symbols in stack #
                for stack in targetStack:
                    subStack = list(stack[subStackIndex])
                    pageCnt = stack[pageAllocIndex]
                    userPageCnt = stack[argIndex][0]
                    cachePageCnt = stack[argIndex][1]
                    kernelPageCnt = stack[argIndex][2]

                    if pageCnt == 0:
                        break

                    if not subStack:
                        continue
                    else:
                        ilen = len("\t" * 36)
                        symbolStack = self.makeUserSymList(subStack, ilen)

                    SysMgr.printPipe(
                        "\t+ {0:>7}({1:>6}/{2:>6}/{3:>6})|{4:32}".format(
                            convSize(pageCnt << 12),
                            convSize(userPageCnt << 12),
                            convSize(cachePageCnt << 12),
                            convSize(kernelPageCnt << 12),
                            symbolStack,
                        )
                    )

                SysMgr.printPipe(oneLine)

            if self.pageUsageCnt == 0:
                SysMgr.printPipe("    None\n%s" % oneLine)

            SysMgr.printPipe("\n")

        # Print memory usage by page allocation in kernel space #
        SysMgr.clearPrint()
        SysMgr.printPipe(
            "[%s] [Total: %s] [Alloc: %s(%s)] [Free: %s(%s)] (KERNEL)\n%s"
            % (
                title,
                userSize,
                allocSize,
                allocCnt,
                freeSize,
                freeCnt,
                twoLine,
            )
        )

        SysMgr.printPipe(
            "{0:^7}({1:^6}/{2:^6}/{3:^6})|{4:_^47}|{5:_^76}\n{6:1}".format(
                "Usage", "Usr", "Buf", "Ker", "Function", "LifeTime", twoLine
            )
        )

        # Print mem usage of stacks #
        for idx, value in sorted(
            self.kerSymData.items(),
            key=lambda e: e[1]["pageCnt"],
            reverse=True,
        ):
            if value["pageCnt"] == 0:
                break

            try:
                avrTime = float(value["pageRemainTotal"] / value["pageCnt"])
            except:
                avrTime = 0

            lifeTime = " AVR: %.3f / MIN: %.3f / MAX: %.3f" % (
                avrTime,
                value["pageRemainMin"],
                value["pageRemainMax"],
            )

            SysMgr.printPipe(
                "{0:>7}({1:>6}/{2:>6}/{3:>6})|{4:^47}|{5:^76}".format(
                    convSize(value["pageCnt"] << 12),
                    convSize(value["userPageCnt"] << 12),
                    convSize(value["cachePageCnt"] << 12),
                    convSize(value["kernelPageCnt"] << 12),
                    idx,
                    lifeTime,
                )
            )

            # Sort stacks by usage #
            value["stack"] = sorted(
                value["stack"], key=lambda x: x[pageAllocIndex], reverse=True
            )

            # Print stacks by symbol #
            for stack in value["stack"]:
                subStack = list(stack[subStackIndex])
                pageCnt = stack[pageAllocIndex]
                userPageCnt = stack[argIndex][0]
                cachePageCnt = stack[argIndex][1]
                kernelPageCnt = stack[argIndex][2]

                if pageCnt == 0:
                    continue

                if not subStack:
                    continue
                else:
                    ilen = len("\t" * 36)
                    symbolStack = self.makeKernelSymList(subStack, ilen)

                SysMgr.printPipe(
                    "\t+ {0:>7}({1:>6}/{2:>6}/{3:>6})|{4:32}".format(
                        convSize(pageCnt << 12),
                        convSize(userPageCnt << 12),
                        convSize(cachePageCnt << 12),
                        convSize(kernelPageCnt << 12),
                        symbolStack,
                    )
                )

            SysMgr.printPipe(oneLine)

        if self.pageUsageCnt == 0:
            SysMgr.printPipe("    None\n%s" % oneLine)

        SysMgr.printPipe("\n")

        self.printKnownMemFreeInfo()

        self.printUnknownMemFreeInfo()

        SysMgr.printPipe("\n\n")

    def printHeapUsage(self):
        # check heap memory event #
        if not self.heapEnabled or not SysMgr.userEnable:
            return

        title = "Function Expand-Heap"
        subStackIndex = FunctionAnalyzer.symStackIdxTable.index("STACK")
        heapExpIndex = FunctionAnalyzer.symStackIdxTable.index("HEAP_EXPAND")
        convSize = UtilMgr.convSize2Unit

        # Print heap usage in user space #
        SysMgr.clearPrint()
        SysMgr.printPipe(
            "[%s Info] [Total: %s] [Alloc: %s(%s)] [Free: %s(%s)] (USER)%s\n"
            % (
                title,
                convSize(self.heapExpSize - self.heapRedSize),
                convSize(self.heapExpSize),
                UtilMgr.convNum(self.heapExpEventCnt),
                convSize(self.heapRedSize),
                UtilMgr.convNum(self.heapRedEventCnt),
                twoLine,
            )
        )

        SysMgr.printPipe(
            "{0:_^9}|{1:_^47}|{2:_^49}|{3:_^46}\n{4:1}".format(
                "Usage", "Function", "Binary", "Source", twoLine
            )
        )

        for idx, value in sorted(
            self.userSymData.items(),
            key=lambda e: e[1]["heapSize"],
            reverse=True,
        ):
            if value["heapSize"] == 0:
                break

            binary = self.posData[value["pos"]]["origBin"]
            source = self.posData[value["pos"]]["src"]
            SysMgr.printPipe(
                "{0:>8} |{1:^47}| {2:48}| {3:37}".format(
                    convSize(value["heapSize"]), idx, binary, source
                )
            )

            if idx == value["pos"]:
                SysMgr.printPipe(oneLine)
                continue

            # Set target stack #
            targetStack = self.getTargetStack(value, heapExpIndex)

            # Merge and Print symbols in stack #
            for stack in targetStack:
                heapSize = stack[heapExpIndex]
                subStack = list(stack[subStackIndex])

                if heapSize == 0:
                    break

                if not subStack:
                    continue
                else:
                    ilen = len("\t" * 16)
                    symbolStack = self.makeUserSymList(subStack, ilen)

                SysMgr.printPipe(
                    "\t+ {0:>8} |{1:32}".format(
                        convSize(heapSize), symbolStack
                    )
                )

            SysMgr.printPipe(oneLine)

        if not self.heapTable:
            SysMgr.printPipe("    None\n%s" % oneLine)

        SysMgr.printPipe("\n")

        # Print remaining heap history #
        if not SysMgr.showAll or not self.heapTable:
            SysMgr.printPipe("\n\n")
            return

        SysMgr.clearPrint()
        SysMgr.printPipe(
            "[%s History] [Cnt: %s]\n%s"
            % (title, UtilMgr.convNum(len(self.heapTable)), twoLine)
        )

        SysMgr.printPipe(
            (
                "{0:_^32}|{1:_^12}|{2:_^17}({3:_^7})|"
                "{4:_^8}|{5:_^17}|\n{6:1}"
            ).format("VAddr", "Size", "COMM", "TID", "CORE", "TIME", twoLine)
        )

        # sort by time #
        for segment in sorted(
            self.heapTable.items(), key=lambda e: e[1]["time"]
        ):
            addr = segment[0]

            size = segment[1]["size"]
            time = segment[1]["time"]
            core = segment[1]["core"]
            tid = segment[1]["tid"]

            usersym = segment[1]["sym"]
            kernelsym = segment[1]["ksym"]
            userstack = segment[1]["subStackAddr"]
            kernelstack = segment[1]["ksubStackAddr"]

            title = (
                "{0:^32}| {1:>10} | {2:>16}({3:>7})|{4:>6} | {5:>15} |"
            ).format(
                addr,
                convSize(size),
                self.threadData[tid]["comm"],
                tid,
                long(core),
                time,
            )
            SysMgr.printPipe("%s\n%s" % (title, len(title) * "-"))

            # Make user call info #
            ilen = 32
            nowLen = ilen
            try:
                userCall = " %s[%s]" % (
                    usersym,
                    self.userSymData[usersym]["origBin"],
                )
                nowLen += len(userCall)

                # Set user stack list #
                if self.sort == "sym":
                    targetStack = self.userSymData[usersym]["symStack"]
                elif self.sort == "pos":
                    targetStack = self.userSymData[usersym]["stack"]

                # Find user stack by addr #
                stack = []
                for val in targetStack:
                    if id(val[subStackIndex]) == userstack:
                        stack = val[subStackIndex]
                        break

                for subcall in stack:
                    try:
                        nextCall = " <- %s[%s]" % (
                            subcall,
                            self.userSymData[subcall]["origBin"],
                        )
                        if SysMgr.lineLength > nowLen + len(nextCall):
                            userCall += nextCall
                            nowLen += len(nextCall)
                        else:
                            userCall = "%s\n%s %s" % (
                                userCall,
                                " " * ilen,
                                nextCall,
                            )
                            nowLen = ilen + len(nextCall)
                    except:
                        pass
            except:
                pass

            # Make kernel call info #
            ilen = 32
            nowLen = ilen
            try:
                kernelCall = " %s" % (kernelsym)
                nowLen += len(kernelCall)

                # Set kernel stack list #
                if self.sort == "sym":
                    targetStack = self.kerSymData[kernelsym]["symStack"]
                elif self.sort == "pos":
                    targetStack = self.kerSymData[kernelsym]["stack"]

                # Find kernel stack by addr #
                stack = []
                for val in targetStack:
                    if id(val[subStackIndex]) == kernelstack:
                        stack = val[subStackIndex]
                        break

                for subcall in stack:
                    try:
                        nextCall = " <- %s" % (subcall)
                        if SysMgr.lineLength > nowLen + len(nextCall):
                            kernelCall += nextCall
                            nowLen += len(nextCall)
                        else:
                            kernelCall = "%s\n%s %s" % (
                                kernelCall,
                                " " * ilen,
                                nextCall,
                            )
                            nowLen = ilen + len(nextCall)
                    except:
                        pass
            except:
                pass

            if userCall != " 0":
                SysMgr.printPipe(
                    "{0:>32}|{1:<121}".format("[User] ", userCall)
                )
            if kernelCall != " 0":
                SysMgr.printPipe(
                    "{0:>32}|{1:<121}".format("[Kernel] ", kernelCall)
                )
            SysMgr.printPipe(oneLine)

        SysMgr.printPipe("\n\n")

    def printLockUsage(self):
        # no lock event #
        if not self.lockEnabled or not SysMgr.userEnable:
            return

        subStackIndex = FunctionAnalyzer.symStackIdxTable.index("STACK")
        lockIndex = FunctionAnalyzer.symStackIdxTable.index("LOCK_TRY")
        unlockIndex = FunctionAnalyzer.symStackIdxTable.index("UNLOCK")

        # Print lock try #
        title = "Function Lock-Try Info"
        SysMgr.clearPrint()
        SysMgr.printPipe(
            "[%s] [Cnt: %d] (USER)\n%s"
            % (title, self.lockTryEventCnt, twoLine)
        )

        SysMgr.printPipe(
            "{0:_^9}|{1:_^47}|{2:_^49}|{3:_^46}\n{4:1}".format(
                "Usage", "Function", "Binary", "Source", twoLine
            )
        )

        for idx, value in sorted(
            self.userSymData.items(),
            key=lambda e: e[1]["lockTryCnt"],
            reverse=True,
        ):
            if value["lockTryCnt"] == 0:
                break

            binary = self.posData[value["pos"]]["origBin"]
            source = self.posData[value["pos"]]["src"]
            SysMgr.printPipe(
                "{0:8} |{1:^47}| {2:48}| {3:37}".format(
                    value["lockTryCnt"], idx, binary, source
                )
            )

            # Set target stack #
            targetStack = self.getTargetStack(value, lockIndex)

            # Merge and Print symbols in stack #
            for stack in targetStack:
                lockTryCnt = stack[lockIndex]
                subStack = list(stack[subStackIndex])

                if lockTryCnt == 0:
                    break

                if not subStack:
                    continue
                else:
                    ilen = len("\t" * 16)
                    symbolStack = self.makeUserSymList(subStack, ilen)

                SysMgr.printPipe(
                    "\t+ {0:8} |{1:32}".format(lockTryCnt, symbolStack)
                )

            SysMgr.printPipe(oneLine)

        if self.lockTryEventCnt == 0:
            SysMgr.printPipe("    None\n%s" % oneLine)

        SysMgr.printPipe("\n")

        # Print lock per-file #
        title = "Function Lock-Try File Info"
        SysMgr.printPipe(
            "[%s] [Cnt: %d] (USER)\n%s"
            % (title, self.lockTryEventCnt, twoLine)
        )

        SysMgr.printPipe(
            "{0:_^9}|{1:_^144}\n{2:1}".format("Usage", "Binary", twoLine)
        )

        for idx, value in sorted(
            self.userFileData.items(),
            key=lambda e: e[1]["lockTryCnt"],
            reverse=True,
        ):
            if value["lockTryCnt"] == 0:
                break

            SysMgr.printPipe(
                "{0:8} | {1:<142}\n{2:1}".format(
                    value["lockTryCnt"], idx, oneLine
                )
            )

        if self.periodicEventCnt == 0:
            SysMgr.printPipe("    None\n%s" % oneLine)

        SysMgr.printPipe("\n")

        # Print unlock #
        title = "Function Unlock Info"
        SysMgr.clearPrint()
        SysMgr.printPipe(
            "[%s] [Cnt: %d] (USER)\n%s" % (title, self.unlockEventCnt, twoLine)
        )

        SysMgr.printPipe(
            "{0:_^9}|{1:_^47}|{2:_^49}|{3:_^46}\n{4:1}".format(
                "Usage", "Function", "Binary", "Source", twoLine
            )
        )

        for idx, value in sorted(
            self.userSymData.items(),
            key=lambda e: e[1]["unlockCnt"],
            reverse=True,
        ):
            if value["unlockCnt"] == 0:
                break

            binary = self.posData[value["pos"]]["origBin"]
            source = self.posData[value["pos"]]["src"]
            SysMgr.printPipe(
                "{0:8} |{1:^47}| {2:48}| {3:37}".format(
                    value["unlockCnt"], idx, binary, source
                )
            )

            # Set target stack #
            targetStack = self.getTargetStack(value, unlockIndex)

            # Merge and Print symbols in stack #
            for stack in targetStack:
                unlockCnt = stack[unlockIndex]
                subStack = list(stack[subStackIndex])

                if unlockCnt == 0:
                    break

                if not subStack:
                    continue
                else:
                    ilen = len("\t" * 16)
                    symbolStack = self.makeUserSymList(subStack, ilen)

                SysMgr.printPipe(
                    "\t+ {0:8} |{1:32}".format(unlockCnt, symbolStack)
                )

            SysMgr.printPipe(oneLine)

        if self.unlockEventCnt == 0:
            SysMgr.printPipe("    None\n%s" % oneLine)

        SysMgr.printPipe("\n")

        # Print unlock per-file #
        title = "Function Unlock File Info"
        SysMgr.printPipe(
            "[%s] [Cnt: %d] (USER)\n%s"
            % (title, self.lockTryEventCnt, twoLine)
        )

        SysMgr.printPipe(
            "{0:_^9}|{1:_^144}\n{2:1}".format("Usage", "Binary", twoLine)
        )

        for idx, value in sorted(
            self.userFileData.items(),
            key=lambda e: e[1]["unlockCnt"],
            reverse=True,
        ):
            if value["unlockCnt"] == 0:
                break

            SysMgr.printPipe(
                "{0:8} | {1:<142}".format(value["unlockCnt"], idx)
            )

            SysMgr.printPipe(oneLine)

        if self.periodicEventCnt == 0:
            SysMgr.printPipe("    None\n%s" % oneLine)

        SysMgr.printPipe("\n")

        # Print lock history #
        if not SysMgr.showAll or not self.lockCallData:
            SysMgr.printPipe("\n\n")
            return

        title = "Function Lock History"
        SysMgr.clearPrint()
        SysMgr.printPipe(
            "[%s] [Lock: %d] [Unlock: %d]\n%s"
            % (title, self.lockTryEventCnt, self.unlockEventCnt, twoLine)
        )

        SysMgr.printPipe(
            (
                "{0:_^32}|{1:_^16}|{2:_^17}({3:_^7})|"
                "{4:_^8}|{5:_^17}|\n{6:1}"
            ).format("Event", "Target", "COMM", "TID", "Core", "Time", twoLine)
        )

        # sort by time #
        for call in self.lockCallData:
            event = call[0]
            target = call[1][0]
            time = call[1][1]
            core = call[1][2]
            tid = call[1][3]
            userstack = call[2]

            comm = self.threadData[tid]["comm"]
            title = (
                "{0:^32}|{1:^16}|{2:>16}({3:>7})| {4:>6} | {5:>15} |".format(
                    event, target, comm, tid, core, time
                )
            )
            SysMgr.printPipe("%s\n%s" % (title, len(title) * "-"))

            # Make user call info #
            ilen = 32
            nowLen = ilen
            try:
                last = userstack[0]
                stack = userstack[1]
                symbol = self.posData[last]["symbol"]
                binary = self.posData[last]["binary"]
                userCall = " %s[%s]" % (symbol, binary)
                nowLen += len(userCall)
                for subcall in stack:
                    try:
                        symbol = self.posData[subcall]["symbol"]
                        binary = self.posData[subcall]["binary"]
                        nextCall = " <- %s[%s]" % (symbol, binary)
                        if SysMgr.lineLength > nowLen + len(nextCall):
                            userCall += nextCall
                            nowLen += len(nextCall)
                        else:
                            userCall = "%s\n%s %s" % (
                                userCall,
                                " " * ilen,
                                nextCall,
                            )
                            nowLen = ilen + len(nextCall)
                    except:
                        pass
            except SystemExit:
                sys.exit(0)
            except:
                pass

            SysMgr.printPipe(
                "{0:>32}|{1:<121}\n{2:1}".format("[User] ", userCall, oneLine)
            )

        SysMgr.printPipe("\n\n")

    def printBlockWrUsage(self):
        # no block write event #
        if not self.bwriteEnabled:
            return

        title = "Function Write-Block Info"
        subStackIndex = FunctionAnalyzer.symStackIdxTable.index("STACK")
        blkWrIndex = FunctionAnalyzer.symStackIdxTable.index("BLK_WRITE")
        convSize = UtilMgr.convSize2Unit
        convNum = UtilMgr.convNum
        size = convSize(self.blockWrUsageCnt << 9)

        # Print block write in user space #
        if SysMgr.userEnable:
            SysMgr.clearPrint()
            SysMgr.printPipe(
                "[%s] [Size: %s] [Cnt: %s] (USER)\n%s"
                % (title, size, convNum(self.blockWrEventCnt), twoLine)
            )

            SysMgr.printPipe(
                "{0:_^9}|{1:_^47}|{2:_^49}|{3:_^46}\n{4:1}".format(
                    "Usage", "Function", "Binary", "Source", twoLine
                )
            )

            for idx, value in sorted(
                self.userSymData.items(),
                key=lambda e: e[1]["blockWrCnt"],
                reverse=True,
            ):
                if value["blockWrCnt"] == 0:
                    break

                binary = self.posData[value["pos"]]["origBin"]
                source = self.posData[value["pos"]]["src"]
                SysMgr.printPipe(
                    "{0:>8} |{1:^47}| {2:48}| {3:37}".format(
                        convSize(value["blockWrCnt"] << 9), idx, binary, source
                    )
                )

                # Set target stack #
                targetStack = self.getTargetStack(value, blkWrIndex)

                # Merge and Print symbols in stack #
                for stack in targetStack:
                    blockWrCnt = stack[blkWrIndex]
                    subStack = list(stack[subStackIndex])

                    if blockWrCnt == 0:
                        break

                    if not subStack:
                        continue
                    else:
                        ilen = len("\t" * 16)
                        symbolStack = self.makeUserSymList(subStack, ilen)

                    SysMgr.printPipe(
                        "\t+ {0:>8} |{1:32}".format(
                            convSize(blockWrCnt << 9), symbolStack
                        )
                    )

                SysMgr.printPipe(oneLine)

            if self.blockWrUsageCnt == 0:
                SysMgr.printPipe("    None\n%s" % oneLine)

            SysMgr.printPipe("\n")

        # Print block write in kernel space #
        SysMgr.clearPrint()
        SysMgr.printPipe(
            "[%s] [Size: %s] [Cnt: %s] (KERNEL)\n%s"
            % (title, size, convNum(self.blockWrEventCnt), twoLine)
        )

        SysMgr.printPipe(
            "{0:_^9}|{1:_^144}\n{2:1}".format("Usage", "Function", twoLine)
        )

        # Print block write usage of stacks #
        for idx, value in sorted(
            self.kerSymData.items(),
            key=lambda e: e[1]["blockWrCnt"],
            reverse=True,
        ):
            if value["blockWrCnt"] == 0:
                break

            SysMgr.printPipe(
                "{0:>8} |{1:^134}".format(
                    convSize(value["blockWrCnt"] << 9), idx
                )
            )

            # Sort stacks by usage #
            value["stack"] = sorted(
                value["stack"], key=lambda x: x[blkWrIndex], reverse=True
            )

            # Print stacks by symbol #
            for stack in value["stack"]:
                blockWrCnt = stack[blkWrIndex]
                subStack = list(stack[subStackIndex])

                if blockWrCnt == 0:
                    continue

                if not subStack:
                    symbolStack = "    None"
                else:
                    ilen = len("\t" * 16)
                    symbolStack = self.makeKernelSymList(subStack, ilen)

                SysMgr.printPipe(
                    "\t+ {0:>8} |{1:32}".format(
                        convSize(blockWrCnt << 9), symbolStack
                    )
                )

            SysMgr.printPipe(oneLine)

        if self.blockWrUsageCnt == 0:
            SysMgr.printPipe("    None\n%s" % oneLine)

        SysMgr.printPipe("\n\n")

    def getExceptionList(self):
        exceptList = {}

        # do not use this method now #
        return exceptList

        for pos, value in self.posData.items():
            if value["symbol"] == "None":
                try:
                    exceptList[pos]
                except:
                    exceptList[pos] = {}

        return exceptList

    def getTargetStack(self, value, index=None):
        targetStack = []
        if self.sort == "sym":
            targetStack = value["symStack"]
        elif self.sort == "pos":
            targetStack = value["stack"]

        # Sort by usage #
        if index:
            targetStack = sorted(
                targetStack, key=lambda x: x[index], reverse=True
            )
        else:
            targetStack.sort(reverse=True)

        return targetStack

    def printBlockRdUsage(self):
        # no block read event #
        if not self.breadEnabled:
            return

        title = "Function Read-Block Info"
        subStackIndex = FunctionAnalyzer.symStackIdxTable.index("STACK")
        blkRdIndex = FunctionAnalyzer.symStackIdxTable.index("BLK_READ")
        convSize = UtilMgr.convSize2Unit
        convNum = UtilMgr.convNum
        size = convSize(self.blockRdUsageCnt << 9)

        # Print block read in user space #
        if SysMgr.userEnable:
            SysMgr.clearPrint()
            SysMgr.printPipe(
                "[%s] [Size: %s] [Cnt: %s] (USER)\n%s"
                % (title, size, convNum(self.blockRdEventCnt), twoLine)
            )

            SysMgr.printPipe(
                "{0:_^9}|{1:_^47}|{2:_^49}|{3:_^46}\n{4:1}".format(
                    "Usage", "Function", "Binary", "Source", twoLine
                )
            )

            for idx, value in sorted(
                self.userSymData.items(),
                key=lambda e: e[1]["blockRdCnt"],
                reverse=True,
            ):
                if value["blockRdCnt"] == 0:
                    break

                binary = self.posData[value["pos"]]["origBin"]
                source = self.posData[value["pos"]]["src"]
                SysMgr.printPipe(
                    "{0:>8} |{1:^47}| {2:48}| {3:37}".format(
                        convSize(value["blockRdCnt"] << 9), idx, binary, source
                    )
                )

                # Set target stack #
                targetStack = self.getTargetStack(value, blkRdIndex)

                # Merge and Print symbols in stack #
                for stack in targetStack:
                    blockRdCnt = stack[blkRdIndex]
                    subStack = list(stack[subStackIndex])

                    if blockRdCnt == 0:
                        break

                    if not subStack:
                        continue
                    else:
                        ilen = len("\t" * 16)
                        symbolStack = self.makeUserSymList(subStack, ilen)

                    SysMgr.printPipe(
                        "\t+ {0:8} |{1:32}".format(
                            convSize(blockRdCnt << 9), symbolStack
                        )
                    )

                SysMgr.printPipe(oneLine)

            SysMgr.printPipe("\n")

        # Print block read in kernel space #
        SysMgr.clearPrint()
        SysMgr.printPipe(
            "[%s] [Size: %s] [Cnt: %s] (KERNEL)\n%s"
            % (title, size, convNum(self.blockRdEventCnt), twoLine)
        )

        SysMgr.printPipe(
            "{0:_^9}|{1:_^144}\n{2:1}".format("Usage", "Function", twoLine)
        )

        # Print block read usage of stacks #
        for idx, value in sorted(
            self.kerSymData.items(),
            key=lambda e: e[1]["blockRdCnt"],
            reverse=True,
        ):
            if value["blockRdCnt"] == 0:
                break

            SysMgr.printPipe(
                "{0:>8} |{1:^144}".format(
                    convSize(value["blockRdCnt"] << 9), idx
                )
            )

            # Sort stacks by usage #
            value["stack"] = sorted(
                value["stack"], key=lambda x: x[blkRdIndex], reverse=True
            )

            # Print stacks by symbol #
            for stack in value["stack"]:
                blockRdCnt = stack[blkRdIndex]
                subStack = list(stack[subStackIndex])

                if blockRdCnt == 0:
                    continue

                if not subStack:
                    symbolStack = "    None"
                else:
                    ilen = len("\t" * 16)
                    symbolStack = self.makeKernelSymList(subStack, ilen)

                SysMgr.printPipe(
                    "\t+ {0:>8} |{1:32}".format(
                        convSize(blockRdCnt << 9), symbolStack
                    )
                )

            SysMgr.printPipe(oneLine)

        SysMgr.printPipe("\n\n")


class LeakAnalyzer(object):
    """Analyzer for leaktracing"""

    startSig = 35  # SIGRT1
    stopSig = 12  # SIGUSR2
    markedIdlePages = False
    idlePageList = []
    repeatCnt = 0

    def __init__(self, f=None, pid=None, kill=False, tobj=None):
        self.pid = pid
        self.comm = None
        self.posData = {}
        self.symData = {}
        self.fileData = {}
        self.callData = []
        self.totalLeakSize = 0
        self.memUsage = "N/A"

        self.init_posData = {
            "offset": 0,
            "path": None,
            "lastPosCnt": 0,
            "callList": None,
            "count": 0,
            "size": 0,
            "lastPosSize": 0,
            "sym": "??",
        }

        self.init_symData = {
            "offset": 0,
            "path": None,
            "lastPosCnt": 0,
            "substack": None,
            "count": 0,
            "size": 0,
            "lastPosSize": 0,
        }

        self.init_fileData = {
            "lastPosCnt": 0,
            "count": 0,
            "size": 0,
            "lastPosSize": 0,
        }

        # get file size #
        fsize = UtilMgr.getFileSizeStr(f)

        # get target info #
        try:
            # get target object #
            proc = Debugger(pid=int(pid), attach=False)

            # save memory usage #
            if tobj:
                memUsage = TaskAnalyzer.getMemStr(tobj, pid)
                if memUsage:
                    self.memUsage = memUsage

            # save comm #
            self.comm = proc.comm

            # save maps #
            try:
                maps = SysMgr.getProcMaps(pid)
                mapFd = MemoryFile(name="buf", data=maps.encode())
                proc.mapFd = mapFd
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr(
                    "failed to save maps for %s(%s)" % (self.comm, self.pid),
                    True,
                )

            if kill:
                try:
                    os.kill(proc.pid, signal.SIGKILL)
                except:
                    pass
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                "failed to analyze leakage for the task with PID %s" % pid,
                True,
            )
            sys.exit(-1)

        # get trace data #
        try:
            SysMgr.printInfo("start loading '%s'%s" % (f, fsize))

            with open(f, "r") as fd:
                self.callData = self.parseLines(fd)
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printOpenErr(f)
            sys.exit(-1)

        # resolve symbols #
        SysMgr.printInfo("start resolving symbols...")
        self.resolveSymbols(proc)

        # merge symbols #
        SysMgr.printInfo("start merging symbols...")
        self.mergeSymbols()

    def printLeakage(self, runtime, profileTime, startTime):
        # define shortcut #
        convSize = UtilMgr.convSize2Unit

        # task info #
        proc = "%s(%s)" % (self.comm, self.pid)

        # set name #
        name = "Leakage"
        if LeakAnalyzer.markedIdlePages:
            if "REPORTIDLE" in SysMgr.environList:
                name = "Idle"
            elif "REPORTACTIVE" in SysMgr.environList:
                name = "Active"

        # set title #
        title = "Function %s Info" % name
        titleStr = (
            "\n\n[%s] [Process: %s] [Start: %s] [Run: %s] [Profile: %s] "
            "[Mem: %s] [%s: %s] [NrCall: %s]"
        ) % (
            title,
            proc,
            startTime,
            runtime,
            profileTime,
            self.memUsage,
            name,
            convSize(self.totalLeakSize),
            convSize(len(self.callData)),
        )
        SysMgr.printPipe(titleStr)

        SysMgr.printPipe(
            "{4:1}\n{0:>7} | {1:>7} | {2:>7} | {3:<122} |".format(
                "Size", "Count", "Avg", "Function", twoLine
            )
        )
        SysMgr.printPipe(
            "{0:>7} | {1:>7} | {2:<132} |\n{3:1}".format(
                " ", "Size", " Backtrace", twoLine
            )
        )

        # init flamegraph variable #
        stackList = {}

        count = 0
        for sym, val in sorted(
            self.symData.items(),
            key=lambda e: long(e[1]["lastPosSize"]),
            reverse=True,
        ):
            if val["lastPosSize"] == 0:
                break

            size = val["lastPosSize"]
            count = val["lastPosCnt"]
            avg = long(size / count)

            SysMgr.printPipe(
                "{0:>7} | {1:>7} | {2:>7} | {3:<122} ".format(
                    convSize(size),
                    convSize(count),
                    convSize(avg),
                    "%s[%s]" % (sym, val["path"]),
                )
            )

            for substack, size in sorted(
                val["substack"].items(), key=lambda e: e[1], reverse=True
            ):
                SysMgr.printPipe(
                    "{0:>7} | {1:>7} | {2:<132} ".format(
                        "", convSize(size), substack
                    )
                )

                # register fullstack to the list for flamegraph #
                fullStack = " ".join(
                    [
                        item.strip()
                        for item in (
                            "%s[%s]%s" % (sym, val["path"], substack)
                        ).split("\n")
                    ]
                )
                stackList[fullStack] = size

            count += 1

            SysMgr.printPipe(oneLine)

        if count == 0:
            SysMgr.printPipe("    None\n%s" % oneLine)

        # set title #
        title = "File %s Info" % name
        SysMgr.printPipe(
            (
                "\n[%s] [Process: %s] [Start: %s] [Run: %s] [Profile: %s] "
                "[Mem: %s] [%s: %s] [NrCall: %s]\n%s"
            )
            % (
                title,
                proc,
                startTime,
                runtime,
                profileTime,
                self.memUsage,
                name,
                convSize(self.totalLeakSize),
                convSize(len(self.callData)),
                twoLine,
            )
        )

        SysMgr.printPipe(
            "{0:>7} | {1:>7} | {2:>7} | {3:<122} |\n{4:1}".format(
                "Size", "Count", "Avg", "Path", twoLine
            )
        )

        count = 0
        for fpath, val in sorted(
            self.fileData.items(),
            key=lambda e: long(e[1]["lastPosSize"]),
            reverse=True,
        ):
            if val["lastPosSize"] == 0:
                break
            elif val["lastPosCnt"] == 0:
                continue

            size = val["lastPosSize"]
            count = val["lastPosCnt"]
            avg = long(size / count)

            SysMgr.printPipe(
                "{0:>7} | {1:>7} | {2:>7} | {3:<122} |".format(
                    convSize(size),
                    convSize(count),
                    convSize(avg),
                    fpath if fpath else "",
                )
            )

            count += 1

        if count == 0:
            SysMgr.printPipe("    None")

        SysMgr.printPipe(oneLine)

        # check exit condition #
        if not self.callData:
            SysMgr.printWarn("no sample data", True)
            return

        # draw flamegraph #
        if SysMgr.inputFile:
            inputFile = SysMgr.inputFile
        else:
            inputFile = "guider.out"
        inputFile = os.path.realpath(inputFile)
        SysMgr.printStat(r"start drawing a flame graph...")
        Debugger.drawFlame(inputFile, stackList, titleStr, suffix=True)

        # print histo stats for size #
        sizeStats = UtilMgr.convList2Histo(
            [v["size"] for v in self.callData.values()]
        )
        UtilMgr.printHist(sizeStats, "Allocation", "byte")

        # check exit condition #
        if not SysMgr.showAll:
            return

        # leakage history #
        title = "Leakage History"
        SysMgr.printPipe(
            "\n[%s] [Process: %s] [Start: %s] [Total: %s] [Count: %s]\n%s"
            % (
                title,
                proc,
                startTime,
                convSize(self.totalLeakSize, True),
                convSize(len(self.callData), True),
                twoLine,
            )
        )

        SysMgr.printPipe(
            "{0:^16} | {1:^16} | {2:^6} |{3:^50}| {4:^53}\n{5:1}".format(
                "Time", "Addr", "Size", "Data", "Stack", oneLine
            )
        )

        if not self.callData:
            SysMgr.printPipe("\tNone\n%s" % oneLine)
            return

        for timeval, items in sorted(
            self.callData.items(), key=lambda e: e[0], reverse=False
        ):
            stack = " <- ".join(
                [
                    "%s(%s)[%s]" % (item[1], item[0], item[2])
                    for item in list(items["symstack"])
                ]
            )

            SysMgr.printPipe(
                "{0:>16} | {1:>16} | {2:>6} |{3:<50}| {4:<53}\n{5:1}".format(
                    timeval,
                    items["addr"] if "addr" in items else " ",
                    long(items["size"]),
                    items["data"][:-1],
                    stack,
                    oneLine,
                )
            )

    def mergeSymbols(self):
        cnt = 0
        total = len(self.posData)
        dobj = Debugger()
        for pos, val in self.posData.items():
            UtilMgr.printProgress(cnt, total)
            cnt += 1

            # merge by symbol #
            sym = val["sym"]

            try:
                symData = self.symData[sym]
                symData["count"] += val["count"]
                symData["size"] += val["size"]
                symData["lastPosSize"] += val["lastPosSize"]
                if val["lastPosSize"] > 0:
                    symData["lastPosCnt"] += val["count"]
            except SystemExit:
                sys.exit(0)
            except:
                self.symData[sym] = dict(self.init_symData)
                symData = self.symData[sym]
                symData["offset"] = val["offset"]
                symData["path"] = val["path"]
                symData["count"] = val["count"]
                symData["size"] = val["size"]
                symData["lastPosSize"] = val["lastPosSize"]
                if val["lastPosSize"] > 0:
                    symData["lastPosCnt"] += val["count"]
                symData["substack"] = {}

            # merge by backtrace #
            path = val["path"]
            if val["callList"]:
                for timeval in list(val["callList"]):
                    callinfo = self.callData[timeval]
                    substack = dobj.getBtStr(callinfo["symstack"][1:])
                    dobj.btStr = None
                    size = long(callinfo["size"])

                    try:
                        symData["substack"][substack] += size
                    except:
                        symData["substack"][substack] = size

            # merge by file #
            try:
                fileData = self.fileData[path]
                fileData["count"] += val["count"]
                fileData["size"] += val["size"]
                fileData["lastPosSize"] += val["lastPosSize"]
                if val["lastPosSize"] > 0:
                    fileData["lastPosCnt"] += val["count"]
            except SystemExit:
                sys.exit(0)
            except:
                self.fileData[path] = dict(self.init_fileData)
                fileData = self.fileData[path]
                fileData["count"] = val["count"]
                fileData["size"] = val["size"]
                fileData["lastPosSize"] = val["lastPosSize"]
                if val["lastPosSize"] > 0:
                    fileData["lastPosCnt"] = val["count"]

            self.totalLeakSize += val["lastPosSize"]

        if cnt:
            UtilMgr.deleteProgress()

    def resolveSymbols(self, proc):
        cnt = 0
        total = len(self.posData) + len(self.callData)

        # disable load messages #
        SysMgr.addEnvironVar("NOLOADMSG")

        # resolve all symbols #
        for pos, val in self.posData.items():
            UtilMgr.printProgress(cnt, total)
            cnt += 1

            try:
                ret = proc.getSymbolInfo(long(pos, 16))
                if ret and len(ret) > 3:
                    val["sym"] = ret[0]
                    val["path"] = ret[1]
                    val["offset"] = ret[2]
            except:
                SysMgr.printWarn(
                    "failed to get symbol for %s" % pos, reason=True
                )
                continue

        posCache = {}

        # resolve symbols in stacks #
        for pos, val in self.callData.items():
            UtilMgr.printProgress(cnt, total)
            cnt += 1

            if not "stack" in val:
                continue

            symstack = list(val["stack"])

            for idx, offset in enumerate(val["stack"]):
                data = self.posData[offset]
                # save stack item info in cache list #
                if not offset in posCache:
                    posCache[offset] = [
                        data["offset"],
                        data["sym"],
                        data["path"],
                    ]
                symstack[idx] = posCache[offset]

            val["symstack"] = symstack

            try:
                posid = val["stack"][0]
                self.posData[posid].setdefault("callList", {})
                self.posData[posid]["callList"][pos] = None
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printWarn("failed to get stack from %s" % val, True)

        if cnt:
            UtilMgr.deleteProgress()

    def parseLines(self, fd):
        callinfo = {}
        total = os.fstat(fd.fileno()).st_size
        pageSize = SysMgr.PAGESIZE

        # check REPORTIDLE variable to report only idle page info #
        bitVal = -1
        if LeakAnalyzer.markedIdlePages:
            if "REPORTIDLE" in SysMgr.environList:
                bitVal = 0
            elif "REPORTACTIVE" in SysMgr.environList:
                bitVal = 1
            else:
                LeakAnalyzer.markedIdlePages = False

        # define stack cache #
        stackCache = {}

        # get size check function #
        _sizeChecker = UtilMgr.getSizeFilterFunc()

        while 1:
            try:
                line = fd.readline()
            except SystemExit:
                sys.exit(0)
            except:
                continue

            # check EOF #
            if not line:
                break

            # print progress #
            cur = fd.tell()
            if cur > total:
                total = os.fstat(fd.fileno()).st_size
            UtilMgr.printProgress(cur, total)

            # check prefix #
            if not line.startswith("leak,"):
                continue

            items = line.split(", ")
            time = None
            item = {}

            for content in items[1:]:
                try:
                    name, body = content.split("=", 1)
                except SystemExit:
                    sys.exit(0)
                except:
                    continue

                if name == "time":
                    time = body
                elif name == "stack":
                    # split callstack #
                    try:
                        if body in stackCache:
                            item[name] = stackCache[body]
                        else:
                            item[name] = body.split()
                            item[name] = item[name][1:]
                            stackCache[body] = item[name]
                    except SystemExit:
                        sys.exit(0)
                    except:
                        pass
                elif not SysMgr.showAll and name == "data":
                    continue
                else:
                    item[name] = body

            # check size value #
            if not item or not "size" in item or not item["size"].isdigit():
                continue

            # get allocation size #
            size = long(item["size"])
            if not _sizeChecker(size):
                continue

            # filter parts on idle pages #
            if LeakAnalyzer.markedIdlePages and "addr" in item:
                # get start address for a chunk #
                addr = long(item["addr"], 16)
                addrStart = long(addr / pageSize) * pageSize
                addrDiff = addr - addrStart

                # get end address for a chunk #
                sizeAligned = long((size + addrDiff + pageSize - 1) / pageSize)
                addrEnd = addrStart + sizeAligned

                # ignore active part for a chunk #
                try:
                    for mem in LeakAnalyzer.idlePageList[self.pid]:
                        mapStart, mapEnd, bitmap = mem

                        # check scope #
                        if addrStart >= mapEnd or addrEnd <= mapStart:
                            continue

                        # get start address for check #
                        start = addrStart - mapStart
                        if start < 0:
                            sizeAligned += start / pageSize
                            start = 0
                        idx = long((start) / pageSize)

                        # subtract the size of used pages #
                        for offset in range(sizeAligned):
                            targetIdx = idx + offset
                            if len(bitmap) <= targetIdx:
                                break
                            elif bitmap[targetIdx] != bitVal:
                                size -= pageSize

                        # TODO: consider spanning chunks between two pages #
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printWarn(
                        "failed to check an idle chunk", True, True
                    )

                # skip all used chunks #
                if size < 1:
                    continue

                # update stack size #
                item["size"] = size

            # save pos in common area #
            for pos in item["stack"]:
                try:
                    self.posData[pos]["count"] += 1
                    self.posData[pos]["size"] += size
                except SystemExit:
                    sys.exit(0)
                except:
                    self.posData[pos] = dict(self.init_posData)
                    self.posData[pos]["count"] = 1
                    self.posData[pos]["size"] = size
                    self.posData[pos]["callList"] = {}

            try:
                lastPos = item["stack"][0]
            except:
                continue

            self.posData[lastPos]["lastPosSize"] += size

            callinfo[time] = item

        UtilMgr.deleteProgress()

        return callinfo


class FileAnalyzer(object):
    """Analyzer for file profiling"""

    procMapCache = {}
    procMapStrCache = {}
    procFdMapCache = {}

    init_mapData = {
        "offset": 0,
        "size": 0,
        "pageCnt": 0,
        "fd": None,
        "totalSize": 0,
        "fileMap": None,
        "pids": None,
        "linkCnt": 0,
        "inode": None,
        "accessTime": None,
        "devid": None,
        "isRep": True,
        "perm": None,
        "nrOpen": 0,
        "repFile": None,
        "hardLink": long(1),
        "linkList": None,
        "vstart": 0,
        "vend": 0,
        "elfInfo": None,
        "nrMap": 0,
    }

    def __init__(self):
        self.profSuccessCnt = 0
        self.profFailedCnt = 0
        self.profPageCnt = 0
        self.pgRclmBg = 0
        self.pgRclmFg = 0
        self.procData = {}
        self.fileData = {}
        self.inodeData = {}
        self.target = [""]
        self.readaheadStr = ""

        self.procList = {}
        self.fileList = {}

        self.intervalProcData = []
        self.intervalFileData = []

        self.init_procData = {
            "tids": None,
            "pageCnt": 0,
            "procMap": None,
            "comm": "",
        }
        self.init_threadData = {"comm": ""}
        self.init_inodeData = {}

        # set system maximum fd number #
        SysMgr.setMaxFd()

        # set specific file #
        targetFiles = []

        # convert target path to abspath #
        for item in SysMgr.customCmd:
            if "*" in item:
                continue
            path = os.path.realpath(item)
            targetFiles.append(path)

        # convert target path to realpath #
        if SysMgr.customCmd:
            SysMgr.printInfo(
                "start checking specific files related to [ %s ]"
                % ", ".join(SysMgr.customCmd)
            )

            # get specific file list #
            convList = UtilMgr.getFileList(SysMgr.customCmd)
            for fname in convList:
                try:
                    if os.path.isdir(fname):
                        for subfname in UtilMgr.getFiles(
                            fname, recursive=SysMgr.recursionEnable
                        ):
                            SysMgr.getCachedFd(subfname)
                            targetFiles.append(os.path.realpath(subfname))
                    elif os.path.isfile(fname):
                        SysMgr.getCachedFd(fname)
                        targetFiles.append(os.path.realpath(fname))
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr("failed to open '%s'" % fname, reason=True)
                    sys.exit(-1)

        # remove redundant filters #
        targetFiles = set(targetFiles)

        # add file filters #
        targetFilter = SysMgr.environList.get("TARGETFILE")

        # handle no target case #
        if SysMgr.filterGroup:
            self.target = SysMgr.filterGroup

        if not SysMgr.guiderObj:
            # load libc #
            SysMgr.loadLibcObj(exit=True)

            # define munmap types #
            SysMgr.libcObj.munmap.argtypes = [POINTER(None), c_size_t]
            SysMgr.libcObj.munmap.restype = c_int

            # define mincore types #
            SysMgr.libcObj.mincore.argtypes = [
                POINTER(None),
                c_size_t,
                POINTER(c_ubyte),
            ]
            SysMgr.libcObj.mincore.restype = c_int

        # set filter list #
        targetFiles = targetFiles if targetFiles else [""]
        exceptFiles = (
            SysMgr.environList["EXCEPTFILE"]
            if "EXCEPTFILE" in SysMgr.environList
            else None
        )
        onlyTarget = "ONLYTARGET" in SysMgr.environList

        while 1:
            # print plan #
            realTargets = set(targetFiles) - {""}
            if realTargets:
                SysMgr.printStat(
                    "start collecting a total of %s files..."
                    % UtilMgr.convNum(len(realTargets))
                )

            SysMgr.printStat("start collecting all files on memory...")

            # scan proc directory and save map information of processes #
            self.scanProcs(
                targetFilter=targetFilter,
                targetFile=targetFiles,
                exceptFilter=exceptFiles,
            )

            # merge maps of processes into a integrated file map #
            self.mergeFileMapInfo(
                targetFilter=targetFilter,
                targetFile=targetFiles,
                exceptFilter=exceptFiles,
                onlyTarget=onlyTarget,
            )

            # get file map info on memory #
            self.getFilePageMaps()

            # fill file map of each processes #
            self.fillFileMaps()

            if SysMgr.intervalEnable:
                # save previous file usage and initialize all variables #
                self.intervalProcData.append(self.procData)
                self.intervalFileData.append(self.fileData)
                self.procData = {}
                self.fileData = {}
                self.inodeData = {}
                self.profSuccessCnt = 0
                self.profFailedCnt = 0

                # check exit condition for interval profile #
                if not SysMgr.condExit:
                    SysMgr.waitEvent()
                else:
                    break
            else:
                break

        # save system reclaim info
        try:
            # get vmstat #
            vmData = SysMgr.getVmstat(retDict=True)

            for name in list(ConfigMgr.BGRECLAIMSTAT):
                if name in vmData:
                    self.pgRclmBg += vmData[name]

            for name in list(ConfigMgr.FGRECLAIMSTAT):
                if name in vmData:
                    self.pgRclmFg += vmData[name]
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to get system reclaim stat", True)

    def __del__(self):
        pass

    def printIntervalInfo(self):
        # merge process info into a global list #
        for data in self.intervalProcData:
            for pid, info in data.items():
                try:
                    if self.procList[pid]["pageCnt"] < info["pageCnt"]:
                        self.procList[pid]["pageCnt"] = info["pageCnt"]
                except:
                    self.procList[pid] = dict(self.init_procData)
                    self.procList[pid]["tids"] = {}
                    self.procList[pid]["pageCnt"] = info["pageCnt"]
                    self.procList[pid]["comm"] = info["comm"]

                for tid, val in info["tids"].items():
                    try:
                        self.procList[pid]["tids"][tid]
                    except:
                        self.procList[pid]["tids"][tid] = dict(
                            self.init_threadData
                        )
                        self.procList[pid]["tids"][tid]["comm"] = val["comm"]

        if not self.procList:
            SysMgr.printErr("no process profiled")
            sys.exit(-1)

        # merge file info into a global list #
        fileData = self.intervalFileData
        for data in fileData:
            for name, stat in data.items():
                try:
                    fl = self.fileList[name]
                    if fl["pageCnt"] < stat["pageCnt"]:
                        fl["pageCnt"] = stat["pageCnt"]
                except:
                    self.fileList[name] = dict(FileAnalyzer.init_mapData)
                    self.fileList[name]["pageCnt"] = stat["pageCnt"]
                    self.fileList[name]["totalSize"] = stat["totalSize"]

        # check result #
        if not self.fileList:
            SysMgr.printErr("no file profiled")
            sys.exit(-1)

        self.procData = self.procList

        SysMgr.printLogo(big=True)

        # print system information #
        SysMgr.printInfoBuffer()

        # define alias #
        pageSize = SysMgr.PAGESIZE
        convSize = UtilMgr.convSize2Unit
        convColor = UtilMgr.convColor
        convNum = UtilMgr.convNum
        uptime = UtilMgr.convTime(SysMgr.updateUptime())

        # print process list #
        self.printProcUsage()

        # print file list #
        SysMgr.printPipe(
            (
                "[%s] [File: %s] [LastRAM: %s] [Reclaim: %s/%s] "
                "[Uptime: %s] [Keys: Foward/Back/Save/Quit]\n%s"
            )
            % (
                "File Usage Info",
                convNum(len(self.fileList)),
                convSize(self.profPageCnt * 4 << 10),
                convSize(self.pgRclmBg * 4 << 10),
                convSize(self.pgRclmFg * 4 << 10),
                uptime,
                twoLine,
            )
        )

        printMsg = "{0:_^8}|{1:_^8}|{2:_^3}|".format("InitRAM", "File", "%")

        if len(fileData) > 1:
            for idx in xrange(1, len(fileData)):
                printMsg += "{0:_^15}|".format(str(idx))

        # print title #
        lineLength = SysMgr.lineLength
        printMsg += "{0:_^8}|{1:_^3}|".format("LastRAM", "%")
        printMsg += "_" * (long((lineLength - len(printMsg)) / 2) - 2)
        printMsg += "Library"
        printMsg += "_" * (lineLength - len(printMsg))
        printMsg += "\n%s" % twoLine
        SysMgr.printPipe(printMsg)

        # print interval usage #
        for fileName, val in sorted(
            self.fileList.items(),
            key=lambda e: long(e[1]["pageCnt"]),
            reverse=True,
        ):
            # memory size #
            try:
                memSize = fileData[0][fileName]["pageCnt"] * pageSize
            except:
                memSize = 0

            try:
                idx = val["totalSize"] + pageSize - 1
                fileSize = long(idx / pageSize) * pageSize
            except:
                fileSize = 0

            # set percentage #
            if fileSize != 0:
                per = long((long(memSize) / float(fileSize)) * 100)
                per = UtilMgr.convCpuColor(per, size=3)
            else:
                per = 0

            if memSize > 0:
                memSize = convColor(convSize(memSize), "YELLOW", 7)

            # check whether this file was profiled or not #
            isRep = False
            for fileData in reversed(fileData):
                if fileName in fileData and fileData[fileName]["isRep"]:
                    printMsg = "{0:>7} |{1:>7} |{2:>3}|".format(
                        memSize, convSize(fileSize), per
                    )
                    isRep = True
                    break

            if not isRep:
                continue

            # calculate diff of on-memory file size #
            if len(fileData) > 1:
                for idx in xrange(len(fileData) - 1):
                    diffNew = 0
                    diffDel = 0

                    try:
                        nowFileMap = fileData[idx][fileName]["fileMap"]
                    except:
                        nowFileMap = None

                    try:
                        prevFileMap = fileData[idx - 1][fileName]["fileMap"]
                    except:
                        prevFileMap = None

                    if not nowFileMap:
                        if prevFileMap:
                            diffDel = fileData[idx - 1][fileName]["pageCnt"]
                    else:
                        if not prevFileMap:
                            diffNew = fileData[idx][fileName]["pageCnt"]
                        else:
                            if len(nowFileMap) == len(prevFileMap):
                                for i in xrange(len(nowFileMap)):
                                    if nowFileMap[i] > prevFileMap[i]:
                                        diffNew += 1
                                    elif nowFileMap[i] < prevFileMap[i]:
                                        diffDel += 1

                    if diffNew > 0:
                        diffNew = convColor(
                            convSize(diffNew * pageSize), "RED", 6
                        )
                    else:
                        diffNew = " "

                    if diffDel > 0:
                        diffDel = convColor(
                            convSize(diffDel * pageSize), "RED", 6
                        )
                    else:
                        diffDel = " "

                    printMsg += "+%6s/-%6s|" % (diffNew, diffDel)

            finalData = fileData[-1][fileName]

            totalMemSize = finalData["pageCnt"] * pageSize

            if fileSize != 0:
                per = long((long(totalMemSize) / float(fileSize)) * 100)
                per = UtilMgr.convCpuColor(per, size=3)
            else:
                per = 0

            if totalMemSize > 0:
                totalMemSize = convColor(convSize(totalMemSize), "YELLOW", 7)

            printMsg += "{0:>7} |{1:>3}| {2:1}".format(
                totalMemSize, per, fileName
            )

            SysMgr.printPipe(printMsg)

        SysMgr.printPipe("%s\n\n\n" % oneLine)

    @staticmethod
    def getMappedList(pid, f):
        rpath = "%s/%s/map_files" % (SysMgr.procPath, pid)
        try:
            dirList = os.listdir(rpath)
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn(
                "failed to get the mapped file list for %s(%s)"
                % (SysMgr.getComm(pid), pid),
                reason=True,
            )

        # convert filter type to list #
        if not isinstance(f, list):
            f = [f]

        retList = []
        for item in dirList:
            try:
                fpath = os.path.join(rpath, item)
                fpathName = os.readlink(fpath)
                if UtilMgr.isValidStr(fpathName, f):
                    retList.append(fpath)
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printWarn(
                    "failed to get mapped file from %s for %s(%s)"
                    % (fpath, SysMgr.getComm(pid), pid),
                    reason=True,
                )

        return retList

    @staticmethod
    def isDeletedFile(fileName):
        return fileName.endswith(" (deleted)")

    @staticmethod
    def isValidFile(fileName, special=False, incDel=False):
        # skip non-files #
        if not fileName.startswith("/"):
            return False
        # skip device nodes #
        elif fileName.startswith("/dev/"):
            return False
        # skip proc nodes #
        elif fileName.startswith("/proc/"):
            return False
        # skip sys nodes #
        elif fileName.startswith("/sys/"):
            return False
        # skip non-contiguous segments #
        elif SysMgr.magicStr in fileName:
            return False
        # skip deleted files #
        elif not incDel and FileAnalyzer.isDeletedFile(fileName):
            return False

        # check special #
        if special:
            if fileName == "vdso":
                return True

        return True

    @staticmethod
    def getMapAddr(pid, fname, fd=None, retList=False):
        if not fname:
            SysMgr.printWarn("no file name to be searched for address")
            return None
        elif not fd:
            fd = FileAnalyzer.getMapFd(pid)
            if not fd:
                return None

        addrList = []
        allAnon = allFile = allPages = noPerm = fileAnon = something = False

        # check anon #
        if fname == "anon":
            allAnon = True
        # check file #
        elif fname == "file":
            allFile = True
        elif fname == "fileanon":
            fileAnon = True
        # check all #
        elif fname == "all":
            allPages = True
        # check noperm #
        elif fname == "noperm":
            noPerm = True
        else:
            something = True

        # get anon area #
        if not allPages and not noPerm:
            anonMap = TaskAnalyzer.getProcAnonMap(pid)
        else:
            anonMap = None

        # check condition #
        if fileAnon and not anonMap:
            SysMgr.printErr("no smaps info")
            sys.exit(0)

        # read maps #
        fd.seek(0, 0)
        for item in fd.readlines():
            mdict = FileAnalyzer.parseMapLine(
                item, needName=allFile or something
            )
            if not mdict:
                continue

            area = item.split(" ", 1)[0]

            # all #
            if allPages:
                pass
            # no perm #
            elif noPerm:
                if not mdict["perm"].startswith("--"):
                    continue
            # anons #
            elif allAnon:
                # include file-mapped anon pages #
                if anonMap:
                    if area in anonMap and anonMap[area]["size"] == 0:
                        continue
                elif mdict["inode"] != "0" and not mdict["perm"].startswith(
                    "rw"
                ):
                    continue
            # file-mapped anons #
            elif fileAnon:
                # check file-mapped anon pages #
                if mdict["inode"] == "0":
                    continue
                elif anonMap:
                    if area in anonMap and anonMap[area]["size"] == 0:
                        continue
            # files #
            else:
                if not "binName" in mdict or not mdict["binName"]:
                    continue
                # all files #
                elif allFile and area in anonMap and anonMap[area]["size"]:
                    continue
                elif (
                    allFile
                    and mdict["binName"].startswith("/")
                    and mdict["inode"] != "0"
                ):
                    pass
                # a specific file #
                elif not mdict["binName"].endswith(
                    fname
                ) and not UtilMgr.isValidStr(mdict["binName"], [fname]):
                    continue

            start = str(mdict["startAddr"])
            end = str(mdict["endAddr"])

            if retList:
                addrList.append([start, end])
            else:
                return start, end

        if retList:
            return addrList
        else:
            return None

    @staticmethod
    def getMapFilePath(pid, fname, fd=None):
        if not fname:
            SysMgr.printWarn("no file name to be searched for path")
            return None
        elif not fd:
            fd = FileAnalyzer.getMapFd(pid)
            if not fd:
                return None

        # read maps #
        fd.seek(0, 0)
        for item in fd.readlines():
            mdict = FileAnalyzer.parseMapLine(item)
            if mdict and mdict["binName"]:
                if os.path.basename(mdict["binName"]).startswith(fname):
                    path = str(mdict["binName"])
                    overlayInfo = SysMgr.getOverlayfsInfo(pid)
                    if overlayInfo:
                        path = UtilMgr.convOverlayPath(path, overlayInfo)
                    return path

        return None

    @staticmethod
    def getEmptyMapAddr(pid, fd=None, size=0, onlyExec=False):
        if not fd:
            fd = FileAnalyzer.getMapFd(pid)
            if not fd:
                return None

        # search empty space #
        for item in fd.readlines():
            mdict = FileAnalyzer.parseMapLine(item)
            if not mdict:
                mapLine = item.split()
                addrs = list(map(lambda x: long(x, 16), mapLine[0].split("-")))
                perm = mapLine[1]
                if onlyExec and not "x" in perm:
                    continue

                return addrs[0]

    @staticmethod
    def getProcFiles(pid, incMap=True, incFd=True):
        fileList = []

        SysMgr.checkRootPerm()

        if incMap:
            files = FileAnalyzer.getProcMapInfo(pid, saveAll=True)
            for f in files:
                if FileAnalyzer.isValidFile(f):
                    fileList.append(f)

        if incFd:
            try:
                fdlistPath = "%s/%s/fd" % (SysMgr.procPath, pid)
                for fd in os.listdir(fdlistPath):
                    fdPath = "%s/%s" % (fdlistPath, fd)
                    fname = os.readlink(fdPath)
                    if FileAnalyzer.isValidFile(fname):
                        fileList.append(fname)
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr(
                    "failed to get open file list for %s(%s)"
                    % (SysMgr.getComm(pid), pid),
                    True,
                )

        return fileList

    @staticmethod
    def getProcMapFd(pid, verb=False):
        fd = FileAnalyzer.getMapFd(pid, verb)
        if fd:
            return fd

        # get comm #
        comm = SysMgr.getComm(pid)

        # check alive #
        if not SysMgr.isAlive(pid):
            reason = " because it is terminated"
        # check root #
        elif not SysMgr.isRoot():
            reason = " because there is no root permission"
        else:
            reason = ""

        SysMgr.printErr(
            "failed to get memory map for %s(%s)%s" % (comm, pid, reason)
        )

        # quit #
        if SysMgr.forceEnable:
            return
        else:
            sys.exit(-1)

    @staticmethod
    def getMapFd(pid, verb=False):
        # open maps #
        try:
            path = "%s/%s/maps" % (SysMgr.procPath, pid)
            return open(path, "r")
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printOpenWarn(path, verb)
            return None

    @staticmethod
    def getAnonMapInfo(pid, fd=None, onlyExec=True):
        # set file descriptor #
        if fd:
            fd.seek(0, 0)
        else:
            fd = FileAnalyzer.getProcMapFd(pid)

        # read maps #
        mapBuf = fd.readlines()

        # define map dictionary #
        anonMap = []

        # parse lines #
        for string in mapBuf:
            m = re.match(
                (
                    r"^(?P<startAddr>.\S+)-(?P<endAddr>.\S+) (?P<perm>.\S+) "
                    r"(?P<offset>.\S+) (?P<devid>.\S+) 0"
                ),
                string,
            )
            if not m:
                continue

            d = m.groupdict()

            # get execution permission #
            if onlyExec and "perm" in d and d["perm"][-2] == "-":
                continue

            # get size info #
            startAddr = long(d["startAddr"], 16)
            endAddr = long(d["endAddr"], 16)

            anonMap.append([startAddr, endAddr])

        return anonMap

    @staticmethod
    def getFileMapSize(pid, target, reverse=False):
        try:
            # get file descriptor #
            fd = FileAnalyzer.getProcMapFd(pid)

            # read maps #
            mapBuf = fd.readlines()
            if reverse:
                mapBuf = reversed(mapBuf)
        except SystemExit:
            sys.exit(0)
        except:
            return 0

        # define map dictionary #
        fileMap = {}

        # parse and merge lines in maps #
        for val in mapBuf:
            FileAnalyzer.mergeMapLine(val, fileMap)
            finfo = fileMap.get(target, {})
            if finfo:
                return finfo["size"]

        return 0

    @staticmethod
    def getProcMapInfo(pid, fd=None, onlyExec=False, saveAll=False):
        try:
            # set file descriptor #
            if fd:
                fd.seek(0, 0)
            else:
                fd = FileAnalyzer.getProcMapFd(pid)

            # read maps #
            mapBuf = fd.readlines()
        except SystemExit:
            sys.exit(0)
        except:
            return {}

        # check map cache #
        try:
            if FileAnalyzer.procMapStrCache[pid] == mapBuf:
                return FileAnalyzer.procMapCache[pid]
        except SystemExit:
            sys.exit(0)
        except:
            pass
        finally:
            FileAnalyzer.procMapStrCache[pid] = mapBuf

        # define map dictionary #
        fileMap = {}

        # parse and merge lines in maps #
        for val in mapBuf:
            FileAnalyzer.mergeMapLine(val, fileMap, saveAll=saveAll)

        # remove non-executable files #
        if onlyExec:
            # check non-executable files having discontiguous segments #
            exeFileMap = {}
            for fname in fileMap:
                if not fname.startswith("/") or not fileMap[fname]["exec"]:
                    continue
                exeFileMap[fname.split(SysMgr.magicStr, 1)[0]] = True

            # exclude non-executable files #
            for fname in list(fileMap):
                if (
                    fname != "stack"
                    and not fileMap[fname]["exec"]
                    and not fname.split(SysMgr.magicStr, 1)[0] in exeFileMap
                ):
                    fileMap.pop(fname, None)

        # save map cache #
        FileAnalyzer.procMapCache[pid] = fileMap

        return fileMap

    @staticmethod
    def addMapLine(dataObj, fileName, newOffset, newSize):
        newEnd = newOffset + newSize

        try:
            savedOffset = dataObj[fileName]["offset"]
            savedSize = dataObj[fileName]["size"]
            savedEnd = savedOffset + savedSize

            # start address bigger than saved one #
            if savedOffset <= newOffset:
                # merge bigger end address than saved one #
                if savedEnd < newEnd:
                    dataObj[fileName]["size"] += (
                        newEnd - savedOffset - savedSize
                    )
                # ignore lesser end address than saved one #
                else:
                    pass
            # start address lesser than saved one #
            else:
                if savedEnd >= newEnd:
                    dataObj[fileName]["size"] += savedOffset - newOffset
                else:
                    dataObj[fileName]["size"] = newSize

                dataObj[fileName]["offset"] = newOffset
        except SystemExit:
            sys.exit(0)
        except:
            dataObj[fileName] = dict(FileAnalyzer.init_mapData)
            dataObj[fileName]["offset"] = newOffset
            dataObj[fileName]["size"] = newSize
            dataObj[fileName]["nrMap"] = 1

    @staticmethod
    def parseMapLine(string, needName=True):
        if needName:
            matchStr = (
                r"^(?P<startAddr>.\S+)-(?P<endAddr>.\S+) (?P<perm>.\S+) "
                r"(?P<offset>.\S+) (?P<devid>.\S+) (?P<inode>[0-9]+)"
                r".\s*(?P<binName>.+)"
            )
        else:
            matchStr = (
                r"^(?P<startAddr>.\S+)-(?P<endAddr>.\S+) (?P<perm>.\S+) "
                r"(?P<offset>.\S+) (?P<devid>.\S+) (?P<inode>[0-9]+)"
            )

        m = re.match(matchStr, string)
        if not m:
            return None

        return m.groupdict()

    @staticmethod
    def getReadaheadListStr(raList, raSummary):
        if not raList:
            SysMgr.printWarn("no readahead item", True)
            return ""

        # define variables #
        convNum = UtilMgr.convNum
        printStr = ""

        # print readahead stat #
        printStr += (
            "\n[Thread Readahead Info] (NrFiles: %s) (NrCalls: %s)\n%s\n"
            % (convNum(len(raSummary)), convNum(len(raList)), twoLine)
        )
        printStr += "{0:>12} {1:>12} {2:>1}\n{3:1}\n".format(
            "Size", "Count", "Path", twoLine
        )

        for fname, stat in sorted(
            raSummary.items(), key=lambda e: e[1]["size"], reverse=True
        ):
            printStr += "{0:>12} {1:>12} {2:>1}\n".format(
                UtilMgr.convSize2Unit(stat["size"]),
                convNum(stat["count"]),
                fname,
            )

        if not raSummary:
            printStr += "    None\n"

        printStr += "%s\n" % oneLine

        return printStr

    @staticmethod
    def makeReadaheadFile(
        raPath, readaheadList, pathConvList, raMin, raAddList
    ):
        # check readahead file path #
        if not raPath:
            return readaheadList, {}

        # apply add list #
        for item in raAddList:
            # parse file info #
            finfo = item.split(":")
            try:
                item = finfo[0]
                offset = size = 0

                if len(finfo) == 1:
                    pass
                elif len(finfo) == 2:
                    if finfo[1]:
                        size = UtilMgr.convUnit2Size(finfo[1])
                else:
                    offset = UtilMgr.convUnit2Size(finfo[1])
                    if finfo[2]:
                        size = UtilMgr.convUnit2Size(finfo[2])
            except SystemExit:
                sys.exit(0)
            except:
                fname = SysMgr.environList["RAADDLIST"][0]
                SysMgr.printErr(
                    "failed to apply readahead add list from '%s'" % fname,
                    reason=True,
                )
                sys.exit(-1)

            # get full path #
            fpath = os.path.realpath(item)
            if not os.path.exists(fpath):
                SysMgr.printWarn(
                    "skipped adding '%s' to readahead list because not exists"
                    % fpath,
                    True,
                )
                continue

            # get file size #
            fsize = UtilMgr.getFileSize(fpath, False)
            fsizeStr = UtilMgr.convSize2Unit(fsize)
            finfo = "%s[%s]" % (fpath, fsizeStr)

            # set full size #
            if size == 0:
                size = fsize

            # get path index #
            if finfo in pathConvList:
                idx = pathConvList[finfo][1]
            else:
                idx = len(pathConvList)
                pathConvList.setdefault(finfo, [fpath, idx])

            # add readahead list #
            readaheadList.append([idx, offset, size])

        # backup exist readahead list #
        SysMgr.backupFile(raPath)

        # create a new file for readahead list #
        try:
            raFd = open(raPath, "wb")
            os.chmod(raPath, 0o777)
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printOpenErr(raPath)
            sys.exit(-1)

        # merge readahead chunks #
        if "RAMERGE" in SysMgr.environList:
            SysMgr.printInfo("merge continuous readahead chunks")

            prevChunk = None
            origRaList = readaheadList
            readaheadList = []
            for chunk in origRaList:
                fid, offset, size = chunk
                if not prevChunk:
                    prevChunk = [fid, offset, size]
                    readaheadList.append(prevChunk)
                    continue

                # merge chunks #
                if (
                    fid == prevChunk[0]
                    and offset == prevChunk[1] + prevChunk[2]
                ):
                    prevChunk[2] += size
                    readaheadList[-1] = prevChunk
                    continue

                # add original chunk to list #
                prevChunk = [fid, offset, size]
                readaheadList.append(prevChunk)

        # write readahead list to file #
        try:
            raSummary = {}

            # encode file list #
            fileList = [
                value[0]
                for path, value in sorted(
                    pathConvList.items(), key=lambda e: e[1][1]
                )
            ]

            raDict = {
                "fileList": fileList,
                "readaheadList": [],
            }

            fileStr = "#".join(fileList)
            fileStr = fileStr.encode()

            # write file list size #
            raFd.write(struct.pack("I", len(fileStr)))

            # write file list #
            raFd.write(fileStr)

            # write readahead chunks #
            for chunk in readaheadList:
                # skip chunks lesser than minimum size #
                if raMin > chunk[2]:
                    continue

                fid, offset, size = chunk

                # write chunks #
                if SysMgr.jsonEnable:
                    raDict["readaheadList"].append(chunk)
                else:
                    raFd.write(struct.pack("HQI", fid, offset, size))

                # save readahead stat #
                fname = fileList[fid]
                raSummary.setdefault(fname, dict({"count": 0, "size": 0}))
                raSummary[fname]["count"] += 1
                raSummary[fname]["size"] += size

            if SysMgr.jsonEnable:
                raFd.write(UtilMgr.convDict2Str(raDict).encode())

            # close output file #
            raFd.close()

            # print file size #
            fsize = UtilMgr.getFileSizeStr(raFd.name)

            SysMgr.printInfo(
                "saved the readahead list to '%s'%s successfully"
                % (raFd.name, fsize)
            )
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                "failed to save the readahead list to '%s'" % raFd.name, True
            )

        return readaheadList, raSummary

    @staticmethod
    def getReadaheadItems():
        # get readahead list path #
        if "RALIST" in SysMgr.environList:
            # set list file #
            raPath = SysMgr.environList["RALIST"][0]
            if raPath == "SET":
                raPath = "readahead.list"

            # convert to absolute path #
            raPath = os.path.realpath(raPath)

            # set inode scan flag #
            if not "CONVINODE" in SysMgr.environList:
                SysMgr.addEnvironVar("CONVINODE")
        else:
            raPath = None

        # set minimum size #
        raMin = 0
        if "RAMIN" in SysMgr.environList:
            try:
                raMin = SysMgr.environList["RAMIN"][0]
                raMin = long(raMin)
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr(
                    "failed to set the minimum size to '%s' for readahead chunk"
                    % raMin,
                    True,
                )

        # get readahead allow list path #
        raAllowList = []
        if "RAALLOWLIST" in SysMgr.environList:
            # get list file #
            try:
                fname = os.path.realpath(SysMgr.environList["RAALLOWLIST"][0])

                SysMgr.printInfo(
                    "apply readahead target list from '%s'" % fname
                )

                with open(fname, "r") as fd:
                    raAllowList = fd.readlines()
                    raAllowList = list(map(lambda x: x.rstrip(), raAllowList))
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printOpenErr(fname)
                sys.exit(-1)

        # get readahead deny list path #
        raDenyList = []
        if "RADENYLIST" in SysMgr.environList:
            # get list file #
            try:
                fname = os.path.realpath(SysMgr.environList["RADENYLIST"][0])

                SysMgr.printInfo(
                    "apply readahead exception list from '%s'" % fname
                )

                with open(fname, "r") as fd:
                    raDenyList = fd.readlines()
                    raDenyList = list(map(lambda x: x.rstrip(), raDenyList))
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printOpenErr(fname)
                sys.exit(-1)

        # get readahead add list path #
        raAddList = []
        if "RAADDLIST" in SysMgr.environList:
            # get list file #
            try:
                fname = os.path.realpath(SysMgr.environList["RAADDLIST"][0])

                SysMgr.printInfo("apply readahead add list from '%s'" % fname)

                with open(fname, "r") as fd:
                    raAddList = fd.readlines()
                    raAddList = list(map(lambda x: x.rstrip(), raAddList))
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printOpenErr(fname)
                sys.exit(-1)

        return raPath, raMin, raAllowList, raDenyList, raAddList

    @staticmethod
    def mergeMapLine(string, procMap, onlyExec=False, saveAll=False):
        d = FileAnalyzer.parseMapLine(string)
        if not d:
            return

        # get execution permission #
        if d["perm"][-2] == "-":
            isExec = False
        else:
            isExec = True

        # check execution permission #
        if onlyExec and not isExec:
            return

        # convert file name #
        fileName = d["binName"]
        if fileName.startswith("["):
            fileName = fileName[1:-1]
        if FileAnalyzer.isDeletedFile(fileName):
            fileName = fileName[:-10]

        # set address #
        startAddr = long(d["startAddr"], 16)
        endAddr = long(d["endAddr"], 16)

        newOffset = long(d["offset"], 16)
        newSize = endAddr - startAddr

        # handle discontiguous segments #
        if fileName in procMap and procMap[fileName]["vend"] != startAddr:
            cnt = 0
            while 1:
                newFileName = "%s%s%s" % (fileName, SysMgr.magicStr, cnt)

                # check next segment is contiguous with this line #
                if (
                    newFileName in procMap
                    and procMap[newFileName]["vend"] != startAddr
                ):
                    cnt += 1
                    continue
                else:
                    break

            fileName = newFileName

        # merge map line #
        FileAnalyzer.addMapLine(procMap, fileName, newOffset, newSize)

        # apply attributes #
        if saveAll:
            procMap[fileName]["perm"] = d["perm"]
            procMap[fileName]["devid"] = d["devid"]
            procMap[fileName]["inode"] = d["inode"]
            procMap[fileName]["offset"] = d["offset"]
            try:
                procMap[fileName]["offset"] = long(d["offset"], 16)
            except SystemExit:
                sys.exit(0)
            except:
                procMap[fileName]["offset"] = d["offset"]

        # set mapped addr #
        if procMap[fileName]["vstart"] == 0:
            procMap[fileName]["vstart"] = startAddr

        # set executable flag #
        if "exec" not in procMap[fileName] or not procMap[fileName]["exec"]:
            procMap[fileName]["exec"] = isExec

        procMap[fileName]["vend"] = endAddr

    def printProcUsage(self):
        # define alias #
        convert = UtilMgr.convSize2Unit
        convColor = UtilMgr.convColor
        pageSize = SysMgr.PAGESIZE
        uptime = UtilMgr.convTime(SysMgr.updateUptime())

        # Print process list #
        SysMgr.printPipe(
            (
                "[%s] [Process : %s] [RAM: %s] [Reclaim: %s/%s] "
                "[Uptime: %s] [Keys: Foward/Back/Save/Quit] "
                "[Capture: Ctrl+\\]\n%s"
            )
            % (
                "File Process Info",
                UtilMgr.convNum(len(self.procData)),
                convert(self.profPageCnt * 4 << 10),
                convert(self.pgRclmBg * 4 << 10),
                convert(self.pgRclmFg * 4 << 10),
                uptime,
                twoLine,
            )
        )

        procTitle = "%s(%s)" % ("Thread", "TID")
        SysMgr.printPipe(
            "{0:_^16}({1:_^7})|{2:_^7}|{3:_^120}\n{4:1}".format(
                "Process", "PID", "RAM", procTitle, twoLine
            )
        )

        procInfo = "{0:^16}({0:^7})|{0:7} |".format("")
        threadInfo = " {0:^16}({0:^7})".format("")
        procLength = len(procInfo)
        threadLength = len(threadInfo)
        lineLength = SysMgr.lineLength
        commLen = SysMgr.commLen

        for pid, val in sorted(
            self.procData.items(),
            key=lambda e: long(e[1]["pageCnt"]),
            reverse=True,
        ):
            try:
                rsize = val["pageCnt"] * pageSize
            except:
                SysMgr.printWarn(
                    "failed to get total mapped size for %s" % val["comm"],
                    reason=True,
                )
                continue

            if rsize > 0:
                rsize = convColor(convert(rsize), "YELLOW", 7)

            printMsg = "{0:>16}({1:>7})|{2:>7}|".format(
                val["comm"][:commLen], pid, rsize
            )
            linePos = len(printMsg)

            for tid, threadVal in sorted(val["tids"].items()):
                threadInfo = "{0:>1}({1:>1})".format(
                    threadVal["comm"][:commLen], tid
                )
                threadInfo = "{0:>25}".format(threadInfo)

                linePos += threadLength

                if linePos > lineLength:
                    linePos = procLength + threadLength
                    printMsg += "\n" + (" " * (procLength - 2)) + "|"

                printMsg += threadInfo

            SysMgr.printPipe(printMsg)

        SysMgr.printPipe("%s\n\n" % oneLine)

        # remove invalid files #
        for fileName in list(self.fileData):
            if not FileAnalyzer.isValidFile(fileName):
                if fileName in FileAnalyzer.procFdMapCache:
                    continue
                self.fileData.pop(fileName, None)
            elif self.fileData[fileName]["totalSize"] == 0:
                self.fileData.pop(fileName, None)

    def printUsage(self):
        if not self.procData:
            SysMgr.printErr("no process profiled")
            sys.exit(-1)
        if not self.fileData:
            SysMgr.printErr("no file profiled")
            sys.exit(-1)

        SysMgr.printLogo(big=True)

        # print system information #
        SysMgr.printInfoBuffer()

        # define alias #
        convert = UtilMgr.convSize2Unit
        convColor = UtilMgr.convColor
        convNum = UtilMgr.convNum
        pageSize = SysMgr.PAGESIZE
        uptime = UtilMgr.convTime(SysMgr.updateUptime())

        # print process list #
        self.printProcUsage()

        # get res check function #
        _resChecker = UtilMgr.getSizeFilterFunc("RESFILTER")

        # get pss check function #
        _pssChecker = UtilMgr.getSizeFilterFunc("PSSFILTER")

        # Print file list #
        SysMgr.printPipe(
            (
                "[%s] [File: %s] [RAM: %s] [Reclaim: %s/%s] [Uptime: %s]"
                " [Keys: Foward/Back/Save/Quit]\n%s"
            )
            % (
                "File Usage Info",
                UtilMgr.convNum(len(self.fileData)),
                convert(self.profPageCnt * 4 << 10),
                convert(self.pgRclmBg * 4 << 10),
                convert(self.pgRclmFg * 4 << 10),
                uptime,
                twoLine,
            )
        )
        SysMgr.printPipe(
            "{0:_^8}|{1:_^8}|{2:_^5}|{3:_^8}|{4:_^121}\n{5:1}".format(
                "RAM", "File", "%", "PSS", "Library & Process", twoLine
            )
        )

        # set sort value #
        if SysMgr.sort == "s":
            sortOpt = "totalSize"
        elif SysMgr.sort == "p":
            for path in self.fileData:
                self.fileData[path]["path"] = path
            sortOpt = "path"
        else:
            sortOpt = "pageCnt"

        for fileName, val in sorted(
            self.fileData.items(),
            key=lambda e: e[1][sortOpt]
            if isinstance(e[1][sortOpt], str)
            else long(e[1][sortOpt]),
            reverse=True,
        ):
            # get stat #
            memSize = val["pageCnt"] * pageSize
            nrFilePage = long((val["totalSize"] + pageSize - 1) / pageSize)
            fileSize = nrFilePage * pageSize
            if fileSize != 0:
                per = long((long(memSize) / float(fileSize)) * 100)
                per = UtilMgr.convCpuColor(per, size=4)
            else:
                per = 0

            try:
                pss = long(memSize / len(val["pids"]))
                if not _pssChecker(pss):
                    continue
                elif pss > 0:
                    pss = convColor(convert(pss), "GREEN", 7)
            except:
                pss = 0
                if not _pssChecker(pss):
                    continue

            # check resident size #
            if not _resChecker(memSize):
                continue

            if memSize > 0:
                memSize = convColor(convert(memSize), "YELLOW", 7)

            if not val["isRep"]:
                continue
            else:
                if val["nrMap"] - 1 > 0:
                    cntStr = " [Map: %s]" % convNum(val["nrMap"] - 1)
                else:
                    cntStr = ""

                if val["nrOpen"] > 0:
                    cntStr += " [Open: %s]" % convNum(val["nrOpen"])
                else:
                    cntStr += ""

                SysMgr.printPipe(
                    (
                        "{0:>7} |{1:>7} |{2:>4} |{3:>7} | {4:1} "
                        "[Proc: {5:1}] [Link: {6:1}]{7:1}"
                    ).format(
                        memSize,
                        convert(fileSize),
                        per,
                        pss,
                        fileName,
                        len(val["pids"]),
                        convNum(val["hardLink"]),
                        cntStr,
                    )
                )

            # prepare for printing process list #
            pidInfo = ""
            lineLength = SysMgr.lineLength
            pidLength = len(" %16s (%6s) |" % ("", ""))
            ilength = len("{0:7} |{0:7} |{0:4} |{0:7} ".format(""))
            linePos = ilength + pidLength

            # print hard-linked list #
            if val["hardLink"] > 1:
                for fileLink, tmpVal in val["linkList"].items():
                    if fileName != fileLink:
                        SysMgr.printPipe((" " * ilength) + "| -> " + fileLink)

            # print process list #
            if not "NOTASKINFO" in SysMgr.environList:
                for pid, comm in val["pids"].items():
                    if linePos > lineLength:
                        linePos = ilength + pidLength
                        pidInfo += "\n" + (" " * ilength) + "|"

                    pidInfo += " %16s (%7s) |" % (comm[: SysMgr.commLen], pid)

                    linePos += pidLength
                SysMgr.printPipe((" " * ilength) + "|" + pidInfo)

            SysMgr.printPipe(oneLine)

        if self.readaheadStr:
            SysMgr.printPipe(self.readaheadStr)

        SysMgr.printPipe("\n\n\n")

    def scanProcs(self, targetFilter=None, targetFile=None, exceptFilter=None):
        # get process list in procfs #
        pids = SysMgr.getPidList()

        # initialize proc object #
        procObj = TaskAnalyzer(onlyInstance=True)

        # get size check function #
        _sizeChecker = UtilMgr.getSizeFilterFunc()

        # scan tasks #
        for idx, pid in enumerate(pids):
            try:
                nrPid = long(pid)

                # skip myself #
                if SysMgr.pid == nrPid and targetFile == [""]:
                    continue
            except SystemExit:
                sys.exit(0)
            except:
                continue

            mappedList = []

            # print progress #
            UtilMgr.printProgress(idx, len(pids))

            # make process path #
            procPath = "%s/%s" % (SysMgr.procPath, pid)

            # save stat of process #
            try:
                procObj.saveProcData(procPath, pid)
                pidComm = procObj.procData[pid]["comm"]
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printOpenWarn(procPath)
                continue

            # skip kernel tasks #
            if procObj.isKernelThread(pid):
                continue

            # make path of tid #
            taskPath = "%s/%s" % (procPath, "task")

            try:
                tids = os.listdir(taskPath)
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printOpenWarn(taskPath)
                continue

            # scan threads #
            for tid in tids:
                try:
                    long(tid)
                except:
                    continue

                # make thread path #
                threadPath = "%s/%s" % (taskPath, tid)

                # save stat of thread #
                try:
                    procObj.saveProcData(threadPath, tid)
                    comm = procObj.procData[tid]["comm"]
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printOpenWarn(threadPath)
                    continue

                # check condition #
                if (
                    self.target != [""]
                    and not tid in self.target
                    and not UtilMgr.isValidStr(comm, self.target)
                ):
                    continue

                # update procData #
                if not pid in self.procData:
                    self.procData[pid] = dict(self.init_procData)
                    self.procData[pid]["tids"] = {}
                    self.procData[pid]["procMap"] = {}
                    self.procData[pid]["comm"] = pidComm

                    # update mapInfo per process #
                    self.procData[pid][
                        "procMap"
                    ] = FileAnalyzer.getProcMapInfo(pid)

                    # save file info per process #
                    try:
                        fdlist = []
                        fdlistPath = "%s/fd" % procPath
                        fdlist = os.listdir(fdlistPath)
                    except SystemExit:
                        sys.exit(0)
                    except:
                        SysMgr.printOpenWarn(fdlistPath)

                    if SysMgr.pid == nrPid:
                        SysMgr.printStat(
                            "start collecting custom files on memory..."
                        )
                        isMe = True
                    else:
                        isMe = False

                    # scan file descriptors #
                    for gidx, fd in enumerate(fdlist):
                        try:
                            size = 0

                            # print progress #
                            if isMe:
                                UtilMgr.printProgress(gidx, len(fdlist))

                            # get real path #
                            fdPath = "%s/%s" % (fdlistPath, long(fd))
                            fname = os.readlink(fdPath)

                            # check files #
                            if not FileAnalyzer.isValidFile(
                                fname, incDel=True
                            ):
                                continue
                            elif (
                                tid != pid
                                and fname in self.procData[pid]["procMap"]
                            ):
                                continue
                            elif targetFile != [""] and not UtilMgr.isValidStr(
                                fname, targetFile
                            ):
                                continue
                            elif targetFilter and not UtilMgr.isValidStr(
                                fname, targetFilter
                            ):
                                continue
                            elif exceptFilter and UtilMgr.isValidStr(
                                fname, exceptFilter
                            ):
                                continue

                            # init file info #
                            if not size:
                                size = UtilMgr.getFileSize(fname, False)
                            if size == 0 or not _sizeChecker(size):
                                if FileAnalyzer.isDeletedFile(fname):
                                    if not mappedList:
                                        mappedList = (
                                            FileAnalyzer.getMappedList(
                                                pid, fname
                                            )
                                        )
                                    if mappedList:
                                        size = UtilMgr.getFileSize(
                                            mappedList[0], False
                                        )
                                if size == 0:
                                    continue

                            procMap = self.procData[pid]["procMap"]
                            procMap[fname] = dict(FileAnalyzer.init_mapData)
                            procMap[fname]["size"] = size
                            procMap[fname]["nrOpen"] = 1
                            if mappedList:
                                FileAnalyzer.procFdMapCache[
                                    fname
                                ] = mappedList[0]
                        except SystemExit:
                            sys.exit(0)
                        except:
                            pass

                # print progress #
                if isMe:
                    UtilMgr.deleteProgress()

                # check thread list #
                if tid in self.procData[pid]["tids"]:
                    continue

                # update threadData #
                threadList = SysMgr.getThreadList(pid)
                for ttid in threadList:
                    if ttid in self.procData[pid]["tids"]:
                        continue

                    self.procData[pid]["tids"][ttid] = dict(
                        self.init_threadData
                    )

                    tcomm = SysMgr.getComm(ttid)
                    self.procData[pid]["tids"][ttid]["comm"] = tcomm

        # remove progress #
        UtilMgr.deleteProgress()

    def fillFileMaps(self):
        self.profPageCnt = 0

        for fileName, val in self.fileData.items():
            if val["fileMap"] and val["isRep"]:
                val["pageCnt"] = val["fileMap"].count(1)
                self.profPageCnt += val["pageCnt"]

        pageSize = SysMgr.PAGESIZE
        for pid, val in self.procData.items():
            for fileName, mapInfo in val["procMap"].items():
                if not fileName in self.fileData:
                    continue
                elif not self.fileData[fileName]["fileMap"] or not mapInfo:
                    continue

                # convert address and size to index in mapping table #
                offset = mapInfo["offset"] - self.fileData[fileName]["offset"]
                offset = long((offset + pageSize - 1) / pageSize)
                size = long((mapInfo["size"] + pageSize - 1) / pageSize)

                mapInfo["fileMap"] = list(
                    self.fileData[fileName]["fileMap"][offset:size]
                )
                mapInfo["pageCnt"] = mapInfo["fileMap"].count(1)
                val["pageCnt"] += mapInfo["pageCnt"]

    def mergeFileMapInfo(
        self,
        targetFilter=None,
        targetFile=None,
        exceptFilter=None,
        onlyTarget=False,
    ):
        SysMgr.printStat("start merging file info...")

        # get size check function #
        _sizeChecker = UtilMgr.getSizeFilterFunc()

        pos = 0
        myPid = str(SysMgr.pid)
        for pid, val in self.procData.items():
            for fileName, scope in val["procMap"].items():
                UtilMgr.printProgress(pos, len(self.procData))

                # check file filter #
                if (
                    targetFile != [""]
                    and onlyTarget
                    and not UtilMgr.isValidStr(fileName, targetFile)
                ):
                    continue
                elif targetFilter and not UtilMgr.isValidStr(
                    fileName, targetFilter
                ):
                    continue
                elif exceptFilter and UtilMgr.isValidStr(
                    fileName, exceptFilter
                ):
                    continue
                elif not _sizeChecker(scope["size"]):
                    continue

                newOffset = scope["offset"]
                newSize = scope["size"]

                # merge map line #
                FileAnalyzer.addMapLine(
                    self.fileData, fileName, newOffset, newSize
                )

                # add map count #
                if scope["nrMap"]:
                    self.fileData[fileName]["nrMap"] += scope["nrMap"]

                # add open count #
                if scope["nrOpen"]:
                    self.fileData[fileName]["nrOpen"] += scope["nrOpen"]

                # add pid into file info #
                if not self.fileData[fileName]["pids"]:
                    self.fileData[fileName]["pids"] = {}
                if not pid in self.fileData[fileName]["pids"] and pid != myPid:
                    self.fileData[fileName]["pids"][pid] = val["comm"]

        UtilMgr.deleteProgress()

    def getFilePageMaps(self):
        # pylint: disable=no-member

        pageSize = SysMgr.PAGESIZE
        self.profSuccessCnt = 0
        self.profFailedCnt = 0

        # get ctypes object #
        if not SysMgr.guiderObj:
            SysMgr.importPkgItems("ctypes")

        # define readahead list #
        pathConvList = {}
        readaheadList = []
        skipFiles = {}

        # get readahead items #
        (
            raPath,
            raMin,
            raAllowList,
            raDenyList,
            raAddList,
        ) = FileAnalyzer.getReadaheadItems()

        for fileName, val in self.fileData.items():
            # check exceptional file #
            if not FileAnalyzer.isValidFile(fileName, incDel=True):
                continue

            if self.intervalFileData:
                # use file descriptor already saved as possible #
                try:
                    fileData = self.intervalFileData
                    fileInfo = fileData[len(self.intervalFileData) - 1][
                        fileName
                    ]

                    val["fd"] = fileInfo["fd"]

                    val["totalSize"] = fileInfo["totalSize"]

                    val["isRep"] = fileInfo["isRep"]
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

                if not val["isRep"]:
                    continue

            if not val["fd"]:
                """
                no fd related to this file
                case 1) no opened
                case 2) closed by mincore error
                case 3) closed because of rlimit
                """

                try:
                    if fileName in FileAnalyzer.procFdMapCache:
                        fileNameReal = FileAnalyzer.procFdMapCache[fileName]
                    else:
                        fileNameReal = fileName

                    # check whether pages are on memory or not #
                    stat = os.stat(fileNameReal)

                    devid = stat.st_dev
                    inode = stat.st_ino

                    # check whether this file was profiled or not #
                    if inode in self.inodeData:
                        found = False
                        repFile = ""
                        fileList = {}
                        procList = dict(val["pids"].items())

                        for fileIdx, devid in self.inodeData[inode].items():
                            # this hard-lined file was already profiled #
                            if devid != devid:
                                continue

                            found = True

                            # add file into same file list #
                            fileList[fileName] = True
                            fileList[fileIdx] = True

                            # merge process list related to this file #
                            procList.update(self.fileData[fileIdx]["pids"])

                            if self.fileData[fileIdx]["isRep"]:
                                repFile = fileIdx

                        self.inodeData[inode][fileName] = devid

                        if found:
                            self.fileData[fileName]["isRep"] = False
                            hardLinkCnt = len(fileList)

                            # set representative file #
                            for fileIdx, value in fileList.items():
                                self.fileData[fileIdx]["repFile"] = repFile
                                self.fileData[fileIdx][
                                    "hardLink"
                                ] = hardLinkCnt

                            # assign merged process list to representative file #
                            self.fileData[repFile]["pids"] = procList
                            self.fileData[repFile]["hardLink"] = hardLinkCnt

                            if self.fileData[repFile]["linkList"]:
                                self.fileData[repFile]["linkList"].update(
                                    fileList
                                )
                            else:
                                self.fileData[repFile]["linkList"] = fileList

                            continue
                    else:
                        self.inodeData[inode] = dict(self.init_inodeData)
                        self.inodeData[inode][fileName] = devid

                    # get meta data #
                    size = stat.st_size
                    linkCnt = stat.st_nlink
                    time = stat.st_atime

                    # update meta data #
                    val["inode"] = inode
                    val["totalSize"] = size
                    val["linkCnt"] = linkCnt
                    val["accessTime"] = time

                    # open file #
                    val["fd"] = open(fileNameReal, "r")
                except SystemExit:
                    sys.exit(0)
                except:
                    self.profFailedCnt += 1
                    if SysMgr.warnEnable:
                        SysMgr.printOpenWarn(fileName)
                    continue

            # check file size whether it is readable or not #
            if val["totalSize"] <= 0:
                self.profFailedCnt += 1
                if SysMgr.warnEnable:
                    SysMgr.printWarn("failed to mmap %s" % fileName)
                continue

            # prepare variables for mincore syscall #
            fd = val["fd"].fileno()
            offset = val["offset"]
            size = val["totalSize"]

            # get page-aligned size #
            tsize = long((size + pageSize - 1) / pageSize)
            size = tsize * pageSize

            if SysMgr.guiderObj:
                # map a file to ram with PROT_NONE(0), MAP_SHARED(0x10) flags #
                mm = SysMgr.guiderObj.mmap(0, size, 0, 2, fd, offset)

                # call mincore syscall by standard libc library #
                pagemap = SysMgr.guiderObj.mincore(mm, size)

                # unmap #
                SysMgr.guiderObj.munmap(mm, size)
            else:
                # map a file to ram with PROT_NONE(0), MAP_SHARED(0x10) flags #
                mm = SysMgr.mmap(POINTER(c_char)(), size, 0, 2, fd, offset)

                # make a pagemap table #
                pagemap = (tsize * c_ubyte)()

                # call mincore syscall by standard libc library #
                ret = SysMgr.libcObj.mincore(
                    mm, size, cast(pagemap, POINTER(c_ubyte))
                )
                if ret < 0:
                    pagemap = None

                # unmap #
                SysMgr.libcObj.munmap(mm, size)

            # save the on-memory file page table #
            if pagemap:
                try:
                    if SysMgr.guiderObj:
                        val["fileMap"] = [
                            ord(pagemap[i]) for i in xrange(tsize)
                        ]
                    else:
                        val["fileMap"] = [pagemap[i] for i in xrange(tsize)]

                    self.profSuccessCnt += 1

                    # fd resource is about to run out #
                    if SysMgr.maxKeepFd < fd:
                        val["fd"].close()
                        val["fd"] = None
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printWarn("failed to access '%s'" % fileName)
                    val["fileMap"] = None
                    self.profFailedCnt += 1

                # add filemap info to readahead list #
                if raPath:
                    # check allow list #
                    if raAllowList and not UtilMgr.isValidStr(
                        fileName, raAllowList
                    ):
                        skip = True
                    # check deny list #
                    elif raDenyList and UtilMgr.isValidStr(
                        fileName, raDenyList
                    ):
                        skip = True
                    else:
                        skip = False

                    # check skip condition #
                    if skip:
                        skipFiles.setdefault(fileName, None)
                        continue

                    fileIdx = len(pathConvList)
                    pathConvList.setdefault(fileName, [fileName, fileIdx])

                    start = -1
                    for idx, bit in enumerate(pagemap):
                        if start == -1:
                            if bit:
                                start = idx
                            else:
                                continue

                        # check contiguous chunk #
                        if bit and idx != len(pagemap) - 1:
                            continue

                        # add contiguous chunks to readahead list #
                        offset = start * pageSize
                        size = (idx - start + 1) * pageSize
                        readaheadList.append([fileIdx, offset, size])

                        start = -1
            else:
                val["fd"].close()
                val["fd"] = None

        if not self.fileData:
            SysMgr.printErr("failed to profile files")
            sys.exit(-1)

        SysMgr.printGood(
            "profiled a total of %s files"
            % UtilMgr.convNum(self.profSuccessCnt)
        )

        if self.profFailedCnt > 0:
            SysMgr.printWarn(
                "failed to open a total of %s files"
                % UtilMgr.convNum(self.profFailedCnt)
            )

        # print skip files #
        for path in sorted(list(skipFiles)):
            SysMgr.printWarn("skipped adding '%s' to readahead list" % path)

        # make readahead list file #
        if raPath:
            readaheadList, raSummary = FileAnalyzer.makeReadaheadFile(
                raPath, readaheadList, pathConvList, raMin, raAddList
            )

            # print readahead list info #
            self.readaheadStr = FileAnalyzer.getReadaheadListStr(
                readaheadList, raSummary
            )


class LogMgr(object):
    """Manager for error log"""

    # define log level #
    LOG_EMERG = 0
    LOG_ALERT = 1
    LOG_CRIT = 2
    LOG_ERR = 3
    LOG_WARNING = 4
    LOG_NOTICE = 5
    LOG_INFO = 6
    LOG_DEBUG = 7

    # define syslog type #
    SYSLOG_ACTION_CLOSE = 0
    SYSLOG_ACTION_OPEN = 1
    SYSLOG_ACTION_READ = 2
    SYSLOG_ACTION_READ_ALL = 3
    SYSLOG_ACTION_READ_CLEAR = 4
    SYSLOG_ACTION_CLEAR = 5
    SYSLOG_ACTION_CONSOLE_OFF = 6
    SYSLOG_ACTION_CONSOLE_ON = 7
    SYSLOG_ACTION_CONSOLE_LEVEL = 8
    SYSLOG_ACTION_SIZE_UNREAD = 9
    SYSLOG_ACTION_SIZE_BUFFER = 10

    # define journal type #
    SD_JOURNAL_LOCAL_ONLY = 1 << 0
    SD_JOURNAL_RUNTIME_ONLY = 1 << 1
    SD_JOURNAL_SYSTEM = 1 << 2
    SD_JOURNAL_CURRENT_USER = 1 << 3
    SD_JOURNAL_OS_ROOT = 1 << 4
    SD_JOURNAL_SYSTEM_ONLY = SD_JOURNAL_SYSTEM

    # define android type #
    ANDROID_LOG_UNKNOWN = 0
    ANDROID_LOG_DEFAULT = 1
    ANDROID_LOG_VERBOSE = 2
    ANDROID_LOG_DEBUG = 3
    ANDROID_LOG_INFO = 4
    ANDROID_LOG_WARN = 5
    ANDROID_LOG_ERROR = 6
    ANDROID_LOG_FATAL = 7
    ANDROID_LOG_SILENT = 8

    lockStat = {}

    def __init__(self, target="error"):
        self.errFd = None

        if target == "error":
            self.terminal = sys.stderr
            self.notified = False
            self.error = False
        else:
            # open #
            try:
                self.terminal = open(target, "a")
                self.error = True
                os.chmod(target, 0o777)
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr("failed to open '%s'" % target, True)

    def write(self, message):
        # write message #
        try:
            self.terminal.write(message)
            self.terminal.flush()
        except SystemExit:
            sys.exit(0)
        except:
            return

        # check error status #
        try:
            if self.error:
                return
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # TODO: check log file limitation #

        # check cache dir #
        if not os.path.exists(SysMgr.cacheDirPath):
            if not SysMgr.mkdirs(SysMgr.cacheDirPath):
                SysMgr.cacheDirPath = "/tmp"
                SysMgr.printWarn("use /tmp as a cache directory", True)

        # set file path for error log #
        if not self.errFd:
            errorFile = "%s/guider.err" % SysMgr.cacheDirPath
            if not SysMgr.isWritable(errorFile):
                SysMgr.printWarn(
                    (
                        "failed to get write permission for %s "
                        "so that use /tmp/guider.err"
                    )
                    % errorFile,
                    True,
                )
                SysMgr.cacheDirPath = "/tmp"
                errorFile = "%s/guider.err" % SysMgr.cacheDirPath

            # open log file #
            try:
                self.errFd = open(errorFile, "a")
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printOpenErr(errorFile)
                self.error = True

            # change permission #
            try:
                os.chmod(errorFile, 0o777)
            except SystemExit:
                sys.exit(0)
            except:
                pass
        try:
            if hasattr(self, "notified") and not self.notified:
                SysMgr.printErr(
                    (
                        "please report '%s' to "
                        "https://github.com/iipeace/guider/issues"
                    )
                    % self.errFd.name
                    if self.errFd
                    else "error"
                )
                self.notified = True

            # write log to the file #
            if self.errFd:
                SysMgr.writeErr(self.errFd, message)
        except SystemExit:
            sys.exit(0)
        except:
            pass

    def flush(self):
        pass

    def __getattr__(self, attr):
        return getattr(self.terminal, attr)

    @staticmethod
    def _lock(fd, lock=True):
        # pylint: disable=undefined-variable
        if not SysMgr.isLinux:
            return

        if lock:
            lname = "lock"
        else:
            lname = "unlock"

        if fd in LogMgr.lockStat:
            print(
                UtilMgr.convColor(
                    ("\n[ERROR] tried to %s %s again" % (lname, fd)), "RED"
                )
            )
            return

        # save lock status #
        LogMgr.lockStat[fd] = True

        try:
            if SysMgr.importPkgItems("fcntl", isExit=False):
                lockf(fd, LOCK_EX if lock else LOCK_UN, 1, 0, 0)
        except SystemExit:
            sys.exit(0)
        except:
            name = fd.name if fd else "logger"
            reason = SysMgr.getErrMsg()
            print(
                "\n[ERROR] failed to %s for %s because %s"
                % (lname, name, reason)
            )

        # remove lock status #
        LogMgr.lockStat.pop(fd, None)

    @staticmethod
    def lock(fd):
        return LogMgr._lock(fd, lock=True)

    @staticmethod
    def unlock(fd):
        return LogMgr._lock(fd, lock=False)

    @staticmethod
    def printTrace(console=False):
        fileSize = 0
        compressed = False

        # get target trace points #
        if SysMgr.hasMainArg():
            targets = SysMgr.getMainArgs()
        else:
            targets = []

        # print trace point list #
        if SysMgr.findOption("l"):
            if not SysMgr.findOption("Q"):
                SysMgr.streamEnable = False
            SysMgr.printDirs(
                os.path.join(
                    SysMgr.getTraceEventPath(), targets[0] if targets else ""
                ),
                SysMgr.funcDepth if SysMgr.funcDepth > 0 else 1,
            )
            sys.exit(0)

        # flush the ring buffer #
        if "FLUSH" in SysMgr.environList:
            SysMgr.clearTraceBuffer()

        # enable events #
        SysMgr.enableEvents(targets)

        # disable events #
        SysMgr.disableEvents()

        # clear filters #
        SysMgr.clearFilters()

        # set trace option #
        SysMgr.setTraceOpt()

        if SysMgr.inputParam:
            # open the target file #
            try:
                path = SysMgr.inputParam

                # open compressed file #
                traceFd = SysMgr.getCompFd(path)
                if traceFd:
                    compressed = True
                else:
                    traceFd = open(path, "r")

                    # get file size #
                    fileSize = UtilMgr.getFileSize(path, string=False)
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printOpenErr(path)
                return -1
        else:
            # check root permission #
            SysMgr.checkRootPerm()

            # open trace file #
            try:
                eventPath = SysMgr.getTraceEventPath()
                tracePath = "%s/../trace" % eventPath
                origStatus = None

                # change trace file to pipe #
                if "CONSUME" in SysMgr.environList:
                    tracePath += "_pipe"
                # save tracing status #
                else:
                    origStatus = SysMgr.readTraceFile("../tracing_on")

                # set trace fd #
                traceFd = open(tracePath, "r")

                # check tracing status change #
                if origStatus == "1":
                    newStatus = SysMgr.readTraceFile("../tracing_on")
                    if newStatus == "0":
                        SysMgr.printWarn(
                            "tracing is stopped because '%s' is open"
                            % tracePath,
                            True,
                        )
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printOpenErr(tracePath)
                return -1

        # get item for log filter #
        if "WATCHLOG" in SysMgr.environList:
            watchcond = SysMgr.environList["WATCHLOG"][0].split("+")
            refilter = SysMgr.environList.get("REFILTER")
        else:
            refilter = watchcond = None

        # get times for tail and until option #
        current = SysMgr.getUptime()
        since, until = SysMgr.getTimeValues(["TAIL", "UNTIL"], current)
        if since or until:
            checkTime = True
            if since and since < 0:
                since += current
            if until and until < 0:
                until += until
        else:
            checkTime = False

        # get INCPROCNAME flag #
        incProcName = "INCPROCNAME" in SysMgr.environList

        SysMgr.printInfo("start printing trace logs... [ STOP(Ctrl+c) ]")

        initialized = False

        while 1:
            log = traceFd.readline()

            # decode and remove newlines #
            if compressed:
                log = log.decode()
            log = log.rstrip()

            if not initialized:
                initialized = True if TaskAnalyzer.setTraceAttr(log) else False

            # check log #
            if not log:
                if SysMgr.inputParam:
                    if traceFd.tell() == fileSize:
                        break
                else:
                    if console:
                        break
                    else:
                        time.sleep(0.1)
                continue
            elif log[0] == "#" or not UtilMgr.isValidStr(log, inc=True):
                continue

            # parse log #
            m = SysMgr.getTraceItem(log)
            if not m:
                continue

            if checkTime:
                d = m.groupdict()
                try:
                    ltime = float(d["time"])

                    if since:
                        if since > ltime:
                            continue
                        else:
                            since = 0

                    if until:
                        if until < ltime:
                            continue
                        else:
                            break

                    if since == until == 0:
                        checkTime = False
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

            if SysMgr.jsonEnable:
                # pick each key and value set #
                d = m.groupdict()
                addAttrs = re.findall("(\w+)=(.+?(?=\s\w+=|\Z))", d["etc"])
                for name, val in addAttrs:
                    d[name] = val.rstrip()

                # set process name #
                if incProcName:
                    tgid = d.get("tgid")
                    if tgid:
                        if tgid == d["thread"]:
                            tgcomm = d["comm"]
                        else:
                            tgcomm = SysMgr.getComm(d["tgid"])
                        d["tgcomm"] = tgcomm

                log = UtilMgr.convDict2Str(d, pretty=not console).rstrip()

            if SysMgr.outPath and console:
                print(log)

            SysMgr.printPipe(log)

            # check log command #
            SysMgr.checkLogCond(log, watchcond, refilter)

    @staticmethod
    def printAndroid(console=False):
        # check logcat #
        logcatPath = UtilMgr.which("logcat")
        if not logcatPath:
            SysMgr.printErr("failed to find logcat")
            sys.exit(-1)

        # create pipe #
        rd, wr = os.pipe()

        # get item for log filter #
        if "WATCHLOG" in SysMgr.environList:
            watchcond = SysMgr.environList["WATCHLOG"][0].split("+")
            refilter = SysMgr.environList.get("REFILTER")
        else:
            refilter = watchcond = None

        SysMgr.printInfo("start printing android logs... [ STOP(Ctrl+c) ]")

        # create a new process #
        pid = SysMgr.createProcess()

        # parent #
        if pid > 0:
            # set pipe #
            os.close(wr)
            rdPipe = os.fdopen(rd, "r")
            SysMgr.setPipeSize(rdPipe)

            # get length of pid #
            pidlen = len(str(SysMgr.maxPid))

            while 1:
                log = rdPipe.readline()

                if not log:
                    if console:
                        break
                    else:
                        time.sleep(0.1)
                elif not UtilMgr.isValidStr(log, inc=True):
                    continue

                try:
                    meta, msg = log.split(": ", 1)
                except SystemExit:
                    sys.exit(0)
                except:
                    continue

                # split items #
                items = meta.split()

                # local time #
                ltime = items[0] + "T" + items[1]

                # others #
                pid, tid, lev = items[2:5]

                if SysMgr.jsonEnable:
                    msgDict = {
                        "pid": pid,
                        "tid": tid,
                        "level": lev,
                    }
                else:
                    msgDict = {}

                # proc #
                pcomm = " "
                if pid != "0":
                    pcomm = SysMgr.getComm(pid, cache=True, save=True)
                    if not pcomm:
                        pcomm = " "

                if SysMgr.jsonEnable:
                    msgDict["pcomm"] = pcomm

                proc = "{0:>16}({1:>{pidlen}})".format(
                    pcomm, pid, pidlen=pidlen
                )

                # thread #
                thread = ""
                if not SysMgr.processEnable:
                    if tid == "0":
                        tcomm = " "
                    else:
                        tcomm = SysMgr.getComm(tid, cache=True, save=True)
                        if not tcomm:
                            tcomm = " "

                    if SysMgr.jsonEnable:
                        msgDict["tcomm"] = tcomm
                    else:
                        thread = "{0:>16}({1:>{pidlen}})".format(
                            tcomm, tid, pidlen=pidlen
                        )
                        proc += "->" + thread

                # remove useless spaces #
                msg = msg.strip()

                if len(items) > 5:
                    msg = items[5] + ": " + msg

                if SysMgr.jsonEnable:
                    msgDict["message"] = msg
                    log = UtilMgr.convDict2Str(msgDict, pretty=not console)
                else:
                    # build log line #
                    log = "%s %s %s %s" % (ltime, proc, lev, msg)

                if SysMgr.outPath and console:
                    print(log)

                SysMgr.printPipe(log)

                # check log command #
                SysMgr.checkLogCond(log, watchcond, refilter)

        # child #
        elif pid == 0:
            # redirect stdout to pipe #
            os.close(rd)
            wrPipe = os.fdopen(wr, "w")
            SysMgr.setPipeSize(wrPipe)
            os.dup2(wr, 1)

            # set SIGPIPE handler for termination of parent #
            SysMgr.setPipeHandler()

            SysMgr.executeProcess(
                cmd=["logcat", "-v", "threadtime"], closeFd=False
            )

            sys.exit(0)

    @staticmethod
    def printSyslog(console=False):
        # check root permission #
        SysMgr.checkRootPerm()

        # open syslog file #
        # TODO: implement reading syslog using syslog syscall #
        try:
            if not SysMgr.syslogFd:
                SysMgr.syslogFd = open(SysMgr.syslogPath, "r")
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printOpenErr(SysMgr.syslogPath)
            sys.exit(-1)

        # set nonblock attribute #
        try:
            if console:
                SysMgr.setBlock(SysMgr.syslogFd, False)
                stream = True
            else:
                SysMgr.setBlock(SysMgr.syslogFd)
                stream = False
        except SystemExit:
            sys.exit(0)
        except:
            pass

        SysMgr.printInfo("start printing syslog... [ STOP(Ctrl+c) ]")

        # set file posiion #
        SysMgr.syslogFd.seek(0)

        # get item for log filter #
        if "WATCHLOG" in SysMgr.environList:
            watchcond = SysMgr.environList["WATCHLOG"][0].split("+")
            refilter = SysMgr.environList.get("REFILTER")
        else:
            refilter = watchcond = None

        while 1:
            log = SysMgr.syslogFd.readline()
            if not log:
                if stream:
                    break
                else:
                    time.sleep(0.1)
            elif not UtilMgr.isValidStr(log, inc=True):
                continue

            if SysMgr.outPath and console:
                print(log)

            SysMgr.printPipe(log, newline=False)

            # check log command #
            SysMgr.checkLogCond(log, watchcond, refilter)

    @staticmethod
    def printJournal(console=False):
        # get ctypes object #
        SysMgr.importPkgItems("ctypes")

        """
        struct sd_journal {
                int toplevel_fd;

                char *path;
                char *prefix;
                char *namespace;

                OrderedHashmap *files;
                IteratedCache *files_cache;
                MMapCache *mmap;

                Location current_location;

                JournalFile *current_file;
                uint64_t current_field;

                Match *level0, *level1, *level2;

                pid_t original_pid;

                int inotify_fd;
                unsigned current_invalidate_counter, last_invalidate_counter;
                usec_t last_process_usec;
                unsigned generation;

                /* Iterating through unique fields and their data values */
                char *unique_field;
                JournalFile *unique_file;
                uint64_t unique_offset;

                /* Iterating through known fields */
                JournalFile *fields_file;
                uint64_t fields_offset;
                uint64_t fields_hash_table_index;
                char *fields_buffer;
                size_t fields_buffer_allocated;

                int flags;

                bool on_network:1;
                bool no_new_files:1;
                bool no_inotify:1;
                bool unique_file_lost:1; /* File we were iterating over got
                                            removed, and there were no more
                                            files, so sd_j_enumerate_unique
                                            will return a value equal to 0. */
                bool fields_file_lost:1;
                bool has_runtime_files:1;
                bool has_persistent_files:1;

                size_t data_threshold;

                Hashmap *directories_by_path;
                Hashmap *directories_by_wd;

                Hashmap *errors;
        };
        """

        errstr = "failed to print journal"

        # load libsystemd library #
        try:
            if not SysMgr.systemdObj:
                SysMgr.systemdObj = SysMgr.loadLib(SysMgr.libsystemdPath)
                if not SysMgr.systemdObj:
                    raise Exception("no %s" % SysMgr.libsystemdPath)

            func = "sd_journal_open"
            if not hasattr(SysMgr.systemdObj, func):
                raise Exception("no %s in %s" % (func, SysMgr.libsystemdPath))
        except:
            SysMgr.printErr(errstr, True)
            sys.exit(-1)

        # define shortcut for object #
        systemdObj = SysMgr.systemdObj

        # open journal #
        jrl = c_void_p(0)
        flag = LogMgr.SD_JOURNAL_LOCAL_ONLY
        res = systemdObj.sd_journal_open(byref(jrl), c_int(flag))
        if res < 0:
            SysMgr.printErr(errstr + " because there is no journal")
            return

        SysMgr.printInfo("start printing journal... [ STOP(Ctrl+c) ]")

        # set head #
        res = systemdObj.sd_journal_seek_head(jrl)
        if res < 0:
            SysMgr.printErr(errstr + " because there is no journal head")
            return

        # get item for log filter #
        if "WATCHLOG" in SysMgr.environList:
            watchcond = SysMgr.environList["WATCHLOG"][0].split("+")
            refilter = SysMgr.environList.get("REFILTER")
        else:
            refilter = watchcond = None

        # get times for tail and until option #
        current = time.time()
        since, until = SysMgr.getTimeValues(["TAIL", "UNTIL"], current)

        # apply relative time #
        if since < 0:
            since += current
        if until < 0:
            until += current

        # initialize variables #
        data = c_void_p(0)
        size = c_size_t(0)
        usec = c_uint64(0)
        timeout = c_uint64(10000)

        # set fields #
        if SysMgr.inputParam:
            fieldList = SysMgr.inputParam.split(",")
        else:
            fieldList = [
                b"_TIME",
                b"_HOSTNAME",
                b"_TRANSPORT",
                b"_COMM",
                b"_PID",
                b"MESSAGE",
            ]

        # move to the end of journal #
        if not SysMgr.showAll:
            systemdObj.sd_journal_seek_tail(jrl)

        # define summary table #
        table = {}

        SysMgr.printPipe("\n")

        # start reading journal in loop #
        try:
            while 1:
                res = systemdObj.sd_journal_next(jrl)
                if res == 0:
                    ret = systemdObj.sd_journal_wait(jrl, timeout)
                    # SD_JOURNAL_NOP / SD_JOURNAL_APPEND / SD_JOURNAL_INVALID #
                    if ret == 0:
                        if console:
                            break
                        else:
                            continue
                    elif ret in (1, 2):
                        continue
                    elif ret < 0:
                        break
                elif res < 1:
                    break

                # check time condition #
                if since or until:
                    ret = systemdObj.sd_journal_get_realtime_usec(
                        jrl, byref(usec)
                    )
                    if ret == 0:
                        realtime = usec.value / 1000000
                        if since and realtime < since:
                            continue
                        elif until and realtime >= until:
                            SysMgr.printWarn(
                                "all previous journal logs are printed", True
                            )
                            sys.exit(0)

                # traverse specific fields #
                if SysMgr.inputParam:
                    res = systemdObj.sd_journal_restart_data(jrl)
                    while 1:
                        res = systemdObj.sd_journal_enumerate_data(
                            jrl, byref(data), byref(size)
                        )
                        if res < 1:
                            break

                        SysMgr.printPipe(
                            cast(data, c_char_p).value.decode("latin-1"),
                            flush=True,
                        )
                    SysMgr.printPipe(flush=True)
                    continue

                # traverse all fields #
                jrlStr = b""
                jrlDict = {}
                for field in fieldList:
                    if field == b"_TIME":
                        # get time #
                        ret = systemdObj.sd_journal_get_realtime_usec(
                            jrl, byref(usec)
                        )
                        if ret < 0:
                            realtime = 0
                        else:
                            realtime = usec.value

                        wtime = time.strftime(
                            "%m %d %H:%M:%S ",
                            time.localtime(realtime / float(1000000)),
                        )
                        """
                        ret = systemdObj.sd_journal_get_monotonic_usec(
                            jrl, byref(usec), boottime)
                        """

                        # set time #
                        if SysMgr.jsonEnable:
                            jrlDict[field.decode().lstrip("_")] = wtime.strip()

                        jrlStr += wtime.encode()

                        continue

                    res = systemdObj.sd_journal_get_data(
                        jrl, field, byref(data), byref(size)
                    )
                    if res < 0:
                        continue

                    val = cast(data, c_char_p).value[len(field) + 1 :]
                    if SysMgr.jsonEnable:
                        pass
                    elif field == b"_COMM":
                        if SysMgr.outPath:
                            val = val.decode("latin-1").rstrip("\x01")
                            table.setdefault(val, 0)
                            table[val] += 1
                    elif field == b"_PID":
                        val = b"[%s]: " % val
                    elif field == b"_TRANSPORT" and val == b"kernel":
                        val += b": "
                    else:
                        val += b" "

                    if SysMgr.jsonEnable:
                        jrlDict[
                            field.decode().lstrip("_")
                        ] = val.decode().strip()

                    if isinstance(val, str):
                        val = val.encode()
                    jrlStr += val

                # print journal #
                if jrlStr:
                    try:
                        decstr = jrlStr.decode("latin-1")

                        # check filter #
                        if not UtilMgr.isValidStr(decstr):
                            continue

                        if SysMgr.jsonEnable:
                            output = UtilMgr.convDict2Str(jrlDict)
                        else:
                            output = decstr

                        if SysMgr.outPath and console:
                            print(output)

                        SysMgr.printPipe(output, flush=True)

                        # check log command #
                        SysMgr.checkLogCond(decstr, watchcond, refilter)
                    except SystemExit:
                        sys.exit(0)
                    except:
                        if SysMgr.jsonEnable:
                            output = jrlDict
                        else:
                            output = jrlStr

                        SysMgr.printPipe(output, flush=True)

            # close journal #
            systemdObj.sd_journal_close(jrl)
        except SystemExit:
            if not table:
                return

            SysMgr.printPipe("\n[Journal Summary]\n%s\n" % twoLine)

            SysMgr.printPipe(
                "{0:>32} {1:>16}\n{2:1}".format("COMM", "COUNT", oneLine)
            )

            total = 0
            for comm, count in sorted(
                table.items(), key=lambda x: x[1], reverse=True
            ):
                SysMgr.printPipe(
                    "{0:>32} {1:>16}".format(comm, UtilMgr.convNum(count))
                )
                total += count

            SysMgr.printPipe(
                "\n{0:>32} {1:>16}\n{2:1}".format(
                    "TOTAL", UtilMgr.convNum(total), oneLine
                )
            )

    @staticmethod
    def getKmsg(line=0):
        try:
            fd = os.open(SysMgr.kmsgPath, os.O_RDONLY | os.O_NONBLOCK)
        except:
            return None

        logs = []
        while 1:
            try:
                data = os.read(fd, SysMgr.PAGESIZE).decode()
                logs.append(data)
            except SystemExit:
                sys.exit(0)
            except:
                break

        # convert logs #
        retList = []
        for log in logs[-line:]:
            # parse log #
            pos = log.find(";")

            meta = log[:pos].split(",")
            if len(meta) > 2:
                nrLevel = long(meta[0])
                try:
                    level = ConfigMgr.LOG_LEVEL[nrLevel]
                except:
                    level = nrLevel

                # time #
                ltime = str(meta[2])
                if len(ltime) < 7:
                    ltime = "0.%s" % ltime
                else:
                    ltime = "%s.%s" % (ltime[:-6], ltime[-6:])

                # name & log #
                log = log[pos + 1 :]
                npos = log.find(":")
                name = log[:npos]
                if log[-1] == "\n":
                    log = log[npos + 1 : -1]
                else:
                    log = log[npos + 1 :]

                retList.append("[%s] (%s) %s: %s" % (ltime, level, name, log))

        return retList[-line:]

    @staticmethod
    def printKmsg(console=False):
        # open kmsg device node #
        try:
            SysMgr.kmsgFd = open(SysMgr.kmsgPath, "r")
            if SysMgr.findOption("Q"):
                SysMgr.setBlock(SysMgr.kmsgFd, False)
        except SystemExit:
            sys.exit(0)
        except:
            # get ctypes object #
            SysMgr.importPkgItems("ctypes")

        SysMgr.printInfo("start printing kernel log... [ STOP(Ctrl+c) ]")

        # get item for log filter #
        if "WATCHLOG" in SysMgr.environList:
            watchcond = SysMgr.environList["WATCHLOG"][0].split("+")
            refilter = SysMgr.environList.get("REFILTER")
        else:
            refilter = watchcond = None

        # get times for tail and until option #
        current = SysMgr.getUptime()
        tail, until = SysMgr.getTimeValues(["TAIL", "UNTIL"], current)

        # apply relative time #
        if tail < 0:
            tail += current
        if until < 0:
            until += current

        # syslog #
        if not SysMgr.kmsgFd:
            # get kernel ring-buffer size #
            size = SysMgr.syscall(
                "syslog", LogMgr.SYSLOG_ACTION_SIZE_BUFFER, 0, 0
            )

            # allocate buffer #
            buf = (c_char * size)()

            ret = SysMgr.syscall(
                "syslog", LogMgr.SYSLOG_ACTION_READ_ALL, buf, size
            )
            if ret > 0:
                logBuf = memoryview(buf).tobytes().decode()
                for line in logBuf.split("\n"):
                    if not UtilMgr.isValidStr(line):
                        continue

                    # convert log level #
                    if line.startswith("<"):
                        try:
                            level, line = line[1:].split(">", 1)

                            # get level number #
                            nrLevel = long(level)
                            try:
                                level = ConfigMgr.LOG_LEVEL[nrLevel]
                            except:
                                level = nrLevel

                            ts, line = line.split("] ", 1)

                            line = "%s] <%s> %s" % (ts, level, line)
                        except SystemExit:
                            sys.exit(0)
                        except:
                            pass

                    # check time condition #
                    if tail or until:
                        try:
                            if not line.startswith("["):
                                raise Exception("no time")

                            ltime = float(line[1:].split("]", 1)[0])

                            if tail and ltime < tail:
                                continue
                            elif until and ltime >= until:
                                SysMgr.printWarn(
                                    "all previous kernel logs are printed",
                                    True,
                                )
                                sys.exit(0)

                        except SystemExit:
                            sys.exit(0)
                        except:
                            pass

                    # print to console #
                    if SysMgr.outPath and console:
                        print(line)

                    # print #
                    SysMgr.printPipe(line)

                    # check log command #
                    SysMgr.checkLogCond(line, watchcond, refilter)

            while 1:
                memset(buf, 0, size)
                ret = SysMgr.syscall(
                    "syslog", LogMgr.SYSLOG_ACTION_READ, buf, size
                )
                if ret < 1:
                    continue

                logBuf = memoryview(buf).tobytes().decode()
                if not UtilMgr.isValidStr(line):
                    continue

                if SysMgr.outPath and console:
                    print(logBuf)

                SysMgr.printPipe(logBuf)

            return

        # kmsg node #
        while 1:
            jsonResult = {}
            log = SysMgr.kmsgFd.readline()
            if not log:
                break

            # apply filter #
            if not UtilMgr.isValidStr(log):
                continue

            # parse log #
            # PRIORITY,SEQUENCE_NUM,TIMESTAMP,-;MESSAGE #
            pos = log.find(";")
            meta = log[:pos].split(",")
            if len(meta) > 2:
                nrLevel = long(meta[0])
                try:
                    level = ConfigMgr.LOG_LEVEL[nrLevel]
                except:
                    level = nrLevel

                # time #
                ltime = str(meta[2])
                if len(ltime) < 7:
                    ltime = "0.%s" % ltime
                else:
                    ltime = "%s.%s" % (ltime[:-6], ltime[-6:])

                # check time condition #
                if tail or until:
                    try:
                        ltime = float(ltime)
                        if tail and ltime < tail:
                            continue
                        elif until and ltime >= until:
                            SysMgr.printWarn(
                                "all previous kernel logs are printed", True
                            )
                            sys.exit(0)

                    except SystemExit:
                        sys.exit(0)
                    except:
                        pass

                # update pos #
                log = log[pos + 1 :]

                # name #
                npos = log.find(":")
                if npos < 0:
                    name = ""
                else:
                    name = log[:npos]

                # log #
                if log[-1] == "\n":
                    log = log[npos + 1 : -1]
                else:
                    log = log[npos + 1 :]

                # update message #
                if name:
                    log = "%s: %s" % (name, log)

                # set message #
                if SysMgr.jsonEnable:
                    jsonResult = dict(
                        uptime=ltime, level=level, name=name, message=log
                    )
                else:
                    if not SysMgr.outPath:
                        level = UtilMgr.convColor(level, "BOLD")
                        name = UtilMgr.convColor(name, "SPECIAL")
                        ltime = UtilMgr.convColor(ltime, "GREEN")
                    log = "[%s] (%s) %s" % (ltime, level, log)
            # skip special info #
            elif tail or until:
                continue

            # print message #
            if SysMgr.jsonEnable:
                if jsonResult:
                    jsonResult = UtilMgr.convDict2Str(
                        jsonResult, pretty=not SysMgr.streamEnable
                    )
                    SysMgr.printPipe(jsonResult)
            else:
                if SysMgr.outPath and console:
                    print(log.rstrip())

                SysMgr.printPipe(log.rstrip())

            # check log command #
            SysMgr.checkLogCond(log, watchcond, refilter)

    @staticmethod
    def doLogTrace(msg=None, level=None):
        # print trace point list #
        if SysMgr.findOption("l"):
            if not SysMgr.findOption("Q"):
                SysMgr.streamEnable = False
            SysMgr.printDirs(
                SysMgr.getTraceEventPath(),
                SysMgr.funcDepth if SysMgr.funcDepth > 0 else 1,
            )
            sys.exit(0)

        # enable events #
        SysMgr.enableEvents()

        # disable events #
        SysMgr.disableEvents()

        # clear filters #
        SysMgr.clearFilters()

        # open trace_marker #
        try:
            if not SysMgr.traceFd:
                tracePath = "%s/../trace_marker" % SysMgr.getTraceEventPath()
                SysMgr.traceFd = open(tracePath, "w")
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printOpenErr(tracePath)
            return -1

        # get trace status #
        traceStat = SysMgr.readTraceFile("../tracing_on")

        # tracing on #
        if SysMgr.forceEnable and traceStat == "0":
            recoverTraceStat = True
            SysMgr.writeTraceCmd("../tracing_on", "1")
        else:
            recoverTraceStat = False

        # print messages #
        try:
            msgList = msg.split("\n")
            for line in msgList:
                SysMgr.traceFd.write(line)
            SysMgr.traceFd.flush()
        except SystemExit:
            sys.exit(0)
        except:
            try:
                if traceStat == "0":
                    SysMgr.printErr(
                        "failed to log trace message because tracing is off"
                    )
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printWarn("failed to get trace status", reason=True)
            return -1
        finally:
            if recoverTraceStat:
                SysMgr.writeTraceCmd("../tracing_on", traceStat)

        return 0

    @staticmethod
    def doLogKmsg(msg=None, level=None):
        # open kmsg device node #
        try:
            if not SysMgr.kmsgFd:
                SysMgr.kmsgFd = open(SysMgr.kmsgPath, "w")
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printOpenErr(SysMgr.kmsgPath)
            return -1

        # print messages #
        try:
            msgList = msg.split("\n")
            for line in msgList:
                SysMgr.kmsgFd.write("guider: %s\n" % line)
            SysMgr.kmsgFd.flush()
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn("failed to log kernel message", reason=True)
            return -1

        return 0

    @staticmethod
    def doLogSyslog(msg=None, level=None):
        if not msg:
            return -1

        # load libc #
        SysMgr.loadLibcObj(exit=True)

        # set default level #
        if level is None:
            level = LogMgr.LOG_NOTICE

        # write message #
        SysMgr.libcObj.syslog(level, msg.encode())

        return 0

    @staticmethod
    def doLogAndroid(msg=None, level=None, tag="guider"):
        # get ctypes object #
        SysMgr.importPkgItems("ctypes")

        if not msg:
            return -1

        # set default priority #
        if level is None:
            level = LogMgr.ANDROID_LOG_INFO

        # load liblog library #
        try:
            if not SysMgr.andlogObj:
                SysMgr.andlogObj = SysMgr.loadLib(SysMgr.liblogPath)
                if not SysMgr.andlogObj:
                    raise Exception("no %s" % SysMgr.liblogPath)

                func = "__android_log_write"
                if not hasattr(SysMgr.andlogObj, func):
                    SysMgr.andlogObj = None
                    raise Exception("no %s in %s" % (func, SysMgr.liblogPath))

                # prototype #
                SysMgr.andlogFunc = getattr(SysMgr.andlogObj, func)
                SysMgr.andlogFunc.argtypes = [c_int, c_char_p, c_char_p]
                SysMgr.andlogFunc.restype = c_int
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to log android message", True)
            return -1

        # print message #
        # pylint: disable=not-callable
        return SysMgr.andlogFunc(level, tag.encode(), msg.encode())

    @staticmethod
    def doLogJournal(msg=None, level=None):
        # get ctypes object #
        SysMgr.importPkgItems("ctypes")

        if not msg:
            return -1

        # set default level #
        if level is None:
            level = LogMgr.LOG_NOTICE

        # load libsystemd library #
        try:
            if not SysMgr.systemdObj:
                SysMgr.systemdObj = SysMgr.loadLib(SysMgr.libsystemdPath)
                if not SysMgr.systemdObj:
                    raise Exception("no %s" % SysMgr.libsystemdPath)

                func = "sd_journal_print"
                if not hasattr(SysMgr.systemdObj, func):
                    SysMgr.systemdObj = None
                    raise Exception(
                        "no %s in %s" % (func, SysMgr.libsystemdPath)
                    )
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to log journal message", True)
            return -1

        # print message #
        return SysMgr.systemdObj.sd_journal_print(level, msg.encode())


class SysMgr(object):
    """Manager for system"""

    arch = None
    origArgs = []
    kernelVersion = None
    platform = ""
    isLinux = True
    isDarwin = False
    isWindows = False
    isAndroid = False
    drawMode = False
    archOption = None

    # page size #
    try:
        PAGESIZE = os.sysconf("SC_PAGE_SIZE")
    except:
        PAGESIZE = 4096

    # define many-core number #
    NRMANYCORE = 8

    # set tick #
    try:
        HZ = 250  # 4ms tick #
        if isLinux:
            TICK = os.sysconf(os.sysconf_names["SC_CLK_TCK"])
        else:
            TICK = long((1 / float(HZ)) * 1000)
    except:
        TICK = long((1 / float(HZ)) * 1000)

    # python call function #
    if sys.version_info < (3, 6):
        pyCallFunc = "PyEval_EvalFrameEx"
    elif sys.version_info < (3, 11):
        pyCallFunc = "_PyEval_EvalFrameDefault"
    else:
        # TODO: change return function #
        pyCallFunc = "_PyThreadState_PopFrame"

    startInitTime = 0  # init time for Guider #
    startTime = 0  # start time for Guider #
    startRecTime = 0  # start time for Recording #
    startRunTime = 0  # start time for Process #
    startOverheadTime = 0  # start overhead time for Process #

    blockSize = 512
    bufferSize = -1
    termGetId = None
    termSetId = None
    ttyRows = 43
    ttyRowsMargin = 2
    ttyCols = 156
    ttyData = None
    encoding = None
    remoteRun = False
    magicStr = "@@@@@"
    launchBuffer = ""
    lineLength = 154
    pid = 0
    comm = __module__
    commLen = 16
    masterPid = 0
    parentPid = 0
    prio = None
    ioprio = None
    funcDepth = 0
    maxPid = 32768
    maxRdCnt = 1024
    maxSize = 2147483647
    pidDigit = 5
    stdlog = None
    stderr = sys.stderr
    packetSize = 32767
    defaultCliPort = 5554
    defaultServPort = 5555
    bgProcList = None
    waitDelay = 0.5
    repeatCnt = 0
    progressCnt = 0
    skipCnt = 0
    termCnt = 0
    repeatInterval = 0
    maxIntervalTick = 0
    ipAddr = None
    swappiness = 0
    vmpressure = 0
    overcommit = 0
    limitRepDirSize = 0
    nrRun = 0
    nrReport = 0
    nrProcMemReport = 6

    # watermark constants #
    cpuPerHighThr = 80
    cpuPerLowThr = 10
    memAvailPerThr = 90
    memHighThr = 1024
    memLowThr = 100
    swapPerThr = 90
    diskPerHighThr = 90
    fdHighThr = 8192

    # print condition #
    printCond = dict(
        CPUCOND=-1,
        MEMFREECOND=sys.maxsize,
        MEMAVLCOND=sys.maxsize,
        BLKRDCOND=-1,
        BLKWRCOND=-1,
    )

    # lifecycle condition #
    startUptime = 0
    deadlineUptime = 0
    startCondCpuMore = -1
    startCondCpuLess = -1
    startCondMemMore = -1
    startCondMemLess = -1
    exitCondCpuMore = -1
    exitCondCpuLess = -1
    exitCondMemMore = -1
    exitCondMemLess = -1
    cpuUsage = -1
    memAvail = -1
    memTotal = -1

    # path #
    procPath = "/proc"
    imagePath = None
    mountCmd = None
    cgroupPathList = {}
    drawFormat = "svg"
    traceEventPath = None
    tracefsPath = None
    defaultTracefsPath = "/sys/kernel/tracing"
    debugfsPath = None
    defaultDebugfsPath = "/sys/kernel/debug"
    debugDirPath = "/usr/lib/debug"
    cacheDirPath = "/var/log/guider"
    outFilePath = "guider.out"
    confFileName = "guider.conf"
    cmdFileName = "guider.cmd"
    tmpPath = "/tmp"
    kmsgPath = "/dev/kmsg"
    nullPath = "/dev/null"
    rootdevPath = None
    syslogPath = "/var/log/syslog"
    lmkPath = "/sys/module/lowmemorykiller/parameters/minfree"
    pythonPath = sys.executable
    pyLibPath = None
    objdumpPath = []
    rootPath = ""
    fontPath = None
    libdltPath = "libdlt"
    libcPath = "libc"
    libgobjPath = "libgobject-2.0"
    libgioPath = "libgio-2.0"
    libdbusPath = "libdbus-1"
    libcppPath = "libstdc++"
    libsystemdPath = "libsystemd"
    liblogPath = "liblog"
    libglesPath = "libGLESv2"
    ldCachePath = "/etc/ld.so.cache"
    libcorkscrewPath = "libcorkscrew"
    libbacktracePath = "libbacktrace"
    libdemanglePath = libcppPath
    libLLVMPath = "libLLVM.so"
    environList = {}
    externList = {}
    environ = {}
    eventLogPath = None
    inputFile = None
    outputFile = None
    inputParam = None
    outPath = None
    origOutPath = None
    outPathList = {}
    freezerPath = None

    # list #
    signalCmd = "trap 'kill $$' INT\n"
    saveCmd = None
    boundaryLine = None
    nrTop = 0
    pipeForPager = None
    printFd = None
    fileSuffix = None
    parsedAnalOption = False
    optionList = []
    customCmd = []
    pyFuncFilter = []
    userCmd = []
    kernelCmd = []
    udpListCache = {}
    tcpListCache = {}
    udsListCache = {}
    ipAddrCache = {}
    customEventList = []
    userEventList = []
    kernelEventList = []
    perfEventChannel = {}
    perfEventData = {}
    perfEventList = {}
    perfTargetEvent = []
    psiData = {}
    prevPsiData = {}
    ignoreItemList = []
    idList = []
    commCache = {}
    commFdCache = {}
    fdCache = {}
    libCache = {}
    rawFdCache = {}
    syscallCache = {}
    netAddrCache = {}
    pciList = []
    limitDirList = {}
    limitDirCntList = {}
    fixedTaskList = []
    overlayfsCache = {}
    vmflagList = []

    # threshold #
    thrData = {}
    prevThrData = {}
    thrTarget = {}
    thrRefreshList = []
    thrTimerList = []
    thrEvtList = {}
    thrEvtHist = {}
    thrEvtCondList = {}
    thrEvtCntList = {}
    thrItemList = {}
    eventLockList = {}
    eventCommandList = {}

    # object #
    impPkg = {}
    impGlbPkg = {}
    skipImpPkg = {}
    exitFuncList = []
    exitPrivateFuncList = {}
    dltObj = None
    dltCtx = None
    shmObj = None
    systemdObj = None
    libcObj = None
    libgioObj = None
    libdbusObj = None
    libcorkscrewObj = None
    libbacktraceObj = None
    libgObj = None
    libglesObj = None
    statvfsObj = None
    guiderObj = None
    libcppObj = None
    libdemangleObj = None
    libLLVMObj = None
    demangleFunc = None
    geterrnoFunc = None
    maxErrStrLen = 256
    raFunc = None
    raMaxSize = 1048576  # 1MB #
    matplotlibVersion = 0
    matplotlibDpi = 500
    matplotlibSize = None
    sigsetObj = None
    sigsetOldObj = None
    ansiObj = None
    localServObj = None
    remoteServObj = None
    netlinkObj = None
    andlogObj = None
    andlogFunc = None

    geAttr = [0] * 9
    addrListForPrint = {}
    addrListForReport = {}

    # number #
    maxCore = 0
    nrCore = 0
    utilProc = 0
    logSize = 0
    kmsgLine = 100
    curLine = 0
    totalLine = 0
    dbgEventLine = 0
    uptime = 0
    prevUptime = 0
    dltTime = 0
    uptimeDiff = 0
    diskStats = []
    prevDiskStats = []
    netstat = ""
    prevNetstat = ""
    loadavg = ""
    battery = ""
    netInIndex = -1
    nrUDPSock = 0
    nrTCPSock = 0
    nrUDSSock = 0
    nrTCPConn = 0
    cpuStealUsage = 0
    nrOOMKill = 0
    nrTotalOOMKill = -1
    nrIrq = 0

    # peak #
    maxBlkRd = 0
    maxBlkWr = 0
    maxCpuUsage = 0
    maxCpuSteal = 0
    maxGpuMemUsage = 0
    maxGpuUsage = 0
    maxInterval = 0
    maxLoad1m = 0
    maxLoad5m = 0
    maxLoad15m = 0
    maxNetIn = 0
    maxNetOut = 0
    maxNrContextSwitch = 0
    maxNrCore = 0
    maxNrFd = 0
    maxNrIrq = 0
    maxNrProcess = 0
    maxNrTaskAbnormal = 0
    maxNrTaskDie = 0
    maxNrTaskNew = 0
    maxNrThread = 0
    maxPsiCpu = 0
    maxPsiIo = 0
    maxPsiMem = 0
    maxSwapPer = 0
    minDiskFree = maxSize
    minMemAvail = maxSize

    # report #
    reportObject = None
    terminalOver = False
    procBuffer = []
    fixedProcList = {}
    topInstance = None
    procInstance = {}
    fileInstance = {}
    sysInstance = None
    procBufferSize = 0
    bufferOverflowed = False
    bufferString = ""
    bufferList = []
    bufferRows = 0
    sysinfoBuffer = ""
    kerSymTable = {}
    jsonData = {}
    nrTopRank = 10
    layout = None
    reportReason = None
    lastHost = None

    showAll = False
    enableAll = False
    disableAll = False
    intervalNow = 0
    recordStatus = False
    bgStatus = False
    condExit = False
    sort = None
    sortCond = None
    sortCondOp = None
    newlined = True
    nsType = "pid"

    # descriptor #
    maxFd = 512
    maxKeepFd = maxFd - 16
    batteryFd = None
    cmdFd = None
    diskStatsFd = None
    cgroupFd = None
    eventLogFd = None
    irqFd = None
    kmsgFd = None
    traceFd = None
    lmkFd = None
    loadavgFd = None
    memFd = None
    mountFd = None
    msgqFd = None
    netdevFd = None
    netstatFd = None
    nullFd = None
    semFd = None
    shmFd = None
    slabFd = None
    softirqFd = None
    statFd = None
    swapFd = None
    syslogFd = None
    uptimeFd = None
    vmstatFd = None
    vmallocFd = None
    zoneFd = None
    swappinessFd = None
    vmpressureFd = None
    overcommitFd = None

    # flag #
    affinityEnable = False
    andlogEnable = False
    attrEnable = False
    barGraphEnable = None
    binderEnable = False
    blockEnable = False
    bufferLossEnable = False
    cgroupEnable = False
    cgroupRootEnable = False
    cloneEnable = True
    cmdEnable = False
    cmdlineEnable = False
    colorEnable = True
    compressEnable = False
    countEnable = False
    cpuAvgEnable = True
    cpuEnable = True
    dbusTopEnable = False
    dbusUnitEnable = False
    delayEnable = False
    demangleEnable = True
    depEnable = False
    diskEnable = False
    dltEnable = False
    dltTimeEnable = False
    dltTopEnable = False
    dmabufEnable = False
    dwarfEnable = False
    elasticEnable = False
    encodeEnable = True
    eventHandleEnable = True
    exceptCommFilter = False
    exeEnable = False
    execEnable = None
    exitFlag = False
    fileEnable = False
    fileTopEnable = False
    fixTargetEnable = False
    floatEnable = False
    forceColorEnable = False
    forceEnable = False
    freeMemEnable = False
    fsEnable = False
    functionEnable = False
    generalEnable = True
    gpuEnable = True
    gpuMemEnable = True
    graphEnable = False
    groupProcEnable = False
    heapEnable = False
    i2cEnable = False
    idlePageEnable = False
    inExit = False
    inWaitStatus = False
    initWssEnable = False
    inotifyEnable = False
    intervalEnable = 0
    ioschedEnable = False
    iouringEnable = False
    irqEnable = False
    journalEnable = False
    jsonEnable = False
    keventEnable = False
    kmsgEnable = False
    kvmEnable = False
    latEnable = cpuEnable
    leakEnable = False
    lockEnable = False
    logEnable = True
    loggingEnable = False
    loggingOpsEnable = False
    logoEnable = True
    memEnable = False
    minStatEnable = False
    networkEnable = False
    nsAllEnable = False
    nsEnable = False
    oomEnable = False
    peakEnable = False
    perfEnable = False
    perfGroupEnable = False
    pipeEnable = False
    powerEnable = False
    printEnable = True
    processEnable = True
    psiEnable = True
    pssEnable = False
    rankProcEnable = True
    recursionEnable = False
    reportEnable = False
    reportFileEnable = False
    resetEnable = False
    rssEnable = False
    schedEnable = False
    schedstatEnable = True
    selectEnable = True
    sigHandlerEnable = False
    stackEnable = False
    streamEnable = False
    sttyEnable = None
    sysEnable = False
    syslogEnable = False
    systemEnable = False
    taskEnable = True
    taskThrEnable = False
    termFlag = True
    tgidEnable = True
    threadEnable = False
    thresholdEnable = False
    totalEnable = False
    truncEnable = True
    ttyEnable = False
    ueventEnable = False
    userEnable = False
    userEnableWarn = False
    userRecordEnable = False
    ussEnable = False
    vssEnable = False
    waitEnable = False
    warnEnable = False
    wchanEnable = False
    wfcEnable = False
    wqEnable = False
    wssEnable = False

    cmdList = {}
    rcmdList = {}
    savedProcTree = {}
    savedProcComm = {}
    savedMountTree = {}
    preemptGroup = []
    filterGroup = []
    schedFilter = []
    affinityFilter = []
    killFilter = []
    syscallList = []
    syscallExceptList = []
    perCoreList = []
    perCoreDrawList = []
    childList = {}
    gpuMemGetters = None
    pidFilter = ""
    fileWatchList = {}
    fileWatchTotalList = {}
    procWatchList = {}
    hostList = {}

    traceTgidFormat1 = (
        r"^\s*(?P<comm>\S+)-(?P<thread>[0-9]+)\s+"
        r"\(\s*(?P<tgid>\S+)\)\s+\[(?P<core>[0-9]+)\]\s+"
        r"(?P<irq>(\S{5}\s)*)(?P<time>\S+):\s+(?P<func>\S+):(?P<etc>.+)"
    )

    traceTgidFormat2 = (
        r"^\s*(?P<comm>\S+)-(?P<thread>[0-9]+)\s+"
        r"\[(?P<core>[0-9]+)\]\s+\(\s*(?P<tgid>.+)\)\s+"
        r"(?P<irq>(\S{5}\s)*)(?P<time>\S+):\s+(?P<func>\S+)(?P<etc>.+)"
    )

    traceNoTgidFormat = (
        r"^\s*(?P<comm>\S+)-(?P<thread>[0-9]+)\s+"
        r"\[(?P<core>[0-9]+)\]\s+(?P<irq>(\S{5}\s)*)(?P<time>\S+):\s+"
        r"(?P<func>\S+):(?P<etc>.+)"
    )

    cliCmdStr = """
Commands:
    - RUN:Command
    - BROADCAST:Command
    - UPSTREAM:Command
    - NOTIFY:Event
    - DOWNLOAD:RemotePath@LocalPath
    - UPLOAD:LocalPath@RemotePath
    - DIR{:RemotePath}
    - LIST
    - JOBS
    - PING
    - CLEAR
    - RESTART
    - HISTORY
    - QUIT\n"""

    def __init__(self, onlyInstance=False):
        self.cpuInfo = {}
        self.cpuCacheInfo = {}
        self.cpuCacheData = {}
        self.memInfo = {}
        self.devInfo = {}
        self.diskInfo = {}
        self.mountInfo = {}
        self.networkInfo = {}
        self.systemInfo = {}
        self.partitionInfo = {}
        self.devArchInfo = {}
        self.devNodeInfo = {}

        self.cpuData = None
        self.gpuData = {}
        self.memData = {}
        self.diskData = {}
        self.blockData = {}
        self.storageData = {}
        self.prevStorageData = {}
        self.storageMapData = {}
        self.ipcData = {}
        self.prevIpcData = {}
        self.userData = {}
        self.mountData = None
        self.mountUpdated = False
        self.loadData = None
        self.nrMaxThread = 0
        self.cmdlineData = None
        self.osData = None
        self.devData = None
        self.procData = None
        self.macAddr = None
        self.uname = []
        self.openFileData = {}
        self.limitData = []

        # resource update time #
        self.netUpdate = None
        self.storageUpdate = None
        self.ipcUpdate = None

        SysMgr.sysInstance = self
        if onlyInstance:
            return
        if not SysMgr.isLinux:
            return

        # save system info first #
        self.saveSysStat(False)

    def __del__(self):
        pass

    @staticmethod
    def writeErr(fd, log):
        LogMgr.lock(fd)

        try:
            fd.write(log)
        except SystemExit:
            sys.exit(0)
        except:
            return

        LogMgr.unlock(fd)

    @staticmethod
    def setRemoteEnv():
        for item in ("REMOTERUN", "NO_ENCODE", "MAXTTY"):
            os.environ[item] = "1"

    @staticmethod
    def setErrorLogger():
        if SysMgr.isLinux:
            sys.stderr = LogMgr()

    @staticmethod
    def getTotalUsage(pid):
        try:
            dobj = Debugger(pid, attach=False)
            dobj.initValues()
            dobj.getCpuUsage()

            cpu = dobj.prevCpuStat[0]
            rss = dobj.getMemUsage()
            proc = "%s(%s)" % (dobj.comm, dobj.pid)

            return [cpu, rss, proc]
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn(
                "failed to get usage info of the task having PID %s" % pid,
                True,
                True,
            )
            return [0, 0, "?"]

    @staticmethod
    def getMaxPid():
        path = "%s/sys/kernel/pid_max" % SysMgr.procPath
        try:
            with open(path, "r") as fd:
                maxPid = fd.readline()[:-1]

                # update pid length #
                if len(maxPid) > SysMgr.pidDigit:
                    SysMgr.pidDigit = len(maxPid)

                SysMgr.maxPid = long(maxPid)
        except SystemExit:
            sys.exit(0)
        except:
            pass

    @staticmethod
    def waitForResource(cpuRes=-1, cpuCond="MORE", memRes=-1, memCond="MORE"):
        # check condition #
        if cpuRes >= 0:
            SysMgr.printInfo(
                "wait for system CPU usage %s than %s%%"
                % (cpuCond, UtilMgr.convNum(cpuRes))
            )
        elif memRes >= 0:
            SysMgr.printInfo(
                ("wait for system available memory %s than %s MB")
                % (memCond, UtilMgr.convNum(memRes))
            )
        else:
            return False

        # convert condition #
        if cpuCond:
            cpuCond = cpuCond.upper()
        if memCond:
            memCond = memCond.upper()

        # init variables #
        dobj = Debugger(SysMgr.pid, attach=False)
        dobj.initValues()

        while 1:
            if cpuRes >= 0:
                cpuStat = dobj.getCpuUsage(system=True)
                if cpuStat and cpuStat[3]:
                    cpuUsage = 100 - (cpuStat[3] / SysMgr.uptimeDiff)
                    SysMgr.cpuUsage = cpuUsage
                    if cpuCond == "MORE" and cpuUsage >= cpuRes:
                        return True
                    elif cpuCond == "LESS" and cpuUsage <= cpuRes:
                        return True

            if memRes >= 0:
                memAvail = SysMgr.getAvailMemInfo(retstr=False)
                if memAvail:
                    SysMgr.memAvail = memAvail = memAvail >> 20
                    if memCond == "MORE" and memAvail >= memRes:
                        return True
                    elif memCond == "LESS" and memAvail <= memRes:
                        return True

            time.sleep(1)
            SysMgr.updateUptime()

    @staticmethod
    def loadLibcObj(exit=False):
        if not SysMgr.isLinux:
            return False
        elif SysMgr.libcObj:
            return True
        elif SysMgr.libcObj is False and not exit:
            return False

        try:
            ret = SysMgr.loadLib(SysMgr.libcPath)
            if ret:
                SysMgr.libcObj = ret
                return True
            else:
                SysMgr.libcObj = False
                if exit:
                    SysMgr.printErr("failed to load libc")
                    sys.exit(-1)
                else:
                    SysMgr.printWarn("failed to load libc", True)
                    return False
        except SystemExit:
            sys.exit(0)
        except:
            return False

    @staticmethod
    def getCpuUsageStr(obj, diff, color=True, proc=False):
        # get CPU usage for me #
        if diff >= 1:
            cpuUsage = obj.getCpuUsage(system=True, proc=proc)
        else:
            cpuUsage = [0, 0, 0, 100]

        ttime = cpuUsage[0] / diff
        utime = cpuUsage[1] / diff
        stime = cpuUsage[2] / diff

        # get CPU usage for myself #
        mcpu = "%d%%" % ttime
        if color:
            mcpu = UtilMgr.convCpuColor(ttime, mcpu)
        mcpuStr = "%s(U%d%%+S%d%%)" % (mcpu, utime, stime)

        # get CPU usage for system #
        ctime = 100 - (cpuUsage[3] / diff)
        ctime = ctime if ctime > 0 else 0
        sysCpuStr = "%d%%" % ctime
        if color:
            sysCpuStr = UtilMgr.convCpuColor(ctime, sysCpuStr)

        return mcpuStr, sysCpuStr

    @staticmethod
    def shrinkHeap():
        if not SysMgr.isLinux:
            return

        # load libc #
        if not SysMgr.loadLibcObj():
            return

        # check malloc_trim #
        if not hasattr(SysMgr.libcObj, "malloc_trim"):
            SysMgr.printWarn("no malloc_trim in %s" % SysMgr.libcPath)
            return

        # check env #
        if "NOSHRINK" in SysMgr.environList:
            return

        # int malloc_trim (size_t pad) #
        SysMgr.libcObj.malloc_trim(0)

    @staticmethod
    def getTimeValues(times, default):
        retList = []
        for item in times:
            if not item in SysMgr.environList:
                retList.append(0)
                continue

            if (
                SysMgr.environList[item][0] == "SET"
                or not SysMgr.environList[item][0]
            ):
                retList.append(default)
                continue

            try:
                val = SysMgr.environList[item][0]
                ret = UtilMgr.convStr2Time(val, isSec=True, verb=False)
                if ret:
                    retList.append(ret)
                else:
                    retList.append(float(val))
            except:
                SysMgr.printErr(
                    "failed to convert %s to float for %s" % (val, item), True
                )
                sys.exit(-1)

        # return value #
        if len(retList) == 1:
            return retList[0]
        else:
            return retList

    @staticmethod
    def doLogMode(mode):
        umode = mode.upper()
        if umode == "KMSG":
            func = LogMgr.doLogKmsg
            mtype = "kernel"
        elif umode == "TRACE":
            func = LogMgr.doLogTrace
            mtype = "trace"
        elif umode == "DLT":
            func = DltAnalyzer.doLogDlt
            mtype = "DLT"
        elif umode == "ANDROID":
            func = LogMgr.doLogAndroid
            mtype = "android"
        elif umode == "JOURNAL":
            func = LogMgr.doLogJournal
            mtype = "journal"
        elif umode == "SYSLOG":
            func = LogMgr.doLogSyslog
            mtype = "syslog"

        SysMgr.printLogo(big=True, onlyFile=True)

        # get message #
        if SysMgr.hasMainArg():
            msg = SysMgr.getMainArg()
        elif SysMgr.inputParam:
            msg = SysMgr.inputParam
        else:
            SysMgr.printErr("no input message for %s" % mtype)
            sys.exit(-1)

        # get log level #
        if not SysMgr.customCmd:
            level = None
        else:
            level = SysMgr.customCmd[0]

        # get repeat count #
        repeatCnt = UtilMgr.getEnvironNum("REPEAT", False, 1, False, True)

        # get interval #
        if SysMgr.intervalEnable:
            interval = SysMgr.intervalEnable / 1000.0
        else:
            interval = 0

        for idx in xrange(repeatCnt):
            ret = func(msg=msg, level=level)
            if ret == 0 or (func == LogMgr.doLogAndroid and ret > 0):
                SysMgr.printInfo(
                    "logged %s message '%s' successfully" % (mtype, msg)
                )
                time.sleep(interval)
            else:
                SysMgr.printErr("failed to log %s message" % mtype)
                break

    @staticmethod
    def getPidList():
        try:
            if SysMgr.isLinux:
                pids = os.listdir(SysMgr.procPath)
            else:
                pids = list(map(str, SysMgr.getPkg("psutil").pids()))
            return pids
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printOpenErr(SysMgr.procPath)
            return []

    @staticmethod
    def checkTimeout(func, timeout=1):
        errStr = "failed to call the function for timeout"

        # create a new process #
        pid = SysMgr.createProcess(keepPrintFd=True)
        if pid > 0:
            try:
                # change SIGALRM handler #
                handle = signal.getsignal(signal.SIGALRM)
                signal.signal(signal.SIGALRM, SysMgr.exceptHandler)
                signal.alarm(timeout)

                # wait for child #
                try:
                    os.waitpid(pid, 0)
                    signal.alarm(0)
                    return True
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.killChildren(children=[pid])
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr(errStr, True)

            # clear alarm #
            signal.alarm(0)

            # recover SIGALRM handler #
            if handle:
                signal.signal(signal.SIGALRM, handle)
        elif pid == 0:
            try:
                # run target function #
                func()
            except SystemExit:
                pass
            except:
                SysMgr.printErr(errStr, True)
            finally:
                os._exit(0)
        else:
            return False

    @staticmethod
    def killProcGroup(pid, sig=None):
        try:
            if not sig:
                sig = signal.SIGINT

            pgrp = os.getpgid(pid)
            os.killpg(pgrp, signal.SIGINT)
        except SystemExit:
            sys.exit(0)
        except:
            pass

    @staticmethod
    def getNrSysFdHandle():
        try:
            with open("%s/sys/fs/file-nr" % SysMgr.procPath) as fd:
                stats = list(map(long, fd.readline()[:-1].split()))
                """
                0: the total allocated file handles
                1: the number of currently used file handles (kernel 2.4)
                2: the maximum file handles that can be allocated
                """
                return stats
        except SystemExit:
            sys.exit(0)
        except:
            return None

    @staticmethod
    def setMaxFd():
        if not SysMgr.isLinux:
            return

        # define resource and value #
        rtype = "RLIMIT_NOFILE"
        slim = 1048576
        hlim = 1048576

        # try to change maximum open file #
        try:
            ret = SysMgr.chRlimit(os.getpid(), rtype, slim, hlim)
            if not ret:
                raise Exception("N/A")

            # update the number of maximum open file #
            SysMgr.maxFd = ret[0]
            SysMgr.maxKeepFd = SysMgr.maxFd - 16
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn(
                "failed to get the number of maximum file descriptor",
                reason=True,
            )

    @staticmethod
    def setReportAttr():
        # get argument #
        if SysMgr.hasMainArg():
            SysMgr.inputFile = SysMgr.getMainArg()
        elif SysMgr.inputParam:
            SysMgr.inputFile = SysMgr.inputParam
        else:
            SysMgr.inputFile = "guider.dat"

        # set default output path #
        if not SysMgr.outPath:
            SysMgr.outPath = "%s.out" % os.path.splitext(SysMgr.inputFile)[0]

    @staticmethod
    def execSystemView():
        # parse all options and make output file path #
        SysMgr.parseAnalOption()

        SysMgr.printStat(r"start recording... [ STOP(Ctrl+c), MARK(Ctrl+\) ]")

        # wait for interval #
        if SysMgr.repeatInterval > 0:
            try:
                time.sleep(SysMgr.repeatInterval)
            except:
                pass
        # wait for user input #
        else:
            SysMgr.waitEvent()

        # save system info #
        SysMgr.saveSysStats()

        # get and remove process tree from data file #
        SysMgr.getProcTreeInfo()

        SysMgr.printLogo(big=True)

        # print system information #
        SysMgr.printInfoBuffer()

    @staticmethod
    def getVmstat(retDict=False):
        # read vmstat buf #
        vmBuf = SysMgr.readProcStat(
            SysMgr.vmstatFd, "vmstat", SysMgr, "vmstatFd"
        )
        if not vmBuf:
            return vmBuf

        # convert list to dictionary #
        if retDict:
            newDict = {}

            for line in vmBuf:
                try:
                    item = line.strip().split()
                    newDict.setdefault(item[0], long(item[1]))
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

            return newDict

        return vmBuf

    @staticmethod
    def setRecordAttr():
        # iorec #
        if SysMgr.checkMode("iorec"):
            SysMgr.blockEnable = True
            SysMgr.cpuEnable = False

        # function #
        elif SysMgr.checkMode("funcrec"):
            SysMgr.functionEnable = True

        # syscall #
        elif SysMgr.checkMode("sysrec"):
            SysMgr.sysEnable = True
            SysMgr.cpuEnable = False

        # file #
        elif SysMgr.checkMode("filerec"):
            SysMgr.fileEnable = True

        # general #
        elif SysMgr.checkMode("genrec"):
            SysMgr.systemEnable = True

        # update record status #
        SysMgr.inputFile = "%s/../trace" % SysMgr.getTraceEventPath()

        SysMgr.parseRecordOption()

        # change the CPU scheduling priority for process #
        if SysMgr.prio is None:
            SysMgr.setPriority(SysMgr.pid, "C", -20)

        SysMgr.printProfileOption()
        SysMgr.printProfileCmd()

    @staticmethod
    def removeFd(fname):
        SysMgr.fdCache.pop(fname, None)

    @staticmethod
    def getProcMaps(pid):
        mpath = "%s/%s/maps" % (SysMgr.procPath, pid)
        return SysMgr.readFile(mpath)

    @staticmethod
    def saveProcMapsFile(pid, path=None):
        # convert PID and check root permission #
        if pid == "self":
            pid = SysMgr.pid
        else:
            SysMgr.checkRootPerm()

        procInfo = "%s(%s)" % (SysMgr.getComm(pid), pid)

        # set temporary path name #
        if not path:
            path = "%s/%s.maps" % (SysMgr.tmpPath, procInfo)

        try:
            maps = SysMgr.getProcMaps(pid)
            if not maps:
                return False

            ret = SysMgr.writeFile(path, maps + "\n", False)
            if not ret:
                return False

            SysMgr.printInfo(
                "saved maps of %s to '%s'%s successfully"
                % (procInfo, path, UtilMgr.getFileSizeStr(path))
            )
            return True
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to save maps of %s" % procInfo, True)
            return False

    @staticmethod
    def setPipeSize(fd, size=0):
        def _printErr(msg):
            SysMgr.printWarn("failed to change pipe size because " + msg)

        fcntl = SysMgr.getPkg("fcntl", False)
        if not fcntl:
            _printErr("no fcntl package")
            return

        # set size to max #
        if not size:
            size = SysMgr.getMaxPipeSize()

        # define constant value #
        F_SETPIPE_SZ = 1024 + 7
        F_GETPIPE_SZ = 1024 + 8

        # change pipe size #
        try:
            ret = fcntl.fcntl(fd, F_SETPIPE_SZ, size)
        except SystemExit:
            sys.exit(0)
        except:
            _printErr(SysMgr.getErrMsg())

    @staticmethod
    def getMaxPipeSize():
        if not SysMgr.isLinux:
            return

        # return max pipe size #
        try:
            return long(SysMgr.readFile("/proc/sys/fs/pipe-max-size"))
        except:
            return None

    @staticmethod
    def removeFiles(flist):
        for f in flist:
            try:
                if not os.path.exists(f):
                    continue

                os.remove(f)
                SysMgr.printInfo("removed '%s'" % f)
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr("failed to remove '%s'" % f, True)

    @staticmethod
    def getCompFd(fname, perm="r", fd=None, verb=True):
        try:
            # TODO: support zip, tar #
            # gz + tar.gz #
            if UtilMgr.isGzipFile(fname, fd):
                gzip = SysMgr.getPkg("gzip", False)
                fd = (
                    gzip.GzipFile(fileobj=fd) if fd else gzip.open(fname, perm)
                )
            # xz (lzma) #
            elif UtilMgr.isXzFile(fname, fd):
                param = fd if fd else fname
                fd = SysMgr.getPkg("lzma", False).open(param, perm)
            # raw data #
            else:
                fd = None
        except SystemExit:
            sys.exit(0)
        except:
            if verb:
                SysMgr.printWarn("failed to open '%s'" % fname, True, True)
            fd = None
        return fd

    @staticmethod
    def getCachedFd(fname, perm="rb", verb=True):
        if fname in SysMgr.fdCache and SysMgr.fdCache[fname]["perm"] == perm:
            return SysMgr.fdCache[fname]["fd"]

        try:
            SysMgr.fdCache[fname] = {"fd": open(fname, perm), "perm": perm}
            return SysMgr.fdCache[fname]["fd"]
        except SystemExit:
            sys.exit(0)
        except:
            if verb:
                SysMgr.printOpenErr(fname)
            return None

    @staticmethod
    def setVisualAttr():
        # set default input path #
        if len(sys.argv) <= 2:
            sys.argv.insert(2, SysMgr.outFilePath)

        SysMgr.graphEnable = True

        # ignore user warning #
        SysMgr.ignoreWarn()

        # apply regex for first path #
        flist = UtilMgr.convPath(sys.argv[2])
        flist = UtilMgr.getFileList(flist, exceptDir=True)
        if isinstance(flist, list) and len(flist) > 0:
            sys.argv = sys.argv[:2] + flist + sys.argv[3:]

        # thread draw mode #
        if TaskAnalyzer.getInitTime(sys.argv[2]) > 0:
            # apply launch option #
            SysMgr.applyLaunchOption()

            # check data type #
            if SysMgr.isFuncMode():
                SysMgr.printErr(
                    "failed to draw because this data is for function"
                )
                sys.exit(-1)
            elif (
                SysMgr.isThreadMode()
                or not SysMgr.sysinfoBuffer
                or SysMgr.forceEnable
            ):
                pass
            else:
                SysMgr.printErr(
                    "failed to draw because this data is not supported"
                )
                sys.exit(-1)

            # set input path #
            SysMgr.inputFile = sys.argv[1] = sys.argv[2]
            SysMgr.intervalEnable = 1
            if not SysMgr.outPath:
                SysMgr.outPath = (
                    "%s.out" % os.path.splitext(SysMgr.inputFile)[0]
                )
            del sys.argv[2]
        # top draw mode #
        else:
            # CPU #
            if SysMgr.checkMode("drawcpu") or SysMgr.checkMode("drawcpuavg"):
                SysMgr.layout = "CPU"
            # delay #
            elif SysMgr.checkMode("drawdelay"):
                SysMgr.layout = "DELAY"
            # memory #
            elif SysMgr.checkMode("drawmem") or SysMgr.checkMode("drawmemavg"):
                SysMgr.layout = "MEM"
            # VSS #
            elif SysMgr.checkMode("drawvss") or SysMgr.checkMode("drawvssavg"):
                SysMgr.layout = "MEM"
                SysMgr.vssEnable = True
            # RSS #
            elif (
                SysMgr.checkMode("drawrss")
                or SysMgr.checkMode("drawrssavg")
                or "DRAWRSS" in SysMgr.environList
            ):
                SysMgr.layout = "MEM"
                SysMgr.rssEnable = True
            # leak #
            elif SysMgr.checkMode("drawleak"):
                SysMgr.layout = "MEM"
                SysMgr.leakEnable = True
            # I/O #
            elif SysMgr.checkMode("drawio"):
                SysMgr.layout = "IO"
            # prio #
            elif SysMgr.checkMode("drawpri"):
                SysMgr.layout = "PRIO"

            # modify args for drawing multiple input files #
            sys.argv[1] = "top"
            args = sys.argv[2:]
            SysMgr.inputParam = UtilMgr.getFileList(args)

    @staticmethod
    def execFileAnalysis():
        SysMgr.checkRootPerm()

        # parse analysis option #
        SysMgr.parseAnalOption()

        SysMgr.printStat(r"start analyzing... [ STOP(Ctrl+c), MARK(Ctrl+\) ]")

        # start analyzing files #
        try:
            pi = FileAnalyzer()
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                "failed to analyze memory-mapped files", reason=True
            )
            sys.exit(-1)

        # save system info #
        SysMgr.saveSysStats()

        # get and remove process tree from data file #
        SysMgr.getProcTreeInfo()

        if SysMgr.intervalEnable == 0:
            # print total file usage per process #
            pi.printUsage()
        else:
            # print file usage per process on timeline #
            pi.printIntervalInfo()

    @staticmethod
    def execRecordLoop():
        skipWait = False

        while SysMgr.repeatInterval > 0:
            # set alarm #
            signal.alarm(SysMgr.repeatInterval)

            # get init time in buffer for verification #
            if SysMgr.graphEnable:
                skipWait = True
                initTime = 0
            else:
                initTime = TaskAnalyzer.getInitTime(SysMgr.inputFile)

            # wait for timer #
            try:
                for cnt in xrange(SysMgr.repeatInterval):
                    UtilMgr.printProgress(cnt, SysMgr.repeatInterval)
                    time.sleep(1)
            except SystemExit:
                sys.exit(0)
            except:
                pass

            # real-time copy from pipe to file #
            if SysMgr.pipeEnable:
                if SysMgr.outputFile:
                    # backup #
                    SysMgr.backupFile(SysMgr.outputFile)

                    # copy #
                    SysMgr.copyPipeToFile(
                        "%s%s" % (SysMgr.inputFile, "_pipe"), SysMgr.outputFile
                    )
                else:
                    SysMgr.printErr(
                        "wrong option used, use also -s option to save data"
                    )
                sys.exit(-1)

            # check counter #
            if SysMgr.repeatCnt <= SysMgr.progressCnt and SysMgr.termFlag:
                # check exit condition #
                if SysMgr.graphEnable:
                    break

                SysMgr.printWarn("terminated by timer\n", True)
                sys.exit(-1)

            # compare init time with now time for buffer verification #
            if 0 < initTime < TaskAnalyzer.getInitTime(SysMgr.inputFile):
                SysMgr.printErr(
                    "buffer size is not enough (%sKB)" % SysMgr.getBufferSize()
                )
                sys.exit(-1)
            else:
                SysMgr.clearTraceBuffer()

        # start writing logs to file through pipe #
        if SysMgr.pipeEnable:
            if SysMgr.outputFile:
                pipePath = "%s%s" % (SysMgr.inputFile, "_pipe")
                SysMgr.copyPipeToFile(pipePath, SysMgr.outputFile)
            else:
                SysMgr.printErr(
                    "wrong option used, use also -s option to save data"
                )
            sys.exit(-1)

        if not SysMgr.graphEnable:
            # get init time from buffer for verification #
            initTime = TaskAnalyzer.getInitTime(SysMgr.inputFile)

        # wait for user input #
        while 1:
            if skipWait:
                break
            elif SysMgr.recordStatus:
                SysMgr.condExit = True

                SysMgr.waitEvent()
                if SysMgr.condExit:
                    break
            else:
                break

        # FUNCTION GRAPH MODE #
        if SysMgr.graphEnable:
            if not SysMgr.outputFile:
                SysMgr.stopRecording()

                # read trace data #
                data = SysMgr.readFile(SysMgr.inputFile)

                # parse options #
                SysMgr.parseAnalOption()

                # apply core filter #
                if SysMgr.perCoreList:
                    data = data.split("\n")
                    data = SysMgr.applyCoreFilter(data)
                    data = "\n".join(data)

                # print trace data #
                SysMgr.printPipe(data)

                sys.exit(0)
            elif SysMgr.repeatInterval:
                sys.exit(0)
        # THREAD & FUNCTION MODE #
        else:
            # compare init time with now time for buffer verification #
            if initTime < TaskAnalyzer.getInitTime(SysMgr.inputFile):
                SysMgr.printErr(
                    "buffer size %sKB is not enough" % SysMgr.getBufferSize()
                )
                sys.exit(-1)

            # save system info #
            SysMgr.saveSysStats()

    @staticmethod
    def dumpFileData():
        if (
            not "DUMPFILE" in SysMgr.environList
            and not "DUMPFILEB64" in SysMgr.environList
        ):
            return False

        # get size #
        commonSize = UtilMgr.getEnvironNum(
            "DUMPFILESIZE", False, 0, False, True
        )

        def _dumpFile(path, size=0, base64=True):
            path = os.path.realpath(path)

            SysMgr.printInfo("dump '%s'" % path)

            if os.path.isdir(path):
                fdata = ",".join(os.listdir(path))
                fsize = ""
            else:
                size = commonSize

                # update size #
                if size < 0:
                    tail = True
                    size = abs(size)
                else:
                    tail = False
                    if size == 0:
                        size = -1

                fdata = SysMgr.readFile(path, size, tail=tail, byte=True)
                fsize = UtilMgr.getFileSizeStr(path)

            if not fdata:
                return

            # print file info #
            SysMgr.printPipe(
                "[ File Dump ] (%s%s)\n%s\n" % (path, fsize, twoLine)
            )

            # write file data base64-encoded #
            if base64:
                fdata = UtilMgr.encodeBase64(fdata)
            SysMgr.printFd.write(fdata)

            SysMgr.printPipe("\n%s\n" % oneLine)

        # save file data for this interval #
        printCnt = 0
        for name in ["DUMPFILE", "DUMPFILEB64"]:
            if not name in SysMgr.environList:
                continue

            base64 = "DUMPFILEB64" in SysMgr.environList

            for path in SysMgr.environList[name]:
                # parse path snd size #
                items = path.split("#", 1)
                path = items[0]
                try:
                    size = long(items[1]) if len(items) > 1 else commonSize
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr(
                        "failed to parse size info of '%s' for dump" % path
                    )
                    size = commonSize

                # dump file #
                _dumpFile(path, size, base64)

                printCnt += 1

        if printCnt > 0:
            return True
        else:
            return False

    @staticmethod
    def getTrimVals(timeline):
        imin = 0
        imax = len(timeline)

        if "TRIM" in SysMgr.environList:
            trim = SysMgr.environList["TRIM"][0].split(":")
        elif "TRIMIDX" in SysMgr.environList:
            trim = SysMgr.environList["TRIMIDX"][0].split(":")
        else:
            return timeline

        try:
            if len(trim) == 1:
                condMin = long(trim[0])
                condMax = SysMgr.maxSize
            elif len(trim) >= 2:
                # first tick #
                if trim[0].strip():
                    condMin = long(trim[0])
                else:
                    condMin = 0

                # last tick #
                if trim[1].strip():
                    condMax = long(trim[1])
                else:
                    condMax = long(SysMgr.maxSize)
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                "failed to recognize '%s' as the START:END time"
                % ":".join(trim)
            )
            sys.exit(-1)

        if "TRIM" in SysMgr.environList:
            # define default values #
            imin = timeline[0]
            imax = timeline[-1]

            # get min index #
            for itime in timeline:
                if itime >= condMin:
                    imin = itime
                    break

            # get max index #
            for itime in timeline:
                if itime >= condMax:
                    imax = itime
                    break

            # convert index range #
            imin = timeline.index(imin)
            imax = timeline.index(imax)
        elif "TRIMIDX" in SysMgr.environList:
            imin = condMin
            imax = condMax

        return timeline[imin:imax]

    @staticmethod
    def applyCoreFilter(lines):
        newList = []
        filterList = set(map(str, SysMgr.perCoreList))

        for line in lines:
            m = re.match(r"^\s*(?P<time>.+) \|\s*(?P<core>[0-9]+)\)", line)
            if not m:
                newList.append(line)
                continue

            # check core number #
            d = m.groupdict()
            if d["core"] in filterList:
                newList.append(line)

        return newList

    @staticmethod
    def getGpuMem(incAll=False):
        if SysMgr.gpuMemGetters is None:
            SysMgr.gpuMemGetters = [
                SysMgr.getNvGpuMem,  # nvidia #
                SysMgr.getMaliGpuMem,  # mali #
            ]

        totalStat = {}
        memStat = {}
        removeList = []
        getters = SysMgr.gpuMemGetters

        # pylint: disable-next=not-an-iterable
        for func in getters:
            gpuStat, procStat = func(incAll)

            # merge total size #
            if gpuStat:
                for item in gpuStat:
                    if item in totalStat:
                        totalStat[item]["size"] += gpuStat[item]["size"]
                    else:
                        totalStat[item] = gpuStat[item]

                # make total stat #
                if not "0" in totalStat:
                    totalMem = 0
                    for stats in totalStat.values():
                        totalMem += stats["size"]
                    totalStat["0"] = {"comm": "TOTAL", "size": totalMem}
            else:
                removeList.append(func)

            # merge per-type size #
            for pid, data in procStat.items():
                memStat.setdefault(pid, {})
                for mtype, size in data.items():
                    memStat[pid].setdefault(mtype, 0)
                    memStat[pid][mtype] += size

        # set total size of each process #
        for pid, data in memStat.items():
            total = 0
            for size in data.values():
                total += size
            memStat[pid]["TOTAL"] = total

        # remove invalid getters #
        if removeList:
            SysMgr.gpuMemGetters = list(set(getters) - set(removeList))

        return totalStat, memStat

    @staticmethod
    def getMaliGpuMem(incAll=False):
        gpuStat = {}
        procStat = {}

        # read Mali GPU memory info #
        try:
            path = "/sys/kernel/debug/mali/gpu_memory"
            if not os.path.exists(path):
                return gpuStat, procStat

            with open(path, "rb") as fd:
                gpuInfo = fd.readlines()[2:]
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printOpenWarn(path)
            return gpuStat, procStat

        # parse per-process memory info #
        for item in gpuInfo:
            try:
                line = item.decode().split()

                if line[0] == "Mali":
                    if line[2] == "usage":
                        pid = "0"
                        comm = "TOTAL"
                    else:
                        continue
                else:
                    comm, pid, mali, malimax, extern, ump, dma = line
            except SystemExit:
                sys.exit(0)
            except:
                continue

            # save stat #
            gpuStat.setdefault(pid, {"comm": comm, "size": long(mali)})

            # save stat to per-process list #
            if incAll:
                procStat.setdefault(pid, {})
                procStat[pid]["mali"] = long(mali)
                procStat[pid]["extern"] = long(extern)
                procStat[pid]["ump"] = long(ump)
                procStat[pid]["dma"] = long(dma)

        return gpuStat, procStat

    @staticmethod
    def getOatHeader(path):
        header = {}

        try:
            if not UtilMgr.isOatFile(path):
                raise ValueError("not oat image")

            # OAT header starts from oatdata symbol adderss (0x1000) #
            data = SysMgr.readFile(
                path, size=80, verb=False, byte=True, offset=0x1000
            )

            # convert values #
            v = struct.unpack("20I", data)
            header = {
                "magic": struct.pack("I", v[0]),
                "version": struct.pack("I", v[1]),
                "checksum": v[2],
                "inst": v[3],
                "instFeat": v[4],
                "dexCnt": v[5],
                "dexOffset": v[6],
                "execOffset": v[7],
                "intp2intpOffset": v[8],
                "compiledOffset": v[9],
                "jniDlsymOffset": v[10],
                "jniTrampOffset": v[11],
                "imtTrampOffset": v[12],
                "resTrampOffset": v[13],
                "compiled2intpOffset": v[14],
                "imgPatchDeltaOffset": v[15],
                "oatFileLocChecksum": v[16],
                "oatDataOffset": v[17],
                "keyValSize": v[18],
                "keyValOffset": v[19],
            }
        except ValueError:
            pass
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to get OAT header from '%s'" % path, True)
        finally:
            return header

    @staticmethod
    def symbolizeOat(path, debug=False, printer=None):
        try:
            if not UtilMgr.isOatFile(path):
                raise ValueError("not oat image")

            # make symbolized file path #
            symPath = os.path.join(
                SysMgr.cacheDirPath, path.replace("/", "_") + ".symbolized"
            )

            # print header #
            if debug:
                header = SysMgr.getOatHeader(path)
                printer("[OAT Header]\n%s" % twoLine)
                for n, v in header.items():
                    if isinstance(v, long):
                        v = "%s (%s)" % (str(v), hex(v))
                    printer("{0:1}: {1:<1}".format(n, str(v)))
                if not header:
                    printer("\tNone")
                printer("%s\n" % oneLine)

            # check skip condition #
            if not "NOFILECACHE" in SysMgr.environList and os.path.exists(
                symPath
            ):
                # update path #
                path = symPath

                raise ValueError("already symbolized")

            SysMgr.printInfo(
                "symbolize OAT image for %s%s"
                % (path, UtilMgr.getFileSizeStr(path))
            )

            # check oatdump binary #
            if "OATDUMP" in SysMgr.environList:
                oatdump = SysMgr.environList["OATDUMP"][0]
            elif not UtilMgr.which("oatdump"):
                SysMgr.printErr("no found oatdump")
                raise ValueError("no oatdump")
            else:
                oatdump = "oatdump"

            # execute oatdump #
            cmd = [
                oatdump,
                "--symbolize=" + path,
                "--output=" + symPath,
                "--only-keep-debug",
            ]
            ret = SysMgr.executeCmdSync(cmd)

            # check output #
            if not os.path.exists(symPath):
                raise Exception("N/A")

            # update path #
            path = symPath
        except ValueError:
            pass
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to symbolize '%s'" % path, True)
        finally:
            return path

    @staticmethod
    def mkdirs(path):
        try:
            if not os.path.isdir(path):
                os.makedirs(path)
            return True
        except SystemExit:
            sys.exit(0)
        except:
            err = SysMgr.getErrMsg()
            SysMgr.printWarn(
                "failed to make %s directory because %s" % (path, err),
                True,
            )
            return False

    @staticmethod
    def getNvGpuMem(incAll=False):
        gpuStat = {}
        procStat = {}
        commonPath = "/sys/kernel/debug/nvmap/"

        # check dir #
        if not os.path.exists(commonPath):
            return gpuStat, procStat

        # find all clients files #
        if incAll or "GPUALLMEM" in SysMgr.environList:
            targetList = UtilMgr.getFiles(commonPath, ["clients"])
        else:
            targetList = ["/sys/kernel/debug/nvmap/iovmm/clients"]

        for path in targetList:
            # read NVIDIA GPU memory info #
            try:
                with open(path, "rb") as fd:
                    gpuInfo = fd.readlines()[1:]
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printOpenWarn(path)
                continue

            # get memory type #
            mtype = UtilMgr.lstrip(path, commonPath).rsplit("/", 1)[0]

            # parse per-process memory info #
            for item in gpuInfo:
                try:
                    line = item.decode().split()
                    if line[0] == "total":
                        pid = "0"
                        comm = "TOTAL"
                        size = line[1]
                    else:
                        comm, pid, size = line[1:]
                except SystemExit:
                    sys.exit(0)
                except:
                    continue

                # convert size to number #
                size = UtilMgr.convUnit2Size(size)

                # save stat #
                gpuStat.setdefault(pid, {"comm": comm, "size": 0})
                gpuStat[pid]["size"] += size

                # save stat to per-process list #
                if incAll:
                    procStat.setdefault(pid, {})
                    procStat[pid][mtype] = size

        return gpuStat, procStat

    @staticmethod
    def execSshCmd(cmd, addr, port=22, user="root", passwd=None):
        # get subprocess object #
        subprocess = SysMgr.getPkg("subprocess")

        # get environment variables #
        env = SysMgr.getEnvList()

        cmdList = []

        if passwd:
            # get sshpass path #
            sshpassPath = UtilMgr.which("sshpass")
            if not sshpassPath:
                SysMgr.printErr("failed to get sshpass path")
                sys.exit(-1)
            sshpassPath = sshpassPath[0]

            # add sshpass command #
            cmdList += [sshpassPath, "-p", passwd]

        # get ssh path #
        sshPath = UtilMgr.which("ssh")
        if not sshPath:
            SysMgr.printErr("failed to get ssh path")
            sys.exit(-1)
        sshPath = sshPath[0]

        # set option #
        opt = "-oStrictHostKeyChecking=no"

        # add ssh command #
        cmdList += [sshPath, opt, "-p", str(port), "%s@%s" % (user, addr), cmd]

        # save start time #
        startTime = time.time()

        # create a new worker process #
        procObj = subprocess.Popen(
            cmdList,
            stdout=subprocess.PIPE,
            stdin=subprocess.PIPE,
            stderr=subprocess.PIPE,
            universal_newlines=True,
            bufsize=0,
            env=env,
        )

        # handle output #
        try:
            select = SysMgr.getPkg("select")
            while 1:
                res = select.select(
                    [procObj.stdout, procObj.stderr], [], [], 1
                )
                if not res[0]:
                    continue
                for item in res[0]:
                    val = item.read()
                    if val:
                        print(val)
                    else:
                        raise Exception()
        except SystemExit:
            sys.exit(0)
        except:
            pass
        finally:
            duration = time.time() - startTime

        # kill subprocess group #
        SysMgr.killProcGroup(procObj.pid)

        SysMgr.printInfo("elapsed %.6f for '%s'" % (duration, cmd))

        sys.exit(0)

    @staticmethod
    def execTopCmd():
        # check background processes #
        if not "FASTINIT" in SysMgr.environList and not SysMgr.isDrawMode():
            SysMgr.checkBgProcs()

        # write user command #
        SysMgr.runProfCmd("BEFORE")

        # set signal handler #
        if SysMgr.isDrawMode():
            SysMgr.setDefaultSignal()
        # print profile option #
        else:
            SysMgr.printProfileOption()
            SysMgr.printProfileCmd()

            # set handler for exit #
            SysMgr.setNormalSignal()

        # thread #
        if SysMgr.checkMode("ttop"):
            SysMgr.processEnable = False

            # check GPU option #
            if not (
                "GPUMEM" in SysMgr.environList
                or "GPUMEMSUM" in SysMgr.environList
            ):
                SysMgr.gpuMemEnable = False
        # file #
        elif SysMgr.checkMode("ftop"):
            SysMgr.fileTopEnable = True

        # stack #
        elif SysMgr.checkMode("stacktop"):
            if SysMgr.checkStackTopCond():
                SysMgr.processEnable = False
                SysMgr.stackEnable = True
            else:
                sys.exit(-1)

        # perf #
        elif SysMgr.checkMode("ptop"):
            if SysMgr.checkPerfTopCond():
                SysMgr.perfEnable = True
                if SysMgr.findOption("g"):
                    SysMgr.processEnable = False
                    SysMgr.perfGroupEnable = True
            else:
                sys.exit(-1)

        # mem #
        elif SysMgr.checkMode("mtop"):
            if SysMgr.checkMemTopCond():
                SysMgr.memEnable = True
                SysMgr.sort = "m"
            else:
                sys.exit(-1)

        # WSS (Working Set Size) #
        elif SysMgr.checkMode("wtop"):
            if SysMgr.checkWssTopCond():
                if not "SYSMEM" in SysMgr.environList:
                    SysMgr.addEnvironVar("NOSYSMEM")
                SysMgr.memEnable = True
                SysMgr.wssEnable = True
                SysMgr.sort = "m"
            else:
                sys.exit(-1)

        # disk #
        elif SysMgr.checkMode("disktop"):
            if SysMgr.checkDiskTopCond():
                SysMgr.diskEnable = True
                SysMgr.blockEnable = True
                SysMgr.sort = "b"
            else:
                sys.exit(-1)

        # all #
        elif SysMgr.checkMode("atop"):
            SysMgr.cpuEnable = True
            SysMgr.diskEnable = True
            SysMgr.irqEnable = True
            SysMgr.memEnable = True
            SysMgr.networkEnable = True

            if SysMgr.isRoot():
                SysMgr.blockEnable = True
                SysMgr.perfEnable = True
            else:
                SysMgr.printWarn(
                    "block stat is disabled "
                    "because there is no root permission"
                )

        # tree #
        elif SysMgr.checkMode("trtop"):
            # get target #
            if SysMgr.hasMainArg():
                targets = SysMgr.getMainArgs()
            else:
                targets = None

            # set default interval #
            if not SysMgr.intervalEnable:
                SysMgr.intervalEnable = 1

            # init dbg object #
            TaskAnalyzer.dbgObj = Debugger(SysMgr.pid, attach=False)
            TaskAnalyzer.dbgObj.initValues()
            TaskAnalyzer.dbgObj.getCpuUsage(system=True)

            # add environment variables #
            SysMgr.addEnvironVar("CURCPU")
            if not "NOMEM" in SysMgr.environList:
                SysMgr.addEnvironVar("ADDMEM")

            SysMgr.doPstree(targets=targets, canRepeat=True)

        # condition #
        elif SysMgr.checkMode("ctop"):
            # check config file #
            if not SysMgr.getOption("C"):
                # search config file #
                SysMgr.updateConfigPath()

            # load config file #
            if ConfigMgr.confData:
                pass
            elif not SysMgr.loadConfig(SysMgr.confFileName):
                sys.exit(-1)

            # ignore output #
            if not SysMgr.outPath:
                if SysMgr.isWindows:
                    SysMgr.outPath = "NUL"
                else:
                    SysMgr.outPath = SysMgr.nullPath

            # get output dir #
            if SysMgr.outPath == SysMgr.nullPath:
                if os.path.exists(SysMgr.tmpPath):
                    targetDir = SysMgr.tmpPath
                else:
                    targetDir = "."
            elif os.path.isdir(SysMgr.outPath):
                targetDir = SysMgr.outPath
            else:
                targetDir = os.path.dirname(SysMgr.outPath)

            # check output dir #
            if not SysMgr.isWritable(targetDir):
                SysMgr.printErr("wrong path '%s' for output" % targetDir)
                sys.exit(-1)

            # remove SIGINT handler to ignore creating the report file #
            signal.signal(signal.SIGINT, SysMgr.exitHandler)

            # update run number #
            if not SysMgr.nrRun:
                try:
                    SysMgr.updateNrRun(targetDir)
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr(
                        "failed to update sequence number of execution", True
                    )

            # set buffer strip #
            SysMgr.bufferLossEnable = True

            # set threshold handle flag #
            SysMgr.thresholdEnable = True

            # disable color and encoding #
            SysMgr.colorEnable = False
            SysMgr.encodeEnable = False

        # DLT #
        elif SysMgr.checkMode("dlttop"):
            SysMgr.dltTopEnable = True
            SysMgr.dltTimeEnable = True

        # D-Bus #
        elif SysMgr.checkMode("dbustop"):
            SysMgr.dbusTopEnable = True
            SysMgr.floatEnable = True

            # set default interval to 3 for performance #
            if not SysMgr.findOption("i") and not SysMgr.findOption("R"):
                SysMgr.intervalEnable = 3

        # usercall #
        elif SysMgr.checkMode("utop"):
            SysMgr.doTrace("usercall")

        # pycall #
        elif SysMgr.checkMode("pytop"):
            SysMgr.doTrace("pycall")

        # breakcall #
        elif SysMgr.checkMode("btop"):
            SysMgr.doTrace("breakcall")

        # syscall #
        elif SysMgr.checkMode("systop"):
            SysMgr.doTrace("syscall")

        # kernelcall #
        elif SysMgr.checkMode("ktop"):
            SysMgr.doTrace("kernel")

        # kernelfunctioncall #
        elif SysMgr.checkMode("kftop"):
            SysMgr.runKernelTop()

        # network #
        elif SysMgr.checkMode("ntop"):
            SysMgr.networkEnable = True

        # background #
        elif SysMgr.checkMode("bgtop"):
            # check condition #
            if not SysMgr.checkBgTopCond():
                sys.exit(-1)

            # check permission #
            if SysMgr.isRoot():
                SysMgr.blockEnable = True
            else:
                SysMgr.printWarn(
                    "block stat is disabled because "
                    "there is no root permission"
                )

            SysMgr.diskEnable = True
            SysMgr.networkEnable = True

            SysMgr.runBackgroundMode()

        # report #
        elif SysMgr.checkMode("rtop"):
            SysMgr.jsonEnable = True

            # check root permission #
            if SysMgr.isRoot():
                SysMgr.diskEnable = True
                SysMgr.networkEnable = True

                # disable resources by option #
                disableList = SysMgr.getOption("d")
                if disableList:
                    if "d" in disableList:
                        SysMgr.diskEnable = False
                    if "n" in disableList:
                        SysMgr.networkEnable = False
            else:
                SysMgr.printWarn(
                    "failed to get stats for disk and network "
                    "because there is no root permission"
                )

            # check command requirement #
            if not SysMgr.checkRepTopCond():
                sys.exit(-1)

        # init network resource data #
        if SysMgr.networkEnable and not SysMgr.sysInstance.networkInfo:
            SysMgr.sysInstance.updateNetworkInfo()

        # init disk resource data #
        if SysMgr.diskEnable and not SysMgr.sysInstance.storageData:
            SysMgr.sysInstance.updateStorageInfo()

        # run process / file monitoring #
        TaskAnalyzer()

    @staticmethod
    def loadLibCache():
        try:
            # no ld cache file #
            if not SysMgr.ldCachePath:
                return

            if not os.path.exists(SysMgr.ldCachePath):
                reason = SysMgr.ldCachePath
                SysMgr.ldCachePath = None
                raise Exception("no %s" % reason)

            libDict = {}

            # load libs from cache #
            libList = UtilMgr.convBin2Str(SysMgr.ldCachePath)
            for idx, item in enumerate(libList):
                try:
                    if libList[idx + 1].startswith("/"):
                        value = libList[idx + 1]
                        libDict.setdefault(item, [])
                        libDict[item].append(value)
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

            # load libs form /usr/local/lib #
            for path in ("/usr/local/lib", "/usr/local/lib64"):
                if not os.path.exists(path):
                    continue

                for item in os.listdir(path):
                    if not ".so" in item:
                        continue

                    value = os.path.join(path, item)

                    libDict.setdefault(item, [])
                    libDict[item].append(value)

            # save lib list #
            SysMgr.libCache = libDict
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn("failed to load library cache", reason=True)
            return False

    @staticmethod
    def readZramStats(dev, target=[]):
        stats = {}
        prefix = "/sys/block/" + dev + "/"
        convNum = UtilMgr.convNum
        convSize = UtilMgr.convSize2Unit
        PAGESIZE = SysMgr.PAGESIZE

        for item in os.listdir(prefix):
            if not target or item in target:
                res = SysMgr.readFile(prefix + item, verb=False)
                if not res:
                    continue

                if item == "mm_stat":
                    """
                    orig_data_size   uncompressed size of data stored in this disk.
                                     This excludes same-element-filled pages (same_pages) since
                                     no memory is allocated for them.
                                     Unit: bytes
                    compr_data_size  compressed size of data stored in this disk
                    mem_used_total   the amount of memory allocated for this disk. This
                                     includes allocator fragmentation and metadata overhead,
                                     allocated for this disk. So, allocator space efficiency
                                     can be calculated using compr_data_size and this statistic.
                                     Unit: bytes
                    mem_limit        the maximum amount of memory ZRAM can use to store
                                     the compressed data
                    mem_used_max     the maximum amount of memory zram have consumed to
                                     store the data
                    same_pages       the number of same element filled pages written to this disk.
                                     No memory is allocated for such pages.
                    pages_compacted  the number of pages freed during compaction
                    huge_pages	  the number of incompressible pages
                    """
                    res = list(map(long, res.split(None, 8)[:8]))

                    items = {
                        "orig_data_size": convSize(res[0]),
                        "compr_data_size": convSize(res[1]),
                        "compr_rate": "%d%%"
                        % ((res[1] / float(res[0])) * 100),
                        "mem_used_total": convSize(res[2]),
                        "mem_limit": convSize(res[3]),
                        "mem_used_max": convSize(res[4]),
                        "same_pages": convSize(res[5] * PAGESIZE),
                        "pages_compacted": convNum(res[6] * PAGESIZE),
                        "huge_pages": convNum(res[7] * PAGESIZE),
                    }

                    # remove zero values #
                    for name in list(items):
                        if items[name] in ("0", 0):
                            items.pop(name, None)

                    # merge stats #
                    stats.update(items)
                else:
                    stats[item] = res

        return stats

    @staticmethod
    def findLib(lib, inc=False):
        if not SysMgr.libCache:
            SysMgr.loadLibCache()

        libs = []

        for key, val in SysMgr.libCache.items():
            # convert for specific caches are only consist of absolute paths #
            if key.startswith("/"):
                val = [key]
                key = os.path.basename(key)

            # check path #
            if key == lib:
                pass
            elif not key.startswith(lib):
                continue
            elif inc and key.startswith(lib):
                pass
            elif len(key) <= len(lib):
                continue
            elif key[len(lib)] in (".", "-"):
                pass
            else:
                continue

            # return found values #
            if len(val) > 1:
                SysMgr.printWarn(
                    "multiple libraries [ %s ] exist for %s"
                    % (", ".join(val), key)
                )

            # add path to list #
            libs += val

        return libs

    @staticmethod
    def convSchedPrio(pri, fromNum=False):
        try:
            if fromNum:
                # RT / DEADLINE #
                if pri > 39:
                    if pri == 140:
                        policy = "D"
                    else:
                        policy = "R"

                    pri -= 40
                # NORMAL #
                else:
                    policy = "N"
                    pri = 19 - pri

                return policy + str(pri)
            else:
                policy = pri[0]
                prio = long(pri[1:])

                # convert priority to number(-101 ~ 40) #
                if policy in ("C", "B"):
                    prio += 20
                elif policy in ("R", "F"):
                    prio = (-1) - prio
                elif policy == "D":
                    prio = -101
                elif policy == "I":
                    prio = 40

                return prio
        except SystemExit:
            sys.exit(0)
        except:
            return None

    @staticmethod
    def convertTaskIdInput(taskList=[]):
        ret = SysMgr.selectTaskId()
        if ret:
            newList = []
            for item in taskList:
                newList.append(item.replace("PID", ret))
            return newList
        else:
            return taskList

    @staticmethod
    def selectTaskId():
        if not "TASKMON" in SysMgr.environList:
            return

        SysMgr.printInfo("start monitoring tasks to get target ID...\n")
        netObj = NetworkMgr("server", None, 0)

        # run monitor #
        cid = SysMgr.runTaskMonitor(
            [""],
            wait=False,
            addOpt=["-cGUIDER send PID -X%s:%s" % (netObj.ip, netObj.port)],
        )

        # wait for target ID #
        try:
            SysMgr.exitFlag = True

            ret = netObj.recv().decode()

            SysMgr.killChildren(children=[cid])

            # remove variable to prevent handling again by child processes #
            SysMgr.environList.pop("TASKMON", None)

            SysMgr.exitFlag = False
            return ret
        except SystemExit:
            os._exit(0)
        except:
            SysMgr.printErr("failed to get target ID to be traced", True)
            os._exit(-1)

    @staticmethod
    def getPrintFlag():
        return not "QUIET" in SysMgr.environList

    @staticmethod
    def loadLib(lib, path=False, inc=False):
        if not SysMgr.importPkgItems("ctypes", False):
            return

        # absolute path #
        if path:
            try:
                return CDLL(lib)
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr("failed to load '%s'" % lib, True)
                return

        # search ld.so.cache #
        target = SysMgr.findLib(lib, inc)
        if not target:
            target = ["%s.so" % lib]
            ret = FileAnalyzer.getMapFilePath(SysMgr.pid, lib)
            if ret:
                target.append(ret)

        # load libraries #
        for item in target:
            try:
                res = cdll.LoadLibrary(item)
                if res:
                    return res
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printWarn("failed to load '%s'" % item, reason=True)

    @staticmethod
    def importNative():
        try:
            # do not use native library to improve initialization time #
            raise Exception

            import guider

            guider.check()  # pylint: disable=no-member
            SysMgr.guiderObj = guider
        except:
            pass

    @staticmethod
    def applyKillVal(value):
        if not value:
            SysMgr.printErr("no value to send signal")
            sys.exit(-1)

        SysMgr.checkRootPerm()

        jobs = value.split(",")
        for job in jobs:
            try:
                value = job.split(":")

                if len(value) > 3:
                    raise Exception("wrong input")

                # set task #
                tid = value[0]

                # set signal #
                if len(value) == 1:
                    sig = signal.SIGKILL
                else:
                    sig = SysMgr.getSigNum(value[1])
                    if not sig:
                        raise Exception("wrong signal %s" % value[1])

                if len(value) > 2 and value[2].upper() == "CONT":
                    flag = "CONT"
                else:
                    flag = "ONCE"

                SysMgr.killFilter.append([tid, sig, flag])
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr("failed to set signal", True)
                sys.exit(-1)

    @staticmethod
    def parseAffinityOption(jobs, launch=False):
        if not jobs:
            SysMgr.printErr("wrong option value")
            sys.exit(-1)

        doneList = set()
        for origVal in jobs:
            try:
                value = origVal.split(":")

                if not len(value) in (2, 3):
                    raise Exception("wrong input")

                # set task #
                tid = value[0]
                if tid == "":
                    tid = str(SysMgr.pid)

                # set mask #
                mask = value[1]
                if not mask:
                    raise Exception("wrong input")

                # convert mask values #
                if "+" in mask:
                    maskList = mask.split("+")
                    mask = 0
                    for val in maskList:
                        if not val:
                            continue
                        mask |= 1 << long(val)
                    mask = str(hex(mask))

                if launch:
                    sibling = SysMgr.groupProcEnable
                    targetList = SysMgr.getTids(tid, sibling=sibling)
                    targetList = list(map(long, targetList))
                    targetList = list(set(targetList) - doneList)
                    if targetList:
                        SysMgr.setAffinity(mask, targetList)
                        doneList = set(list(doneList) + targetList)
                    elif doneList:
                        pass
                    else:
                        SysMgr.printWarn("no thread related to '%s'" % tid)

                if len(value) == 3 and value[2].upper() == "CONT":
                    flag = "CONT"
                else:
                    flag = "ONCE"

                SysMgr.affinityFilter.append([tid, mask, flag])
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr("wrong option value '%s'" % origVal, True)
                sys.exit(-1)

    @staticmethod
    def clearStats():
        if SysMgr.sysInstance:
            SysMgr.sysInstance.intervalData = {}
            SysMgr.sysInstance.intervalFileData = {}

        SysMgr.thrEvtCntList = {}

        SysMgr.maxBlkRd = 0
        SysMgr.maxBlkWr = 0
        SysMgr.maxCpuUsage = 0
        SysMgr.maxCpuSteal = 0
        SysMgr.maxGpuMemUsage = 0
        SysMgr.maxGpuUsage = 0
        SysMgr.maxInterval = 0
        SysMgr.maxLoad15m = 0
        SysMgr.maxLoad1m = 0
        SysMgr.maxLoad5m = 0
        SysMgr.maxNetIn = 0
        SysMgr.maxNetOut = 0
        SysMgr.maxNrContextSwitch = 0
        SysMgr.maxNrCore = 0
        SysMgr.maxNrFd = 0
        SysMgr.maxNrIrq = 0
        SysMgr.maxNrProcess = 0
        SysMgr.maxNrTaskAbnormal = 0
        SysMgr.maxNrTaskDie = 0
        SysMgr.maxNrTaskNew = 0
        SysMgr.maxNrThread = 0
        SysMgr.maxPsiCpu = 0
        SysMgr.maxPsiIo = 0
        SysMgr.maxPsiMem = 0
        SysMgr.maxSwapPer = 0
        SysMgr.minDiskFree = SysMgr.maxSize
        SysMgr.minMemAvail = SysMgr.maxSize
        SysMgr.nrOOMKill = 0
        SysMgr.nrTotalOOMKill = -1

    @staticmethod
    def getPeakList(incInter=True):
        peakList = {}
        if incInter and SysMgr.topInstance:
            for n, v in SysMgr.topInstance.intervalPeakData.items():
                if v:
                    peakList[n] = sum(v) / len(v)

        peakList.update(
            {
                "blkRdMB": SysMgr.maxBlkRd,
                "blkWrMB": SysMgr.maxBlkWr,
                "cpuPer": SysMgr.maxCpuUsage,
                "cpuStealPer": SysMgr.maxCpuSteal,
                "diskFreeMB": SysMgr.minDiskFree,
                "gpuPer": SysMgr.maxGpuUsage,
                "gpumemMB": SysMgr.maxGpuMemUsage,
                "intervalMS": SysMgr.maxInterval * 1000,
                "intervalOptSec": SysMgr.intervalEnable,
                "memAvailMB": SysMgr.minMemAvail,
                "netInMB": SysMgr.maxNetIn,
                "netOutMB": SysMgr.maxNetOut,
                "nrContextSwitch": SysMgr.maxNrContextSwitch,
                "nrCore": SysMgr.maxNrCore,
                "nrFd": SysMgr.maxNrFd,
                "nrIrq": SysMgr.maxNrIrq,
                "nrLoad1m": SysMgr.maxLoad1m,
                "nrLoad5m": SysMgr.maxLoad5m,
                "nrLoad15m": SysMgr.maxLoad15m,
                "nrOOMKill": SysMgr.nrTotalOOMKill,
                "nrProcess": SysMgr.maxNrProcess,
                "nrTaskAbnormal": SysMgr.maxNrTaskAbnormal,
                "nrTaskDie": SysMgr.maxNrTaskDie,
                "nrTaskNew": SysMgr.maxNrTaskNew,
                "nrThread": SysMgr.maxNrThread,
                "psiCpuPer": SysMgr.maxPsiCpu,
                "psiIoPer": SysMgr.maxPsiIo,
                "psiMemPer": SysMgr.maxPsiMem,
                "swapPer": SysMgr.maxSwapPer,
                "uptimeSec": SysMgr.uptime,
            }
        )

        # add custom data #
        if SysMgr.topInstance:
            peakList.update(SysMgr.topInstance.customPeakData)

        # remove maximum stats #
        for n in list(peakList):
            if peakList[n] == SysMgr.maxSize:
                peakList.pop(n, None)

        return peakList

    @staticmethod
    def splitFile(
        infile, size=None, outfile=None, count=0, which="START", verb=True
    ):
        pathList = []
        try:
            # check error condition #
            if not size and not count:
                SysMgr.printErr("no size and count info")
                return pathList

            # convert infile to realpath #
            infile = os.path.realpath(infile).rstrip("/")

            # set direction #
            if which == "END":
                direction = 2
                fromStart = False
            elif which == "START":
                direction = 0
                fromStart = True
            else:
                SysMgr.printErr(
                    "failed to split '%s' because wrong direction '%s'"
                    % (infile, which)
                )
                return pathList

            # split file #
            with open(infile, "rb") as fd:
                # get total size #
                fd.seek(0, 2)
                totalSize = fd.tell()

                # convert size #
                if size:
                    nsize = UtilMgr.convUnit2Size(size)
                    if not count:
                        count = long(totalSize / nsize)
                        if totalSize % nsize:
                            count += 1
                elif count:
                    nsize = long(totalSize / count)
                    if totalSize % count:
                        nsize += 1

                # set position #
                fd.seek(0, direction)

                # init file count #
                if fromStart:
                    nrCnt = 0
                else:
                    if count:
                        nrCnt = long(totalSize / nsize) - 1
                        if totalSize % nsize:
                            nrCnt += 1
                        count = nrCnt - count
                    else:
                        nrCnt = count
                        count = 0

                while 1:
                    # get current pos #
                    pos = fd.tell()

                    # get chunk size #
                    if fromStart:
                        chunk = (
                            nsize
                            if pos + nsize <= totalSize
                            else totalSize - pos
                        )
                    else:
                        chunk = nsize if pos - nsize >= 0 else pos
                        fd.seek(0 - chunk, 1)

                    # set pos from current #
                    buf = fd.read(chunk)

                    # rewind pos from end #
                    if not fromStart:
                        fd.seek(0 - chunk, 1)
                        pos = fd.tell()

                    # make a new outfile path #
                    if outfile:
                        if os.path.isdir(outfile):
                            filename = os.path.basename(infile)
                            outpath = os.path.join(outfile, filename)
                            outpath += "_{0:03}".format(nrCnt)
                        else:
                            outpath = outfile + "_{0:03}".format(nrCnt)
                    else:
                        outpath = infile + "_{0:03}".format(nrCnt)
                    outpath = os.path.realpath(outpath)

                    # backup file #
                    if os.path.exists(outpath):
                        SysMgr.backupFile(outpath)

                    # save to a new file #
                    with open(outpath, "wb") as wfd:
                        wfd.write(buf)

                    if verb:
                        SysMgr.printInfo(
                            "%s bytes from offset %s in '%s' are written to '%s'"
                            % (
                                UtilMgr.convNum(chunk),
                                UtilMgr.convNum(pos),
                                infile,
                                outpath,
                            ),
                            prefix=False,
                        )

                    pathList.append(outpath)

                    # get current pos #
                    pos = fd.tell()

                    # increase file count #
                    if fromStart:
                        nrCnt += 1
                    else:
                        nrCnt -= 1

                    # check exit condition #
                    if fromStart:
                        if pos == totalSize:
                            break
                        elif count and nrCnt >= count:
                            break
                    else:
                        if pos == 0:
                            break
                        elif nrCnt <= count:
                            break

            # sort list #
            pathList.sort()

            return pathList
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                "failed to split '%s' by %s" % (infile, size), True
            )
            return pathList

    @staticmethod
    def doLess(inputArg=None):
        pipeInput = None
        stdin = sys.stdin
        wbuf = []
        cursor = 0
        lastWord = ""
        colorWord = ""

        # check input #
        if inputArg:
            if not isinstance(inputArg, list):
                inputArg = [inputArg]
        elif SysMgr.hasMainArg():
            inputArg = SysMgr.getMainArgs()
        elif SysMgr.inputParam:
            inputArg = str(SysMgr.inputParam).split(",")
            inputArg = UtilMgr.cleanItem(inputArg, True)
        elif SysMgr.isWindows:
            # no support pipe for windows #
            SysMgr.printErr("no input for PATH")
            return -1
        elif not os.isatty(0) or os.getenv("LESSPIPE"):
            # check stdin pipe #
            buf = sys.stdin.readline()
            if buf:
                wbuf.append(buf)

            # set file descriptors #
            pipeInput = sys.stdin
            freeFd = SysMgr.getFreeFd()
            os.dup2(1, freeFd)
            stdin = os.fdopen(freeFd, "r")
        else:
            SysMgr.printErr("no input for PATH")
            return -1

        def _printPage(wbuf, cursor):
            SysMgr.clearScreen()

            pbuf = wbuf[cursor : cursor + SysMgr.ttyRows - 2]

            # apply color #
            if colorWord:
                coloredWord = UtilMgr.convColor(colorWord, "REVERSE")
                for pidx in range(len(pbuf)):
                    pbuf[pidx] = UtilMgr.removeColor(pbuf[pidx]).replace(
                        colorWord, coloredWord
                    )

            SysMgr.printPipe(pbuf, pager=False)

        def _readPipe(wbuf, pipeInput):
            while 1:
                l = pipeInput.readline()
                if not l:
                    break
                wbuf.append(l)

        if pipeInput:
            # read data from stdin pipe #
            _readPipe(wbuf, pipeInput)

            # clear screen and print a page #
            _printPage(wbuf, cursor)
        else:
            # convert input files #
            inputArg = UtilMgr.getFileList(inputArg, sort=True, exceptDir=True)
            if not inputArg:
                SysMgr.printErr("no target file")
                return -1

            # print files #
            for path in inputArg:
                fsize = UtilMgr.getFileSizeStr(path)
                SysMgr.printStat(r"start reading '%s%s'..." % (path, fsize))

                try:
                    lines = SysMgr.readFile(path, strip=False).split("\n")
                    wbuf += [
                        twoLine,
                        "%s%s <%s lines>"
                        % (path, fsize, UtilMgr.convNum(len(lines))),
                        twoLine,
                    ]
                    wbuf += lines
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr(
                        "failed to less '%s%s'" % (path, fsize), True
                    )
                    continue

                # clear screen and print a page #
                _printPage(wbuf, cursor)

        def _printStatus(wbuf, cursor):
            if SysMgr.ttyRows == 0:
                return

            totalPage = long(len(wbuf) / SysMgr.ttyRows)
            curPage = totalPage - long((len(wbuf) - cursor) / SysMgr.ttyRows)
            convNum = UtilMgr.convNum

            sys.stdout.write(
                "Line: %s-%s | Page: %s-%s | Size: %s > "
                % (
                    convNum(cursor),
                    convNum(len(wbuf)),
                    convNum(curPage),
                    convNum(totalPage),
                    convNum(sum(len(s) for s in wbuf)),
                )
            )
            sys.stdout.flush()

        def _handleInput(key, cursor, wbuf, lastWord, lastKey):
            # set cmd #
            if key:
                k = key[0]
            else:
                k = ""

            # use cached key #
            if k == "":
                if not lastKey:
                    lastKey = "k"
                k = lastKey

            # update last key #
            lastKey = k

            # exit #
            if k == "q":
                sys.exit(0)

            # start #
            elif k == "g":
                cursor = 0

            # end #
            elif k == "G":
                cursor = len(wbuf)

            # next page #
            elif k in ("\x1b[C", "f"):
                cursor += SysMgr.ttyRows

            # prev page #
            elif k in ("\x1b[D", "b"):
                cursor -= SysMgr.ttyRows

            # prev line #
            elif k in ("\x1b[A", "j"):
                cursor -= 1

            # next line #
            elif k in ("\x1b[B", "k"):
                cursor += 1

            # search #
            elif k == "/" or (lastWord and k in ("n", "N")):
                # update word #
                idx = -1
                if k == "/":
                    lastWord = key[1:]

                # set prev index #
                if k == "N":
                    src = cursor - 1
                    des = -1
                    inc = -1
                # set next index #
                else:
                    src = cursor + 1
                    des = len(wbuf)
                    inc = 1

                # find word #
                for i in xrange(src, des, inc):
                    try:
                        if lastWord in wbuf[i] or re.match(lastWord, wbuf[i]):
                            cursor = idx = i
                            break
                    except SystemExit:
                        sys.exit(0)
                    except:
                        pass

                # print error #
                if idx == -1:
                    sys.stdout.write("< not found: %s >" % lastWord)
                    sys.stdout.flush()
                    stdin.readline()

                return cursor, lastWord, lastKey

            # move to line #
            elif key.isdigit():
                cursor = long(key)

            # update cursor #
            if cursor < 0 or len(wbuf) < SysMgr.ttyRows:
                cursor = 0
            elif cursor > len(wbuf) - SysMgr.ttyRows + 1:
                cursor = max(len(wbuf) - SysMgr.ttyRows, 0) + 2

            return cursor, lastWord, lastKey

        # print status #
        _printStatus(wbuf, cursor)

        lastKey = None

        # handle input #
        while 1:
            try:
                # read input from stdin #
                k = stdin.readline()

                # read data from stdin pipe #
                if pipeInput:
                    _readPipe(wbuf, pipeInput)

                # handle key #
                cursor, lastWord, lastKey = _handleInput(
                    k.strip(), cursor, wbuf, lastWord, lastKey
                )
                if lastWord:
                    colorWord = lastWord

                # clear screen and print a page #
                _printPage(wbuf, cursor)

                # print status #
                _printStatus(wbuf, cursor)
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printWarn("failed to move page", True, True)

    @staticmethod
    def doDump():
        # get argument #
        ret = SysMgr.selectTaskId()
        if ret:
            inputParam = [ret]
        elif SysMgr.hasMainArg():
            inputParam = SysMgr.getMainArgs()
        elif SysMgr.filterGroup:
            inputParam = SysMgr.filterGroup
        else:
            SysMgr.printErr("no input for COMM or PID")
            sys.exit(-1)

        # check condition #
        if not inputParam:
            SysMgr.printErr("no input for COMM or PID")
            sys.exit(-1)
        elif not SysMgr.outPath:
            SysMgr.printErr("no output path")
            sys.exit(-1)
        elif not SysMgr.inputParam:
            SysMgr.printErr("no input for memory area")
            sys.exit(-1)

        # convert comm to pid #
        targetList = []
        for item in inputParam:
            targetList += SysMgr.getTids(item, isThread=False)
        targetList = list(set(targetList))

        # check target #
        if not targetList:
            SysMgr.printErr("no target process")
            sys.exit(-1)
        elif len(targetList) > 1:
            SysMgr.printErr(
                "found multiple tasks [%s]" % SysMgr.getCommList(targetList)
            )
            sys.exit(-1)

        # set args #
        pid = targetList[0]
        meminfo = SysMgr.inputParam
        output = SysMgr.outPath
        if os.path.isdir(output):
            output = "%s/dump.out" % output

        # dump memory #
        Debugger.dumpTaskMemory(pid, meminfo, output)

    @staticmethod
    def readProcStat(fd, name, obj, attr, err=False, retry=True):
        try:
            buf = None
            while 1:
                fd.seek(0)
                buf = fd.readlines()
                # retry to read for shared descriptor #
                if buf or not retry:
                    break
        except SystemExit:
            sys.exit(0)
        except:
            try:
                bufPath = "%s/%s" % (SysMgr.procPath, name)
                fd = open(bufPath, "r")
                setattr(obj, attr, fd)
                buf = fd.readlines()
            except SystemExit:
                sys.exit(0)
            except:
                if err:
                    SysMgr.printOpenErr(bufPath)
                else:
                    SysMgr.printOpenWarn(bufPath)
        return buf

    @staticmethod
    def getBtStatsKernel(path, verb=False):
        return SysMgr.getBtStatsCrash(path, SysMgr.parseKernel, verb)

    @staticmethod
    def getBtStatsLinux1(path, verb=False):
        return SysMgr.getBtStatsCrash(path, SysMgr.parseCrash, verb)

    @staticmethod
    def getBtStatsAndroid(path, verb=False):
        return SysMgr.getBtStatsCrash(path, SysMgr.parseTombstone, verb)

    @staticmethod
    def getBtStatsCrash(path, parser, verb=False):
        if verb:
            # get output size #
            fsize = UtilMgr.getFileSizeStr(path)
            SysMgr.printStat(r"start loading '%s'%s..." % (path, fsize))

        # read data #
        buf = SysMgr.readFile(path, comp=True)
        if not buf:
            return [], []

        # decode data #
        try:
            buf = buf.decode()
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # get crash info #
        sampleList = parser(buf)
        if isinstance(sampleList, dict):
            sampleList = [sampleList]

        resList = []
        taskList = []
        isValidStr = UtilMgr.isValidStr

        for sample in sampleList:
            # init lists #
            resList.append({})
            res = resList[-1]
            taskList.append({})
            taskInfo = taskList[-1]

            # get process name #
            for name in ("name", "Processname"):
                target = sample.get(name, "")
                if not target:
                    continue

                # android tombstone #
                if name == "name":
                    thread, process = target.split(">>>")
                    process = process.strip(" <")
                    taskInfo["process"] = process
                    thread = thread.strip()
                    taskInfo["thread"] = thread
                else:
                    taskInfo["process"] = target
                break

            # get thread name #
            for name in ("Threadname",):
                target = sample.get(name, "")
                if not target:
                    continue
                else:
                    taskInfo["thread"] = target
                    break

            # get ID #
            for n in ("pid", "tid"):
                idinfo = sample.get(n)
                taskInfo[n] = idinfo

            # get timestamp #
            for t in ("date", "Timestamp"):
                ts = sample.get(t)
                if ts:
                    try:
                        ts = str(
                            SysMgr.getPkg("datetime").datetime.strptime(
                                ts, "%a %b %d %H:%M:%S %Y"
                            )
                        )
                    except SystemExit:
                        sys.exit(0)
                    except:
                        pass
                    taskInfo["time"] = ts
                    break

            # check time filter #
            timeFilter = SysMgr.environList.get("TIMEFILTER")
            timeInfo = taskInfo.get("time")
            if timeFilter and timeInfo:
                if not isValidStr(timeInfo, timeFilter):
                    continue

            # check task filter #
            isValid = True
            for filterName in ("PROCFILTER", "THREADFILTER"):
                taskFilter = SysMgr.environList.get(filterName, [])
                taskExFilter = SysMgr.environList.get(
                    filterName.rstrip("FILTER") + "EXFILTER", []
                )
                if not taskFilter and not taskExFilter:
                    continue

                # get target #
                if filterName == "PROCFILTER":
                    target = taskInfo.get("process")
                else:
                    target = taskInfo.get("thread")
                if not target:
                    continue

                # check filter #
                if taskFilter and not isValidStr(target, taskFilter):
                    isValid = False
                    break
                elif taskExFilter and isValidStr(target, taskExFilter):
                    isValid = False
                    break
            if not isValid:
                continue

            # convert backtrace #
            backtrace = sample.get("backtrace", [])
            stack = ["%s [%s]" % (a[2], a[0].strip("[]")) for a in backtrace]
            if stack:
                stackName = " <- ".join(stack)
                res[stackName] = res.get(stackName, 0) + 1

        return resList, taskList

    @staticmethod
    def parseKernel(buf):
        # check backtrace #
        try:
            if not "Backtrace:" in buf and not "Call trace:":
                return {}
        except:
            return {}

        resList = []

        # get header #
        idx = -1
        lines = buf.split("\n")
        while 1:
            idx += 1
            if idx >= len(lines):
                break
            else:
                l = lines[idx]

            # get context #
            m = re.match(
                (
                    r"^\[\s*(?P<time>\S+)\] CPU:\s*(?P<cpu>[0-9]+)\s*"
                    r"PID:\s*(?P<pid>[0-9]+)\s*Comm:\s*(?P<comm>.*) (Not tainted|Tainted:)"
                ),
                l,
            )
            if not m:
                continue

            # get task info #
            d = m.groupdict()
            pid = d["pid"]
            comm = d["comm"]

            # find backtrace #
            while 1:
                idx += 1
                if idx >= len(lines):
                    break
                else:
                    l = lines[idx]

                m = re.match(
                    r"^\[\s*(?P<time>\S+)\] (Backtrace|Call trace):", l
                )
                if m:
                    break
                else:
                    continue

            if idx >= len(lines):
                break

            resList.append({"pid": pid, "Processname": comm})
            res = resList[-1]
            res["backtrace"] = []

            # parse backtrace #
            while 1:
                idx += 1
                if idx >= len(lines):
                    break
                else:
                    l = lines[idx]

                # check start #
                if re.match(
                    r"^\[\s*(?P<time>\S+)\] (Exception stack\(|---\[ end trace )",
                    l,
                ):
                    break

                # get function #
                m = None
                for s in (
                    r"^\[\s*(?P<time>\S+)\] \[\<(?P<addr>\S+)\>\] \((?P<func>\S+)\)",
                    r"^\[\s*(?P<time>\S+)\] \[\<(?P<addr>\S+)\>\] (?P<func>\S+)\+",
                    r"^\[\s*(?P<time>\S+)\]\s*(?P<func>\S+)\+",
                ):
                    m = re.match(s, l)
                    if m:
                        break
                if not m:
                    continue

                d = m.groupdict()
                res["backtrace"].append(
                    ["KERNEL", long(d.get("addr", "0"), 16), d["func"], 0x0]
                )

        return resList

    @staticmethod
    def parseCrash(buf):
        if buf.startswith("[task info]"):
            return SysMgr.parseCrash1(buf)
        if buf.startswith("ProblemType: Crash"):
            return SysMgr.parseCrash2(buf)
        else:
            return {}

    @staticmethod
    def parseCrash1(buf):
        res = {}
        kfilter = (
            "Processname:*",
            "Threadname:*",
            "signal:*",
        )

        # get header #
        idx = 1
        lines = buf.split("\n")[1:]
        for l in lines:
            if not l or not l[0]:
                idx += 1
                continue
            elif l.startswith("[backtrace stack]"):
                break
            elif UtilMgr.isValidStr(l, kfilter):
                d = l.split(":", 1)
                res[d[0].strip()] = d[1].strip(" '")
            elif l.startswith("pid:"):
                for d in l.split(","):
                    d = d.split(":", 1)
                    res[d[0].strip()] = d[1].strip()
            elif l.startswith("date ="):
                d = l.split(" ", 2)
                res[d[0].strip()] = d[2].strip()

            idx += 1

        # get backtrace #
        res["backtrace"] = []
        for l in lines[idx + 1 :]:
            if not l.lstrip():
                break

            try:
                # split items #
                items = l.strip().split(" ", 5)
                if items[1]:
                    start = 1
                else:
                    start = 2

                # get values #
                items = items[start:]
                if len(items) == 4:
                    pc, boffset, sp, func = items
                else:
                    pc, boffset, sp, func, etc = items
                    func += etc

                # remove useless data #
                path = func.rsplit(" ", 1)[1]
                boffset = long(boffset.split("=", 1)[1], 16)
                func, foffset = (
                    func.rsplit(")", 1)[0].lstrip("(").rsplit("+", 1)
                )
                foffset = long(foffset, 16)

                res["backtrace"].append([path, boffset, func, foffset])
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printWarn(
                    "failed to parse '%s'" % l.strip(), True, True
                )

        return res

    @staticmethod
    def parseCrash2(buf):
        res = {}
        kfilter = (
            "Date:*",
            "ProcCmdline:*",
        )

        # get header #
        idx = 0
        lines = buf.split("\n")
        for l in lines:
            if not l or not l[0]:
                idx += 1
                continue
            elif l.startswith("Stacktrace:"):
                break
            elif UtilMgr.isValidStr(l, kfilter):
                d = l.split(":", 1)
                res[d[0].strip()] = d[1].strip(" '")

            idx += 1

        # get backtrace #
        res["backtrace"] = []
        for l in lines[idx + 1 :]:
            if not l or l[0] != " ":
                break
            elif not l.startswith(" #"):
                continue

            try:
                m = re.match(
                    r"^ #[0-9]+\s*(?P<addr>\S+) in (?P<func>.*) \(.*\)", l
                )
                if not m:
                    continue

                d = m.groupdict()
                res["backtrace"].append(
                    ["??", d.get("addr", "0"), d.get("func", "??"), 0x0]
                )
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printWarn(
                    "failed to parse '%s'" % l.strip(), True, True
                )

        return res

    @staticmethod
    def parseTombstoneJava(lines, res, idx):
        res["backtrace"] = []
        for l in lines[idx + 1 :]:
            if not l.lstrip():
                break

            try:
                # split items #
                items = l.strip().split(" ", 1)[1]
                func, path = items.split("(", 1)
                path, line = path.split(":", 1)
                line = line.rstrip(")")

                res["backtrace"].append([path, line, func, "0x0"])
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printWarn(
                    "failed to parse '%s'" % l.strip(), True, True
                )
        return res

    @staticmethod
    def parseTombstoneLines(lines, res, idx, cnt, start):
        res["backtrace"] = []
        for l in lines[idx + 1 :]:
            if not l.lstrip():
                break

            try:
                # split items #
                items = l.strip().split(" ", cnt)[start:]
                if len(items) == 4:
                    boffset, _, path, func = items
                else:
                    boffset, _, path = items
                    func = "??"

                # unknown #
                if path.startswith("<"):
                    func = path = "??"
                # remove offset info #
                elif func.startswith("(offset "):
                    func = func.split(")", 1)[1]
                    if not func:
                        func = "??"

                # remove BuildId and split function and offset #
                func = (
                    func.rsplit("(BuildId:", 1)[0].strip("( )").rsplit("+", 1)
                )
                if len(func) == 2:
                    func, foffset = func
                else:
                    func = func[0]
                    foffset = 0

                res["backtrace"].append(
                    [path, "0x" + boffset.lstrip("0"), func, foffset]
                )
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printWarn(
                    "failed to parse '%s'" % l.strip(), True, True
                )
        return res

    @staticmethod
    def parseOldTombstone(buf):
        res = {}
        kfilter = (
            "Package:*",
            "Flags:*",
            "Build:*",
            "Revision:*",
        )

        # get header #
        idx = 0
        lines = buf.split("\n")
        isNative = True
        for l in lines:
            if not l or not l[0]:
                idx += 1
                continue
            elif l.startswith("backtrace:"):
                isNative = True
                break
            elif l.startswith("java.lang.NullPointerException"):
                isNative = False
                break
            elif UtilMgr.isValidStr(l, kfilter):
                d = l.split(":", 1)
                res[d[0].strip()] = d[1].strip(" '")
            elif l.startswith("pid:"):
                for d in l.split(","):
                    d = d.split(":", 1)
                    res[d[0].strip()] = d[1].strip()
            elif l.startswith("signal "):
                d = l.split(" ", 1)
                res[d[0].strip()] = d[1].strip()
            elif l.startswith("Process:"):
                res["Processname"] = l.split(": ", 1)[1]

            idx += 1

        # get backtrace #
        if isNative:
            res = SysMgr.parseTombstoneLines(lines, res, idx, 6, 3)
        else:
            res = SysMgr.parseTombstoneJava(lines, res, idx)

        return res

    @staticmethod
    def parseNewTombstone(buf):
        res = {}
        kfilter = (
            "ABI:*",
            "Timestamp:*",
            "Process uptime:*",
            "Cmdline:*",
            "uid:*",
        )

        # get header #
        idx = 1
        lines = buf.split("\n")[1:]
        for l in lines:
            if not l or not l[0]:
                idx += 1
                continue
            elif l.startswith("backtrace:"):
                break
            elif UtilMgr.isValidStr(l, kfilter):
                d = l.split(":", 1)
                res[d[0].strip()] = d[1].strip(" '")
            elif l.startswith("pid:"):
                for d in l.split(","):
                    d = d.split(":", 1)
                    res[d[0].strip()] = d[1].strip()
            elif l.startswith("signal "):
                d = l.split(" ", 1)
                res[d[0].strip()] = d[1].strip()

            idx += 1

        # get backtrace #
        res = SysMgr.parseTombstoneLines(lines, res, idx, 5, 2)

        return res

    @staticmethod
    def parseTombstone(buf):
        if buf.startswith("*** "):
            return SysMgr.parseNewTombstone(buf)
        elif buf.startswith("Process:"):
            return SysMgr.parseOldTombstone(buf)
        else:
            return {}

    @staticmethod
    def getFreeFd(pid="self"):
        try:
            return (
                long(
                    sorted(
                        os.listdir("%s/self/fd" % SysMgr.procPath),
                        key=lambda x: long(x),
                    )[-1]
                )
                + 1
            )
        except SystemExit:
            sys.exit(0)
        except:
            return -1

    @staticmethod
    def getNrFd(pid):
        # get the number of fds #
        try:
            path = "%s/%s/fd" % (SysMgr.procPath, pid)
            return len(os.listdir(path))
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printOpenWarn(path)
            return 0

    @staticmethod
    def getIdleMap():
        try:
            with open("/sys/kernel/mm/page_idle/bitmap", "rb") as fd:
                return fd.read(8888888)
        except SystemExit:
            sys.exit(0)
        except:
            return None

    @staticmethod
    def handleIdlePage(pfn, write=True, flush=True, bitmap=None):
        try:
            # check pfn 0 #
            if not pfn:
                return None

            # set permission #
            if write:
                perm = "wb"
            else:
                perm = "rb"

            # get fd #
            fd = SysMgr.getCachedFd("/sys/kernel/mm/page_idle/bitmap", perm)
            if not fd:
                sys.exit(-1)

            # define size #
            word = 8
            bit = word * 8

            # set pos #
            offset = long(pfn / bit) * word
            if not bitmap:
                fd.seek(offset, 0)

            # handle operation #
            if write:
                val = fd.write(struct.pack("Q", 2**bit - 1))
                if flush:
                    fd.flush()
                return val
            else:
                if bitmap:
                    val = bitmap[offset : offset + 8]
                else:
                    val = fd.read(word)

                val = struct.unpack("Q", val)[0]
                if val & 1 << (pfn % 64):
                    return True
                else:
                    return False
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn(
                "failed to mark idle page (PFN: %s)" % hex(pfn), reason=True
            )
            return None

    @staticmethod
    def clearPageRefs(pid, val):
        """
        The /proc/PID/clear_refs is used to reset the PG_Referenced and ACCESSED/YOUNG
        bits on both physical and virtual pages associated with a process, and the
        soft-dirty bit on pte (see Documentation/admin-guide/mm/soft-dirty.rst
        for details).

        To clear the bits for all the pages associated with the process
            > echo 1 > /proc/PID/clear_refs

        To clear the bits for the anonymous pages associated with the process
            > echo 2 > /proc/PID/clear_refs

        To clear the bits for the file mapped pages associated with the process
            > echo 3 > /proc/PID/clear_refs

        To clear the soft-dirty bit
            > echo 4 > /proc/PID/clear_refs

        To reset the peak resident set size ("high water mark") to the process's
        current value:
            > echo 5 > /proc/PID/clear_refs

        Any other value written to /proc/PID/clear_refs will have no effect.
        """

        try:
            path = "%s/%s/clear_refs" % (SysMgr.procPath, pid)
            with open(path, "w") as fd:
                fd.write(val)
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to write '%s' to %s" % (val, path), True)

    @staticmethod
    def doMount(args=None):
        # get argument #
        if args:
            value = args
        elif SysMgr.hasMainArg():
            value = SysMgr.getMainArgs(False, ":")
        elif SysMgr.inputParam:
            value = SysMgr.inputParam
        else:
            SysMgr.printErr(
                (
                    "wrong value to mount filesystem, "
                    "input in the format DEV:DIR:FS:FLAGS:DATA"
                )
            )
            sys.exit(-1)

        # backup input value #
        origVal = deepcopy(value)

        # mount a filesystem #
        try:
            # convert flags #
            flags = 0
            if len(value) >= 4:
                for item in value[3].split(","):
                    try:
                        name = "MS_%s" % item.upper()
                        flags |= ConfigMgr.MOUNT_TYPE[name]
                    except SystemExit:
                        sys.exit(0)
                    except:
                        pass

                # update flags #
                value[3] = flags

            # get flags #
            if flags > 0:
                flagStr = "|".join(
                    UtilMgr.getFlagList(flags, ConfigMgr.MOUNT_TYPE)
                )
                flagStr = " with %s" % flagStr
            else:
                flagStr = ""

            # mount #
            ret = SysMgr.mount(*value)
            if ret == 0:
                SysMgr.printInfo(
                    "mounted %s on '%s' to '%s'%s successfully"
                    % (
                        value[2],
                        os.path.realpath(value[0]),
                        os.path.realpath(value[1]),
                        flagStr,
                    )
                )
            else:
                errReason = SysMgr.getErrReason()
                SysMgr.printErr(
                    "failed to mount '%s' because %s"
                    % (":".join(origVal), errReason)
                )
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to mount '%s'" % ":".join(origVal), True)

    @staticmethod
    def doUnmount(args=None):
        # get argument #
        if args:
            value = args
        elif SysMgr.hasMainArg():
            value = SysMgr.getMainArgs(False, ":")
        elif SysMgr.inputParam:
            value = SysMgr.inputParam
        else:
            SysMgr.printErr(
                (
                    "wrong value to unmount filesystem, "
                    "input in the format TARGET:FLAGS"
                )
            )
            sys.exit(-1)

        # backup input value #
        origVal = deepcopy(value)

        # mount a filesystem #
        try:
            # convert flags #
            flags = 0
            if len(value) >= 2:
                for item in value[1].split(","):
                    try:
                        name = "MNT_%s" % item.upper()
                        flags |= ConfigMgr.UMOUNT_TYPE[name]
                    except SystemExit:
                        sys.exit(0)
                    except:
                        pass

                # update flags #
                value[1] = flags

            # get flags #
            if flags > 0:
                flagStr = "|".join(
                    UtilMgr.getFlagList(flags, ConfigMgr.UMOUNT_TYPE)
                )
                flagStr = " with %s" % flagStr
            else:
                flagStr = ""

            # mount #
            ret = SysMgr.umount(*value)
            if ret == 0:
                SysMgr.printInfo(
                    "unmounted '%s'%s successfully"
                    % (os.path.realpath(value[0]), flagStr)
                )
            else:
                errReason = SysMgr.getErrReason()
                SysMgr.printErr(
                    "failed to unmount '%s' because %s"
                    % (":".join(origVal), errReason)
                )
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to unmount '%s'" % ":".join(origVal), True)

    @staticmethod
    def initLibbacktrace():
        if "NOLIBBT" in SysMgr.environList:
            SysMgr.libbacktraceObj = -1
        elif SysMgr.libbacktraceObj:
            return
        else:
            SysMgr.libbacktraceObj = SysMgr.loadLib(SysMgr.libbacktracePath)
            if SysMgr.libbacktraceObj:
                libcc = SysMgr.libbacktraceObj

                # define BacktraceMap* BacktraceMap::Create(pid_t pid, bool /*uncached*/) #
                libcc._ZN12BacktraceMap6CreateEib.argtypes = [c_int, c_bool]
                libcc._ZN12BacktraceMap6CreateEib.restype = c_void_p

                # define Backtrace* Backtrace::Create(pid_t pid, pid_t tid, BacktraceMap* map) #
                libcc._ZN9Backtrace6CreateEiiP12BacktraceMap.argtypes = [
                    c_int,
                    c_int,
                    c_void_p,
                ]
                libcc._ZN9Backtrace6CreateEiiP12BacktraceMap.restype = c_void_p

                # define bool UnwindStackPtrace::Unwind(size_t num_ignore_frames, void* context) #
                libcc._ZN17UnwindStackPtrace6UnwindEjPv.argtypes = [
                    c_void_p,
                    c_uint,
                    c_void_p,
                ]
                libcc._ZN17UnwindStackPtrace6UnwindEjPv.restype = c_bool

                # define std::string Backtrace::FormatFrameData(size_t frame_num) #
                libcc._ZN9Backtrace15FormatFrameDataEj.argtypes = [
                    c_void_p,
                    c_uint,
                ]
                libcc._ZN9Backtrace15FormatFrameDataEj.restype = c_void_p

                SysMgr.printInfo("enabled libbacktrace")

                # TODO: implement printing backtrace part #
                # assure ptrace_attached status #
                TID = long(SysMgr.filterGroup[0])
                PID = long(SysMgr.getTgid(TID))
                btmap = libcc._ZN12BacktraceMap6CreateEib(PID, 0)
                bt = libcc._ZN9Backtrace6CreateEiiP12BacktraceMap(
                    PID, TID, c_void_p(btmap)
                )
                while 1:
                    bt = libcc._ZN9Backtrace6CreateEiiP12BacktraceMap(
                        PID, TID, 0
                    )
                    print(libcc._ZN17UnwindStackPtrace6UnwindEjPv(bt, 0, 0))
                    print(
                        libcc._ZN9Backtrace15FormatFrameDataEj(bt, c_uint(0))
                    )
                    time.sleep(1)
            else:
                SysMgr.libbacktraceObj = -1

    @staticmethod
    def initLibcorkscrew():
        if "NOLIBCORK" in SysMgr.environList:
            SysMgr.libcorkscrewObj = -1
        elif SysMgr.libcorkscrewObj:
            return
        else:
            SysMgr.libcorkscrewObj = SysMgr.loadLib(SysMgr.libcorkscrewPath)
            if SysMgr.libcorkscrewObj:
                libcc = SysMgr.libcorkscrewObj

                # define load_ptrace_context #
                libcc.load_ptrace_context.argtypes = [c_int]
                libcc.load_ptrace_context.restype = c_void_p

                # define free_ptrace_context #
                libcc.free_ptrace_context.argtypes = [c_void_p]
                libcc.free_ptrace_context.restype = None

                # define unwind_backtrace_ptrace #
                libcc.unwind_backtrace_ptrace.argtypes = [
                    c_int,
                    c_void_p,
                    c_void_p,
                    c_size_t,
                    c_size_t,
                ]
                libcc.unwind_backtrace_ptrace.restype = c_size_t

                # define get_backtrace_symbols_ptrace #
                libcc.get_backtrace_symbols_ptrace.argtypes = [
                    c_void_p,
                    c_void_p,
                    c_size_t,
                    c_void_p,
                ]
                libcc.get_backtrace_symbols_ptrace.restype = None

                SysMgr.printInfo("enabled libcorkscrew")
            else:
                SysMgr.libcorkscrewObj = -1

    @staticmethod
    def doRlimit():
        # get argument #
        if SysMgr.hasMainArg():
            value = SysMgr.getMainArgs()
        elif SysMgr.filterGroup:
            value = SysMgr.filterGroup
        else:
            SysMgr.printErr(
                (
                    "wrong value to change resource limit, "
                    "input in the format TID|COMM:RTYPE:SLIM:HLIM"
                )
            )
            sys.exit(-1)

        for item in value:
            conf = item.split(":")
            if len(conf) != 1 and len(conf) != 4:
                SysMgr.printErr(
                    (
                        "wrong value to change resource limit, "
                        "input in the format TID|COMM:RTYPE:SLIM:HLIM"
                    )
                )
                sys.exit(-1)

            conf = UtilMgr.cleanItem(conf, False)

            # get tasks #
            target = SysMgr.getTids(conf[0])
            if not target:
                SysMgr.printErr("no task related to '%s'" % conf[0])
                sys.exit(-1)

            # get return type #
            if len(conf) == 4:
                rtype = conf[1]
                slim, hlim = list(map(long, conf[2:4]))

            # apply new limits #
            for tid in target:
                # get comm for the task #
                comm = SysMgr.getComm(tid, cache=True)

                if len(conf) == 1:
                    rlist = SysMgr.readProcData(tid, "limits")
                    if rlist:
                        SysMgr.printPipe(
                            "\n[Task Limit Info] [%s(%s)]\n%s\n%s%s"
                            % (comm, tid, twoLine, "".join(rlist), oneLine)
                        )
                    continue

                # convert resource type #
                rtype = rtype.upper()
                if not rtype in ConfigMgr.RLIMIT_TYPE:
                    candidate = "RLIMIT_" + rtype
                    if not candidate in ConfigMgr.RLIMIT_TYPE:
                        SysMgr.printErr(
                            "failed to change rlimit for %s because wrong resource"
                            % rtype
                        )
                        return False
                    rtype = candidate

                # change resource limit #
                ret = SysMgr.chRlimit(long(tid), rtype, slim, hlim)
                if ret:
                    SysMgr.printInfo(
                        "changed %s to %s/%s for %s(%s)"
                        % (
                            rtype,
                            UtilMgr.convNum(ret[0]),
                            UtilMgr.convNum(ret[1]),
                            comm,
                            tid,
                        )
                    )
                else:
                    SysMgr.printErr(
                        "failed to change %s to %s/%s for %s(%s)"
                        % (
                            rtype,
                            UtilMgr.convNum(slim),
                            UtilMgr.convNum(hlim),
                            comm,
                            tid,
                        )
                    )

    @staticmethod
    def getAvailMemInfo(retstr=True, unit=None):
        try:
            # read memory stats #
            memBuf = SysMgr.getMemInfo()

            # parse lines #
            memData = {}
            for line in memBuf:
                if line.startswith("Buffers"):
                    break

                memList = line.split()
                memData[memList[0][:-1]] = long(memList[1])

            # get available memory #
            if memData.get("MemAvailable"):
                sysMemStr = memData["MemAvailable"] << 10
            elif "MemFree" in memData:
                sysMemStr = memData["MemFree"] << 10
            else:
                sysMemStr = 0

            # calculate again #
            try:
                if sysMemStr != 0:
                    raise Exception()

                for line in memBuf:
                    memList = line.split()
                    if memList[0][:-1] in memData:
                        continue
                    memData[memList[0][:-1]] = long(memList[1])

                sysMemStr = (
                    memData["MemFree"]
                    + memData["Inactive(file)"]
                    + memData["SReclaimable"]
                    + memData["SwapCached"]
                )
                sysMemStr = sysMemStr << 10
            except SystemExit:
                sys.exit(0)
            except:
                pass

            if not retstr:
                return sysMemStr

            sysMemStr = UtilMgr.convSize2Unit(
                sysMemStr, isInt=False, unit=unit
            )
        except SystemExit:
            sys.exit(0)
        except:
            sysMemStr = 0
        finally:
            return sysMemStr

    @staticmethod
    def updateBatInfo():
        try:
            batPath = "/sys/class/power_supply"
            if not os.path.exists(batPath):
                return

            SysMgr.battery = ""

            stats = {
                "capacity": 0,
                "status": "",
                "energy_now": 0,
                "power_now": 0,
            }

            for d in os.listdir(batPath):
                subpath = os.path.join(batPath, d)
                for sub in os.listdir(subpath):
                    if not sub in stats:
                        continue

                    data = SysMgr.readFile(os.path.join(subpath, sub))
                    if sub in ("capacity", "energy_now", "power_now"):
                        stats[sub] += long(data)
                    elif sub == "status" and not data.upper().startswith(
                        "DIS"
                    ):
                        stats[sub] = "+"

            # check capacity #
            if stats["capacity"] == 0:
                return

            SysMgr.battery = (
                stats["capacity"],
                long(stats["energy_now"] / float(stats["power_now"]) * 3600)
                if stats["power_now"]
                else 0,
                stats["status"],
            )
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn("failed to get battery info", reason=True)

    @staticmethod
    def updateNrRun(path):
        if not path or not os.path.isdir(path):
            SysMgr.printErr(
                (
                    "failed to update sequence number of execution "
                    "because wrong directory path '%s'"
                )
                % path
            )
            return

        # get the file list sorted by name in the output dir #
        flist = sorted(
            filter(
                lambda x: os.path.isfile(os.path.join(path, x)),
                os.listdir(path),
            ),
            reverse=True,
        )

        # get the number of last execution #
        for fname in flist:
            m = re.match(r"^guider_(?P<run>[0-9]+)_*", fname)
            if not m:
                continue

            # update the execution number #
            d = m.groupdict()
            SysMgr.nrRun = long(d["run"]) + 1
            return

    @staticmethod
    def getMemInfo():
        # return mem buf #
        return SysMgr.readProcStat(
            SysMgr.memFd, "meminfo", SysMgr, "memFd", True
        )

    @staticmethod
    def getVmFlags(flags=None, retList=False, retStr=False):
        vmList = {
            "READ": "rd",  # readable
            "WRITE": "wr",  # writable
            "EXE": "ex",  # executable
            "SHARED": "sh",  # shared
            "MAYRD": "mr",  # may read
            "MAYWR": "mw",  # may write
            "MAYEXE": "me",  # may execute
            "MAYSHARE": "ms",  # may share
            "STACKDOWN": "gd",  # stack segment grows down
            "PFNRANGE": "pf",  # pure PFN range
            "DISABLEWR": "dw",  # disabled write to the mapped file
            "LOCKED": "lo",  # pages are locked in memory
            "IO": "io",  # memory mapped I/O area
            "SEQRD": "sr",  # sequential read advise provided
            "RANDRD": "rr",  # random read advise provided
            "DONTCOPY": "dc",  # do not copy area on fork
            "DONTREMAP": "de",  # do not expand area on remapping
            "ACCOUNT": "ac",  # area is accountable
            "NOSWAP": "nr",  # swap space is not reserved for the area
            "HUGETLB": "ht",  # area uses huge tlb pages
            "SYNCFAULT": "sf",  # perform synchronous page faults (since Linux 4.15)
            "NOLINEAR": "nl",  # non-linear mapping (removed in Linux 4.0)
            "ARCH": "ar",  # architecture specific flag
            "WIPEONFORK": "wf",  # wipe on fork (since Linux 4.14)
            "DONTDUMP": "dd",  # do not include area into core dump
            "SOFTDIRTY": "sd",  # soft-dirty flag (since Linux 3.13)
            "MIXMAP": "mm",  # mixed map area
            "HUGEPAGE": "hg",  # huge page advise flag
            "NOHUGEPAGE": "nh",  # no-huge page advise flag
            "MERGE": "mg",  # mergeable advise flag
            "MISSING": "um",  # userfaultfd missing pages tracking (since Linux 4.3)
            "WPROTECT": "uw",  # userfaultfd wprotect pages tracking (since Linux 4.3)
        }

        # check vmflag list #
        if retList:
            return vmList
        elif retStr:
            return " ".join(vmList)

        # check type #
        if not isinstance(flags, list):
            SysMgr.printErr(
                "failed to convert '%s' to vmflags because it is not list"
                % flags
            )
            sys.exit(0)

        flagList = []
        for item in flags:
            try:
                flagList.append(vmList[item.upper()])
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr("failed to convert '%s' to vmflag" % item)
                sys.exit(0)

        return flagList

    @staticmethod
    def madvise(addr, length, advise):
        try:
            # load libc #
            ret = SysMgr.loadLibcObj()
            if not ret:
                raise Exception("no library")
        except SystemExit:
            sys.exit(0)
        except:
            sys.exit(-1)

        # prlimit #
        if not hasattr(SysMgr.libcObj, "madvise"):
            SysMgr.printErr("no madvise in libc")
            sys.exit(-1)

        # define prototype #
        SysMgr.libcObj.madvise.argtypes = (POINTER(None), c_size_t, c_int)
        SysMgr.libcObj.madvise.restype = c_int

        # convert page-aligned size #
        start, end, addr, length = UtilMgr.getPageAddrs(addr, length)

        return SysMgr.libcObj.madvise(addr, length, advise)

    @staticmethod
    def chRlimit(pid, rtype, slim, hlim):
        rindex = ConfigMgr.RLIMIT_TYPE.index(rtype)

        # resource package #
        try:
            resource = SysMgr.getPkg("resource", False, True)
            if resource:
                resource.prlimit(pid, rindex, (slim, hlim))
                soft, hard = resource.prlimit(pid, rindex)
                return (soft, hard)
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # get ctypes object #
        if not SysMgr.importPkgItems("ctypes", False):
            return

        class rlimit(Structure):
            _fields_ = (("rlim_cur", c_ulong), ("rlim_max", c_ulong))

        # try to get maxFd by standard library call #
        try:
            # load libc #
            ret = SysMgr.loadLibcObj()
            if not ret:
                raise Exception("no library")

            # create new object #
            rlim = rlimit()
            rlim.rlim_cur = c_ulong(slim)
            rlim.rlim_max = c_ulong(hlim)

            # prlimit #
            if hasattr(SysMgr.libcObj, "prlimit") or hasattr(
                SysMgr.libcObj, "prlimit64"
            ):
                # define real function #
                if hasattr(SysMgr.libcObj, "prlimit"):
                    func = SysMgr.libcObj.prlimit
                else:
                    func = SysMgr.libcObj.prlimit64

                func.argtypes = (
                    c_int,
                    c_int,
                    POINTER(rlimit),
                    POINTER(rlimit),
                )
                func.restype = c_int

                # create origin object #
                oldrlim = rlimit()

                ret = func(
                    c_int(os.getpid()), rindex, byref(rlim), byref(oldrlim)
                )
                if ret != 0:
                    raise Exception("error return %s" % ret)
            # getrlimit / setrlimit #
            elif hasattr(SysMgr.libcObj, "getrlimit"):
                SysMgr.libcObj.getrlimit.argtypes = (c_int, POINTER(rlimit))
                SysMgr.libcObj.getrlimit.restype = c_int

                # set resource value #
                ret = SysMgr.libcObj.setrlimit(rindex, byref(rlim))
                if ret != 0:
                    raise Exception("error return %s" % ret)

                # get new resource value #
                ret = SysMgr.libcObj.getrlimit(rindex, byref(rlim))
            else:
                raise Exception("no function")

            return (rlim.rlim_cur, rlim.rlim_max)
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn(
                "failed to change %s to (%s,%s)" % (rtype, slim, hlim),
                reason=True,
            )

    @staticmethod
    def doStrings():
        # get argument #
        if SysMgr.hasMainArg():
            inputParam = SysMgr.getMainArg()
        elif SysMgr.inputParam:
            inputParam = SysMgr.inputParam
        else:
            SysMgr.printErr("no input for PATH")
            sys.exit(-1)

        # SysMgr.setStream(cut=False)

        # get file size #
        if os.path.exists(inputParam):
            # get output size #
            fsize = UtilMgr.getFileSizeStr(inputParam)
        else:
            SysMgr.printErr("no %s file" % inputParam)
            sys.exit(-1)

        SysMgr.printStat("start reading %s%s..." % (inputParam, fsize))

        # convert binary file to string #
        clist = UtilMgr.convBin2Str(inputParam, pos=SysMgr.showAll)
        if clist:
            SysMgr.printPipe()
        else:
            SysMgr.printErr("no available string")
            return

        convColor = UtilMgr.convColor

        # print position #
        if SysMgr.showAll:
            lastPos = sorted(clist.values())[-1]
            maxDigit = len(hex(lastPos))

            # print strings #
            for string, pos in sorted(clist.items(), key=lambda e: e[1]):
                if not UtilMgr.isValidStr(string, inc=True, ignCap=True):
                    continue
                SysMgr.printPipe(
                    "{0:>{digit}} {1}".format(
                        convColor(hex(pos), "BLUE"),
                        convColor(string, "RED"),
                        digit=maxDigit,
                    )
                )
        else:
            if SysMgr.filterGroup:
                for string in clist:
                    if not UtilMgr.isValidStr(string, inc=True, ignCap=True):
                        continue
                    SysMgr.printPipe(convColor(string, "RED"))
            else:
                SysMgr.printPipe("\n".join(clist))

    @staticmethod
    def restart(cmd=[], env=None):
        try:
            if not cmd:
                cmd = SysMgr.getCmdline(os.getpid()).split()

            if not env:
                env = deepcopy(os.environ)

            os.execvpe(cmd[0], cmd, env)
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to restart %s" % __module__, True)

    @staticmethod
    def removeCgroup(path):
        try:
            # init system context #
            if not SysMgr.sysInstance:
                SysMgr.initSystemContext()

            # get cgroup path #
            cgroupPath = SysMgr.sysInstance.getCgroupPath()
            if not cgroupPath:
                raise Exception("access to cgroup filesystem failed")

            # check and get root path #
            found = False
            for item in cgroupPath:
                if not path.startswith(item):
                    continue
                elif cgroupPath[item].startswith("cgroup2"):
                    continue

                found = True
                item = item[: item.rfind("/", 1)]
                root = UtilMgr.lstrip(path, item).split("/")
                root = UtilMgr.cleanItem(root)
                rootPath = os.path.join(item, root[0], "tasks")
                break

            if not found:
                raise Exception("wrong directory path")

            # find all tasks files #
            targetList = UtilMgr.getFiles(path, ["tasks"])

            # get all tids #
            while 1:
                tids = []
                for f in targetList:
                    try:
                        tids += SysMgr.readFile(f).split("\n")
                    except SystemExit:
                        sys.exit(0)
                    except:
                        pass

                # check items #
                if not UtilMgr.cleanItem(tids):
                    break

                # remove all tasks from sub-groups #
                SysMgr.writeFile(rootPath, tids)

                # wait for a moment #
                time.sleep(0.1)

            # remove target directory #
            for p in sorted(
                targetList, key=lambda x: x.count("/"), reverse=True
            ):
                try:
                    os.rmdir(UtilMgr.rstrip(p, "/tasks"))
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printWarn("failed to remove '%s'" % p, True, True)

            return True
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn("failed to remove '%s'" % path, True, True)
            return False

    @staticmethod
    def limitBlock(pids, attrs, devices=[]):
        if not pids:
            return

        # check root permission #
        SysMgr.checkRootPerm(msg="limit block usage using cgroup")

        # check subsystem #
        blkPath = SysMgr.getCgroupSubPath("blkio", False)
        if not blkPath:
            return

        # set device list for all #
        if not devices:
            blkPath = os.path.join(blkPath, "blkio.throttle.io_service_bytes")
            if not os.path.exists(blkPath):
                SysMgr.printErr("failed to find '%s'" % blkPath)
                sys.exit(-1)

            blkPathList = SysMgr.readFile(blkPath)
            if blkPathList:
                devices = {}
                for line in blkPathList.split("\n"):
                    if line.startswith("Total"):
                        continue
                    try:
                        devices[line.split(None, 1)[0]] = 0
                    except SystemExit:
                        sys.exit(0)
                    except:
                        pass

                devices = list(devices)
            else:
                # save diskstats #
                SysMgr.updateDiskStats()

                for line in SysMgr.diskStats:
                    try:
                        devices.append(":".join(line.split(None, 3)[:2]))
                    except SystemExit:
                        sys.exit(0)
                    except:
                        pass

        name = str(time.time()).replace(".", "")

        # set commands for block #
        cmds = ["CREATE:blkio:guider_{0:1}:".format(name)]

        # set commands #
        for item in attrs:
            for dev in devices:
                cmds.append(
                    (
                        "WRITE:blkio:guider_{0:1}:{3:1} {2:1}"
                        "@blkio.throttle.{1:1}"
                    ).format(
                        name,
                        item[0],
                        item[1],
                        dev,
                    ),
                )

        # set commands #
        for pid in pids:
            cmds.append("ADD:blkio:guider_{0:1}:{1:1}".format(name, pid))

        # execute commands to limit block usage #
        SysMgr.doCgroup(cmds, make=True, remove=True, verb=False)

    @staticmethod
    def limitPid(pids, attrs):
        if not pids:
            return

        # check root permission #
        SysMgr.checkRootPerm(msg="limit pids using cgroup")

        # check subsystem #
        if not SysMgr.getCgroupSubPath("pids", False):
            return

        name = str(time.time()).replace(".", "")

        # set commands for pids #
        cmds = ["CREATE:pids:guider_{0:1}:".format(name)]

        # set commands #
        for item in attrs:
            cmds.append(
                "WRITE:pids:guider_{0:1}:{2:1}@pids.{1:1}".format(
                    name, item[0], item[1]
                ),
            )

        # set commands #
        for pid in pids:
            cmds.append("ADD:pids:guider_{0:1}:{1:1}".format(name, pid))

        # execute commands to limit pids #
        SysMgr.doCgroup(cmds, make=True, remove=True, verb=False)

    @staticmethod
    def limitCpu(pids, attrs):
        if not pids:
            return

        # check root permission #
        SysMgr.checkRootPerm(msg="limit cpu usage using cgroup")

        # check subsystem #
        if not SysMgr.getCgroupSubPath("cpu", False):
            return

        name = str(time.time()).replace(".", "")

        # set commands for CPU usage #
        cmds = ["CREATE:cpu:guider_{0:1}:".format(name)]

        # get node list #
        nodes = [item[0] for item in attrs]

        # add default values #
        for node in ("cfs_period_us", "rt_period_us"):
            if node in nodes:
                continue

            cmds.append(
                "WRITE:cpu:guider_{0:1}:{2:1}@cpu.{1:1}".format(
                    name, node, 1000000
                )
            )

        # set commands #
        for item in attrs:
            cmds.append(
                "WRITE:cpu:guider_{0:1}:{2:1}@cpu.{1:1}".format(
                    name, item[0], item[1]
                ),
            )

        # set commands #
        for pid in pids:
            cmds.append("ADD:cpu:guider_{0:1}:{1:1}".format(name, pid))

        # execute commands to limit CPU usage #
        SysMgr.doCgroup(cmds, make=True, remove=True, verb=False)

    @staticmethod
    def limitCpuset(pids, attrs):
        if not pids:
            return

        # check root permission #
        SysMgr.checkRootPerm(msg="limit cpuset using cgroup")

        # check subsystem #
        cpusetdir = SysMgr.getCgroupSubPath("cpuset", False)
        if not cpusetdir:
            return

        # get prefix #
        if os.path.exists(os.path.join(cpusetdir, "cpuset.cpus")):
            prefix = "cpuset."
        else:
            prefix = ""

        name = str(time.time()).replace(".", "")

        # set commands for CPU set #
        cmds = ["CREATE:cpuset:guider_{0:1}:".format(name)]

        # get node list #
        nodes = [item[0] for item in attrs]

        # set commands #
        for item in attrs:
            cmds.append(
                "WRITE:cpuset:guider_{0:1}:{2:1}@{3:}{1:1}".format(
                    name, item[0], item[1], prefix
                ),
            )

        # init commands #
        addset = [["mems", 0]]
        if "EXCLUSIVE" in SysMgr.environList:
            addset += [["cpu_exclusive", 1]]
        for vals in addset:
            if vals[0] in nodes:
                continue

            cmds.append(
                "WRITE:cpuset:guider_{0:1}:{2:1}@{3:}{1:1}".format(
                    name, vals[0], vals[1], prefix
                )
            )

        # set commands #
        for pid in pids:
            cmds.append("ADD:cpuset:guider_{0:1}:{1:1}".format(name, pid))

        # execute commands to limit CPU set #
        SysMgr.doCgroup(cmds, make=True, remove=True, verb=False)

    @staticmethod
    def limitMemory(pids, attrs):
        if not pids:
            return

        # check root permission #
        SysMgr.checkRootPerm(msg="limit memory usage using cgroup")

        # check subsystem #
        if not SysMgr.getCgroupSubPath("memory", False):
            return

        name = str(time.time()).replace(".", "")

        # set commands for memory size #
        cmds = ["CREATE:memory:guider_{0:1}:".format(name)]

        # set commands #
        for pid in pids:
            cmds.append("ADD:memory:guider_{0:1}:{1:1}".format(name, pid))

        # set commands #
        for item in attrs:
            cmds.append(
                "WRITE:memory:guider_{0:1}:{2:1}@memory.{1:1}".format(
                    name, item[0], item[1]
                ),
            )

        # execute commands to limit memory #
        SysMgr.doCgroup(cmds, make=True, remove=True, verb=False)

    @staticmethod
    def doCgroup(cmds=[], make=False, remove=False, verb=True):
        def _moveTasks(srcFile, desFile, targetTasks):
            # check files #
            if not os.path.exists(srcFile) or not os.path.exists(desFile):
                return

            # read target tasks #
            tasks = SysMgr.readFile(srcFile)
            if tasks:
                tasks = tasks.split("\n")
            else:
                tasks = []

            # filter target tasks #
            tasks = list(set(tasks) & set(targetTasks))
            if not tasks:
                return []

            # move tasks #
            SysMgr.writeFile(desFile, tasks)

            # read moved tasks #
            movedTasks = SysMgr.readFile(desFile)
            if movedTasks:
                movedTasks = movedTasks.split("\n")
            else:
                movedTasks = []

            # get moved tasks #
            tasks = list(set(tasks) & set(movedTasks))
            if not tasks:
                return []

            return tasks

        def _checkFile(targetFile):
            if os.path.exists(targetFile):
                return True
            else:
                SysMgr.printWarn("failed to find '%s'" % targetFile, True)
                return False

        # get argument #
        if cmds:
            value = cmds
        elif SysMgr.hasMainArg():
            value = SysMgr.getMainArgs(False)
        elif SysMgr.filterGroup:
            value = SysMgr.filterGroup
        else:
            SysMgr.printErr("failed to get commands for cgroup")
            sys.exit(-1)

        # get cgroup list #
        cgroupList = SysMgr.getCgroupList()

        # verify and convert command strings #
        cmds = []
        for item in value:
            try:
                # CMD:SUB:NAME:TARGET #
                cmdset = item.split(":", 3)
                cmd, sub, name, target = cmdset
                if target == "PID":
                    target = str(SysMgr.pid)
                cmds.append([cmd.upper(), sub, name, target.strip()])
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr(
                    (
                        "failed to convert cgroup command '%s' "
                        "in CMD:SUB:NAME:TARGET format"
                    )
                    % item,
                    True,
                )
                sys.exit(-1)

        # check task type #
        if SysMgr.processEnable:
            isThread = False
            taskType = "process"
            taskNode = "cgroup.procs"
        else:
            isThread = True
            taskType = "thread"
            taskNode = "tasks"

        # define shortcut variable #
        convNum = UtilMgr.convNum

        # handle commands #
        for clist in cmds:
            # get command info #
            cmd, sub, name, target = clist

            # check subsystem #
            rsub = sub.split("/", 1)[0]
            if not rsub in cgroupList:
                SysMgr.printWarn("no support '%s' subsystem" % rsub, True)
            elif cgroupList[rsub]["enable"] == "0":
                SysMgr.printWarn("'%s' subsystem is disabled" % rsub, True)

            # check command #
            if not cmd in (
                "CREATE",
                "ADD",
                "MOVE",
                "REMOVE",
                "DELETE",
                "READ",
                "WRITE",
                "LIST",
            ):
                SysMgr.printErr("no support '%s' command" % cmd)
                sys.exit(-1)

            # get target tasks #
            if cmd in ("CREATE", "ADD", "MOVE", "REMOVE", "LIST"):
                if cmd == "CREATE" and not target:
                    targetTasks = None
                else:
                    targetTasks = SysMgr.getTids(
                        target,
                        isThread=isThread,
                        sibling=SysMgr.groupProcEnable,
                    )
                    if not targetTasks:
                        SysMgr.printErr("no target %s" % taskType, True)
                        continue
            else:
                targetTasks = None

            # handle read / write command #
            if cmd in ("READ", "WRITE"):
                # get target info #
                targetDir = SysMgr.getCgroup(sub, name, make, remove)
                if not targetDir:
                    continue

                try:
                    if cmd == "READ":
                        name = target
                    else:
                        value, name = target.split("@")
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr(
                        "wrong '%s' for %s in VALUE@FILE format"
                        % (target, cmd),
                        True,
                    )
                    continue

                # convert dir list #
                targetDir = (
                    UtilMgr.convPath(targetDir)
                    if "*" in targetDir
                    else [targetDir]
                )

                for item in targetDir:
                    # get target path #
                    targetFiles = os.path.join(item, name)
                    targetFiles = (
                        UtilMgr.convPath(targetFiles)
                        if "*" in targetFiles
                        else [targetFiles]
                    )

                    for targetFile in targetFiles:
                        if not _checkFile(targetFile):
                            continue

                        # read value #
                        if cmd == "READ":
                            ret = [SysMgr.readFile(targetFile)]

                            # print result #
                            SysMgr.printInfo(
                                "read '%s' from '%s'" % (ret, targetFile)
                            )
                        # write value #
                        else:
                            ret = SysMgr.writeFile(targetFile, value)
                            if ret:
                                # print result #
                                SysMgr.printInfo(
                                    "wrote '%s' to '%s'"
                                    % (
                                        convNum(str(value))
                                        if str(value).isdigit()
                                        else value,
                                        targetFile,
                                    )
                                )

                continue
            # handle list command #
            elif cmd == "LIST":
                # get target path #
                targetDir = SysMgr.getCgroup(sub, name, make, remove)
                if not targetDir:
                    continue

                # convert dir list #
                targetDir = (
                    UtilMgr.convPath(targetDir)
                    if "*" in targetDir
                    else [targetDir]
                )

                for item in targetDir:
                    targetFile = os.path.join(item, taskNode)
                    if not _checkFile(targetFile):
                        continue

                    # print target tasks #
                    tasks = SysMgr.readFile(targetFile)
                    if tasks:
                        tasks = tasks.split("\n")
                    else:
                        SysMgr.printInfo(
                            "no %s in '%s'" % (taskType, targetFile)
                        )
                        continue

                    # apply filter #
                    tasks = list(
                        map(long, list(set(tasks) & set(targetTasks)))
                    )

                    # print message #
                    SysMgr.printInfo(
                        "print %s %s from '%s'"
                        % (convNum(len(tasks)), taskType, targetFile)
                    )

                    # print tasks #
                    for idx, tid in enumerate(sorted(tasks)):
                        if not verb:
                            continue

                        SysMgr.printPipe(
                            "[%s] %s(%s)"
                            % (convNum(idx), SysMgr.getComm(tid), tid),
                            pager=False,
                        )

                continue
            # handle move command #
            elif cmd == "MOVE":

                def _getPathInfo(path, name, make):
                    try:
                        targetInfo = path.split("/", 1)
                        if len(targetInfo) == 1:
                            targetPath = targetInfo[0]
                            targetName = "/"
                        else:
                            targetPath, targetName = targetInfo
                            if not targetName:
                                targetName = "/"

                        targetDir = SysMgr.getCgroup(
                            targetPath, targetName, make, remove
                        )
                        if not targetDir:
                            raise Exception("no dir")

                        targetFile = os.path.join(targetDir, taskNode)
                        if not os.path.exists(targetFile):
                            raise Exception("no file")

                        return targetDir, targetFile
                    except SystemExit:
                        sys.exit(0)
                    except:
                        SysMgr.printErr(
                            "wrong %s of cgroup '%s' for move" % (name, path),
                            True,
                        )
                        return None, None

                # source #
                srcDir, srcFile = _getPathInfo(sub, "source", make)
                if not srcDir:
                    continue

                # destination #
                desDir, desFile = _getPathInfo(name, "destination", make)
                if not desDir:
                    continue

                # move tasks #
                tasks = _moveTasks(srcFile, desFile, targetTasks)
                if not tasks:
                    SysMgr.printErr(
                        "no %s to be moved from '%s' to '%s'"
                        % (taskType, srcFile, desFile)
                    )
                    continue

                # print message #
                SysMgr.printInfo(
                    "moved %s %s from '%s' to '%s'"
                    % (convNum(len(tasks)), taskType, srcDir, desDir)
                )

                # print tasks #
                for idx, tid in enumerate(tasks):
                    if not verb:
                        continue

                    SysMgr.printPipe(
                        "[%s] %s(%s)"
                        % (convNum(idx), SysMgr.getComm(tid), tid),
                        pager=False,
                    )

                continue

            # set make flag #
            if cmd == "CREATE":
                make = True
            else:
                make = False

            # get directory #
            targetDir = SysMgr.getCgroup(sub, name, make, remove)
            if not targetDir:
                cgroupPath = SysMgr.sysInstance.getCgroupPath()
                if not os.path.exists(os.path.join(cgroupPath, sub)):
                    SysMgr.printErr("no %s cgroup subsystem" % sub)
                else:
                    SysMgr.printErr(
                        "failed to get target cgroup '%s/%s'" % (sub, name)
                    )
                continue

            # convert dir list #
            targetDir = (
                UtilMgr.convPath(targetDir)
                if "*" in targetDir
                else [targetDir]
            )

            for item in targetDir:
                if cmd == "DELETE":
                    SysMgr.removeCgroup(item)
                    SysMgr.printInfo("deleted '%s' cgroup" % item)
                    continue
                elif cmd == "CREATE" and targetTasks is None:
                    SysMgr.printInfo("created '%s' cgroup" % item)
                    continue

                # register tasks to cgroup node #
                targetFile = os.path.join(item, taskNode)
                if not _checkFile(targetFile):
                    continue

                # ADD #
                if cmd in ("CREATE", "ADD"):
                    # move tasks #
                    SysMgr.writeFile(targetFile, targetTasks)

                    # print message #
                    SysMgr.printInfo(
                        "added %s %s in total to '%s' cgroup"
                        % (convNum(len(targetTasks)), taskType, item)
                    )

                    SysMgr.printWarn(SysMgr.getCommList(targetTasks))

                    tasks = targetTasks
                # REMOVE #
                else:
                    # get target file #
                    desFile = os.path.join(
                        os.path.dirname(targetFile), "..", taskNode
                    )
                    if not _checkFile(desFile):
                        continue

                    # move tasks #
                    tasks = _moveTasks(targetFile, desFile, targetTasks)
                    if not tasks:
                        SysMgr.printErr(
                            "no %s to be removed from '%s'" % (taskType, item)
                        )
                        continue

                    # print message #
                    SysMgr.printInfo(
                        "removed %s %s from '%s' cgroup"
                        % (convNum(len(tasks)), taskType, item)
                    )

                    SysMgr.printWarn(SysMgr.getCommList(tasks))

                # sort targets #
                tasks = sorted(list(map(long, tasks)))

                # print tasks #
                if verb:
                    for idx, tid in enumerate(tasks):
                        SysMgr.printPipe(
                            "[%s] %s(%s)"
                            % (convNum(idx), SysMgr.getComm(tid), tid),
                            pager=False,
                        )

    @staticmethod
    def getCgroupSubPath(sub, exception=True):
        cgroupInfo = SysMgr.printCgroupList(json=True, ret=True)

        if sub in cgroupInfo:
            return cgroupInfo[sub]["mount"].split("(", 1)[0].strip()
        elif exception:
            raise Exception("access to %s cgroup subsystem failed" % sub)
        else:
            SysMgr.printErr("failed to access to %s cgroup subsystem" % sub)
            return None

    @staticmethod
    def clearCgroupPath():
        SysMgr.cgroupPathList = {}

    @staticmethod
    def umountCgroups(subs=[]):
        umountList = {}

        cgroupInfo = SysMgr.printCgroupList(json=True, ret=True)
        for sub, values in cgroupInfo.items():
            if not values["mount"]:
                continue
            elif subs and not sub in subs:
                continue

            # get mount path #
            mountPath = values["mount"].split("(", 1)[0].strip()
            if mountPath in umountList:
                continue

            # umount a cgroup subsystem #
            SysMgr.umountCgroup(sub, mountPath)

            umountList[mountPath] = True

        SysMgr.clearCgroupPath()

    @staticmethod
    def mountCgroups(subs=[]):
        defPath = "/sys/fs/cgroup"
        if not os.path.exists(defPath):
            SysMgr.printErr("no default cgroup path '%s'" % defPath)
            sys.exit(-1)

        # get mount info #
        cgroupInfo = SysMgr.printCgroupList(json=True, ret=True)

        # merge same hierarch items #
        mergedInfo = {}
        for sub, values in cgroupInfo.items():
            if values["mount"]:
                continue
            elif subs and not sub in subs:
                continue

            # append to list #
            mid = "%s-%s" % (values["hierarchy"], values["num"])
            mergedInfo.setdefault(mid, [])
            mergedInfo[mid].append(sub)

        for _, sub in sorted(mergedInfo.items()):
            sub = ",".join(sub)

            # make a new directory #
            desDir = defPath + "/" + sub
            if not SysMgr.mkdirs(desDir):
                continue

            # mount a cgroup subsystem #
            SysMgr.mountCgroup(sub, desDir)

        SysMgr.clearCgroupPath()

    @staticmethod
    def printCgroupList(json=False, ret=False):
        cgroupList = SysMgr.getCgroupList()
        if not cgroupList:
            SysMgr.printErr("no cgroup mount point")
            return

        # check SysMgr instance #
        if not SysMgr.sysInstance:
            SysMgr(onlyInstance=True)

        # get cgroup2 path list #
        cgroupV2Path = SysMgr.sysInstance.getCgroupPath(
            "cgroup2", force=True, save=False
        )
        if cgroupV2Path:
            v2Info = "(v2: %s)" % list(cgroupV2Path.keys())[0]
        else:
            v2Info = ""

        if json:
            cgDict = {}
        else:
            SysMgr.printPipe(
                (
                    "\n[Cgroup Info] {6:1}\n{5:1}\n{0:>16} {1:>10} "
                    "{2:>8} {3:>8}  {4:1}\n{5:1}"
                ).format(
                    "subsystem",
                    "hierarchy",
                    "number",
                    "enabled",
                    "mount",
                    twoLine,
                    v2Info,
                )
            )

        # get cgroup path list #
        cgroupPath = SysMgr.sysInstance.getCgroupPath(force=True, save=True)

        for sub, values in sorted(
            cgroupList.items(), key=lambda x: long(x[1]["hierarchy"])
        ):
            try:
                # get mount info #
                mountinfo = ""
                for path, option in cgroupPath.items():
                    if sub in option.split()[2].split(
                        ","
                    ) or sub in path.rsplit("/", 1)[1].split(","):
                        mountinfo = "%s (%s)" % (path, cgroupPath[path])
                        break

                if json:
                    cgDict[sub] = values
                    cgDict[sub]["mount"] = mountinfo
                else:
                    SysMgr.printPipe(
                        "{0:>16} {1:>10} {2:>8} {3:>8}  {4:1}".format(
                            sub,
                            values["hierarchy"],
                            values["num"],
                            values["enable"],
                            mountinfo,
                        )
                    )
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printWarn("failed to print cgroup info", reason=True)

        if json:
            if ret:
                return cgDict

            SysMgr.printPipe(
                UtilMgr.convDict2Str(cgDict, pretty=not SysMgr.streamEnable)
            )
        else:
            SysMgr.printPipe(oneLine)

    @staticmethod
    def getCgroupList():
        # read cgroup list #
        cgroups = SysMgr.readProcStat(
            SysMgr.cgroupFd, "cgroups", SysMgr, "cgroupFd"
        )
        if not cgroups:
            return {}

        cgroupList = {}
        for line in cgroups[1:]:
            try:
                sub, hierarchy, num, enable = line.strip().split("\t")
                cgroupList[sub] = {
                    "hierarchy": hierarchy,
                    "num": num,
                    "enable": enable,
                }
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printWarn("failed to get cgroup items", reason=True)

        return cgroupList

    @staticmethod
    def getCgroup(sub, name=None, make=False, remove=False):
        try:
            # get subsystem path #
            cgroupPath = SysMgr.getCgroupSubPath(sub)
            if not cgroupPath:
                raise Exception("no subsystem '%s'" % sub)

            # set dir name #
            if not name:
                name = "guider_%s" % SysMgr.pid
            elif name == "/":
                name = ""

            # make target dir #
            targetDir = os.path.join(cgroupPath, name)
            if make:
                if not SysMgr.mkdirs(targetDir):
                    raise Exception("make fail for '%s'" % targetDir)

            # register handler to remove directory #
            if remove:
                SysMgr.addExitFunc(
                    SysMgr.removeCgroup,
                    [targetDir],
                    private=True,
                    redundant=False,
                )

            return targetDir
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn("failed to get cgroup %s" % sub, True, True)
            return False

    @staticmethod
    def getCgroupFreezer():
        try:
            # check result #
            if SysMgr.freezerPath or SysMgr.freezerPath is False:
                return SysMgr.freezerPath

            # get cgroup #
            tempDir = SysMgr.getCgroup("freezer", make=True, remove=True)
            if not tempDir:
                return

            # freeze tasks #
            SysMgr.writeFile(os.path.join(tempDir, "freezer.state"), "FROZEN")

            # register tasks to cgroup node #
            SysMgr.freezerPath = os.path.join(tempDir, "tasks")
            return SysMgr.freezerPath
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn("failed to create cgroup freezer", True, True)
            SysMgr.freezerPath = False
            return False

    @staticmethod
    def doFreeze(targetList=[]):
        # check root permissino #
        SysMgr.checkRootPerm()

        # get argument #
        ret = SysMgr.selectTaskId()
        if ret:
            value = [ret]
        elif SysMgr.hasMainArg():
            value = SysMgr.getMainArgs()
        elif SysMgr.filterGroup:
            value = SysMgr.filterGroup
        else:
            SysMgr.printErr(
                "failed to freeze tasks because there is no target"
            )
            sys.exit(-1)

        # check task type #
        if SysMgr.processEnable:
            isThread = False
            taskType = "process"
            taskNode = "cgroup.procs"
        else:
            isThread = True
            taskType = "thread"
            taskNode = "tasks"

        # get target tasks #
        targetTasks = []
        if targetList:
            targetTasks = list(map(str, targetList))
        else:
            for item in value:
                targetTasks += SysMgr.getTids(
                    item, isThread=isThread, sibling=SysMgr.groupProcEnable
                )
            if not targetTasks:
                SysMgr.printErr("no target %s" % taskType)
                sys.exit(-1)

        # get cgroup #
        targetDir = SysMgr.getCgroup("freezer", make=True, remove=True)
        if not targetDir:
            SysMgr.mountCgroups("freezer")
            # try again #
            targetDir = SysMgr.getCgroup("freezer", make=True, remove=True)
            if not targetDir:
                return

        # register tasks to cgroup node #
        taskPushFile = os.path.join(targetDir, taskNode)
        SysMgr.writeFile(taskPushFile, targetTasks)

        # freeze tasks #
        SysMgr.writeFile(os.path.join(targetDir, "freezer.state"), "FROZEN")

        if targetList:
            return

        # print target tasks #
        tasks = SysMgr.readFile(taskPushFile).split("\n")
        for tid in tasks:
            if not tid:
                continue
            comm = SysMgr.getComm(tid)
            SysMgr.printInfo("freezed %s(%s) %s" % (comm, tid, taskType))

        # set alarm #
        signal.signal(signal.SIGALRM, SysMgr.onAlarm)
        signal.alarm(SysMgr.intervalEnable)

        # wait for events #
        SysMgr.waitEvent()

    @staticmethod
    def doSetAffinity():
        SysMgr.warnEnable = True

        # get argument #
        if SysMgr.hasMainArg():
            value = SysMgr.getMainArgs()
        elif SysMgr.filterGroup:
            value = SysMgr.filterGroup
        else:
            SysMgr.printErr(
                "failed to set CPU affinity of task because there is no target"
            )
            sys.exit(-1)

        # convert task ID #
        ret = SysMgr.selectTaskId()
        if ret:
            newval = []
            for item in value:
                newval.append(item.replace("PID", ret))
            value = newval

        while 1:
            SysMgr.parseAffinityOption(value, launch=True)
            if SysMgr.intervalEnable:
                time.sleep(SysMgr.intervalEnable)
            else:
                break

        sys.exit(0)

    @staticmethod
    def importPkgItems(pkg, isExit=True):
        if pkg in SysMgr.impGlbPkg:
            return True

        module = SysMgr.getPkg(pkg, isExit)
        if not module:
            return False

        moduleDict = module.__dict__

        try:
            importList = module.__all__
        except SystemExit:
            sys.exit(0)
        except AttributeError:
            importList = [
                name for name in moduleDict if not name.startswith("_")
            ]

        newDict = {}
        for name in importList:
            newDict[name] = moduleDict[name]
        globals().update(newDict)

        SysMgr.impGlbPkg[pkg] = True

        return True

    @staticmethod
    def setBlock(fd, block=True):
        fcntl = SysMgr.getPkg("fcntl", False)

        try:
            if not fcntl:
                raise Exception("no fcntl")

            if not UtilMgr.isNumber(fd):
                fd = fd.fileno()

            flag = fcntl.fcntl(fd, fcntl.F_GETFL)

            if block:
                fcntl.fcntl(fd, fcntl.F_SETFL, flag & ~os.O_NONBLOCK)
            else:
                fcntl.fcntl(fd, fcntl.F_SETFL, flag | os.O_NONBLOCK)

            return True
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn(
                "failed to set block attribute to the file descriptor",
                reason=True,
            )
            return False

    @staticmethod
    def getHomePath():
        try:
            return os.environ["HOME"]
        except:
            return None

    @staticmethod
    def getLogEvents(tail=0, until=0, sort="seconds"):
        logEvents = []

        # get print flag #
        printFlag = "PRINTEVENT" in SysMgr.environList

        for logtype, logcmd in (
            ("DLTEVENT", "printdlt"),
            ("JOURNALEVENT", "printjrl"),
            ("KERNELEVENT", "printkmsg"),
            ("ANDROIDEVENT", "printand"),
            ("SYSLOGEVENT", "printsyslog"),
            ("TRACEEVENT", "printtrace"),
        ):
            if not logtype in SysMgr.environList:
                continue

            logname = UtilMgr.rstrip(logtype, "EVENT")

            SysMgr.printStat("start reading %s events..." % logname)

            for item in SysMgr.environList[logtype]:
                # get parameters #
                values = item.split("|", 2)
                if len(values) == 1:
                    path = None
                    name = values[0]
                    keyword = ""
                elif len(values) == 2:
                    path = None
                    name, keyword = values
                else:
                    path, name, keyword = values

                # get last file path #
                if logtype == "DLTEVENT":
                    if values[0] == "LASTFILE":
                        # convert path #
                        traceFileList = DltAnalyzer.getTraceFileList()
                        if traceFileList:
                            path = traceFileList[-1]
                        else:
                            path = None

                    # convert time #
                    if tail or until:
                        diff = SysMgr.getUptime() - UtilMgr.getClockTime(
                            dlt=True
                        )
                        if tail:
                            tail -= diff
                        if until:
                            until -= diff

                inputParam = [
                    logcmd,
                    "-g%s" % keyword,
                    "-J",
                    "-Q",
                    "-a",
                    "-dL",
                ]

                # set input path #
                inputParam.insert(1, "-I" + (path if path else ""))

                # set time condition #
                if tail or until:
                    inputParam.append(
                        (
                            "-q"
                            + ("TAIL:%s," % tail if tail else "")
                            + ("UNTIL:%s," % until if until else "")
                        ).rstrip(",")
                    )

                # execute filter process #
                """
                use initPkg flag to clear all global packages
                because cast functions are in both ctypes and pylab.
                """
                ret = SysMgr.launchGuider(
                    inputParam,
                    pipe=True,
                    stderr=True,
                    logo=False,
                    copyOpt=False,
                    initPkg=True,
                )

                # read logs from filter process #
                try:
                    pipe = ret[1]

                    while 1:
                        # read a line #
                        line = pipe.readline()
                        if not line:
                            if SysMgr.isAlive(ret[0]):
                                continue
                            else:
                                break

                        # remove useless characters #
                        line = line.strip()
                        if not line or not line.startswith("{"):
                            continue

                        # print progress #
                        UtilMgr.printProgress()

                        # convert string to dict #
                        obj = UtilMgr.convStr2Dict(line)
                        if obj:
                            obj["name"] = name
                            obj["type"] = logtype
                            logEvents.append(obj)

                        if printFlag:
                            SysMgr.printWarn(
                                "<%s> (%s) %s" % (logtype, name, line), True
                            )

                    # delete progress #
                    UtilMgr.deleteProgress()
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr("stopped to read %s logs" % logname, True)

        # sort by seconds #
        try:
            logEvents.sort(key=lambda val: val[sort])
        except SystemExit:
            sys.exit(0)
        except:
            pass

        return logEvents

    @staticmethod
    def initLogWatcher(data):
        logList = {
            "DLT": "printdlt",
            "FILE": "watch",
            "JOURNAL": "printjrl",
            "KERNEL": "printkmsg",
            "ANDROID": "printand",
            "SYSLOG": "printsyslog",
            "TRACE": "printtrace",
        }

        for name, values in data.items():
            # check skip condition #
            if not name in logList:
                SysMgr.printErr("no support '%s' for log monitoring" % name)
                continue

            # check type #
            if isinstance(values, list):
                pass
            elif isinstance(values, dict):
                values = [values]
            else:
                continue

            for value in values:
                # check skip conditions #
                if value.get("apply") != "true":
                    continue
                elif not "filter" in value:
                    SysMgr.printErr("no filter for '%s' monitoring" % name)
                    continue

                # set port opt #
                if not SysMgr.localServObj:
                    SysMgr.printErr("no local network address info")
                    sys.exit(-1)

                portOpt = " -X%s" % SysMgr.localServObj.port

                # get filter #
                target = value["filter"]

                # set event name #
                event = "CMD_NOTIFY:log_%s_%s" % (name, target)

                # get condition for exit #
                if (
                    value.get("oneshot") == "true"
                    or value.get("goneshot") == "true"
                ):
                    exitCond = ",WATCHLOGEXIT"
                else:
                    exitCond = ""

                # set env variables #
                envOpt = (
                    "-qWATCHLOG:%s,TAIL,WATCHLOGCMD:GUIDER event %s%s%s"
                    % (target, event, portOpt, exitCond)
                )
                if name == "FILE":
                    if value.get("taskmon") == "true":
                        envOpt += ",PROCINFO"
                    elif value.get("inmount") == "true":
                        envOpt += ",INMOUNT"
                    elif value.get("allmount") == "true":
                        envOpt += ",ALLMOUNT"
                if value.get("re") == "true":
                    envOpt += ",REFILTER"

                # set default command #
                common = [envOpt, "-dp"]

                # add other options #
                if name == "FILE":
                    if "path" in value:
                        common.append("-g%s" % value["path"])

                # set command #
                cmd = [logList[name]] + common

                # set mute flag #
                mute = not "UNMUTE" in SysMgr.environList

                # execute watcher tasks #
                ret = SysMgr.launchGuider(
                    cmd, mute=mute, pipe=False, stderr=True
                )
                # register the command task #
                if ret > 0:
                    SysMgr.eventCommandList.setdefault(event, [])
                    SysMgr.eventCommandList[event].append(ret)

    @staticmethod
    def initFuncWatcher(data):
        funcList = {
            "NATIVE": "btrace",
            "SYSCALL": "strace",
            "PYTHON": "pytrace",
            "SIGNAL": "sigtrace",
            "DBUS": "printdbus",
        }

        for name, values in data.items():
            # check skip condition #
            if not name in funcList:
                SysMgr.printErr(
                    "no support '%s' for function monitoring" % name
                )
                continue

            # check type #
            if isinstance(values, list):
                pass
            elif isinstance(values, dict):
                values = [values]
            else:
                continue

            for value in values:
                # check skip conditions #
                if value.get("apply") != "true":
                    continue
                elif not "filter" in value:
                    SysMgr.printErr(
                        "no function filter for '%s' monitoring" % name
                    )
                    continue
                elif not "thread" in value:
                    SysMgr.printErr(
                        "no thread filter for '%s' monitoring" % name
                    )
                    continue

                # get function filter #
                target = value["filter"]

                # get task filter #
                task = value["thread"]

                # set event name #
                event = "CMD_NOTIFY:func_%s_%s" % (name, target)

                # get condition for exit #
                if (
                    value.get("oneshot") == "true"
                    or value.get("goneshot") == "true"
                ):
                    logExitCond = ",WATCHLOGEXIT"
                    funcExitCond = "|exit"
                else:
                    logExitCond = ""
                    funcExitCond = ""

                # set mute flag #
                mute = not "UNMUTE" in SysMgr.environList

                # set port opt #
                portOpt = " -X%s" % SysMgr.localServObj.port

                # set handle command #
                handleCmd = "GUIDER event %s%s" % (event, portOpt)

                # set default command #
                if name == "DBUS":
                    common = []
                else:
                    common = ["-g%s" % task]

                # append syscall option #
                if name == "SYSCALL":
                    common += ["-t%s" % target.replace("+", ",")]

                # set filter command #
                if name in ("SIGNAL", "DBUS"):
                    # define mandatory environment variables #
                    addCmd = "-qWATCHLOG:%s,WATCHLOGCMD:%s" % (
                        target,
                        handleCmd,
                    )
                    # append main option #
                    if value.get("main") == "true":
                        addCmd += ",ONLYPROC%s" % logExitCond
                    else:
                        addCmd += logExitCond
                    # composite values #
                    common += [addCmd]

                    addCmd = "-c%s" % target.replace("+", ",")
                else:
                    addCmd = "-c"
                    for func in target.split("+"):
                        addCmd += "%s|exec:%s%s," % (
                            func,
                            handleCmd,
                            funcExitCond,
                        )
                    addCmd = addCmd.rstrip(",")

                    # append main option #
                    if value.get("main") == "true":
                        common += ["-qONLYPROC"]
                common += [addCmd]

                # append process group option #
                if (
                    name != "DBUS"
                    and "group" in value
                    and value["group"] == "true"
                ):
                    common += ["-P"]

                # set command #
                cmd = [funcList[name]] + common

                # execute watcher tasks #
                ret = SysMgr.launchGuider(
                    cmd, mute=mute, pipe=False, stderr=True, copyOpt=False
                )
                # register the command task #
                if ret > 0:
                    SysMgr.eventCommandList.setdefault(event, [])
                    SysMgr.eventCommandList[event].append(ret)

    @staticmethod
    def applyThreshold(update=False):
        def _getMaxInterval(node, maxVal=0, root=None):
            for key, item in node.items():
                # set root resource #
                if root:
                    res = root
                else:
                    res = key

                if isinstance(item, list):
                    for subitem in item:
                        # check skip condition #
                        if not isinstance(subitem, dict):
                            continue
                        elif "apply" in subitem and subitem["apply"] != "true":
                            continue

                        # check sub items #
                        val = _getMaxInterval(subitem, maxVal, res)
                        if maxVal < val:
                            maxVal = val
                elif isinstance(item, dict):
                    # check skip condition #
                    if "apply" in item and item["apply"] != "true":
                        continue

                    # check items #
                    val = _getMaxInterval(item, maxVal, res)
                    if maxVal < val:
                        maxVal = val
                elif key == "interval" and UtilMgr.isNumber(item):
                    item = long(item)
                    if maxVal < item:
                        maxVal = item

            return maxVal

        def _convAttr(item, attr, func, lev=0):
            if isinstance(item, dict) and attr in item:
                item[attr] = func(item[attr])
                return True

            # check type #
            if isinstance(item, list):
                for value in item:
                    _convAttr(value, attr, func, lev + 1)
            elif isinstance(item, dict):
                for key, value in item.items():
                    if lev == 0 and key == "SYSTEM":
                        continue
                    _convAttr(value, attr, func, lev + 1)

            return False

        def _makeAttrList(item):
            # check type #
            if isinstance(item, list):
                for value in item:
                    _makeAttrList(value)
            elif isinstance(item, dict):
                for key, value in item.items():
                    SysMgr.thrItemList[key] = 0
                    _makeAttrList(value)

        def _checkAttr(item, attr, lev=0):
            if isinstance(item, dict) and attr in item:
                return "apply" in item and item["apply"] == "true"

            # check type #
            if isinstance(item, list):
                for value in item:
                    if _checkAttr(value, attr, lev + 1):
                        return True
            elif isinstance(item, dict):
                for key, value in item.items():
                    if lev == 0 and key == "SYSTEM":
                        continue
                    if _checkAttr(value, attr, lev + 1):
                        return True

            return False

        def _checkResTask(item, lev=0):
            if (
                isinstance(item, dict)
                and "apply" in item
                and item["apply"] == "true"
            ):
                return True

            # check type #
            if isinstance(item, list):
                for value in item:
                    if _checkResTask(value, lev + 1):
                        return True
            elif isinstance(item, dict):
                for key, value in item.items():
                    if lev == 0 and key == "SYSTEM":
                        continue
                    if _checkResTask(value, lev + 1):
                        return True

            return False

        def _removeItems(item, depth):
            # traverse list #
            if isinstance(item, list):
                indexes = []
                for idx, value in enumerate(item):
                    if _removeItems(value, depth + 1):
                        indexes.append(idx)
                for idx in sorted(indexes, reverse=True):
                    del item[idx]
                return not item

            if not isinstance(item, dict):
                return False
            elif depth > 0:
                if not "apply" in item:
                    return False
                else:
                    return item["apply"] != "true"

            for n in list(item):
                if _removeItems(item[n], depth + 1):
                    item.pop(n, None)

            return not item

        def _checkResource(item):
            if (
                isinstance(item, dict)
                and "apply" in item
                and item["apply"] == "true"
            ):
                return True

            # check type #
            if isinstance(item, list):
                for value in item:
                    if _checkResource(value):
                        return True
            elif isinstance(item, dict):
                for key, value in item.items():
                    if _checkResource(value):
                        return True

            return False

        def _checkTask(item, threshold=False):
            def _checkValues(subitem, initFlag, taskMon):
                if not isinstance(subitem, dict):
                    return False

                for target, value in subitem.items():
                    # update system flag #
                    if initFlag is None:
                        sysFlag = (
                            True if taskMon and target == "SYSTEM" else False
                        )
                    else:
                        sysFlag = initFlag

                    # check apply in dict #
                    if target == "apply":
                        if value != "true":
                            return False
                        elif taskMon and not sysFlag:
                            return True
                    # check taskmon field #
                    elif (
                        not threshold
                        and target == "taskmon"
                        and value == "true"
                    ):
                        return True
                    # check list value #
                    elif isinstance(value, list):
                        for subval in value:
                            if _checkValues(subval, sysFlag, taskMon):
                                return True
                    # check value type #
                    elif not isinstance(value, dict):
                        continue
                    # check apply for dict value #
                    elif "apply" in value:
                        if value["apply"] != "true":
                            continue
                        elif not sysFlag:
                            return True
                return False

            if not isinstance(item, dict):
                return True

            for res, cond in item.items():
                # skip commands #
                if res.upper() in ("COMMAND",):
                    continue

                # check task monitoring feature #
                if res in (
                    "gpu",
                    "storage",
                    "net",
                    "load",
                    "sock",
                    "file",
                    "log",
                    "func",
                    "psi",
                    "cgroup",
                ):
                    taskMon = False
                else:
                    taskMon = True

                if _checkValues(cond, None, taskMon):
                    return True

            return False

        def _checkPerm(item):
            try:
                if item["apply"] == "true" and item["perm"] == "root":
                    SysMgr.checkRootPerm(msg="check threshold for %s" % item)
            except SystemExit:
                sys.exit(0)
            except:
                pass

            if isinstance(item, list):
                for value in item:
                    _checkPerm(value)
            elif isinstance(item, dict):
                for key, value in item.items():
                    _checkPerm(value)

        if update:
            confData = SysMgr.thrData
        else:
            # check threshold #
            if not "threshold" in ConfigMgr.confData:
                return

            # get threshold dict #
            confData = SysMgr.getConfigItem("threshold")
            if not confData or not isinstance(confData, dict):
                return
            # check active threshold #
            elif not _checkResource(confData):
                SysMgr.printWarn(
                    (
                        "disabled the threshold monitoring "
                        "because there is no active threshold"
                    ),
                    True,
                )
                return
            else:
                SysMgr.thrData = confData

            # check permission #
            _checkPerm(confData)

        # update threshold data #
        if "UPDATETHRESHOLD" in SysMgr.environList:
            for path in SysMgr.environList["UPDATETHRESHOLD"]:
                # update threshold #
                SysMgr.updateThreshold(path)

                # print message #
                SysMgr.printInfo("updated the threshold data from '%s'" % path)

        # apply all thresholds #
        if "APPLYALL" in SysMgr.environList:
            confData = UtilMgr.convStr2Dict(
                UtilMgr.convDict2Str(confData).replace(
                    '"apply": "false"', '"apply": "true"'
                )
            )
            SysMgr.thrData = confData

        # set report table #
        SysMgr.reportEnable = True
        if SysMgr.findOption("j") and not SysMgr.environList.get("NOPROCRANK"):
            SysMgr.rankProcEnable = True
        else:
            SysMgr.rankProcEnable = False

        # reset activation list #
        SysMgr.thrTarget = {}

        # check default resources #
        resourceList = [
            "cpu",
            "core",
            "gpu",
            "mem",
            "gpumem",
            "swap",
            "block",
            "storage",
            "net",
            "load",
            "fd",
            "sock",
            "file",
            "task",
            "log",
            "func",
            "psi",
            "cgroup",
            "battery",
            "time",
        ]

        # check resources for activation #
        for item in resourceList:
            try:
                confDataItem = confData[item]

                # remove invalid items #
                if _removeItems(confDataItem, 0):
                    confData.pop(item, None)
                    continue

                # check valid resource #
                if not _checkResource(confDataItem):
                    raise Exception()

                SysMgr.thrTarget[item] = True
                SysMgr.thrTarget[item] = _checkResTask(confDataItem)
            except SystemExit:
                sys.exit(0)
            except:
                pass

        # make all attribute list #
        try:
            _makeAttrList(confData)
        except SystemExit:
            sys.exit(0)
        except:
            pass
        finally:
            SysMgr.thrItemList = set(SysMgr.thrItemList)

        # print target resources #
        if SysMgr.thrTarget:
            SysMgr.printInfo(
                "enabled resource events [ %s ]"
                % ", ".join([res for res in SysMgr.thrTarget])
            )

        # check cpu #
        if "cpu" in SysMgr.thrTarget:
            # convert all pri items #
            _convAttr(confData["cpu"], "pri", SysMgr.convSchedPrio)

        # check time #
        if "time" in SysMgr.thrTarget:
            # convert all uptime items #
            sysattr = confData["time"].get("SYSTEM")
            if sysattr:
                _convAttr(sysattr, "uptime", UtilMgr.convUnit2Time)
            _convAttr(confData["time"], "runtimeBt", UtilMgr.convUnit2Time)
            _convAttr(confData["time"], "runtimeLt", UtilMgr.convUnit2Time)

        # check block #
        try:
            for kind, cond in confData["block"].items():
                if kind != "SYSTEM" and _checkResource(cond):
                    # check kernel config #
                    if not os.path.isfile(
                        "%s/%s/io" % (SysMgr.procPath, SysMgr.pid)
                    ):
                        SysMgr.printErr(
                            "failed to monitor block I/O, "
                            "please check kernel config"
                        )
                        sys.exit(-1)
                    SysMgr.blockEnable = True
                    SysMgr.sort = "b"
                    break
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # check storage #
        if "storage" in SysMgr.thrTarget:
            SysMgr.diskEnable = True

        # check network #
        if "net" in SysMgr.thrTarget:
            SysMgr.networkEnable = True

        # check fd #
        if "fd" in SysMgr.thrTarget:
            SysMgr.addEnvironVar("ACTUALFD")

        # check log #
        if "log" in SysMgr.thrData:
            SysMgr.initLogWatcher(SysMgr.thrData["log"])

        # check func #
        if "func" in SysMgr.thrData:
            SysMgr.initFuncWatcher(SysMgr.thrData["func"])

        # check cgroup #
        if "cgroup" in SysMgr.thrTarget:
            enableList = []
            cgList = None
            cgroupV2Path = None

            for item in (
                "cpu",
                "throttle",
                "memory",
                "read",
                "write",
                "cpuDelay",
                "memDelay",
                "ioDelay",
            ):
                # check validation #
                if not _checkAttr(confData["cgroup"], item):
                    continue

                if item in ("read", "write"):
                    if not cgList:
                        cgList = SysMgr.printCgroupList(json=True, ret=True)

                    found = False
                    target = "blkio.throttle.io_service_bytes_recursive"

                    if "blkio" in cgList:
                        mountPath = (
                            cgList["blkio"]["mount"].split("(", 1)[0].strip()
                        )
                        if target in os.listdir(mountPath):
                            found = True

                    if not found:
                        SysMgr.printErr(
                            "failed to monitor blkio because there is no '%s'"
                            % target
                        )
                        if not SysMgr.forceEnable:
                            sys.exit(-1)

                    enableList.append("blkio")
                elif item == "cpu":
                    enableList += ["cpu", "cpuacct"]
                elif item.endswith("Delay"):
                    # check pressure (v2) #
                    if not cgroupV2Path:
                        if not SysMgr.sysInstance:
                            SysMgr(onlyInstance=True)
                        cgroupV2Path = SysMgr.sysInstance.getCgroupPath(
                            "cgroup2", force=True, save=False
                        )
                    if not cgroupV2Path:
                        SysMgr.printErr(
                            "failed to monitor pressure "
                            "because there is no cgroup v2"
                        )
                        if not SysMgr.forceEnable:
                            sys.exit(-1)

                    # set node name #
                    if item == "cpuDelay":
                        target = "cpu.pressure"
                    elif item == "memDelay":
                        target = "memory.pressure"
                    elif item == "ioDelay":
                        target = "io.pressure"
                    else:
                        continue

                    if not target in os.listdir(list(cgroupV2Path)[0]):
                        SysMgr.printErr(
                            (
                                "failed to monitor pressure "
                                "because there is no '%s' in cgroup v2"
                            )
                            % target
                        )
                        if not SysMgr.forceEnable:
                            sys.exit(-1)
                    else:
                        enableList.append("cgroup2")
                else:
                    enableList.append(item)

            # apply enable items #
            enableList = set(enableList)
            if enableList:
                enableStr = "+".join(enableList)
                SysMgr.addEnvironVar("PRINTCG", enableStr)

                # remove print limitation by sort #
                if enableList - {"cpu", "cpuacct", "throttle"}:
                    SysMgr.sort = False

        # check task monitoring condition #
        try:
            # check normal stats #
            SysMgr.taskEnable = _checkTask(confData)
            if SysMgr.taskEnable:
                # check thresholds #
                SysMgr.taskThrEnable = _checkTask(confData, True)
            else:
                SysMgr.taskThrEnable = False
                SysMgr.printWarn(
                    (
                        "disabled the task monitoring "
                        "because there is no threshold for task"
                    ),
                    True,
                )
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn("failed to check task monitoring", reason=True)

        # update maximum interval #
        SysMgr.maxIntervalTick = max(
            SysMgr.maxIntervalTick, _getMaxInterval(confData)
        )

        SysMgr.printInfo(
            "applied thresholds from '%s' (maxInterval:%s)"
            % (SysMgr.confFileName, SysMgr.maxIntervalTick)
        )

        # print thresholds #
        if SysMgr.warnEnable:
            SysMgr.printWarn(UtilMgr.convDict2Str(confData))

    @staticmethod
    def doGetAffinity():
        SysMgr.warnEnable = True

        # get argument #
        if SysMgr.hasMainArg():
            value = SysMgr.getMainArgs(False)
        elif SysMgr.filterGroup:
            value = SysMgr.filterGroup
        else:
            SysMgr.printErr(
                "failed to get the CPU affinity of task "
                "because there is no target"
            )
            sys.exit(-1)

        SysMgr.checkRootPerm()

        sibling = SysMgr.groupProcEnable
        targetList = []

        try:
            for item in value:
                targetList += SysMgr.getTids(item, sibling=sibling)

            if not targetList:
                SysMgr.printErr("no thread related to '%s'" % item)
                sys.exit(-1)

            targetList = list(set(targetList))
            targetList = list(map(long, targetList))

            for tid in targetList:
                mask = SysMgr.getAffinity(tid)
                if not mask:
                    SysMgr.printErr(
                        "failed to get the CPU affinity of %s(%s)"
                        % (SysMgr.getComm(tid), tid)
                    )
                else:
                    SysMgr.printInfo(
                        "the CPU affinity of %s(%s) is %s"
                        % (SysMgr.getComm(tid), tid, mask)
                    )
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to get the CPU affinity of task", True)
            sys.exit(-1)

    @staticmethod
    def setStream(cut=True):
        if "LESS" in SysMgr.environList:
            return

        if "NOCUT" in SysMgr.environList or not cut:
            SysMgr.ttyCols = sys.maxsize

        SysMgr.streamEnable = True
        SysMgr.encodeEnable = False

    @staticmethod
    def setAffinity(mask, pids, isProcess=False):
        if not SysMgr.isLinux:
            return

        # check root permission #
        if (
            len(pids) == 1
            and str(pids[0]).isdigit()
            and SysMgr.pid == long(pids[0])
        ):
            pass
        else:
            SysMgr.checkRootPerm(exit=False, attr="warn", msg="set affinity")

        # check pid list #
        if UtilMgr.isNumber(pids):
            pids = [long(pids)]
        elif isinstance(pids, list):
            for pid in pids:
                if not str(pid).isdigit():
                    SysMgr.printErr("failed to recognize pid %s" % pid)
                    return
        else:
            SysMgr.printErr("failed to recognize pid type")
            return

        # check mask type #
        try:
            mask = long(mask, 16)
            if mask == 0:
                SysMgr.printErr(
                    "failed to set mask to 0, use bit mask bigger than 0"
                )
                return
        except:
            SysMgr.printErr("failed to recognize mask type '%s'" % mask, True)
            return

        # load libc #
        SysMgr.loadLibcObj(exit=True)

        # get the number of core #
        nrCore = SysMgr.getNrCore()

        threadList = []
        for pid in pids:
            if isProcess:
                threadList += SysMgr.getThreadList(pid)
            else:
                threadList += [pid]

        for pid in list(set(threadList)):
            try:
                if SysMgr.guiderObj:
                    guiderObj = SysMgr.guiderObj
                    # pylint: disable-next=no-member
                    ret = guiderObj.sched_setaffinity(long(pid), mask)
            except SystemExit:
                sys.exit(0)
            except:
                pass

            try:
                # define function prototype #
                SysMgr.libcObj.sched_setaffinity.argtypes = [
                    c_int,
                    c_ulong,
                    POINTER(c_ulong),
                ]

                cmask = c_ulong(((0x1 << nrCore) - 1) & mask)

                ret = SysMgr.libcObj.sched_setaffinity(
                    c_int(long(pid)), c_ulong(nrCore), byref(cmask)
                )
            except SystemExit:
                sys.exit(0)
            except:
                ret = -1
                SysMgr.printWarn(
                    "failed to set CPU affinity for %s(%s) to 0x%X"
                    % (SysMgr.getComm(pid), pid, cmask.value),
                    reason=True,
                )

            if ret >= 0:
                SysMgr.printInfo(
                    "CPU affinity of %s(%s) is changed to 0x%X"
                    % (SysMgr.getComm(pid), pid, cmask.value)
                )
            else:
                SysMgr.printErr(
                    "failed to set CPU affinity for %s(%s) to 0x%X"
                    % (SysMgr.getComm(pid), pid, cmask.value)
                )

    @staticmethod
    def getAffinity(pid):
        try:
            # pylint: disable-next=no-member
            return "0x%X" % SysMgr.guiderObj.sched_getaffinity(pid)
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # get ctypes object #
        if not SysMgr.importPkgItems("ctypes", False):
            return

        try:
            # load libc #
            if not SysMgr.loadLibcObj():
                raise Exception("no libc")

            # define function prototype #
            SysMgr.libcObj.sched_getaffinity.argtypes = [
                c_int,
                c_ulong,
                POINTER(c_ulong),
            ]

            # define variables #
            __CPU_SETSIZE = 1024
            __NCPUBITS = sizeof(c_ulong) * 8
            mask = (c_ulong * long(__CPU_SETSIZE / __NCPUBITS))()
            size = sizeof(mask)

            # get affinity #
            ret = SysMgr.libcObj.sched_getaffinity(
                c_int(long(pid)), c_ulong(size), mask
            )
            if ret < 0:
                raise Exception("wrong affinity")

            return hex(mask[0]).rstrip("L")
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn(
                "failed to get CPU affinity for %s(%s)"
                % (SysMgr.getComm(pid), pid),
                reason=True,
            )

    @staticmethod
    def getMaxThread():
        maxThdPath = "/proc/sys/kernel/threads-max"
        try:
            with open(maxThdPath, "r") as fd:
                return long(fd.readline()[:-1])
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to read %s" % maxThdPath, reason=True)
            return None

    @staticmethod
    def getOOMPath(pid):
        if not SysMgr.isLinux:
            return

        # check root permission #
        if not SysMgr.isRoot():
            return

        # set path #
        oomPath = "%s/%s/oom_score_adj" % (SysMgr.procPath, pid)
        if not os.path.isfile(oomPath):
            # use deprecated path #
            oomPath = "%s/%s/oom_adj" % (SysMgr.procPath, pid)

        return oomPath

    @staticmethod
    def getOOMAdj(pid="self"):
        oomPath = SysMgr.getOOMPath(pid)
        if not oomPath:
            return

        return SysMgr.readFile(oomPath)

    @staticmethod
    def setOOMAdj(pid="self", pri="-17"):
        oomPath = SysMgr.getOOMPath(pid)
        if not oomPath:
            return

        try:
            with open(oomPath, "w") as fd:
                fd.write(str(pri))
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn("failed to write %s" % oomPath, reason=True)

    @staticmethod
    def ping(url=[], timeout=None, count=None):
        ICMP_ECHO_REQUEST = 8

        def _checksum(source):
            total = 0
            countTo = (len(source) / 2) * 2
            count = 0
            while count < countTo:
                if isinstance(source[count + 1], (int, long)):
                    data1 = source[count + 1]
                else:
                    data1 = ord(source[count + 1])

                if isinstance(source[count], (int, long)):
                    data2 = source[count]
                else:
                    data2 = ord(source[count])

                thisVal = data1 * 256 + data2
                total += thisVal
                total = total & 0xFFFFFFFF  # Necessary?
                count = count + 2

            if countTo < len(source):
                total += ord(source[len(source) - 1])
                total = total & 0xFFFFFFFF  # Necessary?

            total = (total >> 16) + (total & 0xFFFF)
            total = total + (total >> 16)
            answer = ~total
            answer = answer & 0xFFFF

            # Swap bytes. Bugger me if I know why #
            answer = answer >> 8 | (answer << 8 & 0xFF00)

            return answer

        def _receivePing(sockList, timeout, sockInfo):
            select = SysMgr.getPkg("select")

            timeLeft = timeout
            while 1:
                startedSelect = time.time()

                # wait for event #
                whatReady = select.select(sockList, [], [], timeLeft)

                # check timeout #
                if not whatReady[0]:
                    return

                # get current time #
                timeReceived = time.time()

                howLongInSelect = timeReceived - startedSelect

                # check received packets #
                for sock in whatReady[0]:
                    recPacket, addr = sock.recvfrom(1024)
                    icmpHeader = recPacket[20:28]
                    ptype, code, checksum, packetID, sequence = struct.unpack(
                        "bbHHh", icmpHeader
                    )

                    # get sent ID #
                    ID = sockInfo[sock.fileno()][2]

                    # check sent ID #
                    if packetID != ID:
                        continue

                    # decode packet #
                    bytesInDouble = struct.calcsize("d")
                    timeSent = struct.unpack(
                        "d", recPacket[28 : 28 + bytesInDouble]
                    )[0]

                    # save times #
                    delay = timeReceived - timeSent
                    sockInfo[sock.fileno()].append(delay)
                    sockList.pop(sockList.index(sock))

                # finished #
                if not sockList:
                    return

                # timeout #
                timeLeft = timeLeft - howLongInSelect
                if timeLeft <= 0:
                    return

        def _sendPing(sock, destAddr, ID):
            socket = SysMgr.getPkg("socket")

            # Header: type(8), code(8), checksum(16), id(16), sequence(16) #
            checksumData = 0

            # Make a dummy heder with a 0 checksum #
            header = struct.pack(
                "bbHHh", ICMP_ECHO_REQUEST, 0, checksumData, ID, 1
            )
            bytesInDouble = struct.calcsize("d")
            data = (192 - bytesInDouble) * "Q".encode()
            data = struct.pack("d", time.time()) + data

            # Calculate the checksum on the data and the dummy header. #
            checksumData = _checksum(header + data)

            """
            Now that we have the right checksum,
            we put that in. It's just easier
            to make up a new header than to stuff it into the dummy.
            """
            header = struct.pack(
                "bbHHh",
                ICMP_ECHO_REQUEST,
                0,
                socket.htons(checksumData),
                ID,
                1,
            )
            packet = header + data

            sock.sendto(packet, (destAddr, 0))

        def _convAddrs(addrList):
            def __expandAddrs(addrs, item):
                newAddrs = []
                if item.isdigit():
                    if not addrs:
                        newAddrs.append(item)
                    else:
                        for word in addrs:
                            newAddrs.append("%s.%s" % (word, item))
                elif item.count("-") == 1:
                    start, end = item.split("-")
                    if start.strip().isdigit() and end.strip().isdigit():
                        for idx in xrange(long(start), long(end) + 1):
                            if not addrs:
                                newAddrs.append(idx)
                                continue

                            for word in addrs:
                                newAddrs.append("%s.%s" % (word, idx))
                    elif not addrs:
                        newAddrs.append(item)
                    else:
                        for word in addrs:
                            newAddrs.append("%s.%s" % (word, idx))
                elif item == "*":
                    for idx in xrange(1, 255):
                        if not addrs:
                            newAddrs.append(idx)
                        else:
                            for word in addrs:
                                newAddrs.append("%s.%s" % (word, idx))
                else:
                    if not addrs:
                        newAddrs.append(item)
                    else:
                        for word in addrs:
                            newAddrs.append("%s.%s" % (word, item))

                return newAddrs

            if not addrList:
                return addrList

            newAddrList = []
            for addr in addrList:
                # digit address #
                checkAddr = addr.replace(".", "")
                if checkAddr.isdigit():
                    newAddrList.append(addr)
                    continue

                # name address #
                checkAddr = checkAddr.replace("-", "").replace("*", "")
                if not checkAddr.isdigit():
                    newAddrList.append(addr)
                    continue

                # convert addresses #
                addrs = []
                fields = addr.split(".")
                for item in fields:
                    addrs = __expandAddrs(addrs, item)

                # add addresses #
                newAddrList += addrs

            return newAddrList

        def _doPing(addrList, timeout, seq=None, verb=True):
            socket = SysMgr.getPkg("socket")

            # set attributes #
            try:
                ttl = 64
                # icmp = socket.getprotobyname("icmp")
                icmp = socket.IPPROTO_ICMP
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr("failed to ping", True)
                return 0

            # define socket list #
            sockList = []
            sockInfo = {}

            # send packets #
            for idx, destAddr in enumerate(addrList):
                try:
                    destIPAddr = socket.gethostbyname(destAddr)
                except SystemExit:
                    sys.exit(0)
                except:
                    destIPAddr = destAddr

                # create an unique ID #
                ICMP_ID = (os.getpid() + idx) & 0xFFFF

                # create a socket #
                try:
                    sock = socket.socket(socket.AF_INET, socket.SOCK_RAW, icmp)
                    sock.setsockopt(socket.IPPROTO_IP, socket.IP_TTL, ttl)
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr("failed to ping", True)
                    return 0

                # increase socket buffer size to 1MB #
                try:
                    sock.setsockopt(
                        socket.SOL_SOCKET, socket.SO_SNDBUF, 1 << 20
                    )
                    sock.setsockopt(
                        socket.SOL_SOCKET, socket.SO_RCVBUF, 1 << 20
                    )
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

                # send packet #
                try:
                    _sendPing(sock, destIPAddr, ICMP_ID)

                    if SysMgr.warnEnable:
                        SysMgr.printWarn("sent a packet to %s" % destAddr)
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printWarn(
                        "failed to send data to %s" % destAddr, reason=True
                    )
                    continue

                # get address details #
                if destAddr == destIPAddr:
                    addrInfo = destAddr
                else:
                    addrInfo = "%s(%s)" % (destAddr, destIPAddr)

                # save sock info #
                sockList.append(sock)
                sockInfo[sock.fileno()] = [sock, addrInfo, ICMP_ID]

            # receive packet #
            _receivePing(sockList, timeout, sockInfo)

            # add timed out info and close all sockets #
            for name, attr in sockInfo.items():
                if len(attr) <= 3:
                    sockInfo[name].append(timeout)
                try:
                    attr[0].close()
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

            # check return condition #
            if not verb:
                return

            # set sequence string #
            if seq is not None:
                seqstr = "[%s] " % seq
            else:
                seqstr = ""

            # define json variable #
            jsonData = {"seq": seq, "success": {}, "fail": {}}
            pretty = not SysMgr.findOption("Q")

            timeoutstr = ""
            timeoutlinestr = ""

            convColor = UtilMgr.convColor
            removeColor = UtilMgr.removeColor

            printTimeout = not "NOTIMEOUT" in SysMgr.environList

            # print results #
            for attr in sorted(sockInfo.values(), key=lambda x: x[3]):
                name = attr[1]
                elapsed = attr[3]

                # success #
                if elapsed < timeout:
                    delay = attr[3] * 1000

                    if SysMgr.jsonEnable:
                        jsonData["success"].setdefault(name, {})
                        jsonData["success"][name]["time"] = delay
                        continue

                    name = convColor(name, "GREEN", 15, "left")
                    delaystr = convColor(
                        UtilMgr.convNum(delay, True, 3), "YELLOW"
                    )

                    SysMgr.printPipe("%s%s: %s ms" % (seqstr, name, delaystr))
                # timeout #
                elif printTimeout:
                    if SysMgr.jsonEnable:
                        jsonData["fail"].setdefault(name, {})
                        jsonData["fail"][name]["time"] = timeout
                        continue

                    name = convColor(name, "RED", 15, "left")

                    if (
                        len(removeColor(timeoutlinestr))
                        + len(removeColor(name))
                        >= SysMgr.ttyCols
                    ):
                        timeoutstr = "%s %s\n" % (
                            timeoutstr,
                            timeoutlinestr.lstrip(),
                        )
                        timeoutlinestr = name
                    else:
                        timeoutlinestr = "%s %s" % (timeoutlinestr, name)

            # print results in JSON format #
            if SysMgr.jsonEnable:
                SysMgr.printPipe(UtilMgr.convDict2Str(jsonData, pretty=pretty))
            elif timeoutlinestr:
                timeoutstr = "%s %s\n" % (timeoutstr, timeoutlinestr.lstrip())

            # print timeout info #
            if timeoutstr:
                SysMgr.printPipe("%stimeout:\n%s" % (seqstr, timeoutstr))

        # check root permission for Linux #
        if SysMgr.isLinux and not SysMgr.checkRootPerm(exit=False, msg="ping"):
            return

        # get address list #
        urlList = []
        if url:
            urlList = url
        elif SysMgr.hasMainArg():
            urlList = SysMgr.getMainArgs()
        elif SysMgr.inputParam:
            try:
                files = SysMgr.inputParam.split(",")
                files = UtilMgr.cleanItem(files)
                for fname in files:
                    with open(fname, "r") as fd:
                        urlList += fd.readlines()
                urlList = UtilMgr.cleanItem(urlList)
            except:
                SysMgr.printErr(
                    "failed to read addresses from '%s'" % ",".join(files),
                    True,
                )
                sys.exit(-1)

        # check input #
        if not urlList:
            SysMgr.printErr("no input for address")
            sys.exit(-1)

        # convert addresses #
        urlList = _convAddrs(urlList)

        # set repeat count #
        if not count:
            if SysMgr.repeatInterval == 0:
                count = sys.maxsize
            else:
                count = SysMgr.repeatInterval

        # set count string #
        if count == sys.maxsize or count == SysMgr.maxSize:
            countStr = "INFINITE"
        else:
            countStr = "%s" % UtilMgr.convNum(count)

        SysMgr.printInfo("set repeat count to %s" % countStr)

        # set timeout #
        if not timeout:
            timeout = SysMgr.getOption("T")
            if timeout:
                try:
                    timeout = float(timeout)
                except:
                    SysMgr.printErr(
                        "failed to set timeout '%s'" % timeout, True
                    )
                    sys.exit(-1)
            else:
                timeout = 3

        # set interval #
        interval = SysMgr.getOption("i")
        try:
            interval = float(interval)
        except:
            interval = 1

        # print timeout info #
        timeoutstr = "%f" % timeout
        timeoutstr = timeoutstr.rstrip("0")
        if timeoutstr.endswith("."):
            timeoutstr = timeoutstr[:-1]
        SysMgr.printInfo("set timeout to %s sec\n" % timeoutstr)
        master = True

        # ping #
        for seq in xrange(count):
            candIdx = 0

            while 1:
                workload = 1024 - SysMgr.getNrFd(SysMgr.pid)
                candList = urlList[candIdx : candIdx + workload]

                # create worker processes #
                if candList and candList[-1] != urlList[-1]:
                    if len(SysMgr.getChildList()) >= SysMgr.getNrCore():
                        SysMgr.waitChild()
                    pid = SysMgr.createProcess()
                    if pid == 0:
                        master = False
                else:
                    pid = 0

                try:
                    if pid == 0:
                        _doPing(candList, timeout, seq=seq)
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr(
                        "failed to send ping to [ %s ]" % ", ".join(urlList),
                        True,
                    )

                if pid == 0:
                    SysMgr.waitChild()
                    if master:
                        break
                    else:
                        sys.exit(0)
                elif candList and candList[-1] == urlList[-1]:
                    SysMgr.waitChild()
                    break

                candIdx += len(candList)
                if master and pid:
                    UtilMgr.printProgress(candIdx, len(urlList))

            time.sleep(interval)

    @staticmethod
    def getExeCmd(pid):
        # get execute command for Guider #
        cmd = SysMgr.getCmdline(pid, retList=True)
        if len(cmd) < 2:
            return cmd

        # executed using modbule with -m option #
        if cmd[1] == "-m":
            return cmd[:3]

        # TODO: support packaged executable binary #

        # executed using source path #
        cmd = cmd[:2]

        # convert relative path to absolute path for source file #
        if not cmd[-1].startswith("/"):
            pwd = SysMgr.getPwd(pid)
            cmd[-1] = os.path.realpath(
                os.path.join(pwd if pwd else "", cmd[-1])
            )

        return cmd

    @staticmethod
    def getNetDevData():
        # read net buf #
        netBuf = SysMgr.readProcStat(
            SysMgr.netdevFd, "net/dev", SysMgr, "netdevFd"
        )
        if netBuf:
            return netBuf[2:]
        else:
            netBuf

    @staticmethod
    def getConfigItem(name, data=None, itype="dict"):
        if data:
            confData = data
        else:
            confData = ConfigMgr.confData

        if name in confData:
            confData = confData[name]
        else:
            return None

        if itype == "dict":
            if isinstance(confData, list):
                confData = UtilMgr.convStr2Dict("\n".join(confData), True)

            if isinstance(confData, dict):
                return confData
            else:
                return None
        elif itype == "list":
            if isinstance(confData, list):
                confData = ['{"array":'] + confData + ["}"]
                confData = UtilMgr.convStr2Dict("\n".join(confData), True)
                if not confData or not "array" in confData:
                    return None
                else:
                    return confData["array"]
            else:
                return None
        else:
            return confData

    @staticmethod
    def updateThreshold(path):
        # load data from the file #
        try:
            value = SysMgr.loadConfig(path, True, True)
            if not value:
                return None
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printOpenWarn(path, True, True)
            return None

        # get new threshold values #
        value = SysMgr.getConfigItem("threshold", value)

        # update threshold condition #
        try:
            SysMgr.thrData.update(value)
        except:
            SysMgr.printErr("failed to update threshold data", True)
            return None

        SysMgr.applyThreshold(update=True)

        # print threshold data #
        SysMgr.printWarn(UtilMgr.convDict2Str(SysMgr.thrData, pretty=True))

    @staticmethod
    def updateConfigPath():
        if os.path.exists(SysMgr.confFileName):
            return

        for path in (
            os.getcwd(),
            os.path.dirname(os.environ["CMDLINE"])
            if "CMDLINE" in os.environ
            else ".",
            os.path.dirname(SysMgr.getPyPath()),
            os.path.join(sys.prefix, "local/guider"),
            "/usr/share/guider",
        ):
            fullPath = os.path.realpath(os.path.join(path, "guider.conf"))
            if os.path.exists(fullPath):
                SysMgr.confFileName = fullPath
                break

        # no path #
        if not os.path.exists(SysMgr.confFileName):
            SysMgr.printErr("no file for config")
            sys.exit(-1)

    @staticmethod
    def loadConfig(fname, local=False, verb=True):
        try:
            # set return dict #
            if local:
                confData = {}
            else:
                confData = ConfigMgr.confData
            # get real path #
            fname = os.path.realpath(fname)

            targetList = []
            fd = None
            skip = False
            fd = open(fname, "r")
            for line in fd.readlines():
                if not line:
                    continue

                line = line.strip()
                if not line or line == "\n":
                    continue
                elif line.startswith("#") or line.startswith("//"):
                    continue
                elif skip:
                    if line.startswith("'''") or line.startswith("*/"):
                        skip = False
                    continue
                elif line.startswith("'''") or line.startswith("/*"):
                    skip = True
                    continue
                elif line.startswith("<") and line.endswith(">"):
                    entry = line[1:-1]
                    confData.setdefault(entry, [])
                    targetList = confData[entry]
                    continue
                else:
                    targetList.append(line)

            if confData:
                SysMgr.printInfo("loaded config from '%s'" % fname)

            return confData
        except SystemExit:
            sys.exit(0)
        except:
            if verb:
                SysMgr.printErr(
                    "failed to load config from '%s'" % fname, reason=True
                )
            return None
        finally:
            if fd:
                fd.close()

    @staticmethod
    def getPyLibPath(load=True):
        if SysMgr.pyLibPath:
            return SysMgr.pyLibPath

        try:
            # set library name #
            exePath = SysMgr.getExeName(SysMgr.pid)
            exeName = os.path.basename(exePath)
            libName = "lib%s" % exeName
            if not load:
                return libName

            # search ld.so.cache #
            if not SysMgr.pyLibPath:
                SysMgr.pyLibPath = SysMgr.findLib(libName, inc=True)[0]
            # search standard path #
            else:
                libName = SysMgr.loadLib(libName)._name
                if libName:
                    SysMgr.pyLibPath = FileAnalyzer.getMapFilePath(
                        SysMgr.pid, libName
                    )

            return SysMgr.pyLibPath
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn(
                "failed to get path for python library", reason=True
            )

    @staticmethod
    def getExeName(pid, verb=True, cache=False):
        # use psutil #
        if not SysMgr.isLinux:
            try:
                psutil = SysMgr.getPkg("psutil", False)
                if psutil:
                    return psutil.Process(long(pid)).exe()
                else:
                    return None
            except SystemExit:
                sys.exit(0)
            except:
                return None

        try:
            exePath = "%s/%s/exe" % (SysMgr.procPath, pid)
            return os.readlink(exePath)
        except SystemExit:
            sys.exit(0)
        except:
            if verb:
                SysMgr.printWarn(
                    "failed to get binary path for %s process" % pid,
                    reason=True,
                )
            return None

    @staticmethod
    def getPyPath():
        return os.path.realpath(__file__)

    @staticmethod
    def getCmdline(pid, retList=False, cache=False):
        # use psutil #
        if not SysMgr.isLinux:
            try:
                psutil = SysMgr.getPkg("psutil", False)
                if not psutil:
                    raise Exception()

                cmdline = psutil.Process(long(pid)).cmdline()
                if retList:
                    return cmdline
                else:
                    return " ".join(cmdline)
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printWarn(
                    "failed to get cmdline for task %s" % pid, reason=True
                )
                if retList:
                    return []
                else:
                    return ""

        cmdlinePath = "%s/%s/cmdline" % (SysMgr.procPath, pid)

        try:
            with open(cmdlinePath, "r") as fd:
                res = fd.readline()
                if retList:
                    return res.split("\x00")
                else:
                    return res.replace("\x00", " ").strip()
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printOpenWarn(cmdlinePath)
            if retList:
                return []
            else:
                return ""

    @staticmethod
    def getTracerId(pid):
        try:
            statusPath = "%s/%s/status" % (SysMgr.procPath, pid)
            with open(statusPath, "r") as fd:
                for line in fd.readlines():
                    if line.startswith("TracerPid"):
                        return long(line.split(":")[1].split(None, 1)[0])
        except SystemExit:
            sys.exit(0)
        except:
            return 0

    @staticmethod
    def getUid(pid, itype="real"):
        try:
            path = "%s/status" % pid
            data = SysMgr.procReadlines(path)

            for line in data:
                if line.startswith("Uid"):
                    if itype == "real":
                        idx = 0
                    elif itype == "effective":
                        idx = 1
                    elif itype == "saved":
                        idx = 2
                    elif itype == "filesystem":
                        idx = 3

                    return long(line.split(":")[1].split()[idx])
        except SystemExit:
            sys.exit(0)
        except:
            comm = SysMgr.getComm(pid)
            SysMgr.printErr(
                "failed to get UID for %s(%s)" % (comm, pid), reason=True
            )

        return 0

    @staticmethod
    def getPpid(pid, target="PPid"):
        statusPath = "%s/%s/status" % (SysMgr.procPath, pid)
        try:
            with open(statusPath, "r") as fd:
                for line in fd.readlines():
                    if line.startswith(target):
                        return line.split(":")[1].split(None, 1)[0]
        except SystemExit:
            sys.exit(0)
        except:
            return None

    @staticmethod
    def getTgid(pid):
        statusPath = "%s/%s/status" % (SysMgr.procPath, pid)
        try:
            with open(statusPath, "r") as fd:
                for line in fd.readlines():
                    if line.startswith("Tgid"):
                        return line.split(":")[1].split(None, 1)[0]
        except SystemExit:
            sys.exit(0)
        except:
            return None

    @staticmethod
    def resizeShm(shm, size):
        mmap = SysMgr.getPkg("mmap", False)
        if not mmap:
            return

        mmap.resize(size)

    @staticmethod
    def createShm(path=None, size=PAGESIZE):
        if not SysMgr.isLinux:
            return

        mmap = SysMgr.getPkg("mmap", False)
        if not mmap:
            return

        # create a memory segment #
        mem = mmap.mmap(-1, size)

        # initialize the memory #
        if sys.version_info < (3, 0, 0):
            mem.write(bytes("0") * size)

        return mem

    @staticmethod
    def getCommList(pidList, isList=False, tok=", "):
        while 1:
            try:
                if isList:
                    return [SysMgr.getComm(pid) for pid in pidList]
                else:
                    return tok.join(
                        [
                            "%s(%s)" % (SysMgr.getComm(pid), pid)
                            for pid in pidList
                        ]
                    )
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printWarn("failed to convert PIDs to COMMs", True, True)

    @staticmethod
    def getFdName(pid, fd):
        if not SysMgr.isLinux:
            return None

        try:
            fdPath = "%s/%s/fd/%s" % (SysMgr.procPath, pid, fd)
            return os.readlink(fdPath)
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn(
                "failed to read file name from fd(%s) for %s(%s)"
                % (fd, SysMgr.getComm(pid), pid),
                reason=True,
            )
            return None

    @staticmethod
    def getPwd(pid):
        if not SysMgr.isLinux:
            try:
                psutil = SysMgr.getPkg("psutil", False)
                if psutil:
                    return psutil.Process(long(pid)).environ()["PWD"]
                else:
                    return None
            except SystemExit:
                sys.exit(0)
            except:
                return None

        try:
            pwdPath = "%s/%s/cwd" % (SysMgr.procPath, pid)
            pwd = os.readlink(pwdPath)
        except SystemExit:
            sys.exit(0)
        except:
            return None

        return pwd

    @staticmethod
    def clearCommCache():
        SysMgr.commCache = {}

    @staticmethod
    def getComm(pid, cache=False, save=False):
        if pid in SysMgr.commCache:
            return SysMgr.commCache[pid]

        # linux #
        try:
            if pid in SysMgr.commFdCache:
                fd = SysMgr.commFdCache[pid]
                fd.seek(0)
                comm = fd.readlines()[0][:-1]
                if save:
                    SysMgr.commCache[pid] = comm
                return comm
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # use psutil #
        if not SysMgr.isLinux:
            try:
                psutil = SysMgr.getPkg("psutil", False)
                if psutil:
                    return psutil.Process(long(pid)).name()
                else:
                    return None
            except SystemExit:
                sys.exit(0)
            except:
                return None

        comm = None
        commPath = "%s/%s/comm" % (SysMgr.procPath, pid)

        try:
            fd = open(commPath, "r")

            comm = fd.readline()[:-1]

            # cache comm #
            if save:
                SysMgr.commCache[pid] = comm

            # flush fd cache #
            if SysMgr.maxKeepFd < fd.fileno():
                SysMgr.commFdCache = {}
            # cache fd #
            elif cache:
                SysMgr.commFdCache[pid] = fd
            else:
                fd.close()
        except SystemExit:
            sys.exit(0)
        except:
            return None

        return comm

    @staticmethod
    def getPyConfig(item="all", var=None):
        try:
            sysconfig = SysMgr.getPkg("sysconfig")

            if item == "path":
                varDict = sysconfig.get_paths()
            else:
                varDict = sysconfig.get_config_vars()

            if var:
                return varDict[var]
            else:
                return varDict
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn("failed to get python config", True, True)
            return None

    @staticmethod
    def getMyBacktrace():
        # load libc #
        SysMgr.loadLibcObj(exit=True)

        # define functions #
        libcObj = SysMgr.libcObj

        libcObj.backtrace.argtypes = [c_void_p, c_int]
        libcObj.backtrace.restype = c_int

        libcObj.backtrace_symbols.argtypes = [c_void_p, c_int]
        libcObj.backtrace_symbols.restype = POINTER(c_char_p)

        # define buffers #
        buf = (c_void_p * 1024)()

        # call backtrace #
        ret = libcObj.backtrace(byref(buf), c_int(1024))
        syms = libcObj.backtrace_symbols(byref(buf), c_int(ret))

        return syms

    @staticmethod
    def isBlkDev(path):
        stat = SysMgr.getPkg("stat")

        try:
            return stat.S_ISBLK(os.stat(path).st_mode)
        except:
            return False

    @staticmethod
    def isKernelTask(pid):
        return (
            True
            if SysMgr.readFile(
                os.path.join(SysMgr.procPath, str(pid), "cmdline"), 1
            )
            == ""
            else False
        )

    @staticmethod
    def dlopen(path):
        # load libc #
        if not SysMgr.loadLibcObj():
            return

        try:
            path = path.encode()
        except:
            pass

        try:
            func = "__libc_dlopen_mode"
            funcp = getattr(SysMgr.libcObj, func)
            funcp.argtypes = [c_void_p, c_int]
            funcp.restype = c_ulong
            return funcp(path, 1)
        except:
            SysMgr.printErr("failed to call dlopen", reason=True)
            return None

    @staticmethod
    def setComm(comm):
        if not SysMgr.isLinux:
            return

        # try to set comm using native lib #
        try:
            # pylint: disable-next=no-member
            SysMgr.guiderObj.prctl(15, comm, 0, 0, 0)
            return
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # load libc #
        if not SysMgr.loadLibcObj():
            return

        try:
            SysMgr.libcObj.prctl(15, c_char_p(comm.encode("utf-8")), 0, 0, 0)
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn(
                "failed to set comm to '%s'" % comm, True, reason=True
            )

    @staticmethod
    def getPicklePkg(isExit=True):
        pickle = SysMgr.getPkg("cPickle", isExit)
        if not pickle:
            pickle = SysMgr.getPkg("pickle", isExit)
            if not pickle:
                return None
        return pickle

    @staticmethod
    def fadvise(path=None, fd=None, pos=0, size=0, advice=None):
        try:
            if path:
                f = open(path, "br")
                fd = f.fileno()
                doClose = True
            elif not fd:
                SysMgr.printErr(
                    "failed to get file info because neither path nor fd"
                )
                sys.exit(0)
            else:
                path = fd.name
                doClose = False

            # get whole file size #
            if size == 0:
                size = 0

            # set advice #
            if not advice:
                advice = os.POSIX_FADV_DONTNEED

            # call fadvise #
            ret = os.posix_fadvise(fd, pos, size, advice)

            if doClose:
                f.close()

            return ret
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to fadvise '%s'" % path, True)

    @staticmethod
    def isAlive(tid):
        try:
            statPath = "%s/%s/stat" % (SysMgr.procPath, tid)
            with open(statPath, "r") as fd:
                stat = fd.read()

            # convert string to list #
            statList = stat.split(") ", 1)[1].split()

            if statList[0] == "Z":
                return False
            else:
                return True
        except SystemExit:
            sys.exit(0)
        except:
            return False

    @staticmethod
    def getPkg(name, isExit=True, isTemp=False, isRoot=True):
        # check cache #
        try:
            return SysMgr.impPkg[name]
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # check blacklist #
        if not isExit and name in SysMgr.skipImpPkg:
            return None

        # import package #
        try:
            obj = __import__(name, fromlist=[name] if isRoot else [None])
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn(
                "failed to import python package: %s " % name, isExit
            )

            # register to blacklist #
            SysMgr.skipImpPkg[name] = True

            # check return condition #
            if isExit:
                sys.exit(-1)
            return None

        # save package object #
        if not isTemp:
            SysMgr.impPkg[name] = obj

        return obj

    @staticmethod
    def getIowaitTime(dev=None):
        if not SysMgr.diskStats or not SysMgr.prevDiskStats:
            return "0/0"

        flist = {}
        dlist = {}
        curReadMsTotal = prevReadMsTotal = 0
        curWriteMsTotal = prevWriteMsTotal = 0
        curNrReadTotal = prevNrReadTotal = 0
        curNrWriteTotal = prevNrWriteTotal = 0
        curIoMsTotal = prevIoMsTotal = 0
        curWIoMsTotal = prevWIoMsTotal = 0

        # get total iowait time for read #
        for line in SysMgr.diskStats:
            items = line.split()
            dev = items[2]

            if not dev[-1].isdigit():
                flist[dev] = None
            else:
                skip = False
                for item in flist:
                    if item != dev and dev.startswith(item):
                        skip = True

                if skip:
                    continue

            dlist[dev] = {}
            dlist[dev]["curNrRead"] = long(items[3])
            dlist[dev]["curNrWrite"] = long(items[7])
            dlist[dev]["curReadMs"] = long(items[6])
            dlist[dev]["curWriteMs"] = long(items[10])
            dlist[dev]["curIoMs"] = long(items[12])
            dlist[dev]["curWIoMs"] = long(items[13])

            curNrReadTotal += long(items[3])
            curNrWriteTotal += long(items[7])
            curReadMsTotal += long(items[6])
            curWriteMsTotal += long(items[10])
            curIoMsTotal += long(items[12])
            curWIoMsTotal += long(items[13])

        # get total iowait time for write #
        for line in SysMgr.prevDiskStats:
            items = line.split()
            dev = items[2]

            skip = False
            for item in flist:
                if item != dev and dev.startswith(item):
                    skip = True

            if skip:
                continue

            dlist[dev]["prevNrRead"] = long(items[3])
            dlist[dev]["prevNrWrite"] = long(items[7])
            dlist[dev]["prevReadMs"] = long(items[6])
            dlist[dev]["prevWriteMs"] = long(items[10])
            dlist[dev]["prevIoMs"] = long(items[12])
            dlist[dev]["prevWIoMs"] = long(items[13])

            prevNrReadTotal += long(items[3])
            prevNrWriteTotal += long(items[7])
            prevReadMsTotal += long(items[6])
            prevWriteMsTotal += long(items[10])
            prevIoMsTotal += long(items[12])
            prevWIoMsTotal += long(items[13])

        """
        for dev, stat in dlist.items():
            read = stat["curNrRead"] - stat["prevNrRead"]
            write = stat["curNrWrite"] - stat["prevNrWrite"]

        nrReadTotal = curNrReadTotal - prevNrReadTotal
        nrWriteTotal = curNrWriteTotal - prevNrWriteTotal
        ioMsTotal = curIoMsTotal - prevIoMsTotal
        wIoMsTotal = curWIoMsTotal - prevWIoMsTotal
        """

        readMsTotal = curReadMsTotal - prevReadMsTotal
        writeMsTotal = curWriteMsTotal - prevWriteMsTotal

        retstr = "%s/%s" % (
            UtilMgr.convSize2Unit(readMsTotal),
            UtilMgr.convSize2Unit(writeMsTotal),
        )

        return retstr

    def updateNodeInfo(self):
        if self.devNodeInfo:
            return

        try:
            nodePath = "/dev/block"
            for item in os.listdir(nodePath):
                path = os.path.join(nodePath, item)
                if os.path.islink(path):
                    path = os.path.realpath(
                        os.path.join(nodePath, os.readlink(path))
                    )
                self.devNodeInfo[item] = path
        except SystemExit:
            sys.exit(0)
        except:
            pass

    @staticmethod
    def updateDiskStats():
        try:
            SysMgr.diskStatsFd.seek(0)
            SysMgr.prevDiskStats = SysMgr.diskStats
            SysMgr.diskStats = SysMgr.diskStatsFd.readlines()
        except SystemExit:
            sys.exit(0)
        except:
            try:
                diskstatPath = "%s/diskstats" % SysMgr.procPath
                SysMgr.diskStatsFd = open(diskstatPath, "r")
                SysMgr.diskStats = SysMgr.diskStatsFd.readlines()
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printOpenWarn(diskstatPath)

    @staticmethod
    def getMountData(pid="self"):
        try:
            if pid != "self":
                raise Exception()

            SysMgr.mountFd.seek(0)
            return SysMgr.mountFd.readlines()
        except SystemExit:
            sys.exit(0)
        except:
            try:
                mountPath = "%s/%s/mountinfo" % (SysMgr.procPath, pid)
                mountFd = open(mountPath, "r")
                if pid == "self":
                    SysMgr.mountFd = mountFd
                return mountFd.readlines()
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printOpenWarn(mountPath)

    @staticmethod
    def convMemStat(mlist):
        vssIdx = ConfigMgr.STATM_TYPE.index("TOTAL")
        rssIdx = ConfigMgr.STATM_TYPE.index("RSS")
        return {
            "vss": long(mlist[vssIdx]) << 12,
            "rss": long(mlist[rssIdx]) << 12,
        }

    @staticmethod
    def getMemStat(pid):
        try:
            statmPath = "%s/%s/statm" % (SysMgr.procPath, pid)
            with open(statmPath, "r") as fd:
                return fd.readlines()[0].split()
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printOpenWarn(statmPath)
            return

    @staticmethod
    def getPidFilter(name="common_pid", comm=True, force=True):
        if SysMgr.pidFilter and force:
            return SysMgr.pidFilter

        cmd = ""
        for cond in list(SysMgr.filterGroup):
            try:
                # add comm #
                if comm:
                    cmd += """%scomm == "*%s*" || """ % (cmd, cond)

                # just PID #
                if cond.isdigit():
                    cmd += "%s == %s || " % (name, long(cond))
                # PID bigger or lesser #
                elif cond.startswith(">") or cond.endswith("<"):
                    cmd += "%s >= %s || " % (name, long(cond.strip("<>")))
                    continue
                # PID bigger or lesser #
                elif cond.startswith("<") or cond.endswith(">"):
                    cmd += "%s <= %s || " % (name, long(cond.strip("<>")))
                    continue
            except SystemExit:
                sys.exit(0)
            except:
                pass

        if cmd != "":
            cmd = "(" + cmd[: cmd.rfind("||")] + ")"

        if force:
            SysMgr.pidFilter = cmd

        return cmd

    @staticmethod
    def isExceptTarget(tid, tdata, comm=None, plist=[]):
        # check filter #
        if not SysMgr.filterGroup:
            return

        filters = SysMgr.filterGroup

        # get comm #
        if not comm:
            comm = tdata[tid]["comm"]

        # check exceptional comm #
        exceptComm = SysMgr.environList.get("EXCEPTCOMM")
        if exceptComm and UtilMgr.isValidStr(comm, exceptComm):
            return True

        # check exceptional tid #
        exceptTid = SysMgr.environList.get("EXCEPTTID")
        if exceptTid and tid in exceptTid:
            return True

        # check target comm #
        if UtilMgr.isValidStr(comm, filters):
            return False

        # check tid #
        for item in filters:
            if item in (tid, "") or SysMgr.isValidTid(tid, item):
                return False

        # check process group #
        if SysMgr.groupProcEnable:
            tgid = tdata[tid]["tgid"]

            # check tgid in process list by tid #
            if str(tgid) in plist:
                return False

            # check tgid in filter list by tid #
            for item in filters:
                try:
                    if tgid == item or SysMgr.isValidTid(tgid, item):
                        return False
                    elif tgid == "0" or tgid.startswith("-"):
                        continue
                    elif (
                        tgid == tdata[item]["tgid"]
                        or tgid == SysMgr.savedProcTree[item]
                    ):
                        return False
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

        return True

    @staticmethod
    def getTraceItem(string):
        if SysMgr.tgidEnable:
            # record-tgid option #
            m = re.match(SysMgr.traceTgidFormat2, string)
            if not m:
                # print-tgid option #
                return re.match(SysMgr.traceTgidFormat1, string)

        # no tgid #
        return re.match(SysMgr.traceNoTgidFormat, string)

    @staticmethod
    def isValidEnableOption(options):
        if not options:
            return False

        optionList = "ABCDEFGHIKLMNOPRSUTWXYabcdefghijklmnopqrrstuvwxy"
        for opt in options:
            if not opt in optionList:
                return False

        return True

    @staticmethod
    def isValidOption(option):
        if not option:
            return False

        # check number option such like signal number #
        if option.isdigit():
            return True

        optionList = "ABCDEFGHIJKLMNOPQRSTUWXYZabcdefgijklmnopqrstuvwxy"
        if option in optionList:
            return True
        else:
            return False

    @staticmethod
    def isValidTid(tid, cond):
        try:
            tid = long(tid)

            # check same condition #
            if cond in (tid, str(tid)):
                return True

            # lvalue checker #
            lval = cond.find(">")
            if lval == 0 and tid >= long(cond[1:]):
                return True
            elif lval == len(cond) - 1 and tid <= long(cond[:-1]):
                return True

            # rvalue checker #
            rval = cond.find("<")
            if rval == 0 and tid <= long(cond[1:]):
                return True
            elif rval == len(cond) - 1 and tid >= long(cond[:-1]):
                return True

            return False
        except SystemExit:
            sys.exit(0)
        except:
            return False

    @staticmethod
    def printList(itemList, skipList=[], stripPrefix="", printer=None):
        SysMgr.setStream()

        if not printer:
            printer = SysMgr.printPipe

        lineLen = 0
        for idx, item in enumerate(itemList):
            if item in skipList:
                continue
            elif lineLen + len(item) > SysMgr.ttyCols / 2:
                newline = True
                lineLen = 0
            else:
                newline = False
                lineLen += len(item) + 5

            printer(
                "%3s) %s  " % (idx, UtilMgr.lstrip(item, stripPrefix)),
                newline=newline,
            )

        printer("\n")

    @staticmethod
    def printBacktrace():
        traceback = SysMgr.getPkg("traceback")
        traceback.print_stack(file=SysMgr.stderr)

    @staticmethod
    def waitEvent(
        ignChldSig=True, exit=False, forceExit=False, block=True, timeout=0
    ):
        # ignore SIGCHLD #
        if ignChldSig:
            signal.signal(signal.SIGCHLD, signal.SIG_IGN)

        # backup SIGINT handler and set new handler #
        if SysMgr.waitEnable and block:
            handle = signal.getsignal(signal.SIGINT)
            signal.signal(signal.SIGINT, SysMgr.defaultHandler)

        # pause task #
        try:
            if timeout:
                time.sleep(timeout)
            else:
                signal.pause()
        except SystemExit:
            if exit:
                sys.exit(0)
            elif forceExit:
                os._exit(0)
        except:
            pass

        # restore SIGINT handler #
        if SysMgr.waitEnable and block:
            signal.signal(signal.SIGINT, handle)

    @staticmethod
    def backupFile(origFile):
        if not origFile or not os.path.isfile(origFile):
            return None
        elif "NOBACKUP" in SysMgr.environList:
            return None

        try:
            newFile = "%s.old" % origFile

            # delete old backup file #
            if os.path.exists(newFile):
                os.remove(newFile)

            # rename old target file #
            os.rename(origFile, newFile)
            os.chmod(newFile, 0o777)

            SysMgr.printInfo(
                "renamed '%s' to '%s' for backup" % (origFile, newFile)
            )

            return newFile
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                "failed to backup '%s' to '%s'" % (origFile, newFile), True
            )
            return None

    @staticmethod
    def convFullPath(path):
        return os.path.realpath(os.path.expanduser(path))

    @staticmethod
    def convRealPath(flist):
        if UtilMgr.isString(flist):
            flist = [flist]

        nlist = []
        for fpath in flist:
            fpath = fpath.strip()

            # check exception character #
            if fpath.startswith("^"):
                fpath = fpath[1:]
                exflag = True
            else:
                exflag = False

            # check files #
            items = UtilMgr.convPath(fpath, warn=False)
            for path in items:
                try:
                    rpath = os.path.realpath(path)
                    if not rpath.startswith("/"):
                        dirname = os.path.dirname(path)
                        rpath = os.path.join(dirname, rpath)
                        rpath = os.path.realpath(rpath)

                    if exflag:
                        rpath = "^" + rpath

                    nlist.append(rpath)
                except SystemExit:
                    sys.exit(0)
                except:
                    if os.path.exists(path):
                        if exflag:
                            path = "^" + path

                        nlist.append(path)
                    else:
                        SysMgr.printWarn(
                            "failed to convert '%s' to real path" % path,
                            reason=True,
                            always=True,
                        )

        return nlist

    @staticmethod
    def getErrReason():
        # pylint: disable=not-callable
        if not SysMgr.geterrnoFunc:
            # load libc #
            ret = SysMgr.loadLibcObj()
            if not ret:
                SysMgr.geterrnoFunc = -1
                return "N/A"

            # check function #
            if hasattr(SysMgr.libcObj, "__errno_location"):
                fname = "__errno_location"
            elif hasattr(SysMgr.libcObj, "__errno"):
                fname = "__errno"
            else:
                SysMgr.geterrnoFunc = -1
                return "N/A"

            # define function #
            SysMgr.geterrnoFunc = getattr(SysMgr.libcObj, fname)
            SysMgr.geterrnoFunc.restype = POINTER(c_int)

        # check again #
        if SysMgr.geterrnoFunc == -1:
            return "N/A (no errno function)"

        # error code #
        try:
            num = SysMgr.geterrnoFunc()[0]
            if num == 0:
                return "N/A"
            elif not num in errno.errorcode:
                raise Exception("no errorno")
            code = errno.errorcode[num]
            return "%s (%s)" % (code, os.strerror(num))
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn("failed to get error reason", reason=True)
            return "N/A"

    @staticmethod
    def getProcTree():
        procTree = {}

        # get process list in procfs #
        pids = SysMgr.getPidList()
        for pid in pids:
            try:
                long(pid)
            except SystemExit:
                sys.exit(0)
            except:
                continue

            # make path of tid #
            procPath = "%s/%s" % (SysMgr.procPath, pid)
            taskPath = "%s/%s" % (procPath, "task")

            # update comm of main thread #
            comm = SysMgr.getComm(pid, save=True)

            try:
                tids = os.listdir(taskPath)
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printOpenWarn(taskPath)
                continue

            for tid in tids:
                try:
                    # main task #
                    if tid == pid:
                        procTree[tid] = "%s(%s)" % (pid, comm)
                    # sibling task #
                    else:
                        # update comm of thread #
                        SysMgr.getComm(tid, save=True)

                        procTree[tid] = pid
                except SystemExit:
                    sys.exit(0)
                except:
                    continue

        return procTree

    @staticmethod
    def setSortValue(values):
        # check skip mode #
        if SysMgr.checkMode("printslab") or SysMgr.checkMode("checkdup"):
            SysMgr.sort = values
            return True

        if not values:
            value = cond = None
        else:
            values = values.split(":")
            if len(values) == 1:
                value = values[0]
                cond = None
            elif len(values) > 1:
                value, cond = values[:2]

        if value == "c":
            SysMgr.printInfo("sorted by CPU")
        elif value == "N":
            SysMgr.printInfo("sorted by NAME")
        elif value == "m":
            SysMgr.printInfo("sorted by MEMORY")
        elif value == "b":
            SysMgr.printInfo("sorted by BLOCK")
            if SysMgr.checkDiskTopCond():
                SysMgr.blockEnable = True
            else:
                sys.exit(-1)
        elif value == "w":
            SysMgr.printInfo("sorted by CHILD")
            SysMgr.wfcEnable = True
        elif value == "p":
            SysMgr.printInfo("sorted by PID")
        elif value == "n":
            SysMgr.printInfo("sorted by NEW")
        elif value == "e":
            SysMgr.printInfo("sorted by EXECTIME")
            SysMgr.schedEnable = True
        elif value == "d":
            SysMgr.printInfo("sorted by DELAY")
            SysMgr.schedEnable = True
        elif value == "D":
            SysMgr.printInfo("sorted by D-Bus")
        elif value == "r":
            SysMgr.printInfo("sorted by RUNTIME")
        elif value == "o":
            SysMgr.printInfo("sorted by OOMSCORE")
            TaskAnalyzer.setLastField("oom")
        elif value == "P":
            SysMgr.printInfo("sorted by PRIORITY")
        elif value == "f":
            SysMgr.printInfo("sorted by FILE")
            SysMgr.fileTopEnable = True
        elif value == "s":
            SysMgr.printInfo("sorted by SWAP")
        elif value == "C":
            if not SysMgr.checkMode("ttop"):
                SysMgr.printErr(
                    "failed to sort by CONTEXTSWITCH because "
                    "it is supported on thread mode"
                )
                sys.exit(-1)
            SysMgr.printInfo("sorted by CONTEXTSWITCH")
        elif not value:
            value = None
        else:
            SysMgr.printErr("wrong option value '%s' for sort" % value)
            return False

        # set values #
        SysMgr.sort = value
        if cond and len(cond) > 1:
            if cond[0] == ">":
                SysMgr.sortCondOp = ">"
                cond = cond[1:]
            elif cond[0] == "<":
                SysMgr.sortCondOp = "<"
                cond = cond[1:]
        SysMgr.sortCond = cond

        return True

    @staticmethod
    def readAppArmorInfo():
        return SysMgr.readFile(
            "/sys/kernel/security/apparmor/profiles", verb=False
        )

    @staticmethod
    def readPageInfo():
        try:
            pageBlockOrder = 0
            pagePerBlock = 0
            freeTitle = False
            blockTitle = False
            freeInfo = {}
            blockInfo = {}

            # read page info #
            lines = SysMgr.procReadlines("pagetypeinfo")

            for item in lines:
                if item == "\n":
                    continue
                elif not pageBlockOrder and item.startswith(
                    "Page block order:"
                ):
                    pageBlockOrder = long(item.split(":")[1])
                    continue
                elif not pagePerBlock and item.startswith("Pages per block:"):
                    pagePerBlock = long(item.split(":")[1])
                    continue
                elif not freeTitle and item.startswith("Free pages"):
                    freeTitle = True
                    continue
                elif not blockTitle and item.startswith("Number of blocks"):
                    blockTitle = UtilMgr.lstrip(
                        item, "Number of blocks "
                    ).split()[1:]
                    continue
                elif blockTitle:
                    item = item.rstrip().split(",")[:2]

                    node = item[0].split()[1]
                    values = item[1].split()
                    zone = values[1]
                    blocks = list(
                        map(lambda f: long(f.lstrip(">")), values[2:])
                    )

                    blockInfo.setdefault(node, {})
                    blockInfo[node].setdefault(zone, {})

                    for ztype, block in zip(blockTitle, blocks):
                        blockInfo[node][zone][ztype] = block
                else:
                    item = item.rstrip().split(",")[:3]

                    node = item[0].split()[1]
                    zone = item[1].split()[1]
                    values = item[2].split()
                    ztype = values[1]
                    pages = list(
                        map(lambda f: long(f.lstrip(">")), values[2:])
                    )

                    freeInfo.setdefault(node, {})
                    freeInfo[node].setdefault(zone, {})
                    freeInfo[node][zone][ztype] = pages
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn("failed to read page info", reason=True)

        return {
            "pageBlockOrder": pageBlockOrder,
            "pagePerBlock": pagePerBlock,
            "freeInfo": freeInfo,
            "blockInfo": blockInfo,
        }

    @staticmethod
    def readLockInfo():
        try:
            res = {}
            lines = SysMgr.procReadlines("locks")

            for item in lines:
                # parse items #
                num, items = item.split(":", 1)
                reent = "*" if items.strip().startswith("-> ") else ""
                if reent:
                    items = items[3:]
                lclass, attr, op, pid, lfile, start, end = items.split()[:7]

                res.setdefault(pid, [])
                res[pid].append(
                    {
                        "class": lclass,
                        "attr": attr,
                        "op": op,
                        "file": lfile,
                        "start": start,
                        "end": end,
                        "reent": reent,
                    }
                )
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn("failed to read lock info", reason=True)

        return res

    @staticmethod
    def readBuddyInfo():
        try:
            res = {}
            lines = SysMgr.procReadlines("buddyinfo")

            for item in lines:
                # parse items #
                node, items = item.split(",", 1)
                node = node.split()[1]
                items = UtilMgr.lstrip(items.strip(), "zone").strip().split()
                name = items.pop(0)
                orders = list(map(long, items))

                # add orders #
                res.setdefault(node, {})
                res[node][name] = orders
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn("failed to read buddy info", reason=True)

        return res

    @staticmethod
    def readMemoryMap():
        try:
            res = {}
            lines = SysMgr.procReadlines("iomem")
            for idx, item in enumerate(lines):
                addrs, name = item.split(":", 1)
                start, end = UtilMgr.cleanItem(
                    addrs.rstrip().split("-"), False
                )
                startAddr = long(start, 16)
                size = long(end, 16) - startAddr + 1
                sizeUnit = "%s (%7s)" % (
                    UtilMgr.convNum(size),
                    UtilMgr.convSize2Unit(size),
                )

                # non-root user can't read addresses #
                if startAddr in res:
                    startAddr = idx

                res[startAddr] = {
                    "name": name.strip(),
                    "start": start,
                    "end": end,
                    "size": size,
                    "sizeUnit": sizeUnit,
                }
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn("failed to read memory map", reason=True)

        return res

    @staticmethod
    def readDmabufStats():
        if not "DMABUF" in SysMgr.environList:
            return {}

        dpath = "/sys/kernel/dmabuf/buffers"
        if not os.path.exists(dpath):
            return {}

        try:
            stats = {}

            for inode in os.listdir(dpath):
                stats[inode] = {}
                mpath = os.path.join(dpath, inode)
                for f in os.listdir(mpath):
                    fpath = os.path.join(mpath, f)
                    stats[inode][f] = SysMgr.readFile(fpath, verb=False)
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to get dmabuf stats", True)

        return stats

    @staticmethod
    def readPsiStats():
        dpath = "/proc/pressure"
        if not os.path.exists(dpath):
            return {}

        try:
            stats = {}

            for item in os.listdir(dpath):
                # check type #
                fpath = os.path.join(dpath, item)
                if os.path.isdir(fpath):
                    continue

                # read stats #
                values = SysMgr.readFile(fpath)
                if not values:
                    continue

                # init resource struture #
                stats.setdefault(item, {})

                for line in values.split("\n"):
                    if not line.strip():
                        continue

                    # parse items #
                    target, avg10, avg60, avg300, total = line.split()[:5]

                    """
                    some: delay for at least one task
                    full: delay for all tasks simultaneously
                    ----------------------------------------
                    avg: average time for seconds in %
                    total: total time for seconds in us
                    """

                    # avg in %, total in us #
                    stats[item][target] = {
                        "avg10": float(avg10.split("=")[1]),
                        "avg60": float(avg60.split("=")[1]),
                        "avg300": float(avg300.split("=")[1]),
                        "total": float(total.split("=")[1]),
                    }
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to get PSI stats", True)

        return stats

    @staticmethod
    def readSchedFeatures():
        try:
            path = "/sys/kernel/debug/sched_features"
            fd = open(path, "r")
            return sorted(fd.readline().split())
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printOpenWarn(path)
            return []

    @staticmethod
    def readProcData(tid, path, num=-1, verb=True):
        path = "%s/%s/%s" % (SysMgr.procPath, tid, path)

        try:
            f = open(path, "r")

            if num == -1:
                return f.readlines()

            val = f.readline().replace("\n", "")
            if num == 0:
                return val

            return val.split()[num - 1]
        except SystemExit:
            sys.exit(0)
        except:
            if verb:
                SysMgr.printOpenErr(path)
            return None

    @staticmethod
    def hasMainArg(dash=False):
        if len(sys.argv) <= 2 or (not dash and sys.argv[2].startswith("-")):
            return False
        else:
            return True

    @staticmethod
    def getMainArgs(union=True, token=",", path=False):
        args = SysMgr.getMainArg(path)
        if not isinstance(args, list):
            args = args.split(token)
        return UtilMgr.cleanItem(args, union)

    @staticmethod
    def getMainArg(path=False):
        if len(sys.argv) <= 2:
            return None
        elif not path:
            return sys.argv[2]
        else:
            return UtilMgr.convPath(sys.argv[2], retStr=True, separator=",")

    @staticmethod
    def checkEnv():
        def _checkMode():
            # check args #
            if len(sys.argv) == 1:
                return True

            # get command #
            cmd = sys.argv[1]

            # common commands for all OS #
            if cmd in (
                "addr2sym",
                "bgtop",
                "cli",
                "comp",
                "convert",
                "cputest",
                "ctop",
                "decomp",
                "dirdiff",
                "disktop",
                "drawreq",
                "exec",
                "fserver",
                "hserver",
                "iotest",
                "less",
                "list",
                "merge",
                "mkcache",
                "ntop",
                "ping",
                "print",
                "printdir",
                "printdlt",
                "printext",
                "pstree",
                "readelf",
                "report",
                "req",
                "rtop",
                "send",
                "split",
                "strings",
                "sym2addr",
                "tail",
                "top",
                "topdiff",
                "topsum",
                "trtop",
            ):
                return True
            # only commands for MacOS #
            elif SysMgr.isDarwin and cmd in (
                "dlttop",
                "ftop",
                "kill",
                "printdlt",
                "server",
            ):
                return True
            # specific commands for all OS #
            elif SysMgr.isDrawMode() or SysMgr.isHelpMode():
                return True

            return False

        # save platform name #
        SysMgr.platform = sys.platform

        # Linux #
        if SysMgr.platform.startswith("linux"):
            SysMgr.isLinux = True

            # environment variables #
            try:
                # set default terminal to xterm #
                os.environ["TERM"] = "xterm"

                # set run type #
                if "REMOTERUN" in os.environ:
                    SysMgr.encodeEnable = False
                    SysMgr.remoteRun = True
                    SysMgr.colorEnable = False
                else:
                    SysMgr.colorEnable = True

                # check encode condition #
                if not "LANG" in os.environ or "tty" in os.ttyname(
                    sys.stdout.fileno()
                ):
                    SysMgr.encodeEnable = False
            except SystemExit:
                sys.exit(0)
            except:
                pass

            # set force color flag #
            SysMgr.forceColorEnable = SysMgr.isTraceMode()

            # android #
            if "ANDROID_ROOT" in os.environ and not "NO_ANDROID" in os.environ:
                SysMgr.isAndroid = True
                SysMgr.libcPath = "libc"
                SysMgr.libcppPath = "libstdc++"
                if os.path.exists("/system/lib/libgccdemangle.so"):
                    SysMgr.libdemanglePath = "libgccdemangle"
                else:
                    SysMgr.libdemanglePath = "libbacktrace"
                SysMgr.cacheDirPath = "/data/log/guider"

        # Windows #
        elif SysMgr.platform.startswith("win"):
            SysMgr.isLinux = False
            SysMgr.isWindows = True

            if len(sys.argv) == 1:
                pass
            elif not _checkMode():
                SysMgr.colorEnable = False
                if sys.argv[1] in SysMgr.getCmdSet():
                    SysMgr.printErr(
                        "'%s' is not supported on '%s' platform"
                        % (sys.argv[1], SysMgr.platform)
                    )
                else:
                    SysMgr.printErr("no command '%s'" % sys.argv[1])
                sys.exit(-1)

            # set default SIGINT handler #
            signal.signal(signal.SIGINT, SysMgr.stopHandler)

            # set color flag #
            SysMgr.colorEnable = False

        # MacOS #
        elif SysMgr.platform.startswith("darwin"):
            SysMgr.isLinux = False
            SysMgr.isDarwin = True
            ConfigMgr.SIG_LIST = ConfigMgr.SIG_LIST_MACOS

            if len(sys.argv) == 1:
                pass
            elif not _checkMode():
                if sys.argv[1] in SysMgr.getCmdSet():
                    SysMgr.printErr(
                        "'%s' is not supported on '%s' platform"
                        % (sys.argv[1], SysMgr.platform)
                    )
                else:
                    SysMgr.printErr("no command '%s'" % sys.argv[1])
                sys.exit(-1)

            # set color flag #
            SysMgr.colorEnable = True
        else:
            SysMgr.printErr("'%s' platform is not supported" % SysMgr.platform)
            sys.exit(-1)

        # check locale #
        try:
            if not SysMgr.encodeEnable:
                pass
            elif "NO_ENCODE" in os.environ:
                SysMgr.encodeEnable = False
            else:
                lang = os.getenv("LANG")
                if not lang or not "UTF" in lang:
                    SysMgr.encodeEnable = False
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # check python #
        if sys.version_info < (2, 6):
            SysMgr.printWarn(
                "some features may not work on python %d.%d"
                % (sys.version_info[0], sys.version_info[1]),
                True,
            )

    @staticmethod
    def onAlarmExit(signum, frame):
        SysMgr.updateTimer()

        sys.exit(0)

    @staticmethod
    def onAlarm(signum, frame):
        SysMgr.progressCnt += 1
        if SysMgr.repeatCnt <= SysMgr.progressCnt:
            SysMgr.printWarn("terminated by timer\n", True)
            sys.exit(0)

        SysMgr.updateTimer()

    @staticmethod
    def checkProgress():
        if not SysMgr.countEnable:
            return

        # check uptime deadline #
        exitCond = SysMgr.progressCnt > 0 and (
            0 < SysMgr.deadlineUptime <= SysMgr.uptime
            or 0 < SysMgr.exitCondCpuMore <= SysMgr.cpuUsage
            or 0 < SysMgr.exitCondCpuLess >= SysMgr.cpuUsage
            or 0 < SysMgr.exitCondMemMore <= SysMgr.memAvail
            or 0 < SysMgr.exitCondMemLess >= SysMgr.memAvail
        )

        # check reason #
        if exitCond:
            reason = "condition"
        elif SysMgr.progressCnt >= SysMgr.repeatCnt > 0:
            reason = "timer"
        else:
            reason = None

        if reason:
            # remove progress #
            UtilMgr.deleteProgress()

            # send signal to myself #
            try:
                SysMgr.printWarn("terminated by %s\n" % reason, True)
                os.kill(SysMgr.pid, signal.SIGINT)
                os.kill(SysMgr.pid, signal.SIGKILL)
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printSigError(SysMgr.pid, "SIGINT")

        if SysMgr.repeatCnt > 1:
            UtilMgr.printProgress(SysMgr.progressCnt, SysMgr.repeatCnt)

        SysMgr.progressCnt += 1

    @staticmethod
    def getCmdSet():
        cmdSet = []
        for types in SysMgr.getCmdList().values():
            cmdSet += types.keys()
        return set(cmdSet)

    @staticmethod
    def getCmdString():
        cmdList = SysMgr.getCmdList()

        cmdbuf = ""
        for ctype, tvalue in sorted(cmdList.items()):
            prefix = ctype
            for cmd, cvalue in sorted(tvalue.items()):
                if prefix:
                    types = "[%s]" % prefix.upper()
                else:
                    types = " "

                cmdbuf = "%s%4s%-12s%4s%-14s%4s%-15s (%-s)\n" % (
                    cmdbuf,
                    " ",
                    types,
                    " ",
                    cmd,
                    " ",
                    "<%s>" % cvalue[0],
                    cvalue[1],
                )
                prefix = ""
            cmdbuf = "%s\n" % cmdbuf

        return cmdbuf[:-1]

    @staticmethod
    def getCmdList():
        return {
            "monitor": {
                "atop": ("All", "Linux"),
                "bgtop": ("Background", "Linux/MacOS/Windows"),
                "btop": ("Function", "Linux"),
                "cgtop": ("Cgroup", "Linux"),
                "ctop": ("Threshold", "Linux/MacOS/Windows"),
                "dbustop": ("D-Bus", "Linux"),
                "disktop": ("Storage", "Linux/MacOS/Windows"),
                "dlttop": ("DLT", "Linux/MacOS"),
                "fetop": ("File", "Linux"),
                "ftop": ("File", "Linux/MacOS"),
                "irqtop": ("IRQ", "Linux"),
                "kftop": ("Function", "Linux"),
                "ktop": ("Function", "Linux"),
                "mtop": ("Memory", "Linux"),
                "ntop": ("Network", "Linux/MacOS/Windows"),
                "ptop": ("PMU", "Linux"),
                "pytop": ("Python", "Linux"),
                "rtop": ("JSON", "Linux/MacOS/Windows"),
                "slabtop": ("Slab", "Linux"),
                "stacktop": ("Stack", "Linux"),
                "systop": ("Syscall", "Linux"),
                "top": ("Process", "Linux/MacOS/Windows"),
                "trtop": ("Tree", "Linux"),
                "ttop": ("Thread", "Linux"),
                "utop": ("Function", "Linux"),
                "vtop": ("Memory", "Linux"),
                "wtop": ("WSS", "Linux"),
            },
            "trace": {
                "btrace": ("Function", "Linux"),
                "leaktrace": ("Leak", "Linux"),
                "mtrace": ("Memory", "Linux"),
                "pytrace": ("Python", "Linux"),
                "sigtrace": ("Signal", "Linux"),
                "strace": ("Syscall", "Linux"),
                "utrace": ("Function", "Linux"),
            },
            "profile": {
                "filerec": ("File", "Linux"),
                "funcrec": ("Function", "Linux"),
                "genrec": ("System", "Linux"),
                "iorec": ("I/O", "Linux"),
                "mem": ("Page", "Linux"),
                "rec": ("Thread", "Linux"),
                "report": ("Report", "Linux"),
                "sysrec": ("Syscall", "Linux"),
            },
            "visual": {
                "convert": ("Text", "Linux/MacOS/Windows"),
                "draw": ("System", "Linux/MacOS/Windows"),
                "drawavg": ("Average", "Linux/MacOS/Windows"),
                "drawbitmap": ("Bitmap", "Linux/MacOS/Windows"),
                "drawcpu": ("CPU", "Linux/MacOS/Windows"),
                "drawcpuavg": ("CPU", "Linux/MacOS/Windows"),
                "drawdelay": ("Delay", "Linux/MacOS/Windows"),
                "drawflame": ("Function", "Linux/MacOS/Windows"),
                "drawhist": ("Histogram", "Linux/MacOS/Windows"),
                "drawio": ("I/O", "Linux/MacOS/Windows"),
                "drawleak": ("Leak", "Linux/MacOS/Windows"),
                "drawmem": ("Memory", "Linux/MacOS/Windows"),
                "drawmemavg": ("Memory", "Linux/MacOS/Windows"),
                "drawpri": ("Prio", "Linux/MacOS/Windows"),
                "drawreq": ("URL", "Linux/MacOS/Windows"),
                "drawrss": ("RSS", "Linux/MacOS/Windows"),
                "drawrssavg": ("RSS", "Linux/MacOS/Windows"),
                "drawstack": ("System", "Linux/MacOS/Windows"),
                "drawtime": ("Timeline", "Linux/MacOS/Windows"),
                "drawviolin": ("Data", "Linux/MacOS/Windows"),
                "drawvss": ("VSS", "Linux/MacOS/Windows"),
                "drawvssavg": ("VSS", "Linux/MacOS/Windows"),
            },
            "control": {
                "cgroup": ("Cgroup", "Linux"),
                "freeze": ("Thread", "Linux"),
                "hook": ("Function", "Linux"),
                "kill/tkill": ("Signal", "Linux/MacOS"),
                "limitcpu": ("CPU", "Linux"),
                "limitcpuset": ("CPU", "Linux"),
                "limitmem": ("Memory", "Linux"),
                "limitpid": ("Task", "Linux"),
                "limitread": ("I/O", "Linux"),
                "limitwrite": ("I/O", "Linux"),
                "pause": ("Thread", "Linux"),
                "remote": ("Command", "Linux"),
                "rlimit": ("Resource", "Linux"),
                "setafnt": ("Affinity", "Linux"),
                "setcpu": ("Clock", "Linux"),
                "setsched": ("Priority", "Linux"),
            },
            "util": {
                "addr2sym": ("Symbol", "Linux/MacOS/Windows"),
                "checkdup": ("Page", "Linux"),
                "comp": ("Compress", "Linux/MacOS/Windows"),
                "decomp": ("Decompress", "Linux/MacOS/Windows"),
                "dirdiff": ("Dir", "Linux/MacOS/Windows"),
                "dump": ("Memory", "Linux"),
                "exec": ("Command", "Linux/MacOS/Windows"),
                "flush": ("Memory", "Linux"),
                "fadvise": ("File", "Linux"),
                "getafnt": ("Affinity", "Linux"),
                "getpid": ("PID", "Linux"),
                "less": ("Pager", "Linux/MacOS/Windows"),
                "merge": ("File", "Linux/MacOS/Windows"),
                "mkcache": ("Cache", "Linux/MacOS/Windows"),
                "mount": ("Mount", "Linux"),
                "ping": ("ICMP", "Linux/MacOS/Windows"),
                "print": ("File", "Linux/MacOS/Windows"),
                "printbind": ("Function", "Linux"),
                "printcg": ("Cgroup", "Linux"),
                "printdbus": ("D-Bus", "Linux"),
                "printdbusintro": ("D-Bus", "Linux"),
                "printdbusstat": ("D-Bus", "Linux"),
                "printdbussub": ("D-Bus", "Linux"),
                "printdir": ("Dir", "Linux/MacOS/Windows"),
                "printenv": ("Env", "Linux"),
                "printext": ("Ext4", "Linux/MacOS/Windows"),
                "printinfo": ("System", "Linux"),
                "printkconf": ("kernel", "Linux"),
                "printns": ("Namespace", "Linux"),
                "printsdfile": ("Systemd", "Linux"),
                "printsdinfo": ("Systemd", "Linux"),
                "printsdunit": ("Systemd", "Linux"),
                "printsig": ("Signal", "Linux"),
                "printslab": ("Slab", "Linux"),
                "printvma": ("Vmalloc", "Linux"),
                "pstree": ("Process", "Linux/MacOS/Windows"),
                "readahead": ("File", "Linux"),
                "readelf": ("File", "Linux/MacOS/Windows"),
                "req": ("URL", "Linux/MacOS/Windows"),
                "split": ("File", "Linux/MacOS/Windows"),
                "strings": ("Text", "Linux/MacOS/Windows"),
                "sym2addr": ("Address", "Linux/MacOS/Windows"),
                "sync": ("File", "Linux"),
                "sysrq": ("sysrq", "Linux"),
                "systat": ("Status", "Linux"),
                "topdiff": ("Diff", "Linux/MacOS/Windows"),
                "topsum": ("Summary", "Linux/MacOS/Windows"),
                "umount": ("Unmount", "Linux"),
                "watch": ("File", "Linux"),
            },
            "log": {
                "logand": ("Android", "Linux"),
                "logdlt": ("DLT", "Linux"),
                "logjrl": ("Journal", "Linux"),
                "logkmsg": ("Kernel", "Linux"),
                "logsys": ("Syslog", "Linux"),
                "logtrace": ("Ftrace", "Linux"),
                "printdlt": ("DLT", "Linux/MacOS/Windows"),
                "printjrl": ("Journal", "Linux"),
                "printkmsg": ("Kernel", "Linux"),
                "printand": ("Android", "Linux"),
                "printsyslog": ("Syslog", "Linux"),
                "printtrace": ("Ftrace", "Linux"),
            },
            "network": {
                "cli": ("Client", "Linux/MacOS/Windows"),
                "event": ("Event", "Linux"),
                "fserver": ("File", "Linux/MacOS/Windows"),
                "hserver": ("Http", "Linux/MacOS/Windows"),
                "list": ("List", "Linux/MacOS/Windows"),
                "send": ("UDP", "Linux/MacOS/Windows"),
                "server": ("Server", "Linux/MacOS"),
                "start": ("Signal", "Linux"),
            },
            "test": {
                "cputest": ("CPU", "Linux/MacOS/Windows"),
                "iotest": ("Storage", "Linux/MacOS/Windows"),
                "memtest": ("Memory", "Linux/MacOS/Windows"),
                "nettest": ("Network", "Linux"),
            },
        }

    @staticmethod
    def printHelp(force=False, isExit=True):
        # help #
        if force or len(sys.argv) <= 1 or SysMgr.isHelpMode():
            # parse environment vars #
            SysMgr.parseEnvironVars()

            # get environment variable from launcher #
            if "CMDLINE" in os.environ:
                cmd = os.environ["CMDLINE"]
            else:
                cmd = sys.argv[0]

            # remove pyc file extension #
            if ".pyc" in cmd:
                cmd = cmd[: cmd.find(".pyc")]

            # disable extended ascii code support #
            SysMgr.encodeEnable = False

            defStr = """
Usage:
    $ {0:1} COMMAND [OPTIONS] [--help]
                """.format(
                cmd
            )

            # command help #
            if force or (len(sys.argv) > 1 and SysMgr.isHelpMode()):
                # get command #
                mode = sys.argv[1]

                topCommonStr = """
Options:
    -o  <DIR|FILE>              set output path
    -u                          run in the background
    -W  <SEC>                   wait for input
    -f                          force execution
    -b  <SIZE:KB>               set buffer size
    -T  <PROC>                  set process number
    -j  <DIR|FILE>              set report path
    -w  <TIME:FILE{:VALUE}>     set additional command
    -x  <IP:PORT>               set local address
    -X  <REQ@IP:PORT>           set request address
    -N  <REQ@IP:PORT>           set report addresses
    -S  <CHARACTER{:VALUE}>     sort by key
          [ c:cpu / m:mem / p:pid / N:name / b:block / w:wfc
            n:new / f:file / r:runtime / s:swap / e:exectime
            P:prio / C:contextswitch / o:oomscore ]
    -P                          group threads in a same process
    -I  <CMD|FILE>              set input command or file
    -m  <ROWS:COLS:SYSTEM>      set terminal size
    -a                          show all stats and events
    -g  <COMM|TID{:FILE}>       set task filter
    -i  <SEC>                   set interval
    -R  <INTERVAL:TIME:TERM>    set repeat count
    -C  <PATH>                  set config file
    -c  <CMD>                   set hot command
    -Q                          print all rows in a stream
    -q  <NAME{:VALUE}>          set environment variables
    -J                          print in JSON format
    -L  <PATH>                  set log file
    -l  <TYPE>                  set log type
          [ a:Android / d:DLT / k:KMSG / j:JOURNAL / s:SYSLOG ]
    -E  <DIR>                   set cache dir
    -H  <LEVEL>                 set function depth level
    -G  <KEYWORD>               set ignore list
    -k  <COMM|TID:SIG{:CONT}>   set signal
    -z  <COMM|TID:MASK{:CONT}>  set CPU affinity
    -Y  <VALUES>                set sched
          [ POLICY:PRIO|TIME{:TID|COMM:CONT}
            CLASS:{WHICH:}PRIO:TID|COMM ]
    -v                          verbose
                """

                topSubStr = """
Options:
    -e  <CHARACTER>             enable options
          [ a:affinity | A:secAttr | b:block | B:bar
            c:cpu | C:compress | d:disk | D:DWARF
            e:encode | E:exec | f:float | F:wfc | G:cgroup
            h:sigHandler | H:sched | i:irq | I:elastic
            k:peak | K:cgroupRoot | l:threshold | L:cmdline
            m:mem | M:min | n:net | N:namespace
            o:oomScore | O:iosched | p:pipe | P:perf
            q:quit | r:report | R:reportFile | s:stack | S:pss
            t:thread | T:total | u:uss | U:unit
            w:wss | W:wchan | x:fixTarget | X:exe | Y:delay ]
    -d  <CHARACTER>             disable options
          [ a:memAvailable | A:Average | b:buffer | B:bar
            c:cpu | C:clone | d:demangle | D:DWARF
            e:encode | E:exec | g:general | G:gpu
            L:log | m:gpuMem | O:color | p:print | P:PSI
            t:truncate | T:task | x:event ]
                """

                jitProfStr = """\
        * Qt:
            - Run the target with "QV4_PROFILE_WRITE_PERF_MAP=1" environment variable (version >= 5.6.0)
        * Node:
            - Run the target with "--perf-basic-prof" option (version >= 0.11.13, map file growth endlessly)
            - Run the target with "--perf-basic-prof-only-functions" option (version >= 4.4.0)
        * Java:
            1. Install perf-map-agent
            2. Run the target with "-XX:+PreserveFramePointer -XX:+UnlockDiagnosticVMOptions -XX:+DebugNonSafepoints" option
            3. Set environment with "export JAVA_HOME=$(readlink -f /usr/bin/java | sed "s:bin/java::")" command
            4. Create a map file with "perf-map-agent/bin/create-java-perf-map.sh PID" command
        * Mono:
            - Run the target with "--jitmap" option
        * .NET Core:
            - Run the target with "COMPlus_PerfMapEnabled=1" environment variable
                """

                drawSubStr = """
Options:
    -e  <CHARACTER>             enable options
          [ d:disk | n:network ]
    -d  <CHARACTER>             disable options
          [ A:average | G:gpu | S:pss | u:uss ]
    -g  <COMM|TID{:FILE}>       set task filter
    -o  <DIR>                   set output path
    -a                          show all stats and events
    -T  <NUM>                   set top number
    -L  <RES:PER>               set graph layout (TOTAL PER: 6)
          [ C:CPU | D:Delay | M:Memory | I:IO ]
    -l  <BOUNDARY>              set boundary lines
    -F  [svg/png/pdf/ps/eps]    set image format
    -E  <DIR>                   set cache dir path
    -C  <PATH>                  set config file
    -O  <CORE>                  set core filter
    -q  <NAME{:VALUE}>          set environment variables
    -v                          verbose

Segments:
    Long Color   : Run
    Upper Black  : Wait
    Upper Red    : Preempted
    Lower Purple : Read
    Lower Cyan   : Write
    Horizon Gray : Off

Troubleshooting:
    - "No module named pkg_resources" when installing svgwrite package using pip
        # curl https://bootstrap.pypa.io/ez_setup.py | python3
                """

                if SysMgr.checkMode("ttop"):
                    target = "threads"
                else:
                    target = "processes"

                topExamStr = """
Examples:
    - {3:1} {2:1} used CPU resource more than 1% every interval
        # {0:1} {1:1}

    - {3:1} specific {2:1} having TID 1234 or COMM 1234
        # {0:1} {1:1} -g 1234

    - {3:1} {2:1} newly executed
        # {0:1} {1:1} -I ./a.out

    - {3:1} specific {2:1} having COMM starting with kworker
        # {0:1} {1:1} -g "kworker*"
        # {0:1} {1:1} -g "*kworker"

    - {3:1} {2:1} except the one having COMM test
        # {0:1} {1:1} -g "^test"

    - {3:1} specific {2:1} using CPU more than 1%
        # {0:1} {1:1} -S c:1

    - {3:1} {2:1} used system resource totally
        # {0:1} {1:1} -e T

    - {3:1} {2:1} with the number of system cores, divided by the CPU usage
        # {0:1} {1:1} -q SYSPER

    - {3:1} {2:1} after applying maximum length of error logs
        # {0:1} {1:1} -q MAXERRLEN

    - {3:1} all {2:1} on linux, not android
        # NO_ANDROID=1 {0:1} {1:1} -a

    - {3:1} maximum top 20 {2:1}
        # {0:1} {1:1} -a -q NRTOPRANK:20

    - {3:1} {2:1} with cgroup usage
        # {0:1} {1:1} -q PRINTCG
        # {0:1} {1:1} -q PRINTCG:"cpu+cpuacct+memory+blkio"
        # {0:1} {1:1} -q PRINTCG:"all"
        # {0:1} {1:1} -q PRINTCG, MAXCGROWS:5
        # {0:1} {1:1} -q PRINTCG, NOPRESSURE, SKIPNOTASK

    - {3:1} {2:1} with cgroup usage only for specific groups
        # {0:1} {1:1} -q PRINTCG, CGFILTER:"*user.slice" -a

    - {3:1} all {2:1} with specific cores
        # {0:1} {1:1} -e c -O 0:4, 10, 12

    - {3:1} {2:1} with cmdline
        # {0:1} {1:1} -e L
        # {0:1} {1:1} -e L -g "*apps*"
        # {0:1} {1:1} -e L -g "*apps*" -q CMDLINEFILTER:"*test*"

    - {3:1} {2:1} with exe path
        # {0:1} {1:1} -e X
        # {0:1} {1:1} -e X -g "*apps*"
        # {0:1} {1:1} -e X -g "*apps*" -q EXEFILTER:"*test*"

    - {3:1} {2:1} with binder stat
        # {0:1} {1:1} -q BINDERSTAT

    - {3:1} {2:1} with D-Bus unit
        # {0:1} {1:1} -e U

    - {3:1} {2:1} with namespace
        # {0:1} {1:1} -e N
        # {0:1} {1:1} -e N -q NSTYPE:net
        # {0:1} {1:1} -e N -q NSTYPE:mnt
        # {0:1} {1:1} -e N -q PRINTNS
        # {0:1} {1:1} -e N -a -q ETCINCFILTER:"bash*", ETCEXCFILTER:"(1)*"

    - {3:1} {2:1} using total CPU usage by applying the multiplication of the number of CPUs
        # {0:1} {1:1} -d A

    - {3:1} all {2:1} with bar graphs for stats of all cores and PSI
        # {0:1} {1:1} -a -e B

    - {3:1} all {2:1} with short bar graphs for all cores
        # {0:1} {1:1} -a -d B

    - {3:1} all {2:1} with per-core stats without bar graphs
        # {0:1} {1:1} -a -d B -q CORESTAT

    - {3:1} all {2:1} with minimal stats
        # {0:1} {1:1} -a -e M

    - {3:1} all {2:1} after user input
        # {0:1} {1:1} -a -W

    - {3:1} all {2:1} and system stats with KB unit
        # {0:1} {1:1} -a -q KBUNIT

    - {3:1} all {2:1} and execute specific commands when terminated
        # {0:1} {1:1} -a -q EXITCMD:"ls -lha"

    - {3:1} {2:1} with DLT time
        # {0:1} {1:1} -q DLTTIME

    - {3:1} all {2:1} and execute specific commands to save also their output when terminated
        # {0:1} {1:1} -a -o -q PRINTCMD:"DLT#GUIDER printdlt -a -q LASTFILE\, TAIL:-3\, UNTIL -d L -Q", DLTTIME
        # {0:1} {1:1} -a -o -q PRINTCMD:"KERNEL#GUIDER printkmsg -a -q TAIL:-3\, UNTIL -d L -Q"
        # {0:1} {1:1} -a -o -q PRINTCMD:"ls#ls -lha"

    - {3:1} all {2:1} and quit when specific {2:1} are terminated
        # {0:1} {1:1} -a -q EXITCONDTERM:"a.out"

    - {3:1} all {2:1} and quit when specific {2:1} are executed
        # {0:1} {1:1} -a -q EXITCONDNEW:"a.out"

    - {3:1} all {2:1} and quit when specific files are found
        # {0:1} {1:1} -a -q EXITCONDFILE:"/tmp/term"

    - {3:1} all {2:1} and quit when specific files are not found
        # {0:1} {1:1} -a -q EXITCONDNOFILE:"/tmp/term"

    - {3:1} all {2:1} and quit when specific functions return true
        # {0:1} {1:1} -a -q EXITCONDFUNC:"/tmp/check.py":"checkFunc":"123"

    - {3:1} all {2:1} after 5 seconds
        # {0:1} {1:1} -a -W 5s

    - {3:1} {2:1} that use CPU more than 1% except for system in a stream
        # {0:1} {1:1} -a -q TASKSTREAM -S c:1

    - {3:1} all {2:1} from 100 seconds of uptime
        # {0:1} {1:1} -a -q STARTCONDTIME:100 -W

    - {3:1} all {2:1} until 100 seconds of uptime
        # {0:1} {1:1} -a -q EXITCONDTIME:100 -R

    - {3:1} all {2:1} with specific condition
        # {0:1} {1:1} -a -q STARTCONDCPUMORE:10, STARTCONDMEMMORE:1000 -R
        # {0:1} {1:1} -a -q STARTCONDCPULESS:90, STARTCONDMEMLESS:90 -R
        # {0:1} {1:1} -a -q EXITCONDCPUMORE:10, EXITCONDMEMMORE:1000 -R
        # {0:1} {1:1} -a -q EXITCONDCPULESS:90, EXITCONDMEMLESS:90 -R

    - {3:1} all {2:1} with the number of actual file descriptors
        # {0:1} {1:1} -a -q ACTUALFD

    - {3:1} all {2:1} sorted by memory(RSS)
        # {0:1} {1:1} -S m
        # {0:1} {1:1} -S m:500
        # {0:1} {1:1} -S "m:<10" -q ORDERASC
        # {0:1} {1:1} -S m -q SORTINCSWAP

    - {3:1} all {2:1} with memory(RSS) adding swap
        # {0:1} {1:1} -a -q SWAPMEMSUM

    - {3:1} all {2:1} with GPU memory
        # {0:1} {1:1} -a -q GPUMEM
        # {0:1} {1:1} -a -q GPUMEM, GPUALLMEM
        # {0:1} {1:1} -a -q GPUMEMSUM

    - {3:1} all {2:1} with GPU temperature
        # {0:1} {1:1} -a -q GPUTEMP

    - {3:1} all {2:1} sorted by execution time
        # {0:1} {1:1} -S e
        # {0:1} {1:1} -S e:2h
        # {0:1} {1:1} -S "e:<2h" -q ORDERASC

    - {3:1} {2:1} with fastest initialization
        # {0:1} {1:1} -q FASTINIT

    - {3:1} {2:1} without size-unit converting
        # {0:1} {1:1} -q NOCONVSIZE2UNIT

    - {3:1} {2:1} without number formatting
        # {0:1} {1:1} -q NOCONVNUM

    - {3:1} threads context-switched more than 5000 after sorting by context switch
        # {0:1} {1:1} -S C:5000
        # {0:1} {1:1} -S C:5000 -q ORDERDESC

    - {3:1} all {2:1} with changing the CPU scheduling priority every second
        # {0:1} {1:1} -Y "c:-20::CONT" -a

    - {3:1} specific {2:1} and change the CPU scheduling priority for specific threads having specific COMM every second
        # {0:1} {1:1} -g a.out -Y "c:-20:a.out:CONT"

    - {3:1} {2:1} with I/O priority
        # {0:1} {1:1} -e O

    - {3:1} specific {2:1} with the fixed specific targets in the top place
        # {0:1} {1:1} -g a.out -q FIXTASK:"a.out"
        # {0:1} {1:1} -g a.out -q FIXTASK:"a.out|yes|systemd"

    - {3:1} the fixed target {2:1} only to save CPU resource for monitoring
        # {0:1} {1:1} -g a.out -e x

    - {3:1} {2:1} and {5:1} {4:1}
        # {0:1} {1:1} -o .
        # {0:1} {1:1} -o . -q NOBACKUP

    - {3:1} {2:1} and {5:1} {4:1} with memory details of top 10 processes
        # {0:1} {1:1} -o . -q NRPROCMEM:10

    - {3:1} {2:1} and {5:1} with duplicated file list {4:1}
        # {0:1} {1:1} -o . -q DUPOUTPATH:/data/guider, DUPOUTPATH:/log/perf

    - {3:1} {2:1} and {5:1} {4:1} without pid suffix in the file name
        # {0:1} {1:1} -o . -q NOPIDSUFFIX

    - {3:1} {2:1} with contents of specific files or directories every interval and {5:1} {4:1}
        # {0:1} {1:1} -q RECFILE:/tmp/setting1, RECFILE:/tmp/setting2 -o
        # {0:1} {1:1} -q RECFILE:/tmp/setting1, RECFILESIZE:1024 -o
        # {0:1} {1:1} -q RECFILE:/tmp/setting1, RECFILESIZE:-1024 -o

    - {3:1} {2:1} and {5:1} {4:1} with contents of specific files
        # {0:1} {1:1} -q DUMPFILE:/tmp/OUT, DUMPFILE:/tmp/OUT2#-20 -o
        # {0:1} {1:1} -q DUMPFILEB64:/tmp/OUT, DUMPFILEB64:/tmp/OUT2#-20 -o
        # {0:1} {1:1} -q DUMPFILE:/tmp/OUT, DUMPFILESIZE:1024 -o

    - {3:1} {2:1} only for specific user
        # {0:1} {1:1} -q USERFILTER:"root*"

    - {3:1} {2:1} with sepcific user name length
        # {0:1} {1:1} -q USERLEN:100

    - {3:1} {2:1} with cgroup root info
        # {0:1} {1:1} -e K
        # {0:1} {1:1} -e K -q CGINCFILTER:"/docker*"
        # {0:1} {1:1} -e K -q CGEXCFILTER:"/docker*"

    - {3:1} {2:1} including cgroup info
        # {0:1} {1:1} -e G
        # {0:1} {1:1} -e G -o -q NOCGINFO
        # {0:1} {1:1} -e G -q TASKCGFILTER:"cpu*"

    - {3:1} {2:1} and print standard or special logs to specific logging systems
        * a:android / d:DLT / k:KMSG / j:JOURNAL / s:SYSLOG
        # {0:1} {1:1} -l dkjs
        # {0:1} {1:1} -q STDLOG:dkjs
        # {0:1} {1:1} -q OPSLOG:dkjs

    - {3:1} {2:1} with the cpu limitation in % unit using cgroup
        # {0:1} {1:1} -q LIMITCPU:20
        # {0:1} {1:1} -q LIMITCPU:20@"*yes*|a.out"
        # {0:1} {1:1} -q LIMITCPU:cfs_quota_us:20000+cfs_period_us:100000@"*yes*|a.out"

    - {3:1} {2:1} with the cpu set limitation using cgroup
        # {0:1} {1:1} -q LIMITCPUSET:1
        # {0:1} {1:1} -q LIMITCPUSET:"1-2"@"*yes*|a.out"
        # {0:1} {1:1} -q LIMITCPUSET:"1-2&4"@"*yes*|a.out"

    - {3:1} {2:1} with the memory limitation using cgroup
        # {0:1} {1:1} -q LIMITMEM:50M
        # {0:1} {1:1} -q LIMITMEM:50M@"*yes*|a.out"

    - {3:1} {2:1} with the block I/O limitation using cgroup
        # {0:1} {1:1} -q LIMITREAD:50M, LIMITWRITE:50M
        # {0:1} {1:1} -q LIMITREAD:50M@"*yes*|a.out"
        # {0:1} {1:1} -q LIMITWRITE:50M@"*yes*|a.out"
        # {0:1} {1:1} -q LIMITWRITE:50M@"*yes*|a.out", EACHTASK

    - {3:1} {2:1} and {5:1} using UTC time {4:1}
        # {0:1} {1:1} -o . -q UTCTIME

    - {3:1} {2:1} and {5:1} with 100 line of kernel messages {4:1}
        # {0:1} {1:1} -o . -q NRKLOG:100

    - {3:1} {2:1} and {5:1} with memo {4:1}
        # {0:1} {1:1} -o . -q MEMO:"monitoring result for server peak time"

    - {3:1} {2:1} and {5:1} including memory map of specific processes
        # {0:1} {1:1} -o . -q PROCMAP:"*sys*"

    - {3:1} {2:1} and {5:1} except for specific info
        # {0:1} {1:1} -o . -q NOSYSINFO, NOOSINFO, NOCPUINFO, NOCPUCACHEINFO
        # {0:1} {1:1} -o . -q NOMEMINFO, NOSTORAGEINFO, NOBLOCKINFO
        # {0:1} {1:1} -o . -q NOSWAPINFO, NONETWORKINFO, NOGPUINFO, NOGPUMEMINFO
        # {0:1} {1:1} -o . -q NOIPCINFO, NOCGINFO, NOLIMITINFO, NOSCHEDINFO
        # {0:1} {1:1} -o . -q NOSCHEDFEATINFO, NOVMSTATINFO, NOBUDDYINFO
        # {0:1} {1:1} -o . -q NOHUGEPAGEINFO, NOKSMINFO, NOMMAPINFO
        # {0:1} {1:1} -o . -q NOMAPPARMORINFO, NOUSBINFO, NOLOCKINFO

    - {3:1} {2:1} and {5:1} with limited memory buffer 50MB {4:1}
        # {0:1} {1:1} -o . -b 50m

    - {3:1} {2:1} and {5:1} with limited memory buffer 50MB loss possible {4:1}
        # {0:1} {1:1} -o . -b 50m -d b

    - {3:1} {2:1} and {5:1} with specific log events
        # {0:1} {1:1} -o . -q "DLTEVENT:LASTFILE|DLT|"
        # {0:1} {1:1} -o . -q "DLTEVENT:test.dlt|DLT|"
        # {0:1} {1:1} -o . -q "DLTEVENT:test.dlt|TIMEOUT|timeout"
        # {0:1} {1:1} -o . -q "DLTEVENT:test.dlt|TIMEOUT|timeout", MSGALL
        # {0:1} {1:1} -o . -q "DLTEVENT:test.dlt|TIMEOUT|timeout", PRINTEVENT
        # {0:1} {1:1} -o . -q "KERNELEVENT:TIMEOUT|timeout"
        # {0:1} {1:1} -o . -q "TRACEEVENT:TIMEOUT|timeout"
        # {0:1} {1:1} -o . -q "JOURNALEVENT:TIMEOUT|timeout"
        # {0:1} {1:1} -o . -q "SYSLOGEVENT:TIMEOUT|timeout"

    - {3:1} {2:1} and {5:1} without event handling {4:1}
        # {0:1} {1:1} -o . -d x

    - {3:1} {2:1} and {5:1} in real-time until SIGINT arrives
        # {0:1} {1:1} -o . -e p

    - {3:1} {2:1} and save the result composed only of raw data to guider.out in real-time until SIGINT arrives
        # {0:1} {1:1} -o . -e p -q NOSUMMARY

    - {3:1} {2:1} and save the result except for the interval summary to guider.out {4:1}
        # {0:1} {1:1} -o . -q NOINTSUMMARY

    - {3:1} {2:1} only for 5 items
        # {0:1} {1:1} -R 5

    - {3:1} {2:1} {6:1} total 5 minutes to guider.out
        # {0:1} {1:1} -R 3s:5m -o .

    - {3:1} {2:1} {6:1} 5 times to guider.out
        # {0:1} {1:1} -R 3s:5 -o .

    - {3:1} {2:1} {6:1} 60 minutes to guider.out
        # {0:1} {1:1} -i 3 -R 60m -o .

    - {3:1} {2:1} {6:1} 5 minutes to guider.out continuously
        # {0:1} {1:1} -R 3s:5m: -o .

    - {3:1} {2:1} with memory(PSS)
        # {0:1} {1:1} -e S

    - {3:1} {2:1} with memory(USS)
        # {0:1} {1:1} -e u

    - {3:1} newly executed {2:1} with memory(USS)
        # {0:1} {1:1} -I ./a.out -e u

    - {3:1} newly executed {2:1} and keep the task after termination
        # {0:1} {1:1} -I ./a.out -q KEEPTASK, KEEPALIVE

    - {3:1} {2:1} with unique physical memory (RSS - Text - Shm)
        # {0:1} {1:1} -q EXCEPTSHM

    - {3:1} all {2:1} including block usage every 2 seconds
        # {0:1} {1:1} -e b -i 2 -a

    - {3:1} specific {2:1} with the name including system and their siblings
        # {0:1} {1:1} -g "*system*" -P

    - {3:1} specific {2:1} named gdbus among {2:1} with the name including system and their siblings
        # {0:1} {1:1} -g "*system*" -P -q FILTER:"gdbus"

    - {3:1} {2:1} and print stats if only system resource usage exceeds specific threshold
        # {0:1} {1:1} -q CPUCOND:10, MEMFREECOND:100, MEMAVLCOND:100
        # {0:1} {1:1} -q BLKRDCOND:1, BLKWRCOND:1

    - {3:1} {2:1} with the specific length for thread name
        # {0:1} {1:1} -q COMMLEN:16

    - {3:1} {2:1} with peak info
        # {0:1} {1:1} -e k

    - {3:1} {2:1} with peak info including custom peak stats
        # {0:1} {1:1} -e k -q CNTTASKNEW:"weston*", CNTTASKDIE:"systemd", CNTTASKABNORMAL:"dbus-daemon"

    - {3:1} {2:1} with peak info including custom interval peak stats
        # {0:1} {1:1} -e k -q PEAKTICKLIST:3, PEAKTICKLIST:10

    - {3:1} {2:1} with time condition for updating peak info
        # {0:1} {1:1} -q PEAKUPTIMECOND:60
        # {0:1} {1:1} -q PEAKRUNTIMECOND:10

    - {3:1} {2:1} on the minimum terminal
        # {0:1} {1:1} -m

    - {3:1} {2:1} on the optimized terminal
        # {0:1} {1:1} -m :

    - {3:1} {2:1} after optimizing system terminal
        # {0:1} {1:1} -m ::system

    - {3:1} {2:1} and report to elastic search
        # {0:1} {1:1} -e I

    - {3:1} {2:1} with elapsed times for each step
        # {0:1} {1:1} -q PRINTDELAY

    - {3:1} {2:1} and report the results to both guider.out and console {4:1}
        # {0:1} {1:1} -o . -Q

    - {3:1} {2:1} and print environment variables
        # {0:1} {1:1} -q PRINTENV

    - {3:1} {2:1} and report the results to guider.out after freeing up space in the target directories {4:1}
        # {0:1} {1:1} -o . -q LIMITDIR:./:100M, LIMITDIR:/home:1G

    - {3:1} {2:1} and report the results to guider.out after applying the maximum file count limit in the target directories {4:1}
        # {0:1} {1:1} -o . -q LIMITDIRCNT:./:100, LIMITDIRCNT:/home:2000

    - {3:1} {2:1} and execute special commands
        # {0:1} {1:1} -w AFTER:/tmp/touched:1, AFTER:ls

    - {3:1} {2:1} through the local server having 5555 port
        # {0:1} {1:1} -X 5555
        # {0:1} {1:1} -X PRINT@5555
        # {0:1} {1:1} -X REPORT@5555

    - {3:1} {2:1} and report to 192.168.0.5:5555 in real-time
        # {0:1} {1:1} -e r -N PRINT@192.168.0.5:5555, PRINT@192.168.0.6:5555
        # {0:1} {1:1} -e r -N REPORT@192.168.0.5:5555, REPORT@192.168.0.6:5555

    - Print monitoring data from remote servers
        # {0:1} {1:1} -x 5555 -X

    - Print monitoring data from remote servers as possible as long in a page
        # {0:1} {1:1} -x 5555 -X -a

    - Print all monitoring data from remote servers
        # {0:1} {1:1} -x 5555 -X -Q

    - Print monitoring data from remote servers with host name filter
        # {0:1} {1:1} -x 5555 -X -q HOSTFILTER:"*LINUX*"

    - Save all monitoring data from remote servers into a specific file
        # {0:1} {1:1} -x 5555 -X -o .

    - Print and relay monitoring data from remote servers
        # {0:1} {1:1} -x 5555 -X -N PRINT@192.168.0.5:5556

    - {3:1} {2:1} with sending signals to specific {2:1}
        # {0:1} {1:1} -k a.out:SIGKILL
        # {0:1} {1:1} -k a.out:SIGKILL:CONT

    - {3:1} {2:1} with changing CPU affinity for specific {2:1}
        # {0:1} {1:1} -z a.out:3
        # {0:1} {1:1} -z a.out:3:CONT

    - {3:1} {2:1} with the number in front of the name
        # {0:1} {1:1} -c index

    - {3:1} {2:1} after setting hot commands in advance
        # {0:1} {1:1} -c "GUIDER utop -g PID"
        # {0:1} {1:1} -c "GUIDER btrace -g PID *write*|getret\, __write_nocancel|getret"

    - {3:1} {2:1} and execute specific commands for all or a few tasks shown automatically
        # {0:1} {1:1} -c "GUIDER utop -g PID" -e E
        # {0:1} {1:1} -c "GUIDER utop -g PID" -e E -q NRTOPRANK:1
        # {0:1} {1:1} -c "GUIDER btrace -g PID *write*|getret\, __write_nocancel|getret" -e E

    - {3:1} {2:1} after setting config from guider.conf
        # {0:1} {1:1} -C guider.conf

    - {3:1} {2:1} with no encoding for output
        # {0:1} {1:1} -d e
        # NO_ENCODE=1 {0:1} {1:1} -d e

    - {3:1} system only
        # {0:1} {1:1} -d T
                """.format(
                    cmd,
                    mode,
                    target,
                    "Monitor the status of",
                    "when SIGINT arrives",
                    "report the result to guider.out",
                    "and report the result collected every 3 seconds for",
                )

                drawExamStr = """
Examples:
    - {2:1} for specific files
        # {0:1} {1:1} {4:1}
        # {0:1} {1:1} {4:1} guider1.out guider2.out
        # {0:1} {1:1} {4:1} "guider2.out" "guider data.out"
        # {0:1} {1:1} "data/*"
        # {0:1} {1:1} "data/*" -q TARGETFILE:"*test*", EXCEPTFILE:"*old"
        # {0:1} {1:1} {3:1}

    - {2:1} for specific files from current directory to all sub-directories
        # {0:1} {1:1} "**/*"

    - {2:1} for specific files with relative path
        # {0:1} {1:1} {4:1} -q RELPATH

    - {2:1} for specific files with fixed resolution or DPI
        # {0:1} {1:1} {4:1} -q DPI:400
        # {0:1} {1:1} {4:1} -q RESOLUTION:"16*8"

    - {2:1} that haven't been summarized yet for specific files
        # {0:1} {1:1} {4:1} -q TOPSUM
        # {0:1} {1:1} {4:1} -q TOPSUM, INCKERNELMEM
        # {0:1} {1:1} {4:1} -q TOPSUM, INCKERNELMEM, DRAWRSS

    - {2:1} for each file
        # {0:1} {1:1} {4:1} -q NOMERGE
        # {0:1} {1:1} {4:1} -q EACHFILE

    - {2:1} except for call samples in wait status
        # {0:1} {1:1} {4:1} -q EXCEPTWAIT

    - {2:1} only for call samples in wait status
        # {0:1} {1:1} {4:1} -q ONLYWAIT

    - {2:1} with symbol filter for call samples
        # {0:1} {1:1} {4:1} -q TARGETSYM:"*test*", TARGETSYM:"*print*"
        # {0:1} {1:1} {4:1} -q EXCEPTSYM:"*remove*", EXCEPTSYM:"*install*"

    - {2:1} with comm filter for pie chart
        # {0:1} {1:1} {4:1} -q INCFILTER:"*test", EXFILTER:"kworker*"

    - {2:1} after concatenating specific files
        # {0:1} {1:1} {4:1} -q CONCATENATE
        # {0:1} {1:1} {4:1} -q CONCATENATE, ONLYTOTAL

    - {2:1} after concatenating specific files and converting unique physical memory (RSS - Text - Shm)
        # {0:1} {1:1} {4:1} -q CONCATENATE, EXCEPTSHM

    - {2:1} except for specific files do not meet conditions
        # {0:1} {1:1} "guider*.out" -q NOMERGE, TRIMIDX:-5:, CPUFILTER:">80", MEMFREEFILTER:"<100"
        # {0:1} {1:1} "guider*.out" -q NOMERGE, MEMANONFILTER:">90000", MEMCACHEFILTER:"<100"
        # {0:1} {1:1} "guider*.out" -q NOMERGE, SWAPFILTER:">1000", NETRDFILTER:">900", NETWRFILTER:">900"
        # {0:1} {1:1} "guider*.out" -q NOMERGE, BLKWRFILTER:">1000", BLKRDFILTER:">900", NRCOREFILTER:"<4"

    - {2:1} except for specific resources after concatenating specific files
        # {0:1} {1:1} {4:1} -q CONCATENATE, NOCPUSUMMARY, NOGPUSUMMARY
        # {0:1} {1:1} {4:1} -q CONCATENATE, NODELAYSUMMARY, NOGPUMEMSUMMARY
        # {0:1} {1:1} {4:1} -q CONCATENATE, NOVSSSUMMARY, NORSSSUMMARY
        # {0:1} {1:1} {4:1} -q CONCATENATE, NOBLOCKSUMMARY
        # {0:1} {1:1} {4:1} -q CONCATENATE, NOSTORAGESUMMARY, NONETSUMMARY

    - Draw specific items including the specific word
        # {0:1} {1:1} timeline.json -q FILTER:"*test*"

    - {2:1} for all resources and events and tasks
        # {0:1} {1:1} {4:1} -a
        # {0:1} {1:1} {3:1} -a

    - {2:1} with I/O filter
        # {0:1} {1:1} {4:1} -q IOFILTER:"[BR]", IOFILTER:"[NI]", IOFILTER:lo
        # {0:1} {1:1} {4:1} -q IOPROCFILTER:"*system*"

    - {2:1} except for load plots including CPU and I/O usage
        # {0:1} {1:1} {4:1} -q NOLOADPLOT

    - {2:1} except for specific cpu plots
        # {0:1} {1:1} {4:1} -q MAXCPUCOND:10
        # {0:1} {1:1} {4:1} -q AVGCPUCOND:10
        # {0:1} {1:1} {4:1} -q MINCPUCOND:10

    - {2:1} except for specific prio plots having maximum value
        # {0:1} {1:1} {4:1} -q MAXPRICOND:C-1
        # {0:1} {1:1} {4:1} -q MINPRICOND:R1

    - {2:1} for process memory
        # {0:1} {1:1} {4:1} -q DRAWRSS

    - {2:1} including system memory plots
        # {0:1} {1:1} {4:1} -q MEMSYSPLOT

    - {2:1} except for specific memory plots
        # {0:1} {1:1} {4:1} -q NOMEMFREEPLOT
        # {0:1} {1:1} {4:1} -q NOMEMANONPLOT
        # {0:1} {1:1} {4:1} -q NOMEMKERNELPLOT
        # {0:1} {1:1} {4:1} -q NOMEMCACHEPLOT
        # {0:1} {1:1} {4:1} -q NOMEMPROCPLOT
        # {0:1} {1:1} {4:1} -q NOMEMSWAPPLOT
        # {0:1} {1:1} {4:1} -q MAXVSSCOND:1024
        # {0:1} {1:1} {4:1} -q MAXRSSCOND:1024

    - {2:1} with specific log events
        # {0:1} {1:1} {4:1} -q "DLTEVENT:LASTFILE|DLT|"
        # {0:1} {1:1} {4:1} -q "DLTEVENT:test.dlt|DLT|"
        # {0:1} {1:1} {4:1} -q "DLTEVENT:test.dlt|TIMEOUT|timeout"
        # {0:1} {1:1} {4:1} -q "DLTEVENT:test.dlt|TIMEOUT|timeout", PRINTEVENT
        # {0:1} {1:1} {4:1} -q "KERNELEVENT:TIMEOUT|timeout"
        # {0:1} {1:1} {4:1} -q "TRACEEVENT:TIMEOUT|timeout"
        # {0:1} {1:1} {4:1} -q "JOURNALEVENT:TIMEOUT|timeout"
        # {0:1} {1:1} {4:1} -q "SYSLOGEVENT:TIMEOUT|timeout"

    - {2:1} with accumulated stats
        # {0:1} {1:1} {4:1} -q ACCUMULATE

    - {2:1} without system info
        # {0:1} {1:1} {4:1} -q NOSYSINFO

    - {2:1} with highlighting for specific systemd events
        # {0:1} {1:1} {4:1} -q HIGHLIGHTUNIT:"*hmi*"

    - {2:1} and event markers without time info on specific points
        # {0:1} {1:1} {4:1} -q NOEVTTIME

    - {2:1} without systemd events
        # {0:1} {1:1} {4:1} -q NOUNITEVT
        # {0:1} {1:1} {4:1} -q NOTARGETEVT

    - {2:1} with specific systemd events
        # {0:1} {1:1} {4:1} -q UNITFILTER:"*.service"
        # {0:1} {1:1} {4:1} -q UNITFILTER:"*.service", YRANGE::200
        # {0:1} {1:1} {4:1} -q UNITEXFILTER:"*.service"
        # {0:1} {1:1} {4:1} -q TARGETFILTER:"*home*"
        # {0:1} {1:1} {4:1} -q TARGETEXFILTER:"*home*"

    - {2:1} with specific systemd events and line breaks grouped for text separated by a specific y-axis region
        # {0:1} {1:1} {4:1} -q NRVPART:10

    - {2:1} without cgroup stats
        # {0:1} {1:1} {4:1} -q NOCGCPU, NOCGTHROTTLE, NOCGMEM, NOCGBLK

    - {2:1} for specific tasks
        # {0:1} {1:1} {3:1} -g task3
        # {0:1} {1:1} {3:1} -g "*java*" -a
        # {0:1} {1:1} {3:1} -g "*" -q NOTOTAL
        # {0:1} {1:1} {3:1} -g "*java*" -q NOTOTAL

    - {2:1} for specific tasks and their siblings
        # {0:1} {1:1} {3:1} -g task3 -P

    - {2:1} by displaying process info only
        # {0:1} {1:1} {3:1} -q ONLYPROC
        # {0:1} {1:1} {3:1} -q ONLYPROC, PERTASK

    - {2:1} except for syscalls for timeline segments
        # {0:1} {1:1} {3:1} -q NOSYSCALL

    - {2:1} forcefully
        # {0:1} {1:1} {3:1} -f

    - {2:1} using uptime for perf graph
        # {0:1} {1:1} {3:1} -q NOTIMEFORMAT

    - {2:1} with specific task life events for perf graph
        # {0:1} {1:1} {3:1} -q LIFEEVENT, LFONTSIZE:2
        # {0:1} {1:1} {3:1} -q LIFEEVENT, INEVENT:"*system*", INEVENT:"*cat*"
        # {0:1} {1:1} {3:1} -q LIFEEVENT, EXEVENT:"*arp*", EXEVENT:"*pkill*"

    - {2:1} in us time-unit for timeline segments
        # {0:1} {1:1} {3:1} -q TIMEUNIT:us

    - {2:1} except for labels of timeline segments lesser than 100ms
        # {0:1} {1:1} {3:1} -q LABELMIN:100

    - {2:1} with label setting
        # {0:1} {1:1} {3:1} -q LABEL
        # {0:1} {1:1} {3:1} -q NOLABEL

    - {2:1} with stroke only for specific tasks
        # {0:1} {1:1} {3:1} -q STROKE:"*"
        # {0:1} {1:1} {3:1} -q STROKE:"screen*", STROKE:"a.out(1234)"

    - {2:1} and event markers on specific points
        # {0:1} {1:1} {3:1} -q EVENT:14:90:EVENT_1:CPU
        # {0:1} {1:1} {3:1} -q EVENT:30:100:EVENT_2:MEM
        # {0:1} {1:1} {3:1} -q EVENT:30:100:EVENT_3:CIRCLE
        # {0:1} {1:1} {3:1} -q EVENT:30:100:EVENT_4:LARROW
        # {0:1} {1:1} {3:1} -q EVENT:30:100:EVENT_5:RARROW

    - {2:1} only for specific groups or cores
        # {0:1} {1:1} {3:1} -O 1, 4, 10
        # {0:1} {1:1} {3:1} -O 1:10, 14

    - {2:1} with contents of specific files or directories
        # {0:1} {1:1} {3:1} -q RECFILEFILTER:a.out
        # {0:1} {1:1} {3:1} -q RECFILEFILTER:"*.out"

    - {2:1} without file data
        # {0:1} {1:1} {3:1} -q NORECFILE

    - {2:1} with changed groups or cores for timeline segments
        # {0:1} {1:1} {3:1} -q CONVGROUP:0:1, CONVGROUP:1:0

    - {2:1} with stroke only for specific interval / duration bigger than 3000 for timeline segments
        # {0:1} {1:1} {3:1} -q STROKEINTERVAL:3000
        # {0:1} {1:1} {3:1} -q STROKEDURATION:3000

    - {2:1} to specific image format
        # {0:1} {1:1} {4:1} -F png
        # {0:1} {1:1} {4:1} -F pdf
        # {0:1} {1:1} {4:1} -F svg

    - {2:1} with config file
        # {0:1} {1:1} {3:1} -C config.json

    - {2:1} excluding chrome process
        # {0:1} {1:1} {3:1} -g ^chrome
        # {0:1} {1:1} {4:1} -g ^chrome

    - {2:1} with some boundary lines
        # {0:1} {1:1} {4:1} worstcase.out -l 80, 100, 120

    - {2:1} within specific interval range in second unit
        # {0:1} {1:1} {4:1} -q TRIM:9:15
        # {0:1} {1:1} {4:1} -q TRIM:0.9:1.5
        # {0:1} {1:1} {4:1} -q TRIM:11:
        # {0:1} {1:1} {4:1} -q TRIM:11.9:13.5, ABSTIME

    - {2:1} on absolute timeline
        # {0:1} {1:1} {4:1} -q ABSTIME

    - {2:1} on relative timeline
        # {0:1} {1:1} {4:1} -q RELTIME

    - {2:1} only having backtrace for the flame graph
        # {0:1} {1:1} {4:1} -q ONLYBTSTACK

    - {2:1} within specific interval range in index unit
        # {0:1} {1:1} {4:1} -q TRIMIDX:0:3
        # {0:1} {1:1} {4:1} -q TRIMIDX:-5:

    - {2:1} with specific range of ticks
        # {0:1} {1:1} {4:1} worstcase.out -q XRANGE:1:100
        # {0:1} {1:1} {4:1} worstcase.out -q YRANGE:1:100
        # {0:1} {1:1} {4:1} worstcase.out -q XRANGE:10:
        # {0:1} {1:1} {4:1} worstcase.out -q YRANGE:10:
        # {0:1} {1:1} {4:1} worstcase.out -q YRANGE::100
        # {0:1} {1:1} {4:1} worstcase.out -q YRANGE::100, KEEPYRANGE

    - {2:1} with specific font size
        # {0:1} {1:1} {4:1} worstcase.out -q FONTSIZE:15
        # {0:1} {1:1} {4:1} worstcase.out -q XFONTSIZE:2
        # {0:1} {1:1} {4:1} worstcase.out -q LFONTSIZE:5
        # {0:1} {1:1} {4:1} worstcase.out -q GROUPFONTSIZE:30
        # {0:1} {1:1} {4:1} worstcase.out -q LABELSIZE:30

    - {2:1} of top 5 processes
        # {0:1} {1:1} {4:1} worstcase.out -T 5

    - {2:1} of total CPU usage by applying the multiplication of the number of CPUs
        # {0:1} {1:1} {4:1} worstcase.out -d A

    - {2:1} with total usage stats in label
        # {0:1} {1:1} {4:1} worstcase.out -q TOTALLABEL

    - {2:1} on customized layout
        # {0:1} {1:1} {4:1} -L c:3, d:3
        # {0:1} {1:1} {4:1} -L c:2, m:2, i:2
        # {0:1} {1:1} {4:1} -L c:4, r:1, v:1

    - {2:1} for block and network resources
        # {0:1} {1:1} {4:1} -e d n

    - {2:1} with multiple files for comparison
        # {0:1} {1:1} "guider*.out" worstcase.out

    - {2:1} of total resource usage with multiple files for comparison
        # {0:1} {1:1} "guider*.out" worstcase.out -a -g TOTAL

    - Draw fixed-size items for timeline segments
        # {0:1} {1:1} timeline.json -q DURATION:500

    - Draw per-task items for timeline segments
        # {0:1} {1:1} timeline.json -q PERTASK

    - {2:1} with minimum fixed-size for timeline segments
        # {0:1} {1:1} timeline.json -q DURATIONMIN:500
                """.format(
                    cmd, mode, "Draw items", "guider.dat", "guider.out"
                )

                cmdListStr = """
Commands(%s):
    acc      print accumulation stats for specific values [NAME:VAR|REG|VAL]
    add      add breakpoints
    check    check context and execute next commands [VAR|ADDR|REG:OP(EQ/DF/INC/BT/LT):VAR|VAL:SIZE:EVT]
    condexit exit if tracing was started
    debug    run debug mode
    dist     print distribution stats for specific values [NAME:VAR|REG|VAL]
    dump     dump specific memory range to a file [NAME|ADDR:FILE]
    event    write ftrace log [MESSAGE]
    exec     execute the external command [CMD]
    exit     exit tracing
    filter   print only filtered context [VAR|ADDR|REG|RETTIME|RETVAL:OP(EQ/DF/INC/BT/LT):VAR|VAL:SIZE]
    getarg   print specific registers [REGS]
    getaux   print the auxiliary vector
    getenv   print specific environment variable [VAR]
    getret   print return value [CMD]
    help     print help
    hide     hide tracing
    hidden   hide the function call
    setinter print interval time between specific function calls
    inter    print interval stats for the function call
    jump     jump to specific function with specific arguments [FUNC#ARGS]
    kill     terminate the target task
    load     load specific library [PATH]
    log      print specific message [MESSAGE]
    madvise  give advise about use of memory [ADDR:LENGTH:ADVISE]
    map      print memory map
    mprotect set protection on a region of memory
    print    print context [VAR]
    pyfile   execute specific python file remotely [PATH:SYNC]
    pyscript execute python function locally [PATH:FUNC:ARGS]
    pystr    execute python code remotely [CODE:SYNC]
    remove   remove breakpoints [FUNC]
    rdfd     print the path path associated with fd [FD]
    rdmem    print specific memory or register [VAR|ADDR|REG:SIZE]
    repeat   call again repeatedly [CNT]
    ret      return specific value immediately [VAL]
    save     save previous or specific or argument value [VAR:VAL:TYPE]
    setarg   change value for specific register [REG#VAR|VAL]
    setenv   change specific environment variable [VAR:VAR|VAL]
    setret   change return value [VAL:CMD]
    show     show tracing
    sleep    sleep for seconds [SEC]
    start    start tracing
    stop     pause tracing
    syscall  call a syscall [FUNC#ARGS]
    thread   create a new thread [CNT]
    proctop  print total resource usage for processes [PID|COMM]
    proctopg print total resource usage for process groups [PID|COMM]
    thrtop   print total resource usage for threads [PID|COMM]
    thrtopg  print total resource usage for thread groups [PID|COMM]
    usercall call a specific function [FUNC|ADDR#ARGS]
    wrmem    change specific memory or register [VAR|ADDR|REG:VAL:SIZE]
                """
                cmdListStr = cmdListStr % (cmdListStr.count("\n") - 2)

                dbgExamStr = """\n
Common Examples:
    - Set target ID from user input with task monitoring
        # {0:1} {1:1} {2:1} {3:1}
        # {0:1} {1:1} {2:1} {3:1} -e t
        # {0:1} {1:1} {2:1} {3:1}:3
        # {0:1} {1:1} {2:1} {3:1} -a
        # {0:1} {1:1} {2:1} {3:1}, TASKFILTER:"*a.out*"
        # {0:1} {1:1} {2:1} {3:1}, TASKFILTER:"*a.out*" -e t -P
        # {0:1} {1:1} {2:1} {3:1}, TASKMONOPT:"-qNRTOPRANK:10|-Yr:1"

    - Print including backtrace info
        # {0:1} {1:1} {2:1} -H

    - Print including backtrace info with source file and line number
        # {0:1} {1:1} {2:1} -H -eD -q DEBUGINFO, NOFILECACHE

    - Print if only backtrace including specific words
        # {0:1} {1:1} {2:1} -H -q BTFILTER:"*_IO*"

    - Print python backtrace info
        # {0:1} {1:1} {2:1} -H -q PYSTACK

    - Run only for 2 seconds
        # {0:1} {1:1} {2:1} -R 2s

    - Print in JSON format
        # {0:1} {1:1} {2:1} -J
        # {0:1} {1:1} {2:1} -J -Q
        # {0:1} {1:1} {2:1} -J -q COMPLETECALL

    - Disable standard output mute
        # {0:1} {1:1} a.out -q UNMUTE

    - Execute target process monitoring in parallel
        # {0:1} {1:1} a.out -w BEFORE:"GUIDER top -g ls -Q &"

    - Print tracing overhead
        # {0:1} {1:1} {2:1} -q PRINTDELAY

    - Inlcude profiling overhead time
        # {0:1} {1:1} {2:1} -q INCOVERHEAD

    - Redirect standard I/O of new tasks to specific files
        # {0:1} {1:1} a.out -q STDIN:"./stdin"
        # {0:1} {1:1} a.out -q STDOUT:"./stdout"
        # {0:1} {1:1} a.out -q STDERR:"/dev/null"

    - Use merged symbols
        # {0:1} {1:1} a.out -q MERGESYM

    - Use all symbols
        # {0:1} {1:1} a.out -q ALLSYM

    - Don't use previous sampled caches
        # {0:1} {1:1} {2:1} -q NOSAMPLECACHE

    - Print injection info
        # {0:1} {1:1} {2:1} -q TRACEBP

    - Disable file loading messages
        # {0:1} {1:1} {2:1} -q NOLOADMSG

    - Preload a ELF file
        # {0:1} {1:1} a.out -q PRELOAD:./mem.so

    - Preload ELF files from the specific file list
        # {0:1} {1:1} a.out -q PRELOADLIST:./preload.list

    - Replace a ELF file to load
        # {0:1} {1:1} a.out -q REPELF:/usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2:/usr/lib/debug/.build-id/15/921ea631d9f36502d20459c43e5c85b7d6ab76.debug

    - Use compressed ELF caches
        # {0:1} {1:1} {2:1} -q COMPCACHE

    - Apply lazy cache loading
        # {0:1} {1:1} {2:1} -q LAZYCACHE

    - Use debug info
        # {0:1} {1:1} {2:1} -q DEBUGINFO
        # {0:1} {1:1} {2:1} -q DEBUGINFO -H
        # {0:1} {1:1} {2:1} -q DEBUGINFO:/usr/lib/libc.so

    - Except for DWARF table of specific files
        # {0:1} {1:1} {2:1} -q EXCEPTDWARF:"*deno"

    - Use DWARF info
        # {0:1} {1:1} "ls" -eD

    - Print backtrace info including arguments using DWARF
        # {0:1} {1:1} {2:1} -e D -H -q DEBUGINFO, PRINTARG

    - Use file caches forcefully
        # {0:1} {1:1} {2:1} -q FORCEFILECACHE

    - Dont' use file caches
        # {0:1} {1:1} {2:1} -q NOFILECACHE

    - Convert syscall args
        # {0:1} {1:1} {2:1} -q CONVARG

    - Use only PID not TID
        # {0:1} {1:1} {2:1} -q ONLYPROC

    - Apply filter only for specific IDs
        # {0:1} {1:1} -g 1234 -q ONLYPID

    - Apply filter only for specific COMM
        # {0:1} {1:1} -g 1234 -q ONLYCOMM

    - Apply filter except for specific COMM
        # {0:1} {1:1} {2:1} -q EXCEPTCOMM:"thread*"

    - Wait for user input or specific time for starting
        # {0:1} {1:1} {2:1} -W
        # {0:1} {1:1} {2:1} -W 5s

    - Wait for new target task if no task
        # {0:1} {1:1} {2:1} -q WAITTASK
        # {0:1} {1:1} {2:1} -q WAITTASK:1
        # {0:1} {1:1} {2:1} -q WAITTASK, NOPIDCACHE

    - Wait for specific uptime for starting
        # {0:1} {1:1} {2:1} -q STARTCONDTIME:100 -W

    - Wait for specific uptime for termination
        # {0:1} {1:1} {2:1} -q EXITCONDTIME:100 -R

    - Print only specific tasks consumed CPU more than 10%
        # {0:1} {1:1} {2:1} -q CPUCOND:10

    - Wait for child tasks created by specific threads
        # {0:1} {1:1} {2:1} -q WAITCLONE

    - Apply new sched priority for executed process
        # {0:1} {1:1} {2:1} -q EXECSCHED:c:0

    - Apply new sched priority for new tracees
        # {0:1} {1:1} {2:1} -q TRACEESCHED:c:0

    - Except for wait status of target tasks
        # {0:1} {1:1} {2:1} -q EXCEPTWAIT

    - Print only for wait status of target tasks
        # {0:1} {1:1} {2:1} -q ONLYWAIT

    - Except for specific context info
        # {0:1} {1:1} {2:1} -q NOCONTEXT
        # {0:1} {1:1} {2:1} -q NOREG, NOSIG, NOBT

    - Keep going even if the master tracer is terminated
        # {0:1} {1:1} {2:1} -q CONTALONE

    - Print call interval info including stdev, histogram
        # {0:1} {1:1} {2:1} -q INTERCALL, STDEV, PRINTHIST

    - Load all symbols in stop status
        # {0:1} {1:1} {2:1} -q STOPTARGET

    - Sync tasks after clone event
        # {0:1} {1:1} {2:1} -q SYNCTASK

    - Print address instead of anonymous symbol "??"
        # {0:1} {1:1} {2:1} -q SHOWADDR

    - Except for no symbol samples
        # {0:1} {1:1} {2:1} -q ONLYSYM

    - Print thread group info
        # {0:1} {1:1} {2:1} -q INCTGINFO

    - Except for arguments
        # {0:1} {1:1} {2:1} -q NOARG

    - Except for file info
        # {0:1} {1:1} {2:1} -q NOFILE

    - Print ld(loader) info
        # {0:1} {1:1} {2:1} -q PRINTLD

    - Print file access info
        # {0:1} {1:1} {2:1} -q PRINTFILE
        # {0:1} {1:1} {2:1} -c "write|rdfd:0" -q PRINTFILE -o

    - Except for ld(loader) info
        # {0:1} {1:1} {2:1} -q EXCEPTLD

    - Include ELF section info
        # {0:1} {1:1} {2:1} -q INCSECT

    - Check new mapping every time until loading libc
        # {0:1} {1:1} a.out -q WATCHLD

    - Ignore specific signals
        # {0:1} {1:1} {2:1} -q IGNORESIGNAL:SIGABRT
        # {0:1} {1:1} {2:1} -q IGNORESIGNAL:SIGSEGV, SKIPSIGNAL:8

    - Target new 4th and 5th threads in each new processes
        # {0:1} {1:1} a.out -q TARGETNUM:4, TARGETNUM:5

    - Manage specific environment variables
        # {0:1} {1:1} a.out -q ENV:TEST=1, ENV:PATH=/data
        # {0:1} {1:1} a.out -q ENVFILE:/data/env.sh
        # {0:1} {1:1} a.out -q CLEARENV
        # {0:1} {1:1} a.out -q CLEARENV:HOME, CLEARENV:^LANGUAGE
        # {0:1} {1:1} a.out -q ENVPROC:systemd

    - Disable line truncation
        # {0:1} {1:1} {2:1} -q NOCUT

    - Monitor CPU usage on whole system
        # {0:1} {1:1} {2:1} -e c

    - Draw timeline segments for function calls
        # {0:1} {1:1} {2:1} -q TIMELINE, TIMEUNIT:ms, INTERCALL, DURATION:10
        # {0:1} {1:1} {2:1} -q TIMELINE, TIMEUNIT:us, INTERCALL, DURATION:100 -H -a
        # {0:1} {1:1} {2:1} -c "*|getret" -q TIMELINE, TIMEUNIT:ms, COMPLETECALL
        # {0:1} {1:1} {2:1} -c "*|getret" -q TIMELINE, TIMEUNIT:us, COMPLETECALL, GROUPFONTSIZE:30
                """.format(
                    cmd,
                    mode,
                    "-g a.out",
                    "-q TASKMON",
                ).rstrip()

                brkExamStr = (
                    """{2:1}
Examples:
    - {3:1} {7:1}
        # {0:1} {1:1} -g 1234
        # {0:1} {1:1} {9:1}

    - {3:1} {7:1} from specific runtime
        # {0:1} {1:1} -g "*chrome, *test*" -q TARGETEXE
        # {0:1} {1:1} -g "*chrome, *test*" -q TARGETCMD

    - {3:1} {8:1}
        # {0:1} {1:1} "ls"
        # {0:1} {1:1} "sh -c \\"while [ 1 ]; do echo "OK"; done;\\""
        # {0:1} {1:1} -I "ls"

    - {5:1} {7:1}
        # {0:1} {1:1} {9:1} -c printPeace
        # {0:1} {1:1} -g 1234 -c "*printPeace"
        # {0:1} {1:1} -g 1234 -c "printPeace*"
        # {0:1} {1:1} -g 1234 -c "*printPeace*"

    - {5:1} {7:1} and report the result to guider.out
        # {0:1} {1:1} {9:1} -c printPeace -o .
        # {0:1} {1:1} {9:1} -c printPeace -o . -q FORCESUMMARY

    - {3:1} related to specific files {7:1}
        # {0:1} {1:1} {9:1} -c -T /usr/bin/yes
        # {0:1} {1:1} {9:1} -c -T "/usr/lib/*"
        # {0:1} {1:1} {9:1} -c -q TARGETFILE:"/usr/lib/*"

    - {3:1} except for specific files {7:1}
        # {0:1} {1:1} {9:1} -c -T ^/usr/bin/yes
        # {0:1} {1:1} {9:1} -c -T "^/usr/lib/*"
        # {0:1} {1:1} {9:1} -c -q EXCEPTFILE:"/usr/lib/*"

    - {3:1} and JIT-compiled function calls {7:1}
{6:1}
        # {0:1} {1:1} -g node -q JITSYM

    - {3:1} {7:1} with call interval info
        # {0:1} {1:1} {9:1} -q INTERCALL
        # {0:1} {1:1} {9:1} -c "QAnimationDriver::advanceAnimation*" -q INTERCALL -P
        # {0:1} {1:1} {9:1} -c "QSGThreadedRenderLoop::polishAndSync*" -q INTERCALL -P
        # {0:1} {1:1} {9:1} -c "QCoreApplication::notifyInterval2*" -q INTERCALL -P -H
        # {0:1} {1:1} {9:1} -c "QOpenGLContext::swapBuffers*" -q INTERCALL -P

    - {3:1} and print context combined both entry and exit
        # {0:1} {1:1} {9:1} -c "*|getret' -q COMPLETECALL

    - {3:1} except for specific functions {7:1}
        # {0:1} {1:1} {9:1} -c "^printPeace"
        # {0:1} {1:1} {9:1} -c "^g_mutex_*, ^pthread_mutex_*"

    - {5:1} {8:1}
        # {0:1} {1:1} ~/test/mutex -c "std::_Vector_base<unsigned long\, std::allocator<unsigned long> >::~_Vector_base()"

    - {5:1} after removing default breakpoints to improve tracing performance
        # {0:1} {1:1} {9:1} -c "_Exit|stop" -q FIXEDBP

    - {5:1} including specific word in a hidden state
        # {0:1} {1:1} {9:1} -c "*printPeace|hidden"

    - {5:1} including specific word {7:1} until {4:1}
        # {0:1} {1:1} {9:1} -c "*, *printPeace|stop"
        # {0:1} {1:1} {9:1} -c "*, printPeace*|hide"
        # {0:1} {1:1} {9:1} -c "*, *printPeace*|hide"

    - {3:1} and sleep for 0.1 second {4:1}
        # {0:1} {1:1} {9:1} -c "*|sleep:0.1"

    - {5:1} and sleep for 0.1 second only one time {7:1} {4:1}
        # {0:1} {1:1} {9:1} -c "write|oneshot:sleep:0.1"

    - {5:1} {7:1} and kill the target thread {4:1}
        # {0:1} {1:1} {9:1} -c "write|kill"

    - {5:1} {7:1} and modify memory {4:1}
        # {0:1} {1:1} {9:1} -c "write|wrmem:0x1234:aaaa:4"

    - {5:1} {7:1} and modify memory pointed by 1st argument {4:1}
        # {0:1} {1:1} {9:1} -c "write|wrmem:0:aaaa:4"
        # {0:1} {1:1} {9:1} -c "open|wrmem:0:/home/iipeace/test/../test/a.out"

    - {5:1} {7:1} and print null-terminated string pointed by 1st argument {4:1}
        # {0:1} {1:1} {9:1} -c "printf|rdmem:0"

    - {5:1} {7:1} and print 10-length string pointed by 1st argument {4:1}
        # {0:1} {1:1} {9:1} -c "printf|rdmem:0:10"

    - {5:1} {7:1} and print 10-length string from the specific address {4:1}
        # {0:1} {1:1} {9:1} -c "printf|rdmem:0x1234:10"

    - {5:1} {7:1} and print the file path associated with fd in 1st argument {4:1}
        # {0:1} {1:1} {9:1} -c "read|rdfd:0"

    - {5:1} {7:1} and call madvise {4:1}
        # {0:1} {1:1} {9:1} -c "printf|madvise:0x4096:8092:3"
        # {0:1} {1:1} {9:1} -c "printf|madvise:0x4096:4K:DONTNEED|madvise:0x40960:2M:MERGEABLE"
        # {0:1} {1:1} {9:1} -c "printf|madvise:stack:1M:WILLNEED"
        # {0:1} {1:1} {9:1} -c "printf|madvise:heap:4M:MERGEABLE"

    - {5:1} {7:1} and return a specific value immediately {4:1}
        # {0:1} {1:1} {9:1} -c "write|ret:3"

    - {5:1} {7:1} and run debug mode {4:1}
        # {0:1} {1:1} {9:1} -c "write|debug"

    - {5:1} {7:1} and add breakpoints for specific symbols
        # {0:1} {1:1} {9:1} -c "*loadData*|add:*close*"
        # {0:1} {1:1} {9:1} -c "*loadData*|getret|add:func1;func2"
        # {0:1} {1:1} {9:1} -c "*loadData*|getret|add:*close*"
        # {0:1} {1:1} {9:1} -c "*loadData*|getret|add:^*close*"

    - {5:1} {7:1} and remove breakpoints for specific symbols
        # {0:1} {1:1} {9:1} -c "*loadData*|remove:*"
        # {0:1} {1:1} {9:1} -c "*loadData*|getret|remove:func1;func2"
        # {0:1} {1:1} {9:1} -c "*loadData*|getret|remove:^*loadData*"

    - {5:1} {7:1} and write event info of specific symbols into ftrace buffer
        # {0:1} {1:1} {9:1} -c "*loadData*|event:LOG"

    - {5:1} {7:1} and run multiple commands when specific function calls return
        # {0:1} {1:1} {9:1} -c "write|getret:sleep:1\\$exec:ls\\$stop"
        # {0:1} {1:1} {9:1} -c "write|getret:save:VAR1\\$rdmem:@VAR1"

    - {5:1} {7:1} and print resource usage for tasks
        # {0:1} {1:1} {9:1} -c "*loadData*|getret:proctop:a.out|proctop:a.out"
        # {0:1} {1:1} {9:1} -c "*loadData*|getret:proctop:a.out\\,guider|proctop:a.out\\,guider"
        # {0:1} {1:1} {9:1} -c "*loadData*|getret:thrtopg:a.out|thrtopg:a.out"

    - {5:1} {7:1} and dump thread stack to the specific file {4:1}
        # {0:1} {1:1} {9:1} -c "write|dump:stack:stack.out"

    - {5:1} {7:1} and dump specific memory range to the specific file {4:1}
        # {0:1} {1:1} {9:1} -c "write|dump:0x1234-0x4567:dump.out"

    - {5:1} {7:1} and print return value {4:1}
        # {0:1} {1:1} {9:1} -c "write|getret"
        # {0:1} {1:1} {9:1} -c "write|getret" -q NORETBT

    - {5:1} {7:1} until {4:1} and save return value to the specific variable
        # {0:1} {1:1} {9:1} -c "write|getret:stop\\$print"

    - {5:1} {7:1} from when specific function calls return
        # {0:1} {1:1} {9:1} -c "write|getret:start, *"

    - {5:1} with colorful elapsed time exceeds 0.1 seconds {4:1} {7:1}
        # {0:1} {1:1} {9:1} -c "write|getret" -q ELAPSED:0.1

    - {5:1} and call them again repeatedly {4:1} {7:1}
        # {0:1} {1:1} {9:1} -c "write|repeat"
        # {0:1} {1:1} {9:1} -c "write|repeat:5"

    - {5:1} and modify return value {4:1} {7:1}
        # {0:1} {1:1} {9:1} -c "write|setret:3"
        # {0:1} {1:1} {9:1} -c "write|setret:3:print"

    - {5:1} and modify 1st and 2nd arguments {4:1} {7:1}
        # {0:1} {1:1} {9:1} -c "write|setarg:0#2:1#5"

    - {5:1} and print call contexts {4:1} {7:1}
        # {0:1} {1:1} {9:1} -c "write|print"

    - {5:1} and print call contexts {4:1} {7:1}
        # {0:1} {1:1} {9:1} -c "write|print"

    - {5:1} and print python contexts {4:1} {7:1}
        # {0:1} {1:1} {9:1} -c "write|print" -q PYSTACK

    - {5:1} and save specific values to specific variables and handle them {4:1} {7:1}
        # {0:1} {1:1} {9:1} -c "write|save:VAR1:123|print:VAR1"
        # {0:1} {1:1} {9:1} -c "write|save:ARG1:1:arg|print:VAR1"
        # {0:1} {1:1} {9:1} -c "write|save:VAR1:1|save:VAR1:@VAR1:float|print:VAR1"
        # {0:1} {1:1} {9:1} -c "write|getret|save:VAR1|print:VAR1"

    - {5:1} and print call contexts if only specific conditions are met {4:1} {7:1}
        # {0:1} {1:1} {9:1} {11:1}2:EQ:4096"
        # {0:1} {1:1} {9:1} {11:1}2:BT:0x1000"
        # {0:1} {1:1} {9:1} {11:1}*1:EQ:HELLO"
        # {0:1} {1:1} {9:1} {11:1}*1:INC:HE"
        # {0:1} {1:1} {9:1} {11:1}#0:EQ:/dev/null"
        # {0:1} {1:1} {9:1} {11:1}#0:INC:null"
        # {0:1} {1:1} {9:1} {11:1}2:BT:1|filter:1:EQ:1"

    - {5:1} and print call contexts if only the elapsed time exceed 0.0005 second {7:1} {4:1}
        # {0:1} {1:1} {9:1} {11:1}RETTIME:BT:0.0005"
        # {0:1} {1:1} {9:1} -c "write|getret|filter:RETTIME:BT:0.0005" -q COMPLETECALL -a
        # {0:1} {1:1} {9:1} {11:1}RETTIME:BT:0.0005" -H -a
        # {0:1} {1:1} {9:1} {11:1}RETTIME:BT:0.0005|filter:0:BT:0"
        # {0:1} {1:1} {9:1} {11:1}RETTIME:BT:0.0005:exit"
        # {0:1} {1:1} {9:1} {11:1}RETTIME:BT:0.0005:sleep:1"
        # {0:1} {1:1} {9:1} {11:1}RETTIME:BT:0.0005:dist:0"

    - {5:1} and print call contexts if only the return value is bigger than 1 {4:1} {7:1}
        # {0:1} {1:1} {9:1} {11:1}RETVAL:BT:0.0005"
        # {0:1} {1:1} {9:1} {11:1}RETVAL:BT:0.0005" -H -a
        # {0:1} {1:1} {9:1} {11:1}RETVAL:BT:0.0005|filter:0:BT:0"
        # {0:1} {1:1} {9:1} {11:1}RETVAL:BT:0.0005:exit"
        # {0:1} {1:1} {9:1} {11:1}RETVAL:BT:0.0005:sleep:1"

    - {5:1} and check specific conditions {4:1} {7:1}
        # {0:1} {1:1} {9:1} -c "write|check:2:EQ:4096"
        # {0:1} {1:1} {9:1} -c "write|check:2:BT:0x1000"
        # {0:1} {1:1} {9:1} -c "write|check:*1:EQ:HELLO"
        # {0:1} {1:1} {9:1} -c "write|check:*1:INC:HE"
        # {0:1} {1:1} {9:1} -c "write|check:0:EQ:1|sleep:1"
        # {0:1} {1:1} {9:1} -c "write|check:@RET1:EQ:@RET2:EVENT_CONT"

    - {5:1} and print 1st and 2nd arguments {4:1} {7:1}
        # {0:1} {1:1} {9:1} -c "write|getarg:0:1"

    - {5:1} and print 1st and 2nd arguments and save its return value to the specific variable {4:1}
        # {0:1} {1:1} {9:1} -c "write|getarg:0:1|save:writeRet"

    - {5:1} from {4:1} {7:1}
        # {0:1} {1:1} {9:1} -c "write|start, *"
        # {0:1} {1:1} {9:1} -c "write|show, *" -q HIDESYM

    - {5:1} and terminate the target thread {4:1}
        # {0:1} {1:1} {9:1} -c "write|exit"

    - {5:1} within a specific range {4:1} {7:1}
        # {0:1} {1:1} {9:1} -c "open|start|getret:stop, *"
        # {0:1} {1:1} {9:1} -c "open|start|getret:exit, *"
        # {0:1} {1:1} {9:1} -c "open|start|getret:exit, *|getret"
        # {0:1} {1:1} {9:1} -c "open|start, *, close|getret:condexit"

    - {5:1} and call specific functions every time {4:1} {7:1}
        # {0:1} {1:1} {9:1} -c "write|usercall:sleep#3"
        # {0:1} {1:1} {9:1} -c "write|usercall:sleep#3", NOBT
        # {0:1} {1:1} {9:1} -c "write|usercall:printf#PEACE\\n"
        # {0:1} {1:1} {9:1} -c "write|usercall:printf#12345\\n"
        # {0:1} {1:1} {9:1} -c "write|usercall:getenv#PATH, usercall:write#1#@getenv#1024"

    - {5:1} and call specific syscalls {4:1} {7:1}
        # {0:1} {1:1} {9:1} -c "write|syscall:getpid"
        # {0:1} {1:1} {9:1} -c "write|syscall:open#test.out#1"

    - {5:1} and load specific library {4:1} {7:1}
        # {0:1} {1:1} {9:1} -c "write|load:/usr/lib/preload.so"

    - {5:1} and create a new thread {4:1} {7:1}
        # {0:1} {1:1} {9:1} -c "write|thread"

    - {5:1} and execute specific python function locally {4:1} {7:1}
        # {0:1} {1:1} {9:1} -c "write|pyscript:test.py:test_func"
        # {0:1} {1:1} {9:1} -c "write|pyscript:test.py:test_func:1:2:3"

    - {5:1} and execute specific python code remotely {4:1} {7:1}
        # {0:1} {1:1} {9:1} -c "write|pystr:print('OK')" -q {10:1}
        # {0:1} {1:1} {9:1} -c "write|pyfile:test.py:false" -q {10:1} -q STRPAGE

    - {5:1} and print accumulated stats for a specific argument {4:1} {7:1}
        # {0:1} {1:1} {9:1} -c "malloc|acc:CHUNK:0"

    - {5:1} and print distribution stats for a specific argument {4:1} {7:1}
        # {0:1} {1:1} {9:1} -c "malloc|dist:CHUNK:0"

    - {5:1} and print interval time between specific function calls {4:1} {7:1}
        # {0:1} {1:1} {9:1} -c "malloc|setinter:_exit, _exit"
        # {0:1} {1:1} {9:1} -c "malloc|setinter:_exit, _exit" -q ELAPSED:0.1

    - {5:1} and print interval stats for the function call {4:1} {7:1}
        # {0:1} {1:1} {9:1} -c "malloc|inter"
        # {0:1} {1:1} {9:1} -c "malloc|inter" -q ELPASED:0.001

    - {5:1} and jump to specific function with specific arguments {4:1} {7:1}
        # {0:1} {1:1} {9:1} -c "write|jump:sleep#5"

    - {3:1} and execute specific commands {4:1} {7:1}
        # {0:1} {1:1} {9:1} -c "*|exec:ls -lha;sleep 1"
        # {0:1} {1:1} {9:1} -c "*|exec:ls -lha &"
        # {0:1} {1:1} {9:1} -c "*|exec:kill -9 PID"
                """.format(
                        cmd,
                        mode,
                        cmdListStr,
                        "Trace all native calls",
                        "when specific calls detected",
                        "Trace specific native calls",
                        jitProfStr,
                        "for specific threads",
                        "from the specific binary",
                        "-g a.out",
                        "LIBPYTHON:/usr/lib/x86_64-linux-gnu/libpython3.8.so.1.0",
                        '-c "write|filter:',
                    ).rstrip()
                    + dbgExamStr
                )

                logCommonStr = """
Usage:
    # {0:1} {1:1} -I <MESSAGE>

Description:
    Log a message

Options:
    -v                          verbose
    -i  <INTERVAL(ms)>          set logging interval
    -I  <LOG>                   set log message
    -c  <LEVEL>                 set log level

Examples:
    - Log a message
        # {0:1} {1:1} "Hello World!"
        # {0:1} {1:1} -I "Hello World!"

    - Log a message 5 times
        # {0:1} {1:1} "Hello World!" -q REPEAT:5

    - Log a message 5 times with 1.5 sec interval
        # {0:1} {1:1} "Hello World!" -q REPEAT:5 -i 1500
                    """.format(
                    cmd, mode
                )

                printCommonStr = """
Usage:
    # {0:1} {1:1}

Description:
    Print logs in real-time

Options:
    -v                          verbose
    -g  <WORD|TID>              set filter
    -c  <LEVEL>                 set log level
    -I  <FILE|FIELD>            set path / field
    -J                          print in JSON format
    -R  <TIME>                  set timer
    -o  <DIR|FILE>              set output path
    -X  <REQ@IP:PORT>           set request address
    -Q                          print all rows in a stream
    -q  <NAME{{:VALUE}}>          set environment variables

Examples:
    - {2:1} in real-time
        # {0:1} {1:1}
        # {0:1} {1:1} -I test.log

    - {2:1} to the specific file
        # {0:1} {1:1} -o log.out
        # {0:1} {1:1} -I test.log -o log.out

    - {2:1} in real-time for 3 seconds
        # {0:1} {1:1} -R 3s
        # {0:1} {1:1} -I test.log -R 3s

    - {2:1} in real-time until no log
        # {0:1} {1:1} -Q

    - {2:1} generated since specific time in real-time
        # {0:1} {1:1} -q TAIL
        # {0:1} {1:1} -q TAIL:1235.123
        # {0:1} {1:1} -q TAIL:-3.0

    - {2:1} generated until specific time in real-time
        # {0:1} {1:1} -q UNTIL -Q
        # {0:1} {1:1} -q UNTIL:1235.123 -Q

    - {2:1} in JSON format
        # {0:1} {1:1} -J
        # {0:1} {1:1} -I test.log -J

    - {2:1} in real-time until a log containing a specific word is detected
        # {0:1} {1:1} -q WATCHLOG:"*oops*"

    - {2:1} in real-time until a log meeting regex is detected
        # {0:1} {1:1} -q WATCHLOG:"*oops*", REFILTER

    - {2:1} in real-time until a log containing a specific word is detected and execute specific commands when terminated
        # {0:1} {1:1} -q WATCHLOG:"*oops*", WATCHLOGCMD:"ls -lha"
                    """.format(
                    cmd, mode, "Print logs"
                )

                defUsage0 = """
Usage:
    # {0:1} {1:1} [OPTIONS] [--help]
                """.format(
                    cmd, mode
                )

                defUsage1 = """
Usage:
    # {0:1} {1:1} -g <TID|COMM> | <COMMAND> [OPTIONS] [--help]
                """.format(
                    cmd, mode
                )

                defUsage2 = """
Usage:
    # {0:1} {1:1} -g <PID|COMM> [OPTIONS] [--help]
                """.format(
                    cmd, mode
                )

                defUsage3 = """
Usage:
    # {0:1} {1:1} -g <TID|COMM> [OPTIONS] [--help]
                """.format(
                    cmd, mode
                )

                defUsage4 = """
Usage:
    # {0:1} {1:1} -g <TID|COMM> -c <COMMAND> [OPTIONS] [--help]
                """.format(
                    cmd, mode
                )

                defUsage5 = """
Usage:
    # {0:1} {1:1} <FILE> [OPTIONS] [--help]
                """.format(
                    cmd, mode
                )

                # description formatter #
                def _getDesc(s, t=0):
                    usages = [
                        defUsage0,
                        defUsage1,
                        defUsage2,
                        defUsage3,
                        defUsage4,
                        defUsage5,
                    ]
                    return "%s\nDescription:\n    %s\n" % (usages[t], s)

                # function record #
                if SysMgr.checkMode("funcrec"):
                    helpStr = (
                        _getDesc("Record function events on the system")
                        + """
Options:
  [collect]
    -e  <CHARACTER>             enable options
          [ a:all | b:block | c:cgroup | e:encode | g:graph
            h:heap | L:lock | m:mem | p:pipe | u:user ]
    -d  <CHARACTER>             disable options
          [ a:all | c:cpu | C:compress | e:encode
            g:general | l:latency | L:log ]
    -s  <DIR|FILE>              save trace data
    -f                          force execution
    -u                          run in the background
    -b  <SIZE:KB>               set buffer size
    -t  <SYSCALL>               trace syscall
    -B  <DIR|FILE>              set command script path
    -W  <SEC>                   wait for input
    -w  <TIME:FILE{:VALUE}>     set additional command
    -U  <NAME:FUNC|ADDR:FILE>   set user event
    -K  <NAME:FUNC|ADDR:ARGS>   set kernel event

  [report]
    -o  <DIR|FILE>              set output path
    -S  <CHARACTER>             sort by key
          [ c:cpu / m:memory / p:pid / b:block / w:wfc
            n:new / r:runtime /file ]
    -P                          group threads in a same process
    -O  <CORE>                  set core filter
    -r  <DIR>                   set root path
    -m  <ROWS:COLS:SYSTEM>      set terminal size

  [common]
    -a                          show all stats and events
    -C  <PATH>                  set config path
    -g  <COMM|TID{:FILE}>       set task filter
    -R  <INTERVAL:TIME:TERM>    set repeat count
    -Q                          print all rows in a stream
    -q  <NAME{:VALUE}>          set environment variables
    -A  <ARCH>                  set CPU type
    -c  <EVENT:COND>            set trace event
    -E  <DIR>                   set cache dir path
    -H  <LEVEL>                 set function depth level
    -k  <COMM|TID:SIG{:CONT}>   set signal
    -z  <COMM|TID:MASK{:CONT}>  set CPU affinity
    -Y  <VALUES>                set sched
          [ POLICY:PRIO|TIME{:TID|COMM:CONT}
            CLASS:{WHICH:}PRIO:TID|COMM ]
    -v                          verbose
                    """
                    )

                    helpStr += """
Examples:
    - {2:1} for all threads to guider.dat
        # {0:1} {1:1} -s .

    - {2:1} including user-level call-stacks for all threads to guider.dat
        # {0:1} {1:1} -s .

    - {2:1} for all threads to guider.dat for only 3 minutes
        # {0:1} {1:1} -s . -R 3m

    - {2:1} for all threads to guider.dat every 3 minutes continuously
        # {0:1} {1:1} -s . -R 3m:1:1

    - {2:1} for specific threads having TID bigger than 1024 to guider.dat in the background
        # {0:1} {1:1} -s . -g 1024\< -u

    - {2:1} including memory, block, heap for all threads to guider.dat
        # {0:1} {1:1} -s . -e m, b, h

    - {2:1} including all syscalls for all threads to guider.dat
        # {0:1} {1:1} -s . -t

    - {2:1} for all threads and save recording commands to specific script file
        # {0:1} {1:1} -B guider.cmd

    - {2:1} including specific trace points of all threads to guider.dat
        # {0:1} {1:1} -s . -c "irq/softirq_entry:vec==1"

    - {2:1} including segmentation fault for all threads to guider.dat in real-time
        # {0:1} {1:1} -s . -d c -K "segflt:bad_area" -e p

    - {2:1} including blocking for all threads to guider.dat
        # {0:1} {1:1} -s . -d c -K "block:schedule"

    - {2:1} for all threads to guider.dat and execute user commands
        # {0:1} {1:1} -s . -w BEFORE:/tmp/started:1, BEFORE:ls

    - {3:1} for all threads and print the result
        # {0:1} {1:1} -e g
        # {0:1} {1:1} -e g -R 3s
        # {0:1} {1:1} -e g -s -R 3s:9s

    - {3:1} for all threads and print the result only for specific cores
        # {0:1} {1:1} -e g -O 1, 2

    - {3:1} for all threads and report the result to guider.dat with the compression
        # {0:1} {1:1} -e g -s .

    - {3:1} for all threads and report the result to guider.dat without the compression
        # {0:1} {1:1} -e g -s . -d C

    - {3:1} for all threads and print the result to guider.out
        # {0:1} {1:1} -e g -o .

    - {3:1} for specific threads to guider.out
        # {0:1} {1:1} -e g -o . -g 1022, 1211
        # {0:1} {1:1} -e g -o . -g "thread*" -P

    - Record specific kernel function calls for all threads to guider.out
        # {0:1} {1:1} -e g -o . -c "mutex_*"

    - Record kernel function calls except for specific functions for all threads to guider.out
        # {0:1} {1:1} -e g -o . -q EXFUNCFILTER:"*_sys_*", EXFUNCFILTER:"mutex*"

    - {3:1} that took at least 100 us or longer for all threads to guider.out
        # {0:1} {1:1} -e g -o . -q ELAPSED:100

    - Report the results of analyzing the recorded data
        => See report command
                    """.format(
                        cmd,
                        mode,
                        "Record function events",
                        "Record all kernel function calls",
                    )

                # file record #
                elif SysMgr.checkMode("filerec"):
                    helpStr = (
                        _getDesc(
                            "Report all files loaded on the system memory"
                        )
                        + """
Options:
    -e  <CHARACTER>             enable options
          [ p:pipe | e:encode ]
    -d  <CHARACTER>             disable options
          [ e:encode | g:general ]
    -s  <DIR|FILE>              save trace data
    -S  <CHARACTER>             sort by key
          [ s:size / p:path ]
    -u                          run in the background
    -W  <SEC>                   wait for input
    -w  <TIME:FILE{:VALUE}>     set additional command
    -o  <DIR|FILE>              set output path
    -m  <ROWS:COLS:SYSTEM>      set terminal size
    -a                          show all stats and events
    -r                          set recursive traversal of directories
    -g  <COMM|TID{:FILE}>       set task filter
    -c  <PATH>                  set target path
    -Q                          print all rows in a stream
    -q  <NAME{:VALUE}>          set environment variables
    -E  <DIR>                   set cache dir path
    -v                          verbose
                    """
                    )

                    helpStr += """
Examples:
    - {2:1} of on-memory-mapped files for all processes to guider.out
        # {0:1} {1:1} -o . -a

    - {2:1} of on-memory-mapped files for specific threads
        # {0:1} {1:1} -g a.out

    - {2:1} of on-memory-mapped files without task info
        # {0:1} {1:1} -q NOTASKINFO

    - {2:1} of on-memory-mapped files without header info
        # {0:1} {1:1} -q NOHEADER

    - {2:1} of specific on-memory-mapped files for all processes
        # {0:1} {1:1} -q TARGETFILE:"*libc*"
        # {0:1} {1:1} -q EXCEPTFILE:"/usr*"

    - {2:1} of specific on-memory-mapped files for all processes and specific on-memory files
        # {0:1} {1:1} -c "/home/test/BIN, /home/work/DATA"
        # {0:1} {1:1} -c "/home/test/*"
        # {0:1} {1:1} -c "/home/test/**"
        # {0:1} {1:1} -c "/home/test/*", -q TARGETFILE:"*libc*"

    - {2:1} of specific on-memory files only excluding on-memory-mapped files
        # {0:1} {1:1} -c "/home/test/BIN", -q ONLYTARGET

    - {2:1} of specific on-memory files from specific directories recursively
        # {0:1} {1:1} -c "/usr/share" -r

    - {2:1} on each intervals of on-memory files for all processes to guider.out
        # {0:1} {1:1} -o . -i

    - {2:1} of on-memory-mapped files for all processes to guider.out using size filters
        # {0:1} {1:1} -o . -q SIZEFILTER:">100k"
        # {0:1} {1:1} -o . -q RESFILTER:">100m"
        # {0:1} {1:1} -o . -q PSSFILTER:"=4096"

    - {2:1} of on-memory files for all processes to guider.out and make the readahead list to readahead.list
        # {0:1} {1:1} {3:1}
        # {0:1} {1:1} -c "/**" {3:1}, TARGETFILE:"*.so*"
        # {0:1} {1:1} -c "/**" {3:1}, EXCEPTFILE:"*.pid"
        # {0:1} {1:1} {3:1}:/data/readahead2.list
        # {0:1} {1:1} {3:1}, RAMIN:4096
        # {0:1} {1:1} {3:1}, RAMERGE
        # {0:1} {1:1} {3:1}, RAALLOWLIST:allow.list
        # {0:1} {1:1} {3:1}, RADENYLIST:deny.list
        # {0:1} {1:1} {3:1}, RAADDLIST:add.list
                    """.format(
                        cmd, mode, "Report the analysis result", "{3:1}"
                    )

                # report #
                elif SysMgr.checkMode("report"):
                    helpStr = (
                        _getDesc(
                            "Report the results of analyzing the recorded data"
                        )
                        + """
Options:
    -e  <CHARACTER>             enable options
          [ p:pipe | e:encode ]
    -d  <CHARACTER>             disable options
          [ e:encode | g:general | l:latency | u:user ]
    -o  <DIR|FILE>              set output path
    -m  <ROWS:COLS:SYSTEM>      set terminal size
    -a                          show all stats and events
    -g  <COMM|TID{:FILE}>       set task filter
    -I  <DIR|FILE>              set input path
    -q  <NAME{:VALUE}>          set environment variables
    -E  <DIR>                   set cache dir path
    -v                          verbose
                    """
                    )

                    helpStr += """
Examples:
    - {3:1} based on guider.dat to guider.out
        # {0:1} {1:1}
        # {0:1} {1:1} -a

    - {3:1} based on trace.dat
        # {0:1} {1:1} trace.dat

    - {3:1} on a per-process basis
        # {0:1} {1:1} guider.dat -q PROCGROUP
        # {0:1} {1:1} guider.dat -q PROCGROUP -a

    - {3:1} based on guider.dat except for user-level call-stacks to guider.out
        # {0:1} {1:1} -d u

    - {3:1} based on guider.dat to guider.out and draw the flame graph
        # {0:1} {1:1} -q DRAWFLAME

    - {3:1} {2:1} having TID 1234 or COMM including a.out to guider.out
        # {0:1} {1:1} -o . -g "1234, a.out" -a

    - {3:1} excluding specific threads having COMM including a.out to guider.out
        # {0:1} {1:1} -o . -g "*" -q EXCEPTCOMM:"*a.out*" -a

    - {3:1} excluding specific threads having TID including 1234 to guider.out
        # {0:1} {1:1} -o . -g "*" -q EXCEPTTID:1234 -a

    - {3:1} based on guider.dat to guider.out with sched block time
        # {0:1} {1:1} -d l

    - {3:1} based on guider.dat to guider.out with higher time resolution
        # {0:1} {1:1} -q PRECISE

    - Convert the compressed recording data to the original one
        # {0:1} {1:1} guider.dat -s .

    - Report all the analysis result including interval information for all threads to guider.out
        # {0:1} {1:1} -o . -a -i

    - {3:1} including preemption info {2:1} to guider.out
        # {0:1} {1:1} -o . -p 1234, 4567

    - {3:1} {2:1} to guider.out within specific interval range in second unit
        # {0:1} {1:1} -o . -q TRIM:2:9
        # {0:1} {1:1} -o . -q TRIM::9
        # {0:1} {1:1} -o . -q TRIM:2:

    - {3:1} {2:1} to guider.out after converting all target inodes to paths from specific directory
        # {0:1} {1:1} -o . -q CONVINODE
        # {0:1} {1:1} -o . -q CONVINODE:/data

    - {3:1} {2:1} to guider.out and make the readahead list to readahead.list
        # {0:1} {1:1} {4:1}
        # {0:1} {1:1} {4:1}:/data/readahead2.list
        # {0:1} {1:1} {4:1}, CONVINODE:/data
        # {0:1} {1:1} {4:1}, EXCEPTFILE:"/mnt*"
        # {0:1} {1:1} {4:1}, RAMIN:4096
        # {0:1} {1:1} {4:1}, RAMERGE
        # {0:1} {1:1} {4:1}, TRIM:2:
        # {0:1} {1:1} {4:1}, RAALLOWLIST:allow.list
        # {0:1} {1:1} {4:1}, RADENYLIST:deny.list
        # {0:1} {1:1} {4:1}, RAADDLIST:add.list

    - {3:1} {2:1} and their siblings to guider.out
        # {0:1} {1:1} -o . -P -g 1234, 4567 -a

    - Report the function analysis result with maximum 3-depth {2:1} to guider.out
        # {0:1} {1:1} -o . -g 1234 -H 3
                    """.format(
                        cmd,
                        mode,
                        "for specific threads",
                        "Report the analysis result",
                        "-o . -q RALIST",
                    )

                # general record #
                elif SysMgr.checkMode("genrec"):
                    helpStr = (
                        _getDesc("Record system status")
                        + """
Options:
    -e  <CHARACTER>             enable options
          [ p:pipe | e:encode ]
    -d  <CHARACTER>             disable options
          [ e:encode | g:general ]
    -s  <DIR|FILE>              save trace data
    -u                          run in the background
    -W  <SEC>                   wait for input
    -o  <DIR|FILE>              set output path
    -m  <ROWS:COLS:SYSTEM>      set terminal size
    -Q                          print all rows in a stream
    -q  <NAME{:VALUE}>          set environment variables
    -R  <INTERVAL:TIME:TERM>    set repeat count
    -E  <DIR>                   set cache dir path
    -v                          verbose
                    """
                    )

                    helpStr += """
Examples:
    - Report the analysis result of system to guider.out
        # {0:1} {1:1} -o .

    - Report the analysis result of system to guider.out for 3 seconds
        # {0:1} {1:1} -o . -R 3s
                    """.format(
                        cmd, mode
                    )

                # record #
                elif (
                    SysMgr.checkMode("rec")
                    or SysMgr.checkMode("iorec")
                    or SysMgr.checkMode("sysrec")
                ):
                    # thread #
                    if SysMgr.checkMode("rec"):
                        desc = "Record thread events on the system"
                    # syscall #
                    elif SysMgr.checkMode("sysrec"):
                        desc = "Record syscall events on the system"
                    # I/O #
                    else:
                        desc = "Record I/O events on the system"

                    helpStr = (
                        _getDesc(desc)
                        + """
Options:
  [collect]
    -e  <CHARACTER>                      enable options
          [ b:block | B:binder | c:cgroup | d:disk
            e:encode | f:fs | g:graph | i:irq | I:i2c
            k:kvm | L:lock | m:mem | n:net | o:io_uring
            p:pipe | r:reset | P:power | w:workqueue ]
    -d  <CHARACTER>                      disable options
          [ a:all | c:cpu | C:compress | e:encode
            g:general ]
    -s  <DIR|FILE>                       save trace data
    -f                                   force execution
    -u                                   run in the background
    -W  <SEC>                            wait for input
    -b  <SIZE:KB>                        set buffer size
    -D                                   trace task dependency
    -t  <SYSCALL>                        trace syscall
    -B  <DIR|FILE>                       set command script path
    -w  <TIME:FILE{:VALUE}>              set additional command
    -U  <NAME:FUNC|ADDR:FILE|COMM|PID>   set user event
    -K  <NAME:FUNC|ADDR:ARGS>            set kernel event
    -R  <INTERVAL:TIME:TERM>             set repeat count
    -L  <PATH>                           set log file

  [report]
    -a                                   show all stats and events
    -o  <DIR|FILE>                       set output path
    -S  <CHARACTER>                      sort by key
          [ c:cpu / m:memory / p:pid / b:block
            w:wfc / n:new / r:runtime / f:file ]
    -P                                   group threads in a same process
    -p  <TID>                            show preemption info
    -O  <CORE>                           set core filter
    -L  <RES:PER>                        set graph layout (TOTAL PER: 6)
    -m  <ROWS:COLS:SYSTEM>               set terminal size
    -i  <SEC>                            set interval
    -Q                                   print all rows in a stream
    -q  <NAME{:VALUE}>                   set environment variables

  [common]
    -g  <COMM|TID{:FILE}>                set task filter
    -C  <PATH>                           set config path
    -A  <ARCH>                           set CPU type
    -c  <EVENT:COND>                     set trace event
    -E  <DIR>                            set cache dir path
    -k  <COMM|TID:SIG{:CONT}>            set signal
    -z  <COMM|TID:MASK{:CONT}>           set CPU affinity
    -Y  <VALUES>                         set sched
          [ POLICY:PRIO|TIME{:TID|COMM:CONT}
            CLASS:{WHICH:}PRIO:TID|COMM ]
    -v                                   verbose
                    """
                    )

                    helpStr += """
  * uprobe
	p[:[GRP/]EVENT] PATH:OFFSET [FETCHARGS] : Set a uprobe
	r[:[GRP/]EVENT] PATH:OFFSET [FETCHARGS] : Set a return uprobe (uretprobe)
	p[:[GRP/]EVENT] PATH:OFFSET%return [FETCHARGS] : Set a return uprobe (uretprobe)
	-:[GRP/]EVENT                           : Clear uprobe or uretprobe event

	GRP           : Group name. If omitted, "uprobes" is the default value.
	EVENT         : Event name. If omitted, the event name is generated based on PATH+OFFSET.
	PATH          : Path to an executable or a library.
	OFFSET        : Offset where the probe is inserted.
	OFFSET%return : Offset where the return probe is inserted.
	FETCHARGS     : Arguments. Each probe can have up to 128 args.
	 %REG         : Fetch register REG
	 @ADDR        : Fetch memory at ADDR (ADDR should be in userspace)
	 @+OFFSET     : Fetch memory at OFFSET (OFFSET from same file as PATH)
	 $stackN      : Fetch Nth entry of stack (N >= 0)
	 $stack       : Fetch stack address.
	 $retval      : Fetch return value.(\*1)
	 $comm        : Fetch current task comm.
	 +|-[u]OFFS(FETCHARG) : Fetch memory at FETCHARG +|- OFFS address.(\*2)(\*3)
	 \IMM         : Store an immediate value to the argument.
	 NAME=FETCHARG     : Set NAME as the argument name of FETCHARG.
	 FETCHARG:TYPE     : Set TYPE as the type of FETCHARG. Currently, basic types
						 (u8/u16/u32/u64/s8/s16/s32/s64), hexadecimal types
						 (x8/x16/x32/x64), "string" and bitfield are supported.
	(\*1) only for return probe.
	(\*2) this is useful for fetching a field of data structures.
	(\*3) Unlike kprobe event, "u" prefix will just be ignored, becuse uprobe
		  events can access only user-space memory.

  * kprobe
     p[:[GRP/][EVENT]] [MOD:]SYM[+offs]|MEMADDR [FETCHARGS]        : Set a probe
     r[MAXACTIVE][:[GRP/][EVENT]] [MOD:]SYM[+0] [FETCHARGS]        : Set a return probe
     p[:[GRP/][EVENT]] [MOD:]SYM[+0]%return [FETCHARGS]    : Set a return probe
     -:[GRP/][EVENT]                                               : Clear a probe

    GRP            : Group name. If omitted, use "kprobes" for it.
    EVENT          : Event name. If omitted, the event name is generated
                     based on SYM+offs or MEMADDR.
    MOD            : Module name which has given SYM.
    SYM[+offs]     : Symbol+offset where the probe is inserted.
    SYM%return     : Return address of the symbol
    MEMADDR        : Address where the probe is inserted.
    MAXACTIVE      : Maximum number of instances of the specified function that
                     can be probed simultaneously, or 0 for the default value
                     as defined in Documentation/trace/kprobes.rst section 1.3.1.
    FETCHARGS      : Arguments. Each probe can have up to 128 args.
     %REG          : Fetch register REG
     @ADDR         : Fetch memory at ADDR (ADDR should be in kernel)
     @SYM[+|-offs] : Fetch memory at SYM +|- offs (SYM should be a data symbol)
     $stackN       : Fetch Nth entry of stack (N >= 0)
     $stack        : Fetch stack address.
     $argN         : Fetch the Nth function argument. (N >= 1) (\*1)
     $retval       : Fetch return value.(\*2)
     $comm         : Fetch current task comm.
     +|-[u]OFFS(FETCHARG) : Fetch memory at FETCHARG +|- OFFS address.(\*3)(\*4)
     \IMM          : Store an immediate value to the argument.
     NAME=FETCHARG : Set NAME as the argument name of FETCHARG.
     FETCHARG:TYPE : Set TYPE as the type of FETCHARG. Currently, basic types
                     (u8/u16/u32/u64/s8/s16/s32/s64), hexadecimal types
                     (x8/x16/x32/x64), "string", "ustring" and bitfield
                     are supported.
     (\*1) only for the probe on function entry (offs == 0).
     (\*2) only for return probe.
     (\*3) this is useful for fetching a field of data structures.
     (\*4) "u" means user-space dereference. See :ref:`user_mem_access`.
                    """

                    helpStr += """
Examples:
    - {2:1} of all threads to guider.dat
        # {0:1} {1:1} -s .

    - {2:1} of specific threads that having TID bigger than 1234 to guider.dat
        # {0:1} {1:1} -s . -g ">1234"

    - {2:1} of all threads and their commands
        # {0:1} {1:1} -s . -B

    - {2:1} of all threads to guider.dat for only 3 seconds
        # {0:1} {1:1} -s . -R 3

    - {2:1} of all threads to guider.dat every 3 seconds continuously
        # {0:1} {1:1} -s . -R 3:1:1

    - {2:1} of all threads to guider.dat without flushing the ring buffer
        # {0:1} {1:1} -s . -q NOFLUSH

    - {2:1} of all threads to guider.dat with fastest initialization
        # {0:1} {1:1} -s . -q FASTINIT

    - Record specific events including memory, block, irq of all threads to guider.dat in the background
        # {0:1} {1:1} -s . -e m, b, i -u

    - {2:1} including specific syscalls of all threads to guider.dat
        # {0:1} {1:1} -s . -t sys_read, write
        # {0:1} {1:1} -s . -t "write*", "*64"

    - {2:1} including lock of all threads to guider.dat
        # {0:1} {1:1} -s . -e L

    - {2:1} including specific trace points of all threads to guider.dat
        # {0:1} {1:1} -s . -c sched, cgroup
        # {0:1} {1:1} -s . -c sched/sched_switch
        # {0:1} {1:1} -s . -c "irq/softirq_entry:vec==1"

    - Print tracepoint list
        # {0:1} {1:1} -l
        # {0:1} {1:1} -l -H -a

    - {2:1} with control for specific trace points of all threads to guider.dat
        # {0:1} {1:1} -s . -q ENABLETP:"irq/softirq*", ENABLETP:"sched"
        # {0:1} {1:1} -s . -q DISABLETP:"irq*"
        # {0:1} {1:1} -s . -q CLEARFILTER

    - {2:1} with trace options
        # {0:1} {1:1} -s . -q TRACEOPT:irq-info, TRACEOPT:hash-ptr

    - {2:1} including specific user functions of all threads to guider.dat
        # {0:1} {1:1} -s . -U "@SYM:func*:/tmp/a.out"
        # {0:1} {1:1} -s . -U "@SYM:func*:a.*"
        # {0:1} {1:1} -s . -U "evt1:func1:/tmp/a.out, evt2:0x1234:/tmp/b.out" -q OBJDUMP:/usr/bin/objdump
        # {0:1} {1:1} -s . -U "evt1:func*:/tmp/a.out:arg1=%ip arg2=%ax"
        # {0:1} {1:1} -s . -U "evt1:func1:/tmp/a.out:arg1=%ip:RET", "evt1:func2:/tmp/a.out:arg1=%ip:u64"

    - {2:1} including specific kernel functions of all threads to guider.dat
        # {0:1} {1:1} -s . -d c -K "evt1:func*"
        # {0:1} {1:1} -s . -d c -K "evt1:func1:u32, evt2:0x1234:s16, evt3:func2:x16"

    - {2:1} including specific kernel functions with args of all threads on x86 to guider.dat
        # {0:1} {1:1} -s . -d c -K "open:do_sys_open:dfd=%ax filename=%bx;u64 flags=%cx;s32 mode=+4(\$stack):NONE"

    - {2:1} including specific kernel functions with register values of all threads on x86 to guider.dat
        # {0:1} {1:1} -s . -d c -K "strace32:func1:%bp/u32.%sp/s64, strace:0x1234:\$stack:NONE"

    - {2:1} including specific kernel functions with the return value of all threads to guider.dat
        # {0:1} {1:1} -s . -d c -K "openfile:getname::*string, access:0x1234:NONE:*string"
        # {0:1} {1:1} -s . -d c -K "openfile:getname::**string, access:0x1234:NONE:*string"

    - {2:1} of all threads to guider.dat and execute user commands
        # {0:1} {1:1} -s . -w BEFORE:/tmp/started:1, BEFORE:ls

    - Report the results of analyzing the recorded data
        => See report command
                    """.format(
                        cmd, mode, "Record thread events"
                    )

                # file top #
                elif SysMgr.checkMode("ftop"):
                    fileTopStr = topCommonStr

                    helpStr = _getDesc("Monitor open files on the system")

                    examStr = """
Examples:
    - Monitor all processes sorted by the number of file descriptors
        # {0:1} {1:1}

    - Monitor open files, sockets, pipes for the specific process newly executed
        # {0:1} {1:1} -I ./a.out

    - Monitor open files, sockets, pipes for all processes
        # {0:1} {1:1} -a
        # {0:1} {1:1} -a -Q

    - Monitor open files meet size condition for all processes
        # {0:1} {1:1} -a -q BT:10M
        # {0:1} {1:1} -a -q LT:1M

    - Monitor specific processes sorted by the number of file descriptors
        # {0:1} {1:1} -g chrome -q ONLYPROC

    - Monitor open files, sockets, pipes for all processes and print about them in JSON format
        # {0:1} {1:1} -J
        # {0:1} {1:1} -J -Q
        # {0:1} {1:1} -a -J

    - Monitor open files, sockets, pipes for specific processes
        # {0:1} {1:1} "test"
        # {0:1} {1:1} "test*"
        # {0:1} {1:1} "*test"

    - Monitor open files, sockets, pipes including null for all processes
        # {0:1} {1:1} -g :"*null*"

    - Monitor open files, sockets, pipes including 8000 for the process having COMM test
        # {0:1} {1:1} -g test:"*8000*"

    - Monitor open files, sockets, pipes of specific processes including system
        # {0:1} {1:1} -g system

    - Monitor open files, sockets, pipes for all processes with cmdline
        # {0:1} {1:1} -a -e L
        # {0:1} {1:1} -a -e L -g apps

    - Report analysis result of open files, sockets, pipes to guider.out
        # {0:1} {1:1} -o .
        # {0:1} {1:1} -a -o .

    See the top COMMAND help for more examples.
                    """.format(
                        cmd, mode
                    )

                    helpStr += fileTopStr + examStr

                # thread top #
                elif SysMgr.checkMode("ttop"):
                    helpStr = _getDesc("Monitor the status of threads")
                    helpStr += topSubStr + topCommonStr + topExamStr

                # cgroup top #
                elif SysMgr.checkMode("cgtop"):
                    helpStr = _getDesc(
                        "Monitor the status of cgroup on the system"
                    )
                    helpStr += topSubStr + topCommonStr + topExamStr

                    helpStr += """
    - {2:1} with maximum 3-depth
        # {0:1} {1:1} -H 3

    - {2:1} with info of the representative task in each group
        # {0:1} {1:1} -q REPCOMM
        # {0:1} {1:1} -q REPCMDLINE

    - {2:1} except for specific resources
        # {0:1} {1:1} -d cmb

    - {2:1} except for pressure info
        # {0:1} {1:1} -q NOPRESSURE

    - {2:1} except for no task group
        # {0:1} {1:1} -a -q SKIPNOTASK

    - {2:1} after mounting/unmounting all cgroup subsystems
        # {0:1} {1:1} -q MOUNTCG
        # {0:1} {1:1} -q UMOUNTCG

    - {2:1} with specific length for group name
        # {0:1} {1:1} -q NAMELEN:200
                        """.format(
                        cmd, mode, "Monitor the status of cgroup on the system"
                    )

                # vmalloc top #
                elif SysMgr.checkMode("vtop"):
                    helpStr = _getDesc(
                        "Monitor memory objects allocated using vmalloc on the system"
                    )

                    examStr = """
Examples:
    - Monitor the status of vmalloc
        # {0:1} {1:1}

    - Monitor the status of vmalloc using filter
        # {0:1} {1:1} -g fork

    See the top COMMAND help for more examples.
                    """.format(
                        cmd, mode
                    )

                    helpStr += topSubStr + topCommonStr + examStr

                # slab top #
                elif SysMgr.checkMode("slabtop"):
                    helpStr = (
                        _getDesc("Monitor slab objects on the system")
                        + """
Sort:
    size:    the size of an object
    active:  the number of active objects
    actsize: the size of active objects
    total:   the size of all objects
                        """
                    )

                    examStr = """
Examples:
    - {2:1}
        # {0:1} {1:1}

    - {2:1} using type filter
        # {0:1} {1:1} -g dentry

    - {2:1} sorted by specific value
        # {0:1} {1:1} -S size
        # {0:1} {1:1} -S active
        # {0:1} {1:1} -S actsize
        # {0:1} {1:1} -S total

    See the top COMMAND help for more examples.
                    """.format(
                        cmd, mode, "Monitor slab objects"
                    )

                    helpStr += topSubStr + topCommonStr + examStr

                # syscall top #
                elif SysMgr.checkMode("systop"):
                    helpStr = _getDesc(
                        "Monitor syscalls for specific threads", t=1
                    )

                    examStr = """
Examples:
    - {2:1} {3:1}
        # {0:1} {1:1} -g a.out

    - {2:1} with stdev and histogram for elapsed time {3:1}
        # {0:1} {1:1} -g a.out -q STDEV, PRINTHIST

    - {2:1} {3:1} with colorful elapsed time exceeds 0.1 seconds
        # {0:1} {1:1} -g a.out -q ELAPSED:0.1

    - Monitor only successful syscalls {3:1}
        # {0:1} {1:1} -g a.out -q ONLYOK

    - {2:1} for child tasks created by a specific thread
        # {0:1} {1:1} -g a.out -q WAITCLONE

    - Monitor only failed syscalls {3:1}
        # {0:1} {1:1} -g a.out -q ONLYFAIL

    - {2:1} and report the result to guider.out when SIGINT arrives
        # {0:1} {1:1} -o .

    - {2:1} with breakpoint for read syscalls {3:1}
        # {0:1} {1:1} -g 1234 -c read
        # {0:1} {1:1} -g 1234 -c read -a

    See the top COMMAND help for more examples.
                    """.format(
                        cmd,
                        mode,
                        "Monitor syscalls",
                        "for specific threads",
                    )

                    helpStr += (
                        topSubStr
                        + topCommonStr
                        + examStr.rstrip()
                        + dbgExamStr
                    )

                # python top #
                elif SysMgr.checkMode("pytop"):
                    helpStr = _getDesc(
                        "Monitor python calls for specific threads (support up to Python 3.11)",
                        t=1,
                    )

                    examStr = (
                        """
Examples:
    - {2:1} {3:1} using 100 us sampling
        # {0:1} {1:1} -g iotop

    - {2:1} {3:1} using 100 us sampling {4:1}
        # {0:1} {1:1} iotop
        # {0:1} {1:1} iotop -f

    - {2:1} with backtrace {3:1} {5:1}
        # {0:1} {1:1} -g iotop -H

    - {2:1} with backtrace including native symbols {3:1} {5:1}
        # {0:1} {1:1} -g iotop -H -q INCNATIVE

    - {2:1} {3:1} with printing pycalls in backtrace using a fixed interpreter address (only for single-threaded process)
        # {0:1} {1:1} -g iotop -H -q FIXPYSTATE

    - {2:1} {3:1} every 2 second for 1 minute with 1ms sampling
        # {0:1} {1:1} -g 1234 -T 1ms -i 2 -R 1m

    - {2:1} {4:1} and print standard output for child tasks
        # {0:1} {1:1} iotop -q UNMUTE
        # {0:1} {1:1} "python -c \"while 1: print('OK')\""

    - {2:1} with breakpoint for specific functions {3:1}
        # {0:1} {1:1} -g 1234 -c peace
        # {0:1} {1:1} -g 1234 -c peace -a

    See the top COMMAND help for more examples.
                    """.format(
                            cmd,
                            mode,
                            "Monitor python calls",
                            "for specific threads",
                            "from the specific binary",
                            "(merged native stack + python stack from python 3.7)",
                        ).rstrip()
                        + dbgExamStr
                    )

                    helpStr += (
                        topSubStr
                        + topCommonStr.replace(
                            "-T  <PROC>                  set process number",
                            "-T  <TIME>                  set sample rate",
                        )
                        + examStr
                    )

                # kernel function top #
                elif SysMgr.checkMode("kftop"):
                    helpStr = _getDesc("Monitor kernel function calls", t=1)

                    helpStr += """
Options:
    -e  <CHARACTER>             enable options
          [ p:pipe | e:encode ]
    -o  <DIR|FILE>              set output path
    -m  <ROWS:COLS:SYSTEM>      set terminal size
    -a                          show all functions
    -g  <FUNC>                  set function filter
    -c  <CORE>                  set core filter
    -Q                          print all rows in a stream
    -q  <NAME{:VALUE}>          set environment variables
    -E  <DIR>                   set cache dir path
    -v                          verbose
                    """

                    helpStr += """
Examples:
    - {2:1}
        # {0:1} {1:1}

    - {2:1} with function filter
        # {0:1} {1:1} -g "*write*, sched*"

    - {2:1} only for specific core
        # {0:1} {1:1} -c 0,1,2

    - {2:1} with per-core classification
        # {0:1} {1:1} -q PERCORE

    - {2:1} after sorting
        # {0:1} {1:1} -q SORT:hit
        # {0:1} {1:1} -q SORT:total
        # {0:1} {1:1} -q SORT:average
                    """.format(
                        cmd,
                        mode,
                        "Monitor kernel function calls",
                    )

                # kernel top #
                elif SysMgr.checkMode("ktop"):
                    helpStr = _getDesc(
                        "Monitor kernel function calls for specific threads",
                        t=1,
                    )

                    examStr = """
Examples:
    - {2:1} {3:1}
        # {0:1} {1:1} -g a.out

    - {2:1} {4:1}
        # {0:1} {1:1} a.out
        # {0:1} {1:1} "sh -c \\"while [ 1 ]; do echo "OK"; done;\\""
        # {0:1} {1:1} -I a.out

    - {2:1} and report the result to guider.out when SIGINT arrives
        # {0:1} {1:1} -o .

    - {2:1} {3:1} {4:1}
        # {0:1} {1:1} a.out -g a.out
        # {0:1} {1:1} -I a.out -g a.out

    - {2:1} {3:1} every 2 second for 1 minute with 1ms sampling
        # {0:1} {1:1} -g 1234 -T 1ms -i 2 -R 1m

    See the top COMMAND help for more examples.
                    """.format(
                        cmd,
                        mode,
                        "Monitor kernel function calls",
                        "for specific threads",
                        "from the specific binary",
                    )

                    helpStr += (
                        topSubStr
                        + topCommonStr.replace(
                            "-T  <PROC>                  set process number",
                            "-T  <TIME>                  set sample rate",
                        )
                        + examStr.rstrip()
                        + dbgExamStr
                    )

                # usercall top #
                elif SysMgr.checkMode("utop"):
                    helpStr = _getDesc(
                        "Monitor native function calls for specific threads",
                        t=1,
                    )

                    examStr = (
                        """
Examples:
    - {3:1} {4:1}
        # {0:1} {1:1} -g a.out

    - {3:1} {5:1}
        # {0:1} {1:1} a.out
        # {0:1} {1:1} "sh -c \\"while [ 1 ]; do echo "OK"; done;\\""
        # {0:1} {1:1} -I a.out

    - {3:1} {4:1} from specific runtime
        # {0:1} {1:1} -g "*chrome, *test*" -q TARGETEXE
        # {0:1} {1:1} -g "*chrome, *test*" -q TARGETCMD

    - Monitor native and JIT-compiled function calls {4:1}
{2:1}
        # {0:1} {1:1} -g node -q JITSYM
        # {0:1} {1:1} -g java -q JITSYM

    - {3:1} and report the result to guider.out when SIGINT arrives
        # {0:1} {1:1} -o .
        # {0:1} {1:1} -o . -q FORCESUMMARY

    - {3:1} without using incremental sampling rate {4:1}
        # {0:1} {1:1} -g a.out -q FIXSAMPLING

    - {3:1} {4:1} {5:1}
        # {0:1} {1:1} a.out -g a.out
        # {0:1} {1:1} -I a.out -g a.out

    - {3:1} {4:1} even if the master tracer is terminated {5:1}
        # {0:1} {1:1} a.out -g a.out -q CONTALONE

    - {3:1} {4:1} after the target task awakened
        # {0:1} {1:1} -g a.out -q WAITWAKEUP

    - {3:1} with backtrace (no-use-libcorkscrew) {4:1}
        # {0:1} {1:1} -g a.out -H -q NOLIBCORK

    - {3:1} {4:1} every 2 second for 1 minute with 1ms sampling
        # {0:1} {1:1} -g 1234 -T 1ms -i 2 -R 1m

    - {3:1} with breakpoint for specific functions {4:1}
        # {0:1} {1:1} -g a.out -c peace
        # {0:1} {1:1} -g a.out -c peace -a

    See the top COMMAND help for more examples.
                    """.format(
                            cmd,
                            mode,
                            jitProfStr,
                            "Monitor native function calls",
                            "for specific threads",
                            "from the specific binary",
                        ).rstrip()
                        + dbgExamStr
                    )

                    helpStr += (
                        topSubStr
                        + topCommonStr.replace(
                            "-T  <PROC>                  set process number",
                            "-T  <TIME>                  set sample rate",
                        )
                        + examStr
                    )

                # break top #
                elif SysMgr.checkMode("btop"):
                    helpStr = _getDesc(
                        "Monitor function calls for specific threads", t=1
                    )

                    examStr = """
    {0:1}

    See the top COMMAND help for more examples.
                    """.format(
                        brkExamStr
                    )

                    helpStr += topSubStr + topCommonStr + examStr

                # all top #
                elif SysMgr.checkMode("atop"):
                    helpStr = _getDesc("Monitor all resources on the system")

                    examStr = """
Examples:
    - Monitor system resource
        # {0:1} {1:1}

    See the top COMMAND help for more examples.
                    """.format(
                        cmd, mode
                    )

                    helpStr += topCommonStr + examStr

                # tree top #
                elif SysMgr.checkMode("trtop"):
                    helpStr = _getDesc(
                        "Monitor tasks on the system in a tree structure"
                    )

                    examStr = """
Examples:
    - {2:1}
        # {0:1} {1:1}

    - Monitor specific processes and their childs in a tree structure
        # {0:1} {1:1} "a.out"

    - Monitor all threads in a tree structure
        # {0:1} {1:1} -e t

    - {2:1} and highlight specific processes
        # {0:1} {1:1} -g a.out

    - {2:1} and highlight specific processes after cutting childs
        # {0:1} {1:1} -g a.out -q CUTCHILD

    - {2:1} with depth limitation
        # {0:1} {1:1} -H 3

    - {2:1} with I/O stats
        # {0:1} {1:1} -q ADDIO

    - {2:1} without memory stats
        # {0:1} {1:1} -q NOMEM

    - {2:1} with cmdline
        # {0:1} {1:1} -q CMDLINE
        # {0:1} {1:1} -q CMDLINE:50

    See the top COMMAND help for more examples.
                    """.format(
                        cmd,
                        mode,
                        "Monitor all processes in a tree structure",
                    )

                    helpStr += topSubStr + topCommonStr + examStr

                # condition top #
                elif SysMgr.checkMode("ctop"):
                    helpStr = _getDesc(
                        "Monitor resources with threshold on the system"
                    )

                    examStr = """
Examples:
    - {2:1} {3:1}
        # {0:1} {1:1}
        # {0:1} {1:1} -C /tmp/guider.conf

    - {2:1} {3:1} and report to specific file without pid suffix in the file name
        # {0:1} {1:1} -q NOPIDSUFFIX

    - {2:1} {3:1} and report in JSON format every interval
        # {0:1} {1:1} -j -Q -q TEXTREPORT
        # {0:1} {1:1} -j -o /tmp -q TEXTREPORT, NOPROCRANK
        # {0:1} {1:1} -j -o /tmp -q TEXTREPORT -C /tmp/guider.conf

    - {2:1} {3:1} and report in JSON format with contents of specific files or directories every interval
        # {0:1} {1:1} -j -Q -q TEXTREPORT, RECFILE:/tmp/setting1, RECFILE:/tmp/setting2
        # {0:1} {1:1} -j -Q -q RECFILE:/tmp/setting1, RECFILESIZE:1024
        # {0:1} {1:1} -j -Q -q RECFILE:/tmp/setting1, RECFILESIZE:-1024

    - {2:1} {3:1} and report to specific file with contents of specific files
        # {0:1} {1:1} -j -Q -q TEXTREPORT, DUMPFILE:/tmp/OUT, DUMPFILE:/tmp/OUT2
        # {0:1} {1:1} -j -Q -q DUMPFILE:/tmp/OUT, DUMPFILESIZE:1024
        # {0:1} {1:1} -j -Q -q DUMPFILE:/tmp/OUT, DUMPFILESIZE:-1024

    - {2:1} {3:1} and report the compressed monitoring results to a specific file in the specific directory,
      and if the directory size exceeds the limit then remove old report files first.
        # {0:1} {1:1} -o /tmp -e C -b 1M -q TEXTREPORT, LIMITREPDIR:10M

    - {2:1} {3:1} and report the monitoring results to a specific file in the specific directory,
      and if the size or the count of report directories exceeds the limit then remove existing files.
        # {0:1} {1:1} -o /tmp -b 1M -q TEXTREPORT, LIMITREPDIR:10M, REMOVEOTHERS
        # {0:1} {1:1} -o /tmp -b 1M -q TEXTREPORT, LIMITDIR:/var/log/guider:100M
        # {0:1} {1:1} -o /tmp -b 1M -q TEXTREPORT, LIMITDIRCNT:/var/log/guider:10

    - {2:1} {3:1} using UTC time
        # {0:1} {1:1} -g a.out -q UTCTIME

    - {2:1} {3:1} with hostname
        # {0:1} {1:1} -q HOSTNAME:LINUX5

    - {2:1} {3:1} after a specific uptime condition is satisfied
        # {0:1} {1:1} -q THRUPTIMECOND:1m

    - {2:1} {3:1} and ignore all requests of clients
        # {0:1} {1:1} -q IGNREQ

    - {2:1} {3:1} excluding specific resources to conserve monitoring resources
        # {0:1} {1:1} -d cmb

    - {2:1} {3:1} and the fixed specific targets
        # {0:1} {1:1} -g a.out -q FIXTASK:"a.out"
        # {0:1} {1:1} -g a.out -q FIXTASK:"a.out|yes|systemd"

    - {2:1} {3:1} and cgroup usage
        # {0:1} {1:1} -q PRINTCG
        # {0:1} {1:1} -q PRINTCG:"cpu+cpuacct+memory+blkio"
        # {0:1} {1:1} -q PRINTCG:"all"
        # {0:1} {1:1} -q PRINTCG, MAXCGROWS:5
        # {0:1} {1:1} -q PRINTCG, NOPRESSURE

    - {2:1} {3:1} and cgroup usage only for specific groups
        # {0:1} {1:1} -q PRINTCG, CGFILTER:"*user.slice"

    - {2:1} {3:1} and cgroup usage in limited depth
        # {0:1} {1:1} -q PRINTCG -H 2

    - {2:1} including normal tasks {3:1}
        # {0:1} {1:1} -j -q SAVEJSONSTAT

    - {2:1} {3:1} and execute specific commands to save also their output when terminated
        # {0:1} {1:1} -a -o -q PRINTCMD:"DLT#GUIDER printdlt -a -q TAIL:-3\, UNTIL -d L -Q", DLTTIME
        # {0:1} {1:1} -a -o -q PRINTCMD:"KERNEL#GUIDER printkmsg -a -q TAIL:-3\, UNTIL -d L -Q"

    - {2:1} without threshold condition until CMD_RELOAD event is received
        # {0:1} {1:1} -q NOTHRESHOLD

    - {2:1} after updating original threshold data from specific files
        # {0:1} {1:1} -q UPDATETHRESHOLD:guider2.conf, UPDATETHRESHOLD:guider3.conf

    - {2:1} {3:1} after applying the cpu limitation in % unit using cgroup
        # {0:1} {1:1} -q LIMITCPU:20
        # {0:1} {1:1} -q LIMITCPU:20@"*yes*|a.out"
        # {0:1} {1:1} -q LIMITCPU:cfs_quota_us:20000+cfs_period_us:100000@"*yes*|a.out"

    - {2:1} {3:1} after applying the memory limitation using cgroup
        # {0:1} {1:1} -q LIMITMEM:50M
        # {0:1} {1:1} -q LIMITMEM:50M@"*yes*|a.out"

    - {2:1} {3:1} after applying the block I/O limitation using cgroup
        # {0:1} {1:1} -q LIMITREAD:50M
        # {0:1} {1:1} -q LIMITWRITE:50M
        # {0:1} {1:1} -q LIMITREAD:50M@"*yes*|a.out"
        # {0:1} {1:1} -q LIMITWRITE:50M@"*yes*|a.out"
        # {0:1} {1:1} -q LIMITWRITE:50M@"*yes*|a.out", EACHTASK

    - {2:1} {3:1} and print the output of child tasks
        # {0:1} {1:1} -q UNMUTE

    - {2:1} without socket profiling
        # {0:1} {1:1} -q NOSOCKPROF

    - {2:1} without specific file report contents
        # {0:1} {1:1} -J -q NOSTORAGEREP, NOGPUMEMREP

    - {2:1} {3:1} after applying all threhsolds forcefully
        # {0:1} {1:1} -q APPLYALL -f

    - {2:1} with optimized options
        # {0:1} {1:1} -j /tmp/perf.report -i 3 -o /data -q TEXTREPORT, NOSOCKPROF, LIMITREPDIR:100M, LIMITDIRCNT:/data/mini:1, NOPROCRANK, NOSTORAGEREP, NOGPUMEMREP -b 3M -e dC

    See the top COMMAND help for more examples.
                    """.format(
                        cmd,
                        mode,
                        "Monitor resources",
                        "using threshold condition",
                    )

                    helpStr += topSubStr + topCommonStr + examStr

                # stack top #
                elif SysMgr.checkMode("stacktop"):
                    helpStr = _getDesc(
                        "Monitor kernel stacks for specific threads", t=3
                    )

                    examStr = """
Examples:
    - Monitor kernel stacks for specific threads
        # {0:1} {1:1} -g chrome

    See the top COMMAND help for more examples.
                    """.format(
                        cmd, mode
                    )

                    helpStr += topSubStr + topCommonStr + examStr

                # perf top #
                elif SysMgr.checkMode("ptop"):
                    helpStr = _getDesc(
                        "Monitor performance stats using PMU(Performance Monitoring Unit)",
                        t=3,
                    )

                    examStr = """
Examples:
    - {2:1} system
        # {0:1} {1:1}

    - {2:1} specific threads
        # {0:1} {1:1} -g chrome

    - {2:1} specific threads after setting perf values
        # {0:1} {1:1} -g chrome -q perf_event_max_sample_rate:3000
        # {0:1} {1:1} -g chrome -q perf_event_max_stack:100
        # {0:1} {1:1} -g chrome -q perf_event_max_contexts_per_stack:100

    See the top COMMAND help for more examples.
                    """.format(
                        cmd, mode, "Monitor performance stats of"
                    )

                    helpStr += topSubStr + topCommonStr + examStr

                # mem top #
                elif SysMgr.checkMode("mtop"):
                    helpStr = _getDesc("Monitor memory details on the system")

                    vmflagStr = """
Vmflags:
    {0:1}
                    """.format(
                        SysMgr.getVmFlags(retStr=True)
                    )

                    examStr = """
Examples:
    - {2:1} for specific processes
        # {0:1} {1:1} -g chrome

    - {2:1} without system memory info for all processes
        # {0:1} {1:1} -q NOSYSMEM

    - {2:1} with only specific memory info
        # {0:1} {1:1} -q ONLYSYSMEM
        # {0:1} {1:1} -q ONLYSLAB
        # {0:1} {1:1} -q ONLYBUDDY
        # {0:1} {1:1} -q ONLYKSM
        # {0:1} {1:1} -q ONLYVM
        # {0:1} {1:1} -q ONLYZONE
        # {0:1} {1:1} -q LMKINFO
        # {0:1} {1:1} -q DMABUF

    - {2:1} without smaps info for specific processes
        # {0:1} {1:1} -g chrome -q NOSMAPS

    - {2:1} including 30 lines of active slabs for specific processes
        # {0:1} {1:1} -g chrome -q NRSLAB:30

    - {2:1} including total slabs for specific processes
        # {0:1} {1:1} -g chrome -q TOTALSLAB

    - {2:1} with specific vmflags (refer to FLAG line)
        # {0:1} {1:1} {3:1}MERGE
        # {0:1} {1:1} {3:1}MERGE, ONLYVMFLAG, ONLYVMFLAGTASK
        # {0:1} {1:1} {3:1}"HUGETLB+HUGEPAGE"

    See the top COMMAND help for more examples.
                    """.format(
                        cmd,
                        mode,
                        "Monitor memory details",
                        '-g "*" -q VMFLAG:',
                    )

                    helpStr += topSubStr + topCommonStr + vmflagStr + examStr

                # wss top #
                elif SysMgr.checkMode("wtop"):
                    helpStr = _getDesc(
                        "Monitor WSS(Working Set Size) for specific processes",
                        t=2,
                    )

                    examStr = """
Examples:
    - {2:1} and reset WSS when SIGQUIT(Ctrl+\) arrives
        # {0:1} {1:1} chrome

    - {2:1} with all footprints
        # {0:1} {1:1} chrome -a

    - {2:1} with system memory stats
        # {0:1} {1:1} chrome -q SYSMEM

    See the top COMMAND help for more examples.
                    """.format(
                        cmd,
                        mode,
                        "Monitor WSS(Working Set Size) of specific processes",
                    )

                    helpStr += topSubStr + topCommonStr + examStr

                # report top #
                elif SysMgr.checkMode("rtop"):
                    helpStr = _getDesc(
                        "Report all resources on the system in JSON format"
                    )

                    examStr = """
Examples:
    - {2:1} /tmp/guider.report
        # {0:1} {1:1}

    - {2:1} console standard output
        # {0:1} {1:1} -Q

    - {2:1} /tmp/guider.report with maximum 20 task items
        # {0:1} {1:1} -q NRTOPRANK:20

    - {2:1} /tmp/guider.report with contents of specific files or directories
        # {0:1} {1:1} {3:1} RECFILE:/tmp/setting2
        # {0:1} {1:1} {3:1} RECFILESIZE:1024
        # {0:1} {1:1} {3:1} RECFILETAIL:1024

    - Report system status including threshold events in JSON format to /tmp/guider.report
        # {0:1} {1:1} -C ./guider.conf
        # {0:1} {1:1} -C ./guider.conf -e l

    - {2:1} ./guider.report in the background every second
        # {0:1} {1:1} -j . -u

    - Report system status with the return value of external functions in JSON format to /tmp/guider.report
        # {0:1} {1:1} -q REPORTFUNC:test.py:test

    - Stop reporting in the background
        # {0:1} stop

    See the top COMMAND help for more examples.
                    """.format(
                        cmd,
                        mode,
                        "Report system status in JSON format to",
                        "-q RECFILE:/tmp/setting1,",
                    )

                    helpStr += topSubStr + topCommonStr + examStr

                # background top #
                elif SysMgr.checkMode("bgtop"):
                    helpStr = _getDesc(
                        "Collect system status in the background until get SIGSTOP"
                    )

                    examStr = """
Examples:
    - Collect system status to guider.out in the background
        # {0:1} {1:1} -o .

    - Stop collecting processes in the background and let them report system analysis result
        # {0:1} stop

    See the top COMMAND help for more examples.
                    """.format(
                        cmd, mode
                    )

                    helpStr += topSubStr + topCommonStr + examStr

                # irq top #
                elif SysMgr.checkMode("irqtop"):
                    helpStr = _getDesc(
                        "Monitor the status of irqs on the system"
                    )

                    examStr = """
Examples:
    - Monitor the status of all irqs
        # {0:1} {1:1}

    See the top COMMAND help for more examples.
                    """.format(
                        cmd, mode
                    )

                    helpStr += topSubStr + topCommonStr + examStr

                # disk top #
                elif SysMgr.checkMode("disktop"):
                    helpStr = _getDesc(
                        "Monitor the status of storage devices on the system"
                    )

                    examStr = """
Examples:
    - {2:1}
        # {0:1} {1:1}

    - {2:1} and print only busy devices
        # {0:1} {1:1} -q ONLYBUSY

    - {2:1} and print total info including tmpfs-mounted devices
        # {0:1} {1:1} -q ADDTMPFS

    - {2:1} with fs filter
        # {0:1} {1:1} -q INFSLIST:"ext4", INFSLIST:"f2fs"
        # {0:1} {1:1} -q EXFSLIST:"tmpfs"

    - {2:1} with full mount info
        # {0:1} {1:1} -q NOCUT

    See the top COMMAND help for more examples.
                    """.format(
                        cmd,
                        mode,
                        "Monitor the status of all storage devices",
                    )

                    helpStr += topSubStr + topCommonStr + examStr

                # DLT top #
                elif SysMgr.checkMode("dlttop"):
                    helpStr = _getDesc("Monitor DLT logs")

                    examStr = """
Examples:
    - {2:1}
        # {0:1} {1:1}

    - {2:1} including all in the past
        # {0:1} {1:1} -a

    - {2:1} using libdlt.so from specific path
        # {0:1} {1:1} -q LIBDLT:/home/iipeace/test/libdlt.so

    - Change default log level to be printed
        # {0:1} {1:1} -c INFO

    - {2:1} including specific string
        # {0:1} {1:1} -g test

    - {2:1} from specific address for dlt-daemon
        # {0:1} {1:1} -X localhost:12345
        # {0:1} {1:1} -X 127.0.0.1:12345

    - {2:1} with connection retry to dlt-daemon every 1,000 ms
        # {0:1} {1:1} -q RETRYCONN:1000

    - {2:1} generated since specific time
        # {0:1} {1:1} -q TAIL
        # {0:1} {1:1} -q TAIL:1235.123

    - {2:1} generated until specific time
        # {0:1} {1:1} -q UNTIL

    See the top COMMAND help for more examples.
                    """.format(
                        cmd, mode, "Monitor DLT logs"
                    )

                    helpStr += topSubStr + topCommonStr + examStr

                # dbus top #
                elif SysMgr.checkMode("dbustop"):
                    helpStr = _getDesc("Monitor D-Bus messages")

                    examStr = """
Examples:
    - {2:1}
        # {0:1} {1:1}

    - {2:1} including BEGIN messages such like org.freedesktop.DBus.Hello
        # {0:1} {1:1} -a

    - {2:1} including incoming messages
        # {0:1} {1:1} -a -q INCINMSG

    - {2:1} including D-Bus interfaces
        # {0:1} {1:1} -g dbus-daemon

    - {2:1} only for specific processes
        # {0:1} {1:1} -g dbusExam
        # {0:1} {1:1} -g dbusExam -f

    - {2:1} only for a new processes
        # {0:1} {1:1} dbusExam
        # {0:1} {1:1} dbusExam -f

    - {2:1} including specific word
        # {0:1} {1:1} -c test

    - {2:1} and erase specific words in live messages
        # {0:1} {1:1} -G sendData

    - {2:1} for a specific session bus
        # DBUS_SESSION_BUS_ADDRESS=$(cat addr) {0:1} {1:1}

    See the top COMMAND help for more examples.
                    """.format(
                        cmd, mode, "Monitor D-Bus messages"
                    )

                    helpStr += topSubStr + topCommonStr + examStr

                # network top #
                elif SysMgr.checkMode("ntop"):
                    helpStr = _getDesc(
                        "Monitor the status of network devices on the system"
                    )

                    examStr = """
Examples:
    - Monitor the status of all network devices
        # {0:1} {1:1}

    See the top COMMAND help for more examples.
                    """.format(
                        cmd, mode
                    )

                    helpStr += topSubStr + topCommonStr + examStr

                # process top #
                elif SysMgr.isTopMode() and not SysMgr.checkMode("fetop"):
                    helpStr = _getDesc("Monitor the status of processes")

                    helpStr += topSubStr + topCommonStr + topExamStr

                # mount #
                elif SysMgr.checkMode("mount"):
                    helpStr = """
Usage:
    # {0:1} {1:1} -I <DEV:DIR:FS:FLAGS:DATA> [OPTIONS] [--help]

Description:
    Mount filesystem
                        """.format(
                        cmd, mode
                    )

                    helpStr += """
Options:
    -I  <DEV:DIR:FS:FLAGS:DATA> set mount point
    -f                          force execution
    -m  <ROWS:COLS:SYSTEM>      set terminal size
    -v                          verbose

Flags:
    See the man page
                    """

                    helpStr += """
Examples:
    - Mount filesystem
        # {0:1} {1:1} "/dev/sda1:/data/fs:ext4"
        # {0:1} {1:1} -I "/dev/sda1:/data/fs:ext4"

    - Remount filesystem
        # {0:1} {1:1} "/dev/sda1:/data/fs:ext4:REMOUNT,RDONLY"
                    """.format(
                        cmd, mode
                    )

                # umount #
                elif SysMgr.checkMode("umount"):
                    helpStr = """
Usage:
    # {0:1} {1:1} -I <TARGET:FLAGS> [OPTIONS] [--help]

Description:
    Unmount filesystem
                        """.format(
                        cmd, mode
                    )

                    helpStr += """
Options:
    -I  <TARGET:FLAGS>          set mount point
    -f                          force execution
    -m  <ROWS:COLS:SYSTEM>      set terminal size
    -v                          verbose

Flags:
    See the man page
                    """

                    helpStr += """
Examples:
    - Unmount filesystem
        # {0:1} {1:1} "/dev/sda1"
        # {0:1} {1:1} -I "/dev/sda1"
                    """.format(
                        cmd, mode
                    )

                # getpid #
                elif SysMgr.checkMode("getpid"):
                    helpStr = _getDesc("Print task ID", t=2)

                    helpStr += """
Options:
    -e  <CHARACTER>             enable options
          [ t:thread | e:encode ]
    -g  <KEYWORD>               set keyword
    -o  <DIR|FILE>              set output path
    -m  <ROWS:COLS:SYSTEM>      set terminal size
    -J                          print in JSON format
    -Q                          print all rows in a stream
    -v                          verbose
                    """

                    helpStr += """
Examples:
    - {2:1} of specific processes having comm including specific keywords
        # {0:1} {1:1} a.out
        # {0:1} {1:1} "*chrome, *test*"

    - {2:1} of specific threads having comm including specific keywords
        # {0:1} {1:1} a.out -et
        # {0:1} {1:1} "*chrome, *test*" -et

    - {2:1} with the count
        # {0:1} {1:1} a.out -q PRINTCNT

    - {2:1} with comm of specific processes having comm including specific keywords
        # {0:1} {1:1} a.out -q PRINTCOMM

    - {2:1} of specific processes having exe path including specific keywords
        # {0:1} {1:1} a.out -q TARGETEXE
        # {0:1} {1:1} "*chrome, *test*" -q TARGETEXE

    - {2:1} of specific processes having cmdline path including specific keywords
        # {0:1} {1:1} a.out -q TARGETCMD
        # {0:1} {1:1} "*chrome, *test*" -q TARGETCMD
                    """.format(
                        cmd,
                        mode,
                        "Print IDs",
                    )

                # less #
                elif SysMgr.checkMode("less"):
                    helpStr = _getDesc("Print files with pager", t=5)

                    helpStr += """
Options:
    -I  <FILE>                  set file path
    -g  <WORD>                  set filter
    -o  <DIR|FILE>              set output path
    -m  <ROWS:COLS:SYSTEM>      set terminal size
    -v                          verbose

Keys:
     f    Move to next page
     b    Move to prev page
     g    Move to first page
     G    Move to last page
     k    Move to next line
     j    Move to prev line
     NUM  Move to the line
     /    Search line (regex)
     n    Search next line
     N    Search prev line
     q    Quit
     ENT  Reuse last input
                    """

                    helpStr += """
Examples:
    - {2:1}
        # {0:1} {1:1} a.out
        # {0:1} {1:1} -I a.out
                    """.format(
                        cmd, mode, "Print files with pager"
                    )

                # strings #
                elif SysMgr.checkMode("strings"):
                    helpStr = _getDesc(
                        "Print the sequences of printable characters in files",
                        t=5,
                    )

                    helpStr += """
Options:
    -I  <FILE>                  set file path
    -a                          show position info
    -g  <WORD>                  set filter
    -o  <DIR|FILE>              set output path
    -m  <ROWS:COLS:SYSTEM>      set terminal size
    -Q                          print all rows in a stream
    -v                          verbose
                    """

                    helpStr += """
Examples:
    - Print printable characters in a specific file
        # {0:1} {1:1} a.out
        # {0:1} {1:1} -I a.out

    - Print printable characters in a specific file without pager
        # {0:1} {1:1} a.out -Q

    - Print the sequences of printable characters in a specific file
        # {0:1} {1:1} -I a.out -a

    - Print specific printable characters in a specific file
        # {0:1} {1:1} -I a.out -g PEACE
                    """.format(
                        cmd, mode
                    )

                # sysrq #
                elif SysMgr.checkMode("sysrq"):
                    helpStr = """
Usage:
    # {0:1} {1:1} VAL [OPTIONS] [--help]

Description:
    Write values to sysrq
                        """.format(
                        cmd, mode
                    )

                    helpStr += """
Options:
    -l                          print sysrq values
    -v                          verbose
                    """

                    helpStr += SysMgr.writeSysrq(retList=True)

                    helpStr += """
Examples:
    - Write values to sysrq
        # {0:1} {1:1} b
        # {0:1} {1:1} dle

    - Print values for sysrq
        # {0:1} {1:1} -l
                    """.format(
                        cmd, mode
                    )

                # cgroup #
                elif SysMgr.checkMode("cgroup"):
                    helpStr = """
Usage:
    # {0:1} {1:1} -g <CMD:SUB:NAME:TASK|VAL@FILE> [OPTIONS] [--help]

Description:
    Control cgroups
                        """.format(
                        cmd, mode
                    )

                    helpStr += """
Options:
    -e  <CHARACTER>             enable options
          [ t:thread | e:encode ]
    -l                          print cgroup list
    -P                          group threads in a same process
    -J                          print in JSON format
    -m  <ROWS:COLS:SYSTEM>      set terminal size
    -v                          verbose

Commands:
    CREATE   create a new group
    ADD      add specific tasks to a group
    MOVE     move specific tasks from a group to another group
    REMOVE   remove specific tasks from a group
    DELETE   delete a group
    READ     read value from the specific file
    WRITE    write value to the specific file
    LIST     print all tasks in a group
                    """

                    helpStr += """
Examples:
    - Print cgroup info
        # {0:1} {1:1} -l
        # {0:1} {1:1} -l -J

    - Print cgroup info after mounting/unmounting all cgroup subsystems
        # {0:1} {1:1} -l -q MOUNTCG
        # {0:1} {1:1} -l -q UMOUNTCG

    - Make a new cpu group
        # {0:1} {1:1} CREATE:cpu:test1:
        # {0:1} {1:1} CREATE:cpu:test1:"*"

    - Add specific processes to a specific group
        # {0:1} {1:1} ADD:cpu:test1:"*a.out*"

    - Add specific threads to a specific group
        # {0:1} {1:1} ADD:cpu:test1:"*a.out*" -e t

    - Move specific processes between cpu groups
        # {0:1} {1:1} MOVE:cpu/:cpu/test2:"*a.out*"
        # {0:1} {1:1} MOVE:cpu/test1:cpu/test2:"*a.out*"

    - Remove specific processes from a cpu group
        # {0:1} {1:1} REMOVE:cpu:test1:"*a.out*"

    - Delete a cpu group
        # {0:1} {1:1} DELETE:cpu:test1:
        # {0:1} {1:1} DELETE:cpu:"test*":

    - Read value from the specific file
        # {0:1} {1:1} READ:memory:test2:memory.limit_in_bytes
        # {0:1} {1:1} READ:memory:test2:"memory.*"
        # {0:1} {1:1} READ:memory:"test*":tasks

    - Write value to the specific file
        # {0:1} {1:1} WRITE:memory:test2:100000@memory.limit_in_bytes
        # {0:1} {1:1} WRITE:memory:test2:100000@"memory.*"
        # {0:1} {1:1} WRITE:memory:"test*":100000@memory.limit_in_bytes

    - Print all processes in a cpu group
        # {0:1} {1:1} LIST:cpu:/:"*"
        # {0:1} {1:1} LIST:cpu:/:"*" -e t
        # {0:1} {1:1} LIST:cpu:test1:"*"
        # {0:1} {1:1} LIST:cpu:"test*":"*"
                    """.format(
                        cmd, mode
                    )

                # print #
                elif SysMgr.checkMode("print"):
                    helpStr = _getDesc(
                        "Print files including compressed data", t=5
                    )

                    helpStr += """
Options:
    -I  <FILE>                  set file path
    -a                          show position info
    -g  <WORD>                  set filter
    -o  <DIR|FILE>              set output path
    -m  <ROWS:COLS:SYSTEM>      set terminal size
    -Q                          print all rows in a stream
    -q  <NAME{:VALUE}>          set environment variables
    -v                          verbose
                    """

                    helpStr += """
Examples:
    - Print target files
        # {0:1} {1:1} -I a.out, a.tar, a.zip, a.gz, a.apk, a.xz
        # {0:1} {1:1} {2:1} -q EXCEPTFILE:"*100*"
        # {0:1} {1:1} -I a.zip -q PRINTRAW

    - Print target files in compressed and BASE64-encoded data
        # {0:1} {1:1} {3:1} test.xz.base64 -q BASE64
        # {0:1} {1:1} {3:1} test.gz.base64 -q BASE64

    - Print specific lines including specific words for target files
        # {0:1} {1:1} {2:1} -g "peace, best"

    - Print target files to the output file
        # {0:1} {1:1} {2:1} -o output.txt

    - Print the contents of target files as much as a specified size
        # {0:1} {1:1} {2:1} -q SIZE:1K

    - Print the first part of target files
        # {0:1} {1:1} {2:1} -q HEAD:100

    - Print the last part of target files
        # {0:1} {1:1} {2:1} -q TAIL
        # {0:1} {1:1} {2:1} -q TAIL:100
        # {0:1} {1:1} {2:1} -q TAIL:100, CONT

    - Print target files in specific list
        # {0:1} {1:1} {2:1} -q FILELIST:-1
        # {0:1} {1:1} {2:1} -q FILELIST:0:10

    - Print specific target files in compressed data
        # {0:1} {1:1} {3:1} -g "*.so"

    - Print target files in compressed data in tree structure
        # {0:1} {1:1} {3:1} -q PRINTTREE

    - Print specific target files in compressed data and save them to files
        # {0:1} {1:1} {3:1} -g "*.so" -q EXTRACT

    - Print a android package list file
        # {0:1} {1:1} {3:1} /data/system/packages.list -q ANDPKGLIST
                    """.format(
                        cmd,
                        mode,
                        '"a.out, test*.txt"',
                        '"a.zip, test*.apk"',
                    )

                # dump #
                elif SysMgr.checkMode("dump"):
                    helpStr = _getDesc(
                        "Dump memory of the specific process", t=3
                    )

                    helpStr += """
Options:
    -u                          run in the background
    -a                          show all stats with registers
    -g  <COMM|TID>              set task filter
    -I  <RANGE>                 set memory address
    -R  <TIME>                  set timer
    -o  <DIR|FILE>              set output path
    -m  <ROWS:COLS:SYSTEM>      set terminal size
    -v                          verbose
                    """

                    helpStr += """
Examples:
    - {2:1} memory to the specific file
        # {0:1} {1:1} -g a.out -I 0x1234-0x4567 -o dump.out

    - {2:1} memory mapped to a specific file to the specific file
        # {0:1} {1:1} -g a.out -I a.out -o dump.out

    - {2:1} stack to the specific file
        # {0:1} {1:1} -g a.out -I stack -o dump.out

    - {2:1} heap to the specific file
        # {0:1} {1:1} -g a.out -I heap -o dump.out

    - {2:1} heap's bitmap to the specific file
        # {0:1} {1:1} -g a.out -I heap -o dump.out -q SAVEBITMAP
                    """.format(
                        cmd, mode, "Dump the specific process's"
                    )

                # mtrace #
                elif SysMgr.checkMode("mtrace"):
                    helpStr = _getDesc(
                        "Trace mmap allocations for specific threads", t=1
                    )

                    helpStr += """
Options:
    -e  <CHARACTER>             enable options
          [ p:pipe | e:encode ]
    -d  <CHARACTER>             disable options
          [ C:clone | e:encode | E:exec | g:general | O:color ]
    -u                          run in the background
    -a                          show all stats with registers
    -g  <COMM|TID{:FILE}>       set task filter
    -I  <COMMAND>               set command
    -R  <TIME>                  set timer
    -o  <DIR|FILE>              set output path
    -H  <LEVEL>                 set function depth level
    -m  <ROWS:COLS:SYSTEM>      set terminal size
    -E  <DIR>                   set cache dir path
    -q  <NAME{:VALUE}>          set environment variables
    -P                          group threads in a same process
    -v                          verbose
                    """

                    helpStr += (
                        """
Examples:
    - {2:1} from the specific binary and {3:1}
        # {0:1} {1:1} a.out -o ./ -H

    - {2:1} from the specific binary and {3:1} only for the resident set size (some call stacks may be merged and omitted)
        # {0:1} {1:1} a.out -o ./ -H -q ONLYRES

    - {2:1} for the specific thread and {3:1}
        # {0:1} {1:1} -g a.out -o ./ -H

    - {2:1} for specific threads in the same process and {3:1}
        # {0:1} {1:1} -g a.out -o ./ -H -P

    - {2:1} from the specific binary and {3:1} and keep target tasks alive (child Guider processes will be also alive)
        # {0:1} {1:1} a.out -o ./ -H -q KEEPTASK, KEEPALIVE
        # {0:1} {1:1} stop

    - {2:1} from the specific binary with specific allocator variables and {3:1}
        # {0:1} {1:1} a.out -o ./ -H -q ENV:MALLOC_MMAP_THRESHOLD_=1024, ENV:MALLOC_ARENA_MAX=5
                    """.format(
                            cmd,
                            mode,
                            "Trace mmap allocations with user stacks",
                            "report to the specific file",
                        ).rstrip()
                        + dbgExamStr
                    )

                # strace #
                elif SysMgr.checkMode("strace"):
                    helpStr = _getDesc(
                        "Trace syscalls for specific threads", t=1
                    )

                    helpStr += """
Options:
    -e  <CHARACTER>             enable options
          [ p:pipe | e:encode ]
    -d  <CHARACTER>             disable options
          [ C:clone | e:encode | E:exec | g:general | O:color ]
    -u                          run in the background
    -a                          show all stats with registers
    -g  <COMM|TID{:FILE}>       set task filter
    -t  <SYSCALL>               set syscall
    -I  <COMMAND>               set command
    -R  <TIME>                  set timer
    -c  <EVENT>                 set breakpoint
    -l                          print syscall list
    -o  <DIR|FILE>              set output path
    -H  <LEVEL>                 set function depth level
    -m  <ROWS:COLS:SYSTEM>      set terminal size
    -E  <DIR>                   set cache dir path
    -q  <NAME{:VALUE}>          set environment variables
    -J                          print in JSON format
    -v                          verbose
                    """

                    helpStr += (
                        """{2:1}
Examples:
    - {4:1} {5:1}
        # {0:1} {1:1} -g a.out -t read
        # {0:1} {1:1} -g a.out -t "read*", "*64"

    - {3:1} except for read {5:1}
        # {0:1} {1:1} -g a.out -t ^read

    - {4:1} {6:1}
        # {0:1} {1:1} "ls -al" -t write
        # {0:1} {1:1} -I "ls -al" -t write

    - {3:1} {5:1} from specific runtime
        # {0:1} {1:1} -g "*chrome, *test*" -q TARGETEXE
        # {0:1} {1:1} -g "*chrome, *test*" -q TARGETCMD

    - Trace only successful syscalls {5:1}
        # {0:1} {1:1} -g a.out -q ONLYOK

    - Trace only failed syscalls {5:1}
        # {0:1} {1:1} -g a.out -q ONLYFAIL

    - {3:1} {5:1} and ignore specific syscalls
        # {0:1} {1:1} -g a.out -q IGNSYSCALL:"mmap*", HIDEIGNSYSCALL
        # {0:1} {1:1} -g a.out -q IGNSYSCALL:"mmap*", IGNBTFILTER:"*printPeace*" -H

    - {4:1} {6:1} without data output for buffer contents
        # {0:1} {1:1} -I "ls -al" -t process_vm_readv -q ONLYADDR

    - {4:1} {6:1} without strip for buffer contents
        # {0:1} {1:1} -I "ls -al" -t write -q NOSTRIP

    - {4:1} with colorful elapsed time exceeds 0.1 seconds
        # {0:1} {1:1} -g a.out -c write -q ELAPSED:0.1

    - {3:1} {5:1} and print strings in specific maximum size
        # {0:1} {1:1} -g a.out -q STRSIZE:10

    - {4:1} {5:1} and report the result to guider.out
        # {0:1} {1:1} -g a.out -t read -o .

    - {3:1} with breakpoint for read {5:1}
        # {0:1} {1:1} -g a.out -c read
        # {0:1} {1:1} -g a.out -c read -a

    - {3:1} and pause when catching open syscall
        # {0:1} {1:1} -I "ls -al" -c open

    - {3:1} and sleep for 1.5 second when catching open syscall
        # {0:1} {1:1} -I "ls -al" -c "open|sleep:1.5"

    - {3:1} and sleep for 1.5 second whenever catching syscall
        # {0:1} {1:1} -I "ls -al" -c "*|sleep:1.5"

    - {3:1} and print memory that 2nd argument point to
        # {0:1} {1:1} -I "ls -al" -c "write|rdmem:1"

    - {3:1} and print the file path associated with fd in 1st argument
        # {0:1} {1:1} -I "ls -al" -c "read|rdfd:0"

    - {3:1} and print call contexts if only specific conditions are met
        # {0:1} {1:1} {7:1}2:EQ:4096"
        # {0:1} {1:1} {7:1}2:BT:0x1000"
        # {0:1} {1:1} {7:1}*1:EQ:HELLO"
        # {0:1} {1:1} {7:1}*1:INC:HE"
        # {0:1} {1:1} {7:1}#0:EQ:/dev/null"
        # {0:1} {1:1} {7:1}#0:INC:null"
        # {0:1} {1:1} {7:1}2:BT:1|filter:1:EQ:1"
                    """.format(
                            cmd,
                            mode,
                            cmdListStr,
                            "Trace all syscalls",
                            "Trace specific syscalls",
                            "for specific threads",
                            "from the specific binary",
                            '-I "ls -al" -c "write|filter:',
                        ).rstrip()
                        + dbgExamStr
                    )

                # utrace #
                elif SysMgr.checkMode("utrace"):
                    helpStr = _getDesc(
                        "Trace function calls for specific threads", t=1
                    )

                    helpStr += """
Options:
    -e  <CHARACTER>             enable options
          [ p:pipe | D:DWARF | e:encode ]
    -d  <CHARACTER>             disable options
          [ C:clone | e:encode | d:demangle | D:DWARF | E:exec | g:general ]
    -u                          run in the background
    -a                          show all stats with registers
    -g  <COMM|TID{:FILE}>       set task filter
    -I  <COMMAND>               set command
    -R  <TIME>                  set timer
    -c  <EVENT>                 set breakpoint
    -H  <SKIP>                  set instrunction sampling rate
    -o  <DIR|FILE>              set output path
    -m  <ROWS:COLS:SYSTEM>      set terminal size
    -E  <DIR>                   set cache dir path
    -v                          verbose
                    """

                    helpStr += (
                        """
Examples:
    - {2:1} {3:1} in 100us cycles
        # {0:1} {1:1} -g a.out

    - {2:1} {4:1}
        # {0:1} {1:1} "ls -al"
        # {0:1} {1:1} -I "ls -al"

    - {2:1} {3:1} {4:1}
        # {0:1} {1:1} "ls -al" -g a.out
        # {0:1} {1:1} -I "ls -al" -g a.out

    - {2:1} {3:1} in 10ms cycles
        # {0:1} {1:1} -g a.out -i 10000

    - {2:1} with 1/10 instructions {3:1}
        # {0:1} {1:1} -g a.out -H 10

    - {2:1} {3:1} and report the result to guider.out
        # {0:1} {1:1} -g a.out -o . -a

    - {2:1} with breakpoint for specific functions {3:1}
        # {0:1} {1:1} -g a.out -c peace
        # {0:1} {1:1} -g a.out -c peace -a

    - {2:1} {4:1} and pause when catching PLT function call
        # {0:1} {1:1} "ls -al" -c PLT
        # {0:1} {1:1} -I "ls -al" -c PLT
                    """.format(
                            cmd,
                            mode,
                            "Trace usercalls",
                            "for specific threads",
                            "from the specific binary",
                        ).rstrip()
                        + dbgExamStr
                    )

                # pytrace #
                elif SysMgr.checkMode("pytrace"):
                    helpStr = _getDesc(
                        "Trace python calls for specific threads (support up to Python 3.11)",
                        t=1,
                    )

                    helpStr += """
Options:
    -e  <CHARACTER>             enable options
          [ p:pipe | D:DWARF | e:encode ]
    -d  <CHARACTER>             disable options
          [ C:clone | d:demangle | D:DWARF | e:encode | E:exec | g:general ]
    -u                          run in the background
    -f                          force execution
    -a                          show all stats with registers
    -T  <FILE>                  set target file
    -g  <COMM|TID{:FILE}>       set task filter
    -I  <COMMAND>               set command
    -R  <TIME>                  set timer
    -c  <SYMBOL>                set function filter
    -H  <LEVEL>                 set function depth level
    -o  <DIR|FILE>              set output path
    -m  <ROWS:COLS:SYSTEM>      set terminal size
    -E  <DIR>                   set cache dir path
    -q  <NAME{:VALUE}>          set environment variables
    -J                          print in JSON format
    -v                          verbose
                    """

                    examStr = (
                        """{2:1}
Examples:
    - {3:1} {4:1}
        # {0:1} {1:1} -g iotop

    - {3:1} {4:1} {5:1}
        # {0:1} {1:1} iotop
        # {0:1} {1:1} iotop -f
        # {0:1} {1:1} "python -c \"while 1: print('OK')\""
        # {0:1} {1:1} iotop -g iotop -f

    - {3:1} {4:1} in 10ms cycles
        # {0:1} {1:1} -g iotop -i 10000

    - {3:1} with colorful elapsed time exceeds 0 second
        # {0:1} {1:1} -g iotop -c write -q PYELAPSED:0

    - {3:1} with 1/10 instructions {4:1}
        # {0:1} {1:1} -g iotop -H 10

    - {3:1} {4:1} and report the result to guider.out
        # {0:1} {1:1} -g iotop -o . -a

    - {3:1} with breakpoint for specific functions {4:1}
        # {0:1} {1:1} -g iotop -c peace
        # {0:1} {1:1} -g iotop -c peace -a
                    """.format(
                            cmd,
                            mode,
                            cmdListStr,
                            "Trace python calls",
                            "for specific threads",
                            "from the specific binary",
                        ).rstrip()
                        + dbgExamStr
                    )

                    helpStr += examStr

                # btrace #
                elif SysMgr.checkMode("btrace"):
                    helpStr = _getDesc(
                        "Trace native function calls for specific threads", t=1
                    )

                    helpStr += """
Options:
    -e  <CHARACTER>             enable options
          [ p:pipe | D:DWARF | e:encode ]
    -d  <CHARACTER>             disable options
          [ C:clone | d:demangle | D:DWARF | e:encode | E:exec | g:general ]
    -u                          run in the background
    -a                          show all stats with registers
    -T  <FILE>                  set target file
    -g  <COMM|TID{:FILE}>       set task filter
    -I  <COMMAND>               set command
    -R  <TIME>                  set timer
    -c  <SYM|ADDR{:CMD}>        set breakpoint
    -H  <LEVEL>                 set function depth level
    -o  <DIR|FILE>              set output path
    -m  <ROWS:COLS:SYSTEM>      set terminal size
    -E  <DIR>                   set cache dir path
    -q  <NAME{:VALUE}>          set environment variables
    -J                          print in JSON format
    -v                          verbose

Caution:
    - If unexpected problems occur, clear the file cache.
    - If the tracing of the binary you are executing is not displayed properly,
      use the WATCHLD value with the -q option.
                    """

                    helpStr += brkExamStr

                # remote #
                elif SysMgr.checkMode("remote"):
                    helpStr = _getDesc(
                        "Execute commands through specific threads", t=4
                    )

                    helpStr += """
Options:
    -u                          run in the background
    -a                          show all stats with registers
    -g  <COMM|TID{:FILE}>       set task filter
    -R  <TIME>                  set timer
    -i  <REPEAT>                set repeat count
    -c  <SYM|ADDR{:CMD}>        set command
    -H  <LEVEL>                 set function depth level
    -o  <DIR|FILE>              set output path
    -m  <ROWS:COLS:SYSTEM>      set terminal size
    -E  <DIR>                   set cache dir path
    -q  <NAME{:VALUE}>          set environment variables
    -v                          verbose
                    """

                    remoteExamStr = (
                        """{4:1}
Examples:
    - Print the context for the target repeatedly 5 times
        # {0:1} {1:1} {6:1} print -i 5
        # {0:1} {1:1} {6:1} print -i 5 -q ONLYPROC

    - {2:1} sleep for 0.1 second
        # {0:1} {1:1} {6:1} "sleep:0.1"
        # {0:1} {1:1} {6:1} "sleep:0.1" -q ONLYPROC

    - {2:1} be terminated
        # {0:1} {1:1} {6:1} "kill"

    - {2:1} modify its memory
        # {0:1} {1:1} {6:1} "wrmem:0x1234:aaaa:4"

    - {2:1} print null-terminated string from the specific memory address
        # {0:1} {1:1} {6:1} "rdmem:0x1234"

    - {2:1} print 10-length string from the specific memory address
        # {0:1} {1:1} {6:1} "rdmem:0x1234:10"

    - {2:1} print the file path associated with specific fd
        # {0:1} {1:1} {6:1} "rdfd:0"

    - {2:1} call madvise
        # {0:1} {1:1} {6:1} "madvise:0x1234:8092:3"
        # {0:1} {1:1} {6:1} "madvise:0x1234:2K:DONTNEED"
        # {0:1} {1:1} {6:1} "madvise:0x1234:1M:MERGEABLE"
        # {0:1} {1:1} {6:1} "madvise:stack:1M:WILLNEED"
        # {0:1} {1:1} {6:1} "madvise:heap:4M:MERGEABLE"
        # {0:1} {1:1} {6:1} "madvise:heap::MERGEABLE"

    - {2:1} call mprotect
        # {0:1} {1:1} {6:1} "mprotect:0x1234:8092:rwx"
        # {0:1} {1:1} {6:1} "mprotect:0x1234:8092:"

    - {2:1} call fadvise
        # {0:1} {1:1} {6:1} "fadvise:0x1234:8092:3"
        # {0:1} {1:1} {6:1} "fadvise:0x1234:2K:DONTNEED"
        # {0:1} {1:1} {6:1} "fadvise:0x1234:0:DONTNEED"
        # {0:1} {1:1} {6:1} "fadvise:0x1234:4K:WILLNEED"

    - {2:1} return from the current function with a specific value immediately
        # {0:1} {1:1} {6:1} "ret:3"

    - {2:1} dump stack to a file
        # {0:1} {1:1} {6:1} "dump:stack:stack.out"

    - {2:1} dump specific memory range to a file
        # {0:1} {1:1} {6:1} "dump:0x1234-0x4567:dump.out"

    - Check specific register value for the target
        # {0:1} {1:1} {6:1} "check:2:EQ:4096"
        # {0:1} {1:1} {6:1} "check:2:BT:0x1000"
        # {0:1} {1:1} {6:1} "check:*1:EQ:HELLO"
        # {0:1} {1:1} {6:1} "check:*1:INC:HE"

    - Print 1st and 2nd arguments (registers)
        # {0:1} {1:1} {6:1} "getarg:0:1"

    - {2:1} be terminated
        # {0:1} {1:1} {6:1} "exit"

    - {3:1} call the specific function with task info but logs and backtraces
        # {0:1} {1:1} {6:1} "usercall:sleep#3" -d L -q ONLYPROC, INCTASK, NOBT -f

    - {3:1} call the specific function with task info but logs sequentially
        # {0:1} {1:1} {6:1} "usercall:sleep#3" -d L -q ONLYPROC, INCTASK, SEQUENTIAL -f

    - {2:1} call the specific function
        # {0:1} {1:1} {6:1} "usercall:sleep#3"
        # {0:1} {1:1} {6:1} "usercall:printf#PEACE\\n"
        # {0:1} {1:1} {6:1} "usercall:getenv#PATH"
        # {0:1} {1:1} {6:1} "usercall:malloc_stats"
        # {0:1} {1:1} {6:1} "usercall:Test::getInstance()->Test::printValue()"
        # {0:1} {1:1} {6:1} "usercall:malloc_trim#0" -q PRINTDIFF
        # {0:1} {1:1} {6:1} "usercall:malloc_trim#0" -q TARGETFILE:"/usr/lib/libc.so"
        # {0:1} {1:1} {6:1} "usercall:malloc_trim#0" -q EXCEPTFILE:"/usr/lib32/*""
        # {0:1} {1:1} {6:1} "usercall:malloc_trim#0" -q NOBT
        # {0:1} {1:1} {6:1} "usercall:malloc#1024, usercall:memset#@malloc#0#1024" -q PRINTDIFF
        # {0:1} {1:1} {6:1} "usercall:sleep#3" -q NOSYMCACHE

    - {2:1} call the specific syscall
        # {0:1} {1:1} {6:1} "syscall:getpid"
        # {0:1} {1:1} {6:1} "syscall:open#test.out#1"

    - {2:1} load the specific library
        # {0:1} {1:1} {6:1} "load:/usr/lib/preload.so"

    - {2:1} create a thread
        # {0:1} {1:1} {6:1} "thread"

    - {2:1} execute python code remotely
        # {0:1} {1:1} {6:1} pystr:"print('OK')" -q {5:1}
        # {0:1} {1:1} {6:1} pyfile:"test.py":false -q {5:1} -q STRPAGE

    - {2:1} execute python code remotely and execute specific python function locally
        # {0:1} {1:1} {6:1} pystr:"print('OK')", pyscript:test.py:test_func -q {5:1}

    - {2:1} jump to the specific function with specific arguments
        # {0:1} {1:1} {6:1} "jump:sleep#5"

    - {2:1} execute specific commands
        # {0:1} {1:1} {6:1} "exec:ls -lha;sleep 1"
        # {0:1} {1:1} {6:1} "exec:ls -lha &"
                    """.format(
                            cmd,
                            mode,
                            "Control specific threads to",
                            "Control specific processes to",
                            cmdListStr,
                            "LIBPYTHON:/usr/lib/x86_64-linux-gnu/libpython3.8.so.1.0",
                            "-g a.out -c",
                        ).rstrip()
                        + dbgExamStr
                    )

                    helpStr += remoteExamStr

                # hook #
                elif SysMgr.checkMode("hook"):
                    helpStr = (
                        _getDesc(
                            "Replace specific functions for specific processes",
                            t=4,
                        )
                        + """
    Caution) This command will update PLTs in mapped binaries.

Options:
    -u                          run in the background
    -g  <COMM|TID>              set task filter
    -c  <OSYM#NBIN#NSYM>        set command
    -H  <LEVEL>                 set function depth level
    -o  <DIR|FILE>              set output path
    -m  <ROWS:COLS:SYSTEM>      set terminal size
    -E  <DIR>                   set cache dir path
    -q  <NAME{{:VALUE}}>          set environment variables
    -v                          verbose

Examples:
    - Replace malloc() with mallocHook() in libhook.so for specific processes
        # {0:1} {1:1} {2:1}

    - Replace malloc() with mallocHook() in libhook.so for specific processes (wait for new target if no task)
        # {0:1} {1:1} {2:1} -q WAITTASK
        # {0:1} {1:1} {2:1} -q WAITTASK:1
        # {0:1} {1:1} {2:1} -q WAITTASK, NOPIDCACHE
                    """.format(
                            cmd,
                            mode,
                            "-g a.out -c malloc#./libhook.so#mallocHook",
                        )
                    )

                # printbind #
                elif SysMgr.checkMode("printbind"):
                    helpStr = (
                        _getDesc(
                            "Print function binding status for specific processes",
                            t=4,
                        )
                        + """
Options:
    -u                          run in the background
    -g  <COMM|TID>              set task filter
    -c  <FUNC|FILE>             set function filter
    -o  <DIR|FILE>              set output path
    -m  <ROWS:COLS:SYSTEM>      set terminal size
    -E  <DIR>                   set cache dir path
    -q  <NAME{{:VALUE}}>          set environment variables
    -v                          verbose

Environment Variables:
    LD_DEBUG: all, statistics, libs, bindings, detail, files, reloc, symbols
    LD_DEBUG_OUTPUT: PATH

Examples:
    - Print all function binding status for a specific process
        # {0:1} {1:1} -g a.out

    - Print specific function binding status for a specific process
        # {0:1} {1:1} -g a.out -c write
                    """.format(
                            cmd, mode
                        )
                    )

                # sigtrace #
                elif SysMgr.checkMode("sigtrace"):
                    helpStr = _getDesc(
                        "Trace signals for specific threads", t=1
                    )

                    helpStr += """
Options:
    -e  <CHARACTER>             enable options
          [ p:pipe | e:encode ]
    -d  <CHARACTER>             disable options
          [ C:clone | e:encode | E:exec | g:general ]
    -u                          run in the background
    -a                          show all info
    -g  <COMM|TID{:FILE}>       set task filter
    -I  <COMMAND>               set command
    -R  <TIME>                  set timer
    -H  <LEVEL>                 set function depth level
    -o  <DIR|FILE>              set output path
    -m  <ROWS:COLS:SYSTEM>      set terminal size
    -E  <DIR>                   set cache dir path
    -v                          verbose
                    """

                    helpStr += (
                        """
Examples:
    - {2:1} {3:1}
        # {0:1} {1:1} -g a.out

    - {2:1} for a specific command
        # {0:1} {1:1} -I "ls"

    - {2:1} with detailed info {3:1}
        # {0:1} {1:1} -g a.out -a

    - {2:1} with detailed info {3:1} and report the results to the specific file
        # {0:1} {1:1} -g a.out -a -o signal.out

    - Trace SIGINT {3:1}
        # {0:1} {1:1} -g 1234 -c SIGINT

    - {2:1} except for specific signals {3:1}
        # {0:1} {1:1} -g 1234 -c ^SIGINT
                    """.format(
                            cmd,
                            mode,
                            "Trace all signals",
                            "for specific threads",
                        ).rstrip()
                        + dbgExamStr
                    )

                # mem #
                elif SysMgr.checkMode("mem"):
                    helpStr = _getDesc(
                        "Print page attributes for specific processes", t=2
                    )

                    helpStr += """
Options:
    -g  <COMM|TID{:FILE}>       set task filter
    -o  <DIR|FILE>              set output path
    -I  <ADDR>                  set address area
    -a                          show all memory info
    -m  <ROWS:COLS:SYSTEM>      set terminal size
    -v                          verbose
                    """

                    helpStr += """
Examples:
    - Print memory map summary {3:1}
        # {0:1} {1:1} a.out
        # {0:1} {1:1} "a.out, systemd"

    - Print memory map details {3:1}
        # {0:1} {1:1} a.out -a
        # {0:1} {1:1} "a.out, systemd" -a

    - Print the summary for page attributes in specific area {3:1}
        # {0:1} {1:1} a.out -I 0x0-0x4000
        # {0:1} {1:1} a.out -I 0x0-+100M
        # {0:1} {1:1} a.out -I libc-2.23.so
        # {0:1} {1:1} a.out -I /lib/x86_64-linux-gnu/libc-2.23.so
        # {0:1} {1:1} a.out -I "/lib/x86_64-linux-gnu/*"
        # {0:1} {1:1} a.out -I heap, stack
        # {0:1} {1:1} a.out -I anon
        # {0:1} {1:1} a.out -I file
        # {0:1} {1:1} a.out -I fileanon
        # {0:1} {1:1} a.out -I noperm
        # {0:1} {1:1} a.out -I all
        # {0:1} {1:1} a.out -I 0x8000-0x9000, 0x12345678

    - Print memory map info without attributes in specific area {3:1} to improve performance
        # {0:1} {1:1} a.out -I anon -q SKIPATTR
        # {0:1} {1:1} a.out -I anon -q SKIPATTRSTR

    - Print memory map info without attributes in specific area {3:1} and execute specific commands
        # {0:1} {1:1} a.out -I file -q SKIPATTR -c madvise:START:SIZE:DONTNEED -Q
        # {0:1} {1:1} a.out -I file -q SKIPATTR -c fadvise:START:SIZE:DONTNEED -a -Q

    - {2:1} in specific area {3:1}
        # {0:1} {1:1} a.out -I 0x0-0x4000 -a
        # {0:1} {1:1} a.out -I "/lib/x86_64-linux-gnu/*" -a
        # {0:1} {1:1} a.out -I heap, stack -a

    - {2:1} after marking idle flag in specific area {3:1}
        # {0:1} {1:1} a.out -I 0x0-0x4000 -q MARKIDLE

    - {2:1} including idle flag in specific area {3:1}
        # {0:1} {1:1} a.out -I 0x0-0x4000 -q CHECKIDLE

    - Save the bitmap including idle page info to the specific file {3:1}
        # {0:1} {1:1} a.out -I heap -o mem.out -q CHECKIDLE, SAVEBITMAP
                    """.format(
                        cmd,
                        mode,
                        "Print all page attributes",
                        "for specific processes",
                    )

                # stacked draw #
                elif SysMgr.checkMode("drawstack"):
                    helpStr = _getDesc("Draw stacked plots", t=5)

                    helpStr += drawSubStr + drawExamStr

                # CPU average draw #
                elif SysMgr.checkMode("drawcpuavg"):
                    helpStr = _getDesc("Draw CPU average graphs", t=5)

                    helpStr += drawSubStr + drawExamStr

                # memory average draw #
                elif SysMgr.checkMode("drawmemavg"):
                    helpStr = _getDesc(
                        "Draw memory average graphs for system", t=5
                    )

                    helpStr += drawSubStr + drawExamStr

                # VSS average draw #
                elif SysMgr.checkMode("drawvssavg"):
                    helpStr = _getDesc(
                        "Draw VSS average graphs for processes", t=5
                    )

                    helpStr += drawSubStr + drawExamStr

                # RSS average draw #
                elif SysMgr.checkMode("drawrssavg"):
                    helpStr = _getDesc(
                        "Draw RSS average graphs for processes", t=5
                    )

                    helpStr += drawSubStr + drawExamStr

                # CPU draw #
                elif SysMgr.checkMode("drawcpu"):
                    helpStr = _getDesc("Draw CPU graphs", t=5)

                    helpStr += drawSubStr + drawExamStr

                # CPU Delay draw #
                elif SysMgr.checkMode("drawdelay"):
                    helpStr = _getDesc("Draw CPU delay graphs", t=5)

                    helpStr += drawSubStr + drawExamStr

                # bitmap draw #
                elif SysMgr.checkMode("drawbitmap"):
                    helpStr = _getDesc("Draw bitmap", t=5)

                    helpStr += drawSubStr + drawExamStr

                # histogram draw #
                elif SysMgr.checkMode("drawhist"):
                    helpStr = _getDesc("Draw histogram", t=5)

                    helpStr += drawSubStr + drawExamStr

                # violin draw #
                elif SysMgr.checkMode("drawviolin"):
                    helpStr = _getDesc("Draw violin plots", t=5)

                    myStr = """
    - Draw violin plots only for specific items from specific files
        # {0:1} {1:1} "*.out" -q TARGETDRAW:"cpuUsage+mem*"

    - Draw violin plots after sorting each array
        # {0:1} {1:1} "*.out" -q SORT

    - Draw violin plots without scatter plots
        # {0:1} {1:1} "*.out" -q NOSCATTER
                    """.format(
                        cmd,
                        mode,
                    )

                    helpStr += drawSubStr + drawExamStr + myStr

                # flamegraph draw #
                elif SysMgr.checkMode("drawflame"):
                    helpStr = _getDesc("Draw flame graph", t=5)

                    myStr = """
    - {2:1} with redundant call count
        # {0:1} {1:1} guider.out -q KEEPREDCNT

    - {2:1} with valid file list
        # {0:1} {1:1} "./guider_*" -q PRINTFILELIST

    - Print only tree in text
        # {0:1} {1:1} "./guider_*" -q PRINTTREE

    - {2:1} after printing stats on console
        # {0:1} {1:1} "./guider_*" -v

    - {2:1} with time filter
        # {0:1} {1:1} "./guider_*" -q TIMEFILTER:"2024-02*", TIMEFILTER:"2024-03*"

    - {2:1} with task filter
        # {0:1} {1:1} "./guider_*" -q PROCFILTER:"*hmi"
        # {0:1} {1:1} "./guider_*" -q PROCEXFILTER:"*hmi"
        # {0:1} {1:1} "./guider_*" -q THREADFILTER:"*worker*"
        # {0:1} {1:1} "./guider_*" -q THREADEXFILTER:"*worker*"

    - {2:1} for multiple android tombstones
        # {0:1} {1:1} "/data/tombstone/*" -q MERGETOMBSTONE -f

    - {2:1} for multiple linux crash logs
        # {0:1} {1:1} "/data/log/crash/*" -q MERGECRASH -f

    - {2:1} for multiple kernel panic logs
        # {0:1} {1:1} "/var/log/*" -q MERGEPANIC -f
                    """.format(
                        cmd,
                        mode,
                        "Draw a flame graph",
                    )

                    helpStr += drawSubStr + drawExamStr + myStr

                # timeline draw #
                elif SysMgr.checkMode("drawtime"):
                    helpStr = _getDesc(
                        "Draw timeline segment from JSON format data", t=5
                    )

                    drawTimelineStr = """
Format:
    < DATA >
    {
      "title": "example",   // optional for title
      "font_size": 3,       // optional for font size
      "time_unit": "ms",    // [sec | ms | ns]
      "names": {            // optional for name
        '0': "task1",
        '1': "task2",
        '2': "task3"
        }
      "segments": [
        {
          "group": 0,
          "text": "task0",
          "time_start": 10,
          "time_end": 40,
          "id": 12,         // optional for class
          "color": "red",   // optional for color
          "state": "S"      // optional for event
        },
        {
          "group": 1,
          "text": "task2",
          "time_start": 0,
          "time_end": 20,
          "id": 13,
          "color": "black",
          "state": "S"
        },
        {
          "group": 1,
          "text": "task3",
          "time_start": 30,
          "time_end": 35,
          "id": 14,
          "color": "rgb(128,0,128)",
          "state": "S"
        }
      ]
    }

    < CONFIG >
    {
        "width": 400,
        "height": 100,
        "font_size": 4,
        "time_ticks": 20,
        "time_axis_height": 10,
        "label_size_min": 30,
        "palette": [
            "(244, 67, 54)",
            "(233,30,99)",
            "(156, 39, 176)"
        ]
    }
                        """

                    helpStr += drawSubStr + drawTimelineStr + drawExamStr

                # memory draw #
                elif SysMgr.checkMode("drawmem"):
                    helpStr = _getDesc(
                        "Draw system memory graphs and memory chart", t=5
                    )

                    helpStr += drawSubStr + drawExamStr

                # vss draw #
                elif SysMgr.checkMode("drawvss"):
                    helpStr = _getDesc(
                        "Draw process memory(VSS) graphs and memory chart", t=5
                    )

                    helpStr += drawSubStr + drawExamStr

                # rss draw #
                elif SysMgr.checkMode("drawrss"):
                    helpStr = _getDesc(
                        "Draw process memory(RSS) graphs and memory chart", t=5
                    )

                    helpStr += drawSubStr + drawExamStr

                # leak draw #
                elif SysMgr.checkMode("drawleak"):
                    helpStr = _getDesc(
                        "Draw memory(VSS) graphs of processes suspected memory leak and memory chart",
                        t=5,
                    )

                    helpStr += drawSubStr + drawExamStr

                # I/O draw #
                elif SysMgr.checkMode("drawio"):
                    helpStr = _getDesc("Draw system I/O graphs", t=5)

                    helpStr += drawSubStr + drawExamStr

                # prio draw #
                elif SysMgr.checkMode("drawpri"):
                    helpStr = _getDesc(
                        "Draw task scheduling priority graphs", t=5
                    )

                    helpStr += drawSubStr + drawExamStr

                # draw #
                elif SysMgr.isDrawMode():
                    helpStr = (
                        _getDesc(
                            "Draw system resource graph, event timeline, memory chart",
                            t=5,
                        )
                        + """
    * The system memory chart is created only when using PSS-enabled report
    * The event timeline supports sched, block, syscall, mark, custom, user, kernel, off
                        """
                    )

                    helpStr += drawSubStr + drawExamStr

                # topdiff #
                elif SysMgr.checkMode("topdiff"):
                    helpStr = _getDesc("Diff top report files", t=5)

                    helpStr += """
Options:
    -d  <CHARACTER>             disable options
          [ A:Average | e:encode ]
    -g  <COMM|TID>              set task filter
    -o  <DIR|FILE>              set output path
    -J                          print in JSON format
    -q  <NAME{:VALUE}>          set environment variables
                    """

                    helpStr += """
Examples:
    - {2:1}
        # {0:1} {1:1} "tc1.out, tc2.out"
        # {0:1} {1:1} "tc*.out"

    - {2:1} without merging tasks having same name
        # {0:1} {1:1} "tc*.out" -q NOMERGE

    - {2:1} with the first file not before each
        # {0:1} {1:1} "tc*.out" -q FROMSTART

    - {2:1} for specific tasks
        # {0:1} {1:1} "tc1.out, tc2.out" -g a.out
        # {0:1} {1:1} "tc1.out, tc2.out" -g "*task"
        # {0:1} {1:1} "tc1.out, tc2.out" -g "^task*"

    - {2:1} from current directory to all sub-directories
        # {0:1} {1:1} "**/tc*.out"

    - {2:1} with file filter
        # {0:1} {1:1} "**/*" -q TARGETFILE:"*.out", EXCEPTFILE:"*.old"

    - {2:1} by total usage
        # {0:1} {1:1} "tc*.out" -dA

    - {2:1} within specific interval range in index unit
        # {0:1} {1:1} "tc*.out" -q TRIMIDX:1:5

    - {2:1} with just file names
        # {0:1} {1:1} "tc*.out" -q ONLYFILE

    - {2:1} with error range
        # {0:1} {1:1} "tc*.out" -q CPUERRRANGE:1, GPUERRRANGE:10, MEMERRRANGE:20, SDERRRANGE:1

    - {2:1} and apply filter for systemd stats
        # {0:1} {1:1} "tc*.out" -q UNITFILTER:"*hmi*"
        # {0:1} {1:1} "tc*.out" -q UNITEXFILTER:"*hmi*"

    - {2:1} and sort by specific values for systemd stats
        # {0:1} {1:1} "tc*.out" -q SORT:INITDIFF
        # {0:1} {1:1} "tc*.out" -q SORT:START
        # {0:1} {1:1} "tc*.out" -q SORT:NAME

    - {2:1} in JSON format
        # {0:1} {1:1} "tc*.out" -J
                    """.format(
                        cmd, mode, "Diff top report files"
                    )

                # topsum #
                elif SysMgr.checkMode("topsum"):
                    helpStr = _getDesc(
                        "Summarize a raw file for task top mode", t=5
                    )

                    helpStr += """
Options:
    -e  <CHARACTER>             enable options
          [ p:pipe | e:encode ]
    -d  <CHARACTER>             disable options
          [ C:clone | e:encode | E:exec | g:general ]
    -a                          show all info
    -g  <COMM|TID{:FILE}>       set task filter
    -I  <FILE>                  set file path
    -o  <DIR|FILE>              set output path
    -J                          print in JSON format
    -m  <ROWS:COLS:SYSTEM>      set terminal size
    -q  <NAME{:VALUE}>          set environment variables
    -v                          verbose
                    """

                    helpStr += """
Examples:
    - {2:1}
        # {0:1} {1:1} output.out
        # {0:1} {1:1} "report1.out, report2.out.gz"
        # {0:1} {1:1} "output*.out"
        # {0:1} {1:1} "output*.out" -q EXCEPTFILE:"*100*"

    - {2:1} for each file
        # {0:1} {1:1} "report1.out, report2.out.gz" -q NOMERGE
        # {0:1} {1:1} "output*.out" -q NOMERGE

    - {2:1} for each file after trimming specific ticks
        # {0:1} {1:1} "output*.out" -q NOMERGE, TRIMIDX::5
        # {0:1} {1:1} "output*.out" -q NOMERGE, TRIMIDX:-3:

    - {2:1} for each file in specific list
        # {0:1} {1:1} {2:1} -q NOMERGE, FILELIST:-1
        # {0:1} {1:1} {2:1} -q NOMERGE, FILELIST:0:10

    - {2:1} after applying task filter
        # {0:1} {1:1} output.out -g "a.out*"
        # {0:1} {1:1} "output*.out" -g "*a.out*"

    - {2:1} with kernel memory
        # {0:1} {1:1} "report1.out, report2.out.gz" -q INCKERNELMEM

    - {2:1} with relation tree info
        # {0:1} {1:1} "report1.out, report2.out.gz" -q PSTREE

    - {2:1} after removing special characters
        # {0:1} {1:1} "report*.out" -q CLEANCHAR

    - {2:1} in reversed file order
        # {0:1} {1:1} "report*.out" -q REVERSEFILE

    - {2:1} in reversed sample order
        # {0:1} {1:1} "report*.out" -q REVERSESAMPLE

    - {2:1} without header info
        # {0:1} {1:1} "report*.out" -q NOHEADER

    - {2:1} without process info
        # {0:1} {1:1} "report*.out" -q ONLYTOTAL

    - {2:1} with specific resource info
        # {0:1} {1:1} "report*.out" -q SUMMARYFILTER:CPU+RSS
        # {0:1} {1:1} "report*.out" -q SUMMARYFILTER:CGCPU+CGBLOCK

    - {2:1} without detailed info
        # {0:1} {1:1} "report*.out" -q ONLYSUMMARY

    - {2:1} with GPU memory summary
        # {0:1} {1:1} "report*.out" -q GPUMEMSUMMARY

    - {2:1} after adding GPU memory to RSS
        # {0:1} {1:1} "report*.out" -q GPUMEMSUM2

    - {2:1} and print in JSON format after converting all value types to numeric formats
        # {0:1} {1:1} "report*.out" -J -q CONVUNIT2SIZE

    - {2:1} after converting the unique physical memory (RSS-TEXT-SHM)
        # {0:1} {1:1} "report*.out" -q EXCEPTSHM

    - {2:1} into guider.out
        # {0:1} {1:1} output.out.gz -o guider.out

    - {2:1} and extract embedded files
        # {0:1} {1:1} output.out -q EXTRACTFILE:"*"
        # {0:1} {1:1} output.out -q EXTRACTFILE:"*/test.zip"

    - {2:1} and classify process group
        # {0:1} {1:1} "report*.out" -q CLASSIFYGROUP:CPU
        # {0:1} {1:1} "report*.out" -q CLASSIFYGROUP:MEM
        # {0:1} {1:1} "report*.out" -q CLASSIFYGROUP:BLK
        # {0:1} {1:1} "report*.out" -q CLASSIFYGROUP, NRCLASSTOP:10
        # {0:1} {1:1} "report*.out" -q CLASSIFYGROUP, MAXFILECNT:1000
        # {0:1} {1:1} "report*.out" -q CLASSIFYGROUP, INCFILTER:"guider*", EXCFILTER:"*old.out"
        # {0:1} {1:1} "report*.out" -q CLASSIFYGROUP, CLASSSYSBT:90, CLASSALLTASKLT:300
        # {0:1} {1:1} "report*.out" -q CLASSIFYGROUP, CLASSSYSBT:90, PRINTRAW
        # {0:1} {1:1} "report*.out" -q CLASSIFYGROUP, CLASSTASKBT:90, PRINTRAW
        # {0:1} {1:1} "report*.out" -q CLASSIFYGROUP, TASKOUTFILTER:"system*":BT:50, CLASSINCFILTER:"*system*", PRINTRAW
        # {0:1} {1:1} "report*.out" -q CLASSIFYGROUP, UPTIMEBT:120
                    """.format(
                        cmd, mode, "Summarize raw data of the top report files"
                    )

                # kill / send #
                elif SysMgr.isKillMode():
                    helpStr = """
Usage:
    # {0:1} {1:1} -<SIGNUM|SIGNAME> <PID|COMM> [OPTIONS] [--help]

Description:
    Send the specific signal to specific tasks or all Guider processes

Options:
    -i  <SEC>                   set interval
    -l                          print signal list
    -W  <SEC>                   wait for input
    -v                          verbose
                    """.format(
                        cmd, mode
                    )

                    helpStr += """
Examples:
    - Send the notification signal to all Guider processes
        # {0:1} {1:1}

    - {2:1}
        # {0:1} {1:1} -sigstop 1234
        # {0:1} {1:1} -sigstop a.out
        # {0:1} {1:1} -sigstop "a.out*"

    - {2:1} after 5 seconds
        # {0:1} {1:1} -sigstop 1234 -W 5s

    - {2:1} until one gets the signal
        # {0:1} {1:1} -sigstop a.out -q WAITTASK
        # {0:1} {1:1} -sigstop a.out -q WAITTASK:1
        # {0:1} {1:1} -sigstop a.out -q WAITTASK, NOPIDCACHE

    - {2:1} every 2 seconds
        # {0:1} {1:1} -9 1234 -i 2
        # {0:1} {1:1} -sigkill 1234 -i 2
                    """.format(
                        cmd, mode, "Send SIGSTOP to specific tasks"
                    )

                # pause #
                elif SysMgr.checkMode("pause"):
                    helpStr = (
                        _getDesc("Pause specific threads", t=3)
                        + """
Options:
    -g  <TID|COMM>              set task filter
    -R  <TIME>                  set timer
    -u                          run in the background
    -P                          group threads in a same process
    -v                          verbose
                        """
                    )

                    helpStr += """
Examples:
    - Pause specific threads for 3 seconds
        # {0:1} {1:1} -g a.out -R 3
        # {0:1} {1:1} -g 1234 -R 3
        # {0:1} {1:1} -g "a*" -R 3

    - Pause specific threads including a same process group
        # {0:1} {1:1} -g a.out -P
                    """.format(
                        cmd, mode
                    )

                # freeze #
                elif SysMgr.checkMode("freeze"):
                    helpStr = (
                        _getDesc("Freeze specific tasks", t=3)
                        + """
Options:
    -g  <TID|COMM>              set task filter
    -R  <TIME>                  set timer
    -u                          run in the background
    -P                          group threads in a same process
    -v                          verbose
                        """
                    )

                    helpStr += """
Examples:
    - Freeze specific processes for 3 seconds
        # {0:1} {1:1} -g a.out -R 3
        # {0:1} {1:1} -g 1234 -R 3
        # {0:1} {1:1} -g "a*" -R 3

    - Freeze specific threads including a same process group
        # {0:1} {1:1} -g a.out -et -P
                    """.format(
                        cmd, mode
                    )

                # readelf #
                elif SysMgr.checkMode("readelf"):
                    helpStr = (
                        _getDesc("Show information about ELF file", t=5)
                        + """
Options:
        -e  <CHARACTER>             enable options
              [ p:pipe | d:demangle | D:DWARF | e:encode ]
        -I  <FILE>                  set input path
        -g  <ADDR|SYMBOL>           set function filter
        -q  <NAME{:VALUE}>          set environment variables
        -v                          verbose
                        """
                    )

                    helpStr += """
Examples:
    - {2:1}
        # {0:1} {1:1} {3:1}
        # {0:1} {1:1} -I test.zip
        # {0:1} {1:1} -I test.apk

    - {2:1} with LLVM demangler
        # {0:1} {1:1} {3:1} -q LIBLLVM:libLLVM-10.so

    - Print vDSO information
        # {0:1} {1:1} -I vdso

    - {2:1} with debug information
        # {0:1} {1:1} {3:1} -q DEBUGINFO

    - {2:1} with string sections
        # {0:1} {1:1} {3:1} -q PRINTSTR

    - {2:1} without using debug files
        # {0:1} {1:1} {3:1} -q NODEBUG

    - {2:1} only for specific sections
        # {0:1} {1:1} {3:1} -q TARGETSECT:"header", TARGETSECT:".symtab"
        # {0:1} {1:1} {3:1} -q TARGETSECT:"program", TARGETSECT:"*debug*", DEBUGINFO
        # {0:1} {1:1} {3:1} -q EXCEPTSECT:"*debug*", DEBUGINFO

    - {2:1} and export specific sections to file
        # {0:1} {1:1} {3:1} -q EXPORTSECT:".debug_info"
        # {0:1} {1:1} {3:1} -q EXPORTSECT:".debug*"

    - {2:1} in JSON format
        # {0:1} {1:1} {3:1} -J

    - {2:1} in JSON format with embedded objects in zip file
        # {0:1} {1:1} -I test.zip -J -q INCZIPOBJ

    - {2:1} with specific oatdump path
        # {0:1} {1:1} -I framework.oat -q OATDUMP:/usr/bin/oatdump
                    """.format(
                        cmd,
                        mode,
                        "Print ELF information of a specific file",
                        "-I /usr/bin/yes",
                    )

                # log #
                elif (
                    SysMgr.checkMode("logdlt")
                    or SysMgr.checkMode("logjrl")
                    or SysMgr.checkMode("logkmsg")
                    or SysMgr.checkMode("logsys")
                    or SysMgr.checkMode("logtrace")
                    or SysMgr.checkMode("logand")
                ):
                    helpStr = logCommonStr

                    # logdlt #
                    if SysMgr.checkMode("logdlt"):
                        helpStr += """
    - {2:1} without checking daemon
        # {0:1} {1:1} -q NOCHECKDAEMON
                    """.format(
                            cmd,
                            mode,
                            "Log a DLT message",
                        )
                    # logtrace #
                    elif SysMgr.checkMode("logtrace"):
                        helpStr += """
    - {2:1} with control for specific trace points
        # {0:1} {1:1} -q ENABLETP:"irq*"
        # {0:1} {1:1} -q DISABLETP:"irq*"
        # {0:1} {1:1} -q CLEARFILTER

    - {2:1} with trace options
        # {0:1} {1:1} -q TRACEOPT:irq-info, TRACEOPT:hash-ptr

    - {2:1} after enabling tracing forcefully
        # {0:1} {1:1} -f

    - Print tracepoint list
        # {0:1} {1:1} -l
        # {0:1} {1:1} -l -H -a
                    """.format(
                            cmd,
                            mode,
                            "Log a ftrace message",
                        )

                # printlog #
                elif (
                    SysMgr.checkMode("printdlt")
                    or SysMgr.checkMode("printdbus")
                    or SysMgr.checkMode("printjrl")
                    or SysMgr.checkMode("printkmsg")
                    or SysMgr.checkMode("printand")
                    or SysMgr.checkMode("printsyslog")
                    or SysMgr.checkMode("printtrace")
                ):
                    helpStr = printCommonStr

                    # printdbus #
                    if SysMgr.checkMode("printdbus"):
                        helpStr += """
    - {2:1} with detailed information in real-time
        # {0:1} {1:1} -a

    - {2:1} including incoming messages
        # {0:1} {1:1} -q INCINMSG

    - {2:1} including specific word in real-time
        # {0:1} {1:1} -c test

    - {2:1} with backtrace only for specific processes in real-time
        # {0:1} {1:1} -g a.out -H

    - {2:1} with backtrace only for a new process in real-time
        # {0:1} {1:1} a.out -H

    - {2:1} for a specific session bus
        # DBUS_SESSION_BUS_ADDRESS=$(cat addr) {0:1} {1:1}

    - {2:1} except for specific messages
        # {0:1} {1:1} -G sendData

    - {2:1} {3:1}
        # {0:1} {1:1} -q WATCHLOG:"*oops*"

    - {2:1} {3:1} and execute specific commands when terminated
        # {0:1} {1:1} -q WATCHLOG:"*oops*", WATCHLOGCMD:"ls -lha"
                    """.format(
                            cmd,
                            mode,
                            "Print D-Bus messages",
                            (
                                "with detailed information in real-time until "
                                "a log containing a specific word is detected"
                            ),
                        )

                    # printkmsg / printsys #
                    elif SysMgr.checkMode("printkmsg") or SysMgr.checkMode(
                        "printsyslog"
                    ):
                        helpStr += """
    - Print logs including specific words
        # {0:1} {1:1} -g "*test*"
                    """.format(
                            cmd, mode
                        )

                    # printjrl #
                    elif SysMgr.checkMode("printjrl"):
                        helpStr += """
    - {2:1} for all
        # {0:1} {1:1} -a

    - {2:1} including specific words in real-time
        # {0:1} {1:1} -g "*test*"

    - {2:1} with all fields in real-time
        # {0:1} {1:1} -I

    - {2:1} with specific fields in real-time
        # {0:1} {1:1} -I _TIME, _COMM, _PID
                    """.format(
                            cmd, mode, "Print the systemd journal"
                        )

                    # printdlt #
                    elif SysMgr.checkMode("printdlt"):
                        helpStr += """
    - {2:1} {3:1}
        # {0:1} {1:1} "./*.dlt"
        # {0:1} {1:1} -I "./*.dlt"

    - {2:1} {3:1} from current directory to all sub-directories
        # {0:1} {1:1} "**/*.dlt"

    - {2:1} {3:1} decompressed
        # {0:1} {1:1} "./*.dlt" -q DECOMP
        # {0:1} {1:1} "./**" -q DECOMP -dL

    - {2:1} summary {3:1}
        # {0:1} {1:1} "./*.dlt" -q PRINTSUMMARY

    - {2:1} with the file path
        # {0:1} {1:1} "./*.dlt" -q PRINTPATH
        # {0:1} {1:1} "./*.dlt" -q PRINTFILE

    - {2:1} with count limit
        # {0:1} {1:1} "./*.dlt" -q NRMSG:5

    - {2:1} from all trace files
        # {0:1} {1:1} -q TRACEFILE

    - {2:1} with file limit
        # {0:1} {1:1} "./*.dlt" -q PRINTFILE, NRFILE:5

    - Find trace files from specific time
        # {0:1} {1:1} "./*.dlt" -q PRINTFILE, NRFILE:1, NRMSG:1, TAIL:123.123 -dL
        # {0:1} {1:1} "./**" -q PRINTFILE, NRMSG:1 -dL
        # {0:1} {1:1} "./*.dlt" -q FINDFILE, TAIL:-2000, UNTIL:-10
        # {0:1} {1:1} "./*.dlt" -q FINDFILE, TAIL:-2000, UNTIL:-10, USEUPTIME
        # {0:1} {1:1} "./*.dlt" -q FINDFILE, TAIL:2024-04-11T01:30:00Z, UNTIL:2024-04-13T00:00:00Z
        # {0:1} {1:1} "./*.dlt" -q FINDFILE, TAIL:-2000, UNTIL:-10 -J
        # {0:1} {1:1} "./*.dlt" -q FINDFILE, TAIL:-2000, UNTIL:-10, SETCURRENT:1234.123

    - {2:1} from the last trace file
        # {0:1} {1:1} -q LASTFILE

    - {2:1} including all in the past
        # {0:1} {1:1} -a

    - {2:1} including specific words
        # {0:1} {1:1} -g test

    - {2:1} after changing default log level to be printed
        # {0:1} {1:1} -c INFO

    - {2:1} after applying log level filter
        # {0:1} {1:1} -c INFO -q FIXLEVEL

    - {2:1} using libdlt.so from specific path
        # {0:1} {1:1} -q LIBDLT:/home/iipeace/test/libdlt.so
        # {0:1} {1:1} "test.dlt" -q LIBDLT:C:\\Users\\PeaceLee\\Downloads\\qdlt.dll

    - {2:1} sorted by line {3:1}
        # {0:1} {1:1} "./*.dlt" -S
        # {0:1} {1:1} -I "./*.dlt" -S

    - {2:1} from specific address for dlt-daemon
        # {0:1} {1:1} -X localhost:12345
        # {0:1} {1:1} -X 127.0.0.1:12345

    - {2:1} with connection retry to dlt-daemon every 1,000 ms
        # {0:1} {1:1} -q RETRYCONN:1000
                    """.format(
                            cmd,
                            mode,
                            "Print DLT messages",
                            "from specific files",
                        )

                    # printand #
                    elif SysMgr.checkMode("printand"):
                        helpStr += """
    - {2:1} for all
        # {0:1} {1:1} -a

    - {2:1} including specific words in real-time
        # {0:1} {1:1} -g test

    - {2:1} with thread info
        # {0:1} {1:1} -e t
                    """.format(
                            cmd, mode, "Print android logs"
                        )

                    # printtrace #
                    elif SysMgr.checkMode("printtrace"):
                        helpStr += """
    - {2:1} only having specific words
        # {0:1} {1:1} -g test, kworker

    - {2:1} with control for specific trace points
        # {0:1} {1:1} "irq/softirq*, sched"
        # {0:1} {1:1} "sched/sched_blocked_reason"
        # {0:1} {1:1} -q DISABLETP:"irq*"
        # {0:1} {1:1} -q CLEARFILTER

    - {2:1} with trace options
        # {0:1} {1:1} -q TRACEOPT:irq-info, TRACEOPT:hash-ptr

    - {2:1} after flushing the ring buffer
        # {0:1} {1:1} -q FLUSH

    - {2:1} with consuming the ring buffer
        # {0:1} {1:1} -q CONSUME

    - Print tracepoint list
        # {0:1} {1:1} -l
        # {0:1} {1:1} -l -H -a

    - {2:1} including process name in JSON format
        # {0:1} {1:1} -q INCPROCNAME
                    """.format(
                            cmd, mode, "Print ftrace logs"
                        )

                # printsig #
                elif SysMgr.checkMode("printsig"):
                    helpStr = (
                        _getDesc(
                            "Show signal status for specific processes", t=2
                        )
                        + """
Options:
    -o  <DIR|FILE>              set output path
    -g  <PID|COMM>              set task filter
    -J                          print in JSON format
    -v                          verbose
                        """
                    )

                    helpStr += """
Examples:
    - Print signal status info for specific processes
        # {0:1} {1:1} "a.out, java"
        # {0:1} {1:1} -g a.out, java

    - Print signal status info for specific threads
        # {0:1} {1:1} "a.out, java" -e t
        # {0:1} {1:1} -g a.out, java -e t
                    """.format(
                        cmd, mode
                    )

                # print{dbus|systemd}* #
                elif SysMgr.getMode() in (
                    "printdbusintro",
                    "printdbussub",
                    "printdbusstat",
                    "printsdunit",
                    "printsdinfo",
                ):
                    ocmd = SysMgr.getMode()
                    if ocmd == "printdbusintro":
                        msg = "Print D-Bus introspection"
                    elif ocmd == "printdbussub":
                        msg = "Print D-Bus signal subscription info"
                    elif ocmd == "printdbusstat":
                        msg = "Print D-Bus stats"
                    elif ocmd == "printsdunit":
                        msg = "Print systemd unit status"
                    elif ocmd == "printsdinfo":
                        msg = "Print systemd info"

                    helpStr = (
                        _getDesc(msg)
                        + """
Options:
    -v                          verbose
    -a                          show all info
    -c  <WORD>                  set item filter or input
    -g  <COMM>                  set task filter
    -o  <DIR|FILE>              set output path
    -J                          print in JSON format
    -Q                          print all rows in a stream
                        """
                    )

                    helpStr += """
Examples:
    - {2:1}
        # {0:1} {1:1}

    - {2:1} with specific input
        # {0:1} {1:1} -c ActiveEnterTimestampMonotonic
        # {0:1} {1:1} -c cloud-config.service
        # {0:1} {1:1} -c /org/freedesktop/systemd1/unit/_2d_2eslice
        # {0:1} {1:1} -c "*service"
        # {0:1} {1:1} -c "*service" -a

    - {2:1} for all
        # {0:1} {1:1} -a

    - {2:1} to a specific file
        # {0:1} {1:1} -o guider.out

    - {2:1} in JSON format
        # {0:1} {1:1} -J
        # {0:1} {1:1} -J -Q
                    """.format(
                        cmd,
                        mode,
                        msg,
                    )

                    if ocmd == "printsdinfo":
                        helpStr += """
    - {2:1} only for boot time
        # {0:1} {1:1} -q ONLYBOOT
        # {0:1} {1:1} -q ONLYBOOT, SORTBYSTART

    - {2:1} with specific filters
        # {0:1} {1:1} -c "*Enable*" -q ALLFILTER
        # {0:1} {1:1} -q SDFILTER:ActiveState:"active\,inactive"
        # {0:1} {1:1} -q SDFILTER:+ActiveState:active, SDFILTER:+Id:"daily*"
        # {0:1} {1:1} -q SDEXFILTER:ActiveState:"active"
        # {0:1} {1:1} -q SDITEM:"Active*"

    - {2:1} and execute commands with specific filters
        # {0:1} {1:1} -q CMD:"sh -c \\"echo @Id >> out\\"", MUTE
        # {0:1} {1:1} -q SDFILTER:Id:"*service.service", CMD:"systemctl stop @Id", MUTE
        # {0:1} {1:1} -q SDFILTER:+Id:"*service.service", SDFILTER:+ActiveState:active, CMD:"systemctl stop @Id", MUTE
                    """.format(
                            cmd,
                            mode,
                            msg,
                        )

                # checkdup#
                elif SysMgr.checkMode("checkdup"):
                    helpStr = (
                        _getDesc(
                            "Check duplicated page frames of specific processes",
                            t=2,
                        )
                        + """
Options:
    -I  <FILE>                  set input path
    -a                          show page contents
    -o  <DIR|FILE>              set output path
    -g  <PID|COMM>              set task filter
    -q  <NAME{:VALUE}>          set environment variables
    -S  <CHARACTER{:VALUE}>     sort by key
          [ a:address / s:size / f:function ]
    -J                          print in JSON format
    -v                          verbose
                        """
                    )

                    helpStr += """
Examples:
    - {2:1}
        # {0:1} {1:1} "a.out, java"
        # {0:1} {1:1} -g a.out, java
        # {0:1} {1:1} "*service" -q TARGETEXE
        # {0:1} {1:1} "*service" -q TARGETCMD

    - {2:1} with page contents
        # {0:1} {1:1} "a.out, java" -a
        # {0:1} {1:1} "a.out, java" -a -q BT:555
        # {0:1} {1:1} "a.out, java" -a -q LT:1000

    - {2:1} with page contents only including specific words
        # {0:1} {1:1} "a.out, java" -a -q MEMFILTER:"PEACE"

    - {2:1} without file-mapped pages
        # {0:1} {1:1} "a.out, java" -a -q SKIPFILE

    - {2:1} and print process summary list finally
        # {0:1} {1:1} "a.out, java" -q SKIPDUP, PRINTLIST

    - Check all duplicated page frames of all user processes
        # {0:1} {1:1} "*" -q ONLYUSER
        # {0:1} {1:1} "*" -q ONLYUSER, PERPROCINFO -a

    - Print only stats without checking duplication
        # {0:1} {1:1} "a.out, java" -q ONLYSTAT

    - Check duplicated page frames {3:1}
        # {0:1} {1:1} -I guider.out
        # {0:1} {1:1} -I guider.out -q TARGETSYM:"*testFile*"
        # {0:1} {1:1} -I "guider.out, guider2.out" -q TARGETSYM:"*testFile*"
        # {0:1} {1:1} -I "guider*.out" -q TARGETSYM:"*testFile*"
        # {0:1} {1:1} -I guider.out -q EXCEPTSYM:"*verifyFile*"

    - Check all duplicated virtual chunks {3:1}
        # {0:1} {1:1} -I guider.out -a -q REALCHUNK

    - Print all duplicated valid chunks based on callstack {3:1}
        # {0:1} {1:1} -I guider.out -q SHOWMEM
        # {0:1} {1:1} -I guider.out -q SHOWMEM, TARGETSYM:"*testFile*"
        # {0:1} {1:1} -I guider.out -q SHOWMEM -S s
        # {0:1} {1:1} -I guider.out -q SHOWMEM, SHOWHIST
        # {0:1} {1:1} -I guider.out -q SHOWMEM, MERGECALL
        # {0:1} {1:1} -I guider.out -q SHOWMEM, MEMFILTER:"PEACE"

    - Draw a flame graph for duplicated chunk size of each callstack
        # {0:1} {1:1} -I guider.out -q SHOWMEM, MERGECALL, DRAWDUPFLAME

    - Check specific duplicated page frames of specific processes
        # {0:1} {1:1} "a.out, java" -q ONLYHEAP
        # {0:1} {1:1} "a.out, java" -q ONLYSTACK
        # {0:1} {1:1} "a.out" -q MEMAREA:"0x556370ef0000-0x556371afc000"

    - {2:1} and execute specific remote commands for each memory segments
        # {0:1} {1:1} "a.out, java" -c madvise:START:SIZE:DONTNEED
        # {0:1} {1:1} "*" -c madvise:START:SIZE:MERGEABLE -q ONLYUSER, SKIPDUP, FLUSHFILECACHE
                    """.format(
                        cmd,
                        mode,
                        "Check all duplicated page frames of specific processes",
                        "from leaktrace or mtrace reports",
                    )

                # watch #
                elif SysMgr.checkMode("watch") or SysMgr.checkMode("fetop"):
                    if SysMgr.checkMode("fetop"):
                        act = "Monitor"
                        addStr = "in real-time"
                    else:
                        act = "Watch"
                        addStr = ""

                    helpStr = """
Usage:
    # {0:1} {1:1} <PATH> [OPTIONS] [--help]

Description:
    {3:1} specific files or directories {2:1}

Options:
    -g  <PATH:EVENT:FILE:CMD>   set condition
    -l                          print event list
    -v                          verbose
                        """.format(
                        cmd, mode, addStr, act
                    )

                    helpStr += """
Examples:
    - {4:1} {5:1} {2:1}
        # {0:1} {1:1}
        # {0:1} {1:1} -f

    - {4:1} specific files with file filter
        # {0:1} {1:1} "*.txt" -q TARGETFILE:"*test*", EXCEPTFILE:"*100*"

    - {4:1} {5:1} and print summary to specific file {2:1}
        # {0:1} {1:1} -o {4:1}.out
        # {0:1} {1:1} -o {4:1}.out -f
        # {0:1} {1:1} -o {4:1}.out -q TARGETEVT:IN_OPEN

    - {4:1} {5:1} with process info {3:1} {2:1}
        # {0:1} {1:1} -q PROCINFO
        # {0:1} {1:1} -q PROCINFO, LARGEFILE
        # {0:1} {1:1} -q PROCINFO, TARGETCOMM:"kworker*", EXCEPTCOMM:"*1234"
        # {0:1} {1:1} -q PROCINFO, TARGETFILE:"*.data", EXCEPTFILE:"temp*"
        # {0:1} {1:1} -q PROCINFO, EVENTCMD:"ls -lha", EVENTCMD:"touch PATH&"

    - {4:1} {5:1} with thread info {3:1} {2:1}
        # {0:1} {1:1} -q PROCINFO, REPORTTID

    - {4:1} the current mount point with process info {3:1} {2:1}
        # {0:1} {1:1} -q INMOUNT
        # {0:1} {1:1} -q INMOUNT, TARGETCOMM:"kworker*", EXCEPTCOMM:"*1234"
        # {0:1} {1:1} -q INMOUNT, TARGETFILE:"*.data", EXCEPTFILE:"temp*"

    - {4:1} all mount points with process info {3:1} {2:1}
        # {0:1} {1:1} -q ALLMOUNT
        # {0:1} {1:1} -q ALLMOUNT, TARGETCOMM:"kworker*", EXCEPTCOMM:"*1234"
        # {0:1} {1:1} -q ALLMOUNT, TARGETFILE:"*.data", EXCEPTFILE:"temp*"
        # {0:1} {1:1} -q ALLMOUNT, EVENTCMD:"ls -lha", EVENTCMD:"touch PATH&"
        # {0:1} {1:1} -q ALLMOUNT, TARGETPATH:"/media*", EXCEPTPATH:"/proc*"

    - {4:1} specific events for all mount points with process info {3:1} {2:1}
        # {0:1} {1:1} -q ALLMOUNT, TARGETEVT:FAN_MODIFY, TARGETEVT:FAN_CLOSE_WRITE

    - {4:1} specific files to be created and terminate after all them created {2:1}
        # {0:1} {1:1} "/home/iipeace/testFile1, /home/iipeace/testFile2"

    - {4:1} specific files to be created and monitor them continually {2:1}
        # {0:1} {1:1} "/home/iipeace/testFile1, /home/iipeace/testFile2" -q CONT

    - {4:1} multiple directories {2:1}
        # {0:1} {1:1} "/home/iipeace/test, /home/iipeace/test/sub"

    - {4:1} all sub-directories of {5:1} {2:1}
        # {0:1} {1:1} -q INCSUBDIRS

    - Print event list
        # {0:1} {1:1} -l

    - {4:1} specific events for specific files in {5:1} {2:1}
        # {0:1} {1:1} ".:IN_CREATE|IN_CLOSE:a.out"

    - {4:1} specific events for specific files in {5:1} and print the contents of the files {2:1}
        # {0:1} {1:1} ".:IN_MODIFY:a.out" -q PRINTFILE
        # {0:1} {1:1} ".:IN_MODIFY:a.*" -q PRINTFILE
        # {0:1} {1:1} ".:IN_MODIFY:a.out|b.out" -q PRINTFILE
        # {0:1} {1:1} ".:IN_MODIFY:a.out" -q PRINTFILE, TAIL:3

    - {4:1} specific events in {5:1} and terminate if the events occur {2:1}
        # {0:1} {1:1} ".:IN_CREATE|IN_CLOSE:a.out:EXIT"

    - {4:1} specific events in {5:1} and execute specific commands if the events occur {2:1}
        # {0:1} {1:1} ".:::ls -lha"
        # {0:1} {1:1} ".::a.out:ls -lha a.out"
        # {0:1} {1:1} ".:IN_CREATE|IN_CLOSE:a.out:GUIDER event"
                    """.format(
                        cmd,
                        mode,
                        addStr,
                        "except for CREATE, DELETE, MOVE events",
                        act,
                        "the current directory",
                    )

                # addr2sym #
                elif SysMgr.checkMode("addr2sym"):
                    helpStr = """
Usage:
    # {0:1} {1:1} <FILE|PID|COMM> -g <OFFSET> [OPTIONS] [--help]

Description:
    Show symbols mapped to specific addresses from specific files or specific processes

Options:
    -I  <FILE|COMM|PID>         set input path or process
    -g  <OFFSET>                set offset
    -q  <NAME{{:VALUE}}>          set environment variables
    -v                          verbose
                        """.format(
                        cmd, mode
                    )

                    helpStr += """
Examples:
    - {2:1} from specific files
        # {0:1} {1:1} -I /usr/bin/yes -g {3:1}
        # {0:1} {1:1} -I /system/bin/test.apk -g {3:1}
        # {0:1} {1:1} -I "/usr/lib/*" -g {3:1}

    - Print merged symbols mapped to specific addresses from specific files
        # {0:1} {1:1} -I /usr/bin/yes -g {3:1} -q MERGESYM

    - Print all symbols mapped to specific addresses from specific files
        # {0:1} {1:1} -I /usr/bin/yes -g {3:1} -q ALLSYM

    - {2:1} from specific files considering file caches
        # {0:1} {1:1} -I /usr/bin/yes -g {3:1} -q FORCEFILECACHE
        # {0:1} {1:1} -I /usr/bin/yes -g {3:1} -q NOFILECACHE

    - {2:1} from specific processes
        # {0:1} {1:1} -I "yes" -g {3:1}
        # {0:1} {1:1} -I "yes|systemd" -g {3:1}
                    """.format(
                        cmd,
                        mode,
                        "Print symbols mapped to specific addresses",
                        "0xab1cf",
                    )

                # mkcache #
                elif SysMgr.checkMode("mkcache"):
                    helpStr = """
Usage:
    # {0:1} {1:1} <FILE|COMM|PID> [OPTIONS] [--help]

Description:
    Make ELF caches for specific files or specific processes

Options:
    -I  <FILE|COMM|PID>         set input path or process
    -v                          verbose
                        """.format(
                        cmd, mode
                    )

                    helpStr += """
Examples:
    - {2:1} in parallel
        # {0:1} {1:1} "a.out, yes"

    - {2:1} with system monitor in parallel
        # {0:1} {1:1} "a.out, yes" -q SYSMON

    - {2:1} in sequantial
        # {0:1} {1:1} "a.out, yes" -q SEQUANTIAL

    - Make a ELF cache for /usr/bin/yes with ELF symbol filter in sequantial
        # {0:1} {1:1} /usr/bin/yes -q SEQUANTIAL, ELFSYMFILTER:"*test*"

    - Make a ELF cache for /usr/bin/yes file
        # {0:1} {1:1} /usr/bin/yes

    - Remove all cache files
        # {0:1} {1:1} -q REMOVECACHE

    - {2:1} in parallel considering cache files
        # {0:1} {1:1} "a.out, yes" -q FORCEFILECACHE
        # {0:1} {1:1} "a.out, yes" -q NOFILECACHE
                    """.format(
                        cmd,
                        mode,
                        "Make ELF caches for a.out and yes processes",
                    )

                # readahead #
                elif SysMgr.checkMode("readahead"):
                    helpStr = """
Usage:
    # {0:1} {1:1} <FILE> -g <OFFSET> [OPTIONS] [--help]

Description:
    Initiate file readahead into page cache

Options:
    -I  <FILE>                  set input path
    -W  <SEC>                   wait for input
    -Y  <VALUES>                set sched
          [ POLICY:PRIO|TIME{{:TID|COMM:CONT}}
            CLASS:{{WHICH:}}PRIO:TID|COMM ]
    -v                          verbose

Specification:
    - Format of the readahead list
        1. SIZE of the file name list: 4 Bytes
        2. STRING of the file name list: SIZE bytes
            - File names are seperated by '#'
        3. CHUNKS
            - IDX (file index in file name list): 2 Bytes
            - OFFSET (offset in the file): 8 Bytes
            - SIZE (readahead size): 4 Bytes

    - Format of the filter list
        1. File path
            - /data/test
            - /data/test*
            - /data/*test*
        2. File range
            - /data/test:4096:100M
            - /data/test:4096:4096

    - CAUTION
        - The maximum size of a valid size readahead syscall is about 1MB
        - Readahead chunks larger than 1MB are automatically split into multiple chunks by Guider
                        """.format(
                        cmd, mode
                    )

                    helpStr += """
Examples:
    - {2:1}
        # {0:1} {1:1} {3:1}

    - {2:1} after setting the scheduling priority
        # {0:1} {1:1} -Y "c:10, idle:1:0"

    - {2:1} using specific lists
        # {0:1} {1:1} {3:1} -q RAMIN:4096
        # {0:1} {1:1} {3:1} -q RAMAX:1048576
        # {0:1} {1:1} {3:1} -q RAALLOWLIST:allow.list
        # {0:1} {1:1} {3:1} -q RADENYLIST:deny.list
        # {0:1} {1:1} {3:1} -q RADENYITEM:"/run/*|/tmp/*"
        # {0:1} {1:1} {3:1} -q RAADDLIST:add.list
        # {0:1} {1:1} {3:1} -q RAADDITEM:"/usr/lib/*|/etc/*"

    - {2:1} and print summary
        # {0:1} {1:1} -q PRINTSUMMARY

    - Flush file caches on ram matching the readahead list
        # {0:1} {1:1} -q FLUSH

    - {2:1} and wait for signals
        # {0:1} {1:1} -q WAIT

    - Check readahead results from kernel log
        # {0:1} printkmsg -g "*guider*"
        # dmesg | grep guider
                    """.format(
                        cmd,
                        mode,
                        "Initiate file readahead from the readahead list into page cache",
                        "readahead.list",
                    )

                # sym2addr#
                elif SysMgr.checkMode("sym2addr"):
                    helpStr = """
Usage:
    # {0:1} {1:1} <FILE|COMM|PID> -g <SYMBOL> [OPTIONS] [--help]

Description:
    Show addresses mapped to specific symbols from specific files or specific processes

Options:
    -I  <FILE|COMM|PID>         set input path or process
    -g  <SYMBOL>                set offset
    -q  <NAME{{:VALUE}}>          set environment variables
    -v                          verbose
                        """.format(
                        cmd, mode
                    )

                    helpStr += """
Examples:
    - {2:1} from a file
        # {0:1} {1:1} -I {4:1} -g {3:1}
        # {0:1} {1:1} -I /system/bin/test.apk -g {3:1}

    - Print addresses of specific merged symbols from a file
        # {0:1} {1:1} -I {4:1} -g {3:1} -q MERGESYM

    - {2:1} from a file after removing redundant symbol tables from the file cache
        # {0:1} {1:1} -I {4:1} -g {3:1} -q RMSYMTAB

    - Print addresses of all symbols related to specific word from a file
        # {0:1} {1:1} -I {4:1} -g {3:1} -q ALLSYM

    - {2:1} considering file caches
        # {0:1} {1:1} -I {4:1} -g {3:1} -q FORCEFILECACHE
        # {0:1} {1:1} -I {4:1} -g {3:1} -q NOFILECACHE

    - {2:1} from specific files
        # {0:1} {1:1} -I "/usr/bin/*" -g {3:1}

    - Print addresses of all symbols from a file
        # {0:1} {1:1} -I {4:1} -g "*"

    - {2:1} including specific word from a file
        # {0:1} {1:1} -I {4:1} -g "*{3:1}"
        # {0:1} {1:1} -I {4:1} -g "{3:1}*"
        # {0:1} {1:1} -I {4:1} -g "*{3:1}*"

    - {2:1} including specific word from a file
        # {0:1} {1:1} -I ~/test/mutex -g "std::_Vector_base<unsigned long\, std::allocator<unsigned long> >::~_Vector_base()"

    - {2:1} from specific processes
        # {0:1} {1:1} -I "yes" -g {3:1}
        # {0:1} {1:1} -I "yes|systemd" -g {3:1}
                    """.format(
                        cmd,
                        mode,
                        "Print addresses of specific symbols",
                        "testFunc",
                        "/usr/bin/yes",
                    )

                # printcgroup #
                elif SysMgr.checkMode("printcg"):
                    helpStr = (
                        _getDesc("Show system cgroup tree")
                        + """
Options:
    -v                          verbose
    -a                          show all processes
    -g  <COMM>                  set task filter
    -H  <LEVEL>                 set depth level
    -J                          print in JSON format
    -o  <DIR|FILE>              set output path
    -Q                          print all rows in a stream
                        """
                    )

                    helpStr += """
Examples:
    - {2:1}
        # {0:1} {1:1}

    - {2:1} for specific subsystem
        # {0:1} {1:1} cpu
        # {0:1} {1:1} "cpu*"
        # {0:1} {1:1} "cpu memory blkio"

    - {2:1} after mounting/umounting all cgroup subsystems
        # {0:1} {1:1} -q MOUNTCG
        # {0:1} {1:1} -q UMOUNTCG

    - {2:1} with tasks
        # {0:1} {1:1} -a

    - {2:1} except for no task group
        # {0:1} {1:1} -q SKIPNOTASK

    - {2:1} with tasks only for specific type
        # {0:1} {1:1} -a -q ONLYPROC
        # {0:1} {1:1} -a -q ONLYTASK

    - {2:1} with info of the representative task in each group
        # {0:1} {1:1} -a -q REPCOMM
        # {0:1} {1:1} -a -q REPCMDLINE

    - {2:1} with only specific tasks used resources
        # {0:1} {1:1} -a -q SKIPIDLE

    - {2:1} with only processes having specific name
        # {0:1} {1:1} -a -g kworker

    - {2:1} with the highlited processes having specific name
        # {0:1} {1:1} -a -c kworker

    - {2:1} with depth limitation
        # {0:1} {1:1} -H 3

    - {2:1} in JSON format
        # {0:1} {1:1} -J
        # {0:1} {1:1} -J -Q

                    """.format(
                        cmd, mode, "Print system cgroup tree"
                    )

                # exec #
                elif SysMgr.checkMode("exec"):
                    helpStr = """
Usage:
    # {0:1} {1:1} <COMMAND> [OPTIONS] [--help]

Description:
    Execute commands repeatedly with various conditions

Options:
    -v                          verbose
    -I  <COMMAND>               set commands
    -c  <VARIABLE>              set variables
    -C  <PATH>                  set config path
    -q  <NAME{{:VALUE}}>          set environment variables
    -R  <COUNT>                 set repeatation count
    -u                          run in the background

Variables:
    RANGE     number
    FILE      file path
    PID       process ID
    PCOMM     process name
    TID       thread ID
    TCOMM     thread name
                        """.format(
                        cmd, mode
                    )

                    helpStr += """
Examples:
    - {2:1}
        # {0:1} {1:1} -I "ls -lha"

    - {2:1} in background
        # {0:1} {1:1} -I "ls -lha" -u

    - {2:1} 5 times
        # {0:1} {1:1} -I "ls -lha" -R 5

    - {2:1} 5 times in parallel
        # {0:1} {1:1} -I "ls -lha" -R 5 -q PARALLEL

    - {2:1} only for %s seconds
        # {0:1} {1:1} -I "ls -lha" -q TIMEOUT:3s

    - {3:1} with RANGE variables
        # {0:1} {1:1} -I "touch RANGE" -c RANGE:1:100:0.1

    - {3:1} with FILE variables
        # {0:1} {1:1} -I "ls -lha FILE" -c FILE:"/data/*"
        # {0:1} {1:1} -I "ls -lha FILE" -c FILE:"/data/*" -q EXCEPTFILE:"*200*"

    - {3:1} with ID variables
        # {0:1} {1:1} -I "kill -9 PID" -c PID:"*task"
        # {0:1} {1:1} -I "kill -9 TID" -c TID:"*task"

    - {3:1} with COMM variables
        # {0:1} {1:1} -I "kill -9 PCOMM" -c PCOMM:"*task"
        # {0:1} {1:1} -I "kill -9 TCOMM" -c TCOMM:"*task"

    - {3:1} with RANGE and FILE variables
        # {0:1} {1:1} -I "echo PCOMM_RANGE" -c PCOMM:"*task", RANGE:1:10:1
        # {0:1} {1:1} -I "{0:1} {1:1} -I \"echo PCOMM=PID\" -c PID:PCOMM" -c PCOMM:"*task"

    - {3:1} with RANGE and FILE variables without logo and log
        # VERB=0 {0:1} {1:1} -I "{0:1} {1:1} -I \"echo PCOMM=PID\" -c PID:PCOMM" -c PCOMM:"*task"

    - {3:1} without shell
        # {0:1} {1:1} -I "ls -lha" -q NOSHELL

    - {3:1} with LDD
        # {0:1} {1:1} -I "ls -lha" -q NOSHELL, LDD

    - {3:1} including specific environment variables
        # {0:1} {1:1} -I "ls -lha FILE" -q ENV:TEST=1, ENV:PATH=/data
        # {0:1} {1:1} -I "ls -lha FILE" -q ENV:MALLOC_MMAP_THRESHOLD_=131072, ENV:MALLOC_ARENA_MAX=5
        # {0:1} {1:1} -I "ls -lha FILE" -q ENVFILE:/data/env.sh
        # {0:1} {1:1} -I "ls -lha FILE" -q CLEARENV
        # {0:1} {1:1} -I "ls -lha FILE" -q CLEARENV:HOME, CLEARENV:^LANGUAGE
        # {0:1} {1:1} -I "ls -lha FILE" -q ENVPROC:systemd

    - {2:1} and redirect standard I/O of child tasks to specific files
        # {0:1} {1:1} -I "ls" -q STDIN:"./stdin"
        # {0:1} {1:1} -I "ls" -q STDINDATA:"1\\n2\\n3\\n"
        # {0:1} {1:1} -I "ls" -q STDOUT:"./stdout"
        # {0:1} {1:1} -I "ls" -q STDERR:"/dev/null"

    - {2:1} after blocking specific signals
        # {0:1} {1:1} -I "sleep 100" -q IGNORESIGNAL:SIGHUP, IGNORESIGNAL:SIGINT

    - {2:1} after converting it using the config file
        # {0:1} {1:1} -I "CMD_TOP" -C guider.conf

    - {2:1} with monitoring
        # {0:1} {1:1} {5:1} {4:1}
        # {0:1} {1:1} {5:1} {4:1}:3
        # {0:1} {1:1} {5:1} {4:1} -a
        # {0:1} {1:1} {5:1} {4:1}, TASKFILTER:"*a.out*"
        # {0:1} {1:1} {5:1} {4:1}, TASKFILTER:"*a.out*" -et -P
        # {0:1} {1:1} {5:1} {4:1}, CHILDSCHED:c:0 -Y r:1 -z :2
        # {0:1} {1:1} {5:1} {4:1}, EXECSCHED:c:0 -Y r:1 -z :2
        # {0:1} {1:1} {5:1} {4:1}, TASKMONOPT:"-qNRTOPRANK:10|-Yr:1"

    - {2:1} with the cpu limitation in % unit using cgroup
        # {0:1} {1:1} {5:1} -q LIMITCPU:20
        # {0:1} {1:1} {5:1} -q LIMITCPU:20@"*yes*|a.out"
        # {0:1} {1:1} {5:1} -q LIMITCPU:cfs_quota_us:20000+cfs_period_us:100000@"*yes*|a.out"

    - {2:1} with the cpu set limitation using cgroup
        # {0:1} {1:1} {5:1} -q LIMITCPUSET:1
        # {0:1} {1:1} {5:1} -q LIMITCPUSET:"1-2"@"*yes*|a.out"
        # {0:1} {1:1} {5:1} -q LIMITCPUSET:"1-2&4"@"*yes*|a.out"

    - {2:1} with the memory limitation using cgroup
        # {0:1} {1:1} {5:1} -q LIMITMEM:50M
        # {0:1} {1:1} {5:1} -q LIMITMEM:50M+swappiness:10
        # {0:1} {1:1} {5:1} -q LIMITMEM:50M@"*yes*|a.out"

    - {2:1} with the block I/O limitation using cgroup
        # {0:1} {1:1} {5:1} -q LIMITREAD:50M
        # {0:1} {1:1} {5:1} -q LIMITWRITE:50M
        # {0:1} {1:1} {5:1} -q LIMITREAD:50M@"*yes*|a.out"
        # {0:1} {1:1} {5:1} -q LIMITWRITE:50M@"*yes*|a.out"
        # {0:1} {1:1} {5:1} -q LIMITWRITE:50M@"*yes*|a.out", EACHTASK

    - {2:1} applying cgroup
        # {0:1} {1:1} {5:1} -q APPLYCG:CREATE:cpu:user.slice:PID
        # {0:1} {1:1} {5:1} -q APPLYCG:ADD:cpu:/:PID
        # {0:1} {1:1} {5:1} -q APPLYCG:ADD:cpu:user.slice:PID
        # {0:1} {1:1} {5:1} -q APPLYCG:REMOVE:cpu:user.slice:PID
        # {0:1} {1:1} {5:1} -q APPLYCG:READ:memory:user.slice:memory.limit_in_bytes
        # {0:1} {1:1} {5:1} -q APPLYCG:WRITE:memory:user.slice:100000@memory.limit_in_bytes

    - {2:1} and print environment variables
        # {0:1} {1:1} {5:1} -q PRINTENV
                    """.format(
                        cmd,
                        mode,
                        "Execute a command",
                        "Execute commands",
                        "-q PARALLEL, TASKMON",
                        '-I "a.out"',
                    )

                # printext #
                elif SysMgr.checkMode("printext"):
                    helpStr = """
Usage:
    # {0:1} {1:1} <DEV> [OPTIONS] [--help]

Description:
    Show ext4 inode attributes from a specific device file

Options:
    -v                          verbose
    -I  <DIR>                   set input path
    -a                          show all attributes
    -g  <WORD>                  set filter
    -q  <NAME{{:VALUE}}>          set environment variables
                        """.format(
                        cmd, mode
                    )

                    helpStr += """
Examples:
    - Print all ext4 inode attributes from the specific device file
        # {0:1} {1:1} /dev/sda1

    - Print specific ext4 inode attributes from the specific device file
        # {0:1} {1:1} /dev/sda1 -g data
                    """.format(
                        cmd, mode
                    )

                # dirdiff #
                elif SysMgr.checkMode("dirdiff"):
                    helpStr = (
                        _getDesc(
                            "Show the difference between specific directories"
                        )
                        + """
Options:
    -v                          verbose
    -I  <DIR>                   set input path
    -a                          show all attributes
    -g  <WORD>                  set filter
    -c  <COMMAND>               set command
    -i  <SEC>                   set interval
    -q  <NAME{:VALUE}>          set environment variables
    -J                          print in JSON format
    -H  <LEVEL>                 set function depth level
    -Q                          print all rows in a stream
                        """
                    )

                    helpStr += """
Examples:
    - {2:1}
        # {0:1} {1:1} {3:1}

    - {2:1} with files
        # {0:1} {1:1} {3:1} -a

    - {2:1} after sorting per-directory
        # {0:1} {1:1} {3:1} -q SORT:SIZE
        # {0:1} {1:1} {3:1} -q SORT:TYPE

    - {2:1} after sorting all files
        # {0:1} {1:1} {3:1} -q SORT:SIZE -a -g "*"

    - {2:1} including linked directories from current working directory
        # {0:1} {1:1} {3:1} -q INCLINK

    - {2:1} with absolute path
        # {0:1} {1:1} {3:1} -q ABSPATH

    - {2:1} with time info
        # {0:1} {1:1} {3:1} -q TIMEINFO

    - {2:1} using full size without size unit
        # {0:1} {1:1} {3:1} -q FULLSIZE

    - {2:1} after saving specific directory info to a file
        # {0:1} printdir /data -J -a -o data.out -q FULLSIZE
        # {0:1} {1:1} "data.out, /tmp" -a

    - {2:1} for specific directories with waiting interval
        # {0:1} {1:1} "test1, test1" -i 100
        # {0:1} {1:1} {3:1} -i 100

    - {2:1} in 2-depth
        # {0:1} {1:1} {3:1} -H 2

    - {2:1} with files applying filter
        # {0:1} {1:1} {3:1} -a -g
        # {0:1} {1:1} {3:1} -a -g test
        # {0:1} {1:1} {3:1} -a -g "test*"
        # {0:1} {1:1} {3:1} -a -g "*test"

    - {2:1} with files bigger than 1MB
        # {0:1} {1:1} {3:1} -a -q SIZECOND:BT:1M

    - {2:1} with files lesser than 1MB
        # {0:1} {1:1} {3:1} -a -q SIZECOND:LT:1M

    - {2:1} and apply specific commands
        # {0:1} {1:1} {3:1} -a -g test -q ONLYDIR -c "rm -rf TARGET"
        # {0:1} {1:1} {3:1} -a -q ONLYFILE -c "cat TARGET > /dev/null"
                    """.format(
                        cmd,
                        mode,
                        "Print the difference between specific directories",
                        '"/data, /tmp"',
                    )

                # printdir #
                elif SysMgr.checkMode("printdir"):
                    helpStr = (
                        _getDesc("Show the structure of a specific directory")
                        + """
Options:
    -v                          verbose
    -I  <DIR>                   set input path
    -a                          show all attributes
    -g  <WORD>                  set filter
    -c  <COMMAND>               set command
    -q  <NAME{:VALUE}>          set environment variables
    -J                          print in JSON format
    -H  <LEVEL>                 set function depth level
    -Q                          print all rows in a stream
                        """
                    )

                    helpStr += """
Examples:
    - {2:1} from {3:1}
        # {0:1} {1:1}

    - {2:1} with files from / directory
        # {0:1} {1:1} / -a

    - {2:1} from a specific directory
        # {0:1} {1:1} /
        # {0:1} {1:1} /proc/device-tree

    - {2:1} from {3:1} with absolute path
        # {0:1} {1:1} -q ABSPATH

    - {2:1} from {3:1} with time info
        # {0:1} {1:1} -q TIMEINFO

    - {2:1} from {3:1} with user info
        # {0:1} {1:1} -q USERINFO

    - {2:1} from {3:1} using full size without size unit
        # {0:1} {1:1} -q FULLSIZE

    - {2:1} from {3:1} after sorting per-directory
        # {0:1} {1:1} -q SORT:SIZE
        # {0:1} {1:1} -q SORT:TYPE

    - {2:1} from {3:1} after sorting all files
        # {0:1} {1:1} -q SORT:SIZE -a -g "*"

    - {2:1} including linked directories from {3:1}
        # {0:1} {1:1} -q INCLINK

    - {2:1} for specific directories
        # {0:1} {1:1} "/data, /tmp"

    - {2:1} in 2-depth from / directory
        # {0:1} {1:1} / -H 2

    - Print specific directories and files from / directory
        # {0:1} {1:1} / -a -g
        # {0:1} {1:1} / -a -g test
        # {0:1} {1:1} / -a -g "test*"
        # {0:1} {1:1} / -a -g "*test"

    - {2:1} with files bigger than 1MB from / dir
        # {0:1} {1:1} / -a -q SIZECOND:BT:1M

    - {2:1} with files lesser than 1MB from / dir
        # {0:1} {1:1} / -a -q SIZECOND:LT:1M

    - Print specific directories or files from a specific directory and apply specific commands
        # {0:1} {1:1} / -a -g test -q ONLYDIR -c "rm -rf TARGET"
        # {0:1} {1:1} /usr -a -q ONLYFILE -c "cat TARGET > /dev/null"
                    """.format(
                        cmd,
                        mode,
                        "Print directory structure",
                        "the current working directory",
                    )

                # leaktracer #
                elif SysMgr.checkMode("leaktrace"):
                    helpStr = """
Usage:
    # {0:1} {1:1} -g <PID|COMM> -T <PATH> [OPTIONS] [--help]

Description:
    Show functions caused memory leakage

    1) Get libleaktracer.so for your CPU architecture from
       https://github.com/iipeace/portable/tree/master/leaktracer.
    2) Run the target process with below specific environment variables
       if you can't inject the hook binary to the target process.
       - Auto start from loader
           $ LD_PRELOAD=./libleaktracer.so \\
             LEAKTRACER_AUTO_REPORTFILENAME=/tmp/leaks.out \\
             LEAKTRACER_ONSIG_REPORTFILENAME=/tmp/leaks.out \\
             LEAKTRACER_ONSIG_REPORT=12 EXEC_PATH
       - Manual start by signal(SIGRT1)
           $ LD_PRELOAD=./libleaktracer.so \\
             LEAKTRACER_ONSIG_REPORTFILENAME=/tmp/leaks.out \\
             LEAKTRACER_ONSIG_STARTALLTHREAD=35 \\
             LEAKTRACER_ONSIG_REPORT=12 EXEC_PATH
    3) Check below specification.
       - If the target process is on secure-execution mode,
         libleaktracer.so should be in standard search directories
         specified in  /etc/ld.so.conf,
         And all slashes in the preload path will be ignored.
         Otherwise add the library path to /etc/ld.so.preload.
       - Once leaktrace starts, the memory usage of the target process
         will increase rapidly due to logging data.
       - Specific allocators and deallocators called from inside
         standard libraries such as libstdc++ can't be traced
         if you injected libleaktracer.so to the target process dynamically.
         Because using dynamic injection just update PLTs in mapped binaries.
       - Memory allocations using direct mmap() are not tracked
         Because mmap() is not called using reallocation table.
         They can be traced using mtrace command.

Options:
    -I  <DIR|FILE>              set input path
    -o  <DIR|FILE>              set output path
    -c  <{{STARTSIZE:}}ENDSIZE>   set condition for RSS
    -T  <FILE>                  set hook file
    -g  <PID|COMM>              set target process
    -k  <{{START,}}STOP>          set signal
    -R  <TIME>                  set timer
    -C  <PATH>                  set config path
    -q  <NAME{{:VALUE}}>          set environment variables
    -v                          verbose
                        """.format(
                        cmd, mode
                    )

                    helpStr += (
                        """
Examples:
    - {3:1} {2:1} {6:1} {9:1}
        # {0:1} {1:1} ./a.out {7:1}
        # {0:1} {1:1} ./a.out -o guider.out {7:1}
        # {0:1} {1:1} ./a.out {7:1} -q MUTE

    - {3:1} {2:1} {6:1} and waiting for SIGINT to start profiling
        # {0:1} {1:1} ./a.out -o guider.out {7:1} -q WAITSIGNAL

    - {3:1} {2:1} {6:1} after setting environment variables
        # {0:1} {1:1} ./a.out {7:1} -q ENV:TEST=1, ENV:PATH=/data
        # {0:1} {1:1} ./a.out {7:1} -q ENV:MALLOC_MMAP_THRESHOLD_=131072, ENV:MALLOC_ARENA_MAX=5
        # {0:1} {1:1} ./a.out {7:1} -q ENVFILE:/data/env.sh
        # {0:1} {1:1} ./a.out {7:1} -q CLEARENV
        # {0:1} {1:1} ./a.out {7:1} -q CLEARENV:HOME, CLEARENV:^LANGUAGE
        # {0:1} {1:1} ./a.out {7:1} -q ENVPROC:systemd

    - {3:1} {2:1} {6:1} {9:1} using uptime
        # {0:1} {1:1} ./a.out {7:1} -q USEUPTIME

    - {3:1} {2:1} for 5 seconds
        # {0:1} {1:1} -g a.out {7:1} -R 5

    - {3:1} {2:1} {4:1}
        # {0:1} {1:1} -g a.out {7:1}

    - {3:1} including memory tracking {2:1} {4:1}
        # {0:1} {1:1} -g a.out {7:1} -q MEMPROF
        # {0:1} {1:1} -g a.out {7:1} -q MEMPROF:3

    - {3:1} {2:1} {4:1} and a temporary writable path
        # {0:1} {1:1} -g a.out {7:1} -I /var/log/guider {7:1}

    - {3:1} after sending SIGRT2(36) to stop profiling
        # {0:1} {1:1} -g a.out {7:1} -k 36
        # {0:1} {1:1} -g a.out {7:1} -k SIGRT2

    - {3:1} {2:1} after starting profiling {5:1}
        # {0:1} {1:1} -g a.out {7:1} -c 20m,0

    - Report idle memory hints of the target process {2:1} {8:1} {6:1}
        # {0:1} {1:1} ./a.out {7:1} -q REPORTIDLE

    - Report active memory hints of the target process {2:1} {8:1} {6:1}
        # {0:1} {1:1} ./a.out {7:1} -q REPORTACTIVE

    - {3:1} {5:1}
        # {0:1} {1:1} -g a.out {7:1} -c 20m
        # {0:1} {1:1} -g a.out {7:1} -c +20m
        # {0:1} {1:1} -g a.out {7:1} -c 15m,20m
        # {0:1} {1:1} -g a.out {7:1} -c +15m,+20m

    - {3:1} {2:1} {4:1} (wait for new process if no process)
        # {0:1} {1:1} -g a.out {7:1} -q WAITTASK
        # {0:1} {1:1} -g a.out {7:1} -q WAITTASK:1
        # {0:1} {1:1} -g a.out {7:1} -q WAITTASK, NOPIDCACHE

    - {3:1} using allocation size filter {2:1} {6:1} {9:1}
        # {0:1} {1:1} ./a.out {7:1} -q SIZEFILTER:">100k"
        # {0:1} {1:1} ./a.out {7:1} -q SIZEFILTER:">100m"
        # {0:1} {1:1} ./a.out {7:1} -q SIZEFILTER:"=4096"

    - {3:1} {9:1} and stop target
        # {0:1} {1:1} ./a.out {7:1} -q STOPTARGET

    - {3:1} {9:1} and coredump
        # {0:1} {1:1} ./a.out {7:1} -q USECOREDUMP

    - {3:1} {9:1} and ignore specific signals
        # {0:1} {1:1} ./a.out {7:1} -q IGNORESIGNAL:SIGABRT

    - {3:1} {9:1} and trace specific signals
        # {0:1} {1:1} ./a.out {7:1} -w START:"GUIDER sigtrace -g PID &" -q IGNORESIGNAL:SIGSEGV, SKIPSIGNAL:8, STOPTARGET

    - {3:1} {9:1} but no clean up
        # {0:1} {1:1} ./a.out {7:1} -q NOCLEANUP

    - {3:1} {9:1} and debug info
        # {0:1} {1:1} ./a.out {7:1} -q DEBUG

    - {3:1} {9:1} repeatedly
        # {0:1} {1:1} ./a.out {7:1} -q REPEAT:3

    - {3:1} {9:1} without redundant count info
        # {0:1} {1:1} ./a.out {7:1} -q NOREDCNT

    - {3:1} {9:1} with redundant calls
        # {0:1} {1:1} ./a.out {7:1} -q KEEPREDCALL

    - {3:1} {9:1} and keep the target process alive after termination
        # {0:1} {1:1} ./a.out {7:1} -q KEEPTASK, KEEPALIVE

    - {3:1} {9:1} and specific stat interval
        # {0:1} {1:1} ./a.out {7:1} -q STATINTERVAL:2

    - {3:1} {2:1} with the specific command execution in background
        # {0:1} {1:1} "ls" {7:1} -w BEFORE:"GUIDER top -Q &"
        # {0:1} {1:1} "ls" {7:1} -w START:"GUIDER sigtrace -g PID -c SIGSEGV &"
        # {0:1} {1:1} "ls" {7:1} -w AFTER:/tmp/touched:1, AFTER:ls
                    """.format(
                            cmd,
                            mode,
                            "when SIGINT is received",
                            "Report memory leakage hints of the target process",
                            "after hooking binary injection",
                            "when it's RSS reached the specific size",
                            "after executing the target program",
                            "-T ./libleaktracer.so",
                            "after marking all anonymous pages as idle when SIGQUIT is received",
                            "with auto start",
                        ).rstrip()
                        + dbgExamStr
                    )

                # printslab #
                elif SysMgr.checkMode("printslab"):
                    helpStr = (
                        _getDesc("Show slab caches on the system")
                        + """
Options:
    -g  <WORD>                  set target object
    -S  <WORD>                  set sort value
    -v                          verbose

Sort:
    size:    the size of an object
    active:  the number of active objects
    actsize: the size of active objects
    total:   the size of all objects
                        """
                    )

                    helpStr += """
Examples:
    - Print all slab caches
        # {0:1} {1:1} dentry

    - Print specific slab caches
        # {0:1} {1:1} -g dentry

    - Print all slab caches sorted by specific value
        # {0:1} {1:1} -S size
        # {0:1} {1:1} -S active
        # {0:1} {1:1} -S actsize
        # {0:1} {1:1} -S total
                    """.format(
                        cmd, mode
                    )

                # printvma #
                elif SysMgr.checkMode("printvma"):
                    helpStr = (
                        _getDesc(
                            "Show memory objects allocated using vmalloc on the system"
                        )
                        + """
Options:
    -g  <WORD>                  set target object
    -v                          verbose
                    """
                    )

                    helpStr += """
Examples:
    - Print all {2:1}
        # {0:1} {1:1}

    - Print specific {2:1}
        # {0:1} {1:1} -g fork
                    """.format(
                        cmd,
                        mode,
                        "memory objects allocated using vmalloc on the system",
                    )

                # printenv #
                elif SysMgr.checkMode("printenv"):
                    helpStr = (
                        _getDesc(
                            "Show environment variables for a specific process",
                            t=2,
                        )
                        + """
Options:
    -g  <PID|COMM>              set target process
    -l                          print values from the auxiliary vector
    -I  <WORD>                  set filter
    -o  <DIR|FILE>              set output path
    -J                          print in JSON format
    -v                          verbose
                        """
                    )

                    helpStr += """
Examples:
    - {2:1} for a specific process
        # {0:1} {1:1} a.out
        # {0:1} {1:1} -g 1234

    - {2:1} for all processes
        # {0:1} {1:1} "*"
        # {0:1} {1:1} "*" -a

    - {2:1} for a specific process to the specific file
        # {0:1} {1:1} a.out -o a.out.env

    - {2:1} including PWD for all processes
        # {0:1} {1:1} "*" -I PWD

    - {2:1} for the auxiliary vector
        # {0:1} {1:1} -l
        # {0:1} {1:1} -l -g a.out, 1234
                    """.format(
                        cmd,
                        mode,
                        "Print environment variables",
                    )

                # printns #
                elif SysMgr.checkMode("printns"):
                    helpStr = (
                        _getDesc("Show namespaces on the system")
                        + """
Options:
    -a                          show all attributes
    -v                          verbose
                        """
                    )

                    helpStr += """
Examples:
    - {2:1}
        # {0:1} {1:1}

    - {2:1} only for target group
        # {0:1} {1:1} "cgroup, net"

    - {2:1} with tasks
        # {0:1} {1:1} -a
                    """.format(
                        cmd, mode, "Print namespaces"
                    )

                # printsdfile #
                elif SysMgr.checkMode("printsdfile"):
                    helpStr = (
                        _getDesc("Show systemd files on the system")
                        + """
Options:
    -a                          show all attributes
    -g  <NAME>                  set target file
    -c  <ATTR>                  set target attribute
    -I  <DIR>                   set input path
    -v                          verbose
                        """
                    )

                    helpStr += """
Examples:
    - {2:1}
        # {0:1} {1:1}

    - {2:1} about only specific files
        # {0:1} {1:1} -g test

    - {2:1} including specific attributes
        # {0:1} {1:1} -c Restart

    - {2:1} including specific value of attributes
        # {0:1} {1:1} -c :pid

    - {2:1} in specific directories
        # {0:1} {1:1} -I /home/iipeace/services
                    """.format(
                        cmd, mode, "Print systemd files"
                    )

                # printinfo #
                elif SysMgr.checkMode("printinfo"):
                    helpStr = (
                        _getDesc("Show system general info")
                        + """
Options:
    -v                          verbose
    -J                          print in JSON format
    -q  <NAME{:VALUE}>          set environment variables
                        """
                    )

                    helpStr += """
Examples:
    - {2:1}
        # {0:1} {1:1}

    - {2:1} in JSON format
        # {0:1} {1:1} -J

    - {2:1} without size-unit converting
        # {0:1} {1:1} -q NOCONVSIZE2UNIT

    - {2:1} without number formatting
        # {0:1} {1:1} -q NOCONVNUM

    - {2:1} with system mergeable memory size
        # {0:1} {1:1} -q PRINTMERGE
                    """.format(
                        cmd,
                        mode,
                        "Print system info",
                    )

                # pstree #
                elif SysMgr.checkMode("pstree"):
                    helpStr = (
                        _getDesc("Show the task tree")
                        + """
Options:
    -e  <CHARACTER>             enable options
          [ t:thread ]
    -g  <COMM>                  set filter
    -i  <SEC>                   set interval
    -H  <LEVEL>                 set depth level
    -o  <DIR|FILE>              set output path
    -v                          verbose
                        """
                    )

                    helpStr += """
Examples:
    - {2:1} all processes
        # {0:1} {1:1}

    - {2:1} specific processes
        # {0:1} {1:1} a.out
        # {0:1} {1:1} "a.out, yes"

    - {2:1} specific processes with cmdline
        # {0:1} {1:1} a.out -q CMDLINE
        # {0:1} {1:1} a.out -q CMDLINE:50

    - {2:1} all threads
        # {0:1} {1:1} -e t

    - {2:1} without stats
        # {0:1} {1:1} -q NOSTAT

    - {2:1} with user info
        # {0:1} {1:1} -q USERINFO

    - {2:1} all processes repeatedly
        # {0:1} {1:1} -i

    - {2:1} all processes with current cpu usage
        # {0:1} {1:1} -q CURCPU

    - {2:1} all processes with memory info
        # {0:1} {1:1} -q ADDMEM

    - {2:1} all processes with total I/O info
        # {0:1} {1:1} -q ADDIO -e T

    - Print the highlighted processes having specific name
        # {0:1} {1:1} -g kworker

    - {2:1} all processes with depth limitation
        # {0:1} {1:1} -H 3
                    """.format(
                        cmd,
                        mode,
                        "Print the tree of",
                    )

                # split #
                elif SysMgr.checkMode("split"):
                    helpStr = (
                        _getDesc("Split files", t=5)
                        + """
Options:
    -o  <FILE>                  set output path
    -q  <NAME{:VALUE}>          set environment variables
    -v                          verbose
                        """
                    )

                    helpStr += """
Examples:
    - Split files to 10M size
        # {0:1} {1:1} "guider.tgz, guider2.tgz" -q CHUNK:10m

    - Split files into 5 files each
        # {0:1} {1:1} "guider.tgz, guider2.tgz" -q COUNT:5

    - Split files into 5 files of 10M size each
        # {0:1} {1:1} "guider.tgz, guider2.tgz" -q CHUNK:10M, COUNT:5

    - Split files into 5 files of 10M size each from the back 
        # {0:1} {1:1} "guider.tgz, guider2.tgz" -q CHUNK:10M, COUNT:5, TAIL

    - Split files to 10M size with file filter
        # {0:1} {1:1} "guider*.tgz" -q CHUNK:10m, TARGETFILE:"*.tgz", EXCEPTFILE:"*1*"
                    """.format(
                        cmd, mode
                    )

                # merge #
                elif SysMgr.checkMode("merge"):
                    helpStr = (
                        _getDesc("Merge files", t=5)
                        + """
Options:
    -o  <FILE>                  set output path
    -q  <NAME{:VALUE}>          set environment variables
    -v                          verbose
                        """
                    )

                    helpStr += """
Examples:
    - Merge files into a new file
        # {0:1} {1:1} "guider.tgz, guider2.tgz" -o guiderMerged.mg

    - Merge files into a new file with file filter
        # {0:1} {1:1} "guider*.tgz" -o guiderMerged.mg -q TARGETFILE:"*.tgz", EXCEPTFILE:"*1*"
                    """.format(
                        cmd, mode
                    )

                # comp #
                elif SysMgr.checkMode("comp"):
                    helpStr = (
                        _getDesc("Compress files")
                        + """
Options:
    -o  <FILE>                  set output path
    -q  <NAME{:VALUE}>          set environment variables
    -v                          verbose

Format:
    gzip, zip, base64
                        """
                    )

                    helpStr += """
Examples:
    - Compress a file
        # {0:1} {1:1} guider.out
        # {0:1} {1:1} guider.out -o guider.out.gz

    - Compress a directory
        # {0:1} {1:1} guiderDir -q zip

    - Compress multiple files
        # {0:1} {1:1} "guider.out, guider2.out"
        # {0:1} {1:1} "guider*.out"
        # {0:1} {1:1} "guider*.out" -q zip

    - Compress multiple files with file filter
        # {0:1} {1:1} "guider*" -q TARGETFILE:"*.out", EXCEPTFILE:"*.old"

    - Encode a file in BASE64 format
        # {0:1} {1:1} guider.out -q BASE64
        # {0:1} {1:1} guider.out -o guider.out.gz -q BASE64
                    """.format(
                        cmd, mode
                    )

                # decomp #
                elif SysMgr.checkMode("decomp"):
                    helpStr = (
                        _getDesc("Decompress files")
                        + """
Options:
    -o  <DIR>                   set output path
    -g  <NAME>                  set target file
    -q  <NAME{:VALUE}>          set environment variables
    -v                          verbose

Format:
    gzip, lzma, zip, tar, base64, apk
                        """
                    )

                    helpStr += """
Examples:
    - Decompress a file to specific directory
        # {0:1} {1:1} a.out, a.tar, a.zip, a.gz, a.apk, a.xz -o ./test
        # {0:1} {1:1} guider.gz -o ./test
        # {0:1} {1:1} guider.zip -o ./test
        # {0:1} {1:1} "**/*" -o ./test

    - Decompress specific files in a compression file
        # {0:1} {1:1} guider.zip -g "test*", "*guider*"

    - {2:1}
        # {0:1} {1:1} "guider.out.gz, guider2.out.gz"
        # {0:1} {1:1} "guider*.gz"

    - {2:1} with file filter
        # {0:1} {1:1} "guider*.gz" -q TARGETFILE:"*.gz", EXCEPTFILE:"*test*"

    - {2:1} after making new dirs having same
        # {0:1} {1:1} "guider*.gz" -q MKFILENAMEDIR

    - Decode a file in BASE64 format to specific directory
        # {0:1} {1:1} guider.b64 -o ./test -q BASE64
                    """.format(
                        cmd,
                        mode,
                        "Decompress multiple files",
                    )

                # systat #
                elif SysMgr.checkMode("systat"):
                    helpStr = (
                        _getDesc("Print system status")
                        + """
Options:
    -e  <CHARACTER>             enable options
          [ t:thread ]
    -J                          print in JSON format
    -v                          verbose
                        """
                    )

                    helpStr += """
Examples:
    - Print system total status
        # {0:1} {1:1}

    - Print system interval status
        # {0:1} {1:1} -i 3
                    """.format(
                        cmd, mode
                    )

                # request #
                elif SysMgr.checkMode("req"):
                    helpStr = """
Usage:
    # {0:1} {1:1} <ADDR> [OPTIONS] [--help]

Description:
    Request URLs

Options:
    -I  <METHOD#OPT#ADDR>       input requests
    -o  <DIR|FILE>              set output path
    -R  <DELAY:COUNT>           set repeat count
    -T  <PROC>                  set process number
    -q  <NAME{{:VALUE}}>          set environment variables
    -v                          verbose
                        """.format(
                        cmd, mode
                    )

                    helpStr += """
Examples:
    - Run client with interaction menu
        # {0:1} {1:1}

    - {2:1}
        # {0:1} {1:1} http://127.0.0.1:5000
        # {0:1} {1:1} GET#http://127.0.0.1:5000
        # {0:1} {1:1} "GET#http://127.0.0.1:5000|GET#http://10.25.123.123:5000"

    - {2:1} with Keep-Alive attribute
        # {0:1} {1:1} http://127.0.0.1:5000 -q KEEPALIVE

    - Request GET/URL with alias to specific server
        # {0:1} {1:1} ALIAS:TEST1#http://127.0.0.1:5000

    - {2:1} and print contents for the request
        # {0:1} {1:1} http://127.0.0.1:5000 -q PRINTREQ

    - {2:1} and print only summary for requests
        # {0:1} {1:1} http://127.0.0.1:5000 -q MUTE

    - {3:1}
        # {0:1} {1:1} POST#DATA:"data"#http://127.0.0.1:5000
        # {0:1} {1:1} POST#JSONDATA:"{{'key':'value'}}"#http://127.0.0.1:5000

    - {3:1} after base64 encoding specific file data from specific string "@@@FILE:PATH@@@"
        # {0:1} {1:1} POST#DATA:"@@@FILE:a.out@@@"#http://127.0.0.1:5000
        # {0:1} {1:1} POST#JSONDATA:"{{'date':'123', 'image': {{'name': 'good', 'data':'@@@FILE:a.out@@@'}}}}"#http://127.0.0.1:5000
        # {0:1} {1:1} POST#JSONFILE:input.json#http://127.0.0.1:5000

    - {3:1} with base64 encoded data from specific string "@@@BIN:SIZE@@@"
        # {0:1} {1:1} POST#DATA:"@@@BIN:4MB@@@"#http://127.0.0.1:5000

    - {3:1} with files
        # {0:1} {1:1} POST#FILE:image:test.png:img/png#http://127.0.0.1:5000
        # {0:1} {1:1} POST#FILE:doc:test.txt:doc/txt#http://127.0.0.1:5000
        # {0:1} {1:1} POST#FILE:test.png:img/png#http://127.0.0.1:5000

    - {3:1} with data from data.json file
        # {0:1} {1:1} POST#JSONFILE:data.json#http://127.0.0.1:5000

    - {3:1} with data from data file
        # {0:1} {1:1} POST#DATAFILE:data#http://127.0.0.1:5000

    - {2:1} infinitely
        # {0:1} {1:1} GET#http://127.0.0.1:5000 -R

    - {2:1} 10 times with 500ms delay
        # {0:1} {1:1} GET#http://127.0.0.1:5000 -R 500:10

    - {2:1} 10 times by 10 processes
        # {0:1} {1:1} GET#http://127.0.0.1:5000 -R 10 -T 10

    - {2:1} with 5 second timeout
        # {0:1} {1:1} GET#TIMEOUT:5#http://127.0.0.1:5000

    - {2:1} with no verification for SSL
        # {0:1} {1:1} GET#VERIFY:false#https://127.0.0.1:5000

    - {2:1} with auth
        # {0:1} {1:1} GET#AUTH:id,passwd#https://127.0.0.1:5000

    - {2:1} with cookies
        # {0:1} {1:1} GET#COOKIES:sessionKey:sessionValue#https://127.0.0.1:5000

    - {2:1} with headers
        # {0:1} {1:1} GET#HEADERS:"Content-Type:application/json;charset=utf-8"#https://127.0.0.1:5000
                    """.format(
                        cmd,
                        mode,
                        "Request GET/URL to specific server",
                        "Request POST/URL to specific server",
                    )

                # limitcpu / limitcpuset / limitmem / limitread / limitwrite #
                elif SysMgr.isLimitMode():
                    if SysMgr.checkMode("limitcpu"):
                        res = "CPU usage"
                        value = '"10"'
                        addinfo = """
        # {0:1} {1:1} "yes:cfs_quota_us:20000+cfs_period_us:100000"
        # {0:1} {1:1} "yes:rt_runtime_us:20000+rt_period_us:100000"

    - {2:1} for specific threads using signal not cgroup
        # {0:1} {1:1} yes:20 -q NOCG
        # {0:1} {1:1} "yes|a.out":20 -q NOCG
        # {0:1} {1:1} "yes:10, a.out:10" -q NOCG"""
                    elif SysMgr.checkMode("limitcpuset"):
                        res = "CPU set"
                        value = '"1-2&4"'
                        addinfo = """
        # {0:1} {1:1} yes:2 -q EXCLUSIVE"""
                    elif SysMgr.checkMode("limitmem"):
                        res = "memory usage"
                        value = "20M"
                        addinfo = ""
                    elif SysMgr.checkMode("limitread"):
                        res = "I/O read throughput"
                        value = "20M"
                        addinfo = ""
                    elif SysMgr.checkMode("limitpid"):
                        res = "the total number"
                        value = "10"
                        addinfo = ""
                    else:
                        res = "I/O write throughput"
                        value = "20M"
                        addinfo = ""

                    helpStr = """
Usage:
    # {0:1} {1:1} -g <COMM|TID|PID:SIZE> [OPTIONS] [--help]

Description:
    Limit {2:1} of specific tasks

Options:
    -g  <TID|COMM>              set task filter
    -R  <TIME>                  set timer
    -P                          group threads in a same process
    -q  <NAME{{:VALUE}}>          set environment variables
    -v                          verbose
                        """.format(
                        cmd, mode, res
                    )

                    helpStr += (
                        """
Examples:
    - {2:1} for specific threads
        # {0:1} {1:1} yes:{3:1}
        # {0:1} {1:1} "yes|a.out":{3:1}
        # {0:1} {1:1} -g yes:{3:1}, a.out:{3:1}%s

    - {2:1} for specific threads after mounting/unmounting all cgroup subsystems
        # {0:1} {1:1} yes:{3:1} -q MOUNTCG
        # {0:1} {1:1} yes:{3:1} -q UMOUNTCG

    - {2:1} for specific threads with monitoring
        # {0:1} {1:1} yes:{3:1} -q TASKMON
        # {0:1} {1:1} yes:{3:1} -q TASKMON:3
        # {0:1} {1:1} yes:{3:1} -q TASKMON -a
        # {0:1} {1:1} yes:{3:1} -q TASKMON, TASKFILTER:"*a.out*"
        # {0:1} {1:1} yes:{3:1} -q TASKMON, TASKFILTER:"*a.out*" -e t -P
        # {0:1} {1:1} yes:{3:1} -q TASKMON, CHILDSCHED:c:0 -Y r:1
        # {0:1} {1:1} yes:{3:1} -q TASKMON, EXECSCHED:c:0 -Y r:1

    - {2:1} for specific threads (wait for new target if no task)
        # {0:1} {1:1} yes:{3:1} -q WAITTASK
        # {0:1} {1:1} yes:{3:1} -q WAITTASK:1
        # {0:1} {1:1} yes:{3:1} -q WAITTASK, NOPIDCACHE

    - {2:1} of specific threads for 3 seconds
        # {0:1} {1:1} -g 1234:{3:1},yes:{3:1} -R 3
                    """
                        % addinfo
                    ).format(cmd, mode, "Limit %s" % res, value)

                # setcpu #
                elif SysMgr.checkMode("setcpu"):
                    helpStr = """
Usage:
    # {0:1} {1:1} -g <CORE:CLOCK|ON|OFF:GOVERNOR> [OPTIONS] [--help]

Description:
    Set CPU clock, governor, hotplug

Options:
    -g  <CORE:CLOCK|ON|OFF:GOVERNOR>   set filter
    -l                                 print core info
    -v                                 verbose
                        """.format(
                        cmd, mode
                    )

                    helpStr += """
Examples:
    - {2:1} 2,000,000HZ for CPU0
        # {0:1} {1:1} 0:20000000

    - {2:1} 2,000,000HZ and the governor to userspace for specific CPUs
        # {0:1} {1:1} 1-4:20000000:userspace
        # {0:1} {1:1} 0-8:20000000:performance

    - {2:1} 2,000,000HZ and the governor to userspace for All CPUs
        # {0:1} {1:1} :20000000:userspace

    - Set hotplug status of specific CPUs
        # {0:1} {1:1} 0-3:ON, 4-7:OFF

    - Set the governor to performance for CPU2
        # {0:1} {1:1} 2::performance

    - Set the governor to performance for all CPUs
        # {0:1} {1:1} ::performance

    - Print core info
        # {0:1} {1:1} -l
                    """.format(
                        cmd, mode, "Set the clock speed to"
                    )

                # convert #
                elif SysMgr.checkMode("convert"):
                    helpStr = (
                        _getDesc("Convert a text file to a image file")
                        + """
Options:
    -v                          verbose
                        """
                    )

                    helpStr += """
Examples:
    - Convert a text file to a image file
        # {0:1} {1:1} guider.out
                    """.format(
                        cmd, mode
                    )

                # drawreq #
                elif SysMgr.checkMode("drawreq"):
                    helpStr = (
                        _getDesc(
                            "Draw graphs for response time of URL requests"
                        )
                        + """
Options:
    -v                          verbose
    -I  <DIR|FILE>              set input path
    -o  <DIR|FILE>              set output path
    -g  <WORD>                  set filter
                        """
                    )

                    helpStr += """
Examples:
    - {2:1} from specific files
        # {0:1} {1:1} guider.out
        # {0:1} {1:1} "guider*.out"

    - {2:1} of specific requests
        # {0:1} {1:1} guider.out -g www.google.com
                    """.format(
                        cmd,
                        mode,
                        "Draw graphs for response time",
                    )

                # rlimit #
                elif SysMgr.checkMode("rlimit"):
                    helpStr = """
Usage:
    # {0:1} {1:1} -g <TID|COMM:RTYPE:SLIM:HLIM> [OPTIONS] [--help]

Description:
    Change resource limit for specific tasks

Resource:
    {2:1}

Options:
    -g <TID|COMM:RTYPE:SLIM:HLIM> set value
    -v                            verbose
                        """.format(
                        cmd, mode, "\n    ".join(ConfigMgr.RLIMIT_TYPE)
                    )

                    helpStr += """
Examples:
    - Change resource limit for specific tasks
        # {0:1} {1:1} "a.out:RLIMIT_NOFILE:1000:2000"
        # {0:1} {1:1} -g "a.out:RLIMIT_NOFILE:1000:2000"

    - Show resource limits for specific tasks
        # {0:1} {1:1} a.out
        # {0:1} {1:1} -g a.out
                    """.format(
                        cmd, mode
                    )

                # setsched #
                elif SysMgr.checkMode("setsched"):
                    helpStr = """
Usage:
    # {0:1} {1:1} -g <POLICY:PRIO|TIME:TID|COMM> [OPTIONS] [--help]
    # {0:1} {1:1} -g <CLASS:WHICH:PRIO:TID|COMM> [OPTIONS] [--help]

Description:
    Set the scheduler policy and priority for tasks

Policy(CPU):
    c: CFS [default]
    f: FIFO(RT)
    r: RR(RT)
    b: BATCH
    i: IDLE
    d: DEADLINE

Class(I/O):
    rt:   IOPRIO_CLASS_RT
    be:   IOPRIO_CLASS_BE
    idle: IOPRIO_CLASS_IDLE

Who(I/O):
    process: IOPRIO_WHO_PROCESS
    pgrp:    IOPRIO_WHO_PGRP
    user:    IOPRIO_WHO_USER

Options:
    -g <POLICY:PRIO|TIME:TID|COMM>  set value for CPU scheduling
    -g <CLASS:WHO:PRIO:TID|COMM>    set value for I/O scheduling
    -P                              group threads in a same process
    -i  <SEC>                       set interval
    -W  <SEC>                       wait for input
    -q  <NAME{{:VALUE}}>              set environment variables
    -v                              verbose
                        """.format(
                        cmd, mode
                    )

                    helpStr += """
Examples:
    - {3:1} policy(CFS), priority(-20) {2:1}
        # {0:1} {1:1} {5:1}
        # {0:1} {1:1} "c:-20:1234"
        # {0:1} {1:1} "c:-20:1234" -f
        # {0:1} {1:1} "-20:a*"
        # {0:1} {1:1} "-20:1234, 10:a.out, 15:test"
        # {0:1} {1:1} "-20:a.out|test"

    - Set the I/O scheduler {2:1}
        # {0:1} {1:1} "rt:process:1:a.out"
        # {0:1} {1:1} rt:process:1:$$
        # {0:1} {1:1} "idle:process:1:a.out"
        # {0:1} {1:1} "be:5:a.out"
        # {0:1} {1:1} "rt:process:1:0"

    - {3:1} policy(CFS), priority(-20) for specific target task to be selected
        # {0:1} {1:1} {4:1}
        # {0:1} {1:1} {4:1} -e t
        # {0:1} {1:1} {4:1}:3
        # {0:1} {1:1} {4:1} -a
        # {0:1} {1:1} {4:1}, TASKFILTER:"*a.out*"
        # {0:1} {1:1} {4:1}, TASKFILTER:"*a.out*" -e t -P
        # {0:1} {1:1} {4:1}, TASKMONOPT:"-qNRTOPRANK:10|-Yr:1"

    - {3:1} policy(CFS), priority(-20) for specific TID
        # {0:1} {1:1} "-20:1234" -q ONLYPID

    - {3:1} policy(CFS), priority(-20) for specific task name
        # {0:1} {1:1} "-20:1234" -q ONLYCOMM

    - {3:1} policy(CFS), priority(-20) for specific processes
        # {0:1} {1:1} {5:1} -q PROCSEARCH

    - {3:1} policy(CFS), priority(-20) for all sibling threads of specific processes
        # {0:1} {1:1} {5:1} -q PROCSEARCH -P (save CPU resource for searching tasks)

    - {3:1} policy(CFS), priority(-20) {2:1} (wait for new target if no task)
        # {0:1} {1:1} {5:1} -q WAITTASK
        # {0:1} {1:1} {5:1} -q WAITTASK:1
        # {0:1} {1:1} {5:1} -q WAITTASK, NOPIDCACHE

    - {3:1} policy(CFS), priority(-20) {2:1} after 5 seconds
        # {0:1} {1:1} {5:1} -W 5s

    - {3:1} policy(CFS), priority(-20) {2:1} every 2 seconds
        # {0:1} {1:1} {5:1} -i 2

    - {3:1} policy(CFS), priority(-20) {2:1} and their siblings
        # {0:1} {1:1} {5:1} -P

    - {3:1} policy(FIFO), priority(90) {2:1}
        # {0:1} {1:1} "f:90:a.out"

    - {3:1} policy(DEADLINE), runtime(1ms), deadline(10ms), period(10ms) {2:1}
        # {0:1} {1:1} "d:1000000/10000000/10000000:a.out"
                    """.format(
                        cmd,
                        mode,
                        "for specific threads",
                        "Set the CPU scheduler",
                        '"-20:PID" -q TASKMON',
                        '"-20:a.out"',
                    )

                # printkconf #
                elif SysMgr.checkMode("printkconf"):
                    helpStr = (
                        _getDesc("Print kernel configs")
                        + """
Options:
    -g  <OPTION>                set value
    -I                          set input path
    -v                          verbose
                        """
                    )

                    helpStr += """
Examples:
    - Print all kernel configs
        # {0:1} {1:1}

    - Print specific kernel configs
        # {0:1} {1:1} "CONFIG_RT_GROUP_SCHED*"
        # {0:1} {1:1} -g "CONFIG_RT_GROUP_SCHED*"

    - Print kernel configs from the specific file
        # {0:1} {1:1} -I /boot/config-4.4.0-210-generic
                    """.format(
                        cmd, mode
                    )

                # getaffinity #
                elif SysMgr.checkMode("getafnt"):
                    helpStr = (
                        _getDesc("Get CPU affinity of specific threads", t=3)
                        + """
Options:
    -g  <TID|COMM>              set values
    -P                          group threads in a same process
    -v                          verbose
                        """
                    )

                    helpStr += """
Examples:
    - Get CPU affinity of specific threads
        # {0:1} {1:1} -g a.out, 1234
        # {0:1} {1:1} "a.out, 1234"
                    """.format(
                        cmd, mode
                    )

                # setaffinity #
                elif SysMgr.checkMode("setafnt"):
                    helpStr = """
Usage:
    # {0:1} {1:1} -g <TID|COMM:MASK> [OPTIONS] [--help]

Description:
    Set CPU affinity of specific threads

Options:
    -g  <TID|COMM:MASK>         set values
    -P                          group threads in a same process
    -i  <SEC>                   set interval
    -q  <NAME{{:VALUE}}>          set environment variables
    -v                          verbose
                        """.format(
                        cmd, mode
                    )

                    helpStr += """
Examples:
    - {2:1} to use only CPU 1 and CPU 2
        # {0:1} {1:1} a.out:1+2
        # {0:1} {1:1} a.out:0x6
        # {0:1} {1:1} -g a.out:0x6

    - {2:1} to use only CPU 0 (wait for new target if no task)
        # {0:1} {1:1} a.out:0+ -q WAITTASK
        # {0:1} {1:1} a.out:0x1 -q WAITTASK:1
        # {0:1} {1:1} a.out:0x1 -q WAITTASK, NOPIDCACHE

    - {2:1} to use only CPU 1 every 2 seconds
        # {0:1} {1:1} a.out:1+ -i 2

    - {2:1} to use only CPU 1 and CPU 2 after selecting the target task
        # {0:1} {1:1} {3:1}
        # {0:1} {1:1} {3:1} -e t
        # {0:1} {1:1} {3:1}:3
        # {0:1} {1:1} {3:1} -a
        # {0:1} {1:1} {3:1}, TASKFILTER:"*a.out*"
        # {0:1} {1:1} {3:1}, TASKFILTER:"*a.out*" -e t -P
        # {0:1} {1:1} {3:1}, TASKMONOPT:"-qNRTOPRANK:10|-Yr:1"
                    """.format(
                        cmd,
                        mode,
                        "Set CPU affinity of a specific thread",
                        '"PID:0x3" -q TASKMON',
                    )

                # ping #
                elif SysMgr.checkMode("ping"):
                    helpStr = """
Usage:
    # {0:1} {1:1} <IP|URL> [OPTIONS] [--help]

Description:
    Send ICMP ECHO_REQUEST to network hosts

Options:
    -R  <COUNT>                 set repeat count
    -T  <TIMEOUT>               set timeout
    -I  <FILE>                  set input path
    -i  <SEC>                   set interval
    -J                          print in JSON format
    -v                          verbose
                        """.format(
                        cmd, mode
                    )

                    helpStr += """
Examples:
    - {2:1}
        # {0:1} {1:1} www.google.com
        # {0:1} {1:1} "www.google.com, www.naver.com"
        # {0:1} {1:1} "192.168.100.*"
        # {0:1} {1:1} "192.168.100.10-250"

    - {2:1} in a specific file
        # {0:1} {1:1} -I ip.txt

    - {2:1} with 3 second interval infinitely
        # {0:1} {1:1} www.google.com -i 3

    - {2:1} 3 times
        # {0:1} {1:1} www.google.com -R 3

    - {2:1} with 2.5 second timeout
        # {0:1} {1:1} www.google.com -T 2.5

    - {2:1} and don't print timeouted addresses
        # {0:1} {1:1} www.google.com -q NOTIMEOUT
                    """.format(
                        cmd, mode, "Send ICMP ECHO_REQUEST to network hosts"
                    )

                # cputest #
                elif SysMgr.checkMode("cputest"):
                    helpStr = """
Usage:
    # {0:1} {1:1} <LOAD:NRTASK> [OPTIONS] [--help]

Description:
    Create tasks using cpu

Options:
    -R  <TIME>                  set timer
    -Y  <VALUES>                set sched
          [ POLICY:PRIO|TIME{{:TID|COMM:CONT}} ]
    -v                          verbose
                        """.format(
                        cmd, mode
                    )

                    helpStr += """
Examples:
    - Create 10 processes using 5% of a core each other
        # {0:1} {1:1} 50:10

    - {2:1} 250% CPU totally
        # {0:1} {1:1} 250

    - {2:1} 250% CPU totally with custom process name
        # {0:1} {1:1} 250 -q COMM:newbee

    - Create threads in a process using 250% CPU totally
        # {0:1} {1:1} 250 -et

    - {2:1} 250% CPU totally with RR 1 priority
        # {0:1} {1:1} 250 -Y r:1

    - {2:1} 250% CPU totally and run them only on CPU 1
        # {0:1} {1:1} 250 -z :1

    - {2:1} 250% CPU totally and terminate them after 3 seconds
        # {0:1} {1:1} 250 -R 3
                    """.format(
                        cmd, mode, "Create processes using"
                    )

                # memtest #
                elif SysMgr.checkMode("memtest"):
                    helpStr = """
Usage:
    # {0:1} {1:1} <SIZE:INTERVAL:COUNT> [OPTIONS] [--help]

Description:
    Allocate physical memory

Options:
    -i  <SEC>                   set interval
    -R  <TIME>                  set timer
    -v                          verbose
                        """.format(
                        cmd, mode
                    )

                    helpStr += """
Examples:
    - {2:1} 1G and {3:1} every second
        # {0:1} {1:1} 1G

    - {2:1} 1G and {3:1} every 3 seconds
        # {0:1} {1:1} 1G -i 3

    - {2:1} 1G without printing stats
        # {0:1} {1:1} 1G -d p

    - {2:1} 1G of dummy data that is consist of random values
        # {0:1} {1:1} 1G -q RAND

    - {2:1} 1G and {3:1} every second with custom process name
        # {0:1} {1:1} 1G -q COMM:newbee

    - {2:1} 200MB using a new process every 3 seconds
        # {0:1} {1:1} 200M:3

    - {2:1} 100MB twice using 2 processes and {3:1} every second
        # {0:1} {1:1} 100M:0:2

    - {2:1} 100MB twice using 2 processes and terminate them after 3 seconds
        # {0:1} {1:1} 100M:0:2 -R 3
                    """.format(
                        cmd,
                        mode,
                        "Allocate physical memory",
                        "print memory stats",
                    )

                # iotest #
                elif SysMgr.checkMode("iotest"):
                    helpStr = """
Usage:
    # {0:1} {1:1} -g <OP:PATH> [OPTIONS] [--help]

Description:
    Run storage I/O operations

Options:
    -g  <OP:PATH>               set path
    -i  <TIME>                  set timer
    -R  <COUNT>                 set repeat count
    -q  <NAME{{:VALUE}}>          set environment variables
    -v                          verbose
                        """.format(
                        cmd, mode
                    )

                    helpStr += """
Examples:
    - {2:1} mount point
        # {0:1} {1:1}

    - {2:1} directory recursively
        # {0:1} {1:1} .
        # {0:1} {1:1} -g .

    - {2:1} directory recursively for 3 seconds
        # {0:1} {1:1} . -i 3
        # {0:1} {1:1} -g . -i 3

    - {2:1} mount point with custom process name
        # {0:1} {1:1} -q COMM:newbee

    - {3:1} 5 times
        # {0:1} {1:1} "read:TEST1,TEST2" -R 5
        # {0:1} {1:1} "read:TEST*" -R 5

    - {3:1} 5 times after applying new priority
        # {0:1} {1:1} "read:TEST1,TEST2" -R 5 -Y r:1, rt:process:1:$$

    - {3:1} 5 times with sleep for 1.5 seconds
        # {0:1} {1:1} "read:TEST1,TEST2" -R 5 -q SLEEP:1.5s

    - Read the specific file with specific chunk size
        # {0:1} {1:1} read:TEST -q CHUNK:1M

    - Read all device nodes mounted
        # {0:1} {1:1} -a

    - Read all device nodes mounted after flushing the system cache
        # {0:1} {1:1} -a -q DROPCACHE
        # {0:1} {1:1} -a -q DROPSLABCACHE
        # {0:1} {1:1} -a -q DROPPAGECACHE

    - {3:1} infinitely after flushing target file caches
        # {0:1} {1:1} "read:TEST1,TEST2" -R -q DROPFILECACHE

    - Write dummy data to the specific file infinitely
        # {0:1} {1:1} write:TEST
        # {0:1} {1:1} -g write:TEST

    - {3:1} infinitely with elapsed time exceeds specific seconds for a chunk
        # {0:1} {1:1} "read:TEST1,TEST2" -R -q ELAPSED:0
        # {0:1} {1:1} "read:TEST1,TEST2" -R -q ELAPSED:0.1

    - Write 100MB of dummy data to the specific file
        # {0:1} {1:1} write:TEST:100M
        # {0:1} {1:1} "write:TEST:100M, write:TEST2:50M"
        # {0:1} {1:1} -g write:TEST:100M
        # {0:1} {1:1} write:TEST:100M -q SYNC

    - Write 100MB of dummy data that is consist of random values to the specific file
        # {0:1} {1:1} write:TEST:100M -q RAND, PROGRESS
                    """.format(
                        cmd,
                        mode,
                        "Read all files from current",
                        "Read specific files",
                    )

                # nettest #
                elif SysMgr.checkMode("nettest"):
                    helpStr = (
                        _getDesc("Run network I/O operations")
                        + """
Options:
    -x  <IP:PORT>               set local address
    -I  <PROTOCOL{:IP:PORT}>    set job
    -i  <TIME>                  set timer
    -R  <COUNT>                 set repeat count
    -q  <NAME{:VALUE}>          set environment variables
    -v                          verbose
                        """
                    )

                    helpStr += """
Examples:
    - {2:1}
        # {0:1} {1:1}
        # {0:1} {1:1} udp:192.168.105.10:2222

    - {2:1} with logs
        # {0:1} {1:1} -v

    - {2:1} 5 times
        # {0:1} {1:1} udp:192.168.105.10:2222 -R 5

    - {2:1} 5 times with sleep for 1.5 seconds
        # {0:1} {1:1} udp:192.168.105.10:2222 -R 5 -q SLEEP:1.5s

    - {2:1} for 3 seconds
        # {0:1} {1:1} udp:192.168.105.10:2222 -R -i 3

    - {2:1} with 3 processes
        # {0:1} {1:1} "udp:192.168.105.10:2222, udp:192.168.105.10:2222, udp:192.168.105.10:2222"
                    """.format(
                        cmd, mode, "Send UDP packages"
                    )

                # flush #
                elif SysMgr.checkMode("flush"):
                    helpStr = (
                        _getDesc("Flush specific file pages on memory")
                        + """
Options:
    -i  <SEC>                   set interval
    -I  <PID|COMM>              set input path
    -v                          verbose
                        """
                    )

                    helpStr += """
Examples:
    - Flush all caches on memory
        # {0:1} {1:1}
        # {0:1} {1:1} 1 (page caches)
        # {0:1} {1:1} 2 (slab caches)
        # {0:1} {1:1} 3 (both caches)

    - Flush specific file pages for the file
        # {0:1} {1:1} ./TEST

    - Flush all file caches mapped or opened on memory for specific processes
        # {0:1} {1:1} -I "*systemd*"
        # {0:1} {1:1} -I 1231

    - Flush specific file pages for the file every 3 seconds
        # {0:1} {1:1} ./TEST -i 3
                    """.format(
                        cmd,
                        mode,
                    )

                # sync #
                elif SysMgr.checkMode("sync"):
                    helpStr = (
                        _getDesc("Commit filesystem caches to disk")
                        + """
Options:
    -v                          verbose
                        """
                    )

                    helpStr += """
Examples:
    - Commit system filesystem caches to disk
        # {0:1} {1:1}

    - Commit filesystem caches of specific files to disk
        # {0:1} {1:1} "TEST,TEST2"
                    """.format(
                        cmd,
                        mode,
                    )

                # fadvise #
                elif SysMgr.checkMode("fadvise"):
                    helpStr = (
                        _getDesc("Predeclare an access pattern for file data")
                        + """
Options:
    -v                          verbose
                        """
                    )

                    helpStr += """
Examples:
    - Predeclare an access pattern for whole file data
        # {0:1} {1:1} {2:1}:RANDOM
        # {0:1} {1:1} {2:1}:SEQUENTIAL
        # {0:1} {1:1} {2:1}:WILLNEED
        # {0:1} {1:1} {2:1}:DONTNEED
        # {0:1} {1:1} {2:1}:NOREUSE

    - Predeclare an access pattern for specific file data
        # {0:1} {1:1} {2:1}:DONTNEED:10M
        # {0:1} {1:1} {2:1}:DONTNEED:0:100M
        # {0:1} {1:1} {2:1}:DONTNEED:100K:1M
                    """.format(
                        cmd,
                        mode,
                        "/home/iipeace/a.out",
                    )

                # list #
                elif SysMgr.checkMode("list"):
                    helpStr = (
                        _getDesc("Show %s processes" % __module__)
                        + """
Options:
    -Q                          print all rows in a stream
    -v                          verbose
                        """
                    )

                # start #
                elif SysMgr.checkMode("start"):
                    helpStr = (
                        _getDesc("Send signal to all Guider processes to run")
                        + """
Options:
    -v                          verbose
                        """
                    )

                # event #
                elif SysMgr.checkMode("event"):
                    helpStr = """
Usage:
    # {0:1} {1:1} <EVENT> [OPTIONS] [--help]

Description:
    Notify the event to Guider processes

Options:
    -I  <EVENT>                 set event name
    -g  <PID>                   set target
    -X  <IP:PORT>               set request address
    -v                          verbose

Commands:
    CMD_BUFFER:SIZE                  resize the monitoring buffer
    CMD_CLEAR                        clear the monitoring buffer
    CMD_DISABLE:ATTR                 disable monitoring of the specific resource
    CMD_ENABLE:ATTR                  enable monitoring of the specific resource
    CMD_EXIT                         terminate the Guider process
    CMD_FILTER:ITEM                  set the task filter
    CMD_GETDUMP:LEN                  get monitoring data
    CMD_GETINFO                      get threshold data
    CMD_GETSUM:LEN                   summarize monitoring data
    CMD_INTERVAL:SEC                 change the monitoring interval
    CMD_KILL                         kill all child tasks
    CMD_OUTPATH:PATH                 change the output path
    CMD_PAUSE{{:SEC}}                  pause all monitoring activities with buffer clear
    CMD_RELOAD:PATH                  reload the threshold config
    CMD_RESET                        clear monitoring stats
    CMD_RESTART                      restart the Guider process
    CMD_SAVEMIN{{:TICK@NAME#PATH}}     save the minimal monitoring results
    CMD_SAVERAW{{:TICK@NAME#PATH}}     save the monitoring results without summary
    CMD_SAVE{{:TICK@NAME#PATH}}        save the monitoring results with summary
    CMD_SLEEP{{:SEC}}                  sleep for seconds
    CMD_SAVECPU                      save current cpu usages
    CMD_SAVECPUTOTAL                 save total cpu usages
    CMD_SAVEMEM                      save memory usages
    CMD_SAVEMEMPROC                  save memory usages with smaps info
    CMD_SAVEBLOCK                    save current block usages
    CMD_SAVEBLOCKTOTAL               save total block usages
    CMD_SAVEPID                      save PIDs
    CMD_SAVERUNTIME                  save runtimes
    CMD_SAVEPRIO                     save priorities
    CMD_SAVESWAP                     save swap usages
    CMD_STOP                         stop the threshold monitoring
    CMD_UPDATE:PATH                  update the threshold data from the file
                        """.format(
                        cmd, mode
                    )

                    helpStr += """
Examples:
    - Notify custom events {2:1}
        # {0:1} {1:1} EVENT1
        # {0:1} {1:1} "EVENT1, EVENT2"
        # {0:1} {1:1} -I "EVENT1, EVENT2"

    - Notify custom events to the specific address for minimum latency
        # {0:1} {1:1} "EVENT1, EVENT2" -X
        # {0:1} {1:1} "EVENT1, EVENT2" -X 4545
        # {0:1} {1:1} "EVENT1, EVENT2" -X 192.168.155.200:4545

    - Notify custom events to specific Guider processes
        # {0:1} {1:1} "EVENT1, EVENT2" -g 1234, 1237

    - Notify CMD_SAVE family event {2:1} to save the monitoring results to the specific file
        # {0:1} {1:1} CMD_SAVE
        # {0:1} {1:1} CMD_SAVE:1m
        # {0:1} {1:1} CMD_SAVERAW
        # {0:1} {1:1} CMD_SAVEMIN
        # {0:1} {1:1} "CMD_SAVECPU, CMD_SAVEMEM, CMD_SAVEBLOCK"
        # {0:1} {1:1} "CMD_SAVEMEMPROC, CMD_SAVESWAP"
        # {0:1} {1:1} "CMD_SAVEPID, CMD_SAVERUNTIME, CMD_SAVEPRIO"
        # {0:1} {1:1} CMD_SAVE:2s@FrameDropCase
        # {0:1} {1:1} CMD_SAVE:2s@FrameDropCase#/data/guider
        # {0:1} {1:1} CMD_SAVE:#/data/guider+/tmp
        # {0:1} {1:1} CMD_SAVE:#+/tmp

    - Notify CMD_OUTPATH event {2:1} to change the output path
        # {0:1} {1:1} CMD_PATH:/data/guider

    - Notify CMD_RESET event {2:1} to reset the monitoring stats
        # {0:1} {1:1} CMD_RESET

    - Notify CMD_BUFFER event {2:1} to resize the monitoring buffer
        # {0:1} {1:1} CMD_BUFFER:500k
        # {0:1} {1:1} CMD_BUFFER:2m

    - Notify CMD_INTERVAL event {2:1} to change the monitoring interval
        # {0:1} {1:1} CMD_INTERVAL:3s
        # {0:1} {1:1} CMD_INTERVAL:1m

    - Notify CMD_FILTER event {2:1} to set the task filter
        # {0:1} {1:1} CMD_FILTER:"*kworker*"
        # {0:1} {1:1} CMD_FILTER:"a.out|systemd"

    - Notify CMD_UPDATE event {2:1} to update the threshold data from the specific file
        # {0:1} {1:1} CMD_UPDATE:test.conf

    - Notify CMD_ENABLE/CMD_DISABLE events {2:1} to control monitoring of specific resources
        # {0:1} {1:1} CMD_ENABLE:ALL
        # {0:1} {1:1} CMD_DISABLE:ALL
        # {0:1} {1:1} "CMD_DISABLE:CPU, CMD_DISABLE:GPU"
        # {0:1} {1:1} "CMD_ENABLE:MEM, CMD_ENABLE:IRQ, CMD_ENABLE:DISK"
        # {0:1} {1:1} "CMD_ENABLE:BLOCK, CMD_ENABLE:NETWORK, CMD_ENABLE:PMU"
        # {0:1} {1:1} CMD_ENABLE:LOG
                    """.format(
                        cmd, mode, "to all Guider processes"
                    )

                # server / fserver / hserver #
                elif (
                    SysMgr.checkMode("server")
                    or SysMgr.checkMode("fserver")
                    or SysMgr.checkMode("hserver")
                ):
                    helpStr = (
                        _getDesc("Run server")
                        + """
Options:
    -x  <IP:PORT>               set local address
    -X  <IP:PORT>               set agent address
    -I  <DIR>                   set default path
    -u                          run in the background
    -C  <PATH>                  set config path
    -E  <DIR>                   set cache dir path
    -q  <NAME{:VALUE}>          set environment variables
    -v                          verbose
                        """
                    )

                    helpStr += """
Examples:
    - {2:1} in background
        # {0:1} {1:1} -u

    - {2:1} with specific local address
        # {0:1} {1:1} -x localhost:5556
        # {0:1} {1:1} -x 127.0.0.1:5556
        # {0:1} {1:1} -x 0 -q PUBLICIP

    - {2:1} in parallel mode
        # {0:1} {1:1} -q PARALLEL

    - Run file server and register to the agent as a service node
        # {0:1} {1:1} -X localhost:3456
        # {0:1} {1:1} -X 127.0.0.1:3456 -q CLIIP:127.0.0.1, CLIPORT:12345
        # {0:1} {1:1} -X 127.0.0.1:3456 -q CLIPORT:12345-12399
        # {0:1} {1:1} -X 127.0.0.1:3456 -q CLIPORT:12345-

    - {2:1} with specific config file
        # {0:1} {1:1} -C
        # {0:1} {1:1} -C guider.conf

    - {2:1} without timeout
        # {0:1} {1:1} -q NOTIMEOUT

    - {2:1} with specific timeout
        # {0:1} {1:1} -q TIMEOUT:1.5

    - {2:1} with specific read chunk size for command process
        # {0:1} {1:1} -q READCHUNK:4096

    - {2:1} without output for remote request
        # {0:1} {1:1} -q QUIET

    - Request the performance image to HTTP server
        # {0:1} req "http://127.0.0.1:12345/draw?ip=127.0.0.1&port=5555&interval=5&type=cpu&drawlen:50"
        # {0:1} req "http://127.0.0.1:12345/draw?filter=test*,guider&env=trimidx:-20:&all=true"
                    """.format(
                        cmd,
                        mode,
                        "Run %s"
                        % {
                            "server": "server",
                            "fserver": "file server",
                            "hserver": "HTTP server",
                        }[mode],
                    )

                # send #
                elif SysMgr.checkMode("send"):
                    helpStr = """
Usage:
    # {0:1} {1:1} <MESSAGE> [OPTIONS] [--help]

Description:
    Send a UDP message

Options:
    -x  <IP:PORT>               set local address
    -X  <IP:PORT>               set request address
    -R  <COUNT>                 set repeat count
    -q  <NAME{{:VALUE}}>          set environment variables
    -v                          verbose
                        """.format(
                        cmd, mode
                    )

                    helpStr += """
Examples:
    - Send a UDP message
        # {0:1} {1:1} OK -X 192.168.100.100:12345

    - Send a UDP message 5 times
        # {0:1} {1:1} OK -X 192.168.100.100:12345 -R 5
                    """.format(
                        cmd, mode
                    )

                # client #
                elif SysMgr.checkMode("cli"):
                    helpStr = """
Usage:
    # {0:1} {1:1} <COMMAND> [OPTIONS] [--help]

Description:
    Request remote commands to the server
{2:1}
Options:
    -x  <IP:PORT>               set local address
    -X  <IP:PORT>               set request address
    -E  <DIR>                   set cache dir path
    -c  <COMMAND>               set command
    -q  <NAME{{:VALUE}}>          set environment variables
    -v                          verbose
                        """.format(
                        cmd, mode, SysMgr.cliCmdStr
                    )

                    helpStr += """
Examples:
    - Run client with interaction menu
        # {0:1} {1:1}

    - List files and directories in the specific directory
        # {0:1} {1:1} "dir:.."

    - Download a.out from the server to ./a.out
        # {0:1} {1:1} "d:a.out@./a.out"
        # {0:1} {1:1} "download:a.out@./a.out"
        # {0:1} {1:1} "download:a.out"

    - Download all files in test directory from the server to backup directory
        # {0:1} {1:1} "download:test/*@backup/"

    - Download TEST* from the server to ./
        # {0:1} {1:1} "d:TEST*"

    - Upload ./a.out to a.out in the server
        # {0:1} {1:1} "u:./a.out@a.out"
        # {0:1} {1:1} "upload:./a.out@a.out"
        # {0:1} {1:1} "upload:./a.out"

    - Ping to the server
        # {0:1} {1:1} p
        # {0:1} {1:1} "ping"

    - Print node list registered in the server
        # {0:1} {1:1} l
        # {0:1} {1:1} "list"

    - Print all worker processes executed by the server
        # {0:1} {1:1} j
        # {0:1} {1:1} "jobs"

    - Terminate all worker processes executed by the server
        # {0:1} {1:1} c
        # {0:1} {1:1} "clear"

    - Request remote commands in parallel by the server
        # {0:1} {1:1} "ls -lha", "date"
        # {0:1} {1:1} "192.168.0.100:5050|vmstat 1, 192.168.0.101:1234|find /"

    - Request remote commands by all nodes connected to the agent
        # {0:1} {1:1} "b:ls -lha"
        # {0:1} {1:1} "broadcast:ls -lha"
        # {0:1} {1:1} "b:restart"
        # {0:1} {1:1} "b:download:test/*@backup/""

    - Notify an event occurred to the server
        # {0:1} {1:1} "n:TEST"
        # {0:1} {1:1} "notify:TEST"

    - Request remote commands on the central server through the agent
        # {0:1} {1:1} "s:ls -lha"
        # {0:1} {1:1} "upstream:ls -lha"
        # {0:1} {1:1} "upstream:notify:TEST

    - Request remote commands by specific nodes connected to the agent
        # {0:1} {1:1} "b:@192.168.105.86/5589@192.168.105.100/5566:ls -lha"

    - Execute a remote command with no timeout
        # {0:1} {1:1} "ls -lha" -q NOTIMEOUT

    - Execute a remote command with specific timeout
        # {0:1} {1:1} "ls -lha" -q TIMEOUT:1.5

    - Execute a remote command with no output
        # {0:1} {1:1} "ls -lha" -q QUIET

    - Run client with interaction menu using specific client addresses
        # {0:1} {1:1} -q CLIIP:127.0.0.1, CLIPORT:12345
        # {0:1} {1:1} -q CLIPORT:12345-12399
        # {0:1} {1:1} -q CLIPORT:12345-

    - Execute remote Guider commands in fixed-line-output
        # {0:1} {1:1} "192.168.0.100:5050|GUIDER top -m 15:, 192.168.0.101:1234|GUIDER ttop -m 15:"
                    """.format(
                        cmd, mode
                    )

                # default #
                elif mode.startswith("-") or mode == "help":
                    helpStr = (
                        defStr
                        + """
COMMAND({2:1}):
{0:1}
FILE:
    Profile file (e.g. guider.dat)
    Report  file (e.g. guider.out)

Options:
    Check COMMAND with --help (e.g. {1:1} top --help)
                    """.format(
                            SysMgr.getCmdString(),
                            cmd,
                            len(SysMgr.getCmdSet()),
                        )
                    )

                # wrong command #
                else:
                    SysMgr.printErr("wrong command %s" % mode)
                    sys.exit(-1)

                # no pager #
                if SysMgr.findOption("Q"):
                    SysMgr.setStream()

                # print small logo #
                SysMgr.printLogo()

                # print help #
                SysMgr.printPipe(helpStr)

            else:
                helpStr = (
                    defStr
                    + """
Author:
    {0:1} ({1:1})

Contact:
    {1:1} | {2:1}/issues

Copyright:
    {3:1}
    License {4:1}.
    This is free software
                    """.format(
                        __author__,
                        __email__,
                        __repository__,
                        __copyright__,
                        __license__,
                    )
                )

                # no pager #
                SysMgr.setStream()

                # print help #
                SysMgr.printPipe(helpStr)

            if isExit:
                sys.exit(0)

    @staticmethod
    def writeSysrq(key=None, isList=False, retList=False):
        status = SysMgr.readFile("/proc/sys/kernel/sysrq", verb=False)

        helpStr = """
Key Enable Status:
 [ %s ]
  0 - disable sysrq completely
  1 - enable all functions of sysrq
 >1 - bitmask of allowed sysrq functions (see below for detailed function
 description)::
      2 =   0x2 - enable control of console logging level
      4 =   0x4 - enable control of keyboard (SAK, unraw)
      8 =   0x8 - enable debugging dumps of processes etc.
     16 =  0x10 - enable sync command
     32 =  0x20 - enable remount read-only
     64 =  0x40 - enable signalling of processes (term, kill, oom-kill)
    128 =  0x80 - allow reboot/poweroff
    256 = 0x100 - allow nicing of all RT tasks

Key Value List:
 b      Will immediately reboot the system without syncing or unmounting your disks.
 c      Will perform a system crash and a crashdump will be taken if configured.
 d      Shows all locks that are held.
 e      Send a SIGTERM to all processes, except for init.
 f      Will call the oom killer to kill a memory hog process, but do not panic if nothing can be killed.
 g      Used by kgdb (kernel debugger)
 h      Will display help (actually any other key than those listed here will display help. but ``h`` is easy to remember :-)
 i      Send a SIGKILL to all processes, except for init.
 j      Forcibly "Just thaw it" - filesystems frozen by the FIFREEZE ioctl.
 k      Secure Access Key (SAK) Kills all programs on the current virtual console. NOTE: See important comments below in SAK section.
 l      Shows a stack backtrace for all active CPUs.
 m      Will dump current memory info to your console.
 n      Used to make RT tasks nice-able
 o      Will shut your system off (if configured and supported).
 p      Will dump the current registers and flags to your console.
 q      Will dump per CPU lists of all armed hrtimers (but NOT regular timer_list timers) and detailed information about all clockevent devices.
 r      Turns off keyboard raw mode and sets it to XLATE.
 s      Will attempt to sync all mounted filesystems.
 t      Will dump a list of current tasks and their information to your console.
 u      Will attempt to remount all mounted filesystems read-only.
 v      Forcefully restores framebuffer console causes ETM buffer dump [ARM-specific]
 w      Dumps tasks that are in uninterruptible (blocked) state.
 x      Used by xmon interface on ppc/powerpc platforms. Show global PMU Registers on sparc64. Dump all TLB entries on MIPS.
 y      Show global CPU Registers [SPARC-64 specific]
 z      Dump the ftrace buffer
 0-9    Sets the console log level, controlling which kernel messages will be printed to your console.
        (``0``, for example would make it so that only emergency messages like PANICs or OOPSes would make it to your console.)
        """ % (
            "0x%x" % long(status) if status else "N/A"
        )

        # return help #
        if retList:
            return helpStr

        # check help #
        if isList:
            SysMgr.printPipe(helpStr)
            return

        # check input key #
        if not key:
            if SysMgr.hasMainArg():
                key = SysMgr.getMainArg()
            elif SysMgr.inputParam:
                key = SysMgr.inputParam
            else:
                SysMgr.printErr("no input for PATH")
                sys.exit(-1)

        # check sysrq knob #
        sysrqPath = "%s/sysrq-trigger" % SysMgr.procPath
        if not os.path.exists(sysrqPath):
            SysMgr.printErr("no '%s'" % sysrqPath)
            return

        # write key #
        try:
            SysMgr.printInfo("start writing '%s' to sysrq" % key)

            with open(sysrqPath, "w") as fd:
                for k in key:
                    SysMgr.printInfo("wrote '%s' to sysrq" % k)
                    fd.write(k)
                    fd.flush()
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                "failed to write '%s' to '%s'" % (key, sysrqPath), True
            )

    @staticmethod
    def getKernelVersion(name=False, number=False):
        if not number and SysMgr.kernelVersion:
            return SysMgr.kernelVersion

        try:
            kernel = os.uname()[2]
            if not number and kernel:
                return kernel

            kernelList = kernel.split(".")

            # return number tuple #
            if number:
                return (long(kernelList[0]), long(kernelList[1]))

            # get kernel major version #
            kernelVersion = ".".join(kernelList[0:2])

            # get kernel minor version #
            if len(kernel) > 2:
                kernelVersion = "%s.%s" % (
                    kernelVersion,
                    kernelList[2].split("-")[0],
                )

            # update system info #
            SysMgr.kernelVersion = kernelVersion

            return kernelVersion
        except SystemExit:
            sys.exit(0)
        except:
            return None

    @staticmethod
    def getArch():
        if SysMgr.arch:
            return SysMgr.arch

        try:
            if SysMgr.isWindows:
                if "PROCESSOR_ARCHITECTURE" in os.environ:
                    arch = os.environ["PROCESSOR_ARCHITECTURE"]
                else:
                    arch = "N/A"
            else:
                arch = os.uname()[4]

            if arch.startswith("arm"):
                SysMgr.arch = "arm"
            elif arch.startswith("aarch64"):
                SysMgr.arch = "aarch64"
            elif (
                arch.startswith("x86_64")
                or arch.startswith("ia64")
                or arch == "AMD64"
            ):
                SysMgr.arch = "x64"
            elif (
                arch.startswith("i386")
                or arch.startswith("i686")
                or arch == "x86"
            ):
                SysMgr.arch = "x86"
            else:
                SysMgr.arch = arch
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.arch = "arm"

        return SysMgr.arch

    @staticmethod
    def setArch(arch):
        if not arch or not arch:
            return

        arch = UtilMgr.cleanItem(arch)

        # set syscall table #
        if arch == "aarch64":
            ConfigMgr.sysList = ConfigMgr.SYSCALL_AARCH64
            ConfigMgr.regList = ConfigMgr.REGS_AARCH64
            ConfigMgr.wordSize = 8
        elif arch == "x64":
            ConfigMgr.sysList = ConfigMgr.SYSCALL_X64
            ConfigMgr.regList = ConfigMgr.REGS_X64
            ConfigMgr.wordSize = 8
        elif arch == "riscv64":
            ConfigMgr.sysList = ConfigMgr.SYSCALL_RISCV64
            ConfigMgr.regList = ConfigMgr.REGS_RISCV64
            ConfigMgr.wordSize = 8
        elif arch == "arm":
            ConfigMgr.sysList = ConfigMgr.SYSCALL_ARM
            ConfigMgr.regList = ConfigMgr.REGS_ARM
            ConfigMgr.wordSize = 4
        elif arch == "x86":
            ConfigMgr.sysList = ConfigMgr.SYSCALL_X86
            ConfigMgr.regList = ConfigMgr.REGS_X86
            ConfigMgr.wordSize = 4
        else:
            support = " / ".join(ConfigMgr.supportArch)
            SysMgr.printErr(
                "failed to set architecture to %s, only %s supported"
                % (arch, support)
            )
            sys.exit(-1)

        SysMgr.arch = arch

    @staticmethod
    def waitForFile(dirname, filename):
        while 1:
            try:
                events = SysMgr.inotify(dirname)
                if not events:
                    break

                finished = False
                for item in events:
                    if item[2] == filename:
                        finished = True
                        break
                if finished:
                    break
            except SystemExit:
                sys.exit(0)
            except:
                break

    @staticmethod
    def fanotify(
        path,
        flags=[],
        oflags=[],
        mark=[],
        mask=[],
        dirfd="AT_FDCWD",
        wait=False,
        retfd=False,
        fd=None,
        retlist=True,
        verb=False,
    ):
        # set path #
        if not path:
            return False
        elif not isinstance(path, list):
            path = [path]

        # check flags type #
        if not isinstance(flags, list) or not isinstance(oflags, list):
            SysMgr.printErr("failed to get flags as a list")
            return False

        # load libc #
        SysMgr.loadLibcObj(exit=True)

        """
        class fanotify_event_metadata(Structure):
            _fields_ = (
                ("event_len", c_uint32),
                ("vers", c_uint8),
                ("reserved", c_uint8),
                ("metadata_len", c_uint16),
                ("mask", c_uint64),
                ("fd", c_int32),
                ("pid", c_int32),
            )

        class fanotify_event_info_header(Structure):
            _fields_ = (
                ("info_type", c_uint8),
                ("pad", c_uint8),
                ("len", c_uint16),
            )

        class fanotify_event_info_fid(Structure):
            _fields_ = (
                ("hdr", fanotify_event_info_header),
                ("fsid", c_int * 2),  # __kernel_fsid_t
                ("file_handle", c_char * 1),
            )

        class fanotify_response(Structure):
            _fields_ = (
                ("fd", c_int32),
                ("response", c_uint32),
            )
        """

        reportTid = "REPORTTID" in SysMgr.environList

        if not fd:
            # make init flags #
            if not flags:
                flags = ["FAN_CLOEXEC", "FAN_CLASS_NOTIF"]

            if reportTid:
                kernelVersion = SysMgr.getKernelVersion(number=True)
                if kernelVersion < (4, 2):
                    SysMgr.printErr(
                        (
                            "failed to report TID "
                            "because kernel version %s is lesser than 4.2"
                        )
                        % ".".join(list(map(str, kernelVersion)))
                    )
                    sys.exit(-1)
                else:
                    flags.append("FAN_REPORT_TID")
                    reportTid = True

            UtilMgr.makeReverseDict(
                ConfigMgr.FAN_INIT_TYPE, ConfigMgr.FAN_INIT_TYPE_REVERSE
            )
            flagList = ConfigMgr.FAN_INIT_TYPE_REVERSE
            flagBits = 0
            for flag in flags:
                try:
                    flagBits |= flagList[flag]
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr("failed to get init flags", True)
                    return False

            # get open flags #
            if not oflags:
                oflags = ["O_RDONLY", "O_CLOEXEC"]
                if "LARGEFILE" in SysMgr.environList:
                    oflags += ["O_LARGEFILE"]
            oflagList = {v: k for k, v in ConfigMgr.OPEN_TYPE.items()}
            oflagBits = 0
            for flag in oflags:
                try:
                    oflagBits |= oflagList[flag]
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr("failed to get open flags", True)
                    return False

            # check libc funcion #
            if hasattr(SysMgr.libcObj, "fanotify_init"):
                hasLibc = True
            else:
                hasLibc = False

            # get event fd #
            if hasLibc:
                fd = SysMgr.libcObj.fanotify_init(flagBits, oflagBits)
            else:
                fd = SysMgr.syscall("fanotify_init", flagBits, oflagBits)
            if fd < 0:
                SysMgr.printErr(
                    "failed to call fanotify_init because %s"
                    % SysMgr.getErrReason()
                )

                # check kernel config #
                if not "CONFIG_FANOTIFY=" in SysMgr.getKconf():
                    SysMgr.printErr(
                        "failed to use fanotify, please check kernel configs"
                    )
                    sys.exit(-1)

                return False

            # get mask flags #
            if not mask:
                mask = [
                    "FAN_ACCESS",
                    "FAN_MODIFY",
                    "FAN_CLOSE_WRITE",
                    "FAN_CLOSE_NOWRITE",
                    "FAN_OPEN",
                    "FAN_ONDIR",
                    "FAN_EVENT_ON_CHILD",
                ]

            # check version #
            if SysMgr.getKernelVersion(number=True) < (5, 1):
                common = set(mask) & set(
                    [
                        "FAN_CREATE",
                        "FAN_DELETE",
                        "FAN_DELETE_SELF",
                        "FAN_MOVED_FROM",
                        "FAN_MOVED_TO",
                        "FAN_MOVE_SELF",
                        "FAN_OPEN_EXEC_PERM",
                    ]
                )
                if common:
                    SysMgr.printErr(
                        (
                            "failed to set '%s' "
                            "because they are supported from kernel 5.1"
                        )
                        % "|".join(common)
                    )
                    sys.exit(0)

            # make reverse table #
            UtilMgr.makeReverseDict(
                ConfigMgr.FAN_EVENT_TYPE, ConfigMgr.FAN_EVENT_TYPE_REVERSE
            )

            eflagList = ConfigMgr.FAN_EVENT_TYPE_REVERSE
            eflagBits = 0
            for flag in mask:
                try:
                    eflagBits |= eflagList[flag]
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr("failed to get mask flags", True)
                    return False

            # get mark option #
            if not mark:
                mark = ["FAN_MARK_ADD"]

            # add mount info #
            if "ALLMOUNT" in SysMgr.environList:
                mark += ["FAN_MARK_MOUNT"]
                path = list(SysMgr.convMountList(SysMgr.getMountData()).keys())
            elif "INMOUNT" in SysMgr.environList:
                mark += ["FAN_MARK_MOUNT"]

            # apply target path filter #
            if "TARGETPATH" in SysMgr.environList:
                newPath = []
                for item in path:
                    if not UtilMgr.isValidStr(
                        item, SysMgr.environList["TARGETPATH"]
                    ):
                        continue
                    newPath.append(item)
                path = newPath

            # apply except path filter #
            if "EXCEPTPATH" in SysMgr.environList:
                newPath = []
                for item in path:
                    if UtilMgr.isValidStr(
                        item, SysMgr.environList["EXCEPTPATH"]
                    ):
                        continue
                    newPath.append(item)
                path = newPath

            mflagList = {v: k for k, v in ConfigMgr.FAN_MARK_TYPE.items()}
            mflagBits = 0
            for flag in mark:
                try:
                    mflagBits |= mflagList[flag]
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr("failed to get mark flags", True)
                    return False

            # get dirfd option #
            try:
                UtilMgr.makeReverseDict(
                    ConfigMgr.FAT_TYPE, ConfigMgr.FAT_TYPE_REVERSE
                )
                dirfd = ConfigMgr.FAT_TYPE_REVERSE[dirfd]
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr("failed to convert dirfd '%s'" % dirfd, True)
                return False

            # define prototype for fanotify_mark() #
            if hasLibc:
                SysMgr.libcObj.fanotify_mark.argtypes = [
                    c_int,
                    c_uint,
                    c_uint64,
                    c_int,
                    c_void_p,
                ]

            # check path list #
            newPath = []
            for item in path:

                def _checker():
                    try:
                        return os.access(item, os.R_OK)
                    except SystemExit:
                        sys.exit(0)
                    except:
                        return

                # check timeout #
                if SysMgr.checkTimeout(_checker, 1):
                    newPath.append(item)
                else:
                    continue
            path = newPath

            # mark each target #
            for item in path:
                # mark #
                if hasLibc:
                    ret = SysMgr.libcObj.fanotify_mark(
                        fd, mflagBits, eflagBits, dirfd, item.encode("latin-1")
                    )
                else:
                    ret = SysMgr.syscall(
                        "fanotify_mark",
                        fd,
                        mflagBits,
                        eflagBits,
                        dirfd,
                        item.encode("latin-1"),
                    )
                if ret < 0:
                    SysMgr.printErr(
                        "failed to call fanotify_mark for '%s' because %s"
                        % (item, SysMgr.getErrReason())
                    )
                    continue

            # just return fd #
            if retfd:
                return fd

        # define variables #
        BUF_LEN = 8192
        buf = (c_char * BUF_LEN)()
        fmt = "IBBHQii"
        revents = []
        size = struct.calcsize(fmt)
        conv = UtilMgr.convColor
        isValidStr = UtilMgr.isValidStr
        current = None

        # get target file list #
        targetFile = SysMgr.environList.get("TARGETFILE", [])

        # get except file list #
        exceptFile = SysMgr.environList.get("EXCEPTFILE", [])

        # get target comm list #
        targetComm = SysMgr.environList.get("TARGETCOMM", [])

        # get except comm list #
        exceptComm = SysMgr.environList.get("EXCEPTCOMM", [])

        # get event list #
        targetEvt = SysMgr.environList.get("TARGETEVT", [])

        # get command list #
        targetCmd = SysMgr.environList.get("EVENTCMD", [])

        # get select object #
        selectObj = SysMgr.getPkg("select", False)

        # read events #
        length = SysMgr.libcObj.read(fd, byref(buf), BUF_LEN)
        if length < 0:
            SysMgr.printWarn("failed to read fanotify event", verb)
            return False

        while 1:
            i = 0
            while i < length:
                elen, vers, _, dlen, emask, efd, epid = struct.unpack(
                    fmt, buf[i : i + size]
                )

                # ignore my event #
                if epid == SysMgr.pid:
                    i += size
                    os.close(efd)
                    continue

                # get path and comm info #
                epath = SysMgr.getFdName("self", efd)
                ecomm = SysMgr.getComm(epid, cache=True, save=True)

                # close event fd for target file #
                try:
                    os.close(efd)
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

                # check skip conditions #
                if (
                    (targetComm and not isValidStr(ecomm, targetComm))
                    or (exceptComm and isValidStr(ecomm, exceptComm))
                    or (targetFile and not isValidStr(epath, targetFile))
                    or (exceptFile and isValidStr(epath, exceptFile))
                ):
                    i += size
                    continue

                # get event info #
                events = UtilMgr.getFlagString(emask, ConfigMgr.FAN_EVENT_TYPE)

                # check target event #
                if targetEvt and not UtilMgr.isValidStr(
                    events, targetEvt, inc=True
                ):
                    i += size
                    continue

                if reportTid:
                    ppid = SysMgr.getTgid(epid)
                    pcomm = SysMgr.getComm(ppid)
                    parentInfo = [ppid, pcomm]
                else:
                    parentInfo = []

                # add items to return list #
                revents.append([epath, events, epid, ecomm] + parentInfo)

                # print event info #
                if not retlist:
                    if not current:
                        current = SysMgr.updateUptime()

                    procInfo = "%s(%s)" % (ecomm, epid)
                    SysMgr.printPipe(
                        "[%.6f] %s@%s by %s"
                        % (
                            current,
                            conv(events, "WARNING"),
                            conv(epath, "GREEN"),
                            conv(procInfo, "YELLOW"),
                        )
                    )

                # execute command #
                if targetCmd:
                    SysMgr.runFileCmd(targetCmd, epath)

                i += size

            # check and read remain data #
            try:
                res = selectObj.select([fd], [], [], 0)
                if not res[0]:
                    break

                # read events #
                res = SysMgr.libcObj.read(fd, byref(buf), BUF_LEN)
                if res < 0:
                    break
                else:
                    length = res
            except SystemExit:
                sys.exit(0)
            except:
                break

        # return empty list #
        if length > 0 and not revents:
            revents.append([])

        return revents

    @staticmethod
    def inotify(
        path,
        flags=[],
        wait=False,
        retfd=False,
        fd=None,
        retlist=True,
        verb=False,
    ):
        # check and set path list #
        if not path:
            return False
        elif not isinstance(path, list):
            path = [path]

        # load libc #
        SysMgr.loadLibcObj(exit=True)

        # check flags type #
        if not isinstance(flags, list):
            SysMgr.printErr("failed to get flags as a list")
            return False

        # check and wait for files to be created #
        if wait:
            exitFlag = False

            for item in path:
                if os.path.exists(item):
                    continue

                # get file info #
                dirname, filename = UtilMgr.getPath(item)

                # set exit flag #
                if not "CONT" in SysMgr.environList:
                    exitFlag = True

                SysMgr.printWarn(
                    "failed to access to '%s', wait for it..." % item, verb
                )

                # wait for file creation #
                SysMgr.waitForFile(dirname, filename)

                # check result #
                if os.path.exists(item):
                    current = SysMgr.updateUptime()
                    fpath = SysMgr.convFullPath(item)
                    SysMgr.printPipe("[%.6f] IN_CREATE@%s" % (current, fpath))
                else:
                    SysMgr.printWarn(
                        "skipped to wait '%s' to be created" % item, True
                    )

            # check exit condition #
            if exitFlag:
                sys.exit(0)

        if not SysMgr.inotifyEnable:
            inotifyFuncs = [
                "inotify_init",
                "inotify_add_watch",
                "read",
                "inotify_rm_watch",
                "close",
            ]

            # check functions #
            for func in inotifyFuncs:
                if not hasattr(SysMgr.libcObj, func):
                    SysMgr.printWarn(
                        "no %s in %s" % (func, SysMgr.libcPath), verb
                    )
                    return False

            SysMgr.inotifyEnable = True

        class inotify_event(Structure):
            _fields_ = (
                ("wd", c_int),
                ("mask", c_uint32),
                ("cookie", c_uint32),
                ("len", c_uint32),
                ("name", c_char_p),
            )

        # create an object #
        ie = inotify_event()

        # define variables #
        fmt = "iIII"
        revents = []
        EVENT_SIZE = sizeof(ie)
        size = struct.calcsize(fmt)
        BUF_LEN = 1024 * (EVENT_SIZE + 16)
        buf = (c_char * BUF_LEN)()
        conv = UtilMgr.convColor
        isValidStr = UtilMgr.isValidStr
        origFd = fd

        if not fd:
            # create a file descriptor #
            fd = SysMgr.libcObj.inotify_init()
            if fd < 0:
                SysMgr.printWarn("failed to inotify_init", verb)
                return False

            if retfd:
                return fd

        # get flag bits #
        fbits = UtilMgr.getFlagBit(ConfigMgr.INOTIFY_TYPE, flags)
        if fbits == 0:
            # IN_ALL #
            fbits = 0xFFF

        # create reverse list #
        flist = {}
        for flag in flags:
            flist[ConfigMgr.INOTIFY_TYPE[flag]] = flag

        # add watch #
        wlist = {}
        for item in path:
            wd = SysMgr.libcObj.inotify_add_watch(fd, item.encode(), fbits)
            if wd < 0:
                SysMgr.printWarn(
                    "failed to inotify_add_watch for '%s'" % item, verb
                )
                continue
            wlist[wd] = item

        # get target file list #
        targetFile = SysMgr.environList.get("TARGETFILE", [])

        # get except file list #
        exceptFile = SysMgr.environList.get("EXCEPTFILE", [])

        # get event list #
        targetEvt = SysMgr.environList.get("TARGETEVT", [])

        # get command list #
        targetCmd = SysMgr.environList.get("EVENTCMD", [])

        # get select object #
        selectObj = SysMgr.getPkg("select", False)

        # read events #
        length = SysMgr.libcObj.read(fd, byref(buf), BUF_LEN)
        if length < 0:
            SysMgr.printWarn("failed to read inotify event", verb)
            return False

        if not retlist:
            current = SysMgr.updateUptime()

        # check events #
        while 1:
            i = 0
            while i < length:
                wd, mask, cookie, flen = struct.unpack(fmt, buf[i : i + size])

                # get file name #
                if flen > 0:
                    (fname,) = struct.unpack(
                        "%ds" % flen, buf[i + size : i + size + flen]
                    )
                    fname = fname.decode().rstrip("\0")
                else:
                    fname = ""

                # check skip conditions #
                if (targetFile and not isValidStr(fname, targetFile)) or (
                    exceptFile and isValidStr(fname, exceptFile)
                ):
                    i += size + flen
                    continue

                # get events #
                try:
                    rtypes = UtilMgr.getFlagList(mask, ConfigMgr.INOTIFY_TYPE)

                    # check target event #
                    if targetEvt and not UtilMgr.isValidStr(
                        rtypes, targetEvt, inc=True
                    ):
                        i += size
                        continue

                    revents.append([wlist[wd], rtypes, fname])
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

                if not retlist:
                    SysMgr.printPipe(
                        "[%.6f] %s@%s"
                        % (
                            current,
                            conv("|".join(rtypes), "WARNING"),
                            conv(os.path.join(wlist[wd], fname), "GREEN"),
                        )
                    )

                # execute command #
                if targetCmd:
                    SysMgr.runFileCmd(targetCmd, fname)

                i += size + flen

            # check and read remain data #
            try:
                res = selectObj.select([fd], [], [], 0)
                if not res[0]:
                    break

                # read events #
                res = SysMgr.libcObj.read(fd, byref(buf), BUF_LEN)
                if res < 0:
                    break
                else:
                    length = res
            except SystemExit:
                sys.exit(0)
            except:
                break

        # return empty list #
        if length > 0 and not revents:
            revents.append([])

        # clean up #
        if not origFd:
            for wd in list(wlist):
                SysMgr.libcObj.inotify_rm_watch(fd, wd)

            SysMgr.libcObj.close(fd)

        return revents

    @staticmethod
    def getNrSyscall(name):
        try:
            if not name.startswith("sys_"):
                name = "sys_" + name

            return SysMgr.syscallCache[name]
        except SystemExit:
            sys.exit(0)
        except:
            num = ConfigMgr.sysList.index(name)
            SysMgr.syscallCache[name] = num
            return num

    @staticmethod
    def calloc(nmemb, size):
        try:
            # load libc #
            SysMgr.loadLibcObj(exit=True)

            # define prototype #
            SysMgr.libcObj.calloc.argtypes = [c_int, c_int]
            SysMgr.libcObj.calloc.restype = c_void_p

            # call #
            ret = SysMgr.libcObj.calloc(nmemb, size)
            if c_long(ret).value == -1:
                raise Exception(SysMgr.getErrReason())
            else:
                return ret
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                "failed to call calloc(%s, %s)" % (nmemb, size), True
            )
            return -1

    @staticmethod
    def mmap(addr, length, prot, flags, fd, offset):
        try:
            # load libc #
            SysMgr.loadLibcObj(exit=True)

            # define prototype #
            SysMgr.libcObj.mmap.argtypes = [
                POINTER(None),
                c_size_t,
                c_int,
                c_int,
                c_int,
                c_long,
            ]
            SysMgr.libcObj.mmap.restype = POINTER(None)

            # call #
            ret = SysMgr.libcObj.mmap(addr, length, prot, flags, fd, offset)
            if c_long(ret).value == -1:
                raise Exception(SysMgr.getErrReason())
            else:
                return ret
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                "failed to call mmap(%x, %d, %x, %x, %d, %x)"
                % (addr, length, prot, flags, fd, offset),
                True,
            )
            return -1

    @staticmethod
    def syscall(syscall, *args):
        if not SysMgr.isLinux:
            return None

        try:
            # load libc #
            if not SysMgr.loadLibcObj():
                raise Exception("no libc")

            # check syscall #
            if UtilMgr.isNumber(syscall):
                nrSyscall = long(syscall)
                if nrSyscall >= len(ConfigMgr.sysList):
                    raise Exception("wrong syscall number")
                nmSyscall = ConfigMgr.sysList[nrSyscall]
            elif UtilMgr.isString(syscall):
                val = syscall.lower()
                if val[0:4] == "sys_":
                    nmSyscall = val
                else:
                    nmSyscall = "sys_%s" % val

                nrSyscall = SysMgr.getNrSyscall(nmSyscall)
            else:
                raise Exception("wrong syscall")

            """
            try:
                nrParams = len(ConfigMgr.SYSCALL_PROTOTYPES[nmSyscall[4:]][1])
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr(
                    "failed to get the number of arguments for %s" % nmSyscall
                )
                raise Exception("wrong syscall")

            # check arguments #
            if len(args) != nrParams:
                SysMgr.printErr(
                    "failed to get arguments for %s because of wrong parameters"
                    % nmSyscall
                )
                raise Exception("wrong params")
            """

            # call syscall #
            ret = SysMgr.libcObj.syscall(nrSyscall, *[arg for arg in args])

            # print error code #
            if SysMgr.warnEnable and ret < 0:
                SysMgr.printWarn(
                    "failed to call %s syscall" % syscall, code=True
                )

            return ret
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn(
                "failed to call %s syscall" % syscall, True, reason=True
            )

    @staticmethod
    def checkRootPerm(exit=True, verb=True, attr="error", msg=""):
        if SysMgr.isRoot():
            return True

        # print log #
        if verb:
            if msg:
                msg = " to %s" % msg

            if attr == "error":
                logger = SysMgr.printErr
            else:
                logger = SysMgr.printWarn

            logger("failed to get root permission%s" % msg)

        if SysMgr.forceEnable:
            return True

        # exit #
        if exit:
            sys.exit(-1)
        else:
            return False

    @staticmethod
    def isRoot():
        if (SysMgr.isLinux or SysMgr.isDarwin) and os.geteuid() == 0:
            return True
        else:
            return False

    @staticmethod
    def openPerfEvent(econfig, cpu=-1, pid=-1):
        try:
            perfEventList = ConfigMgr.PERF_EVENT_TYPE
            if econfig in ConfigMgr.PERF_HW_EVENT_TYPE:
                nrType = perfEventList.index("PERF_TYPE_HARDWARE")
                nrConfig = ConfigMgr.PERF_HW_EVENT_TYPE.index(econfig)
            elif econfig in ConfigMgr.PERF_SW_EVENT_TYPE:
                nrType = perfEventList.index("PERF_TYPE_SOFTWARE")
                nrConfig = ConfigMgr.PERF_SW_EVENT_TYPE.index(econfig)
            else:
                raise Exception("wrong perf event")
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                "failed to recognize '%s' as the perf event type" % econfig
            )
            return False

        if SysMgr.guiderObj:
            # reference to http://man7.org/linux/man-pages/man2/perf_event_open.2.html #
            # pylint: disable-next=no-member
            func = SysMgr.guiderObj.perf_event_open
            fd = func(nrType, nrConfig, pid, cpu, -1, 0)
            if fd < 0:
                # check root permission #
                if not SysMgr.checkRootPerm(
                    exit=False, msg="open perf events"
                ):
                    return False
                else:
                    return -1
            else:
                return fd

        # load libc #
        if not SysMgr.loadLibcObj():
            SysMgr.perfEnable = False
            SysMgr.perfGroupEnable = False
            return False

        # define struct perf_event_attr #
        class union_anon_5(Union):
            pass

        union_anon_5.__slots__ = ["sample_period", "sample_freq"]
        union_anon_5._fields_ = [
            ("sample_period", c_uint64),
            ("sample_freq", c_uint64),
        ]

        class union_anon_6(Union):
            pass

        union_anon_6.__slots__ = ["wakeup_events", "wakeup_watermark"]
        union_anon_6._fields_ = [
            ("wakeup_events", c_uint32),
            ("wakeup_watermark", c_uint32),
        ]

        class union_anon_7(Union):
            pass

        union_anon_7.__slots__ = [
            "bp_addr",
            "kprobe_func",
            "uprobe_path",
            "config1",
        ]
        union_anon_7._fields_ = [
            ("bp_addr", c_uint64),
            ("kprobe_func", c_uint64),
            ("uprobe_path", c_uint64),
            ("config1", c_uint64),
        ]

        class union_anon_8(Union):
            pass

        union_anon_8.__slots__ = [
            "bp_len",
            "kprobe_addr",
            "probe_offset",
            "config2",
        ]
        union_anon_8._fields_ = [
            ("bp_len", c_uint64),
            ("kprobe_addr", c_uint64),
            ("probe_offset", c_uint64),
            ("config2", c_uint64),
        ]

        class struct_perf_event_attr(Structure):
            pass

        """
        > kernel/include/uapi/linux/perf_event.h

        struct perf_event_attr {

            /*
             * Major type: hardware/software/tracepoint/etc.
             */
            __u32			type;

            /*
             * Size of the attr structure, for fwd/bwd compat.
             */
            __u32			size;

            /*
             * Type specific configuration information.
             */
            __u64			config;

            union {
                __u64		sample_period;
                __u64		sample_freq;
            };

            __u64			sample_type;
            __u64			read_format;

            __u64			disabled       :  1, /* off by default        */
                            inherit	       :  1, /* children inherit it   */
                            pinned	       :  1, /* must always be on PMU */
                            exclusive      :  1, /* only group on PMU     */
                            exclude_user   :  1, /* don't count user      */
                            exclude_kernel :  1, /* ditto kernel          */
                            exclude_hv     :  1, /* ditto hypervisor      */
                            exclude_idle   :  1, /* don't count when idle */
                            mmap           :  1, /* include mmap data     */
                            comm	       :  1, /* include comm data     */
                            freq           :  1, /* use freq, not period  */
                            inherit_stat   :  1, /* per task counts       */
                            enable_on_exec :  1, /* next exec enables     */
                            task           :  1, /* trace fork/exit       */
                            watermark      :  1, /* wakeup_watermark      */
                            /*
                             * precise_ip:
                             *
                             *  0 - SAMPLE_IP can have arbitrary skid
                             *  1 - SAMPLE_IP must have constant skid
                             *  2 - SAMPLE_IP requested to have 0 skid
                             *  3 - SAMPLE_IP must have 0 skid
                             *
                             *  See also PERF_RECORD_MISC_EXACT_IP
                             */
                            precise_ip     :  2, /* skid constraint       */
                            mmap_data      :  1, /* non-exec mmap data    */
                            sample_id_all  :  1, /* sample_type all events */

                            exclude_host   :  1, /* don't count in host   */
                            exclude_guest  :  1, /* don't count in guest  */

                            exclude_callchain_kernel : 1, /* exclude kernel callchains */
                            exclude_callchain_user   : 1, /* exclude user callchains */
                            mmap2          :  1, /* include mmap with inode data     */
                            comm_exec      :  1, /* flag comm events that are due to an exec */
                            use_clockid    :  1, /* use @clockid for time fields */
                            context_switch :  1, /* context switch data */
                            write_backward :  1, /* Write ring buffer from end to beginning */
                            namespaces     :  1, /* include namespaces data */
                            ksymbol        :  1, /* include ksymbol events */
                            bpf_event      :  1, /* include bpf events */
                            aux_output     :  1, /* generate AUX records instead of events */
                            cgroup         :  1, /* include cgroup events */
                            text_poke      :  1, /* include text poke events */
                            build_id       :  1, /* use build id in mmap2 events */
                            inherit_thread :  1, /* children only inherit if cloned with CLONE_THREAD */
                            remove_on_exec :  1, /* event is removed from task on exec */
                            sigtrap        :  1, /* send synchronous SIGTRAP on event */
                            __reserved_1   : 26;

            union {
                __u32		wakeup_events;	  /* wakeup every n events */
                __u32		wakeup_watermark; /* bytes before wakeup   */
            };

            __u32			bp_type;
            union {
                __u64		bp_addr;
                __u64		kprobe_func; /* for perf_kprobe */
                __u64		uprobe_path; /* for perf_uprobe */
                __u64		config1; /* extension of config */
            };
            union {
                __u64		bp_len;
                __u64		kprobe_addr; /* when kprobe_func == NULL */
                __u64		probe_offset; /* for perf_[k,u]probe */
                __u64		config2; /* extension of config1 */
            };
            __u64	branch_sample_type; /* enum perf_branch_sample_type */

            /*
             * Defines set of user regs to dump on samples.
             * See asm/perf_regs.h for details.
             */
            __u64	sample_regs_user;

            /*
             * Defines size of the user stack to dump on samples.
             */
            __u32	sample_stack_user;

            __s32	clockid;
            /*
             * Defines set of regs to dump for each sample
             * state captured on:
             *  - precise = 0: PMU interrupt
             *  - precise > 0: sampled instruction
             *
             * See asm/perf_regs.h for details.
             */
            __u64	sample_regs_intr;

            /*
             * Wakeup watermark for AUX area
             */
            __u32	aux_watermark;
            __u16	sample_max_stack;
            __u16	__reserved_2;
            __u32	aux_sample_size;
            __u32	__reserved_3;

            /*
             * User provided data if sigtrap=1, passed back to user via
             * siginfo_t::si_perf_data, e.g. to permit user to identify the event.
             */
            __u64	sig_data;
        };
        """
        struct_perf_event_attr.__slots__ = [
            "type",
            "size",
            "config",
            "unnamed_1",
            "sample_type",
            "read_format",
            "disabled",
            "inherit",
            "pinned",
            "exclusive",
            "exclude_user",
            "exclude_kernel",
            "exclude_hv",
            "exclude_idle",
            "mmap",
            "comm",
            "freq",
            "inherit_stat",
            "enable_on_exec",
            "task",
            "watermark",
            "precise_ip",
            "mmap_data",
            "sample_id_all",
            "exclude_host",
            "exclude_guest",
            "exclude_callchain_kernel",
            "exclude_callchain_user",
            "mmap2",
            "comm_exec",
            "use_clockid",
            "context_switch",
            "write_backward",
            "namespaces",
            "ksymbol",
            "bpf_event",
            "aux_output",
            "cgroup",
            "text_poke",
            "__reserved_1",
            "unnamed_2",
            "bp_type",
            "unnamed_3",
            "unnamed_4",
            "branch_sample_type",
            "sample_regs_user",
            "sample_stack_user",
            "clockid",
            "sample_regs_intr",
            "aux_watermark",
            "sample_max_stack",
            "__reserved_2",
        ]
        struct_perf_event_attr._anonymous_ = [
            "unnamed_1",
            "unnamed_2",
            "unnamed_3",
            "unnamed_4",
        ]
        struct_perf_event_attr._fields_ = [
            ("type", c_uint32),
            ("size", c_uint32),
            ("config", c_uint64),
            ("unnamed_1", union_anon_5),
            ("sample_type", c_uint64),
            ("read_format", c_uint64),
            ("disabled", c_uint64, 1),
            ("inherit", c_uint64, 1),
            ("pinned", c_uint64, 1),
            ("exclusive", c_uint64, 1),
            ("exclude_user", c_uint64, 1),
            ("exclude_kernel", c_uint64, 1),
            ("exclude_hv", c_uint64, 1),
            ("exclude_idle", c_uint64, 1),
            ("mmap", c_uint64, 1),
            ("comm", c_uint64, 1),
            ("freq", c_uint64, 1),
            ("inherit_stat", c_uint64, 1),
            ("enable_on_exec", c_uint64, 1),
            ("task", c_uint64, 1),
            ("watermark", c_uint64, 1),
            ("precise_ip", c_uint64, 2),
            ("mmap_data", c_uint64, 1),
            ("sample_id_all", c_uint64, 1),
            ("exclude_host", c_uint64, 1),
            ("exclude_guest", c_uint64, 1),
            ("exclude_callchain_kernel", c_uint64, 1),
            ("exclude_callchain_user", c_uint64, 1),
            ("mmap2", c_uint64, 1),
            ("comm_exec", c_uint64, 1),
            ("use_clockid", c_uint64, 1),
            ("context_switch", c_uint64, 1),
            ("write_backward", c_uint64, 1),
            ("namespaces", c_uint64, 1),
            ("ksymbol", c_uint64, 1),
            ("bpf_event", c_uint64, 1),
            ("aux_output", c_uint64, 1),
            ("cgroup", c_uint64, 1),
            ("text_poke", c_uint64, 1),
            ("build_id", c_uint64, 1),
            ("inherit_thread", c_uint64, 1),
            ("remove_on_exec", c_uint64, 1),
            ("sigtrap", c_uint64, 1),
            ("__reserved_1", c_uint64, 26),
            ("unnamed_2", union_anon_6),
            ("bp_type", c_uint32),
            ("unnamed_3", union_anon_7),
            ("unnamed_4", union_anon_8),
            ("branch_sample_type", c_uint64),
            ("sample_regs_user", c_uint64),
            ("sample_stack_user", c_uint32),
            ("clockid", c_int32),
            ("sample_regs_intr", c_uint64),
            ("aux_watermark", c_uint32),
            ("sample_max_stack", c_uint16),
            ("__reserved_2", c_uint16),
            ("aux_sample_size", c_uint32),
            ("__reserved_3", c_uint32),
            ("sig_data", c_uint64),
        ]

        # get perf event list #
        perfEvents = SysMgr.getPerfEventList()

        # set struct perf_event_attr #
        perf_attr = struct_perf_event_attr()
        perf_attr.type = nrType
        perf_attr.config = nrConfig
        perf_attr.size = sizeof(perf_attr)
        perf_attr.disabled = 1

        # set struct perf_event_attr for function profiling #
        if False and pid > -1:
            # TODO: set core ID for task #
            if cpu < 0:
                cpu = 1

            EVENT_SAMPLE = ConfigMgr.PERF_EVENT_SAMPLE
            perf_attr.type = perfEventList.index("PERF_TYPE_HARDWARE")
            perf_attr.config = ConfigMgr.PERF_HW_EVENT_TYPE.index(
                "PERF_COUNT_HW_CPU_CYCLES"
            )
            perf_attr.size = sizeof(perf_attr)
            perf_attr.sample_freq = 600
            perf_attr.sample_type = (
                EVENT_SAMPLE["PERF_SAMPLE_IP"]
                | EVENT_SAMPLE["PERF_SAMPLE_TID"]
                | EVENT_SAMPLE["PERF_SAMPLE_TIME"]
                # | EVENT_SAMPLE["PERF_SAMPLE_ID"]
                # | EVENT_SAMPLE["PERF_SAMPLE_CPU"]
                | EVENT_SAMPLE["PERF_SAMPLE_PERIOD"]
                # | EVENT_SAMPLE["PERF_SAMPLE_STACK_USER"]
                # | EVENT_SAMPLE["PERF_SAMPLE_REGS_USER"]
                | EVENT_SAMPLE["PERF_SAMPLE_CALLCHAIN"]
            )
            perf_attr.read_format = ConfigMgr.PERF_EVENT_READ_FORMAT[
                "PERF_FORMAT_ID"
            ]
            perf_attr.disabled = 1
            perf_attr.inherit = 1
            perf_attr.pinned = 0
            perf_attr.exclusive = 0
            perf_attr.exclude_user = 0
            perf_attr.exclude_kernel = 0
            perf_attr.exclude_hv = 0
            perf_attr.exclude_idle = 0
            perf_attr.mmap = 1
            perf_attr.comm = 1
            perf_attr.freq = 1
            perf_attr.inherit_stat = 0
            perf_attr.enable_on_exec = 0
            perf_attr.task = 1
            perf_attr.watermark = 0
            perf_attr.precise_ip = 0
            perf_attr.mmap_data = 0
            perf_attr.sample_id_all = 1
            perf_attr.exclude_host = 0
            perf_attr.exclude_guest = 1
            perf_attr.exclude_callchain_kernel = 0
            perf_attr.exclude_callchain_user = 0
            perf_attr.mmap2 = 1
            perf_attr.comm_exec = 1
            perf_attr.use_clockid = 0
            perf_attr.context_switch = 0
            perf_attr.write_backward = 0
            perf_attr.namespaces = 0
            perf_attr.ksymbol = 1
            perf_attr.bpf_event = 1
            perf_attr.aux_output = 0
            perf_attr.cgroup = 0
            perf_attr.text_poke = 0
            perf_attr.build_id = 0
            perf_attr.inherit_thread = 0
            perf_attr.remove_on_exec = 0
            perf_attr.sigtrap = 0
            perf_attr.wakeup_events = 0
            perf_attr.config1 = 0
            perf_attr.config2 = 0
            perf_attr.sample_regs_user = 0
            perf_attr.sample_regs_intr = 0
            perf_attr.aux_watermark = 0
            perf_attr.sample_maxstack = 0
            perf_attr.aux_sample_size = 0
            perf_attr.sig_data = 0

        """
        perf_event_open()

        > definition:
        int perf_event_open(struct perf_event_attr *attr,
            pid_t pid, int cpu, int group_fd, unsigned long flags);

        > attr->size:
        PERF_ATTR_SIZE_VER0 64  /* sizeof first published struct */
        PERF_ATTR_SIZE_VER1 72  /* add: config2 */
        PERF_ATTR_SIZE_VER2 80  /* add: branch_sample_type */
        PERF_ATTR_SIZE_VER3 96  /* add: sample_regs_user */
                                /* add: sample_stack_user */
        PERF_ATTR_SIZE_VER4 104 /* add: sample_regs_intr */
        PERF_ATTR_SIZE_VER5 112 /* add: aux_watermark */
        PERF_ATTR_SIZE_VER6 120 /* add: aux_sample_size */
        PERF_ATTR_SIZE_VER7 128 /* add: sig_data */

        > reference:
        http://man7.org/linux/man-pages/man2/perf_event_open.2.html

        > trigger:
        static int __cmd_record(struct record *rec, int argc, const char **argv)
        """

        # call perf_event_open syscall #
        fd = SysMgr.syscall(
            "sys_perf_event_open", pointer(perf_attr), pid, cpu, -1, 0
        )
        if fd < 0:
            # check root permission #
            if not SysMgr.checkRootPerm(exit=False, msg="open perf events"):
                return False
            else:
                return -1

        # allocate buffer for mapping #
        try:
            if False and pid > 0:
                # get mlock size #
                mlockSize = SysMgr.readFile(
                    "/proc/sys/kernel/perf_event_mlock_kb"
                )
                mlockSize = long(mlockSize) if mlockSize else mlockSize

                # allocate initialized buffer #
                # rbuf = SysMgr.calloc(mlockSize << 10, 1)

                # map perf fd to buf #
                buf = SysMgr.mmap(0, mlockSize << 10, 0x3, 0x1, fd, 0)

                # get event ID #
                eid = c_uint64(0)
                SysMgr.syscall(
                    "ioctl", fd, perfEvents["PERF_EVENT_IOC_ID"], byref(eid)
                )
                eid = eid.value

                # print buffer to check #
                UtilMgr.printCtypeArray(buf, mlockSize)
            else:
                raise Exception()
        except SystemExit:
            sys.exit(0)
        except:
            mlockSize = buf = eid = 0

        # set NONBLOCK fd #
        SysMgr.setBlock(fd, False)

        # control perf event #
        SysMgr.syscall("ioctl", fd, perfEvents["PERF_EVENT_IOC_RESET"], 0)
        SysMgr.syscall("ioctl", fd, perfEvents["PERF_EVENT_IOC_ENABLE"], 0)

        # free perf_attr object, but memory leak exists now #
        del perf_attr

        return fd

    @staticmethod
    def getPerfEventList():
        if SysMgr.perfEventList:
            return SysMgr.perfEventList

        # define constants for ioctl #
        _IOC_NRBITS = 8
        _IOC_TYPEBITS = 8
        _IOC_SIZEBITS = 14  # architecture specific
        _IOC_DIRBITS = 2
        _IOC_NRMASK = (1 << _IOC_NRBITS) - 1
        _IOC_TYPEMASK = (1 << _IOC_TYPEBITS) - 1
        _IOC_SIZEMASK = (1 << _IOC_SIZEBITS) - 1
        _IOC_DIRMASK = (1 << _IOC_DIRBITS) - 1
        _IOC_NRSHIFT = 0
        _IOC_TYPESHIFT = _IOC_NRSHIFT + _IOC_NRBITS
        _IOC_SIZESHIFT = _IOC_TYPESHIFT + _IOC_TYPEBITS
        _IOC_DIRSHIFT = _IOC_SIZESHIFT + _IOC_SIZEBITS
        _IOC_NONE = 0
        _IOC_WRITE = 1
        _IOC_READ = 2

        def _IOC(dir, type, nr, size):
            return (
                dir << _IOC_DIRSHIFT
                | ord(type) << _IOC_TYPESHIFT
                | nr << _IOC_NRSHIFT
                | size << _IOC_SIZESHIFT
            )

        def _IO(type, nr):
            return _IOC(_IOC_NONE, type, nr, 0)

        def _IOR(type, nr, size):
            return _IOC(_IOC_READ, type, nr, size)

        def _IOW(type, nr, size):
            return _IOC(_IOC_WRITE, type, nr, size)

        def _IOWR(type, nr, size):
            return _IOC(_IOC_READ | _IOC_WRITE, type, nr, size)

        # save event list #
        SysMgr.perfEventList = {
            "PERF_EVENT_IOC_ENABLE": _IO("$", 0),
            "PERF_EVENT_IOC_DISABLE": _IO("$", 1),
            "PERF_EVENT_IOC_REFRESH": _IO("$", 2),
            "PERF_EVENT_IOC_RESET": _IO("$", 3),
            "PERF_EVENT_IOC_PERIOD": _IOW("$", 4, sizeof(c_uint64)),
            "PERF_EVENT_IOC_SET_OUTPUT": _IO("$", 5),
            "PERF_EVENT_IOC_SET_FILTER": _IOW("$", 6, sizeof(c_uint)),
            "PERF_EVENT_IOC_ID": _IOR("$", 7, sizeof(c_uint64)),
            "PERF_EVENT_IOC_SET_BPF": _IOW("$", 8, sizeof(c_uint32)),
            "PERF_EVENT_IOC_PAUSE_OUTPUT": _IOW("$", 9, sizeof(c_uint32)),
        }

        return SysMgr.perfEventList

    @staticmethod
    def readPerfEvents(fdList):
        if SysMgr.guiderObj:
            retList = []

            for fd in fdList:
                try:
                    # read PMU data #
                    # pylint: disable-next=no-member
                    value = SysMgr.guiderObj.perf_event_read(fd)

                    # add value to list #
                    retList.append(value)
                except SystemExit:
                    sys.exit(0)
                except:
                    retList.append(None)

            return retList

        # load libc #
        if not SysMgr.loadLibcObj():
            return

        # define struct read_group_format #
        class struct_anon_5(Structure):
            __slots__ = ["value", "id"]

            _fields_ = [("value", c_uint64), ("id", c_uint64)]

        """
        struct read_group_format {
            uint64_t nr;            /* The number of events */
            uint64_t time_enabled;  /* if PERF_FORMAT_TOTAL_TIME_ENABLED */
            uint64_t time_running;  /* if PERF_FORMAT_TOTAL_TIME_RUNNING */
            struct {
                uint64_t value;     /* The value of the event */
                uint64_t id;        /* if PERF_FORMAT_ID */
            } values[];
        };
        """
        """
        class struct_read_group_format(Structure):
            __slots__ = [
                'nr',
                #'time_enabled',
                #'time_running',
                'values',
            ]

            _fields_ = [
                ('nr', c_uint64),
                #('time_enabled', c_uint64),
                #('time_running', c_uint64),
                ('values', POINTER(struct_anon_5)),
            ]
        """

        # define struct read_format #
        """
        struct read_format {
            uint64_t value;         /* The value of the event */
            uint64_t time_enabled;  /* if PERF_FORMAT_TOTAL_TIME_ENABLED */
            uint64_t time_running;  /* if PERF_FORMAT_TOTAL_TIME_RUNNING */
            uint64_t id;            /* if PERF_FORMAT_ID */
        };
        """

        class struct_read_format(Structure):
            __slots__ = [
                "value",
                #'time_enabled',
                #'time_running',
                "id",
            ]

            _fields_ = [
                ("value", c_uint64),
                # ('time_enabled', c_uint64),
                # ('time_running', c_uint64),
                ("id", c_uint64),
            ]

        # get perf event list #
        perfEvents = SysMgr.getPerfEventList()

        # declare syscalls #
        SysMgr.libcObj.ioctl.restype = c_int
        SysMgr.libcObj.ioctl.argtypes = [c_int, c_ulong]
        SysMgr.libcObj.read.argtypes = [c_int, POINTER(None), c_uint]
        SysMgr.libcObj.read.restype = c_int

        # declare buffer and retList #
        pbuf = (8 * c_ubyte)()
        retList = []

        for fd in fdList:
            try:
                # read PMU data #
                SysMgr.libcObj.read(fd, pointer(pbuf), sizeof(pbuf))

                # control perf event #
                SysMgr.libcObj.ioctl(fd, perfEvents["PERF_EVENT_IOC_RESET"], 0)

                # cast buffer to data #
                retList.append(cast(pbuf, POINTER(c_ulong)).contents.value)
            except SystemExit:
                sys.exit(0)
            except:
                retList.append(None)

        return retList

    @staticmethod
    def initSystemPerfEvents():
        # check perf option #
        if not SysMgr.perfEnable:
            return

        # check root permission #
        if not SysMgr.isRoot():
            SysMgr.perfEnable = False
            return

        confPath = "%s/sys/kernel" % SysMgr.procPath

        # check config #
        try:
            # PMUs = "/sys/bus/event_source/devices"
            attrPath = "%s/perf_event_paranoid" % confPath
            with open(attrPath, "w+") as fd:
                """
                -1 - not paranoid at all
                 0 - disallow raw tracepoint access for unpriv
                 1 - disallow CPU events for unpriv
                 2 - disallow kernel profiling for unpriv
                 3 - disallow user profiling for unpriv
                """
                paranoid = long(fd.readline()[:-1])
                if paranoid > -1:
                    fd.seek(0)
                    # write all privilege to read perf events #
                    fd.write("-1")
                    SysMgr.printWarn(
                        (
                            "change value of %s from %s to -1 "
                            "to read all perf events"
                        )
                        % (attrPath, paranoid)
                    )
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn("enable CONFIG_PERF_EVENTS kernel option")
            return

        # set perf attributes #
        for attr in os.listdir(confPath):
            try:
                if not attr.startswith("perf_"):
                    continue

                attrCap = attr.upper()
                if not attrCap in SysMgr.environList:
                    continue

                attrPath = "%s/%s" % (confPath, attr)
                val = SysMgr.environList[attrCap][0] + "\n"

                with open(attrPath, "w") as fd:
                    ret = fd.write(val)
                    if ret > 0:
                        SysMgr.printInfo(
                            "changed value of %s to %s" % (attr, val)
                        )
                    else:
                        raise Exception(SysMgr.getErrReason())
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printWarn(
                    "failed to change value of %s to %s" % (attr, val),
                    True,
                    True,
                )

        hwTargetList = [
            "PERF_COUNT_HW_CPU_CYCLES",
            "PERF_COUNT_HW_INSTRUCTIONS",
            "PERF_COUNT_HW_CACHE_REFERENCES",
            "PERF_COUNT_HW_CACHE_MISSES",
            "PERF_COUNT_HW_BRANCH_INSTRUCTIONS",
            "PERF_COUNT_HW_BRANCH_MISSES",
        ]

        swTargetList = [
            "PERF_COUNT_SW_CPU_CLOCK",
            "PERF_COUNT_SW_PAGE_FAULTS_MIN",
            "PERF_COUNT_SW_PAGE_FAULTS_MAJ",
            "PERF_COUNT_SW_DUMMY",
        ]

        successCnt = 0
        cpuPath = "/sys/devices/system/cpu"
        cpuList = [
            UtilMgr.lstrip(coreId, "cpu")
            for coreId in os.listdir(cpuPath)
            if coreId.startswith("cpu")
        ]

        for item in cpuList:
            try:
                coreId = long(item)
            except:
                continue

            SysMgr.perfEventChannel[coreId] = {}

            # HW Events #
            for evt in hwTargetList:
                # initialize hw event channels #
                ret = SysMgr.openPerfEvent(evt, coreId)
                SysMgr.perfEventChannel[coreId][evt] = ret

                # handle unavailable hw events #
                if SysMgr.perfEventChannel[coreId][evt] == -1:
                    del SysMgr.perfEventChannel[coreId][evt]
                elif not SysMgr.perfEventChannel[coreId][evt]:
                    return
                else:
                    successCnt += 1

            # SW Events #
            for evt in swTargetList:
                # initialize sw event channels #
                ret = SysMgr.openPerfEvent(evt, coreId)
                SysMgr.perfEventChannel[coreId][evt] = ret

                # handle unavailable sw events #
                if SysMgr.perfEventChannel[coreId][evt] == -1:
                    del SysMgr.perfEventChannel[coreId][evt]
                elif not SysMgr.perfEventChannel[coreId][evt]:
                    return
                else:
                    successCnt += 1

            if (
                not SysMgr.perfTargetEvent
                and len(SysMgr.perfEventChannel[coreId]) > 0
            ):
                SysMgr.perfTargetEvent = list(SysMgr.perfEventChannel[coreId])

        if successCnt == 0:
            SysMgr.printWarn("failed to find available perf event")
            SysMgr.perfEnable = False
            SysMgr.perfGroupEnable = False

    @staticmethod
    def initProcPerfEvents(pid):
        eventChannel = {}

        for evt in SysMgr.perfTargetEvent:
            eventChannel[evt] = SysMgr.openPerfEvent(evt, pid=pid)

        return eventChannel

    @staticmethod
    def collectSystemPerfData():
        SysMgr.perfEventData = {}

        # check perf event option #
        if SysMgr.perfGroupEnable and not SysMgr.perfEventChannel:
            return

        # check perf event channel #
        for coreId in list(SysMgr.perfEventChannel):
            # make event list #
            events = list(SysMgr.perfEventChannel[coreId])

            # remove all core events if specific target process exist #
            if SysMgr.perfGroupEnable:
                for fd in SysMgr.perfEventChannel[coreId].values():
                    os.close(fd)
                del SysMgr.perfEventChannel[coreId]
                continue

            # get event data #
            values = SysMgr.readPerfEvents(
                SysMgr.perfEventChannel[coreId].values()
            )

            if not values:
                continue

            # summarize perf data of each cores #
            for idx, evt in enumerate(events):
                try:
                    SysMgr.perfEventData[evt] += values[idx]
                except:
                    SysMgr.perfEventData[evt] = values[idx]

    @staticmethod
    def collectProcPerfData(fdList):
        perfData = {}

        # make event list #
        events = list(fdList)

        # get event data #
        values = SysMgr.readPerfEvents(fdList.values())

        # summarize perf data of each cores #
        for idx, evt in enumerate(events):
            perfData[evt] = values[idx]

        return perfData

    @staticmethod
    def getPerfString(value):
        perfbuf = ""

        if not value:
            return perfbuf

        convColor = UtilMgr.convColor
        convSize = UtilMgr.convSize2Unit

        inst = cpucycle = -1
        cacheref = cachemiss = cachemissrate = -1
        branch = branchmiss = branchmissrate = -1

        # IPC stats #
        try:
            cpucycle = value["PERF_COUNT_HW_CPU_CYCLES"]
            perfbuf = "%sCycle: %s / " % (
                perfbuf,
                convColor(convSize(cpucycle), "YELLOW"),
            )
            inst = value["PERF_COUNT_HW_INSTRUCTIONS"]
            perfbuf = "%sInst: %s / " % (
                perfbuf,
                convColor(convSize(inst), "YELLOW"),
            )
            ipc = inst / float(cpucycle)
            perfbuf = "%sIPC: %s / " % (
                perfbuf,
                convColor("%.2f" % ipc, "YELLOW"),
            )
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # CACHE stats #
        try:
            cacheref = value["PERF_COUNT_HW_CACHE_REFERENCES"]
            cachemiss = value["PERF_COUNT_HW_CACHE_MISSES"]
            cachemissrate = long((cachemiss / float(cacheref)) * 100)
            perfbuf = "%sCacheMiss : %s(%s) / " % (
                perfbuf,
                convSize(cachemiss),
                convColor("%s%%" % cachemissrate, "YELLOW"),
            )
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # BRANCH stats #
        try:
            branch = value["PERF_COUNT_HW_BRANCH_INSTRUCTIONS"]
            branchmiss = value["PERF_COUNT_HW_BRANCH_MISSES"]
            branchmissrate = long((branchmiss / float(branch)) * 100)
            perfbuf = "%sBrcMiss: %s(%s) / " % (
                perfbuf,
                convSize(branchmiss),
                convColor("%s%%" % branchmissrate, "YELLOW"),
            )
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # CPU stats #
        try:
            perfbuf = "%sClk: %s / " % (
                perfbuf,
                convSize(value["PERF_COUNT_SW_CPU_CLOCK"]),
            )
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # FAULT stats #
        try:
            faultmin = value["PERF_COUNT_SW_PAGE_FAULTS_MIN"]
            faultmaj = value["PERF_COUNT_SW_PAGE_FAULTS_MAJ"]
            perfbuf = "%sMinFlt: %s / MajFlt: %s / " % (
                perfbuf,
                format(faultmin, ","),
                format(faultmaj, ","),
            )
        except SystemExit:
            sys.exit(0)
        except:
            pass

        try:
            if perfbuf:
                perfbuf = "%s" % perfbuf[: perfbuf.rfind(" /")]
        except SystemExit:
            sys.exit(0)
        except:
            pass

        return perfbuf

    @staticmethod
    def writeKernelCmd():
        effectiveCmd = []

        if not SysMgr.keventEnable:
            return
        elif not SysMgr.kernelCmd:
            SysMgr.printErr(
                "wrong format for kernel command [NAME:FUNC|ADDR{:ARGS:RET}]"
            )
            sys.exit(-1)
        elif not os.path.isfile(SysMgr.traceEventPath + "/../kprobe_events"):
            SysMgr.printErr(
                "enable CONFIG_KPROBES & CONFIG_KPROBE_EVENTS kernel option"
            )
            sys.exit(-1)

        for cmd in SysMgr.kernelCmd:
            cmdFormat = cmd.split(":")

            # check command format #
            cmdCnt = len(cmdFormat)
            if not (2 <= cmdCnt <= 4):
                SysMgr.printErr(
                    "wrong format for kernel command "
                    "[NAME:FUNC|ADDR{:ARGS:RET}]"
                )
                sys.exit(-1)

            for item in effectiveCmd:
                if cmdFormat[0] == item[0]:
                    SysMgr.printErr(
                        "redundant kernel event name '%s'" % item[0]
                    )
                    sys.exit(-1)

            effectiveCmd.append(cmdFormat)

        # convert kprobe commands #
        hasTable = False
        tempCmd = []
        for cmd in list(effectiveCmd):
            # convert name #
            if cmd[0] == "@SYM":
                realname = True
            else:
                realname = False

            if "*" in cmd[1]:
                # make kernel symbol table #
                if not hasTable:
                    SysMgr.makeKerSymTable()
                    hasTable = True

                symList = [
                    sym
                    for sym in SysMgr.kerSymTable
                    if UtilMgr.isValidStr(sym, [cmd[1]])
                ]

                for sym in symList:
                    newCmd = list(cmd)
                    if realname:
                        newCmd[0] = sym + "_KERNEL"
                    newCmd[1] = sym
                    tempCmd.append(newCmd)
            else:
                newCmd = list(cmd)
                if realname:
                    newCmd[0] = cmd[1] + "_KERNEL"
                tempCmd.append(newCmd)

        # check result #
        effectiveCmd = tempCmd
        if not effectiveCmd:
            SysMgr.printErr("failed to find kernel symbol")
            sys.exit(-1)

        # print kprobe event list #
        SysMgr.printInfo(
            "enabled kernel events [ %s ]"
            % ", ".join([":".join(cmd) for cmd in effectiveCmd])
        )

        # apply kprobe events #
        for cmd in effectiveCmd:
            name, sym = cmd[:2]

            # check redundant event name #
            if (
                SysMgr.userCmd
                and not name != "@SYM"
                and name in (ucmd.split(":")[0] for ucmd in SysMgr.userCmd)
            ):
                SysMgr.printErr(
                    "redundant name '%s' for user event and kernel event"
                    % name
                )
                sys.exit(-1)

            # make entry commands #
            pCmd = "p:%s_enter %s" % (name, sym)
            sCmd = ""
            try:
                # parse argument option #
                for rCmd in cmd[2].split("."):
                    # convert ; to : for type definition #
                    rCmd = rCmd.replace(";", ":")

                    # check absolute argument #
                    if not rCmd:
                        pass
                    elif rCmd[0] == "#":
                        sCmd = "%s %s" % (sCmd, rCmd[1:])
                        continue
                    elif len(rCmd.split("/")) == 1:
                        sCmd = "%s %s" % (sCmd, rCmd)
                        continue

                    rVal = rCmd.split("/")
                    if len(rVal) < 2:
                        continue
                    elif len(rVal) > 2:
                        SysMgr.printErr("wrong command '%s'" % rCmd)
                        sys.exit(-1)
                    tVal = rVal[1]

                    # count the number of prefix * #
                    wCnt = 0
                    for idx, ch in enumerate(tVal):
                        if ch != "*":
                            wCnt = idx
                            break

                    # make entry command #
                    tVal = "%s%s%s:%s" % (
                        "+0(" * wCnt,
                        rVal[0],
                        ")" * wCnt,
                        tVal[wCnt:],
                    )

                    # add argument command to entry command #
                    sCmd = "%s %s" % (sCmd, tVal)
            except SystemExit:
                sys.exit(0)
            except:
                pass

            # apply entry command #
            if sCmd != " NONE":
                pCmd = "%s %s" % (pCmd, sCmd)
                if (
                    SysMgr.writeTraceCmd("../kprobe_events", pCmd, append=True)
                    < 0
                ):
                    SysMgr.printErr("wrong command '%s'" % pCmd)
                    sys.exit(-1)

            # make return commands #
            rCmd = "r:%s_exit %s" % (name, sym)
            sCmd = ""

            try:
                tCmd = cmd[3]

                # check absolute argument #
                if tCmd[0] == "#":
                    sCmd = "%s" % (tCmd[1:])
                else:
                    rVal = tCmd.split("/")
                    if len(rVal) > 2:
                        SysMgr.printErr("wrong command '%s'" % tCmd)
                        sys.exit(-1)
                    tVal = rVal[0]

                    # count the number of prefix * #
                    wCnt = 0
                    for idx, ch in enumerate(tVal):
                        if ch != "*":
                            wCnt = idx
                            break

                    if tCmd != "NONE":
                        # make return command #
                        sCmd = "%s%s%s:%s" % (
                            "+0(" * wCnt,
                            "$retval",
                            ")" * wCnt,
                            tVal[wCnt:],
                        )
                    else:
                        sCmd = "NONE"
            except SystemExit:
                sys.exit(0)
            except:
                pass

            # apply return command #
            if sCmd != "NONE":
                rCmd = "%s %s" % (rCmd, sCmd)
                if (
                    SysMgr.writeTraceCmd("../kprobe_events", rCmd, append=True)
                    < 0
                ):
                    SysMgr.printErr("wrong command '%s'" % rCmd)
                    sys.exit(-1)

        # apply filter #
        if SysMgr.filterGroup:
            cmd = SysMgr.getPidFilter()
            if cmd != "":
                SysMgr.writeTraceCmd("kprobes/filter", cmd)
            else:
                SysMgr.printErr("failed to apply '%s' to kprobe filter" % cmd)
                sys.exit(-1)

        # enable kprobe events #
        if SysMgr.writeTraceCmd("kprobes/enable", "1") < 0:
            SysMgr.printErr("failed to apply '%s' to kprobe events" % cmd)
            sys.exit(-1)

    @staticmethod
    def writeUserCmd():
        effectiveCmd = []

        if not SysMgr.ueventEnable:
            return
        elif not SysMgr.userCmd:
            SysMgr.printErr(
                "wrong format for user command [NAME:FUNC|ADDR:FILE]"
            )
            sys.exit(-1)
        elif not os.path.isfile("%s/uprobe_events" % SysMgr.getTracefsPath()):
            SysMgr.printErr(
                "enable CONFIG_UPROBES & CONFIG_UPROBE_EVENT kernel option"
            )
            sys.exit(-1)

        kernelCmd = SysMgr.kernelCmd

        for cmd in SysMgr.userCmd:
            addr = None
            cvtCmd = cmd.replace("::", "#")
            cmdFormat = cvtCmd.split(":")
            cmdFormat = [cmd.replace("#", "::") for cmd in cmdFormat]

            # check count of items #
            if not (3 <= len(cmdFormat) <= 5):
                SysMgr.printErr(
                    "wrong format for user command [NAME:FUNC|ADDR:FILE{:ARGS:RET}]"
                )
                sys.exit(-1)

            # get items #
            name, func, path = cmdFormat[:3]

            if len(cmdFormat) == 4:
                args = cmdFormat[3]
            else:
                args = ""

            if len(cmdFormat) == 5:
                retval = cmdFormat[4]
            else:
                retval = ""

            # check redundant event name #
            if (
                kernelCmd
                and name != "@SYM"
                and name in (kcmd.split(":")[0] for kcmd in kernelCmd)
            ):
                SysMgr.printErr(
                    (
                        "redundant event name '%s' exists "
                        "both user event and kernel event"
                    )
                    % name,
                )
                sys.exit(-1)

            # check binary file #
            if not os.path.isfile(path):
                pids = SysMgr.getTgids(path)
                if pids:
                    addr = []
                    for pid in pids:
                        symList = SysMgr.getProcAddrBySymbol(pid, [func])
                        for val in symList.values():
                            addr.append([long(val[0], 16), val[3], val[1]])
                else:
                    SysMgr.printErr("failed to find '%s' binary" % path)
                    sys.exit(-1)

            # symbol input #
            objdumpPath = None
            if addr:
                pass
            elif not func.startswith("0x"):
                # symbol input with no objdump path #
                if not "OBJDUMP" in SysMgr.environList:
                    # get address of symbol in binary #
                    addr = ElfAnalyzer.getSymOffset(func, path, loadAddr=False)
                # symbol input with objdump #
                else:
                    objdumpPath = SysMgr.environList["OBJDUMP"][0]

                    SysMgr.printInfo(
                        "apply '%s' to objdump path" % objdumpPath
                    )

                    if not os.path.isfile(objdumpPath):
                        SysMgr.printErr(
                            "failed to find %s to use objdump" % objdumpPath
                        )
                        sys.exit(-1)

                    # get address of symbol in binary #
                    addr = ElfAnalyzer.getSymOffset(
                        func, path, objdumpPath, loadAddr=True
                    )

                if not addr:
                    SysMgr.printErr("failed to find '%s' in %s" % (func, path))
                    sys.exit(-1)
            # address input #
            else:
                addr = func
                try:
                    hex(long(addr, base=16))
                except:
                    SysMgr.printErr("failed to recognize address %s" % addr)
                    sys.exit(-1)

            for item in effectiveCmd:
                if name == item[0] and name != "@SYM":
                    SysMgr.printErr("redundant user event name '%s'" % item[0])
                    sys.exit(-1)

            # convert address #
            if isinstance(addr, list):
                symList = []
                for one in addr:
                    try:
                        # set addr #
                        target = str(hex(one[0])).rstrip("L")
                    except SystemExit:
                        sys.exit(0)
                    except:
                        # set symbol #
                        target = str(one[1])

                    # set path #
                    if len(one) >= 3:
                        path = one[2]

                    # convert name #
                    if name == "@SYM":
                        realname = one[1] + "_USER"
                    else:
                        realname = name

                    symList.append(one[1])
                    effectiveCmd.append([realname, target, path, args, retval])

                SysMgr.printInfo(
                    "found multiple symbols [ %s ]" % ", ".join(symList)
                )
            else:
                effectiveCmd.append([name, addr, path, args, retval])

        # print uprobe event list #
        SysMgr.printInfo(
            "enabled user events [ %s ]"
            % ", ".join([":".join(cmd) for cmd in effectiveCmd])
        )

        # apply uprobe events #
        for cmd in effectiveCmd:
            # apply entry events #
            pCmd = "p:%s_enter %s:%s %s" % (cmd[0], cmd[2], cmd[1], cmd[3])
            if SysMgr.writeTraceCmd("../uprobe_events", pCmd, append=True) < 0:
                SysMgr.printErr("wrong command '%s'" % pCmd)
                sys.exit(-1)

            # get retval name #
            if cmd[4]:
                if cmd[4].upper() == "RET":
                    retval = "ret=$retval"
                else:
                    retval = "ret=$retval:%s" % cmd[4]
            else:
                retval = ""

            # apply return events #
            rCmd = "r:%s_exit %s:%s %s %s" % (
                cmd[0],
                cmd[2],
                cmd[1],
                cmd[3],
                retval,
            )
            if SysMgr.writeTraceCmd("../uprobe_events", rCmd, append=True) < 0:
                SysMgr.printErr("wrong command '%s'" % rCmd)
                sys.exit(-1)

        # apply filter #
        if SysMgr.filterGroup:
            cmd = SysMgr.getPidFilter()
            if cmd != "":
                SysMgr.writeTraceCmd("uprobes/filter", cmd)
            else:
                SysMgr.printErr("failed to apply '%s' to uprobe filter" % cmd)
                sys.exit(-1)

        # enable uprobe events #
        if SysMgr.writeTraceCmd("uprobes/enable", "1") < 0:
            SysMgr.printErr("failed to apply '%s' to uprobe events" % cmd)
            sys.exit(-1)

    @staticmethod
    def writeSyscallCmd(enable):
        scmd = ""
        defaultList = [
            "sys_bpf",
            "sys_execve",
            "sys_execveat",
            "sys_ioprio_set",
            "sys_nice",
            "sys_sched_setattr",
            "sys_sched_setparam",
            "sys_sched_setscheduler",
            "sys_setpriority",
        ]

        if SysMgr.isFuncMode() and not SysMgr.heapEnable:
            cmd = "raw_syscalls/sys_enter/enable"
        else:
            cmd = "raw_syscalls/enable"

        # specific syscalls #
        if enable:
            sfilter = ""
            pfilter = SysMgr.getPidFilter()
            if SysMgr.syscallList:
                sfilter = "("
                for val in SysMgr.syscallList:
                    sfilter += " id == %s ||" % val
                sfilter = "%s )" % sfilter[: sfilter.rfind(" ||")]

            if sfilter and pfilter:
                scmd = "(%s && %s)" % (sfilter, pfilter)
            elif sfilter:
                scmd = sfilter
                for item in defaultList:
                    try:
                        scmd = "%s || ( id == %s )" % (
                            scmd,
                            SysMgr.getNrSyscall(item),
                        )
                    except:
                        continue
            elif pfilter:
                scmd = "(%s)" % pfilter
            else:
                pass
        # default syscalls #
        else:
            for item in defaultList:
                try:
                    scmd = "%s || ( id == %s )" % (
                        scmd,
                        SysMgr.getNrSyscall(item),
                    )
                except:
                    continue
            scmd = scmd[scmd.find("(") :]

        SysMgr.writeTraceCmd("raw_syscalls/filter", scmd)
        ret = SysMgr.writeTraceCmd(cmd, "1")
        if ret < 0:
            SysMgr.printWarn("failed to enable syscall events", True)

    @staticmethod
    def printSigError(tid, signal, warn=True):
        if warn:
            printFunc = SysMgr.printWarn
        else:
            printFunc = SysMgr.printErr

        # get comm #
        comm = SysMgr.getComm(tid)

        # get signal name #
        try:
            signal = ConfigMgr.SIG_LIST[signal]
        except:
            pass
        if "." in signal:
            signal = signal.split(".", 2)[1]

        printFunc(
            "failed to send %s to %s(%s)" % (signal, comm, tid), reason=True
        )

    @staticmethod
    def getLine():
        try:
            inspect = SysMgr.getPkg("inspect", False)
            if not inspect:
                return None

            lines = ""
            debug = "DEBUG" in SysMgr.environList
            if sys.version_info >= (3, 5, 0):
                # filename, lineno, function, code_context #
                for fb in reversed(inspect.trace()):
                    if debug:
                        print(
                            "[DEBUG] %s() at %s in %s"
                            % (fb.function, fb.lineno, fb.filename)
                        )
                    lines += "%s<" % fb.lineno
            else:
                # obj, filename, lineno, function, code_context #
                for fb in reversed(inspect.trace()):
                    if debug:
                        print(
                            "[DEBUG] %s() at %s in %s" % (fb[3], fb[2], fb[1])
                        )
                    lines += "%s<" % fb[2]

            if lines:
                return lines.rstrip("<")
            else:
                return "??"
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn("failed to get line info", True, True)
            return None

    @staticmethod
    def getErrMsg():
        et, err, to = sys.exc_info()
        lineno = SysMgr.getLine()
        if lineno:
            linestr = " at %s" % lineno
        else:
            linestr = ""

        # error #
        try:
            if not err.args or err.args[0] == 0:
                return sys.exc_info()[0].__name__ + linestr
        except SystemExit:
            sys.exit(0)
        except:
            if to:
                return "N/A%s" % linestr
            else:
                return "N/A"

        # error code #
        try:
            code = "%s-" % errno.errorcode[err.args[0]]
        except SystemExit:
            sys.exit(0)
        except:
            code = ""

        errstr = " ".join(
            [str(item) for item in err.args if item is not None]
        )[: SysMgr.maxErrStrLen]
        return "%s%s%s" % (code, errstr, linestr)

    def disableAllEvents(self):
        for event in list(self.cmdList):
            self.cmdList[event] = False

    def enableAllEvents(self):
        for event in list(self.cmdList):
            self.cmdList[event] = True

    @staticmethod
    def writeCustomCmd():
        effectiveCmd = []

        if not SysMgr.customCmd:
            return

        if not SysMgr.filterGroup:
            pidFilter = "common_pid != 0"
        else:
            pidFilter = SysMgr.getPidFilter()

        for cmd in SysMgr.customCmd:
            cmdFormat = cmd.split(":")

            if cmdFormat[0] == "":
                SysMgr.printErr("wrong event '%s'" % cmdFormat[0])
                sys.exit(-1)

            # check filter #
            if len(cmdFormat) == 1:
                origFilter = ""
                cmdFormat.append(pidFilter)
            else:
                origFilter = cmdFormat[1]
                cmdFormat[1] = pidFilter + " && " + cmdFormat[1]

            # check effective event #
            if SysMgr.writeTraceCmd(cmdFormat[0] + "/enable", "0") < 0:
                SysMgr.printErr("wrong event '%s'" % cmdFormat[0])
                sys.exit(-1)

            # check and enable effective filter #
            if (
                len(cmdFormat) > 1
                and SysMgr.writeTraceCmd(
                    cmdFormat[0] + "/filter", cmdFormat[1]
                )
                < 0
            ):
                SysMgr.printErr(
                    "wrong filter '%s' for '%s' event"
                    % (origFilter, cmdFormat[0])
                )
                sys.exit(-1)

            # check and enable effective event #
            if SysMgr.writeTraceCmd(cmdFormat[0] + "/enable", "1") < 0:
                SysMgr.printErr("wrong event '%s'" % cmdFormat[0])
                sys.exit(-1)
            else:
                effectiveCmd.append(cmdFormat[0])

        if effectiveCmd:
            SysMgr.printInfo(
                "enabled trace events [ %s ]" % ", ".join(effectiveCmd)
            )

    @staticmethod
    def printAnalOption():
        def _setOpt(items, val, name):
            if val:
                items["enable"] += name
            else:
                items["disable"] += name

        if SysMgr.outputFile:
            return

        items = {"enable": "", "disable": ""}

        if not SysMgr.isRecordMode() and not SysMgr.isTopMode():
            # common options #
            items["enable"] += SysMgr.arch.upper() + " "
            _setOpt(items, SysMgr.warnEnable, "WARN ")

        # function mode #
        if SysMgr.isFuncMode():
            _setOpt(items, SysMgr.heapEnable, "HEAP ")
            _setOpt(items, SysMgr.userEnable, "USER ")
        # thread mode #
        else:
            _setOpt(items, SysMgr.depEnable, "DEP ")
            _setOpt(items, SysMgr.fsEnable, "FS ")
            _setOpt(items, SysMgr.intervalEnable, "INTERVAL ")
            _setOpt(items, SysMgr.irqEnable, "IRQ ")
            _setOpt(items, SysMgr.keventEnable, "KEVT ")
            _setOpt(items, SysMgr.latEnable, "LATENCY ")
            _setOpt(items, SysMgr.networkEnable, "NET ")
            _setOpt(items, SysMgr.perCoreList, "PERCORE ")
            _setOpt(items, SysMgr.powerEnable, "POWER ")
            _setOpt(items, SysMgr.preemptGroup, "PREEMPT ")
            _setOpt(items, SysMgr.ueventEnable, "UEVT ")
            _setOpt(items, SysMgr.wqEnable, "WQ ")
            _setOpt(items, not SysMgr.latEnable, "SCHEDBLK ")

        if SysMgr.customCmd:
            SysMgr.printInfo(
                "selected trace events [ %s ]" % ", ".join(SysMgr.customCmd)
            )

        # common options #
        _setOpt(items, SysMgr.blockEnable, "BLOCK ")
        _setOpt(items, SysMgr.compressEnable, "COMP ")
        _setOpt(items, SysMgr.countEnable, "CUT ")
        _setOpt(items, SysMgr.cpuEnable, "CPU ")
        _setOpt(items, SysMgr.enableAll, "ENABLE ")
        _setOpt(items, SysMgr.disableAll, "DISABLE ")
        _setOpt(items, SysMgr.groupProcEnable, "PGRP ")
        _setOpt(items, SysMgr.lockEnable, "LOCK ")
        _setOpt(items, SysMgr.memEnable, "MEM ")
        _setOpt(items, SysMgr.printEnable, "PRINT ")
        _setOpt(items, SysMgr.showAll, "ALL ")
        _setOpt(items, SysMgr.sysEnable, "SYSCALL ")

        enableStat = items["enable"]
        disableStat = items["disable"]

        # print options #
        if enableStat:
            SysMgr.printInfo("enabled analysis options [ %s]" % enableStat)

        if disableStat:
            SysMgr.printWarn("disabled analysis options [ %s]" % disableStat)

    @staticmethod
    def getUdsList(addrList):
        def _getStats(addrList):
            retList = []
            for item in addrList:
                if item in SysMgr.udsListCache:
                    retList.append(SysMgr.udsListCache[item])
            return retList

        if SysMgr.udsListCache:
            return _getStats(addrList)

        inodeIdx = ConfigMgr.UDS_ATTR.index("Inode")
        pathIdx = ConfigMgr.UDS_ATTR.index("Path")

        udsBuf = []
        udsPath = "%s/net/unix" % SysMgr.procPath

        # init UDS stats #
        SysMgr.nrUDSSock = 0

        # make dictionary #
        try:
            with open(udsPath, "r") as fd:
                udsBuf = fd.read().split("\n")
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printOpenWarn(udsPath)
            return udsBuf

        # remove title #
        udsBuf.pop(0)

        for line in udsBuf:
            try:
                if not line:
                    continue

                # increase UDS socket number #
                SysMgr.nrUDSSock += 1

                uds = line.split()

                item = "UDS>%s" % uds[pathIdx]

                SysMgr.udsListCache[uds[inodeIdx]] = item
            except SystemExit:
                sys.exit(0)
            except:
                pass

        return _getStats(addrList)

    @staticmethod
    def getUdpList(addrList):
        def _getStats(addrList):
            retList = []
            for item in addrList:
                if item in SysMgr.udpListCache:
                    retList.append(SysMgr.udpListCache[item])
            return retList

        if SysMgr.udpListCache:
            return _getStats(addrList)

        inodeIdx = ConfigMgr.PROTOCOL_ATTR.index("inode")
        laddrIdx = ConfigMgr.PROTOCOL_ATTR.index("local_address")

        udpBuf = []
        udpFileList = [
            "%s/net/udp" % SysMgr.procPath,
            "%s/net/udp6" % SysMgr.procPath,
        ]

        # init UDP stats #
        SysMgr.nrUDPSock = 0

        for udpPath in udpFileList:
            try:
                with open(udpPath, "r") as fd:
                    udpBuf = fd.read().split("\n")
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printOpenWarn(udpPath)
                continue

            # remove title #
            udpBuf.pop(0)

            for line in udpBuf:
                try:
                    if not line:
                        continue

                    # increase UCP socket number #
                    SysMgr.nrUDPSock += 1

                    udp = line.split()

                    # convert local address #
                    lip, lport = udp[laddrIdx].split(":")
                    lip = SysMgr.convCIDR(lip)
                    item = "UDP>%s:%s" % (lip, long(lport, base=16))

                    # convert remote address #
                    rip, rport = udp[laddrIdx + 1].split(":")

                    # append remote address #
                    try:
                        rport = long(rport, 16)
                        if long(rip, 16) > 0 and rport > 0:
                            rip = SysMgr.convCIDR(rip)
                            item = "%s->%s:%s" % (item, rip, rport)
                    except SystemExit:
                        sys.exit(0)
                    except:
                        pass

                    # cache string #
                    SysMgr.udpListCache[udp[inodeIdx]] = item
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printWarn(
                        "failed to parse UDP data '%s'" % line, reason=True
                    )

        return _getStats(addrList)

    @staticmethod
    def getTcpList(addrList):
        def _getStats(addrList):
            retList = []
            for item in addrList:
                if item in SysMgr.tcpListCache:
                    retList.append(SysMgr.tcpListCache[item])
            return retList

        if SysMgr.tcpListCache:
            return _getStats(addrList)

        stIdx = ConfigMgr.PROTOCOL_ATTR.index("st")
        inodeIdx = ConfigMgr.PROTOCOL_ATTR.index("inode")
        laddrIdx = ConfigMgr.PROTOCOL_ATTR.index("local_address")
        estIdx = ConfigMgr.TCP_STAT.index("ESTABLISHED")

        tcpBuf = []
        tcpFileList = [
            "%s/net/tcp" % SysMgr.procPath,
            "%s/net/tcp6" % SysMgr.procPath,
        ]

        # init TCP stats #
        SysMgr.nrTCPSock = 0
        SysMgr.nrTCPConn = 0

        # make dictionary #
        for tcpPath in tcpFileList:
            try:
                with open(tcpPath, "r") as fd:
                    tcpBuf = fd.read().split("\n")
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printOpenWarn(tcpPath)
                continue

            # remove title #
            tcpBuf.pop(0)

            for line in tcpBuf:
                try:
                    if not line:
                        continue

                    # increase TCP socket number #
                    SysMgr.nrTCPSock += 1

                    tcp = line.split()

                    # convert local address #
                    lip, lport = tcp[laddrIdx].split(":")
                    lip = SysMgr.convCIDR(lip)
                    item = "TCP>%s:%s" % (lip, long(lport, base=16))

                    # convert remote address #
                    rip, rport = tcp[laddrIdx + 1].split(":")

                    # append remote address #
                    try:
                        rport = long(rport, 16)
                        if rport > 0 and long(rip, 16) > 0:
                            rip = SysMgr.convCIDR(rip)
                            item = "%s->%s:%s" % (item, rip, rport)
                    except SystemExit:
                        sys.exit(0)
                    except:
                        pass

                    # define status #
                    try:
                        tcpStat = long(tcp[stIdx], 16)

                        # increase established TCP socket number #
                        if tcpStat == estIdx:
                            SysMgr.nrTCPConn += 1

                        stat = "/%s" % ConfigMgr.TCP_STAT[tcpStat]
                    except SystemExit:
                        sys.exit(0)
                    except:
                        stat = ""

                    # append status #
                    item += stat

                    # cache string #
                    SysMgr.tcpListCache[tcp[inodeIdx]] = item
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printWarn(
                        "failed to parse TCP data '%s'" % line, reason=True
                    )

        return _getStats(addrList)

    @staticmethod
    def printProfileCmd():
        for idx, val in SysMgr.rcmdList.items():
            if not val:
                continue

            cmds = []
            for item in val:
                cmds.append(":".join(item))
            SysMgr.printInfo(
                "user custom commands on %s [ %s ]" % (idx, ", ".join(cmds))
            )

    @staticmethod
    def getVDSO(elf=True, debug=False):
        # get address of vDSO object #
        addr = SysMgr.getauxval("AT_SYSINFO_EHDR")
        if not addr:
            return None

        # get vDSO size #
        size = FileAnalyzer.getFileMapSize(SysMgr.pid, "vdso", reverse=True)
        if not size:
            size = 8096

        # create a memory file #
        fd = MemoryFile(addr, size=size, name="vdso")
        if not fd:
            SysMgr.printErr("failed to create memory file for vDSO object")
            return None
        elif not elf:
            return fd

        # return vDSO #
        obj = ElfAnalyzer(path="vdso", fd=fd, debug=debug)
        if obj:
            ElfAnalyzer.cachedFiles["vdso"] = obj

        return obj

    @staticmethod
    def getAuxList():
        if not SysMgr.isLinux:
            return {}

        auxList = {}
        for attr, idx in sorted(ConfigMgr.AT_TYPE.items(), key=lambda e: e[1]):
            # skip AT_IGNORE #
            if idx == 1:
                continue

            auxList[attr] = hex(SysMgr.getauxval(attr))

        return auxList

    @staticmethod
    def getauxval(attype):
        if not SysMgr.isLinux:
            return None

        try:
            nrType = ConfigMgr.AT_TYPE[attype]
        except:
            SysMgr.printErr("failed to get entry type %s" % attype, True)
            return

        # load libc #
        if not SysMgr.loadLibcObj():
            return

        # declare syscalls #
        try:
            SysMgr.libcObj.getauxval.restype = c_ulong
            SysMgr.libcObj.getauxval.argtypes = [c_ulong]
        except:
            SysMgr.printErr("failed to find getauxval in libc")
            return None

        return SysMgr.libcObj.getauxval(c_ulong(nrType))

    @staticmethod
    def printProfileOption():
        def _setOpt(items, val, name):
            if val:
                items["enable"] += name
            else:
                items["disable"] += name

        items = {"enable": "", "disable": ""}

        # common options #
        items["enable"] += SysMgr.arch.upper() + " "

        _setOpt(items, SysMgr.cgroupEnable, "CGROUP ")
        _setOpt(items, SysMgr.cpuEnable, "CPU ")
        _setOpt(items, SysMgr.encodeEnable, "ENCODE ")
        _setOpt(items, SysMgr.pipeEnable, "PIPE ")
        _setOpt(items, SysMgr.printEnable, "PRINT ")
        _setOpt(items, SysMgr.warnEnable, "WARN ")

        # check current mode #
        if SysMgr.isTopMode():
            SysMgr.printInfo("<TOP MODE>")

            if SysMgr.fileTopEnable:
                items["enable"] += "FILE "
            else:
                if SysMgr.processEnable:
                    items["enable"] += "PROCESS "
                else:
                    items["enable"] += "THREAD "

                _setOpt(items, SysMgr.affinityEnable, "AFNT ")
                _setOpt(items, SysMgr.andlogEnable, "ANDL ")
                _setOpt(items, SysMgr.attrEnable, "ATTR ")
                _setOpt(items, SysMgr.barGraphEnable, "BAR ")
                _setOpt(items, SysMgr.blockEnable, "BLOCK ")
                _setOpt(items, SysMgr.cmdlineEnable, "CMD ")
                _setOpt(items, SysMgr.compressEnable, "COMP ")
                _setOpt(items, SysMgr.delayEnable, "DELAY ")
                _setOpt(items, SysMgr.diskEnable, "DISK ")
                _setOpt(items, SysMgr.dltEnable, "DLT ")
                _setOpt(items, SysMgr.dwarfEnable, "DWARF ")
                _setOpt(items, SysMgr.eventHandleEnable, "EVENT ")
                _setOpt(items, SysMgr.floatEnable, "FLOAT ")
                _setOpt(items, SysMgr.gpuEnable, "GPU ")
                _setOpt(items, SysMgr.gpuMemEnable, "GPUMEM ")
                _setOpt(items, SysMgr.groupProcEnable, "PGRP ")
                _setOpt(items, SysMgr.irqEnable, "IRQ ")
                _setOpt(items, SysMgr.journalEnable, "JRL ")
                _setOpt(items, SysMgr.kmsgEnable, "KMSG ")
                _setOpt(items, SysMgr.memEnable, "MEM ")
                _setOpt(items, SysMgr.minStatEnable, "MIN ")
                _setOpt(items, SysMgr.networkEnable, "NET")
                _setOpt(items, SysMgr.nsEnable, "NS ")
                _setOpt(items, SysMgr.oomEnable, "OOM ")
                _setOpt(items, SysMgr.peakEnable, "PEAK ")
                _setOpt(items, SysMgr.perfEnable, "PERF ")
                _setOpt(items, SysMgr.perfGroupEnable, "PERFPROC ")
                _setOpt(items, SysMgr.psiEnable, "PSI ")
                _setOpt(items, SysMgr.pssEnable, "PSS ")
                _setOpt(items, SysMgr.reportEnable, "REPORT ")
                _setOpt(items, SysMgr.reportFileEnable, "RFILE ")
                _setOpt(items, SysMgr.schedEnable, "SCHED ")
                _setOpt(items, SysMgr.sigHandlerEnable, "SIG ")
                _setOpt(items, SysMgr.stackEnable, "STACK ")
                _setOpt(items, SysMgr.syslogEnable, "SYSLOG ")
                _setOpt(items, SysMgr.thresholdEnable, "THRESHOLD ")
                _setOpt(items, SysMgr.totalEnable, "TOTAL ")
                _setOpt(items, SysMgr.ussEnable, "USS ")
                _setOpt(items, SysMgr.wchanEnable, "WCHAN ")
                _setOpt(items, SysMgr.wfcEnable, "WFC ")
                _setOpt(items, SysMgr.wssEnable, "WSS ")

        elif SysMgr.isFuncMode():
            SysMgr.printInfo("<FUNCTION MODE>")

            _setOpt(items, SysMgr.getCompressFlag(), "COMP ")

            if SysMgr.graphEnable:
                items["enable"] += "GRAPH "
            else:
                items["disable"] += "GRAPH "

                _setOpt(items, SysMgr.blockEnable, "BLOCK ")
                _setOpt(items, SysMgr.enableAll, "ENABLE ")
                _setOpt(items, SysMgr.disableAll, "DISABLE ")
                _setOpt(items, SysMgr.heapEnable, "HEAP ")
                _setOpt(items, SysMgr.lockEnable, "LOCK ")
                _setOpt(items, SysMgr.memEnable, "MEM ")
                _setOpt(items, SysMgr.sysEnable, "SYSCALL ")
                _setOpt(items, SysMgr.userEnable, "USER ")

        elif SysMgr.isFileMode():
            SysMgr.printInfo("<FILE MODE>")

        elif SysMgr.isSystemMode():
            SysMgr.printInfo("<SYSTEM MODE>")

        else:
            SysMgr.printInfo("<THREAD MODE>")
            SysMgr.threadEnable = True

            _setOpt(items, SysMgr.binderEnable, "BINDER ")
            _setOpt(items, SysMgr.blockEnable, "BLOCK ")
            _setOpt(items, SysMgr.bufferLossEnable, "BUFLOSS ")
            _setOpt(items, SysMgr.depEnable, "DEP ")
            _setOpt(items, SysMgr.enableAll, "ENABLE ")
            _setOpt(items, SysMgr.disableAll, "DISABLE ")
            _setOpt(items, SysMgr.diskEnable, "DISK ")
            _setOpt(items, SysMgr.fsEnable, "FS ")
            _setOpt(items, SysMgr.i2cEnable, "I2C ")
            _setOpt(items, SysMgr.iouringEnable, "IOURING ")
            _setOpt(items, SysMgr.kvmEnable, "KVM ")
            _setOpt(items, SysMgr.irqEnable, "IRQ ")
            _setOpt(items, SysMgr.keventEnable, "KEVT ")
            _setOpt(items, SysMgr.latEnable, "LATENCY ")
            _setOpt(items, SysMgr.lockEnable, "LOCK ")
            _setOpt(items, SysMgr.memEnable, "MEM ")
            _setOpt(items, SysMgr.networkEnable, "NET ")
            _setOpt(items, SysMgr.powerEnable, "POWER ")
            _setOpt(items, SysMgr.resetEnable, "RESET ")
            _setOpt(items, SysMgr.sysEnable, "SYSCALL ")
            _setOpt(items, SysMgr.ueventEnable, "UEVT ")
            _setOpt(items, SysMgr.wqEnable, "WQ ")
            _setOpt(items, SysMgr.getCompressFlag(), "COMP ")

        enableStat = items["enable"]
        disableStat = items["disable"]

        # print options #
        if enableStat:
            SysMgr.printInfo("enabled runtime options [ %s]" % enableStat)

        if disableStat:
            SysMgr.printWarn("disabled runtime options [ %s]" % disableStat)

    @staticmethod
    def isThreadMode():
        return SysMgr.threadEnable

    @staticmethod
    def isFuncMode():
        return SysMgr.functionEnable

    @staticmethod
    def isFileMode():
        return SysMgr.fileEnable

    @staticmethod
    def isSystemMode():
        return SysMgr.systemEnable

    @staticmethod
    def exceptHandler(signum, frame):
        raise Exception("signal")

    @staticmethod
    def defaultHandler(signum, frame):
        return

    @staticmethod
    def chldHandler(signum, frame):
        try:
            os.waitpid(-1, os.WNOHANG)
        except:
            pass

    @staticmethod
    def fgHandler(signum, frame):
        SysMgr.bgStatus = False

    @staticmethod
    def bgHandler(signum, frame):
        SysMgr.bgStatus = True

        if signum == signal.SIGTSTP:
            os.kill(SysMgr.pid, signal.SIGSTOP)
        elif signum == signal.SIGTTIN:
            if sys.stdin:
                sys.stdin.close()
            sys.stdin = None

    @staticmethod
    def winchHandler(signum, frame):
        SysMgr.getTty(update=True)

    @staticmethod
    def printDelayTime(pid=None, force=False):
        if not SysMgr.warnEnable and not force:
            return

        if not pid:
            pid = os.getpid()

        try:
            # get sched stats #
            schedData = SysMgr.readFile(
                "%s/%s/schedstat" % (SysMgr.procPath, pid)
            )
            if not schedData:
                return

            execTime, waitTime, nrSlice = list(
                map(long, schedData.split()[:3])
            )
            execPer = (execTime / (execTime + waitTime)) * 100
            if execPer == 0:
                execPer = 100

            # get runtime #
            statData = SysMgr.readFile("%s/%s/stat" % (SysMgr.procPath, pid))
            statList = statData.split(") ", 1)[1].split()
            utimeIdx = ConfigMgr.PROCSTAT_ATTR.index("UTIME")
            stimeIdx = ConfigMgr.PROCSTAT_ATTR.index("STIME")
            utime = long(statList[utimeIdx - 2])
            stime = long(statList[stimeIdx - 2])
            ttime = utime + stime

            # get delay time #
            totalTime = ttime * (100 / execPer)
            dtime = long(totalTime - ttime)
            if not dtime:
                return

            # print delay time #
            dtime = UtilMgr.convNum(dtime / 100.0, True, 2)
            SysMgr.printWarn(
                "%s(%s) is delayed for %s sec totally"
                % (SysMgr.getComm(pid), pid, dtime),
                True,
            )
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn(
                "failed to get delay time for %s(%s)"
                % (SysMgr.getComm(pid), pid),
                reason=True,
            )

    @staticmethod
    def stopHandler(signum=None, frame=None):
        # check exit condition #
        if SysMgr.exitFlag:
            os._exit(0)

        # mask signal #
        if signum:
            signal.signal(signum, signal.SIG_IGN)

        # print delay #
        SysMgr.printDelayTime()

        # clear child list to keep them alive #
        if "KEEPTASK" in SysMgr.environList:
            SysMgr.clearChildList()

        # write user command #
        SysMgr.runProfCmd("STOP")

        # JSON output #
        if SysMgr.jsonEnable:
            sys.exit(0)
        # filerec mode #
        elif SysMgr.checkMode("filerec") and not SysMgr.intervalEnable:
            sys.exit(0)
        # ftop / system mode #
        elif SysMgr.isFileMode() or SysMgr.isSystemMode():
            SysMgr.condExit = True
        # top / trace mode #
        elif SysMgr.isTopMode() or SysMgr.isTraceMode():
            # skip summary for Debugger #
            if Debugger.dbgInstance:
                pass
            elif SysMgr.outPath and not "NOSUMMARY" in SysMgr.environList:
                # update exit status #
                SysMgr.inExit = True

                SysMgr.printStat("start reporting... [ STOP(Ctrl+\) ]")

                # report log events #
                if any(
                    [
                        item in SysMgr.environList
                        for item in (
                            "DLTEVENT",
                            "KERNELEVENT",
                            "JOURNALEVENT",
                            "ANDROIDEVENT",
                            "SYSLOGEVENT",
                            "TRACEEVENT",
                        )
                    ]
                ):
                    # set break signal #
                    if signum:
                        signal.signal(signum, SysMgr.exitHandler)

                    # get message all field #
                    msgall = "MSGALL" in SysMgr.environList

                    # get current #
                    current = SysMgr.getUptime()

                    # get log item #
                    logInfo = {
                        # TODO: add more log event format #
                        "DLTEVENT": {
                            "time": "mtime",
                            "diff": UtilMgr.getClockTime(dlt=True) - current,
                            "field": [
                                "mtime",
                                "ecuId",
                                "apId",
                                "ctxId",
                                "message",
                            ]
                            if msgall
                            else [],
                        },
                    }

                    SysMgr.printStat(
                        "start gathering log events... [ STOP(Ctrl+c) ]"
                    )

                    for log in SysMgr.getLogEvents(
                        tail=SysMgr.startTime, until=SysMgr.getUptime()
                    ):
                        try:
                            # get log time #
                            info = logInfo[log["type"]]
                            ltime = float(log[info["time"]])
                            uptime = ltime - info["diff"]

                            # get message name #
                            name = log["name"]
                            for f in info["field"]:
                                if f in log:
                                    name += ">%s" % log[f]

                            # append event to list #
                            TaskAnalyzer.procEventData.append(
                                [uptime, name, uptime]
                            )
                        except SystemExit:
                            break
                        except:
                            SysMgr.printWarn(
                                "failed to recognize a below log\n%s"
                                % UtilMgr.convDict2Str(log, pretty=True),
                                True,
                                True,
                            )

                    # mask signal again #
                    if signum:
                        signal.signal(signum, signal.SIG_IGN)

                # reload data written to file #
                if SysMgr.pipeEnable:
                    SysMgr.reloadFileBuffer()

                # init summary variables #
                current = True

                # handle reports based on remote monitoring data #
                for n, v in SysMgr.hostList.items():
                    buf = v["buffer"]
                    if not buf:
                        continue

                    # create a summarizing process #
                    pid = SysMgr.createProcess()
                    if pid > 0:
                        continue

                    # init environments #
                    current = False
                    SysMgr.procBuffer = buf
                    SysMgr.addEnvironVar("NOHEADER")
                    SysMgr.fileSuffix = "%s_%s_%s" % (
                        v["hostname"],
                        n,
                        SysMgr.pid,
                    )
                    break

                SysMgr.printLogo(absolute=True, big=True)

                # save system info #
                SysMgr.saveSysStats(True)

                # submit summary report and details #
                if "NOINTSUMMARY" in SysMgr.environList:
                    if "PRINTEVENTHIST" in SysMgr.environList:
                        TaskAnalyzer.printEventInterval()
                    if "PRINTFILEHIST" in SysMgr.environList:
                        TaskAnalyzer.printFileInterval()
                    SysMgr.printProcBuffer()
                else:
                    TaskAnalyzer.printIntervalUsage(current=current)

                # print output of last commands #
                if "PRINTCMD" in SysMgr.environList:
                    for cmd in SysMgr.environList["PRINTCMD"]:
                        # get title #
                        if "#" in cmd:
                            title, cmd = cmd.split("#", 1)
                        else:
                            title = cmd

                        # print output of the command #
                        title = " " + title + " "
                        stars = "*" * long(
                            (long(SysMgr.lineLength) - len(title)) / 2
                        )
                        SysMgr.printPipe(
                            "\n\n\n\n%s%s%s\n\n" % (stars, title, stars)
                        )
                        SysMgr.printPipe(SysMgr.executeCmdSync(cmd))

                if os.path.exists(SysMgr.inputFile):
                    # get output size #
                    fsize = UtilMgr.getFileSizeStr(SysMgr.inputFile)

                    SysMgr.printInfo(
                        "saved the results based monitoring into "
                        "'%s'%s successfully" % (SysMgr.inputFile, fsize)
                    )

            # broadcast signal to childs and close all FDs #
            SysMgr.releaseResource(signal.SIGINT)

            # update exit status #
            SysMgr.inExit = False

            # unmask signal #
            if signum:
                signal.signal(signum, SysMgr.stopHandler)

            # quit to avoid termination #
            if not SysMgr.termFlag:
                SysMgr.progressCnt = 0
                return

            """
            # enable for cProfile #
            sys.settrace
            sys.exit(0)
            """

            # remove stopHandler in exit list to prevent recursive calls #
            SysMgr.removeExitFunc(SysMgr.stopHandler)

            # call exit handlers #
            SysMgr.doExit()

            # do terminate #
            os._exit(0)
        elif not SysMgr.isLinux:
            sys.exit(0)
        # recording mode #
        else:
            # write trace message #
            SysMgr.writeEvent("EVENT_STOP", False)

            if signum:
                signal.signal(signal.SIGINT, signal.SIG_DFL)

            SysMgr.stopRecording()

        # update record stats #
        SysMgr.recordStatus = False
        SysMgr.repeatCnt = 0

        SysMgr.printStat("ready to save and analyze... [ STOP(Ctrl+c) ]")

        # unmask signal #
        if signum:
            signal.signal(signum, SysMgr.stopHandler)

        if not "ISMAIN" in os.environ:
            sys.exit(0)

        raise Exception("stop signal")

    @staticmethod
    def closePrintFd():
        try:
            SysMgr.printFd.close()
        except SystemExit:
            sys.exit(0)
        except:
            pass
        finally:
            SysMgr.printFd = None

    @staticmethod
    def newHandler(signum=None, frame=None):
        # check force exit condition #
        if SysMgr.inExit:
            SysMgr.releaseResource()
            os._exit(0)

        SysMgr.condExit = False

        if SysMgr.isFileMode():
            SysMgr.printStat("saved file usage successfully")
        elif SysMgr.isSystemMode():
            pass
        elif SysMgr.isTopMode():
            # reset WSS #
            if SysMgr.wssEnable:
                SysMgr.initWssEnable = True
                SysMgr.printInfo("initialize WSS")

            # check silent mode #
            if not SysMgr.outPath:
                return

            # masking signal #
            if signum:
                signal.signal(signum, signal.SIG_IGN)

            # reload data written to file #
            if SysMgr.pipeEnable:
                SysMgr.reloadFileBuffer()

            # create a new process #
            pid = SysMgr.createProcess()
            if pid > 0:
                # clear buffer as parent #
                SysMgr.procBufferSize = 0
                SysMgr.procBuffer = []

                # close an output file to sync #
                SysMgr.closePrintFd()

                # enable signal again #
                if signum:
                    signal.signal(signum, SysMgr.newHandler)

                return True

            # change priority #
            SysMgr.setLowPriority(True)

            SysMgr.printLogo(absolute=True, big=True)

            # save system info #
            SysMgr.saveSysStats(True)

            # submit summarized report and details #
            TaskAnalyzer.printIntervalUsage()

            # print output info #
            fsize = UtilMgr.getFileSizeStr(SysMgr.inputFile)

            SysMgr.printInfo(
                "saved the results based monitoring into "
                "'%s'%s successfully" % (SysMgr.inputFile, fsize)
            )

            sys.exit(0)
        elif SysMgr.resetEnable:
            SysMgr.writeEvent("EVENT_START")
        else:
            SysMgr.writeEvent("EVENT_MARK")

    @staticmethod
    def alarmExitHandler(signum, frame):
        SysMgr.printWarn("terminated by timer\n", True)
        SysMgr.exitHandler(signum, frame)

    @staticmethod
    def exitHandler(signum, frame):
        if SysMgr.exitFlag or SysMgr.checkMode("report"):
            os._exit(0)

        # block signals and disable alarm for stable termination #
        SysMgr.setIgnoreSignal()
        signal.alarm(0)
        SysMgr.condExit = True

        SysMgr.printWarn("terminated by user\n")

        sys.exit(0)

    @staticmethod
    def faultHandler(signum, frame):
        """
        SysMgr.releaseResource()
        sys.stdout.write('terminated by SEGFAULT signal\n')
        """
        os._exit(0)

    @staticmethod
    def timerHandler(signum, frame):
        raise Exception("timer expired")

    @staticmethod
    def alarmHandler(signum, frame):
        # check exit condition #
        if SysMgr.repeatCnt <= SysMgr.progressCnt and SysMgr.termFlag:
            UtilMgr.deleteProgress()
            SysMgr.printWarn("terminated by timer\n", True)
            sys.exit(0)

        # print progress #
        if not SysMgr.isRecordMode() and SysMgr.repeatCnt > 0:
            UtilMgr.printProgress(SysMgr.progressCnt, SysMgr.repeatCnt)

        # update count #
        SysMgr.progressCnt += 1
        progressCnt = SysMgr.progressCnt
        repeatInterval = SysMgr.repeatInterval
        repeatCnt = SysMgr.repeatCnt

        # disable alarm handler #
        signal.signal(signal.SIGALRM, SysMgr.defaultHandler)

        if SysMgr.pipeEnable:
            if repeatCnt == progressCnt:
                # stop recording #
                SysMgr.stopRecording()
            signal.alarm(repeatInterval)
        elif SysMgr.outputFile:
            if repeatCnt == 1 and SysMgr.termFlag:
                output = SysMgr.outputFile
            else:
                output = "%s_%s" % (SysMgr.outputFile, SysMgr.getRuntime())

            # stop recording #
            SysMgr.stopRecording()

            # save system info #
            SysMgr.saveSysStats()

            # read trace data #
            try:
                rpath = "%s/trace" % SysMgr.getTracefsPath()
                if sys.version_info >= (3, 0, 0):
                    with open(rpath, "r", encoding="latin-1") as fr:
                        lines = fr.readlines()
                else:
                    with open(rpath, "r") as fr:
                        lines = fr.readlines()
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printOpenErr(rpath)
                sys.exit(-1)

            # write trace data #
            try:
                SysMgr.saveTraceData(lines, output)
                SysMgr.clearInfoBuffer()
            except:
                sys.exit(-1)
        else:
            SysMgr.printWarn(
                "failed to save data because the path is not set", True
            )

        # set alarm again #
        signal.signal(signal.SIGALRM, SysMgr.alarmHandler)

    @staticmethod
    def isTermSignal(sig):
        if sig in (signal.SIGKILL, signal.SIGSEGV, signal.SIGABRT):
            return True
        else:
            return False

    @staticmethod
    def getCompressFlag():
        # compress data by default #
        disabledOptions = SysMgr.getOption("d")
        if disabledOptions and "C" in disabledOptions:
            ret = False
        else:
            ret = True
        return ret

    @staticmethod
    def saveTraceData(lines, outputFile=None):
        if not outputFile:
            outputFile = SysMgr.outputFile
        outputFile = os.path.realpath(outputFile)

        # backup file already exists #
        SysMgr.backupFile(outputFile)

        # compress by gzip #
        if SysMgr.isRecordMode() and SysMgr.getCompressFlag():
            compressor = SysMgr.getPkg("gzip", False)
        else:
            compressor = None

        size = 0
        for line in lines:
            size += len(line)

        try:
            # strip trace data #
            traceData = "\n".join(lines)
            magicPos = traceData.rfind(SysMgr.magicStr)
            if magicPos > 0:
                traceData = traceData[magicPos:]

            # encoding data #
            encodedInfoData = SysMgr.sysinfoBuffer.encode("latin-1")
            encodedTraceData = traceData.encode("latin-1")
            del traceData
            totalSize = len(encodedInfoData) + len(encodedTraceData)

            SysMgr.printInfo(
                "wait for writing data to '%s' [%s]"
                % (outputFile, UtilMgr.convSize2Unit(totalSize))
            )

            # open compressor #
            f = open(outputFile, "wb")
            os.chmod(outputFile, 0o777)
            if compressor:
                f = compressor.GzipFile(mode="wb", fileobj=f)

            # write system info #
            if SysMgr.sysinfoBuffer:
                magicStr = "%s\n" % SysMgr.magicStr
                magicStr = magicStr.encode("latin-1")

                # write start mark #
                f.write(magicStr)

                # write info data #
                f.write(encodedInfoData)

                # write end mark #
                f.write(magicStr)

            # write trace info and close file #
            f.write(encodedTraceData)
            f.close()

            # get output size #
            fsize = UtilMgr.getFileSizeStr(outputFile)

            SysMgr.printInfo(
                "finished saving trace data into '%s'%s successfully"
                % (outputFile, fsize)
            )
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                "failed to write trace data to '%s'" % outputFile, True
            )

    @staticmethod
    def runFileCmd(cmdList, path):
        for cmd in cmdList:
            if cmd.upper() == "EXIT":
                sys.exit(0)
            elif cmd.endswith("&"):
                cmd = cmd[:-1]
                wait = False
            else:
                wait = True

            # convert path #
            if path:
                cmd = cmd.replace("PATH", path)

            # create a new process #
            SysMgr.createCmdProcess(cmd)
            if wait:
                os.wait()

    @staticmethod
    def runProfCmd(time, convList={}):
        if not SysMgr.isLinux:
            return
        elif not SysMgr.rcmdList:
            return

        for cmd in SysMgr.rcmdList[time.upper()]:
            if len(cmd) == 1:
                if cmd[0].endswith("&"):
                    command = cmd[0].rstrip("& ")
                    wait = False
                else:
                    command = cmd[0]
                    wait = True

                # convert items #
                for name, value in convList.items():
                    command = command.replace(name, value)

                # execute a command using a new process #
                SysMgr.execBgCmd(command, mute=False, wait=wait)
                continue
            elif len(cmd) != 2:
                continue

            try:
                path, val = cmd

                with open(path, "w") as fd:
                    fd.write(val)
                    SysMgr.printInfo(
                        "applied command '%s' to %s successfully" % (val, path)
                    )
            except:
                SysMgr.printWarn(
                    "failed to apply command '%s' to %s" % (val, path)
                )

    @staticmethod
    def readFile(
        path,
        size=-1,
        tail=False,
        verb=True,
        byte=False,
        offset=0,
        block=True,
        strip=True,
        comp=False,
    ):
        try:
            perm = "rb" if byte else "r"

            def _readFile(fd):
                # set non-block flag #
                if not block:
                    SysMgr.setBlock(fd, False)

                # read data #
                if tail:
                    pos = os.path.getsize(path) - size
                    if pos < 0:
                        pos = 0
                    fd.seek(pos)
                    return fd.read().strip()[-size:]
                else:
                    fd.seek(offset)
                    res = fd.read(size)
                    return res.strip() if strip else res

            # get fd #
            fd = SysMgr.getCompFd(path, perm=perm, verb=verb) if comp else None
            if fd:
                pass
            elif sys.version_info >= (3, 0, 0):
                fd = open(path, perm, encoding=None if byte else "UTF-8")
            else:
                fd = open(path, perm)

            return _readFile(fd)
        except SystemExit:
            sys.exit(0)
        except:
            if verb:
                SysMgr.printErr("failed to read '%s'" % path, True)
            return None

    @staticmethod
    def writeFile(path, val, errval=True, byte=False):
        try:
            perm = "wb" if byte else "w"
            dirpath = os.path.dirname(path)
            SysMgr.mkdirs(dirpath)
            with open(path, perm) as fd:
                if isinstance(val, list):
                    for item in val:
                        if not item:
                            continue
                        fd.seek(0)
                        fd.write(item)
                else:
                    fd.write(val)

            return True
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                "failed to write '%s' to '%s'"
                % (
                    "%s" % val[: SysMgr.maxErrStrLen] if errval else "",
                    path,
                ),
                True,
            )
            return False

    @staticmethod
    def readTraceFile(path):
        # open for applying command #
        try:
            target = "%s/%s" % (SysMgr.traceEventPath, path)
            with open(target, "r") as fd:
                return fd.read()[:-1]
        except:
            SysMgr.printErr("failed to read data from %s\n" % target)
            return None

    @staticmethod
    def procReadline(path):
        target = "%s/%s" % (SysMgr.procPath, path)
        with open(target, "r") as fd:
            return fd.readline()

    @staticmethod
    def procReadlines(path):
        target = "%s/%s" % (SysMgr.procPath, path)
        with open(target, "r") as fd:
            return fd.readlines()

    @staticmethod
    def getDirSize(path):
        # get target dir info #
        dirInfo = SysMgr.printDirs(path=path, retVal=True)
        if not dirInfo:
            return 0

        # get current size #
        try:
            item = next(iter(dirInfo))
            curSize = dirInfo[item].get("_size_")
            if curSize is None:
                return 0
            return UtilMgr.convUnit2Size(curSize)
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to get current dir info", True)
            return None

    @staticmethod
    def freeReportDir():
        # check limit size #
        if not SysMgr.limitRepDirSize:
            return 0

        # get output dir #
        if SysMgr.outPath == SysMgr.nullPath:
            targetDir = SysMgr.tmpPath
        elif os.path.isdir(SysMgr.outPath):
            targetDir = SysMgr.outPath
        else:
            targetDir = os.path.dirname(SysMgr.outPath)

        # get total size of the directory #
        curSize = SysMgr.getDirSize(targetDir)
        if not curSize:
            return 0

        # get free-up size #
        try:
            diff = SysMgr.limitRepDirSize - curSize
            if diff >= 0:
                return 0

            needSize = abs(diff)
            rmSize = 0
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to get free-up size of target dir", True)
            return 0

        # get the file list sorted by name in the output dir #
        flist = sorted(
            filter(
                lambda x: os.path.isfile(os.path.join(targetDir, x)),
                os.listdir(targetDir),
            )
        )

        # check remove variable #
        removeOthers = "REMOVEOTHERS" in SysMgr.environList

        convSize = UtilMgr.convSize2Unit

        # remove report files #
        for fname in flist:
            m = re.match(r"^guider_(?P<run>[0-9]+)_*", fname)
            if not m and not removeOthers:
                continue

            fpath = os.path.join(targetDir, fname)

            # get file size #
            size = UtilMgr.getFileSize(fpath, False)

            msg = "'%s' [%s] to free up space of the report directory" % (
                fpath,
                convSize(size),
            )

            # remove the file #
            try:
                os.remove(fpath)

                rmSize += size
                curSize -= size

                msg += " [%s]" % (convSize(curSize))
                SysMgr.printWarn("removed %s" % msg, True)
            except SystemExit:
                sys.exit(0)
            except:
                msg += " [%s]" % (convSize(curSize))
                SysMgr.printErr("failed to remove %s" % msg, True)

            if rmSize >= needSize:
                break

        # print freed up space #
        if rmSize:
            curSize = SysMgr.getDirSize(targetDir)

            SysMgr.printInfo(
                (
                    "removed files with a total size of [%s] "
                    "to free up space of the report directory [%s]"
                )
                % (
                    convSize(rmSize),
                    convSize(curSize),
                )
            )

        return rmSize

    @staticmethod
    def freeDirsCnt(candidate=[]):
        convSize = UtilMgr.convSize2Unit

        # convert type of output candidate #
        if not isinstance(candidate, list):
            candidate = [candidate]

        for targetDir, limitCnt in SysMgr.limitDirCntList.items():
            # get the file list sorted by name in the taget dir #
            try:
                flist = sorted(
                    filter(
                        lambda x: os.path.isfile(os.path.join(targetDir, x)),
                        os.listdir(targetDir),
                    )
                )
                if not flist:
                    continue
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printWarn(
                    "failed to check file count of '%s'" % targetDir,
                    True,
                    True,
                )
                continue

            # calculate the number of deletion #
            nrCandNew = 0
            for cand in candidate:
                if not os.path.isdir(cand):
                    cand = os.path.dirname(cand)
                if cand in SysMgr.limitDirCntList:
                    nrCandNew += 1

            # get file count #
            try:
                diff = limitCnt - len(flist) - nrCandNew
                if diff >= 0:
                    continue
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr(
                    "failed to get count limit for '%s'" % targetDir, True
                )
                continue

            # get total size of the directory #
            curSize = SysMgr.getDirSize(targetDir)

            # get remove file list #
            flist = flist[: abs(diff)]

            # init remove count #
            rmCnt = 0

            # remove files #
            for fname in flist:
                fpath = os.path.join(targetDir, fname)

                # get file size #
                size = UtilMgr.getFileSize(fpath, False)

                msg = "'%s' [%s] to keep the file count (%s) for '%s'" % (
                    fpath,
                    convSize(size),
                    limitCnt,
                    targetDir,
                )

                # remove the file #
                try:
                    os.remove(fpath)

                    rmCnt += 1
                    curSize -= size

                    msg += " [%s]" % (convSize(curSize))
                    SysMgr.printWarn("removed %s" % msg, True)
                except SystemExit:
                    sys.exit(0)
                except:
                    msg += " [%s]" % (convSize(curSize))
                    SysMgr.printErr("failed to remove %s" % msg, True)

            # print freed up space #
            if rmCnt:
                SysMgr.printInfo(
                    (
                        "removed a total of %s files "
                        "to keep the file count for '%s' [%s]"
                    )
                    % (
                        UtilMgr.convNum(rmCnt),
                        targetDir,
                        convSize(curSize),
                    )
                )

    @staticmethod
    def freeDirs():
        convSize = UtilMgr.convSize2Unit
        for targetDir, limitSize in SysMgr.limitDirList.items():
            # get total size of the directory #
            curSize = SysMgr.getDirSize(targetDir)
            if not curSize:
                continue

            # get free-up size #
            try:
                diff = limitSize - curSize
                if diff >= 0:
                    continue

                needSize = abs(diff)
                rmSize = 0
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr(
                    "failed to get free-up size for '%s'" % targetDir, True
                )
                continue

            # get the file list sorted by name in the taget dir #
            flist = sorted(
                filter(
                    lambda x: os.path.isfile(os.path.join(targetDir, x)),
                    os.listdir(targetDir),
                )
            )

            # remove files #
            for fname in flist:
                fpath = os.path.join(targetDir, fname)

                # get file size #
                size = UtilMgr.getFileSize(fpath, False)

                msg = "'%s' [%s] to free up space for a directory '%s'" % (
                    fpath,
                    convSize(size),
                    targetDir,
                )

                # remove the file #
                try:
                    os.remove(fpath)

                    rmSize += size
                    curSize -= size

                    msg += "  [%s]" % convSize(curSize)
                    SysMgr.printWarn("removed %s" % msg, True)
                except SystemExit:
                    sys.exit(0)
                except:
                    msg += "  [%s]" % convSize(curSize)
                    SysMgr.printErr("failed to remove %s" % msg, True)

                if rmSize >= needSize:
                    break

            # print freed up space #
            if rmSize:
                curSize = SysMgr.getDirSize(targetDir)

                SysMgr.printInfo(
                    (
                        "removed files in a directory with a total size of [%s] "
                        "to free up space for '%s' [%s]"
                    )
                    % (
                        convSize(rmSize),
                        targetDir,
                        convSize(curSize),
                    )
                )

    @staticmethod
    def writeTraceCmd(path, val, append=False):
        # set open permission #
        if append:
            perm = "a+"
        else:
            perm = "w"

        # record command to file #
        if SysMgr.cmdEnable:
            if not SysMgr.cmdFd:
                try:
                    SysMgr.cmdFd = open(SysMgr.cmdEnable, perm)

                    # TODO: check debugfs and tracefs #
                    SysMgr.cmdFd.write(
                        "mount -t debugfs nodev %s 2>%s\n"
                        % (SysMgr.debugfsPath, SysMgr.nullPath)
                    )

                    SysMgr.cmdFd.write(
                        'echo "\n[Info] start recording... '
                        '[ STOP(Ctrl+c) ]\n"\n'
                    )
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printOpenErr(SysMgr.cmdEnable)
                    return -1

            if SysMgr.cmdFd:
                try:
                    cmd = 'echo "%s" > %s/%s 2>%s\n' % (
                        str(val),
                        SysMgr.traceEventPath,
                        path,
                        SysMgr.nullPath,
                    )
                    SysMgr.cmdFd.write(cmd)
                    SysMgr.cmdFd.flush()
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr("failed to write command", True)
                    return -1

        # open node #
        try:
            target = "%s/%s" % (SysMgr.getTraceEventPath(), path)
            if append:
                fd = os.open(target, os.O_RDWR | os.O_CREAT | os.O_APPEND)
            else:
                fd = open(target, perm)
        except SystemExit:
            sys.exit(0)
        except:
            fpos = path.rfind("/")
            try:
                if path[fpos + 1 :] == "enable":
                    epath = path[:fpos]
                else:
                    epath = path[fpos + 1 :]
            except:
                epath = path

            try:
                SysMgr.sysInstance.cmdList[epath] = False
            except:
                pass

            SysMgr.printWarn("failed to use %s event" % epath, reason=True)
            return -1

        # write value #
        try:
            if append:
                os.write(fd, bytes(UtilMgr.encodeStr(val)))
                try:
                    os.close(fd)
                except:
                    pass
            elif val:
                fd.write(val)
                try:
                    fd.close()
                except:
                    pass

            # update command list #
            if path.endswith("/enable"):
                if not SysMgr.sysInstance:
                    SysMgr(onlyInstance=True)

                if val == "1":
                    SysMgr.sysInstance.cmdList[
                        path[: path.rfind("/enable")]
                    ] = True
                elif val == "0":
                    SysMgr.sysInstance.cmdList[
                        path[: path.rfind("/enable")]
                    ] = False
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn(
                "failed to apply command '%s' to %s" % (val, path), True, True
            )
            return -2

        return 0

    @staticmethod
    def doPrint(
        newline=True, clear=False, flush=True, addLine=False, isList=False
    ):
        if not SysMgr.bufferString and not SysMgr.bufferList:
            return

        if isList:
            output = SysMgr.getPrintList(retStr=True, char="")
        else:
            output = SysMgr.bufferString

        if addLine:
            output += "\n"

        SysMgr.printPipe(output, newline=newline, flush=flush)

        if clear:
            SysMgr.clearPrint()

    @staticmethod
    def printConsole(string):
        # split and cut lines by cols #
        string = "\n".join(
            [nline[: SysMgr.ttyCols - 1] for nline in string.split("\n")]
        )

        # print on console #
        try:
            print(string.rstrip())
        except SystemExit:
            sys.exit(0)
        except:
            return

    @staticmethod
    def addPrint(string, newline=1, force=False, isList=False):
        # check print flag #
        if not SysMgr.printEnable:
            return

        # check skip condition #
        if not force and SysMgr.checkCutCond(newline):
            return False

        # add string to buffer #
        if isList:
            SysMgr.bufferList.append(string)
        else:
            try:
                SysMgr.bufferString += string
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.bufferString = "%s%s" % (SysMgr.bufferString, string)

        SysMgr.bufferRows += newline

        if (
            SysMgr.terminalOver
            or not SysMgr.outPath
            or not SysMgr.streamEnable
        ):
            return True

        SysMgr.printConsole(string)

        return True

    @staticmethod
    def getPrintList(retStr=False, sort=False, char="\n"):
        if sort:
            SysMgr.bufferList.sort()

        if retStr:
            return char.join(SysMgr.bufferList)
        else:
            return SysMgr.bufferList

    @staticmethod
    def delPrint(newline=1):
        try:
            target = SysMgr.bufferString
            start = target.rfind("\n")
            pos = newline

            while start >= 0 and pos > 1:
                start = target.rfind("\n", start - 1)
                pos -= 1

            SysMgr.bufferString = target[: start + 1]
            SysMgr.bufferRows -= newline
        except:
            pass

    @staticmethod
    def clearPrint():
        del SysMgr.bufferString
        SysMgr.bufferList = []
        SysMgr.bufferString = ""
        SysMgr.bufferRows = 0
        SysMgr.terminalOver = False

    @staticmethod
    def clearScreen():
        # check stdout status #
        if not SysMgr.printEnable or SysMgr.pipeForPager:
            return

        if (
            SysMgr.isLinux or SysMgr.isDarwin
        ) and not "REMOTERUN" in os.environ:
            sys.stdout.write("\x1b[2J\x1b[H")
        elif SysMgr.platform.startswith("win"):
            os.system("cls")

    @staticmethod
    def printLogo(absolute=False, big=False, onlyFile=False, pager=True):
        if not SysMgr.logoEnable:
            return

        # check package #
        if not "ISMAIN" in os.environ:
            print("\nPowered by %s" % ConfigMgr.logo)
            return

        # check print option and remote runner #
        if not SysMgr.printEnable or SysMgr.remoteRun:
            return

        # check extended ascii support #
        SysMgr.convExtAscii(ConfigMgr.logo)

        if not SysMgr.outPath:
            if SysMgr.streamEnable:
                if not absolute:
                    return
            elif onlyFile:
                return

        # print logo #
        if big:
            logo = UtilMgr.convColor(ConfigMgr.logo, "GREEN")
            if pager:
                SysMgr.printPipe(logo)
            else:
                print(logo)
        else:
            title = "/ G.u.i.d.e.r \tver.%s_%s /" % (__version__, __revision__)
            SysMgr.printPipe(
                " %s\n%s\n%s"
                % (underLine[: len(title)], title, oneLine[: len(title)])
            )

        # append new line #
        if onlyFile:
            SysMgr.printPipe("\n\n")

    @staticmethod
    def parseInfoData(lines):
        jsonData = {}

        oneLinePart = oneLine[:10]
        twoLinePart = twoLine[:10]
        cleanItem = UtilMgr.cleanItem

        for l in lines:
            try:
                l = l.strip()

                # new context #
                m = re.match(r"\s*\[(?P<context>.+) Info\]", l)
                if not m:
                    continue

                d = m.groupdict()
                context = d["context"].replace(" ", "")
                jsonSubData = jsonData[context] = {}
                prev = prev2 = None
                menu = []

                for idx, item in enumerate(l.split("\n")[1:]):
                    if item in (oneLine, twoLine):
                        continue
                    elif item.startswith(oneLinePart):
                        continue
                    elif item.startswith(twoLinePart):
                        continue

                    if context in (
                        "SystemGeneral",
                        "SystemOS",
                        "SystemCPU",
                        "SystemCPUCache",
                    ):
                        if idx < 3:
                            continue

                        ret = item.lstrip().split(None, 1)
                        if item.startswith(" "):
                            if prev:
                                jsonSubData[prev] += ret[0]
                        else:
                            key, val = ret
                            key = key.strip()
                            jsonSubData[key] = val.strip(" |")
                            prev = key

                    elif context in (
                        "Buddy",
                        "FreePage",
                    ):
                        items = item.split("|")
                        if len(items) == 6:
                            isFree = True
                        else:
                            isFree = False

                        node = items[0].strip()
                        if node == "NODE":
                            # save menu #
                            menu = items[3 if isFree else 2].split()
                            continue

                        if not node:
                            node = prev
                        else:
                            prev = node

                        # FreePage #
                        if isFree:
                            mtype = items[2].strip()
                            zoneIdx = 1
                            valIdx = 3
                            totalIdx = 4
                        # Buddy #
                        else:
                            zoneIdx = 1
                            valIdx = 2
                            totalIdx = 3

                        zone = items[zoneIdx].strip()
                        if isFree:
                            if not zone:
                                zone = prev2
                            else:
                                prev2 = zone

                        values = [
                            items[valIdx][idx * 10 : idx * 10 + 10]
                            for idx in range(long(len(items[valIdx]) / 10))
                        ]
                        total = items[totalIdx].strip()

                        jsonSubData.setdefault(node, {})

                        # FreePage #
                        if isFree:
                            jsonSubData[node].setdefault(zone, {})
                            jsonSubData[node][zone][mtype] = {"total": total}
                            jsonSubTarget = jsonSubData[node][zone][mtype]
                        # Buddy #
                        else:
                            jsonSubData[node][zone] = {"total": total}
                            jsonSubTarget = jsonSubData[node][zone]

                        for i, v in zip(menu, values):
                            v = v.strip()
                            if not v:
                                continue
                            jsonSubTarget[i] = v

                    elif context in (
                        "SchedFactor",
                        "VM",
                        "Peak",
                        "Threshold",
                        "KSM",
                    ):
                        if idx < 3:
                            continue

                        ret = item.lstrip().split("|")[:2]
                        if len(ret) == 1:
                            if prev:
                                jsonSubData[prev] += ret[0]
                        else:
                            key, val = ret
                            key = key.strip()
                            val = val.strip(" |")
                            if not key or not val:
                                continue
                            jsonSubData[key] = val
                            prev = key

                    elif context in ("SchedFeature"):
                        if idx < 3:
                            continue

                        ret = cleanItem(item.lstrip().split("|"), False)
                        new = []
                        for i in item.split("|"):
                            new.append(i.strip())
                        ret = new
                        name = ret[0]
                        if not name:
                            if prev:
                                jsonSubData[prev] += cleanItem(ret[1:], False)
                        else:
                            jsonSubData[name] = cleanItem(ret[1:], False)
                            prev = name

                    elif context in ("SystemGPU", "HugePage"):
                        if idx < 3:
                            continue

                        ret = cleanItem(item.lstrip().split("|", 2), False)
                        if len(ret) == 2:
                            name = prev
                            stat, val = ret
                        else:
                            name, stat, val = ret
                        jsonSubData.setdefault(name, {})
                        jsonSubData[name][stat] = val.strip(" |")
                        prev = name

                    elif context in ("AppArmor", "SystemUSB", "MemoryMap"):
                        if idx < 1:
                            continue

                        if not isinstance(jsonSubData, list):
                            jsonSubData = jsonData[context] = []

                        if context == "AppArmor":
                            jsonSubData += UtilMgr.cleanItem(
                                item.split("|"), False
                            )
                        elif context == "MemoryMap":
                            if idx < 2:
                                continue
                            jsonSubData.append(
                                UtilMgr.cleanItem(item.split("|"), False)
                            )
                        else:
                            jsonSubData.append(item.rstrip(" |"))

                    elif context in ("SystemMemory"):
                        items = item.split()
                        items = UtilMgr.removeItems(items, "]")
                        name = items[1].strip(" ]")
                        if name == "DESC":
                            # save menu #
                            menu = items[5:-1]
                        elif name in ("TOTAL", "FREE", "AVAIL"):
                            mem, swap = items[2:4]
                            jsonSubData["mem" + name] = mem
                            jsonSubData["swap" + name] = swap
                        elif name == "LAST" and menu:
                            for n, stat in enumerate(items[5:-1]):
                                jsonSubData["mem" + menu[n]] = stat

                    elif context in ("SystemStorage"):
                        items = item.split()
                        name = items[0].strip()
                        if name == "DEV":
                            # save menu #
                            menu = items[1:-1]
                        elif name.startswith("/"):
                            jsonSubData[name] = {}
                            prev = name
                        elif name == "TOTAL" and menu:
                            jsonSubData[name] = {}
                            for n, item in enumerate(items[1:7]):
                                jsonSubData[name][menu[n]] = item
                        elif menu:
                            for n, item in enumerate(items[:8]):
                                jsonSubData[prev][menu[n]] = item
                            jsonSubData[prev][menu[8]] = " ".join(items[8:])

                    elif context in ("SystemBlock"):
                        items = item.split()
                        name = items[0].strip()
                        if name == "DEV":
                            # save menu #
                            menu = items
                        elif menu:
                            jsonSubData[name] = {}
                            for n, item in enumerate(items[1:8]):
                                jsonSubData[name][menu[n]] = item
                            jsonSubData[name][menu[8]] = " ".join(
                                items[8:]
                            ).rstrip(" |")

                    elif context in ("SystemNetwork"):
                        items = item.split()
                        items = UtilMgr.removeItems(items, "|")
                        name = items[0].strip()
                        if name == "Network":
                            continue
                        elif name == "Dev":
                            # save menu #
                            menu = items
                        elif menu:
                            if len(items) >= len(menu):
                                jsonSubData[name] = {}
                                prev = name
                                continue

                            for n, item in enumerate(items[1:]):
                                bound = "recv" if n < 5 else "tran"
                                jsonSubData[prev].setdefault(bound, {})
                                jsonSubData[prev][bound][menu[n + 2]] = item

                    elif context in ("SystemGPUMemory"):
                        items = item.split("|")
                        name = items[0].strip()
                        if name == "Process":
                            # save menu #
                            menu = cleanItem(items, False)
                        elif menu:
                            jsonSubData[name] = {}
                            for n, item in enumerate(items[1:]):
                                item = item.strip()
                                if not item:
                                    continue
                                jsonSubData[name][menu[n + 1]] = item

                    elif context in ("SystemSwap"):
                        items = item.strip(" |").split(None, 5)
                        if len(items) > 5:
                            name = items[5]
                        else:
                            name = items[0]

                        if name in ("Type", "None", "Path(Stat)"):
                            continue

                        if name.startswith("-"):
                            if not prev in jsonSubData:
                                continue
                            items = item.split(":", 1)
                            iname = items[0].strip(" -")
                            ival = items[1].strip()
                            jsonSubData[prev][iname] = ival
                        else:
                            jsonSubData[name] = {
                                "size": items[1],
                                "used": items[2].rstrip("("),
                                "usedPer": items[3].rstrip(")"),
                                "type": items[0],
                            }

                            if len(items) > 4:
                                jsonSubData[name]["prio"] = items[4]
                                jsonSubData[name]["path"] = items[5]

                            prev = name

                    elif context in ("TaskLimit"):
                        if item.startswith("Limit"):
                            continue
                        items = item.strip(" |").rsplit(None, 3)

                        jsonSubData[items[0]] = {
                            "soft": items[1],
                            "hard": items[2],
                            "unit": items[3],
                        }

                    else:
                        pass
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printWarn(
                    "failed to convert below system info in JSON format\n%s\n"
                    % l,
                    True,
                    True,
                )

        # convert type of values to number #
        if "CONVUNIT2SIZE" in SysMgr.environList:
            # define type converter #
            def _convValues(d):
                result = {}

                for key, value in d.items():
                    if isinstance(value, dict):
                        result[key] = _convValues(value)
                    else:
                        try:
                            result[key] = UtilMgr.convUnit2Size(
                                value.replace(",", ""), verb=False
                            )
                        except SystemExit:
                            sys.exit(0)
                        except:
                            pass

                return result

            jsonData = _convValues(jsonData)

        return jsonData

    @staticmethod
    def printInfoBuffer():
        SysMgr.printPipe(SysMgr.sysinfoBuffer + "\n")
        SysMgr.clearInfoBuffer()

    @staticmethod
    def parseCustomRecordCmd(cmdList):
        tempList = {"BEFORE": [], "START": [], "AFTER": [], "STOP": []}

        if not cmdList:
            return {}

        cmdList = cmdList.split(",")

        for item in cmdList:
            sitem = item.split(":")
            ltime = sitem[0].strip().upper()

            if not len(sitem) in (2, 3) or not ltime in (
                "BEFORE",
                "START",
                "AFTER",
                "STOP",
            ):
                SysMgr.printErr(
                    "wrong format used, BEFORE|START|AFTER|STOP:file:value"
                )
                sys.exit(-1)
            elif len(sitem) == 2:
                tempList[ltime].append([sitem[1]])
            elif len(sitem) == 3:
                tempList[ltime].append([sitem[1], sitem[2]])

        return tempList

    @staticmethod
    def getOverlayfsInfo(pid, cache=True):
        if cache and pid in SysMgr.overlayfsCache:
            return SysMgr.overlayfsCache[pid]

        data = SysMgr.getMountData(pid)
        mountList = SysMgr.convMountList(data)
        if not mountList:
            return {}

        overlayList = {}
        for point, info in mountList.items():
            try:
                if info["fs"] != "overlay":
                    continue

                overlayList[point] = {}
                items = info["subopt"].split(",")
                for line in items:
                    if line.startswith("lowerdir"):
                        attr = "lowerdir"
                    elif line.startswith("upperdir"):
                        attr = "upperdir"
                    elif line.startswith("mergedir"):
                        continue
                    else:
                        continue

                    dirs = line.lstrip(attr)[1:].split(":")
                    overlayList[point][attr] = dirs
            except SystemExit:
                sys.exit(0)
            except:
                continue

        SysMgr.overlayfsCache[pid] = overlayList

        return overlayList

    @staticmethod
    def convMountList(mountInfo):
        if not mountInfo:
            return {}

        mountList = {}
        for l in mountInfo:
            # split mount info #
            values = l.split(" - ")
            if len(values) != 2:
                continue
            left = values[0]
            right = values[1]

            # split left-side part #
            left = left.split()
            mountid, parentid, devid, root, path = left[:5]
            major, minor = devid.split(":")
            option = " ".join(left[5:-1])

            # split right-side part #
            right = right.split()
            fs, dev = right[0:2]
            soption = " ".join(right[2:])

            # get device ID #
            if not "/" in dev and ":" in dev:
                major, minor = dev.split(":")
            else:
                major = minor = -1

            # save mount info #
            mountList[path] = {
                "major": major,
                "minor": minor,
                "mountid": mountid,
                "fs": fs,
                "opt": option,
                "subopt": soption,
            }

        return mountList

    @staticmethod
    def getMountInfo():
        # check whether there is mount info in saved buffer #
        infoBuf = SysMgr.sysinfoBuffer

        if infoBuf == "":
            return

        # get area info #
        mountPosStart = infoBuf.find("Storage Info")
        if mountPosStart == -1:
            return

        mountPosStart = infoBuf.find(twoLine, mountPosStart + 1)
        if mountPosStart == -1:
            return

        mountPosStart = infoBuf.find(twoLine, mountPosStart + 1)
        if mountPosStart == -1:
            return

        mountPosStart = infoBuf.find("\n", mountPosStart)
        if mountPosStart == -1:
            return

        mountPosEnd = infoBuf.find(oneLine, mountPosStart)
        if mountPosEnd == -1:
            return

        # split string to list #
        try:
            mountTable = []
            tempTable = infoBuf[mountPosStart:mountPosEnd].split("\n")
            for idx, line in enumerate(tempTable):
                nrItem = len(line.split())
                if 1 < nrItem < 4:
                    mountTable.append("%s %s" % (line, tempTable[idx + 1]))
        except:
            pass

        init_mountData = {"dev": " ", "filesystem": " ", "mount": " "}

        # parse items #
        for item in mountTable:
            m = re.match(
                (
                    r"(?P<dev>\S+)\s+\((?P<devt>\S+)\)\s+\[(?P<range>\S+)\]\s+"
                    r"(?P<maj>[0-9]+):(?P<min>[0-9]+)\s+(?P<readSize>\S+)\s+"
                    r"(?P<writeSize>\S+)\s+(?P<totalSize>\S+)\s+"
                    r"(?P<freeSize>\S+)\s+(?P<Usage>\S+)\s+(?P<nrFile>\S+)\s+"
                    r"(?P<filesystem>\S+)\s+(?P<mount>.+)"
                ),
                item,
            )
            if not m:
                continue

            d = m.groupdict()
            mid = "%s:%s" % (d["maj"], d["min"])
            SysMgr.savedMountTree[mid] = dict(init_mountData)
            SysMgr.savedMountTree[mid]["dev"] = d["dev"]
            SysMgr.savedMountTree[mid]["filesystem"] = d["filesystem"]
            SysMgr.savedMountTree[mid]["mount"] = d["mount"]

            # add block range info #
            try:
                start, end = d["range"].split("-")
                SysMgr.savedMountTree[mid]["start"] = long(start)
                SysMgr.savedMountTree[mid]["end"] = long(end)
            except:
                pass

    @staticmethod
    def getProcTreeInfo():
        # check whether there is procTreeInfo in saved buffer #
        infoBuf = SysMgr.sysinfoBuffer

        if infoBuf == "":
            return

        magic = "!!!!!"

        # get tree pos #
        treePos = infoBuf.find(magic)
        if treePos == -1:
            return

        newPos = treePos + len(magic)

        # get comm pos #
        commPos = infoBuf[newPos:].find(magic)

        # check whether there is procTreeInfo in saved buffer #
        if commPos < 0:
            procTree = infoBuf[newPos:]
            procTree = procTree[: procTree.find("\n")].split(",")
        else:
            procTree = infoBuf[newPos : newPos + commPos].split(",")

        # parse task tree #
        for pair in procTree:
            try:
                ids = pair.split("#")
                tid = ids[0]
                pid = ids[1]

                # get pid and comm #
                if not pid.isdigit():
                    group = pid
                    pidPos = group.find("(")
                    pid = group[:pidPos]
                    comm = group[pidPos + 1 :].rstrip(")")
                    SysMgr.savedProcComm[pid] = comm

                SysMgr.savedProcTree[tid] = pid
            except:
                continue

        # remove task tree info #
        if commPos < 0:
            SysMgr.sysinfoBuffer = infoBuf[:treePos]
        else:
            commData = infoBuf[newPos + commPos + len(magic) :]
            commData = commData[: commData.find("\n")]
            SysMgr.commCache = UtilMgr.convStr2Dict(commData)
            SysMgr.sysinfoBuffer = infoBuf[: newPos - len(magic)]

    @staticmethod
    def checkVersion(infoBuf):
        # check version #
        try:
            verPosStart = infoBuf.find("Version")
            verPosEnd = infoBuf.find("\n", verPosStart)
            recVer = infoBuf[verPosStart:verPosEnd].split()[1]
            if recVer != __version__:
                SysMgr.printWarn(
                    "data version %s is different from current software %s"
                    % (__version__, recVer),
                    True,
                )
        except:
            pass

    @staticmethod
    def applyLaunchOption():
        # check whether there is launch option in saved buffer #
        infoBuf = SysMgr.sysinfoBuffer

        # get position of launch option #
        if infoBuf == "":
            return

        launchPosStart = infoBuf.find("Launch")
        if launchPosStart == -1:
            return

        launchPosEnd = infoBuf.find("\n", launchPosStart)
        if launchPosEnd == -1:
            return

        SysMgr.launchBuffer = infoBuf[launchPosStart:launchPosEnd]

        SysMgr.checkVersion(infoBuf)

        # get arch type #
        archPosStart = infoBuf.find("CPU")
        archPosEnd = infoBuf.find("\n", archPosStart)

        # apply arch type #
        if not SysMgr.archOption:
            try:
                arch = infoBuf[archPosStart:archPosEnd].split()[1]
                SysMgr.setArch(arch)
            except SystemExit:
                sys.exit(0)
            except:
                pass

        # print arch type #
        if archPosStart >= 0 and archPosEnd >= 0:
            analOption = "{0:20} {1:<100}".format(
                "Analysis", "# %s" % (" ".join(sys.argv))
            )
            SysMgr.sysinfoBuffer = "%s\n%s\n%s" % (
                infoBuf[:archPosEnd],
                analOption,
                infoBuf[archPosEnd + 1 :],
            )

        # apply mode option #
        if SysMgr.isDrawMode():
            SysMgr.printInfo("<DRAW MODE>")

        if (
            " funcrec " in SysMgr.launchBuffer
            or " funcrecord " in SysMgr.launchBuffer
        ):
            SysMgr.threadEnable = False
            SysMgr.functionEnable = True
            if not SysMgr.isDrawMode():
                SysMgr.printInfo("<FUNCTION MODE>")
        else:
            SysMgr.threadEnable = True
            if not SysMgr.isDrawMode():
                SysMgr.printInfo("<THREAD MODE>")

        # apply filter option #
        filterList = None
        launchPosStart = SysMgr.launchBuffer.find(" -g")
        if SysMgr.isThreadMode() and launchPosStart > -1:
            filterList = SysMgr.launchBuffer[launchPosStart + 3 :]
            filterList = filterList[: filterList.find(" -")].strip()
            SysMgr.filterGroup = filterList.split(",")
            SysMgr.filterGroup = UtilMgr.cleanItem(SysMgr.filterGroup)
            SysMgr.printInfo(
                "only specific threads [ %s ] were recorded"
                % ", ".join(SysMgr.filterGroup)
            )

        # check filter list #
        if SysMgr.filterGroup:
            if not SysMgr.groupProcEnable:
                SysMgr.printInfo(
                    "only specific threads [ %s ] are shown"
                    % ", ".join(SysMgr.filterGroup)
                )
            else:
                SysMgr.printInfo(
                    (
                        "only specific threads that involved "
                        "in the process group [ %s ] are shown"
                    )
                    % ", ".join(SysMgr.filterGroup)
                )

        # apply dependency option #
        if " -D" in SysMgr.launchBuffer:
            SysMgr.depEnable = True

        # apply syscall option #
        if " -t" in SysMgr.launchBuffer:
            SysMgr.sysEnable = True

        # apply disable option #
        launchPosStart = SysMgr.launchBuffer.find(" -d")
        if launchPosStart > -1:
            filterList = SysMgr.launchBuffer[launchPosStart + 3 :]
            filterList = filterList[: filterList.find(" -")]
            if "a" in filterList:
                SysMgr.disableAll = True
            if "c" in filterList:
                SysMgr.cpuEnable = False
                SysMgr.latEnable = False
            if "l" in filterList:
                SysMgr.latEnable = False

        # apply enable option #
        launchPosStart = SysMgr.launchBuffer.find(" -e")
        if launchPosStart > -1:
            filterList = SysMgr.launchBuffer[launchPosStart + 3 :]
            filterList = filterList[: filterList.find(" -")]
            if "u" in filterList:
                dlist = SysMgr.getOption("d")
                if not dlist or not "u" in dlist:
                    SysMgr.userEnable = True
                    SysMgr.userRecordEnable = True
            if "m" in filterList:
                SysMgr.memEnable = True
            if "b" in filterList:
                SysMgr.blockEnable = True
            if "P" in filterList:
                SysMgr.powerEnable = True
            if "w" in filterList:
                SysMgr.wqEnable = True
            if "B" in filterList:
                SysMgr.binderEnable = True
            if "I" in filterList:
                SysMgr.i2cEnable = True
            if "f" in filterList:
                SysMgr.fsEnable = True
            if "h" in filterList:
                SysMgr.heapEnable = True
            if "L" in filterList:
                SysMgr.lockEnable = True
            if "i" in filterList:
                SysMgr.irqEnable = True
            if "n" in filterList:
                SysMgr.networkEnable = True

            # get disable options #
            disOpt = SysMgr.getOption("d")
            disOpt = disOpt if disOpt else ""

            # memory type #
            if "S" in filterList and not "S" in disOpt:
                SysMgr.pssEnable = True
            elif "u" in filterList and not "u" in disOpt:
                SysMgr.ussEnable = True
            elif not SysMgr.isDrawMode():
                SysMgr.rssEnable = True
        elif not SysMgr.isDrawMode():
            SysMgr.rssEnable = True

        # apply custom option #
        launchPosStart = SysMgr.launchBuffer.find(" -c")
        if launchPosStart > -1:
            filterList = SysMgr.launchBuffer[launchPosStart + 3 :]
            endIdx = filterList.find(" -")
            if endIdx >= 0:
                filterList = filterList[:endIdx]
            filterList = filterList.strip().split(",")
            filterList = UtilMgr.cleanItem(filterList, union=False)
            filterList = list(map(lambda c: c.split(":", 1)[0], filterList))
            for idx, item in enumerate(filterList):
                tempItem = filterList[idx].split("/")
                if len(tempItem) == 2:
                    filterList[idx] = tempItem[1]
                    SysMgr.customEventList.append(tempItem[1])
                elif len(tempItem) == 1:
                    filterList[idx] = tempItem[0]
                    SysMgr.customEventList.append(tempItem[0])
                else:
                    filterList.pop(idx)
            if filterList:
                SysMgr.printInfo(
                    "profiled trace events [ %s ]" % ", ".join(filterList)
                )
                if not SysMgr.customCmd:
                    SysMgr.customCmd = filterList

        # apply user event option #
        launchPosStart = SysMgr.launchBuffer.find(" -U")
        if launchPosStart > -1:
            SysMgr.ueventEnable = True
            filterList = SysMgr.launchBuffer[launchPosStart + 3 :]
            filterList = filterList[: filterList.find(" -")].strip()
            SysMgr.userCmd = str(filterList).split(",")
            SysMgr.userCmd = UtilMgr.cleanItem(SysMgr.userCmd)
            SysMgr.printInfo(
                "profiled user events [ %s ]"
                % ", ".join([cmd.strip() for cmd in SysMgr.userCmd])
            )
            SysMgr.userEventList = [
                cmd.split(":")[0].strip() for cmd in SysMgr.userCmd
            ]

        # apply kernel event option #
        launchPosStart = SysMgr.launchBuffer.find(" -K")
        if launchPosStart > -1:
            SysMgr.keventEnable = True
            filterList = SysMgr.launchBuffer[launchPosStart + 3 :]
            filterList = filterList[: filterList.find(" -")].strip()
            SysMgr.kernelCmd = str(filterList).split(",")
            SysMgr.kernelCmd = UtilMgr.cleanItem(SysMgr.kernelCmd)
            SysMgr.printInfo(
                "profiled kernel events [ %s ]"
                % ", ".join([cmd.strip() for cmd in SysMgr.kernelCmd])
            )
            SysMgr.kernelEventList = [
                cmd.split(":")[0].strip() for cmd in SysMgr.kernelCmd
            ]

        # apply arch option #
        launchPosStart = SysMgr.launchBuffer.find(" -A")
        if launchPosStart > -1:
            filterList = SysMgr.launchBuffer[launchPosStart + 3 :]
            filterList = filterList[: filterList.find(" -")].strip()

            if SysMgr.arch != filterList:
                SysMgr.printErr(
                    (
                        "arch(%s) of recorded target is different with "
                        "current arch(%s), use -A option with %s"
                    )
                    % (filterList, SysMgr.arch, filterList)
                )
                sys.exit(-1)

    @staticmethod
    def writeEvent(message, show=True):
        if not SysMgr.isLinux:
            return

        # check event path #
        if not SysMgr.getTraceEventPath():
            return

        # check trace file #
        if not SysMgr.eventLogFd:
            if not SysMgr.eventLogPath:
                SysMgr.eventLogPath = "%s/%s" % (
                    SysMgr.traceEventPath,
                    "../trace_marker",
                )

            try:
                SysMgr.eventLogFd = open(SysMgr.eventLogPath, "w")
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printOpenWarn(
                    "failed to open %s" % SysMgr.eventLogPath, True, True
                )
                return

        # write trace log #
        if SysMgr.eventLogFd:
            try:
                SysMgr.eventLogFd.write(message)
                event = message[message.find("_") + 1 :]
                if show:
                    SysMgr.printInfo("wrote '%s' event" % event)

                try:
                    SysMgr.eventLogFd.flush()
                except:
                    pass

                return True
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printWarn(
                    "failed to write %s event" % (message), reason=True
                )
                return
        else:
            SysMgr.printErr(
                "failed to write %s event because there is no file\n" % message
            )

    @staticmethod
    def infoBufferPrint(line):
        SysMgr.sysinfoBuffer = "%s%s\n" % (SysMgr.sysinfoBuffer, line)

    @staticmethod
    def clearInfoBuffer():
        SysMgr.sysinfoBuffer = ""

    @staticmethod
    def printPipWarn(name, pkg):
        SysMgr.printWarn(
            "failed to import python package: %s, try to run 'pip%s install %s'"
            % (name, sys.version_info[0], pkg),
            True,
        )

    @staticmethod
    def drawTimeline(
        inputPath=None,
        inputData=None,
        outputPath=None,
        configPath=None,
        configData=None,
        taskList=None,
        start=0,
        annotation=None,
        yval=None,
        begin=0,
        end=0,
    ):
        def _addUserEvent(inputData):
            if not inputData or not "EVENT" in SysMgr.environList:
                return

            for item in SysMgr.environList["EVENT"]:
                try:
                    values = item.split(":")
                    if not (3 <= len(values) <= 5):
                        SysMgr.printErr(
                            (
                                "failed to recognize %s in X:Y:NAME:[CPU|MEM"
                                "|IO]:[BOX|CIRCLE|LARROW|RARROW] format"
                            )
                            % item
                        )
                        sys.exit(-1)

                    x, y, name = values[:3]

                    # add timeline data #
                    inputData["segments"].append(
                        {
                            "group": long(y),
                            "text": name,
                            "id": None,
                            "state": "EVENT_USER",
                            "time_start": float(x),
                            "time_end": float(x) + 0.000001,
                        }
                    )

                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr(
                        "failed to recognize event for timeline", reason=True
                    )
                    sys.exit(-1)

        # get svgwrite object #
        svgwrite = SysMgr.getPkg("svgwrite")

        if inputPath:
            inputStr = ' from "%s"' % inputPath
        else:
            inputStr = ""

        SysMgr.printStat("start drawing timeline%s..." % inputStr)

        try:
            dwg = svgwrite.Drawing(outputPath, profile="tiny", debug=True)

            config = Timeline.Config()
            if configPath or configData:
                config = Timeline.Config._load(configPath, configData)

            # check timeunit #
            if "TIMEUNIT" in SysMgr.environList:
                config.TIMEUNIT = SysMgr.environList["TIMEUNIT"][0].lower()
            else:
                config.TIMEUNIT = "us"

            # set duration #
            duration = 0
            if "DURATION" in SysMgr.environList:
                try:
                    duration = SysMgr.environList["DURATION"][0]
                    duration = long(duration)
                except:
                    SysMgr.printErr(
                        "failed to set duration to '%s'" % duration, True
                    )
                    sys.exit(-1)

            # set font size #
            durationMin = 0
            if "DURATIONMIN" in SysMgr.environList:
                try:
                    durationMin = SysMgr.environList["DURATIONMIN"][0]
                    durationMin = long(durationMin)
                except:
                    SysMgr.printErr(
                        "failed to set minimum duration to '%s'" % durationMin,
                        True,
                    )
                    sys.exit(-1)

            # apply user event #
            _addUserEvent(inputData)

            # set convert table #
            Timeline.conv_table = {}
            if "CONVGROUP" in SysMgr.environList:
                try:
                    for item in SysMgr.environList["CONVGROUP"]:
                        src, des = item.split(":", 1)
                        Timeline.conv_table[src.strip()] = des.strip()
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr(
                        "failed to set group conversion table", True
                    )
                    sys.exit(-1)

            # load data #
            timeline = Timeline.load(
                inputPath,
                inputData,
                config,
                taskList,
                begin,
                end,
                duration,
                durationMin,
            )
            if not timeline:
                SysMgr.printErr("no time segment on timeline")
                return

            # set stroke candidate #
            if "STROKE" in SysMgr.environList:
                timeline.stroke_text = SysMgr.environList["STROKE"]
            else:
                timeline.stroke_text = None

            # set stroke interval #
            if "STROKEINTERVAL" in SysMgr.environList:
                try:
                    stroke_interval = SysMgr.environList["STROKEINTERVAL"][0]
                    timeline.stroke_interval = long(stroke_interval)
                except:
                    SysMgr.printErr(
                        "failed to set interval for stroke to '%s'"
                        % stroke_interval,
                        True,
                    )
                    sys.exit(-1)
            else:
                timeline.stroke_interval = 0

            # set stroke duration #
            if "STROKEDURATION" in SysMgr.environList:
                try:
                    stroke_duration = SysMgr.environList["STROKEDURATION"][0]
                    timeline.stroke_duration = long(stroke_duration)
                except:
                    SysMgr.printErr(
                        "failed to set duration for stroke to '%s'"
                        % stroke_duration,
                        True,
                    )
                    sys.exit(-1)
            else:
                timeline.stroke_duration = 0

            # draw timeslices #
            timeline.draw(dwg, start=start, annotation=annotation, yval=yval)

            dwg.save()
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to draw timeline", reason=True)
            return

        # get output size #
        fsize = UtilMgr.getFileSizeStr(outputPath)

        SysMgr.printStat(
            "wrote timeline segments into '%s'%s" % (outputPath, fsize)
        )

    @staticmethod
    def drawText(lines):
        # pylint: disable=import-error

        imageType = None

        # get textwrap object #
        textwrap = SysMgr.getPkg("textwrap", False)
        if not textwrap:
            SysMgr.printPipWarn("textwrap", "textwrap3")
            sys.exit(-1)

        # get PIL object #
        PIL = SysMgr.getPkg("PIL", False)
        if not PIL:
            SysMgr.printPipWarn("PIL", "pillow")
            sys.exit(-1)

        from PIL import Image, ImageFont, ImageDraw

        # load jpeg plugin #
        try:
            if not imageType:
                from PIL import JpegImagePlugin

                imageType = "jpg"
        except ImportError:
            err = sys.exc_info()[1]
            SysMgr.printWarn(
                "failed to import python package: %s" % err.args[0]
            )
        except SystemExit:
            sys.exit(0)

        # load bmp plugin instead of jpeg #
        try:
            if not imageType:
                from PIL import BmpImagePlugin

                imageType = "bmp"
        except ImportError:
            err = sys.exc_info()[1]
            SysMgr.printErr(
                "failed to import python package: %s" % err.args[0]
            )
            return
        except SystemExit:
            sys.exit(0)

        if not SysMgr.imagePath:
            SysMgr.printErr("failed to load image path")
            return

        # set image file extension #
        SysMgr.imagePath += ".%s" % imageType

        if SysMgr.fontPath:
            try:
                # load specific font #
                imageFont = ImageFont.truetype(SysMgr.fontPath, 10)
            except:
                SysMgr.printErr(
                    "failed to load font from %s" % SysMgr.fontPath
                )
                return
        else:
            try:
                # load default font #
                imageFont = ImageFont.load_default().font
            except:
                SysMgr.printErr(
                    (
                        "failed to load default font because %s, "
                        "try to use -T option"
                    )
                    % SysMgr.getErrMsg()
                )
                return

        # get default font size and image length #
        text = textwrap.fill("A", width=150)
        fontSizeX, fontSizeY = imageFont.getsize(text)

        # check input parameter #
        if isinstance(lines, list):
            lines = "".join(lines)

        # convert string to list #
        lines = lines.split("\n")

        # calculate image size #
        imageSizeX = fontSizeX * SysMgr.lineLength
        imageSizeY = fontSizeY * len(lines) + (fontSizeY * 2)
        imagePosY = 1

        # make new blink image #
        if imageType == "jpg":
            pass
        elif imageType == "bmp":
            imageSizeX = 900
        else:
            SysMgr.printErr("no output image type")
            return

        imageObject = Image.new(
            "RGB", (imageSizeX, imageSizeY), (255, 255, 255)
        )

        # make palette #
        drawnImage = ImageDraw.Draw(imageObject)

        for line in lines:
            text = textwrap.fill(line, width=170)

            imagePosY += fontSizeY

            # write text on image #
            try:
                drawnImage.text(
                    (1, imagePosY), text, (0, 0, 0), font=imageFont
                )
            except SystemExit:
                sys.exit(0)
            except:
                try:
                    drawnImage.text((1, imagePosY), text, (0, 0, 0))
                except SystemExit:
                    sys.exit(0)
                except:
                    continue

        try:
            # save image as file #
            imageObject.save(SysMgr.imagePath)
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to save image as %s\n" % SysMgr.imagePath)
            return

        # get output size #
        fsize = UtilMgr.getFileSizeStr(SysMgr.imagePath)

        SysMgr.printStat(
            "saved image into %s%s successfully" % (SysMgr.imagePath, fsize)
        )

    @staticmethod
    def clearProcBuffer():
        SysMgr.procBufferSize = 0
        SysMgr.procBuffer = []

    @staticmethod
    def printProcBuffer(suffix="\n"):
        msg = " Detailed Statistics "
        stars = "*" * long((long(SysMgr.lineLength) - len(msg)) / 2)
        SysMgr.printPipe("\n\n%s%s%s\n\n" % (stars, msg, stars))
        if SysMgr.procBuffer:
            SysMgr.printPipe(SysMgr.procBuffer)
        else:
            SysMgr.printPipe("\n    None%s" % suffix)

    @staticmethod
    def addProcBuffer(data):
        SysMgr.procBuffer.insert(0, data)
        SysMgr.procBufferSize += len(data)

        bufferSize = SysMgr.bufferSize
        convSize = UtilMgr.convSize2Unit

        while 0 < bufferSize < SysMgr.procBufferSize:
            # flush all data in buffer to the file #
            if not SysMgr.bufferLossEnable:
                SysMgr.printInfo(
                    (
                        "start writing interval statistics because "
                        "buffer (%s) exceed %s"
                    )
                    % (
                        convSize(SysMgr.procBufferSize),
                        convSize(bufferSize),
                    )
                )

                # create a new process #
                pid = SysMgr.createProcess(isDaemon=True, chPgid=True)
                # save output to file as child #
                if pid == 0:
                    # append time to the output file #
                    dtime = UtilMgr.getTime("UTCTIME" in SysMgr.environList)
                    if not dtime:
                        dtime = long(SysMgr.getUptime())

                    # append PID to the output file #
                    SysMgr.fileSuffix = "%s_%s" % (dtime, SysMgr.pid)

                    # change priority #
                    SysMgr.setLowPriority(True)

                    # flush all data to the file and exit #
                    SysMgr.stopHandler()
                # clear buffer as parent #
                elif pid > 0:
                    SysMgr.clearProcBuffer()
                    break
                # pop old data in buffer because of fork failure #
                else:
                    pass

            # pop old data in buffer #
            if not SysMgr.bufferOverflowed:
                SysMgr.printWarn(
                    (
                        "new data is going to be overwritten to the buffer"
                        " because of buffer overflow\n"
                        "\tincrease buffer size (%s) "
                        "if you want to prevent data loss"
                    )
                    % convSize(bufferSize),
                    True,
                )
                SysMgr.bufferOverflowed = True

            if not SysMgr.procBuffer:
                break

            # remove proc data #
            SysMgr.procBufferSize -= len(SysMgr.procBuffer[-1])
            SysMgr.procBuffer.pop(-1)

            # remove file data #
            for path, data in TaskAnalyzer.fileIntData.items():
                if data and len(SysMgr.procBuffer) < len(data):
                    data.pop(sorted(data, key=lambda e: float(e))[0], None)

    @staticmethod
    def updateSession():
        if not SysMgr.addrListForPrint:
            return

        addrListForPrint = dict(SysMgr.addrListForPrint)
        for addr, cli in addrListForPrint.items():
            if cli.status == "SENT" and cli.ignore > 1:
                SysMgr.printInfo(
                    "unregistered %s:%d for PRINT" % (cli.ip, cli.port)
                )
                del SysMgr.addrListForPrint[addr]

    @staticmethod
    def printTopStats():
        # JSON mode #
        if SysMgr.jsonEnable:
            # print report in JSON format later #
            if SysMgr.reportEnable:
                pass
            else:
                # convert dict data to JSON string #
                jsonStr = UtilMgr.convDict2Str(SysMgr.jsonData, pretty=False)
                if not jsonStr:
                    SysMgr.printWarn(
                        "failed to convert report data to JSON format"
                    )
                else:
                    SysMgr.printPipe(jsonStr)
        # realtime mode #
        elif not SysMgr.outPath:
            if not SysMgr.streamEnable:
                SysMgr.clearScreen()
            SysMgr.doPrint()
        # pipe mode #
        elif SysMgr.pipeEnable:
            SysMgr.doPrint(addLine=True)
        # buffered mode #
        else:
            SysMgr.addProcBuffer(SysMgr.bufferString + "\n")

            # send output to nodes registered #
            SysMgr.sendOutput2Nodes(SysMgr.bufferString)

        # flush buffer #
        SysMgr.clearPrint()

    @staticmethod
    def checkCutCond(newline=0):
        if SysMgr.terminalOver:
            return True
        elif (
            not SysMgr.outPath
            and not SysMgr.jsonEnable
            and not SysMgr.streamEnable
            and SysMgr.bufferRows + newline
            >= SysMgr.ttyRows - SysMgr.ttyRowsMargin
        ):
            SysMgr.terminalOver = True
            SysMgr.addPrint("---more---", force=True)
            return True
        else:
            return False

    @staticmethod
    def updateTimer(interval=None):
        if not SysMgr.isLinux:
            return

        try:
            if interval:
                signal.alarm(long(interval))
            else:
                signal.alarm(SysMgr.intervalEnable)
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to set interval timer", reason=True)
            sys.exit(-1)

    @staticmethod
    def updateOutPath():
        # dir #
        if SysMgr.outPath and os.path.isdir(SysMgr.outPath):
            SysMgr.inputFile = os.path.join(SysMgr.outPath, SysMgr.outFilePath)
        # file #
        else:
            SysMgr.inputFile = SysMgr.outPath

        # check output path #
        if not SysMgr.inputFile:
            return

        SysMgr.inputFile = os.path.realpath(SysMgr.inputFile)

        # check device node #
        if SysMgr.inputFile.startswith("/dev/"):
            return

        # append suffix to output file #
        if SysMgr.fileSuffix:
            dirname, filename = UtilMgr.getPath(SysMgr.inputFile)
            name, ext = os.path.splitext(filename)
            filepath = os.path.join(dirname, name)
            SysMgr.inputFile = "%s_%s%s" % (filepath, SysMgr.fileSuffix, ext)

        # append uptime to the output file #
        if not SysMgr.termFlag:
            SysMgr.inputFile = "%s_%s" % (
                SysMgr.inputFile,
                SysMgr.getRuntime(),
            )

    @staticmethod
    def setDefaultOutPath():
        if (
            SysMgr.inputFile
            and SysMgr.outPath
            and os.path.isdir(SysMgr.outPath)
        ):
            SysMgr.outPath = os.path.join(
                SysMgr.outPath, SysMgr.inputFile + ".out"
            )

    @staticmethod
    def sendOutput2Nodes(line, force=False):
        if not SysMgr.addrListForPrint:
            return
        elif not line:
            return
        else:
            optList = [x[0] for x in SysMgr.optionList]
            if not force and "X" in optList and "N" in optList:
                return

        # add hostname to packet #
        hostname = SysMgr.environList.get("HOSTNAME", ["ANON"])[0]
        line = SysMgr.magicStr + hostname + SysMgr.magicStr + line

        addrListForPrint = dict(SysMgr.addrListForPrint)
        for addr, cli in addrListForPrint.items():
            udpSeg = 65507  # maximum UDP diagram size
            start = 0
            end = udpSeg
            while 1:
                # split by newline #
                if len(line) >= end:
                    pos = line[start:end].rfind("\n")
                    if pos > 0:
                        end = pos + start

                # send data #
                ret = cli.send(line[start:end])
                if not ret:
                    del SysMgr.addrListForPrint[addr]
                    break
                else:
                    cli.ignore += 1

                if end >= len(line):
                    break

                # update sending part #
                start = end
                end += udpSeg

    @staticmethod
    def printPipe(
        line="",
        newline=True,
        flush=False,
        pager=True,
        trim=True,
        colorCtl=True,
    ):
        # check logging option #
        if SysMgr.loggingEnable:
            SysMgr.printLog(line)

        # send output to nodes registered #
        SysMgr.sendOutput2Nodes(line)

        # check print flag #
        if not SysMgr.printEnable:
            return

        # convert list to string #
        if isinstance(line, list):
            if not line:
                line = ""
            elif line[0] and line[0][-1] == "\n":
                line = "".join(line)
            else:
                line = "\n".join(line)

        # pager initialization #
        if (
            not pager
            or SysMgr.pipeForPager
            or SysMgr.outPath
            or SysMgr.streamEnable
            or SysMgr.checkMode("hserver")
        ):
            pass
        elif not SysMgr.isTopMode() or SysMgr.isHelpMode():
            try:
                if SysMgr.isLinux or SysMgr.isDarwin:
                    # internal less #
                    if "LESS" in SysMgr.environList:
                        # make less command string #
                        ecmd = "%s %s %s -q NOLOGO" % (
                            SysMgr.getExeName(os.getpid()),
                            SysMgr.getPyPath(),
                            "less",
                        )

                        # backup LOGO value #
                        envLogo = os.getenv("LOGO")
                        os.putenv("LOGO", "0")
                        os.putenv("LESSPIPE", "1")

                        # run less as pager #
                        SysMgr.pipeForPager = os.popen(ecmd, "w")
                        SysMgr.setPipeSize(SysMgr.pipeForPager)
                        SysMgr.ttyRows = sys.maxsize

                        # recover LOGO value #
                        os.putenv("LOGO", envLogo if envLogo else "")
                        os.putenv("LESSPIPE", "")
                    # outernal less #
                    elif UtilMgr.which("less"):
                        # defopt = '-FRSXMQi'
                        defopt = "-FRXMQi"

                        # receive SIGCHLD #
                        signal.signal(signal.SIGCHLD, signal.SIG_DFL)

                        # verify pager option support #
                        ret = os.popen(
                            "echo | less %s 2>&1" % defopt, "r"
                        ).read()
                        if len(ret) <= 1:
                            poption = "less %s" % defopt
                        else:
                            poption = "less"

                        # run less as pager #
                        SysMgr.pipeForPager = os.popen(poption, "w")

                        # set signals to prevent abnormal terminal status #
                        SysMgr.setDefaultSignal()
                    # outernal more #
                    elif UtilMgr.which("more"):
                        # run more as pager #
                        SysMgr.pipeForPager = os.popen("more", "w")
                elif SysMgr.platform.startswith("win"):
                    try:
                        SysMgr.pipeForPager = os.popen("more", "w")
                    except SystemExit:
                        sys.exit(0)
                    except:
                        pass
                else:
                    # no supported OS #
                    SysMgr.pipeForPager = None

                SysMgr.encodeEnable = False

                SysMgr.setPipeHandler()
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printWarn("failed to use pager", True, reason=True)

        # pager output #
        if SysMgr.pipeForPager:
            try:
                if line:
                    SysMgr.pipeForPager.write(line)

                    if newline and line[-1] != "\n":
                        SysMgr.pipeForPager.write("\n")

                return
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr("failed to print to pager\n", True)
                SysMgr.pipeForPager = None

        # file initialization #
        if SysMgr.outPath and not SysMgr.printFd:
            # profile #
            if SysMgr.isRuntimeMode():
                SysMgr.updateOutPath()
            # analysis #
            else:
                # dir #
                if os.path.isdir(SysMgr.outPath):
                    name, ext = os.path.splitext(
                        os.path.basename(SysMgr.inputFile)
                    )
                    if ext in ("", ".dat"):
                        name = "%s.out" % name
                    if name.endswith(".dat"):
                        name = name.replace(".dat", ".out")
                    SysMgr.inputFile = os.path.join(SysMgr.outPath, name)
                # file #
                else:
                    SysMgr.inputFile = SysMgr.outPath

            # convert abnormal characters from full path #
            SysMgr.inputFile = os.path.normpath(SysMgr.inputFile)

            # apply filename extension for compression #
            if SysMgr.compressEnable:
                if not SysMgr.inputFile.endswith(".gz"):
                    SysMgr.inputFile += ".gz"
                if SysMgr.outPath and not SysMgr.outPath.endswith(".gz"):
                    SysMgr.outPath += ".gz"

            # backup an exist file #
            SysMgr.backupFile(SysMgr.inputFile)

            # open file #
            try:
                # change permission #
                try:
                    os.chmod(SysMgr.inputFile, 0o777)
                except:
                    pass

                # open output file #
                SysMgr.printFd = open(SysMgr.inputFile, "wb")

                # apply compression to the file #
                if SysMgr.compressEnable:
                    compressor = SysMgr.getPkg("gzip", False)
                    if compressor:
                        SysMgr.printFd = compressor.GzipFile(
                            mode="wb", fileobj=SysMgr.printFd
                        )

                # print file name #
                if SysMgr.outPath:
                    SysMgr.printInfo(
                        "start writing output to '%s'" % SysMgr.inputFile
                    )
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printOpenErr(SysMgr.inputFile)
                sys.exit(-1)

        # file output #
        if SysMgr.printFd:
            try:
                if line:
                    SysMgr.printFd.write(line.encode("utf-8"))

                    if newline and line[-1] != "\n":
                        SysMgr.printFd.write("\n".encode("utf-8"))

                if flush:
                    SysMgr.printFd.flush()
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr(
                    "failed to write to %s" % SysMgr.printFd.name, True
                )
        # console output #
        else:
            # set truncate size #
            if trim:
                cols = SysMgr.ttyCols
            else:
                cols = 0

            # append the end color character to head #
            if SysMgr.colorEnable and not SysMgr.jsonEnable:
                try:
                    line = ConfigMgr.ENDC + line
                except SystemExit:
                    sys.exit(0)
                except:
                    line = "%s%s" % (ConfigMgr.ENDC, line)

            # rstrip by terminal size #
            try:
                # no limit #
                if cols == 0 or SysMgr.jsonEnable:
                    line = "\n".join([nline for nline in line.split("\n")])
                # trim a colorful line by terminal width #
                elif (
                    not SysMgr.forceColorEnable
                    and SysMgr.colorEnable
                    and ConfigMgr.ENDC in line
                    and colorCtl
                ):
                    if not SysMgr.ansiObj:
                        UtilMgr.removeColor("")
                    ansiObj = SysMgr.ansiObj
                    line = "\n".join(
                        [
                            ansiObj.sub("", n)[:cols]
                            if len(ansiObj.sub("", n)) > cols
                            else n
                            for n in line.split("\n")
                        ]
                    )
                # trim a normal line by terminal width #
                else:
                    line = "\n".join(
                        [nline[: cols - 1] for nline in line.split("\n")]
                    )
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printWarn("failed to print to console", reason=True)
                return

            # convert to extended ascii #
            nline = SysMgr.convExtAscii(line)

            # print string to console #
            try:
                sys.stdout.write(nline)
            except SystemExit:
                sys.exit(0)
            except:
                if SysMgr.encodeEnable:
                    SysMgr.encodeEnable = False

                    try:
                        sys.stdout.write(line)
                    except SystemExit:
                        sys.exit(0)
                    except:
                        pass

            # print newline #
            if newline:
                sys.stdout.write("\n")
                SysMgr.newlined = True
            else:
                SysMgr.newlined = False

            # flush buffer #
            if flush or SysMgr.remoteRun:
                try:
                    sys.stdout.flush()
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

    @staticmethod
    def convExtAscii(line, reverse=False):
        # pylint: disable=no-member,undefined-variable
        if not SysMgr.encodeEnable and not reverse:
            return line

        try:
            if not reverse:
                newline = line.replace("-------", "───────")
                newline = newline.replace("=", "═")
                newline = newline.replace("|-", "├─")
                newline = newline.replace("|", "│")
            else:
                newline = line.replace("───────", "-------")
                newline = newline.replace("═", "=")
                newline = newline.replace("├─", "|-")
                newline = newline.replace("│", "|")

            if sys.version_info < (3, 0) and not SysMgr.encoding:
                if not sys.getdefaultencoding().lower().startswith("utf"):
                    try:
                        reload(sys)
                        sys.setdefaultencoding("utf-8")
                        SysMgr.encoding = sys.getdefaultencoding()
                    except:
                        pass

            return newline
        except:
            SysMgr.encodeEnable = False
            return line

    @staticmethod
    def printLog(log, level="INFO"):
        if SysMgr.dltEnable:
            DltAnalyzer.doLogDlt(msg=log, level=level)
        if SysMgr.kmsgEnable:
            LogMgr.doLogKmsg(msg=log)
        if SysMgr.syslogEnable:
            LogMgr.doLogSyslog(msg=log)
        if SysMgr.journalEnable:
            LogMgr.doLogJournal(msg=log)
        if SysMgr.andlogEnable:
            LogMgr.doLogAndroid(msg=log)

    @staticmethod
    def printWarn(line, always=False, reason=False, newline=True, code=False):
        # print backtrace #
        # SysMgr.printBacktrace()

        if not SysMgr.logEnable or (not SysMgr.warnEnable and not always):
            return

        # add error message #
        if reason:
            rstring = " because %s" % SysMgr.getErrMsg()
        elif code:
            rstring = " because %s" % SysMgr.getErrReason()
        else:
            rstring = ""

        proc = SysMgr.getProcInfo()

        log = "%s%s%s%s" % ("[WARN] ", proc, line, rstring)

        # log #
        if SysMgr.loggingOpsEnable:
            SysMgr.printLog(log, level="WARN")

        log = "\n%s" % (
            UtilMgr.convColor(
                log, "WARNING", force=False if SysMgr.remoteRun else True
            )
        )

        if newline:
            log = "%s\n" % log

        # write log to stdout #
        if SysMgr.stdlog:
            SysMgr.stdlog.write(log)

        # write log #
        if "REMOTERUN" in os.environ:
            print(log.replace("\n", ""))
        else:
            SysMgr.writeErr(SysMgr.stderr, log)

    @staticmethod
    def printErr(line, reason=False, code=False):
        # print backtrace #
        # SysMgr.printBacktrace()

        if not SysMgr.logEnable and SysMgr.forceEnable:
            return

        SysMgr.flushAllForPrint()

        # add error message #
        if reason:
            rstring = " because %s" % SysMgr.getErrMsg()
        elif code:
            rstring = " because %s" % SysMgr.getErrReason()
        else:
            rstring = ""

        try:
            line = line.rstrip("\n")
        except SystemExit:
            sys.exit(0)
        except:
            pass

        proc = SysMgr.getProcInfo()

        log = "%s%s%s%s" % ("[ERROR] ", proc, line, rstring)

        # log #
        if SysMgr.loggingOpsEnable:
            SysMgr.printLog(log, level="ERROR")

        log = "\n%s\n" % (
            UtilMgr.convColor(
                log, "FAIL", force=False if SysMgr.remoteRun else True
            )
        )

        # write to logger #
        if SysMgr.stdlog:
            SysMgr.stdlog.write(log)

        # write log to stderr #
        if SysMgr.remoteRun:
            print(log.replace("\n", ""))
        else:
            SysMgr.writeErr(SysMgr.stderr, log)

    @staticmethod
    def getProcInfo(pid=None):
        if pid:
            return "<%s(%s)> " % (SysMgr.getComm(pid), pid)
        else:
            if SysMgr.parentPid > 0:
                return "<%s(%s)> " % (SysMgr.comm, SysMgr.pid)
            else:
                return ""

    @staticmethod
    def printInfo(line, prefix=True, suffix=True, title=True):
        if not SysMgr.logEnable:
            return

        # title and task #
        if title:
            title = "[INFO] "
            proc = SysMgr.getProcInfo()
        else:
            title = ""
            proc = ""

        # prefix #
        if prefix:
            prefix = "\n"
        else:
            prefix = ""

        # add newline #
        if not SysMgr.newlined:
            prefix += "\n"
            SysMgr.newlined = True

        # build #
        log = "%s%s%s" % (title, proc, line)

        # log #
        if SysMgr.loggingOpsEnable:
            SysMgr.printLog(log)

        # color #
        log = UtilMgr.convColor(
            log, "BOLD", force=False if SysMgr.remoteRun else True
        )
        try:
            log = prefix + log
        except SystemExit:
            sys.exit(0)
        except:
            log = "%s%s" % (prefix, log)

        # write to logger #
        if SysMgr.stdlog:
            SysMgr.stdlog.write(log)

        # write to stdout #
        if suffix:
            try:
                print(log)
            except SystemExit:
                sys.exit(0)
            except:
                return
        else:
            try:
                sys.stdout.write(log)
                sys.stdout.flush()
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr("failed to flush stdout", reason=True)

    @staticmethod
    def printGood(line):
        if not SysMgr.logEnable:
            return

        proc = SysMgr.getProcInfo()

        log = "%s%s%s" % ("[INFO] ", proc, line)
        log = "\n%s" % (
            UtilMgr.convColor(
                log, "OKGREEN", force=False if SysMgr.remoteRun else True
            )
        )

        # write to logger #
        if SysMgr.stdlog:
            SysMgr.stdlog.write(log)

        print(log)

    @staticmethod
    def printLine(line):
        if not SysMgr.logEnable:
            return

        proc = SysMgr.getProcInfo()

        log = "%s%s" % (proc, line)
        log = "\n%s" % (
            UtilMgr.convColor(
                log, "UNDERLINE", force=False if SysMgr.remoteRun else True
            )
        )

        # write to logger #
        if SysMgr.stdlog:
            SysMgr.stdlog.write(log)

        print(log)

    @staticmethod
    def printStat(line):
        if not SysMgr.logEnable:
            return

        proc = SysMgr.getProcInfo()

        log = "%s%s%s" % ("[STEP] ", proc, line)

        # log #
        if SysMgr.loggingOpsEnable:
            SysMgr.printLog(log)

        log = "\n%s" % (
            UtilMgr.convColor(
                log, "SPECIAL", force=False if SysMgr.remoteRun else True
            )
        )

        # write to logger #
        if SysMgr.stdlog:
            SysMgr.stdlog.write(log)

        print(log)

    @staticmethod
    def isValidRequest(request):
        try:
            if request.startswith("EVENT_") or TaskAnalyzer.requestType.index(
                request
            ):
                pass
            else:
                raise Exception("wrong request")

            return True
        except SystemExit:
            sys.exit(0)
        except:
            return False

    @staticmethod
    def printOpenErr(path, reason=True):
        SysMgr.printErr("failed to open '%s'" % os.path.realpath(path), reason)

    @staticmethod
    def printOpenWarn(path, always=False, reason=True):
        SysMgr.printWarn(
            "failed to open '%s'" % os.path.realpath(path), always, reason
        )

    @staticmethod
    def addOption(option):
        if not SysMgr.optionList:
            SysMgr.parseOption()

        if not isinstance(SysMgr.optionList, list):
            return

        if not option in SysMgr.optionList:
            SysMgr.optionList.append(option)

    @staticmethod
    def parseOption(option=None):
        if not "ISMAIN" in os.environ:
            return

        if not option and SysMgr.optionList:
            return

        # parse options #
        parsedOpt = []
        prevIdx = 0

        # choose option #
        if option:
            optList = [sys.argv[1]] + option
        else:
            optList = sys.argv[1:]

        # parse option string #
        for idx, opt in enumerate(optList):
            if opt.startswith("-"):
                parsedOpt.append(" ".join(optList[prevIdx:idx])[1:])
                prevIdx = idx
        parsedOpt.append(" ".join(optList[prevIdx:])[1:])

        # save parsed option #
        SysMgr.optionList = parsedOpt[1:]

        # check redundant option #
        usedOpt = {}
        for opt in SysMgr.optionList:
            try:
                if not opt[0] in usedOpt or opt[0] == "-":
                    usedOpt[opt[0]] = True
                    continue
            except:
                continue

            SysMgr.printErr(
                "wrong -%s option because of redundant use" % opt[0]
            )
            sys.exit(-1)

    @staticmethod
    def findOption(option):
        if not "ISMAIN" in os.environ:
            return

        if len(sys.argv) <= 2 and not SysMgr.optionList:
            return False

        SysMgr.parseOption()

        for item in SysMgr.optionList:
            if item == "":
                pass
            elif item[0] == option:
                return True

        return False

    @staticmethod
    def getOption(option):
        if not "ISMAIN" in os.environ:
            return

        if len(sys.argv) <= 2 and not SysMgr.optionList:
            return None

        SysMgr.parseOption()

        for item in SysMgr.optionList:
            if item == "":
                pass
            elif item[0] == option and len(item[1:]) > 0:
                return item[1:].strip()

        return None

    @staticmethod
    def parseRuntimeOption(value):
        SysMgr.countEnable = True
        convNum = UtilMgr.convNum
        convTime = UtilMgr.convUnit2Time

        # split params #
        if value:
            repeatParams = UtilMgr.cleanItem(value.split(":"), False)

        # check interval value by other option #
        if SysMgr.getOption("i"):
            applyInterval = False
        else:
            applyInterval = True

        if not value:
            if applyInterval:
                SysMgr.intervalEnable = 1
            SysMgr.repeatCnt = 1
            repeatParams = None
        elif len(repeatParams) in (2, 3):
            try:
                # get interval #
                interval = SysMgr.getOption("i")
                if interval:
                    interval = long(interval)
                else:
                    interval = convTime(repeatParams[0])

                if applyInterval:
                    SysMgr.intervalEnable = interval
                SysMgr.repeatInterval = interval

                # get count #
                cnt = repeatParams[1]
                if not cnt:
                    SysMgr.repeatCnt = sys.maxsize
                elif cnt.isdigit():
                    SysMgr.repeatCnt = long(cnt)
                else:
                    SysMgr.repeatCnt = long(convTime(cnt) / interval)
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr(
                    (
                        "wrong value for runtime option because %s, "
                        "input integer values"
                    )
                    % SysMgr.getErrMsg()
                )
                sys.exit(-1)
        elif len(repeatParams) == 1:
            try:
                interval = long(convTime(repeatParams[0]))

                # top mode #
                if SysMgr.isTopMode():
                    ival = SysMgr.getOption("i")
                    if ival:
                        ival = long(ival)
                        interval = long(interval / ival)
                        SysMgr.repeatCnt = interval
                        SysMgr.repeatInterval = interval
                        if applyInterval:
                            SysMgr.intervalEnable = ival
                    else:
                        SysMgr.repeatCnt = interval
                        SysMgr.repeatInterval = interval
                        if applyInterval:
                            SysMgr.intervalEnable = 1
                # record mode #
                else:
                    SysMgr.repeatCnt = 1
                    SysMgr.repeatInterval = interval
                    if applyInterval:
                        SysMgr.intervalEnable = interval
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr(
                    (
                        "wrong value for runtime option because %s, "
                        "input integer values"
                    )
                    % SysMgr.getErrMsg()
                )
                sys.exit(-1)
        else:
            SysMgr.printErr(
                (
                    "wrong value for runtime option because %s, "
                    "input in the format INTERVAL:REPEAT"
                )
                % SysMgr.getErrMsg()
            )
            sys.exit(-1)

        # check variables #
        if (
            not SysMgr.intervalEnable
            or SysMgr.intervalEnable < 1
            or SysMgr.repeatCnt < 1
        ):
            SysMgr.printErr(
                "wrong value for runtime option, input values bigger than 0"
            )
            sys.exit(-1)

        # change too big interval to prevent overflow #
        try:
            # just use maximum value for 32-bit system #
            if SysMgr.intervalEnable == sys.maxsize:
                SysMgr.intervalEnable = SysMgr.maxSize
            if SysMgr.repeatInterval == sys.maxsize:
                SysMgr.repeatInterval = SysMgr.maxSize
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                (
                    "wrong value for interval option because %s, "
                    "input integer value"
                )
                % SysMgr.getErrMsg()
            )
            sys.exit(-1)

        # get termination flag #
        if SysMgr.checkMode("req"):
            pass
        elif repeatParams and len(repeatParams) == 3:
            SysMgr.termFlag = False
            SysMgr.printInfo(
                "run every %s sec %s time"
                % (convNum(SysMgr.intervalEnable), convNum(SysMgr.repeatCnt))
            )
        else:
            interval = SysMgr.intervalEnable
            repeat = SysMgr.repeatCnt
            totalSec = convNum(interval)
            totalCnt = convNum(repeat)
            totalTime = convNum(long(interval * repeat))
            SysMgr.printInfo(
                "run only %s times in %s sec for a total of %s sec"
                % (totalCnt, totalSec, totalTime)
            )

    @staticmethod
    def reloadFileBuffer(path=None, retFd=False, fd=None):
        # pylint: disable=no-member
        SysMgr.procBuffer = []

        # load raw data from the file #
        try:
            # get path #
            if path or fd:
                pass
            elif SysMgr.printFd:
                path = SysMgr.printFd.name

                # close previous print fd #
                try:
                    SysMgr.printFd.close()
                    SysMgr.printFd = None
                except:
                    pass

                SysMgr.outPath = SysMgr.origOutPath

                # backup a exist output file #
                path = SysMgr.backupFile(path)
            else:
                SysMgr.printErr(
                    "failed to reload monitoring data "
                    "because there is no target file"
                )
                return []

            # open file #
            if not fd:
                fd = SysMgr.getCompFd(path)
                if not fd:
                    fd = open(path, "r")

            # check fd option #
            if retFd:
                return fd

            # load data #
            buf = fd.read()

            # decode data #
            try:
                buf = buf.decode()
            except SystemExit:
                sys.exit(0)
            except:
                pass
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printOpenErr(path)
            return None if retFd else []

        # split each interval stats #
        try:
            buf = buf.replace("\n\n", "NEWSTAT\n\n")
            if "CLEANCHAR" in SysMgr.environList:
                buf = buf.replace("[Top Info] ", "NEWSTAT[Top Info] ")

            SysMgr.procBuffer = buf.split("NEWSTAT")

            if not path:
                SysMgr.printFd.seek(0, 0)
                SysMgr.printFd.truncate()
        except SystemExit:
            sys.exit(0)
        except:
            pass

        return SysMgr.procBuffer

    @staticmethod
    def addEnvironVar(name, value="SET"):
        SysMgr.environList.setdefault(name, [])
        if isinstance(value, list):
            SysMgr.environList[name] += value
        elif SysMgr.environList[name] and value == "SET":
            pass
        else:
            SysMgr.environList[name].append(value)

    @staticmethod
    def checkLogCond(log, cond, refilter=False):
        try:
            # check condition #
            if not cond:
                return
            elif refilter:
                if not all([re.match(c, log) for c in cond]):
                    return
            elif not UtilMgr.isValidStr(log, cond):
                return
            elif "EVENT_CMD_NOTIFY:log_" in log:
                return

            SysMgr.printWarn(
                "detected '%s' in %s" % (", ".join(cond), [log]), True
            )

            # write log to kernel #
            LogMgr.doLogKmsg(msg=UtilMgr.removeColor(log))

            # execute watch commands #
            if "WATCHLOGCMD" in SysMgr.environList:
                args = SysMgr.environList["WATCHLOGCMD"]
                SysMgr.executeCommand(args)

            # check exit condition #
            if "WATCHLOGEXIT" in SysMgr.environList:
                os.kill(SysMgr.pid, signal.SIGINT)
                signal.pause()
        except SystemExit:
            sys.exit(0)
        except:
            pass

    @staticmethod
    def checkCgroup(manList=()):
        if not manList:
            manList = ("cpu", "cpuacct", "memory", "blkio")
        elif UtilMgr.isString(manList):
            manList = set(
                [
                    manList,
                ]
            )

        cgroupInfo = SysMgr.printCgroupList(json=True, ret=True)
        subList = list(set(manList) - set(list(cgroupInfo)))
        if subList:
            SysMgr.printWarn(
                "no subsystem support for [%s]" % ", ".join(subList), True
            )
            return False

        ret = True
        for sub, values in cgroupInfo.items():
            if sub in subList or not sub in manList:
                continue
            elif not values["mount"]:
                SysMgr.printWarn("no mount subsystem for [%s]" % sub, True)
                ret = False
        return ret

    @staticmethod
    def applyCgroupVars(varList=[]):
        try:
            if not varList:
                varList = SysMgr.environList

            if not "APPLYCG" in varList:
                return

            cmds = []
            for cmd in varList["APPLYCG"]:
                for item in (
                    "CREATE",
                    "ADD",
                    "MOVE",
                    "REMOVE",
                    "DELETE",
                    "READ",
                    "WRITE",
                    "LIST",
                ):
                    if cmd.upper().startswith(item + ":"):
                        cmds.append(cmd)

            if cmds:
                SysMgr.doCgroup(cmds)
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to apply cgroup commands", True)
            sys.exit(-1)

    @staticmethod
    def applyLimitVars(varList=[]):
        try:
            if not varList:
                varList = SysMgr.environList

            allPids = []

            for item in (
                "LIMITCPU",
                "LIMITCPUSET",
                "LIMITMEM",
                "LIMITREAD",
                "LIMITWRITE",
                "LIMITPID",
            ):
                if not item in varList:
                    continue

                # set values #
                if item == "LIMITCPU":
                    res = resName = "cpu"
                    default = ["cfs_quota_us", "rt_runtime_us"]
                    mandatory = "cfs_quota_us"
                    func = SysMgr.limitCpu
                elif item == "LIMITCPUSET":
                    res = resName = "cpuset"
                    default = mandatory = "cpus"
                    func = SysMgr.limitCpuset
                elif item == "LIMITMEM":
                    res = resName = "memory"
                    default = mandatory = "limit_in_bytes"
                    func = SysMgr.limitMemory
                elif item == "LIMITREAD":
                    res = "blkio"
                    resName = "I/O read"
                    default = "read_bps_device"
                    mandatory = "throttle.read_bps_device"
                    func = SysMgr.limitBlock
                elif item == "LIMITWRITE":
                    res = "blkio"
                    resName = "I/O write"
                    default = "write_bps_device"
                    mandatory = "throttle.write_bps_device"
                    func = SysMgr.limitBlock
                elif item == "LIMITPID":
                    res = resName = "pids"
                    default = "max"
                    mandatory = None
                    func = SysMgr.limitPid
                else:
                    continue

                # check mandatory subsystem #
                if not SysMgr.checkCgroup(res):
                    sys.exit(0)

                # check mandatory knob #
                if mandatory:
                    subPath = SysMgr.getCgroup(res, "/")
                    knobPath = os.path.join(subPath, res + "." + mandatory)
                    knobPath2 = os.path.join(subPath, mandatory)
                    if not os.path.exists(knobPath) and not os.path.exists(
                        knobPath2
                    ):
                        SysMgr.printErr(
                            "no both '%s' and '%s' for %s subsystem"
                            % (knobPath, knobPath2, res)
                        )
                        sys.exit(0)

                for limits in varList[item]:
                    pids = []
                    attrs = []

                    # get target info #
                    limits = limits.rsplit("@", 1)
                    if len(limits) == 1:
                        limits = limits[0]
                        targets = []
                    else:
                        limits, targets = limits

                    # convert & to , #
                    limits = limits.replace("&", ",")

                    for limit in limits.split("+"):
                        # get limit info #
                        values = limit.split(":")
                        if len(values) == 1:
                            name = default
                            val = values[0]
                            if item != "LIMITCPUSET":
                                val = UtilMgr.convUnit2Size(val)
                        elif len(values) == 2:
                            name, val = values
                            if item != "LIMITCPUSET":
                                val = UtilMgr.convUnit2Size(val)
                        else:
                            SysMgr.printErr(
                                "failed to parse '%s' to limit %s"
                                % (item, resName)
                            )
                            sys.exit(-1)

                        # convert unit #
                        if item == "LIMITCPU":
                            # change %-per-sec to ms #
                            if len(values) == 1:
                                unitSize = "%s%%" % val
                                val = long(val) * 10000
                            else:
                                unitSize = UtilMgr.convTime2Unit(val / 1000000)
                        elif item == "LIMITCPUSET":
                            unitSize = val
                        else:
                            unitSize = UtilMgr.convSize2Unit(val)

                        # set attributes #
                        if isinstance(name, list):
                            for subname in name:
                                attrs.append([subname, val])
                        else:
                            attrs.append([name, val])

                        # print info #
                        SysMgr.printInfo(
                            "limit %s(%s) to '%s'"
                            % (
                                resName,
                                ", ".join(name)
                                if isinstance(name, list)
                                else name,
                                unitSize,
                            )
                        )

                        # get limit pids #
                        if targets:
                            pids += SysMgr.getTids(
                                targets,
                                isThread=not SysMgr.processEnable,
                                sibling=SysMgr.groupProcEnable,
                            )
                        else:
                            pids += [str(SysMgr.pid)]

                    # remove redundant tasks #
                    pids = list(set(pids))

                    allPids += pids

                    # set resource of specific tasks #
                    if "EACHTASK" in varList:
                        for pid in pids:
                            func([pid], attrs)
                    else:
                        func(pids, attrs)

            return list(set(allPids))
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to apply %s limit values" % resName, True)
            sys.exit(-1)

    @staticmethod
    def runTaskMonitor(pids=[], wait=True, block=False, addOpt=[]):
        if not "TASKMON" in SysMgr.environList:
            return
        elif not pids:
            return

        # get interval #
        interval = UtilMgr.getEnvironNum("TASKMON", False, 1, False, True)

        # apply filter #
        if "TASKFILTER" in SysMgr.environList:
            pids += SysMgr.environList["TASKFILTER"]

        # apply additional options #
        if "TASKMONOPT" in SysMgr.environList:
            environOption = ""

            # append monitoring options #
            monOption = "|".join(SysMgr.environList["TASKMONOPT"]).split("|")
        else:
            monOption = []

            # set environ options #
            environOption = "-qFASTINIT,NOSPECIALTASK"

        # set enable options #
        enableOption = "-eb"
        startOption = SysMgr.getOption("e")
        if startOption:
            enableOption += startOption

        pid = SysMgr.launchGuider(
            [
                "top",
                "-g%s" % ",".join(pids),
                "-i%s" % interval,
                "-a" if SysMgr.showAll else "",
                "-P" if SysMgr.groupProcEnable else "",
                enableOption,
                environOption,
                "-f",
                "-Q",
            ]
            + addOpt
            + monOption,
            pipe=False,
            stderr=True,
            block=block,
        )

        # wait for termination of child #
        if wait:
            SysMgr.waitEvent()

        return pid

    @staticmethod
    def applyIgnoreSignals():
        if not "IGNORESIGNAL" in SysMgr.environList:
            return

        if Debugger.ignoreSignals:
            for signum in Debugger.ignoreSignals:
                signal.signal(signum, signal.SIG_IGN)
            return

        for sig in SysMgr.environList["IGNORESIGNAL"]:
            try:
                usig = sig.upper()
                if UtilMgr.isNumber(sig):
                    signum = long(sig)
                elif usig in ConfigMgr.SIG_LIST:
                    signum = ConfigMgr.SIG_LIST.index(usig)
                elif "SIG" + usig in ConfigMgr.SIG_LIST:
                    signum = ConfigMgr.SIG_LIST.index("SIG" + usig)
                else:
                    raise Exception("no signal")

                # ignore signal #
                signal.signal(signum, signal.SIG_IGN)

                # regisetr ignore list #
                Debugger.ignoreSignals[signum] = 0

                SysMgr.printInfo("blocked %s" % ConfigMgr.SIG_LIST[signum])
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr("failed to block %s signal" % sig, True)
                sys.exit(-1)

    @staticmethod
    def applyEnvironVars():
        def _applyVar(name, tobj, tvar):
            if not name in SysMgr.environList:
                return

            var = SysMgr.environList[name][0]
            if var.strip().isdigit():
                if hasattr(tobj, tvar):
                    setattr(tobj, tvar, long(var))
                else:
                    SysMgr.printErr("no %s in %s" % (tvar, tobj))
                    sys.exit(-1)
            else:
                SysMgr.printErr("wrong value %s for %s" % (var, name))
                sys.exit(-1)

        # set maximum top rank #
        SysMgr.nrTopRank = UtilMgr.getEnvironNum(
            "NRTOPRANK", False, SysMgr.nrTopRank, False, isInt=True
        )

        # set maximum proc mem report #
        SysMgr.nrProcMemReport = UtilMgr.getEnvironNum(
            "NRPROCMEM", False, SysMgr.nrProcMemReport, False, isInt=True
        )

        # set kernel log size #
        SysMgr.kmsgLine = UtilMgr.getEnvironNum(
            "NRKLOG", False, SysMgr.kmsgLine, False, isInt=True
        )

        # set maximum error reason length #
        SysMgr.maxErrStrLen = UtilMgr.getEnvironNum(
            "MAXERRLEN", False, SysMgr.maxErrStrLen, False, isInt=True
        )

        # set fixed tasks #
        if "FIXTASK" in SysMgr.environList:
            for item in SysMgr.environList["FIXTASK"]:
                SysMgr.fixedTaskList += item.split("|")

        def _setLogger(options):
            SysMgr.dltEnable = "d" in options
            SysMgr.kmsgEnable = "k" in options
            SysMgr.journalEnable = "j" in options
            SysMgr.syslogEnable = "s" in options
            SysMgr.andlogEnable = "a" in options
            if any(
                [
                    SysMgr.dltEnable,
                    SysMgr.kmsgEnable,
                    SysMgr.journalEnable,
                    SysMgr.syslogEnable,
                    SysMgr.andlogEnable,
                ]
            ):
                SysMgr.colorEnable = False
                return True
            else:
                return False

        # set operation logging #
        if "OPSLOG" in SysMgr.environList:
            options = SysMgr.environList["OPSLOG"][0]
            SysMgr.loggingOpsEnable = _setLogger(options)

        # set standard logging #
        if "STDLOG" in SysMgr.environList:
            options = SysMgr.environList["STDLOG"][0]
            SysMgr.loggingEnable = _setLogger(options)

        # ignore size unit converting #
        if "NOCONVSIZE2UNIT" in SysMgr.environList:
            UtilMgr.convSize2Unit = UtilMgr.convSize2UnitIgn

        # ignore number converting #
        if "NOCONVNUM" in SysMgr.environList:
            UtilMgr.convNum = UtilMgr.convNumIgn

        convSize2Unit = UtilMgr.convSize2Unit
        convUnit2Size = UtilMgr.convUnit2Size

        # get limited dir info #
        if "LIMITDIR" in SysMgr.environList:
            for dirInfo in SysMgr.environList["LIMITDIR"]:
                # get target dir and limit size #
                try:
                    path, size = UtilMgr.cleanItem(
                        dirInfo.split(":", 1), union=False
                    )
                    if not path or not size:
                        continue
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr(
                        "failed to get target dir info to be limited", True
                    )
                    continue

                # get limit size #
                try:
                    size = convUnit2Size(size)
                    if not size:
                        continue
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr(
                        "failed to convert reclaim size '%s'" % size, True
                    )
                    sys.exit(-1)

                # register limit dir info #
                SysMgr.limitDirList[path] = size

                SysMgr.printInfo(
                    "limit the size of '%s' to [%s]"
                    % (path, convSize2Unit(size))
                )
        # get limited report dir info #
        if "LIMITREPDIR" in SysMgr.environList:
            try:
                size = convUnit2Size(SysMgr.environList["LIMITREPDIR"][0])
                SysMgr.limitRepDirSize = size
                SysMgr.printInfo(
                    "limit the size of the report directory to [%s]"
                    % convSize2Unit(size)
                )
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr(
                    "failed to get the limit size of report directory", True
                )
                sys.exit(-1)

        # get limited file count info for dirs #
        if "LIMITDIRCNT" in SysMgr.environList:
            for dirInfo in SysMgr.environList["LIMITDIRCNT"]:
                # get target dir and limit size #
                try:
                    path, cnt = UtilMgr.cleanItem(
                        dirInfo.split(":", 1), union=False
                    )
                    if not path or not cnt:
                        continue
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr(
                        "failed to get target dir count info to be limited",
                        True,
                    )
                    continue

                # get limit count #
                try:
                    cnt = long(cnt)
                    if not cnt:
                        continue
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr(
                        "failed to convert limit count '%s'" % cnt, True
                    )
                    sys.exit(-1)

                # register limited file count info in dir #
                SysMgr.limitDirCntList[path] = cnt

                SysMgr.printInfo(
                    "limit the count of files in '%s' to [%s]"
                    % (path, convSize2Unit(cnt))
                )
        # register exit commands #
        if "EXITCMD" in SysMgr.environList:
            SysMgr.addExitFunc(
                SysMgr.executeCommand, [SysMgr.environList["EXITCMD"]]
            )

            SysMgr.printInfo(
                "registered exit commands '%s'"
                % ", ".join(SysMgr.environList["EXITCMD"])
            )

        # ignore signals #
        SysMgr.applyIgnoreSignals()

        # limit resources #
        SysMgr.applyLimitVars()

        # apply cgroup #
        SysMgr.applyCgroupVars()

        # define threshold list #
        varList = [
            ("STARTCONDTIME", "startUptime"),
            ("STARTCONDCPUMORE", "startCondCpuMore"),
            ("STARTCONDCPULESS", "startCondCpuLess"),
            ("STARTCONDMEMMORE", "startCondMemMore"),
            ("STARTCONDMEMLESS", "startCondMemLess"),
            ("EXITCONDTIME", "deadlineUptime"),
            ("EXITCONDCPUMORE", "exitCondCpuMore"),
            ("EXITCONDCPULESS", "exitCondCpuLess"),
            ("EXITCONDMEMMORE", "exitCondMemMore"),
            ("EXITCONDMEMLESS", "exitCondMemLess"),
        ]

        # apply items #
        for item in varList:
            _applyVar(item[0], SysMgr, item[1])

        # wait for bigger CPU condition #
        if SysMgr.startCondCpuMore:
            SysMgr.waitForResource(
                cpuRes=SysMgr.startCondCpuMore, cpuCond="MORE"
            )

        # wait for lesser CPU condition #
        if SysMgr.startCondCpuLess:
            SysMgr.waitForResource(
                cpuRes=SysMgr.startCondCpuLess, cpuCond="LESS"
            )

        # wait for bigger memory condition #
        if SysMgr.startCondMemMore:
            SysMgr.waitForResource(
                memRes=SysMgr.startCondMemMore, memCond="MORE"
            )

        # wait for lesser memory condition #
        if SysMgr.startCondMemLess:
            SysMgr.waitForResource(
                memRes=SysMgr.startCondMemLess, memCond="LESS"
            )

        # print environment variables #
        if "PRINTENV" in SysMgr.environList:
            SysMgr.printWarn(
                "The following environment variables will be used\n%s"
                % UtilMgr.convDict2Str(dict(os.environ), pretty=True),
                True,
            )

        # enable all namespace info #
        SysMgr.nsAllEnable = "PRINTNS" in SysMgr.environList

        # set specific namespace type #
        if "NSTYPE" in SysMgr.environList:
            SysMgr.nsType = SysMgr.environList["NSTYPE"][0].lower()

        # mount all cgroup subsystems #
        if "MOUNTCG" in SysMgr.environList:
            SysMgr.mountCgroups()

        # umount all cgroup subsystems #
        if "UMOUNTCG" in SysMgr.environList:
            SysMgr.umountCgroups()

        # save vmflags #
        if "VMFLAG" in SysMgr.environList:
            flags = SysMgr.environList["VMFLAG"][0].split("+")
            SysMgr.vmflagList = SysMgr.getVmFlags(flags)

        # split summaryfilter #
        if "SUMMARYFILTER" in SysMgr.environList:
            filters = []
            for item in SysMgr.environList["SUMMARYFILTER"]:
                filters += item.split("+")
            SysMgr.environList["SUMMARYFILTER"] = filters

        # enable DLT time #
        SysMgr.dltTimeEnable = "DLTTIME" in SysMgr.environList

    @staticmethod
    def checkOptVal(option, value):
        if not value:
            SysMgr.printErr("no input value with -%s option" % option)
            sys.exit(-1)

    @staticmethod
    def applySaveOption(value=None):
        # apply default path #
        if not value:
            value = "."

        # change output path #
        try:
            if not SysMgr.isWritable(value):
                raise Exception("not writable")

            if os.path.isdir(value):
                SysMgr.outputFile = "%s/guider.dat" % value
            else:
                SysMgr.outputFile = value
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("wrong path '%s'" % os.path.realpath(value), True)
            sys.exit(-1)

        # remove double slashes #
        SysMgr.outputFile = os.path.normpath(SysMgr.outputFile)

        # support no-report record mode #
        if SysMgr.checkMode("filerec") or SysMgr.checkMode("genrec"):
            if SysMgr.outputFile.endswith(".dat"):
                SysMgr.outPath = (
                    "%s.out" % os.path.splitext(SysMgr.outputFile)[0]
                )
            else:
                SysMgr.outPath = SysMgr.outputFile

    @staticmethod
    def removeOptionArgs():
        if len(sys.argv) < 3:
            return

        # find first option args #
        firstOptIdx = None
        for idx, item in enumerate(sys.argv):
            if item.startswith("-"):
                firstOptIdx = idx
                break

        # update args #
        if firstOptIdx:
            sys.argv = sys.argv[:firstOptIdx]

    @staticmethod
    def parseAnalOption(option=None):
        if not "ISMAIN" in os.environ:
            return

        # check call history #
        if not option and SysMgr.parsedAnalOption:
            return
        else:
            SysMgr.parsedAnalOption = True

        # set default processor option #
        if SysMgr.isTopMode():
            if SysMgr.findOption("a") or SysMgr.isDrawMode():
                SysMgr.cpuEnable = True
                SysMgr.gpuEnable = True
            else:
                SysMgr.cpuEnable = False

        # check argument count #
        if option:
            optionList = option.split()
            SysMgr.parseOption(optionList)
        elif len(sys.argv) <= 2:
            return
        else:
            optionList = None

        for item in SysMgr.optionList:
            if not item:
                continue

            option = item[0]
            value = item[1:].strip()

            if option == "i":
                # set default interval #
                if not value:
                    SysMgr.intervalEnable = 1
                    continue

                errStr = "wrong value '%s' for interval, " % value

                try:
                    SysMgr.intervalEnable = long(value)

                    if SysMgr.intervalEnable <= 0:
                        SysMgr.printErr(
                            "%sinput number bigger than 0" % errStr
                        )
                        sys.exit(0)
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr(
                        "%sinput number in integer format" % errStr
                    )
                    sys.exit(-1)

            elif SysMgr.isCommonOption(option):
                SysMgr.parseCommonOption(option, value)

            elif option == "I":
                SysMgr.inputParam = value.strip()

            elif option == "w":
                SysMgr.checkOptVal(option, value)
                SysMgr.rcmdList = SysMgr.parseCustomRecordCmd(value)

            elif option == "a":
                SysMgr.printOptionWarn(option, value)
                SysMgr.showAll = True

            elif option == "P":
                SysMgr.printOptionWarn(option, value)
                SysMgr.groupProcEnable = True

            elif option == "p":
                SysMgr.checkOptVal(option, value)

                SysMgr.preemptGroup = UtilMgr.cleanItem(value.split(","))

            elif option == "Y":
                SysMgr.checkOptVal(option, value)
                if SysMgr.prio is None:
                    SysMgr.applyPriority(value)

            elif option == "J":
                SysMgr.printOptionWarn(option, value)
                SysMgr.jsonEnable = True

            elif option == "k":
                SysMgr.checkOptVal(option, value)
                if not SysMgr.isKillMode():
                    SysMgr.applyKillVal(value)

            elif option == "d":
                SysMgr.checkOptVal(option, value)
                options = value.replace(" ", "")

                if "b" in options:
                    SysMgr.bufferLossEnable = True

                if "p" in options:
                    SysMgr.printEnable = False

                if "u" in options:
                    SysMgr.userEnable = False

                if "c" in options:
                    SysMgr.cpuEnable = False

                if "C" in options:
                    SysMgr.cloneEnable = False

                if "B" in options:
                    SysMgr.barGraphEnable = False

                if "O" in options:
                    SysMgr.colorEnable = False

                if "E" in options:
                    SysMgr.execEnable = False

                if "x" in options:
                    SysMgr.eventHandleEnable = False

                if "t" in options:
                    SysMgr.truncEnable = False

                if "T" in options:
                    SysMgr.taskEnable = False

                if "e" in options:
                    SysMgr.encodeEnable = False

                if "a" in options:
                    SysMgr.freeMemEnable = True

                if "G" in options:
                    SysMgr.gpuEnable = False

                if "A" in options:
                    SysMgr.cpuAvgEnable = False

                if "L" in options:
                    SysMgr.logEnable = False

                if "g" in options:
                    SysMgr.generalEnable = False

                if "d" in options:
                    SysMgr.demangleEnable = False

                if "D" in options:
                    SysMgr.dwarfEnable = False

                if "l" in options:
                    SysMgr.latEnable = False

                if "P" in options:
                    SysMgr.psiEnable = False

                if "m" in options:
                    SysMgr.gpuMemEnable = False

            elif option == "c":
                itemList = UtilMgr.splitString(value)

                # set union option #
                if SysMgr.isTraceMode() or SysMgr.checkMode("cli"):
                    union = False
                else:
                    union = True

                SysMgr.customCmd = UtilMgr.cleanItem(itemList, union=union)

            elif option == "g":
                itemList = UtilMgr.splitString(value)
                SysMgr.filterGroup = UtilMgr.cleanItem(itemList)

            elif "F" in option:
                SysMgr.checkOptVal(option, value)

                SysMgr.drawFormat = value.strip()

            elif option == "e":
                options = value.replace(" ", "")

                if "g" in options:
                    SysMgr.graphEnable = True

                if "D" in options:
                    SysMgr.dwarfEnable = True

                if "l" in options:
                    SysMgr.thresholdEnable = True

                if "t" in options:
                    SysMgr.processEnable = False

                if "H" in options:
                    if not SysMgr.checkMode("ttop"):
                        SysMgr.printErr(
                            "sched option is supported only in thread mode"
                        )
                        sys.exit(-1)
                    SysMgr.schedEnable = True

                if "Y" in options:
                    SysMgr.checkRootPerm()
                    SysMgr.delayEnable = True

                # no more options except for top mode #
                if not SysMgr.isTopMode():
                    continue

                if "T" in options:
                    SysMgr.totalEnable = True

                if "c" in options:
                    SysMgr.cpuEnable = True

                if "p" in options:
                    SysMgr.pipeEnable = True

                if "P" in options:
                    SysMgr.perfEnable = True
                    if SysMgr.findOption("g"):
                        SysMgr.perfGroupEnable = True

                if "i" in options:
                    SysMgr.irqEnable = True

                if "b" in options:
                    if SysMgr.checkDiskTopCond():
                        SysMgr.blockEnable = True
                    else:
                        sys.exit(-1)

                if "B" in options:
                    SysMgr.barGraphEnable = True

                if "s" in options:
                    if SysMgr.checkStackTopCond():
                        SysMgr.stackEnable = True
                    else:
                        sys.exit(-1)

                if "S" in options:
                    SysMgr.checkRootPerm()
                    SysMgr.pssEnable = True
                    if not SysMgr.findOption("S"):
                        SysMgr.sort = "m"

                if "u" in options:
                    SysMgr.checkRootPerm()
                    SysMgr.ussEnable = True
                    if not SysMgr.findOption("S"):
                        SysMgr.sort = "m"

                if "L" in options:
                    SysMgr.cmdlineEnable = True

                if "X" in options:
                    SysMgr.exeEnable = True

                if "U" in options:
                    SysMgr.dbusUnitEnable = True

                # check last field #
                if "O" in options:
                    TaskAnalyzer.setLastField("iosched")
                elif "a" in options:
                    TaskAnalyzer.setLastField("affinity")
                elif "o" in options:
                    TaskAnalyzer.setLastField("oom")
                elif "W" in options:
                    TaskAnalyzer.setLastField("wchan")
                elif "h" in options:
                    TaskAnalyzer.setLastField("signal")
                elif "A" in options:
                    TaskAnalyzer.setLastField("attr")
                elif "K" in options:
                    TaskAnalyzer.setLastField("cgroup")

                if "f" in options:
                    SysMgr.floatEnable = True

                    # set default interval to 3 for accuracy #
                    if not SysMgr.findOption("i") and not SysMgr.findOption(
                        "R"
                    ):
                        SysMgr.intervalEnable = 3

                if "F" in options:
                    SysMgr.wfcEnable = True

                if "R" in options:
                    SysMgr.reportEnable = True
                    SysMgr.reportFileEnable = True

                if "e" in options:
                    SysMgr.encodeEnable = True

                if "k" in options:
                    SysMgr.peakEnable = True

                if "m" in options:
                    if SysMgr.checkMemTopCond():
                        SysMgr.memEnable = True
                    else:
                        sys.exit(-1)

                if "M" in options:
                    SysMgr.minStatEnable = True

                if "w" in options:
                    if SysMgr.checkWssTopCond():
                        SysMgr.memEnable = True
                        SysMgr.wssEnable = True
                        if not SysMgr.findOption("S"):
                            SysMgr.sort = "m"
                    else:
                        sys.exit(-1)

                if "n" in options:
                    SysMgr.networkEnable = True

                if "N" in options:
                    SysMgr.nsEnable = True

                if "P" in options:
                    if SysMgr.checkPerfTopCond():
                        SysMgr.perfEnable = True
                        if SysMgr.findOption("g"):
                            SysMgr.perfGroupEnable = True
                    else:
                        sys.exit(-1)

                if "r" in options:
                    SysMgr.reportEnable = True

                if "I" in options:
                    SysMgr.reportEnable = True
                    SysMgr.elasticEnable = True

                if "d" in options:
                    SysMgr.diskEnable = True

                if "E" in options:
                    SysMgr.execEnable = True

                if "G" in options:
                    SysMgr.cgroupEnable = True

                if "C" in options:
                    SysMgr.compressEnable = True

                if "q" in options:
                    SysMgr.exitFlag = True

                if "x" in options:
                    SysMgr.fixTargetEnable = True

                if not SysMgr.isValidEnableOption(options):
                    SysMgr.printErr(
                        "unrecognized option -%s to enable" % options
                    )
                    sys.exit(-1)

            elif option == "O":
                SysMgr.checkOptVal(option, value)

                # split core values #
                perCoreList = UtilMgr.cleanItem(value.split(","))
                if not perCoreList:
                    SysMgr.printErr("no input value for filter" % option)
                    sys.exit(-1)

                perCoreList = UtilMgr.convxrange(perCoreList)

                # check value type #
                for item in perCoreList:
                    if not item.isdigit():
                        SysMgr.printErr(
                            (
                                "wrong value for core list, "
                                "input number in integer format"
                            )
                            % option
                        )
                        sys.exit(-1)

                SysMgr.printInfo(
                    "only specific cores [ %s ] are shown"
                    % ", ".join(perCoreList)
                )

                # convert items to number #
                SysMgr.perCoreList = list(map(long, perCoreList))

                if SysMgr.isDrawMode():
                    SysMgr.perCoreDrawList = SysMgr.perCoreList
                    SysMgr.perCoreList = []

            # ignore below options for function mode #
            elif SysMgr.isFuncMode():
                SysMgr.functionEnable = True

            elif option == "l":
                # BOUNDARY #
                if SysMgr.isDrawMode():
                    SysMgr.boundaryLine = UtilMgr.cleanItem(value.split(","))

                    try:
                        cval = sorted(list(map(long, SysMgr.boundaryLine)))

                        SysMgr.printInfo(
                            "set boundary line [%s]"
                            % ", ".join(SysMgr.boundaryLine)
                        )

                        SysMgr.boundaryLine = cval
                    except SystemExit:
                        sys.exit(0)
                    except:
                        SysMgr.printErr(
                            "failed to convert [%s] to number"
                            % ", ".join(SysMgr.boundaryLine),
                            True,
                        )
                        sys.exit(-1)
                # LOG #
                else:
                    options = value
                    SysMgr.dltEnable = "d" in options
                    SysMgr.kmsgEnable = "k" in options
                    SysMgr.journalEnable = "j" in options
                    SysMgr.syslogEnable = "s" in options
                    SysMgr.andlogEnable = "a" in options
                    if any(
                        [
                            SysMgr.dltEnable,
                            SysMgr.kmsgEnable,
                            SysMgr.journalEnable,
                            SysMgr.syslogEnable,
                            SysMgr.andlogEnable,
                        ]
                    ):
                        SysMgr.loggingEnable = True
                        SysMgr.colorEnable = False

            elif option == "r":
                if value.strip():
                    SysMgr.rootPath = value
                else:
                    SysMgr.recursionEnable = True

            elif option == "T":
                SysMgr.checkOptVal(option, value)

                if SysMgr.checkMode("convert"):
                    SysMgr.fontPath = value
                elif SysMgr.isDrawMode():
                    try:
                        SysMgr.nrTop = long(value)
                    except:
                        SysMgr.printErr(
                            (
                                "wrong value for the number of task, "
                                "input number in integer format"
                            )
                            % option
                        )
                        sys.exit(-1)
                # this value will be used in various mode #
                else:
                    try:
                        SysMgr.utilProc = long(value)
                    except:
                        pass

            elif option == "t" and not SysMgr.isRecordMode():
                SysMgr.parseSyscallOption(value)

            elif option == "m":
                try:
                    SysMgr.ttyEnable = True

                    if not value:
                        SysMgr.setTTYAuto()
                    else:
                        rows = cols = 0
                        term = value.split(":")

                        # get size #
                        term[0] = term[0].strip()
                        if term[0].isdigit():
                            rows = long(term[0])

                        if len(term) > 1:
                            term[1] = term[1].strip()
                            if term[1].isdigit():
                                cols = long(term[1])

                        # update system terminal #
                        if len(term) > 2 and term[2].upper() == "SYSTEM":
                            SysMgr.setTTY(rows, cols)
                        # update local terminal #
                        else:
                            if rows > 0:
                                SysMgr.ttyRows = rows
                            if cols > 0:
                                SysMgr.ttyCols = cols
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr(
                        (
                            "wrong value for screen size, "
                            "input number in COLS:ROWS format"
                        )
                        % option,
                        reason=True,
                    )
                    sys.exit(-1)

            elif option == "b" and not SysMgr.isRecordMode():
                SysMgr.checkOptVal(option, value)
                try:
                    if value.isdigit():
                        bsize = long(value) << 10
                    else:
                        bsize = UtilMgr.convUnit2Size(value)

                    if bsize >= 0:
                        SysMgr.bufferSize = bsize
                    else:
                        SysMgr.printErr(
                            (
                                "wrong value %s for buffer size, "
                                "input number bigger than 0"
                            )
                            % option
                        )
                        sys.exit(0)
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr(
                        (
                            "wrong value %s for buffer size, "
                            "input number in integer format"
                        )
                        % option
                    )
                    sys.exit(-1)

            elif option == "N":
                SysMgr.checkOptVal(option, value)
                networkList = UtilMgr.cleanItem(value.split(","))
                for item in networkList:
                    service, ip, port = NetworkMgr.parseAddr(item)
                    NetworkMgr.setRemoteNetwork(service, ip, port)

            elif option == "j":
                if not SysMgr.checkRepTopCond(value):
                    sys.exit(-1)

            elif option == "x":
                SysMgr.checkOptVal(option, value)
                service, ip, port = NetworkMgr.parseAddr(value)

                # check public IP #
                if "PUBLICIP" in SysMgr.environList:
                    ip = NetworkMgr.getPublicIp()

                ret = NetworkMgr.setServerNetwork(ip, port, verb=True)
                if not ret:
                    sys.exit(-1)

            elif option == "X":
                if not SysMgr.findOption("x"):
                    service, ip, port = NetworkMgr.parseAddr(value)
                    NetworkMgr.setServerNetwork(None, None)

                NetworkMgr.setRemoteServer(value)

            elif option == "S":
                if not SysMgr.setSortValue(value):
                    sys.exit(-1)

            # Ignore options #
            elif SysMgr.isValidOption(option):
                continue

            else:
                SysMgr.printErr(
                    "unrecognized option -%s for analysis" % option
                )
                sys.exit(-1)

    @staticmethod
    def printOptionWarn(option, value):
        if not value:
            return

        SysMgr.printWarn("the %s option does not require value" % option, True)

    @staticmethod
    def parseCommonOption(option, value):
        if value:
            value = value.strip()

        if option == "f":
            SysMgr.printOptionWarn(option, value)
            SysMgr.forceEnable = True

        elif option == "L":
            if SysMgr.isDrawMode():
                SysMgr.layout = value
            else:
                newPath = None

                # default file #
                if not value:
                    if not os.path.exists(SysMgr.cacheDirPath):
                        SysMgr.mkdirs(SysMgr.cacheDirPath)
                    newPath = os.path.join(SysMgr.cacheDirPath, "guider.log")
                # new file #
                elif not SysMgr.stdlog:
                    if os.path.isdir(value):
                        newPath = os.path.join(value, "guider.log")
                    else:
                        newPath = value

                # apply new logger path #
                if newPath:
                    SysMgr.printInfo(
                        "apply '%s'%s to log path from %s"
                        % (
                            newPath,
                            UtilMgr.getFileSizeStr(newPath),
                            UtilMgr.getTime(),
                        )
                    )
                    SysMgr.stdlog = LogMgr(newPath)

        elif option == "o":
            # apply default path #
            if value == "":
                value = "."

            # check writable access #
            if not SysMgr.isWritable(value):
                SysMgr.printErr(
                    "wrong path '%s' for output because of permission" % value
                )
                sys.exit(-1)

            SysMgr.origOutPath = SysMgr.outPath = os.path.normpath(value)

        elif option == "s":
            if not SysMgr.isKillMode():
                SysMgr.applySaveOption(value)

        elif option == "H":
            try:
                if not value:
                    SysMgr.funcDepth = 100
                else:
                    SysMgr.funcDepth = long(value)

                if SysMgr.funcDepth < 0:
                    raise Exception("wrong depth")
            except:
                SysMgr.printErr(
                    "wrong input value for depth, "
                    "input an unsigned integer value"
                )
                sys.exit(-1)

        elif option == "Q":
            SysMgr.printOptionWarn(option, value)
            SysMgr.setStream()

        elif option == "q":
            SysMgr.checkOptVal(option, value)

            # NOTE: variables are already parsed in SysMgr.initEnvironment() #

            # apply environ variables #
            SysMgr.applyEnvironVars()

        elif option == "R":
            # set maximum count #
            if not value:
                value = str(sys.maxsize)
            SysMgr.parseRuntimeOption(value)

        elif option == "z":
            SysMgr.checkOptVal(option, value)
            SysMgr.parseAffinityOption(
                UtilMgr.cleanItem(value.split(",")), launch=True
            )

        elif option == "A":
            SysMgr.checkOptVal(option, value)
            SysMgr.archOption = value
            SysMgr.setArch(value)

        elif option == "W":
            # uptime deadline #
            if SysMgr.startUptime > 0:
                start = SysMgr.startUptime
                SysMgr.printStat(
                    "wait until %s seconds for uptime"
                    % (UtilMgr.convNum(start))
                )
                # wait until the uptime #
                SysMgr.waitUptime(start)
            elif value:
                if not SysMgr.waitEnable:
                    SysMgr.waitEnable = UtilMgr.convUnit2Time(value)
                    SysMgr.printStat(
                        "wait %s more seconds"
                        % (UtilMgr.convNum(SysMgr.waitEnable))
                    )
            else:
                SysMgr.waitEnable = True

        elif option == "C":
            if not ConfigMgr.confData:
                if value:
                    SysMgr.confFileName = os.path.realpath(value)
                else:
                    SysMgr.confFileName = os.path.realpath(SysMgr.confFileName)

                # load configuration #
                ret = SysMgr.loadConfig(SysMgr.confFileName)
                if SysMgr.isDrawMode():
                    pass
                elif not ret:
                    sys.exit(-1)

                # launch commands #
                cmdList = SysMgr.getConfigItem("command")
                if (
                    cmdList
                    and "apply" in cmdList
                    and cmdList["apply"] == "true"
                ):
                    SysMgr.executeCommand(cmdList["list"])

        elif option == "E":
            SysMgr.cacheDirPath = value
            SysMgr.printInfo("use '%s' as the cache directory" % value)

        elif option == "G":
            SysMgr.checkOptVal(option, value)
            itemList = UtilMgr.splitString(value)
            SysMgr.ignoreItemList = UtilMgr.cleanItem(itemList)
            SysMgr.printInfo(
                "applied ignore keyword [ %s ]"
                % ", ".join(SysMgr.ignoreItemList)
            )

        elif option == "u":
            SysMgr.printOptionWarn(option, value)
            SysMgr.runBackgroundMode()

    @staticmethod
    def isCommonOption(option):
        optionList = "ACEGHLQRWfoqsuz"
        if option in optionList:
            return True
        else:
            return False

    @staticmethod
    def parseSyscallOption(value, targetList=None, exceptList=None):
        syscallList = UtilMgr.cleanItem(value.split(","))
        enabledSyscall = []
        disabledSyscall = []

        # set lists #
        if targetList is None:
            targetList = SysMgr.syscallList
        if exceptList is None:
            exceptList = SysMgr.syscallExceptList

        for val in syscallList:
            try:
                if val.startswith("^"):
                    exceptFlag = True
                    val = val[1:]
                else:
                    exceptFlag = False

                # add sys_ prefix #
                if val.lstrip("*").startswith("sys_"):
                    if val.startswith("*"):
                        prefix = "*"
                    else:
                        prefix = ""
                    val = prefix + UtilMgr.lstrip(val.lstrip("*"), "sys_")

                # get syscall index #
                nrList = []
                if "*" in val:
                    for idx, syscall in enumerate(ConfigMgr.sysList):
                        if UtilMgr.isValidStr(
                            UtilMgr.lstrip(syscall, "sys_"), [val]
                        ):
                            nrList.append(idx)
                else:
                    nrList = [SysMgr.getNrSyscall(val)]

                # classify syscall #
                for nrSyscall in nrList:
                    if exceptFlag:
                        disabledSyscall.append(ConfigMgr.sysList[nrSyscall])
                    else:
                        enabledSyscall.append(ConfigMgr.sysList[nrSyscall])

                    if exceptFlag:
                        exceptList.append(nrSyscall)
                    else:
                        targetList.append(nrSyscall)
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr("no %s syscall in %s ABI" % (val, SysMgr.arch))
                sys.exit(-1)

            # no syscall #
            if syscallList and not targetList and not exceptList:
                SysMgr.printErr("no %s syscall in %s ABI" % (val, SysMgr.arch))
                sys.exit(-1)

        # print logs #
        if not enabledSyscall:
            SysMgr.printInfo("enabled syscall list [ ALL ]")
        else:
            SysMgr.printInfo(
                "enabled syscall list [ %s ]" % ", ".join(enabledSyscall)
            )

        if disabledSyscall:
            SysMgr.printInfo(
                "disabled syscall list [ %s ]" % ", ".join(disabledSyscall)
            )

    @staticmethod
    def parseRecordOption():
        if not "ISMAIN" in os.environ:
            return
        elif len(sys.argv) <= 2:
            return

        SysMgr.parseOption()

        for item in SysMgr.optionList:
            try:
                option = item[0]
                value = item[1:].strip()
            except:
                continue

            if option == "b":
                try:
                    if value.isdigit():
                        bsize = long(value)
                        osize = bsize << 10
                    else:
                        osize = UtilMgr.convUnit2Size(value)
                        bsize = osize >> 10

                    if bsize > 0:
                        SysMgr.bufferSize = bsize

                        SysMgr.printInfo(
                            "set buffer size to [%s]"
                            % UtilMgr.convSize2Unit(osize)
                        )
                    else:
                        SysMgr.printErr(
                            "wrong value for buffer size, "
                            "input number bigger than 0"
                        )
                        sys.exit(-1)
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr(
                        "wrong value for buffer size, "
                        "input number in integer format"
                    )
                    sys.exit(-1)

            elif option == "Y":
                SysMgr.applyPriority(value)

            elif option == "e":
                options = value.replace(" ", "")

                if "a" in options:
                    SysMgr.enableAll = True

                if "i" in options:
                    SysMgr.irqEnable = True

                if "m" in options:
                    SysMgr.memEnable = True

                if "n" in options:
                    SysMgr.networkEnable = True

                if "h" in options:
                    SysMgr.heapEnable = True

                if "b" in options:
                    SysMgr.blockEnable = True

                if "p" in options:
                    SysMgr.pipeEnable = True

                if "P" in options:
                    SysMgr.powerEnable = True

                if "w" in options:
                    SysMgr.wqEnable = True

                if "B" in options:
                    SysMgr.binderEnable = True

                if "d" in options:
                    SysMgr.diskEnable = True

                if "o" in options:
                    SysMgr.iouringEnable = True

                if "k" in options:
                    SysMgr.kvmEnable = True

                if "I" in options:
                    SysMgr.i2cEnable = True

                if "f" in options:
                    SysMgr.fsEnable = True

                if "r" in options:
                    SysMgr.resetEnable = True

                if "g" in options:
                    SysMgr.graphEnable = True

                if "L" in options:
                    SysMgr.lockEnable = True

                if "c" in options:
                    SysMgr.cgroupEnable = True

                if "u" in options:
                    SysMgr.userEnable = True

                if not SysMgr.isValidEnableOption(options):
                    SysMgr.printErr(
                        "unrecognized option '%s' to enable" % options
                    )
                    sys.exit(-1)

            elif option == "g":
                itemList = UtilMgr.splitString(value)
                SysMgr.filterGroup = UtilMgr.cleanItem(itemList)
                if not SysMgr.filterGroup:
                    SysMgr.printErr("no input value for filter")
                    sys.exit(-1)

                SysMgr.printInfo(
                    "only specific threads [ %s ] are recorded"
                    % ", ".join(SysMgr.filterGroup)
                )

            elif option == "w":
                SysMgr.rcmdList = SysMgr.parseCustomRecordCmd(value)

            elif option == "U":
                SysMgr.ueventEnable = True
                itemList = UtilMgr.splitString(value)
                SysMgr.userCmd = UtilMgr.cleanItem(itemList)

            elif option == "K":
                SysMgr.keventEnable = True
                itemList = UtilMgr.splitString(value)
                SysMgr.kernelCmd = UtilMgr.cleanItem(itemList)

            elif option == "M":
                SysMgr.objdumpPath = value

                SysMgr.printInfo(
                    "use '%s' as the objdump path" % SysMgr.objdumpPath
                )

            elif option == "F":
                SysMgr.printOptionWarn(option, value)
                SysMgr.fileEnable = True

            elif option == "B":
                # get output path #
                if not value:
                    value = SysMgr.cmdFileName

                # change output path #
                try:
                    if not SysMgr.isWritable(value):
                        raise Exception("not writable")

                    if os.path.isdir(value):
                        SysMgr.cmdEnable = "%s/%s" % (
                            value,
                            SysMgr.cmdFileName,
                        )
                    else:
                        SysMgr.cmdEnable = value
                except:
                    SysMgr.printErr(
                        "wrong value for command script path" % value
                    )
                    sys.exit(-1)

                # remove double slashes #
                SysMgr.cmdEnable = os.path.normpath(SysMgr.cmdEnable)

            elif option == "t":
                SysMgr.sysEnable = True
                SysMgr.parseSyscallOption(value)

            elif SysMgr.isCommonOption(option):
                SysMgr.parseCommonOption(option, value)

            elif option == "c":
                itemList = UtilMgr.splitString(value)
                SysMgr.customCmd = UtilMgr.cleanItem(itemList)
                if not SysMgr.customCmd:
                    SysMgr.printErr("failed to recognize trace events")
                    sys.exit(-1)

            elif option == "d":
                options = value.replace(" ", "")

                if "c" in options:
                    SysMgr.cpuEnable = False
                    SysMgr.latEnable = False

                if "m" in options:
                    SysMgr.memEnable = False

                if "h" in options:
                    SysMgr.heapEnable = False

                if "b" in options:
                    SysMgr.blockEnable = False

                if "p" in options:
                    SysMgr.printEnable = False

                if "l" in options:
                    SysMgr.latEnable = False

                if "L" in options:
                    SysMgr.logEnable = False

                if "a" in options:
                    SysMgr.disableAll = True

                if "C" in options:
                    SysMgr.compressEnable = False

                if "g" in options:
                    SysMgr.generalEnable = False

            elif option == "D":
                SysMgr.printOptionWarn(option, value)
                SysMgr.depEnable = True

            # Ignore options #
            elif SysMgr.isValidOption(option):
                continue

            else:
                SysMgr.printErr(
                    "unrecognized option -%s for recording" % option
                )
                sys.exit(-1)

    @staticmethod
    def makeKerSymTable(symbol=None):
        SysMgr.printInfo("start reading kernel symbols...")

        # disable restrict #
        try:
            restPath = "%s/sys/kernel/kptr_restrict" % SysMgr.procPath
            with open(restPath, "w+") as fd:
                fd.write("0")
        except SystemExit:
            sys.exit(0)
        except:
            pass

        try:
            symPath = "%s/kallsyms" % SysMgr.procPath
            f = open(symPath, "r")
        except IOError:
            SysMgr.printOpenWarn(symPath)
        except SystemExit:
            sys.exit(0)

        ret = None
        startPos = len(SysMgr.kerSymTable)
        curPos = 0

        while 1:
            line = f.readline()
            curPos += 1

            if startPos > curPos:
                continue

            if not line:
                break

            # Cache address and symbol #
            line = line.split(" ", 2)
            SysMgr.kerSymTable[line[2].strip()] = line[0]

            if line[2] == symbol:
                ret = line[0]
                break

        f.close()
        return ret

    @staticmethod
    def getAddrFromKernel(symbol):
        try:
            return SysMgr.kerSymTable[symbol]
        except:
            return SysMgr.makeKerSymTable(symbol)

    @staticmethod
    def isRecordMode():
        if len(sys.argv) > 1 and sys.argv[1] in (
            "rec",
            "funcrec",
            "iorec",
            "sysrec",
            "filerec",
            "genrec",
        ):
            return True
        else:
            return False

    @staticmethod
    def isHelpMode():
        if len(sys.argv) > 1 and sys.argv[1] == "help":
            return True
        elif {"-help", "--help", "-h"} & set(sys.argv):
            return True
        else:
            return False

    @staticmethod
    def getMode(orig=False):
        # set target list #
        if orig:
            target = SysMgr.origArgs
        else:
            target = sys.argv

        # check value #
        if len(target) > 1:
            return target[1]
        else:
            return None

    @staticmethod
    def checkMode(mode, orig=False):
        # set target list #
        if orig:
            target = SysMgr.origArgs
        else:
            target = sys.argv

        # check value #
        if len(target) > 1 and target[1] == mode:
            return True
        else:
            return False

    @staticmethod
    def isKillMode():
        if len(sys.argv) < 2:
            return False
        elif sys.argv[1] in ("kill", "tkill"):
            return True
        else:
            return False

    @staticmethod
    def isWritable(value):
        # file exist #
        if os.access(value, os.F_OK):
            if not os.access(value, os.W_OK):
                return False
        # no file exist #
        else:
            dirPath = os.path.dirname(value)
            if not dirPath:
                dirPath = "."

            # no dir exist #
            if not os.path.isdir(dirPath):
                return False
            # dir is not writable #
            elif not os.access(dirPath, os.W_OK):
                return False

        return True

    @staticmethod
    def isTopMode():
        if len(sys.argv) > 1 and sys.argv[1] in (
            "top",
            "atop",
            "bgtop",
            "btop",
            "cgtop",
            "ctop",
            "dbustop",
            "disktop",
            "dlttop",
            "fetop",
            "ftop",
            "irqtop",
            "kftop",
            "ktop",
            "mtop",
            "ntop",
            "ptop",
            "pytop",
            "rtop",
            "slabtop",
            "stacktop",
            "systop",
            "trtop",
            "ttop",
            "utop",
            "vtop",
            "wtop",
        ):
            return True
        else:
            return False

    @staticmethod
    def isTraceMode():
        if len(sys.argv) > 1 and sys.argv[1] in (
            "btrace",
            "leaktrace",
            "mtrace",
            "pytrace",
            "remote",
            "sigtrace",
            "strace",
            "utrace",
        ):
            return True
        else:
            return False

    @staticmethod
    def isRuntimeMode():
        if (
            SysMgr.isRecordMode()
            or SysMgr.isTopMode()
            or SysMgr.isTraceMode()
            or SysMgr.checkMode("req")
        ):
            return True

        return False

    @staticmethod
    def checkCmdMode():
        # parse options #
        SysMgr.parseAnalOption()

        # set tty setting automatically #
        if not SysMgr.ttyEnable:
            SysMgr.setTTYAuto(True, False, False)

        # wait for input #
        if SysMgr.waitEnable:
            SysMgr.waitUserInput(SysMgr.waitEnable, msg="DEFAULT", force=True)

        # CMDTEST MODE #
        if SysMgr.checkMode("cmdtest"):
            SysMgr.loggingEnable = False
            SysMgr.streamEnable = True
            SysMgr.logoEnable = False
            for i in SysMgr.getCmdSet():
                print("[%s]" % i)
                ret = SysMgr.launchGuider(
                    [i],
                    pipe=False,
                    stderr=True,
                    log=False,
                    wait=False,
                    logo=False,
                )
                time.sleep(1)
                try:
                    os.kill(ret, signal.SIGKILL)
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

        # LIST MODE #
        if SysMgr.checkMode("list"):
            SysMgr.setStream(not SysMgr.streamEnable)
            SysMgr.printBgProcs()

        # SERVER MODE #
        elif SysMgr.checkMode("server"):
            SysMgr.runServerMode()

        # FSERVER MODE #
        elif SysMgr.checkMode("fserver"):
            SysMgr.runFileServerMode()

        # HSERVER MODE #
        elif SysMgr.checkMode("hserver"):
            SysMgr.runHttpServerMode()

        # SEND MODE #
        elif SysMgr.checkMode("send"):
            SysMgr.sendMessage()

        # CLIENT MODE #
        elif SysMgr.checkMode("cli"):
            SysMgr.runClientMode()

        # START / STOP MODE #
        elif SysMgr.checkMode("start") or SysMgr.checkMode("stop"):
            # make list of arguments #
            if len(sys.argv) > 2:
                argList = sys.argv[2:]
            else:
                argList = None

            SysMgr.sendSignalProcs(signal.SIGINT, argList)

        # KILL MODE #
        elif SysMgr.isKillMode():
            # make list of arguments #
            if len(sys.argv) > 2:
                argList = sys.argv[2:]
            else:
                argList = [" "]

            # print signal list #
            if SysMgr.findOption("l"):
                SysMgr.printList(ConfigMgr.SIG_LIST, ["0"])
                sys.exit(0)

            # remove additional options #
            if len(argList) > 1:
                idx = 0
                for idx, item in enumerate(argList[1:]):
                    if item.strip().startswith("-"):
                        break

                if argList[-1].strip().startswith("-"):
                    argList = argList[: idx + 1]
                else:
                    argList = argList[: idx + 2]

            # convert task ID #
            argList = SysMgr.convertTaskIdInput(argList)

            while 1:
                # set target type #
                if SysMgr.checkMode("tkill"):
                    isThread = True
                else:
                    isThread = False

                # send signal #
                SysMgr.sendSignalArgs(list(argList), isThread=isThread)

                # check interval #
                if SysMgr.intervalEnable:
                    time.sleep(SysMgr.intervalEnable)
                else:
                    break

        # TOPDIFF MODE #
        elif SysMgr.checkMode("topdiff"):
            # remove option args #
            SysMgr.removeOptionArgs()

            # get file list #
            if SysMgr.hasMainArg():
                argList = SysMgr.getMainArg().split(",")
                argList = UtilMgr.getFileList(argList)
            else:
                argList = None

            SysMgr.printLogo(big=True, onlyFile=True)

            TaskAnalyzer.doDiffReports(argList)

        # TOPSUM MODE #
        elif SysMgr.checkMode("topsum"):
            # check input #
            if SysMgr.hasMainArg():
                inputArg = SysMgr.getMainArgs()
            elif SysMgr.inputParam:
                inputArg = str(SysMgr.inputParam).split(",")
                inputArg = UtilMgr.cleanItem(inputArg, True)
            else:
                SysMgr.printErr("no input for PATH")
                sys.exit(-1)

            # set default output path #
            SysMgr.setDefaultOutPath()

            # convert input files #
            inputArg = UtilMgr.getFileList(inputArg, sort=True, exceptDir=True)

            TaskAnalyzer.doSumReport(inputArg)

        # PAUSE MODE #
        elif SysMgr.checkMode("pause"):
            # get target list #
            ret = SysMgr.selectTaskId()
            if ret:
                targets = [ret]
            elif SysMgr.hasMainArg():
                targets = SysMgr.getMainArgs()
            else:
                targets = SysMgr.filterGroup

            if not targets:
                SysMgr.printErr("no input value for COMM or TID")
                sys.exit(-1)

            targetList = []
            sibling = SysMgr.groupProcEnable

            # convert comm to tid #
            targetList = SysMgr.convTaskList(
                targets, isThread=True, exceptMe=True, sibling=sibling
            )
            targetList = list(set(targetList))

            # check target list #
            if not targetList:
                SysMgr.printErr("no task related to '%s'" % ", ".join(targets))
                sys.exit(-1)

            Debugger.pauseThreads(targetList)

        # READELF MODE #
        elif SysMgr.checkMode("readelf"):
            # get path #
            if SysMgr.hasMainArg():
                path = SysMgr.getMainArg()
            elif SysMgr.inputParam:
                path = SysMgr.inputParam
            else:
                SysMgr.printErr("no input value for path")
                sys.exit(-1)

            SysMgr.printLogo(big=True, onlyFile=True)

            # set debug flag #
            debug = not SysMgr.jsonEnable

            # run ELF analyzer #
            try:
                if path == "vdso":
                    obj = SysMgr.getVDSO(debug=debug)
                else:
                    obj = ElfAnalyzer(path, debug, incArg=True)

                if SysMgr.jsonEnable:
                    jsonStr = UtilMgr.convDict2Str(
                        obj.attr, pretty=not SysMgr.streamEnable
                    )
                    SysMgr.printPipe(jsonStr)
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr("failed to analyze '%s'" % path, True)

        # LEAKTRACE MODE #
        elif SysMgr.checkMode("leaktrace"):
            SysMgr.doLeaktrace()

        # ADDR2SYM MODE #
        elif SysMgr.checkMode("addr2sym"):
            SysMgr.doAddr2sym()

        # SYM2ADDR MODE #
        elif SysMgr.checkMode("sym2addr"):
            SysMgr.doSym2addr()

        # MKCACHE MODE #
        elif SysMgr.checkMode("mkcache"):
            SysMgr.doMkCache()

        # READAHEAD MODE #
        elif SysMgr.checkMode("readahead"):
            # get list path #
            if SysMgr.hasMainArg():
                path = SysMgr.getMainArg()
            elif SysMgr.inputParam:
                path = SysMgr.inputParam
            else:
                path = "readahead.list"

            SysMgr.doReadahead(path)

        # PRINTEXT MODE #
        elif SysMgr.checkMode("printext"):
            SysMgr.doPrintExt()

        # PRINTDIR MODE #
        elif SysMgr.checkMode("printdir"):
            SysMgr.printLogo(big=True, onlyFile=True)

            # get start dir #
            if SysMgr.hasMainArg():
                rootList = SysMgr.getMainArgs()
            elif SysMgr.inputParam:
                rootList = SysMgr.inputParam.split(",")
                rootList = UtilMgr.cleanItem(rootList, True)
            else:
                rootList = ["."]

            # get depth #
            if not SysMgr.funcDepth:
                maxLevel = -1
            else:
                maxLevel = SysMgr.funcDepth

            # get size unit option #
            sizeUnit = not "FULLSIZE" in SysMgr.environList

            # start printing dirs #
            for root in rootList:
                SysMgr.printDirs(root, maxLevel, sizeUnit=sizeUnit)

        # DIRDIFF MODE #
        elif SysMgr.checkMode("dirdiff"):
            SysMgr.printLogo(big=True, onlyFile=True)

            # get start dir #
            if SysMgr.hasMainArg():
                rootList = SysMgr.getMainArgs(union=False)
            elif SysMgr.inputParam:
                rootList = SysMgr.inputParam.split(",")
                rootList = UtilMgr.cleanItem(rootList, True)
            else:
                rootList = ["."]

            # check path number #
            if len(rootList) != 2:
                SysMgr.printErr("wrong input value for path")
                sys.exit(0)

            # get depth #
            if not SysMgr.funcDepth:
                maxLevel = -1
            else:
                maxLevel = SysMgr.funcDepth

            # get size unit option #
            sizeUnit = not "FULLSIZE" in SysMgr.environList

            # print dir diff #
            SysMgr.printDirDiff(rootList, maxLevel=maxLevel, sizeUnit=sizeUnit)

        # PRINTCGROUP MODE #
        elif SysMgr.checkMode("printcg"):
            SysMgr.cgroupEnable = True
            SysMgr.printCgroupList(
                json=SysMgr.jsonEnable, ret=SysMgr.jsonEnable
            )
            SysMgr().printCgroupInfo(printTitle=False, progress=True, cmd=True)
            SysMgr.printInfoBuffer()

        # LOGJRL MODE #
        elif SysMgr.checkMode("logjrl"):
            SysMgr.doLogMode("journal")

        # LOGDLT MODE #
        elif SysMgr.checkMode("logdlt"):
            SysMgr.doLogMode("dlt")

        # LOGKMSG MODE #
        elif SysMgr.checkMode("logkmsg"):
            SysMgr.doLogMode("kmsg")

        # LOGTRACE MODE #
        elif SysMgr.checkMode("logtrace"):
            SysMgr.doLogMode("trace")

        # LOGSYS MODE #
        elif SysMgr.checkMode("logsys"):
            SysMgr.doLogMode("syslog")

        # LOGAND MODE #
        elif SysMgr.checkMode("logand"):
            SysMgr.doLogMode("android")

        # PRINT{DBUS|SD}* MODE #
        elif SysMgr.getMode() in (
            "printdbus",
            "printdbusintro",
            "printdbusstat",
            "printdbussub",
            "printsdinfo",
            "printsdunit",
        ):
            # remove dbus word #
            cmd = SysMgr.getMode().replace("dbus", "")

            # set console info #
            if cmd == "print":
                SysMgr.setStream()

            SysMgr.printLogo(big=True, onlyFile=True)

            # convert command by JSON option #
            if SysMgr.jsonEnable:
                if cmd == "printsdinfo":
                    cmd = "getunitlist"
                elif cmd == "printsdunit":
                    cmd = "getunitstat"

            # run command #
            ret = DbusMgr.runDbusSnooper(mode=cmd)

            # print result in JSON format #
            if SysMgr.jsonEnable:
                SysMgr.printPipe(
                    UtilMgr.convDict2Str(ret, pretty=not SysMgr.streamEnable)
                )

        elif SysMgr.isPrintLogMode():
            # set console info #
            SysMgr.setStream(cut=False)

            # get console option #
            console = SysMgr.findOption("Q")

            # set alarm handler #
            if SysMgr.intervalEnable > 0:
                signal.signal(signal.SIGALRM, SysMgr.onAlarm)
                signal.alarm(SysMgr.intervalEnable)

            SysMgr.printLogo(big=True, onlyFile=True)

            # PRINTDLT MODE #
            if SysMgr.checkMode("printdlt"):
                DltAnalyzer.runDltReceiver(mode="print")

            # PRINTJRL MODE #
            elif SysMgr.checkMode("printjrl"):
                LogMgr.printJournal(console)

            # PRINTKMSG MODE #
            elif SysMgr.checkMode("printkmsg"):
                LogMgr.printKmsg(console)

            # PRINTTRACE MODE #
            elif SysMgr.checkMode("printtrace"):
                LogMgr.printTrace(console)

            # PRINTAND MODE #
            elif SysMgr.checkMode("printand"):
                LogMgr.printAndroid(console)

            # PRINTSYSLOG MODE #
            elif SysMgr.checkMode("printsyslog"):
                LogMgr.printSyslog(console)

        # PRINTSIG MODE #
        elif SysMgr.checkMode("printsig"):
            SysMgr.doPrintSig()

        # PAGE MODE #
        elif SysMgr.checkMode("mem"):
            SysMgr.printLogo(big=True, onlyFile=True)

            # check input #
            ret = SysMgr.selectTaskId()
            if ret:
                target = [ret]
            elif SysMgr.hasMainArg():
                target = SysMgr.getMainArgs(union=False)
            else:
                target = SysMgr.filterGroup

            PageAnalyzer.getPageInfo(
                target, SysMgr.inputParam, cmdset=SysMgr.customCmd
            )

        # LIMIT MODE #
        elif SysMgr.isLimitMode():
            # change the CPU scheduling priority for tasks #
            if SysMgr.prio is None:
                SysMgr.setPriority(SysMgr.pid, "C", -20)

            # get argument #
            if SysMgr.hasMainArg():
                filterGroup = SysMgr.getMainArgs()
            else:
                filterGroup = SysMgr.filterGroup

            # check input #
            if not filterGroup:
                SysMgr.printErr("no input value for task limit info")
                sys.exit(-1)

            # get resource name #
            if SysMgr.checkMode("limitcpu"):
                if "NOCG" in SysMgr.environList:
                    limitInfo = SysMgr.getLimitCpuInfo(filterGroup)
                    SysMgr.doLimitCpu(limitInfo, SysMgr.processEnable)
                    sys.exit(0)
                name = "LIMITCPU"
            elif SysMgr.checkMode("limitcpuset"):
                name = "LIMITCPUSET"
            elif SysMgr.checkMode("limitmem"):
                name = "LIMITMEM"
            elif SysMgr.checkMode("limitread"):
                name = "LIMITREAD"
            elif SysMgr.checkMode("limitwrite"):
                name = "LIMITWRITE"
            elif SysMgr.checkMode("limitpid"):
                name = "LIMITPID"
            else:
                SysMgr.printErr("no support resource limitation")
                sys.exit(-1)

            # parse limit info #
            limitList = {name: []}
            try:
                for item in filterGroup:
                    target = item.split(":", 1)
                    if len(target) == 1:
                        raise Exception("no limit value")

                    target, value = target

                    # check targets #
                    targets = SysMgr.getTids(
                        target,
                        isThread=not SysMgr.processEnable,
                        sibling=SysMgr.groupProcEnable,
                    )
                    if not targets:
                        SysMgr.printErr("no task for '%s'" % target)
                        sys.exit(-1)

                    limitList[name].append(value + "@" + target)
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr(
                    "failed to limit resource using %s" % name, True
                )
                sys.exit(-1)

            # limit resources #
            pids = SysMgr.applyLimitVars(limitList)

            # start monitoring #
            if "TASKMON" in SysMgr.environList:
                SysMgr.runTaskMonitor(pids, wait=True)
            else:
                # wait for events #
                SysMgr.waitEvent()

        # PSTREE MODE #
        elif SysMgr.checkMode("pstree"):
            # get target #
            if SysMgr.hasMainArg():
                targets = SysMgr.getMainArgs()
            else:
                targets = None

            # check total info #
            enableOptList = SysMgr.getOption("e")
            if enableOptList and "T" in enableOptList:
                SysMgr.totalEnable = True

            SysMgr.doPstree(targets=targets, canRepeat=True)

        # COMP MODE #
        elif SysMgr.checkMode("comp"):
            try:
                SysMgr.doCompress()
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr("failed to compress files", True)

        # DECOMP MODE #
        elif SysMgr.checkMode("decomp"):
            try:
                SysMgr.doDecompress(ffilter=SysMgr.filterGroup)
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr("failed to decompress files", True)

        # SPLIT MODE #
        elif SysMgr.checkMode("split"):
            try:
                SysMgr.doSplit()
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr("failed to split files", True)

        # MERGE MODE #
        elif SysMgr.checkMode("merge"):
            try:
                SysMgr.doMerge()
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr("failed to mergefiles", True)

        # SYSTAT MODE #
        elif SysMgr.checkMode("systat"):
            SysMgr.doSystat()

        # DRAWTIMELINE MODE #
        elif SysMgr.checkMode("drawtime"):
            SysMgr.doDrawTimeline()

        # CPUTEST MODE #
        elif SysMgr.checkMode("cputest"):
            SysMgr.doCpuTest()

        # IOTEST MODE #
        elif SysMgr.checkMode("iotest"):
            SysMgr.doIoTest()

        # NETTEST MODE #
        elif SysMgr.checkMode("nettest"):
            SysMgr.doNetTest()

        # MEMTEST MODE #
        elif SysMgr.checkMode("memtest"):
            # remove option args #
            SysMgr.removeOptionArgs()

            SysMgr.doMemTest()

        # FADVISE MODE #
        elif SysMgr.checkMode("fadvise"):
            SysMgr.doFadvise()

        # FLUSH MODE #
        elif SysMgr.checkMode("flush"):
            SysMgr.doFlush()

        # SYNC MODE #
        elif SysMgr.checkMode("sync"):
            SysMgr.doSync()

        # EXEC MODE #
        elif SysMgr.checkMode("exec"):
            # check config file #
            if SysMgr.findOption("C"):
                # search config file #
                SysMgr.updateConfigPath()

                # load config file #
                if ConfigMgr.confData:
                    pass
                elif not SysMgr.loadConfig(SysMgr.confFileName):
                    sys.exit(-1)

            SysMgr.doExec()

        # REQUEST MODE #
        elif SysMgr.checkMode("req"):
            SysMgr.setStream()

            SysMgr.printLogo(big=True, onlyFile=True)

            SysMgr.doRequest()

        # SETCPU MODE #
        elif SysMgr.checkMode("setcpu"):
            # remove option args #
            SysMgr.removeOptionArgs()

            SysMgr.doSetCpu()

        # SETSCHED MODE #
        elif SysMgr.checkMode("setsched"):
            SysMgr.doSetSched()

        # RLIMIT MODE #
        elif SysMgr.checkMode("rlimit"):
            SysMgr.doRlimit()

        # CONVERT MODE #
        elif SysMgr.checkMode("convert"):
            SysMgr.doConvert()

        # DRAWREQ MODE #
        elif SysMgr.checkMode("drawreq"):
            SysMgr.doDrawReq()

        # MOUNT MODE #
        elif SysMgr.checkMode("mount"):
            SysMgr.doMount()

        # UNMOUNT MODE #
        elif SysMgr.checkMode("umount"):
            SysMgr.doUnmount()

        # STRINGS MODE #
        elif SysMgr.checkMode("strings"):
            SysMgr.doStrings()

        # SYSRQ MODE #
        elif SysMgr.checkMode("sysrq"):
            isList = SysMgr.findOption("l")
            SysMgr.writeSysrq(isList=isList)

        # PRINT MODE #
        elif SysMgr.checkMode("print"):
            SysMgr.doPrintFile()

        # LESS MODE #
        elif SysMgr.checkMode("less"):
            SysMgr.doLess()

        # DUMP MODE #
        elif SysMgr.checkMode("dump"):
            SysMgr.doDump()

        # STRACE MODE #
        elif SysMgr.checkMode("strace"):
            # just print syscall list #
            if SysMgr.findOption("l"):
                SysMgr.printList(
                    ConfigMgr.sysList,
                    skipList=["sys_null"],
                    stripPrefix="sys_",
                )
                sys.exit(0)

            SysMgr.doTrace("syscall")

        # MTRACE MODE #
        elif SysMgr.checkMode("mtrace"):
            SysMgr.sysEnable = True
            SysMgr.parseSyscallOption("mmap, exit, exit_group")
            SysMgr.addEnvironVar("INCTGINFO")
            if not SysMgr.outPath:
                SysMgr.outPath = "."
            SysMgr.doTrace("syscall")

        # UTRACE MODE #
        elif SysMgr.checkMode("utrace"):
            SysMgr.doTrace("usercall")

        # REMOTE MODE #
        elif SysMgr.checkMode("remote"):
            SysMgr.doTrace("remote")

        # HOOK MODE #
        elif SysMgr.checkMode("hook"):
            SysMgr.doTrace("hook")

        # HOOK MODE #
        elif SysMgr.checkMode("printbind"):
            SysMgr.doTrace("bind")

        # BTRACE MODE #
        elif SysMgr.checkMode("btrace"):
            SysMgr.doTrace("breakcall")

        # PYTRACE MODE #
        elif SysMgr.checkMode("pytrace"):
            SysMgr.doTrace("pytrace")

        # CHECKDUP MODE #
        elif SysMgr.checkMode("checkdup"):
            SysMgr.doCheckDup()

        # GETPID MODE #
        elif SysMgr.checkMode("getpid"):
            SysMgr.doGetpid()

        # WATCH/FETOP MODE #
        elif SysMgr.checkMode("watch") or SysMgr.checkMode("fetop"):
            # just print event list #
            if SysMgr.findOption("l"):
                if (
                    "PROCINFO" in SysMgr.environList
                    or "INMOUNT" in SysMgr.environList
                    or "ALLMOUNT" in SysMgr.environList
                ):
                    UtilMgr.makeReverseDict(
                        ConfigMgr.FAN_EVENT_TYPE,
                        ConfigMgr.FAN_EVENT_TYPE_REVERSE,
                    )
                    SysMgr.printList(ConfigMgr.FAN_EVENT_TYPE_REVERSE)
                else:
                    SysMgr.printList(ConfigMgr.INOTIFY_TYPE)
                sys.exit(0)

            SysMgr.doWatch(top=SysMgr.checkMode("fetop"))

        # SIGTRACE MODE #
        elif SysMgr.checkMode("sigtrace"):
            SysMgr.doTrace("signal")

        # PRINTENV MODE #
        elif SysMgr.checkMode("printenv"):
            SysMgr.doPrintEnv()

        # PRINTSLAB MODE #
        elif SysMgr.checkMode("printslab"):
            SysMgr.doPrintSlab()

        # PRINTVMALLOC MODE #
        elif SysMgr.checkMode("printvma"):
            SysMgr.doPrintVmalloc()

        # PRINTKCONF MODE #
        elif SysMgr.checkMode("printkconf"):
            SysMgr.doPrintKconf()

        # PRINTNS MODE #
        elif SysMgr.checkMode("printns"):
            SysMgr.doPrintNs()

        # PRINTSDFILE MODE #
        elif SysMgr.checkMode("printsdfile"):
            SysMgr.doPrintSdFile()

        # PRINTINFO MODE #
        elif SysMgr.checkMode("printinfo"):
            SysMgr.doPrintInfo()

        # SETAFFINITY MODE #
        elif SysMgr.checkMode("setafnt"):
            SysMgr.doSetAffinity()

        # GETAFFINITY MODE #
        elif SysMgr.checkMode("getafnt"):
            SysMgr.doGetAffinity()

        # CGROUP MODE #
        elif SysMgr.checkMode("cgroup"):
            SysMgr.setStream()

            # just print cgroup list #
            if SysMgr.findOption("l"):
                SysMgr.printCgroupList(json=SysMgr.jsonEnable)
            else:
                SysMgr.doCgroup()

        # FREEZE MODE #
        elif SysMgr.checkMode("freeze"):
            SysMgr.doFreeze()

        # PING MODE #
        elif SysMgr.checkMode("ping"):
            SysMgr.setStream()

            # set system maximum fd number #
            SysMgr.setMaxFd()

            SysMgr.ping()

        # EVENT MODE #
        elif SysMgr.checkMode("event"):
            SysMgr.handleEventInput()

        # HELPTEST MODE #
        elif SysMgr.checkMode("helptest"):
            SysMgr.loggingEnable = False
            SysMgr.streamEnable = True
            for i in SysMgr.getCmdSet():
                SysMgr.origArgs = sys.argv = [
                    sys.argv[0],
                    i.split("/")[0],
                    "-h",
                ]
                print(sys.argv)
                SysMgr.printHelp(isExit=False)

        else:
            return

        sys.exit(0)

    @staticmethod
    def isLimitMode():
        if len(sys.argv) > 1 and sys.argv[1] in (
            "limitcpu",
            "limitcpuset",
            "limitmem",
            "limitpid",
            "limitread",
            "limitwrite",
        ):
            return True
        else:
            return False

    @staticmethod
    def isDrawAvgMode():
        if len(SysMgr.origArgs) > 1 and SysMgr.origArgs[1] in (
            "drawavg",
            "drawcpuavg",
            "drawmemavg",
            "drawvssavg",
            "drawrssavg",
        ):
            return True
        else:
            return False

    @staticmethod
    def isDrawMode():
        orig = SysMgr.drawMode
        SysMgr.drawMode = True

        if len(sys.argv) == 1:
            return False
        elif sys.argv[1] == "draw" or orig:
            return True
        elif len(SysMgr.origArgs) > 1 and SysMgr.origArgs[1] in (
            "drawbitmap",
            "drawcpu",
            "drawdelay",
            "drawflame",
            "drawhist",
            "drawio",
            "drawleak",
            "drawmem",
            "drawpri",
            "drawrss",
            "drawstack",
            "drawtime",
            "drawviolin",
            "drawvss",
        ):
            return True
        elif SysMgr.isDrawAvgMode():
            return True
        else:
            SysMgr.drawMode = orig
            return False

    @staticmethod
    def isPrintLogMode():
        if len(sys.argv) > 1 and sys.argv[1] in (
            "printdlt",
            "printjrl",
            "printkmsg",
            "printand",
            "printsyslog",
            "printtrace",
        ):
            return True
        else:
            return False

    @staticmethod
    def checkPerfTopCond():
        # check root permission #
        if not SysMgr.checkRootPerm(exit=False, msg="use PMU"):
            return False
        elif not os.path.isfile(
            "%s/sys/kernel/perf_event_paranoid" % SysMgr.procPath
        ):
            SysMgr.printErr("failed to use PMU, please check kernel config")
            return False
        else:
            return True

    @staticmethod
    def checkMemTopCond():
        if not SysMgr.checkRootPerm(exit=False, msg="analyze memory details"):
            return False
        else:
            return True

    @staticmethod
    def checkBgTopCond():
        if SysMgr.outPath:
            return True

        logPath = "/var/log"

        # select temporary dir #
        if SysMgr.isLinux and not SysMgr.isAndroid:
            tmpPath = SysMgr.tmpPath
        else:
            tmpPath = os.path.realpath(".")

        # apply output dir #
        if os.path.isdir(logPath) and os.access(logPath, os.W_OK):
            SysMgr.outPath = logPath
        elif os.path.isdir(tmpPath) and os.access(tmpPath, os.W_OK):
            SysMgr.outPath = tmpPath
        else:
            SysMgr.printErr("failed to get path to save output, use -o option")
            return False

        SysMgr.printInfo("save reporting files to '%s'" % SysMgr.outPath)
        return True

    @staticmethod
    def checkRepTopCond(val=None, ignore=True):
        # check whether report option is already enabled #
        if SysMgr.reportEnable:
            return True

        if SysMgr.streamEnable:
            SysMgr.reportObject = sys.stdout
            reportPath = SysMgr.nullPath
        else:
            # ignore printing text-based stats #
            if ignore and not "TEXTREPORT" in SysMgr.environList:
                SysMgr.printEnable = False

            # check stdout report option #
            if not val:
                reportPath = SysMgr.getOption("j")
            else:
                reportPath = val

            # check report path #
            if not reportPath or not reportPath:
                if os.path.exists(SysMgr.tmpPath):
                    reportPath = SysMgr.tmpPath
                else:
                    reportPath = "."

            # check report directory #
            if not os.path.isdir(reportPath):
                upDirPos = reportPath.rfind("/")
                if upDirPos > 0 and not os.path.isdir(reportPath[:upDirPos]):
                    SysMgr.printErr(
                        "wrong path '%s' to report stats" % reportPath
                    )
                    return False
            # check report file #
            else:
                reportPath = "%s/guider.report" % reportPath

            # remove redundant slashes and save it as the global report path #
            reportPath = os.path.normpath(reportPath)

            # backup a exist output file #
            SysMgr.backupFile(reportPath)

            # open report file #
            try:
                if SysMgr.truncEnable:
                    perm = "w"
                else:
                    perm = "a"

                SysMgr.reportObject = open(reportPath, perm)
                os.chmod(reportPath, 0o777)
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printOpenErr(reportPath)
                sys.exit(-1)

        # report option is enabled #
        SysMgr.reportEnable = True

        SysMgr.printInfo(
            "start writing report in JSON format to '%s'" % reportPath
        )

        return True

    @staticmethod
    def checkWssTopCond():
        if not SysMgr.hasMainArg() and not SysMgr.filterGroup:
            SysMgr.printErr("no target task")
            return False
        elif not SysMgr.checkRootPerm(exit=False, msg="clear refcnts"):
            return False
        else:
            return True

    @staticmethod
    def checkIdlePageCond():
        if os.path.exists("/sys/kernel/mm/page_idle/bitmap"):
            return True

        # check kernel version #
        ver = float(".".join(SysMgr.getKernelVersion().split(".")[0:2]))
        if ver < 4.3:
            SysMgr.printErr(
                (
                    "failed to use Idle Page Tracking "
                    "because kernel version %s is lesser than 4.3"
                )
                % UtilMgr.convFloat2Str(ver)
            )
        else:
            SysMgr.printErr(
                "failed to use Idle Page Tracking, "
                "please check kernel CONFIG_IDLE_PAGE_TRACKING"
            )

        return False

    @staticmethod
    def checkDiskTopCond():
        if SysMgr.forceEnable:
            return True

        procPath = SysMgr.procPath
        if SysMgr.isLinux:
            if not SysMgr.checkRootPerm(
                exit=False, msg="analyze block I/O for tasks"
            ):
                return False
            elif not os.path.isfile("%s/self/io" % procPath):
                SysMgr.printErr(
                    "failed to use bio event to analyze block I/O for tasks, "
                    "please check kernel configs"
                )
                return False
        return True

    @staticmethod
    def checkStackTopCond():
        if SysMgr.forceEnable:
            return True
        elif not SysMgr.checkRootPerm(exit=False, msg="collect stacks"):
            return False
        elif not os.path.isfile("%s/self/stack" % SysMgr.procPath):
            SysMgr.printErr(
                "failed to sample stack, please check kernel configs"
            )
            return False
        else:
            return True

    @staticmethod
    def convCIDR(addr):
        if addr in SysMgr.ipAddrCache:
            return SysMgr.ipAddrCache[addr]

        addrList = []

        # slice last 64bits for IPv6 #
        try:
            addr = addr[-8:]
        except:
            pass

        # check 0 #
        if long(addr, 16) == 0:
            retval = "*"
        else:
            # convert IP #
            splitAddr = [addr[i : i + 2] for i in xrange(0, len(addr), 2)]
            for num in reversed(splitAddr):
                addrList.append(str(long(num, base=16)))

            # build IP string #
            retval = ".".join(addrList)

        # cache result #
        SysMgr.ipAddrCache[addr] = retval

        return retval

    @staticmethod
    def umountCgroup(sub, path):
        ret = SysMgr.umount(path)
        if ret == 0:
            SysMgr.printInfo("umounted cgroup '%s' on '%s'" % (sub, path))
            return True
        else:
            # get error reason #
            reason = SysMgr.getErrReason()
            if reason and reason != "N/A":
                reason = " because " + reason

            SysMgr.printWarn(
                "failed to umount cgroup '%s' on '%s'%s" % (sub, path, reason),
                True,
            )
            return False

    @staticmethod
    def mountCgroup(sub, path):
        ret = SysMgr.mount(
            sub, path, "cgroup", ConfigMgr.MOUNT_TYPE["MS_SILENT"], sub
        )
        if ret == 0:
            SysMgr.printInfo("mounted cgroup '%s' on '%s'" % (sub, path))
            return True
        else:
            # get error reason #
            reason = SysMgr.getErrReason()
            if reason and reason != "N/A":
                reason = " because " + reason

            SysMgr.printWarn(
                "failed to mount cgroup '%s' on '%s'%s" % (sub, path, reason),
                True,
            )
            return False

    @staticmethod
    def mountFsCmd(fs, path):
        # check root permission #
        SysMgr.checkRootPerm(msg="mount %s" % fs)

        # mount debugfs #
        SysMgr.mountCmd = "mount -t %s nodev %s 2> /dev/null" % (fs, path)
        os.system(SysMgr.mountCmd)

    @staticmethod
    def mountTracefs():
        # mount tracefs #
        SysMgr.mountFsCmd("tracefs", SysMgr.defaultTracefsPath)

        # check tracefs #
        SysMgr.tracefsPath = SysMgr.getTracefsPath(mount=False)
        if not SysMgr.tracefsPath:
            SysMgr.printWarn("failed to mount tracefs to trace events")

    @staticmethod
    def mountDebugfs():
        # mount debugfs #
        SysMgr.mountFsCmd("debugfs", SysMgr.defaultDebugfsPath)

        # check debugfs #
        SysMgr.debugfsPath = SysMgr.getDebugfsPath(mount=False)
        if not SysMgr.debugfsPath:
            SysMgr.printErr("failed to mount debugfs to trace events")
            sys.exit(-1)

    @staticmethod
    def handleEventInput():
        def _sendEvent(event, ip, port):
            # get uptime #
            uptime = SysMgr.getUptime()

            # convert event name #
            for idx, item in enumerate(list(event)):
                if not item.startswith("EVENT_"):
                    event[idx] = "EVENT_%s" % item

            SysMgr.sendEvents(ip, port, event, uptime)

        # get event #
        if SysMgr.hasMainArg():
            event = SysMgr.getMainArgs(False)
        elif SysMgr.inputParam:
            event = SysMgr.inputParam.split(",")
            event = UtilMgr.cleanItem(event, False)
        else:
            event = None

        # mount debug fs #
        if not SysMgr.getTraceEventPath():
            SysMgr.printWarn("failed to get tracefs/debugfs mount point", True)

        # check target address #
        if SysMgr.remoteServObj:
            # use default address #
            if SysMgr.remoteServObj == "NONE":
                ip = SysMgr.localServObj.ip
                port = SysMgr.defaultServPort
            else:
                ip = SysMgr.remoteServObj.ip
                port = SysMgr.remoteServObj.port
        else:
            ip = port = None

            # check root permission #
            SysMgr.checkRootPerm(
                msg="send event, try to use the target address"
            )

        # convert pid #
        try:
            if not ip:
                target = list(set(list(map(long, SysMgr.filterGroup))))
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                "failed to get PID '%s' for Guider processes"
                % ", ".join(SysMgr.filterGroup),
                True,
            )
            sys.exit(-1)

        # oneshot #
        if event:
            for item in event:
                SysMgr.writeEvent("EVENT_%s" % item)

            # deliver events to specific address #
            if ip:
                _sendEvent(event, ip, port)
            # deliver events to specific processes #
            else:
                SysMgr.broadcastEvent(event, target)

            return

        while 1:
            SysMgr.printStat("input event name... [ STOP(Ctrl+c) ]")

            # get events from user input #
            try:
                event = sys.stdin.readline().strip()
            except SystemExit:
                sys.exit(0)
            except:
                continue

            # convert events #
            if event:
                event = UtilMgr.cleanItem(event.split(","), False)
            else:
                event = ["USER"]

            # deliver events to specific address #
            if ip:
                _sendEvent(event, ip, port)
            # deliver events to specific processes #
            else:
                SysMgr.broadcastEvent(event, target)

    @staticmethod
    def getSocketAddrList(addrList):
        portList = []

        # get udp list #
        portList += SysMgr.getUdpList(addrList)

        # get tcp list #
        portList += SysMgr.getTcpList(addrList)

        return list(set(portList))

    @staticmethod
    def getProcSocketObjs(pid):
        socketAddrList = []
        fdlistPath = "%s/%s/fd" % (SysMgr.procPath, pid)

        # save file info per process #
        try:
            fdlist = os.listdir(fdlistPath)
        except:
            SysMgr.printOpenWarn(fdlistPath)
            return socketAddrList

        # save fd info of process #
        for fd in fdlist:
            try:
                long(fd)
            except:
                continue

            try:
                # add file info into fdList #
                fdPath = "%s/%s" % (fdlistPath, fd)
                filename = os.readlink(fdPath)

                if filename.startswith("socket"):
                    socketAddrList.append(filename.split("[")[1][:-1])
            except:
                SysMgr.printOpenWarn(fdPath)

        return socketAddrList

    @staticmethod
    def getProcNetAddrs(name):
        if not SysMgr.isLinux or not name:
            return None

        # get pids #
        pids = SysMgr.getProcPids(name)

        # get only Guider server processes #
        if len(pids) > 1 and name == __module__:
            servers = []
            for pid in pids:
                try:
                    cmdline = SysMgr.getCmdline(pid).split()
                    if len(cmdline) > 2 and cmdline[2] == "server":
                        servers.append(pid)
                except:
                    SysMgr.printWarn(
                        "failed to get cmdline for %s(%s)"
                        % (pid, SysMgr.getComm(pid, cache=True)),
                        True,
                    )
            pids = servers

        # get target network info #
        if len(pids) == 1:
            # get socket objects #
            objs = SysMgr.getProcSocketObjs(pids[0])

            # get bind address #
            addrs = SysMgr.getSocketAddrList(objs)
            if not addrs:
                SysMgr.printWarn(
                    "failed to get socket attribute of server", True
                )
                return None

            # get server address #
            addr = addrs[0]

            return addr[addr.find(">") + 1 :]
        elif len(pids) > 1:
            SysMgr.printWarn("Found multiple %s processes" % name, True)
            SysMgr.printBgProcs(cache=True)
        else:
            SysMgr.printWarn(
                "failed to find %s process using PID" % name, True
            )

        return None

    @staticmethod
    def getProcPids(name):
        if not SysMgr.isLinux:
            return None

        pidList = []
        myPid = str(SysMgr.pid)

        pids = SysMgr.getPidList()
        for pid in pids:
            if myPid == pid:
                continue

            # check pid #
            try:
                nrPid = long(pid)
            except:
                continue

            # check comm #
            comm = SysMgr.getComm(pid)
            if comm and comm.startswith(name):
                pidList.append(nrPid)

        return pidList

    @staticmethod
    def getStartTime(pid=None):
        try:
            if not pid:
                pid = "self"

            statPath = "%s/%s/stat" % (SysMgr.procPath, pid)
            with open(statPath, "r") as fd:
                stat = fd.read().split()
                runtimeIdx = ConfigMgr.PROCSTAT_ATTR.index("STARTTIME")
                return float(stat[runtimeIdx]) / 100
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn(
                "failed to get start time of %s(%s)"
                % (SysMgr.getComm(pid), pid),
                True,
                True,
            )

    @staticmethod
    def getRuntime(pid=None, sec=False):
        # get start time #
        start = SysMgr.getStartTime(pid)

        # init start time #
        if not pid and SysMgr.startRunTime == 0:
            SysMgr.startRunTime = start

        # calculate runtime #
        runtime = SysMgr.getUptime() - start

        # return runtime #
        if sec:
            return runtime
        else:
            return UtilMgr.convTime(long(runtime))

    @staticmethod
    def getUptime():
        if not SysMgr.isLinux:
            # get psutil object #
            try:
                psutil = SysMgr.getPkg("psutil", False)
                if not psutil:
                    return -1
                return time.time() - psutil.boot_time()
            except SystemExit:
                sys.exit(0)
            except:
                return -1

        # read uptime buf #
        try:
            uptime = SysMgr.readProcStat(
                SysMgr.uptimeFd, "uptime", SysMgr, "uptimeFd"
            )
            return float(uptime[0].split(None, 1)[0])
        except SystemExit:
            sys.exit(0)
        except:
            return -1

    @staticmethod
    def updateUptime():
        SysMgr.prevUptime = SysMgr.uptime
        SysMgr.uptime = SysMgr.getUptime()
        SysMgr.uptimeDiff = SysMgr.uptime - SysMgr.prevUptime

        if SysMgr.dltTimeEnable:
            SysMgr.dltTime = UtilMgr.getClockTime(dlt=True)

        return SysMgr.uptime

    @staticmethod
    def broadcastEvent(event, pids=[]):
        if not isinstance(event, list):
            event = [event]

        # convert event name #
        for idx, item in enumerate(list(event)):
            if not item.startswith("EVENT_"):
                event[idx] = "EVENT_%s" % item

        # get pid list of Guider processes #
        if not pids:
            pids = SysMgr.getProcPids(__module__)
            if not pids:
                if SysMgr.checkMode("event"):
                    print("\nno process in the background\n")
                else:
                    SysMgr.printWarn(
                        "failed to find %s process to send event" % __module__
                    )
                return []

        # update uptime #
        uptime = SysMgr.getUptime()

        # get socket inode address list of Guider processes #
        for pid in pids:
            # get UDP port list of Guider processes #
            if pid in SysMgr.netAddrCache:
                addrs = SysMgr.netAddrCache[pid]
            else:
                objs = SysMgr.getProcSocketObjs(pid)
                addrs = SysMgr.getSocketAddrList(objs)
                SysMgr.netAddrCache[pid] = addrs

            # send event to Guider processes #
            for addr in addrs:
                try:
                    attr, addr = addr.split(">", 1)
                    ip, port = addr.split(":")
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printWarn(
                        "failed to use '%s' as the remote address" % addr,
                        reason=True,
                    )
                    continue

                SysMgr.sendEvents(ip, port, event, uptime, pid)

        return pids

    @staticmethod
    def reorderProcBuffer(buf):
        timeList = []

        # get time sequence #
        for d in buf:
            try:
                sd = UtilMgr.lstrip(d.lstrip(), "[Top Info] [Time: ")
                uptime = float(sd.split("]", 1)[0])
                timeList.append(uptime)
                if len(timeList) >= 2:
                    break
            except SystemExit:
                sys.exit(0)
            except:
                pass

        # change data sequence #
        if len(timeList) >= 2 and timeList[0] < timeList[1]:
            buf.reverse()

    @staticmethod
    def sendEvents(ip, port, event, uptime=None, pid=None, verb=True):
        # create a network object #
        netObj = NetworkMgr("client", ip, long(port))
        ip, port = netObj.ip, netObj.port

        # check network object #
        if not ip or not port:
            SysMgr.printWarn(
                "failed to use '%s:%s' as the remote address" % (ip, port)
            )
            return

        # get uptime #
        if not uptime:
            uptime = SysMgr.getUptime()

        # get comm #
        if pid:
            comm = SysMgr.getComm(pid, save=True)
            procInfo = " <%s(%s)>" % (comm, pid)
        else:
            comm = None
            procInfo = ""

        # send events #
        for item in event:
            try:
                netObj.request = item
                netObj.send("%s@%s" % (item, uptime))
                SysMgr.printInfo(
                    "sent event '%s' to %s:%s%s" % (item, ip, port, procInfo)
                )

                # check connection events #
                if not (
                    item.startswith("EVENT_CMD_GETDUMP")
                    or item.startswith("EVENT_CMD_GETSUM")
                    or item == "EVENT_CMD_GETINFO"
                ):
                    continue

                # set network info #
                if not SysMgr.localServObj:
                    NetworkMgr.setServerNetwork(
                        None, None, reuse=False, weakPort=True
                    )

                # receive data #
                data = NetworkMgr.recvData(netObj, SysMgr.localServObj.ip)
                if data:
                    data = data.decode()
                else:
                    raise Exception("no data")

                if not verb:
                    return data

                # processing data #
                if (
                    item.startswith("EVENT_CMD_GETDUMP")
                    or item == "EVENT_CMD_GETINFO"
                ):
                    SysMgr.printPipe(data, pager=False)
                else:
                    # split each interval stats #
                    try:
                        data = data.replace("\n\n", "NEWSTAT\n\n")
                        if "CLEANCHAR" in SysMgr.environList:
                            data = data.replace(
                                "[Top Info] ", "NEWSTAT[Top Info] "
                            )

                        SysMgr.procBuffer = data.split("NEWSTAT")
                        TaskAnalyzer.getSummaryData(None, update=True)

                        # reorder buffer #
                        SysMgr.reorderProcBuffer(SysMgr.procBuffer)

                        # print intervals #
                        TaskAnalyzer.printIntervalUsage(current=False)
                    except SystemExit:
                        sys.exit(0)
                    except:
                        SysMgr.printErr("failed to print summary", True)

                return data
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printWarn(
                    "failed to send event '%s' to %s:%s%s"
                    % (item, ip, port, procInfo),
                    True,
                    True,
                )

    @staticmethod
    def updateBgProcs(cache=False):
        if SysMgr.bgProcList and cache:
            return

        SysMgr.bgProcList = SysMgr.getBgProcList()

    @staticmethod
    def checkBgProcs():
        # scan background tasks #
        SysMgr.updateBgProcs()

        # ignore my siblings #
        if SysMgr.bgProcList:
            ppid = os.getppid()
            myComm = SysMgr.getComm(SysMgr.pid)
            parentComm = SysMgr.getComm(ppid)
            if myComm == parentComm:
                bgList = SysMgr.bgProcList.split("\n")
                tempBgList = list(bgList)
                for idx, line in enumerate(tempBgList):
                    rline = line.lstrip()
                    if not rline or rline.startswith("("):
                        continue
                    pid = long(line.split(None, 1)[0])
                    if pid == ppid:
                        bgList.pop(idx)
                SysMgr.bgProcList = "\n".join(bgList)

        if SysMgr.bgProcList:
            SysMgr.printWarn(SysMgr.getBgProcString(), True)

    @staticmethod
    def getBgProcCount(update=True, cache=False):
        if update:
            SysMgr.updateBgProcs(cache)

        cnt = 0
        for line in SysMgr.bgProcList.split("\n"):
            rline = line.lstrip()
            if rline and not rline.startswith("("):
                cnt += 1

        return cnt

    @staticmethod
    def getBgProcString():
        if not SysMgr.bgProcList:
            return ""

        procList = SysMgr.bgProcList
        procs = SysMgr.getBgProcCount(update=False)
        if not procs:
            return ""

        bgStr = "\n[Running Process] [TOTAL: %s]\n" % procs
        bgStr = "%s%s\n%7s %7s %7s %8s %5s %12s %s\n%s\n" % (
            bgStr,
            twoLine,
            "PID",
            "PPID",
            "COMM",
            "STATE",
            "RSS",
            "RUNTIME",
            "COMMAND",
            oneLine,
        )
        bgStr = "%s%s%s" % (bgStr, procList, oneLine)

        return bgStr

    @staticmethod
    def printBgProcs(cache=False, pager=False):
        if SysMgr.jsonEnable:
            result = SysMgr.getBgProcList(isJson=True)
            jsonResult = UtilMgr.convDict2Str(
                result, pretty=not SysMgr.streamEnable
            )
            SysMgr.printPipe(jsonResult)
            return

        SysMgr.updateBgProcs(cache)
        if not SysMgr.bgProcList:
            SysMgr.printPipe("\nno process in the background\n", pager=False)
        else:
            SysMgr.printPipe(SysMgr.getBgProcString(), pager=False)

    @staticmethod
    def getTgids(
        name, isThread=True, sibling=False, main=False, inc=False, cache=False
    ):
        # get tids #
        pids = SysMgr.getTids(name, isThread, sibling, main, inc, cache)
        if not pids:
            return pids

        # convert to tgids #
        tgids = []
        for tid in pids:
            tgids.append(SysMgr.getTgid(tid))
        pids = list(set(tgids))

        return pids

    @staticmethod
    def getTids(
        name, isThread=True, sibling=False, main=False, inc=False, cache=False
    ):
        def _checkWait(pidList, waitTime):
            if not waitTime:
                return False

            SysMgr.printWarn(
                "wait for '%s' for %s sec because there is no task yet"
                % (name, waitTime)
            )

            time.sleep(waitTime)

            return True

        # define wait time for target #
        waitTime = (
            (UtilMgr.getEnvironNum("WAITTASK", False, 100, False) / 1000)
            if "WAITTASK" in SysMgr.environList
            else 0
        )

        # define lists #
        pidList = []
        prevTargetList = []
        tidList = []

        # check input #
        if not name:
            return pidList

        # split items #
        try:
            nameList = name.split("|")
        except SystemExit:
            sys.exit(0)
        except:
            nameList = [name]

        # check tid #
        if not SysMgr.isLinux:
            try:
                psutil = SysMgr.getPkg("psutil")
                procs = psutil.process_iter(
                    attrs=["pid", "name"], ad_value=None
                )

                for proc in procs:
                    pid = proc.info["pid"]
                    comm = proc.info["name"]
                    if pid in nameList:
                        pidList.append(pid)
                    elif UtilMgr.isValidStr(comm, nameList, inc=inc):
                        pidList.append(pid)

                return pidList
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printWarn(
                    "failed to find tasks related to '%s'" % name, reason=True
                )
                return pidList

        # check tid #
        for item in nameList:
            if "ONLYCOMM" in SysMgr.environList:
                break

            # check task #
            if not UtilMgr.isNumber(item) or not os.path.isdir(
                "%s/%s" % (SysMgr.procPath, item)
            ):
                continue

            # add sibling tasks #
            if sibling:
                path = "%s/%s/task" % (SysMgr.procPath, item)
                for pid in os.listdir(path):
                    if pid.isdigit():
                        pidList.append(pid)
            # add only main task #
            elif main:
                pidList.append({item, SysMgr.getTgid(item)})
            # add a task #
            else:
                pidList.append(item)

            tidList.append(item)

        # pop tids from list #
        nameList = list(set(nameList) - set(tidList))
        if not nameList or "ONLYPID" in SysMgr.environList:
            return pidList

        # set ONLYPROC variable #
        onlyProc = "ONLYPROC" in SysMgr.environList

        # set PIDCACHE variable #
        pidcache = "NOPIDCACHE" in SysMgr.environList

        # set EXCEPTCOMM variable #
        exceptList = SysMgr.environList.get("EXCEPTCOMM", [])

        # check only kernel task option #
        onlyKernel = "ONLYKERNEL" in SysMgr.environList

        # check only user task option #
        onlyUser = "ONLYUSER" in SysMgr.environList

        # define getter #
        if "TARGETEXE" in SysMgr.environList:
            getter = SysMgr.getExeName
        elif "TARGETCMD" in SysMgr.environList:
            getter = SysMgr.getCmdline
        else:
            getter = SysMgr.getComm

        while 1:
            # set check list #
            targetList = SysMgr.getPidList()
            curList = set(targetList) - set(prevTargetList)
            if pidcache:
                prevTargetList = targetList

            for pid in curList:
                if not pid.isdigit():
                    continue

                # check task type #
                if onlyKernel and not SysMgr.isKernelTask(pid):
                    continue
                elif onlyUser and SysMgr.isKernelTask(pid):
                    continue

                # process #
                if not isThread:
                    # get comm #
                    comm = getter(pid, cache)
                    if exceptList and UtilMgr.isValidStr(
                        comm, exceptList, inc=inc
                    ):
                        continue
                    elif UtilMgr.isValidStr(comm, nameList, inc=inc):
                        pidList.append(pid)
                    continue

                # thread #
                try:
                    threadPath = "%s/%s/task" % (SysMgr.procPath, pid)
                    tids = os.listdir(threadPath)
                except SystemExit:
                    sys.exit(0)
                except:
                    continue

                for tid in tids:
                    if not tid.isdigit():
                        continue

                    # get comm #
                    comm = getter(tid, cache=cache)
                    if exceptList and UtilMgr.isValidStr(
                        comm, exceptList, inc=inc
                    ):
                        continue
                    elif not UtilMgr.isValidStr(comm, nameList, inc=inc):
                        continue

                    # include all siblings #
                    if sibling:
                        pidList += tids
                        break

                    # include the main thread #
                    if main or onlyProc:
                        pidList.append(pid)

                        # skip other threads #
                        if onlyProc:
                            continue

                    # include a thread #
                    pidList.append(tid)

            # check retry condition #
            if not pidList and _checkWait(pidList, waitTime):
                continue

            return list(set(pidList))

    @staticmethod
    def mount(source, path, fs, flags=0, data=0):
        try:
            # get ctypes object #
            SysMgr.importPkgItems("ctypes")

            # device #
            if source:
                sourcep = c_char_p(source.encode())
            else:
                sourcep = 0

            # path #
            if path:
                pathp = c_char_p(path.encode())
            else:
                pathp = 0

            # filesystem #
            if fs:
                fsp = c_char_p(fs.encode())
            else:
                fsp = 0

            if data:
                data = c_char_p(data.encode())

            # call mount syscall #
            ret = SysMgr.syscall("mount", sourcep, pathp, fsp, flags, data)
            if ret != 0:
                SysMgr.printWarn(
                    "failed to mount(%s, %s, %s, %s, %s)"
                    % (source, path, fs, flags, data)
                )
            return ret
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn(
                "failed to call mount(%s, %s, %s, %s, %s)"
                % (source, path, fs, flags, data),
                True,
                True,
            )

    @staticmethod
    def umount(target, flags=0):
        try:
            # get ctypes object #
            SysMgr.importPkgItems("ctypes")

            # path #
            if target:
                targetp = c_char_p(target.encode())
            else:
                targetp = 0

            # call umount2 syscall #
            ret = SysMgr.syscall("umount2", targetp, flags)
            if ret != 0:
                SysMgr.printWarn(
                    "failed to call umount2(%s, %s)" % (target, flags)
                )
            return ret
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn(
                "failed to call umount2(%s, %s)" % (target, flags), True, True
            )

    @staticmethod
    def readahead(path, offset, size=0, closeFd=False, raMax=0):
        # pylint: disable=not-callable
        if not SysMgr.raFunc:
            # load libc #
            SysMgr.loadLibcObj()

            # check readahead #
            if not hasattr(SysMgr.libcObj, "readahead"):
                SysMgr.printErr("no readahead function in libc")
                sys.exit(-1)

            # define readahead #
            SysMgr.raFunc = getattr(SysMgr.libcObj, "readahead")
            SysMgr.raFunc.argtypes = [c_int, c_int64, c_int]
            SysMgr.raFunc.restype = c_int

        # get fd #
        try:
            fd = SysMgr.rawFdCache[path]
        except SystemExit:
            sys.exit(0)
        except:
            try:
                fd = os.open(path, os.O_RDONLY)
                SysMgr.rawFdCache[path] = fd
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printOpenErr(path)
                return False

        # change size 0 to total #
        if not size:
            try:
                size = os.stat(path).st_size
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr(
                    "failed to get file size for '%s'" % path, True
                )
                return True

        # split readahead chunks #
        if raMax:
            chunk = raMax
        else:
            chunk = SysMgr.raMaxSize
        remain = size
        coffset = offset

        # readahead chunks #
        while 1:
            # split by chunk size #
            if remain < chunk:
                csize = remain
            else:
                csize = chunk

            # readahead a chunk #
            os.lseek(fd, coffset, 0)
            SysMgr.raFunc(c_int(fd), c_int64(coffset), c_int(csize))

            # check break condition #
            if csize < chunk:
                break

            # update offset and remain size #
            coffset += chunk
            remain -= chunk

        # close fd #
        if closeFd:
            try:
                os.close(fd)
            except SystemExit:
                sys.exit(0)
            except:
                pass

            # remove cache #
            SysMgr.rawFdCache.pop(path, None)

        return True

    @staticmethod
    def doReadahead(path):
        # change priority #
        SysMgr.setLowPriority(force=False, cpuVal=10, verb=True)

        # get readahead items #
        (
            raPath,
            raMin,
            raAllowList,
            raDenyList,
            raAddList,
        ) = FileAnalyzer.getReadaheadItems()

        # get readahead max size #
        raMax = SysMgr.raMaxSize
        if "RAMAX" in SysMgr.environList:
            try:
                raMax = long(SysMgr.environList["RAMAX"][0])
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr(
                    "failed to set the maximum size to '%s' for readahead chunk"
                    % raMax,
                    True,
                )

        # add readahead files #
        if "RAADDITEM" in SysMgr.environList:
            for cond in SysMgr.environList["RAADDITEM"][0].split("|"):
                res = UtilMgr.convPath(cond)
                if res:
                    raAddList += res

        # add readahead deny files #
        if "RADENYITEM" in SysMgr.environList:
            raDenyList += SysMgr.environList["RADENYITEM"][0].split("|")

        # set action #
        if "FLUSH" in SysMgr.environList:
            flushPages = True
            action = "flush"
        else:
            flushPages = False
            action = "readahead"

        # get absolute path #
        path = os.path.realpath(path)
        SysMgr.printInfo("start %s from '%s'" % (action, path))
        startTime = SysMgr.getUptime()

        # open list #
        try:
            raData = open(path, "rb").read()
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printOpenErr(path)
            sys.exit(-1)

        # set system maximum fd number #
        SysMgr.setMaxFd()

        # get name list size #
        try:
            pos = 4
            chunkSize = 20
            nameListSize = struct.unpack("I", raData[:pos])[0]
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to get name list size for readahead", True)
            sys.exit(-1)

        # get file list #
        try:
            nameList = raData[pos : pos + nameListSize]
            nameList = nameList.decode().split("#")
            pos = pos + nameListSize
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to get name list for readahead", True)

        # check filter #
        raAllowIndexList = []
        raDenyIndexList = []
        if raAllowList or raDenyList:
            for idx, path in enumerate(nameList):
                # check allow list #
                if raAllowList and not UtilMgr.isValidStr(path, raAllowList):
                    raAllowIndexList.append(idx)
                # check deny list #
                elif raDenyList and UtilMgr.isValidStr(path, raDenyList):
                    raDenyIndexList.append(idx)

        # init variabels #
        totalSize = 0
        failFileList = {}
        summaryList = {}
        printSummary = "PRINTSUMMARY" in SysMgr.environList

        # do readahead #
        while 1:
            try:
                # check break condition #
                nextPos = pos + chunkSize
                if len(raData) < nextPos:
                    break

                # get a chunk #
                fid, offset, size = struct.unpack("HQI", raData[pos:nextPos])

                # update values #
                pos = nextPos

                # check fail list #
                if fid in failFileList:
                    continue
                elif fid in raDenyIndexList:
                    continue
                elif raAllowList and not fid in raAllowIndexList:
                    continue
                elif raMin > size:
                    continue

                # get file name #
                fname = nameList[fid]

                # print workload #
                if SysMgr.warnEnable:
                    SysMgr.printWarn(
                        "try to %s %s|%s|%s" % (action, fname, offset, size)
                    )

                # readahead a chunk #
                if flushPages:
                    ret = SysMgr.fadvise(fname, None, offset, size)
                    ret = True
                else:
                    ret = SysMgr.readahead(fname, offset, size, raMax=raMax)
                if not ret:
                    failFileList[fid] = 0
                    continue

                # add to summary #
                if printSummary:
                    summaryList.setdefault(fname, 0)
                    summaryList[fname] += size

                totalSize += size
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printWarn("failed to %s" % action, True, True)

        # readahead from add list #
        for item in raAddList:
            # parse file info #
            finfo = item.split(":")
            try:
                item = finfo[0]
                offset = size = 0

                if len(finfo) == 1:
                    pass
                elif len(finfo) == 2:
                    if finfo[1]:
                        size = UtilMgr.convUnit2Size(finfo[1])
                else:
                    offset = UtilMgr.convUnit2Size(finfo[1])
                    if finfo[2]:
                        size = UtilMgr.convUnit2Size(finfo[2])

                # set full size #
                if size == 0:
                    size = UtilMgr.getFileSize(item, False)

                # print workload #
                if SysMgr.warnEnable:
                    SysMgr.printWarn(
                        "try to %s %s|%s|%s" % (action, item, offset, size)
                    )

                # readahead a chunk #
                if flushPages:
                    ret = SysMgr.fadvise(item, None, offset, size)
                else:
                    ret = SysMgr.readahead(item, offset, size, raMax=raMax)
                # add to summary #
                if ret and printSummary:
                    summaryList.setdefault(item, 0)
                    summaryList[fname] += size

                totalSize += size
            except SystemExit:
                sys.exit(0)
            except:
                fname = SysMgr.environList["RAADDLIST"][0]
                SysMgr.printErr(
                    "failed to %s '%s' from '%s'" % (action, item, fname), True
                )

        # get elapsed time #
        elapsed = SysMgr.getUptime() - startTime

        # get CPU usage #
        try:
            dobj = Debugger(pid=os.getpid(), attach=False)
            dobj.initValues()
            dobj.getCpuUsage()
            cpu = "using CPU %s%% " % dobj.prevCpuStat[0]
        except SystemExit:
            sys.exit(0)
        except:
            cpu = ""

        # check error condition #
        if not totalSize:
            SysMgr.printErr("failed to %s from '%s'" % (action, path))
            sys.exit(-1)

        # print results #
        logStr = "finished %s a total of %s data %sfor %.3f sec" % (
            action,
            UtilMgr.convSize2Unit(totalSize),
            cpu,
            elapsed,
        )
        SysMgr.printInfo(logStr)
        LogMgr.doLogKmsg(logStr)

        if printSummary:
            SysMgr.printPipe(
                "[Readahead Summary] (Size: %s) (Elapsed: %.3f sec) %s\n%s\n"
                % (
                    UtilMgr.convSize2Unit(totalSize),
                    elapsed,
                    "(%s)" % cpu.strip("using ") if cpu else "",
                    twoLine,
                )
            )
            SysMgr.printPipe(
                "{0:>8} {1:1}\n{2:1}".format("SIZE", "FILE", twoLine)
            )

            for path, size in sorted(
                summaryList.items(), key=lambda e: e[1], reverse=True
            ):
                SysMgr.printPipe(
                    "{0:>8} {1:1}".format(UtilMgr.convSize2Unit(size), path)
                )

            if not summaryList:
                SysMgr.printPipe("    None")
            SysMgr.printPipe(oneLine)

        # check wait option #
        if "WAIT" in SysMgr.environList:
            SysMgr.waitEvent(False, True)

    @staticmethod
    def getBgProcList(checkCmdline=False, isJson=False):
        printBuf = ""
        printDict = {}
        myPid = str(SysMgr.pid)
        gstatList = ConfigMgr.PROCSTAT_ATTR
        commIdx = ConfigMgr.PROCSTAT_ATTR.index("COMM")

        # update uptime #
        uptime = SysMgr.getUptime()

        # get my info #
        myComm = SysMgr.getComm(SysMgr.pid)
        myCmdline = SysMgr.getCmdline(SysMgr.pid, True)
        myExeName = SysMgr.getExeName(SysMgr.pid)

        # get pid list #
        pids = SysMgr.getPidList()

        for pid in pids:
            if not pid.isdigit() or myPid == pid:
                continue

            # ignore tasks except for python #
            exeName = SysMgr.getExeName(pid, verb=False)
            if myExeName != exeName:
                continue

            # check comm #
            comm = SysMgr.getComm(pid)
            if not comm or not comm.startswith(myComm):
                continue

            # check cmdline again #
            if myComm != __module__ and checkCmdline:
                cmdlineList = SysMgr.getCmdline(pid, True)
                if len(cmdlineList) > 2 and myCmdline[:2] != cmdlineList[:2]:
                    continue

            runtime = "?"

            # info #
            try:
                # other OS #
                if not SysMgr.isLinux:
                    psutil = SysMgr.getPkg("psutil", False)
                    proc = psutil.Process(long(pid))
                    ppid = proc.ppid()
                    runtime = long(time.time() - proc.create_time())
                    cmdline = SysMgr.getCmdline(pid)
                    state = proc.status()
                    rss = UtilMgr.convSize2Unit(proc.memory_info()[0], True)
                    raise Exception()

                statPath = "%s/%s/stat" % (SysMgr.procPath, pid)
                with open(statPath, "r") as fd:
                    statList = fd.read().split()

                if len(statList) + 1 < len(ConfigMgr.PROCSTAT_ATTR):
                    SysMgr.printWarn(
                        "failed to read stat for %s(%s)" % (comm, pid)
                    )
                    continue

                statList = SysMgr.mergeCommStat(statList, commIdx)

                # runtime #
                procStart = float(statList[gstatList.index("STARTTIME")]) / 100
                runtime = long(uptime - procStart)

                # ppid #
                ppid = statList[gstatList.index("PPID")]

                # state #
                try:
                    state = ConfigMgr.PROC_STAT_TYPE[
                        statList[gstatList.index("STATE")]
                    ]
                except SystemExit:
                    sys.exit(0)
                except:
                    state = "N/A"

                # rss #
                rss = long(statList[gstatList.index("RSS")])
                rss = UtilMgr.convSize2Unit(rss << 12, True)
            except SystemExit:
                sys.exit(0)
            except:
                pass

            # runtime #
            if runtime != "?":
                runtime = UtilMgr.convTime(runtime)

            # socket #
            try:
                if not SysMgr.isLinux:
                    raise Exception()

                objs = SysMgr.getProcSocketObjs(pid)
                addrs = SysMgr.getSocketAddrList(objs)

                # merge address #
                netDict = {}
                for item in addrs:
                    ret = item.split("/")
                    if len(ret) == 2:
                        addr, stat = ret
                    else:
                        addr = ret[0]
                        stat = None

                    if not addr in netDict:
                        netDict[addr] = []

                    if stat:
                        netDict[addr].append(stat)

                # build string #
                netList = ""
                for addr, stat in netDict.items():
                    if stat:
                        netList = "%s%s/%s|" % (netList, addr, "/".join(stat))
                    else:
                        netList = "%s%s|" % (netList, addr)
                if netList:
                    network = "(%s)" % netList[:-1]
                else:
                    network = ""
            except SystemExit:
                sys.exit(0)
            except:
                network = ""

            # cmdline #
            try:
                cmdline = SysMgr.getCmdline(pid)
            except SystemExit:
                sys.exit(0)
            except:
                cmdline = "?"

            # build #
            if isJson:
                printDict[pid] = {
                    "comm": comm,
                    "ppid": ppid,
                    "state": state,
                    "rss": rss,
                    "runtime": runtime,
                    "cmdline": cmdline,
                    "network": network,
                }
            else:
                status = "%7s %7s %7s %8s %5s %12s" % (
                    pid,
                    ppid,
                    comm,
                    state,
                    rss,
                    runtime,
                )
                indent = len(status) * " "
                addline = "%s %s" % (cmdline, network)

                remain = SysMgr.ttyCols - (len(status) + len(addline))
                if remain < 0:
                    status = "%s %s\n%s %s" % (
                        status,
                        cmdline,
                        indent,
                        network,
                    )
                else:
                    status = "%s %s" % (status, addline)

                printBuf = "%s%s\n" % (printBuf, status)

        # return result #
        if isJson:
            return printDict
        else:
            return printBuf

    @staticmethod
    def waitUptime(deadline):
        while 1:
            current = SysMgr.getUptime()
            if current <= deadline:
                time.sleep(0.1)
                SysMgr.printWarn(
                    "%.1f seconds left until %s seconds"
                    % (deadline - current, deadline)
                )
            else:
                return

    @staticmethod
    def waitUserInput(wait=0, msg=None, newline=True, force=False):
        # check condition #
        if force:
            pass
        elif (
            SysMgr.outPath
            or SysMgr.bgStatus
            or not sys.stdin
            or SysMgr.checkMode("rtop")
            or SysMgr.checkMode("btop")
            or not SysMgr.selectEnable
            or SysMgr.remoteRun
        ):
            return

        # get select object #
        selectObj = SysMgr.getPkg("select", False)
        if not selectObj:
            SysMgr.selectEnable = False
            return

        # convert infinite value #
        if wait is True:
            wait = 0

        # set default message #
        if msg == "DEFAULT":
            msg = "\ninput ENTER to continue or Ctrl+c to quit..."
        elif SysMgr.idList:
            msg = "input a task index... ( Help / Quit)"
        elif msg:
            pass
        else:
            msg = "input a command... ( Help / Quit )"

        # wait for user input #
        try:
            SysMgr.inWaitStatus = True

            # set suffix #
            if newline:
                suffix = "\n"
            else:
                suffix = ""

            def _handleInput():
                sys.stdout.write("\b" * SysMgr.ttyCols)
                sys.stdout.write(msg + suffix)
                sys.stdout.flush()

                # flush buffered enter key #
                sys.stdin.readline()

                sys.stdout.write("=> ")
                sys.stdout.flush()

                # process user input #
                SysMgr.procUserInput(sys.stdin.readline())

            # set input list #
            if SysMgr.bgStatus:
                inputList = []
            else:
                stdinPath = SysMgr.getFdName(SysMgr.pid, sys.stdin.fileno())
                if stdinPath and stdinPath.startswith("/dev/null"):
                    inputList = []
                else:
                    inputList = [sys.stdin]

            # there was user input #
            if SysMgr.isWindows:
                if inputList and SysMgr.getPkg("msvcrt").kbhit():
                    _handleInput()
                else:
                    time.sleep(wait)
            elif selectObj.select(inputList, [], [], wait) == (
                [sys.stdin],
                [],
                [],
            ):
                _handleInput()
            elif wait == 0:
                sys.stdout.write(msg + suffix)
                sys.stdout.flush()
                if force:
                    SysMgr.waitEvent()
                else:
                    sys.stdin.readline()
                    sys.stdout.write("\033[F")
        except SystemExit:
            sys.exit(0)
        except Exception as ex:
            SysMgr.printWarn("failed to read user input", reason=True)
            if type(ex).__name__ == "IOError":
                sys.stdin = None
        finally:
            SysMgr.inWaitStatus = False

        return True

    @staticmethod
    def procUserInput(uinput):
        def _printHelp():
            SysMgr.printPipe(
                """
[Filter]   {COMM|PID}
  exam) f init, 1234

[Sched]    {SCHED:PRIO:COMM|PID}
  exam) s r:1:123, c:-1:1234

[Kill]     {-SIGNAL} {COMM|PID}
  exam) k -sigstop 123, a.out

[Affinity] {MASK} {COMM|PID}
  exam) a 1f 123, a.out

[Sort]     {VAL}
  exam) S p

[Tree]     {COMM|PID}
  exam) t 123

[Option]   {VAL}
  exam) o -e bs -g task

[Run]      {COMMAND}
  exam) r utop a.out
  exam) r btrace a.out
            """
            )

            # wait for enter #
            SysMgr.printPipe("input ENTER to continue...")
            sys.stdin.readline()

        ulist = uinput.split()
        if not ulist:
            return

        cmd = ulist[0]
        ucmd = cmd.upper()

        # hotkey #
        if SysMgr.idList and cmd.isdigit():
            try:
                pid = SysMgr.idList[long(cmd)]
            except SystemExit:
                sys.exit(0)
            except:
                return

            SysMgr.executeCommand(pid=pid)
            return

        # help #
        if ucmd in ("HELP", "H"):
            _printHelp()
        # kill #
        elif ucmd in ("KILL", "K"):
            if len(ulist) > 1:
                SysMgr.sendSignalArgs(ulist[1:])
            else:
                _printHelp()
        # sched #
        elif ucmd == "SCHED" or cmd == "s":
            if len(ulist) > 1:
                SysMgr.applyPriority((" ".join(ulist[1:])))
            else:
                _printHelp()
        # sort #
        elif ucmd == "SORT" or cmd == "S":
            if len(ulist) > 1:
                SysMgr.setSortValue(ulist[1])
            else:
                _printHelp()
        # tree #
        elif ucmd == "TREE" or cmd == "t":
            SysMgr.doPstree(
                targets=ulist[1:] if len(ulist) > 1 else [], canRepeat=True
            )
        # affinity #
        elif ucmd == "AFFINITY" or cmd == "a":
            if len(ulist) > 2:
                pids = (" ".join(ulist[2:])).split(",")
                pids = SysMgr.convTaskList(pids, isThread=True)
                SysMgr.setAffinity(ulist[1], pids)
            else:
                _printHelp()
        # filter #
        elif ucmd == "FILTER" or cmd == "f":
            if len(ulist) == 1:
                SysMgr.filterGroup = []
            else:
                SysMgr.filterGroup = (" ".join(ulist[1:])).split(",")

            SysMgr.filterGroup = UtilMgr.cleanItem(SysMgr.filterGroup)

            if SysMgr.isThreadMode():
                mode = "threads"
            else:
                mode = "processes"

            SysMgr.printInfo(
                "only specific %s [ %s ] are shown"
                % (mode, ", ".join(SysMgr.filterGroup))
            )
        # option #
        elif ucmd == "OPTION" or cmd == "o":
            if len(ulist) > 1:
                SysMgr.parseAnalOption(uinput[1:].strip())
            else:
                _printHelp()
        # run #
        elif ucmd == "RUN" or cmd == "r":
            if len(ulist) > 1:
                cmd = SysMgr.getExeCmd(SysMgr.pid)
                cmd.extend(ulist[1:])

                # launch new command #
                pid = SysMgr.createProcess(cmd)
                if pid < 0:
                    sys.exit(-1)

                # ignore signals and wait for children #
                SysMgr.setIgnoreSignal()
                os.wait()
                SysMgr.setNormalSignal()
            else:
                _printHelp()
        # quit #
        elif ucmd in ("QUIT", "Q"):
            sys.exit(0)

        """
        SysMgr.printPipe("input ENTER to continue...")
        sys.stdin.readline()
        """

    @staticmethod
    def getLimitCpuInfo(limitInfo):
        SysMgr.checkRootPerm()

        if not limitInfo:
            SysMgr.printErr("no input value for task limit info")
            sys.exit(-1)

        limitList = {}
        try:
            for item in limitInfo:
                (tid, per) = item.split(":")

                # get ID for tasks #
                if tid.isdigit():
                    limitList[tid] = long(per)
                else:
                    tidList = SysMgr.getTids(tid)
                    for tid in tidList:
                        limitList[tid] = long(per)
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("wrong input value for task limit info", True)
            sys.exit(-1)

        return limitList

    @staticmethod
    def ignoreWarn():
        try:
            import warnings

            warnings.simplefilter("ignore", UserWarning)
        except:
            pass

    @staticmethod
    def executeCommand(
        cmds=None, pid=None, comm=None, uptime=True, params=None
    ):
        if not cmds:
            cmds = SysMgr.customCmd

        # execute command #
        for cmd in cmds:
            # convert PID #
            if pid:
                cmd = cmd.replace("PID", pid)

            # convert COMM #
            if comm:
                cmd = cmd.replace("COMM", comm)

            # convert TIME #
            if uptime:
                cmd = cmd.replace("TIME", str(SysMgr.uptime))

            if params:
                for item in params:
                    tcmd = "@" + item
                    if tcmd in cmd:
                        cmd = cmd.replace(tcmd, params[item])

            SysMgr.printInfo("executed '%s'" % cmd)

            # create a new process #
            ret = SysMgr.createCmdProcess(cmd)
            if ret is False or ret < 0:
                continue

            # ignore signals and wait for children #
            try:
                SysMgr.setIgnoreSignal()
                os.wait()
            except SystemExit:
                sys.exit(0)
            except:
                pass
            finally:
                try:
                    SysMgr.setNormalSignal()
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

    @staticmethod
    def getEnvList():
        def _applyList(myEnv, envList):
            for env in envList:
                var = env.split("=", 1)
                if len(var) > 1:
                    myEnv[var[0]] = var[1].rstrip(" |\n")

        try:
            # copy original variables #
            myEnv = deepcopy(os.environ)

            # clear variables #
            if "CLEARENV" in SysMgr.environList:
                exceptList = {}

                # clear all #
                if "SET" in SysMgr.environList["CLEARENV"]:
                    envlist = myEnv
                # clear specific variables #
                else:
                    envlist = SysMgr.environList["CLEARENV"]

                    # check exception list #
                    for env in envlist:
                        if not env.startswith("^"):
                            continue
                        exceptList.setdefault(env.lstrip("^"), None)
                        envlist.remove(env)

                    # check empty list #
                    if not envlist:
                        envlist = myEnv

                for name in myEnv:
                    if name in exceptList:
                        continue
                    elif name in envlist:
                        myEnv.pop(name, None)

            # parse new variables #
            if "ENV" in SysMgr.environList:
                envList = SysMgr.environList["ENV"]
            else:
                envList = []

            # parse new variables from file #
            if "ENVFILE" in SysMgr.environList:
                envFileList = SysMgr.environList["ENVFILE"]
            else:
                envFileList = []

            # apply new variables from process #
            if "ENVPROC" in SysMgr.environList:
                SysMgr.checkRootPerm()

                # get PIDs #
                tasks = SysMgr.environList["ENVPROC"]
                pids = SysMgr.convTaskList(tasks, exceptMe=True)
                if not pids:
                    SysMgr.printErr(
                        "no process related to '%s'" % ", ".join(tasks)
                    )
                    return None

                # copy variables #
                for pid in pids:
                    comm = SysMgr.getComm(pid, True)
                    myEnv.update(SysMgr.getEnv(pid, retdict=True))
                    SysMgr.printInfo(
                        "applied environment variables for %s(%s)"
                        % (comm, pid)
                    )

            # check return condition #
            if not envList and not envFileList:
                return myEnv

            # read variables from files #
            for fname in envFileList:
                try:
                    with open(os.path.realpath(fname), "r") as fd:
                        fileEnvList = fd.readlines()
                        _applyList(myEnv, fileEnvList)
                except:
                    SysMgr.printErr(
                        "failed to parse environment variable from %s" % fname,
                        True,
                    )
                    sys.exit(-1)

            # apply each variables #
            _applyList(myEnv, envList)

            return myEnv
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                "failed to parse environment variables", reason=True
            )
            return None

    @staticmethod
    def executeProcess(cmd=None, mute=False, closeFd=True):
        # get new environ variables #
        env = SysMgr.getEnvList()

        # print environment variables #
        if "PRINTENV" in SysMgr.environList:
            SysMgr.printWarn(
                (
                    "The following environment variables will be used "
                    "to execute '%s'\n%s"
                )
                % (
                    " ".join(cmd) if cmd else "Guider",
                    UtilMgr.convDict2Str(dict(env), pretty=True),
                ),
                True,
            )

        # set priority #
        if "EXECSCHED" in SysMgr.environList:
            for cmd in SysMgr.environList["EXECSCHED"]:
                SysMgr.applyPriority(cmd)

        # exec #
        try:
            SysMgr.resetFileTable(mute, closeFd)

            os.execvpe(cmd[0], cmd, env)
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to execute '%s'" % " ".join(cmd), True)
            return -1

    @staticmethod
    def initTimes(first=True):
        # update start time #
        if first:
            newtime = SysMgr.updateUptime()
        else:
            newtime = SysMgr.getUptime()
        SysMgr.startInitTime = SysMgr.startTime = newtime
        SysMgr.startRecTime = 0  # start time for Recording #
        SysMgr.startRunTime = 0  # start time for Process #

    @staticmethod
    def initSystemContext():
        if not SysMgr.outPath or "FASTINIT" in SysMgr.environList:
            SysMgr(onlyInstance=True)
        else:
            SysMgr()

    @staticmethod
    def initEnvironVars():
        def _checkVal(item, val):
            if item in os.environ and os.environ[item] == val:
                return True
            else:
                return False

        try:
            # verb #
            if _checkVal("VERB", "0"):
                SysMgr.logoEnable = False
                SysMgr.logEnable = False

            # logo #
            if _checkVal("LOGO", "0"):
                SysMgr.logoEnable = False

            # log #
            if _checkVal("LOG", "0"):
                SysMgr.logEnable = False
        except SystemExit:
            sys.exit(0)
        except:
            pass

    @staticmethod
    def parseEnvironVars():
        value = SysMgr.getOption("q")
        if not value:
            return

        itemList = UtilMgr.splitString(value)
        SysMgr.environList = UtilMgr.convList2Dict(itemList, cap=True)

        # update environment variable list #
        os.environ = SysMgr.getEnvList()

    @staticmethod
    def initEnvironment():
        # init times #
        SysMgr.initTimes()

        # save original args #
        SysMgr.origArgs = deepcopy(sys.argv)

        # register exit handler #
        atexit.register(SysMgr.doExit)

        # init environ variables #
        SysMgr.initEnvironVars()

        # increase stack depth #
        sys.setrecursionlimit(2000)

        # check environment #
        SysMgr.checkEnv()

        # print logo #
        if not SysMgr.isSilentCmd():
            SysMgr.printLogo(big=True, pager=False)

        # print help #
        SysMgr.printHelp()

        # set default I/O #
        SysMgr.inputFile = sys.argv[1]
        SysMgr.outputFile = None

        # check log level #
        SysMgr.warnEnable = SysMgr.findOption("v")

        # set error logger #
        SysMgr.setErrorLogger()

        # import Guider native module #
        SysMgr.importNative()

        # create shared memory #
        SysMgr.shmObj = SysMgr.createShm()

        # set oom_adj #
        SysMgr.setOOMAdj()

        # set pid #
        SysMgr.getMaxPid()
        SysMgr.pid = os.getpid()

        # set arch #
        SysMgr.setArch(SysMgr.getArch())

        # set default signal #
        SysMgr.setSimpleSignal()

        # shrink heap #
        SysMgr.shrinkHeap()

        # get environment variables #
        SysMgr.parseEnvironVars()

        # set comm #
        SysMgr.setComm(
            SysMgr.environList["COMM"][0]
            if "COMM" in SysMgr.environList
            else __module__
        )
        SysMgr.comm = SysMgr.getComm(SysMgr.pid)

    @staticmethod
    def getOutput(fd, retLine=False, progress=False):
        # get select object #
        selectObj = SysMgr.getPkg("select")

        output = ""

        while 1:
            # wait for event #
            [read, write, error] = selectObj.select([fd], [], [])

            if progress:
                UtilMgr.printProgress()

            if read:
                line = read[0].readline()
                if retLine:
                    return line
                elif line == "":
                    return output

                output += line

    @staticmethod
    def launchGuider(
        cmd,
        log=False,
        mute=False,
        pipe=True,
        stderr=False,
        wait=False,
        stream=True,
        logo=True,
        copyOpt=True,
        initPkg=False,
        block=False,
        remote=False,
    ):
        """
        - desc: launch a new Guider process as a child
        - pros: can save memory such as ELF caches
                can control Guider functions such as logo, stdio
        - cons: should use global variables well
        """

        # check mute #
        if mute:
            pipe = False

        # create pipe #
        if pipe:
            rd, wr = os.pipe()

            # set SIGPIPE handler #
            SysMgr.setPipeHandler()
        else:
            rd = wr = None

        # create a new process #
        pid = SysMgr.createProcess(mute=mute)

        # parent #
        if pid > 0:
            if pipe:
                os.close(wr)
                rdFd = os.fdopen(rd, "r")
            else:
                rdFd = None

            # wait for children temrination #
            if wait:
                SysMgr.waitChild(pid)
                return pid

            if pipe:
                return (pid, rdFd)
            else:
                return pid

        # child #
        elif pid == 0:
            # disable alarm #
            signal.alarm(0)

            # set main environment #
            os.environ["ISMAIN"] = "True"

            # reinitialize static variables #
            # globals().update(vars(SysMgr))

            # disable pager, print output both to file and to stdout #
            if stream:
                SysMgr.setStream()

            # set environment variables #
            if remote:
                SysMgr.setRemoteEnv()

            # inherit options #
            disOptVal = SysMgr.getOption("d")
            enOptVal = SysMgr.getOption("e")
            if copyOpt and (disOptVal or enOptVal):
                applyDisable = False
                applyEnable = False

                # append option values #
                for idx, val in enumerate(cmd):
                    if val.startswith("-d") and disOptVal:
                        cmd[idx] = val + disOptVal
                        applyDisable = True
                    elif val.startswith("-e") and enOptVal:
                        cmd[idx] = val + enOptVal
                        applyEnable = True

                # append option #
                if disOptVal and not applyDisable:
                    cmd.append("-d %s" % disOptVal)
                if enOptVal and not applyEnable:
                    cmd.append("-e %s" % enOptVal)

            # disable logo #
            if not logo:
                SysMgr.logoEnable = False

            # disable logs #
            if not log:
                SysMgr.logEnable = False

            # change standard I/O #
            if pipe:
                os.dup2(wr, 1)
                os.close(wr)
                os.close(rd)

            # close stderr #
            if not stderr:
                sys.stderr.close()

            # block stdin #
            if block:
                SysMgr.redirectFd(SysMgr.nullPath, sys.stdin.fileno())

            # init time variables #
            SysMgr.startInitTime = 0
            SysMgr.startTime = 0
            SysMgr.startRecTime = 0
            SysMgr.startRunTime = 0
            SysMgr.startOverheadTime = 0

            # init resource variables #
            SysMgr.taskEnable = True
            SysMgr.cpuEnable = True
            SysMgr.memEnable = False
            SysMgr.blockEnable = False
            SysMgr.diskEnable = False
            SysMgr.reportEnable = False
            SysMgr.networkEnable = False

            # init print variables #
            SysMgr.inputFile = None
            SysMgr.outPath = None
            SysMgr.printFd = None
            SysMgr.printEnable = True
            SysMgr.encodeEnable = False
            SysMgr.reportEnable = SysMgr.jsonEnable = False

            # init number variables #
            SysMgr.nrTopRank = 10
            SysMgr.repeatCnt = 0
            SysMgr.progressCnt = 0
            SysMgr.skipCnt = 0
            SysMgr.repeatInterval = 0
            SysMgr.intervalEnable = 0
            SysMgr.maxIntervalTick = 0

            # init other variables #
            SysMgr.parsedAnalOption = False
            SysMgr.groupProcEnable = False
            SysMgr.optionList = []
            ConfigMgr.confData = {}
            SysMgr.procBuffer = []
            SysMgr.exitFuncList = []
            SysMgr.customCmd = []
            SysMgr.rcmdList = {}

            # clear global packages #
            if initPkg:
                SysMgr.impGlbPkg = {}

            # clear print buffer #
            SysMgr.clearPrint()

            # launch Guider command #
            main(cmd)

            sys.exit(0)

        # error #
        else:
            SysMgr.printErr("failed to launch Guider because of fork fail")
            return -1

    @staticmethod
    def executeCmdSync(cmd, stdout=1, stderr=True, remote=True):
        # create pipe #
        rd, wr = os.pipe()

        # set SIGPIPE handler #
        SysMgr.setPipeHandler()

        # create a new process #
        rdFd = None
        pid = SysMgr.createCmdProcess(
            cmd if SysMgr.isGuiderCmd(cmd) else None, pipe=True, remote=remote
        )
        if isinstance(pid, tuple):
            pid, rdFd = pid

        # parent #
        if pid > 0:
            if rdFd is None:
                os.close(wr)
                rdFd = os.fdopen(rd, "r")
            output = []

            while 1:
                ret = rdFd.readline().rstrip("\n")
                if not ret and not SysMgr.isAlive(pid):
                    break
                output.append(ret)

            return output

        # child #
        elif pid == 0:
            # change standard I/O #
            os.dup2(wr, stdout)
            os.close(wr)
            os.close(rd)

            # close stderr #
            if not stderr:
                sys.stderr.close()

            # set environment variables #
            if remote:
                SysMgr.setRemoteEnv()

            # split command #
            cmd = UtilMgr.parseCommand(cmd)

            # execute #
            SysMgr.executeProcess(cmd=cmd)

            sys.exit(0)

        # error #
        else:
            SysMgr.printErr(
                "failed to execute %s because of fork failure" % cmd
            )
            return -1

    @staticmethod
    def waitChild(pid=None, hang=True):
        # wait for all children #
        if not pid:
            while 1:
                try:
                    os.waitpid(-1, 0)
                except:
                    pass

                if SysMgr.condExit:
                    break

                # check children #
                SysMgr.updateChildList()
                if SysMgr.isNoChild():
                    break
            return

        # set blocking flag #
        if hang:
            flag = 0
        else:
            flag = os.WNOHANG

        # wait for a specific child #
        try:
            return os.waitpid(pid, flag)
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn("failed to wait %s task" % pid, reason=True)

    @staticmethod
    def spawnProcess(func=None, args=[], cnt=1, wait=True):
        # load multiprocessing #
        multiprocessing = SysMgr.getPkg("multiprocessing")

        plist = []
        pidlist = []

        for _ in xrange(cnt):
            try:
                # create a new process #
                p = multiprocessing.Process(target=func, args=args)

                # run a new process #
                p.start()

                # save process info #
                plist.append(p)
                pidlist.append(p.pid)
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr("failed to create a new process", reason=True)
                continue

        # sync #
        if wait:
            for p in plist:
                p.join()
        else:
            return pidlist

    @staticmethod
    def execBgCmd(cmd, mute=True, wait=True):
        # create a new process #
        ret = SysMgr.createCmdProcess(cmd, mute)

        # check result #
        if ret is False or ret < 0:
            return -1
        # check wait option for parent #
        elif ret > 0:
            if wait:
                os.waitpid(ret, 0)
            return

        # temrinate #
        os._exit(0)

    @staticmethod
    def isGuiderCmd(cmd):
        # check Guider command #
        if UtilMgr.isString(cmd) and cmd.startswith("GUIDER "):
            isGuider = True
        elif isinstance(cmd, list) and cmd[0] == "GUIDER":
            isGuider = True
        else:
            isGuider = False
        return isGuider

    @staticmethod
    def createCmdProcess(
        cmd, mute=False, pipe=False, remote=False, initSig=False
    ):
        # init signal handlers #
        if initSig:
            SysMgr.setDefaultSignal()

        # check guider command #
        isGuider = SysMgr.isGuiderCmd(cmd)

        # launch Guider #
        if isGuider:
            # remove Guider command #
            if isinstance(cmd, list):
                cmdList = cmd[1:]
            else:
                cmdList = UtilMgr.parseCommand(UtilMgr.lstrip(cmd, "GUIDER "))

            # launch Guider command #
            try:
                ret = SysMgr.launchGuider(
                    cmdList,
                    pipe=pipe,
                    stderr=True,
                    stream=False,
                    logo=False,
                    log=True,
                    remote=remote,
                )
            except SystemExit:
                sys.exit(0)
            except:
                ret = False
                SysMgr.printErr(
                    "failed to launch %s" % __module__, reason=True
                )
        # launch command #
        else:
            ret = SysMgr.createProcess(cmd, mute=mute)

        return ret

    @staticmethod
    def createProcess(
        cmd=None,
        isDaemon=False,
        mute=False,
        chPgid=False,
        chMid=False,
        sleep=0,
        keepPrintFd=False,
    ):
        # flush print buffer before fork #
        SysMgr.flushAllForPrint()

        # save tty info #
        if cmd and SysMgr.ttyData is None:
            SysMgr.saveTTY()

        # create a new process #
        try:
            pid = os.fork()
        except SystemExit:
            sys.exit(0)
        except:
            # multiprocessing #
            try:
                ret = SysMgr.spawnProcess(wait=False)
                if ret:
                    return ret[0]
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr("failed to create a new process", True)
            return -1

        # parent #
        if pid > 0:
            if not isDaemon:
                SysMgr.childList[pid] = True

            # set child priority #
            if "CHILDSCHED" in SysMgr.environList:
                for cmd in SysMgr.environList["CHILDSCHED"]:
                    SysMgr.applyPriority("%s:%s" % (cmd, pid))

            return pid
        # child #
        elif pid == 0:
            # init times #
            SysMgr.initTimes(False)
            SysMgr.getRuntime()

            # update parent PID #
            SysMgr.parentPid = SysMgr.pid

            # initialize child list #
            SysMgr.clearChildList()

            if chPgid:
                os.setpgid(0, 0)

            # setsid to keep alive alone #
            if "KEEPALIVE" in SysMgr.environList:
                try:
                    os.setsid()
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printWarn("failed to call setsid()", reason=True)

            # close fd for output #
            # WARN: If printFd is a GzipFile object, it may be corrupted #
            if not keepPrintFd:
                SysMgr.closePrintFd()

            # Guider #
            if not cmd:
                # update master pid #
                if chMid:
                    SysMgr.masterPid = SysMgr.pid

                # update pid #
                if not "NOPIDSUFFIX" in SysMgr.environList:
                    SysMgr.fileSuffix = SysMgr.pid = os.getpid()

                # init shared resources #
                SysMgr.initSharedResource(mute)

                return 0

            # split command #
            cmd = UtilMgr.parseCommand(cmd)

            # convert ~ to realpath #
            cmd[0] = os.path.expanduser(cmd[0])

            # make a delay #
            if sleep:
                time.sleep(sleep)

            # execute #
            SysMgr.executeProcess(cmd, mute)

            # execute fail #
            os._exit(0)
        # fail #
        else:
            SysMgr.printErr("failed to create a new process")
            return -1

    @staticmethod
    def redirectFd(path, fileno):
        try:
            SysMgr.printInfo("redirect fd(%s) to '%s'" % (fileno, path))
            fd = os.open(path, os.O_RDWR | os.O_CREAT | os.O_APPEND)
            os.dup2(fd, fileno)
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                "failed to redirect %s to '%s'" % (fileno, path), True
            )
            sys.exit(-1)

    @staticmethod
    def initSharedResource(mute, net=True):
        # set mute #
        if mute:
            SysMgr.closeStdFd(stderr=False)

        # clear threshold condition #
        SysMgr.prevThrData = SysMgr.thrData
        SysMgr.thrData = {}
        SysMgr.eventCommandList = {}
        SysMgr.eventLockList = {}

        # reset report resource #
        SysMgr.reportEnable = False
        SysMgr.reportObject = None

        # reinit network #
        if net and SysMgr.localServObj:
            ip = SysMgr.localServObj.ip

            # remove previous socket #
            try:
                SysMgr.localServObj.close()
            except:
                pass

            NetworkMgr.setServerNetwork(ip, None, force=True, anyPort=True)

    @staticmethod
    def closeStdFd(stdin=False, stdout=True, stderr=True):
        try:
            if not SysMgr.nullFd:
                SysMgr.nullFd = open(SysMgr.nullPath, "w")
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn("failed to open '%s'" % SysMgr.nullPath, True)
            return

        # get null fd #
        nullFd = SysMgr.nullFd.fileno()

        # redirect stdin to file #
        if "STDIN" in SysMgr.environList:
            path = SysMgr.environList["STDIN"][0]
            if path != "SET":
                SysMgr.redirectFd(path, sys.stdin.fileno())
        # redirect stdin to null #
        elif stdin:
            os.dup2(nullFd, sys.stdin.fileno())

        # redirect stdout to file #
        if "STDOUT" in SysMgr.environList:
            path = SysMgr.environList["STDOUT"][0]
            if path != "SET":
                SysMgr.redirectFd(path, sys.stdout.fileno())
        elif stdout:
            os.dup2(nullFd, sys.stdout.fileno())

        # redirect stderr to file #
        if "STDERR" in SysMgr.environList:
            path = SysMgr.environList["STDERR"][0]
            if path != "SET":
                SysMgr.redirectFd(path, sys.stderr.fileno())
        elif stderr:
            os.dup2(nullFd, sys.stderr.fileno())

    @staticmethod
    def resetFileTable(mute=True, closeFd=True):
        # redirect stdout and stderr to null #
        try:
            if mute:
                SysMgr.closeStdFd(stderr=False)
        except SystemExit:
            sys.exit(0)
        except:
            pass

        if not closeFd:
            return

        # close all file descriptors except for stds #
        try:
            # set path for descriptor list #
            path = "%s/self/fd" % SysMgr.procPath

            # close descriptors #
            for fd in os.listdir(path):
                try:
                    fd = long(fd)
                    if fd > 2:
                        os.close(fd)
                except SystemExit:
                    sys.exit(0)
                except:
                    pass
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                "failed to get file descriptors in %s" % path, reason=True
            )
            return

    @staticmethod
    def runBackgroundMode():
        if SysMgr.bgStatus:
            return

        # update start overhead time #
        if SysMgr.startOverheadTime == 0:
            SysMgr.getRuntime()
            SysMgr.startOverheadTime = (
                SysMgr.startInitTime - SysMgr.startRunTime
            )

        # create a new process #
        pid = SysMgr.createProcess(isDaemon=True)
        if pid > 0:
            # wait a minute for child message #
            time.sleep(0.1)

            # terminate parent process #
            sys.exit(0)
        else:
            SysMgr.bgStatus = True

            # continue child process #
            SysMgr.printStat(
                "run %s(%s) in background"
                % (SysMgr.getComm(SysMgr.pid), SysMgr.pid)
            )

    @staticmethod
    def isSignalAvailable():
        if not SysMgr.platform:
            SysMgr.checkEnv()

        return SysMgr.isLinux or SysMgr.isDarwin

    @staticmethod
    def setDefaultSignal():
        if not SysMgr.isSignalAvailable():
            return

        signal.signal(signal.SIGINT, signal.SIG_DFL)
        signal.signal(signal.SIGQUIT, signal.SIG_DFL)
        signal.signal(signal.SIGCHLD, signal.SIG_DFL)
        signal.signal(signal.SIGPIPE, signal.SIG_DFL)

        SysMgr.applyIgnoreSignals()

    @staticmethod
    def setSimpleSignal():
        if not SysMgr.isSignalAvailable():
            return

        SysMgr.setCommonSignal()

        signal.signal(signal.SIGINT, SysMgr.exitHandler)
        signal.signal(signal.SIGQUIT, SysMgr.exitHandler)
        signal.signal(signal.SIGPIPE, signal.SIG_IGN)
        signal.signal(signal.SIGHUP, signal.SIG_IGN)

        SysMgr.applyIgnoreSignals()

    @staticmethod
    def setPipeHandler():
        if not SysMgr.isSignalAvailable():
            return

        signal.signal(signal.SIGPIPE, SysMgr.exitHandler)

    @staticmethod
    def setCommonSignal():
        if not SysMgr.isSignalAvailable():
            return

        signal.signal(signal.SIGCHLD, SysMgr.chldHandler)
        signal.signal(signal.SIGWINCH, SysMgr.winchHandler)
        signal.signal(signal.SIGCONT, SysMgr.fgHandler)
        signal.signal(signal.SIGTSTP, SysMgr.bgHandler)
        signal.signal(signal.SIGTTIN, SysMgr.bgHandler)
        signal.signal(signal.SIGTTOU, signal.SIG_IGN)
        signal.signal(signal.SIGUSR1, SysMgr.defaultHandler)
        signal.signal(signal.SIGUSR2, SysMgr.defaultHandler)

    @staticmethod
    def setIgnoreSignal():
        if not SysMgr.isSignalAvailable():
            return

        signal.signal(signal.SIGALRM, signal.SIG_IGN)
        signal.signal(signal.SIGINT, signal.SIG_IGN)
        signal.signal(signal.SIGQUIT, signal.SIG_IGN)
        signal.signal(signal.SIGPIPE, signal.SIG_IGN)

    @staticmethod
    def signalfd(fd=-1, sig=None, act="block", wait=False):
        # block signals #
        if not SysMgr.blockSignal(sig, act, wait=False):
            return -1

        # return new FD for signal events #
        sigset = SysMgr.sigsetObj
        return SysMgr.libcObj.signalfd(fd, byref(sigset), 0)

    @staticmethod
    def blockSignal(sig=None, act="block", wait=False):
        # load libc #
        if not SysMgr.loadLibcObj():
            return False

        # set default signal #
        if not sig:
            sig = signal.SIGINT

        if not SysMgr.sigsetObj:
            NWORDS = long(1024 / (8 * sizeof(c_uint)))

            class sigset_t(Structure):
                _fields_ = [("__sigbits", c_uint * NWORDS)]

            SysMgr.sigsetObj = sigset_t()
            SysMgr.sigsetOldObj = sigset_t()

        sigset = SysMgr.sigsetObj
        sigsetold = SysMgr.sigsetOldObj

        # check act #
        if act == "block":  # signal.SIG_BLOCK
            atype = 0
        elif act == "unblock":  # signal.SIG_UNBLOCK
            atype = 1
        else:  # signal.SIG_SETMASK
            SysMgr.printErr("no supported '%s' for blocking signal" % act)
            return

        SysMgr.libcObj.memset(byref(sigset), 0, sizeof(sigset))

        if not isinstance(sig, list):
            sig = [sig]

        for sigbit in sig:
            if hasattr(SysMgr.libcObj, "sigaddset"):
                SysMgr.libcObj.sigaddset(byref(sigset), sigbit)

        SysMgr.libcObj.sigprocmask(atype, byref(sigset), byref(sigsetold))

        if wait:
            if hasattr(SysMgr.libcObj, "sigsuspend"):
                SysMgr.libcObj.sigsuspend(byref(sigsetold))

        return True

    @staticmethod
    def pendingSignal(sig):
        # load libc #
        if not SysMgr.libcObj and not SysMgr.loadLibcObj():
            return False
        elif not SysMgr.sigsetObj:
            return False

        sigset = SysMgr.sigsetObj

        # get pending list #
        SysMgr.libcObj.sigpending(byref(sigset))

        if not isinstance(sig, list):
            sig = [sig]

        pendList = []
        for item in sig:
            if SysMgr.libcObj.sigismember(byref(sigset), item):
                pendList.append(item)

        return set(list(pendList))

    @staticmethod
    def callSigHandler(sig, frame=None):
        try:
            sh = signal.getsignal(sig)
            sh(sig, frame)
        except SystemExit:
            sys.exit(0)
        except:
            if sh == signal.SIG_IGN:
                reason = " because %s is ignored" % sig
            elif sh == signal.SIG_DFL:
                reason = " because %s is handled in default" % sig
            else:
                reason = ""

            SysMgr.printWarn(
                "failed to call signal handler for %s%s" % (sig, reason)
            )

    @staticmethod
    def setNormalSignal():
        if not SysMgr.isSignalAvailable():
            return

        SysMgr.setCommonSignal()

        signal.signal(signal.SIGALRM, SysMgr.alarmHandler)
        signal.signal(signal.SIGINT, SysMgr.stopHandler)
        signal.signal(signal.SIGQUIT, SysMgr.newHandler)
        signal.signal(signal.SIGPIPE, signal.SIG_IGN)

        SysMgr.applyIgnoreSignals()

    @staticmethod
    def getServerPkg(parallel=False):
        if sys.version_info >= (3, 0, 0):
            return SysMgr.getPkg("http.server")
        else:
            if parallel:
                return SysMgr.getPkg("BaseHTTPServer")
            else:
                return SysMgr.getPkg("SimpleHTTPServer")

    @staticmethod
    def runHttpServerMode():
        # set environment variables #
        SysMgr.setRemoteEnv()

        # get subprocess object #
        subprocess = SysMgr.getPkg("subprocess")

        # set parallel mode #
        parallel = "PARALLEL" in SysMgr.environList

        # get server package #
        server = SysMgr.getServerPkg(parallel)

        # set handler #
        if parallel:
            handler = server.BaseHTTPRequestHandler
        else:
            handler = server.SimpleHTTPRequestHandler

        class requestHandler(handler):
            def do_GET(self):
                ip = port = ctype = interval = None
                origFilter = SysMgr.filterGroup
                origEnv = SysMgr.environList
                origAll = SysMgr.showAll

                # extract query param #
                try:
                    # parse query #
                    urllib = SysMgr.getPkg("urllib", isExit=False)
                    urlParse = urllib.parse
                    parseObj = urlParse.urlparse(self.path)

                    # get command #
                    cmd = parseObj.path.lstrip("/")

                    # get params #
                    queryList = urlParse.parse_qs(parseObj.query)

                    # get IP #
                    if "ip" in queryList:
                        ip = queryList["ip"][0]

                    # get PORT #
                    if "port" in queryList:
                        port = queryList["port"][0]

                    # get TYPE #
                    if "type" in queryList:
                        ctype = queryList["type"][0]

                    # get INTERVAL #
                    if "interval" in queryList:
                        interval = queryList["interval"][0]

                    # get FILTER #
                    if "filter" in queryList:
                        SysMgr.filterGroup = queryList["filter"][0].split(",")

                    # get ENV #
                    if "env" in queryList:
                        envs = UtilMgr.splitString(queryList["env"][0])
                        SysMgr.environList = UtilMgr.convList2Dict(
                            envs, cap=True
                        )

                    # get all flag #
                    if "all" in queryList:
                        SysMgr.showAll = True
                except SystemExit:
                    sys.exit(0)
                except:
                    # get command #
                    cmd = self.path.lstrip("/")

                # set default command #
                if not cmd:
                    cmd = "help"

                # set default ip and port #
                if not ip:
                    ip = SysMgr.localServObj.ip
                if not port:
                    port = SysMgr.defaultServPort

                # set response #
                self.send_response(200)

                # print command #
                print("COMMAND '%s' is executed..." % cmd)

                # graph #
                if cmd == "graph":
                    # set header #
                    self.send_header("Content-type", "image/png")
                    self.end_headers()

                    drawCmd = "EVENT_CMD_GETDUMP"
                    if "DRAWLEN" in SysMgr.environList:
                        drawCmd += ":%s" % SysMgr.environList["DRAWLEN"][0]

                    # get monitoring data from remote server #
                    data = SysMgr.sendEvents(ip, port, [drawCmd], verb=False)
                    if not data:
                        SysMgr.printErr("failed to get monitoring data")
                        return

                    # convert data to file object #
                    fd = MemoryFile(name="buf", data=data.encode())

                    # initialize environment for drawing #
                    TaskAnalyzer.initDrawEnv(dpi=100, size=None, fontsize=None)

                    # set canvas layout #
                    if ctype:
                        SysMgr.layout = ctype

                    # draw graphs #
                    try:
                        SysMgr.addEnvironVar("TOPSUM")
                        TaskAnalyzer(onlyInstance=True).drawStats(
                            fdlist=[fd],
                            outFd=self.wfile,
                            onlyGraph=True,
                            applyOpt=False,
                        )
                    except:
                        pass

                # help #
                elif cmd == "help":
                    # set header #
                    self.send_header("Content-type", "text/plain")
                    self.end_headers()

                    # build full command #
                    cmd = " ".join(SysMgr.getExeCmd(SysMgr.pid)) + " " + cmd

                    # launch a command #
                    try:
                        procObj = None

                        # create a new worker process #
                        procObj = subprocess.Popen(
                            cmd,
                            shell=True,
                            bufsize=0,
                            stdout=self.wfile,
                            stderr=self.wfile,
                        )
                    except SystemExit:
                        sys.exit(0)
                    except:
                        SysMgr.printErr(
                            "failed to create a new process to execute '%s'"
                            % cmd,
                            True,
                        )

                    # wait for worker process #
                    try:
                        if procObj:
                            procObj.wait()
                    except SystemExit:
                        sys.exit(0)
                    except:
                        SysMgr.printErr(
                            "failed to wait termination for '%s'" % cmd, True
                        )
                    finally:
                        # kill subprocess group #
                        if procObj:
                            SysMgr.killProcGroup(procObj.pid)
                # draw #
                elif cmd == "draw":
                    # set header #
                    self.send_header("Content-type", "text/html")
                    self.end_headers()

                    # make HTML string #
                    html = """
<!DOCTYPE html>
<html>
<head>
  <title>Guider Graph</title>
  <meta http-equiv="refresh" content="{0:1}">
  <style>
    body {{
      margin: 0{1:1}
      padding: 0{1:1}
    }}

    img {{
      max-width: 100%{1:1}
      height: auto{1:1}
    }}
  </style>
</head>
<script>
  const img = new Image(){1:1}
  /*
  let i = 0{1:1}
  setInterval(() => {{
     img.src = '/graph?ip=127.0.0.1&&port=5555'
     document.body.appendChild(img){1:1}
  }}, 1000){1:1}
  */
</script>
<body>
  <h2>Guider Graph</h2>
  <img id="image" src="/graph?ip={2:1}&&port={3:1}" alt="image">
</body>
</html>
                    """.format(
                        interval if interval else 3, ";", ip, port
                    )

                    # write the HTML contents with UTF-8 #
                    self.wfile.write(bytes(html, "utf8"))

                # recover environment variables #
                SysMgr.filterGroup = origFilter
                SysMgr.environList = origEnv
                SysMgr.showAll = origAll

                return

                # attach file explorer #
                return handler.doGET(self)

        # run server using HTTP #
        SysMgr.runHttpServer(requestHandler, parallel)

    @staticmethod
    def runFileServerMode():
        # set environment variables #
        SysMgr.setRemoteEnv()

        # set parallel mode #
        parallel = "PARALLEL" in SysMgr.environList

        # get server package #
        server = SysMgr.getServerPkg(parallel)

        # set handler #
        if parallel:
            handler = server.BaseHTTPRequestHandler
        else:
            handler = server.SimpleHTTPRequestHandler

        # run file server using HTTP #
        SysMgr.runHttpServer(handler, parallel)

    @staticmethod
    def runHttpServer(handler, parallel=False):
        # get socketserver #
        if sys.version_info >= (3, 0, 0):
            socketserver = SysMgr.getPkg("socketserver")
        else:
            socketserver = SysMgr.getPkg("SocketServer")

        # set IP and port #
        if SysMgr.localServObj:
            ip = SysMgr.localServObj.ip
            port = SysMgr.localServObj.port
        else:
            ip = ""
            port = 8080

        # set default path #
        if SysMgr.inputParam:
            try:
                defaultPath = SysMgr.inputParam
                os.chdir(defaultPath)
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr(
                    "failed to set default dir path to '%s'" % defaultPath,
                    True,
                )
                sys.exit(0)
        else:
            defaultPath = "."
        defaultPath = os.path.realpath(defaultPath)

        # disable logger guide #
        try:
            sys.stderr.notified = True
        except:
            pass

        # print debug info #
        SysMgr.warnEnable = True

        # start TCP server #
        try:
            if parallel:
                # get server package #
                server = SysMgr.getServerPkg(parallel)

                class ThreadedHTTPServer(
                    socketserver.ThreadingMixIn, server.HTTPServer
                ):
                    pass

                httpd = ThreadedHTTPServer((ip, port), handler)
            else:
                socketserver.TCPServer.allow_reuse_address = True
                httpd = socketserver.TCPServer((ip, port), handler)
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to create a TCP server", True)
            sys.exit(0)

        SysMgr.printInfo(
            "start a HTTP server in %s mode from '%s' using %s:%s"
            % (
                "parallel" if parallel else "sequential",
                defaultPath,
                ip if ip else "localhost",
                port,
            )
        )

        # start HTTP handling #
        try:
            httpd.serve_forever()
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to serve a HTTP server", True)
            sys.exit(0)
        finally:
            httpd.server_close()

    @staticmethod
    def runServerMode():
        nodeList = {}
        agentList = {}

        def _sendErrMsg(netObj, message):
            message = "ERROR|%s:%s:%s" % (message, netObj.ip, netObj.port)
            netObj.send(message)

        def _updateNodeList(ret=False):
            # close sockets for terminated connections #
            for addr in list(nodeList):
                if not NetworkMgr.requestPing(addr, verb=SysMgr.warnEnable):
                    try:
                        nodeList[addr]["sock"].close()
                    except:
                        pass
                    finally:
                        nodeList.pop(addr, None)

            # get network info #
            try:
                netinfo = " [%s/%s]" % (
                    NetworkMgr.getHostName(),
                    NetworkMgr.getPublicIp(),
                )
            except SystemExit:
                sys.exit(0)
            except:
                netinfo = ""

            # print node list #
            idx = 0
            current = time.time()
            listStr = "[Service Node List]%s\n%s\n" % (netinfo, twoLine)
            listStr += "{0:^5} {1:^13} {2:^25} {3:<1} \n{4:1}\n".format(
                "Idx", "Time", "Addr", "Host", oneLine
            )
            for addr, value in sorted(
                nodeList.items(), key=lambda e: e[1]["time"]
            ):
                diff = current - value["time"]
                listStr += "[{0:>3}] {1:^13} {2:<25} {3:<1}\n".format(
                    idx, UtilMgr.convTime(diff), addr, value["host"]
                )
                idx += 1
            listStr += (
                "%s\n" % oneLine if nodeList else "    None\n%s\n" % oneLine
            )
            SysMgr.printWarn(listStr, True)

            if ret:
                return listStr

        def _onDownload(netObj, value, response):
            # pick path #
            try:
                files = UtilMgr.cleanItem(value.split("@", 1), False)
                if len(files) == 2:
                    src, des = files
                    if src and not des:
                        des = src
                    elif not src:
                        raise Exception()
                else:
                    src = des = files[0]
            except:
                SysMgr.printWarn("failed to recognize %s" % value, True)
                _sendErrMsg(
                    netObj,
                    "wrong format for path, input in the format {SRC@DES}",
                )
                return

            # response from command request #
            netObj.send(response)

            # transfer file #
            try:
                NetworkMgr.sendFile(netObj, netObj.ip, netObj.port, src, des)
            except:
                SysMgr.printErr(
                    "failed to upload %s to %s in %s:%s"
                    % (src, des, netObj.ip, netObj.port),
                    True,
                )
            finally:
                try:
                    netObj.send("@DONE#0".encode())
                    netObj.close()
                except:
                    pass

        def _onUpload(netObj, value, response):
            try:
                files = UtilMgr.cleanItem(value.split("@", 1), False)
                if len(files) == 2:
                    src, des = files
                    if src and not des:
                        des = src
                    elif not src:
                        raise Exception()
                else:
                    src = files[0]
                    des = None
            except:
                SysMgr.printWarn("failed to recognize %s" % value, True)
                _sendErrMsg(
                    netObj,
                    "wrong format for path, input in the format {SRC@DES}",
                )
                return

            # send response #
            netObj.send(response)

            # receive file #
            try:
                NetworkMgr.recvFile(netObj, netObj.ip, netObj.port, src, des)
            except:
                SysMgr.printErr(
                    "failed to download %s from %s in %s:%s"
                    % (src, des, netObj.ip, netObj.port),
                    True,
                )
            finally:
                netObj.close()

        def _onNew(connObj, value, response):
            try:
                # reply message #
                connObj.send("PONG")

                host, addr = value.rsplit("/", 1)

                # register node info #
                if not addr in nodeList or nodeList[addr]["sock"] != connObj:
                    nodeList[addr] = {
                        "sock": connObj,
                        "time": time.time(),
                        "host": host,
                    }

                SysMgr.printInfo(
                    "registered '%s' as a service node successfully" % value
                )

                # update service node list #
                _updateNodeList()
            except:
                SysMgr.printWarn(
                    "failed to register '%s' as a service node" % value,
                    reason=True,
                )

        def _onClear(connObj, value, response):
            try:
                # send message packet #
                connObj.send("MSG:terminated all worker processes")

                # terminate worker processes #
                SysMgr.killChildren(sig=signal.SIGINT)

                # get pstree #
                SysMgr.doPstree(
                    [str(SysMgr.pid)], False, SysMgr.infoBufferPrint
                )
                pstree = SysMgr.sysinfoBuffer
                SysMgr.clearInfoBuffer()

                """
                receive an ACK packet
                to prevent receiving two packets at once
                """
                connObj.recv()

                # send reply packet for command #
                connObj.send("CLEAR:%s" % pstree)
            except:
                SysMgr.printWarn(
                    "failed to terminate worker processes", reason=True
                )

        def _onJobs(connObj, value, response):
            try:
                # send message packet #
                connObj.send("MSG:list all worker processes")

                # get pstree #
                SysMgr.doPstree(
                    [str(SysMgr.pid)], False, SysMgr.infoBufferPrint
                )
                pstree = SysMgr.sysinfoBuffer.rstrip()
                SysMgr.clearInfoBuffer()

                """
                receive an ACK packet
                to prevent receiving two packets at once
                """
                connObj.recv()

                # send reply packet for command #
                connObj.send("JOBS:%s" % pstree)
            except:
                SysMgr.printWarn(
                    "failed to list worker processes", reason=True
                )

        def _onRestart(connObj, value, response):
            return True

        def _onList(connObj, value, response):
            try:
                # update service node list #
                ret = _updateNodeList(ret=True)
                connObj.send("LIST:" + ret.strip())
            except:
                pass

        def _onPing(connObj, value, response):
            try:
                connObj.send("PONG")
            except:
                pass

        def _onRun(connObj, value, response):
            def _enableSigPipe():
                signal.signal(signal.SIGPIPE, signal.SIG_DFL)

            # response from command request #
            connObj.send(response)

            # get subprocess object #
            subprocess = SysMgr.getPkg("subprocess")

            # get connection info #
            addr = "%s:%s" % (connObj.ip, connObj.port)

            # convert Guider path #
            if value.startswith("GUIDER "):
                cmd = " ".join(value.split()[1:])
                path = " ".join(SysMgr.getExeCmd(SysMgr.pid))
                value = "%s %s" % (path, cmd)

            # run command #
            try:
                # copy environment variables #
                myEnv = deepcopy(os.environ)
                myEnv["REMOTERUN"] = "True"

                # receive SIGCHLD #
                signal.signal(signal.SIGCHLD, signal.SIG_DFL)

                # set print flag #
                printFlag = SysMgr.getPrintFlag()
                if printFlag:
                    procOut = subprocess.PIPE
                else:
                    procOut = open(SysMgr.nullPath, "wb")

                # create a new worker process #
                procObj = subprocess.Popen(
                    value,
                    shell=True,
                    stdout=procOut,
                    stderr=procOut,
                    env=myEnv,
                    bufsize=0,
                    preexec_fn=os.setsid,
                )

                SysMgr.printInfo("executed '%s' for %s" % (value, addr))

                # get select object #
                selectObj = SysMgr.getPkg("select")

                # set fds #
                listenFds = []
                if procObj.stdout:
                    listenFds.append(procObj.stdout)
                if procObj.stderr:
                    listenFds.append(procObj.stderr)
                if connObj.socket:
                    listenFds.append(connObj.socket)

                # get I/O buffer size #
                if "READCHUNK" in SysMgr.environList:
                    readChunkSize = UtilMgr.getEnvironNum("READCHUNK")
                else:
                    readChunkSize = None

                # handle output #
                while 1:
                    try:
                        # wait for event #
                        [read, write, error] = selectObj.select(
                            listenFds, [], [], 1
                        )

                        # read output from pipe #
                        for robj in read:
                            # check connection close #
                            if robj == connObj.socket:
                                raise Exception()

                            # handle data arrived #
                            while 1:
                                # read output from pipe #
                                if readChunkSize:
                                    output = robj.read(readChunkSize)
                                else:
                                    output = robj.readline()

                                # check and transfer output #
                                if output == "\n":
                                    continue
                                elif output and len(output) > 0:
                                    if not connObj.write(output):
                                        raise Exception()
                                else:
                                    break
                    except:
                        break

                    # check process status #
                    if procObj.poll() != None:
                        break

                SysMgr.printInfo("terminated '%s' for %s" % (value, addr))
            except:
                SysMgr.printErr(
                    "failed to execute '%s' from %s" % (value, addr), True
                )
            finally:
                try:
                    # kill subprocess group #
                    SysMgr.killProcGroup(procObj.pid, signal.SIGKILL)

                    # close socket #
                    connObj.socket.shutdown(socket.SHUT_RDWR)
                    connObj.close()
                except:
                    pass

        def _onNotify(netObj, value, response):
            # send message packet #
            connObj.send(
                'MSG:"%s" is going to be delivered by the agent' % value
            )

            """
            receive an ACK packet
            to prevent receiving two packets at once
            """
            connObj.recv()

            # send reply packet for command #
            connObj.send("NOTIFY:%s" % value)

            # execute event handlers #
            _runEventHandlers(value)

        def _onDir(netObj, value, response):
            try:
                if not value:
                    value = "."

                # convert real path #
                value = os.path.realpath(value)

                if os.path.isdir(value):
                    dirlist = [
                        "%s%s"
                        % (
                            d,
                            UtilMgr.getFileSizeStr(os.path.join(value, d))
                            if os.path.isfile(d)
                            else "",
                        )
                        for d in os.listdir(value)
                    ]
                    dirlist.sort()
                    ret = "\n".join(dirlist)
                else:
                    ret = "No such file or directory"

                connObj.send("DIR: [ %s ]\n%s" % (value, ret.strip()))
            except:
                pass

        def _onUpstream(netObj, value, response, sync=True):
            # check agent list #
            if not agentList:
                connObj.send("NO_SERV_AGENT")
                return

            # split items #
            itemList = value.split(":")

            # pick command #
            reqCmd = itemList[0].upper()
            if reqCmd in NetworkMgr.REQUEST_LIST:
                cmd = value
            else:
                cmd = "run:" + value

            # send message packet #
            connObj.send('MSG:"%s" is going to be executed by the agent' % cmd)

            """
            receive an ACK packet
            to prevent receiving two packets at once
            """
            connObj.recv()

            # send reply packet for command #
            connObj.send("run|%s" % value)

            # execute remote commands #
            pid = 0
            for addr in list(agentList):
                # create a new worker process #
                pid = SysMgr.createProcess()
                if pid > 0:
                    continue

                try:
                    SysMgr.printInfo(
                        "execute '%s' at %s for %s:%s"
                        % (cmd, addr, connObj.ip, connObj.port)
                    )

                    # disable log #
                    SysMgr.logEnable = False

                    # execute a command from the agent #
                    rcmd = "%s|%s" % (addr, cmd)
                    SysMgr.runClientMode(rcmd, connObj)
                except SystemExit:
                    pass
                except:
                    SysMgr.logEnable = True
                    SysMgr.printErr(
                        "failed to execute '%s' at %s for %s:%s"
                        % (cmd, addr, connObj.ip, connObj.port)
                    )

                sys.exit(0)

            # wait for termination for remote commands #
            if pid > 0 and sync:
                SysMgr.waitChild()

        def _onBroadcast(connObj, value, response, sync=True):
            # check node list #
            if not nodeList:
                connObj.send("NO_SERV_NODE")
                return

            # split items #
            itemList = value.split(":")

            # check target list #
            if itemList[0].startswith("@"):
                targetList = itemList.pop(0)[1:].split("@")
                value = ":".join(itemList)
                nrTarget = len(targetList)
            else:
                targetList = []
                nrTarget = len(nodeList)

            # pick command #
            reqCmd = itemList[0].upper()
            if reqCmd in NetworkMgr.REQUEST_LIST:
                cmd = value
            else:
                cmd = "run:" + value

            # send message packet #
            connObj.send(
                'MSG:"%s" is going to be executed by %s nodes'
                % (cmd, UtilMgr.convNum(nrTarget))
            )

            """
            receive an ACK packet
            to prevent receiving two packets at once
            """
            connObj.recv()

            # send reply packet for command #
            connObj.send("run|%s" % value)

            # execute remote commands #
            pid = 0
            for addr in list(nodeList):
                if targetList and not addr.replace(":", "/") in targetList:
                    continue

                # create a new worker process #
                pid = SysMgr.createProcess()
                if pid > 0:
                    continue

                try:
                    # get host name #
                    if addr in nodeList:
                        hostinfo = "%s@" % nodeList[addr]["host"]
                    else:
                        hostinfo = ""

                    SysMgr.printInfo(
                        "execute '%s' at %s%s for %s:%s"
                        % (cmd, hostinfo, addr, connObj.ip, connObj.port)
                    )

                    # disable log #
                    SysMgr.logEnable = False

                    # execute a command from a remote node #
                    rcmd = "%s|%s" % (addr, cmd)
                    SysMgr.runClientMode(rcmd, connObj)
                except SystemExit:
                    pass
                except:
                    SysMgr.logEnable = True
                    SysMgr.printErr(
                        "failed to execute '%s' at %s for %s:%s"
                        % (cmd, addr, connObj.ip, connObj.port)
                    )

                sys.exit(0)

            # wait for termination for remote commands #
            if pid > 0 and sync:
                SysMgr.waitChild()

        def _register(connObj):
            # create a new socket for TCP #
            rip = SysMgr.remoteServObj.ip
            rport = SysMgr.remoteServObj.port
            raddr = "%s:%s" % (rip, rport)
            caddr = "%s:%s" % (connObj.ip, connObj.port)
            cliObj = None
            errMsg = None

            while 1:
                try:
                    # create a new socket for TCP #
                    cliObj = NetworkMgr.setRemoteServer(
                        raddr, tcp=True, verb=not cliObj
                    )

                    SysMgr.printWarn(
                        "try to connect to the agent (%s)" % raddr
                    )

                    # connect to the agent #
                    cliObj.connect()
                    SysMgr.printInfo("connected to the agent (%s)" % raddr)

                    # register agent info #
                    agentList["%s:%s" % (cliObj.ip, cliObj.port)] = True

                    # init error message #
                    errMsg = None

                    # get host name #
                    hostname = NetworkMgr.getHostName()

                    # register to another server #
                    ret = NetworkMgr.getCmdPipe(
                        cliObj, "new:%s/%s" % (hostname, caddr)
                    )
                    if not ret:
                        # try to connect again #
                        time.sleep(1)
                        continue

                    # monitor connection #
                    cliObj.recv()
                except SystemExit:
                    sys.exit(0)
                except:
                    # print error message #
                    curErrMsg = SysMgr.getErrMsg()
                    if errMsg != curErrMsg:
                        SysMgr.printErr(
                            "failed to keep connection with the agent (%s)"
                            % raddr,
                            True,
                        )
                        SysMgr.printInfo(
                            "start trying to connect to the agent (%s)" % raddr
                        )

                    # update latest error message #
                    errMsg = curErrMsg

                    # remove agent info #
                    try:
                        agentList.pop("%s:%s" % (cliObj.ip, cliObj.port), None)
                    except SystemExit:
                        sys.exit(0)
                    except:
                        pass

                    # close invalid socket #
                    if cliObj.connected:
                        cliObj.close()

                    cliObj.connected = False

                    # retry after 1 second #
                    time.sleep(1)

        def _loadConfig():
            initCmds = []
            eventHandlers = {}

            # get server config #
            config = SysMgr.getConfigItem("server")
            if not config:
                return initCmds, eventHandlers

            SysMgr.printInfo(
                "loaded server config from '%s'" % SysMgr.confFileName
            )

            # print config #
            SysMgr.printWarn(UtilMgr.convDict2Str(config))

            if "INIT" in config and isinstance(config["INIT"], list):
                # get command shortcut #
                if "COMMAND" in config and isinstance(config["COMMAND"], dict):
                    cmds = config["COMMAND"]
                else:
                    cmds = []

                # get valid commands #
                for item in config["INIT"]:
                    if item.get("apply") != "true":
                        continue
                    elif not "command" in item:
                        continue

                    # convert command shortcut to real command #
                    if UtilMgr.isString(item["command"]):
                        pcmds = item["command"].split("->")
                        clist = []

                        for pcmd in pcmds:
                            if pcmd in cmds:
                                pcmd = cmds[pcmd]
                            clist.append(pcmd)

                        item["command"] = "->".join(clist)
                    elif isinstance(item["command"], list):
                        newCmdList = []
                        for command in item["command"]:
                            pcmds = command.split("->")
                            clist = []

                            for pcmd in pcmds:
                                if pcmd in cmds:
                                    pcmd = cmds[pcmd]
                                clist.append(pcmd)

                            command = "->".join(clist)
                            newCmdList.append(command)
                        item["command"] = newCmdList
                    else:
                        continue

                    # add command to list #
                    initCmds.append(item)

            if initCmds:
                SysMgr.printInfo(
                    "applied init commands from '%s'" % SysMgr.confFileName
                )

                # print thresholds #
                if SysMgr.warnEnable:
                    SysMgr.printWarn(UtilMgr.convDict2Str(initCmds))

            if "EVENT" in config and isinstance(config["EVENT"], list):
                # get valid commands #
                for item in config["EVENT"]:
                    if item.get("apply") != "true":
                        continue

                    # check duplicated registation #
                    name = item["name"]
                    if name in eventHandlers:
                        SysMgr.printWarn(
                            "'%s' event handler is already registered" % name,
                            True,
                        )
                        continue

                    # register handler #
                    eventHandlers[name] = item

            if eventHandlers:
                SysMgr.printInfo(
                    "applied event handlers from '%s'" % SysMgr.confFileName
                )

                # print thresholds #
                if SysMgr.warnEnable:
                    SysMgr.printWarn(UtilMgr.convDict2Str(eventHandlers))

            return initCmds, eventHandlers

        def _runEventHandlers(event):
            # check event handler #
            if event not in eventHandlers:
                SysMgr.printErr("no event handler registered for '%s'" % event)
                return

            pid = 0
            for name, value in eventHandlers.items():
                # command #
                if "command" in value:
                    for cmd in value["command"]:
                        SysMgr.printInfo(
                            "execute '%s' via %s event" % (cmd, event)
                        )

                        # create a new worker process to execute a command #
                        pid = SysMgr.createProcess(cmd)
                        if pid == 0:
                            sys.exit(0)

                # handler #
                if "handler" in value:
                    for value in value["handler"]:
                        SysMgr.printInfo(
                            "call '%s' via %s event" % (value, event)
                        )

                        # create a new worker process #
                        pid = SysMgr.createProcess()
                        if pid != 0:
                            continue

                        # get function items #
                        cmd = value.split(":")
                        path, func = cmd[:2]
                        if len(cmd) > 2:
                            argset = cmd[2:]
                        else:
                            argset = []

                        # call handler #
                        res = UtilMgr.callPyFunc(path, func, argset)

            # wait for termination for remote commands #
            if pid != 0:
                SysMgr.waitChild()

        def _runInitCmds(addr, initCmds):
            for item in initCmds:
                # check command #
                if not "command" in item:
                    continue

                # check permission #
                if item.get("perm") == "root":
                    SysMgr.checkRootPerm(
                        msg="execute init command\n'%s'"
                        % UtilMgr.convDict2Str(item)
                    )

                # set timer #
                if "duration" in item:
                    sec = UtilMgr.convUnit2Time(item["duration"])
                    signal.signal(signal.SIGALRM, SysMgr.onAlarmExit)
                    signal.alarm(sec)

                # execute init commands #
                for cmd in item["command"]:
                    # split command by pipe character #
                    pipeCmds = cmd.split("->")

                    # normal command #
                    if len(pipeCmds) == 1:
                        # run remote task #
                        try:
                            rcmd = "%s|%s" % (addr, cmd)
                            SysMgr.runClientMode(rcmd)
                        except SystemExit:
                            continue
                        except:
                            SysMgr.printErr(
                                "failed to execute remote command '%s'" % rcmd,
                                reason=True,
                            )
                    # piped command #
                    elif len(pipeCmds) == 2:
                        # create a pipe #
                        rd, wr = os.pipe()

                        # create a new process #
                        pid = SysMgr.createProcess()
                        # writer relaying remote task #
                        if pid > 0:
                            # set stdio #
                            os.close(rd)
                            os.dup2(wr, 1)

                            # run remote task #
                            try:
                                pcmd = pipeCmds[0]
                                rcmd = "%s|%s" % (addr, pcmd)
                                SysMgr.runClientMode(rcmd)
                            except SystemExit:
                                continue
                            except:
                                SysMgr.printErr(
                                    "failed to execute remote command '%s'"
                                    % rcmd,
                                    reason=True,
                                )
                        # reader handling data #
                        else:
                            # set stdio #
                            os.close(wr)
                            os.dup2(rd, 0)

                            # run local task #
                            pcmd = UtilMgr.parseCommand(pipeCmds[1])
                            SysMgr.executeProcess(cmd=pcmd, closeFd=False)
                    else:
                        SysMgr.printErr(
                            "no support piped commands bigger than 2 for '%s'"
                            % cmd
                        )

        def _handleConn(connObj, connMan, initCmds=[], eventHandlers={}):
            # read command #
            req = connObj.recvfrom()

            # unmarshalling #
            if isinstance(req, tuple):
                # check garbage value #
                if req[0] == "":
                    return

                try:
                    message = req[0].decode()
                except:
                    message = req[0]

                if req[1] is None:
                    ip = connObj.ip
                    port = connObj.port
                else:
                    try:
                        ip = req[1][0]
                        port = req[1][1]
                    except:
                        SysMgr.printWarn(
                            "failed to get address of client from message"
                        )
                        return False

                SysMgr.printInfo(
                    "received the request '%s' from %s:%s"
                    % (message, ip, port)
                )
            else:
                SysMgr.printErr("received wrong request '%s'" % req)
                return False

            # get request and value #
            try:
                request, value = message.split(":", 1)
            except:
                request = value = None
                request = message

            # convert request to capital #
            request = request.upper()

            # define valid request list #
            requestList = {
                "BROADCAST": _onBroadcast,
                "CLEAR": _onClear,
                "DIR": _onDir,
                "DOWNLOAD": _onDownload,
                "JOBS": _onJobs,
                "LIST": _onList,
                "NEW": _onNew,
                "NOTIFY": _onNotify,
                "PING": _onPing,
                "RESTART": _onRestart,
                "RUN": _onRun,
                "UPSTREAM": _onUpstream,
                "UPLOAD": _onUpload,
            }

            # check request type #
            if not request in requestList:
                SysMgr.printWarn(
                    "failed to recognize the request '%s'" % message, True
                )

                _sendErrMsg(connObj, "no support the request '%s'" % message)

                return False
            elif request == "NEW":
                # register a new node #
                requestList[request](connObj, value, None)
                if not initCmds:
                    return False
            elif request == "CLEAR":
                # terminate all worker processes #
                requestList[request](connObj, value, None)
                return True
            elif request == "JOBS":
                # list all worker processes #
                requestList[request](connObj, value, None)
                return True
            elif request == "BROADCAST":
                # update service node list #
                _updateNodeList()
            elif request == "RESTART":
                SysMgr.printInfo("RESTART by %s:%s" % (ip, port))

                # restart with all nodes connected #
                try:
                    _onBroadcast(connObj, request, None, sync=False)
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printWarn(
                        "failed to handle %s command for all nodes" % request,
                        always=True,
                        reason=True,
                    )

                # close sockets #
                try:
                    connMan.close()
                except:
                    pass

                try:
                    connObj.close()
                except:
                    pass

                # restart server #
                SysMgr.restart()

            cmd = None
            if request == "NEW":
                # create new worker processes #
                for item in initCmds:
                    pid = SysMgr.createProcess()
                    if pid == 0:
                        cmd = item
                        break

                # connection manager return #
                if pid > 0:
                    return False
            else:
                # create a new worker process #
                pid = SysMgr.createProcess()
                if pid > 0:
                    return True

            # close listen socket of parent #
            if connMan:
                connMan.close()

            # save timestamp #
            before = time.time()

            # request init command #
            if request == "NEW":
                # remove host name from address #
                value = value.split("/", 1)[1]

                _runInitCmds(value, [cmd])
            # handle request #
            else:
                # build response data #
                response = "%s|%s" % (request, value)

                requestList[request](connObj, value, response)

            # print elapsed time #
            elapsed = time.time() - before
            SysMgr.printInfo(
                "elapsed %.6f for '%s' from %s:%s"
                % (elapsed, message, ip, port)
            )

            sys.exit(0)

        # start server mode #
        SysMgr.printInfo("SERVER MODE")

        # set environment variables #
        SysMgr.setRemoteEnv()

        # set default logger #
        if not SysMgr.stdlog:
            # get current time #
            try:
                datetime = SysMgr.getPkg("datetime", False)
                now = datetime.datetime.utcfromtimestamp(time.time())
                now = now.strftime("%y%m%dT%H%M%S")
            except SystemExit:
                sys.exit(0)
            except:
                now = long(time.time())

            # set file name #
            value = os.path.join(
                SysMgr.tmpPath, "guider_server_%s_%s.log" % (SysMgr.pid, now)
            )
            SysMgr.printInfo("applied '%s' as the log file" % value)

            # set logger #
            SysMgr.stdlog = LogMgr(value)

        # load config #
        try:
            initCmds, eventHandlers = _loadConfig()
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn("failed to load config", reason=True)
            initCmds = []
            eventHandlers = {}

        # import packages #
        SysMgr.getPkg("select")
        socket = SysMgr.getPkg("socket")

        # get IP and PORT #
        if SysMgr.localServObj:
            ip = SysMgr.localServObj.ip
            port = SysMgr.localServObj.port
            SysMgr.localServObj.close()
        else:
            ip = port = None

        # set local address #
        connMan = NetworkMgr.setServerNetwork(
            ip, port, force=True, blocking=True, tcp=True
        )
        if not connMan:
            return

        # register to another server as a service node #
        if SysMgr.remoteServObj:
            # create a new thread to manage socket #
            threadObj = SysMgr.getPkg("threading")
            tobj = threadObj.Thread(target=_register, args=[connMan])
            tobj.daemon = True
            tobj.start()

        SysMgr.printStat(
            "run server(%s:%s) in %s(%s)"
            % (
                SysMgr.localServObj.ip,
                SysMgr.localServObj.port,
                SysMgr.getComm(SysMgr.pid),
                SysMgr.pid,
            )
        )

        # set SA_RESTART for SIGCHLD #
        signal.siginterrupt(signal.SIGCHLD, False)

        # listen #
        try:
            connMan.listen()
            connMan.timeout()
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to listen to prepare for connection", True)
            sys.exit(-1)

        # run mainloop #
        while 1:
            # accept #
            try:
                sock, addr = connMan.accept()
            except SystemExit:
                sys.exit(0)
            except IOError as err:
                if err.errno != errno.EINTR:
                    SysMgr.printWarn(
                        "failed to accept the connection request", reason=True
                    )
                continue
            except socket.timeout:
                continue
            except:
                SysMgr.printWarn(
                    "failed to accept the connection request", reason=True
                )
                continue

            SysMgr.printInfo(
                "connected to the new client (%s:%s)" % (addr[0], addr[1])
            )

            # create a TCP socket #
            connObj = NetworkMgr(
                "server", addr[0], addr[1], tcp=True, bind=False
            )
            if not connObj.ip:
                continue

            # apply connected socket to object #
            connObj.socket = sock

            # handle request from client #
            if _handleConn(connObj, connMan, initCmds, eventHandlers):
                connObj.close()
                SysMgr.printBgProcs()

        sys.exit(0)

    @staticmethod
    def sendMessage(ip=None, port=None):
        # get argument #
        if SysMgr.hasMainArg():
            msg = SysMgr.getMainArg()
        else:
            SysMgr.printErr("no input message")
            sys.exit(-1)

        # use address from option #
        try:
            if ip is None and port is None:
                # check remote address #
                if not SysMgr.remoteServObj:
                    SysMgr.printErr("no server address info with -X option")
                    sys.exit(-1)

                netObj = SysMgr.remoteServObj
            # use address from args #
            else:
                netObj = NetworkMgr("client", ip, port)

            # set IP and PORT #
            ip = netObj.ip
            port = netObj.port

            # check repeat count #
            if SysMgr.repeatInterval:
                repeat = SysMgr.repeatInterval
            else:
                repeat = 1

            # send message #
            progStr = ""
            for idx in range(1, repeat + 1):
                netObj.send(msg)

                if repeat > 1:
                    progStr = " [%s/%s]" % (
                        UtilMgr.convNum(idx),
                        UtilMgr.convNum(repeat),
                    )

                SysMgr.printInfo(
                    "sent '%s' message to %s:%s%s" % (msg, ip, port, progStr)
                )
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                "failed to send message to %s:%s" % (ip, port), True
            )
            return -1

        return 0

    @staticmethod
    def runClientMode(cmds=None, writer=None):
        def _printMenu():
            sys.stdout.write("%s\n" % SysMgr.cliCmdStr)

        def _doPing(uinput, cmd=None, hasarg=False):
            # set default command to ping #
            if not cmd:
                cmd = "ping"

            # get args #
            args = UtilMgr.lstrip(uinput, [cmd, cmd.upper()])
            if args and not args[0].isdigit():
                args = args[1:]

            # set variable #
            if hasarg:
                items = args
                addrs = None
            else:
                addrs = args
                items = ""

            # classify IP and PORT #
            if addrs:
                service, ip, port = NetworkMgr.parseAddr(addrs)
                if service == ip == port == None:
                    SysMgr.printErr(
                        "failed to recognize command %s" % uinput, True
                    )
                    return
                else:
                    NetworkMgr.setRemoteServer(addrs, tcp=True)

            # append args to command #
            if items:
                cmd = "%s|%s" % (cmd, items)

            # request command #
            ret = NetworkMgr.requestPing(cmd=cmd)

            # PING #
            ucmd = cmd.upper()
            if ucmd.startswith("PING"):
                if ret:
                    SysMgr.printInfo("server is alive")
                else:
                    SysMgr.printInfo("server is not responding")
            # RESTART #
            elif ucmd.startswith("RESTART"):
                SysMgr.printInfo("server is restarted")

        def _printHistory(hlist):
            print("\n<History>")
            if hlist:
                for idx, cmd in enumerate(hlist):
                    print(
                        "[%0d] %s" % (idx, UtilMgr.convColor(cmd, "UNDERLINE"))
                    )
                print('input "! + index" to execute the above commands')
            else:
                print("no history")

        def _getUserInput():
            _printMenu()
            sys.stdout.write("input command for request...\n=> ")
            sys.stdout.flush()

            return sys.stdin.readline()[:-1]

        def _convUserCmd(uinput):
            uinputUpper = uinput.upper()
            if uinputUpper.startswith("D:"):
                uinput = "download" + uinput[1:]
            elif uinputUpper.startswith("U:"):
                uinput = "upload" + uinput[1:]
            elif uinputUpper.startswith("R:"):
                uinput = "run" + uinput[1:]
            elif uinputUpper.startswith("B:"):
                uinput = "broadcast" + uinput[1:]
            elif uinputUpper.startswith("S:"):
                uinput = "upstream" + uinput[1:]
            elif uinputUpper.startswith("N:"):
                uinput = "notify" + uinput[1:]
            elif uinputUpper == "H":
                uinput = "history"
            elif uinputUpper == "P":
                uinput = "ping"
            elif uinputUpper == "L":
                uinput = "list"
            elif uinputUpper == "C":
                uinput = "clear"
            elif uinputUpper == "J":
                uinput = "jobs"
            elif uinputUpper == "Q":
                uinput = "quit"

            return uinput

        def _execUserCmd(uinput, addr=None, retPipe=False):
            def _unsetAlarm():
                if SysMgr.isLinux:
                    signal.signal(signal.SIGALRM, SysMgr.defaultHandler)

            def _setAlarm():
                if SysMgr.isLinux:
                    signal.signal(signal.SIGALRM, SysMgr.onAlarmExit)
                    SysMgr.intervalEnable = 1
                    SysMgr.repeatCnt = sys.maxsize

            # check short command #
            uinputUpper = uinput.upper()
            if uinputUpper.startswith("PING"):
                _doPing(uinput)
                return
            elif uinputUpper.startswith("RESTART"):
                _doPing(uinputUpper, "RESTART", hasarg=True)
                return

            # launch remote command #
            pipe = NetworkMgr.execRemoteCmd(uinput, addr)
            if not pipe:
                if addr:
                    addrstr = " at %s" % addr
                else:
                    addrstr = ", check server IP and port"

                SysMgr.printErr("failed to execute '%s'%s" % (uinput, addrstr))
                return

            # return pipe or True #
            if retPipe or pipe is True:
                return pipe

            # set alarm #
            _setAlarm()

            # block signal #
            SysMgr.blockSignal(act="block")

            # set print flag #
            printFlag = SysMgr.getPrintFlag()

            while 1:
                try:
                    SysMgr.updateTimer()

                    output = pipe.getData()
                    if not output:
                        break

                    # check data type #
                    # dataType = pipe.getDataType(output)

                    if printFlag:
                        print(output[:-1])
                except SystemExit:
                    if not SysMgr.isLinux:
                        break
                except KeyboardInterrupt:
                    break
                except:
                    SysMgr.printErr(SysMgr.getErrMsg())
                    break

                if SysMgr.pendingSignal(signal.SIGINT):
                    break

            # close remote task #
            try:
                pipe.close()
            except:
                pass

            # disable alarm handler #
            _unsetAlarm()

            # unblock signal #
            SysMgr.blockSignal(act="unblock")

        # start client mode #
        SysMgr.printInfo("CLIENT MODE")

        # load libc #
        if SysMgr.loadLibcObj():
            # disable SIGINT #
            signal.signal(signal.SIGINT, signal.SIG_IGN)

        # get argument #
        if cmds:
            cmdList = cmds.split(",")
            cmdList = UtilMgr.cleanItem(cmdList, False)
        elif SysMgr.hasMainArg():
            cmdList = SysMgr.getMainArgs(False)
        elif SysMgr.customCmd:
            cmdList = SysMgr.customCmd
        else:
            cmdList = []

        # set environment for parallel commands #
        if cmdList:
            SysMgr.setDefaultSignal()

            selectObj = SysMgr.getPkg("select")

            # print window size for commands #
            windowSize = long(SysMgr.ttyRows / len(cmdList))
            SysMgr.printInfo("set each window height to %s" % (windowSize + 2))

            # get print flag #
            printFlag = SysMgr.getPrintFlag()

            # set default writer #
            if not writer:
                writer = sys.stdout

        # save timestamp for start #
        start = time.time()

        # run parallel commands #
        cmdPipeList = {}
        for idx, uinput in enumerate(cmdList):
            fullInput = uinput

            # get address #
            if "|" in uinput:
                addr, uinput = uinput.split("|", 1)
            else:
                addr = None

            # convert command shortcut #
            uinput = _convUserCmd(uinput)

            # fit Guider's window size #
            if uinput.startswith("run:GUIDER") and not " -m" in uinput:
                uinput += " -m %s:%s" % (windowSize + 2, SysMgr.ttyCols)

            # execute an user command #
            pipe = _execUserCmd(uinput, addr, retPipe=True)
            if not pipe:
                sys.exit(0)
            elif pipe is True:
                continue

            # set timeout #
            pipe.timeout(0.1)

            # register socket to command list #
            cmdPipeList[pipe.socket] = [fullInput, pipe, [""] * SysMgr.ttyRows]

        # run mainloop for parallel commands #
        while 1:
            if not cmdPipeList:
                if cmdList:
                    SysMgr.printInfo(
                        "elapsed %.6f for '%s'"
                        % (time.time() - start, ", ".join(cmdList))
                    )
                    sys.exit(0)
                break

            try:
                # set fds #
                listenFds = [item for item in cmdPipeList]

                if len(listenFds) > 1:
                    isMulti = True
                else:
                    isMulti = False

                # update window size #
                windowSize = long(SysMgr.ttyRows / len(cmdPipeList))

                # wait for event #
                [read, write, error] = selectObj.select(listenFds, [], [], 0.1)

                # handle output from multiple commands #
                for robj in read:
                    while 1:
                        # clear screen #
                        if len(cmdPipeList) > 1:
                            SysMgr.clearScreen()

                        # read output #
                        output = cmdPipeList[robj][1].getData(noTimeout=False)
                        if output == "\n":
                            if isMulti:
                                break
                            continue
                        elif not output:
                            cmdPipeList.pop(robj, None)
                            break

                        # handle output #
                        if len(cmdPipeList) == 1:
                            if printFlag:
                                writer.write(output)
                            if isMulti:
                                break
                            else:
                                continue

                        # update a surface #
                        fullSurface = ""
                        surface = cmdPipeList[robj][2]
                        output = [line for line in output.split("\n") if line]
                        surface = surface[len(output) :] + output
                        cmdPipeList[robj][2] = surface

                        # composite surfaces #
                        nrLine = 0
                        for idx, item in enumerate(cmdPipeList.values()):
                            surface = item[2]

                            if idx == len(cmdPipeList) - 1:
                                nrStrip = (
                                    SysMgr.ttyRows - nrLine - windowSize - 1
                                )
                                window = surface[-windowSize:nrStrip]
                            else:
                                window = surface[-windowSize:]

                            nrLine += len(window) + 1

                            if idx < len(cmdPipeList) - 1:
                                fullSurface += "\n".join(window)
                                fullSurface += "\n%s\n" % splitLine
                            else:
                                fullSurface += "\n".join(window)
                                fullSurface += "\n"

                        # update screen in 20 FPS #
                        if printFlag:
                            writer.write(fullSurface)

                        # delay for overhead #
                        time.sleep(0.05)

                        if isMulti:
                            break
            except SystemExit:
                sys.exit(0)
            except KeyboardInterrupt:
                sys.exit(0)
            except:
                SysMgr.printErr(
                    "failed to handle multiple commands", reason=True
                )

        # run mainloop for user interaction #
        hlist = []
        while 1:
            try:
                isHistory = False

                # get input #
                uinput = _getUserInput()

                # check input for history #
                if uinput.startswith("!") and len(uinput) > 1:
                    try:
                        uinput = hlist[long(uinput[1:])]
                        isHistory = True
                    except:
                        SysMgr.printErr(
                            "wrong history index for '%s'" % uinput[1:]
                        )
                        continue

                # convert command shortcut #
                uinput = _convUserCmd(uinput)
                uinputUpper = uinput.upper()

                # handle local command #
                if not uinput or uinput == "!" or uinputUpper == "HISTORY":
                    _printHistory(hlist)
                    continue
                elif uinputUpper.startswith("PING"):
                    _doPing(uinput)
                    continue
                elif uinputUpper == "QUIT":
                    break

                # backup command #
                if not isHistory and (not hlist or hlist[-1] != uinput):
                    hlist.append(uinput)

                # execute an user command #
                _execUserCmd(uinput)
            except SystemExit:
                return
            except:
                pass

    @staticmethod
    def getIdleTime():
        try:
            # read stat buf #
            cpuBuf = SysMgr.readProcStat(
                SysMgr.statFd, "stat", SysMgr, "statFd"
            )
            return long(cpuBuf[0].split()[4])
        except SystemExit:
            sys.exit(0)
        except:
            return 0

    @staticmethod
    def getNrCore():
        if SysMgr.nrCore > 0:
            return SysMgr.nrCore

        if not SysMgr.isLinux:
            try:
                # get psutil object #
                psutil = SysMgr.getPkg("psutil", False)
                nrCore = psutil.cpu_count()
                SysMgr.nrCore = nrCore
                return nrCore
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printWarn(
                    "failed to get the number of CPU", reason=True
                )
                return 0

        # read stat buf #
        cpuBuf = SysMgr.readProcStat(SysMgr.statFd, "stat", SysMgr, "statFd")

        nrCore = 0
        if cpuBuf:
            for line in cpuBuf:
                statList = line.split()
                cpuId = statList[0]
                if cpuId != "cpu" and cpuId.startswith("cpu"):
                    nrCore += 1

            # set the number of core #
            SysMgr.nrCore = nrCore

        return nrCore

    @staticmethod
    def doDrawReq():
        def _drawRes(stats):
            # pylint: disable=undefined-variable
            def _drawMeta(labelList, xfsize):
                # pylint: disable=undefined-variable
                # draw label #
                TaskAnalyzer.drawLabel(labelList, draw=True, anchor=(1.12, 1))

                # update yticks #
                TaskAnalyzer.drawYticks(ax, ymax=None, adjust=False)

                # draw time unit #
                try:
                    ytickLabel = ax.get_yticks().tolist()
                    ytickLabel = list(map(long, ytickLabel))
                    ytickLabel = [val for val in ytickLabel]
                    ytickLabel[0] = "ms"

                    # apply formatter #
                    ax.set_yticks(ax.get_yticks().tolist())
                    ax.set_yticklabels(ytickLabel)
                except:
                    pass

                # draw grid #
                xticks(fontsize=xfsize)
                grid(which="both", linestyle=":", linewidth=0.2)
                tick_params(direction="in")

            # draw base #
            TaskAnalyzer.drawFigure()

            # get font size #
            if "FONTSIZE" in SysMgr.environList:
                fontsize = UtilMgr.getEnvironNum("FONTSIZE")
                xfsize = lfsize = fontsize
            else:
                xfsize = UtilMgr.getEnvironNum("XFONTSIZE", False, 4, False)
                lfsize = UtilMgr.getEnvironNum("LFONTSIZE", False, 3, False)

            # draw title #
            ax = subplot2grid((6, 1), (0, 0), rowspan=3, colspan=1)
            ax.xaxis.set_major_locator(MaxNLocator(integer=True))
            suptitle("Guider Response Graph", fontsize=8)

            # define integrated request list for processes #
            totalStats = {}
            maxLabelLen = 80

            # draw line plots for per-process requests #
            labelList = []
            for req, stat in stats.items():
                timeline = stat["reqtime"]
                response = stat["restime"]

                realreq = req[req.find(")_") + 2 :]
                totalStats.setdefault(realreq, [])
                totalStats[realreq] += response

                # draw total GPU graph #
                plot(
                    timeline,
                    response,
                    "-",
                    linewidth=1,
                    marker="d",
                    markersize=1,
                    solid_capstyle="round",
                )

                labelList.append(req[:maxLabelLen])

            # set xticks #
            try:
                xtickLabel = ax.get_xticks().tolist()

                try:
                    datetime = SysMgr.getPkg("datetime")

                    # convert time unit #
                    for idx, item in enumerate(xtickLabel):
                        ms = item % 1000
                        value = datetime.datetime.utcfromtimestamp(item / 1000)
                        xtickLabel[idx] = value.strftime("%H:%M:%S")
                        xtickLabel[idx] = "%s.%0.3d" % (xtickLabel[idx], ms)
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printWarn(
                        "failed to convert time unit in xticks", reason=True
                    )

                xtickLabel[-1] = "Time"
                ax.set_xticks(ax.get_xticks())
                ax.set_xticklabels(xtickLabel)
            except SystemExit:
                sys.exit(0)
            except:
                pass

            _drawMeta(labelList, xfsize)

            # draw bar plots for requests #
            ax = subplot2grid((6, 1), (3, 0), rowspan=3, colspan=1)
            ax.xaxis.set_major_locator(MaxNLocator(integer=True))

            start = 0
            width = 0.1
            for req, value in totalStats.items():
                totval = sum(value)
                avgval = totval / len(value)
                minval = min(value)
                maxval = max(value)
                data = [minval, avgval, maxval]

                # draw bar #
                brange = []
                for idx in xrange(-1, 2):
                    brange.append(start + idx / 10)
                ax.bar(
                    brange,
                    data,
                    width=width,
                    edgecolor="white",
                    label=req[:maxLabelLen],
                )

                # draw stat text on the bar #
                for idx, value in enumerate(data):
                    text(
                        start + (idx - 1) / 10,
                        value,
                        UtilMgr.convNum(value),
                        color="black",
                        fontweight="bold",
                        fontsize=lfsize,
                    )

                # draw name text over the bar #
                text(
                    start,
                    maxval / 2,
                    req[:maxLabelLen],
                    color="black",
                    fontsize=lfsize,
                    rotation=35,
                )

                start += 1

            # set ticks #
            try:
                xtickLabel = ax.get_xticks().tolist()
                xtickLabel = list(map(long, xtickLabel))
                xtickLabel = list(xrange(-1, len(totalStats) + 1))
                ax.set_xticks(xtickLabel)
                xtickLabel[0] = ""
                xtickLabel[-1] = "Req"
                for idx in xrange(1, len(totalStats) + 1):
                    xtickLabel[idx] = "Min/Avg/Max"
                ax.set_xticklabels(xtickLabel)
            except:
                pass

            _drawMeta(None, xfsize)

            # save to file #
            return TaskAnalyzer.saveImage(SysMgr.inputFile, "graph")

        def _getDrawStat(path):
            try:
                fd = open(path, "r")
                data = fd.readlines()
                fd.close()
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr("failed to read '%s'" % path, reason=True)
                sys.exit(-1)

            startPos = -1
            task = None
            for idx, line in enumerate(data):
                if line.startswith("[Response Time]"):
                    for item in line.split("["):
                        if item.startswith("Task:"):
                            task = item[5:].strip().rstrip("]")
                    startPos = idx + 4
                    break

            # check start pos #
            if startPos == -1:
                SysMgr.printErr("failed to recognize '%s'" % path)
                sys.exit(-1)

            edata = data[startPos:]

            # parse response time for requests #
            resTable = {}
            for line in edata:
                if not line.strip():
                    break
                elif line.startswith(oneLine):
                    continue

                req, times = line.split("|", 1)

                # check filter #
                if SysMgr.filterGroup:
                    if not UtilMgr.isValidStr(req):
                        continue

                # add timestamps in ms #
                reqtimeList = []
                restimeList = []
                timeList = times.split(",")
                for item in timeList:
                    reqtime, restime = item.split("/")
                    reqtimeList.append(float(reqtime) * 1000)
                    restimeList.append(float(restime) * 1000)

                # initialize lists #
                reqid = "%s_%s" % (task, req)
                resTable.setdefault(reqid, {})
                resTable[reqid].setdefault("reqtime", [])
                resTable[reqid].setdefault("restime", [])

                resTable[reqid]["reqtime"] += reqtimeList
                resTable[reqid]["restime"] += restimeList

            return resTable

        # initialize environment for drawing #
        TaskAnalyzer.initDrawEnv()

        # get argument #
        if SysMgr.hasMainArg():
            inputParam = SysMgr.getMainArg().split(",")
            inputParam = UtilMgr.getFileList(inputParam)
        elif SysMgr.inputParam:
            inputParam = SysMgr.inputParam.split(",")
            inputParam = UtilMgr.getFileList(inputParam)
        else:
            inputParam = [SysMgr.outFilePath]

        # check input path #
        if not inputParam:
            SysMgr.printErr("no input for PATH")
            sys.exit(-1)

        # get response time from file #
        stats = {}
        for path in inputParam:
            try:
                if not os.path.exists(path):
                    raise Exception("not exist")

                SysMgr.printStat(r"start loading '%s'..." % path)
                stat = _getDrawStat(path)
                if not stat:
                    raise Exception("no data")
                stats.update(stat)
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr(
                    "failed to get stats from '%s'" % path, reason=True
                )
                sys.exit(-1)

        SysMgr.printStat(r"start drawing graphs...")

        # draw response time #
        _drawRes(stats)

    @staticmethod
    def doConvert():
        # remove option args #
        SysMgr.removeOptionArgs()

        # get argument #
        if SysMgr.hasMainArg():
            value = SysMgr.getMainArg()
        else:
            SysMgr.printErr(
                "no path to convert file, input the path of a text file"
            )
            sys.exit(-1)

        # check file #
        if not os.path.isfile(value):
            SysMgr.printErr("wrong path '%s' for converting" % value)
            sys.exit(-1)

        # set output file name #
        SysMgr.imagePath = os.path.splitext(value)[0]

        # open text file #
        try:
            with open(value, "r") as fd:
                textBuf = fd.read()
        except:
            SysMgr.printErr("failed to read data from %s" % value)
            sys.exit(-1)

        # draw image #
        SysMgr.drawText(textBuf)

    @staticmethod
    def getCpuList():
        path = "/sys/devices/system/cpu"
        if not os.path.exists(path):
            return []

        return [
            c.lstrip("cpu")
            for c in os.listdir(path)
            if re.match("cpu+[0-9].*", c)
        ]

    @staticmethod
    def doSetCpu():
        freqPath = "/sys/devices/system/cpu"

        SysMgr.checkRootPerm()

        # check CPU driver #
        if not os.path.isdir(freqPath):
            SysMgr.printErr("failed to find CPU node for governor")
            sys.exit(-1)

        SysMgr.setStream()

        # get list option #
        printList = SysMgr.findOption("l")

        # get argument #
        if SysMgr.hasMainArg():
            filterGroup = SysMgr.getMainArgs()
        elif SysMgr.filterGroup:
            filterGroup = SysMgr.filterGroup
        elif printList:
            filterGroup = []
        else:
            SysMgr.printErr("no input for core info")
            sys.exit(-1)

        errMsg = (
            "wrong value to set CPU clock, "
            "input in the format CORE:CLOCK(HZ){:GOVERNOR}"
        )

        # parse values #
        targetlist = []
        for val in list(filterGroup):
            # split items #
            vals = val.split(":")
            if not len(vals) in (2, 3):
                SysMgr.printErr(errMsg)
                sys.exit(-1)

            core, clock = vals[0:2]

            # check core #
            if not core:
                pass
            elif not core.isdigit() and not "-" in core:
                SysMgr.printErr(errMsg)
                sys.exit(-1)

            # check clock #
            if not clock:
                pass
            elif not clock.isdigit() and not clock.upper() in ("ON", "OFF"):
                SysMgr.printErr(errMsg)
                sys.exit(-1)

            coreRange = vals[0].split("-", 1)
            if len(coreRange) == 1:
                targetlist.append(vals)
            else:
                try:
                    start, end = list(map(long, coreRange))
                    for idx in range(start, end + 1):
                        vals[0] = idx
                        targetlist.append(list(vals))
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr(errMsg)
                    sys.exit(-1)

        def _printCpuInfo(cpulist, core):
            conv = UtilMgr.convNum
            convColor = UtilMgr.convColor
            status = {}

            for item in ("cur", "min", "max", "governor", "online"):
                try:
                    status[item] = conv(cpulist[core][item].strip())
                except:
                    status[item] = "?"

            try:
                if "avail" in cpulist[core]:
                    avail = list(map(conv, cpulist[core]["avail"]))

                    idx = (
                        UtilMgr.bisect_left(
                            avail, status["cur"].replace(",", "")
                        )
                        - 1
                    )

                    if idx >= 0:
                        avail[idx] = convColor("*%s" % avail[idx], "GREEN")

                    avail = " ".join(avail)
                else:
                    avail = convColor("*%s" % status["cur"], "GREEN")
            except:
                avail = "?"

            try:
                governors = cpulist[core]["governors"]
                idx = governors.index(status["governor"])
                if idx >= 0:
                    governors[idx] = convColor("*%s" % governors[idx], "GREEN")
                governors = " ".join(governors)
            except:
                governors = "?"

            SysMgr.printPipe(
                (
                    "[ CPU(%s) ]\n"
                    "- cur_frequency: [ %s ]\n"
                    "- min_frequency: [ %s ]\n"
                    "- max_frequency: [ %s ]\n"
                    "- governor: [ %s ]\n"
                    "- online: [ %s ]\n\n"
                )
                % (
                    core,
                    avail,
                    status["min"],
                    status["max"],
                    governors,
                    status["online"],
                ),
                trim=False,
            )

        # get available CPU list #
        cpulist = {}
        for f in sorted(os.listdir(freqPath)):
            if not f.startswith("cpu"):
                continue

            cpu = f.split("cpu")[1]
            if not cpu.isdigit():
                continue

            # set path #
            commonpath = "%s/%s/cpufreq" % (freqPath, f)
            onlinepath = "%s/%s/online" % (freqPath, f)
            affectpath = "%s/affected_cpus" % commonpath
            govpath = "%s/scaling_available_governors" % commonpath
            curgovpath = "%s/scaling_governor" % commonpath
            availfreqpath = "%s/scaling_available_frequencies" % commonpath
            curfreqpath = "%s/scaling_cur_freq" % commonpath
            minfreqpath = "%s/scaling_min_freq" % commonpath
            maxfreqpath = "%s/scaling_max_freq" % commonpath

            cpulist[cpu] = {}

            # affected_cpus #
            try:
                with open(affectpath, "r") as fd:
                    cpulist[cpu]["affect"] = fd.readlines()[0].split()
            except SystemExit:
                sys.exit(0)
            except:
                pass

            # available_governors #
            try:
                with open(govpath, "r") as fd:
                    cpulist[cpu]["governors"] = fd.readlines()[0].split()
            except SystemExit:
                sys.exit(0)
            except:
                pass

            # cur_governors #
            try:
                with open(curgovpath, "r") as fd:
                    cpulist[cpu]["governor"] = fd.readlines()[0].strip()
            except SystemExit:
                sys.exit(0)
            except:
                pass

            # available_freq #
            try:
                with open(availfreqpath, "r") as fd:
                    cpulist[cpu]["avail"] = list(
                        map(long, fd.readlines()[0].split())
                    )
                    cpulist[cpu]["avail"].sort()
                    cpulist[cpu]["avail"] = list(
                        map(str, cpulist[cpu]["avail"])
                    )
            except SystemExit:
                sys.exit(0)
            except:
                pass

            # cur_freq #
            try:
                with open(curfreqpath, "r") as fd:
                    cpulist[cpu]["cur"] = fd.readlines()[0]
            except SystemExit:
                sys.exit(0)
            except:
                cpulist[cpu]["cur"] = 0

            # min_max_freq #
            try:
                with open(minfreqpath, "r") as fd:
                    cpulist[cpu]["min"] = fd.readlines()[0]

                with open(maxfreqpath, "r") as fd:
                    cpulist[cpu]["max"] = fd.readlines()[0]
            except SystemExit:
                sys.exit(0)
            except:
                cpulist.pop(cpu, None)

            # online #
            try:
                with open(onlinepath, "r") as fd:
                    cpulist[cpu]["online"] = fd.readlines()[0]
            except SystemExit:
                sys.exit(0)
            except:
                pass

            # print cpu info #
            if printList:
                _printCpuInfo(cpulist, cpu)

        if not targetlist:
            return

        # set CPU clock #
        for vals in targetlist:
            if len(vals) == 2:
                core, clock = vals
                gov = None
            elif len(vals) == 3:
                core, clock, gov = vals

            # check values #
            if not core and not clock and not gov:
                SysMgr.printErr("no value for CPU")
                continue

            # set hotplug status #
            if clock.upper() in ("ON", "OFF"):
                onlinepath = "%s/cpu%s/online" % (freqPath, core)
                try:
                    with open(onlinepath, "w") as fd:
                        if clock.upper() == "ON":
                            fd.write("1")
                            power = "ONLINE"
                        else:
                            fd.write("0")
                            power = "OFFLINE"

                    SysMgr.printInfo(
                        "changed CPU(%s) status to %s" % (core, power)
                    )
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr(
                        "failed to change hotplug status for CPU(%s)" % core,
                        True,
                    )
                    sys.exit(-1)

                continue

            if not core:
                cpuRange = list(cpulist)
            else:
                cpuRange = [core]

            # check support #
            if not core:
                pass
            elif not core in cpulist:
                SysMgr.printErr(
                    (
                        "failed to set CPU(%s) clock because "
                        "it doesn't support governor"
                    )
                    % core
                )
                sys.exit(-1)
            # check available clock #
            elif (
                "avail" in cpulist[core]
                and clock
                and long(clock) > 0
                and not clock in cpulist[core]["avail"]
            ) or (gov and not gov in cpulist[core]["governors"]):
                try:
                    avail = " ".join(cpulist[core]["avail"])
                except:
                    avail = "?"

                governors = " ".join(cpulist[core]["governors"])

                SysMgr.printErr(
                    (
                        "failed to set CPU(%s) clock because it"
                        " only supports \n\t-clock: [%s]\n\t-governor: [%s]"
                    )
                    % (core, avail, governors)
                )
                sys.exit(-1)

            def _printErr(res, core):
                SysMgr.printErr(
                    "failed to set %s of CPU(%s)" % (res, core), True
                )

            def _writeData(tpath, ttype, data, core):
                try:
                    with open(tpath, "w") as fd:
                        fd.write(data)
                except SystemExit:
                    sys.exit(0)
                except:
                    _printErr("%s clock" % ttype, core)

            for core in cpuRange:
                # set path #
                commonpath = "%s/cpu%s/cpufreq" % (freqPath, core)
                curgovpath = "%s/scaling_governor" % commonpath
                curfreqpath = "%s/scaling_cur_freq" % commonpath
                minfreqpath = "%s/scaling_min_freq" % commonpath
                maxfreqpath = "%s/scaling_max_freq" % commonpath

                # set clock range #
                if clock and long(clock) > 0:
                    _writeData(minfreqpath, "min clock", clock, core)
                    _writeData(maxfreqpath, "max clock", clock, core)

                if gov:
                    _writeData(curgovpath, "governor", gov, core)

                # cur_clock #
                curfreqstr = SysMgr.readFile(curfreqpath, verb=False)

                # cur_governor #
                try:
                    with open(curgovpath, "r") as fd:
                        curgovernor = fd.readlines()[0].split(None, 1)[0]
                except SystemExit:
                    sys.exit(0)
                except:
                    curgovernor = "?"

                # get affected CPU list #
                if (
                    "affect" in cpulist[core]
                    and len(cpulist[core]["affect"]) > 1
                ):
                    affectstring = " (it may affect CPU %s)" % ",".join(
                        cpulist[core]["affect"]
                    )
                else:
                    affectstring = ""

                if "cur" in cpulist[core]:
                    prevclockstr = "%sHz" % UtilMgr.convNum(
                        cpulist[core]["cur"]
                    )
                else:
                    prevclockstr = "?"

                if curfreqstr:
                    curclockstr = "%sHz" % UtilMgr.convNum(curfreqstr)
                else:
                    curclockstr = "?"

                SysMgr.printInfo(
                    "changed CPU(%s) %s[%s] -> %s[%s]%s"
                    % (
                        core,
                        prevclockstr,
                        cpulist[core]["governor"],
                        curclockstr,
                        curgovernor,
                        affectstring,
                    )
                )

    @staticmethod
    def doSetSched():
        SysMgr.warnEnable = True

        # get argument #
        if SysMgr.hasMainArg(dash=True):
            value = SysMgr.getMainArgs()
        elif SysMgr.filterGroup:
            value = SysMgr.filterGroup
        else:
            SysMgr.printErr(
                (
                    "wrong value to set priority, "
                    "input in the format POLICY:PRIORITY|TIME:TID|COMM"
                )
            )
            sys.exit(-1)

        # convert task ID #
        value = SysMgr.convertTaskIdInput(value)
        value = ",".join(value)

        while 1:
            # change priority #
            SysMgr.applyPriority(value)

            if not SysMgr.intervalEnable:
                break

            # wait for next tick #
            time.sleep(SysMgr.intervalEnable)

    @staticmethod
    def getKconf():
        if os.path.exists("/proc/config.gz"):
            inputParam = "/proc/config.gz"
        else:
            inputParam = "/boot/config-%s" % SysMgr.getKernelVersion(name=True)

        # check config file #
        if not os.path.exists(inputParam):
            SysMgr.printWarn("no kernel config file", True)
            return ""

        # get configs #
        try:
            if inputParam.endswith("gz"):
                return UtilMgr.ungzip(inputParam).decode()
            else:
                return open(inputParam, "r").read()
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printOpenErr(inputParam)
            return ""

    @staticmethod
    def doPrintKconf():
        SysMgr.printLogo(big=True, onlyFile=True)

        # get path argument #
        if SysMgr.inputParam:
            inputParam = SysMgr.inputParam.split(",")
            inputParam = UtilMgr.getFileList(inputParam)
        elif os.path.exists("/proc/config.gz"):
            inputParam = "/proc/config.gz"
        else:
            inputParam = "/boot/config-%s" % SysMgr.getKernelVersion(name=True)

        # check config file #
        if not os.path.exists(inputParam):
            SysMgr.printErr("no kernel config file (%s)" % inputParam)

        # get filter argument #
        if SysMgr.hasMainArg():
            filterGroup = SysMgr.getMainArgs()
        elif SysMgr.filterGroup:
            filterGroup = SysMgr.filterGroup
        else:
            filterGroup = ""

        # get configs #
        try:
            if inputParam.endswith("gz"):
                configs = UtilMgr.ungzip(inputParam)
                configs = configs.decode().split("\n")
            else:
                configs = open(inputParam, "r").readlines()
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printOpenErr(inputParam)
            return

        for config in configs:
            if filterGroup and not UtilMgr.isValidStr(
                config, filterGroup, ignCap=True
            ):
                continue
            SysMgr.printPipe(config)

    @staticmethod
    def doPrintVmalloc(topMode=False):
        if not topMode:
            SysMgr.pipeEnable = True
            SysMgr.printLogo(big=True, onlyFile=True)

        SysMgr.checkRootPerm()

        # get argument #
        if SysMgr.hasMainArg():
            target = SysMgr.getMainArgs()
        elif SysMgr.filterGroup:
            target = SysMgr.filterGroup
        else:
            target = []

        # read vmalloc info #
        instance = TaskAnalyzer(onlyInstance=True)
        instance.saveVmallocInfo()

        # set title and uptime #
        if topMode:
            title = "Top Vmalloc Info"
            uptime = "[Time: %.6f] " % SysMgr.updateUptime()
        else:
            title = "Vmalloc Info"
            uptime = ""

        # get total size #
        totalSize = sum([x["size"] for x in instance.vmallocData.values()])

        # print title #
        SysMgr.addPrint(
            "[%s] %s[NrItems: %s] [Total: %s] \n%s\n"
            % (
                title,
                uptime,
                UtilMgr.convNum(len(instance.vmallocData)),
                UtilMgr.convSize2Unit(totalSize),
                twoLine,
            ),
            newline=2,
        )
        SysMgr.addPrint(
            "{0:>7} {1:>1}\n{2:1}\n".format("SIZE", "NAME", twoLine), newline=2
        )

        # print vmalloc info #
        try:
            nrCnt = 0
            for name, items in sorted(
                instance.vmallocData.items(),
                key=lambda x: x[1]["size"],
                reverse=True,
            ):
                # check name #
                if target and not UtilMgr.isValidStr(name, target, inc=True):
                    continue

                SysMgr.addPrint(
                    "{0:>7} {1:>1}\n".format(
                        UtilMgr.convSize2Unit(items["size"]), name
                    ),
                    force=True,
                )
                nrCnt += 1

                # check terminal rows #
                if topMode and SysMgr.checkCutCond():
                    nrCnt = -1
                    break

            if nrCnt == 0:
                SysMgr.addPrint("    None\n")
            SysMgr.addPrint("%s\n" % oneLine, force=True)
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to print vmalloc info", True)
            sys.exit(-1)

        if topMode:
            SysMgr.printTopStats()
        else:
            SysMgr.doPrint()

    @staticmethod
    def doPrintSlab(topMode=False):
        if not topMode:
            SysMgr.pipeEnable = True
            SysMgr.printLogo(big=True, onlyFile=True)

        SysMgr.checkRootPerm()

        # check slab file #
        if not os.path.exists("/proc/slabinfo"):
            SysMgr.printErr("enable CONFIG_SLAB kernel option")
            sys.exit(-1)

        # get argument #
        if SysMgr.hasMainArg():
            target = SysMgr.getMainArgs()
        elif SysMgr.filterGroup:
            target = SysMgr.filterGroup
        else:
            target = []

        # read slab info #
        instance = TaskAnalyzer(onlyInstance=True)
        instance.saveSlabInfo()

        # set sort value #
        if SysMgr.sort:
            sortval = SysMgr.sort

            # check sort item #
            if not sortval in ("size", "active", "actsize", "total"):
                SysMgr.printErr("failed to sort by '%s'" % sortval)
                sys.exit(-1)
        else:
            sortval = "totsize"

        # set title and uptime #
        if topMode:
            title = "Top Slab Info"
            uptime = "[Time: %.6f] " % SysMgr.updateUptime()
        else:
            title = "Slab Info"
            uptime = ""

        # get total size #
        totalSize = sum([x["totsize"] for x in instance.slabData.values()])

        # print title #
        SysMgr.addPrint(
            "[%s] %s[NrItems: %s] [Total: %s]\n%s\n"
            % (
                title,
                uptime,
                UtilMgr.convNum(len(instance.slabData)),
                UtilMgr.convSize2Unit(totalSize),
                twoLine,
            ),
            newline=2,
        )
        SysMgr.addPrint(
            "{0:>11} {1:>11} {2:>4} {3:>7} {4:>12} {5:>12} {6:1}\n".format(
                "OBJS",
                "ACTIVE",
                "USE",
                "OBJSIZE",
                "ACTSIZE",
                "TOTSIZE",
                "NAME",
            )
        )
        SysMgr.addPrint("%s\n" % twoLine)

        conv = UtilMgr.convNum

        # print slab info #
        try:
            nrCnt = 0
            for name, items in sorted(
                instance.slabData.items(),
                key=lambda x: x[1][sortval],
                reverse=True,
            ):
                # check name #
                if target and not UtilMgr.isValidStr(name, target, inc=True):
                    continue

                total = items["total"]
                active = items["active"]
                if total > 0:
                    actper = long((active / float(total)) * 100)
                else:
                    actper = 0
                size = items["size"] / float(1024)
                actsize = conv(long(items["actsize"] / 1024))
                totsize = conv(long(items["totsize"] / 1024))
                SysMgr.addPrint(
                    (
                        "{0:>11} {1:>11} {2:3}% {3:6.2f}K "
                        "{4:>11}K {5:>11}K {6:1}\n"
                    ).format(
                        conv(total),
                        conv(active),
                        actper,
                        size,
                        actsize,
                        totsize,
                        name,
                    ),
                    force=True,
                )
                nrCnt += 1

                # check terminal rows #
                if topMode and SysMgr.checkCutCond():
                    nrCnt = -1
                    break

            if nrCnt == 0:
                SysMgr.addPrint("    None\n")
            if nrCnt >= 0:
                SysMgr.addPrint("%s\n" % oneLine, force=True)
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to print slab info", True)
            sys.exit(-1)

        if topMode:
            SysMgr.printTopStats()
        else:
            SysMgr.doPrint()

    @staticmethod
    def doPrintEnv():
        SysMgr.printLogo(big=True, onlyFile=True)

        # set line length #
        lenLine = long(len(oneLine) / 2)

        # get argument #
        onlyMe = False
        ret = SysMgr.selectTaskId()
        if ret:
            filterGroup = [ret]
        elif SysMgr.hasMainArg():
            filterGroup = SysMgr.getMainArgs()
        elif SysMgr.filterGroup:
            filterGroup = SysMgr.filterGroup
        else:
            onlyMe = True
            filterGroup = [str(SysMgr.pid)]

        # check root permission #
        if not onlyMe:
            SysMgr.checkRootPerm()

        pids = SysMgr.convTaskList(filterGroup)
        if not pids:
            SysMgr.printErr(
                "failed to find the process with PID %s"
                % ", ".join(filterGroup)
            )
            sys.exit(-1)

        # print values from the auxiliary vector #
        def _printAux(pid, comm):
            cmdline = SysMgr.getCmdline(pid)
            lenLine = 1

            auxList = SysMgr.getAuxList()
            if SysMgr.jsonEnable:
                SysMgr.printPipe(
                    UtilMgr.convDict2Str(
                        auxList, pretty=not SysMgr.streamEnable
                    ),
                    pager=False,
                )
                return

            if not auxList:
                # get maximum line length #
                line = "    None"
                lenLine = len(line)

                # print items #
                line = "    None"
                line += " " * (lenLine - len(line) - 1) + "|\n"
                SysMgr.addPrint(line, force=True)
            else:
                # get maximum line length #
                for name, val in sorted(auxList.items()):
                    line = "{0:<30} {1:1}".format(name, val)
                    if len(oneLine) < len(line):
                        lenLine = len(oneLine)
                    elif len(line) > lenLine:
                        lenLine = len(line) + 2

                # print items #
                for name, val in sorted(auxList.items()):
                    line = "{0:<30} {1:1}".format(name, val)
                    line += " " * (lenLine - len(line) - 1) + "|\n"
                    SysMgr.addPrint(line, force=True)

            SysMgr.printPipe(
                "\n[Env Info] [Proc: %s(%s)] [Cmd: %s]\n%s\n"
                % (comm, pid, cmdline, oneLine[:lenLine])
            )

            SysMgr.doPrint(clear=True)

            SysMgr.printPipe(oneLine[:lenLine])

        # check filter #
        if SysMgr.inputParam:
            filters = SysMgr.inputParam.split(",")
            filters = UtilMgr.cleanItem(filters)
        else:
            filters = []

        printCnt = 0
        for pid in pids:
            # get comm #
            comm = SysMgr.getComm(pid, True)

            # print just aux info #
            if SysMgr.findOption("l"):
                printCnt += 1
                _printAux(pid, comm)
                continue

            if SysMgr.jsonEnable:
                envs = SysMgr.getEnv(pid, retdict=True)
                if not envs:
                    continue

                envs["PID"] = pid
                envs["COMM"] = comm
                envs = UtilMgr.convDict2Str(envs, pretty=False)
                SysMgr.printPipe(envs)
                continue

            # get environment variables #
            envs = SysMgr.getEnv(pid)
            if not envs:
                if SysMgr.showAll:
                    envs = []
                else:
                    continue

            # get cmdline #
            cmdline = SysMgr.getCmdline(pid)

            # filter variables #
            if filters:
                filteredList = []
                for env in envs:
                    if UtilMgr.isValidStr(env, key=filters):
                        filteredList.append(env)
                envs = filteredList

            # remove useless items #
            envs = UtilMgr.cleanItem(envs, False)

            # get maximum line length #
            lenLine = 1
            for env in envs:
                if len(oneLine) < len(env):
                    lenLine = len(oneLine)
                elif len(env) > lenLine:
                    lenLine = len(env) + 2

            # print variables #
            for env in envs:
                if not env:
                    continue
                env += " " * (lenLine - len(env) - 1) + "|\n"
                SysMgr.addPrint(env, force=True)

            if not envs:
                line = "    None |\n"
                lenLine = len(line) - 1

            SysMgr.printPipe(
                "\n[Env Info] [Proc: %s(%s)] [Cmd: %s]\n%s\n"
                % (comm, pid, cmdline, oneLine[:lenLine])
            )

            SysMgr.doPrint(clear=True)

            if not envs:
                SysMgr.printPipe("%s%s" % (line, oneLine[:lenLine]))
            else:
                SysMgr.printPipe(oneLine[:lenLine])

            printCnt += 1

        if printCnt:
            SysMgr.printPipe("\n")
        else:
            SysMgr.printWarn("no accessible variable", True)

    @staticmethod
    def initNetlink():
        if not SysMgr.isLinux or SysMgr.netlinkObj:
            return

        array = SysMgr.getPkg("array", False)
        if not array:
            return

        # create netlink socket #
        sockObj = SysMgr.netlinkObj = NetworkMgr(
            "server", 0, 0, netlink=True, blocking=False, anyPort=True
        )

        NLM_F_REQUEST = 1

        CTRL_CMD_UNSPEC = 0
        CTRL_CMD_NEWFAMILY = 1
        CTRL_CMD_DELFAMILY = 2
        CTRL_CMD_GETFAMILY = 3
        CTRL_CMD_NEWOPS = 4
        CTRL_CMD_DELOPS = 5
        CTRL_CMD_GETOPS = 6

        CTRL_ATTR_UNSPEC = 0
        CTRL_ATTR_FAMILY_ID = 1
        CTRL_ATTR_FAMILY_NAME = 2
        CTRL_ATTR_VERSION = 3
        CTRL_ATTR_HDRSIZE = 4
        CTRL_ATTR_MAXATTR = 5
        CTRL_ATTR_OPS = 6

        geAttrStruct = [
            9,  # 0 CTRL_ATTR_UNSPEC
            """=H""",  # 1 U16(skb, CTRL_ATTR_FAMILY_ID
            0,  # 2 STRING(skb, CTRL_ATTR_FAMILY_NAME
            """=I""",  # 3 U32(skb, CTRL_ATTR_VERSION
            """=I""",  # 4 U32(skb, CTRL_ATTR_HDRSIZE
            """=I""",  # 5 U32(skb, CTRL_ATTR_MAXATTR
            """=I""",  # 6 U32(skb, CTRL_ATTR_OP_ID
            """=I""",  # 7 U32(skb, CTRL_ATTR_OP_FLAGS
            """=I""",  # 8 U32(skb, CTRL_ATTR_MCAST_GRP_ID
            9,  # 9 STRING(skb, CTRL_ATTR_MCAST_GRP_NAME
        ]

        cmd = "TASKSTATS\0"
        msgLen = len(cmd) + 4

        # build request packet #
        msg = array.array(str("B"))

        # check version #
        if sys.version_info < (3, 0, 0):
            conv = msg.fromstring
        else:
            conv = msg.frombytes

        conv(struct.pack("BBxx", CTRL_CMD_GETFAMILY, 0))
        conv(struct.pack("HH", msgLen, CTRL_ATTR_FAMILY_NAME))
        conv(cmd.encode())
        conv(b"\0" * ((4 - (len(cmd) % 4)) & 0x3))

        nlmsghdr = array.array(
            str("B"),
            struct.pack(
                str("=IHHII"),
                len(msg) + 16,
                ConfigMgr.NETLINK_TYPE["NETLINK_GENERIC"],
                NLM_F_REQUEST,
                0,
                0,
            ),
        )
        nlmsghdr.extend(msg)

        # send GETFAMILY command #
        sockObj.send(nlmsghdr)

        # recv result #
        data = sockObj.recv()
        if not data:
            return

        (size, type, flags, seq, pid) = struct.unpack(str("=IHHII"), data[:16])
        data = data[16:size]

        cmd, version = struct.unpack(str("=BBxx"), data[:4])
        data = data[4:]

        while len(data) > 0:
            length, typ = struct.unpack(str("=HH"), data[:4])
            length = length & 0x7FFF

            if geAttrStruct[typ] == 0:
                SysMgr.geAttr[typ] = data[4 : length - 1]
            elif geAttrStruct[typ] == 9:
                pass
            elif typ > 5:
                pass
            else:
                SysMgr.geAttr[typ] = struct.unpack(
                    geAttrStruct[typ], data[4:length]
                )[0]

            data = data[((((length + 3))) & ~0x3) :]

    @staticmethod
    def getTaskstats(target):
        if SysMgr.isAndroid:
            return None

        sockObj = SysMgr.netlinkObj
        geAttr = SysMgr.geAttr

        if not sockObj:
            SysMgr.printErr("not initialized netlink socket yet")
            return None

        NLMSG_MIN_TYPE = 0x10
        GENL_ID_CTRL = NLMSG_MIN_TYPE
        ACK_REQUEST = 4 | 1

        TASKSTATS_CMD_ATTR_PID = 1
        TASKSTATS_CMD_ATTR_TGID = 2
        TASKSTATS_CMD_GET = 1  # user -> kernel request/get-response
        TASKSTATS_CMD_NEW = 2  # kernel -> user event

        TASKSTATS_TYPE_PID = 1  # Process id
        TASKSTATS_TYPE_TGID = 2  # Thread group id
        TASKSTATS_TYPE_STATS = 3  # taskstats structure
        TASKSTATS_TYPE_AGGR_PID = 4  # contains pid + stats
        TASKSTATS_TYPE_AGGR_TGID = 5  # contains tgid + stats

        NLMSG_NOOP = 0x1
        NLMSG_ERROR = 0x2
        NLMSG_DONE = 0x3
        NLMSG_OVERRUN = 0x4

        CTRL_ATTR_UNSPEC = 0
        CTRL_ATTR_FAMILY_ID = 1
        CTRL_ATTR_FAMILY_NAME = 2
        CTRL_ATTR_VERSION = 3
        CTRL_ATTR_HDRSIZE = 4
        CTRL_ATTR_MAXATTR = 5
        CTRL_ATTR_OPS = 6

        TASKSTATS_STRUCT = "HIBBQQQQQQQQ32sIxxxIIIIIQQQQQQQQQQQQQQQQQQQQQQQ"
        TASKSTATS_FIELD = [
            "version",
            "ac_exitcode",
            "ac_flag",
            "ac_nice",
            "cpu_count",
            "cpu_delay_total",
            "blkio_count",
            "blkio_delay_total",
            "swapin_count",
            "swapin_delay_total",
            "cpu_run_real_total",
            "cpu_run_virtual_total",
            "ac_comm",
            "ac_sched",
            "ac_uid",
            "ac_gid",
            "ac_pid",
            "ac_ppid",
            "ac_btime",
            "ac_etime",
            "ac_utime",
            "ac_stime",
            "ac_minflt",
            "ac_majflt",
            "coremem",
            "virtmem",
            "hiwater_rss",
            "hiwater_vm",
            "read_char",
            "write_char",
            "read_syscalls",
            "write_syscalls",
            "read_bytes",
            "write_bytes",
            "cancelled_write_bytes",
            "nvcsw",
            "nivcsw",
            "utimescaled",
            "stimescaled",
            "cpu_scaled_run_real_total",
            "freepages_count",
            "freepages_delay_total",
        ]

        array = SysMgr.getPkg("array", False)
        if not array:
            return None

        # request #
        msg = array.array(str("B"))

        # check version #
        if sys.version_info < (3, 0, 0):
            conv = msg.fromstring
        else:
            conv = msg.frombytes

        conv(struct.pack("BBxx", TASKSTATS_CMD_GET, 0))

        cmd = struct.pack("=I", long(target))
        msgLen = len(cmd) + 4

        conv(struct.pack("HH", msgLen, TASKSTATS_CMD_ATTR_PID))
        conv(cmd)
        conv(b"\0" * ((4 - (len(cmd) % 4)) & 0x3))

        pid = sockObj.socket.getsockname()[0]
        nlmsghdr = array.array(
            str("B"),
            struct.pack(
                str("=IHHII"),
                len(msg) + 16,
                geAttr[CTRL_ATTR_FAMILY_ID],
                ACK_REQUEST,
                1,
                pid,
            ),
        )
        nlmsghdr.extend(msg)

        sockObj.send(nlmsghdr)

        cnt = 0
        while 1:
            data = sockObj.recv()
            if isinstance(data, bytes) and len(data) >= 328:
                break

            cnt += 1
            if cnt > 3:
                return None

        (size, ftype, flags, seq, pid) = struct.unpack(
            str("=IHHII"), data[:16]
        )
        data = data[16:size]

        cmd, version = struct.unpack(str("=BBxx"), data[:4])
        data = data[4:]

        attrs = dict(
            zip(TASKSTATS_FIELD, struct.unpack(TASKSTATS_STRUCT, data[16:344]))
        )
        attrs["ac_comm"] = attrs["ac_comm"].decode().rstrip("\0")

        return attrs

    @staticmethod
    def doPrintSdFile():
        def _getAttr(fpath):
            try:
                fd = open(fpath, "r")
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printOpenErr(fpath)
                return

            lines = fd.readlines()

            attrList = {}
            for line in lines:
                try:
                    if (
                        line == "\n"
                        or line.startswith("#")
                        or line.startswith("[")
                    ):
                        continue

                    name, value = line[:-1].split("=", 1)
                    attrList[name.strip()] = value.strip()
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printWarn(
                        "failed to parse line '%s'" % line[:-1], reason=True
                    )

            return attrList

        SysMgr.printLogo(big=True, onlyFile=True)

        # set dir path #
        if SysMgr.inputParam:
            sdPathList = SysMgr.inputParam.split(",")
            for d in sdPathList:
                if not os.path.isdir(d.strip()):
                    SysMgr.printErr("%s is not an accessible directory" % d)
                    sys.exit(-1)
        else:
            sdPathList = ["/etc/systemd/system", "/lib/systemd/system"]

        cv = UtilMgr.convNum

        busServiceList = {}
        filteredList = {}

        # save system stat #
        SysMgr.cmdlineEnable = True
        obj = TaskAnalyzer(onlyInstance=True)
        obj.saveSystemStat()

        # set filter #
        fileFilter = ["*.service", "*.target", "*.socket"]

        # parse service files #
        for spath in sdPathList:
            for items in os.walk(spath):
                for node in items[2]:
                    if not UtilMgr.isValidStr(node, fileFilter):
                        continue
                    elif node in busServiceList:
                        continue
                    elif not UtilMgr.isValidStr(node, ignCap=True):
                        continue

                    fpath = os.path.join(items[0], node)
                    if os.path.islink(fpath):
                        continue

                    busServiceList[node] = _getAttr(fpath)
                    busServiceList[node]["path"] = fpath

        # parse filter #
        attrList = []
        valList = []
        for item in SysMgr.customCmd:
            args = item.split(":", 1)
            if len(args) == 1:
                attrList.append(args[0])
            else:
                valList.append(args[1])

        # print service files #
        if busServiceList:
            SysMgr.printPipe(
                "[Systemd Target Service] (NrItems: %s)\n%s"
                % (cv(len(busServiceList)), twoLine)
            )
            nrItems = 0
            for node, value in sorted(
                busServiceList.items(), key=lambda e: e[0]
            ):
                cnt = 0
                for attr, val in sorted(value.items()):
                    if not UtilMgr.isValidStr(attr, attrList, ignCap=True):
                        continue
                    elif not UtilMgr.isValidStr(val, valList, ignCap=True):
                        continue

                    SysMgr.addPrint(
                        "{0:32} {1:1} = {2:1}\n".format(" ", attr, val),
                        isList=True,
                    )
                    cnt += 1

                if cnt > 0:
                    SysMgr.printPipe("[ %s ]" % node)
                    SysMgr.doPrint(clear=True, isList=True)
                    nrItems += 1
                else:
                    SysMgr.clearPrint()
                    filteredList[node] = value

            if nrItems == 0:
                SysMgr.printPipe("    None")
            SysMgr.printPipe("%s\n" % oneLine)

        # print filtered list #
        if filteredList:
            SysMgr.printPipe(
                "\n[Systemd Exceptional Service] (NrItems: %s)\n%s"
                % (cv(len(filteredList)), twoLine)
            )
            for node, value in sorted(filteredList.items()):
                SysMgr.printPipe("[ %s ]" % node)
                for attr, val in sorted(value.items()):
                    SysMgr.printPipe(
                        "{0:32} {1:1} = {2:1}".format(" ", attr, val)
                    )
                SysMgr.printPipe()
            SysMgr.printPipe("%s\n" % oneLine)

        sys.exit(0)

    @staticmethod
    def doPrintNs():
        SysMgr.printLogo(big=True, onlyFile=True)

        SysMgr.checkRootPerm()

        # get keyword #
        if SysMgr.hasMainArg():
            targetList = SysMgr.getMainArg(False)
        elif SysMgr.filterGroup:
            targetList = ",".join(SysMgr.filterGroup)
        else:
            targetList = []

        SysMgr.nsEnable = True
        cv = UtilMgr.convNum

        obj = TaskAnalyzer(onlyInstance=True)
        obj.saveSystemStat()

        for ns, val in sorted(obj.nsData.items(), key=lambda e: e[0]):
            if targetList and not ns in targetList:
                continue

            SysMgr.printPipe(
                "\n[%s] (Total: %s)\n%s" % (ns, cv(len(val)), twoLine)
            )

            cnt = 1
            for key, tids in sorted(val.items(), key=lambda e: e[0]):
                tid = sorted(list(tids), key=lambda e: long(e))[0]
                comm = obj.procData[tid]["stat"][obj.commIdx].strip("()")
                subStr = "{ %s(%s)" % (comm, tid)
                if len(tids) == 1:
                    subStr += " }"
                else:
                    subStr += ", ... }"

                nsStr = "(%3s) %s [Total: %s] %s" % (
                    cnt,
                    key,
                    len(tids),
                    subStr,
                )
                SysMgr.printPipe(nsStr)
                cnt += 1
                indentStr = " " * long(len(nsStr) / 2)

                if not SysMgr.showAll:
                    continue

                for tid in sorted(tids, key=lambda e: long(e)):
                    comm = obj.procData[tid]["stat"][obj.commIdx].strip("()")
                    cmdline = SysMgr.getCmdline(tid)
                    if cmdline:
                        cmdline = "<%s>" % cmdline
                    SysMgr.printPipe(
                        "%s - %s(%s) %s" % (indentStr, comm, tid, cmdline)
                    )

            SysMgr.printPipe("%s\n" % oneLine)

        sys.exit(0)

    @staticmethod
    def doPrintInfo():
        SysMgr.printLogo(big=True, onlyFile=True)

        # save system info #
        SysMgr.saveSysStats()

        if SysMgr.jsonEnable:
            # convert dict data to JSON string #
            jsonStr = UtilMgr.convDict2Str(
                SysMgr.jsonData, pretty=not SysMgr.streamEnable
            )
            if not jsonStr:
                SysMgr.printWarn(
                    "failed to convert report data to JSON format"
                )
            else:
                SysMgr.printPipe(jsonStr)
        else:
            SysMgr.printInfoBuffer()

        sys.exit(0)

    @staticmethod
    def isSilentCmd():
        if SysMgr.checkMode("getpid"):
            return True
        else:
            return False

    @staticmethod
    def doGetpid():
        # get keyword #
        if SysMgr.hasMainArg():
            targetList = SysMgr.getMainArg(False)
        elif SysMgr.filterGroup:
            targetList = ",".join(SysMgr.filterGroup)
        else:
            SysMgr.printErr("no target process info")
            sys.exit(-1)

        # get pids #
        ret = UtilMgr.sort(
            SysMgr.getTids(
                targetList,
                isThread=not SysMgr.processEnable,
                sibling=SysMgr.groupProcEnable,
            )
        )

        # apply seperator #
        sep = SysMgr.environList.get("SEP", [","])[0]

        # check output #
        if not ret:
            return

        # print count #
        if "PRINTCNT" in SysMgr.environList:
            print(len(ret))

        # print comm list #
        if "PRINTCOMM" in SysMgr.environList:
            if SysMgr.jsonEnable:
                print({targetList: SysMgr.getCommList(ret, isList=True)})
            else:
                print(SysMgr.getCommList(ret))
            return

        # print output #
        if SysMgr.jsonEnable:
            print({targetList: ret})
        else:
            for res in sep.join(ret).split("\\n"):
                print(res)

    @staticmethod
    def doCheckDup():
        SysMgr.checkRootPerm()

        pids = {}
        memsProc = {}
        totalRss = totalPss = 0

        # init option values #
        pageAlign = not "REALCHUNK" in SysMgr.environList
        mergeCall = "MERGECALL" in SysMgr.environList
        showHist = "SHOWHIST" in SysMgr.environList
        skipFile = "SKIPFILE" in SysMgr.environList
        printList = "PRINTLIST" in SysMgr.environList
        drawDupFlame = "DRAWDUPFLAME" in SysMgr.environList

        # set stream #
        SysMgr.setStream()

        # define variables #
        mergeTable = {}
        procUsageList = {}
        PAGESIZE = SysMgr.PAGESIZE
        SysMgr.memEnable = True
        SysMgr.ussEnable = True
        convSize = UtilMgr.convSize2Unit
        convNum = UtilMgr.convNum
        convColor = UtilMgr.convColor
        perProcInfo = SysMgr.environList.get("PERPROCINFO")
        memFilter = SysMgr.environList.get("MEMFILTER")
        if memFilter:
            memFilter = list(map(str.encode, memFilter))

        # set condition variables #
        if "BT" in SysMgr.environList:
            condBig = UtilMgr.getEnvironNum("BT", default=0, isInt=True)
        else:
            condBig = 0
        if "LT" in SysMgr.environList:
            condLess = UtilMgr.getEnvironNum("LT", default=0, isInt=True)
        else:
            condLess = 0

        def _checkInc(m, f):
            for fstr in f:
                fpos = m.find(fstr)
                if fpos > -1:
                    return fpos
            return -1

        def _getMemUsage(pid, totalRss, totalPss):
            # get memory usage #
            try:
                tobj = SysMgr.initTaskMon(pid, update=False)
                TaskAnalyzer.saveProcSmaps(
                    None, pid, True, ["mg"], tobj.procData
                )

                # get mergeable size by MADV_MERGEABLE #
                try:
                    mergeStat = tobj.procData[pid]["maps"]["FLAG"]
                    if not mergeStat:
                        raise Exception("no stat")

                    rss = mergeStat["Rss:"]
                    pss = mergeStat["Pss:"]
                    rate = (pss / float(rss)) * 100

                    totalRss += rss
                    totalPss += pss

                    mgsize = (long(rss) - long(pss)) << 10
                    mg = convSize(mgsize)
                    mg = convColor(mg, "GREEN")
                    rss = convSize(long(rss) << 10)
                    rss = convColor(rss, "YELLOW")
                    pss = convSize(long(pss) << 10)
                    pss = convColor(pss, "YELLOW")
                    rate = convColor("%.1f%%" % rate, "RED")

                    mgstr = "<Merged(%s)/Resident(%s)/Marked(%s)[%s]> " % (
                        mg,
                        pss,
                        rss,
                        rate,
                    )
                except SystemExit:
                    sys.exit(0)
                except:
                    mgsize = 0
                    mgstr = "<No Mergeable>"

                # save process info #
                if printList:
                    pinfo = "%s(%s)" % (SysMgr.getComm(pid), pid)
                    procUsageList[pinfo] = {"merged": mgsize, "status": mgstr}

                # save memory stats #
                tobj.saveProcInstance()
                mstat = TaskAnalyzer.getMemStr(
                    tobj, pid, stats=["rss", "pss", "uss"], color="YELLOW"
                )

                # save process info #
                if printList:
                    procUsageList[pinfo]["mstat"] = mstat

                mstat = " <%s> %s" % (mstat, mgstr)
                del tobj
            except SystemExit:
                sys.exit(0)
            except:
                mstat = ""
            return mstat, totalRss, totalPss

        def _printMemHist(mtable):
            if not mtable:
                return

            SysMgr.printPipe(dotLine)

            for key, cnt in sorted(
                mtable.items(),
                key=lambda x: long(x[1]),
                reverse=True,
            ):
                if condBig and cnt < condBig:
                    break
                elif condLess and cnt > condLess:
                    continue

                origLen = len(key)

                # check zero chunk #
                if len(set(key)) == 1:
                    key = "(%s * %s)" % (
                        key[0],
                        convNum(len(key)),
                    )
                else:
                    # convert to string #
                    key = repr(key).replace("\\x", "").strip("b'")

                key = ("<%7s> " % (convSize(cnt * origLen))) + key

                SysMgr.printPipe(
                    "{0:>20} {1:>12} {2:>7} {3:1}".format(
                        " ",
                        convNum(cnt),
                        convSize(origLen),
                        key,
                    )
                )

        def _isValidPage(pagemap, idx):
            # get page frame info #
            try:
                entry = struct.unpack("Q", pagemap[idx : idx + 8])[0]
            except SystemExit:
                sys.exit(0)
            except:
                entry = 0

            # check page attribute and return result #
            return (
                not PageAnalyzer.isSwapped(entry)
                and PageAnalyzer.isPresent(entry)
                and PageAnalyzer.isExmapped(entry)
            )

        # get chunk info from report files #
        if SysMgr.inputParam:
            sizeMB = 1048576
            flist = UtilMgr.cleanItem(SysMgr.inputParam.split(","), False)
            flist = UtilMgr.getFileList(flist, exceptDir=True)
            flist = flist if flist else []
            for fname in flist:
                fsize = UtilMgr.getFileSize(fname, string=False)
                fsizestr = convSize(fsize)
                SysMgr.printInfo(
                    "start reading '%s [%s]'..." % (fname, fsizestr)
                )

                # find starting position #
                cur = 0
                pos = -1
                while 1:
                    data = SysMgr.readFile(
                        fname, sizeMB, offset=cur, strip=False
                    )
                    if not data:
                        break

                    offset = data.find("[Leakage History] ")
                    if offset >= 0:
                        pos = cur + offset
                        break
                    else:
                        cur += len(data)
                if pos < 0:
                    SysMgr.printErr("no leakage history in '%s'" % fname)
                    continue

                # get fd #
                fd = SysMgr.getCachedFd(fname, "r")
                if fd:
                    fd.seek(pos)
                else:
                    sys.exit(-1)

                # get process info #
                info = fd.readline()
                pid = info.split("(", 1)[1].split(")", 1)[0]

                # check process #
                procInfo = info[info.find(":") + 2 :].split("]", 1)[0]
                if not SysMgr.isAlive(pid):
                    SysMgr.printErr("%s is not alive" % procInfo)
                    continue

                pids[pid] = None
                bufLen = fsize - pos
                pos = 0
                symTable = {}

                # get filter list #
                incList, exList = SysMgr.getTargetSymList()

                # remake call list with filter #
                callList = []
                for line in fd:
                    pos += len(line) + 1

                    UtilMgr.printProgress(pos, bufLen)

                    # skip line #
                    if line.startswith("-"):
                        continue

                    # add call info #
                    items = line.split("|")
                    try:
                        sym = items[4].strip()

                        # check filter #
                        if incList and not UtilMgr.isValidStr(sym, incList):
                            continue
                        elif exList and UtilMgr.isValidStr(sym, exList):
                            continue

                        if not sym in symTable:
                            symTable[sym] = sym

                        callList.append(
                            [
                                long(items[1].strip(), 16),
                                long(items[2].strip()),
                                symTable[sym],
                            ]
                        )
                    except SystemExit:
                        sys.exit(0)
                    except:
                        continue

                if pos:
                    UtilMgr.deleteProgress()

                # no sort #
                if not SysMgr.sort:
                    pass
                # sort by address #
                elif SysMgr.sort == "a":
                    SysMgr.printInfo("sorted by address")
                    callList.sort(key=lambda e: e[0])
                # sort by size #
                elif SysMgr.sort == "s":
                    SysMgr.printInfo("sorted by size")
                    callList.sort(key=lambda e: e[1])
                # sort by function #
                elif SysMgr.sort == "f":
                    SysMgr.printInfo("sorted by function")
                    callList.sort(key=lambda e: e[2])

                # print callstack-basis memory contents #
                if "SHOWMEM" in SysMgr.environList:
                    nrCallList = len(callList)
                    mergedMemTable = {}

                    # get memory usage #
                    mstat, totalRss, totalPss = _getMemUsage(
                        pid, totalRss, totalPss
                    )

                    # print summary #
                    title = "\n[Mem Info] (Proc: %s%s) (Call: %s)" % (
                        SysMgr.getCommList([pid]),
                        ":" + mstat if mstat else "",
                        convNum(nrCallList),
                    )
                    SysMgr.printPipe(title)

                    # print menu #
                    SysMgr.printPipe(
                        "{0:1}\n{1:>20} {2:>12} {3:>7} {4:1}".format(
                            twoLine,
                            "Count" if mergeCall else "Address",
                            "Alloc",
                            "Res",
                            "Stack",
                        )
                    )
                    if showHist or mergeCall:
                        SysMgr.printPipe(
                            "{4:1}\n{0:>20} {1:>12} {2:>7} {3:1}".format(
                                " ", "Count", " ", "Data", dotLine
                            )
                        )
                    SysMgr.printPipe(twoLine)

                    if SysMgr.outPath:
                        SysMgr.printInfo("start checking memory regions...")

                    # create a debugger object #
                    dbgObj = Debugger(pid)
                    dbgObj.initValues()
                    dbgObj.updateProcMap(onlyExec=False, saveAll=True)

                    # make global chunk table for dup check #
                    gMemTable = {}
                    dupCallTable = {}
                    dupCallList = callList if drawDupFlame else []
                    for ci, item in enumerate(dupCallList):
                        UtilMgr.printProgress(ci, nrCallList)

                        pos, size, sym = item

                        if skipFile and dbgObj.getFileFromMap(pos):
                            continue

                        # read segment #
                        chunk = dbgObj.readMem(
                            pos, size, verb=False, onlyBulk=True
                        )
                        if not chunk:
                            continue

                        # get page aligned pos and size #
                        npos = pos - (pos % PAGESIZE)
                        d = (pos + size) - npos
                        m = d % PAGESIZE
                        nsize = (d - m) + (PAGESIZE if m else 0)
                        last = pos + size

                        # check valid pages and chunks #
                        idx = 0
                        pagemap = PageAnalyzer.getPagemap(pid, npos, nsize)
                        for pidx, addr in enumerate(
                            xrange(npos, npos + nsize, PAGESIZE)
                        ):
                            # check page validation #
                            isValid = _isValidPage(pagemap, idx)
                            idx += 8

                            # page validation #
                            if not isValid:
                                continue

                            # save chunk #
                            start = 0 if pos >= addr else addr - pos
                            nextPage = addr + PAGESIZE
                            end = nextPage - pos if last > nextPage else None
                            cdata = chunk[start:end]
                            gMemTable[cdata] = gMemTable.get(cdata, 0) + 1
                    if drawDupFlame:
                        # remove uniq chunks #
                        gMemTable = {
                            k: v for k, v in gMemTable.items() if v != 1
                        }

                    nrPrint = 0
                    tsize = 0
                    tmsize = 0
                    for ci, item in enumerate(
                        sorted(callList, key=lambda k: k[1], reverse=True)
                    ):
                        UtilMgr.printProgress(ci, nrCallList)

                        pos, size, sym = item

                        if skipFile and dbgObj.getFileFromMap(pos):
                            continue

                        tsize += size

                        memFilterPos = -1

                        # read segment #
                        chunk = dbgObj.readMem(
                            pos, size, verb=False, onlyBulk=True
                        )
                        if not chunk:
                            continue
                        elif memFilter:
                            memFilterPos = _checkInc(chunk, memFilter)
                            if memFilterPos == -1:
                                continue

                        # get page aligned pos and size #
                        npos = pos - (pos % PAGESIZE)
                        d = (pos + size) - npos
                        m = d % PAGESIZE
                        nsize = (d - m) + (PAGESIZE if m else 0)
                        last = pos + size

                        # init mem table #
                        memTable = {}
                        if mergeCall:
                            mm = mergedMemTable.get(sym, [])
                            if mm:
                                memTable = mm[0]
                            else:
                                mergedMemTable[sym] = [memTable, 0, 0, 0]

                        # check valid pages and chunks #
                        idx = 0
                        mem = []
                        msize = 0
                        pagemap = PageAnalyzer.getPagemap(pid, npos, nsize)
                        for pidx, addr in enumerate(
                            xrange(npos, npos + nsize, PAGESIZE)
                        ):
                            # check page validation #
                            isValid = _isValidPage(pagemap, idx)
                            idx += 8

                            # page validation #
                            if not isValid:
                                continue

                            # get real data #
                            start = 0 if pos >= addr else addr - pos
                            nextPage = addr + PAGESIZE
                            end = nextPage - pos if last > nextPage else None
                            cdata = chunk[start:end]

                            # check dup of call chunks #
                            if drawDupFlame and cdata in gMemTable:
                                dupCallTable[sym] = dupCallTable.get(
                                    sym, 0
                                ) + len(cdata)

                            # save data #
                            if showHist or mergeCall:
                                # add data to hist table #
                                memTable[cdata] = memTable.get(cdata, 0) + 1
                            else:
                                # append data to list #
                                mem.append(cdata)

                            msize += len(cdata)

                        tmsize += msize

                        # save function chunk info #
                        if mergeCall:
                            mergedMemTable[sym][1] += size
                            mergedMemTable[sym][2] += msize
                            mergedMemTable[sym][3] += 1
                            continue

                        # convert list to bytes #
                        if not showHist:
                            mem = b"".join(mem)

                        # rebuild callstack #
                        if sym:
                            sym = UtilMgr.convStackStr(
                                sym, 41, len(oneLine)
                            ).lstrip()
                        else:
                            sym = ""

                        # print contents #
                        SysMgr.printPipe(
                            "{0:>20} {1:>12} {2:>7} {3:1}".format(
                                hex(pos), convNum(size), convSize(msize), sym
                            )
                        )

                        if not showHist:
                            # remove memTable #
                            memTable = {}

                            # check zero chunk #
                            if len(set(mem)) == 1:
                                mem = "(%s * %s)" % (mem[0], convNum(len(mem)))
                            mem = repr(mem).replace("\\x", "").strip("b'")
                            SysMgr.printPipe((" " * 43) + mem, colorCtl=False)

                        # print hist #
                        _printMemHist(memTable)

                        nrPrint += 1

                        SysMgr.printPipe(oneLine)

                    if callList:
                        UtilMgr.deleteProgress()

                        if SysMgr.outPath:
                            SysMgr.printInfo(
                                "memory %s/%s(%s%%) is valid for %s"
                                % (
                                    convSize(tmsize),
                                    convSize(tsize),
                                    convSize(
                                        tmsize / float(tsize) * 100, True
                                    ),
                                    procInfo,
                                )
                            )

                    # destroy debugger object #
                    dbgObj.detach()
                    del dbgObj
                    del gMemTable

                    if mergedMemTable and SysMgr.outPath:
                        SysMgr.printInfo(
                            "start merging valid memory regions..."
                        )

                    # print merged call info #
                    mi = 0
                    for sym, vals in sorted(
                        mergedMemTable.items(),
                        key=lambda x: x[1][2],
                        reverse=True,
                    ):
                        UtilMgr.printProgress(mi, len(mergedMemTable))
                        mi += 1

                        # rebuild callstack #
                        if sym:
                            sym = UtilMgr.convStackStr(
                                sym, 41, len(oneLine)
                            ).lstrip()
                        else:
                            sym = ""

                        memTable, size, msize, tcnt = vals

                        # print contents #
                        SysMgr.printPipe(
                            "{0:>20} {1:>12} {2:>7} {3:1}".format(
                                convNum(tcnt),
                                convNum(size),
                                convSize(msize),
                                sym,
                            )
                        )

                        # print hist #
                        _printMemHist(memTable)

                        nrPrint += 1

                        SysMgr.printPipe(oneLine)

                    if not nrPrint:
                        SysMgr.printPipe("    None\n" + oneLine)

                    if mergedMemTable:
                        UtilMgr.deleteProgress()

                    # draw flame graph for dup size #
                    if drawDupFlame and dupCallTable:
                        outputPath = UtilMgr.getDrawOutputPath(
                            UtilMgr.getInputNames(fname)[0], "flamegraph"
                        )

                        Debugger.drawFlame(
                            callList=dupCallTable,
                            title=UtilMgr.convHtmlChar(
                                UtilMgr.removeColor(title).strip()
                            ),
                            outFile=outputPath,
                        )

                # sort calls by address #
                if not SysMgr.sort:
                    callList.sort(key=lambda e: e[0])

                # merge calls in page unit #
                if pageAlign:
                    chunk = None
                    for call in callList:
                        # convert to page-aligned number #
                        addr, size = call[:2]
                        addr = long(addr / PAGESIZE) * PAGESIZE
                        size = (
                            long((size + PAGESIZE - 1) / PAGESIZE) * PAGESIZE
                        )

                        if not chunk:
                            chunk = [addr, size]
                            continue

                        # contiguous #
                        prev = chunk[0] + chunk[1]
                        if prev >= addr:
                            chunk[1] += addr - prev + size
                        # distant #
                        else:
                            memsProc.setdefault(pid, [])
                            memsProc[pid].append(chunk)
                            chunk = [addr, size]
                    # add last chunk #
                    if chunk:
                        memsProc.setdefault(pid, [])
                        memsProc[pid].append(chunk)
                # merge calls in each size #
                else:
                    for call in callList:
                        memsProc.setdefault(pid, [])
                        memsProc[pid].append(call[:2])

            # check target process list #
            if not pids:
                SysMgr.printErr("no target process alive")
                return

        elif SysMgr.hasMainArg():
            targetList = SysMgr.getMainArgs(False)
        elif SysMgr.filterGroup:
            targetList = SysMgr.filterGroup
        else:
            SysMgr.printErr("no target process info")
            sys.exit(-1)

        # get target task #
        if not SysMgr.inputParam:
            pids = SysMgr.convTaskList(targetList, exceptMe=True)
            if not pids:
                SysMgr.printErr("no process for '%s'" % ", ".join(targetList))
                sys.exit(-1)
        commList = SysMgr.getCommList(pids)

        # get target area #
        if "ONLYHEAP" in SysMgr.environList:
            target = "[heap]"
        elif "ONLYSTACK" in SysMgr.environList:
            target = "[stack]"
        else:
            target = "anon"

        SysMgr.printInfo(
            "check duplicated memory areas for [ %s ]\n" % commList
        )

        if SysMgr.customCmd:
            SysMgr.printInfo(
                "'%s' will be executed" % ("|".join(SysMgr.customCmd))
            )

        # get memory area option #
        addMems = []
        if "MEMAREA" in SysMgr.environList:
            for item in SysMgr.environList["MEMAREA"]:
                addMems.append(item.split("-"))

        # get skip checkdup #
        onlyStat = "ONLYSTAT" in SysMgr.environList
        skipDup = "SKIPDUP" in SysMgr.environList

        # get chunk info for processes #
        for pid in sorted(pids, key=lambda x: long(x)):
            # check task #
            if not SysMgr.isAlive(pid):
                SysMgr.printErr("no PID " + pid)
                continue

            # create a debugger object #
            try:
                dbgObj = Debugger(pid)
                dbgObj.initValues()
                dbgObj.updateProcMap(onlyExec=False, saveAll=True)
            except SystemExit:
                sys.exit(0)
            except:
                continue

            # get memory usage #
            mstat, totalRss, totalPss = _getMemUsage(pid, totalRss, totalPss)

            # read segments from memory map #
            if addMems:
                mems = addMems
            elif SysMgr.inputParam and memsProc.get(pid):
                mems = memsProc[pid]
            else:
                mems = FileAnalyzer.getMapAddr(pid, target, retList=True)

            # print status #
            procInfo = convColor("%s(%s)" % (SysMgr.getComm(pid), pid), "CYAN")
            SysMgr.printPipe(
                "[INFO] start reading %s memory areas for %s%s..."
                % (convNum(len(mems)), procInfo, mstat)
            )

            # skip checkdup #
            if onlyStat:
                continue

            # check duplicated memory #
            for idx, segment in enumerate(mems):
                UtilMgr.printProgress(idx, len(mems))

                try:
                    start, end = long(segment[0], 16), long(segment[1], 16)
                    size = end - start
                except SystemExit:
                    sys.exit(0)
                except:
                    start, size = list(map(long, segment))
                    end = start + size

                # check file filter #
                if skipFile:
                    mfname = dbgObj.getFileFromMap(start)
                    if mfname and mfname.startswith("/"):
                        continue

                # get addr and size #
                if pageAlign:
                    # get pagemap #
                    pagemap = PageAnalyzer.getPagemap(pid, start, size)

                    # get a list for contiguous chunks #
                    idx = addr = chunkStart = 0
                    chunkList = []
                    for addr in xrange(start, end, PAGESIZE):
                        # check page validation #
                        isValid = _isValidPage(pagemap, idx)
                        idx += 8

                        # check page attribute #
                        if isValid:
                            if chunkStart == 0:
                                chunkStart = addr
                        elif chunkStart != 0:
                            chunkList.append([chunkStart, addr - chunkStart])
                            chunkStart = 0

                    # handle the last valid chunks #
                    if 0 < addr <= end and chunkStart != 0:
                        if addr == chunkStart:
                            csize = PAGESIZE
                        else:
                            csize = addr - chunkStart
                        chunkList.append([chunkStart, csize])
                else:
                    chunkList = [[start, size]]

                # read current data #
                for which, length in chunkList:
                    memFilterPos = -1

                    # read segment #
                    mem = dbgObj.readMem(
                        which, length, verb=False, onlyBulk=True
                    )
                    if not mem:
                        continue
                    elif memFilter:
                        memFilterPos = _checkInc(mem, memFilter)
                        if memFilterPos == -1:
                            continue

                    # skip checking memory duplication #
                    if skipDup:
                        pass
                    # merge pages #
                    elif pageAlign:
                        pos = 0
                        while pos < len(mem):
                            # check size #
                            if pos + PAGESIZE > len(mem):
                                break
                            elif memFilterPos > -1:
                                if pos <= memFilterPos <= pos + PAGESIZE:
                                    pass
                                else:
                                    pos += PAGESIZE
                                    continue

                            # split to page size #
                            frame = mem[pos : pos + PAGESIZE]

                            if perProcInfo:
                                try:
                                    mergeTable[frame][0] += 1
                                    if pid in mergeTable[frame][1]:
                                        mergeTable[frame][1][pid] += 1
                                    else:
                                        mergeTable[frame][1][pid] = 1
                                except SystemExit:
                                    sys.exit(0)
                                except:
                                    mergeTable[frame] = [1, {pid: 1}]
                            else:
                                try:
                                    mergeTable[frame] += 1
                                except SystemExit:
                                    sys.exit(0)
                                except:
                                    mergeTable[frame] = 1

                            pos += PAGESIZE
                    # merge allocs #
                    else:
                        if perProcInfo:
                            try:
                                mergeTable[mem][0] += 1
                                if pid in mergeTable[mem][1]:
                                    mergeTable[mem][1][pid] += 1
                                else:
                                    mergeTable[mem][1][pid] = 1
                            except SystemExit:
                                sys.exit(0)
                            except:
                                mergeTable[mem] = [1, {pid: 1}]
                        else:
                            try:
                                mergeTable[mem] += 1
                            except SystemExit:
                                sys.exit(0)
                            except:
                                mergeTable[mem] = 1

                    # execute commands #
                    if SysMgr.customCmd:
                        cmds = []
                        # convert START and SIZE values #
                        for item in SysMgr.customCmd:
                            item = item.replace("START", str(which))
                            item = item.replace("SIZE", str(length))
                            cmds.append(item)

                        # execute remote commands #
                        dbgObj.executeCmd(cmds, force=True)
                        SysMgr.printPipe()

            UtilMgr.deleteProgress()

            # destroy debugger object #
            dbgObj.detach()
            del dbgObj

        # print process list #
        if printList:
            removeColor = UtilMgr.removeColor
            SysMgr.printPipe(
                "{0:1} (NrProc: {1:1})\n{2:1}\n{3:>30} {4:>40} {5:1}\n{2:1}".format(
                    "\n[Process List]",
                    convNum(len(procUsageList)),
                    twoLine,
                    "Proc",
                    "Stats",
                    "Status",
                )
            )
            for n, v in sorted(
                procUsageList.items(),
                key=lambda x: x[1]["merged"],
                reverse=True,
            ):
                SysMgr.printPipe(
                    "{0:>30} {1:>40} {2:1}\n{3:1}".format(
                        n,
                        removeColor(v["mstat"]) if "mstat" in v else "N/A",
                        removeColor(v["status"]).strip("<>"),
                        oneLine,
                    )
                )
            if not procUsageList:
                SysMgr.printPipe("\tN/A\n%s" % oneLine)

        # check result #
        if not mergeTable:
            SysMgr.printErr("no memory area to be checked")
            sys.exit(0)

        # calculate results #
        if perProcInfo:
            counts = [a[0] for a in mergeTable.values()]
        else:
            counts = mergeTable.values()

        # set sizes #
        if pageAlign:
            chunks = []
            uniqSize = len(counts) * PAGESIZE
            totalSize = sum(counts) * PAGESIZE
        else:
            chunks = mergeTable.keys()
            uniqSize = totalSize = 0
            for chunk, cnt in zip(chunks, counts):
                uniqSize += len(chunk)
                totalSize += len(chunk) * cnt
        dupSize = totalSize - uniqSize

        # build summary string #
        sizeMB = 1048576
        summary = "%s/%s" % (
            convSize(dupSize, unit="M" if dupSize > sizeMB else None),
            convSize(totalSize, unit="M" if totalSize > sizeMB else None),
        )
        summary = convColor(summary, "YELLOW")
        summary += "[%s]" % convColor(
            "%.1f%%" % ((dupSize / float(totalSize)) * 100), "RED"
        )

        # build mergeable string #
        try:
            rate = (totalPss / float(totalRss)) * 100
            totalMg = long(totalRss) - long(totalPss)
            totalMg = convSize(totalMg << 10)
            totalMg = convColor(totalMg, "GREEN")
            totalRss = convSize(long(totalRss) << 10)
            totalRss = convColor(totalRss, "YELLOW")
            totalPss = convSize(long(totalPss) << 10)
            totalPss = convColor(totalPss, "YELLOW")
            rate = convColor("%.1f%%" % rate, "RED")

            mgstr = " <Merged(%s)/Resident(%s)/Marked(%s)[%s]>" % (
                totalMg,
                totalPss,
                totalRss,
                rate,
            )
        except SystemExit:
            sys.exit(0)
        except:
            mgstr = " <No Mergeable>"

        # get resource usage #
        cpu, rss, proc = SysMgr.getTotalUsage(SysMgr.pid)

        # print summary #
        nrProc = len(commList) if isinstance(commList, list) else 1
        SysMgr.printPipe(
            "\n[Dup Info] <Dup: %s>%s (nrProc: %s) (%s: %s%%/%s)"
            % (summary, mgstr, convNum(nrProc), proc, convNum(cpu), rss)
        )

        # make count table #
        table = {}
        for cnt in counts:
            if cnt in table:
                table[cnt] += 1
            else:
                table[cnt] = 1

        # print details #
        lineLen = 74
        SysMgr.printPipe(
            (
                "{6:1}\n{0:>12} ({1:>12}) {2:>12} ({3:>4}) "
                "{4:>12} {5:>12}|\n{6:1}"
            ).format(
                "DupPages",
                "Size",
                "Count",
                "%",
                "Total",
                "Mergeable",
                twoLine[:lineLen],
            )
        )
        if pageAlign:
            for dupCnt, num in sorted(
                table.items(), key=lambda x: long(x[0]), reverse=True
            ):
                # get total size #
                totalSize = dupCnt * PAGESIZE * num
                totalStr = convSize(totalSize)
                if totalSize > sizeMB:
                    totalStr = convColor(totalStr, "YELLOW", 12)

                mgSize = (dupCnt - 1) * num * PAGESIZE
                mgStr = convSize(mgSize)
                if mgSize > sizeMB:
                    mgStr = convColor(mgStr, "YELLOW", 12)

                SysMgr.printPipe(
                    "{0:>12} ({1:>12}) {2:>12} ({3:>3}%) {4:>12} {5:>12}|".format(
                        convNum(dupCnt),
                        convNum(dupCnt * PAGESIZE),
                        convNum(num),
                        long((num / len(counts)) * 100),
                        totalStr,
                        mgStr,
                    )
                )
        else:
            for chunk, cntv in sorted(
                mergeTable.items(),
                key=lambda x: (long(x[1][0]) if perProcInfo else long(x[1]))
                * len(x[0]),
                reverse=True,
            ):
                length = len(chunk)
                cnt = cntv[0] if perProcInfo else cntv

                # get total size #
                totalSize = length * cnt
                totalStr = convSize(totalSize)

                mgSize = length * (cnt - 1)
                mgStr = convSize(mgSize)
                if mgSize > sizeMB:
                    mgStr = convColor(mgStr, "YELLOW", 12)

                SysMgr.printPipe(
                    "{0:>12} ({1:>12}) {2:>12} ({3:>3}%) {4:>12} {5:>12}|".format(
                        convNum(cnt),
                        convNum(length),
                        convNum(1),
                        100,
                        totalStr,
                        mgStr,
                    )
                )
        if not table:
            SysMgr.printPipe("    None")
        SysMgr.printPipe(oneLine[:lineLen])

        # print histo stats for size #
        dupStats = UtilMgr.convList2Histo(list(counts))
        UtilMgr.printHist(dupStats, "Dup", "Page" if pageAlign else "Chunk")

        # print contents #
        if SysMgr.showAll:
            SysMgr.printPipe(
                "{0:1}\n{1:1}\n{2:>12} {3:>7} {4:1}\n{1:1}".format(
                    "\n[Chunk Info]",
                    twoLine,
                    "DupPages" if pageAlign else "DupChunks",
                    "SIZE",
                    "Content (HEX)",
                )
            )

            printCnt = 0
            for chunk, cntv in sorted(
                mergeTable.items(),
                key=lambda x: (len(x[0]) * long(x[1][0]))
                if perProcInfo
                else (len(x[0]) * long(x[1])),
                reverse=True,
            ):
                cnt = cntv[0] if perProcInfo else cntv

                if condBig and cnt < condBig:
                    break
                elif condLess and cnt > condLess:
                    continue

                origLen = len(chunk)

                # check zero chunk #
                if len(set(chunk)) == 1:
                    chunk = "(%s * %s)" % (chunk[0], convNum(len(chunk)))

                # convert to string #
                chunk = repr(chunk).replace("\\x", "").strip("b'")

                if not pageAlign:
                    chunk = "[%s] %s" % (convNum(origLen), chunk)

                SysMgr.printPipe(
                    "{0:>12} {1:>7} {2:1}".format(
                        convNum(cnt),
                        convSize(cnt * (PAGESIZE if pageAlign else origLen)),
                        chunk,
                    ),
                    colorCtl=False,
                )

                if perProcInfo:
                    procList = [
                        "%s(%s): %s(%s)"
                        % (
                            SysMgr.getComm(pid),
                            pid,
                            convNum(pv),
                            convSize(
                                pv * (PAGESIZE if pageAlign else origLen)
                            ),
                        )
                        for pid, pv in sorted(
                            cntv[1].items(), key=lambda x: x[1], reverse=True
                        )
                    ]

                    procListStr = UtilMgr.convLineStr(
                        " | ".join(procList),
                        "",
                        20,
                        len(oneLine) - 21,
                    )

                    SysMgr.printPipe(procListStr)

                printCnt += 1

            if printCnt == 0:
                SysMgr.printPipe("    None")

            SysMgr.printPipe(oneLine)

    @staticmethod
    def doWatch(top=False):
        SysMgr.printLogo(big=True, onlyFile=True)

        # get target path #
        if SysMgr.hasMainArg():
            opList = SysMgr.getMainArgs(False)
        elif SysMgr.filterGroup:
            opList = SysMgr.filterGroup
        else:
            opList = ["."]

        # define lists #
        targetList = []
        targetInfo = {}

        # define summary function #
        def _printSummary(fileSummary={}, procSummary={}, final=False):
            isTopMode = SysMgr.isTopMode()
            convNum = UtilMgr.convNum
            convColor = UtilMgr.convColor

            # update uptime #
            SysMgr.updateUptime()

            title = "Watch File Summary"
            ioStr = ""

            # get time #
            if isTopMode:
                dbgObj = TaskAnalyzer.dbgObj
                tmpObj = TaskAnalyzer.tmpObj

                # clear comm cache #
                SysMgr.clearCommCache()

                # get CPU usages #
                mcpuStr, sysCpuStr = SysMgr.getCpuUsageStr(
                    dbgObj, SysMgr.uptimeDiff
                )
                sysStr = " [%s(%s): %s]" % (
                    SysMgr.comm,
                    SysMgr.pid,
                    mcpuStr,
                )
                sysStr += " [SYS: %s/%s]" % (
                    sysCpuStr,
                    SysMgr.getAvailMemInfo(),
                )

                if not SysMgr.outPath and not SysMgr.streamEnable:
                    SysMgr.clearScreen()

                if final:
                    timename = "Runtime"
                    runtime = SysMgr.getRuntime()
                else:
                    title = "Top File Event"
                    timename = "Interval"
                    runtime = "%.3f" % SysMgr.uptimeDiff

                # get I/O usage for system #
                try:
                    tmpObj.saveVmData()
                    pgInDiff = (
                        tmpObj.vmData["pgpgin"] - tmpObj.prevVmData["pgpgin"]
                    ) >> 10
                    if pgInDiff:
                        pgInDiff = convColor("%sM" % pgInDiff, "RED")
                    pgOutDiff = (
                        tmpObj.vmData["pgpgout"] - tmpObj.prevVmData["pgpgout"]
                    ) >> 10
                    if pgOutDiff:
                        pgOutDiff = convColor("%sM" % pgOutDiff, "RED")
                    ioStr = " [I/O: %s/%s]" % (pgInDiff, pgOutDiff)
                except SystemExit:
                    sys.exit(0)
                except:
                    pass
            else:
                timename = "Runtime"
                runtime = SysMgr.getRuntime()
                sysStr = ""

            outStr = ""

            # build file list #
            outStr += "[%s] [Time: %.3f] [%s: %s] [NrFile: %s]%s%s%s\n%s\n" % (
                title,
                SysMgr.uptime,
                timename,
                runtime,
                convNum(len(fileSummary)),
                " [nrProc: %s]" % convNum(len(procSummary))
                if procSummary
                else "",
                sysStr,
                ioStr,
                twoLine,
            )
            outStr += "{0:<32} {1:<32} {2:<32}\n{3:1}\n".format(
                "File", "Proc", "Attr", twoLine
            )
            for path, vals in sorted(
                fileSummary.items(), key=lambda e: e[1]["TOTAL"], reverse=True
            ):
                if not path:
                    continue

                outStr += path + "\n"
                for proc, subvals in vals.items():
                    if proc == "TOTAL":
                        continue

                    outStr += "{0:32} {1:32}".format(" ", proc)

                    # merge events #
                    evtList = []
                    for attr, cnt in sorted(
                        subvals.items(), key=lambda e: e[1], reverse=True
                    ):
                        evtList.append(
                            "%s(%s)"
                            % (attr, convColor(convNum(cnt), "YELLOW"))
                        )

                        # check skip condition for event merge #
                        if final or not isTopMode or not SysMgr.outPath:
                            continue

                        try:
                            SysMgr.fileWatchTotalList[path]["TOTAL"] += cnt
                        except SystemExit:
                            sys.exit(0)
                        except:
                            SysMgr.fileWatchTotalList.setdefault(path, {})
                            SysMgr.fileWatchTotalList[path]["TOTAL"] = cnt

                        try:
                            SysMgr.fileWatchTotalList[path][proc][attr] += cnt
                        except SystemExit:
                            sys.exit(0)
                        except:
                            SysMgr.fileWatchTotalList.setdefault(path, {})
                            SysMgr.fileWatchTotalList[path].setdefault(
                                proc, {}
                            )
                            SysMgr.fileWatchTotalList[path][proc][attr] = cnt

                    evtStr = "|".join(evtList)
                    outStr += "%s\n" % evtStr
                outStr += oneLine + "\n"

            if not fileSummary:
                outStr += "    None\n%s\n" % oneLine

            # print file list #
            if isTopMode:
                if SysMgr.outPath:
                    if final:
                        SysMgr.printPipe(outStr + "\n")
                    else:
                        SysMgr.addProcBuffer(outStr + "\n")
                else:
                    for line in outStr.split("\n"):
                        if not SysMgr.addPrint(line + "\n"):
                            break
                    SysMgr.printTopStats()
            else:
                SysMgr.printPipe(outStr + "\n")

            # print console #
            if isTopMode and SysMgr.outPath and SysMgr.streamEnable:
                sys.stdout.write(outStr + "\n")

            # clear events #
            if isTopMode:
                fileSummary.clear()
                procSummary.clear()
                SysMgr.clearPrint()
                return

            outStr = ""

            # print proc list #
            outStr += (
                "[Watch Proc Summary] [Time: %.3f] [%s: %s] [NrProc: %s]\n%s\n"
                % (
                    SysMgr.uptime,
                    timename,
                    runtime,
                    convNum(len(procSummary)),
                    twoLine,
                )
            )

            outStr += "{0:<32} {1:<32} {2:<32}\n{3:1}\n".format(
                "Proc", "File", "Attr", twoLine
            )
            for proc, vals in sorted(
                procSummary.items(), key=lambda e: e[1]["TOTAL"], reverse=True
            ):
                outStr += proc + "\n"
                for path, subvals in vals.items():
                    if path == "TOTAL":
                        continue

                    outStr += "{0:32} {1:32}".format(" ", path)

                    # merge events #
                    evtList = []
                    for attr, cnt in sorted(
                        subvals.items(), key=lambda e: e[1], reverse=True
                    ):
                        evtList.append("%s(%s)" % (attr, convNum(cnt)))

                    evtStr = "|".join(evtList)
                    outStr += " [%s]\n" % evtStr
                outStr += oneLine + "\n"

            if not procSummary:
                outStr += "    None\n%s\n" % oneLine
            SysMgr.printPipe(outStr + "\n")

            # print history #
            SysMgr.printPipe("\n")
            SysMgr.doPrint(clear=True, isList=True)

        # define summary function #
        def _printLastSummary(fileSummary={}, procSummary={}):
            _printSummary(fileSummary, procSummary, final=True)
            SysMgr.printProcBuffer()
            SysMgr.clearProcBuffer()

        # set output attributes #
        if top:
            # initialize task stat #
            TaskAnalyzer.dbgObj = Debugger(SysMgr.pid, attach=False)
            TaskAnalyzer.dbgObj.initValues()
            TaskAnalyzer.dbgObj.getCpuUsage(system=True)
            TaskAnalyzer.tmpObj = TaskAnalyzer(onlyInstance=True)
            TaskAnalyzer.tmpObj.saveVmData()

            if SysMgr.outPath:
                SysMgr.addExitFunc(
                    _printLastSummary, [SysMgr.fileWatchTotalList, {}]
                )
        elif SysMgr.outPath:
            # register summary function #
            SysMgr.addExitFunc(
                _printSummary, [SysMgr.fileWatchList, SysMgr.procWatchList]
            )
        else:
            SysMgr.setStream()

        streamEnable = True if SysMgr.findOption("Q") else False

        # set notifier #
        if (
            "PROCINFO" in SysMgr.environList
            or "INMOUNT" in SysMgr.environList
            or "ALLMOUNT" in SysMgr.environList
        ):
            # check permission #
            SysMgr.checkRootPerm()

            notifier = SysMgr.fanotify
        else:
            notifier = SysMgr.inotify

        # parse items #
        for item in opList:
            args = item.split(":")

            # get full path #
            pathList = UtilMgr.getFileList([args[0]])
            if not pathList:
                pathList = []

            # check option #
            if "INCSUBDIRS" in SysMgr.environList:
                for path in pathList:
                    pathList += UtilMgr.getFiles(
                        path, incFile=False, incDir=True
                    )

            # add path to list #
            for path in pathList:
                # convert to absolute path #
                path = SysMgr.convFullPath(path)

                # add to target list #
                targetList.append(path)

                # no condition #
                if len(args) == 1:
                    continue

                # add event #
                if len(args) > 1:
                    events = args[1].strip().split("|")
                    if events == [""]:
                        events = []
                else:
                    events = []

                # add file name #
                if len(args) > 2:
                    fname = UtilMgr.cleanItem(args[2].split("|"), False)
                else:
                    fname = None

                # add command #
                if len(args) > 3:
                    cmd = args[3].strip().split("|")
                    if cmd == [""]:
                        cmd = []
                else:
                    cmd = []

                targetInfo[path] = {
                    "event": events,
                    "cmd": cmd,
                    "fname": fname,
                }

        if not targetList:
            SysMgr.printErr("no traget list")
            sys.exit(-1)

        conv = UtilMgr.convColor

        # remove redundant path #
        targetList = list(set(targetList))

        # set maximum notify object number #
        muiPath = "/proc/sys/fs/inotify/max_user_instances"
        try:
            userInstance = long(SysMgr.readFile(muiPath))
            if userInstance < 8182:
                SysMgr.writeFile(muiPath, "8192")
                userInstance = SysMgr.readFile(muiPath)
                SysMgr.printInfo(
                    "changed max_user_instance to %s"
                    % UtilMgr.convNum(userInstance)
                )
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn(
                "failed to apply inotify max_user_instances", True, True
            )
            sys.exit(-1)

        # print start message #
        SysMgr.printInfo("start watching [%s]\n" % ", ".join(targetList))

        # get event file descriptor #
        try:
            fd = notifier(
                targetList, wait=True, retfd=True, retlist=False, verb=True
            )
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to watch", reason=True)
            sys.exit(-1)

        # set timer #
        if top:

            def _alarmHandler(signum, frame):
                _printSummary(SysMgr.fileWatchList, SysMgr.procWatchList)
                signal.alarm(SysMgr.intervalEnable)

            signal.signal(signal.SIGALRM, _alarmHandler)

            # set interval #
            if not SysMgr.intervalEnable:
                SysMgr.intervalEnable = 1

            signal.alarm(SysMgr.intervalEnable)

        # get item for log filter #
        if "WATCHLOG" in SysMgr.environList:
            watchcond = SysMgr.environList["WATCHLOG"][0].split("+")
            refilter = SysMgr.environList.get("REFILTER")
        else:
            refilter = watchcond = None

        # start watching #
        while 1:
            try:
                current = None

                # wait for events #
                ret = notifier(targetList, fd=fd)
                if not ret:
                    if top or SysMgr.forceEnable:
                        continue
                    else:
                        SysMgr.printWarn(
                            "stopped to watch files because there is no event",
                            True,
                        )
                        break

                # pylint: disable-next=not-an-iterable
                for item in ret:
                    # error #
                    if not item:
                        continue

                    # get current time #
                    if not current:
                        current = SysMgr.getUptime()

                    # inotify #
                    if len(item) == 3:
                        path, events, fname = item

                        # check conditions #
                        if targetInfo:
                            # event condition #
                            if targetInfo[path]["event"]:
                                cond = set(targetInfo[path]["event"])
                                new = set(events)
                                if cond - new == cond:
                                    continue

                            # file condition #
                            if targetInfo[path][
                                "fname"
                            ] and not UtilMgr.isValidStr(
                                fname, targetInfo[path]["fname"]
                            ):
                                continue

                        # add file name #
                        if fname:
                            epath = os.path.join(path, fname)
                        else:
                            epath = path

                        # print events #
                        if SysMgr.jsonEnable:
                            dictInfo = {
                                "time": current,
                                "event": events,
                                "path": epath,
                            }

                            SysMgr.printPipe(
                                UtilMgr.convDict2Str(
                                    dictInfo, pretty=not streamEnable
                                )
                            )
                        else:
                            if top or SysMgr.outPath:
                                # add to file summary #
                                watchList = SysMgr.fileWatchList
                                for event in events:
                                    try:
                                        watchList[epath]["TOTAL"] += 1
                                    except SystemExit:
                                        sys.exit(0)
                                    except:
                                        watchList.setdefault(epath, {})
                                        watchList[epath]["TOTAL"] = 1

                                    try:
                                        watchList[epath]["N/A"][event] += 1
                                    except SystemExit:
                                        sys.exit(0)
                                    except:
                                        watchList.setdefault(epath, {})
                                        watchList[epath].setdefault("N/A", {})
                                        watchList[epath]["N/A"][event] = 1
                            if not top:
                                outStr = "[%.6f] %s@%s" % (
                                    current,
                                    conv("|".join(events), "WARNING"),
                                    conv(epath, "GREEN"),
                                )

                                # check log command #
                                SysMgr.checkLogCond(
                                    outStr, watchcond, refilter
                                )

                                if SysMgr.outPath:
                                    SysMgr.addPrint(outStr + "\n", isList=True)
                                    if streamEnable:
                                        sys.stdout.write(outStr + "\n")
                                else:
                                    SysMgr.printPipe(outStr)
                    # fanotify #
                    elif len(item) in (4, 6):
                        epath, events, epid, ecomm = item[:4]

                        if len(item) == 6:
                            ppid, pcomm = item[4:6]
                        else:
                            ppid = pcomm = None

                        # check file condition #
                        if targetInfo:
                            path, fname = epath.rsplit("/", 1)
                            if (
                                path in targetInfo
                                and targetInfo[path]["fname"]
                                and not UtilMgr.isValidStr(
                                    fname, targetInfo[path]["fname"]
                                )
                            ):
                                continue

                        # print events #
                        if SysMgr.jsonEnable:
                            dictInfo = {
                                "time": current,
                                "event": events,
                                "path": epath,
                                "pid": epid,
                                "comm": ecomm,
                                "ppid": ppid,
                                "pcomm": pcomm,
                            }

                            SysMgr.printPipe(
                                UtilMgr.convDict2Str(
                                    dictInfo, pretty=not streamEnable
                                )
                            )
                        else:
                            procInfo = "%s(%s)" % (ecomm, epid)
                            if ppid:
                                procInfo += " in %s(%s)" % (pcomm, ppid)

                            if top or SysMgr.outPath:
                                # get event list #
                                eventList = events.split("|")

                                # add to file summary #
                                fwatchList = SysMgr.fileWatchList
                                for event in eventList:
                                    try:
                                        fwatchList[epath]["TOTAL"] += 1
                                    except SystemExit:
                                        sys.exit(0)
                                    except:
                                        fwatchList.setdefault(epath, {})
                                        fwatchList[epath]["TOTAL"] = 1

                                    try:
                                        fwatchList[epath][procInfo][event] += 1
                                    except SystemExit:
                                        sys.exit(0)
                                    except:
                                        if epath in fwatchList:
                                            fwatchList[epath].setdefault(
                                                procInfo, {}
                                            )
                                            fwatchList[epath][procInfo][
                                                event
                                            ] = 1
                                        else:
                                            fwatchList[epath] = {
                                                procInfo: {event: 1}
                                            }

                                # add to proc summary #
                                pwatchList = SysMgr.procWatchList
                                for event in eventList:
                                    try:
                                        pwatchList[procInfo]["TOTAL"] += 1
                                    except SystemExit:
                                        sys.exit(0)
                                    except:
                                        pwatchList.setdefault(procInfo, {})
                                        pwatchList[procInfo]["TOTAL"] = 1

                                    try:
                                        pwatchList[procInfo][epath][event] += 1
                                    except SystemExit:
                                        sys.exit(0)
                                    except:
                                        if procInfo in pwatchList:
                                            pwatchList[procInfo].setdefault(
                                                epath, {}
                                            )
                                            pwatchList[procInfo][epath][
                                                event
                                            ] = 1
                                        else:
                                            pwatchList[procInfo] = {
                                                epath: {event: 1}
                                            }

                            if not top:
                                outStr = "[%.6f] %s@%s by %s" % (
                                    current,
                                    conv(events, "WARNING"),
                                    conv(epath, "GREEN"),
                                    conv(procInfo, "YELLOW"),
                                )

                                # check log command #
                                SysMgr.checkLogCond(
                                    outStr, watchcond, refilter
                                )

                                if SysMgr.outPath:
                                    SysMgr.addPrint(outStr + "\n", isList=True)
                                    if streamEnable:
                                        sys.stdout.write(outStr + "\n")
                                else:
                                    SysMgr.printPipe(outStr)
                    # error #
                    else:
                        continue

                    # print file #
                    if "PRINTFILE" in SysMgr.environList:
                        try:
                            SysMgr.doPrintFile(epath)
                        except SystemExit:
                            sys.exit(0)
                        except:
                            pass

                    # check skip condition #
                    if not targetInfo or not path in targetInfo:
                        continue

                    # execute command #
                    SysMgr.runFileCmd(targetInfo[path]["cmd"], epath)
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr("failed to watch", reason=True)
                sys.exit(-1)

    @staticmethod
    def getTargetSymList():
        # get target symbol list #
        targetList = SysMgr.environList.get("TARGETSYM", [])

        # get except symbol list #
        exceptList = SysMgr.environList.get("EXCEPTSYM", [])

        return targetList, exceptList

    @staticmethod
    def getTargetFileList():
        # get target file list #
        targetList = SysMgr.environList.get("TARGETFILE", [])

        # get except file list #
        exceptList = SysMgr.environList.get("EXCEPTFILE", [])

        return targetList, exceptList

    @staticmethod
    def isExceptFile(mfile, targetList, exceptList):
        if targetList and not UtilMgr.isValidStr(mfile, targetList):
            ElfAnalyzer.failedFiles[mfile] = True
            return True
        elif exceptList and UtilMgr.isValidStr(mfile, exceptList):
            ElfAnalyzer.failedFiles[mfile] = True
            return True

        return False

    @staticmethod
    def getMapList(pidList=[], onlyExec=True):
        mapList = []
        for pid in pidList:
            maps = FileAnalyzer.getProcMapInfo(pid, onlyExec=onlyExec)
            overlayInfo = SysMgr.getOverlayfsInfo(pid)
            if overlayInfo:
                for idx, path in enumerate(list(maps)):
                    newpath = UtilMgr.convOverlayPath(path, overlayInfo)
                    if path != newpath:
                        maps[newpath] = maps.pop(path, None)
            mapList += maps
        return list(set(mapList))

    @staticmethod
    def runKernelTop():
        # check root permission #
        SysMgr.checkRootPerm()

        # get event path #
        tracePath = SysMgr.getTraceEventPath()
        if not tracePath:
            SysMgr.printErr("failed to get tracefs/debugfs path")
            return

        # get stat knob #
        statPath = os.path.realpath(
            os.path.join(tracePath, "..", "trace_stat")
        )
        if not os.path.exists(statPath):
            SysMgr.printErr("failed to find '%s'" % statPath)
            sys.exit(0)

        # get control knob #
        knobPath = os.path.realpath(
            os.path.join(tracePath, "..", "function_profile_enabled")
        )
        if not os.path.exists(knobPath):
            SysMgr.printErr("failed to find '%s'" % knobPath)
            sys.exit(0)

        # define func to disable function profiling #
        def _disableFuncProf(val="0"):
            SysMgr.writeFile(knobPath, val)

        # get filter knob #
        filterPath = os.path.realpath(
            os.path.join(tracePath, "..", "set_ftrace_filter")
        )

        # define func to apply function filter #
        def _applyFuncFilter(val=""):
            SysMgr.writeFile(filterPath, val)

        # read original status value #
        val = SysMgr.readFile(knobPath)

        # add exit handler to recover original status #
        SysMgr.addExitFunc(_disableFuncProf, [val])

        # get sort attr #
        if "SORT" in SysMgr.environList:
            sortval = SysMgr.environList["SORT"][0].lower()
            if not sortval in ("hit", "total", "average"):
                SysMgr.printErr("no sort value '%s'" % sortval)
                sys.exit(0)
        else:
            sortval = "total"

        # set interval #
        if not SysMgr.intervalEnable:
            SysMgr.intervalEnable = 1

        # apply function filter #
        if SysMgr.filterGroup:
            filterStr = " ".join(SysMgr.filterGroup)
        else:
            filterStr = ""
        SysMgr.printInfo(
            "%s function filter... " % ("apply" if filterStr else "remove"),
            suffix=False,
        )
        SysMgr.writeFile(filterPath, filterStr)
        SysMgr.printInfo("[Done]", prefix=False, title=False)

        # enable function profiling #
        SysMgr.writeFile(knobPath, "1")

        totalStat = {}
        prevCoreStat = {}
        curCoreStat = {}

        def _applyStats(node, path):
            statList = []
            with open(path, "r") as fd:
                statList = fd.readlines()
            if not statList:
                return

            if not node in curCoreStat:
                curCoreStat[node] = {}

            perCoreStat = "PERCORE" in SysMgr.environList
            core = node.strip("function")
            if SysMgr.customCmd and not core in SysMgr.customCmd:
                return

            for l in statList[2:]:
                name, hit, total, _, avg, _, s2, _ = l.strip().split()
                if name == "schedule":
                    continue

                hit = long(hit)
                total = float(total)

                # save per-core stats #
                try:
                    curCoreStat[node][name] = {
                        "hit": hit,
                        "total": total,
                    }
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr(
                        "failed to get stats for '%s'" % node, True
                    )
                    continue

                if not prevCoreStat:
                    continue

                tname = "[%s]%s" % (core, name) if perCoreStat else name

                # init total stats #
                if not tname in totalStat:
                    totalStat[tname] = {
                        "hit": 0,
                        "total": 0,
                    }

                # save diff stats #
                try:
                    # new func #
                    if not name in prevCoreStat[node]:
                        totalStat[tname]["hit"] += hit
                        totalStat[tname]["total"] += total
                        continue

                    prevStat = prevCoreStat[node][name]
                    curStat = curCoreStat[node][name]

                    # no change #
                    if curStat["hit"] == prevStat["hit"]:
                        totalStat.pop(tname, None)
                        continue

                    # diff #
                    totalStat[tname]["hit"] += hit - prevStat["hit"]
                    totalStat[tname]["total"] += total - prevStat["total"]
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr("failed to get diff for stats", True)
                    continue

        def _printStats():
            nrCore = SysMgr.getNrCore()
            maxTime = nrCore * 1000000 * SysMgr.uptimeDiff

            # get CPU usages #
            try:
                mcpuStr, sysCpuStr = SysMgr.getCpuUsageStr(
                    TaskAnalyzer.dbgObj, SysMgr.uptimeDiff
                )
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr("failed to get my CPU usage", True)
                mcpuStr = sysCpuStr = "?"

            # get available memory for system #
            sysMemStr = SysMgr.getAvailMemInfo()

            # print header stats #
            SysMgr.addPrint(
                (
                    "[Top Kernelcall Info] [Time: %7.3f] [Interval: %.1f] "
                    "[NrFunc: %s] [NrCore: %s] [SYS: %s/%s] "
                    "[%s(%s): %s] (Unit: us)\n%s\n"
                )
                % (
                    SysMgr.uptime,
                    SysMgr.uptimeDiff,
                    UtilMgr.convNum(len(totalStat)),
                    UtilMgr.convNum(nrCore),
                    sysCpuStr,
                    sysMemStr,
                    SysMgr.comm,
                    SysMgr.pid,
                    mcpuStr,
                    twoLine,
                ),
                newline=2,
            )

            # print title #
            SysMgr.addPrint(
                "{0:>10} | {1:>20}({2:>5}) | {3:>20} | {4:1}\n{5:1}\n".format(
                    "Hit",
                    "Total",
                    "%",
                    "Average",
                    "Name",
                    twoLine,
                ),
                newline=2,
            )

            convNum = UtilMgr.convNum

            # get average time for each function #
            if sortval == "average":
                for n in totalStat:
                    item = totalStat[n]
                    item["average"] = item["total"] / item["hit"]

            # print stats #
            for name, vals in sorted(
                totalStat.items(), key=lambda e: e[1][sortval], reverse=True
            ):
                hit = vals["hit"]
                total = vals["total"]
                if "average" in vals:
                    avg = vals["average"]
                else:
                    avg = total / hit
                per = UtilMgr.convNum(long((total / maxTime) * 100))

                ret = SysMgr.addPrint(
                    "{0:>10} | {1:>20}({2:>5}) | {3:>20} | {4:1}\n".format(
                        convNum(hit),
                        convNum(total),
                        per,
                        convNum(avg),
                        name,
                    )
                )
                if not ret:
                    break

            SysMgr.addPrint(oneLine + "\n")

            # flush print buffer #
            SysMgr.printTopStats()

        # initialize task stat #
        TaskAnalyzer.dbgObj = Debugger(SysMgr.pid, attach=False)
        TaskAnalyzer.dbgObj.initValues()
        TaskAnalyzer.dbgObj.getCpuUsage(system=True)

        # update uptime #
        SysMgr.updateUptime()

        while 1:
            try:
                # save timestamp #
                prevTime = time.time()

                # collect stats #
                for node in os.listdir(statPath):
                    try:
                        _applyStats(node, os.path.join(statPath, node))
                    except SystemExit:
                        sys.exit(0)
                    except:
                        SysMgr.printErr(
                            "failed to get stats from '%s'" % node, True
                        )

                # print stats #
                if prevCoreStat:
                    _printStats()

                prevCoreStat = curCoreStat
                curCoreStat = {}
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr("failed to monitor kernel functions", True)
                sys.exit(0)

            # clear total stats #
            totalStat = {}

            # get delayed time #
            delayTime = time.time() - prevTime
            waitTime = SysMgr.intervalEnable - delayTime

            # wait for next tick #
            if not SysMgr.waitUserInput(waitTime):
                time.sleep(waitTime)

            SysMgr.updateUptime()

        sys.exit(0)

    @staticmethod
    def doTrace(mode, tid=None):
        def _doCommonJobs(pids, procList):
            # check STOP condition #
            needStop = "STOPTARGET" in SysMgr.environList

            # get file list #
            targetFileList, exceptFileList = SysMgr.getTargetFileList()

            # get pid list #
            for tid in pids:
                try:
                    pid = SysMgr.getTgid(tid)
                    if not pid:
                        continue

                    # stop target #
                    try:
                        if needStop:
                            os.kill(long(pid), signal.SIGSTOP)
                    except SystemExit:
                        sys.exit(0)
                    except:
                        pass

                    procList.setdefault(pid, [])
                    procList[pid].append(tid)
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

            pidList = list(map(long, procList))

            # merge map files #
            mapList = SysMgr.getMapList(pidList)

            # load symbol caches at once #
            printLog = True
            if not "LAZYCACHE" in SysMgr.environList:
                for item in mapList:
                    # skip invalid file #
                    if not FileAnalyzer.isValidFile(item, special=True):
                        continue
                    # check file filter #
                    elif SysMgr.isExceptFile(
                        item, targetFileList, exceptFileList
                    ):
                        continue

                    try:
                        eobj = ElfAnalyzer.getObject(item, log=printLog)
                        if len(pidList) == 1 and eobj:
                            eobj.mergeSymTable()
                            if printLog:
                                printLog = False
                    except SystemExit:
                        sys.exit(0)
                    except:
                        pass

            # continue target #
            try:
                if needStop:
                    for pid in pidList:
                        os.kill(pid, signal.SIGCONT)
            except SystemExit:
                sys.exit(0)
            except:
                pass

            # check mode #
            if not mode in ("breakcall", "pytrace"):
                return

            # save original data to be injected for multi-threaded process #
            for pid in pidList:
                # stop a process #
                try:
                    os.kill(pid, signal.SIGSTOP)
                except SystemExit:
                    sys.exit(0)
                except:
                    continue

                # initialize lists #
                bpList.setdefault(pid, {})
                exceptBpList.setdefault(pid, {})
                targetBpList.setdefault(pid, {})
                targetBpFileList.setdefault(pid, {})
                exceptBpFileList.setdefault(pid, {})

                # create object #
                procObj = Debugger(pid=pid, mode="break")
                if not procObj:
                    os.kill(pid, signal.SIGCONT)
                    continue
                elif (
                    mode in ("pycall", "pytrace") and not procObj.checkPyVer()
                ):
                    os.kill(pid, signal.SIGCONT)
                    continue

                # register signal sender for resume #
                SysMgr.addExitFunc(
                    SysMgr.sendSignalProcs,
                    [signal.SIGCONT, [pid], False, False],
                )

                # load common ELF cache files #
                procObj.loadSymbols()
                procObj.updateBpList()

                # save per-process breakpoint info #
                bpList[pid] = UtilMgr.deepcopy(procObj.bpList)
                exceptBpList[pid] = UtilMgr.deepcopy(procObj.exceptBpList)
                targetBpList[pid] = UtilMgr.deepcopy(procObj.targetBpList)
                targetBpFileList[pid] = UtilMgr.deepcopy(
                    procObj.targetBpFileList
                )
                exceptBpFileList[pid] = UtilMgr.deepcopy(
                    procObj.exceptBpFileList
                )

                # create a lock for the multi-threaded process #
                if SysMgr.getTids(pid, sibling=True):
                    lockList[pid] = Debugger.getGlobalLock(
                        pid, len(bpList[pid])
                    )

                # detach task #
                procObj.detach()
                del procObj

                # remove signal sender #
                SysMgr.removeExitFunc(
                    SysMgr.sendSignalProcs,
                    [signal.SIGCONT, [pid], False, False],
                )

        # no use pager #
        if not SysMgr.isTopMode() and not SysMgr.outPath:
            SysMgr.setStream()

        # define variables #
        wait = None
        multi = False
        execCmd = None
        lockObj = None
        procList = {}
        allpids = []
        bpList = {}
        lockList = {}
        exceptBpList = {}
        targetBpList = {}
        targetBpFileList = {}
        exceptBpFileList = {}

        # get argument #
        if tid:
            inputParam = None
        elif SysMgr.hasMainArg():
            inputParam = SysMgr.inputParam = SysMgr.getMainArg()
        elif SysMgr.inputParam:
            inputParam = SysMgr.inputParam
        else:
            inputParam = None

        # monitor tasks to get target ID #
        ret = SysMgr.selectTaskId()
        if ret:
            inputParam = None
            SysMgr.filterGroup = [ret]

        # check input #
        if not tid and not SysMgr.filterGroup and not inputParam:
            SysMgr.printErr("no input value for target")
            sys.exit(-1)

        # check condition #
        if mode == "pytrace":
            SysMgr.pyFuncFilter = SysMgr.customCmd
            if "GETRET" in SysMgr.environList:
                SysMgr.customCmd = ["%s|getret" % SysMgr.pyCallFunc]
            else:
                SysMgr.customCmd = [SysMgr.pyCallFunc]
        elif mode in ("remote", "hook", "bind"):
            if not SysMgr.customCmd and mode != "bind":
                SysMgr.printErr("failed to get remote command")
                sys.exit(-1)
            elif inputParam:
                SysMgr.printErr("executing a program is not supported")
                sys.exit(-1)
            elif mode == "remote":
                if set(SysMgr.customCmd) == {
                    "print",
                }:
                    SysMgr.environList["LAZYCACHE"] = True
        elif mode == "signal":
            if not SysMgr.sysInstance:
                SysMgr()

        # set priority #
        if SysMgr.prio is None:
            SysMgr.setPriority(SysMgr.pid, "C", -20)

        # write user command #
        if not SysMgr.isTopMode():
            SysMgr.runProfCmd("BEFORE")

        # create event memory #
        Debugger.globalEvent = SysMgr.createShm()

        # check symbol requirement #
        needSymbol = SysMgr.funcDepth > 0 or mode in (
            "bind",
            "breakcall",
            "hook",
            "pytrace",
            "remote",
            "sample",
            "usercall",
        )

        # set dwarf flag #
        SysMgr.setDwarfFlag()

        # preload ELF caches using path #
        preload = SysMgr.environList.get("PRELOAD", [])
        if preload:
            SysMgr.printStat("start preloading ELF files")
        for p in preload:
            ElfAnalyzer.getObject(p)

        # preload ELF caches from list #
        preloadList = SysMgr.environList.get("PRELOADLIST", [])
        for p in preloadList:
            SysMgr.printStat("start preloading ELF files from '%s'" % p)
            Debugger.loadElfList(p)

        # get pids #
        if tid:
            if not isinstance(tid, list):
                tid = [tid]
            allpids = pids = tid
        elif not inputParam:
            if mode in ("hook", "bind"):
                isThread = False
            else:
                isThread = True

            # convert comm to pid #
            pids = SysMgr.convTaskList(
                SysMgr.filterGroup,
                isThread=isThread,
                exceptMe=True,
                sibling=SysMgr.groupProcEnable,
            )

            # except for my parent #
            ppid = str(os.getppid())
            if ppid in pids:
                pids.remove(ppid)

            # check multiple process #
            if len(pids) > 1 and mode in ("bind"):
                SysMgr.printErr(
                    "no support for multiple processes [%s]"
                    % SysMgr.getCommList(pids)
                )
                sys.exit(0)

            # get pids of process groups #
            if mode in ("breakcall", "pytrace"):
                allpids = SysMgr.convTaskList(
                    SysMgr.filterGroup, isThread=True, sibling=True
                )
            else:
                allpids = pids

        # check command #
        if inputParam:
            pid = None
            execCmd = UtilMgr.parseCommand(inputParam)
        # check permission #
        elif not SysMgr.checkRootPerm(msg="trace %s" % mode):
            # exit #
            pass
        # check pid #
        elif not pids:
            if SysMgr.filterGroup:
                flist = ", ".join(SysMgr.filterGroup)
                SysMgr.printErr("no thread related to '%s'" % flist)
            elif not inputParam:
                SysMgr.printErr("no input for TID or command")
            else:
                SysMgr.printErr("no input for TID")

            SysMgr.outPath = SysMgr.printFd = None

            sys.exit(-1)
        # check targets for tracing based on breakpoint #
        elif len(allpids) > 1 or mode in ("breakcall", "pytrace"):
            parent = SysMgr.pid

            # set multi-task attributes #
            if len(pids) > 1:
                multi = True
                if not SysMgr.outPath:
                    SysMgr.setStream()

                SysMgr.printWarn(
                    "multiple tasks [ %s ] are targeted"
                    % SysMgr.getCommList(pids),
                    True,
                )

                # check support for multiple targets #
                if (
                    mode in ("remote", "hook", "bind")
                    and not SysMgr.forceEnable
                ):
                    SysMgr.printWarn("use -f option to continue tracing", True)
                    sys.exit(0)

            # load symbol caches in advance #
            if needSymbol:
                _doCommonJobs(pids, procList)

            # create new worker processes #
            try:
                isFinished = True
                isParallel = not "SEQUENTIAL" in SysMgr.environList
                for tid in allpids:
                    ret = SysMgr.createProcess(chPgid=True, chMid=True)
                    if ret != 0:
                        # wait for a child tracer to run sequentially #
                        if not isParallel and ret > 0:
                            SysMgr.waitEvent(ignChldSig=False, block=False)
                        continue

                    if not tid in pids:
                        SysMgr.printEnable = False

                        # mute tasks except for targets #
                        if (
                            not SysMgr.warnEnable
                            and SysMgr.getTgid(tid) != tid
                        ):
                            SysMgr.logEnable = False

                    # update target PID #
                    pid = long(tid)
                    break
            except:
                isFinished = False

            # wait for children tracers as their parent #
            if SysMgr.pid == parent:
                # disable printing to file #
                SysMgr.outPath = SysMgr.printFd = None

                if isFinished:
                    SysMgr.termFlag = False
                    while 1:
                        SysMgr.waitEvent(ignChldSig=False, block=False)
                        if SysMgr.condExit:
                            break

                        # check children #
                        SysMgr.updateChildList()
                        if SysMgr.isNoChild():
                            break

                # broadcast term signal to children and wait for them #
                signal.signal(signal.SIGCHLD, signal.SIG_IGN)
                children = TaskAnalyzer.getDescendantList(SysMgr.pid)
                SysMgr.killChildren(
                    sig=signal.SIGINT, children=children, wait=True, group=True
                )

                # continue processes #
                SysMgr.sendSignalProcs(
                    signal.SIGCONT, list(procList), verb=False
                )

                # remove temporary files #
                if mode in ("breakcall", "pytrace"):
                    # remove all lock files #
                    for lockPath in list(lockList.values()):
                        # remove lock file #
                        try:
                            os.remove(lockPath.name)
                        except:
                            pass

                sys.exit(0)
        else:
            # load symbol caches in advance #
            if needSymbol:
                _doCommonJobs(pids, procList)

            pid = long(pids[0])

        # recover SIGCHLD #
        signal.signal(signal.SIGCHLD, signal.SIG_DFL)

        # start tracing #
        try:
            # add environment variable to look up python variable symbols #
            if (
                mode in ("pycall", "pytrace")
                or "PYSTACK" in SysMgr.environList
            ):
                SysMgr.addEnvironVar("ALLSYM")

            # create Debugger instance and start tracing #
            if mode == "kernelcall":
                dobj = Debugger(pid=pid, execCmd=execCmd, attach=False)
                dobj.trace(mode="kernel", wait=wait, multi=multi)
            elif mode == "usercall":
                # tracing #
                if SysMgr.isTraceMode():
                    dobj = Debugger(pid=pid, execCmd=execCmd, attach=False)
                    dobj.trace(mode="inst", wait=wait, multi=multi)
                # monitoring #
                else:
                    dobj = Debugger(pid=pid, execCmd=execCmd, attach=True)
                    dobj.trace(mode="sample", wait=wait, multi=multi)
            elif mode == "pycall":
                dobj = Debugger(pid=pid, execCmd=execCmd, attach=True)
                dobj.trace(mode="pycall", wait=wait, multi=multi)
            elif mode in ("breakcall", "pytrace"):
                if pid:
                    try:
                        ppid = long(SysMgr.getTgid(pid))
                    except SystemExit:
                        sys.exit(0)
                    except:
                        ppid = None

                    # set per-process breakpoint list #
                    if ppid in bpList:
                        bpList = bpList[ppid]
                    if ppid in exceptBpList:
                        exceptBpList = exceptBpList[ppid]
                    if ppid in targetBpList:
                        targetBpList = targetBpList[ppid]
                    if ppid in targetBpFileList:
                        targetBpFileList = targetBpFileList[ppid]
                    if ppid in exceptBpFileList:
                        exceptBpFileList = exceptBpFileList[ppid]
                    if ppid in lockList:
                        lockObj = lockList[ppid]
                else:
                    ppid = SysMgr.pid

                # set real mode #
                if mode == "breakcall":
                    rmode = "break"
                else:
                    rmode = "pybreak"

                Debugger(pid=pid, execCmd=execCmd).trace(
                    mode=rmode,
                    wait=wait,
                    multi=multi,
                    bpList=bpList,
                    exceptBpList=exceptBpList,
                    lock=lockObj,
                    targetBpList=targetBpList,
                    targetBpFileList=targetBpFileList,
                    exceptBpFileList=exceptBpFileList,
                )
            elif mode == "hook":
                Debugger.hookFunc(pid, SysMgr.customCmd)
            elif mode == "bind":
                Debugger.hookFunc(pid, SysMgr.customCmd, mode="print")
            else:
                # syscall / signal / remote #
                dobj = Debugger(
                    pid=pid, execCmd=execCmd, attach=False, mode=mode
                )

                dobj.trace(mode=mode, wait=wait, multi=multi)
        except SystemExit:
            pass
        except:
            SysMgr.printErr("stopped to trace %s" % mode, True)

        sys.exit(0)

    @staticmethod
    def doAddr2sym():
        SysMgr.printLogo(big=True, onlyFile=True)

        # check input #
        if SysMgr.hasMainArg():
            inputArg = SysMgr.getMainArg()
        elif SysMgr.inputParam:
            inputArg = str(SysMgr.inputParam)
        else:
            SysMgr.printErr("no input for PATH")
            sys.exit(-1)

        # check symbol #
        if not SysMgr.filterGroup:
            SysMgr.printErr("no input for offset")
            sys.exit(-1)
        else:
            addrList = []
            for idx, addr in enumerate(SysMgr.filterGroup):
                if not UtilMgr.isNumber(addr):
                    SysMgr.printErr(
                        "failed to recognize '%s' as a number" % addr
                    )
                    sys.exit(-1)

                try:
                    addrList.append(long(addr, 16))
                except:
                    addrList.append(long(addr))

        def _printRes(procInfo, resInfo, maxSymLen):
            if procInfo:
                procInfo = " [Task: %s]" % procInfo

            # make space between symbol and path #
            maxSymLen += 4

            SysMgr.printPipe(
                "\n[Address Info]%s (NrAddr: %s)\n%s"
                % (procInfo, UtilMgr.convNum(len(resInfo)), twoLine)
            )
            SysMgr.printPipe(
                "{0:<18} {1:<18} {2:<{maxSymLen}} {3:<1}\n{4:1}".format(
                    menu1st,
                    menu2nd,
                    "Symbol",
                    "File",
                    twoLine,
                    maxSymLen=maxSymLen,
                )
            )

            # print symbols from offset list #
            for addr, val in resInfo.items():
                SysMgr.printPipe(
                    "{0:<18} {1:18} {2:<{maxSymLen}} {3:<1}".format(
                        hex(addr).rstrip("L"),
                        val[2],
                        val[0],
                        val[1],
                        maxSymLen=maxSymLen,
                    )
                )

            if not resInfo:
                SysMgr.printPipe("    None")

            SysMgr.printPipe(oneLine + "\n")

        # init variables #
        resInfo = {}
        menu1st = "Offset"
        menu2nd = "Address"
        maxSymLen = 5
        procInfo = ""

        # get pid list #
        pids = SysMgr.getTids(inputArg)
        taskList = []
        for tid in pids:
            taskList.append(SysMgr.getTgid(tid))
        pids = list(set(taskList))

        # files #
        if not pids:
            # get file list #
            fileList = UtilMgr.getFileList(
                UtilMgr.cleanItem(inputArg.split(","), False), exceptDir=True
            )

            # check file #
            if not fileList:
                SysMgr.printErr(
                    "failed to recognize '%s' as a file or a process"
                    % inputArg
                )
                sys.exit(-1)

            menu1st = "Address"
            menu2nd = "Offset"

            for filePath in fileList:
                # create an ELF object #
                try:
                    binObj = ElfAnalyzer.getObject(filePath)
                    if not binObj:
                        err = SysMgr.getErrMsg()
                        raise Exception(err)
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr(
                        "failed to load '%s' as an ELF object" % filePath, True
                    )
                    continue

                for addr in addrList:
                    try:
                        sym, size = binObj.getSymbolByOffset(
                            addr, onlyFunc=False
                        )
                        resInfo[addr] = [sym, filePath, "N/A"]
                        if maxSymLen < len(sym) < SysMgr.ttyCols / 2:
                            maxSymLen = len(sym)
                    except SystemExit:
                        sys.exit(0)
                    except:
                        resInfo[addr] = ["??", filePath, "N/A"]

            # print result #
            _printRes(procInfo, resInfo, maxSymLen)
        else:
            for pid in pids:
                # init variables #
                resInfo = {}
                maxSymLen = 5
                comm = SysMgr.getComm(pid)
                procInfo = "%s(%s)" % (comm, pid)

                try:
                    dobj = Debugger(pid=pid, attach=False)
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr("failed to analyze %s" % procInfo, True)
                    continue

                for addr in addrList:
                    ret = dobj.getSymbolInfo(
                        addr, onlyFunc=False, onlyExec=False
                    )
                    if not ret:
                        SysMgr.printErr(
                            "failed to analyze %s" % procInfo, True
                        )
                        continue
                    elif isinstance(ret, list):
                        resInfo[addr] = [ret[0], ret[1], ret[2]]
                        if maxSymLen < len(ret[0]) < SysMgr.ttyCols / 2:
                            maxSymLen = len(ret[0])
                    else:
                        resInfo[addr] = ["??", "??", "N/A"]

                # print result #
                _printRes(procInfo, resInfo, maxSymLen)

    @staticmethod
    def printDirDiff(rootList, maxLevel=0, sizeUnit=False):
        # define functions #
        if sizeUnit:
            convSize = UtilMgr.convSize2Unit
            convNum = UtilMgr.convNum
        elif SysMgr.jsonEnable:
            convSize = lambda x: x
            convNum = lambda x: x
        else:
            convSize = lambda x: UtilMgr.convNum(x)
            convNum = UtilMgr.convNum

        # get abspath option #
        if "ABSPATH" in SysMgr.environList:
            convAbs = os.path.realpath
        else:
            convAbs = lambda x: x

        def _getDiff(name1, dict1, name2, dict2, depth=0):
            diff = {}
            for key in dict1.keys():
                # define keyNames #
                if key == name1:
                    keyName1 = name1
                    keyName2 = name2
                else:
                    keyName1 = UtilMgr.lstrip(key, name1).lstrip("/")
                    if keyName1 in dict2:
                        keyName2 = keyName1
                    else:
                        keyName2 = os.path.join(name2, keyName1)

                if depth == 0:
                    nestedDiff = _getDiff(
                        name1, dict1[key], name2, dict2[keyName2], depth + 1
                    )
                    if nestedDiff:
                        diff[name1] = nestedDiff
                elif keyName1 not in dict2 and keyName2 not in dict2:
                    diff[keyName1] = dict1[key]
                elif isinstance(dict1[key], dict) and isinstance(
                    dict2[keyName2], dict
                ):
                    nestedDiff = _getDiff(
                        name1, dict1[key], name2, dict2[keyName2], depth + 1
                    )
                    if nestedDiff:
                        diff[keyName1] = nestedDiff
                elif (
                    key in ("_size_", "_type_")
                    and keyName2 in dict2
                    and dict1[key] != dict2[keyName2]
                ):
                    diff[keyName1] = dict1[key]
                elif dict1[key] != dict2[keyName2]:
                    diff[keyName1] = dict1[key]

            return diff

        def _printDir(
            cur, level=0, maxLevel=0, isDir=True, other={}, before=0
        ):
            if not isinstance(cur, dict) or 0 < maxLevel < level:
                return False

            # build indent #
            prefix = "  |" * level
            if prefix:
                prefix += "-"
            else:
                prefix = ""

            def _getOtherDict(name, other):
                return other[name] if name in other else {}

            printed = False
            for name, val in sorted(cur.items()):
                if name in ("_subFiles_", "_subDirs_"):
                    pass
                else:
                    # check skip condition #
                    if not isinstance(val, dict):
                        continue

                    # get real name #
                    rname = os.path.basename(name)

                    # apply color #
                    if not name in other:
                        rname = UtilMgr.convColor(rname, "RED")

                    # dir #
                    if "_subDirs_" in val:
                        rname = "[%s]" % rname

                    attrList = []

                    # size #
                    if "_size_" in val:
                        size = val["_size_"]
                        if (
                            name in other
                            and "_size_" in other[name]
                            and size != other[name]["_size_"]
                        ) or name not in other:
                            if name in other:
                                osize = other[name]["_size_"]
                                if before:
                                    beforeSize = size
                                    afterSize = osize
                                else:
                                    beforeSize = osize
                                    afterSize = size
                            else:
                                if before:
                                    beforeSize = size
                                    afterSize = 0
                                else:
                                    beforeSize = 0
                                    afterSize = size

                            sizeDiff = afterSize - beforeSize
                            if sizeDiff > 0:
                                sizeDiffOp = "+"
                                sizeDiffColor = "CYAN"
                            else:
                                sizeDiffOp = ""
                                sizeDiffColor = "WARNING"

                            if sizeDiff:
                                sizeChange = "%s->%s" % (
                                    convSize(beforeSize),
                                    convSize(afterSize),
                                )
                                sizeDiff = UtilMgr.convColor(
                                    sizeDiffOp + convSize(sizeDiff),
                                    sizeDiffColor,
                                )
                                sizeDiff = "(%s)" % sizeDiff
                            else:
                                sizeChange = convSize(size)
                                sizeDiff = ""

                            attrList.append(
                                "SIZE: %s%s" % (sizeChange, sizeDiff)
                            )

                    # times #
                    if "_times_" in val:
                        attrList += val["_times_"]

                    # attributes #
                    for item in ("_subFiles_", "_subDirs_"):
                        if item == "_subFiles_":
                            iname = "DIR"
                        else:
                            iname = "FILE"

                        if item in val:
                            attrList.append(
                                "%s: %s" % (iname, convNum(len(val[item])))
                            )

                    if attrList:
                        rname += " <%s>" % ", ".join(attrList)

                    SysMgr.printPipe(prefix + rname)
                    printed = True

                ret = _printDir(
                    val,
                    level + 1,
                    maxLevel,
                    True,
                    _getOtherDict(name, other),
                    before,
                )
                if ret:
                    printed = True

            return printed

        # set jsonEnable #
        origJsonEnable = SysMgr.jsonEnable
        SysMgr.jsonEnable = True

        # define result dict #
        retList = []

        # get dir info #
        for idx, root in enumerate(rootList):
            root = convAbs(root)

            if os.path.isfile(root):
                SysMgr.printStat(r"start reading dirs from '%s'..." % root)
                data = SysMgr.readFile(root)
                if not data:
                    SysMgr.printErr("'%s' has no data" % root)
                    sys.exit(0)

                try:
                    start = data.find("{")
                    if start < 0:
                        raise Exception("no JSON format")

                    ret = SysMgr.getPkg("json").loads(data[start:])
                    root = list(ret.keys())[0]
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr("'%s' has no dir data" % root, True)
                    sys.exit(0)
            elif not os.path.isdir(root):
                SysMgr.printErr(
                    "'%s' is neither a file nor a directory" % root
                )
                return
            else:
                SysMgr.printStat(r"start traversing dirs from '%s'..." % root)
                ret = SysMgr.printDirs(
                    root, maxLevel, retVal=True, sizeUnit=False
                )

            retList.append([root, ret])

            try:
                if idx >= len(rootList) - 1:
                    break

                if SysMgr.intervalEnable:
                    SysMgr.printStat(
                        "wait until %s seconds"
                        % UtilMgr.convNum(SysMgr.intervalEnable)
                    )
                    SysMgr.waitEvent(timeout=SysMgr.intervalEnable)
            except SystemExit:
                sys.exit(0)
            except:
                pass

        # recover jsonEnable #
        SysMgr.jsonEnable = origJsonEnable

        # get each diffs #
        name1, dict1 = retList[0]
        name2, dict2 = retList[1]
        diff1 = _getDiff(name1, dict1, name2, dict2)
        diff2 = _getDiff(name2, dict2, name1, dict1)

        # print dir diff in JSON format #
        if SysMgr.jsonEnable:
            SysMgr.printPipe(
                UtilMgr.convDict2Str(diff1, pretty=not SysMgr.streamEnable)
            )
            SysMgr.printPipe(
                UtilMgr.convDict2Str(diff2, pretty=not SysMgr.streamEnable)
            )
            return

        # start printing diffs #
        for idx, fpath in enumerate((name1, name2)):
            size1 = (
                convSize(dict1[name1]["_size_"])
                if "_size_" in dict1[name1]
                else "?"
            )
            dir1 = (
                convSize(dict1[name1]["_nrDir_"])
                if "_nrDir_" in dict1[name1]
                else "?"
            )
            file1 = (
                convSize(dict1[name1]["_nrFile_"])
                if "_nrFile_" in dict1[name1]
                else "?"
            )

            size2 = (
                convSize(dict2[name2]["_size_"])
                if "_size_" in dict2[name2]
                else "?"
            )
            dir2 = (
                convSize(dict2[name2]["_nrDir_"])
                if "_nrDir_" in dict2[name2]
                else "?"
            )
            file2 = (
                convSize(dict2[name2]["_nrFile_"])
                if "_nrFile_" in dict2[name2]
                else "?"
            )

            if idx == 0:
                prefix1 = "*"
                prefix2 = ""
                cname1 = UtilMgr.convColor(name1, "GREEN")
                cname2 = name2
            else:
                prefix1 = ""
                prefix2 = "*"
                cname1 = name1
                cname2 = UtilMgr.convColor(name2, "GREEN")

            # print title #
            SysMgr.printPipe(
                (
                    "\n[Dirdiff Info] (Path: %s%s[SIZE:%s/DIR:%s/FILE:%s] "
                    "-> %s%s[SIZE:%s/DIR:%s/FILE:%s])\n%s"
                )
                % (
                    prefix1,
                    cname1,
                    size1,
                    dir1,
                    file1,
                    prefix2,
                    cname2,
                    size2,
                    dir2,
                    file2,
                    twoLine,
                )
            )

            # print dir diff #
            if idx == 0 and fpath in diff1:
                target = diff1
                subOther = diff2[name2] if name2 in diff2 else {}
            elif idx == 1 and fpath in diff2:
                target = diff2
                subOther = diff1[name1] if name1 in diff1 else {}
            else:
                SysMgr.printPipe("\tNone")
                SysMgr.printPipe("%s\n" % oneLine)
                continue

            ret = _printDir(
                target[fpath],
                level=0,
                maxLevel=maxLevel,
                isDir=True,
                other=subOther,
                before=idx == 0,
            )
            if not ret:
                SysMgr.printPipe("\tNone")
            SysMgr.printPipe("%s\n" % oneLine)

    @staticmethod
    def printDirs(path=".", maxLevel=-1, retVal=False, sizeUnit=True):
        # define functions #
        if sizeUnit:
            convSize = UtilMgr.convSize2Unit
            convNum = UtilMgr.convNum
        elif SysMgr.jsonEnable:
            convSize = lambda x: x
            convNum = lambda x: x
        else:
            convSize = lambda x: UtilMgr.convNum(x)
            convNum = UtilMgr.convNum

        # get abspath option #
        if "ABSPATH" in SysMgr.environList:
            convAbs = os.path.realpath
        else:
            convAbs = lambda x: x

        # get timeinfo option #
        printTime = "TIMEINFO" in SysMgr.environList

        # get userinfo option #
        printUser = "USERINFO" in SysMgr.environList
        if printUser:
            SysMgr(onlyInstance=True)
            SysMgr.sysInstance.saveUserInfo()

        def _getDirsJson(result, parentPath, level, maxLevel):
            # get subdir #
            try:
                fileList = os.listdir(parentPath)
                parentAbsPath = "%s" % (convAbs(parentPath))
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printWarn(
                    "failed to access '%s'" % parentPath,
                    always=True,
                    reason=True,
                )
                return (0, 0, 0)

            # check return condition #
            if not fileList or (maxLevel != -1 and maxLevel <= level):
                return (0, 0, 0)

            # print progress #
            if not retVal:
                UtilMgr.printProgress()

            totalSize = 0
            totalFile = 0
            totalDir = 0

            # set block size #
            topDir = parentPath.lstrip("/").split("/", 1)[0]
            if topDir in ("proc", "sys", "dev"):
                blockSize = 0
            else:
                blockSize = SysMgr.PAGESIZE

            # check including link option #
            incLink = "INCLINK" in SysMgr.environList

            for idx, subPath in enumerate(fileList):
                fullPath = os.path.join(parentPath, subPath)
                subAbsPath = convAbs(fullPath)

                if os.path.isdir(fullPath):
                    totalDir += 1
                    totalSize += blockSize

                    # skip link #
                    if not incLink and os.path.islink(fullPath):
                        continue

                    if SysMgr.showAll:
                        info = dict(_subDirs_={}, _subFiles_={})
                    else:
                        info = dict(_subDirs_={})

                    result[parentAbsPath]["_subDirs_"][subAbsPath] = info

                    totalInfo = _getDirsJson(
                        result[parentAbsPath]["_subDirs_"],
                        fullPath,
                        level + 1,
                        maxLevel,
                    )

                    if blockSize:
                        totalSize += totalInfo[0]
                    totalDir += totalInfo[1]
                    totalFile += totalInfo[2]

                elif os.path.isfile(fullPath):
                    # apply filter #
                    if SysMgr.filterGroup:
                        if not UtilMgr.isValidStr(subPath):
                            continue

                    totalFile += 1

                    # get size #
                    if not blockSize or (
                        not incLink and os.path.islink(fullPath)
                    ):
                        size = 0
                    else:
                        size = os.stat(fullPath).st_size

                    if size < blockSize:
                        totalSize += blockSize
                    else:
                        totalSize += size

                    if not SysMgr.showAll:
                        continue

                    # check size #
                    if not _isValidSize(condop, condval, size):
                        continue

                    # get time info #
                    times = _getTimes(fullPath)
                    if times:
                        result[parentAbsPath]["_times_"] = times

                    if "_subFiles_" not in result[parentAbsPath]:
                        result[parentAbsPath]["_subFiles_"] = {}

                    result[parentAbsPath]["_subFiles_"][subAbsPath] = dict(
                        _size_=convSize(size), _type_="file"
                    )

            # save stats for dir #
            result[parentAbsPath]["_size_"] = convSize(totalSize)
            result[parentAbsPath]["_nrDir_"] = convNum(totalDir)
            result[parentAbsPath]["_nrFile_"] = convNum(totalFile)

            return (totalSize, totalDir, totalFile)

        def _getUser(fullPath):
            if not printUser:
                return ""

            try:
                return SysMgr.sysInstance.userData[
                    str(os.stat(fullPath).st_uid)
                ]["name"]
            except SystemExit:
                sys.exit(0)
            except:
                return ""

        def _getTimes(fullPath):
            times = []
            if not printTime:
                return times

            stats = os.stat(fullPath)
            datetime = SysMgr.getPkg("datetime")
            for tname, tval in (
                ("CTIME", "st_ctime"),
                ("MTIME", "st_mtime"),
                ("ATIME", "st_atime"),
            ):
                try:
                    times.append(
                        "%s: %s"
                        % (
                            tname,
                            datetime.datetime.fromtimestamp(
                                getattr(stats, tval)
                            ).strftime("%y%m%dT%H%M%S"),
                        )
                    )
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

            return times

        def _executeCmd(path):
            for cmd in SysMgr.customCmd:
                command = cmd.replace("TARGET", path)
                SysMgr.printInfo("execute '%s'" % command)

                # launch a command #
                try:
                    procObj = None

                    # create a new worker process #
                    procObj = SysMgr.getPkg("subprocess").Popen(
                        command,
                        shell=True,
                        bufsize=0,
                    )
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr(
                        "failed to create a new process to execute '%s'" % cmd,
                        True,
                    )

                # wait for worker process #
                try:
                    if procObj:
                        procObj.wait()
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr(
                        "failed to wait termination for '%s'" % cmd, True
                    )
                finally:
                    # kill subprocess group #
                    if procObj:
                        SysMgr.killProcGroup(procObj.pid)

        def _isValidSize(condop, condval, size):
            if not condop:
                return True

            if condop == "BT":
                if condval <= size:
                    return True
                else:
                    return False
            elif condop == "LT":
                if condval >= size:
                    return True
                else:
                    return False
            else:
                return True

        def _getDirs(
            parentPath, fileList, prefix, result, level, maxLevel, sortRes
        ):
            totalSize = 0
            totalFile = 0
            totalDir = 0

            # check return condition #
            if not fileList or (maxLevel != -1 and maxLevel <= level):
                return (0, 0, 0)

            # print progress #
            UtilMgr.printProgress()

            convColor = UtilMgr.convColor
            BIGSIZE = 1 << 30

            # set block size #
            topDir = parentPath.lstrip("/").split("/", 1)[0]
            if topDir in ("proc", "sys", "dev"):
                blockSize = 0
            else:
                blockSize = SysMgr.PAGESIZE

            # check including link option #
            incLink = "INCLINK" in SysMgr.environList

            # sort #
            sortVal = None
            if "SORT" in SysMgr.environList:
                sortList = list(map(str.upper, SysMgr.environList["SORT"]))
                # sort by size #
                if "SIZE" in sortList:
                    sortVal = "SIZE"
                    fileList.sort(
                        key=lambda name: os.path.getsize(
                            "%s/%s" % (parentPath, name)
                        )
                        if os.path.exists("%s/%s" % (parentPath, name))
                        else 0,
                        reverse=True,
                    )
                # sort by type (file or directory) #
                elif "TYPE" in sortList:
                    sortVal = "TYPE"
                    fileList.sort(
                        key=lambda f: os.path.isfile(
                            os.path.join(parentPath, f)
                        )
                    )
                # sort by name #
                else:
                    fileList.sort()
            # sort by name #
            else:
                fileList.sort()

            # print tree #
            for idx, subPath in enumerate(fileList):
                idc = "|-"

                fullPath = os.path.join(parentPath, subPath)

                # check dir #
                if os.path.isdir(fullPath):
                    totalDir += 1
                    isTarget = False

                    # apply filter #
                    if SysMgr.filterGroup:
                        if UtilMgr.isValidStr(subPath) and sortVal != "SIZE":
                            isTarget = True
                            SysMgr.printPipe(
                                "[%s]" % convColor(fullPath, "GREEN")
                            )
                    else:
                        # get user info #
                        user = _getUser(fullPath)

                        if user:
                            userStr = " <%s>" % convColor(user, "YELLOW")
                        else:
                            userStr = ""

                        totalSize += blockSize
                        isTarget = True
                        string = "%s%s[%s]%s" % (
                            prefix,
                            idc,
                            convColor(subPath, "GREEN"),
                            userStr,
                        )
                        result.append(string)

                    # apply command #
                    if isTarget and SysMgr.customCmd:
                        if not "ONLYFILE" in SysMgr.environList:
                            _executeCmd(fullPath)

                    # update prefix #
                    tmpPrefix = prefix + "|  "

                    # check link #
                    if not incLink and os.path.islink(fullPath):
                        pass
                    else:
                        # get subdir #
                        try:
                            subdirs = os.listdir(fullPath)
                        except SystemExit:
                            sys.exit(0)
                        except:
                            SysMgr.printWarn(
                                "failed to access '%s'" % fullPath,
                                always=True,
                                reason=True,
                            )
                            continue

                        # traverse subdirs #
                        rlist = _getDirs(
                            fullPath,
                            subdirs,
                            tmpPrefix,
                            result,
                            level + 1,
                            maxLevel,
                            sortRes,
                        )

                        # add to stat #
                        if blockSize:
                            totalSize += rlist[0]
                        totalFile += rlist[2]
                        if isTarget:
                            totalDir += rlist[1]
                # check file #
                elif os.path.isfile(fullPath):
                    size = ""

                    # apply filter #
                    if SysMgr.filterGroup:
                        # name #
                        if not UtilMgr.isValidStr(subPath):
                            continue

                        # get size #
                        try:
                            if blockSize:
                                size = os.stat(fullPath).st_size
                                totalSize += size
                            else:
                                size = 0
                        except SystemExit:
                            sys.exit(0)
                        except:
                            SysMgr.printWarn(
                                "failed to get size for '%s'" % fullPath,
                                reason=True,
                            )
                            continue

                        # size #
                        if not _isValidSize(condop, condval, size):
                            continue

                        # convert size to string #
                        try:
                            if size >= BIGSIZE or condval:
                                color = "RED"
                            else:
                                color = "CYAN"
                            sizeStr = convSize(size)
                            sizeStr = " <%s>" % convColor(sizeStr, color)
                        except SystemExit:
                            sys.exit(0)
                        except:
                            sizeStr = ""

                        string = fullPath + sizeStr

                        if sortVal == "SIZE":
                            sortRes.append([fullPath, size, sizeStr])
                        else:
                            SysMgr.printPipe(string)

                        # apply command #
                        if SysMgr.customCmd:
                            if not "ONLYDIR" in SysMgr.environList:
                                _executeCmd(fullPath)

                    totalFile += 1

                    # get size #
                    try:
                        if not blockSize:
                            size = 0
                        elif not incLink and os.path.islink(fullPath):
                            size = 0
                        elif not size:
                            size = os.stat(fullPath).st_size
                            if size < blockSize:
                                totalSize += blockSize
                            else:
                                totalSize += size
                    except SystemExit:
                        sys.exit(0)
                    except:
                        SysMgr.printWarn(
                            "failed to get size for '%s'" % fullPath,
                            reason=True,
                        )
                        continue

                    # check skip condition #
                    if not SysMgr.showAll or SysMgr.filterGroup:
                        continue

                    # check size #
                    if not _isValidSize(condop, condval, size):
                        continue

                    # apply command #
                    if SysMgr.customCmd:
                        if not "ONLYDIR" in SysMgr.environList:
                            _executeCmd(fullPath)

                    # get time info #
                    times = _getTimes(fullPath)

                    # get user info #
                    user = _getUser(fullPath)

                    # convert size to string #
                    try:
                        if size >= BIGSIZE or condval:
                            color = "RED"
                        elif size > blockSize:
                            color = "CYAN"
                        else:
                            color = None

                        sizeStr = convSize(size)

                        # link #
                        nrLink = os.stat(fullPath).st_nlink
                        if nrLink > 1:
                            linkStr = ", LINK: %s" % nrLink
                        elif os.path.islink(fullPath):
                            try:
                                linkStr = ", LINK: %s" % os.readlink(fullPath)
                            except SystemExit:
                                sys.exit(0)
                            except:
                                linkStr = ""
                        else:
                            linkStr = ""

                        # time #
                        if times:
                            timeStr = ", %s" % ", ".join(times)
                        else:
                            timeStr = ""

                        if user:
                            userStr = " <%s>" % convColor(user, "YELLOW")
                        else:
                            userStr = ""

                        sizeStr = " <SIZE: %s%s%s>" % (
                            convColor(sizeStr, color),
                            linkStr,
                            timeStr,
                        )
                    except SystemExit:
                        sys.exit(0)
                    except:
                        sizeStr = ""
                        userStr = ""

                    string = "%s%s%s%s%s" % (
                        prefix,
                        idc,
                        subPath,
                        userStr,
                        sizeStr,
                    )
                    result.append(string)

            # check size #
            if not _isValidSize(condop, condval, totalSize):
                return (totalSize, totalDir, totalFile)

            # convert total size #
            if totalSize:
                if totalSize >= BIGSIZE or condval:
                    color = "RED"
                else:
                    color = "CYAN"
                tsize = "SIZE: %s, " % convColor(convSize(totalSize), color)
            else:
                tsize = ""

            summary = " <%sDIR: %s, FILE: %s>" % (
                tsize,
                convNum(totalDir),
                convNum(totalFile),
            )

            # add summary by reverse traverse #
            if level == 0:
                result[0] += summary
            else:
                tprefix = "%s-[%s]" % (
                    prefix[:-2],
                    os.path.basename(parentPath),
                )
                for i, val in enumerate(reversed(result)):
                    if not UtilMgr.removeColor(val).startswith(tprefix):
                        continue
                    result[-(i) - 1] += summary
                    break

            return (totalSize, totalDir, totalFile)

        # check filter option #
        if SysMgr.findOption("g"):
            if not SysMgr.filterGroup:
                SysMgr.filterGroup = ["*"]

        # parse filter for file size #
        condop = None
        condval = None
        if "SIZECOND" in SysMgr.environList:
            sizeFilter = SysMgr.environList["SIZECOND"][0].split(":")

            # init variables #
            error = False

            # parse condition values #
            if len(sizeFilter) != 2:
                error = True
            elif sizeFilter[0].strip().upper() == "BT":
                condop = "BT"
                try:
                    condval = UtilMgr.convUnit2Size(sizeFilter[1].strip())
                except SystemExit:
                    sys.exit(0)
                except:
                    condval = None
                    error = True
            elif sizeFilter[0].strip().upper() == "LT":
                condop = "LT"
                try:
                    condval = UtilMgr.convUnit2Size(sizeFilter[1].strip())
                except SystemExit:
                    sys.exit(0)
                except:
                    error = True
            else:
                error = True

            # handle error #
            if error:
                SysMgr.printErr("wrong input value for SIZECOND", True)
                sys.exit(-1)
        else:
            sizeFilter = None

        # print start directory #
        abspath = convAbs(path)

        # get sort list #
        sortList = []

        if not retVal:
            SysMgr.printStat(r"start traversing dirs from '%s'..." % abspath)

        # print start path #
        if SysMgr.jsonEnable or retVal:
            result = {}
            if SysMgr.showAll:
                result[abspath] = dict(_subDirs_={}, _subFiles_={})
            else:
                result[abspath] = dict(_subDirs_={})

            _getDirsJson(result, path, 0, -1)

            if retVal:
                return result

            output = UtilMgr.convDict2Str(
                result, pretty=not SysMgr.streamEnable
            )
        else:
            try:
                initDir = os.listdir(path)
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr("failed to access '%s'" % path, reason=True)
                sys.exit(-1)

            result = ["[%s]" % abspath]

            if SysMgr.filterGroup:
                SysMgr.printPipe()

            _getDirs(
                abspath, initDir, "  ", result, 0, maxLevel, sortRes=sortList
            )

            output = "\n%s\n" % "\n".join(result)

        # print results #
        UtilMgr.deleteProgress()

        for path, size, sizeStr in sorted(
            sortList, key=lambda e: e[1], reverse=True
        ):
            SysMgr.printPipe(path + sizeStr)

        SysMgr.printPipe(output)

        return output

    @staticmethod
    def setLowPriority(force=False, cpuVal=15, verb=False):
        # set CPU priority #
        if force or SysMgr.prio is None:
            SysMgr.setPriority(SysMgr.pid, "C", cpuVal, verb=verb)

        # set I/O priority #
        if force or SysMgr.ioprio is None:
            SysMgr.setIoPriority(ioclass="IOPRIO_CLASS_IDLE", verb=verb)

    @staticmethod
    def getProcAddrBySymbol(pid, symbolList, fileFilter=None):
        resInfo = {}

        # get file list on memory map #
        fileList = FileAnalyzer.getProcMapInfo(pid, onlyExec=False)
        if not fileList:
            comm = SysMgr.getComm(pid)
            procInfo = "%s(%s)" % (comm, pid)
            SysMgr.printErr("failed to get memory map for %s" % procInfo)
            return resInfo

        # convert path from overlayfs mount point #
        overlayInfo = SysMgr.getOverlayfsInfo(pid)
        if overlayInfo:
            for idx, path in enumerate(list(fileList)):
                newpath = UtilMgr.convOverlayPath(path, overlayInfo)
                if path != newpath:
                    fileList[newpath] = fileList.pop(path, None)

        # apply file filter #
        if fileFilter:
            newFileList = {}
            if isinstance(fileFilter, str):
                fileFilter = [fileFilter]
            for path, value in sorted(
                fileList.items(), key=lambda e: e[1]["vstart"]
            ):
                for item in fileFilter:
                    if path.startswith(item):
                        newFileList[path] = value
            if not newFileList:
                comm = SysMgr.getComm(pid)
                procInfo = "%s(%s)" % (comm, pid)
                SysMgr.printErr(
                    "failed to get [ %s ] from memory map for %s"
                    % (", ".join(fileFilter), procInfo)
                )
                sys.exit(-1)
            fileList = newFileList

        # scan files #
        magicStr = SysMgr.magicStr
        for filePath, attr in sorted(
            fileList.items(), key=lambda e: e[1]["vstart"]
        ):
            # skip invalid file #
            if not FileAnalyzer.isValidFile(filePath, special=True):
                continue

            for sym in symbolList:
                # create an ELF object #
                try:
                    if magicStr in filePath:
                        origFilePath = filePath[: filePath.rfind(magicStr)]
                    else:
                        origFilePath = filePath

                    res = ElfAnalyzer.getSymOffset(sym, origFilePath)
                    if not res:
                        continue

                    origStartAddr = fileList[origFilePath]["vstart"]

                    for item in res:
                        fname = item[2]
                        fobj = ElfAnalyzer.cachedFiles[fname]
                        foffset = long(item[0])
                        startAddr = attr["vstart"]
                        totalDiff = 0

                        # get real offset for memory hole #
                        if (
                            origFilePath != filePath
                            and attr["offset"] <= foffset
                        ):
                            (
                                filePath,
                                startAddr,
                                totalDiff,
                            ) = Debugger.getRealOffsetInfo(fileList, filePath)

                        # check relocatable type #
                        if fobj.loadAddr == 0:
                            addr = hex(startAddr + foffset - totalDiff)
                        elif origStartAddr > fobj.loadAddr:
                            addr = hex(startAddr + foffset - totalDiff)
                        else:
                            addr = hex(foffset)

                        resInfo["%s|%s" % (item[1], origFilePath)] = (
                            hex(item[0]),
                            origFilePath,
                            addr,
                            item[1],
                        )
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printWarn(
                        "failed to save offset info", True, reason=True
                    )

        return resInfo

    @staticmethod
    def setDwarfFlag():
        def _printDwarfStatus(stat):
            statStr = "enabled" if stat else "disabled"
            SysMgr.printWarn("%s DWARF parser" % statStr, True)

        # check DWARF requirement #
        if SysMgr.dwarfEnable:
            _printDwarfStatus(True)
            return

        # enable DWARF by default for ARM #
        if SysMgr.arch in ("aarch64", "arm"):
            SysMgr.dwarfEnable = False
        else:
            SysMgr.dwarfEnable = True

        # check disable option #
        disableList = SysMgr.getOption("d")
        if disableList and "D" in disableList:
            SysMgr.dwarfEnable = False
            _printDwarfStatus(False)
            return

        # init and check libcorkscrew #
        SysMgr.initLibcorkscrew()
        if SysMgr.libcorkscrewObj and SysMgr.libcorkscrewObj != -1:
            SysMgr.dwarfEnable = False
            _printDwarfStatus(False)
            return

        _printDwarfStatus(SysMgr.dwarfEnable)

    @staticmethod
    def doPrintFile(inputArg=None):
        # check input #
        if inputArg:
            if not isinstance(inputArg, list):
                inputArg = [inputArg]
        elif SysMgr.hasMainArg():
            inputArg = SysMgr.getMainArgs()
        elif SysMgr.inputParam:
            inputArg = str(SysMgr.inputParam).split(",")
            inputArg = UtilMgr.cleanItem(inputArg, True)
        else:
            SysMgr.printErr("no input for PATH")
            return -1

        # convert input files #
        inputArg = UtilMgr.getFileList(inputArg, sort=True, exceptDir=True)
        if not inputArg:
            SysMgr.printErr("no target file")
            return -1

        # get read size #
        head = long(SysMgr.getTimeValues(["HEAD"], 0))
        tail = long(SysMgr.getTimeValues(["TAIL"], 100))

        if head or tail:
            SysMgr.streamEnable = True

        # get chunk size #
        if "SIZE" in SysMgr.environList:
            try:
                reqSize = UtilMgr.convUnit2Size(SysMgr.environList["SIZE"][0])
            except:
                return -1
        else:
            reqSize = None

        prevStr = ""
        printRaw = "PRINTRAW" in SysMgr.environList
        base64 = "BASE64" in SysMgr.environList
        andpkglist = "ANDPKGLIST" in SysMgr.environList

        def _printReadErr(fd):
            SysMgr.printErr(
                "failed to read data from '%s'" % fd.name
                if hasattr(fd, "name")
                else "FILE",
                True,
            )

        # print files #
        for path in inputArg:
            fsize = UtilMgr.getFileSizeStr(path)
            SysMgr.printStat(r"start printing '%s%s'..." % (path, fsize))
            SysMgr.printPipe(
                "\n-> File: %s%s\n\n"
                % (UtilMgr.convColor(path, "YELLOW"), fsize)
            )

            # decode BASE64 data #
            if base64:
                data = UtilMgr.decodeBase64(SysMgr.readFile(path, byte=True))
                fd = MemoryFile(size=len(data), name=path, data=data)
            else:
                fd = None

            # open the target file #
            try:
                # raw data #
                if printRaw:
                    fd = fd if fd else open(path, "r")
                # gz + tar.gz #
                elif UtilMgr.isGzipFile(path, fd):
                    flist = UtilMgr.getTarFileList(path, verb=False)
                    if flist:
                        UtilMgr.printAllAttrs(
                            flist, path, "name", SysMgr.filterGroup
                        )
                        continue
                    else:
                        gzip = SysMgr.getPkg("gzip", False)
                        fd = (
                            gzip.GzipFile(fileobj=fd)
                            if fd
                            else gzip.open(path, "r")
                        )
                # zip #
                elif UtilMgr.isZipFile(path, fd):
                    flist = UtilMgr.getZipFileList(path)
                    UtilMgr.printAllAttrs(
                        flist, path, "filename", SysMgr.filterGroup
                    )
                    continue
                # xz (lzma) #
                elif UtilMgr.isXzFile(path, fd):
                    flist = UtilMgr.getTarFileList(path, verb=False)
                    if flist:
                        UtilMgr.printAllAttrs(
                            flist, path, "name", SysMgr.filterGroup
                        )
                        continue
                    else:
                        param = fd if fd else path
                        fd = SysMgr.getPkg("lzma", False).open(param)
                # tar #
                elif UtilMgr.isTarFile(path, fd):
                    flist = UtilMgr.getTarFileList(path)
                    UtilMgr.printAllAttrs(
                        flist, path, "name", SysMgr.filterGroup
                    )
                    continue
                # android package list #
                elif andpkglist:
                    fd = fd if fd else open(path, "rb")
                # raw data #
                else:
                    fd = fd if fd else open(path, "r")
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printOpenErr(path)
                continue

            pos = 0
            buf = ""

            if head:
                # print last lines first #
                try:
                    for idx, item in enumerate(fd.readlines()):
                        buf += item
                        if idx >= head:
                            break
                except SystemExit:
                    sys.exit(0)
                except:
                    _printReadErr(fd)
                    continue

                # print buf #
                SysMgr.printPipe(buf)
                continue
            # set pos to EOF #
            elif tail:
                if andpkglist:
                    SysMgr.printErr(
                        "no support tail for %s because of android package list file"
                        % path
                    )
                    continue

                # print last lines first #
                try:
                    for idx, item in enumerate(reversed(fd.readlines())):
                        buf = item + buf
                        if idx >= tail:
                            break
                except SystemExit:
                    sys.exit(0)
                except:
                    _printReadErr(fd)
                    continue

                # print buf #
                SysMgr.printPipe(buf)

                # check exit condition #
                if not "CONT" in SysMgr.environList:
                    continue

                try:
                    size = os.stat(path).st_size
                    fd.seek(0, 2)
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

                # set path #
                pathList = [path, os.path.dirname(os.path.realpath(path))]

                # set flags #
                flags = [
                    "IN_ATTRIB",
                    "IN_MOVED_TO",
                    "IN_CREATE",
                    "IN_DELETE",
                    "IN_DELETE_SELF",
                    "IN_MODIFY",
                    "IN_MOVE_SELF",
                ]

            while 1:
                if tail:
                    # watch the file #
                    SysMgr.inotify(pathList, flags)
                    try:
                        # reopen the file #
                        fd.close()
                        fd = open(path, "r")
                        newSize = os.stat(path).st_size

                        # check file size #
                        if pos == 0 or size > newSize:
                            pos = newSize - tail
                            if pos < 0:
                                pos = 0

                        # update size #
                        size = newSize
                        fd.seek(pos, 0)
                    except SystemExit:
                        sys.exit(0)
                    except:
                        pos = 0
                        continue

                # read data #
                try:
                    if reqSize:
                        data = fd.read(reqSize)
                    else:
                        data = fd.read()
                except SystemExit:
                    sys.exit(0)
                except:
                    _printReadErr(fd)
                    break

                # decode data #
                try:
                    data = data.decode()
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

                # decode android package list #
                if andpkglist:
                    lineList = ""
                    for line in data.split(b"\n"):
                        if not line:
                            continue
                        # TODO: parse binary format #
                    data = "\n".join(lineList)

                # handle redundant data #
                if tail:
                    origPrevStr = prevStr
                    prevStr = data
                    if origPrevStr:
                        # get common part #
                        common = UtilMgr.getCommonPart([origPrevStr, data])
                        if common:
                            data = data.replace(common, "")
                            origPrevStr = common

                # handle event #
                if not data:
                    if tail:
                        continue
                    else:
                        break

                # decode bytes #
                try:
                    data = data.decode("latin-1")
                except:
                    pass

                # print data #
                for line in data.split("\n"):
                    # apply filter #
                    if SysMgr.filterGroup and not UtilMgr.isValidStr(line):
                        continue

                    # print line #
                    if line:
                        SysMgr.printPipe(line)
                    else:
                        SysMgr.printPipe("\n")

                if tail:
                    SysMgr.printPipe(
                        "--- END [%s] ---"
                        % UtilMgr.getTime("UTCTIME" in SysMgr.environList)
                    )
                else:
                    break

    @staticmethod
    def doPrintExt():
        SysMgr.printLogo(big=True, onlyFile=True)

        # check input #
        if SysMgr.hasMainArg():
            inputArg = SysMgr.getMainArgs()
        elif SysMgr.inputParam:
            inputArg = str(SysMgr.inputParam).split(",")
            inputArg = UtilMgr.cleanItem(inputArg, True)
        else:
            SysMgr.printErr("no input for PATH")
            sys.exit(-1)

        for path in inputArg:
            SysMgr.printStat(
                r"start traversing ext4 filesystem from '%s'..." % path
            )

            # make an ext4 object #
            try:
                eobj = Ext4Analyzer(path)
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr(
                    "failed to create ext4 object for %s" % path, True
                )
                continue

            # traverse all inodes #
            try:
                inodeList = eobj.getInodeList(
                    filters=SysMgr.filterGroup, verb=SysMgr.warnEnable
                )
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr("failed to traverse %s" % path, True)
                continue

            # print menu #
            SysMgr.printPipe("\n[Ext4 Info] [Path: %s]\n%s" % (path, twoLine))
            SysMgr.printPipe(
                "{0:>12} {1:>6} {2:>1}\n{3:1}".format(
                    "INODE", "TYPE", "PATH", twoLine
                )
            )

            # print items #
            for inode, values in sorted(inodeList.items()):
                fpath = os.path.join(values["path"], values["name"])
                SysMgr.printPipe(
                    "{0:>12} {1:>6} {2:>1}".format(
                        inode, values["type"], fpath
                    )
                )
            SysMgr.printPipe(oneLine)

    @staticmethod
    def doMkCache():
        SysMgr.printLogo(big=True, onlyFile=True)

        # change priority #
        SysMgr.setLowPriority(force=False, cpuVal=10, verb=True)

        # set dwarf flag #
        SysMgr.setDwarfFlag()

        # get parallel flag #
        isParallel = not "SEQUANTIAL" in SysMgr.environList

        # remove caches #
        if "REMOVECACHE" in SysMgr.environList:
            ElfAnalyzer.removeCacheFiles()

        # check input #
        ret = SysMgr.selectTaskId()
        if ret:
            inputArg = [ret]
        elif SysMgr.hasMainArg():
            inputArg = SysMgr.getMainArgs()
        elif SysMgr.inputParam:
            inputArg = str(SysMgr.inputParam).split(",")
            inputArg = UtilMgr.cleanItem(inputArg, True)
        else:
            SysMgr.printErr("no input for PATH or COMM or PID")
            sys.exit(-1)

        # get pid list #
        pids = []
        for item in inputArg:
            pids += SysMgr.getTgids(item)
        pids = list(set(pids))

        # register exit handler #
        signal.signal(signal.SIGINT, SysMgr.faultHandler)

        # single file #
        if not pids:
            for item in inputArg:
                # check file #
                if not os.path.isfile(item):
                    SysMgr.printErr(
                        "failed to recognize '%s' as file or process" % item
                    )
                    sys.exit(-1)

                # load symbol caches #
                ElfAnalyzer.getObject(item)
        else:
            procList = {}

            # get pid list #
            for tid in pids:
                try:
                    pid = SysMgr.getTgid(tid)
                    if not pid:
                        continue

                    procList.setdefault(pid, [])
                    procList[pid].append(tid)
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

            pidList = list(map(long, procList))

            # print process list #
            commList = SysMgr.getCommList(pidList)
            SysMgr.printInfo("start making ELF caches for %s\n" % commList)

            # merge map files #
            mapList = SysMgr.getMapList(pidList)

            # load symbol caches at once #
            for item in mapList:
                try:
                    # skip partial files #
                    if SysMgr.magicStr in item:
                        continue

                    if isParallel:
                        SysMgr.printInfo("load %s... " % item)

                    # create a new worker #
                    pid = SysMgr.createProcess(mute=isParallel)
                    if pid != 0:
                        if not isParallel:
                            SysMgr.waitChild()
                        continue

                    # create a file cache #
                    ElfAnalyzer.getObject(item)

                    # terminate a worker #
                    sys.exit(0)
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

            SysMgr.printInfo("start waiting for workers... ", suffix=False)

            # launch system monitor #
            monPid = 0
            if "SYSMON" in SysMgr.environList:
                # start monitoring #
                SysMgr.addEnvironVar("TASKMON")
                monPid = SysMgr.runTaskMonitor([__module__], wait=False)
                SysMgr.childList.pop(monPid)

            # wait for children #
            SysMgr.waitChild()

            if monPid:
                SysMgr.childList[monPid] = True

            SysMgr.printInfo("[Done]", prefix=False, title=False)

    @staticmethod
    def doSym2addr():
        SysMgr.printLogo(big=True, onlyFile=True)

        # check input #
        if SysMgr.hasMainArg():
            inputArg = SysMgr.getMainArg()
        elif SysMgr.inputParam:
            inputArg = str(SysMgr.inputParam)
        else:
            SysMgr.printErr("no input for PATH or COMM or PID")
            sys.exit(-1)

        # check symbol #
        if not SysMgr.filterGroup:
            SysMgr.printInfo("print all symbols")
            SysMgr.filterGroup.append("**")
        else:
            SysMgr.printInfo(
                "print all symbols including [ %s ]"
                % ",".join(SysMgr.filterGroup)
            )

        def _printRes(procInfo, resInfo, maxSymLen):
            if procInfo:
                procInfo = " [Task: %s]" % procInfo

            # make space between symbol and path #
            maxSymLen += 4

            SysMgr.printPipe(
                "\n[Symbol Info]%s (NrSym: %s)\n%s"
                % (procInfo, UtilMgr.convNum(len(resInfo)), twoLine)
            )
            SysMgr.printPipe(
                "{0:<{maxSymLen}} {1:<18} {2:<18} {3:1}\n{4:1}".format(
                    "Symbol",
                    "Offset",
                    "Address",
                    "PATH",
                    twoLine,
                    maxSymLen=maxSymLen,
                )
            )

            # print symbols from offset list #
            for sym, val in sorted(resInfo.items()):
                symbol = sym.split("|")[0]
                offset, filePath, addr, origsym = val

                if offset is None:
                    offset = "N/A"

                if addr is None:
                    addr = "N/A"

                SysMgr.printPipe(
                    "{0:<{maxSymLen}} {1:<18} {2:<18} {3:1}".format(
                        symbol,
                        offset.rstrip("L"),
                        addr.rstrip("L"),
                        filePath,
                        maxSymLen=maxSymLen,
                    )
                )

            if not resInfo:
                SysMgr.printPipe("    None")

            SysMgr.printPipe(oneLine + "\n")

        # init variables #
        resInfo = {}
        maxSymLen = 5

        # get pid list #
        pids = SysMgr.getTgids(inputArg)

        # init process string #
        procInfo = ""

        # files #
        if not pids:
            # get file list #
            fileList = UtilMgr.getFileList(
                UtilMgr.cleanItem(inputArg.split(","), False), exceptDir=True
            )

            # check file #
            if not fileList:
                SysMgr.printErr(
                    "failed to recognize '%s' as a file or a process"
                    % inputArg
                )
                sys.exit(-1)

            for filePath in fileList:
                for sym in SysMgr.filterGroup:
                    # create an ELF object #
                    try:
                        offset = ElfAnalyzer.getSymOffset(sym, filePath)
                        if not offset:
                            continue

                        for item in offset:
                            resInfo["%s|%s" % (item[1], filePath)] = (
                                hex(item[0]),
                                filePath,
                                None,
                                item[1],
                            )

                            if maxSymLen < len(item[1]) < SysMgr.ttyCols / 2:
                                maxSymLen = len(item[1])
                    except SystemExit:
                        sys.exit(0)
                    except:
                        SysMgr.printErr("failed to get '%s' info" % sym, True)
                        sys.exit(-1)

            # print result #
            _printRes(procInfo, resInfo, maxSymLen)
        else:
            for pid in pids:
                # init variables #
                resInfo = {}
                maxSymLen = 5
                comm = SysMgr.getComm(pid)
                procInfo = "%s(%s)" % (comm, pid)
                symbolList = SysMgr.filterGroup

                # get symbol offset #
                resInfo = SysMgr.getProcAddrBySymbol(pid, symbolList)
                for key, item in resInfo.items():
                    if maxSymLen < len(item[3]) < SysMgr.ttyCols / 2:
                        maxSymLen = len(item[3])

                # print result #
                _printRes(procInfo, resInfo, maxSymLen)

    @staticmethod
    def initTaskMon(pid, update=True):
        if not SysMgr.isLinux:
            return None

        # save proc data #
        tobj = TaskAnalyzer(None, onlyInstance=True)
        path = "%s/%s" % (SysMgr.procPath, pid)
        tobj.saveProcData(path, pid)

        # update time and instances #
        SysMgr.updateUptime()
        if update:
            tobj.saveProcInstance()

        return tobj

    @staticmethod
    def getTaskMon(tobj, pid, res):
        nowData = tobj.procData[pid]["stat"]
        prevData = tobj.prevProcData[pid]["stat"]

        if res == "ttime":
            utick = nowData[tobj.utimeIdx] - prevData[tobj.utimeIdx]
            stick = nowData[tobj.stimeIdx] - prevData[tobj.stimeIdx]
            return utick + stick
        else:
            return None

    @staticmethod
    def updateTaskMon(tobj, pid):
        if not SysMgr.isLinux:
            raise Exception("N/A")

        path = "%s/%s" % (SysMgr.procPath, SysMgr.pid)
        SysMgr.updateUptime()
        tobj.saveProcData(path, pid)
        tobj.setProcUsage()

    @staticmethod
    def doLeaktrace(restart=False):
        def _sendSignal(sig, comm, pid, purpose):
            # save idle page status #
            if LeakAnalyzer.markedIdlePages:
                SysMgr.printStat(r"start checking all idle pages...")
                LeakAnalyzer.idlePageList = PageAnalyzer.getPageInfo(
                    [pid],
                    "anon",
                    checkIdle=True,
                    retList=True,
                    verb=False,
                    progress=True,
                )

            # kill background tasks for user command #
            try:
                runList = UtilMgr.deepcopy(SysMgr.getChildList())
                runList.pop(long(pid), None)
                if runList:
                    SysMgr.killChildren(
                        sig=signal.SIGINT,
                        children=runList,
                        wait=True,
                        group=True,
                        clear=False,
                    )
            except SystemExit:
                sys.exit(0)
            except:
                pass

            # send signal #
            try:
                os.kill(long(pid), sig)
                SysMgr.printStat(
                    "sent %s to %s(%s) to %s memory profiling"
                    % (ConfigMgr.SIG_LIST[sig], comm, pid, purpose)
                )
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr(
                    "failed to send %s to %s memory profiling"
                    % (ConfigMgr.SIG_LIST[sig], purpose),
                    reason=True,
                )
                sys.exit(-1)

        def _waitAndKill(tobj, pid, comm, cond, sig, purpose, hookCmd=None):
            conv = UtilMgr.convSize2Unit

            # define indexes #
            utimeIdx = ConfigMgr.PROCSTAT_ATTR.index("UTIME")
            stimeIdx = ConfigMgr.PROCSTAT_ATTR.index("STIME")
            rssIdx = ConfigMgr.PROCSTAT_ATTR.index("RSS")
            path = "%s/%s" % (SysMgr.procPath, pid)

            # check destination value #
            if cond in (sys.maxsize, SysMgr.maxSize):
                unit = None
                des = ""
            else:
                unit = "M"
                des = " at RSS(%s)" % conv(cond)

            # reset and save proc instance #
            tobj.saveProcInstance()

            # set start time #
            if "USEUPTIME" in SysMgr.environList:
                startTime = 0
            else:
                startTime = SysMgr.startTime

            # set interval #
            statInterval = UtilMgr.getEnvironNum(
                "STATINTERVAL", False, 1, False
            )

            # wait for RSS #
            prevCpu = None
            prevMem = None
            printSame = False
            while 1:
                ret = tobj.saveProcData(path, pid)
                if not ret:
                    if not SysMgr.isAlive(pid):
                        SysMgr.printErr("%s(%s) is terminated" % (comm, pid))
                    else:
                        SysMgr.printErr(
                            "failed to get RSS of %s(%s)" % (comm, pid)
                        )
                    return -1

                # get time #
                SysMgr.updateUptime()
                diff = "[%s]" % UtilMgr.convTime(SysMgr.uptime - startTime)

                # get CPU usage #
                procData = tobj.procData[pid]["stat"]
                utime = long(procData[utimeIdx])
                stime = long(procData[stimeIdx])
                ttime = utime + stime
                if prevCpu != None:
                    cpu = ttime - prevCpu
                    cpustr = "%.1f" % (cpu / SysMgr.uptimeDiff)
                else:
                    cpustr = ""
                    prevCpu = ttime
                    time.sleep(statInterval)
                    continue

                # get memory usage #
                rss = long(procData[rssIdx]) << 12
                mstat = TaskAnalyzer.getMemStr(tobj, pid, tok=" / ", unit=unit)

                # reset and save proc instance #
                tobj.saveProcInstance()

                # print memory usage #
                if prevCpu != ttime or prevMem != rss:
                    SysMgr.printInfo(
                        "%s %s(%s): CPU(%s%%) / %s before %s%s"
                        % (
                            diff,
                            comm,
                            pid,
                            cpustr,
                            mstat,
                            purpose,
                            des,
                        ),
                        prefix=False,
                    )
                    printSame = False
                elif not printSame:
                    SysMgr.printInfo("%s NO CHANGE" % diff, prefix=False)
                    printSame = True

                # update stats #
                prevMem = rss
                prevCpu = ttime

                if cond <= rss:
                    break
                else:
                    time.sleep(statInterval)

            # set hook #
            if hookCmd:
                SysMgr.printInfo(r"start applying hook commands... ")

                SysMgr.launchGuider(
                    hookCmd, pipe=False, stderr=True, log=True, wait=True
                )

            # check signal #
            if (
                not sig
                or not isinstance(sig, (int, long))
                or sig < 0
                or sig >= len(ConfigMgr.SIG_LIST)
            ):
                SysMgr.printErr("wrong signal %s" % sig)
                return -1

            # send signal #
            try:
                _sendSignal(sig, comm, pid, purpose)
            except:
                return -1

            return 0

        def _getOutputPath(multiple, pid):
            fname = None
            dirname = None

            # get dir name #
            if SysMgr.inputParam:
                dirname = os.path.realpath(SysMgr.inputParam)
                if not os.path.isdir(dirname):
                    fname = dirname
            elif os.path.exists(SysMgr.tmpPath):
                dirname = SysMgr.tmpPath
            elif SysMgr.isWritable("."):
                dirname = os.path.realpath(".")
            else:
                SysMgr.printErr("no input for temporary file path")
                sys.exit(-1)

            # set file name #
            if not fname:
                if multiple:
                    fname = "%s/leaks_%s.out" % (dirname, pid)
                else:
                    fname = "%s/leaks.out" % dirname

            return fname

        def _checkSignals(tobj, path, pid, startSig, stopSig):
            # update signal list #
            tobj.saveProcStatusData(path, pid)
            sigList = tobj.procData[pid]["status"]["SigCgt"]

            # check start signal #
            if startSig and UtilMgr.isBitEnabled(startSig, sigList):
                SysMgr.printWarn(
                    "found %s(%s) handler for start"
                    % (ConfigMgr.SIG_LIST[startSig], startSig),
                    True,
                )

            # check stop signal #
            if stopSig and UtilMgr.isBitEnabled(stopSig, sigList):
                SysMgr.printWarn(
                    "found %s(%s) handler for stop"
                    % (ConfigMgr.SIG_LIST[stopSig], stopSig),
                    True,
                )

        # check package #
        SysMgr.getPkg("ctypes")

        # check output path #
        if not SysMgr.outPath:
            if SysMgr.isAndroid or not SysMgr.isLinux:
                SysMgr.outPath = os.path.realpath("./guider.out")
            else:
                SysMgr.outPath = "%s/guider.out" % SysMgr.tmpPath

        # check target info #
        ret = SysMgr.selectTaskId()
        if ret:
            inputCmd = []
            targetList = [ret]
        elif not SysMgr.filterGroup and SysMgr.hasMainArg():
            inputCmd = SysMgr.getMainArg().split()
            targetList = []
        else:
            inputCmd = []
            targetList = SysMgr.filterGroup
            if not targetList:
                SysMgr.printErr("no input for PID or COMM")
                sys.exit(-1)

        # get signals #
        startSig = stopSig = None
        if SysMgr.killFilter:
            sigList = SysMgr.killFilter

            if len(sigList) >= 2:
                # start #
                sigval = sigList[0][0].strip()
                startSig = SysMgr.getSigNum(sigval)
                if not startSig:
                    SysMgr.printErr("wrong signal %s for start" % sigval)
                    sys.exit(-1)
                LeakAnalyzer.startSig = startSig

                stopIdx = 1
            else:
                stopIdx = 0

            # stop #
            sigval = sigList[stopIdx][0].strip()
            stopSig = SysMgr.getSigNum(sigval)
            if not stopSig:
                SysMgr.printErr("wrong signal %s for stop" % sigval)
                sys.exit(-1)
            LeakAnalyzer.stopSig = stopSig

        # check WAITSIGNAL flag #
        waitSignal = "WAITSIGNAL" in SysMgr.environList

        # execute before commands #
        SysMgr.runProfCmd("BEFORE")

        # get PID #
        pid = None
        comm = None
        isMulti = False
        startTime = endTime = 0
        if inputCmd:
            # set mute flag #
            mute = "MUTE" in SysMgr.environList

            # set environment variable #
            SysMgr.environList.setdefault("ENV", [])

            # set PRELOAD path #
            libPath = SysMgr.getOption("T")
            if libPath:
                # handle special characters in path #
                newPath = UtilMgr.convPath(libPath)
                if not newPath:
                    SysMgr.printErr("wrong path '%s'" % libPath)
                    sys.exit(-1)
                elif len(newPath) > 1:
                    SysMgr.printErr(
                        "found multiple libraries [ %s ]" % ", ".join(newPath)
                    )
                    sys.exit(-1)
                else:
                    # convert to absolute path #
                    libPath = os.path.realpath(newPath[0])

                    # add PRELOAD path #
                    env = SysMgr.getEnvList()
                    if "LD_PRELOAD" in env:
                        libPath += ":%s" % SysMgr.convFullPath(
                            env["LD_PRELOAD"]
                        )
                    SysMgr.environList["ENV"].append("LD_PRELOAD=%s" % libPath)

                # check ld.so.preload #
                ldPreloadPath = "/etc/ld.so.preload"
                if os.path.exists(ldPreloadPath):
                    ldPreloadList = SysMgr.readFile(ldPreloadPath).split("\n")
                    if ldPreloadList:
                        SysMgr.printWarn(
                            "preloading [ %s ] is already applied from '%s'"
                            % (", ".join(ldPreloadList), ldPreloadPath),
                            True,
                        )
            else:
                SysMgr.printErr("no path for libleaktracer.so")
                sys.exit(-1)

            # set output path #
            fname = _getOutputPath(isMulti, pid)

            # add start variable #
            if startSig or len(SysMgr.customCmd) > 1 or waitSignal:
                SysMgr.environList["ENV"].append(
                    "LEAKTRACER_ONSIG_STARTALLTHREAD=%s"
                    % LeakAnalyzer.startSig,
                )
            else:
                SysMgr.environList["ENV"].append(
                    "LEAKTRACER_ONSTART_STARTALLTHREAD=1"
                )

            # add report variable #
            SysMgr.environList["ENV"].extend(
                [
                    "LEAKTRACER_ONSIG_REPORT=%s" % LeakAnalyzer.stopSig,
                    "LEAKTRACER_ONSIG_REPORTFILENAME=%s" % fname,
                    "LEAKTRACER_ONEXIT_REPORT=1",
                    "LEAKTRACER_ONEXIT_REPORTFILENAME=%s" % fname,
                ]
            )

            # disable core dump for child #
            if not "USECOREDUMP" in SysMgr.environList:
                SysMgr.chRlimit(long(SysMgr.pid), "RLIMIT_CORE", 0, 0)

            # create the target process #
            pid = SysMgr.createProcess(inputCmd, mute=mute, chPgid=True)
            if pid < 0:
                sys.exit(-1)
            elif pid == 0:
                sys.exit(0)

            pids = [str(pid)]

            # stop target process #
            if "STOPTARGET" in SysMgr.environList:
                SysMgr.sendSignalProcs(signal.SIGSTOP, pids)

            # wait for initialization of the target #
            # TODO: sync with the child to get memory map before termination #
            time.sleep(1)
        else:
            pids = SysMgr.convTaskList(targetList, exceptMe=True)

        # check PID #
        if not pids:
            SysMgr.printErr("no '%s' process" % ", ".join(targetList))
            sys.exit(-1)
        elif len(pids) > 1:
            SysMgr.printWarn(
                "multiple tasks [ %s ] are targeted"
                % SysMgr.getCommList(pids),
                True,
            )

            isMulti = True

            # create multiple tracers #
            for item in pids:
                ret = SysMgr.createProcess()
                if not ret:
                    pid = item
                    break

            # wait for child tracers as a parent process #
            if not pid:
                SysMgr.setIgnoreSignal()
                SysMgr.waitChild()
                SysMgr.setNormalSignal()
                sys.exit(0)
        else:
            pid = pids[0]

        # get comm and check alive #
        comm = SysMgr.getComm(pid)
        if not comm and not SysMgr.isAlive(pid):
            SysMgr.printErr("the task with PID %s is terminated" % pid)
            sys.exit(0)

        # execute start commands #
        SysMgr.runProfCmd("START", {"PID": pid})

        # get environment variables of the target #
        envList = SysMgr.getEnv(pid, retdict=True)

        # check permission #
        SysMgr.checkRootPerm()

        # define remote command list #
        remoteCmd = []
        hookCmd = []
        hookList = [
            "calloc",
            "malloc",
            "realloc",
            "free",
            "operator new(unsigned long)",
            "operator new(unsigned long\, std::align_val_t)",
            "operator new(unsigned long\, std::align_val_t\, std::nothrow_t const&)",
            "operator new(unsigned long\, std::nothrow_t const&)",
            "operator new[](unsigned long)",
            "operator new[](unsigned long\, std::align_val_t)",
            "operator new[](unsigned long\, std::align_val_t\, std::nothrow_t const&)",
            "operator new[](unsigned long\, std::nothrow_t const&)",
            "operator delete(void*)",
            "operator delete(void*\, std::align_val_t)",
            "operator delete(void*\, std::align_val_t\, std::nothrow_t const&)",
            "operator delete(void*\, std::nothrow_t const&)",
            "operator delete(void*\, unsigned long)",
            "operator delete(void*\, unsigned long\, std::align_val_t)",
            "operator delete[](void*)",
            "operator delete[](void*\, std::align_val_t)",
            "operator delete[](void*\, std::align_val_t\, std::nothrow_t const&)",
            "operator delete[](void*\, std::nothrow_t const&)",
            "operator delete[](void*\, unsigned long)",
            "operator delete[](void*\, unsigned long\, std::align_val_t)",
        ]

        # check PRELOAD result #
        libPath = None
        ret = FileAnalyzer.getMapFilePath(pid, "libleaktracer")
        if ret:
            preloaded = True
            SysMgr.printStat(
                "'%s' is already preloaded to %s(%s)" % (ret, comm, pid)
            )
        else:
            preloaded = False
            libPath = SysMgr.getOption("T")
            if libPath:
                # handle special characters in path #
                newPath = UtilMgr.convPath(libPath)
                if not newPath:
                    SysMgr.printErr("wrong path '%s'" % libPath)
                    sys.exit(-1)
                elif len(newPath) > 1:
                    SysMgr.printErr(
                        "found multiple libraries [ %s ]" % ", ".join(newPath)
                    )
                    sys.exit(-1)

                # convert to absolute path #
                libPath = os.path.realpath(newPath[0])

                remoteCmd.append("load:%s" % libPath)

                for item in hookList:
                    hookCmd.append("%s#%s#%s" % (item, libPath, item))

                SysMgr.printStat(
                    "%s is going to be injected automatically" % libPath
                )
            elif (
                not "LD_PRELOAD" in envList
                or not "libleaktracer" in envList["LD_PRELOAD"]
            ):
                SysMgr.printErr(
                    "failed to find libleaktracer.so on memory map "
                    "because the library is not preloaded"
                )
                sys.exit(-1)
            else:
                SysMgr.printErr(
                    "failed to find libleaktracer.so on memory map "
                    "because the library is not preloaded\n"
                    "\tIf the target process is on secure-execution mode,"
                    "\tlibleaktracer.so should be in standard search "
                    "directories specified in /etc/ld.so.conf,n"
                    "\tAnd all slashes in it's preload path will be ignored."
                )
                sys.exit(-1)

        # create a task object #
        SysMgr.ussEnable = True
        tobj = SysMgr.initTaskMon(pid, update=False)
        path = "%s/%s" % (SysMgr.procPath, pid)

        # execute watcher tasks #
        monTasks = []
        if "MEMPROF" in SysMgr.environList:
            interval = UtilMgr.getEnvironNum("MEMPROF", False, 1, False, True)
            output = os.path.join(
                os.path.dirname(SysMgr.outPath), "guider_mem_%s.out" % comm
            )
            ret = SysMgr.launchGuider(
                [
                    "mtop",
                    "-g%s" % pid,
                    "-i%s" % interval,
                    "-o%s" % output,
                    "-qFASTINIT",
                ],
                mute=mute,
                pipe=False,
                stderr=True,
            )
            if ret > 0:
                monTasks.append(ret)
                output = "%s_%s.out" % (UtilMgr.rstrip(output, ".out"), ret)
                SysMgr.printInfo(
                    "start saving the monitoring results of %s(%s) to %s"
                    % (comm, pid, output)
                )

        # set input file path #
        autostart = False
        if "LEAKTRACER_ONSTART_STARTALLTHREAD" in envList:
            autostart = True
            fname = envList["LEAKTRACER_ONSIG_REPORTFILENAME"]
            startTime = SysMgr.uptime - tobj.procData[pid]["runtime"]
        elif "LEAKTRACER_ONSIG_REPORTFILENAME" in envList:
            fname = envList["LEAKTRACER_ONSIG_REPORTFILENAME"]
        else:
            fname = _getOutputPath(isMulti, pid)

            # set output file path #
            if fname:
                remoteCmd.insert(
                    0, "setenv:LEAKTRACER_ONSIG_REPORTFILENAME#%s" % fname
                )

        # make full path #
        if not fname.startswith("/"):
            pwd = SysMgr.getPwd(pid)
            if pwd:
                fname = os.path.join(pwd, fname)

        # backup previous output file already exists #
        SysMgr.backupFile(fname)

        # check signal on platform #
        try:
            signal.signal(LeakAnalyzer.startSig, signal.SIG_IGN)
        except:
            # SIGUSR1 #
            LeakAnalyzer.startSig = 10
        try:
            signal.signal(LeakAnalyzer.stopSig, signal.SIG_IGN)
        except:
            # SIGUSR2 #
            LeakAnalyzer.stopSig = 12

        # set start signal and add an environment variable #
        if not autostart:
            startSig = LeakAnalyzer.startSig
            if not preloaded:
                remoteCmd.insert(
                    0, 'setenv:LEAKTRACER_ONSIG_STARTALLTHREAD#"%s"' % startSig
                )

        # set stop signal and add an environment variable #
        if "LEAKTRACER_ONSIG_REPORT" in envList:
            stopSig = long(envList["LEAKTRACER_ONSIG_REPORT"])
        if not stopSig:
            stopSig = LeakAnalyzer.stopSig
            if not preloaded:
                remoteCmd.insert(
                    0, 'setenv:LEAKTRACER_ONSIG_REPORT#"%s"' % stopSig
                )

        # register signal handler to mark all pages as idle #
        def _markIdlePages(signum, frame):
            SysMgr.printStat(r"start marking all pages as idle...")
            PageAnalyzer.getPageInfo(
                [pid], "anon", markIdle=True, verb=False, progress=True
            )
            LeakAnalyzer.markedIdlePages = True

        signal.signal(signal.SIGQUIT, _markIdlePages)

        # add an init call for tracing #
        if remoteCmd and not libPath:
            remoteCmd.append(
                "usercall:leaktracer::MemoryTrace::init_full_from_once()"
            )

        # init process status #
        if "status" in tobj.procData[pid]:
            tobj.procData[pid]["status"] = {}

        # check signals for preloaded target #
        if not preloaded:
            _checkSignals(tobj, path, pid, startSig, stopSig)

        # check signal handler #
        failCnt = 0
        while 1:
            # apply environment command #
            if remoteCmd:
                SysMgr.printInfo(r"start applying environment commands... ")

                SysMgr.launchGuider(
                    [
                        "remote",
                        "-g%s" % pid,
                        "-c%s" % ",".join(remoteCmd),
                        "-I",
                        "-qNOCONTEXT",
                        "-dL" if not "debug" in SysMgr.environList else "",
                    ],
                    pipe=False,
                    stderr=True,
                    log=True,
                    wait=True,
                )

            try:
                # check library #
                ret = FileAnalyzer.getMapFilePath(pid, "libleaktracer")
                if not ret:
                    SysMgr.printErr(
                        "failed to find '%s' on memory map" % libPath
                    )
                    if failCnt > 3:
                        sys.exit(-1)
                    else:
                        failCnt += 1
                        continue

                # init process status #
                if "status" in tobj.procData[pid]:
                    tobj.procData[pid]["status"] = {}

                # update signal list #
                tobj.saveProcStatusData(path, pid)
                sigList = tobj.procData[pid]["status"]["SigCgt"]

                # check start signal #
                if startSig and not UtilMgr.isBitEnabled(startSig, sigList):
                    SysMgr.printWarn(
                        "failed to find %s(%s) handler for start"
                        % (ConfigMgr.SIG_LIST[startSig], startSig),
                        True,
                    )

                # check stop signal #
                if stopSig and not UtilMgr.isBitEnabled(stopSig, sigList):
                    SysMgr.printWarn(
                        "failed to find %s(%s) handler for stop"
                        % (ConfigMgr.SIG_LIST[stopSig], stopSig),
                        True,
                    )

                break
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr("failed to check signal", reason=True)

        # set hook command #
        if hookCmd:
            hcmd = [
                "hook",
                "-g%s" % pid,
                "-c%s" % ",".join(hookCmd),
                "-I",
                "-qNOCONTEXT",
                "-dL" if not "debug" in SysMgr.environList else "",
            ]
        else:
            hcmd = []

        # wait for START signal #
        if not autostart and startSig and waitSignal:
            try:
                SysMgr.printStat(
                    r"start waiting for input... [ START(Ctrl+c) ]"
                )

                ret = _waitAndKill(
                    tobj, pid, comm, sys.maxsize, startSig, "start"
                )
            except:
                ret = 0

            # check exit condition #
            if ret < 0:
                sys.exit(-1)
            else:
                SysMgr.setSimpleSignal()
                signal.signal(signal.SIGQUIT, _markIdlePages)

        # init condition variables #
        convUnit = UtilMgr.convUnit2Size
        cmd = SysMgr.customCmd
        startSize = endSize = 0

        # START #
        if cmd:

            def _getThreshold(value, rss):
                if value.startswith("+"):
                    return convUnit(value[1:]) + rss
                else:
                    return convUnit(value)

            # get RSS #
            try:
                mlist = SysMgr.getMemStat(pid)
                rssIdx = ConfigMgr.STATM_TYPE.index("RSS")
                rss = long(mlist[rssIdx]) << 12
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printWarn(
                    "failed to get RSS info for %s(%s)" % (comm, pid),
                    True,
                    True,
                )
                rss = 0

            # get threshold size #
            if len(cmd) >= 2:
                startSize = _getThreshold(cmd[0], rss)
                endSize = _getThreshold(cmd[1], rss)
            else:
                endSize = _getThreshold(cmd[0], rss)

            # hook #
            if not startSize and hookCmd:
                startSize = 1

                SysMgr.printInfo(r"start applying hook commands... ")

                SysMgr.launchGuider(
                    hcmd, pipe=False, stderr=True, log=True, wait=True
                )
                hcmd = []

            # wait for start threshold #
            if startSize > 0:
                ret = _waitAndKill(
                    tobj, pid, comm, startSize, startSig, "start", hcmd
                )
                if ret < 0:
                    sys.exit(-1)
            # send signal for start #
            elif not autostart:
                _sendSignal(LeakAnalyzer.startSig, comm, pid, "start")
        elif startSig:
            # hook #
            if hookCmd:
                SysMgr.printInfo(r"start applying hook commands... ")

                SysMgr.launchGuider(
                    hcmd, pipe=False, stderr=True, log=True, wait=True
                )
                hcmd = []

            # send signal for start #
            _sendSignal(startSig, comm, pid, "start")

        # update start time #
        if not startTime:
            SysMgr.updateUptime()
            startTime = SysMgr.uptime

        # set termination timer #
        signal.signal(signal.SIGALRM, SysMgr.onAlarm)
        signal.alarm(SysMgr.intervalEnable)

        # STOP #
        try:
            # set unlimited end size #
            if not endSize:
                endSize = sys.maxsize

            # wait for stop threshold or signal #
            try:
                SysMgr.printStat(
                    r"start monitoring... [ STOP(Ctrl+c), MARK(Ctrl+\) ]"
                )

                ret = _waitAndKill(tobj, pid, comm, endSize, stopSig, "stop")
            # stop profiling #
            except:
                ret = 0

                # send stop signal to target #
                _sendSignal(stopSig, comm, pid, "stop")

            # check exit condition #
            if ret < 0:
                sys.exit(-1)
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                "failed to send %s to stop memory profiling"
                % ConfigMgr.SIG_LIST[stopSig],
                reason=True,
            )
            sys.exit(-1)

        """
        set OOM adjust value of the target process to maximum
        to prevent becoming victim by OOM killer during analysis
        """
        oomAdj = SysMgr.getOOMAdj(pid)
        SysMgr.setOOMAdj(pid, -17)

        # calculate runtime and profile time #
        try:
            SysMgr.updateTaskMon(tobj, pid)
            endTime = SysMgr.uptime
            runtime = UtilMgr.convTime(tobj.procData[pid]["runtime"])
            profileTime = UtilMgr.convTime(endTime - startTime)
        except SystemExit:
            sys.exit(0)
        except:
            runtime = "?"
            profileTime = "?"

        # terminate worker processes #
        if monTasks:
            SysMgr.killChildren(
                sig=signal.SIGINT, children=monTasks, clear=False
            )

        SysMgr.printStat("wait for %s" % fname)

        # wait for the output file to be closed #
        while 1:
            if not SysMgr.isAlive(pid):
                SysMgr.printErr("%s(%s) is terminated" % (comm, pid))
                sys.exit(0)

            tobj.saveFileStat([[pid], []])
            if not fname in tobj.fileData:
                break

            time.sleep(1)
            tobj.reinitStats()

        # set signal handler #
        SysMgr.setNormalSignal()

        # wait for the output file is written #
        while not os.path.exists(fname) or os.stat(fname).st_size == 0:
            time.sleep(1)
            if not SysMgr.isAlive(pid):
                SysMgr.printErr("%s(%s) is terminated" % (comm, pid))
                sys.exit(0)

        try:
            # stop target process #
            if (
                inputCmd and not "KEEPTASK" in SysMgr.environList
            ) or "STOPTARGET" in SysMgr.environList:
                SysMgr.sendSignalProcs(signal.SIGSTOP, [pid])
            # clean up target process #
            elif not "NOCLEANUP" in SysMgr.environList:
                SysMgr.printInfo("clean up %s(%s)" % (comm, pid))

                SysMgr.launchGuider(
                    [
                        "remote",
                        "-g%s" % pid,
                        "-cusercall:leaktracer_stopAllMonitoring",
                    ],
                    log=False,
                    mute=True,
                    pipe=False,
                    stderr=True,
                )

            # get kill flag #
            if inputCmd and not "KEEPTASK" in SysMgr.environList:
                killTask = True
            else:
                killTask = False

            # analyze leakage #
            lt = LeakAnalyzer(fname, pid, killTask, tobj)

            # print logo #
            SysMgr.printLogo(big=True)

            # print resource info to temporary buffer #
            SysMgr().printResourceInfo()

            # print system information #
            SysMgr.printInfoBuffer()

            # print leakage #
            lt.printLeakage(runtime, profileTime, startTime)
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                "failed to analyze memory leakage for %s(%s)" % (comm, pid),
                True,
            )
        finally:
            # recover OOM adjust value #
            SysMgr.setOOMAdj(pid, oomAdj)

        # init repeat count #
        if not LeakAnalyzer.repeatCnt and "REPEAT" in SysMgr.environList:
            LeakAnalyzer.repeatCnt = UtilMgr.getEnvironNum(
                "REPEAT", False, 0, False, True
            )

        # execute after commands #
        SysMgr.runProfCmd("AFTER", {"PID": pid})

        # check repeat count #
        LeakAnalyzer.repeatCnt -= 1
        if LeakAnalyzer.repeatCnt <= 0:
            return

        # -------------------- REPEATATION -------------------- #

        # prepare for repeatation #
        if not SysMgr.filterGroup:
            SysMgr.filterGroup.append(pid)

        # reset default signal handlers #
        SysMgr.setSimpleSignal()
        signal.signal(signal.SIGQUIT, _markIdlePages)

        # close all files #
        SysMgr.closeAllForPrint()

        # set output file suffix #
        SysMgr.fileSuffix = LeakAnalyzer.repeatCnt

        # continue target process #
        SysMgr.sendSignalProcs(signal.SIGCONT, [pid])

        # restart tracing #
        SysMgr.doLeaktrace()

    @staticmethod
    def doNetTest():
        workload = []
        msg = "*" * 4096
        msg = msg.encode()

        def _nettask(val):
            prot = val[0].lower()
            if prot == "tcp":
                tcp = True
                SysMgr.printErr("TCP protocol is not supported yet")
                sys.exit(-1)
            elif prot == "udp":
                tcp = False
            else:
                SysMgr.printErr("%s protocol is not supported" % prot)
                sys.exit(-1)

            gObj = SysMgr.localServObj
            networkObject = NetworkMgr("client", gObj.ip, gObj.port, tcp=tcp)

            # set repeat count #
            repeat = SysMgr.repeatInterval
            if repeat == 0:
                repeat = 1

            # get sleep time #
            if "SLEEP" in SysMgr.environList:
                sleep = UtilMgr.convUnit2Time(
                    SysMgr.environList["SLEEP"][0], True
                )
            else:
                sleep = 0

            SysMgr.printInfo(
                "start sending %s packets to %s:%s\n"
                % (
                    prot,
                    val[1],
                    val[2],
                )
            )

            for seq in xrange(repeat):
                if SysMgr.warnEnable:
                    sys.stdout.write(
                        "[%s] (%s) send a %s packet to %s:%s\n"
                        % (
                            SysMgr.getUptime(),
                            UtilMgr.convNum(seq),
                            prot,
                            val[1],
                            val[2],
                        )
                    )

                networkObject.sendto(msg, val[1], val[2])

                # sleep #
                time.sleep(sleep)

            sys.exit(0)

        SysMgr.setStream()

        # set network config #
        value = SysMgr.getOption("x")
        if not value:
            NetworkMgr.setServerNetwork(None, None)
        else:
            service, ip, port = NetworkMgr.parseAddr(value)
            NetworkMgr.setServerNetwork(ip, port)

        # get tasks #
        try:
            prot = "udp"
            ip = "0.0.0.0"
            port = 55555

            if SysMgr.hasMainArg():
                jobs = SysMgr.getMainArg()
            elif SysMgr.inputParam:
                jobs = SysMgr.inputParam
            else:
                jobs = None

            if jobs:
                for item in UtilMgr.cleanItem(jobs.split(","), False):
                    task = item.split(":")
                    if len(task) == 1:
                        prot = task[0]
                    elif len(task) == 2:
                        prot = task[0]
                        port = long(task[1])
                    elif len(task) == 3:
                        prot = task[0]
                        ip = task[1]
                        port = long(task[2])
                    else:
                        raise Exception("too many arguments")

                    workload.append([prot, ip, port])
            else:
                workload.append([prot, ip, port])
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                (
                    "wrong value for NETWORK load because %s, "
                    "input number in the format PROTOCOL{:IP:PORT}"
                )
                % SysMgr.getErrMsg()
            )
            sys.exit(-1)

        # run tasks #
        ioTasks = {}
        for idx, item in enumerate(workload):
            try:
                pid = SysMgr.createProcess()
                if pid == 0:
                    _nettask(workload[idx])
                else:
                    ioTasks[pid] = workload[idx]
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr("failed to create process", True)
                sys.exit(-1)

        # set alarm #
        try:
            interval = SysMgr.getOption("i")
            if interval:
                interval = long(interval)
                signal.signal(signal.SIGALRM, SysMgr.onAlarm)
                signal.alarm(interval)
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to set alarm to %s" % interval, True)
            sys.exit(-1)

        # wait for childrenren #
        while 1:
            if not ioTasks:
                break
            SysMgr.waitEvent(ignChldSig=False, exit=True)
            SysMgr.updateChildList()
            if SysMgr.isNoChild():
                break

    @staticmethod
    def statvfs(path):
        # load libc #
        if not SysMgr.loadLibcObj():
            return None

        if hasattr(SysMgr.libcObj, "statvfs"):
            func = SysMgr.libcObj.statvfs
        elif hasattr(SysMgr.libcObj, "statfs"):
            func = SysMgr.libcObj.statfs
        else:
            return None

        if SysMgr.statvfsObj:
            ret = func(path.encode(), byref(SysMgr.statvfsObj))
            if ret == 0:
                return SysMgr.statvfsObj
            else:
                return None

        # define statvfs object #
        class struct_statvfs(Structure):
            _fields_ = (
                ("f_bsize", c_ulong),  # filesystem block size
                ("f_frsize", c_ulong),  # fragment size
                ("f_blocks", c_ulong),  # size of fs in f_frsize units
                ("f_bfree", c_ulong),  # free blocks
                ("f_bavail", c_ulong),  # free blocks for unprivileged users
                ("f_files", c_ulong),  # inodes
                ("f_ffree", c_ulong),  # free inodes
                ("f_favail", c_ulong),  # free inodes for unprivileged users
                ("f_fsid", c_ulong),  # filesystem ID
                ("f_flag", c_ulong),  # mount flags
                ("f_namemax", c_ulong),  # maximum filename length
                ("f_type", c_ulong),  # maximum filename length
                ("reserved", c_char * 32),  # reserved
            )

        # define statfs object #
        class struct_statfs(Structure):
            _fields_ = (
                ("f_type", c_ulong),
                ("f_bsize", c_ulong),
                ("f_blocks", c_ulong),
                ("f_bfree", c_ulong),
                ("f_bavail", c_ulong),
                ("f_files", c_ulong),
                ("f_ffree", c_ulong),
                ("f_fsid", c_ulong),
                ("f_namelen", c_ulong),
                ("reserved", c_ulong * 12),
            )

        if hasattr(SysMgr.libcObj, "statvfs"):
            SysMgr.statvfsObj = struct_statvfs()
        elif hasattr(SysMgr.libcObj, "statfs"):
            SysMgr.statvfsObj = struct_statfs()

        ret = func(path.encode(), byref(SysMgr.statvfsObj))
        if ret == 0:
            return SysMgr.statvfsObj
        else:
            return None

    @staticmethod
    def doFadvise():
        # get command #
        if SysMgr.hasMainArg():
            pathList = SysMgr.getMainArgs()
        elif SysMgr.filterGroup:
            pathList = SysMgr.filterGroup
        else:
            SysMgr.printErr("no input file")
            sys.exit(-1)

        for item in pathList:
            try:
                cmd = item.split(":", 4)
                if len(cmd) < 2:
                    raise Exception("wrong command")

                path, advice = cmd[0:2]

                # update advice #
                advice = advice.upper()
                if not advice.startswith("FADV_"):
                    advice = "FADV_" + advice

                # check advice #
                UtilMgr.makeReverseDict(
                    ConfigMgr.FADV_TYPE, ConfigMgr.FADV_TYPE_REVERSE
                )
                if advice in ConfigMgr.FADV_TYPE_REVERSE:
                    advicestr = advice
                    advice = ConfigMgr.FADV_TYPE_REVERSE[advice]
                else:
                    raise Exception("wrong advice")

                # get pos and size #
                pos = size = 0
                if len(cmd) > 2:
                    pos = UtilMgr.convUnit2Size(cmd[2])
                    if len(cmd) == 3:
                        size = UtilMgr.getFileSize(path, string=False) - pos

                if len(cmd) > 3:
                    size = UtilMgr.convUnit2Size(cmd[3])

                if pos or size:
                    offsetstr = " ( +%s from %s )" % (
                        UtilMgr.convNum(size),
                        UtilMgr.convNum(pos),
                    )
                else:
                    offsetstr = " ( %s )" % UtilMgr.getFileSize(path)

                SysMgr.printInfo(
                    "pass advise %s for %s%s" % (advicestr, path, offsetstr)
                )

                # call fadvise #
                SysMgr.fadvise(path, pos=pos, size=size, advice=advice)
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr(
                    "failed to handle '%s', input in the format FILE:ADVICE{:POS:SIZE}"
                    % item,
                    True,
                )
                sys.exit(-1)

    @staticmethod
    def doSync(pathList=[]):
        # get command #
        if pathList:
            pass
        elif SysMgr.hasMainArg():
            pathList = SysMgr.getMainArgs()
        elif SysMgr.filterGroup:
            pathList = SysMgr.filterGroup
        else:
            pathList = None

        # flush caches #
        if pathList:
            for path in pathList:
                try:
                    SysMgr.printInfo(
                        "flush filesystem caches for %s..." % path,
                        suffix=False,
                    )
                    fd = open(path, "r")
                    os.fsync(fd)
                    fd.close()
                    SysMgr.printInfo(
                        " [done]\n", prefix=False, suffix=False, title=False
                    )
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr("failed to sync %s" % path, True)
        else:
            SysMgr.printInfo("flush system filesystem caches...", suffix=False)
            os.sync()
            SysMgr.printInfo(
                " [done]\n", prefix=False, suffix=False, title=False
            )

    @staticmethod
    def doFlush():
        # check root permission #
        SysMgr.checkRootPerm()

        # get command #
        if SysMgr.hasMainArg():
            cmd = SysMgr.getMainArg()
        elif SysMgr.filterGroup:
            cmd = SysMgr.filterGroup
        else:
            cmd = None

        if SysMgr.findOption("i"):
            interval = SysMgr.intervalEnable
        else:
            interval = None

        while 1:
            # flush caches #
            if not cmd:
                if SysMgr.inputParam:
                    pids = SysMgr.getTids(
                        SysMgr.inputParam,
                        isThread=not SysMgr.processEnable,
                        sibling=SysMgr.groupProcEnable,
                    )
                    files = list()
                    for pid in pids:
                        files += FileAnalyzer.getProcFiles(pid)

                    # remove redundant files #
                    files = set(files)

                    # call fadvise for each file #
                    for f in files:
                        SysMgr.printInfo(
                            "flush caches for '%s'..." % f, suffix=False
                        )
                        SysMgr.fadvise(path=f, advice=os.POSIX_FADV_DONTNEED)
                    sys.stdout.write("\n")
                else:
                    SysMgr.dropCaches("3", verb=True)
            elif cmd in ("1", "2", "3"):
                SysMgr.dropCaches(cmd, verb=True)
            else:
                SysMgr.doSync([os.path.realpath(cmd)])

                # call fadvise #
                SysMgr.fadvise(path=cmd, advice=os.POSIX_FADV_DONTNEED)

            if interval is None:
                break
            else:
                time.sleep(interval)

    @staticmethod
    def dropCaches(val, verb=False):
        try:
            cacheList = {
                "1": "page caches",
                "2": "reclaimable slab objects (includes dentries and inodes)",
                "3": "slab objects and page caches",
            }

            dropCachePath = "%s/sys/vm/drop_caches" % SysMgr.procPath

            with open(dropCachePath, "w") as fd:
                if verb:
                    SysMgr.printInfo(
                        "flush %s(%s)... " % (cacheList[val], val),
                        suffix=False,
                    )
                fd.write(val)
                if verb:
                    SysMgr.printInfo("[Done]", prefix=False, title=False)
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn("failed to flush system caches", reason=True)

    @staticmethod
    def doIoTest():
        # snapshot system info including mount #
        SysMgr()

        workload = []

        writeData = b"0" * 4096

        def _getChunk():
            if "RAND" in SysMgr.environList:
                random = SysMgr.getPkg("random")
                return bytes([random.randint(0, 255) for _ in range(10)])
            else:
                return writeData

        def _flushCache(val, verb=False):
            SysMgr.checkRootPerm(verb=False)
            SysMgr.dropCaches(val, verb)

        def _iotask(num, load):
            def _readChunk(fobj, chunk=4096, sync=False, elapsed=False):
                while 1:
                    if elapsed != -1:
                        prev = time.time()

                    ret = os.read(fobj, chunk)

                    if elapsed != -1:
                        diff = time.time() - prev
                        if diff >= elapsed:
                            SysMgr.printWarn(
                                "elapsed %.3f sec for reading %s"
                                % (diff, UtilMgr.convSize2Unit(chunk)),
                                True,
                            )

                    yield ret

            def _writeChunk(fobj, chunk=4096, sync=False, elapsed=False):
                while 1:
                    chunkData = _getChunk()

                    if elapsed != -1:
                        prev = time.time()

                    ret = os.write(fobj, chunkData[:chunk])

                    if sync:
                        os.fsync(fobj)

                    if elapsed != -1:
                        diff = time.time() - prev
                        if diff >= elapsed:
                            SysMgr.printWarn(
                                "elapsed %.3f sec for writing %s"
                                % (diff, UtilMgr.convSize2Unit(chunk)),
                                True,
                            )

                    yield ret

            # set default signal handlers #
            SysMgr.setDefaultSignal()

            # get jobs #
            op = load["op"]
            path = load["path"]
            size = load["size"]

            # set handler #
            if op == "read":
                opFunc = _readChunk
                flag = os.O_RDONLY
            elif op == "write":
                opFunc = _writeChunk
                flag = os.O_WRONLY | os.O_CREAT | os.O_TRUNC
            else:
                SysMgr.printErr("failed to recognize operation %s" % op)
                sys.exit(-1)

            # get progress value #
            printProgress = "PROGRESS" in SysMgr.environList
            printElapsed = UtilMgr.getEnvironNum(
                "ELAPSED", False, -1, False, isFloat=True
            )

            # get data format #
            if "RAND" in SysMgr.environList:
                dataFormat = "random data"
            else:
                dataFormat = "data"

            # set operation #
            if op == "write":
                # set direction #
                direct = "to"

                # convert path list #
                pathList = path.split(",")
                pathList = UtilMgr.cleanItem(pathList)
            elif op == "read":
                # check random read #
                if "RAND" in SysMgr.environList:
                    SysMgr.printErr("random read is not implemented yet")
                    sys.exit(-1)

                # set direction #
                direct = "from"

                # convert path list #
                pathList = UtilMgr.convPath(path, separator=",")
                if not pathList:
                    SysMgr.printErr("failed to recognize '%s'" % path)
                    sys.exit(-1)

            # declare shortcut function #
            conv = UtilMgr.convNum

            for path in pathList:
                # check I/O type #
                target = "file"
                if os.path.isfile(path) or SysMgr.isBlkDev(path):
                    pass
                elif os.path.isdir(path):
                    if op == "write":
                        path = os.path.join(path, "WRTEST")
                    else:
                        target = "dir"
                elif op == "write" and SysMgr.isWritable(path):
                    pass
                else:
                    SysMgr.printErr("failed to access '%s'" % path)
                    return

                # set repeat count #
                repeat = SysMgr.repeatInterval
                if repeat == 0:
                    repeat = 1

                # make file size string #
                fsize = ""
                if target == "file":
                    # get output size #
                    fsize = UtilMgr.getFileSizeStr(path)

                # make load string #
                if size > 0:
                    loadStr = "only %s " % UtilMgr.convSize2Unit(size)
                else:
                    loadStr = ""

                # flush page caches #
                if "CHUNK" in SysMgr.environList:
                    try:
                        chunkVar = SysMgr.environList["CHUNK"][0]
                        chunk = UtilMgr.convUnit2Size(chunkVar)
                    except:
                        SysMgr.printErr(
                            "failed to convert I/O chunk '%s'" % chunkVar, True
                        )
                        sys.exit(-1)
                else:
                    chunk = 4096

                SysMgr.printInfo(
                    "%s %s%s %s '%s%s' %s times\n"
                    % (
                        op,
                        loadStr,
                        dataFormat,
                        direct,
                        path,
                        fsize,
                        conv(repeat),
                    )
                )

                # set sync flag #
                sync = "SYNC" in SysMgr.environList

                # get sleep time #
                if "SLEEP" in SysMgr.environList:
                    sleep = UtilMgr.convUnit2Time(
                        SysMgr.environList["SLEEP"][0], True
                    )
                else:
                    sleep = 0

                # run loop #
                for seq in xrange(repeat):
                    if SysMgr.printEnable:
                        sys.stdout.write(
                            "(%s) %s %s '%s'... "
                            % (
                                conv(seq),
                                op,
                                target,
                                UtilMgr.convColor(path, "CYAN"),
                            )
                        )
                    sys.stdout.flush()

                    # flush page caches #
                    if "DROPCACHE" in SysMgr.environList:
                        _flushCache("3")
                    elif "DROPSLABCACHE" in SysMgr.environList:
                        _flushCache("2")
                    elif "DROPPAGECACHE" in SysMgr.environList:
                        _flushCache("1")

                    # save timestamp #
                    start = time.time()

                    # FILE #
                    if target == "file":
                        try:
                            # flush file pages #
                            if "DROPFILECACHE" in SysMgr.environList:
                                # call fadvise(DONTNEED) #
                                SysMgr.fadvise(path, advice=4)

                            done = 0
                            fd = os.open(path, flag)
                            for piece in opFunc(fd, chunk, sync, printElapsed):
                                # update progress #
                                if isinstance(piece, (int, long)):
                                    done += piece
                                else:
                                    done += len(piece)

                                if printProgress:
                                    UtilMgr.printProgress(done, size)

                                # check stop condition #
                                if not piece:
                                    break
                                elif 0 < size <= done:
                                    break
                            os.close(fd)

                            # sleep #
                            time.sleep(sleep)
                        except SystemExit:
                            sys.exit(0)
                        except:
                            SysMgr.printWarn(
                                "failed to access '%s' for %s" % (path, op),
                                True,
                                True,
                            )
                            break
                    elif target != "dir":
                        continue

                    # DIR #
                    for r, d, f in os.walk(path):
                        for item in f:
                            try:
                                fpath = os.path.join(r, item)
                                if not os.path.isfile(fpath):
                                    continue

                                # flush file pages #
                                if "DROPFILECACHE" in SysMgr.environList:
                                    # call fadvise(DONTNEED) #
                                    SysMgr.fadvise(path, advice=4)

                                # execute io job #
                                fd = os.open(fpath, flag)
                                for piece in opFunc(
                                    fd, chunk, sync, printElapsed
                                ):
                                    if not piece:
                                        break
                                os.close(fd)

                                # sleep #
                                time.sleep(sleep)
                            except SystemExit:
                                sys.exit(0)
                            except:
                                SysMgr.printWarn(
                                    "failed to access '%s' for %s"
                                    % (fpath, op),
                                    True,
                                    True,
                                )

                    elapsed = "%.6f" % (time.time() - start)
                    if SysMgr.printEnable:
                        sys.stdout.write(
                            "[%s]\n" % UtilMgr.convColor(elapsed, "GREEN")
                        )

        # get tasks #
        try:
            # get option #
            if SysMgr.hasMainArg():
                opList = SysMgr.getMainArgs(False)
            elif SysMgr.filterGroup:
                opList = SysMgr.filterGroup
            else:
                opList = None

            # parse option #
            if opList:
                for item in opList:
                    size = 0

                    item = item.split(":")

                    if len(item) == 1:
                        op = "read"
                        path = item[0]
                    elif len(item) == 3:
                        op, path, size = item
                        try:
                            size = UtilMgr.convUnit2Size(size)
                        except SystemExit:
                            sys.exit(0)
                        except:
                            sys.exit(0)
                    else:
                        op, path = item

                    workload.append({"op": op, "path": path, "size": size})
            # read all mount directories #
            elif SysMgr.showAll:
                for path, value in SysMgr.sysInstance.mountInfo.items():
                    if (
                        not path.startswith("/dev/")
                        or "loop" in path
                        or not os.path.exists(value["path"])
                    ):
                        continue

                    if hasattr(os, "statvfs"):
                        stat = os.statvfs(value["path"])
                    else:
                        stat = SysMgr.statvfs(value["path"])

                    size = stat.f_bsize * stat.f_blocks

                    workload.append({"op": "read", "path": path, "size": size})
            # read current directory #
            else:
                # get device ID #
                fstat = os.lstat(".")
                major = str(os.major(fstat.st_dev))
                minor = str(os.minor(fstat.st_dev))

                # get mount point #
                mountPoint = None
                for path, value in SysMgr.sysInstance.mountInfo.items():
                    if value["major"] == major and value["minor"] == minor:
                        mountPoint = value["path"]
                        break

                # just use PWD #
                if not mountPoint:
                    mountPoint = "."

                if not os.path.exists(mountPoint):
                    SysMgr.printErr("failed to access to %s" % mountPoint)
                    sys.exit(-1)

                workload.append({"op": "read", "path": mountPoint, "size": 0})

        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                (
                    "wrong value for I/O load because %s, "
                    "input number in the format OP:PATH"
                )
                % SysMgr.getErrMsg()
            )
            sys.exit(-1)

        # flush caches #
        if "DROPCACHE" in SysMgr.environList:
            _flushCache("3", verb=True)
        elif "DROPSLABCACHE" in SysMgr.environList:
            _flushCache("2", verb=True)
        elif "DROPPAGECACHE" in SysMgr.environList:
            _flushCache("1", verb=True)

        # run tasks #
        ioTasks = {}
        for _ in xrange(1):
            for idx in xrange(len(workload)):
                try:
                    pid = SysMgr.createProcess()
                    if pid == 0:
                        _iotask(idx, workload[idx])
                        sys.exit(0)
                    else:
                        ioTasks[pid] = workload[idx]
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr("failed to create process", True)
                    sys.exit(-1)

        # set alarm #
        try:
            interval = SysMgr.getOption("i")
            if interval:
                interval = long(interval)
                signal.signal(signal.SIGALRM, SysMgr.onAlarm)
                signal.alarm(interval)
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to set alarm to %s" % interval, True)
            sys.exit(-1)

        # wait for childrenren #
        while 1:
            if not ioTasks:
                break
            SysMgr.waitEvent(ignChldSig=False, exit=True)
            SysMgr.updateChildList()
            if SysMgr.isNoChild():
                break

    @staticmethod
    def getPciList(cache=True):
        if cache and SysMgr.pciList:
            return SysMgr.pciList

        try:
            with open("/usr/share/misc/pci.ids") as reader:
                SysMgr.pciList = reader.read().strip().split("\n")
        except:
            try:
                with open("/usr/share/hwdata/pci.ids") as reader:
                    SysMgr.pciList = reader.read().strip().split("\n")
            except:
                SysMgr.pciList = []

        return SysMgr.pciList

    @staticmethod
    def getGpuList():
        gpuNameList = []
        gpuVendorList = []
        gpuDeviceList = []

        # get PCI list #
        pciList = SysMgr.getPciList()
        if not pciList:
            return gpuNameList

        pciList = "\n".join(pciList)

        # get driver list #
        try:
            gpuList = [
                name
                for name in os.listdir("/dev/dri/")
                if name.rstrip("0123456789") == "card"
            ]
        except SystemExit:
            sys.exit(0)
        except:
            return gpuNameList

        for gpu in gpuList:
            try:
                vendor = device = "UNKNOWN"
                commonPath = "/sys/class/drm/" + gpu + "/device/"
                with open(commonPath + "vendor") as reader:
                    vendor = reader.read().split("x")[1].strip()
                with open(commonPath + "device") as reader:
                    device = reader.read().split("x")[1].strip()
            except SystemExit:
                sys.exit(0)
            except:
                pass

            vendorStr = "\n" + vendor + "  "
            deviceStr = "\n\t" + device + "  "

            if vendorStr in pciList:
                rest = pciList.split(vendorStr, 1)[1]
                vendorName = rest.split("\n", 1)[0].strip()
            else:
                vendorName = "UNKNOWN"

            if deviceStr in rest and vendorName != "UNKNOWN":
                rest = rest.split(deviceStr, 1)[1]
                deviceName = rest.split("\n", 1)[0].strip()
            else:
                deviceName = "UNKNOWN"

            gpuNameList.append("%s - %s" % (vendorName, deviceName))
            gpuVendorList.append(vendor)
            gpuDeviceList.append(device)

        return gpuNameList

    @staticmethod
    def getGpuInfo():
        return SysMgr.getGpuInfo_NVIDIA()

    @staticmethod
    def getGpuInfo_NVIDIA():
        def _ConvertSMVer2Cores(major, minor):
            # Returns the number of CUDA cores per multiprocessor for a given
            # Compute Capability version. There is no way to retrieve that via
            # the API, so it needs to be hard-coded. See _ConvertSMVer2Cores
            # in helper_cuda.h in NVIDIA's CUDA Samples.
            return {
                # Tesla
                (1, 0): 8,  # SM 1.0
                (1, 1): 8,  # SM 1.1
                (1, 2): 8,  # SM 1.2
                (1, 3): 8,  # SM 1.3
                # Fermi
                (2, 0): 32,  # SM 2.0: GF100 class
                (2, 1): 48,  # SM 2.1: GF10x class
                # Kepler
                (3, 0): 192,  # SM 3.0: GK10x class
                (3, 2): 192,  # SM 3.2: GK10x class
                (3, 5): 192,  # SM 3.5: GK11x class
                (3, 7): 192,  # SM 3.7: GK21x class
                # Maxwell
                (5, 0): 128,  # SM 5.0: GM10x class
                (5, 2): 128,  # SM 5.2: GM20x class
                (5, 3): 128,  # SM 5.3: GM20x class
                # Pascal
                (6, 0): 64,  # SM 6.0: GP100 class
                (6, 1): 128,  # SM 6.1: GP10x class
                (6, 2): 128,  # SM 6.2: GP10x class
                # Volta
                (7, 0): 64,  # SM 7.0: GV100 class
                (7, 2): 64,  # SM 7.2: GV11b class
                # Turing
                (7, 5): 64,  # SM 7.5: TU10x class
            }.get(
                (major, minor), 64
            )  # unknown architecture

        CUDA_SUCCESS = 0
        CU_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT = 16
        CU_DEVICE_ATTRIBUTE_MAX_THREADS_PER_MULTIPROCESSOR = 39
        CU_DEVICE_ATTRIBUTE_CLOCK_RATE = 13
        CU_DEVICE_ATTRIBUTE_MEMORY_CLOCK_RATE = 36

        if not SysMgr.importPkgItems("ctypes", False):
            return None

        nGpus = c_int()
        name = b" " * 100
        cc_major = c_int()
        cc_minor = c_int()
        cores = c_int()
        threads_per_core = c_int()
        clockrate = c_int()
        freeMem = c_size_t()
        totalMem = c_size_t()

        result = c_int()
        device = c_int()
        context = c_void_p()

        try:
            cuda = SysMgr.loadLib("libcuda.so")
        except:
            return None

        result = cuda.cuInit(0)
        if result != CUDA_SUCCESS:
            return None

        result = cuda.cuDeviceGetCount(byref(nGpus))
        if result != CUDA_SUCCESS:
            return None

        gpuInfo = {}

        for i in xrange(nGpus.value):
            result = cuda.cuDeviceGet(byref(device), i)
            if result != CUDA_SUCCESS:
                return None

            gpuInfo[i] = {}

            if (
                cuda.cuDeviceGetName(c_char_p(name), len(name), device)
                == CUDA_SUCCESS
            ):
                gpuInfo[i]["name"] = name.split(b"\0", 1)[0].decode()

            if (
                cuda.cuDeviceComputeCapability(
                    byref(cc_major), byref(cc_minor), device
                )
                == CUDA_SUCCESS
            ):
                gpuInfo[i]["capa"] = "%d.%d" % (cc_major.value, cc_minor.value)

            if (
                cuda.cuDeviceGetAttribute(
                    byref(cores),
                    CU_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT,
                    device,
                )
                == CUDA_SUCCESS
            ):
                gpuInfo[i]["processor"] = cores.value
                gpuInfo[i]["core"] = cores.value * _ConvertSMVer2Cores(
                    cc_major.value, cc_minor.value
                )

                if (
                    cuda.cuDeviceGetAttribute(
                        byref(threads_per_core),
                        CU_DEVICE_ATTRIBUTE_MAX_THREADS_PER_MULTIPROCESSOR,
                        device,
                    )
                    == CUDA_SUCCESS
                ):
                    gpuInfo[i]["threads"] = (
                        cores.value * threads_per_core.value
                    )

            if (
                cuda.cuDeviceGetAttribute(
                    byref(clockrate), CU_DEVICE_ATTRIBUTE_CLOCK_RATE, device
                )
                == CUDA_SUCCESS
            ):
                gpuInfo[i]["gpuClock(MHz)"] = clockrate.value / 1000.0

            if (
                cuda.cuDeviceGetAttribute(
                    byref(clockrate),
                    CU_DEVICE_ATTRIBUTE_MEMORY_CLOCK_RATE,
                    device,
                )
                == CUDA_SUCCESS
            ):
                gpuInfo[i]["memClock(MHz)"] = clockrate.value / 1000.0

            result = cuda.cuCtxCreate(byref(context), 0, device)
            if result == CUDA_SUCCESS:
                result = cuda.cuMemGetInfo(byref(freeMem), byref(totalMem))
                if result == CUDA_SUCCESS:
                    gpuInfo[i]["totalMem(MB)"] = totalMem.value / 1024**2
                    gpuInfo[i]["freeMem(MB)"] = freeMem.value / 1024**2

                cuda.cuCtxDetach(context)

        return gpuInfo

    @staticmethod
    def doRequest(reqstr=None):
        def _convStr2Data(string, enc=False):
            # define keys #
            skey = "@@@BIN:"
            ekey = "@@@"

            # get pos #
            start = string.find(skey)
            if start < 0:
                return string
            secStart = start + len(skey)
            end = string[secStart:].find(ekey)
            if end < 0:
                return string

            # get real size #
            size = string[secStart : secStart + end]
            rsize = UtilMgr.convUnit2Size(size)

            # allocate byte array #
            data = bytes(rsize)

            return data

        def _convPath2Data(path, enc=False):
            if not path or not isinstance(path, str):
                return path

            # define keys #
            skey = "@@@FILE:"
            ekey = "@@@"

            # get pos #
            start = path.find(skey)
            if start < 0:
                return _convStr2Data(path, enc=enc)
            secStart = start + len(skey)
            end = path[secStart:].find(ekey)
            if end < 0:
                return _convStr2Data(path, enc=enc)

            # get real path #
            rpath = path[secStart : secStart + end]

            # read data #
            with open(rpath, "rb") as fd:
                data = fd.read()

                # encode to base64 #
                if enc:
                    data = UtilMgr.encodeBase64(data)
                else:
                    data = data

                data = (
                    path[:start].encode()
                    + data
                    + path[secStart + end + len(ekey) :].encode()
                )

            return data

        def _convPath2DataJson(obj, enc=False):
            if isinstance(obj, list):
                for idx, item in enumerate(obj):
                    if isinstance(item, list):
                        obj[idx] = _convPath2DataJson(item, enc)
                    elif isinstance(item, dict):
                        obj[idx] = _convPath2DataJson(item, enc)
                    else:
                        obj[idx] = _convPath2Data(item, enc)
            elif isinstance(obj, dict):
                for idx, item in obj.items():
                    if isinstance(item, list):
                        obj[idx] = _convPath2DataJson(item, enc)
                    elif isinstance(item, dict):
                        obj[idx] = _convPath2DataJson(item, enc)
                    else:
                        obj[idx] = _convPath2Data(item, enc)
            else:
                return _convPath2Data(obj, enc)

            return obj

        def _request(
            req, cache, stats, idx, lastReqTime, verb=False, mute=False
        ):
            cmd = None
            arg = None
            json = None

            # get data from cache #
            try:
                (
                    cmd,
                    method,
                    content,
                    arg,
                    timeout,
                    auth,
                    verify,
                    cookies,
                    hdrs,
                    reqstr,
                    files,
                    json,
                ) = cache[req]
            # parse request #
            except:
                timeout = None
                auth = None
                verify = False
                cookies = None
                hdrs = None

                # alias #
                if req.startswith("ALIAS:"):
                    alias, remain = req.split("#", 1)
                    alias = alias.split(":", 1)[1]
                    stats["name"].setdefault(req, alias)
                else:
                    remain = req

                # create a session to improve performance by session reuse #
                try:
                    if not "KEEPALIVE" in SysMgr.environList:
                        raise Exception()
                    obj = requests.Session()
                except SystemExit:
                    sys.exit(0)
                except:
                    obj = requests

                # GET #
                if remain.startswith("GET#"):
                    method = "GET"
                    cmd = obj.get
                    remain = remain[len(method) + 1 :]
                # POST #
                elif remain.startswith("POST#"):
                    method = "POST"
                    cmd = obj.post
                    remain = remain[len(method) + 1 :]
                # default #
                else:
                    method = "GET"
                    cmd = obj.get

                # check method #
                if not cmd:
                    return

                files = []

                # parse options #
                # refer to https://requests.readthedocs.io #
                while 1:
                    if remain.startswith("DATA:") or remain.startswith(
                        "JSONDATA:"
                    ):
                        orig = remain
                        data, remain = remain.split("#", 1)
                        data = data.split(":", 1)[1]

                        # convert string to dictionary #
                        if orig.startswith("JSONDATA:"):
                            json = UtilMgr.convStr2Dict(data, verb=True)
                        else:
                            arg = data
                    elif remain.startswith("DATAFILE:") or remain.startswith(
                        "JSONFILE:"
                    ):
                        try:
                            orig = remain
                            data, remain = remain.split("#", 1)

                            # read data from file #
                            path = "??"
                            path = data.split(":", 1)[1]
                            with open(path, "rb") as fd:
                                data = fd.read()
                                try:
                                    data = data.decode()
                                except:
                                    pass

                            # convert string to dictionary #
                            if orig.startswith("JSONFILE:"):
                                json = UtilMgr.convStr2Dict(data, verb=True)
                            else:
                                arg = data
                        except SystemExit:
                            sys.exit(0)
                        except:
                            SysMgr.printErr(
                                "failed to get data from '%s'" % path,
                                reason=True,
                            )
                            sys.exit(-1)
                    elif remain.startswith("FILE:"):
                        try:
                            orig = remain
                            data, remain = remain.split("#", 1)
                            path = "??"

                            # define name and path #
                            fileInfo = data.split(":")[1:]
                            if len(fileInfo) == 1:
                                name = path = fileInfo[0]
                                addinfo = [name]
                            else:
                                name = fileInfo[0]
                                path = fileInfo[1]
                                addinfo = []

                            # add a file descriptor #
                            fileInfo.append(open(path, "rb"))

                            # complete tuple data #
                            fileArgs = tuple(addinfo + fileInfo[1:])

                            files.append((name, fileArgs))
                        except SystemExit:
                            sys.exit(0)
                        except:
                            SysMgr.printErr(
                                "failed to get data from '%s'" % path,
                                reason=True,
                            )
                            sys.exit(-1)
                    elif remain.startswith("TIMEOUT:"):
                        timestr, remain = remain.split("#", 1)
                        timeout = timestr.split(":", 1)[1].strip()
                        if not timeout.isdigit():
                            SysMgr.printErr(
                                "failed to get timeout(sec) in %s" % timestr
                            )
                            sys.exit(-1)
                        timeout = long(timeout)
                    elif remain.startswith("AUTH:"):
                        authstr, remain = remain.split("#", 1)
                        auth = authstr.split(":", 1)[1].strip()
                        auth = tuple(
                            [item.strip() for item in auth.split(",")]
                        )
                    elif remain.startswith("VERIFY:"):
                        verifystr, remain = remain.split("#", 1)
                        verify = verifystr.split(":", 1)[1].strip().upper()
                        if verify == "TRUE":
                            verify = True
                        elif verify == "FALSE":
                            verify = False
                        else:
                            SysMgr.printErr(
                                "failed to get verify(TRUE/FALSE) value"
                            )
                            sys.exit(-1)
                    elif remain.startswith("COOKIES:"):
                        cookiestr, remain = remain.split("#", 1)
                        cookiestr = cookiestr.split(":", 1)[1].strip()
                        cookielist = cookiestr.split(",")
                        cookies = {}
                        for item in cookielist:
                            data = item.split(":", 1)
                            cookies.setdefault(
                                data[0].strip(), data[1].strip()
                            )
                    elif remain.startswith("HEADERS:"):
                        hdrstr, remain = remain.split("#", 1)
                        hdrstr = hdrstr.split(":", 1)[1].strip()
                        hdrlist = hdrstr.split(",")
                        hdrs = {}
                        for item in hdrlist:
                            data = item.split(":", 1)
                            hdrs.setdefault(data[0].strip(), data[1].strip())
                    else:
                        break

                # check protocol #
                content = remain
                if not content.startswith("http"):
                    SysMgr.printErr(
                        'no protocol such like "http" in %s' % content
                    )

                # convert request #
                reqstr = "%s %s" % (method, content)
                if req in stats["name"]:
                    reqstr = "(%s) %s" % (stats["name"][req], reqstr)
                if arg:
                    reqstr += " DATA:%s" % repr(arg)
                if json:
                    reqstr += " JSON:%s" % repr(json)
                if timeout:
                    reqstr += " TIMEOUT:%s" % timeout
                if auth:
                    reqstr += " AUTH:%s" % str(auth)
                if verify:
                    reqstr += " VERIFY:%s" % verify
                if cookies:
                    reqstr += " COOKIES:%s" % repr(cookies)
                if hdrs:
                    reqstr += " HEADERS:%s" % repr(hdrs)
                reqstr = UtilMgr.convColor(reqstr, "UNDERLINE")

                # convert path to data #
                if json:
                    _convPath2DataJson(json, enc=True)
                if arg:
                    arg = _convPath2Data(arg, enc=True)

                # cache data #
                cache[req] = (
                    cmd,
                    method,
                    content,
                    arg,
                    timeout,
                    auth,
                    verify,
                    cookies,
                    hdrs,
                    reqstr,
                    files,
                    json,
                )

            # convert sequence #
            idx = UtilMgr.convNum(idx)

            # print request #
            if not mute:
                SysMgr.printPipe(
                    "\n%s(%s) <%s> [%.6f] -> %s"
                    % (SysMgr.comm, SysMgr.pid, idx, time.time(), reqstr)
                )

            before = time.time()
            lastReqTime[0] = before

            # request #
            if json:
                res = cmd(
                    content,
                    json=json,
                    timeout=timeout,
                    auth=auth,
                    verify=verify,
                    cookies=cookies,
                    headers=hdrs,
                    files=files,
                )
            elif arg:
                res = cmd(
                    content,
                    arg,
                    timeout=timeout,
                    auth=auth,
                    verify=verify,
                    cookies=cookies,
                    headers=hdrs,
                    files=files,
                )
            else:
                res = cmd(
                    content,
                    timeout=timeout,
                    auth=auth,
                    verify=verify,
                    cookies=cookies,
                    headers=hdrs,
                    files=files,
                )

            # update time #
            after = time.time()
            elapsed = after - before

            # print request #
            if verb:
                data = res.request.hdrs
                data["Body"] = res.request.body
                data = str(data).replace("\\n", "\n")
                data = str(data).replace("\\r", "")
                SysMgr.printWarn(data, True)

            # save statistics #
            stats["perReqTime"].setdefault(req, [])
            stats["perReqTime"][req].append(elapsed)
            stats["perReqTimeAll"][req].append([before, elapsed])

            # check mute option #
            if mute:
                # raise Exception for error #
                if not res.ok:
                    raise Exception(res.reason)
                return

            # convert result #
            if res.ok:
                success = UtilMgr.convColor("OK", "GREEN")
            else:
                success = UtilMgr.convColor(res.reason, "RED")

            # convert code #
            code = UtilMgr.convColor(res.status_code, "SPECIAL")

            # convert text #
            if SysMgr.showAll:
                text = ": %s" % res.text
            else:
                text = ""

            # round elapsd time #
            elapsed = "%.6f" % elapsed

            # print response #
            SysMgr.printPipe(
                "%s(%s) <%s> [%.6f] <- [%s/%s] %s%s"
                % (
                    SysMgr.comm,
                    SysMgr.pid,
                    idx,
                    after,
                    code,
                    UtilMgr.convColor(elapsed, "CYAN"),
                    success,
                    text,
                )
            )

            # raise Exception for error #
            if not res.ok:
                raise Exception(res.reason)

        def _task(reqs, repeat, delay, cache):
            # initialize statistics #
            stats = {
                "perReqTime": {},
                "perReqTimeAll": {},
                "perReqErr": {},
                "perCycleTime": [],
                "name": {},
            }

            convNum = UtilMgr.convNum

            # set mute flag #
            mute = "MUTE" in SysMgr.environList

            # set verb flag #
            verb = "PRINTREQ" in SysMgr.environList

            # save task stat #
            tobj = SysMgr.initTaskMon(SysMgr.pid)

            start = time.time()

            try:
                lastReqTime = [0]
                idx = 1
                for idx in xrange(repeat):
                    before = time.time()

                    for req in reqs:
                        try:
                            stats["perReqTimeAll"].setdefault(req, [])
                            stats["perReqErr"].setdefault(req, 0)

                            # do request #
                            _request(
                                req, cache, stats, idx, lastReqTime, verb, mute
                            )

                            # make a delay #
                            if delay > 0:
                                time.sleep(delay)

                            # print progress #
                            if mute:
                                UtilMgr.printProgress(idx, repeat)
                        except SystemExit:
                            sys.exit(0)
                        except:
                            stats["perReqTimeAll"][req].append(
                                [lastReqTime[0], 0]
                            )
                            stats["perReqErr"][req] += 1
                            SysMgr.printErr(
                                "failed to request '%s'" % req, reason=True
                            )

                            # reset signal and exit flag #
                            SysMgr.setSimpleSignal()

                    elapsed = time.time() - before
                    stats["perCycleTime"].append(elapsed)
            except:
                pass

            totalElapsed = time.time() - start

            # get CPU usage #
            try:
                SysMgr.updateTaskMon(tobj, SysMgr.pid)
                tcpu = SysMgr.getTaskMon(tobj, SysMgr.pid, "ttime")
                acpu = tcpu / totalElapsed
                if tcpu <= totalElapsed * 100:
                    tcpu = convNum(tcpu)
                    acpu = convNum(acpu)
                else:
                    tcpu = acpu = 0
            except SystemExit:
                sys.exit(0)
            except:
                tcpu = "?"
                acpu = "?"

            # make repeat string #
            if repeat in (sys.maxsize, SysMgr.maxSize):
                repeatStr = "INFINITE"
            else:
                repeatStr = convNum(len(reqs) * repeat)

            # list per-request response time #
            if SysMgr.outPath:
                SysMgr.printPipe(
                    (
                        "\n[Response Time] [Task: %s(%s)] [Elapsed: %.6f] "
                        "[NrReq: %s] [ReqCnt: %s/%s] [Delay: %s] "
                        "[TotalCPU: %s%%] [AvgCPU: %s%%]\n%s"
                    )
                    % (
                        SysMgr.comm,
                        SysMgr.pid,
                        totalElapsed,
                        convNum(len(reqs)),
                        convNum(idx + 1),
                        repeatStr,
                        delay,
                        tcpu,
                        acpu,
                        twoLine,
                    )
                )

                SysMgr.printPipe(
                    "{0:^50} | {1:^80}\n{2:1}".format(
                        "Request", "Response Times", oneLine
                    )
                )

                for ridx, value in stats["perReqTimeAll"].items():
                    interval = ", ".join(
                        list(map(lambda x: "%s/%s" % (x[0], x[1]), value))
                    )

                    SysMgr.printPipe(
                        "{0:>20} | {1:1}\n{2:1}".format(
                            ridx, interval, oneLine
                        )
                    )

            # summarize per-request response time #
            SysMgr.printPipe(
                (
                    "\n[Response Summary] [Task: %s(%s)] [Elapsed: %.6f] "
                    "[NrReq: %s] [ReqCnt: %s/%s] [Delay: %s] "
                    "[TotalCPU: %s%%] [AvgCPU: %s%%]\n%s"
                )
                % (
                    SysMgr.comm,
                    SysMgr.pid,
                    totalElapsed,
                    convNum(len(reqs)),
                    convNum(idx + 1),
                    repeatStr,
                    delay,
                    tcpu,
                    acpu,
                    twoLine,
                )
            )

            SysMgr.printPipe(
                (
                    "{0:^7} | {1:^7} | {2:^7} | {3:^10} | {4:^10} | "
                    "{5:^10} | {6:^7} | {7:1} \n{8:1}"
                ).format(
                    "Cnt",
                    "Elapsed",
                    "Avg",
                    "Min",
                    "Max",
                    "Std",
                    "Err",
                    "Request",
                    oneLine,
                )
            )

            for idx, value in stats["perReqTime"].items():
                cnt = len(value)
                totval = sum(value)
                avgval = totval / len(value)
                minval = min(value)
                maxval = max(value)
                stdval = UtilMgr.getStdev(value)

                # get error #
                if idx in stats["perReqErr"]:
                    errcnt = stats["perReqErr"][idx]
                    cnt += errcnt
                    err = convNum(errcnt)
                    if errcnt > 0:
                        err = UtilMgr.convColor(err, "RED", 7)
                else:
                    err = 0

                # get count #
                cnt = convNum(cnt)

                # apply alias #
                if idx in stats["name"]:
                    name = "(%s)%s" % (
                        stats["name"][idx],
                        idx.split("#", 1)[1],
                    )
                else:
                    name = idx

                SysMgr.printPipe(
                    (
                        "{0:>7} | {1:>7.3f} | {2:>7.3f} | {3:>10.6f} | "
                        "{4:>10.6f} | {5:>10.6f} | {6:>7} | {7:1}"
                    ).format(
                        cnt, totval, avgval, minval, maxval, stdval, err, name
                    )
                )

            # print only errors #
            for idx, value in stats["perReqErr"].items():
                if not idx in stats["perReqTime"]:
                    err = UtilMgr.convColor(value, "RED", 7)
                    SysMgr.printPipe(
                        (
                            "{0:>7} | {1:>7.3f} | {2:>7.3f} | {3:>10.6f} | "
                            "{4:>10.6f} | {5:>10.6f} | {6:>7} | {7:1}"
                        ).format(value, 0, 0, 0, 0, 0, err, idx)
                    )

            SysMgr.printPipe(oneLine)

        def _runCLIMode():
            def __printMenu():
                sys.stdout.write(
                    "\n<Command List>\n"
                    "- HISTORY\n"
                    "- PING\n"
                    "- REPEAT\n"
                    "- QUIT\n"
                    "\n"
                )

            def __printHistory(hlist):
                print("\n<History>")
                if hlist:
                    for idx, cmd in enumerate(hlist):
                        print(cmd)
                        print(
                            "[%0d] %s"
                            % (idx, UtilMgr.convColor(cmd, "UNDERLINE"))
                        )
                    print('input "! + index" to execute the above commands')
                else:
                    print("no history")

            def __getUserInput():
                __printMenu()
                sys.stdout.write("input command for request...\n=> ")
                sys.stdout.flush()

                return sys.stdin.readline()[:-1]

            def __doPing(uinput):
                pass

            def __convUserCmd(uinput):
                uinputUpper = uinput.upper()
                if uinputUpper == "H":
                    uinput = "history"
                elif uinputUpper == "P":
                    uinput = "ping"
                elif uinputUpper == "Q":
                    uinput = "quit"
                elif uinputUpper.startswith("R:"):
                    uinput = "repeat" + uinput[1:]

                return uinput

            def __setRepeat(uinput):
                try:
                    cmd = "repeat:"
                    count = UtilMgr.lstrip(uinput, [cmd, cmd.upper()])

                    # set repeat count #
                    SysMgr.repeatCnt = long(count)
                    if SysMgr.repeatCnt == 0:
                        SysMgr.repeatCnt = sys.maxsize

                    SysMgr.printInfo(
                        "set repeat count to %s"
                        % UtilMgr.convNum(SysMgr.repeatCnt)
                    )
                except:
                    SysMgr.printErr("failed to set repeat count to '%s'", True)

            # run mainloop for user interaction #
            hlist = []
            while 1:
                try:
                    isHistory = False

                    # get input #
                    uinput = __getUserInput()

                    # check input for history #
                    if uinput.startswith("!") and len(uinput) > 1:
                        try:
                            uinput = hlist[long(uinput[1:])]
                            isHistory = True
                        except:
                            SysMgr.printErr(
                                "wrong history index for '%s'" % uinput[1:]
                            )
                            continue

                    # convert command shortcut #
                    uinput = __convUserCmd(uinput)
                    uinputUpper = uinput.upper()

                    # handle local command #
                    if not uinput or uinput == "!" or uinputUpper == "HISTORY":
                        __printHistory(hlist)
                        continue
                    elif uinputUpper.startswith("PING"):
                        __doPing(uinput)
                        continue
                    elif uinputUpper == "QUIT":
                        break
                    elif uinputUpper.startswith("REPEAT"):
                        __setRepeat(uinput)
                        continue

                    # backup command #
                    if not isHistory and (not hlist or hlist[-1] != uinput):
                        hlist.append(uinput)

                    # request #
                    SysMgr.doRequest(uinput)
                except SystemExit:
                    return
                except:
                    SysMgr.printErr("failed to request URL", True)
                finally:
                    # reset signal and exit flag #
                    SysMgr.setSimpleSignal()

        # import package #
        requests = SysMgr.getPkg("requests")

        # add ssl variables for TLS 1.0 #
        try:
            urllib3 = requests.packages.urllib3
            urllib3.util.ssl_.DEFAULT_CIPHERS += ":DES-CBC3-SHA"
            urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
        except:
            pass

        # get requests #
        if reqstr:
            pass
        elif SysMgr.hasMainArg():
            reqstr = SysMgr.getMainArg()
        elif SysMgr.inputParam:
            reqstr = SysMgr.inputParam
        else:
            _runCLIMode()
            sys.exit(0)

        # split requests #
        reqs = reqstr.split("|")

        # get repeat count #
        if SysMgr.repeatCnt > 1:
            repeat = SysMgr.repeatCnt
            delay = SysMgr.intervalEnable / 1000.0
        elif SysMgr.intervalEnable > 0:
            repeat = SysMgr.intervalEnable
            delay = 0
        else:
            repeat = 1
            delay = 0

        # define cache list #
        cache = {}

        # set log for delay #
        if delay > 0:
            delayStr = " with %s second delay" % delay
        else:
            delayStr = ""

        # set log for processes #
        if SysMgr.utilProc > 1:
            procStr = " by %s processes" % SysMgr.utilProc
        else:
            procStr = ""

        # make repeat string #
        if repeat in (sys.maxsize, SysMgr.maxSize):
            repeatStr = "INFINITE"
        else:
            repeatStr = UtilMgr.convNum(repeat)

        SysMgr.printInfo(
            "request %s times%s%s" % (repeatStr, delayStr, procStr)
        )

        start = time.time()

        try:
            # process #
            if SysMgr.utilProc > 1:
                if SysMgr.isLinux:
                    for idx in xrange(1, SysMgr.utilProc + 1):
                        # create a new worker #
                        pid = SysMgr.createProcess()
                        if pid == 0:
                            _task(reqs, repeat, delay, cache)

                            sys.exit(0)

                    # wait for childrenren #
                    SysMgr.waitChild()
                else:
                    SysMgr.spawnProcess(
                        _task, (reqs, repeat, delay, cache), SysMgr.utilProc
                    )
            else:
                _task(reqs, repeat, delay, cache)
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to request", reason=True)
            sys.exit(-1)

        # print elapsed time #
        elapsed = time.time() - start
        SysMgr.printInfo("finished all requests for %.6f sec" % elapsed)

    @staticmethod
    def doExec():
        def _customxrange(start, end, step):
            r = start
            while r < end:
                yield r
                r += step

        def _exeCmd(cmd, seq=1, repeat=1):
            # get subprocess object #
            subprocess = SysMgr.getPkg("subprocess")

            # get repeatation progress #
            if repeat > 1:
                repeatStr = " (%s/%s)" % (
                    UtilMgr.convNum(seq + 1),
                    UtilMgr.convNum(repeat),
                )
            else:
                repeatStr = ""

            SysMgr.printInfo("executed '%s'%s" % (cmd, repeatStr))

            # get environment variables #
            env = SysMgr.getEnvList()

            startTime = time.time()

            # set mute #
            if "MUTE" in SysMgr.environList:
                SysMgr.closeStdFd(stderr=False)

            # redirect stdin to file #
            indata = None
            if "STDIN" in SysMgr.environList:
                path = SysMgr.environList["STDIN"][0]
                stdin = open(path, "rb")
            elif "STDINDATA" in SysMgr.environList:
                try:
                    indata = SysMgr.environList["STDINDATA"][0]
                    indata = indata.encode().decode("unicode_escape").encode()
                    stdin = subprocess.PIPE
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr("failed to encode '%s'" % indata, True)
                    indata = stdin = None
            else:
                stdin = None

            # redirect stdout to file #
            if "STDOUT" in SysMgr.environList:
                path = SysMgr.environList["STDOUT"][0]
                stdout = open(path, "wb")
            else:
                stdout = None

            # redirect stderr to file #
            if "STDERR" in SysMgr.environList:
                path = SysMgr.environList["STDERR"][0]
                stderr = open(path, "wb")
            else:
                stderr = None

            # apply LDD #
            if "LDD" in SysMgr.environList:
                # get RTLD path #
                rtld = FileAnalyzer.getMapFilePath(SysMgr.pid, "ld-")
                if not rtld:
                    SysMgr.printErr("failed to get RTLD path")
                    sys.exit(-1)

                # add RTLD path to cmdline #
                cmd = UtilMgr.cleanItem(cmd.split(), False)
                cmd[0] = os.path.realpath(cmd[0])
                cmd.insert(0, rtld)

                # set LD environment variables #
                env["LD_TRACE_LOADED_OBJECTS"] = "1"
                env["LD_WARN"] = "1"
                env["LD_BIND_NOW"] = "1"
                env["LD_VERBOSE"] = "1"
                env["LD_DEBUG"] = "all"

                # execute ld #
                os.execvpe(cmd[0], cmd, env)

            # check shell mode #
            if "NOSHELL" in SysMgr.environList:
                shell = False
                cmd = cmd.split(" ")
            else:
                shell = True

            # create a new worker process #
            procObj = subprocess.Popen(
                cmd,
                shell=shell,
                bufsize=0,
                env=env,
                stdin=stdin,
                stdout=stdout,
                stderr=stderr,
            )

            if indata:
                procObj.stdin.write(indata)
                # procObj.stdin.close()

            # set priority #
            if "EXECSCHED" in SysMgr.environList:
                for cmd in SysMgr.environList["EXECSCHED"]:
                    SysMgr.applyPriority("%s:%s" % (cmd, procObj.pid))

            # wait for worker process #
            try:
                procObj.wait()
            except SystemExit:
                sys.exit(0)
            except:
                duration = time.time() - startTime

                SysMgr.printErr(
                    "failed to wait termination for '%s'" % cmd, True
                )

                sys.exit(-1)

            # kill subprocess group #
            SysMgr.killProcGroup(procObj.pid)

            # get duration time #
            duration = time.time() - startTime

            SysMgr.printInfo(
                "terminated '%s' and elapsed %.6f sec"
                % (cmd, round(duration, 6))
            )

        def _iterVarCmd(cmd, var, seq=1, repeat=1):
            if not var:
                _exeCmd(cmd, seq, repeat)
                return

            # pop a variable #
            cmdset = var.pop(0)

            # process iteration #
            item = cmdset.split(":", 1)
            if len(item) != 2:
                # error #
                SysMgr.printErr("wrong variable '%s'" % cmdset)
                sys.exit(-1)

            # get key and value #
            key, val = item

            # range #
            if key == "RANGE":
                val = val.split(":")
                if len(val) != 3:
                    # error #
                    SysMgr.printErr("wrong variable '%s'" % cmdset)
                    sys.exit(-1)

                # split variables #
                start, end, step = val

                # convert type #
                if "." in step:
                    step = float(step)
                    start = float(start)
                    end = float(end)
                else:
                    step = long(step)
                    start = long(start)
                    end = long(end)

                # loop in range #
                ilist = [
                    round(num, 6) if isinstance(num, float) else num
                    for num in _customxrange(start, end, step)
                ]
                ilist = list(map(str, ilist))
            # file #
            elif key == "FILE":
                ilist = UtilMgr.getFileList([val], exceptDir=True)
            # process PID #
            elif key == "PID":
                ilist = SysMgr.getTids(
                    val, isThread=False, sibling=SysMgr.groupProcEnable
                )
            # process COMM #
            elif key == "PCOMM":
                ilist = SysMgr.getTids(
                    val, isThread=False, sibling=SysMgr.groupProcEnable
                )
                ilist = SysMgr.getCommList(ilist, isList=True)
            # thread PID #
            elif key == "TID":
                ilist = SysMgr.getTids(
                    val, isThread=True, sibling=SysMgr.groupProcEnable
                )
            # thread COMM #
            elif key == "TCOMM":
                ilist = SysMgr.getTids(
                    val, isThread=True, sibling=SysMgr.groupProcEnable
                )
                ilist = SysMgr.getCommList(ilist, isList=True)
            # error #
            else:
                SysMgr.printErr("wrong variable '%s'" % key)
                sys.exit(-1)

            # no item #
            if not ilist:
                SysMgr.printWarn(
                    "failed to get value of '%s' for %s" % (val, key), True
                )

            # run loop #
            for item in ilist:
                try:
                    tcmd = cmd.replace(key, item)
                    _iterVarCmd(tcmd, list(var), seq, repeat)
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr("failed to execute '%s'" % tcmd, True)
                    sys.exit(-1)

        # get command #
        if SysMgr.hasMainArg():
            cmd = SysMgr.getMainArg()
        elif SysMgr.inputParam:
            cmd = SysMgr.inputParam
        else:
            SysMgr.printErr("no input for command")
            sys.exit(-1)

        # convert command #
        try:
            confData = SysMgr.getConfigItem("threshold")
            cmd = confData["COMMAND"][cmd]
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # convert Guider path #
        if cmd.startswith("GUIDER "):
            exe = " ".join(SysMgr.getExeCmd(SysMgr.pid))
            cmd = UtilMgr.lstrip(cmd, "GUIDER ")
            cmd = "%s %s" % (exe, cmd)

        # get repeat count #
        if SysMgr.intervalEnable:
            repeat = SysMgr.intervalEnable
        else:
            repeat = 1

        # get parallel flag #
        parallel = "PARALLEL" in SysMgr.environList

        # set timeout #
        if "TIMEOUT" in SysMgr.environList:
            timeout = UtilMgr.convUnit2Time(SysMgr.environList["TIMEOUT"][0])

            def __onAlarm(signum, frame):
                children = TaskAnalyzer.getDescendantList(SysMgr.pid)
                SysMgr.killChildren(sig=signal.SIGKILL, children=children)
                SysMgr.onAlarm(signum, frame)

            signal.signal(signal.SIGALRM, __onAlarm)
            signal.alarm(timeout)

        for seq in range(repeat):
            # create a worker process #
            if parallel:
                ret = SysMgr.createProcess()
                if ret:
                    continue

            # convert variables #
            try:
                if SysMgr.customCmd:
                    _iterVarCmd(
                        cmd, UtilMgr.deepcopy(SysMgr.customCmd), seq, repeat
                    )
                else:
                    _exeCmd(cmd, seq, repeat)
            except SystemExit:
                sys.exit(0)
            except:
                if SysMgr.customCmd:
                    errstr = " with %s" % SysMgr.customCmd
                else:
                    errstr = ""
                SysMgr.printErr(
                    "failed to execute '%s'%s" % (cmd, errstr), True
                )
                sys.exit(-1)

            # terminate a worker process #
            if parallel and not ret:
                sys.exit(0)

        # wait for childs #
        if parallel:
            if "TASKMON" in SysMgr.environList:
                # start monitoring #
                SysMgr.runTaskMonitor([""], wait=True)
            else:
                SysMgr.waitChild()

    @staticmethod
    def doGpuTest():
        pass

    @staticmethod
    def doDrawTimeline():
        # get output file name #
        if SysMgr.hasMainArg():
            inputList = SysMgr.getMainArgs()
        elif SysMgr.inputParam:
            inputList = SysMgr.inputParam.split(",")
            inputList = UtilMgr.cleanItem(inputList)
        else:
            SysMgr.printErr("no input for PATH")
            sys.exit(-1)

        # get config path #
        if SysMgr.getOption("C"):
            config = SysMgr.confFileName
        else:
            config = None

        # set custom interval #
        try:
            begin, end = SysMgr.environList["TRIM"][0].split(":")
            begin = begin.strip()
            begin = float(begin) if begin else 0
            end = end.strip()
            end = float(end) if end else 0
        except SystemExit:
            sys.exit(0)
        except:
            begin = end = 0

        # draw files #
        for inputPath in inputList:
            # set output path #
            outputPath = UtilMgr.getDrawOutputPath(inputPath, "timeline")

            # backup #
            SysMgr.backupFile(outputPath)

            # draw timeline #
            SysMgr.drawTimeline(
                inputPath=inputPath,
                outputPath=outputPath,
                configPath=config,
                begin=begin,
                end=end,
            )

    @staticmethod
    def doCpuTest():
        random = SysMgr.getPkg("random")

        def _cputask(idx, load):
            try:
                SysMgr.setDefaultSignal()
            except:
                pass

            # print profile #
            if SysMgr.isLinux:
                tid = SysMgr.syscall("gettid")
            else:
                tid = os.getpid()

            SysMgr.printWarn(
                "started %sth %s(%s)"
                % (UtilMgr.convNum(idx), SysMgr.comm, tid)
            )

            # run loop #
            while 1:
                if load == 0:
                    signal.pause()
                else:
                    before = time.time()
                    sorted([long(random.random()) for i in xrange(1 << 20)])
                    elapsed = time.time() - before
                    SysMgr.printWarn(
                        "%sth %s(%s) took %.6f seconds to finish one job"
                        % (UtilMgr.convNum(idx), SysMgr.comm, tid, elapsed)
                    )

        SysMgr.setStream()

        # get the number of task and load #
        try:
            if SysMgr.hasMainArg():
                value = SysMgr.getMainArgs(union=False, token=":")
            else:
                value = [100 * SysMgr.getNrCore()]

            # parse values #
            if len(value) > 2:
                raise Exception("too many arguments")
            elif len(value) == 2:
                totalLoad, nrTask = list(map(long, value))
                if nrTask == 0:
                    nrTask = 1
                load = totalLoad / nrTask
            else:
                totalLoad = long(value[0])
                nrTask = long(totalLoad / 100)
                modLoad = totalLoad % 100

                if modLoad > 0:
                    nrTask += 1

                load = long(totalLoad / nrTask)

            nrTask = long(nrTask)
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                (
                    "wrong value for CPU load because %s, "
                    "input number in the format LOAD{:NRTASK}"
                )
                % SysMgr.getErrMsg()
            )
            sys.exit(-1)

        # check target type #
        if SysMgr.processEnable:
            if nrTask > 1:
                taskstr = "%s processes" % UtilMgr.convNum(nrTask)
            else:
                taskstr = "a process"
        else:
            if nrTask > 1:
                taskstr = "%s threads" % UtilMgr.convNum(nrTask)
            else:
                taskstr = "a thread"

        # run tasks #
        limitInfo = {}
        try:
            # process #
            if SysMgr.processEnable:
                for idx in xrange(1, nrTask + 1):
                    pid = SysMgr.createProcess()
                    if pid == 0:
                        _cputask(idx, load)
                    else:
                        limitInfo[pid] = load
            # thread #
            else:
                threadObj = SysMgr.getPkg("threading")
                pid = SysMgr.createProcess()
                if pid == 0:
                    for idx in xrange(1, nrTask):
                        tobj = threadObj.Thread(
                            target=_cputask, args=[idx, load]
                        )
                        tobj.daemon = True
                        tobj.start()
                    _cputask(nrTask, load)
                else:
                    limitInfo[pid] = load
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to create %s" % taskstr, True)
            sys.exit(-1)

        SysMgr.printInfo(
            (
                "created %s and limited them to use CPU a total of %d%% "
                "and %d%% respectively"
            )
            % (taskstr, totalLoad, load)
        )

        # set affinity #
        for pid in list(limitInfo):
            for item in SysMgr.affinityFilter:
                SysMgr.setAffinity(item[1], [pid])

        # set signal #
        try:
            # set alarm #
            signal.signal(signal.SIGALRM, SysMgr.onAlarm)
            signal.alarm(SysMgr.intervalEnable)

            # receive SIGCHLD #
            signal.signal(signal.SIGCHLD, signal.SIG_DFL)
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # limit CPU usage of tasks #
        SysMgr.doLimitCpu(limitInfo, verb=False)

    @staticmethod
    def doMemTest():
        def _getMeminfo():
            # save mem info #
            memBuf = SysMgr.getMemInfo()
            if not memBuf:
                return ""

            memData = {}
            for line in memBuf:
                memList = line.split()
                memData[memList[0][:-1]] = long(memList[1])

            conv = UtilMgr.convSize2Unit
            memTotal = conv(memData["MemTotal"] << 10)
            memTotalFloat = float(memData["MemTotal"])
            memFree = conv(memData["MemFree"] << 10)
            memFreePer = (memData["MemFree"] / memTotalFloat) * 100

            try:
                memAvailKb = memData["MemAvailable"]
                memAvail = conv(memAvailKb << 10)
                memAvailPer = (memAvailKb / memTotalFloat) * 100
                memAvailPer = "%.1f%%" % memAvailPer
            except:
                memAvail = memAvailPer = "-"

            memCache = conv(memData["Cached"] << 10)
            memCachePer = (memData["Cached"] / memTotalFloat) * 100
            swapTotalKb = memData["SwapTotal"]
            swapTotal = conv(swapTotalKb << 10)
            swapFreeKb = memData["SwapFree"]
            swapFree = conv(swapFreeKb << 10)
            if swapTotal == "0":
                swapFreePer = 100.0
            else:
                swapFreePer = (swapFreeKb / float(swapTotalKb)) * 100

            memstr = (
                "\n[%9s] MemTotal: %s, MemFree: %s(%.1f%%), "
                "MemAvail: %s(%s), Cached: %s(%.1f%%), "
                "SwapTotal: %s, SwapFree: %s(%.1f%%)"
            ) % (
                "TOTAL",
                memTotal,
                memFree,
                memFreePer,
                memAvail,
                memAvailPer,
                memCache,
                memCachePer,
                swapTotal,
                swapFree,
                swapFreePer,
            )

            return memstr

        def _getVminfo():
            # save mem info #
            vmBuf = SysMgr.getVmstat()
            if not vmBuf:
                return ""

            vmData = {}

            conv = UtilMgr.convSize2Unit
            for line in vmBuf:
                vmList = line.split()
                item = vmList[0]
                if (
                    item.startswith("pgscan_")
                    or item.startswith("pgsteal_")
                    or item.startswith("kswapd_")
                    or item.startswith("compact_")
                    or item.startswith("oom_")
                    or item.startswith("pswin")
                ):
                    vmData[item] = long(vmList[1])

            cnt = 1
            vmstr = "\n[%9s] " % "VMSTAT"
            for vm, item in sorted(vmData.items()):
                vmstr += "%s: %s, " % (vm, conv(item << 12))
                if cnt % 4 == 0 and cnt != len(vmData):
                    vmstr += "\n[%9s] " % "VMSTAT"
                cnt += 1

            return vmstr[:-2]

        def _getZoneinfo():
            # read zone buf #
            memBuf = SysMgr.readProcStat(
                SysMgr.zoneFd, "zoneinfo", SysMgr, "zoneFd"
            )
            if not memBuf:
                return ""

            memData = {}
            conv = UtilMgr.convSize2Unit

            zone = None
            for line in memBuf:
                zl = line.split()
                item = zl[0]
                if item == "Node":
                    zone = "%s-%s" % (zl[1][:-1], zl[3])
                    memData[zone] = {}
                elif item == "pages" and zl[1] == "free":
                    memData[zone]["free"] = long(zl[2])
                elif item in (
                    "min",
                    "low",
                    "high",
                    "spanned",
                    "present",
                    "managed",
                ):
                    memData[zone][item] = long(zl[1])
                elif item == "protection:":
                    values = []
                    for item in zl[1:]:
                        if item.startswith("("):
                            item = item[1:]
                        if item.endswith(",") or item.endswith(")"):
                            item = item[:-1]
                        values.append(item)

                    values = list(map(lambda x: conv(long(x) << 12), values))
                    memData[zone]["protection"] = values
                else:
                    continue

            # define stat list #
            STAT_LIST = [
                "free",
                "min",
                "low",
                "high",
                "managed",
                "present",
                "spanned",
                "protection",
            ]

            zonestr = "\n"
            for zone, items in sorted(memData.items()):
                zonestr += "[%9s] " % zone

                for name in STAT_LIST:
                    val = items[name]

                    if name == "protection":
                        zonestr = "%s%s: (%7s)" % (
                            zonestr,
                            "protection",
                            ", ".join(val),
                        )
                        zonestr = "%s\n" % zonestr
                    else:
                        zonestr = "%s%s:%7s, " % (
                            zonestr,
                            name,
                            conv(val << 12),
                        )

            return zonestr[:-2]

        def _getLMKinfo():
            # save LMK info #
            try:
                memBuf = None
                SysMgr.lmkFd.seek(0)
                memBuf = SysMgr.lmkFd.readline()
            except:
                try:
                    memPath = SysMgr.lmkPath
                    SysMgr.lmkFd = open(memPath, "r")
                    memBuf = SysMgr.lmkFd.readline()
                except:
                    SysMgr.printOpenWarn(memPath)

            if not memBuf:
                return ""

            # threshold list #
            threshold = [
                "FGAPP",
                "VISAPP",
                "SECSER",
                "HIDAPP",
                "CONPRO",
                "EMPAPP",
            ]

            stats = memBuf.split(",")
            if stats:
                stats = list(map(long, stats))

            lmkstr = "\n[%9s] " % "LMK"

            for idx, item in enumerate(stats):
                lmkstr = "%s%s: %s, " % (
                    lmkstr,
                    threshold[idx],
                    UtilMgr.convSize2Unit(item << 12),
                )

            return "%s" % lmkstr[:-2]

        def _allocMemory(size, wrPipe=None, ret=False):
            SysMgr.setDefaultSignal()

            # allocate memory #
            try:
                if "RAND" in SysMgr.environList:
                    chunk = bytearray(
                        SysMgr.getPkg("random").getrandbits(8)
                        for _ in range(size)
                    )
                else:
                    chunk = bytearray(size)

                SysMgr.procBuffer = chunk
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr("failed to allocate memory", True)
                sys.exit(-1)

            if wrPipe:
                os.write(wrPipe, "1".encode())

            if ret:
                return

            SysMgr.waitEvent()

            sys.exit(0)

        def _printUsage(obj, size, alloc=True):
            conv = UtilMgr.convSize2Unit

            # get system stat #
            try:
                memstr = _getMeminfo()
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr("failed to get memory stat", True)
                return

            # get vmstat #
            try:
                vmstr = _getVminfo()
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr("failed to get virtual memory stat", True)
                return

            # get zone stat #
            try:
                zonestr = _getZoneinfo()
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr("failed to get zone memory stat", True)
                return

            # get LMK stat #
            try:
                lmkstr = _getLMKinfo()
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printWarn("failed to get LMK stat", reason=True)

            # update process info #
            obj.saveProcStat()
            procs = obj.procData
            prevProcs = obj.prevProcData

            # get new task #
            newTasks = set(procs) - set(prevProcs)
            if newTasks:
                newstr = "\n[%9s]" % "NEW"
                for pid in sorted(newTasks):
                    comm = procs[pid]["stat"][obj.commIdx].strip("()")
                    mstat = TaskAnalyzer.getMemStr(
                        obj, pid, stats=["rss", "pss", "uss"]
                    )
                    if not mstat and pid in prevProcs:
                        mstat = conv(
                            long(prevProcs[pid]["stat"][obj.rssIdx]) << 12
                        )
                    else:
                        mstat = "??"
                    newstr = "%s %s(%s)[%s], " % (newstr, comm, pid, mstat)
                newstr = newstr[:-2]
            else:
                newstr = ""

            # get die task #
            dieTasks = set(prevProcs) - set(procs)
            if dieTasks:
                diestr = "\n[%9s]" % "DIE"
                for pid in sorted(dieTasks):
                    comm = prevProcs[pid]["stat"][obj.commIdx].strip("()")
                    rss = conv(long(prevProcs[pid]["stat"][obj.rssIdx]) << 12)
                    diestr = "%s %s(%s)[%s], " % (diestr, comm, pid, rss)
                diestr = diestr[:-2]
            else:
                diestr = ""

            allocstr = "\n[%9s] %.3f" % ("TIME", SysMgr.updateUptime())

            # get memory usage #
            for idx, pid in enumerate(SysMgr.childList):
                pid = str(pid)
                statstr = TaskAnalyzer.getMemStr(obj, pid, tok=", ")

                allocstr += "\n[%9s] SIZE(%s), %s" % (
                    "ALLOC(%s)" % idx,
                    conv(size, True),
                    statstr,
                )

            SysMgr.printPipe(
                "%s%s%s%s%s%s%s"
                % (allocstr, memstr, vmstr, zonestr, lmkstr, newstr, diestr),
                pager=False,
            )

        SysMgr.setStream(cut=False)

        # convert time #
        try:
            if SysMgr.hasMainArg():
                value = SysMgr.getMainArgs(token=":")
            else:
                value = ["100M"]

            # parse option #
            if len(value) == 3:
                size, interval, count = value
            elif len(value) == 2:
                size, interval = value
                count = 0
            elif len(value) == 1:
                size = value[0]
                interval = count = 0
            else:
                raise Exception("wrong arguments")

            # get interval #
            cont = False
            try:
                if interval:
                    cont = True
                    interval = UtilMgr.convUnit2Time(interval)
                elif SysMgr.getOption("i"):
                    try:
                        interval = long(SysMgr.getOption("i"))
                    except:
                        SysMgr.printErr("failed to get interval time", True)
                        sys.exit(-1)
                else:
                    interval = 0
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr(
                    "failed to convert %s to interval" % interval, True
                )
                sys.exit(0)

            count = long(count)

            # convert memory size #
            size = UtilMgr.convUnit2Size(size)
            if not size:
                raise Exception("wrong size")
        except SystemExit:
            sys.exit(0)
        except:
            errMsg = (
                "wrong value for memory load because %s, "
                "input number in the format SIZE{:INTERVAL:COUNT}"
            ) % SysMgr.getErrMsg()
            SysMgr.printErr(errMsg)
            sys.exit(-1)

        # set alarm #
        if SysMgr.getOption("R"):
            try:
                timeout = long(SysMgr.getOption("R"))
            except:
                SysMgr.printErr("failed to get timeout value", True)
                sys.exit(-1)

            signal.signal(signal.SIGALRM, SysMgr.onAlarm)
            signal.alarm(timeout)

        # create task object #
        obj = TaskAnalyzer(onlyInstance=True)
        obj.saveProcStat()
        pid = SysMgr.pid
        pidList = []

        if count == 0:
            count = sys.maxsize

        for _ in xrange(count):
            try:
                pid = SysMgr.createProcess()
            except SystemExit:
                pass
            except:
                SysMgr.printErr("failed to start process", True)
                sys.exit(-1)

            # child #
            if pid == 0:
                try:
                    _allocMemory(size)
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr("failed to alloc memory", True)
                    sys.exit(-1)
            # parent #
            elif pid > 0:
                pidList.append(pid)

                # print stats #
                try:
                    _printUsage(obj, size)
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr(
                        "failed to print memory stats", reason=True
                    )

                # no count #
                if count == sys.maxsize and interval == 0:
                    time.sleep(1)
                else:
                    time.sleep(interval)

                if count == sys.maxsize and not cont:
                    break
            # error #
            else:
                sys.exit(-1)

        # set default interval for print #
        if interval == 0:
            interval = 1

        # wait for childrenren #
        if pidList:
            while 1:
                try:
                    _printUsage(obj, size, alloc=False)
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr(
                        "failed to print memory stats", reason=True
                    )

                time.sleep(interval)

    @staticmethod
    def doPstree(targets, title=True, printFunc=None, canRepeat=False):
        # get stream option #
        stream = SysMgr.findOption("Q")

        # set I/O flag #
        SysMgr.blockEnable = "ADDIO" in SysMgr.environList

        maxLine = 0
        if canRepeat and SysMgr.intervalEnable:
            SysMgr.setStream()
            if not stream and not SysMgr.outPath:
                maxLine = SysMgr.ttyRows - 5

        if title:
            SysMgr.printLogo(big=True, onlyFile=True)

        obj = TaskAnalyzer(onlyInstance=True)

        while 1:
            if SysMgr.isLinux:
                obj.saveSystemStat()
            else:
                obj.saveSystemStatGen()

            if maxLine and not stream:
                SysMgr.clearScreen()

            # save timestamp #
            prevTime = time.time()

            # oneshot #
            if not canRepeat or not SysMgr.intervalEnable:
                TaskAnalyzer.printProcTree(
                    obj,
                    title=title,
                    printFunc=printFunc,
                    targets=targets,
                    maxLine=maxLine,
                    newline=True,
                )
                break

            if obj.prevCpuData:
                SysMgr.printEnable = True
            else:
                SysMgr.printEnable = False

            # top #
            TaskAnalyzer.printProcTree(
                obj,
                title=title,
                printFunc=printFunc,
                targets=targets,
                maxLine=maxLine,
                newline=stream,
            )

            # check repeat count #
            SysMgr.checkProgress()

            # reset system status #
            obj.reinitStats()

            # get delayed time #
            delayTime = time.time() - prevTime
            if delayTime > SysMgr.intervalEnable:
                waitTime = 0.000001
            else:
                waitTime = SysMgr.intervalEnable - delayTime

            # wait for next tick #
            if not SysMgr.waitUserInput(waitTime):
                time.sleep(waitTime)

    @staticmethod
    def doMerge():
        outPath = SysMgr.outPath

        # check output file #
        if outPath:
            outPath = os.path.realpath(outPath)
        else:
            SysMgr.printErr("no output path")
            sys.exit(-1)

        SysMgr.setStream()
        SysMgr.setDefaultSignal()

        # check input #
        if SysMgr.hasMainArg():
            args = SysMgr.getMainArgs()
        else:
            SysMgr.printErr("no path for merge")
            sys.exit(-1)

        # get file list #
        targetList = []
        for item in args:
            files = UtilMgr.getFileList([item], sort=True, exceptDir=True)
            targetList += files
        if not targetList:
            SysMgr.printErr("no file for input path")
            sys.exit(-1)

        # check exist file #
        if os.path.exists(outPath):
            SysMgr.backupFile(outPath)

        # merge files #
        with open(outPath, "wb") as fd:
            for f in targetList:
                f = os.path.realpath(f)
                SysMgr.printInfo(
                    "append '%s%s' to '%s%s'"
                    % (
                        f,
                        UtilMgr.getFileSizeStr(f),
                        outPath,
                        UtilMgr.getFileSizeStr(outPath),
                    ),
                    prefix=False,
                )

                with open(f, "rb") as rfd:
                    fd.write(rfd.read())

        SysMgr.printInfo(
            "%s files are merged to %s%s"
            % (
                UtilMgr.convNum(len(targetList)),
                outPath,
                UtilMgr.getFileSizeStr(outPath),
            )
        )

    @staticmethod
    def doSplit():
        SysMgr.setStream()
        SysMgr.setDefaultSignal()

        # check input #
        if SysMgr.hasMainArg():
            args = SysMgr.getMainArgs()
        else:
            SysMgr.printErr("no path for split")
            sys.exit(-1)

        # get file list #
        targetList = []
        for item in args:
            files = UtilMgr.getFileList([item], sort=True, exceptDir=True)
            targetList += files
        if not targetList:
            SysMgr.printErr("no file for input path")
            sys.exit(-1)

        # get direction #
        if "TAIL" in SysMgr.environList:
            which = "END"
        else:
            which = "START"

        # get chunk size #
        chunk = SysMgr.environList.get("CHUNK", [None])[0]

        # get count #
        count = UtilMgr.getEnvironNum("COUNT", False, 0, False, isInt=True)

        # split files #
        for f in targetList:
            SysMgr.printInfo("start splitting '%s'..." % f)

            ret = SysMgr.splitFile(f, chunk, SysMgr.outPath, count, which)

            SysMgr.printInfo("spliited '%s' to below files" % f)
            for idx, nf in enumerate(ret):
                SysMgr.printInfo(
                    "({0:03}) {1:1}{2:1}".format(
                        idx, nf, UtilMgr.getFileSizeStr(nf)
                    ),
                    prefix=False,
                    title=False,
                )

    @staticmethod
    def doCompress():
        SysMgr.printFd = None
        SysMgr.setStream()
        SysMgr.setDefaultSignal()

        # check input #
        if SysMgr.hasMainArg():
            args = SysMgr.getMainArgs()
        else:
            SysMgr.printErr("no path for compression")
            sys.exit(-1)

        # get file list #
        infileList = UtilMgr.getFileList(args)
        if not infileList:
            SysMgr.printErr("no file for the path")
            sys.exit(-1)

        # get compressor type #
        if "BASE64" in SysMgr.environList:
            compType = "base64"
            compressor = os
            extent = ".b64"
        elif "ZIP" in SysMgr.environList:
            compType = "zip"
            compressor = SysMgr.getPkg("zipfile")
            extent = ".zip"
        else:
            compType = "gzip"
            compressor = SysMgr.getPkg("gzip")
            extent = ".gz"

        outList = {}
        origOutPath = SysMgr.outPath
        for infile in infileList:
            # check file #
            if compType != "zip" and not os.path.isfile(infile):
                SysMgr.printErr(
                    "wrong file path '%s' for decompression" % infile
                )
                sys.exit(-1)

            # check output #
            outfile = SysMgr.outPath
            SysMgr.outPath = None
            if outfile:
                # check dir #
                if os.path.isdir(outfile):
                    outfile = os.path.join(outfile, infile + extent)
            elif origOutPath and compType == "zip":
                outfile = origOutPath
            else:
                outfile = infile + extent

            # check final input and output path #
            if infile == outfile:
                SysMgr.printErr(
                    "both input and output are the same as '%s' " % infile
                )
                sys.exit(-1)

            # check exist file #
            if not outList.get(outfile) and os.path.exists(outfile):
                SysMgr.backupFile(outfile)
                newline = False
            else:
                newline = True
            outList[outfile] = 1

            # 1MB chunk size #
            chunkSize = 1 << 20

            # open input file #
            wroteSize = 0
            if os.path.isfile(infile):
                infd = open(infile, "rb")

                # get total file size #
                fileSize = long(os.fstat(infd.fileno()).st_size)
                infileSizeStr = UtilMgr.convSize2Unit(fileSize)
            else:
                fileSize = 0
                infileSizeStr = (
                    SysMgr.printDirs(infile, -1, retVal=True, sizeUnit=True)
                    .get(infile, {})
                    .get("_size_", 0)
                )

            SysMgr.printInfo(
                "start compressing '%s' [%s] to '%s'"
                % (infile, infileSizeStr, outfile),
                prefix=newline,
            )

            # BASE64 #
            if compType == "base64":
                # open output file #
                outfd = open(outfile, "wb")
                os.chmod(outfile, 0o777)

                buf = UtilMgr.encodeBase64(infd.read())
                wroteSize = outfd.write(buf)
            # ZIP #
            elif compType == "zip":
                # open output file #
                outfd = compressor.ZipFile(
                    outfile, "a", compressor.ZIP_DEFLATED
                )
                os.chmod(outfile, 0o777)

                if os.path.isdir(infile):
                    for root, dirs, files in os.walk(infile):
                        for file in files:
                            fpath = os.path.join(root, file)
                            relpath = os.path.relpath(fpath, infile)
                            outfd.write(fpath, relpath)
                else:
                    outfd.write(infile)
            # GZIP #
            else:
                # open output file #
                outfd = compressor.open(outfile, "wb")
                os.chmod(outfile, 0o777)

                while 1:
                    chunk = infd.read(chunkSize)
                    if not chunk:
                        outfd.flush()
                        break

                    outfd.write(chunk)

                    wroteSize += len(chunk)
                    UtilMgr.printProgress(wroteSize, fileSize)

            if wroteSize:
                UtilMgr.deleteProgress()

            # close and sync output file #
            outfd.close()
            outfileSizeStr = UtilMgr.getFileSizeStr(outfile)

            SysMgr.printInfo(
                "finished compressing '%s' [%s] to '%s'%s"
                % (infile, infileSizeStr, outfile, outfileSizeStr),
                prefix=False,
            )

    @staticmethod
    def doDecompress(args=[], ffilter=[], verb=True):
        SysMgr.setStream()
        SysMgr.setDefaultSignal()

        # check input #
        if not args:
            if not SysMgr.hasMainArg():
                SysMgr.printErr("no path for decompression")
                sys.exit(-1)

            args = SysMgr.getMainArgs()

        # get file list #
        infileList = UtilMgr.getFileList(args, exceptDir=True)
        if not infileList:
            SysMgr.printErr("no file for the path")
            sys.exit(-1)

        retList = []
        mkFileNameDir = "MKFILENAMEDIR" in SysMgr.environList

        # save global out path #
        outPath = SysMgr.outPath
        if outPath:
            if os.path.isdir(outPath):
                outDir = outPath
            elif os.path.dirname(outPath):
                outDir = os.path.dirname(outPath)
            else:
                outDir = outPath
        else:
            outDir = None

        for infile in infileList:
            # check file #
            if not os.path.isfile(infile):
                SysMgr.printErr("wrong path '%s' for decompression" % infile)
                sys.exit(-1)

            # base64 #
            isMulti = True
            if "BASE64" in SysMgr.environList:
                compType = "base64"
                extent = ".b64"
                inList = [infile]
                isMulti = False
            # gz + tar.gz #
            elif UtilMgr.isGzipFile(infile):
                compType = "gzip"
                extent = ".gz"
                inList = [infile]
                isMulti = False
            # xz + tar.xz #
            elif UtilMgr.isXzFile(infile):
                compType = "lzma"
                extent = ".xz"
                inList = [infile]
                isMulti = False
            # zip #
            elif UtilMgr.isZipFile(infile) or UtilMgr.isPKZipFile(infile):
                compType = "zip"
                extent = ".zip"
                inList = UtilMgr.getZipFileList(infile)
                inList = sorted([out.filename for out in inList])
            # tar #
            elif UtilMgr.isTarFile(infile):
                compType = "tar"
                extent = ".tar"
                inList = UtilMgr.getTarFileList(infile)
                inList = sorted([out.name for out in inList])
            else:
                extent = compType = ""
                if verb:
                    SysMgr.printErr(
                        "no support compression type for '%s'" % infile
                    )
                continue

            # check MKFILENAMEDIR option #
            if mkFileNameDir:
                subDir = UtilMgr.rstrip(os.path.basename(infile), extent)
            else:
                subDir = ""

            # set output dir #
            outDirFinal = outDir if outDir else os.path.dirname(infile)
            decompPath = os.path.join(outDirFinal, subDir)
            if not decompPath:
                decompPath = "./"

            SysMgr.printInfo(
                "start decompressing '%s'%s to '%s'..."
                % (
                    infile,
                    UtilMgr.getFileSizeStr(infile),
                    decompPath,
                ),
                prefix=True,
            )

            for item in inList:
                # apply filter #
                if ffilter and not UtilMgr.isValidStr(item, ffilter):
                    continue

                # make out path #
                if item.endswith("/"):
                    outfile = item
                    isDir = True
                elif not isMulti and item.endswith(extent):
                    outfile = UtilMgr.rstrip(item, extent)
                    isDir = False
                else:
                    outfile = item
                    isDir = False
                outfile = os.path.join(outDirFinal, subDir, outfile)
                if infile == outfile:
                    if outfile.endswith(extent):
                        outfile = UtilMgr.rstrip(outfile, extent)
                    else:
                        outfile += ".decomp"

                # check exist file #
                if os.path.exists(outfile):
                    # backup file #
                    newFile = SysMgr.backupFile(outfile)

                    # replace input file #
                    if os.path.realpath(outfile) == item:
                        item = newFile

                # 1MB chunk size #
                chunkSize = 1 << 20

                # open input file #
                if compType == "base64":
                    infd = open(item, "rb")
                    fileSize = long(os.fstat(infd.fileno()).st_size)
                elif compType == "gzip":
                    infd = SysMgr.getPkg("gzip").open(item, "rb")
                    fileSize = long(os.fstat(infd.fileno()).st_size)
                elif compType == "lzma":
                    infd = SysMgr.getPkg("lzma").open(item, "r")
                    fileSize = long(os.fstat(infd.fileno()).st_size)
                elif compType == "zip":
                    infd = (
                        SysMgr.getPkg("zipfile")
                        .ZipFile(infile)
                        .open(item, "r")
                    )
                    fileSize = infd._orig_compress_size
                elif compType == "tar":
                    infd = (
                        SysMgr.getPkg("tarfile").open(infile).extractfile(item)
                    )
                    fileSize = (
                        SysMgr.getPkg("tarfile")
                        .open(infile)
                        .getmember(item)
                        .size
                    )
                else:
                    continue

                # get total file size #
                itemSizeStr = UtilMgr.convSize2Unit(fileSize)

                # make dir #
                if isDir:
                    SysMgr.mkdirs(outfile)
                # open file #
                else:
                    dirname = os.path.dirname(outfile)
                    if dirname:
                        SysMgr.mkdirs(dirname)
                    outfd = open(outfile, "wb")
                os.chmod(outfile, 0o777)

                SysMgr.printInfo(
                    "decompress '%s' [%s] to '%s'"
                    % (item, itemSizeStr, outfile),
                    prefix=False,
                    suffix=False,
                )

                if isDir:
                    outSize = UtilMgr.getFileSizeStr(outfile)
                    SysMgr.printInfo(
                        "%s ... [Done]" % outSize, prefix=False, title=False
                    )
                    continue

                wroteSize = 0

                # BASE64 #
                if compType == "base64":
                    buf = UtilMgr.decodeBase64(infd.read())
                    wroteSize = outfd.write(buf)
                # OTHER #
                else:
                    while 1:
                        chunk = infd.read(chunkSize)
                        if not chunk:
                            outfd.flush()
                            break

                        size = outfd.write(chunk)

                        wroteSize += size
                        UtilMgr.printProgress()

                    if wroteSize:
                        UtilMgr.deleteProgress()

                outSize = UtilMgr.getFileSizeStr(outfile)
                SysMgr.printInfo(
                    "%s ... [Done]" % outSize, prefix=False, title=False
                )

                retList.append(outfile)

        return retList

    @staticmethod
    def doPrintSig(target=None):
        SysMgr.printLogo(big=True, onlyFile=True)

        # get pid list #
        pids = []
        isThread = not SysMgr.processEnable
        ret = SysMgr.selectTaskId()
        if ret:
            pids.append(ret)
        elif target:
            pids += SysMgr.getTids(target, isThread=isThread)
        elif SysMgr.hasMainArg():
            items = SysMgr.getMainArgs()
            for item in items:
                pids += SysMgr.getTids(item, isThread=isThread)
        else:
            for item in SysMgr.filterGroup:
                pids += SysMgr.getTids(item, isThread=isThread)

        # check pid #
        if not pids:
            SysMgr.printErr("no target thread")
            sys.exit(-1)

        # initialize task stats #
        tobj = TaskAnalyzer(onlyInstance=True)

        for pid in sorted(pids):
            # get process info #
            proc = "%s(%s)" % (SysMgr.getComm(pid), pid)
            path = "%s/%s" % (SysMgr.procPath, pid)
            tobj.saveProcData(path, pid)
            tobj.saveProcStatusData(path, pid)
            if not tobj.procData[pid]["status"]:
                SysMgr.printWarn(
                    "failed to get signal info for %s" % proc, True
                )
                continue

            # print process name #
            if not SysMgr.jsonEnable:
                SysMgr.printPipe(
                    "\n[Signal Status Info] %s\n%s" % (proc, twoLine)
                )

            # set maximum signal length #
            printed = False
            sigDict = {}
            maxSigLen = max([len(sig) for sig in ConfigMgr.SIG_LIST])

            # get signal info #
            for name, val in tobj.procData[pid]["status"].items():
                if not name.startswith("Sig") or len(name) != 6:
                    continue

                sigList = []
                listLen = len(val) * 4 + 1
                for pos in xrange(1, listLen):
                    if (
                        UtilMgr.isBitEnabled(pos, val)
                        and len(ConfigMgr.SIG_LIST) > pos
                        and ConfigMgr.SIG_LIST[pos] != "NONE"
                    ):
                        sigList.append(ConfigMgr.SIG_LIST[pos])

                # check skip condition #
                if not sigList:
                    continue
                elif SysMgr.jsonEnable:
                    sigDict.setdefault(name, sigList)
                    continue

                # set variables for print #
                printed = True
                maxItemLine = long(SysMgr.ttyCols / (maxSigLen + 2)) - 1
                nameStr = "%s: " % name
                string = nameStr

                # build string for print #
                for idx, sig in enumerate(sigList):
                    if idx and idx % maxItemLine == 0:
                        string = "%s\n%s" % (string, " " * len(nameStr))
                    string = "{0:1} {1:{sz}}".format(
                        string,
                        "{0:{sz}}|".format(sig, sz=maxSigLen),
                        sz=maxSigLen,
                    )

                # print string for signal list #
                SysMgr.printPipe(string)

            if SysMgr.jsonEnable:
                SysMgr.printPipe(
                    UtilMgr.convDict2Str(
                        sigDict, pretty=not SysMgr.streamEnable
                    )
                )
                return
            elif not printed:
                SysMgr.printPipe("    None\n")
            SysMgr.printPipe(oneLine)

    @staticmethod
    def doSystat(isProcess=True):
        SysMgr.printLogo(big=True, onlyFile=True)

        SysMgr.checkRootPerm()

        # enable default attributes #
        SysMgr.showAll = True
        SysMgr.memEnable = True
        SysMgr.cgroupEnable = True
        SysMgr.cmdlineEnable = True
        SysMgr.irqEnable = True
        SysMgr.perfEnable = True
        SysMgr.nsEnable = True
        SysMgr.ttyRows = sys.maxsize
        SysMgr.diskEnable = True
        SysMgr.blockEnable = True
        SysMgr.networkEnable = True
        SysMgr.delayEnable = True

        # initialize perf events #
        SysMgr.initSystemPerfEvents()

        # snapshot system info #
        SysMgr()
        obj = TaskAnalyzer(onlyInstance=True)
        obj.saveSystemStat()

        if SysMgr.intervalEnable:
            time.sleep(SysMgr.intervalEnable)
        else:
            SysMgr.totalEnable = True
            time.sleep(1)

        # save system stat #
        obj.reinitStats()
        obj.saveSystemStat()

        # print stat #
        if SysMgr.jsonEnable:
            # process system stat #
            obj.printSystemUsage()

            # process task stat #
            obj.printTaskUsage()
        else:
            # process system stat #
            obj.printSystemStat()

        SysMgr.printTopStats()

    @staticmethod
    def mergeCommStat(statList, commIdx=None):
        if not commIdx:
            commIdx = ConfigMgr.PROCSTAT_ATTR.index("COMM")

        if statList[commIdx][-1] == ")":
            return statList

        # merge comm parts that splited by space #
        idx = commIdx + 1
        while 1:
            tmpStr = str(statList[idx])
            statList[commIdx] = "%s %s" % (statList[commIdx], tmpStr)
            statList.pop(idx)
            if ")" in tmpStr:
                break

        return statList

    @staticmethod
    def doLimitCpu(limitInfo, isProcess=False, verb=True):
        CLK_PRECISION = 100000
        MAX_BUCKET = CLK_PRECISION / 1000
        SLEEP_SEC = 1 / float(MAX_BUCKET)
        COMM_IDX = ConfigMgr.PROCSTAT_ATTR.index("COMM")
        UTIME_IDX = ConfigMgr.PROCSTAT_ATTR.index("UTIME")
        STIME_IDX = ConfigMgr.PROCSTAT_ATTR.index("STIME")

        elapsed = 0
        nowTime = None
        taskList = {}

        def _openStatFd(tid, isProcess):
            if not SysMgr.isLinux:
                try:
                    return SysMgr.getPkg("psutil").Process(long(tid))
                except SystemExit:
                    sys.exit(0)
                except:
                    return None

            # set stat file path #
            if isProcess:
                statPath = "%s/%s/stat" % (SysMgr.procPath, tid)
            else:
                statPath = "%s/%s/task/%s/stat" % (SysMgr.procPath, tid, tid)

            # return stat fd #
            try:
                return open(statPath, "r")
            except:
                return None

        def _getTaskStat(fd):
            if not SysMgr.isLinux:
                try:
                    stats = fd.cpu_times()
                    usage = stats[0] + stats[1]
                    return (fd.name(), usage * 100)
                except SystemExit:
                    sys.exit(0)
                except:
                    return None

            # read buffer #
            try:
                fd.seek(0)
                statBuf = fd.read()
            except SystemExit:
                sys.exit(0)
            except:
                return None

            # convert string to list #
            statList = statBuf.split()
            if len(statList) + 1 < len(ConfigMgr.PROCSTAT_ATTR):
                SysMgr.printWarn("failed to read stat for the task")
                return None

            # merge comm parts that splited by space #
            statList = SysMgr.mergeCommStat(statList, COMM_IDX)

            comm = statList[COMM_IDX].strip("()")
            cputime = long(statList[UTIME_IDX]) + long(statList[STIME_IDX])
            return (comm, cputime)

        # initialize task list #
        for task, per in limitInfo.items():
            taskList[task] = {
                "per": per,
                "nowTick": 0,
                "prevTick": 0,
                "ticks": 0,
                "running": True,
            }

            if isProcess:
                taskList[task]["group"] = SysMgr.getThreadList(task)
                if not taskList[task]["group"]:
                    SysMgr.printErr(
                        "failed to get thread list of '%s' process" % task
                    )
                    return

                taskList[task]["fd"] = _openStatFd(task, isProcess)
                if not taskList[task]["fd"]:
                    SysMgr.printErr("failed to get stats of %s thread" % task)
                    return
            else:
                taskList[task]["group"] = [int(task)]
                taskList[task]["fd"] = _openStatFd(task, isProcess)
                if not taskList[task]["fd"]:
                    SysMgr.printErr("failed to get stats of %s thread" % task)
                    return

        # set alarm #
        try:
            signal.signal(signal.SIGALRM, SysMgr.onAlarm)
            signal.alarm(SysMgr.intervalEnable)
        except SystemExit:
            sys.exit(0)
        except:
            pass

        try:
            while 1:
                # backup time #
                prevTime = nowTime

                # get current time #
                nowTime = time.time()

                # check task #
                if not taskList:
                    SysMgr.printErr("failed to find task to limit CPU usage")
                    return

                for tid in list(taskList):
                    val = taskList[tid]
                    val["prevTick"] = val["nowTick"]

                    # get current tick #
                    stat = _getTaskStat(val["fd"])
                    if not stat:
                        SysMgr.printErr(
                            "failed to get CPU time of %s thread" % tid
                        )
                        taskList.pop(tid, None)
                    else:
                        val["comm"], val["nowTick"] = stat

                if not prevTime:
                    continue

                for tid, val in taskList.items():
                    # get used tick for interval #
                    diffTick = val["nowTick"] - val["prevTick"]
                    val["ticks"] += diffTick

                # get interval time #
                diffTime = nowTime - prevTime
                elapsed += diffTime
                if elapsed >= 1:
                    elapsed = 0

                    for tid, val in taskList.items():
                        # update thread list in a process #
                        if isProcess:
                            taskList[tid]["group"] = SysMgr.getThreadList(tid)
                            if not taskList[tid]["group"]:
                                continue

                            tasktype = "process"
                        else:
                            tasktype = "thread"

                        if verb:
                            SysMgr.printInfo(
                                (
                                    "try to limit CPU usage for %s(%s) %s"
                                    " to %s%%, it used %s%%"
                                )
                                % (
                                    val["comm"],
                                    tid,
                                    tasktype,
                                    val["per"],
                                    val["ticks"],
                                )
                            )

                        val["ticks"] = 0
                    continue

                for tid, val in taskList.items():
                    limitTick = val["per"] * elapsed

                    # exceed limited tick #
                    if val["ticks"] > limitTick:
                        if val["running"]:
                            for tid in val["group"]:
                                try:
                                    if SysMgr.isLinux:
                                        os.kill(tid, signal.SIGSTOP)
                                    else:
                                        val["fd"].suspend()
                                except SystemExit:
                                    raise Exception("exit")
                                except:
                                    SysMgr.printSigError(tid, "SIGSTOP")
                            val["running"] = False
                    # continue #
                    else:
                        if not val["running"]:
                            val["running"] = True
                            for tid in val["group"]:
                                try:
                                    if SysMgr.isLinux:
                                        os.kill(tid, signal.SIGCONT)
                                    else:
                                        val["fd"].resume()
                                except SystemExit:
                                    raise Exception("exit")
                                except:
                                    SysMgr.printSigError(tid, "SIGCONT")

                time.sleep(SLEEP_SEC)
        except SystemExit:
            pass
        except:
            SysMgr.printWarn("failed to limit CPU for tasks", reason=True)
        finally:
            for task, val in taskList.items():
                for tid in val["group"]:
                    try:
                        if SysMgr.isLinux:
                            os.kill(tid, signal.SIGCONT)
                        else:
                            val["fd"].resume()
                    except SystemExit:
                        pass
                    except:
                        SysMgr.printSigError(tid, "SIGCONT")

    @staticmethod
    def terminateTasks(targetList, sig=ConfigMgr.SIGKILL, group=False):
        if group:
            kill = os.killpg
        else:
            kill = os.kill

        for pid in targetList:
            try:
                pid = long(pid)

                if SysMgr.isLinux:
                    kill(pid, sig)
                else:
                    SysMgr.getPkg("psutil").Process(pid).terminate()
            except:
                SysMgr.printSigError(pid, "SIGKILL")

    @staticmethod
    def convTaskList(
        procList,
        isThread=False,
        exceptMe=False,
        sibling=False,
        inc=False,
        cache=False,
    ):
        if not procList:
            return

        targetList = []

        # get pids #
        for pid in procList:
            taskList = SysMgr.getTids(
                pid, isThread, sibling, False, False, cache
            )
            targetList += taskList

        # remove redundant items #
        finalList = list(set(targetList))

        if exceptMe:
            if SysMgr.pid in finalList:
                finalList.remove(SysMgr.pid)
            elif str(SysMgr.pid) in finalList:
                finalList.remove(str(SysMgr.pid))

        return finalList

    @staticmethod
    def getSigNum(val):
        if val.isdigit():
            return long(val)

        val = val.upper()
        if val in ConfigMgr.SIG_LIST:
            return ConfigMgr.SIG_LIST.index(val)

        val = "SIG%s" % val
        if val in ConfigMgr.SIG_LIST:
            return ConfigMgr.SIG_LIST.index(val)

        return None

    @staticmethod
    def sendSignalArgs(argList, isThread=False):
        sig = signal.SIGQUIT
        if not argList:
            return

        # get signal candidates #
        options = [item for item in argList if item.startswith("-")]

        isFound = False
        for val in options:
            try:
                if not val.startswith("-"):
                    continue

                if not isFound:
                    sig = SysMgr.getSigNum(val[1:])
                    if sig:
                        isFound = True

                del argList[argList.index(val)]
            except SystemExit:
                sys.exit(0)
            except:
                pass

        # check type #
        if not isFound:
            sig = signal.SIGINT

        # check signal #
        if sig is None:
            SysMgr.printErr("failed to recognize signal to be sent")
            return

        # convert target list #
        try:
            argList = UtilMgr.cleanItem(("".join(argList)).split(","))
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # convert items to pid #
        targets = SysMgr.convTaskList(
            argList, isThread=isThread, exceptMe=True
        )
        if targets:
            targetList = targets
        else:
            targetList = argList

        # send signal #
        SysMgr.sendSignalProcs(sig, targetList, isThread=isThread)

    @staticmethod
    def sendSignalProcs(
        nrSig, pidList=[], isThread=False, verb=True, exceptList=[]
    ):
        def _kill(pid, nrSig):
            if isThread:
                return SysMgr.syscall("tkill", pid, nrSig)
            else:
                return os.kill(pid, nrSig)

        SIG_LIST = ConfigMgr.SIG_LIST
        exceptList = list(map(long, exceptList))

        if isThread:
            taskType = "thread"
        else:
            taskType = "process"

        nrProc = 0
        isSent = False
        if pidList and isinstance(pidList, list):
            for pid in pidList:
                try:
                    pid = pid.strip()
                except:
                    pass

                if not pid:
                    continue

                # check pid type #
                try:
                    pid = long(pid)
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr(
                        "failed to find '%s' as a %s" % (pid, taskType)
                    )
                    return

                # skip myself #
                if pid == SysMgr.pid or pid in exceptList:
                    continue

                isSent = True

                # send signal to a process #
                try:
                    # get comm #
                    comm = SysMgr.getComm(pid)

                    _kill(pid, nrSig)

                    if verb:
                        SysMgr.printInfo(
                            "sent %s to %s(%s) %s"
                            % (SIG_LIST[nrSig], comm, pid, taskType)
                        )
                except SystemExit:
                    sys.exit(0)
                except:
                    if nrSig in SIG_LIST:
                        signame = SIG_LIST[nrSig]
                    else:
                        signame = nrSig

                    SysMgr.printSigError(pid, signame, False)

            if isSent:
                return

        # get my comm #
        myComm = SysMgr.getComm(SysMgr.pid)
        if not myComm:
            SysMgr.printErr("failed to get my comm", True)
            sys.exit(-1)

        # get my cmdline #
        myCmdline = SysMgr.getCmdline(SysMgr.pid)
        if myCmdline:
            myCmdline = myCmdline.split()
        else:
            SysMgr.printErr("failed to get my cmdline", True)
            sys.exit(-1)

        # get pid list #
        myPid = str(SysMgr.pid)
        myPpid = str(os.getppid())
        pids = SysMgr.getPidList()

        # handle Guider processes #
        nrProc = 0
        for pid in pids:
            if not pid.isdigit() or pid in (myPid, myPpid):
                continue

            # check comm #
            comm = SysMgr.getComm(pid)
            if not comm or not comm.startswith(myComm):
                continue

            # check cmdline again #
            if myComm != __module__:
                cmdlineList = SysMgr.getCmdline(pid, True)
                if len(cmdlineList) > 2 and myCmdline[:2] != cmdlineList[:2]:
                    continue

            if nrSig == signal.SIGINT:
                waitStatus = False

                # check wait option in cmdline #
                try:
                    cmdList = SysMgr.getCmdline(pid, True)
                    for val in cmdList:
                        if val == "-W":
                            waitStatus = True
                except SystemExit:
                    sys.exit(0)
                except:
                    continue

                # send signal #
                try:
                    pid = long(pid)
                    if pid in exceptList:
                        continue

                    _kill(pid, nrSig)

                    if verb:
                        if SysMgr.checkMode("start") and waitStatus:
                            SysMgr.printInfo(
                                "started %s(%s) to profile" % (comm, pid)
                            )
                        else:
                            SysMgr.printInfo(
                                "sent %s to %s(%s) %s"
                                % (SIG_LIST[nrSig], comm, pid, taskType)
                            )
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printSigError(pid, SIG_LIST[nrSig])
            else:
                try:
                    _kill(long(pid), nrSig)

                    if verb:
                        SysMgr.printInfo(
                            "sent %s to %s(%s) %s"
                            % (SIG_LIST[nrSig], comm, pid, taskType)
                        )
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printSigError(pid, SIG_LIST[nrSig])

            nrProc += 1

        if nrProc == 0 and verb:
            SysMgr.printInfo("no process in the background")

    @staticmethod
    def getThreadList(tid):
        taskPath = "%s/%s/task" % (SysMgr.procPath, tid)

        try:
            return list(map(long, os.listdir(taskPath)))
        except:
            return None

    @staticmethod
    def getDeadlineArgs(value):
        value = value.split("/")
        if len(value) == 3:
            return list(map(long, value))
        elif len(value) == 2:
            value.append(value[-1])
            return list(map(long, value))
        else:
            return [0, 0, 0]

    @staticmethod
    def applyPriority(value):
        if not value:
            SysMgr.printErr(
                (
                    "wrong value '%s' to apply new priority, "
                    "input POLICY:PRIORITY|TIME:TID|COMM in the format"
                )
                % value
            )
            sys.exit(-1)

        SysMgr.checkRootPerm()

        # check process based search option #
        procSearch = "PROCSEARCH" in SysMgr.environList

        # parse sched group #
        schedGroup = value.split(",")
        schedGroup = UtilMgr.cleanItem(schedGroup)

        for item in schedGroup:
            lastIdx = 4
            schedSet = item.split(":")

            # I/O #
            try:
                # check I/O sched #
                ioclass = None
                if schedSet[0] in ConfigMgr.IOSCHED_CLASS:
                    # set class #
                    ioclass = ConfigMgr.IOSCHED_CLASS.index(schedSet[0])
                else:
                    nmClass = "IOPRIO_CLASS_%s" % schedSet[0].upper()
                    if nmClass in ConfigMgr.IOSCHED_CLASS:
                        ioclass = ConfigMgr.IOSCHED_CLASS.index(nmClass)

                # apply I/O sched #
                if ioclass:
                    # set who #
                    if len(schedSet) == 4:
                        if schedSet[1] in ConfigMgr.IOSCHED_TARGET:
                            who = ConfigMgr.IOSCHED_TARGET.index(schedSet[1])
                        else:
                            nmWho = "IOPRIO_WHO_%s" % schedSet[1].upper()
                            who = ConfigMgr.IOSCHED_TARGET.index(nmWho)
                        pri = long(schedSet[2])
                        task = schedSet[3]
                    else:
                        who = 1
                        pri = long(schedSet[1])
                        task = schedSet[2]

                    # get thread list #
                    sibling = SysMgr.groupProcEnable
                    if task == "0":
                        targetList = [SysMgr.pid]
                        SysMgr.ioprio = pri
                    elif procSearch:
                        targetList = []
                        pidList = SysMgr.getTids(task, isThread=False)
                        if sibling:
                            for pid in pidList:
                                siblingList = SysMgr.getThreadList(pid)
                                if siblingList:
                                    targetList += siblingList
                        else:
                            targetList = pidList
                    else:
                        targetList = SysMgr.getTids(task, sibling=sibling)

                    # convert thread list #
                    targetList = list(map(long, list(set(targetList))))
                    if not targetList:
                        SysMgr.printWarn("no thread related to '%s'" % task)

                    # change the I/O scheduling priority for tasks #
                    for task in sorted(targetList):
                        SysMgr.setIoPriority(task, ioclass, pri, who)

                    continue
            except SystemExit:
                sys.exit(0)
            except:
                err = map(str, sys.exc_info()[1].args)
                SysMgr.printErr(
                    (
                        "wrong value '%s' to apply new priority because %s, "
                        "input in the format CLASS:WHO:PRI:TID|COMM"
                    )
                    % (item, " ".join(list(err)))
                )
                sys.exit(-1)

            # CPU #
            try:
                # policy and priority #
                try:
                    pri = long(schedSet[0])
                    policy = "C"

                    # change myself #
                    if len(schedSet) == 1:
                        task = SysMgr.pid
                        SysMgr.prio = pri
                    # change others #
                    else:
                        task = schedSet[1]

                    lastIdx = 3
                except SystemExit:
                    sys.exit(0)
                except:
                    policy = schedSet[0].upper()
                    ConfigMgr.SCHED_POLICY.index(policy)

                    pri = schedSet[1]
                    if UtilMgr.isNumber(pri):
                        pri = long(pri)

                    # change myself #
                    if len(schedSet) == 2:
                        task = SysMgr.pid
                        SysMgr.prio = pri
                    # change others #
                    else:
                        task = schedSet[2]

                # get thread list #
                sibling = SysMgr.groupProcEnable
                if procSearch:
                    targetList = []
                    pidList = SysMgr.getTids(task, isThread=False)
                    if sibling:
                        for pid in pidList:
                            siblingList = SysMgr.getThreadList(pid)
                            if siblingList:
                                targetList += siblingList
                    else:
                        targetList = pidList
                else:
                    targetList = SysMgr.getTids(task, sibling=sibling)

                # convert thread list #
                targetList = list(map(long, list(set(targetList))))
                if not targetList:
                    SysMgr.printWarn("no thread related to '%s'" % task)

                # change the CPU scheduling priority for tasks #
                for task in sorted(targetList):
                    if schedSet[0].upper() == "D":
                        # parse deadline arguments #
                        runtime, deadline, period = SysMgr.getDeadlineArgs(
                            schedSet[1]
                        )

                        # set deadline sched #
                        SysMgr.setDeadlinePriority(
                            task, runtime, deadline, period
                        )
                    else:
                        # set other sched #
                        SysMgr.setPriority(task, policy, pri)

                # change others continually #
                if len(schedSet) == lastIdx:
                    if schedSet[lastIdx - 1] != "CONT":
                        raise Exception("wrong last value")

                    # add sched item to list #
                    SysMgr.schedFilter.append([policy, pri, task])
            except SystemExit:
                sys.exit(0)
            except:
                err = map(str, sys.exc_info()[1].args)
                SysMgr.printErr(
                    (
                        "wrong value '%s' to apply new priority because %s, "
                        "input in the format POLICY:PRIORITY|TIME:TID|COMM"
                    )
                    % (item, " ".join(list(err)))
                )
                sys.exit(-1)

    @staticmethod
    def setDeadlinePriority(pid, runtime, deadline, period):
        if not SysMgr.isLinux:
            return

        # get comm #
        comm = SysMgr.getComm(pid)

        # check kernel version #
        try:
            ver = SysMgr.getKernelVersion().split(".")[0:2]
            ver = float(".".join(ver))
            # check whether kernel version is higher than 3.14 #
            if ver < 3.14:
                SysMgr.printErr(
                    (
                        "failed to set the CPU scheduling priority for %s(%s) "
                        "because kernel version %s is lesser than 3.14"
                    )
                    % (comm, pid, UtilMgr.convFloat2Str(ver))
                )
                return -1
        except SystemExit:
            sys.exit(0)
        except:
            err = sys.exc_info()[1]
            SysMgr.printWarn(
                (
                    "failed to check kernel version because %s "
                    "to set deadline priority"
                )
                % err.args[0],
                True,
            )
            return -1

        # load libc #
        SysMgr.loadLibcObj(exit=True)

        # define struct sched_attr #
        class struct_sched_attr(Structure):
            pass

        struct_sched_attr._slots_ = [
            "size",
            "sched_policy",
            "sched_flags",
            "sched_nice",
            "sched_priority",
            "sched_runtime",
            "sched_deadline",
            "sched_period",
        ]

        struct_sched_attr._fields_ = [
            ("size", c_uint32),
            ("sched_policy", c_uint32),
            ("sched_flags", c_uint64),
            ("sched_nice", c_int32),
            ("sched_priority", c_uint32),
            ("sched_runtime", c_uint64),
            ("sched_deadline", c_uint64),
            ("sched_period", c_uint64),
        ]

        # set parameters #
        sched_attr = struct_sched_attr()
        sched_attr.size = c_uint32(sizeof(sched_attr))
        sched_attr.sched_flags = c_uint64(0)
        sched_attr.sched_nice = c_int32(0)
        sched_attr.sched_priority = c_uint32(0)
        sched_attr.sched_policy = c_uint32(ConfigMgr.SCHED_POLICY.index("D"))

        # set runtime(ns) #
        sched_attr.sched_runtime = c_uint64(runtime)

        # check deadline and period #
        if deadline == period == 0:
            SysMgr.printErr(
                (
                    "failed to set the CPU scheduling priority for %s(%s) "
                    "to runtime(ns)/deadline(ns)/period(ns)[D]"
                )
                % (comm, pid)
            )
            return -1
        elif deadline == 0:
            deadline = period
        elif period == 0:
            period = deadline

        # set period(ns) #
        sched_attr.sched_deadline = c_uint64(deadline)
        sched_attr.sched_period = c_uint64(period)

        # call sched_setattr() to set deadline sched #
        ret = SysMgr.syscall("sys_sched_setattr", pid, pointer(sched_attr), 0)

        # check return value #
        if ret == 0:
            SysMgr.printInfo(
                (
                    "changed the CPU scheduling priority for %s(%s) to "
                    "runtime(%d)/deadline(%d)/period(%d)[D]"
                )
                % (comm, pid, runtime, deadline, period)
            )
        else:
            SysMgr.printErr(
                (
                    "failed to set the CPU scheduling priority for %s(%s) to "
                    "runtime(%d)/deadline(%d)/period(%d)[D]"
                )
                % (comm, pid, runtime, deadline, period)
            )

        return ret

    @staticmethod
    def getEnv(pid, retdict=False):
        path = "%s/%s/environ" % (SysMgr.procPath, pid)

        # open the environ file #
        try:
            with open(path, "r") as fd:
                data = fd.readlines()
                if not data:
                    comm = SysMgr.getComm(pid, True)
                    SysMgr.printWarn(
                        "failed to read environment variables for %s(%s)"
                        % (comm, pid)
                    )
                    return
                elist = data[0].split("\x00")[:-1]
        except SystemExit:
            sys.exit(0)
        except:
            comm = SysMgr.getComm(pid, True)
            SysMgr.printWarn(
                "failed to get environment variables of %s(%s)" % (comm, pid),
                True,
            )
            elist = []

        # convert list to dictionary #
        if retdict:
            dlist = {}
            for item in elist:
                var = item.split("=", 1)
                name = var[0]
                if len(var) == 1:
                    val = ""
                else:
                    val = var[1]
                dlist[name] = val
            return dlist
        else:
            return elist

    @staticmethod
    def getIoPriority(pid=0, who=1, verb=True):
        if not SysMgr.isLinux:
            return

        # get ctypes object #
        SysMgr.importPkgItems("ctypes")

        # define attributes #
        IOPRIO_CLASS_SHIFT = 13

        try:
            # set pid #
            pid = long(pid)
            if pid == 0:
                pid = os.getpid()

            # set group (default: IOPRIO_WHO_PROCESS) #
            if not UtilMgr.isNumber(who):
                who = ConfigMgr.IOSCHED_TARGET.index(who)

            # call syscall #
            ret = SysMgr.syscall("ioprio_get", c_int(who), c_int(pid))
            if ret < 0:
                SysMgr.printErr(
                    "failed to get I/O priority of %s(%s)"
                    % (pid, SysMgr.getComm(pid)),
                    True,
                )
                return None

            # convert data #
            nmClass = ret >> IOPRIO_CLASS_SHIFT
            pri = ret & 0xFF

            return nmClass, pri
        except SystemExit:
            sys.exit(0)
        else:
            SysMgr.printErr(
                "failed to get I/O priority of %s(%s)"
                % (pid, SysMgr.getComm(pid))
            )
            return None

    @staticmethod
    def setIoPriority(pid=0, ioclass=2, pri=0, who=1, verb=True):
        if not SysMgr.isLinux:
            return

        # define attributes #
        IOPRIO_CLASS_SHIFT = 13

        nmWho = nmClass = None

        try:
            # get pri #
            pri = long(pri)

            # set pid #
            pid = long(pid)
            if pid == 0:
                pid = os.getpid()

            # set comm #
            comm = SysMgr.getComm(pid)

            # set group (default: IOPRIO_WHO_PROCESS) #
            if not UtilMgr.isNumber(who):
                who = ConfigMgr.IOSCHED_TARGET.index(who)
            nmWho = ConfigMgr.IOSCHED_TARGET[who]

            # set I/O class (default: IOPRIO_CLASS_BE) #
            if not UtilMgr.isNumber(ioclass):
                ioclass = ConfigMgr.IOSCHED_CLASS.index(ioclass)
            nmClass = ConfigMgr.IOSCHED_CLASS[ioclass]

            # set priority value #
            iopri = ((ioclass) << IOPRIO_CLASS_SHIFT) | pri

            # call syscall #
            ret = SysMgr.syscall(
                "ioprio_set", c_int(who), c_int(pid), c_int(iopri)
            )
            if ret < 0:
                raise Exception("error return")

            if verb:
                SysMgr.printInfo(
                    (
                        "changed the I/O scheduling priority "
                        "for %s(%s) to %s(%s)[%s]"
                    )
                    % (comm, pid, nmClass, pri, nmWho)
                )
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                (
                    "failed to set the I/O schedling priority "
                    "for %s(%s) to %s(%s)[%s]"
                )
                % (comm, pid, ioclass, pri, nmWho),
                True,
            )
            sys.exit(-1)

    @staticmethod
    def setPriority(
        pid, policy, pri, runtime=0, deadline=0, period=0, verb=True
    ):
        if not SysMgr.isLinux:
            return

        try:
            # get args #
            comm = SysMgr.getComm(pid)
            upolicy = policy.upper()
            pri = long(pri)

            # load libc #
            if not SysMgr.loadLibcObj():
                raise Exception("no libc")

            argPolicy = ConfigMgr.SCHED_POLICY.index(upolicy)
            if not SysMgr.guiderObj:
                argPolicy = c_int(argPolicy)

            # set default priority #
            if upolicy in ("C", "I", "B"):
                argPriority = 0
            else:
                argPriority = pri

            # prepare for libc call #
            if not SysMgr.guiderObj:
                argPriority = c_int(argPriority)

            # define object #
            if not SysMgr.guiderObj:
                funcObj = SysMgr.libcObj
            else:
                funcObj = SysMgr.guiderObj

            # define sched_setscheduler function #
            if not hasattr(funcObj, "sched_setscheduler"):
                raise Exception("no sched_setscheduler")
            func = funcObj.sched_setscheduler

            # call sched_setscheduler function #
            ret = func(pid, argPolicy, byref(argPriority))
            if ret != 0:
                policy = upolicy
                emsg = SysMgr.getErrReason()
                raise Exception(emsg)

            # set nice value #
            if upolicy in ("C", "B"):
                # check setpriority function #
                if not hasattr(funcObj, "setpriority"):
                    raise Exception("no setpriority")

                # call setpriority function #
                ret = funcObj.setpriority(0, pid, c_int(pri))
                if ret != 0:
                    policy = upolicy
                    emsg = SysMgr.getErrReason()
                    raise Exception(emsg)

            # print result #
            if verb:
                SysMgr.printInfo(
                    (
                        "changed the CPU scheduling priority "
                        "for %s(%s) to %d[%s]"
                    )
                    % (comm, pid, pri, upolicy)
                )
        except SystemExit:
            sys.exit(0)
        except:
            err = (
                "failed to set the CPU scheduling priority "
                "for %s(%s) to %s[%s]"
            ) % (comm, pid, pri, upolicy)
            SysMgr.printWarn(err, always=True, reason=True)

    @staticmethod
    def setTTYAuto(setRows=True, setCols=True, verb=True):
        if not SysMgr.isSignalAvailable():
            return

        # update current terminal size #
        SysMgr.getTty()

        # decide terminal size #
        if setRows and SysMgr.ttyRows < 24:
            SysMgr.ttyRows = 24
        if setCols and SysMgr.ttyCols <= len(oneLine):
            SysMgr.ttyCols = len(oneLine) + 1

        # set terminal size #
        SysMgr.setTTY(SysMgr.ttyRows, SysMgr.ttyCols, verb)

        # update current terminal size #
        SysMgr.getTty()

    @staticmethod
    def disableBuffer(fd=None):
        if not SysMgr.isLinux:
            return

        if not fd:
            fd = sys.stdin.fileno()

        try:
            termios = SysMgr.getPkg("termios", False)
            attr = termios.tcgetattr(fd)
            attr[3] = attr[3] & ~termios.ECHO
            termios.tcsetattr(fd, termios.TCSANOW, attr)
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                "failed to remove buffer for fd %s" % fd, reason=True
            )

    @staticmethod
    def hasStty():
        if SysMgr.sttyEnable:
            return True
        elif SysMgr.sttyEnable is False:
            return False

        if UtilMgr.which("stty"):
            SysMgr.sttyEnable = True
        else:
            SysMgr.sttyEnable = False

        return SysMgr.sttyEnable

    @staticmethod
    def saveTTY():
        if not SysMgr.isLinux:
            return
        elif not SysMgr.hasStty():
            return

        # read current setting #
        try:
            subprocess = SysMgr.getPkg("subprocess", False)
            pd = subprocess.Popen(["stty", "-g"], stdout=subprocess.PIPE)
            SysMgr.ttyData = pd.stdout.read().strip()
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn("failed to get terminal info", reason=True)

    @staticmethod
    def resetTTY():
        if not SysMgr.isLinux:
            return
        elif not SysMgr.hasStty():
            return
        elif not SysMgr.ttyData:
            return

        # reset terminal #
        try:
            SysMgr.getPkg("subprocess").Popen(
                ["stty", "sane"],
                stdout=open(os.devnull, "wb"),
                stderr=open(os.devnull, "wb"),
            )
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # restore terminal #
        try:
            SysMgr.getPkg("subprocess").Popen(
                ["stty", SysMgr.ttyData],
                stdout=open(os.devnull, "wb"),
                stderr=open(os.devnull, "wb"),
            )
        except SystemExit:
            sys.exit(0)
        except:
            pass

    @staticmethod
    def setTTY(rows, cols, verb=True):
        if not SysMgr.isSignalAvailable() or SysMgr.parentPid > 0:
            return

        # set terminal size by ioctl #
        try:
            if not SysMgr.termSetId:
                termios = SysMgr.getPkg("termios", False)
                SysMgr.termSetId = getattr(termios, "TIOCSWINSZ", -2146929561)

            # get fcntl object #
            fcntlObj = SysMgr.getPkg("fcntl", False)

            # set terminal width size #
            fcntlObj.ioctl(
                sys.stdout.fileno(),
                SysMgr.termSetId,
                struct.pack("HHHH", rows, cols, 0, 0),
            )

            # update current terminal size #
            SysMgr.getTty()

            if not verb:
                return

            SysMgr.printInfo(
                "set terminal size [ %sx%s ]"
                % (SysMgr.ttyRows, SysMgr.ttyCols)
            )

            return
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # set terminal size by command #
        try:
            if not SysMgr.hasStty():
                return

            os.system("stty rows %d 2> %s" % (long(rows), SysMgr.nullPath))
            os.system("stty cols %d 2> %s" % (long(cols), SysMgr.nullPath))
            SysMgr.ttyRows = rows
            SysMgr.ttyCols = cols
        except SystemExit:
            sys.exit(0)
        except:
            return

    @staticmethod
    def getTty(update=False):
        if "MAXTTY" in os.environ:
            SysMgr.ttyRows = SysMgr.ttyCols = sys.maxsize
            return
        elif (not SysMgr.isSignalAvailable()) or SysMgr.parentPid > 0:
            return
        elif update and not SysMgr.termGetId:
            return

        # update tty info by ioctl #
        try:
            if not SysMgr.termGetId:
                termios = SysMgr.getPkg("termios", False)
                SysMgr.termGetId = termios.TIOCGWINSZ

            # get fcntl object #
            fcntlObj = SysMgr.getPkg("fcntl", False)

            SysMgr.ttyRows, SysMgr.ttyCols = struct.unpack(
                "hh",
                fcntlObj.ioctl(sys.stdout.fileno(), SysMgr.termGetId, "1234"),
            )

            # update encoding option #
            if SysMgr.encodeEnable and SysMgr.ttyCols < SysMgr.lineLength:
                SysMgr.encodeEnable = False

            return
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # update tty info by stty #
        try:
            if not SysMgr.hasStty():
                raise Exception("no stty")

            subprocess = SysMgr.getPkg("subprocess", False)
            pd = subprocess.Popen(["stty", "size"], stdout=subprocess.PIPE)
            SysMgr.ttyRows, SysMgr.ttyCols = list(
                map(long, pd.stdout.readline().split())
            )
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn("failed to get terminal info", reason=True)

    def saveSystemInfo(self):
        # update uptime #
        SysMgr.updateUptime()

        # cmdline #
        try:
            self.cmdlineData = SysMgr.procReadline("cmdline")[0:-1]
        except:
            SysMgr.printOpenWarn("cmdline")

        # load #
        try:
            self.loadData = SysMgr.procReadline("loadavg").split()
            """
            [0] = 1min usage,
            [1] = 5min usage,
            [2] = 15min usage,
            [3] = running/total thread,
            [4] = lastPid
            """
        except:
            SysMgr.printWarn("failed to get load", reason=True)

        # maximum threads #
        self.nrMaxThread = SysMgr.getMaxThread()

        # rtc #
        try:
            timeInfo = SysMgr.procReadlines("driver/rtc")

            for val in timeInfo:
                timeEntity = val.split()

                if timeEntity[0] == "rtc_time":
                    self.systemInfo["time"] = timeEntity[2]
                elif timeEntity[0] == "rtc_date":
                    self.systemInfo["date"] = timeEntity[2]
        except:
            pass

    @staticmethod
    def saveSysStats(printBuf=False):
        if not SysMgr.sysInstance:
            SysMgr()

        if SysMgr.sysInstance:
            SysMgr.sysInstance.saveSysStat()

        if printBuf:
            SysMgr.printInfoBuffer()

    def saveSysStat(self, initialized=True):
        if not SysMgr.isLinux:
            return

        # update time #
        SysMgr.updateUptime()

        # save resource usage #
        self.updateMemInfo()
        self.updateStorageInfo(isGeneral=True)
        self.updateNetworkInfo()
        self.updateIPCInfo()
        self.saveMacAddr()

        # save system info #
        self.saveUnameInfo()
        self.saveUserInfo()
        self.saveOpenFileInfo()
        self.saveLimitInfo()

        # save system info #
        if initialized:
            # process info #
            if SysMgr.isRecordMode():
                self.saveProcTreeComm()

            # resource info #
            self.saveSystemInfo()
            self.saveCpuInfo()
            self.saveCpuCacheInfo()

            # block device info #
            self.saveBlockInfo()

            # os specific info #
            self.saveWebOSInfo()
            self.saveLinuxInfo()

            # print resource info to temporary buffer #
            self.printResourceInfo()

    def saveCommCache(self):
        try:
            path = "%s/saved_cmdlines" % SysMgr.getTracefsPath()
            with open(path, "r") as fd:
                commList = fd.readlines()
                for item in commList:
                    pid, comm = item.split(" ", 1)
                    if not pid in SysMgr.commCache:
                        SysMgr.commCache[pid] = comm.rstrip()
        except SystemExit:
            sys.exit(0)
        except:
            pass

    def saveProcTreeComm(self):
        # read comm cache #
        self.saveCommCache()

        # read proc tree #
        procTree = SysMgr.getProcTree()
        self.procData = "!!!!!"
        for tid, pid in procTree.items():
            self.procData += "%s#%s," % (tid, pid)

        # add comm cache #
        self.procData += "!!!!!"
        self.procData += str(SysMgr.commCache)

    def saveLinuxInfo(self):
        OSFile = "/etc/os-release"

        try:
            with open(OSFile, "r") as osf:
                self.osData = osf.readlines()
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printOpenWarn(OSFile)

    def saveWebOSInfo(self):
        OSFile = "/var/run/nyx/os_info.json"
        devFile = "/var/run/nyx/device_info.json"

        try:
            with open(OSFile, "r") as osf:
                self.osData = osf.readlines()
        except SystemExit:
            sys.exit(0)
        except:
            pass

        try:
            with open(devFile, "r") as devf:
                self.devData = devf.readlines()
        except SystemExit:
            sys.exit(0)
        except:
            pass

    def saveCpuInfo(self):
        try:
            self.cpuData = SysMgr.procReadlines("cpuinfo")
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn("failed to save CPU info", reason=True)

    def saveCpuCacheInfo(self, data=False):
        cpuPath = "/sys/devices/system/cpu"

        if data:
            cpuCacheList = self.cpuCacheData
        else:
            cpuCacheList = self.cpuCacheInfo

        try:
            corelist = os.listdir(cpuPath)

            for core in corelist:
                cachePath = "/sys/devices/system/cpu/%s/cache" % core

                if data:
                    core = core.lstrip("cpu")

                cpuCacheList[core] = ""

                try:
                    typelist = os.listdir(cachePath)

                    for index in sorted(typelist):
                        if not index.startswith("index"):
                            continue

                        level = "?"
                        path = "%s/%s/level" % (cachePath, index)
                        with open(path, "r") as fd:
                            level = fd.readline()

                        type = "?"
                        path = "%s/%s/type" % (cachePath, index)
                        with open(path, "r") as fd:
                            type = fd.readline()

                        size = "?"
                        path = "%s/%s/size" % (cachePath, index)
                        with open(path, "r") as fd:
                            size = fd.readline()

                        if data:
                            size = UtilMgr.convUnit2Size(size[:-1])
                            size = UtilMgr.convSize2Unit(size, isInt=True)
                            if cpuCacheList[core]:
                                cpuCacheList[core] += "/%s" % size
                            else:
                                cpuCacheList[core] = size
                        else:
                            cpuCacheList[core] = "%sL%s(%s)=%s   " % (
                                cpuCacheList[core],
                                level[:-1],
                                type[:-1],
                                size[:-1],
                            )
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

                if not cpuCacheList[core]:
                    del cpuCacheList[core]
        except SystemExit:
            sys.exit(0)
        except:
            pass

    def saveDevInfo(self):
        try:
            target = None
            devData = SysMgr.procReadlines("devices")

            for line in devData:
                if line.startswith("Character"):
                    target = self.devInfo["char"] = {}
                elif line.startswith("Block"):
                    target = self.devInfo["block"] = {}

                item = line.split()

                if len(item) != 2:
                    continue

                try:
                    num = long(item[0])
                except:
                    continue

                try:
                    target[num].append(item[1])
                except:
                    target[num] = [item[1]]
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn("failed to save deice info", reason=True)

    def saveBlockInfo(self):
        try:
            blockDir = "/sys/block"

            for dirnames in os.walk(blockDir):
                for dev in dirnames[1]:
                    # I/O scheduler #
                    schedPath = os.path.join(blockDir, dev, "queue/scheduler")
                    if not os.path.exists(schedPath):
                        sched = None
                        continue
                    schedList = SysMgr.readFile(schedPath).split()
                    sched = None
                    for item in schedList:
                        if item.startswith("["):
                            sched = item.strip("[]")
                            break

                    # readahead size in KB #
                    raPath = os.path.join(blockDir, dev, "queue/read_ahead_kb")
                    if not os.path.exists(raPath):
                        ra = 0
                        continue
                    ra = SysMgr.readFile(raPath)
                    if ra:
                        ra = UtilMgr.convSize2Unit(long(ra) << 10)

                    # physical block #
                    sizePath = os.path.join(
                        blockDir, dev, "queue/physical_block_size"
                    )
                    if not os.path.exists(sizePath):
                        size = 0
                        continue
                    size = SysMgr.readFile(sizePath)
                    if size:
                        size = long(size)

                    # write cache #
                    wrPath = os.path.join(blockDir, dev, "queue/write_cache")
                    if not os.path.exists(wrPath):
                        wr = 0
                        continue
                    wr = SysMgr.readFile(wrPath)
                    if wr:
                        wr = wr.split()
                        if len(wr) == 2:
                            wr = wr[1]

                    # min size for random I/O #
                    minSizePath = os.path.join(
                        blockDir, dev, "queue/minimum_io_size"
                    )
                    if not os.path.exists(minSizePath):
                        minSize = 0
                        continue
                    minSize = SysMgr.readFile(minSizePath)

                    # optimal size for streaming I/O #
                    optSizePath = os.path.join(
                        blockDir, dev, "queue/optimal_io_size"
                    )
                    if not os.path.exists(optSizePath):
                        optSize = 0
                        continue
                    optSize = SysMgr.readFile(optSizePath)

                    # rq_affinity #
                    afntPath = os.path.join(blockDir, dev, "queue/rq_affinity")
                    if not os.path.exists(afntPath):
                        afnt = 0
                        continue
                    afnt = SysMgr.readFile(afntPath)

                    # io_poll #
                    pollPath = os.path.join(blockDir, dev, "queue/io_poll")
                    if not os.path.exists(pollPath):
                        poll = 0
                        continue
                    poll = SysMgr.readFile(pollPath)

                    # save info #
                    self.blockData[dev] = {
                        "size": size,
                        "sched": sched,
                        "schedList": schedList,
                        "wrCache": wr,
                        "readahead": ra,
                        "minSize": minSize,
                        "optSize": optSize,
                        "affinity": afnt,
                        "poll": poll,
                    }
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn("failed to block deice info", reason=True)

    def saveStorageInfo(self, isGeneral):
        blockDir = "/sys/class/block"

        # update disk data #
        SysMgr.updateDiskStats()
        self.updateNodeInfo()

        # update only a last diskstats if there is a first diskstats exist #
        if not "prev" in self.diskData:
            self.diskData["prev"] = SysMgr.diskStats
            self.updateDiskInfo("prev", SysMgr.diskStats)
        else:
            self.diskData["next"] = SysMgr.diskStats
            self.updateDiskInfo("next", SysMgr.diskStats)

        # update mount data #
        newMountData = SysMgr.getMountData()
        if self.mountData == newMountData:
            self.mountUpdated = False
        else:
            self.mountUpdated = True
            self.mountData = newMountData

        # check data type #
        if not isGeneral:
            return

        # save partition range #
        for dirnames in os.walk(blockDir):
            for subdirname in dirnames[1]:
                try:
                    devPath = "/sys/class/block/%s/dev" % subdirname
                    startPath = "/sys/class/block/%s/start" % subdirname
                    sizePath = "/sys/class/block/%s/size" % subdirname
                    listPath = "/sys/class/block/%s/device" % subdirname

                    with open(sizePath, "r") as sizeFd:
                        size = sizeFd.readline()[:-1]

                    if any(
                        char.isdigit() for char in subdirname
                    ) and os.path.exists(startPath):
                        with open(startPath, "r") as startFd:
                            start = startFd.readline()[:-1]
                    else:
                        start = 0

                    with open(devPath, "r") as devFd:
                        partName = devFd.readline()[:-1]
                        self.partitionInfo[partName] = {
                            "start": long(start),
                            "end": long(start) + long(size),
                        }

                    # skip slave dev #
                    if not os.path.exists(listPath):
                        continue

                    # master dev #
                    dirPath = "/sys/class/block/%s" % subdirname
                    dirList = [subdirname] + os.listdir(dirPath)
                    for subdev in dirList:
                        if subdev == subdirname:
                            subdevPath = "%s/dev" % (dirPath)
                        elif subdev.startswith(subdirname):
                            subdevPath = "%s/%s/dev" % (dirPath, subdev)
                        else:
                            continue

                        with open(subdevPath, "r") as subdevFd:
                            devid = subdevFd.readline()[:-1]
                            if devid in self.devNodeInfo:
                                rpath = self.devNodeInfo[devid]
                            elif subdev in self.devNodeInfo:
                                rpath = self.devNodeInfo[subdev]
                            else:
                                rpath = subdevPath

                            self.devArchInfo.setdefault(subdirname, {})
                            self.devArchInfo[subdirname][rpath] = devid
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

    def updateMemInfo(self):
        try:
            lines = SysMgr.procReadlines("meminfo")

            if not "prev" in self.memData:
                self.memData["prev"] = lines
            else:
                self.memData["next"] = lines
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn("failed to update memory", reason=True)

    @staticmethod
    def getBufferSize():
        bufFile = "%s/buffer_size_kb" % SysMgr.getTracefsPath()

        try:
            f = open(bufFile, "r")
            size = f.readlines()[0]
            f.close()
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printOpenWarn(bufFile)
            return 0

        # get maximum buffer size #
        if "expanded:" in size:
            return long(size.split("expanded: ")[-1].split(")")[0])
        else:
            return long(size)

    @staticmethod
    def setBufferSize(bufferSize):
        SysMgr.writeTraceCmd("../buffer_size_kb", bufferSize)

    @staticmethod
    def copyFile(orig, new, chunk=None, backup=True):
        def _printErr():
            SysMgr.printErr("failed to copy '%s' to '%s'" % (orig, new), True)

        try:
            shutil = SysMgr.getPkg("shutil", False)
            if not shutil:
                raise ValueError()

            # backup new file #
            if backup and os.path.exists(new):
                SysMgr.backupFile(new)

            # copy file using shutil #
            if not shutil.copy(orig, new):
                raise Exception("N/A")

            return True
        except ValueError:
            pass
        except:
            _printErr()
            return False

        # open orig file #
        try:
            od = open(orig, "r")
        except:
            SysMgr.printOpenErr(orig)
            return False

        # backup new file #
        if backup and os.path.exists(new):
            SysMgr.backupFile(new)

        # open new file #
        try:
            nd = open(new, "w")
            os.chmod(new, 0o777)
        except:
            SysMgr.printOpenErr(new)
            return False

        # set 4MB chunk #
        if not chunk:
            chunk = SysMgr.PAGESIZE << 10

        # copy file #
        while 1:
            try:
                buf = od.read(chunk)
                if not buf:
                    return True
                nd.write(buf)
            except:
                _printErr()
                return False

    @staticmethod
    def copyPipeToFile(pipePath, filePath):
        try:
            pd = open(pipePath, "r")
        except:
            SysMgr.printOpenErr(pipePath)
            sys.exit(-1)

        try:
            # use os.O_DIRECT | os.O_RDWR | os.O_TRUNC | os.O_CREAT #
            fd = open(filePath, "w")
            os.chmod(filePath, 0o777)
        except:
            SysMgr.printOpenErr(filePath)
            sys.exit(-1)

        pageSize = SysMgr.PAGESIZE

        while 1:
            try:
                # read each 4k data through pipe #
                buf = pd.read(pageSize)

                fd.write(buf)

                # print to console #
                if SysMgr.streamEnable:
                    SysMgr.printPipe(buf, newline=False)

                if SysMgr.recordStatus:
                    continue

                raise Exception("recording termination")
            except:
                # close pipe #
                pd.close()

                # read the remaining data under 4k from log buffer #
                tpath = "%s/trace" % SysMgr.getTracefsPath()
                with open(tpath, "r") as fr:
                    fd.write(fr.read())

                # close file to sync disk buffer #
                fd.close()

                # save system info #
                SysMgr.saveSysStats()

                path = SysMgr.outputFile

                rbuf = ""
                with open(path, "r") as fd:
                    rbuf = fd.read()

                with open(path, "w") as fd:
                    if SysMgr.sysinfoBuffer != "":
                        fd.writelines(SysMgr.magicStr + "\n")
                        fd.writelines(SysMgr.sysinfoBuffer)
                        fd.writelines(SysMgr.magicStr + "\n")
                        fd.writelines(rbuf)
                    os.chmod(path, 0o777)

                SysMgr.printInfo(
                    "wrote data to '%s' [%s] successfully"
                    % (path, UtilMgr.getFileSize(path))
                )

                return

    @staticmethod
    def getFsPath(fs):
        if not SysMgr.isLinux:
            return None

        if not isinstance(fs, list):
            fs = [fs]

        try:
            lines = SysMgr.procReadlines("mounts")
        except:
            SysMgr.printErr("failed to get mount path", True)
            return None

        for l in lines:
            m = re.match(r"(?P<dev>\S+)\s+(?P<dir>\S+)\s+(?P<fs>\S+)", l)
            if not m:
                continue

            d = m.groupdict()
            if d["fs"] in fs:
                return d["dir"]

        SysMgr.printWarn("failed to get %s info from mount list" % fs)
        return None

    @staticmethod
    def getTraceEventPath():
        if SysMgr.traceEventPath:
            return SysMgr.traceEventPath

        # get tracefs path #
        tracefsPath = SysMgr.getTracefsPath()
        if tracefsPath:
            SysMgr.traceEventPath = "%s/events" % tracefsPath
        else:
            debugfsPath = SysMgr.getDebugfsPath()
            if not debugfsPath:
                return None
            SysMgr.traceEventPath = "%s/tracing/events" % debugfsPath

        return SysMgr.traceEventPath

    @staticmethod
    def getTracefsPath(mount=True):
        if SysMgr.tracefsPath:
            return SysMgr.tracefsPath

        for idx in range(2):
            SysMgr.tracefsPath = SysMgr.getFsPath("tracefs")
            if SysMgr.tracefsPath:
                return SysMgr.tracefsPath
            if mount and idx == 0:
                SysMgr.mountTracefs()

        return None

    @staticmethod
    def getDebugfsPath(mount=True):
        if SysMgr.debugfsPath:
            return SysMgr.debugfsPath

        for idx in range(2):
            SysMgr.debugfsPath = SysMgr.getFsPath("debugfs")
            if SysMgr.debugfsPath:
                return SysMgr.debugfsPath
            if mount and idx == 0:
                SysMgr.mountDebugfs()

        return None

    @staticmethod
    def getRootDevice():
        try:
            if SysMgr.rootdevPath:
                return SysMgr.rootdevPath

            cmdline = SysMgr.procReadline("cmdline")[0:-1]
            for item in cmdline.split(" "):
                if item.startswith("root="):
                    SysMgr.rootdevPath = UtilMgr.lstrip(item, "root=")
                    return SysMgr.rootdevPath
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn("failed to get root device path", reason=True)
            SysMgr.rootdevPath = "/dev/root"
            return SysMgr.rootdevPath

    @staticmethod
    def getChildList(update=True):
        if update:
            SysMgr.updateChildList()
        return SysMgr.childList

    @staticmethod
    def clearChildList():
        SysMgr.childList = {}

    @staticmethod
    def isNoChild():
        if not SysMgr.childList:
            return True
        else:
            return False

    @staticmethod
    def updateChildList():
        childList = list(SysMgr.childList)

        for pid in childList:
            if not SysMgr.isAlive(pid):
                try:
                    os.waitpid(pid, os.WNOHANG)
                except:
                    pass

                SysMgr.childList.pop(pid, None)

    @staticmethod
    def killChildren(
        sig=None, children=None, wait=False, group=False, clear=True
    ):
        if not sig:
            sig = ConfigMgr.SIGKILL

        # get child list #
        if children is None:
            SysMgr.updateChildList()
            children = list(SysMgr.childList)

        # kill children #
        SysMgr.terminateTasks(children, sig, group)

        # remove child list #
        if clear:
            SysMgr.clearChildList()

        # check wait flag #
        if not wait:
            return

        # wait for termination for all children #
        children = set(map(str, children))
        if not children:
            return

        while 1:
            # update child list #
            children = children & set(SysMgr.getPidList())
            if not children:
                break

            # wait for task termination #
            try:
                # set timer for force termination for abnormal child tasks #
                def _onAlarm(signum, frame):
                    SysMgr.termCnt += 1
                    SysMgr.sendSignalProcs(
                        signal.SIGINT
                        if SysMgr.termCnt < 10
                        else signal.SIGKILL,
                        children,
                        verb=False,
                    )

                # register kill timer #
                signal.signal(signal.SIGALRM, _onAlarm)
                signal.alarm(3)

                # wait for termination for child tasks #
                watchList = ["/proc/%s/comm" % tid for tid in children]
                SysMgr.inotify(watchList)
            except SystemExit:
                sys.exit(0)
            except:
                pass

            # remove zombie tasks #
            aliveChildren = []
            for pid in children:
                if SysMgr.isAlive(pid):
                    aliveChildren.append(pid)
            children = set(aliveChildren)

        # disable alarm #
        signal.alarm(0)

    @staticmethod
    def removeExitFunc(func, args=None):
        newList = []
        for handler in SysMgr.exitFuncList:
            if handler[0] == func and handler[1] == args:
                continue
            newList.append(handler)
        SysMgr.exitFuncList = newList

        if not SysMgr.pid in SysMgr.exitPrivateFuncList:
            return

        newList = []
        for handler in SysMgr.exitPrivateFuncList[SysMgr.pid]:
            if handler[0] == func and handler[1] == args:
                continue
            newList.append(handler)
        SysMgr.exitPrivateFuncList[SysMgr.pid] = newList

    @staticmethod
    def addExitFunc(func, args=None, private=False, redundant=True):
        if not redundant:
            SysMgr.removeExitFunc(func, args)

        if private:
            SysMgr.exitPrivateFuncList.setdefault(SysMgr.pid, [])
            targetList = SysMgr.exitPrivateFuncList[SysMgr.pid]
        else:
            targetList = SysMgr.exitFuncList

        targetList.append([func, args])

    @staticmethod
    def doExit():
        if not hasattr(SysMgr, "exitFuncList"):
            return

        # block signals #
        SysMgr.setIgnoreSignal()

        # clear exit handler list #
        exitList = SysMgr.exitFuncList
        SysMgr.exitFuncList = []
        if SysMgr.pid in SysMgr.exitPrivateFuncList:
            exitList += SysMgr.exitPrivateFuncList[SysMgr.pid]
            SysMgr.exitPrivateFuncList[SysMgr.pid] = []

        # clear child list to keep them alive #
        if "KEEPTASK" in SysMgr.environList:
            SysMgr.clearChildList()

        # call functions registered #
        for func, args in exitList:
            try:
                if args:
                    func(*args)
                else:
                    func()
            except SystemExit:
                continue
            except OSError:
                pass
            except:
                SysMgr.printErr("failed to execute %s" % func, True)

        # destroy termination hooks #
        del SysMgr.exitFuncList

        # release all resources #
        SysMgr.releaseResource()

        # reset terminal #
        SysMgr.resetTTY()

        os._exit(0)

    @staticmethod
    def releaseResource(sig=None):
        # kill child tasks #
        SysMgr.killChildren(sig)

        # close all files #
        SysMgr.closeAllForPrint()

    @staticmethod
    def flushAllForPrint():
        try:
            sys.stdout.flush()
        except:
            pass

        if SysMgr.pipeForPager:
            try:
                SysMgr.pipeForPager.flush()
            except:
                pass

        if SysMgr.printFd:
            try:
                SysMgr.printFd.flush()
            except:
                pass

    @staticmethod
    def closeAllForPrint():
        if SysMgr.pipeForPager:
            try:
                SysMgr.pipeForPager.close()
            except:
                pass
            finally:
                SysMgr.pipeForPager = None

        if not SysMgr.printFd:
            return

        # flush output file #
        try:
            fdName = os.path.realpath(SysMgr.printFd.name)

            try:
                SysMgr.printFd.flush()
            except SystemExit:
                sys.exit(0)
            except:
                pass

            # get output size #
            fsize = UtilMgr.getFileSizeStr(fdName)

            SysMgr.printInfo(
                "finish saving all results into '%s'%s successfully"
                % (fdName, fsize)
            )

            # close fd for output #
            SysMgr.closePrintFd()
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # handle duplicated output list #
        try:
            if "DUPOUTPATH" in SysMgr.environList:
                for path in SysMgr.environList["DUPOUTPATH"]:
                    SysMgr.outPathList.setdefault(path, None)
                SysMgr.environList.pop("DUPOUTPATH", None)

            for p in SysMgr.outPathList:
                # set new path #
                if os.path.isdir(p):
                    p = os.path.join(p, os.path.basename(fdName))
                p = os.path.realpath(p)

                # copy file #
                if SysMgr.copyFile(fdName, p):
                    SysMgr.printInfo(
                        "duplicated '%s'%s to '%s'%s successfully"
                        % (
                            fdName,
                            UtilMgr.getFileSizeStr(fdName),
                            p,
                            UtilMgr.getFileSizeStr(p),
                        )
                    )
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                "failed to duplicate '%s' to '%s'" % (fdName, p), True
            )

    @staticmethod
    def clearTraceBuffer():
        # check skip value #
        if "NOFLUSH" in SysMgr.environList:
            return

        SysMgr.printInfo(r"clear trace buffer... ", suffix=False)
        SysMgr.writeTraceCmd("../trace", "")
        SysMgr.printInfo("[done]", prefix=False, title=False)

    @staticmethod
    def clearTraceFilter():
        SysMgr.writeTraceCmd("../set_ftrace_filter", "")
        SysMgr.writeTraceCmd("../set_ftrace_pid", "")
        SysMgr.writeTraceCmd("../set_ftrace_notrace", "")
        SysMgr.writeTraceCmd("../set_event", "")
        SysMgr.writeTraceCmd("../set_event_pid", "")
        SysMgr.writeTraceCmd("../set_graph_function", "")
        SysMgr.writeTraceCmd("../set_graph_notrace", "")
        SysMgr.writeTraceCmd("../tracing_max_latency", "0")
        SysMgr.writeTraceCmd("../tracing_thresh", "0")
        SysMgr.writeTraceCmd("../uprobe_events", "")
        SysMgr.writeTraceCmd("../kprobe_events", "")

    def initCmdList(self):
        sm = SysMgr

        # default #
        self.cmdList["sched/sched_process_fork"] = True
        self.cmdList["sched/sched_process_exit"] = True
        self.cmdList["sched/sched_process_wait"] = True
        self.cmdList["task"] = True
        self.cmdList["signal"] = True
        self.cmdList["printk"] = True
        self.cmdList["lowmemorykiller/lowmemory_kill"] = True
        self.cmdList["module/module_load"] = True
        self.cmdList["module/module_free"] = True
        self.cmdList["module/module_put"] = True
        self.cmdList["module/module_get"] = True

        # sched #
        schedFlag = sm.cpuEnable
        self.cmdList["sched/sched_switch"] = schedFlag
        self.cmdList["sched/sched_migrate_task"] = schedFlag
        wakeupFlag = (sm.cpuEnable and sm.latEnable) or sm.depEnable
        self.cmdList["sched/sched_wakeup"] = wakeupFlag
        self.cmdList["sched/sched_wakeup_new"] = wakeupFlag

        # irq #
        irqFlag = sm.irqEnable
        self.cmdList["irq/irq_handler_entry"] = irqFlag
        self.cmdList["irq/irq_handler_exit"] = irqFlag
        self.cmdList["irq/softirq_entry"] = irqFlag
        self.cmdList["irq/softirq_exit"] = irqFlag
        self.cmdList["irq/softirq_raise"] = False

        # syscall #
        syscallFlag = sm.sysEnable | sm.depEnable | sm.lockEnable
        self.cmdList["raw_syscalls"] = syscallFlag

        # mem #
        memFlag = sm.memEnable
        self.cmdList["kmem/mm_page_alloc"] = memFlag
        self.cmdList["kmem/mm_page_free"] = memFlag
        self.cmdList["kmem/kmalloc"] = memFlag
        self.cmdList["kmem/kfree"] = memFlag
        self.cmdList["filemap/mm_filemap_delete_from_page_cache"] = memFlag
        self.cmdList["vmscan/mm_vmscan_direct_reclaim_begin"] = memFlag
        self.cmdList["vmscan/mm_vmscan_direct_reclaim_end"] = memFlag
        self.cmdList["kmem/mm_page_free_direct"] = False
        self.cmdList["timer/hrtimer_start"] = False
        self.cmdList["vmscan/mm_vmscan_wakeup_kswapd"] = False
        self.cmdList["vmscan/mm_vmscan_kswapd_sleep"] = False

        # bio #
        bioFlag = sm.blockEnable
        self.cmdList["block/block_bio_queue"] = bioFlag
        """
        ignore block_bio_complete for redundant operations
        self.cmdList["block/block_bio_complete"] = bioFlag
        """
        self.cmdList["block/block_rq_complete"] = bioFlag
        self.cmdList["writeback/writeback_dirty_page"] = bioFlag
        self.cmdList["filemap/mm_filemap_add_to_page_cache"] = bioFlag
        # self.cmdList["writeback/wbc_writepage"] = bioFlag

        # fs #
        fsFlag = sm.fsEnable
        self.cmdList["fs"] = fsFlag  # file name support for open, exec, ... #
        self.cmdList["f2fs/f2fs_dataread_start"] = fsFlag
        self.cmdList["f2fs/f2fs_dataread_end"] = fsFlag
        self.cmdList["f2fs/f2fs_datawrite_start"] = fsFlag
        self.cmdList["f2fs/f2fs_datawrite_end"] = fsFlag
        """
        self.cmdList["f2fs/f2fs_sync_file_enter"] = fsFlag
        self.cmdList["f2fs/f2fs_sync_file_exit"] = fsFlag
        self.cmdList["f2fs/f2fs_write_begin"] = fsFlag
        self.cmdList["f2fs/f2fs_write_end"] = fsFlag
        self.cmdList["ext4/ext4_da_write_begin"] = fsFlag
        self.cmdList["ext4/ext4_da_write_end"] = fsFlag
        self.cmdList["ext4/ext4_sync_file_enter"] = fsFlag
        self.cmdList["ext4/ext4_sync_file_exit"] = fsFlag
        self.cmdList["ext4/ext4_es_lookup_extent_enter"] = fsFlag
        self.cmdList["ext4/ext4_es_lookup_extent_exit"] = fsFlag
        self.cmdList["ext4/ext4_direct_IO_enter"] = fsFlag
        self.cmdList["ext4/ext4_direct_IO_exit"] = fsFlag
        self.cmdList["btrfs"] = fsFlag
        """

        # network #
        netFlag = sm.networkEnable
        self.cmdList["net/net_dev_xmit"] = netFlag

        # probes #
        self.cmdList["uprobes"] = sm.ueventEnable
        self.cmdList["kprobes"] = sm.keventEnable

        # lock #
        self.cmdList["filelock/locks_get_lock_context"] = sm.lockEnable

        # power #
        powerFlag = sm.powerEnable
        self.cmdList["power/cpu_idle"] = powerFlag
        self.cmdList["power/cpu_frequency"] = powerFlag
        self.cmdList["power/cpu_frequency_limits"] = True
        self.cmdList["power/suspend_resume"] = powerFlag

        # binder #
        binderFlag = sm.binderEnable
        self.cmdList["binder/binder_transaction"] = binderFlag
        self.cmdList["binder/binder_transaction_received"] = binderFlag
        """
        self.cmdList["binder/binder_lock"] = binderFlag
        self.cmdList["binder/binder_locked"] = binderFlag
        self.cmdList["binder/binder_unlock"] = binderFlag
        self.cmdList["binder/binder_set_priority"] = binderFlag
        """

        # workqueue #
        self.cmdList["workqueue/workqueue_queue_work"] = sm.wqEnable
        self.cmdList["workqueue/workqueue_execute_start"] = sm.wqEnable
        self.cmdList["workqueue/workqueue_execute_end"] = sm.wqEnable

        # TODO: implement analyzer for below events #

        # i2c #
        self.cmdList["i2c"] = sm.i2cEnable

        # io_uring #
        self.cmdList["io_uring"] = sm.iouringEnable

        # kvm #
        self.cmdList["kvm"] = sm.iouringEnable

    def runPeriodProc(self):
        pid = SysMgr.createProcess()

        if pid == 0:
            signal.signal(signal.SIGINT, 0)

            while 1:
                time.sleep(0.0001)

            sys.exit(0)

    def prepareForTracing(self):
        stat = SysMgr.readTraceFile("../tracing_on")
        if stat == "0":
            pass
        elif SysMgr.forceEnable:
            # write command to stop tracing #
            SysMgr.writeTraceCmd("../tracing_on", "0")
        elif stat == "1":
            # no Guider process except for myself #
            if SysMgr.getBgProcCount(cache=True) <= 1:
                res = SysMgr.readTraceFile("enable")
                # default status #
                if res == "0":
                    pass
                # tracing status #
                else:
                    SysMgr.printErr(
                        "failed to start tracing because "
                        "tracing is already in progress on system\n"
                        "\tit would be stopped so that try to record again"
                    )
                    sys.exit(-1)
            else:
                SysMgr.printErr(
                    "failed to start tracing because "
                    "another Guider is already running"
                )
                os._exit(0)

        # check fast init condition #
        if "FASTINIT" in SysMgr.environList:
            return

        # clean up trace buffer #
        SysMgr.clearTraceBuffer()

        # clear trace filter #
        SysMgr.clearTraceFilter()

    def setPidFilter(self, targets):
        params = ", ".join(targets)

        # get thread ID #
        targetTasks = []
        for item in targets:
            targetTasks += SysMgr.getTids(item, sibling=SysMgr.findOption("P"))

        # check thread list #
        if not targetTasks:
            SysMgr.printErr("no task related to '%s'" % params)
            sys.exit(0)

        targetTasks = " ".join(list(map(str, targetTasks)))

        try:
            SysMgr.writeTraceCmd("../set_ftrace_pid", targetTasks, True)
            SysMgr.printInfo(
                "only specific processes [ %s ] are traced" % targetTasks
            )
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                "failed to add '%s' to PID filter" % params,
                True,
            )
            sys.exit(-1)

    def startTracing(self):
        # write command to start tracing #
        SysMgr.writeTraceCmd("../tracing_on", "1")

        # write start event #
        SysMgr.writeEvent("EVENT_START", False)

        # update start recording time #
        SysMgr.startRecTime = SysMgr.getUptime()

        # update status #
        SysMgr.recordStatus = True

        # register cleanup callback #
        if "FASTINIT" in SysMgr.environList:
            SysMgr.addExitFunc(SysMgr.clearTraceBuffer)
            SysMgr.addExitFunc(SysMgr.clearTraceFilter)

    @staticmethod
    def applyTraceAttr(filters, var, val, recover=False):
        # get target list #
        eventPath = SysMgr.getTraceEventPath() + "/"
        targets = UtilMgr.getFiles(eventPath, [var])

        # change var for rstrip #
        var = "/" + var

        # init write list #
        writeList = []

        for item in targets:
            # get name #
            name = UtilMgr.lstrip(item, eventPath)
            name = UtilMgr.rstrip(name, var)

            if UtilMgr.isValidStr(name, filters):
                if recover:
                    orig = SysMgr.readFile(item)
                    SysMgr.addExitFunc(
                        SysMgr.writeTraceCmd,
                        [UtilMgr.lstrip(item, eventPath), orig],
                    )

                writeList.append((item, val))

        # write value to file #
        for item, val in writeList:
            SysMgr.writeFile(item, val)

    @staticmethod
    def clearFilters():
        if not "CLEARFILTER" in SysMgr.environList:
            return

        SysMgr.printInfo(r"clear trace filters... ", suffix=False)
        SysMgr.applyTraceAttr(["*"], "filter", "0")
        SysMgr.printInfo("[done]", prefix=False, title=False)

    @staticmethod
    def enableEvents(targets=[]):
        if not targets and not "ENABLETP" in SysMgr.environList:
            return

        # get filter list #
        filters = targets if targets else SysMgr.environList["ENABLETP"]

        SysMgr.applyTraceAttr(filters, "enable", "1", True)

        # prepare for recovering tracing status after printing #
        orig = SysMgr.readTraceFile("../tracing_on")
        SysMgr.addExitFunc(SysMgr.writeTraceCmd, ["../tracing_on", orig])

        # write command to start tracing #
        SysMgr.writeTraceCmd("../tracing_on", "1")

    @staticmethod
    def setTraceOpt():
        # set comm cache size #
        SysMgr.writeTraceCmd("../saved_cmdlines_size", "32767")

        # set default options #
        for opt in (
            "noirq-info",
            "noannotate",
            "print-tgid",
            "record-tgid",
            "overwrite",
        ):
            SysMgr.writeTraceCmd("../trace_options", opt)

        # set trace options #
        for opt in SysMgr.environList.get("TRACEOPT", []):
            SysMgr.writeTraceCmd("../trace_options", opt)

        SysMgr.writeTraceCmd("../current_tracer", "nop")

    @staticmethod
    def disableEvents():
        if not "DISABLETP" in SysMgr.environList:
            return

        # get filter list #
        filters = SysMgr.environList["DISABLETP"]

        SysMgr.applyTraceAttr(filters, "enable", "0", True)

    def enableCmdEvents(self):
        for cmd, value in self.cmdList.items():
            if value:
                SysMgr.writeTraceCmd("%s/enable" % cmd, "1")

    def startRecording(self):
        def _printStartLog():
            # write user command #
            SysMgr.runProfCmd("AFTER")

            SysMgr.printStat(
                r"start recording... [ STOP(Ctrl+c), MARK(Ctrl+\) ]"
            )

        def _writeCommonCmd():
            # enable dynamic events #
            SysMgr.writeCustomCmd()
            SysMgr.writeKernelCmd()
            SysMgr.writeUserCmd()

            # enable signal filter #
            if self.cmdList["signal"]:
                if SysMgr.filterGroup:
                    commonFilter = SysMgr.getPidFilter()
                    genFilter = commonFilter.replace("common_", "")
                    SysMgr.writeTraceCmd(
                        "signal/signal_deliver/filter", commonFilter
                    )
                    SysMgr.writeTraceCmd(
                        "signal/signal_generate/filter", genFilter
                    )

        def _startFuncGraph(self):
            # reset events #
            SysMgr.stopRecording()

            # set function_graph tracer #
            if SysMgr.writeTraceCmd("../current_tracer", "function_graph") < 0:
                SysMgr.printErr(
                    "enable CONFIG_FUNCTION_GRAPH_TRACER kernel option"
                )
                sys.exit(-1)

            # apply PID filter #
            if SysMgr.filterGroup:
                self.setPidFilter(SysMgr.filterGroup)

            # set function_graph options #
            optPath = "../trace_options"
            SysMgr.writeTraceCmd(optPath, "nofuncgraph-proc")
            SysMgr.writeTraceCmd(optPath, "funcgraph-abstime")
            SysMgr.writeTraceCmd(optPath, "funcgraph-overhead")
            SysMgr.writeTraceCmd(optPath, "funcgraph-duration")
            SysMgr.writeTraceCmd("../max_graph_depth", str(SysMgr.funcDepth))

            # apply function filter #
            if SysMgr.customCmd:
                params = " ".join(SysMgr.customCmd)
                SysMgr.printInfo(
                    "start setting function filter [ %s ]" % params
                )
                if SysMgr.writeTraceCmd("../set_ftrace_filter", params) < 0:
                    SysMgr.printErr("failed to set function filter")
                    sys.exit(-1)
                else:
                    SysMgr.printStat(
                        "finished applying function filter [ %s ]" % params
                    )
            else:
                SysMgr.writeTraceCmd("../set_ftrace_filter", "")

            # apply exclude function filter #
            if "EXFUNCFILTER" in SysMgr.environList:
                params = " ".join(SysMgr.environList["EXFUNCFILTER"])
                SysMgr.printInfo(
                    "start setting exclude function filter [ %s ]" % params
                )
                if SysMgr.writeTraceCmd("../set_ftrace_notrace", params) < 0:
                    SysMgr.printErr("failed to set exclude function filter")
                    sys.exit(-1)
                else:
                    SysMgr.printStat(
                        "finished applying exclude function filter [ %s ]"
                        % params
                    )
            else:
                SysMgr.writeTraceCmd("../set_ftrace_notrace", "")

            # apply threshold (us) #
            if "ELAPSED" in SysMgr.environList:
                threshold = UtilMgr.getEnvironNum("ELAPSED")
                SysMgr.printInfo(
                    (
                        "only specific functions that took "
                        "at least '%s us' or longer are printed"
                    )
                    % UtilMgr.convNum(threshold)
                )
                if (
                    SysMgr.writeTraceCmd("../tracing_thresh", str(threshold))
                    < 0
                ):
                    SysMgr.printErr("failed to set threshold value")
                    sys.exit(-1)

            # start tracing #
            self.startTracing()

        # print trace point list #
        if SysMgr.findOption("l"):
            if not SysMgr.findOption("Q"):
                SysMgr.streamEnable = False
            SysMgr.printDirs(
                SysMgr.getTraceEventPath(),
                SysMgr.funcDepth if SysMgr.funcDepth > 0 else 1,
            )
            sys.exit(0)

        # check root permission #
        SysMgr.checkRootPerm(msg="trace system")

        # update event path #
        SysMgr.getTraceEventPath()

        if not os.path.isdir(SysMgr.traceEventPath):
            cmd = "/boot/config-$(uname -r)"
            SysMgr.printErr(
                (
                    "check whether ftrace options are enabled in kernel "
                    "through %s"
                )
                % cmd
            )
            sys.exit(-1)

        # write user command #
        SysMgr.runProfCmd("BEFORE")

        # set size of trace buffer per core #
        if SysMgr.bufferSize <= 0:
            SysMgr.bufferSize = "40960"  # 40MB #
        else:
            # Change from integer to string #
            SysMgr.bufferSize = str(SysMgr.bufferSize)

        # set system buffer size #
        SysMgr.setBufferSize(SysMgr.bufferSize)

        # get system buffer size #
        setBufferSize = SysMgr.getBufferSize()

        # check system buffer size #
        if long(SysMgr.bufferSize) != setBufferSize:
            SysMgr.printWarn(
                "failed to set buffer size to %s KB, now is %s KB"
                % (SysMgr.bufferSize, setBufferSize),
                True,
            )

        # initialize event list to enable #
        self.initCmdList()

        # handle all option #
        if SysMgr.disableAll:
            self.disableAllEvents()
        elif SysMgr.enableAll:
            self.enableAllEvents()

        # set trace option #
        SysMgr.setTraceOpt()

        SysMgr.printStat(
            r"prepare for recording... [ STOP(Ctrl+c), MARK(Ctrl+\) ]"
        )

        # prepare for tracing #
        self.prepareForTracing()

        # -------------------- FUNCTION MODE -------------------- #
        if SysMgr.isFuncMode():
            # check conditions for kernel function_graph #
            if SysMgr.graphEnable:
                # start function graph #
                _startFuncGraph(self)

                _printStartLog()
                return

            # define initialized command variable #
            cmd = ""

            # make filter for function mode #
            if SysMgr.filterGroup:
                try:
                    try:
                        cmd += SysMgr.getPidFilter()
                    except SystemExit:
                        sys.exit(0)
                    except:
                        pass

                    if not cmd:
                        raise Exception("no command")
                except:
                    SysMgr.printErr("wrong TID %s" % SysMgr.filterGroup, True)
                    sys.exit(-1)

            # trace except for swapper threads #
            if cmd == "":
                cmd = "(common_pid != 0)"

            # set userstacktrace options #
            if SysMgr.userEnable:
                SysMgr.writeTraceCmd("../trace_options", "userstacktrace")
                SysMgr.writeTraceCmd("../trace_options", "sym-userobj")
            else:
                SysMgr.writeTraceCmd("../trace_options", "nouserstacktrace")
                SysMgr.writeTraceCmd("../trace_options", "nosym-userobj")

            SysMgr.writeTraceCmd("../trace_options", "sym-addr")
            SysMgr.writeTraceCmd("../options/stacktrace", "1")

            if SysMgr.enableAll or SysMgr.disableAll:
                _printStartLog()
                return

            # segmentation fault events #
            elist = SysMgr.customCmd
            if elist:
                pass
            elif any([True for evt in elist if evt.startswith("signal")]):
                pass
            else:
                sigCmd = "sig == %d" % signal.SIGSEGV
                SysMgr.writeTraceCmd("signal/filter", sigCmd)

            # CPU events #
            if SysMgr.cpuEnable:
                addr = SysMgr.getAddrFromKernel("tick_sched_timer")
                if addr:
                    SysMgr.writeTraceCmd(
                        "timer/hrtimer_start/filter",
                        "%s && function == 0x%s" % (cmd, addr),
                    )
                SysMgr.writeTraceCmd("timer/hrtimer_start/enable", "1")
            else:
                SysMgr.writeTraceCmd("timer/hrtimer_start/enable", "0")

            # page events #
            if SysMgr.memEnable:
                SysMgr.writeTraceCmd("kmem/mm_page_alloc/filter", cmd)

                if SysMgr.writeTraceCmd("kmem/mm_page_free/filter", cmd) < 0:
                    SysMgr.writeTraceCmd(
                        "kmem/mm_page_free_direct/filter", cmd
                    )

                SysMgr.writeTraceCmd("kmem/mm_page_alloc/enable", "1")

                if SysMgr.writeTraceCmd("kmem/mm_page_free/enable", "1") < 0:
                    SysMgr.writeTraceCmd(
                        "kmem/mm_page_free_direct/enable", "1"
                    )
            else:
                SysMgr.writeTraceCmd("kmem/mm_page_alloc/enable", "0")

                if SysMgr.writeTraceCmd("kmem/mm_page_free/enable", "0") < 0:
                    SysMgr.writeTraceCmd(
                        "kmem/mm_page_free_direct/enable", "0"
                    )

            # all syscall events #
            if SysMgr.sysEnable:
                if SysMgr.heapEnable or SysMgr.lockEnable:
                    SysMgr.printErr(
                        "failed to enable syscall events with other events"
                    )
                    sys.exit(-1)
                elif not SysMgr.syscallList:
                    pass
            else:
                # heap events #
                if SysMgr.heapEnable:
                    if SysMgr.arch == "arm":
                        mmapId = SysMgr.getNrSyscall("sys_mmap2")
                    else:
                        mmapId = SysMgr.getNrSyscall("sys_mmap")

                    brkId = SysMgr.getNrSyscall("sys_brk")

                    SysMgr.syscallList.append(mmapId)
                    SysMgr.syscallList.append(brkId)

                    self.cmdList["raw_syscalls"] = True

                # lock events #
                if SysMgr.lockEnable:
                    futexId = SysMgr.getNrSyscall("sys_futex")

                    SysMgr.syscallList.append(futexId)

                    self.cmdList["raw_syscalls"] = True

            # target syscall events #
            SysMgr.writeSyscallCmd(self.cmdList["raw_syscalls"])

            # block events #
            if SysMgr.blockEnable:
                blkCmd = cmd + (
                    """ && (rwbs == "R" || rwbs == "RA" || """
                    """rwbs == "RM" || rwbs == "WS")"""
                )
                SysMgr.writeTraceCmd("block/block_bio_queue/filter", blkCmd)
                SysMgr.writeTraceCmd("block/block_bio_queue/enable", "1")
                SysMgr.writeTraceCmd(
                    "writeback/writeback_dirty_page/filter", cmd
                )
                SysMgr.writeTraceCmd(
                    "writeback/writeback_dirty_page/enable", "1"
                )
                """
                SysMgr.writeTraceCmd('writeback/wbc_writepage/filter', cmd)
                SysMgr.writeTraceCmd('writeback/wbc_writepage/enable', '1')
                """
            else:
                SysMgr.writeTraceCmd("block/block_bio_queue/enable", "0")
                SysMgr.writeTraceCmd(
                    "writeback/writeback_dirty_page/enable", "0"
                )
                # SysMgr.writeTraceCmd('writeback/wbc_writepage/enable', '0')

            # special events #
            _writeCommonCmd()

            # write user command #
            SysMgr.runProfCmd("AFTER")

            # start tracing #
            self.startTracing()

            # print log #
            _printStartLog()
            return

        # -------------------- THREAD MODE -------------------- #
        def _applySchedFilter():
            cmd = "prev_pid == 0 || next_pid == 0 || "

            # apply filter #
            for comm in list(SysMgr.filterGroup):
                # comm #
                cmd = (
                    """%sprev_comm == "*%s*" || next_comm == "*%s*" || """
                    % (cmd, comm, comm)
                )

                # tid #
                try:
                    # just TID #
                    if comm.isdigit():
                        pid = long(comm)
                        cmd = "%sprev_pid == %s || next_pid == %s || " % (
                            cmd,
                            pid,
                            pid,
                        )
                    # TID bigger or lesser #
                    elif comm.startswith(">") or comm.endswith("<"):
                        cmd += "prev_pid >= %s || " % long(comm.strip("<>"))
                        cmd += "next_pid >= %s || " % long(comm.strip("<>"))
                    # PID bigger or lesser #
                    elif comm.startswith("<") or comm.endswith(">"):
                        cmd += "prev_pid <= %s || " % long(comm.strip("<>"))
                        cmd += "next_pid <= %s || " % long(comm.strip("<>"))
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

            cmd = cmd[: cmd.rfind("||")].strip()
            if SysMgr.writeTraceCmd("sched/sched_switch/filter", cmd) < 0:
                SysMgr.printErr(
                    "failed to set filter [ %s ]"
                    % " ".join(SysMgr.filterGroup)
                )
                sys.exit(-1)

        # sched events #
        if self.cmdList["sched/sched_switch"]:
            if SysMgr.filterGroup:
                _applySchedFilter()
            else:
                SysMgr.writeTraceCmd("sched/sched_switch/filter", "0")

            if SysMgr.writeTraceCmd("sched/sched_switch/enable", "1") < 0:
                SysMgr.printErr("failed to enable sched events")
                sys.exit(-1)

        # build sched filter #
        if SysMgr.filterGroup:
            cmd = SysMgr.getPidFilter("pid", True, False)
        else:
            cmd = "0"

        # set sched filters #
        filterStr = " ".join(SysMgr.filterGroup)
        for schedItem in (
            "wakeup",
            "wakeup_new",
            "migrate_task",
            "process_wait",
        ):
            name = "sched/sched_" + schedItem
            if not self.cmdList[name]:
                continue

            if SysMgr.writeTraceCmd(name + "/filter", cmd) < 0:
                SysMgr.printErr(
                    "failed to set '%s' filter to [ %s ]" % (name, filterStr)
                )
                sys.exit(-1)

        # events for dependency tracing #
        if SysMgr.depEnable:
            ecmd = "("
            rcmd = "(("

            for syscall in ("sys_write", "sys_futex"):
                ecmd += "id == %s || " % SysMgr.getNrSyscall(syscall)
                rcmd += "id == %s || " % SysMgr.getNrSyscall(syscall)

            if SysMgr.arch == "aarch64":
                syscallList = ("sys_recvfrom", "sys_recvmmsg", "sys_recvmsg")
            elif SysMgr.arch == "arm":
                syscallList = (
                    "sys_recv",
                    "sys_epoll_wait",
                    "sys_poll",
                    "sys_select",
                    "sys_recvfrom",
                    "sys_recvmmsg",
                    "sys_recvmsg",
                )
            else:
                syscallList = (
                    "sys_epoll_wait",
                    "sys_poll",
                    "sys_select",
                    "sys_recvfrom",
                    "sys_recvmmsg",
                    "sys_recvmsg",
                )

            for syscall in syscallList:
                ecmd += "id == %s || " % SysMgr.getNrSyscall(syscall)
                rcmd += "id == %s || " % SysMgr.getNrSyscall(syscall)

            ecmd = ecmd.rstrip(" |") + ")"
            rcmd = rcmd.rstrip(" |") + ") && ret > 0)"

            SysMgr.writeTraceCmd("raw_syscalls/sys_enter/filter", ecmd)
            SysMgr.writeTraceCmd("raw_syscalls/sys_exit/filter", rcmd)
        elif SysMgr.lockEnable:
            nrFutex = SysMgr.getNrSyscall("sys_futex")
            if nrFutex not in SysMgr.syscallList:
                SysMgr.syscallList.append(nrFutex)
        else:
            SysMgr.writeTraceCmd("raw_syscalls/sys_enter/filter", "0")
            SysMgr.writeTraceCmd("raw_syscalls/sys_enter/enable", "0")

        # syscall events #
        SysMgr.writeSyscallCmd(self.cmdList["raw_syscalls"])

        # memory events #
        if self.cmdList["kmem/mm_page_free"]:
            if SysMgr.writeTraceCmd("kmem/mm_page_free/enable", "1") < 0:
                SysMgr.writeTraceCmd("kmem/mm_page_free_direct/enable", "1")

        # block events #
        cmd = '''rwbs == "R" || rwbs == "RA" || rwbs == "RM" || rwbs == "WS"'''
        if self.cmdList["block/block_bio_queue"]:
            SysMgr.writeTraceCmd("block/block_bio_queue/filter", cmd)
        if self.cmdList["block/block_rq_complete"]:
            SysMgr.writeTraceCmd("block/block_rq_complete/filter", cmd)

        # special events #
        _writeCommonCmd()

        # enable command events #
        self.enableCmdEvents()

        # enable events #
        SysMgr.enableEvents()

        # disable events #
        SysMgr.disableEvents()

        # clear filters #
        SysMgr.clearFilters()

        # start tracing #
        self.startTracing()

        # print log #
        _printStartLog()

    @staticmethod
    def stopRecording():
        if not SysMgr.recordStatus:
            return
        elif not (
            SysMgr.isRecordMode()
            and (SysMgr.isThreadMode() or SysMgr.isFuncMode())
        ):
            return

        # write command #
        if SysMgr.cmdEnable is not False and SysMgr.cmdFd:
            # write signal command #
            if SysMgr.signalCmd:
                try:
                    if SysMgr.intervalEnable > 0:
                        SysMgr.signalCmd += (
                            "sleep %s\n" % SysMgr.intervalEnable
                        )
                    else:
                        SysMgr.signalCmd += "sleep 32767\n"
                    SysMgr.cmdFd.write(SysMgr.signalCmd)
                    SysMgr.cmdFd.flush()
                    SysMgr.signalCmd = None
                    SysMgr.printInfo(
                        "write trace commands to '%s'" % SysMgr.cmdEnable
                    )
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr("failed to write signal commands", True)

            if SysMgr.outputFile:
                SysMgr.saveCmd = "cat %s_pipe >> %s\n" % (
                    SysMgr.inputFile,
                    os.path.realpath(SysMgr.outputFile),
                )

        # stop tracing #
        SysMgr.writeTraceCmd("../tracing_on", "0")
        SysMgr.recordStatus = False

        SysMgr.printStat(
            r"finished recording for %.3f sec"
            % (SysMgr.getUptime() - SysMgr.startRecTime)
        )

        # disable all ftrace options #
        for idx, val in SysMgr.cmdList.items():
            if val and SysMgr.writeTraceCmd(str(idx) + "/enable", "0") >= 0:
                SysMgr.writeTraceCmd(str(idx) + "/filter", "0")

        if not SysMgr.graphEnable and SysMgr.customCmd:
            for cmd in SysMgr.customCmd:
                event = cmd.split(":")[0]
                SysMgr.writeTraceCmd(event + "/enable", "0")
                SysMgr.writeTraceCmd(event + "/filter", "0")

        # reset stacktrace options #
        if SysMgr.isFuncMode():
            SysMgr.writeTraceCmd("../options/stacktrace", "0")
            SysMgr.writeTraceCmd("../trace_options", "nouserstacktrace")

        # write command #
        if SysMgr.cmdEnable is not False and SysMgr.cmdFd:
            outputPath = os.path.realpath(SysMgr.outputFile)

            # remove exist file #
            try:
                SysMgr.cmdFd.write("rm -f %s 2> /dev/null\n" % outputPath)
            except SystemExit:
                sys.exit(0)
            except:
                pass

            # write system info #
            try:
                SysMgr.sysInstance.printResourceInfo(tree=False)
                infoStr = 'echo "%s\n%s\n%s\n" >> %s\n' % (
                    SysMgr.magicStr,
                    SysMgr.sysinfoBuffer,
                    SysMgr.magicStr,
                    outputPath,
                )
                SysMgr.clearInfoBuffer()
                SysMgr.cmdFd.write(infoStr)
                SysMgr.cmdFd.flush()
            except SystemExit:
                sys.exit(0)
            except:
                pass

            # write save command #
            if SysMgr.saveCmd:
                try:
                    SysMgr.cmdFd.write(SysMgr.saveCmd)
                    SysMgr.cmdFd.write(
                        "echo '\n[Info] saved commands for tracing into %s\n'\n"
                        % outputPath
                    )
                    SysMgr.cmdFd.flush()
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr("failed to write command", True)

    def printResourceInfo(self, tree=True):
        if not SysMgr.generalEnable or "NOHEADER" in SysMgr.environList:
            return

        self.printSystemInfo()

        self.printOSInfo()

        self.printCpuInfo()

        self.printCpuCacheInfo()

        self.printMemInfo()

        self.printStorageInfo()

        self.printBlockInfo()

        self.printSwapInfo()

        self.printNetworkInfo()

        self.printGpuInfo()

        self.printGpuMemInfo()

        self.printIPCInfo()

        self.printCgroupInfo()

        self.printLimitInfo()

        self.printSchedInfo()

        self.printSchedFeatInfo()

        self.printVmstatInfo()

        self.printBuddyInfo()

        self.printLockInfo()

        self.printPageInfo()

        self.printHugePageInfo()

        self.printPeakInfo()

        self.printThresholdInfo()

        self.printKsmInfo()

        self.printMmapInfo()

        self.printAppArmorInfo()

        self.printUSBInfo()

        self.printProcMapInfo()

        if not tree:
            return

        if SysMgr.isRecordMode():
            TaskAnalyzer.printThreadTree()

        # keep last position for parser #
        self.printProcTreeInfo()

    def printProcTreeInfo(self):
        if self.procData:
            SysMgr.infoBufferPrint(self.procData)

    def printOSInfo(self):
        if not self.osData and not self.devData:
            return

        if "NOOSINFO" in SysMgr.environList:
            return

        # add JSON stats #
        if SysMgr.jsonEnable:
            SysMgr.jsonData.setdefault("general", {})
            SysMgr.jsonData["general"]["os"] = {}
            jsonData = SysMgr.jsonData["general"]["os"]
        else:
            jsonData = {}

        # get maximum length #
        lenLine = 1
        for data in (
            self.osData
            if self.osData
            else [] + self.devData
            if self.devData
            else []
        ):
            if lenLine < len(data):
                lenLine = len(data)
        lenLine += 36

        SysMgr.infoBufferPrint("\n[System OS Info]")
        SysMgr.infoBufferPrint(twoLine[:lenLine])
        SysMgr.infoBufferPrint(
            "{0:^35} {1:11} {2:1}|".format(
                "Type", "Information", " " * (lenLine - 49)
            )
        )
        SysMgr.infoBufferPrint(twoLine[:lenLine])

        # save os and device data #
        for data in (
            self.osData
            if self.osData
            else [] + self.devData
            if self.devData
            else []
        ):
            try:
                val = data.split("=")
                if len(val) < 2:
                    val = data.split(":")
                if len(val) < 2:
                    continue

                name = val[0].replace('"', "")
                value = (
                    val[1].replace('"', "").replace("\n", "").replace(",", "")
                ).rstrip()
                after = " " * (lenLine - 38 - len(value))
                SysMgr.infoBufferPrint(
                    "{0:35} {1:<1} {2:1}|".format(name, value, after)
                )

                if SysMgr.jsonEnable:
                    jsonData[name] = value
            except:
                SysMgr.printWarn("failed to parse osData")

        SysMgr.infoBufferPrint(twoLine[:lenLine])

    def printProcMapInfo(self):
        # check skip option #
        if not "PROCMAP" in SysMgr.environList:
            return

        # get pid list #
        pids = SysMgr.convTaskList(SysMgr.environList["PROCMAP"])

        for pid in pids:
            path = "%s/%s/maps" % (SysMgr.procPath, pid)
            data = SysMgr.readFile(path)
            if not data:
                continue

            SysMgr.infoBufferPrint(
                "\n[Proc Map Info] %s\n%s" % (SysMgr.getProcInfo(pid), twoLine)
            )
            SysMgr.infoBufferPrint(data)
            SysMgr.infoBufferPrint(twoLine)

    def printUSBInfo(self):
        # check skip option #
        if "NOUSBINFO" in SysMgr.environList:
            return

        # define check function #
        def _openNode():
            try:
                with open("/sys/kernel/debug/usb/devices", "r") as fd:
                    return fd.readlines()
            except SystemExit:
                sys.exit(0)
            except:
                return

        # check open timeout #
        if not SysMgr.checkTimeout(_openNode, 1):
            return

        # read USB info #
        data = _openNode()

        # get maximum line length #
        lenLine = 10
        for line in data if data else []:
            if lenLine < len(line):
                lenLine = len(line) + 1

        SysMgr.infoBufferPrint("\n[System USB Info]")
        SysMgr.infoBufferPrint(twoLine[:lenLine])

        if not data:
            SysMgr.infoBufferPrint("    None |")
            SysMgr.infoBufferPrint(twoLine[:lenLine])
            return

        for idx, line in enumerate(data):
            line = line.strip()
            if line:
                SysMgr.infoBufferPrint(
                    line + (" " * (lenLine - len(line) - 1)) + "|"
                )
            elif idx in (0, len(data) - 1):
                pass
            else:
                SysMgr.infoBufferPrint(oneLine[:lenLine])

        SysMgr.infoBufferPrint(twoLine[:lenLine])

    def printAppArmorInfo(self):
        if "NOAPPARMORINFO" in SysMgr.environList:
            return

        info = SysMgr.readAppArmorInfo()
        if not info:
            return

        # set max length #
        if SysMgr.outPath:
            maxLen = SysMgr.lineLength
        else:
            maxLen = min(SysMgr.lineLength, SysMgr.ttyCols)

        SysMgr.infoBufferPrint("\n[AppArmor Info]\n%s" % twoLine[:maxLen])

        current = ""
        for line in info.split("\n"):
            if len(current) + len(line) + 3 > maxLen:
                SysMgr.infoBufferPrint(current)
                current = ""
            if current:
                current += " | "
            current += line
        SysMgr.infoBufferPrint(current + "\n" + oneLine[:maxLen])

    def printPageInfo(self):
        if "NOVPAGEINFO" in SysMgr.environList:
            return

        PAGE = SysMgr.PAGESIZE
        convSize = UtilMgr.convSize2Unit
        lineLen = len(oneLine)

        # get page info #
        pageInfo = SysMgr.readPageInfo()
        if not pageInfo or not pageInfo["freeInfo"]:
            return

        # add JSON stats #
        if SysMgr.jsonEnable:
            SysMgr.jsonData.setdefault("general", {})
            SysMgr.jsonData["general"].setdefault("page", pageInfo)

        SysMgr.infoBufferPrint(
            "\n[FreePage Info] (PageBlockOrder: %s) (PagesPerBlock: %s)\n%s"
            % (
                pageInfo["pageBlockOrder"],
                pageInfo["pagePerBlock"],
                twoLine[:lineLen],
            )
        )
        orders = [
            "%9s"
            % (str(order) + "(%s)" % convSize((2**order) * PAGE, isInt=True))
            for order in range(0, 11)
        ]
        orders.append(" |%7s" % "TOTAL")
        SysMgr.infoBufferPrint(
            "{0:>5} | {1:<8} | {2:<11} | {3:<1} |\n{4:1}".format(
                "NODE", "ZONE", "TYPE", " ".join(orders), twoLine[:lineLen]
            )
        )

        # print page info #
        for node, data in pageInfo["freeInfo"].items():
            for zone, vals in data.items():
                for ztype, orders in vals.items():
                    orderSize = [
                        (2**order) * PAGE * val
                        for order, val in enumerate(orders)
                    ]
                    orders = [
                        "%9s" % UtilMgr.convNum(order) if order else " " * 9
                        for order in orders
                    ]
                    orders.append(" |%7s" % convSize(sum(orderSize)))
                    SysMgr.infoBufferPrint(
                        "{0:>5} | {1:<8} | {2:<11} | {3:1} |".format(
                            node, zone, ztype, " ".join(orders)
                        )
                    )
                    node = zone = ""
                SysMgr.infoBufferPrint(oneLine[:lineLen])

    def printLockInfo(self):
        if "NOLOCKINFO" in SysMgr.environList:
            return

        lineLen = 121
        SysMgr.infoBufferPrint("\n[Lock Info]\n%s" % twoLine[:lineLen])
        SysMgr.infoBufferPrint(
            (
                "{0:>25} | {1:>7} | {2:>12} | {3:>7} | "
                "{4:>15} | {5:>30} | {6:>5} | \n{7:1}"
            ).format(
                "PROC",
                "CLASS",
                "ATTR",
                "OP",
                "FILE",
                "RANGE",
                "REENT",
                twoLine[:lineLen],
            )
        )

        # get lock info #
        locks = SysMgr.readLockInfo()
        if not locks:
            SysMgr.infoBufferPrint("    None")
            SysMgr.infoBufferPrint(twoLine[:lineLen])
            return

        # print lock info #
        for pid, items in locks.items():
            proc = "%s(%s)" % (SysMgr.getComm(pid), pid)
            for d in items:
                SysMgr.infoBufferPrint(
                    (
                        "{0:>25} | {1:>7} | {2:>12} | {3:>7} | "
                        "{4:>15} | {5:>30} | {6:>5} |"
                    ).format(
                        proc,
                        d["class"],
                        d["attr"],
                        d["op"],
                        d["file"],
                        "%s-%s" % (d["start"], d["end"]),
                        d["reent"],
                    )
                )
                proc = ""
            SysMgr.infoBufferPrint(oneLine[:lineLen])

    def printBuddyInfo(self):
        if "NOBUDDYINFO" in SysMgr.environList:
            return

        PAGE = SysMgr.PAGESIZE
        convSize = UtilMgr.convSize2Unit

        lineLen = 140
        SysMgr.infoBufferPrint("\n[Buddy Info]\n%s" % twoLine[:lineLen])
        orders = [
            "%9s"
            % (str(order) + "(%s)" % convSize((2**order) * PAGE, isInt=True))
            for order in range(0, 11)
        ]
        orders.append(" |%7s" % "TOTAL")
        SysMgr.infoBufferPrint(
            "{0:>5} | {1:<8} | {2:<1} |\n{3:1}".format(
                "NODE", "ZONE", " ".join(orders), twoLine[:lineLen]
            )
        )

        # get buddy info #
        buddy = SysMgr.readBuddyInfo()
        if not buddy:
            SysMgr.infoBufferPrint("    None")
            SysMgr.infoBufferPrint(twoLine[:lineLen])
            return

        # print buddy info #
        for node, data in buddy.items():
            for zone, orders in data.items():
                orderSize = [
                    (2**order) * PAGE * val
                    for order, val in enumerate(orders)
                ]
                orders = [
                    "%9s" % UtilMgr.convNum(order) if order else " " * 9
                    for order in orders
                ]
                orders.append(" |%7s" % convSize(sum(orderSize)))
                SysMgr.infoBufferPrint(
                    "{0:>5} | {1:<8} | {2:<1} |".format(
                        node, zone, " ".join(orders)
                    )
                )
                node = ""
            SysMgr.infoBufferPrint(oneLine[:lineLen])

    def printMmapInfo(self):
        if "NOMMAPINFO" in SysMgr.environList:
            return

        # get memory map #
        maps = SysMgr.readMemoryMap()

        # get maximum line length #
        lenLine = 9
        for value in maps.values():
            if lenLine < len(value["name"]):
                lenLine = len(value["name"])
        lenLine += 76

        # get non-root message #
        if not SysMgr.isRoot():
            msg = (
                " (Warn: Addresses are not readable "
                "because there is no root permission)"
            )
        else:
            msg = ""

        SysMgr.infoBufferPrint(
            "\n[Memory Map Info]%s\n%s" % (msg, twoLine[:lenLine])
        )
        SysMgr.infoBufferPrint(
            "{0:^35} | {1:^32} | {2:<1} {3:1}|\n{4:1}".format(
                "ADDR", "SIZE", "NAME", " " * (lenLine - 79), twoLine[:lenLine]
            )
        )

        if not maps:
            noneStr = "    None"
            SysMgr.infoBufferPrint(
                noneStr + (" " * (lenLine - len(noneStr) - 1)) + "|"
            )
            SysMgr.infoBufferPrint(twoLine[:lenLine])
            return

        # print memory map #
        for start, value in sorted(maps.items(), key=lambda e: long(e[0])):
            addr = "%16s - %16s" % (value["start"], value["end"])
            SysMgr.infoBufferPrint(
                "{0:>35} | {1:>32} | {2:<1} {3:1}|".format(
                    addr,
                    value["sizeUnit"],
                    value["name"],
                    " " * (lenLine - 75 - len(value["name"])),
                )
            )

        SysMgr.infoBufferPrint(twoLine[:lenLine])

    def printSchedInfo(self):
        if "NOSCHEDINFO" in SysMgr.environList:
            return

        # add JSON stats #
        if SysMgr.jsonEnable:
            SysMgr.jsonData.setdefault("general", {})
            SysMgr.jsonData["general"].setdefault("sched", {})
            jsonData = SysMgr.jsonData["general"]["sched"]
        else:
            jsonData = {}

        try:
            dpath = "/proc/sys/kernel"
            targetList = os.listdir(dpath)

            # get max length #
            try:
                maxLen = max(list(map(lambda x: len(x), targetList)))
            except SystemExit:
                sys.exit(0)
            except:
                maxLen = 36

            lineLen = maxLen + 23
            SysMgr.infoBufferPrint("\n[Sched Factor Info]")
            SysMgr.infoBufferPrint(twoLine[:lineLen])
            SysMgr.infoBufferPrint(
                "{0:^{maxLen}} | {1:^18} |".format(
                    "Factor", "Value", maxLen=maxLen
                )
            )
            SysMgr.infoBufferPrint(twoLine[:lineLen])

            for item in os.listdir(dpath):
                if not item.startswith("sched_"):
                    continue

                # check type #
                fpath = os.path.join(dpath, item)
                if os.path.isdir(fpath):
                    continue

                value = UtilMgr.convNum(SysMgr.readFile(fpath))
                SysMgr.infoBufferPrint(
                    "{0:<{maxLen}} | {1:>18} |".format(
                        item, value, maxLen=maxLen
                    )
                )

                if SysMgr.jsonEnable:
                    jsonData[item] = value

            SysMgr.infoBufferPrint(twoLine[:lineLen])
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to get sched factors", True)

    def printHugePageInfo(self):
        if "NOHUGEPAGEINFO" in SysMgr.environList:
            return

        dpath = "/sys/kernel/mm/hugepages"
        if not os.path.exists(dpath):
            return

        lineLen = 69
        SysMgr.infoBufferPrint("\n[HugePage Info]")
        SysMgr.infoBufferPrint(twoLine[:lineLen])
        SysMgr.infoBufferPrint(
            "{0:^16} | {1:^30} | {2:^15} |".format("Size", "Stat", "Value")
        )
        SysMgr.infoBufferPrint(twoLine[:lineLen])

        try:
            for item in os.listdir(dpath):
                size = UtilMgr.lstrip(item, "hugepages-")
                try:
                    sizeBytes = UtilMgr.convUnit2Size(size)
                    size = UtilMgr.convSize2Unit(sizeBytes)
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

                for stat in os.listdir(os.path.join(dpath, item)):
                    # check type #
                    fpath = os.path.join(dpath, item, stat)
                    if os.path.isdir(fpath):
                        continue

                    data = SysMgr.readFile(fpath, verb=False)
                    if not data:
                        continue

                    value = UtilMgr.convNum(data)
                    SysMgr.infoBufferPrint(
                        "{0:>16} | {1:<30} | {2:>15} |".format(
                            size, stat, value
                        )
                    )
                    size = ""
                SysMgr.infoBufferPrint(oneLine[:lineLen])
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to get HugePage stats", True)

    def printVmstatInfo(self):
        if "NOVMSTATINFO" in SysMgr.environList:
            return

        dpath = "/proc/sys/vm"
        if not os.path.exists(dpath):
            return

        # add JSON stats #
        if SysMgr.jsonEnable:
            SysMgr.jsonData.setdefault("general", {})
            SysMgr.jsonData["general"].setdefault("vm", {})
            jsonData = SysMgr.jsonData["general"]["vm"]
        else:
            jsonData = {}

        try:
            targetList = os.listdir(dpath)

            # get max length #
            try:
                maxLen = max(list(map(lambda x: len(x), targetList)))
            except SystemExit:
                sys.exit(0)
            except:
                maxLen = 40

            lineLen = maxLen + 21
            SysMgr.infoBufferPrint("\n[VM Info]")
            SysMgr.infoBufferPrint(twoLine[:lineLen])
            SysMgr.infoBufferPrint(
                "{0:^{maxLen}} | {1:^16} |".format(
                    "Stat", "Value", maxLen=maxLen
                )
            )
            SysMgr.infoBufferPrint(twoLine[:lineLen])

            for item in targetList:
                # check type #
                fpath = os.path.join(dpath, item)
                if os.path.isdir(fpath):
                    continue

                # read value #
                value = SysMgr.readFile(fpath, verb=False)
                if not value:
                    continue

                value = value.replace("\t", " ")
                if value.isdigit():
                    value = UtilMgr.convNum(value)

                SysMgr.infoBufferPrint(
                    "{0:<{maxLen}} | {1:>16} |".format(
                        item, value, maxLen=maxLen
                    )
                )

                if SysMgr.jsonEnable:
                    jsonData[item] = value

            SysMgr.infoBufferPrint(twoLine[:lineLen])
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to get VM stats", True)

    def printThresholdInfo(self):
        if "NOTHRESHOLDINFO" in SysMgr.environList:
            return

        # get max length #
        try:
            maxLen = max(list(map(lambda x: len(x), SysMgr.thrEvtCntList)))
        except SystemExit:
            sys.exit(0)
        except:
            maxLen = 30

        lineLen = maxLen + 20
        SysMgr.infoBufferPrint("\n[Threshold Info]\n%s" % twoLine[:lineLen])
        SysMgr.infoBufferPrint(
            "{0:^{maxLen}} | {1:^15} |\n{2:1}".format(
                "Event", "Count", oneLine[:lineLen], maxLen=maxLen
            )
        )

        for name, val in sorted(SysMgr.thrEvtCntList.items()):
            SysMgr.infoBufferPrint(
                "{0:^{maxLen}} | {1:>15} |".format(
                    name, UtilMgr.convNum(val), maxLen=maxLen
                )
            )

        if not SysMgr.thrEvtCntList:
            SysMgr.infoBufferPrint("{0:<49}|".format("    None"))

        SysMgr.infoBufferPrint(oneLine[:lineLen])

    def printPeakInfo(self):
        if "NOPEAKINFO" in SysMgr.environList:
            return

        # get peak items #
        peakList = SysMgr.getPeakList()

        # get max length #
        try:
            maxLen = max(list(map(lambda x: len(x), peakList)))
        except SystemExit:
            sys.exit(0)
        except:
            maxLen = 20

        lineLen = maxLen + 20
        SysMgr.infoBufferPrint(
            "\n[Peak Info] (Unit: %%/MB)\n%s" % twoLine[:lineLen]
        )
        SysMgr.infoBufferPrint(
            "{0:<{maxLen}} | {1:^15} |\n{2:1}".format(
                "Resource", "Value", oneLine[:lineLen], maxLen=maxLen
            )
        )

        for name in UtilMgr.sort(list(peakList)):
            val = peakList[name]
            if val == SysMgr.maxSize:
                val = 0

            SysMgr.infoBufferPrint(
                "{0:<{maxLen}} | {1:>15} |".format(
                    name, UtilMgr.convNum(val), maxLen=maxLen
                )
            )

        SysMgr.infoBufferPrint(oneLine[:lineLen])

    def printKsmInfo(self):
        if "NOKSMINFO" in SysMgr.environList:
            return

        dpath = "/sys/kernel/mm/ksm"
        if not os.path.exists(dpath):
            return

        convSize = UtilMgr.convSize2Unit
        convNum = UtilMgr.convNum

        # add JSON stats #
        if SysMgr.jsonEnable:
            SysMgr.jsonData.setdefault("general", {})
            SysMgr.jsonData["general"].setdefault("ksm", {})
            jsonData = SysMgr.jsonData["general"]["ksm"]
        else:
            jsonData = {}

        # check and get total MERGEABLE size #
        mergeStr = ""
        if "PRINTMERGE" in SysMgr.environList:
            # check root permission #
            SysMgr.checkRootPerm()

            totalRss = totalPss = 0
            for pid in SysMgr.getPidList():
                if not pid.isdigit():
                    continue

                tobj = SysMgr.initTaskMon(pid, update=False)
                TaskAnalyzer.saveProcSmaps(
                    None, pid, True, ["mg"], tobj.procData
                )
                maps = tobj.procData[pid]["maps"]
                if not maps or not maps["FLAG"]:
                    continue

                try:
                    totalRss += long(
                        tobj.procData[pid]["maps"]["FLAG"]["Rss:"]
                    )
                    totalPss += long(
                        tobj.procData[pid]["maps"]["FLAG"]["Pss:"]
                    )
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

            if totalRss:
                mergeStr = " (Total: %s) (Resident: %s(%.1f%%))" % (
                    convSize(totalRss << 10),
                    convSize(totalPss << 10),
                    (totalPss / float(totalRss)) * 100,
                )

        try:
            targetList = sorted(os.listdir(dpath))

            # get max length #
            try:
                maxLen = max(list(map(lambda x: len(x), targetList)))
            except SystemExit:
                sys.exit(0)
            except:
                maxLen = 40

            lineLen = maxLen + 31
            SysMgr.infoBufferPrint(
                "\n[KSM Info] (Path: /sys/kernel/mm/ksm)%s" % mergeStr
            )
            SysMgr.infoBufferPrint(twoLine[:lineLen])
            SysMgr.infoBufferPrint(
                "{0:^{maxLen}} | {1:^15} | {2:^8} |".format(
                    "Stat", "Value", "Size", maxLen=maxLen
                )
            )
            SysMgr.infoBufferPrint(twoLine[:lineLen])

            total = 0
            for item in targetList:
                # check type #
                fpath = os.path.join(dpath, item)
                if os.path.isdir(fpath):
                    continue

                # get value #
                data = SysMgr.readFile(fpath)
                value = convNum(data)

                # get size #
                if item.startswith("pages_"):
                    size = convSize(long(data) * SysMgr.PAGESIZE)
                else:
                    size = ""

                SysMgr.infoBufferPrint(
                    "{0:<{maxLen}} | {1:>15} | {2:>8} |".format(
                        item, value, size, maxLen=maxLen
                    )
                )

                if SysMgr.jsonEnable:
                    jsonData[item] = value

                # accumulate pages #
                if item in ("pages_shared", "pages_sharing", "pages_unshared"):
                    total += long(data)

            # print total pages #
            SysMgr.infoBufferPrint(
                "{3:1}\n{0:<{maxLen}} | {1:>15} | {2:>8} |".format(
                    "total",
                    convNum(total),
                    convSize(total * SysMgr.PAGESIZE),
                    oneLine[:lineLen],
                    maxLen=maxLen,
                )
            )

            SysMgr.infoBufferPrint(twoLine[:lineLen])
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to get KSM stats", True)

    def printSchedFeatInfo(self):
        if "NOSCHEDFEATINFO" in SysMgr.environList:
            return

        # set max length #
        if SysMgr.outPath:
            maxLen = SysMgr.lineLength
        else:
            maxLen = min(SysMgr.lineLength, SysMgr.ttyCols)

        twoLineLoc = twoLine[:maxLen]

        def _printItems(status, items):
            indentStr = " " * 10
            typeStr = "{0:^10}".format(status)

            for item in items:
                curLineLen = len(typeStr.split("\n")[-1]) + len(item) + 3
                if curLineLen >= maxLen:
                    tailStr = " " * (maxLen - (curLineLen - len(item) - 2))
                    typeStr += "%s|\n%s" % (tailStr, indentStr)
                typeStr += " | %s" % item

            curLineLen = len(typeStr.split("\n")[-1])
            tailStr = " " * (maxLen - curLineLen - 1)
            typeStr += "%s|" % tailStr

            SysMgr.infoBufferPrint(typeStr)

        # get sched features #
        features = SysMgr.readSchedFeatures()
        if not features:
            return

        SysMgr.infoBufferPrint("\n[Sched Feature Info]")
        SysMgr.infoBufferPrint(twoLineLoc)
        SysMgr.infoBufferPrint(
            "{0:^10} | {1:<{ilen}}|".format(
                "STATUS", "FEATURES", ilen=maxLen - 14
            )
        )
        SysMgr.infoBufferPrint(twoLineLoc)

        # distinguish items #
        enabled = []
        disabled = []
        for item in features:
            if item.startswith("NO_"):
                disabled.append(item)
            else:
                enabled.append(item)

        # print enabled items #
        if enabled:
            _printItems("ENABLE", enabled)

        # print disabled items #
        if enabled:
            _printItems("DISABLE", disabled)

        SysMgr.infoBufferPrint(twoLineLoc)

    def printLimitInfo(self):
        if not self.limitData:
            return

        if "NOLIMITINFO" in SysMgr.environList:
            return

        # add JSON stats #
        if SysMgr.jsonEnable:
            SysMgr.jsonData.setdefault("general", {})
            SysMgr.jsonData["general"].setdefault("limit", {})
            jsonData = SysMgr.jsonData["general"]["limit"]
        else:
            jsonData = {}

        limitData = self.limitData
        title = limitData.pop(0)

        lineLen = 80
        twoLineLoc = twoLine[:lineLen]

        SysMgr.infoBufferPrint("\n[Task Limit Info]")
        SysMgr.infoBufferPrint(twoLineLoc)
        SysMgr.infoBufferPrint("{0:<78} |".format(title.rstrip()))
        SysMgr.infoBufferPrint(twoLineLoc)

        for line in self.limitData[1:]:
            limit = line.rstrip()
            SysMgr.infoBufferPrint("{0:<78} |".format(limit))

            if not SysMgr.jsonEnable:
                continue

            try:
                items = limit.split()
                name = " ".join(items[:-4])
                jsonData.setdefault(name, {})
                jsonData[name]["soft"] = items[-3]
                jsonData[name]["hard"] = items[-2]
                jsonData[name]["unit"] = items[-1]
            except SystemExit:
                sys.exit(0)
            except:
                pass

        SysMgr.infoBufferPrint(twoLineLoc)

    def printSystemInfo(self):
        if "NOSYSINFO" in SysMgr.environList:
            return

        # add JSON stats #
        if SysMgr.jsonEnable:
            SysMgr.jsonData.setdefault("general", {})
            jsonData = SysMgr.jsonData["general"]
        else:
            jsonData = {}

        convNum = UtilMgr.convNum
        convTime = UtilMgr.convTime

        if SysMgr.outPath:
            maxLineLen = SysMgr.lineLength
        else:
            maxLineLen = min(SysMgr.lineLength, SysMgr.ttyCols)
        indent = 20
        indentRes = maxLineLen - indent - 1

        twoLineLoc = twoLine[:maxLineLen]

        SysMgr.infoBufferPrint("\n\n[System General Info]")
        SysMgr.infoBufferPrint(twoLineLoc)
        SysMgr.infoBufferPrint(
            "{0:^20} {1:<{itemLen}}|".format(
                "Type", "Information", itemLen=maxLineLen - 22
            )
        )
        SysMgr.infoBufferPrint(twoLineLoc)

        # launch option #
        try:
            launchOption = " ".join(sys.argv) + " -"
            SysMgr.infoBufferPrint(
                "{0:20} # {1:<1}".format("Launch", launchOption)
            )

            if SysMgr.jsonEnable:
                jsonData["launch"] = launchOption
        except:
            pass

        # version #
        try:
            SysMgr.infoBufferPrint(
                "{0:20} {1:<1}".format("Version", "%s" % __version__)
            )

            if SysMgr.jsonEnable:
                jsonData["version"] = __version__
        except:
            pass

        # python #
        try:
            pv = ".".join(list(map(str, sys.version_info)))
            SysMgr.infoBufferPrint("{0:20} {1:<1}".format("Python", pv))

            if SysMgr.jsonEnable:
                jsonData["python"] = pv
        except:
            pass

        # hostname #
        try:
            hostname = self.uname[1]
            SysMgr.infoBufferPrint("{0:20} {1:<1}".format("Host", hostname))

            if SysMgr.jsonEnable:
                jsonData["host"] = hostname
        except:
            pass

        # IP #
        try:
            ip = NetworkMgr.getPublicIp()
            SysMgr.infoBufferPrint("{0:20} {1:<1}".format("IP", ip))

            if SysMgr.jsonEnable:
                jsonData["ip"] = ip
        except:
            pass

        # MAC #
        try:
            if not self.macAddr:
                raise Exception("no MAC")

            macStr = "%s_%s" % (self.macAddr[0], self.macAddr[1])
            SysMgr.infoBufferPrint("{0:20} {1:<10}".format("Mac", macStr))

            if SysMgr.jsonEnable:
                jsonData["mac"] = macStr
        except:
            pass

        # CPU #
        try:
            SysMgr.infoBufferPrint("{0:20} {1:<1}".format("CPU", SysMgr.arch))

            if SysMgr.jsonEnable:
                jsonData["arch"] = SysMgr.arch
        except:
            pass

        # GPU #
        try:
            gpuList = SysMgr.getGpuList()
            if not gpuList:
                raise Exception("no GPU")

            name = "GPU"
            for item in gpuList:
                SysMgr.infoBufferPrint("{0:20} {1:<1}".format(name, item))
                name = " "

            if SysMgr.jsonEnable:
                jsonData["gpu"] = gpuList
        except:
            pass

        # OS #
        try:
            osInfo = self.uname[0]
            SysMgr.infoBufferPrint("{0:20} {1:<1}".format("OS", osInfo))

            if SysMgr.jsonEnable:
                jsonData["os"] = osInfo
        except:
            pass

        # kernel #
        try:
            kernelInfo = self.uname[2]
            SysMgr.infoBufferPrint(
                "{0:20} {1:<1}".format("Kernel", kernelInfo)
            )

            if SysMgr.jsonEnable:
                jsonData["kernel"] = kernelInfo
        except:
            pass

        # release #
        try:
            releaseInfo = self.uname[3]
            SysMgr.infoBufferPrint(
                "{0:20} {1:<1}".format("Release", releaseInfo)
            )

            if SysMgr.jsonEnable:
                jsonData["release"] = releaseInfo
        except:
            pass

        # user #
        try:
            uid = str(SysMgr.getUid("self"))

            SysMgr.infoBufferPrint(
                "{0:20} {1:<1}".format("User", self.userData[uid]["name"])
            )

            if SysMgr.jsonEnable:
                jsonData["user"] = self.userData[uid]["name"]
        except:
            pass

        # RTC #
        try:
            timeInfo = "%s %s" % (
                self.systemInfo["date"],
                self.systemInfo["time"],
            )
            SysMgr.infoBufferPrint("{0:20} {1:<1}".format("RTC", timeInfo))

            if SysMgr.jsonEnable:
                jsonData["rtc"] = timeInfo
        except:
            pass

        # date #
        try:
            dtime, ntime = UtilMgr.getTime().rstrip("Z").split("T")
            SysMgr.infoBufferPrint(
                "{0:20} {1:<1}".format("Date", "%s %s" % (dtime, ntime))
            )

            if SysMgr.jsonEnable:
                jsonData["date"] = dtime
                jsonData["time"] = ntime
        except:
            pass

        # epoch2Start #
        try:
            e2s = UtilMgr.getEpoch2Start()

            SysMgr.infoBufferPrint("{0:20} {1:<1}".format("Epoch2Start", e2s))

            if SysMgr.jsonEnable:
                jsonData["epoch2Start"] = e2s
        except:
            pass

        # starttime #
        try:
            startTimeNum = long(str(SysMgr.startTime).split(".")[0])
            startTime = convTime(startTimeNum)
            startTimeSec = convNum(startTimeNum)
            startTimeStr = "%s (%s sec)" % (startTime, startTimeSec)
            SysMgr.infoBufferPrint(
                "{0:20} {1:<1}".format("Start", startTimeStr)
            )

            if SysMgr.jsonEnable:
                jsonData["starttime"] = startTime
        except:
            pass

        # endtime #
        try:
            endTime = convTime(SysMgr.uptime)
            endTimeSec = convNum(SysMgr.uptime)
            endTimeStr = "%s (%s sec)" % (endTime, endTimeSec)
            SysMgr.infoBufferPrint("{0:20} {1:<1}".format("End", endTimeStr))

            if SysMgr.jsonEnable:
                jsonData["endtime"] = endTime
                jsonData["uptime"] = endTime
        except:
            pass

        # runtime #
        try:
            runtime = SysMgr.getRuntime(sec=True)
            runtimeSec = convNum(runtime)
            runtime = convTime(runtime)
            runtimeStr = "%s (%s sec)" % (runtime, runtimeSec)
            SysMgr.infoBufferPrint(
                "{0:20} {1:<1}".format("Runtime", runtimeStr)
            )

            if SysMgr.jsonEnable:
                jsonData["runtime"] = runtime
        except:
            pass

        # init overhead for python #
        try:
            if SysMgr.startOverheadTime == 0:
                overhead = SysMgr.startInitTime - SysMgr.startRunTime
            else:
                overhead = SysMgr.startOverheadTime
            overhead = "%.3f sec" % overhead
            SysMgr.infoBufferPrint(
                "{0:20} {1:<1}".format("InitOverhead", overhead)
            )

            if SysMgr.jsonEnable:
                jsonData["initOverhead"] = overhead
        except:
            pass

        # recording overhead #
        try:
            if SysMgr.startRecTime > 0:
                overhead = "%.3f sec" % (
                    SysMgr.startRecTime - SysMgr.startInitTime
                )
                SysMgr.infoBufferPrint(
                    "{0:20} {1:<1}".format("RecOverhead", overhead)
                )

                if SysMgr.jsonEnable:
                    jsonData["recOverhead"] = overhead
        except:
            pass

        # load #
        try:
            SysMgr.infoBufferPrint(
                "{0:20} {1:<1} / {2:<1} / {3:<1}".format(
                    "Load",
                    "%s(1m)" % self.loadData[0],
                    "%s(5m)" % self.loadData[1],
                    "%s(15m)" % self.loadData[2],
                )
            )

            if SysMgr.jsonEnable:
                jsonData["load1m"] = self.loadData[0]
                jsonData["load5m"] = self.loadData[1]
                jsonData["load15m"] = self.loadData[2]
        except:
            pass

        # task #
        try:
            try:
                maxThd = " / %s(max)" % convNum(self.nrMaxThread)
            except:
                maxThd = ""

            running, total = self.loadData[3].split("/")
            SysMgr.infoBufferPrint(
                "{0:20} {1:<10}".format(
                    "Threads",
                    "%s(running) / %s(total)%s"
                    % (convNum(running), convNum(total), maxThd),
                )
            )

            if SysMgr.jsonEnable:
                jsonData["nrRunTask"] = running
                jsonData["nrTotalTask"] = total
        except:
            pass

        # oomkill #
        try:
            vmData = SysMgr.getVmstat(retDict=True)
            oomKill = long(vmData["oom_kill"])
            SysMgr.infoBufferPrint(
                "{0:20} {1:<10}".format("OOMKill", convNum(oomKill))
            )

            if SysMgr.jsonEnable:
                jsonData["oomKill"] = oomKill
        except:
            pass

        # last PID #
        try:
            SysMgr.infoBufferPrint(
                "{0:20} {1:<10}".format("LastPid", self.loadData[4])
            )

            if SysMgr.jsonEnable:
                jsonData["lastPid"] = self.loadData[4]
        except:
            pass

        # open files #
        try:
            SysMgr.infoBufferPrint(
                "{0:20} {1:<1}(cur) / {2:<1}(max)".format(
                    "OpenFile",
                    convNum(self.openFileData["cur"]),
                    convNum(self.openFileData["max"]),
                )
            )

            if SysMgr.jsonEnable:
                jsonData["nrCurOpenFile"] = self.openFileData["cur"]
                jsonData["nrMaxOpenFile"] = self.openFileData["max"]
        except:
            pass

        # battery #
        try:
            SysMgr.updateBatInfo()

            if SysMgr.battery:
                batStr = "%d%% / %s / %s" % (
                    SysMgr.battery[0],
                    convTime(SysMgr.battery[1]),
                    "+" if SysMgr.battery[2] else "-",
                )

                SysMgr.infoBufferPrint(
                    "{0:20} {1:<10}".format("Battery", batStr)
                )

                if SysMgr.jsonEnable:
                    jsonData["battery"] = batStr
        except:
            pass

        # kernel args #
        try:
            title = "Cmdline"
            cmdline = UtilMgr.convLineStr(
                self.cmdlineData,
                "",
                indent,
                indentRes,
                startIndent=False,
            ).lstrip()
            SysMgr.infoBufferPrint("{0:20} {1:<1}".format(title, cmdline))

            if SysMgr.jsonEnable:
                jsonData["cmdline"] = self.cmdlineData
        except:
            pass

        # memo #
        try:
            if not "MEMO" in SysMgr.environList:
                raise Exception("no memo")

            memo = SysMgr.environList["MEMO"][0]

            SysMgr.infoBufferPrint("{0:20} {1:<1}".format("Memo", memo))

            if SysMgr.jsonEnable:
                jsonData["memo"] = memo
        except:
            pass

        # report file #
        try:
            if not SysMgr.outPath:
                raise Exception("no out path")

            outPath = SysMgr.convFullPath(SysMgr.outPath)

            outPathNew = UtilMgr.convLineStr(
                outPath,
                "",
                indent,
                indentRes,
                startIndent=False,
            ).lstrip()

            SysMgr.infoBufferPrint(
                "{0:20} {1:<1}".format("ReportPath", outPathNew)
            )

            if SysMgr.jsonEnable:
                jsonData["reportPath"] = outPath
        except:
            pass

        # report reason #
        if SysMgr.reportReason:
            SysMgr.infoBufferPrint(
                "{0:20} {1:<1}".format("ReportReason", SysMgr.reportReason)
            )

            if SysMgr.jsonEnable:
                jsonData["reportReason"] = SysMgr.reportReason

        # report last DLT file #
        try:
            lastDltFile = DltAnalyzer.getTraceFileList()[-1]
            outPath = UtilMgr.convLineStr(
                lastDltFile,
                "",
                indent,
                indentRes,
                startIndent=False,
            ).lstrip()

            SysMgr.infoBufferPrint(
                "{0:20} {1:<1}".format("lastDltFile", outPath)
            )

            if SysMgr.jsonEnable:
                jsonData["lastDltFile"] = lastDltFile
        except:
            pass

        SysMgr.infoBufferPrint(twoLineLoc)

    def printCpuCacheInfo(self):
        if not self.cpuCacheInfo:
            return

        if "NOCPUINFO" in SysMgr.environList:
            return

        # add JSON stats #
        if SysMgr.jsonEnable:
            SysMgr.jsonData.setdefault("general", {})
            SysMgr.jsonData["general"]["cache"] = {}
            jsonData = SysMgr.jsonData["general"]["cache"]
        else:
            jsonData = {}

        cnt = 0
        lineLen = 23
        cacheInfo = {}

        try:
            for core, info in sorted(
                self.cpuCacheInfo.items(), key=lambda e: long(e[0][3:])
            ):
                try:
                    name = core[3:]
                    value = info.strip()
                    cacheInfo[name] = value

                    thisLine = len(value) + 23
                    if thisLine > lineLen:
                        lineLen = thisLine

                    cnt += 1

                    if SysMgr.jsonEnable:
                        jsonData[name] = value
                except:
                    pass
        except:
            pass

        oneLineLoc = oneLine[:lineLen]
        twoLineLoc = twoLine[:lineLen]

        SysMgr.infoBufferPrint("\n[System CPU Cache Info]")
        SysMgr.infoBufferPrint(twoLineLoc)
        SysMgr.infoBufferPrint(
            "{0:<20} {1:{pd}} |".format("Core", "Information", pd=lineLen - 23)
        )
        SysMgr.infoBufferPrint(twoLineLoc)

        for core, info in sorted(cacheInfo.items(), key=lambda e: long(e[0])):
            SysMgr.infoBufferPrint(
                "{0:<20} {1:<{pd}} |".format(core, info, pd=lineLen - 23)
            )

        if cnt == 0:
            SysMgr.infoBufferPrint(
                "{0:<23}|\n{1:1}".format("    None", oneLineLoc)
            )

        SysMgr.infoBufferPrint(twoLineLoc)

    def printCpuInfo(self):
        if not self.cpuData:
            return

        if "NOCPUINFO" in SysMgr.environList:
            return

        # parse data #
        for l in self.cpuData:
            m = re.match(r"(?P<type>.*):\s+(?P<val>.*)", l)
            if not m:
                continue

            d = m.groupdict()
            self.cpuInfo[d["type"][0 : len(d["type"]) - 1]] = d["val"]

        # add JSON stats #
        if SysMgr.jsonEnable:
            SysMgr.jsonData.setdefault("general", {})
            SysMgr.jsonData["general"]["cpu"] = {}
            jsonData = SysMgr.jsonData["general"]["cpu"]
        else:
            jsonData = {}

        # get maximum item length #
        itemLen = 20
        for i in ("vendor_id", "model name", "address sizes"):
            if not i in self.cpuInfo:
                continue
            itemLen = max(itemLen, len(self.cpuInfo[i]))

        lineLen = itemLen + 23
        twoLineLoc = twoLine[:lineLen]

        SysMgr.infoBufferPrint("\n[System CPU Info]")
        SysMgr.infoBufferPrint(twoLineLoc)
        SysMgr.infoBufferPrint(
            "{0:^20} {1:^{ilen}} |".format("Type", "Information", ilen=itemLen)
        )
        SysMgr.infoBufferPrint(twoLineLoc)

        try:
            physical = long(self.cpuInfo["physical id"]) + 1
            SysMgr.infoBufferPrint(
                "{0:20} {1:<{ilen}} |".format(
                    "Physical", physical, ilen=itemLen
                )
            )

            if SysMgr.jsonEnable:
                jsonData["physical"] = physical
        except:
            pass

        try:
            SysMgr.infoBufferPrint(
                "{0:20} {1:<{ilen}} |".format(
                    "CoresPerCPU", self.cpuInfo["cpu cores"], ilen=itemLen
                )
            )

            if SysMgr.jsonEnable:
                jsonData["corePerCPU"] = self.cpuInfo["cpu cores"]
        except:
            pass

        try:
            logical = long(self.cpuInfo["processor"]) + 1
            SysMgr.infoBufferPrint(
                "{0:20} {1:<{ilen}} |".format("Logical", logical, ilen=itemLen)
            )

            if SysMgr.jsonEnable:
                jsonData["logical"] = logical
        except:
            pass

        try:
            SysMgr.infoBufferPrint(
                "{0:20} {1:<{ilen}} |".format(
                    "Vendor", self.cpuInfo["vendor_id"], ilen=itemLen
                )
            )

            if SysMgr.jsonEnable:
                jsonData["vendor"] = self.cpuInfo["vendor_id"]
        except:
            pass

        try:
            SysMgr.infoBufferPrint(
                "{0:20} {1:<{ilen}} |".format(
                    "Model", self.cpuInfo["model name"], ilen=itemLen
                )
            )

            if SysMgr.jsonEnable:
                jsonData["model"] = self.cpuInfo["model name"]
        except:
            pass

        try:
            SysMgr.infoBufferPrint(
                "{0:20} {1:<{ilen}} |".format(
                    "Cache(L2)", self.cpuInfo["cache size"], ilen=itemLen
                )
            )

            if SysMgr.jsonEnable:
                jsonData["cacheL2"] = self.cpuInfo["cache size"]
        except:
            pass

        try:
            SysMgr.infoBufferPrint(
                "{0:20} {1:<{ilen}} |".format(
                    "Perf", self.cpuInfo["bogomips"], ilen=itemLen
                )
            )

            if SysMgr.jsonEnable:
                jsonData["perf"] = self.cpuInfo["bogomips"]
        except:
            pass

        try:
            SysMgr.infoBufferPrint(
                "{0:20} {1:<{ilen}} |".format(
                    "Address", self.cpuInfo["address sizes"], ilen=itemLen
                )
            )

            if SysMgr.jsonEnable:
                jsonData["address"] = self.cpuInfo["address sizes"]
        except:
            pass

        # oneline #
        for item in ("online", "possible", "present"):
            try:
                with open("/sys/devices/system/cpu/%s" % item, "r") as fd:
                    data = fd.readline().strip()

                SysMgr.infoBufferPrint(
                    "{0:20} {1:<{ilen}} |".format(
                        item.title(), data, ilen=itemLen
                    )
                )

                if SysMgr.jsonEnable:
                    jsonData[item] = data
            except:
                pass

        SysMgr.infoBufferPrint(twoLineLoc)

    def updateDiskInfo(self, time, data):
        self.diskInfo[time] = {}

        if not SysMgr.isLinux:
            return
        elif not data:
            return

        for l in data:
            diskStat = {}
            for idx, item in enumerate(list(l.split())):
                if len(ConfigMgr.DISKSTAT) <= idx:
                    SysMgr.printWarn(
                        "failed to parse all diskstat because of overflow"
                    )
                    break
                else:
                    diskStat[ConfigMgr.DISKSTAT[idx]] = item

            self.diskInfo[time][diskStat["name"]] = {}
            diskInfoBuf = self.diskInfo[time][diskStat["name"]]

            # save recent stat #
            diskInfoBuf["major"] = diskStat["major"]
            diskInfoBuf["minor"] = diskStat["minor"]
            diskInfoBuf["sectorRead"] = diskStat["sectorRead"]
            diskInfoBuf["readTime"] = diskStat["readTime"]
            diskInfoBuf["sectorWrite"] = diskStat["sectorWrite"]
            diskInfoBuf["writeTime"] = diskStat["writeTime"]
            diskInfoBuf["currentIO"] = diskStat["currentIO"]
            diskInfoBuf["ioTime"] = diskStat["ioTime"]
            diskInfoBuf["ioWTime"] = diskStat["ioWTime"]

    def updateMountInfo(self):
        if not SysMgr.isLinux:
            return
        elif not self.mountData:
            return

        class MountException(Exception):
            pass

        rootdevPath = SysMgr.getRootDevice()

        if self.mountUpdated:
            # parse mount info #
            for l in self.mountData:
                # leave for /proc/mounts #
                # dev, path, fs, option, etc1, etc2 = l.split()

                # split mount info #
                values = l.split(" - ")
                if len(values) != 2:
                    continue
                left = values[0]
                right = values[1]

                # split left-side part #
                left = left.split()
                mountid, parentid, devid, root, path = left[:5]
                major, minor = devid.split(":")
                option = " ".join(left[5:-1])

                # split right-side part #
                right = right.split()
                fs, dev = right[0:2]
                soption = " ".join(right[2:])

                # convert root device path #
                if dev == "/dev/root" and rootdevPath:
                    dev = rootdevPath

                # check skip condition #
                try:
                    # special device #
                    if not dev.startswith("/"):
                        rpath = path
                    else:
                        rpath = os.path.realpath(dev)
                        dev = os.path.basename(rpath)

                    if fs == "tmpfs":
                        raise MountException
                        continue

                    if ":" in dev:
                        major, minor = dev.split(":")
                        for mp in self.diskInfo["prev"].values():
                            if mp["major"] == major and mp["minor"] == minor:
                                raise MountException

                    # check nodes by device ID #
                    if not dev in self.diskInfo["prev"]:
                        for node, attr in self.diskInfo["prev"].items():
                            if (
                                attr["major"] == major
                                and attr["minor"] == minor
                            ):
                                raise MountException
                        continue
                except MountException:
                    pass
                except:
                    continue

                # check previous mount #
                if rpath in self.mountInfo:
                    SysMgr.printWarn(
                        "'%s' is already mounted on '%s' but remounted on '%s'"
                        % (rpath, self.mountInfo[rpath]["path"], path)
                    )
                    continue

                # save mount info #
                self.mountInfo[rpath] = {
                    "major": major,
                    "minor": minor,
                    "mountid": mountid,
                    "path": path,
                    "fs": fs,
                    "option": option,
                    "soption": soption,
                }

        for name, subdir in self.devArchInfo.items():
            for node, devid in subdir.items():
                if node in self.mountInfo:
                    continue

                major, minor = devid.strip().split(":")

                # save mount info #
                self.mountInfo[node] = {
                    "major": major,
                    "minor": minor,
                    "mountid": "-1",
                    "path": "-",
                    "fs": "-",
                    "option": "",
                    "soption": "",
                }

    def updateShmInfo(self):
        if not SysMgr.isLinux:
            return

        # read shm buf #
        try:
            data = SysMgr.readProcStat(
                SysMgr.shmFd, "sysvipc/shm", SysMgr, "shmFd"
            )[1:]
        except SystemExit:
            sys.exit(0)
        except:
            return

        # backup shm data #
        try:
            self.prevIpcData["shm"] = self.ipcData["shm"]
        except:
            pass

        # initialize shm variable #
        self.ipcData["shm"] = {}

        # parse new shm data #
        for line in data:
            try:
                (
                    key,
                    shmid,
                    perms,
                    size,
                    cpid,
                    lpid,
                    nattch,
                    uid,
                    gid,
                    cuid,
                    cgid,
                    atime,
                    dtime,
                    ctime,
                    rss,
                    swap,
                ) = line.split()[:16]
            except SystemExit:
                sys.exit(0)
            except:
                continue

            if key == "key":
                continue

            shmData = self.ipcData["shm"][shmid] = {}
            shmData["key"] = key
            shmData["perms"] = perms
            shmData["size"] = long(size)
            shmData["rss"] = long(rss)
            shmData["swap"] = long(swap)
            shmData["uid"] = uid
            shmData["gid"] = gid
            shmData["cpid"] = cpid
            shmData["lpid"] = lpid
            shmData["nattch"] = nattch
            shmData["atime"] = dtime
            shmData["dtime"] = dtime
            shmData["ctime"] = ctime

    def updateMsgqInfo(self):
        if not SysMgr.isLinux:
            return

        # read msgq buf #
        try:
            data = SysMgr.readProcStat(
                SysMgr.msgqFd, "sysvipc/msg", SysMgr, "msgqFd"
            )[1:]
        except SystemExit:
            sys.exit(0)
        except:
            return

        # backup msgq data #
        try:
            self.prevIpcData["msgq"] = self.ipcData["msgq"]
        except:
            pass

        # initialize msgq variable #
        self.ipcData["msgq"] = {}

        # parse new msgq data #
        for line in data:
            try:
                (
                    key,
                    msgqid,
                    perms,
                    cbytes,
                    qnum,
                    lspid,
                    lrpid,
                    uid,
                    gid,
                    cuid,
                    cgid,
                    stime,
                    rtime,
                    ctime,
                ) = line.split()
            except:
                pass

            msgqData = self.ipcData["msgq"][msgqid] = {}
            msgqData["key"] = key
            msgqData["perms"] = perms
            msgqData["cbytes"] = cbytes
            msgqData["qnum"] = qnum
            msgqData["lspid"] = lspid
            msgqData["lrpid"] = lrpid
            msgqData["uid"] = uid
            msgqData["gid"] = gid
            msgqData["cuid"] = cuid
            msgqData["cgid"] = cgid
            msgqData["stime"] = stime
            msgqData["rtime"] = rtime
            msgqData["ctime"] = ctime

    def updateSemInfo(self):
        if not SysMgr.isLinux:
            return

        # read sem buf #
        try:
            data = SysMgr.readProcStat(
                SysMgr.semFd, "sysvipc/sem", SysMgr, "semFd"
            )[1:]
        except SystemExit:
            sys.exit(0)
        except:
            return

        # backup sem data #
        try:
            self.prevIpcData["sem"] = self.ipcData["sem"]
        except:
            pass

        # initialize sem variable #
        self.ipcData["sem"] = {}

        # parse new sem data #
        for line in data:
            try:
                (
                    key,
                    semid,
                    perms,
                    nsems,
                    uid,
                    gid,
                    cuid,
                    cgid,
                    otime,
                    ctime,
                ) = line.split()
            except:
                pass

            semData = self.ipcData["sem"][semid] = {}
            semData["key"] = key
            semData["perms"] = perms
            semData["nsems"] = nsems
            semData["uid"] = uid
            semData["gid"] = gid
            semData["cuid"] = cuid
            semData["cgid"] = cgid
            semData["otime"] = otime
            semData["ctime"] = ctime

    def saveMacAddr(self):
        if not SysMgr.isLinux:
            return
        elif self.macAddr:
            return

        # mac address #
        try:
            self.macAddr = NetworkMgr.getMainMacAddr()
        except SystemExit:
            sys.exit(0)
        except:
            pass

    def updateIPCInfo(self):
        if not SysMgr.isLinux:
            return

        # check update time #
        if self.ipcUpdate == SysMgr.uptime:
            return
        else:
            self.ipcUpdate = SysMgr.uptime

        # shared memory #
        self.updateShmInfo()

        # message queue #
        self.updateMsgqInfo()

        # semaphore #
        self.updateSemInfo()

    def updateNetworkInfoGen(self):
        # get psutil object #
        psutil = SysMgr.getPkg("psutil")

        try:
            # netConn = psutil.net_connections(kind='inet')
            # nicStat = psutil.net_if_stats()

            # define new device list #
            devList = []

            # get IP address for device #
            addrList = psutil.net_if_addrs()
            for dev, addrItems in addrList.items():
                for item in addrItems:
                    if not item or not item[0]:
                        continue
                    elif len(addrItems) > 1 and not "AF_INET" in str(item[0]):
                        continue
                    elif dev in devList:
                        continue

                    # update ip address #
                    devList.append(dev)
                    self.networkInfo.setdefault(dev, {})
                    family, addr, mask, broadcast, ptp = item
                    self.networkInfo[dev]["ipaddr"] = addr

            # remove devices #
            for dev in list(self.networkInfo):
                if not dev in devList:
                    self.networkInfo.pop(dev, None)

            statData = [0, 0, 0, 0, 0, 0, 0, 0]

            # get stats for device #
            statList = psutil.net_io_counters(pernic=True, nowrap=True)
            for dev, value in statList.items():
                if not dev in self.networkInfo:
                    continue

                (
                    tsize,
                    rsize,
                    tpacket,
                    rpacket,
                    rerr,
                    terr,
                    rdrop,
                    tdrop,
                ) = value

                # update recv #
                if "recv" in self.networkInfo[dev]:
                    rvalue = self.networkInfo[dev]["recv"]
                    self.networkInfo[dev]["rdiff"] = [
                        rsize - rvalue[0],
                        rpacket - rvalue[1],
                        rerr - rvalue[2],
                        rdrop - rvalue[3],
                        0,
                        0,
                        0,
                        0,
                    ]
                else:
                    self.networkInfo[dev]["rdiff"] = statData

                self.networkInfo[dev]["recv"] = [
                    rsize,
                    rpacket,
                    rerr,
                    rdrop,
                    0,
                    0,
                    0,
                    0,
                ]

                # update tran #
                if "tran" in self.networkInfo[dev]:
                    tvalue = self.networkInfo[dev]["tran"]
                    self.networkInfo[dev]["tdiff"] = [
                        tsize - tvalue[0],
                        tpacket - tvalue[1],
                        terr - tvalue[2],
                        tdrop - tvalue[3],
                        0,
                        0,
                        0,
                        0,
                    ]
                else:
                    self.networkInfo[dev]["tdiff"] = statData

                self.networkInfo[dev]["tran"] = [
                    tsize,
                    tpacket,
                    terr,
                    tdrop,
                    0,
                    0,
                    0,
                    0,
                ]
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn("failed to get general network info", reason=True)
            return

    def updateNetworkInfo(self):
        if not SysMgr.isLinux:
            return

        # check update time #
        if (
            self.netUpdate
            and SysMgr.uptime - self.netUpdate < SysMgr.intervalEnable
        ):
            return
        else:
            self.netUpdate = SysMgr.uptime

        # get device data #
        data = SysMgr.getNetDevData()
        if not data:
            return

        # import packages #
        try:
            socket = SysMgr.getPkg("socket", False)
            fcntl = SysMgr.getPkg("fcntl", False)
        except SystemExit:
            sys.exit(0)
        except:
            socket = fcntl = None

        try:
            for line in data:
                dev, stats = line.split(":")

                dev = dev.strip()

                self.networkInfo.setdefault(dev, {})

                # set IP addr #
                try:
                    if not SysMgr.localServObj:
                        NetworkMgr.setServerNetwork(
                            None, None, reuse=False, weakPort=True
                        )

                    sockObj = SysMgr.localServObj

                    res = fcntl.ioctl(
                        sockObj.socket.fileno(),
                        0x8915,  # SIOCGIFADDR
                        struct.pack("256s", dev[:15].encode("utf-8")),
                    )
                    ipaddr = socket.inet_ntoa(res[20:24])
                except SystemExit:
                    sys.exit(0)
                except:
                    ipaddr = ""

                self.networkInfo[dev]["ipaddr"] = ipaddr

                stats = stats.split()
                """
                bytes, packets, errs, drop, fifo,
                    frame, compressed, multicast
                """

                nrdvd = long(len(stats) / 2)

                # Receive #
                rlist = list(map(long, stats[:nrdvd]))

                if "recv" in self.networkInfo[dev]:
                    self.networkInfo[dev]["rdiff"] = []

                    for idx, val in enumerate(rlist):
                        if SysMgr.totalEnable:
                            rdiff = val
                        else:
                            rdiff = val - self.networkInfo[dev]["recv"][idx]
                        self.networkInfo[dev]["rdiff"].append(rdiff)
                else:
                    self.networkInfo[dev]["initrecv"] = rlist

                self.networkInfo[dev]["recv"] = rlist

                # Transmit #
                tlist = list(map(long, stats[nrdvd:]))

                if "tran" in self.networkInfo[dev]:
                    self.networkInfo[dev]["tdiff"] = []

                    for idx, val in enumerate(tlist):
                        if SysMgr.totalEnable:
                            tdiff = val
                        else:
                            tdiff = val - self.networkInfo[dev]["tran"][idx]
                        self.networkInfo[dev]["tdiff"].append(tdiff)
                else:
                    self.networkInfo[dev]["inittran"] = tlist

                self.networkInfo[dev]["tran"] = tlist
        except SystemExit:
            sys.exit(0)
        except:
            return

    def saveLimitInfo(self):
        if self.limitData:
            return
        else:
            self.limitData = SysMgr.readProcData("self", "limits")

    def saveOpenFileInfo(self):
        if self.openFileData:
            return

        # get fd status #
        stats = SysMgr.getNrSysFdHandle()
        if not stats:
            return

        self.openFileData["cur"] = stats[0]
        self.openFileData["max"] = stats[2]

    def saveUnameInfo(self):
        try:
            if not self.uname:
                self.uname = os.uname()
        except:
            pass

    def getAndroidUserList(self):
        # android_filesystem_config.h #
        AID_ROOT = 0
        AID_SYSTEM = 1000
        AID_RADIO = 1001
        AID_BLUETOOTH = 1002
        AID_GRAPHICS = 1003
        AID_INPUT = 1004
        AID_AUDIO = 1005
        AID_CAMERA = 1006
        AID_LOG = 1007
        AID_COMPASS = 1008
        AID_MOUNT = 1009
        AID_WIFI = 1010
        AID_ADB = 1011
        AID_INSTALL = 1012
        AID_MEDIA = 1013
        AID_DHCP = 1014
        AID_SDCARD_RW = 1015
        AID_VPN = 1016
        AID_KEYSTORE = 1017
        AID_USB = 1018
        AID_DRM = 1019
        AID_MDNSR = 1020
        AID_GPS = 1021
        AID_UNUSED1 = 1022
        AID_MEDIA_RW = 1023
        AID_MTP = 1024
        AID_UNUSED2 = 1025
        AID_DRMRPC = 1026
        AID_NFC = 1027
        AID_SDCARD_R = 1028
        AID_SHELL = 2000
        AID_CACHE = 2001
        AID_DIAG = 2002
        AID_NET_BT_ADMIN = 3001
        AID_NET_BT = 3002
        AID_INET = 3003
        AID_NET_RAW = 3004
        AID_NET_ADMIN = 3005
        AID_NET_BW_STATS = 3006
        AID_NET_BW_ACCT = 3007
        AID_NET_BT_STACK = 3008
        AID_MISC = 9998
        AID_NOBODY = 9999
        AID_APP = 10000
        AID_ISOLATED_START = 99000
        AID_ISOLATED_END = 99999
        AID_USER = 100000
        AID_SHARED_GID_START = 50000
        AID_SHARED_GID_END = 59999

        return {
            AID_ROOT: "root",
            AID_SYSTEM: "system",
            AID_RADIO: "radio",
            AID_BLUETOOTH: "bluetooth",
            AID_GRAPHICS: "graphics",
            AID_INPUT: "input",
            AID_AUDIO: "audio",
            AID_CAMERA: "camera",
            AID_LOG: "log",
            AID_COMPASS: "compass",
            AID_MOUNT: "mount",
            AID_WIFI: "wifi",
            AID_DHCP: "dhcp",
            AID_ADB: "adb",
            AID_INSTALL: "install",
            AID_MEDIA: "media",
            AID_DRM: "drm",
            AID_MDNSR: "mdnsr",
            AID_NFC: "nfc",
            AID_DRMRPC: "drmrpc",
            AID_SHELL: "shell",
            AID_CACHE: "cache",
            AID_DIAG: "diag",
            AID_NET_BT_ADMIN: "net_bt_admin",
            AID_NET_BT: "net_bt",
            AID_NET_BT_STACK: "net_bt_stack",
            AID_SDCARD_R: "sdcard_r",
            AID_SDCARD_RW: "sdcard_rw",
            AID_MEDIA_RW: "media_rw",
            AID_VPN: "vpn",
            AID_KEYSTORE: "keystore",
            AID_USB: "usb",
            AID_MTP: "mtp",
            AID_GPS: "gps",
            AID_INET: "inet",
            AID_NET_RAW: "net_raw",
            AID_NET_ADMIN: "net_admin",
            AID_NET_BW_STATS: "net_bw_stats",
            AID_NET_BW_ACCT: "net_bw_acct",
            AID_MISC: "misc",
            AID_NOBODY: "nobody",
        }

    def saveUserInfo(self):
        # check user data #
        if self.userData:
            return

        # get data #
        if SysMgr.isAndroid:
            # register user group #
            for n, v in self.getAndroidUserList().items():
                self.userData[str(n)] = {"name": v}

            try:
                path = "/data/system/packages.list"
                with open(path, "rb") as fd:
                    data = fd.readlines()
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printOpenWarn(path)
                return

            # parse package data #
            for line in data:
                try:
                    appname, uid, debug, path, seinfo, usergroups = (
                        line.rstrip().decode().split()[:6]
                    )

                    if uid in self.userData:
                        continue

                    self.userData[uid] = {
                        "name": appname,
                        "debug": debug,
                        "path": path,
                        "seinfo": seinfo,
                        "usergroups": usergroups,
                    }
                except SystemExit:
                    sys.exit(0)
                except:
                    pass
        else:
            try:
                path = "/etc/passwd"
                with open(path, "rb") as fd:
                    data = fd.readlines()
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printOpenWarn(path)
                return

            # parse data #
            for line in data:
                try:
                    user, passwd, uid, gid, info, home, shell = (
                        line.rstrip().decode().split(":")[:7]
                    )

                    self.userData[uid] = {
                        "name": user,
                        "gid": gid,
                        "info": info,
                        "home": home,
                        "shell": shell,
                    }
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

    def getStorageMapInfo(self):
        if not SysMgr.isDarwin and not SysMgr.isWindows:
            return {}

        # get subprocess object #
        subprocess = SysMgr.getPkg("subprocess")

        if SysMgr.isDarwin:
            command = ["diskutil", "list"]
            shell = False
        elif "PATH" in os.environ:
            # find powershell path #
            powershellPath = None
            for item in os.environ["PATH"].split(";"):
                if "WindowsPowerShell" in item:
                    powershellPath = "%s/powershell.exe" % item
                    break
            if not powershellPath:
                return

            command = [powershellPath, "get-partition"]
            shell = True
        else:
            shell = False

        # start diskutil process #
        try:
            proc = subprocess.Popen(
                command,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                bufsize=-1,
                shell=shell,
            )
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                "failed to execute '%s'" % " ".join(command), reason=True
            )
            sys.exit(-1)

        mapTable = {}
        path = None
        node = None
        devnum = -1

        while 1:
            try:
                # read a line from diskutil process #
                line = proc.stdout.readline()
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr(
                    "failed to read from '%s'" % " ".join(command), reason=True
                )
                sys.exit(-1)

            # handle error #
            if not line:
                err = proc.stderr.read()
                if err:
                    try:
                        proc.terminate()
                    except:
                        pass

                    SysMgr.printErr(err[err.find(":") + 2 :])
                    sys.exit(-1)
                break

            # decode data #
            try:
                line = line.decode()
            except SystemExit:
                sys.exit(0)
            except:
                pass

            # MacOS #
            if SysMgr.isDarwin:
                # disk node #
                if line.startswith("/"):
                    path, attr = line.split(" ", 1)
                    continue

                line = line.strip()
                if not line:
                    continue
                elif line[0].isdigit():
                    node = "/dev/%s" % line.split()[-1]
                    mapTable[node] = path
                elif line.startswith("Physical Store "):
                    # change previous map info #
                    mapTable.pop(node, None)
                    path = "/dev/%s" % line.split()[-1]
                    mapTable[node] = path
                    path = node

                    # update new physical path #
                    while 1:
                        try:
                            if path == mapTable[path]:
                                break
                            else:
                                path = mapTable[path]
                        except:
                            break
                else:
                    continue
            # Windows #
            else:
                line = line.strip()
                if not line:
                    continue
                elif line.startswith("PartitionNumber"):
                    devnum += 1
                    continue
                elif line[0].isdigit():
                    items = line.split()
                    # drive #
                    if items[1].isalpha():
                        node = "%s:\\" % items[1]
                        path = "PhysicalDrive%s" % devnum
                    else:
                        continue

            # check values #
            if not node or not path:
                continue

            mapTable[node] = path

        return mapTable

    def updateStorageInfoGen(self):
        # get psutil object #
        psutil = SysMgr.getPkg("psutil")

        self.prevStorageData = self.storageData
        self.storageData = {}

        try:
            # mount #
            partition = psutil.disk_partitions(all=True)
            for item in partition:
                dev, path, fs, opts, maxfile, maxpath = item
                self.storageData.setdefault(dev, {})
                self.storageData[dev]["mount"] = {
                    "fs": fs,
                    "path": path,
                    "option": opts,
                }

            # usage #
            for dev in list(self.storageData):
                try:
                    target = self.storageData[dev]
                    path = target["mount"]["path"]
                    total, used, free, per = psutil.disk_usage(path=path)
                    target["total"] = total >> 20
                    target["free"] = free >> 20
                    target["usagePer"] = long(per)
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printWarn(
                        "failed to get disk usage for '%s'" % path, reason=True
                    )

            # stat #
            stats = psutil.disk_io_counters(perdisk=True, nowrap=True)
            if not stats:
                SysMgr.printWarn("failed to get disk stats")
                return

            # get logical to physical disk map table #
            if not self.storageMapData:
                self.storageMapData = self.getStorageMapInfo()
            mapTable = self.storageMapData

            for dev in list(self.storageData):
                try:
                    # get physical device path #
                    if dev in mapTable:
                        if SysMgr.isWindows:
                            rpath = mapTable[dev]
                        else:
                            rpath = mapTable[dev].split("/")[-1]
                    else:
                        continue

                    # apply stat #
                    value = stats[rpath]
                    target = self.storageData[dev]
                    target["read"] = value[2] >> 20
                    target["write"] = value[3] >> 20
                    target["readtime"] = value[4]
                    target["writetime"] = value[5]
                except SystemExit:
                    sys.exit(0)
                except:
                    # reset map table #
                    self.storageMapData = {}

                    SysMgr.printWarn(
                        "failed to get disk stat for '%s'" % dev, reason=True
                    )
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn("failed to get general storage info", reason=True)
            return

    def updateStorageInfo(self, isGeneral=False):
        if not SysMgr.isLinux:
            return

        # check update time #
        if (
            self.storageUpdate
            and SysMgr.uptime - self.storageUpdate < SysMgr.intervalEnable
        ):
            return
        else:
            self.storageUpdate = SysMgr.uptime

        # get device type #
        self.saveDevInfo()

        # get storage info and update stat #
        self.saveStorageInfo(isGeneral)

        # get mount info #
        self.updateMountInfo()

        # get storage stat #
        self.prevStorageData = self.storageData
        self.storageData = self.getStorageInfo()

    def getStorageInfo(self):
        storageData = {}
        init_storageData = {
            "total": 0,
            "free": 0,
            "favail": 0,
            "read": 0,
            "write": 0,
            "usage": 0,
            "usagePer": 0,
            "mount": None,
        }

        storageData["total"] = dict(init_storageData)

        # get ADDTMPFS flag #
        addTmpfs = "ADDTMPFS" in SysMgr.environList

        # get filter list #
        infsList = SysMgr.environList.get("INFSLIST", [])
        exfsList = SysMgr.environList.get("EXFSLIST", [])

        # make block device table #
        for key, val in sorted(self.mountInfo.items(), key=lambda e: e[0]):
            # check device node path #
            if key[0] != "/":
                continue

            fs = val["fs"]

            # check filter #
            if infsList and not fs in infsList:
                continue
            if exfsList and fs in exfsList:
                continue

            storageData[key] = dict(init_storageData)
            storageData[key]["mount"] = val

            # calculate read & write load of devices #
            try:
                # get node name from full-path #
                dev = key[key.rfind("/") + 1 :]

                # update node name #
                if ":" in dev:
                    major, minor = dev.split(":")
                    for name, mp in self.diskInfo["prev"].items():
                        if mp["major"] == major and mp["minor"] == minor:
                            dev = name

                # define shortcut variables for this device info #
                beforeInfo = self.diskInfo["prev"][dev]
                afterInfo = self.diskInfo["next"][dev]

                # get interval I/O of this device #
                if SysMgr.totalEnable:
                    read = long(afterInfo["sectorRead"]) >> 11

                    write = long(afterInfo["sectorWrite"]) >> 11
                else:
                    read = (
                        long(afterInfo["sectorRead"])
                        - long(beforeInfo["sectorRead"])
                    ) >> 11

                    write = (
                        long(afterInfo["sectorWrite"])
                        - long(beforeInfo["sectorWrite"])
                    ) >> 11

                load = long(afterInfo["sectorRead"]) + long(
                    afterInfo["sectorWrite"]
                )

                readtime = long(afterInfo["readTime"])
                writetime = long(afterInfo["writeTime"])
                iotime = long(afterInfo["ioTime"])
                iowtime = long(afterInfo["ioWTime"])

                # save recent stats #
                storageData[key]["read"] = read
                storageData[key]["write"] = write
                storageData[key]["load"] = load
                storageData[key]["readtime"] = readtime
                storageData[key]["writetime"] = writetime
                storageData[key]["iotime"] = iotime
                storageData[key]["iowtime"] = iowtime

                if fs != "tmpfs":
                    storageData["total"]["read"] += read
                    storageData["total"]["write"] += write
            except:
                pass

            # get device stats #
            try:
                if hasattr(os, "statvfs"):
                    stat = os.statvfs(val["path"])
                else:
                    stat = SysMgr.statvfs(val["path"])

                total = stat.f_bsize * stat.f_blocks
                free = stat.f_bsize * stat.f_bavail

                if hasattr(stat, "f_favail"):
                    avail = stat.f_favail
                else:
                    avail = 0

                usage = long(((total - free) / float(total)) * 100)

                # convert to MB #
                total = total >> 20
                free = free >> 20

                storageData[key]["total"] = total
                storageData[key]["free"] = free
                storageData[key]["usage"] = total - free
                storageData[key]["usagePer"] = usage
                storageData[key]["favail"] = avail

                # exclude tmpfs #
                if addTmpfs or fs != "tmpfs":
                    storageData["total"]["total"] += total
                    storageData["total"]["free"] += free
                    storageData["total"]["favail"] += avail
            except:
                pass

        # set total storage stat #
        try:
            total = storageData["total"]
            storageData["total"]["usage"] = total["total"] - total["free"]
            storageData["total"]["usagePer"] = long(
                ((total["total"] - total["free"]) / float(total["total"]))
                * 100
            )
        except:
            pass

        return storageData

    def getCgroupPath(self, fs="cgroup", force=False, save=True):
        if not force and SysMgr.cgroupPathList:
            return SysMgr.cgroupPathList

        if force or not self.mountData:
            # update mount data #
            self.mountData = SysMgr.getMountData()

        # check mount path again #
        if not self.mountData:
            return None

        pathList = {}

        # search cgroup mount point #
        for mount in self.mountData:
            mountList = mount.split(" - ")

            # skip other fs #
            if not mountList[1].startswith(fs):
                continue

            # add cgroup mount point #
            mountInfo = mountList[0].split()
            path = mountInfo[4]
            options = mountList[1].strip()
            pathList.setdefault(path, options)

        if save:
            SysMgr.cgroupPathList = pathList

        return pathList

    def getCgroupTree(self):
        def _updateValues(dirpath, subfiles, item):
            for target in subfiles:
                if not target in ConfigMgr.CGROUP_VALUE:
                    continue

                try:
                    path = "%s/%s" % (dirpath, target)
                    with open(path, "r") as fd:
                        if target in ("tasks", "cgroup.procs"):
                            taskList = fd.read().splitlines()
                            item[target] = UtilMgr.convNum(len(taskList))
                            if (
                                SysMgr.showAll
                                and len(taskList) > 0
                                and target in ("cgroup.procs", "tasks")
                            ):
                                if target == "cgroup.procs":
                                    name = "PROCS"
                                else:
                                    name = "TASKS"
                                item[name] = dict.fromkeys(taskList, {})
                        else:
                            cval = fd.read().strip()
                            if cval.isdigit():
                                cval = UtilMgr.convNum(long(cval))
                            elif cval == "":
                                cval = "none"
                            item[target] = cval
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

        def _getPaths(root, path):
            for dirpath, subdirs, subfiles in path:
                UtilMgr.printProgress()

                # update subdir #
                for item in subdirs:
                    subdir = os.path.join(dirpath, item)
                    root[subdir] = {}

                # update subfiles #
                for item in subfiles:
                    # check value file #
                    if not item in ConfigMgr.CGROUP_VALUE:
                        continue

                    root.setdefault(dirpath, {})
                    _updateValues(dirpath, subfiles, root[dirpath])

        # get cgroup path #
        cgroupPath = self.getCgroupPath()
        if not cgroupPath:
            return None

        # get full path list #
        dirList = {}
        for path in cgroupPath:
            _getPaths(dirList, os.walk(path))

        # split a path to multiple tokens #
        dirDict = {}
        for item, val in dirList.items():
            # skip symbolic link #
            if os.path.islink(item):
                continue

            for path in cgroupPath:
                if not item.startswith(path):
                    continue

                # add items #
                path = path[: path.rfind("/")]
                p = dirDict
                tokList = item[len(path) :].split("/")[1:]
                for x in tokList:
                    p = p.setdefault(x, {})

                # merge with a value #
                p.update(val)

                break

        # convert subsystem name #
        cgroupInfo = SysMgr.printCgroupList(json=True, ret=True)
        for path in list(dirDict.keys()):
            for sub, val in cgroupInfo.items():
                if not val["mount"]:
                    continue

                name = val["mount"].split("(", 1)[0].rsplit("/", 1)[1].strip()
                if path == name:
                    dirDict[sub] = dirDict.pop(path)
                    break

        return dirDict

    def printCgroupInfo(self, printTitle=True, progress=False, cmd=False):
        # check cgroup mode #
        if not SysMgr.cgroupEnable:
            return
        elif "NOCGINFO" in SysMgr.environList:
            return

        # set printer #
        if cmd and SysMgr.jsonEnable:
            _printer = lambda x: x
        else:
            _printer = SysMgr.infoBufferPrint

        # get cgroup list #
        try:
            SysMgr.printInfo("start collecting cgroups...")

            cgroupTree = self.getCgroupTree()
            if not cgroupTree:
                return
        except SystemExit:
            sys.exit(0)
        except:
            return

        # filter cgroup subsystem #
        if SysMgr.hasMainArg():
            # get filter for subsystems #
            items = SysMgr.getMainArgs(token=" ")

            # remove subsystems from tree #
            for subsystem in list(cgroupTree):
                if UtilMgr.isValidStr(subsystem, key=items, ignCap=True):
                    continue

                found = False
                for item in subsystem.split(","):
                    if UtilMgr.isValidStr(item, key=items, ignCap=True):
                        found = True
                        break

                if not found:
                    cgroupTree.pop(subsystem, None)

            # check result #
            if not cgroupTree:
                SysMgr.printErr(
                    "no cgroup info related to '%s'" % ",".join(items)
                )
                sys.exit(-1)

        # define lists #
        commList = {}
        memCaches = {}

        # define stat indexes #
        utimeIdx = ConfigMgr.PROCSTAT_ATTR.index("UTIME")
        stimeIdx = ConfigMgr.PROCSTAT_ATTR.index("STIME")

        # enable stat flags #
        if "memory" in list(cgroupTree):
            SysMgr.memEnable = True
        if "blkio" in list(cgroupTree):
            SysMgr.blockEnable = True

        # save process stats #
        procMgr = TaskAnalyzer(onlyInstance=True)
        if SysMgr.showAll and not "ONLYTASK" in SysMgr.environList:
            procMgr.saveSystemStat()

        # save thread stats #
        SysMgr.processEnable = False
        taskMgr = TaskAnalyzer(onlyInstance=True)
        if SysMgr.showAll and not "ONLYPROC" in SysMgr.environList:
            taskMgr.saveSystemStat()

        def _printDirTree(root, depth, totals={}, parent="", res=""):
            # check depth #
            if 0 < SysMgr.funcDepth <= depth:
                return

            # check type #
            if not isinstance(root, dict):
                return

            # print progress #
            if progress:
                UtilMgr.printProgress()

            # copy sub-tree #
            tempRoot = UtilMgr.deepcopy(root)

            convColor = UtilMgr.convColor
            convNum = UtilMgr.convNum
            convSize = UtilMgr.convSize2Unit

            # init shared values #
            variables = {"cpu.shares", "blkio.weight", "blkio.bfq.weight"}
            for item in variables:
                totals.setdefault(item, 0)

            # get options #
            repComm = "REPCOMM" in SysMgr.environList
            repCmdline = "REPCMDLINE" in SysMgr.environList
            skipNoTask = "SKIPNOTASK" in SysMgr.environList

            # sum values for subdirs #
            newTotals = {}
            for curdir, subdir in tempRoot.items():
                if not (variables - set(subdir)):
                    continue

                for parent, children in subdir.items():
                    if not isinstance(children, dict):
                        continue

                    for item in variables:
                        if not item in children:
                            continue
                        value = children[item].replace(",", "")
                        newTotals.setdefault(item, 0)
                        newTotals[item] += long(value)

                if depth == 0:
                    for item in variables:
                        if not item in subdir:
                            continue
                        value = long(subdir[item].replace(",", ""))
                        newTotals.setdefault(item, 0)
                        newTotals[item] += value

                        totals[item] += newTotals[item]

            # sort subsystem #
            if depth == 0:
                sortedList = sorted(tempRoot.items())
            else:
                sortedList = sorted(
                    tempRoot.items(),
                    key=lambda e: long(e[0]) if e[0].isdigit() else e[0],
                )

            # traverse subdir #
            for curdir, subdir in sortedList:
                cstr = ""
                nrProcs = 0
                nrTasks = 0

                # check subdir type #
                if not isinstance(subdir, dict):
                    continue

                # update res #
                if depth == 0:
                    res = curdir

                tempSubdir = UtilMgr.deepcopy(subdir)
                for val in sorted(list(subdir), reverse=True):
                    cname = ""

                    # check target value #
                    if not val in ConfigMgr.CGROUP_VALUE:
                        continue

                    # thread #
                    if val == "tasks":
                        nrTasks = subdir[val]
                        tempSubdir.pop(val, None)
                        continue
                    # process #
                    elif val == "cgroup.procs":
                        nrProcs = subdir[val]
                        tempSubdir.pop(val, None)
                        continue
                    # shares, weight #
                    elif val in variables and totals[val] > 0:
                        num = long(subdir[val].replace(",", ""))
                        per = "%.1f" % ((num / float(totals[val])) * 100)
                        if num > 0:
                            per = convColor(per, "YELLOW")
                        value = "%s/%s%%" % (subdir[val], per)
                    # runtime, period #
                    elif val.endswith("_us"):
                        if subdir[val] in ("-1", "0"):
                            value = subdir[val]
                        else:
                            num = long(subdir[val].replace(",", ""))
                            sec = num / 1000000.0
                            value = UtilMgr.convTime2Unit(sec)
                            if val in (
                                "cpu.cfs_quota_us",
                                "cpu.rt_runtime_us",
                            ):
                                value = convColor(value, "RED")
                        cname = UtilMgr.rstrip(
                            ".".join(val.split(".")[1:]), "_us"
                        )
                    # CPU usage #
                    elif val == "cpuacct.usage":
                        num = long(subdir[val].replace(",", ""))
                        per = long(long(num) / 10000000)
                        value = convNum(per) + "%"
                        value = convColor(value, "YELLOW")
                    # uclamp #
                    elif val in (
                        "cpu.uclamp.min",
                        "cpu.uclamp.max",
                        "cpu.uclamp.latency_sensitive",
                    ):
                        value = subdir[val]
                        if value.startswith("0") or value == "max":
                            value = ""
                        else:
                            value = convColor(str(float(value)), "RED")
                    # memory limit #
                    elif val.endswith("limit_in_bytes"):
                        num = long(subdir[val].replace(",", ""))
                        value = convSize(num)
                        if num < 9223372036854771712:
                            value = convColor(value, "RED")
                    # CPU throttled_time, block wait #
                    elif val in ("cpu.stat", "blkio.io_wait_time"):
                        try:
                            value = ""
                            for item in reversed(subdir[val].split("\n")):
                                if item.startswith("Total"):
                                    unit = 1000000000.0
                                elif item.startswith("throttled_time"):
                                    cname = "throttled_time"
                                    unit = 1000000000.0
                                elif "_usec" in item:
                                    cname = "cpu." + item.split("_", 1)[0]
                                    unit = 1000000.0
                                else:
                                    continue

                                # get time in sec from us #
                                num = long(item.split()[1]) / unit
                                if num == 0:
                                    value += "%s:0, " % cname
                                    cname = " "
                                    continue

                                if unit == 1000000000.0:
                                    value = convNum(num, True, 3) + "sec"
                                    value = convColor(value, "RED")
                                    break

                                num = convNum(num, True, 3) + "sec"
                                num = convColor(num, "YELLOW")
                                value += "%s:%s, " % (cname, num)
                                cname = " "
                        except SystemExit:
                            sys.exit(0)
                        except:
                            SysMgr.printWarn(
                                "failed to get %s value" % val, reason=True
                            )

                        value = value.rstrip(" ,")
                    # cpus #
                    elif val == "cpus":
                        cname = "cpus"
                        value = subdir[val]
                    # block usage #
                    elif val == "blkio.throttle.io_service_bytes":
                        try:
                            value = ""
                            prevDev = ""
                            for item in subdir[val].split("\n"):
                                if item.startswith("Total"):
                                    continue

                                # get device stats #
                                dev, op, stat = item.split()
                                if stat == "0" or op == "Total":
                                    continue

                                # convert device ID to PATH #
                                for (
                                    path,
                                    minfo,
                                ) in SysMgr.sysInstance.mountInfo.items():
                                    try:
                                        devid = "%s:%s" % (
                                            minfo["major"],
                                            minfo["minor"],
                                        )
                                        if devid == dev:
                                            dev = path
                                    except SystemExit:
                                        sys.exit(0)
                                    except:
                                        pass

                                # new device #
                                if prevDev != dev:
                                    prevDev = dev
                                    if value:
                                        value = "%s" % value.strip(" |")
                                    value += convColor("[%s]" % dev, "CYAN")

                                # add stats #
                                stat = convSize(long(stat))
                                stat = convColor(stat, "YELLOW")
                                value += "%s=%s|" % (op, stat)
                            if value:
                                value = value.strip(" |")
                        except SystemExit:
                            sys.exit(0)
                        except:
                            SysMgr.printWarn(
                                "failed to get %s value" % val, reason=True
                            )
                            value = ""
                    # skip weight_device #
                    elif (
                        val == "blkio.weight_device" and subdir[val] == "none"
                    ):
                        value = ""
                    # PSI #
                    elif val.endswith(".pressure"):
                        try:
                            totalstr = ""

                            for item in subdir[val].split("\n"):
                                if not item:
                                    continue

                                tempval = ""

                                # parse items #
                                items = item.split()
                                target = items[0]

                                for data in items[1:]:
                                    name, stat = data.split("=")
                                    stat = float(stat)
                                    if stat <= 0:
                                        continue

                                    # convert us to % #
                                    stat = float(stat)
                                    if name == "total":
                                        stat = stat / 10000
                                        if stat < 0.1:
                                            continue
                                    elif not SysMgr.showAll:
                                        continue

                                    stat = "%s%%" % convNum(stat, isFloat=True)
                                    if name == "total":
                                        stat = convColor(stat, "RED")

                                    # add title #
                                    if not tempval:
                                        tempval = "[%s]" % (
                                            convColor(target, "YELLOW")
                                        )

                                    tempval += "%s=%s," % (name, stat)

                                if tempval:
                                    totalstr += tempval.rstrip(",")

                            value = totalstr
                            if value:
                                cname = convColor(val, "PINK")
                        except SystemExit:
                            sys.exit(0)
                        except:
                            SysMgr.printWarn(
                                "failed to handle PSI data", reason=True
                            )
                            value = ""
                    # data #
                    else:
                        value = subdir[val]

                    # set item name #
                    if cname:
                        cname = cname.strip()
                        if cname:
                            cname += ":"
                    else:
                        cname = ".".join(val.split(".")[1:]) + ":"

                    # append item #
                    if value:
                        cstr = "%s%s%s, " % (cstr, cname, value)

                    # remove item from tree #
                    tempSubdir.pop(val, None)

                indent = ""
                if depth == 0:
                    indent = "\n"

                for _ in xrange(depth):
                    indent = "%s%s|" % (indent, "     ")

                if cstr:
                    cstr = " <%s>" % cstr.strip(", ")

                # define process info #
                if not nrProcs in ("0", 0):
                    procstr = convColor(nrProcs, "YELLOW")
                else:
                    procstr = nrProcs

                # define tasks info #
                if not nrTasks in ("0", 0):
                    taskstr = convColor(nrTasks, "CYAN")
                else:
                    taskstr = nrTasks

                nrWorker = " (proc:%s/task:%s)" % (procstr, taskstr)

                # highlight subsystem name #
                if depth == 0:
                    curdir = convColor(curdir, "GREEN")

                # parent node #
                if tempSubdir:
                    # print only representative task's name #
                    if (
                        (repComm or repCmdline)
                        and "PROCS" in subdir
                        and subdir["PROCS"]
                    ):
                        if repComm:
                            _func = SysMgr.getComm
                        else:
                            _func = SysMgr.getCmdline

                        name = "(%s)" % convColor(
                            _func(list(subdir["PROCS"])[0]), "GREEN"
                        )
                    else:
                        name = ""

                    # check skip condition #
                    if curdir in ("PROCS", "TASKS"):
                        if repComm or repCmdline:
                            continue
                        elif (
                            "ONLYPROC" in SysMgr.environList
                            and curdir == "TASKS"
                        ):
                            continue
                        elif (
                            "ONLYTASK" in SysMgr.environList
                            and curdir == "PROCS"
                        ):
                            continue
                        else:
                            nrWorker = ""

                    nrChild = "[sub:%s]" % len(tempSubdir)

                    _printer(
                        "%s- %s%s%s%s%s"
                        % (indent, curdir, name, nrChild, nrWorker, cstr)
                    )
                # task node #
                elif depth > 0 and nrProcs == nrTasks == 0:
                    # define function to get comm #
                    def _getComm(curdir):
                        # get comm #
                        if curdir in commList:
                            comm = commList[curdir]
                        else:
                            comm = commList[curdir] = SysMgr.getComm(
                                curdir, save=True
                            )
                        return comm

                    # define function to get stats #
                    def _getCpuStat(pid, task):
                        statstr = ""
                        ttimestr = ""
                        dtimestr = ""

                        # get stats #
                        try:
                            # define target instance #
                            if task == "PROCS":
                                instance = procMgr.procData[pid]
                            else:
                                instance = taskMgr.procData[pid]
                                if not instance["status"]:
                                    taskMgr.saveProcSchedData(
                                        instance["taskPath"], pid
                                    )

                            # get usage #
                            utime = long(instance["stat"][utimeIdx])
                            stime = long(instance["stat"][stimeIdx])
                            ttime = utime + stime

                            # build usage string #
                            if long(ttime):
                                ttimestr = convNum(ttime)
                                ttimestr = "+%s%%" % ttimestr
                                ttimestr = convColor(ttimestr, "YELLOW")

                            # get delay #
                            if task == "TASKS":
                                try:
                                    execTime = instance["execTime"]
                                    waitTime = instance["waitTime"]
                                    execPer = (
                                        execTime / (execTime + waitTime)
                                    ) * 100
                                    totalTime = ttime * (100 / execPer)
                                    dtime = long(totalTime - ttime)
                                    if dtime:
                                        dtimestr = convNum(dtime)
                                        dtimestr = "-%s%%" % dtimestr
                                        dtimestr = convColor(
                                            dtimestr, "WARNING"
                                        )
                                except SystemExit:
                                    sys.exit(0)
                                except:
                                    pass

                            # build final string #
                            if task == "PROCS" and ttimestr:
                                statstr = "[%s]" % ttimestr
                            elif task == "TASKS" and (ttimestr or dtimestr):
                                statstr = " [%s%s]" % (
                                    ttimestr + "," if ttimestr else ttimestr,
                                    dtimestr,
                                )
                        except SystemExit:
                            sys.exit(0)
                        except:
                            SysMgr.printWarn(
                                "failed to get cpu stats", reason=True
                            )
                        finally:
                            return statstr

                    def _getMemStat(pid, task):
                        mstat = ""

                        # get stats #
                        try:
                            # define target instance #
                            if task == "PROCS":
                                tgid = pid
                            else:
                                tgid = SysMgr.getTgid(pid)

                            # check cache #
                            if tgid in memCaches:
                                mstat = memCaches[tgid]
                                return mstat

                            # get memory usage #
                            mstat = TaskAnalyzer.getMemStr(
                                taskMgr, tgid, verb=False, color="YELLOW"
                            )
                            if not mstat:
                                return mstat

                            mstat = "[%s]" % mstat

                            # save cache #
                            memCaches[tgid] = mstat
                        except SystemExit:
                            sys.exit(0)
                        except:
                            SysMgr.printWarn(
                                "failed to get memory stats", reason=True
                            )
                        finally:
                            return mstat

                    def _getIoStat(pid, task):
                        statstr = ""

                        # get stats #
                        try:
                            # define target instance #
                            if task == "PROCS":
                                instance = procMgr.procData
                            else:
                                instance = taskMgr.procData
                                if not instance[pid]["status"]:
                                    taskMgr.saveProcSchedData(
                                        instance[pid]["taskPath"], pid
                                    )

                            # get I/O usage #
                            if not instance[pid]["io"]:
                                path = "%s/%s" % (SysMgr.procPath, pid)
                                ioBuf = procMgr.saveTaskData(path, pid, "io")
                                if not ioBuf:
                                    return

                                instance[pid]["io"] = {}

                                for line in ioBuf:
                                    if not (
                                        line.startswith("read_bytes:")
                                        or line.startswith("write_bytes:")
                                    ):
                                        continue

                                    name, val = line.split(None, 1)[:2]
                                    if val == "0":
                                        continue

                                    # save stats #
                                    instance[pid]["io"][name[:-1]] = long(val)

                            # build final string #
                            procData = instance[pid]["io"]
                            for item in ("read_bytes", "write_bytes"):
                                if not item in procData:
                                    continue
                                elif procData[item] in ("0", 0):
                                    continue

                                val = convSize(procData[item])
                                val = convColor(val, "YELLOW")
                                if statstr:
                                    statstr += ", "
                                statstr += "%s:%s" % (
                                    item.split("_")[0].upper(),
                                    val,
                                )

                            if statstr:
                                statstr = "[%s]" % statstr
                        except SystemExit:
                            sys.exit(0)
                        except:
                            SysMgr.printWarn(
                                "failed to get block stats", reason=True
                            )
                        finally:
                            return statstr

                    # get comm #
                    comm = _getComm(curdir)

                    # filter process #
                    pstr = ""
                    statstr = ""
                    try:
                        missMark = False

                        # get parent ID #
                        if parent == "PROCS":
                            pid = SysMgr.getPpid(curdir)
                        elif parent == "TASKS":
                            pid = SysMgr.getTgid(curdir)

                            # get thread List #
                            if not taskMgr.procData[pid]["tids"]:
                                taskMgr.procData[pid][
                                    "tids"
                                ] = SysMgr.getThreadList(curdir)
                            thrList = taskMgr.procData[pid]["tids"]

                            # check missing tasks #
                            if thrList:
                                missList = set(map(str, thrList)) - set(
                                    tempRoot
                                )
                                if missList:
                                    missMark = True
                        else:
                            raise ValueError()

                        # get stats #
                        if res == "cpu" or res.startswith("cpu,"):
                            statstr = _getCpuStat(curdir, parent)
                        elif res == "memory":
                            statstr = _getMemStat(curdir, parent)
                        elif res == "blkio":
                            statstr = _getIoStat(curdir, parent)

                        # check skip condition #
                        if "SKIPIDLE" in SysMgr.environList and not statstr:
                            continue

                        # get parent comm #
                        if pid in ("0", 0):
                            pcomm = "swapper"
                        else:
                            pcomm = _getComm(pid)

                        pstr = "%s <- %s(%s)" % (statstr, pcomm, pid)

                        # check partial task #
                        if missMark:
                            pstr += " [%s]" % convColor("PARTIAL", "RED")
                    except ValueError:
                        pass
                    except SystemExit:
                        sys.exit(0)
                    except:
                        SysMgr.printWarn(
                            "failed to get parent info of %s(%s)"
                            % (comm, curdir),
                            reason=True,
                        )

                    # check filter and print task info #
                    if comm and UtilMgr.isValidStr(comm, ignCap=True):
                        if SysMgr.filterGroup:
                            comm = convColor(comm, "RED")
                        elif SysMgr.customCmd and UtilMgr.isValidStr(
                            comm, SysMgr.customCmd
                        ):
                            comm = convColor(comm, "RED")
                        _printer("%s- %s(%s)%s" % (indent, comm, curdir, pstr))
                # leap node #
                else:
                    if (
                        skipNoTask
                        and nrProcs in ("0", 0)
                        and nrTasks in ("0", 0)
                        and not tempSubdir
                    ):
                        continue

                    _printer("%s- %s%s%s" % (indent, curdir, nrWorker, cstr))

                _printDirTree(tempSubdir, depth + 1, newTotals, curdir, res)

            if depth == 0:
                _printer(" ")

        # print cgroup info #
        if printTitle:
            _printer("\n[System Cgroup Info]")
            _printer(twoLine)

        if progress:
            SysMgr.printInfo("start traversing cgroups...")

        # print cgroup tree #
        _printDirTree(cgroupTree, 0)

        # delete progress #
        if progress:
            UtilMgr.deleteProgress()

        if cmd and SysMgr.jsonEnable:
            SysMgr.printPipe(
                UtilMgr.convDict2Str(
                    cgroupTree, pretty=not SysMgr.streamEnable
                ),
                pager=False,
            )
            return

        # check result #
        if not SysMgr.sysinfoBuffer.strip():
            SysMgr.printErr("no cgroup info")
            sys.exit(-1)

        # append last line #
        if printTitle:
            _printer(twoLine)

        # add JSON stats #
        if SysMgr.jsonEnable:
            SysMgr.jsonData.setdefault("general", {})
            SysMgr.jsonData["general"]["cgroup"] = cgroupTree

    def printIPCInfo(self):
        if "NOIPCINFO" in SysMgr.environList:
            return

        self.printShmInfo()
        self.printMsgqInfo()
        self.printSemInfo()

    def printShmInfo(self):
        # check shm data #
        if not "shm" in self.ipcData:
            return

        # print IPC info #
        SysMgr.infoBufferPrint("\n[System SHM Info]")
        SysMgr.infoBufferPrint(twoLine)
        SysMgr.infoBufferPrint(
            "{0:^65} | {1:^24} | {2:^15} | {3:^36}    |".format(
                "ID", "Segment", "Attr", "Time"
            )
        )
        SysMgr.infoBufferPrint(oneLine)
        SysMgr.infoBufferPrint(
            (
                "{0:^25}   {1:^8}   {2:^26} | {3:>6}   {4:>6}   {5:>6} | "
                "{6:>6}   {7:>6} | {8:>11}   {9:>11}   {10:>11} |".format(
                    "OWNER",
                    "SHM",
                    "USER",
                    "SIZE",
                    "RSS",
                    "SWAP",
                    "REF",
                    "PERM",
                    "ATIME",
                    "DTIME",
                    "CTIME",
                )
            )
        )
        SysMgr.infoBufferPrint(twoLine)

        # get attr #
        cnt = 0
        prevOwner = None
        now = time.mktime(time.gmtime())
        convertSizeFunc = UtilMgr.convSize2Unit
        convTimeFunc = UtilMgr.convTime

        # merge stats per-owner #
        ownerData = {}
        for shmid, stats in self.ipcData["shm"].items():
            try:
                owner = stats["cpid"]

                ownerData[owner]["count"] += 1
                ownerData[owner]["size"] += stats["size"]
                ownerData[owner]["rss"] += stats["rss"]
                ownerData[owner]["swap"] += stats["swap"]
            except:
                ownerData[owner] = {}
                ownerData[owner]["uid"] = stats["uid"]
                ownerData[owner]["count"] = 1
                ownerData[owner]["size"] = stats["size"]
                ownerData[owner]["rss"] = stats["rss"]
                ownerData[owner]["swap"] = stats["swap"]

        # print stats #
        for shmid, stats in sorted(
            self.ipcData["shm"].items(), key=lambda e: e[1]["cpid"]
        ):
            # get owner name #
            try:
                pid = stats["cpid"]

                if pid != prevOwner:
                    prevOwner = pid

                    comm = SysMgr.getComm(pid, save=True)
                    if not comm:
                        raise Exception("no comm")

                    owner = "%s(%s)" % (comm[:16], pid)
                else:
                    owner = ""
            except:
                owner = "? (%s)" % pid

            # print total stat #
            try:
                if not owner:
                    raise Exception("no owner")

                try:
                    user = self.userData[stats["uid"]]["name"]
                    owner = "%s[%s]" % (owner, user)
                except:
                    pass

                if cnt > 0:
                    SysMgr.infoBufferPrint(oneLine)

                totalStat = "[ TOTAL: %s ]" % ownerData[pid]["count"]
                space = 65 - len(owner) - len(totalStat)
                totalStr = "%s%s%s" % (owner, " " * space, totalStat)
                SysMgr.infoBufferPrint(
                    "{0:>40}   {1:>6}   {2:>6}   {3:>6}   {4:>57} |".format(
                        totalStr,
                        convertSizeFunc(ownerData[pid]["size"], True),
                        convertSizeFunc(ownerData[pid]["rss"], True),
                        convertSizeFunc(ownerData[pid]["swap"], True),
                        " ",
                    )
                )
            except:
                pass

            # key #
            key = hex(long(stats["key"]))
            shmids = "%s(%s)" % (key, shmid)

            try:
                pid = stats["lpid"]
                comm = SysMgr.getComm(pid)
                if not comm:
                    raise Exception("no comm")

                access = "%s (%s)" % (comm[:16], pid)
            except:
                access = "? (%s)" % stats["lpid"]

            # get access time #
            atime = long(stats["atime"])
            if atime:
                atime = now - time.mktime(time.gmtime(atime))
                if atime < 0:
                    atime = "?"
            else:
                atime = "?"

            # get modification time #
            dtime = long(stats["dtime"])
            if dtime:
                dtime = now - time.mktime(time.gmtime(dtime))
                if dtime < 0:
                    dtime = "?"
            else:
                dtime = "?"

            # get creation time #
            ctime = long(stats["ctime"])
            if ctime:
                ctime = now - time.mktime(time.gmtime(ctime))
                if ctime < 0:
                    ctime = "?"
            else:
                ctime = "?"

            # print stats #
            try:
                SysMgr.infoBufferPrint(
                    (
                        "{0:>36}   {1:>26}   {2:>6}   {3:>6}   {4:>6}   "
                        "{5:>6}   {6:>6}   {7:>11}   {8:>11}   {9:>11} |"
                    ).format(
                        shmids,
                        access,
                        convertSizeFunc(stats["size"], True),
                        convertSizeFunc(stats["rss"], True),
                        convertSizeFunc(stats["swap"], True),
                        stats["nattch"],
                        stats["perms"],
                        atime if atime == "?" else convTimeFunc(atime)[:10],
                        dtime if dtime == "?" else convTimeFunc(dtime)[:10],
                        ctime if ctime == "?" else convTimeFunc(ctime)[:10],
                    )
                )

                cnt += 1
            except:
                pass

        # check output count #
        if cnt == 0:
            SysMgr.infoBufferPrint("{0:<153}|".format("    None"))

        SysMgr.infoBufferPrint(twoLine)

    def printSemInfo(self):
        pass

    def printMsgqInfo(self):
        pass

    def printGpuInfo(self):
        if "NOGPUINFO" in SysMgr.environList:
            return

        try:
            gpuInfo = SysMgr.getGpuInfo()
            if not gpuInfo:
                return None
        except:
            return None

        # add JSON stats #
        if SysMgr.jsonEnable:
            SysMgr.jsonData.setdefault("general", {})
            SysMgr.jsonData["general"]["gpu"] = {}
            jsonData = SysMgr.jsonData["general"]["gpu"]
        else:
            jsonData = {}

        # print GPU info #
        lineLen = 88
        SysMgr.infoBufferPrint("\n[System GPU Info]")
        SysMgr.infoBufferPrint(twoLine[:lineLen])
        SysMgr.infoBufferPrint(
            "{0:^32} | {1:^16} | {2:^32} |\n{3:1}".format(
                "Name", "Stat", "Value", oneLine[:lineLen]
            )
        )

        for item in gpuInfo.values():
            name = item["name"]
            if SysMgr.jsonEnable:
                origName = name
                jsonData.setdefault(origName, {})

            for key, value in sorted(item.items()):
                if key == "name":
                    continue

                SysMgr.infoBufferPrint(
                    "{0:<32} | {1:<16} | {2:>32} |".format(name, key, value)
                )

                if SysMgr.jsonEnable:
                    jsonData[origName].setdefault(key, value)

                name = ""

            SysMgr.infoBufferPrint(oneLine[:lineLen])

    def printGpuMemInfo(self):
        if "NOGPUMEMINFO" in SysMgr.environList:
            return

        # get per-process GPU memory info #
        gpuInfo, procInfo = SysMgr.getGpuMem(incAll=True)
        if not gpuInfo:
            return

        # add JSON stats #
        if SysMgr.jsonEnable:
            SysMgr.jsonData.setdefault("general", {})
            SysMgr.jsonData["general"].setdefault("gpu", {})
            SysMgr.jsonData["general"].setdefault("gpumem", {})
            jsonData = SysMgr.jsonData["general"]["gpumem"]
        else:
            jsonData = {}

        # merge all types #
        mtypes = set()
        for types in procInfo.values():
            mtypes = mtypes | set(types)
        mtypes.discard("TOTAL")
        mtypes = list(mtypes)

        # set default title string #
        title = "{0:^32} | {1:^16} |".format("Process", "TOTAL")
        for mtype in mtypes:
            title += " {0:^16} |".format(mtype)

        lenLine = len(title)

        # print GPU Memory info #
        SysMgr.infoBufferPrint(
            "\n[System GPU Memory Info]\n%s" % twoLine[:lenLine]
        )
        SysMgr.infoBufferPrint("%s\n%s" % (title, oneLine[:lenLine]))

        totals = {"TOTAL": 0}

        for pid, item in sorted(
            procInfo.items(), key=lambda e: e[1]["TOTAL"], reverse=True
        ):
            if pid == "0":
                continue

            proc = "%s(%s)" % (SysMgr.getComm(pid), pid)

            if SysMgr.jsonEnable:
                jsonData.setdefault(proc, {})

            pstr = "{0:>32} | {1:>16} |".format(
                proc, UtilMgr.convSize2Unit(item["TOTAL"])
            )

            # add to total #
            totals["TOTAL"] += item["TOTAL"]

            for mtype in mtypes:
                if mtype in item:
                    size = item[mtype]
                else:
                    size = 0

                # reconvert size to unit #
                if size:
                    origSize = size
                    size = UtilMgr.convSize2Unit(size)

                    if SysMgr.jsonEnable:
                        jsonData[proc].setdefault(mtype, size)

                    # add to total #
                    totals.setdefault(mtype, 0)
                    totals[mtype] += origSize

                pstr += " {0:>16} |".format(size if size else " ")

            SysMgr.infoBufferPrint(pstr)

        # print total stats #
        tstr = "{2:1}\n{0:>32} | {1:>16} |".format(
            "TOTAL", UtilMgr.convSize2Unit(totals["TOTAL"]), oneLine[:lenLine]
        )
        for mtype in mtypes:
            if mtype in totals:
                size = totals[mtype]
            else:
                size = 0
            tstr += " {0:>16} |".format(
                UtilMgr.convSize2Unit(size) if size else " "
            )
        SysMgr.infoBufferPrint(tstr)

        SysMgr.infoBufferPrint(oneLine[:lenLine])

    def printNetworkInfo(self):
        if "NONETWORKINFO" in SysMgr.environList:
            return

        # add JSON stats #
        if SysMgr.jsonEnable:
            SysMgr.jsonData.setdefault("general", {})
            SysMgr.jsonData["general"]["network"] = {}
            jsonData = SysMgr.jsonData["general"]["network"]
        else:
            jsonData = {}

        # print network info #
        lineLen = 136
        SysMgr.infoBufferPrint("\n[System Network Info]")
        SysMgr.infoBufferPrint(twoLine[:lineLen])
        SysMgr.infoBufferPrint(
            "{0:^38} | {1:^45} | {2:^45} |\n{3:1}".format(
                "Network", "Receive", "Transfer", oneLine[:lineLen]
            )
        )
        SysMgr.infoBufferPrint(
            (
                "{0:^16} {1:^21} | "
                "{2:^8} {3:^8} {4:^8} {5:^8} {6:^9} | "
                "{2:^8} {3:^8} {4:^8} {5:^8} {6:^9} |"
            ).format(
                "Dev", "TYPE", "Size", "Packet", "Error", "Drop", "Multicast"
            )
        )
        SysMgr.infoBufferPrint(twoLine[:lineLen])

        convSize = UtilMgr.convSize2Unit

        cnt = 1
        totalStat = {
            "rdiff": [0] * 5,
            "tdiff": [0] * 5,
            "rtotal": [0] * 5,
            "ttotal": [0] * 5,
        }

        for dev, val in sorted(self.networkInfo.items(), key=lambda e: e[0]):
            try:
                """
                [ network stat sequence ]
                bytes, packets, errs, drop, fifo,
                    frame, compressed, multicast
                """

                # recv diff #
                rdiff = []
                for idx, stat in enumerate(val["recv"]):
                    rdiff.append(stat - val["initrecv"][idx])

                # tran diff #
                tdiff = []
                for idx, stat in enumerate(val["tran"]):
                    tdiff.append(stat - val["inittran"][idx])

                # sum diff stats #
                totalStat["rdiff"][0] += rdiff[0]
                totalStat["rdiff"][1] += rdiff[1]
                totalStat["rdiff"][2] += rdiff[2]
                totalStat["rdiff"][3] += rdiff[3]
                totalStat["rdiff"][4] += rdiff[-1]
                totalStat["tdiff"][0] += tdiff[0]
                totalStat["tdiff"][1] += tdiff[1]
                totalStat["tdiff"][2] += tdiff[2]
                totalStat["tdiff"][3] += tdiff[3]
                totalStat["tdiff"][4] += tdiff[-1]

                SysMgr.infoBufferPrint(
                    (
                        "{0:>16} {1:^21} | "
                        "{2:>8} {3:>8} {4:>8} {5:>8} {6:>9} | "
                        "{7:>8} {8:>8} {9:>8} {10:>8} {11:>9} |"
                    ).format(
                        dev[-16:],
                        "DIFF",
                        convSize(rdiff[0]),
                        convSize(rdiff[1]),
                        convSize(rdiff[2]),
                        convSize(rdiff[3]),
                        convSize(rdiff[-1]),
                        convSize(tdiff[0]),
                        convSize(tdiff[1]),
                        convSize(tdiff[2]),
                        convSize(tdiff[3]),
                        convSize(tdiff[-1]),
                    )
                )

                # per-device total stats #
                rlist = val["recv"]
                tlist = val["tran"]

                # sum total stats #
                totalStat["rtotal"][0] += rlist[0]
                totalStat["rtotal"][1] += rlist[1]
                totalStat["rtotal"][2] += rlist[2]
                totalStat["rtotal"][3] += rlist[3]
                totalStat["rtotal"][4] += rlist[-1]
                totalStat["ttotal"][0] += tlist[0]
                totalStat["ttotal"][1] += tlist[1]
                totalStat["ttotal"][2] += tlist[2]
                totalStat["ttotal"][3] += tlist[3]
                totalStat["ttotal"][4] += tlist[-1]

                SysMgr.infoBufferPrint(
                    (
                        "{0:>16} {1:^21} | "
                        "{2:>8} {3:>8} {4:>8} {5:>8} {6:>9} | "
                        "{7:>8} {8:>8} {9:>8} {10:>8} {11:>9} |"
                    ).format(
                        " ",
                        "TOTAL",
                        convSize(rlist[0]),
                        convSize(rlist[1]),
                        convSize(rlist[2]),
                        convSize(rlist[3]),
                        convSize(rlist[-1]),
                        convSize(tlist[0]),
                        convSize(tlist[1]),
                        convSize(tlist[2]),
                        convSize(tlist[3]),
                        convSize(tlist[-1]),
                    )
                )

                if cnt < len(self.networkInfo):
                    SysMgr.infoBufferPrint("{0:1}".format(oneLine[:lineLen]))

                cnt += 1

                if SysMgr.jsonEnable:
                    jsonData[dev] = {}

                    jsonData[dev]["ipaddr"] = val["ipaddr"]

                    jsonData[dev]["recv"] = {
                        "bytes": convSize(rlist[0]),
                        "packets": convSize(rlist[1]),
                        "errs": convSize(rlist[2]),
                        "drop": convSize(rlist[3]),
                        "fifo": convSize(rlist[4]),
                        "frame": convSize(rlist[5]),
                        "compressed": convSize(rlist[6]),
                        "multicast": convSize(rlist[7]),
                    }

                    jsonData[dev]["trans"] = {
                        "bytes": convSize(tlist[0]),
                        "packets": convSize(tlist[1]),
                        "errs": convSize(tlist[2]),
                        "drop": convSize(tlist[3]),
                        "fifo": convSize(tlist[4]),
                        "frame": convSize(tlist[5]),
                        "compressed": convSize(tlist[6]),
                        "multicast": convSize(tlist[7]),
                    }
            except SystemExit:
                sys.exit(0)
            except:
                pass

        if cnt == 1:
            SysMgr.infoBufferPrint("{0:<128}|".format("    None"))
        else:
            rdiff = totalStat["rdiff"]
            tdiff = totalStat["tdiff"]
            SysMgr.infoBufferPrint(
                (
                    "{12:1}\n{0:>16} {1:^21} | "
                    "{2:>8} {3:>8} {4:>8} {5:>8} {6:>9} | "
                    "{7:>8} {8:>8} {9:>8} {10:>8} {11:>9} |"
                ).format(
                    "[ TOTAL ]",
                    "DIFF",
                    convSize(rdiff[0]),
                    convSize(rdiff[1]),
                    convSize(rdiff[2]),
                    convSize(rdiff[3]),
                    convSize(rdiff[-1]),
                    convSize(tdiff[0]),
                    convSize(tdiff[1]),
                    convSize(tdiff[2]),
                    convSize(tdiff[3]),
                    convSize(tdiff[-1]),
                    oneLine[:lineLen],
                )
            )

            rtotal = totalStat["rtotal"]
            ttotal = totalStat["ttotal"]
            SysMgr.infoBufferPrint(
                (
                    "{0:>16} {1:^21} | "
                    "{2:>8} {3:>8} {4:>8} {5:>8} {6:>9} | "
                    "{7:>8} {8:>8} {9:>8} {10:>8} {11:>9} |"
                ).format(
                    " ",
                    "TOTAL",
                    convSize(rtotal[0]),
                    convSize(rtotal[1]),
                    convSize(rtotal[2]),
                    convSize(rtotal[3]),
                    convSize(rtotal[-1]),
                    convSize(ttotal[0]),
                    convSize(ttotal[1]),
                    convSize(ttotal[2]),
                    convSize(ttotal[3]),
                    convSize(ttotal[-1]),
                )
            )

        SysMgr.infoBufferPrint("%s" % twoLine[:lineLen])

    def printSwapInfo(self):
        if "NOSWAPINFO" in SysMgr.environList:
            return

        # set max length #
        if SysMgr.outPath:
            maxLen = SysMgr.lineLength
        else:
            maxLen = min(SysMgr.lineLength, SysMgr.ttyCols)

        oneLineLoc = oneLine[:maxLen]
        twoLineLoc = twoLine[:maxLen]

        # add JSON stats #
        if SysMgr.jsonEnable:
            SysMgr.jsonData.setdefault("general", {})
            SysMgr.jsonData["general"].setdefault("swap", {})
        else:
            # print block info #
            SysMgr.infoBufferPrint("\n[System Swap Info]")
            SysMgr.infoBufferPrint(twoLineLoc)
            SysMgr.infoBufferPrint(
                (
                    "{0:>12} {1:>12} {2:>12}({3:>4}) {4:>12}   "
                    "{5:10}{6:1}|\n{7:1}"
                ).format(
                    "Type",
                    "Size",
                    "Used",
                    "Per",
                    "Prio",
                    "Path(Stat)",
                    " " * (maxLen - 71),
                    twoLineLoc,
                )
            )

        # read swap info #
        swapBuf = SysMgr.readProcStat(SysMgr.swapFd, "swaps", SysMgr, "swapFd")
        if not swapBuf or len(swapBuf) == 1:
            if not SysMgr.jsonEnable:
                SysMgr.infoBufferPrint("    None\n%s" % oneLineLoc)
            return

        # remove title line #
        swapBuf = swapBuf[1:]

        totalSize = totalUsed = 0

        for line in swapBuf:
            try:
                path, stype, size, used, prio = line.split()
                size = long(size) << 10
                totalSize += size
                used = long(used) << 10
                totalUsed += used
                per = long((used / float(size)) * 100)
            except SystemExit:
                sys.exit(0)
            except:
                continue

            # get additional info #
            try:
                addInfo = ""
                addDict = {}
                indent = " " * 60
                if os.path.basename(path).startswith("zram"):
                    target = ["comp_algorithm", "mm_stat"]
                    addDict.update(
                        SysMgr.readZramStats(path.split("/")[-1], target)
                    )
                    for item, val in addDict.items():
                        addInfo += "\n{0:1}{1:20}: {2:1}".format(
                            indent, "-" + item, val
                        )
            except SystemExit:
                sys.exit(0)
            except:
                pass

            # add JSON stats #
            if SysMgr.jsonEnable:
                SysMgr.jsonData["general"]["swap"][path] = {
                    "type": stype,
                    "size": size,
                    "used": used,
                    "per": per,
                    "prio": prio,
                    "info": addDict,
                }
                continue

            SysMgr.infoBufferPrint(
                (
                    "{0:>12} {1:>12} {2:>12}({3:>3}%) {4:>12}   "
                    "{5:1}\n{6:1}"
                ).format(
                    stype,
                    UtilMgr.convSize2Unit(size, isInt=True, unit="M"),
                    UtilMgr.convSize2Unit(used, isInt=True, unit="M"),
                    per,
                    prio,
                    path + addInfo,
                    oneLineLoc,
                )
            )

        # print total stat #
        SysMgr.infoBufferPrint(
            "{0:>12} {1:>12} {2:>12}({3:>3}%){4:1}|\n{5:1}".format(
                "TOTAL",
                UtilMgr.convSize2Unit(totalSize, isInt=True, unit="M"),
                UtilMgr.convSize2Unit(totalUsed, isInt=True, unit="M"),
                long((totalUsed / float(totalSize)) * 100),
                " " * (maxLen - 45),
                oneLineLoc,
            )
        )

    def printBlockInfo(self):
        if "NOBLOCKINFO" in SysMgr.environList:
            return

        # add JSON stats #
        if SysMgr.jsonEnable:
            SysMgr.jsonData.setdefault("general", {})
            SysMgr.jsonData["general"]["block"] = self.blockData

        # get maximum sched length #
        lenLine = 9
        for data in self.blockData.values():
            if not "schedList" in data:
                continue
            lenItem = len(" ".join(data["schedList"]))
            if lenLine < lenItem:
                lenLine = lenItem + 1
        lenLine += 85
        oneLineLoc = oneLine[:lenLine]
        twoLineLoc = twoLine[:lenLine]

        # print block info #
        SysMgr.infoBufferPrint("\n[System Block Info]")
        SysMgr.infoBufferPrint(twoLineLoc)
        SysMgr.infoBufferPrint(
            (
                "{0:>16} {1:>8} {2:>8} {3:>8} {4:>10} "
                "{5:>8} {6:>8} {7:>8} {8:<1} {9:1}|"
            ).format(
                "DEV",
                "BlkSize",
                "MinSize",
                "OptSize",
                "Readahead",
                "Affinity",
                "Poll",
                "WrCache",
                "Sched",
                " " * (lenLine - 89),
            )
        )
        SysMgr.infoBufferPrint(twoLineLoc)

        # print block device info #
        for dev, data in UtilMgr.sort(self.blockData):
            try:
                schedList = " ".join(data["schedList"])
                SysMgr.infoBufferPrint(
                    (
                        "{0:>16} {1:>8} {2:>8} {3:>8} {4:>10} "
                        "{5:>8} {6:>8} {7:>8} {8:<1} {9:1} |"
                    ).format(
                        dev,
                        data["size"],
                        data["minSize"],
                        data["optSize"],
                        data["readahead"],
                        data["affinity"],
                        data["poll"],
                        data["wrCache"],
                        schedList,
                        " " * (lenLine - 85 - len(schedList)),
                    )
                )
            except SystemExit:
                sys.exit(0)
            except:
                pass

        if self.blockData:
            SysMgr.infoBufferPrint(oneLineLoc)
        else:
            SysMgr.infoBufferPrint(
                "{0:<1} {1:1}|\n{2:1}".format(
                    "    None", " " * (lenLine - 10), oneLineLoc
                )
            )

    def printStorageInfo(self):
        # add JSON stats #
        if SysMgr.jsonEnable:
            SysMgr.jsonData.setdefault("general", {})
            SysMgr.jsonData["general"]["storage"] = {}
            jsonData = SysMgr.jsonData["general"]["storage"]
        else:
            jsonData = {}

        # set max line length #
        if SysMgr.outPath:
            maxLineLen = SysMgr.lineLength
        else:
            maxLineLen = min(SysMgr.lineLength, SysMgr.ttyCols)

        oneLineLoc = oneLine[:maxLineLen]
        twoLineLoc = twoLine[:maxLineLen]
        remain = max(maxLineLen - 127, 0)
        isTotalMode = not SysMgr.isRuntimeMode()

        # print storage info #
        SysMgr.infoBufferPrint(
            "\n[System Storage Info]%s" % (" (TOTAL)" if isTotalMode else "")
        )
        SysMgr.infoBufferPrint(twoLineLoc)
        SysMgr.infoBufferPrint(
            (
                "{0:^16} {1:>7} {2:>8} {3:>8} {4:>8} "
                "{5:>8} {6:>6} {7:>7} {8:>8} {9:>40} {10:1}|"
            ).format(
                "DEV",
                "NUM",
                "READ",
                "WRITE",
                "TOTAL",
                "FREE",
                "USAGE",
                "NrAVF",
                "FS",
                "MountPoint <Option>",
                " " * remain,
            )
        )
        SysMgr.infoBufferPrint(twoLineLoc)

        outputCnt = 0
        devInfo = {}
        totalInfo = {"total": 0, "free": 0, "favail": 0, "read": 0, "write": 0}
        convSize = UtilMgr.convSize2Unit

        # create block device table #
        for key, val in sorted(self.mountInfo.items(), key=lambda e: e[0]):
            fs = val["fs"]

            # check device node path #
            if fs == "tmpfs":
                key = val["path"]

            if key[0] == "/":
                devInfo[key] = {}
                outputCnt += 1
            else:
                continue

            # calculate read & write size of devices #
            try:
                # initialize device data #
                dev = key[key.rfind("/") + 1 :]
                readSize = writeSize = "?"

                # get real device node #
                if ":" in dev:
                    major, minor = dev.split(":", 1)
                    for name, mp in self.diskInfo["prev"].items():
                        if mp["major"] == major and mp["minor"] == minor:
                            dev = name
                elif (
                    not dev in self.diskInfo["prev"]
                    and not dev in self.diskInfo["next"]
                ):
                    for k, v in self.diskInfo["next"].items():
                        if (
                            val["major"] == v["major"]
                            and val["minor"] == v["minor"]
                        ):
                            dev = k
                            break

                beforeInfo = self.diskInfo["prev"][dev]
                afterInfo = self.diskInfo["next"][dev]

                # read #
                read = readSize = (
                    long(afterInfo["sectorRead"])
                    - (0 if isTotalMode else long(beforeInfo["sectorRead"]))
                ) << 9
                readSize = convSize(readSize)

                # write #
                write = writeSize = (
                    long(afterInfo["sectorWrite"])
                    - (0 if isTotalMode else long(beforeInfo["sectorWrite"]))
                ) << 9
                writeSize = convSize(writeSize)

                if not fs in ("tmpfs", "-"):
                    totalInfo["read"] += read
                    totalInfo["write"] += write
            except SystemExit:
                sys.exit(0)
            except:
                pass

            # get device stat #
            try:
                major = minor = total = free = use = avail = "?"

                fstat = os.lstat(val["path"])
                major = os.major(fstat.st_dev)
                minor = os.minor(fstat.st_dev)

                if hasattr(os, "statvfs"):
                    stat = os.statvfs(val["path"])
                else:
                    stat = SysMgr.statvfs(val["path"])

                total = stat.f_bsize * stat.f_blocks
                free = stat.f_bsize * stat.f_bavail
                if hasattr(stat, "f_favail"):
                    avail = stat.f_favail
                else:
                    avail = 0

                use = "%d%%" % long(((total - free) / float(total)) * 100)

                try:
                    if fs != "tmpfs":
                        totalInfo["total"] += total
                        totalInfo["free"] += free
                        totalInfo["favail"] += avail
                except:
                    pass

                total = convSize(total)
                free = convSize(free)
                avail = convSize(avail)
            except SystemExit:
                sys.exit(0)
            except:
                pass

            # update device number #
            if major == minor == "?":
                if "major" in val:
                    major = long(val["major"])
                if "minor" in val:
                    minor = long(val["minor"])

            # get partition range #
            try:
                devid = "%s:%s" % (major, minor)
                blkStart = self.partitionInfo[devid]["start"]
                blkEnd = self.partitionInfo[devid]["end"]
                prange = "[%s-%s]" % (blkStart, blkEnd)
            except:
                prange = "[?]"
                blkStart = blkEnd = 0

            # update total #
            try:
                length = blkEnd - blkStart
                if total == "?":
                    # devname = key.rsplit("/", 1)[1]
                    total = convSize(length * 512)
            except:
                length = 0

            # update device path with partition range #
            try:
                key = "%s (%s) %s" % (
                    key,
                    ",".join(self.devInfo["block"][major]),
                    prange,
                )
            except SystemExit:
                sys.exit(0)
            except:
                pass

            # print device info #
            SysMgr.infoBufferPrint("{0:<16}".format(key))

            # build block device info string #
            try:
                diskInfo = (
                    "{0:<16} {1:>7} {2:>8} {3:>8} {4:>8} "
                    "{5:>8} {6:>6} {7:>7} {8:>8} "
                ).format(
                    " ",
                    "%s:%s" % (major, minor),
                    readSize,
                    writeSize,
                    total,
                    free,
                    use,
                    avail,
                    fs,
                )
            except:
                continue

            # print storage info #
            try:
                title = diskInfo
                splitLen = SysMgr.lineLength - len(diskInfo) - 1

                if val["option"]:
                    option = "+".join([val["option"], val["soption"]])
                else:
                    option = val["option"]

                if option:
                    option = " <%s>" % option
                mountList = "%s%s" % (val["path"], option)

                mountList = [
                    mountList[i : i + splitLen]
                    for i in xrange(0, len(mountList), splitLen)
                ]

                for string in mountList:
                    SysMgr.infoBufferPrint(
                        "{0:85} {1:<1}".format(title, string)
                    )
                    title = " "
            except SystemExit:
                sys.exit(0)
            except:
                pass

            try:
                if SysMgr.jsonEnable:
                    jsonData[key] = {
                        "major": major,
                        "minor": minor,
                        "read": readSize,
                        "write": writeSize,
                        "total": total,
                        "free": free,
                        "use": use,
                        "avail": avail,
                        "fs": fs,
                        "mount": "%s %s" % (val["path"], val["option"]),
                    }
            except SystemExit:
                sys.exit(0)
            except:
                pass

        # print total I/O size #
        if outputCnt == 0:
            SysMgr.infoBufferPrint("\tN/A")
        else:
            try:
                try:
                    usage = long(
                        (
                            (totalInfo["total"] - totalInfo["free"])
                            / float(totalInfo["total"])
                        )
                        * 100
                    )
                except:
                    usage = 0

                totalInfo["total"] = convSize(totalInfo["total"])
                totalInfo["free"] = convSize(totalInfo["free"])
                totalInfo["favail"] = convSize(totalInfo["favail"])
                totalInfo["read"] = convSize(totalInfo["read"])
                totalInfo["write"] = convSize(totalInfo["write"])
                totalInfo["use"] = "%d%%" % usage
            except SystemExit:
                sys.exit(0)
            except:
                totalInfo["use"] = "?%"

            SysMgr.infoBufferPrint(
                (
                    "{0:^16}\n{1:^24} {2:>8} {3:>8} {4:>8} "
                    "{5:>8} {6:>6} {7:>7} {8:>8} {9:<20} {10:1}|"
                ).format(
                    oneLineLoc,
                    "TOTAL",
                    totalInfo["read"],
                    totalInfo["write"],
                    totalInfo["total"],
                    totalInfo["free"],
                    totalInfo["use"],
                    totalInfo["favail"],
                    " ",
                    " ",
                    " " * (remain + 20),
                )
            )

        SysMgr.infoBufferPrint("%s" % twoLineLoc)

    def printMemInfo(self):
        if len(self.memData) != 2:
            return

        if "NOMEMINFO" in SysMgr.environList:
            return

        # parse previous data #
        time = "prev"
        self.memInfo[time] = {}
        for l in self.memData[time]:
            m = re.match(r"(?P<type>\S+):\s+(?P<size>[0-9]+)", l)
            if m:
                d = m.groupdict()
                self.memInfo[time][d["type"]] = d["size"]

        # parse current data #
        time = "next"
        self.memInfo[time] = {}
        for l in self.memData[time]:
            m = re.match(r"(?P<type>\S+):\s+(?P<size>[0-9]+)", l)
            if m:
                d = m.groupdict()
                self.memInfo[time][d["type"]] = d["size"]

        before = self.memInfo["prev"]
        after = self.memInfo["next"]

        # check items for compatibility #
        try:
            before["Shmem"]
        except:
            before["Shmem"] = "0"
            after["Shmem"] = "0"

        try:
            before["SReclaimable"]
        except:
            before["SReclaimable"] = "0"
            after["SReclaimable"] = "0"

        try:
            before["Sunreclaim"]
        except:
            before["Sunreclaim"] = "0"
            after["Sunreclaim"] = "0"

        try:
            before["Mlocked"]
        except:
            before["Mlocked"] = "0"
            after["Mlocked"] = "0"

        # define convert function #
        convSize = UtilMgr.convSize2Unit

        # print memory info #
        lineLen = len(oneLine)
        SysMgr.infoBufferPrint("\n[System Memory Info]")
        SysMgr.infoBufferPrint(twoLine[:lineLen])
        SysMgr.infoBufferPrint(
            (
                "[%6s] %10s %10s %10s %10s %10s %10s %10s %10s "
                "%10s %10s %10s %10s %10s  |"
            )
            % (
                "DESC ",
                "Memory",
                "Swap",
                "Buffer",
                "Cache",
                "Shared",
                "Mapped",
                "Active",
                "Inactive",
                "PageTables",
                "Slab",
                "SlabRclm",
                "SlabUnRclm",
                "Mlocked",
            )
        )
        SysMgr.infoBufferPrint(twoLine[:lineLen])

        SysMgr.infoBufferPrint(
            "[ TOTAL] %10s %10s %122s|"
            % (
                convSize(long(before["MemTotal"]) << 10),
                convSize(long(before["SwapTotal"]) << 10),
                " ",
            )
        )

        SysMgr.infoBufferPrint(
            "[ FREE ] %10s %10s %122s|"
            % (
                convSize(long(before["MemFree"]) << 10),
                convSize(long(before["SwapFree"]) << 10),
                " ",
            )
        )
        if "MemAvailable" in before:
            SysMgr.infoBufferPrint(
                "[ AVAIL] %10s %10s %122s|"
                % (
                    convSize(long(before["MemAvailable"]) << 10),
                    convSize(long(before["SwapFree"]) << 10),
                    " ",
                )
            )
        SysMgr.infoBufferPrint(oneLine[:lineLen])

        if "MemAvailable" in before:
            memBeforeUsage = long(before["MemTotal"]) - long(
                before["MemAvailable"]
            )
        else:
            memBeforeUsage = long(before["MemTotal"]) - long(before["MemFree"])

        swapBeforeUsage = long(before["SwapTotal"]) - long(before["SwapFree"])
        if "MemAvailable" in before:
            memAfterUsage = long(after["MemTotal"]) - long(
                after["MemAvailable"]
            )
        else:
            memAfterUsage = long(after["MemTotal"]) - long(after["MemFree"])

        swapAfterUsage = long(after["SwapTotal"]) - long(after["SwapFree"])

        SysMgr.infoBufferPrint(
            (
                "[ FIRST] %10s %10s %10s %10s %10s %10s %10s "
                "%10s %10s %10s %10s %10s %10s  |"
            )
            % (
                convSize(memBeforeUsage << 10),
                convSize(swapBeforeUsage << 10),
                convSize(long(before["Buffers"]) << 10),
                convSize(long(before["Cached"]) << 10),
                convSize(long(before["Shmem"]) << 10),
                convSize(long(before["Mapped"]) << 10),
                convSize(long(before["Active"]) << 10),
                convSize(long(before["Inactive"]) << 10),
                convSize(long(before["PageTables"]) << 10),
                convSize(long(before["Slab"]) << 10),
                convSize(long(before["SReclaimable"]) << 10),
                convSize(long(before["SUnreclaim"]) << 10),
                convSize(long(before["Mlocked"]) << 10),
            )
        )

        SysMgr.infoBufferPrint(
            (
                "[ LAST ] %10s %10s %10s %10s %10s %10s %10s "
                "%10s %10s %10s %10s %10s %10s  |"
            )
            % (
                convSize(memAfterUsage << 10),
                convSize(swapAfterUsage << 10),
                convSize(long(after["Buffers"]) << 10),
                convSize(long(after["Cached"]) << 10),
                convSize(long(after["Shmem"]) << 10),
                convSize(long(after["Mapped"]) << 10),
                convSize(long(after["Active"]) << 10),
                convSize(long(after["Inactive"]) << 10),
                convSize(long(after["PageTables"]) << 10),
                convSize(long(after["Slab"]) << 10),
                convSize(long(after["SReclaimable"]) << 10),
                convSize(long(after["SUnreclaim"]) << 10),
                convSize(long(after["Mlocked"]) << 10),
            )
        )

        SysMgr.infoBufferPrint(oneLine[:lineLen])

        def _diffItem(a, b, item):
            return (long(a[item]) - long(b[item])) << 10

        SysMgr.infoBufferPrint(
            (
                "[ DIFF ] %10s %10s %10s %10s %10s %10s %10s "
                "%10s %10s %10s %10s %10s %10s  |"
            )
            % (
                convSize((memAfterUsage - memBeforeUsage) << 10),
                convSize((swapAfterUsage - swapBeforeUsage) << 10),
                convSize(_diffItem(after, before, "Buffers")),
                convSize(_diffItem(after, before, "Cached")),
                convSize(_diffItem(after, before, "Shmem")),
                convSize(_diffItem(after, before, "Mapped")),
                convSize(_diffItem(after, before, "Active")),
                convSize(_diffItem(after, before, "Inactive")),
                convSize(_diffItem(after, before, "PageTables")),
                convSize(_diffItem(after, before, "Slab")),
                convSize(_diffItem(after, before, "SReclaimable")),
                convSize(_diffItem(after, before, "SUnreclaim")),
                convSize(_diffItem(after, before, "Mlocked")),
            )
        )

        SysMgr.infoBufferPrint(twoLine[:lineLen])

        # add JSON stats #
        if SysMgr.jsonEnable:
            SysMgr.jsonData.setdefault("general", {})
            SysMgr.jsonData["general"]["mem"] = {
                "memTotal": convSize(long(after["MemTotal"]) << 10),
                "memFree": convSize(long(after["MemFree"]) << 10),
                "swapTotal": convSize(long(after["SwapTotal"]) << 10),
                "swapFree": convSize(long(after["SwapFree"]) << 10),
                "buffer": convSize(long(after["Buffers"]) << 10),
                "cache": convSize(long(after["Cached"]) << 10),
                "shmem": convSize(long(after["Shmem"]) << 10),
                "mapped": convSize(long(after["Mapped"]) << 10),
                "active": convSize(long(after["Active"]) << 10),
                "inactive": convSize(long(after["Inactive"]) << 10),
                "pagetable": convSize(long(after["PageTables"]) << 10),
                "slab": convSize(long(after["Slab"]) << 10),
                "sreclaimable": convSize(long(after["SReclaimable"]) << 10),
                "sunreclaimable": convSize(long(after["SUnreclaim"]) << 10),
                "mlock": convSize(long(after["Mlocked"]) << 10),
            }

            if "MemAvailable" in after:
                SysMgr.jsonData["general"]["mem"]["memAvailable"] = convSize(
                    long(after["MemAvailable"]) << 10
                )


class DbusMgr(object):
    """Analyzer for D-Bus"""

    errObj = None
    dbusErrObj = None
    msgData = []
    sentData = {}
    recvData = {}
    prevData = {}
    msgSentTable = {}
    msgRecvTable = {}
    msgColorList = {}
    connCache = {}
    dbgObj = None
    isTopMode = False
    watchList = []
    refilterList = []
    totalTime = 0
    pidUnitList = {}

    G_IO_ERROR_TYPE = [
        "G_IO_ERROR_FAILED",
        "G_IO_ERROR_NOT_FOUND",
        "G_IO_ERROR_EXISTS",
        "G_IO_ERROR_IS_DIRECTORY",
        "G_IO_ERROR_NOT_DIRECTORY",
        "G_IO_ERROR_NOT_EMPTY",
        "G_IO_ERROR_NOT_REGULAR_FILE",
        "G_IO_ERROR_NOT_SYMBOLIC_LINK",
        "G_IO_ERROR_NOT_MOUNTABLE_FILE",
        "G_IO_ERROR_FILENAME_TOO_LONG",
        "G_IO_ERROR_INVALID_FILENAME",
        "G_IO_ERROR_TOO_MANY_LINKS",
        "G_IO_ERROR_NO_SPACE",
        "G_IO_ERROR_INVALID_ARGUMENT",
        "G_IO_ERROR_PERMISSION_DENIED",
        "G_IO_ERROR_NOT_SUPPORTED",
        "G_IO_ERROR_NOT_MOUNTED",
        "G_IO_ERROR_ALREADY_MOUNTED",
        "G_IO_ERROR_CLOSED",
        "G_IO_ERROR_CANCELLED",
        "G_IO_ERROR_PENDING",
        "G_IO_ERROR_READ_ONLY",
        "G_IO_ERROR_CANT_CREATE_BACKUP",
        "G_IO_ERROR_WRONG_ETAG",
        "G_IO_ERROR_TIMED_OUT",
        "G_IO_ERROR_WOULD_RECURSE",
        "G_IO_ERROR_BUSY",
        "G_IO_ERROR_WOULD_BLOCK",
        "G_IO_ERROR_HOST_NOT_FOUND",
        "G_IO_ERROR_WOULD_MERGE",
        "G_IO_ERROR_FAILED_HANDLED",
        "G_IO_ERROR_TOO_MANY_OPEN_FILES",
        "G_IO_ERROR_NOT_INITIALIZED",
        "G_IO_ERROR_ADDRESS_IN_USE",
        "G_IO_ERROR_PARTIAL_INPUT",
        "G_IO_ERROR_INVALID_DATA",
        "G_IO_ERROR_DBUS_ERROR",
        "G_IO_ERROR_HOST_UNREACHABLE",
        "G_IO_ERROR_NETWORK_UNREACHABLE",
        "G_IO_ERROR_CONNECTION_REFUSED",
        "G_IO_ERROR_PROXY_FAILED",
        "G_IO_ERROR_PROXY_AUTH_FAILED",
        "G_IO_ERROR_PROXY_NEED_AUTH",
        "G_IO_ERROR_PROXY_NOT_ALLOWED",
        "G_IO_ERROR_BROKEN_PIPE",
        "G_IO_ERROR_CONNECTION_CLOSED",
        "G_IO_ERROR_NOT_CONNECTED",
        "G_IO_ERROR_MESSAGE_TOO_LARGE",
    ]

    DBusBusType = {
        "DBUS_BUS_SESSION": 0,
        "DBUS_BUS_SYSTEM": 1,
        "DBUS_BUS_STARTER": 2,
    }

    GDBusMessageType = [
        "INVALID",
        "METHOD",  # METHOD_CALL
        "RETURN",  # METHOD_RETURN
        "ERROR",
        "SIGNAL",
    ]

    taskInfo = {"min": 0, "max": 0, "err": 0, "cnt": 0, "size": 0, "total": 0}

    @staticmethod
    def getErrInfo():
        if DbusMgr.dbusErrObj:
            errObj = DbusMgr.dbusErrObj
            return "%s: %s" % (errObj.name, errObj.message)
        else:
            return "N/A"

    @staticmethod
    def getErrP():
        if DbusMgr.dbusErrObj:
            dbusErrP = byref(DbusMgr.dbusErrObj)
            SysMgr.libdbusObj.dbus_error_init(dbusErrP)
            return dbusErrP

        # define error object #
        class DBusError(Structure):
            _fields_ = (
                ("name", c_char_p),
                ("message", c_char_p),
                ("padding2", c_void_p * 2),
            )

        DbusMgr.dbusErrObj = dbusErr = DBusError()
        dbusErrP = byref(dbusErr)
        SysMgr.libdbusObj.dbus_error_init(dbusErrP)
        return dbusErrP

    @staticmethod
    def getBus(bus, tid=None, addr=None):
        def _setEuid():
            # set EUID #
            try:
                euidOrig = os.geteuid()
                if tid:
                    euidNew = SysMgr.getUid(tid, "effective")
                    os.seteuid(euidNew)
            except SystemExit:
                sys.exit(0)
            except:
                comm = SysMgr.getComm(tid, cache=True)
                SysMgr.printErr(
                    "failed to set EUID for %s(%s)'s one" % (comm, tid),
                    reason=True,
                )

            return euidOrig

        # reuse connection from cache #
        if bus in DbusMgr.connCache:
            return DbusMgr.connCache[bus]

        ADDRENV = "DBUS_SESSION_BUS_ADDRESS"
        dbusObj = SysMgr.libdbusObj
        name = "guider.method.caller".encode()
        procInfo = "%s(%s)" % (SysMgr.getComm(tid, cache=True), tid)

        # get bus type #
        if bus == "system":
            busType = DbusMgr.DBusBusType["DBUS_BUS_SYSTEM"]

            # save EUID #
            euidOrig = os.geteuid()
        else:
            busType = DbusMgr.DBusBusType["DBUS_BUS_SESSION"]

            if tid:
                # set bus address #
                envList = SysMgr.getEnv(tid)
                for env in envList:
                    if env.startswith(ADDRENV):
                        saddr = UtilMgr.lstrip(env, ADDRENV)[1:]
                        os.environ[ADDRENV] = saddr
                        break

            # set EUID #
            euidOrig = _setEuid()

        # get connection #
        conn = dbusObj.dbus_bus_get_private(busType, DbusMgr.getErrP())
        if conn:
            conn = dbusObj.dbus_connection_ref(conn)
        else:
            # get connection by session address #
            if ADDRENV in os.environ:
                addr = os.environ[ADDRENV]
                addr = c_char_p(addr.encode())
                conn = dbusObj.dbus_connection_open(addr, DbusMgr.getErrP())
            elif tid:
                # recover EUID #
                os.seteuid(euidOrig)

                # set bus address #
                envList = SysMgr.getEnv(tid)
                for env in envList:
                    if env.startswith(ADDRENV):
                        saddr = UtilMgr.lstrip(env, ADDRENV)[1:]
                        renv = saddr.encode()
                        addr = c_char_p(renv)
                        conn = dbusObj.dbus_connection_open(
                            addr, DbusMgr.getErrP()
                        )
                        break

            # check error #
            if conn:
                conn = dbusObj.dbus_connection_ref(conn)
                ret = dbusObj.dbus_bus_set_unique_name(
                    c_void_p(conn), c_char_p(name)
                )
                ret = dbusObj.dbus_bus_get_unique_name(c_void_p(conn))
                ret = dbusObj.dbus_bus_register(
                    c_void_p(conn), DbusMgr.getErrP()
                )
                if not ret:
                    SysMgr.printWarn(
                        "failed to register D-Bus %s bus for %s because %s"
                        % (bus, procInfo, DbusMgr.getErrInfo())
                    )
                    return None
            else:
                SysMgr.printWarn(
                    "failed to get D-Bus %s bus for %s because %s"
                    % (bus, procInfo, DbusMgr.getErrInfo())
                )
                return None

        """
        # request name #
        DBUS_NAME_FLAG_ALLOW_REPLACEMENT = c_uint(0x1)
        DBUS_NAME_FLAG_REPLACE_EXISTING = c_uint(0x2)
        ret = dbusObj.dbus_bus_request_name(
            conn, c_char_p(name), DBUS_NAME_FLAG_REPLACE_EXISTING,
            DbusMgr.getErrP())
        if ret < 0:
            SysMgr.printWarn(
                "failed to request D-Bus bus name to %s for %s because %s" % \
                    (name.decode(), procInfo, DbusMgr.getErrInfo()))
        """

        try:
            ret = conn

            # check connection #
            if dbusObj.dbus_connection_get_is_connected(conn) == 0:
                SysMgr.printWarn(
                    "D-Bus %s bus is not connected yet for %s"
                    % (bus, procInfo)
                )
                ret = None
            # send a message for method call #
            elif dbusObj.dbus_connection_get_is_authenticated(conn) == 0:
                path = "/org/freedesktop/DBus"
                des = iface = "org.freedesktop.DBus"
                method = "Hello"
                msg, reply = DbusMgr.callMethod(conn, des, path, iface, method)
                if not msg or not reply:
                    if busType == DbusMgr.DBusBusType["DBUS_BUS_SESSION"]:
                        SysMgr.printWarn(
                            "check DBUS_SESSION_BUS_ADDRESS for %s bus for %s"
                            % (bus, procInfo),
                            True,
                        )
                    ret = None
        finally:
            # recover EUID #
            try:
                os.seteuid(euidOrig)
            except:
                pass

            # save connection cache #
            if ret:
                DbusMgr.connCache[bus] = ret

            return ret

    @staticmethod
    def runMonitor(bus, des=None):
        if not bus:
            return

        dbusObj = SysMgr.libdbusObj

        conn = DbusMgr.getBus(bus)
        if not conn:
            return

        # prepare method args #
        path = "/"
        if not des:
            des = "org.freedesktop.DBus"
        if not des.startswith(":"):
            path += des.replace(".", "/")
        if (
            dbusObj.dbus_validate_path(
                c_char_p(path.encode()), DbusMgr.getErrP()
            )
            == 0
        ):
            SysMgr.printWarn(
                "failed to create a D-Bus message because %s"
                % DbusMgr.getErrInfo()
            )
            return

        # set interface, method, timeout #
        iface = "org.freedesktop.DBus.Monitoring"
        method = "BecomeMonitor"
        timeout = c_int(-1)

        # create a message for method call #
        msg = dbusObj.dbus_message_new_method_call(
            des.encode(), path.encode(), iface.encode(), method.encode()
        )
        if not msg:
            # dbusObj.dbus_connection_unref(conn)
            SysMgr.printWarn("failed to create a D-Bus message")
            return

        # prepare args #
        d = DbusMgr.getTypeList()
        zero = c_uint32(0)
        char = c_char("s".encode())

        msgIterP = byref(DbusMgr.getMessageIterObj())
        arrayIterP = byref(DbusMgr.getMessageIterObj())

        # initialize iteration #
        ret = dbusObj.dbus_message_iter_init_append(msg, msgIterP)
        if not ret:
            SysMgr.printWarn("failed to initialize D-Bus message iteration")
            dbusObj.dbus_message_unref(msg)
            # dbusObj.dbus_connection_unref(conn)
            return

        # append container #
        ret = dbusObj.dbus_message_iter_open_container(
            msgIterP, d["DBUS_TYPE_ARRAY"], byref(char), arrayIterP
        )
        if not ret:
            SysMgr.printWarn("failed to initialize D-Bus message container")
            dbusObj.dbus_message_unref(msg)
            # dbusObj.dbus_connection_unref(conn)
            return

        """
        # append filter #
        ret = dbusObj.dbus_message_iter_append_basic(
            arrayIterP, d["DBUS_TYPE_STRING"], byref(char))
        if not ret:
            SysMgr.printWarn("failed to initialize D-Bus message iteration")
            dbusObj.dbus_message_unref(msg)
            #dbusObj.dbus_connection_unref(conn)
            return
        """

        # close container #
        ret = dbusObj.dbus_message_iter_close_container(msgIterP, arrayIterP)
        if not ret:
            SysMgr.printWarn("failed to close D-Bus message container")
            dbusObj.dbus_message_unref(msg)
            # dbusObj.dbus_connection_unref(conn)
            return

        # append zero to container #
        ret = dbusObj.dbus_message_iter_append_basic(
            msgIterP, d["DBUS_TYPE_UINT32"], pointer(zero)
        )
        if not ret:
            SysMgr.printWarn("failed to initialize D-Bus message iteration")
            dbusObj.dbus_message_unref(msg)
            # dbusObj.dbus_connection_unref(conn)
            return

        # call a remote method #
        reply = dbusObj.dbus_connection_send_with_reply_and_block(
            conn, msg, timeout, DbusMgr.getErrP()
        )
        if not reply:
            dbusObj.dbus_message_unref(msg)
            # dbusObj.dbus_connection_unref(conn)
            SysMgr.printWarn(
                "failed to call a D-Bus remote method because %s at %s"
                % (DbusMgr.getErrInfo(), SysMgr.getLine())
            )
            return

        while 1:
            reply = dbusObj.dbus_connection_read_write_dispatch(conn, -1)

    @staticmethod
    def callMethod(conn, des, path, iface, method, timeout=None):
        dbusObj = SysMgr.libdbusObj

        if timeout is None:
            timeout = c_int(-1)

        # create a message for method call #
        msg = dbusObj.dbus_message_new_method_call(
            des.encode(), path.encode(), iface.encode(), method.encode()
        )
        if not msg:
            # dbusObj.dbus_connection_unref(conn)
            SysMgr.printWarn("failed to create a D-Bus message")
            return msg, None

        # call a remote method #
        reply = dbusObj.dbus_connection_send_with_reply_and_block(
            conn, msg, timeout, DbusMgr.getErrP()
        )
        if not reply:
            dbusObj.dbus_message_unref(msg)
            # dbusObj.dbus_connection_unref(conn)
            SysMgr.printWarn(
                "failed to call a D-Bus remote method because %s at %s"
                % (DbusMgr.getErrInfo(), SysMgr.getLine())
            )
            return msg, reply

        return msg, reply

    @staticmethod
    def getMessageIterObj():
        class DBusMessageIter(Structure):
            _fields_ = (
                ("dummy1", c_void_p),
                ("dummy2", c_void_p),
                ("dummy3", c_uint32),
                ("dummy4", c_int),
                ("dummy5", c_int),
                ("dummy6", c_int),
                ("dummy7", c_int),
                ("dummy8", c_int),
                ("dummy9", c_int),
                ("dummy10", c_int),
                ("dummy11", c_int),
                ("pad1", c_int),
                ("pad2", c_void_p),
                ("pad3", c_void_p),
            )

        return DBusMessageIter()

    @staticmethod
    def getStats(bus, request, des=None, tid=None, procStr=None, path=None):
        # pylint: disable=no-member
        def _printWarn(procStr, line, err):
            SysMgr.printWarn(
                ("failed to parse D-Bus message for %s at %s " "because %s")
                % (procStr, line, err),
                True,
            )

        def _cleanUp(msg, reply, procStr, warn=True):
            SysMgr.libdbusObj.dbus_message_unref(msg)
            SysMgr.libdbusObj.dbus_message_unref(reply)
            # dbusObj.dbus_connection_unref(conn)
            if warn:
                _printWarn(procStr, getLine(), getErr())

        if not bus:
            return

        dbusObj = SysMgr.libdbusObj

        conn = DbusMgr.getBus(bus, tid)
        if not conn:
            return

        getLine = SysMgr.getLine
        getErr = DbusMgr.getErrInfo

        # prepare method args #
        if not path:
            path = "/"
        if not des:
            des = "org.freedesktop.DBus"
        if not des.startswith(":"):
            path += des.replace(".", "/")
        if (
            dbusObj.dbus_validate_path(
                c_char_p(path.encode()), DbusMgr.getErrP()
            )
            == 0
        ):
            _printWarn(procStr, getLine(), getErr())
            return

        # set interface, method, timeout #
        if request == "introspect":
            iface = "org.freedesktop.DBus.Introspectable"
            method = "Introspect"
            timeout = c_int(100)
        elif request == "allmatch":
            iface = "org.freedesktop.DBus.Debug.Stats"
            method = "GetAllMatchRules"
            timeout = c_int(-1)
        elif request == "stats":
            iface = "org.freedesktop.DBus.Debug.Stats"
            method = "GetStats"
            timeout = c_int(-1)
        else:
            SysMgr.printErr("unknown request %s for %s" % (request, procStr))
            sys.exit(-1)

        # send a message for method call #
        msg, reply = DbusMgr.callMethod(
            conn, des, path, iface, method, timeout
        )
        if not msg or not reply:
            return

        # prepare args #
        # refer to https://dbus.freedesktop.org/doc/api/html/group__DBusProtocol.html #
        rd = {}
        d = DbusMgr.getTypeList()
        UtilMgr.makeReverseDict(d, rd)

        # introspect #
        if request == "introspect":
            strRes = c_char_p("".encode())
            res = dbusObj.dbus_message_get_args(
                reply,
                DbusMgr.getErrP(),
                d["DBUS_TYPE_STRING"],
                byref(strRes),
                d["DBUS_TYPE_INVALID"],
            )
            if not res:
                _cleanUp(msg, reply, procStr)
                return

            # parse args #
            strRes = c_char_p("".encode())
            res = dbusObj.dbus_message_get_args(
                reply,
                DbusMgr.getErrP(),
                d["DBUS_TYPE_STRING"],
                byref(strRes),
                d["DBUS_TYPE_INVALID"],
            )
            if not res:
                _printWarn(procStr, getLine(), getErr())
                return

            # convert value #
            buf = str(strRes.value.decode())

            # clean up #
            _cleanUp(msg, reply, procStr, False)

            return buf

        # allmatch #
        perProcList = {}
        perSigList = {}

        # initialize message iterator #
        rootIterP = byref(DbusMgr.getMessageIterObj())
        arrayIterP = byref(DbusMgr.getMessageIterObj())
        dictIterP = byref(DbusMgr.getMessageIterObj())
        arraySigIterP = byref(DbusMgr.getMessageIterObj())

        procInfo = c_char_p("".encode())
        sigInfo = c_char_p("".encode())

        # initialize iteration #
        ret = dbusObj.dbus_message_iter_init(reply, rootIterP)
        if not ret:
            _cleanUp(msg, reply, procStr)
            return

        ret = dbusObj.dbus_message_iter_get_arg_type(rootIterP)
        if ret != d["DBUS_TYPE_ARRAY"]:
            _cleanUp(msg, reply, procStr)
            return

        # get item count #
        # cnt = dbusObj.dbus_message_iter_get_element_count(rootIterP)

        # get root #
        dbusObj.dbus_message_iter_recurse(rootIterP, arrayIterP)

        # stats #
        if request == "stats":
            SysMgr.printStat(
                "start collecting stats for %s bus for %s" % (bus, procStr)
            )

            name = c_char_p("".encode())
            value = c_uint32(0)
            statList = {}

            # array item loop #
            while 1:
                ret = dbusObj.dbus_message_iter_get_arg_type(arrayIterP)
                if ret != d["DBUS_TYPE_DICT_ENTRY"]:
                    _cleanUp(msg, reply, procStr)
                    return statList

                dbusObj.dbus_message_iter_recurse(arrayIterP, dictIterP)

                # dictionary item loop #
                while 1:
                    ret = dbusObj.dbus_message_iter_get_arg_type(dictIterP)
                    if ret != d["DBUS_TYPE_STRING"]:
                        _cleanUp(msg, reply, procStr)
                        return statList

                    # get name #
                    dbusObj.dbus_message_iter_get_basic(dictIterP, byref(name))
                    if not name.value:
                        return statList

                    # decode name #
                    sname = name.value.decode()

                    # register stat name #
                    statList.setdefault(sname, [])

                    # next stat value #
                    if not dbusObj.dbus_message_iter_next(dictIterP):
                        break

                    # get stat values as a variant-type value #
                    ret = dbusObj.dbus_message_iter_get_arg_type(dictIterP)
                    if ret != d["DBUS_TYPE_VARIANT"]:
                        _cleanUp(msg, reply, procStr)
                        break

                    # get items #
                    statList[sname] = DbusMgr.getVariantItems(
                        dictIterP, value, d, rd
                    )

                    # next stat #
                    if not dbusObj.dbus_message_iter_next(dictIterP):
                        break

                # next item #
                if not dbusObj.dbus_message_iter_next(arrayIterP):
                    break

            return statList

        SysMgr.printStat(
            "start collecting subscription info for %s bus for %s"
            % (bus, procStr)
        )

        # array item loop #
        while 1:
            ret = dbusObj.dbus_message_iter_get_arg_type(arrayIterP)
            if ret != d["DBUS_TYPE_DICT_ENTRY"]:
                _cleanUp(msg, reply, procStr)
                return

            dbusObj.dbus_message_iter_recurse(arrayIterP, dictIterP)

            # dictionary item loop #
            while 1:
                ret = dbusObj.dbus_message_iter_get_arg_type(dictIterP)
                if ret != d["DBUS_TYPE_STRING"]:
                    _cleanUp(msg, reply, procStr)
                    return

                # get process ID #
                dbusObj.dbus_message_iter_get_basic(dictIterP, byref(procInfo))
                if not procInfo.value:
                    _cleanUp(msg, reply, procStr)
                    return

                if not dbusObj.dbus_message_iter_next(dictIterP):
                    break

                ret = dbusObj.dbus_message_iter_get_arg_type(dictIterP)
                if ret != d["DBUS_TYPE_ARRAY"]:
                    _cleanUp(msg, reply, procStr)
                    return

                # check array size #
                if dbusObj.dbus_message_iter_get_element_count(dictIterP) == 0:
                    if not dbusObj.dbus_message_iter_next(dictIterP):
                        break
                    else:
                        continue

                # allocate a new task dict #
                procId = procInfo.value.decode()
                perProcList.setdefault(procId, {})
                procSigList = perProcList[procId]

                # parse signal array #
                dbusObj.dbus_message_iter_recurse(dictIterP, arraySigIterP)

                while 1:
                    ret = dbusObj.dbus_message_iter_get_arg_type(arraySigIterP)
                    if ret != d["DBUS_TYPE_STRING"]:
                        _cleanUp(msg, reply, procStr)
                        return

                    # get signal info #
                    dbusObj.dbus_message_iter_get_basic(
                        arraySigIterP, byref(sigInfo)
                    )

                    sinfo = {}

                    # parse items #
                    if sigInfo.value:
                        for item in sigInfo.value.decode().split(","):
                            slist = item.strip('"').split("=")
                            sinfo[slist[0]] = slist[1].strip("'")

                    # save items to list #
                    if "interface" in sinfo:
                        iface = sinfo["interface"]

                        if "sender" in sinfo:
                            sender = sinfo["sender"]
                        else:
                            sender = iface

                        if "member" in sinfo:
                            member = ".%s" % sinfo["member"]
                        else:
                            member = ""

                        addr = "%s%s" % (iface, member)

                        # save perProc items #
                        procSigList.setdefault(sender, {})
                        procSigList[sender].setdefault(addr, {})
                        if "arg0" in sinfo:
                            argList = [
                                "%s" % sinfo[i]
                                for i in sorted(list(sinfo))
                                if i.startswith("arg")
                            ]
                            procSigList[sender][addr].setdefault(
                                ", ".join(argList)
                            )

                        # save perSignal items #
                        perSigList.setdefault(sender, {})
                        sigProcList = perSigList[sender]
                        sigProcList.setdefault(addr, {})
                        sigProcList[addr].setdefault(procId)

                    if not dbusObj.dbus_message_iter_next(arraySigIterP):
                        break

                if not dbusObj.dbus_message_iter_next(dictIterP):
                    break

            if not dbusObj.dbus_message_iter_next(arrayIterP):
                break

        # clean up #
        _cleanUp(msg, reply, procStr, False)

        return perProcList, perSigList

    @staticmethod
    def getUnitAttr(bus, path, attr):
        # pylint: disable=no-member

        # check input #
        if not all([bus, path, attr]):
            return

        dbusObj = SysMgr.libdbusObj

        # get connection #
        conn = DbusMgr.getBus(bus)
        if not conn:
            return

        # create a message for method call #
        des = "org.freedesktop.systemd1"
        unit = "org.freedesktop.systemd1.Unit"
        iface = "org.freedesktop.DBus.Properties"
        method = "Get"
        timeout = c_int(100)

        msg = dbusObj.dbus_message_new_method_call(
            des.encode(), path.encode(), iface.encode(), method.encode()
        )
        if not msg:
            # dbusObj.dbus_connection_unref(conn)
            SysMgr.printWarn("failed to create a D-Bus message")
            return

        # prepare args #
        d = DbusMgr.getTypeList()

        # append args #
        for item in (c_char_p(unit.encode()), c_char_p(attr.encode())):
            res = dbusObj.dbus_message_append_args(
                msg, d["DBUS_TYPE_STRING"], byref(item), d["DBUS_TYPE_INVALID"]
            )
            if not res:
                dbusObj.dbus_message_unref(msg)
                # dbusObj.dbus_connection_unref(conn)
                SysMgr.printWarn("failed to append D-Bus message args")
                return

        # call a remote method #
        reply = dbusObj.dbus_connection_send_with_reply_and_block(
            conn, msg, timeout, DbusMgr.getErrP()
        )
        if not reply:
            dbusObj.dbus_message_unref(msg)
            # dbusObj.dbus_connection_unref(conn)
            SysMgr.printWarn(
                "failed to call a D-Bus remote method because %s at %s"
                % (DbusMgr.getErrInfo(), SysMgr.getLine())
            )
            return

        val = c_uint64(0)
        rootIterP = byref(DbusMgr.getMessageIterObj())
        varIterP = byref(DbusMgr.getMessageIterObj())

        # initialize iteration #
        ret = dbusObj.dbus_message_iter_init(reply, rootIterP)
        if not ret:
            SysMgr.printWarn(
                "failed to parse D-Bus message args because %s"
                % DbusMgr.getErrInfo()
            )
            dbusObj.dbus_message_unref(msg)
            dbusObj.dbus_message_unref(reply)
            # dbusObj.dbus_connection_unref(conn)
            return

        # get root #
        dbusObj.dbus_message_iter_recurse(rootIterP, varIterP)

        # variant item loop #
        ret = dbusObj.dbus_message_iter_get_arg_type(varIterP)
        if ret != d["DBUS_TYPE_UINT64"]:
            SysMgr.printWarn(
                "failed to parse D-Bus message args because %s"
                % DbusMgr.getErrInfo()
            )
            dbusObj.dbus_message_unref(msg)
            dbusObj.dbus_message_unref(reply)
            # dbusObj.dbus_connection_unref(conn)
            return

        # get value #
        dbusObj.dbus_message_iter_get_basic(varIterP, byref(val))

        # clean up #
        dbusObj.dbus_message_unref(msg)
        dbusObj.dbus_message_unref(reply)
        # dbusObj.dbus_connection_unref(conn)

        if val is None:
            return val
        else:
            return val.value

    @staticmethod
    def getUnitByPID(bus, pid, verb=False):
        # pylint: disable=no-member

        # check input #
        if not all([bus, pid]):
            return

        dbusObj = SysMgr.libdbusObj

        # get connection #
        conn = DbusMgr.getBus(bus)
        if not conn:
            return

        # create a message for method call #
        des = "org.freedesktop.systemd1"
        path = "/org/freedesktop/systemd1"
        iface = "org.freedesktop.systemd1.Manager"
        method = "GetUnitByPID"
        timeout = c_int(100)

        msg = dbusObj.dbus_message_new_method_call(
            des.encode(), path.encode(), iface.encode(), method.encode()
        )
        if not msg:
            if verb:
                SysMgr.printWarn("failed to create a D-Bus message")
            return

        # prepare args #
        d = DbusMgr.getTypeList()
        pid = c_uint32(pid)

        # append args #
        res = dbusObj.dbus_message_append_args(
            msg, d["DBUS_TYPE_UINT32"], byref(pid), d["DBUS_TYPE_INVALID"]
        )
        if not res:
            dbusObj.dbus_message_unref(msg)
            if verb:
                SysMgr.printWarn("failed to append D-Bus message args")
            return

        # call a remote method #
        reply = dbusObj.dbus_connection_send_with_reply_and_block(
            conn, msg, timeout, DbusMgr.getErrP()
        )
        if not reply:
            dbusObj.dbus_message_unref(msg)
            if verb:
                SysMgr.printWarn(
                    "failed to call a D-Bus remote method because %s at %s"
                    % (DbusMgr.getErrInfo(), SysMgr.getLine())
                )
            return

        unit = c_char_p("".encode())
        res = dbusObj.dbus_message_get_args(
            reply,
            DbusMgr.getErrP(),
            d["DBUS_TYPE_OBJECT_PATH"],
            byref(unit),
            d["DBUS_TYPE_INVALID"],
        )

        # clean up #
        dbusObj.dbus_message_unref(msg)
        dbusObj.dbus_message_unref(reply)
        # dbusObj.dbus_connection_unref(conn)

        if not res:
            if verb:
                SysMgr.printWarn(
                    "failed to parse D-Bus message args because %s"
                    % DbusMgr.getErrInfo()
                )
            return

        return unit.value.decode()

    @staticmethod
    def getJobList(bus):
        """
        https://www.freedesktop.org/wiki/Software/systemd/dbus/

        0) The numeric job id
        1) The primary unit name for this job
        2) The job type as string
        3) The job state as string
        4) The job object path
        5) The unit object path
        """
        return DbusMgr.getList(bus, "ListJobs")

    @staticmethod
    def getUnitList(bus):
        """
        https://www.freedesktop.org/wiki/Software/systemd/dbus/

        0) The primary unit name as string
        1) The human readable description string
        2) The load state (i.e. whether the unit file has been loaded successfully)
        3) The active state (i.e. whether the unit is currently started or not)
        4) The sub state (a more fine-grained version of the active state that is specific to the unit type, which the active state is not)
        5) A unit that is being followed in its state by this unit, if there is any, otherwise the empty string.
        6) The unit object path
        7) If there is a job queued for the job unit the numeric job id, 0 otherwise
        8) The job type as string
        9) The job object path
        """
        return DbusMgr.getList(bus, "ListUnits")

    @staticmethod
    def getVariantItems(varIterP, value, d, rd):
        items = []
        dbusObj = SysMgr.libdbusObj
        iterP = byref(DbusMgr.getMessageIterObj())
        dbusObj.dbus_message_iter_recurse(varIterP, iterP)

        while 1:
            # get item type #
            ret = dbusObj.dbus_message_iter_get_arg_type(iterP)
            if not ret:
                break

            # get an item #
            if ret in (d["DBUS_TYPE_STRING"], d["DBUS_TYPE_OBJECT_PATH"]):
                dbusObj.dbus_message_iter_get_basic(iterP, byref(value))
                val = value.value.decode()
            elif ret == d["DBUS_TYPE_ARRAY"]:
                array = DbusMgr.getVariantItems(iterP, value, d, rd)
                val = str(array)
            elif ret == d["DBUS_TYPE_STRUCT"]:
                val = DbusMgr.getVariantItems(iterP, value, d, rd)
            elif ret in rd:
                dbusObj.dbus_message_iter_get_basic(iterP, byref(value))
                ctype = DbusMgr.getCtype(rd[ret])
                if ctype:
                    val = cast(byref(value), POINTER(ctype)).contents.value
                else:
                    SysMgr.printWarn(
                        "no implementation of type (%s)" % ret, True
                    )
                    val = None
            else:
                SysMgr.printWarn(
                    "no implementation of arg type (%s)" % rd[ret], True
                )
                val = None

            # add to list #
            items.append(val)

            # move to next item #
            if not dbusObj.dbus_message_iter_next(iterP):
                break

        return items

    @staticmethod
    def getList(bus, method):
        # pylint: disable=no-member
        def _printWarn(line, err):
            SysMgr.printWarn(
                ("failed to parse D-Bus message at %s " "because %s")
                % (line, err),
                True,
            )

        # check input #
        if not all([bus, method]):
            return

        dbusObj = SysMgr.libdbusObj
        getLine = SysMgr.getLine
        getErr = DbusMgr.getErrInfo

        # get connection #
        conn = DbusMgr.getBus(bus)
        if not conn:
            return

        # create a message for method call #
        des = "org.freedesktop.systemd1"
        path = "/org/freedesktop/systemd1"
        iface = "org.freedesktop.systemd1.Manager"
        timeout = c_int(100)

        msg = dbusObj.dbus_message_new_method_call(
            des.encode(), path.encode(), iface.encode(), method.encode()
        )
        if not msg:
            # dbusObj.dbus_connection_unref(conn)
            SysMgr.printWarn("failed to create a D-Bus message")
            return

        # call a remote method #
        reply = dbusObj.dbus_connection_send_with_reply_and_block(
            conn, msg, -1, DbusMgr.getErrP()
        )
        if not reply:
            dbusObj.dbus_message_unref(msg)
            # dbusObj.dbus_connection_unref(conn)
            SysMgr.printWarn(
                "failed to call a D-Bus remote method because %s at %s"
                % (DbusMgr.getErrInfo(), SysMgr.getLine())
            )
            return

        # get arg types #
        rd = {}
        d = DbusMgr.getTypeList()
        UtilMgr.makeReverseDict(d, rd)

        # parse args #
        value = c_char_p("".encode())
        statList = []

        # initialize message iterator #
        rootIterP = byref(DbusMgr.getMessageIterObj())
        arrayIterP = byref(DbusMgr.getMessageIterObj())

        # initialize iteration #
        ret = dbusObj.dbus_message_iter_init(reply, rootIterP)
        if not ret:
            _printWarn(getLine(), getErr())
            dbusObj.dbus_message_unref(msg)
            dbusObj.dbus_message_unref(reply)
            # dbusObj.dbus_connection_unref(conn)
            return

        ret = dbusObj.dbus_message_iter_get_arg_type(rootIterP)
        if ret != d["DBUS_TYPE_ARRAY"]:
            _printWarn(getLine(), getErr())
            dbusObj.dbus_message_unref(msg)
            dbusObj.dbus_message_unref(reply)
            # dbusObj.dbus_connection_unref(conn)
            return

        # get item count #
        # cnt = dbusObj.dbus_message_iter_get_element_count(rootIterP)

        # get root #
        dbusObj.dbus_message_iter_recurse(rootIterP, arrayIterP)

        # array item loop #
        while 1:
            ret = dbusObj.dbus_message_iter_get_arg_type(arrayIterP)
            if ret != d["DBUS_TYPE_STRUCT"]:
                if ret != 0:
                    _printWarn(getLine(), getErr())
                break

            statList.append(DbusMgr.getVariantItems(arrayIterP, value, d, rd))

            # next item #
            if not dbusObj.dbus_message_iter_next(arrayIterP):
                break

        # clean up #
        dbusObj.dbus_message_unref(msg)
        dbusObj.dbus_message_unref(reply)
        # dbusObj.dbus_connection_unref(conn)

        return statList

    @staticmethod
    def getCtype(typename):
        try:
            return {
                "DBUS_TYPE_BOOLEAN": c_bool,
                "DBUS_TYPE_BYTE": c_byte,
                "DBUS_TYPE_DOUBLE": c_double,
                "DBUS_TYPE_FLOAT": c_float,
                "DBUS_TYPE_INT16": c_int16,
                "DBUS_TYPE_INT32": c_int32,
                "DBUS_TYPE_INT64": c_int64,
                "DBUS_TYPE_UINT16": c_uint16,
                "DBUS_TYPE_UINT32": c_uint32,
                "DBUS_TYPE_UINT64": c_uint64,
            }[typename]
        except SystemExit:
            sys.exit(0)
        except:
            return None

    @staticmethod
    def getTypeList():
        array = c_char("a".encode())
        boolean = c_char("b".encode())
        byte = c_char("y".encode())
        char = c_char("s".encode())
        dicte = c_char("e".encode())
        double = c_char("d".encode())
        null = c_char("\0".encode())
        objpath = c_char("o".encode())
        struct = c_char("r".encode())
        ufd = c_char("h".encode())
        int16 = c_char("n".encode())
        uint16 = c_char("q".encode())
        int32 = c_char("i".encode())
        uint32 = c_char("u".encode())
        int64 = c_char("x".encode())
        uint64 = c_char("t".encode())
        variant = c_char("v".encode())

        def _getVal(tname):
            return cast(byref(tname), POINTER(c_int)).contents.value

        return {
            "DBUS_TYPE_ARRAY": _getVal(array),
            "DBUS_TYPE_BOOLEAN": _getVal(boolean),
            "DBUS_TYPE_BYTE": _getVal(byte),
            "DBUS_TYPE_DICT_ENTRY": _getVal(dicte),
            "DBUS_TYPE_DOUBLE": _getVal(double),
            "DBUS_TYPE_INVALID": _getVal(null),
            "DBUS_TYPE_OBJECT_PATH": _getVal(objpath),
            "DBUS_TYPE_STRING": _getVal(char),
            "DBUS_TYPE_STRUCT": _getVal(struct),
            "DBUS_TYPE_INT16": _getVal(int16),
            "DBUS_TYPE_UINT16": _getVal(uint16),
            "DBUS_TYPE_INT32": _getVal(int32),
            "DBUS_TYPE_UINT32": _getVal(uint32),
            "DBUS_TYPE_INT64": _getVal(int64),
            "DBUS_TYPE_UINT64": _getVal(uint64),
            "DBUS_TYPE_UNIX_FD": _getVal(ufd),
            "DBUS_TYPE_VARIANT": _getVal(variant),
        }

    @staticmethod
    def getAllInfo(bus, path, des=None, verb=True):
        # pylint: disable=no-member
        def _printWarn(des, line, err):
            SysMgr.printWarn(
                ("failed to parse D-Bus message for %s at %s " "because %s")
                % (des, line, err),
                verb,
            )

        if not des:
            des = "org.freedesktop.systemd1"

        # check input #
        if not all([bus, des, path]):
            return

        dbusObj = SysMgr.libdbusObj
        getLine = SysMgr.getLine
        getErr = DbusMgr.getErrInfo

        # get connection #
        conn = DbusMgr.getBus(bus)
        if not conn:
            return

        # create a message for method call #
        iface = "org.freedesktop.DBus.Properties"
        method = "GetAll"
        timeout = c_int(100)

        msg = dbusObj.dbus_message_new_method_call(
            des.encode(), path.encode(), iface.encode(), method.encode()
        )
        if not msg:
            # dbusObj.dbus_connection_unref(conn)
            SysMgr.printWarn("failed to create a D-Bus message")
            return

        # prepare args #
        rd = {}
        d = DbusMgr.getTypeList()
        UtilMgr.makeReverseDict(d, rd)

        # append args #
        item = c_char_p("".encode())
        res = dbusObj.dbus_message_append_args(
            msg, d["DBUS_TYPE_STRING"], byref(item), d["DBUS_TYPE_INVALID"]
        )
        if not res:
            dbusObj.dbus_message_unref(msg)
            # dbusObj.dbus_connection_unref(conn)
            SysMgr.printWarn("failed to append D-Bus message args")
            return

        # call a remote method #
        reply = dbusObj.dbus_connection_send_with_reply_and_block(
            conn, msg, timeout, DbusMgr.getErrP()
        )
        if not reply:
            dbusObj.dbus_message_unref(msg)
            # dbusObj.dbus_connection_unref(conn)
            SysMgr.printWarn(
                "failed to call a D-Bus remote method because %s at %s"
                % (DbusMgr.getErrInfo(), SysMgr.getLine())
            )
            return

        # parse args #
        name = c_char_p("".encode())
        value = c_char_p("".encode())
        statList = {}

        # initialize message iterator #
        rootIterP = byref(DbusMgr.getMessageIterObj())
        arrayIterP = byref(DbusMgr.getMessageIterObj())
        dictIterP = byref(DbusMgr.getMessageIterObj())

        # initialize iteration #
        ret = dbusObj.dbus_message_iter_init(reply, rootIterP)
        if not ret:
            _printWarn(des, getLine(), getErr())
            dbusObj.dbus_message_unref(msg)
            dbusObj.dbus_message_unref(reply)
            # dbusObj.dbus_connection_unref(conn)
            return

        ret = dbusObj.dbus_message_iter_get_arg_type(rootIterP)
        if ret != d["DBUS_TYPE_ARRAY"]:
            _printWarn(des, getLine(), getErr())
            dbusObj.dbus_message_unref(msg)
            dbusObj.dbus_message_unref(reply)
            # dbusObj.dbus_connection_unref(conn)
            return

        # get item count #
        # cnt = dbusObj.dbus_message_iter_get_element_count(rootIterP)

        # get root #
        dbusObj.dbus_message_iter_recurse(rootIterP, arrayIterP)

        # array item loop #
        while 1:
            ret = dbusObj.dbus_message_iter_get_arg_type(arrayIterP)
            if ret != d["DBUS_TYPE_DICT_ENTRY"]:
                _printWarn(des, getLine(), getErr())
                break

            dbusObj.dbus_message_iter_recurse(arrayIterP, dictIterP)

            # dictionary item loop #
            while 1:
                ret = dbusObj.dbus_message_iter_get_arg_type(dictIterP)
                if ret != d["DBUS_TYPE_STRING"]:
                    _printWarn(des, getLine(), getErr())
                    break

                # get name #
                dbusObj.dbus_message_iter_get_basic(dictIterP, byref(name))
                if not name.value:
                    break

                # decode name #
                sname = name.value.decode()

                # next stat value #
                if not dbusObj.dbus_message_iter_next(dictIterP):
                    break

                # get stat values as a variant-type value #
                ret = dbusObj.dbus_message_iter_get_arg_type(dictIterP)
                if ret != d["DBUS_TYPE_VARIANT"]:
                    _printWarn(des, getLine(), getErr())
                    break

                # get items #
                statList[sname] = DbusMgr.getVariantItems(
                    dictIterP, value, d, rd
                )[0]

                # next stat #
                if not dbusObj.dbus_message_iter_next(dictIterP):
                    break

            # next item #
            if not dbusObj.dbus_message_iter_next(arrayIterP):
                break

        # clean up #
        dbusObj.dbus_message_unref(msg)
        dbusObj.dbus_message_unref(reply)
        # dbusObj.dbus_connection_unref(conn)

        return statList

    @staticmethod
    def getServiceProc(bus, service):
        if not bus or not service:
            return

        dbusObj = SysMgr.libdbusObj

        conn = DbusMgr.getBus(bus)
        if not conn:
            return

        # create a message for method call #
        des = "org.freedesktop.DBus"
        path = "/"
        iface = des
        method = "GetConnectionUnixProcessID"
        msg = dbusObj.dbus_message_new_method_call(
            des.encode(), path.encode(), iface.encode(), method.encode()
        )
        if not msg:
            # dbusObj.dbus_connection_unref(conn)
            SysMgr.printWarn("failed to create a D-Bus message")
            return

        # prepare args #
        d = DbusMgr.getTypeList()

        # append args #
        item = c_char_p(service.encode())
        res = dbusObj.dbus_message_append_args(
            msg, d["DBUS_TYPE_STRING"], byref(item), d["DBUS_TYPE_INVALID"]
        )
        if not res:
            dbusObj.dbus_message_unref(msg)
            # dbusObj.dbus_connection_unref(conn)
            SysMgr.printWarn("failed to append D-Bus message args")
            return

        # call a remote method #
        reply = dbusObj.dbus_connection_send_with_reply_and_block(
            conn, msg, -1, DbusMgr.getErrP()
        )
        if not reply:
            dbusObj.dbus_message_unref(msg)
            # dbusObj.dbus_connection_unref(conn)
            SysMgr.printWarn(
                "failed to call a D-Bus remote method because %s at %s"
                % (DbusMgr.getErrInfo(), SysMgr.getLine())
            )
            return

        # parse args #
        pid = c_uint32(0)
        res = dbusObj.dbus_message_get_args(
            reply,
            DbusMgr.getErrP(),
            d["DBUS_TYPE_UINT32"],
            byref(pid),
            d["DBUS_TYPE_INVALID"],
        )

        # clean up #
        dbusObj.dbus_message_unref(msg)
        dbusObj.dbus_message_unref(reply)
        # dbusObj.dbus_connection_unref(conn)

        if not res:
            SysMgr.printWarn(
                "failed to parse D-Bus message args because %s"
                % DbusMgr.getErrInfo()
            )
            return

        # get comm #
        comm = SysMgr.getComm(pid.value)
        if not comm:
            comm = "??"

        try:
            return "%s(%s)" % (comm, pid.value)
        except SystemExit:
            sys.exit(0)
        except:
            return

    @staticmethod
    def getBusService(bus, tid=None, addr=None):
        if not bus:
            return

        dbusObj = SysMgr.libdbusObj

        conn = DbusMgr.getBus(bus, tid, addr)
        if not conn:
            return

        # create a message for method call #
        des = "org.freedesktop.DBus"
        path = "/"
        iface = des
        method = "ListNames"

        # send a message for method call #
        msg, reply = DbusMgr.callMethod(
            conn, des, path, iface, method, c_int(1000)
        )
        if not msg or not reply:
            return

        # prepare args #
        d = DbusMgr.getTypeList()

        # parse args #
        cntRes = c_int(0)
        arrayRes = (POINTER(c_char_p))()
        res = dbusObj.dbus_message_get_args(
            reply,
            DbusMgr.getErrP(),
            d["DBUS_TYPE_ARRAY"],
            d["DBUS_TYPE_STRING"],
            byref(arrayRes),
            byref(cntRes),
            d["DBUS_TYPE_INVALID"],
        )
        if not res:
            dbusObj.dbus_message_unref(msg)
            dbusObj.dbus_message_unref(reply)
            # dbusObj.dbus_connection_unref(conn)
            SysMgr.printWarn(
                "failed to parse D-Bus message args because %s"
                % DbusMgr.getErrInfo()
            )
            return

        slist = []
        for idx in xrange(cntRes.value):
            slist.append(str(arrayRes[idx].decode()))

        dbusObj.dbus_free_string_array(arrayRes)

        # clean up #
        dbusObj.dbus_message_unref(msg)
        dbusObj.dbus_message_unref(reply)
        # dbusObj.dbus_connection_unref(conn)

        return slist

    @staticmethod
    def prepareDbusMethods():
        # get ctypes object #
        SysMgr.importPkgItems("ctypes")

        # try to load libraries #
        try:
            # load libgio library #
            if not SysMgr.libgioObj:
                SysMgr.libgioObj = SysMgr.loadLib(SysMgr.libgioPath)

            # load libgobj library #
            if not SysMgr.libgObj:
                SysMgr.libgObj = SysMgr.loadLib(SysMgr.libgobjPath)

            # load standard libdbus library #
            if not SysMgr.libdbusObj:
                SysMgr.libdbusObj = SysMgr.loadLib(SysMgr.libdbusPath)
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                "failed to load library to analyze D-Bus packets", True
            )
            sys.exit(-1)

        # define error object #
        class GError(Structure):
            _fields_ = (
                ("domain", c_uint32),
                ("code", c_int),
                ("message", c_char_p),
            )

        DbusMgr.errObj = GError

        # define gobject methods #
        gObj = SysMgr.libgObj

        gObj.g_object_unref.argtypes = [c_void_p]

        # define gio methods #
        gioObj = SysMgr.libgioObj

        gioObj.g_dbus_message_new_from_blob.argtypes = [
            c_char_p,
            c_ulong,
            c_ulong,
            c_void_p,
        ]
        gioObj.g_dbus_message_new_from_blob.restype = c_ulong

        gioObj.g_dbus_message_bytes_needed.argtypes = [
            c_char_p,
            c_ulong,
            c_void_p,
        ]
        gioObj.g_dbus_message_bytes_needed.restype = c_ulong

        gioObj.g_error_free.argtypes = [c_void_p]

        gioObj.g_dbus_message_get_message_type.argtypes = [c_ulong]
        gioObj.g_dbus_message_get_message_type.restype = c_ulong

        gioObj.g_dbus_message_get_arg0.argtypes = [c_ulong]
        gioObj.g_dbus_message_get_arg0.restype = c_char_p

        gioObj.g_dbus_message_get_sender.argtypes = [c_ulong]
        gioObj.g_dbus_message_get_sender.restype = c_char_p

        gioObj.g_dbus_message_get_destination.argtypes = [c_ulong]
        gioObj.g_dbus_message_get_destination.restype = c_char_p

        gioObj.g_dbus_message_get_path.argtypes = [c_ulong]
        gioObj.g_dbus_message_get_path.restype = c_char_p

        gioObj.g_dbus_message_get_signature.argtypes = [c_ulong]
        gioObj.g_dbus_message_get_signature.restype = c_char_p

        gioObj.g_dbus_message_get_interface.argtypes = [c_ulong]
        gioObj.g_dbus_message_get_interface.restype = c_char_p

        gioObj.g_dbus_message_get_member.argtypes = [c_ulong]
        gioObj.g_dbus_message_get_member.restype = c_char_p

        gioObj.g_dbus_message_get_error_name.argtypes = [c_ulong]
        gioObj.g_dbus_message_get_error_name.restype = c_char_p

        gioObj.g_dbus_message_get_serial.argtypes = [c_ulong]
        gioObj.g_dbus_message_get_serial.restype = c_ulong

        gioObj.g_dbus_message_print.argtypes = [c_ulong, c_ulong]
        gioObj.g_dbus_message_print.restype = c_char_p

        gioObj.g_dbus_message_get_reply_serial.argtypes = [c_ulong]
        gioObj.g_dbus_message_get_reply_serial.restype = c_ulong

        # define dbus methods #
        dbusObj = SysMgr.libdbusObj

        dbusObj.dbus_bus_get.argtypes = [c_uint, c_void_p]
        dbusObj.dbus_bus_get.restype = c_void_p

        dbusObj.dbus_connection_open.argtypes = [c_char_p, c_void_p]
        dbusObj.dbus_connection_open.restype = c_void_p

        dbusObj.dbus_connection_open_private.argtypes = [c_char_p, c_void_p]
        dbusObj.dbus_connection_open_private.restype = c_void_p

        dbusObj.dbus_bus_get_private.argtypes = [c_uint, c_void_p]
        dbusObj.dbus_bus_get_private.restype = c_void_p

        dbusObj.dbus_message_iter_init.restype = c_bool
        dbusObj.dbus_message_iter_init.argtypes = [c_void_p, c_void_p]

        dbusObj.dbus_error_init.restype = None
        dbusObj.dbus_error_init.argtypes = [c_void_p]

        dbusObj.dbus_message_iter_next.restype = c_bool
        dbusObj.dbus_message_iter_next.argtypes = [c_void_p]

        dbusObj.dbus_message_iter_get_basic.restype = None
        dbusObj.dbus_message_iter_get_basic.argtypes = [c_void_p, c_void_p]

        dbusObj.dbus_connection_ref.argtypes = [c_void_p]
        dbusObj.dbus_connection_ref.restype = c_void_p

        dbusObj.dbus_message_iter_get_arg_type.restype = c_int
        dbusObj.dbus_message_iter_get_arg_type.argtypes = [c_void_p]

        dbusObj.dbus_message_iter_recurse.restype = None
        dbusObj.dbus_message_iter_recurse.argtypes = [c_void_p, c_void_p]

        dbusObj.dbus_message_iter_get_element_count.restype = c_int
        dbusObj.dbus_message_iter_get_element_count.argtypes = [c_void_p]

        dbusObj.dbus_bus_get_unique_name.argtypes = [c_void_p]
        dbusObj.dbus_bus_get_unique_name.restype = c_char_p

        dbusObj.dbus_connection_close.argtypes = [c_void_p]
        dbusObj.dbus_connection_close.restype = None

        dbusObj.dbus_bus_set_unique_name.argtypes = [c_void_p, c_char_p]
        dbusObj.dbus_bus_set_unique_name.restype = c_bool

        dbusObj.dbus_connection_get_is_connected.argtypes = [c_void_p]
        dbusObj.dbus_connection_get_is_connected.restype = c_bool

        dbusObj.dbus_connection_get_is_authenticated.argtypes = [c_void_p]
        dbusObj.dbus_connection_get_is_authenticated.restype = c_bool

        dbusObj.dbus_connection_get_is_anonymous.argtypes = [c_void_p]
        dbusObj.dbus_connection_get_is_anonymous.restype = c_bool

        dbusObj.dbus_bus_register.argtypes = [c_void_p, c_void_p]
        dbusObj.dbus_bus_register.restype = c_bool

        dbusObj.dbus_parse_address.argtypes = [
            c_void_p,
            c_void_p,
            POINTER(c_int),
            c_void_p,
        ]
        dbusObj.dbus_parse_address.restype = c_bool

        dbusObj.dbus_bus_request_name.argtypes = [
            c_void_p,
            c_char_p,
            c_uint,
            c_void_p,
        ]
        dbusObj.dbus_bus_request_name.restype = c_int

        dbusObj.dbus_message_unref.argtypes = [c_void_p]
        dbusObj.dbus_message_unref.restype = None

        dbusObj.dbus_connection_unref.argtypes = [c_void_p]
        dbusObj.dbus_connection_unref.restype = None

        dbusObj.dbus_message_new_method_call.argtypes = [
            c_char_p,
            c_char_p,
            c_char_p,
            c_char_p,
        ]
        dbusObj.dbus_message_new_method_call.restype = c_void_p

        dbusObj.dbus_connection_send_with_reply_and_block.argtypes = [
            c_void_p,
            c_void_p,
            c_int,
            c_void_p,
        ]
        dbusObj.dbus_connection_send_with_reply_and_block.restype = c_void_p

        dbusObj.dbus_message_get_args.argtypes = [c_void_p, c_void_p, c_int]
        dbusObj.dbus_message_get_args.restype = c_bool

        dbusObj.dbus_message_append_args.argtypes = [c_void_p, c_int]
        dbusObj.dbus_message_append_args.restype = c_bool

        dbusObj.dbus_message_demarshal.argtypes = [c_char_p, c_int, c_void_p]
        dbusObj.dbus_message_demarshal.restype = c_ulong

        dbusObj.dbus_message_demarshal_bytes_needed.argtypes = [
            c_char_p,
            c_int,
        ]
        dbusObj.dbus_message_demarshal_bytes_needed.restype = c_int

        dbusObj.dbus_message_get_interface.argtypes = [c_void_p]
        dbusObj.dbus_message_get_interface.restype = c_char_p

        dbusObj.dbus_message_get_type.argtypes = [c_void_p]
        dbusObj.dbus_message_get_type.restype = c_int

    @staticmethod
    def printIntrospection(tid, introList):
        procId = "%s(%s)" % (SysMgr.getComm(tid, cache=True), tid)

        if not introList:
            SysMgr.printErr("no introspection for %s" % procId)
            return

        # print title #
        SysMgr.printPipe(
            "\n[D-Bus Introspection Info] <Target: %s>\n%s" % (procId, twoLine)
        )

        # print introspection #
        for name, value in introList.items():
            if not value:
                continue
            SysMgr.printPipe(
                "\n[%s]\n%s%s%s\n" % (name, oneLine, value, oneLine)
            )

    @staticmethod
    def printSdInfo(bus, procStr=None, retList=False):
        # get unit info #
        ret = DbusMgr.getUnitList(bus)
        if not ret:
            SysMgr.printWarn("no unit info for %s" % procStr, True)
            return

        # get only boot option #
        onlyBoot = "ONLYBOOT" in SysMgr.environList

        # set print attribute #
        if "CMD" in SysMgr.environList:
            SysMgr.streamEnable = True

        # get mute option #
        mute = "MUTE" in SysMgr.environList

        # get all filter flag #
        allFilter = "ALLFILTER" in SysMgr.environList and SysMgr.customCmd

        # make lists #
        if allFilter:
            unitDict = {item[6]: item for item in ret}
        else:
            unitDict = {
                item[6]: item
                for item in ret
                if not SysMgr.customCmd
                or UtilMgr.isValidStr(item[0], SysMgr.customCmd)
            }
        units = list(unitDict)
        units.sort()
        units.insert(0, "/org/freedesktop/systemd1")
        unitStats = {}
        resList = {"bus": bus}

        SysMgr.printInfo("start gathering unit info for %s..." % procStr)

        # get stats #
        for idx, cpath in enumerate(units):
            UtilMgr.printProgress(idx, len(units))

            # print path info #
            if SysMgr.warnEnable:
                SysMgr.printWarn("start reading '%s' info..." % cpath)

            # get all stats #
            ret = DbusMgr.getAllInfo(
                bus, cpath, verb=True if SysMgr.warnEnable else False
            )
            if not ret:
                continue

            if cpath == "/org/freedesktop/systemd1":
                # check skip condition #
                if mute or (SysMgr.customCmd and not SysMgr.showAll):
                    continue

                # print boot info #
                res = DbusMgr.printSystemBootInfo(ret, retList)
                if res:
                    resList["systemd"] = res
                    continue

                # print systemd info #
                if not onlyBoot:
                    DbusMgr.printUnitStatInfo("systemd", ret, bus, procStr)
            else:
                bootInfo = DbusMgr.getUnitBootInfo(ret)

                # get activation time #
                ret.setdefault("Activation", bootInfo["activation"])
                ret.setdefault("Boot", bootInfo)

                # save stats #
                unitStats[unitDict[cpath][0]] = ret

        UtilMgr.deleteProgress()

        # return list #
        if retList and not onlyBoot:
            resList.update(unitStats)
            return resList

        if onlyBoot:
            if retList:
                resList = {"systemd": resList["systemd"]}
            else:
                # print title #
                SysMgr.printPipe(
                    "\n[Systemd Boot Info] (Bus: %s) \n%s" % (bus, twoLine)
                )

        # get filters #
        incCond = {}
        exCond = {}
        for cond, condDict in (("SDFILTER", incCond), ("SDEXFILTER", exCond)):
            if not cond in SysMgr.environList:
                continue

            for item in SysMgr.environList[cond]:
                items = item.split(":", 1)
                if len(items) == 1:
                    condDict[items[0]] = None
                else:
                    condDict[items[0]] = items[1].split(",")

        # print stats #
        cnt = 0
        sortByStart = "SORTBYSTART" in SysMgr.environList
        for unit, stats in sorted(
            unitStats.items(),
            key=lambda x: x[1]["Boot"]["activating"]
            if sortByStart
            else x[1]["Activation"],
            reverse=False if sortByStart else True,
        ):
            if onlyBoot:
                start = float(stats["Boot"]["activating"]) / 1000000.0

                if stats["Boot"]["activated"]:
                    last = stats["Boot"]["activated"]
                else:
                    last = stats["Boot"]["deactivated"]
                last = float(last) / 1000000.0

                activation = float(stats["Activation"] / 1000000.0)

                if retList:
                    resList[unit] = {
                        "activation": activation,
                        "start": start,
                        "last": last,
                    }
                    continue

                SysMgr.printPipe(
                    "{0:>10.3f}s {1:1} ({2:>.3f}s ~ {3:>.3f}s)".format(
                        activation,
                        UtilMgr.convColor(unit, "YELLOW"),
                        start,
                        last,
                    )
                )
            else:
                # check inclusive condition #
                skip = False
                for f, v in incCond.items():
                    if f.startswith("+"):
                        andCond = True
                        f = UtilMgr.lstrip(f, "+")
                    else:
                        andCond = False

                    if f not in stats or not UtilMgr.isValidStr(
                        str(stats[f]), v
                    ):
                        skip = True
                        if andCond:
                            break
                    else:
                        skip = False
                        if not andCond:
                            break
                if skip:
                    continue

                # check exclusive condition #
                skip = False
                for f, v in exCond.items():
                    if f in stats and UtilMgr.isValidStr(str(stats[f]), v):
                        skip = True
                        break
                if skip:
                    continue

                # add MainCOMM field #
                if "MainPID" in stats:
                    comm = SysMgr.getComm(stats["MainPID"])
                    if comm:
                        stats["MainCOMM"] = comm

                # execute commands #
                if "CMD" in SysMgr.environList:
                    SysMgr.executeCommand(
                        SysMgr.environList["CMD"], uptime=False, params=stats
                    )
                    continue

                if mute:
                    continue

                # print info #
                DbusMgr.printUnitStatInfo(unit, stats, bus, procStr)

            cnt += 1

        if onlyBoot:
            if retList:
                return resList
            if not cnt:
                SysMgr.printPipe("    None")
            SysMgr.printPipe(oneLine)

    @staticmethod
    def printUnitInfo(tid, unitList, bus, procStr, retList=False):
        convColor = UtilMgr.convColor
        procId = "%s(%s)" % (SysMgr.getComm(tid, cache=True), tid)
        resList = {"bus": bus}

        if not unitList:
            SysMgr.printErr("no unit for %s" % procId)
            return

        # print title #
        if not retList:
            SysMgr.printPipe(
                "\n[Systemd Unit Status] (Target: %s) (Bus: %s) (Proc: %s)\n%s"
                % (procId, bus, procStr, twoLine)
            )
            SysMgr.printPipe("{0:^16}".format("Unit (Description)"))
            SysMgr.printPipe(
                (
                    "{0:<1} {1:>9} | {2:>8} | {3:>9} | {4:>1} | "
                    "{5:>1} | {6:>1}({7:>1}/{8:>1})\n{9:1}"
                ).format(
                    "    ",
                    "Load",
                    "Active",
                    "State",
                    "Path",
                    "Follow",
                    "Job",
                    "Path",
                    "Type",
                    twoLine,
                )
            )

            if not unitList:
                SysMgr.printPipe("    None\n%s" % oneLine)
                return

        # get all filter flag #
        allFilter = "ALLFILTER" in SysMgr.environList and SysMgr.customCmd

        # print units #
        for items in sorted(unitList, key=lambda i: i[0].lower()):
            # check filter #
            if allFilter:
                if not UtilMgr.isValidStr(
                    " ".join(list(map(str, items))), SysMgr.customCmd
                ):
                    continue

            (
                unit,
                desc,
                load,
                active,
                state,
                stat,
                path,
                job,
                jtype,
                jpath,
            ) = items

            # apply colors #
            if not retList:
                if load != "loaded":
                    load = convColor(load, "WARNING", 8)

                if active == "active":
                    active = convColor(active, "GREEN", 8)
                else:
                    active = convColor(active, "WARNING", 8)

            if not stat:
                stat = "."

            # save items to list #
            if retList:
                resList[unit] = {
                    "desc": desc,
                    "load": load,
                    "active": active,
                    "state": state,
                    "path": path,
                    "stat": stat,
                    "job": job,
                    "jobtype": jtype,
                    "jobpath": jpath,
                }
                continue

            if job and job != "0":
                jobstr = " | %s(%s/%s)" % (job, jtype, jpath)
            else:
                jobstr = ""

            SysMgr.printPipe("{0:1} ({1:1})".format(unit, desc))
            SysMgr.printPipe(
                (
                    "{0:<1} {1:>9} | {2:>8} | {3:>9} | {4:>1} | "
                    "{5:>1}{6:>1}\n{7:1}"
                ).format(
                    "    ", load, active, state, path, stat, jobstr, oneLine
                )
            )

        return resList

    @staticmethod
    def printSystemBootInfo(info, ret=False):
        # define field name #
        finish = "FinishTimestampMonotonic"
        firmwareStart = "FirmwareTimestampMonotonic"
        loaderStart = "LoaderTimestampMonotonic"
        kernelStart = "KernelTimestamp"
        securityStart = "SecurityStartTimestamp"
        securityTime = "SecurityStartTimestampMonotonic"
        securityFinish = "SecurityFinishTimestamp"
        initrdStart = "InitRDTimestampMonotonic"
        userStart = "UserspaceTimestampMonotonic"
        genStart = "GeneratorsStartTimestampMonotonic"
        genFinish = "GeneratorsFinishTimestampMonotonic"
        unitStart = "UnitsLoadStartTimestampMonotonic"
        unitFinish = "UnitsLoadFinishTimestampMonotonic"
        initrdSecurityStart = "InitRDSecurityStartTimestampMonotonic"
        initrdSecurityFinish = "InitRDSecurityFinishTimestampMonotonic"
        initrdGenStart = "InitRDGeneratorsStartTimestampMonotonic"
        initrdGenFinish = "InitRDGeneratorsFinishTimestampMonotonic"
        initrdUnitStart = "InitRDUnitsLoadStartTimestampMonotonic"
        initrdUnitFinish = "InitRDUnitsLoadFinishTimestampMonotonic"

        offset = 0

        def _getTime(name):
            if name in info:
                return long(info[name]) - offset
            else:
                return 0

        initrdTime = _getTime(initrdStart)
        userTime = _getTime(userStart)

        finishTime = _getTime(finish)
        if finishTime <= 0:
            SysMgr.printPipe("Bootup is not finished\n%s" % oneLine)
            return

        if initrdTime > 0:
            kernelDoneTime = initrdTime
        else:
            kernelDoneTime = userTime

        # get times #
        offset = userTime
        firmwareTime = _getTime(firmwareStart)
        loaderTime = _getTime(loaderStart)
        genStartTime = _getTime(genStart)
        genFinishTime = _getTime(genFinish)
        unitStartTime = _getTime(unitStart)
        unitFinishTime = _getTime(unitFinish)
        securityStartTime = _getTime(securityStart)
        securityFinishTime = _getTime(securityFinish)

        if firmwareTime:
            firmware = firmwareTime - loaderTime
        else:
            firmware = 0

        # get duration #
        loader = loaderTime
        kernel = kernelDoneTime
        user = finishTime - userTime
        security = securityFinishTime - securityStartTime
        unit = unitFinishTime - unitStartTime
        gen = genFinishTime - genStartTime

        if initrdTime:
            initrd = finishTime - initrdTime - user
        else:
            initrd = 0

        if ret:
            return {
                "firmware": firmware,
                "kernel": kernel,
                "initrd": initrd,
                "unitload": unit,
                "generator": gen,
                "user": user,
            }

        # print title #
        SysMgr.printPipe("\n[Systemd Boot Info]\n" + twoLine)

        total = 0

        def _convTime(name, value):
            if value <= 0:
                return "", 0
            elapsed = float(value / 1000000.0)
            return "{0:<20}:{1:>10.3f}s\n".format(name, elapsed), elapsed

        timeStr = ""
        for name, value in (
            ("Firmware", firmware),
            ("Kernel", kernel),
            ("Initrd", initrd),
            ("User", user),
            ("|_SecuritySetup", security),
            ("|_Generators", gen),
            ("|_UnitLoading", unit),
        ):
            addStr, val = _convTime(name, value)
            timeStr += addStr
            if not addStr.startswith("|"):
                total += val

        if timeStr:
            timeStr += "{2:1}\n{0:<20}:{1:>10.3f}s\n".format(
                "Total", total, oneLine
            )
        else:
            timeStr = "    None\n"

        SysMgr.printPipe(timeStr + oneLine)

    @staticmethod
    def getUnitBootInfo(info):
        offset = 0
        userStart = "UserspaceTimestampMonotonic"
        inactiveExit = "InactiveExitTimestampMonotonic"
        activeEnter = "ActiveEnterTimestampMonotonic"
        activeExit = "ActiveExitTimestampMonotonic"
        inactiveEnter = "InactiveEnterTimestampMonotonic"

        def _getTime(name):
            if name in info:
                return long(info[name]) - offset
            else:
                return 0

        # get time stats #
        offset = _getTime(userStart)
        activating = _getTime(inactiveExit)
        activated = _getTime(activeEnter)
        deactivating = _getTime(activeExit)
        deactivated = _getTime(inactiveEnter)

        # activating -> activated #
        if activated >= activating:
            activation = activated - activating
        # activating -> deactivated #
        elif deactivated >= activating:
            activation = deactivated - activating
        else:
            activation = 0

        return {
            "start": offset,
            "activating": activating,
            "activated": activated,
            "deactivating": deactivating,
            "deactivated": deactivated,
            "activation": activation,
        }

    @staticmethod
    def printUnitStatInfo(path, info, bus=None, procStr=None):
        # print title #
        SysMgr.printPipe(
            "\n[Systemd Unit Info] (Target: %s) (Bus: %s) (Proc: %s)\n%s"
            % (path, bus, procStr, twoLine)
        )

        cnt = 0

        for attr, value in sorted(info.items()):
            # get items #
            if "SDITEM" in SysMgr.environList:
                if not UtilMgr.isValidStr(attr, SysMgr.environList["SDITEM"]):
                    continue

            # convert number format #
            if UtilMgr.isNumber(value) and not "PID" in attr:
                value = UtilMgr.convNum(value)

            SysMgr.printPipe("{0:<40}: {1:1}".format(attr, str(value)))

            cnt += 1

        if cnt == 0:
            SysMgr.printPipe("    None")

        # print unit stats #
        SysMgr.printPipe(oneLine)

    @staticmethod
    def printStatInfo(tid, statList):
        conv = UtilMgr.convNum
        procId = "%s(%s)" % (SysMgr.getComm(tid, cache=True), tid)

        if not statList:
            SysMgr.printErr("no stat for %s" % procId)
            return

        # print title #
        SysMgr.printPipe(
            "\n[D-Bus Stat Info] <Target: %s>\n%s" % (procId, twoLine)
        )
        SysMgr.printPipe(
            "{0:^32} {1:<16}\n{2:1}".format("Name", "Value", oneLine)
        )

        # print stats #
        for name, value in statList.items():
            if value:
                value = " ".join(list(map(conv, value)))
            else:
                value = "N/A"

            SysMgr.printPipe("{0:<32} {1:>16}".format(name, value))

        SysMgr.printPipe(oneLine)

    @staticmethod
    def printSignalInfo(tid, perProc, perSig, procInfo):
        conv = UtilMgr.convNum
        totalSubscription = 0
        procId = "%s(%s)" % (SysMgr.getComm(tid, cache=True), tid)

        if not perProc and not perSig:
            SysMgr.printErr("no subscription info for %s" % procId)
            return

        # create a table for perProc signals #
        nrPerProcSignals = {}
        for cli, items in perProc.items():
            nrPerProcSignals.setdefault(
                cli, dict({"nrStub": 0, "nrSignal": 0})
            )

            for sender, iface in items.items():
                nrPerProcSignals[cli]["nrStub"] += 1
                nrPerProcSignals[cli]["nrSignal"] += len(iface)
                totalSubscription += len(iface)

        # print perProc signals #
        SysMgr.printPipe(
            (
                "\n[D-Bus Signal Proxy Info] (Target: %s) "
                "(nrProc: %s) (nrSub: %s)\n%s"
            )
            % (procId, conv(len(perProc)), conv(totalSubscription), twoLine)
        )

        SysMgr.printPipe(
            "{0:^23} {1:<23} {2:^10} {3:>1}\n{4:1}".format(
                "Client", "Server", "Interface", "Args", oneLine
            )
        )

        for cli, stats in sorted(
            nrPerProcSignals.items(),
            key=lambda e: e[1]["nrSignal"],
            reverse=True,
        ):
            if cli in procInfo:
                proc = procInfo[cli]
            else:
                proc = cli

            # print signal stat #
            mergedList = {}
            for sender, iface in perProc[cli].items():
                if sender in procInfo:
                    sender = procInfo[sender]
                else:
                    tokens = sender.split(".")
                    pos = len(tokens) - 1
                    while 1:
                        if pos == 0:
                            break

                        key = ".".join(tokens[:pos])
                        if key in procInfo:
                            procInfo[sender] = procInfo[key]
                            sender = procInfo[key]
                            break

                        pos -= 1

                if sender in mergedList:
                    mergedList[sender].update(iface)
                else:
                    mergedList[sender] = dict(iface)

            # print process stat #
            SysMgr.printPipe(
                "{0:>23} <nrStub: {1:1}, nrSignal: {2:1}>".format(
                    proc, conv(len(mergedList)), conv(stats["nrSignal"])
                )
            )

            # print signal stat #
            for sender, iface in sorted(
                mergedList.items(), key=lambda e: len(e[1]), reverse=True
            ):
                SysMgr.printPipe(
                    "{0:>23} {1:<23} <nrSignal: {2:1}>".format(
                        " ", sender, conv(len(iface))
                    )
                )
                if not SysMgr.showAll:
                    continue

                for name, arg in sorted(iface.items()):
                    SysMgr.printPipe(
                        "{0:>23} {0:<23} {1:<12}".format(" ", name)
                    )
                    for key in sorted(list(arg)):
                        SysMgr.printPipe(
                            "{0:>23} {0:<23} {0:<10} ({1:<1})".format(" ", key)
                        )
            SysMgr.printPipe(oneLine)
        if not nrPerProcSignals:
            SysMgr.printPipe("    None\n%s" % oneLine)

        # create a table for perSignal processes #
        nrPerSigProcs = {}
        for sender, items in perSig.items():
            if sender in procInfo:
                proc = procInfo[sender]
            else:
                proc = sender

            initDict = {"proxyList": {}, "nrSignal": 0, "interface": {}}

            nrPerSigProcs.setdefault(proc, dict(initDict))
            nrPerSigProcs[proc]["interface"].setdefault(sender, {})
            nrPerSigProcs[proc]["interface"][sender].update(items)

            nrPerSigProcs[proc]["nrSignal"] += len(items)
            for iface, receiver in items.items():
                nrPerSigProcs[proc]["proxyList"].update(receiver)

        # print perSignal processes #
        SysMgr.printPipe(
            (
                "\n[D-Bus Signal Stub Info] (Target: %s) "
                "(nrProc: %s) (nrSub: %s)\n%s"
            )
            % (procId, conv(len(perSig)), conv(totalSubscription), twoLine)
        )

        SysMgr.printPipe(
            "{0:^23} {1:^12} {2:<23}\n{3:1}".format(
                "Server", "Interface", "Client", oneLine
            )
        )

        for serv, stats in sorted(
            nrPerSigProcs.items(),
            key=lambda e: len(e[1]["proxyList"]),
            reverse=True,
        ):
            # print stub process stat #
            SysMgr.printPipe(
                "{0:>23} <nrProxy: {1:1}, nrSignal: {2:1}>".format(
                    serv,
                    conv(len(stats["proxyList"])),
                    conv(stats["nrSignal"]),
                )
            )

            # print interface stat #
            for iface, receiver in sorted(
                stats["interface"].items(),
                key=lambda e: len(e[1]),
                reverse=True,
            ):
                procList = {}
                for signame, procs in receiver.items():
                    procList.update(procs)

                SysMgr.printPipe(
                    (
                        "{0:>23} {1:<12} <nrProxy: {2:1}> " "<nrSignal: {3:1}>"
                    ).format(
                        " ", iface, conv(len(procList)), conv(len(receiver))
                    )
                )

                if not SysMgr.showAll:
                    continue

                for signame, procs in sorted(
                    receiver.items(), key=lambda e: len(e[1]), reverse=True
                ):
                    SysMgr.printPipe(
                        "{0:>23} {1:<12} {2:<23} [nrProxy: {3:<1}]".format(
                            " ", " ", signame, conv(len(procs))
                        )
                    )
                    procs = [
                        procInfo[name] if name in procInfo else name
                        for name in list(procs)
                    ]
                    # print proxy process stat #
                    for name in sorted(procs):
                        if name in procInfo:
                            cproc = procInfo[name]
                        else:
                            cproc = name

                        SysMgr.printPipe(
                            "{0:>23} {0:<12} {0:<12} {1:<1}".format(" ", cproc)
                        )
            SysMgr.printPipe(oneLine)
        if not nrPerSigProcs:
            SysMgr.printPipe("    None\n%s" % oneLine)

    @staticmethod
    def printSummary():
        # define summary data #
        total = err = 0
        msgs = {}
        convNum = UtilMgr.convNum

        # summarize messages #
        for intval in DbusMgr.msgData:
            total += intval.pop("totalCnt", None)
            err += intval.pop("totalErr", None)

            for pid, data in intval.items():
                # set default PID dict #
                msgs.setdefault(pid, {})

                # accumulate message count #
                for name, val in data.items():
                    # save total message count of a process #
                    if name == "totalCnt":
                        msgs[pid].setdefault("totalCnt", 0)
                        msgs[pid]["totalCnt"] += val
                        continue

                    # save count of a message #
                    msgs[pid].setdefault(name, 0)
                    msgs[pid][name] += val["cnt"]

        # print menu #
        SysMgr.printPipe(
            "[D-Bus Summary Info] [Run: %.1f] [Total: %s] [Error: %s]\n%s"
            % (DbusMgr.totalTime, convNum(total), convNum(err), twoLine)
        )
        SysMgr.printPipe(
            "{0:>16}({1:>7}) {2:>10}({3:>6}) {4:1}\n{5:1}".format(
                "Name", "PID", "Count", "%", "Message", twoLine
            )
        )

        # print message stats #
        for pid, value in sorted(
            msgs.items(), key=lambda e: e[1]["totalCnt"], reverse=True
        ):
            # print process info #
            comm = SysMgr.getComm(pid)
            cmdline = SysMgr.getCmdline(pid)
            procTotal = float(value["totalCnt"])
            SysMgr.printPipe(
                "{0:>16}({1:>7}) {2:>10}(100.0%) {3:1}\n{4:1}".format(
                    comm, pid, convNum(procTotal), cmdline, oneLine
                )
            )

            value.pop("totalCnt", None)

            for msg, cnt in sorted(
                value.items(), key=lambda e: e[1], reverse=True
            ):
                per = (cnt / procTotal) * 100
                SysMgr.printPipe(
                    "{0:>36}({1:5.1f}%) {2:1}".format(convNum(cnt), per, msg)
                )

            SysMgr.printPipe(twoLine)

        if not total:
            SysMgr.printPipe("    None\n" + twoLine)

    @staticmethod
    def runDbusSnooper(mode="top"):
        # pylint: disable=undefined-variable

        def _updateTaskInfo(dbusData, sentData, recvData):
            try:
                taskManager.saveSystemStat()
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr("failed to update system stat", True)

            convNum = UtilMgr.convNum
            convSize = UtilMgr.convSize2Unit

            for pid in taskList:
                try:
                    if pid not in dbusData:
                        continue

                    dbusList = []

                    # build D-Bus usage string #
                    dbusCnt = dbusData[pid]["totalCnt"]
                    dbusList.append(
                        "[TOTAL]: %s / [TYPE]: %s"
                        % (convNum(dbusCnt), convNum(len(dbusData[pid]) - 1))
                    )

                    for name, value in sorted(
                        dbusData[pid].items(),
                        key=lambda x: x[1]["cnt"] if x[0] != "totalCnt" else 0,
                        reverse=True,
                    ):
                        if name == "totalCnt" or value["cnt"] == 0:
                            continue

                        try:
                            per = long((value["cnt"] / float(dbusCnt)) * 100)
                        except SystemExit:
                            sys.exit(0)
                        except:
                            per = 0

                        # get time info #
                        if pid in sentData and name in sentData[pid]:
                            data = sentData[pid][name]
                        elif pid in recvData and name in recvData[pid]:
                            data = recvData[pid][name]
                        else:
                            continue

                        # get complete count and status #
                        cnt = data["cnt"]
                        if data["time"] > 0:
                            wstat = "/WAIT"
                        else:
                            wstat = ""

                        # get complete call count #
                        if cnt > 0:
                            avr = data["total"] / cnt
                        else:
                            avr = 0

                        # check error #
                        if data["err"] > 0:
                            errstr = ", Err: %s" % data["err"]
                        else:
                            errstr = ""

                        # set color #
                        try:
                            mtype, stats = name.lstrip("[").split("]", 1)
                            mtype = DbusMgr.msgColorList[mtype.strip()]
                            name = "[%s]%s" % (mtype, stats)
                        except SystemExit:
                            sys.exit(0)
                        except:
                            pass

                        if data["max"] > 0:
                            name = (
                                "%s {Min: %.3f, Avr: %.3f, Max: %.3f%s}%s"
                            ) % (
                                name,
                                data["min"],
                                avr,
                                data["max"],
                                errstr,
                                wstat,
                            )

                        count = convNum(value["cnt"])
                        size = convSize(data["size"], isInt=True)

                        dbusList.append(
                            "{0:>4}({1:>5}/{2:>3}%) {3:1}".format(
                                count, size, per, name
                            )
                        )

                    # handle terminated tasks #
                    if (
                        not pid in taskManager.procData
                        and pid in taskManager.prevProcData
                    ):
                        taskManager.procData[pid] = taskManager.prevProcData[
                            pid
                        ]

                    # add D-Bus usage #
                    taskManager.procData[pid]["dbusList"] = dbusList
                    taskManager.procData[pid]["dbusCnt"] = dbusCnt
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printWarn(
                        "failed to update task info", True, reason=True
                    )

            # save D-Bus data #
            DbusMgr.totalTime += SysMgr.uptimeDiff
            DbusMgr.msgData.append(UtilMgr.deepcopy(dbusData))

        def _printSummary(signum, frame):
            def _checkRepeatCnt():
                # check user event #
                if SysMgr.condExit:
                    if SysMgr.repeatCnt == 0:
                        SysMgr.printWarn("terminated by user\n", True)
                    os.kill(SysMgr.pid, signal.SIGINT)
                    return
                elif SysMgr.repeatCnt == 0:
                    return

                # check timer event #
                SysMgr.progressCnt += 1
                if SysMgr.repeatCnt <= SysMgr.progressCnt:
                    SysMgr.printWarn("terminated by timer\n", True)
                    if SysMgr.outPath:
                        SysMgr.removeExitFunc(_printSummary, [0, 0])
                    sys.exit(0)

            # check repeat count #
            if SysMgr.checkMode("printdbus"):
                _checkRepeatCnt()
                return

            # disable alarm #
            signal.alarm(0)

            # check user input #
            SysMgr.waitUserInput(0.000001)

            # acquire lock for shared stats #
            if lock:
                lock.acquire()

            # initialize data #
            prevRecvData = DbusMgr.recvData
            prevSentData = DbusMgr.sentData
            DbusMgr.sentData = {}
            DbusMgr.recvData = {}
            DbusMgr.msgSentTable = {}
            DbusMgr.msgRecvTable = {}
            prevDbusData = TaskAnalyzer.dbusData
            TaskAnalyzer.dbusData = {"totalCnt": 0, "totalErr": 0}

            # release lock for shared stats #
            if lock and lock.locked():
                try:
                    lock.release()
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

            convNum = UtilMgr.convNum

            # update CPU usage of tasks #
            _updateTaskInfo(prevDbusData, prevSentData, prevRecvData)

            if DbusMgr.dbgObj:
                # get CPU usages #
                mcpuStr, sysCpuStr = SysMgr.getCpuUsageStr(
                    DbusMgr.dbgObj, SysMgr.uptimeDiff
                )

                # get memory usage for myself #
                rssStr = DbusMgr.dbgObj.getMemUsage()
            else:
                mcpuStr = rssStr = sysCpuStr = "?"

            # get available memory for system #
            sysMemStr = SysMgr.getAvailMemInfo()

            # set error #
            nrErr = prevDbusData["totalErr"]
            if nrErr > 0:
                nrErr = UtilMgr.convColor(convNum(nrErr), "RED")
            else:
                nrErr = convNum(nrErr)

            title = "[D-Bus Info]"

            # print title #
            SysMgr.addPrint(
                (
                    "%s [Time: %7.3f] [Interval: %.1f] "
                    "[NrMsg: %s] [NrErr: %s] [SYS: %s/%s] [%s(%s): %s/%s] \n"
                )
                % (
                    title,
                    SysMgr.uptime,
                    SysMgr.uptimeDiff,
                    convNum(prevDbusData["totalCnt"]),
                    nrErr,
                    sysCpuStr,
                    sysMemStr,
                    DbusMgr.dbgObj.comm,
                    DbusMgr.dbgObj.pid,
                    mcpuStr,
                    rssStr,
                )
            )

            # print PSI stat #
            try:
                taskManager.printPsiStat(len(title))
            except SystemExit:
                sys.exit(0)
            except:
                pass

            # print resource usage of tasks #
            taskManager.printSystemUsage()
            taskManager.printTaskUsage()
            taskManager.reinitStats()
            SysMgr.printTopStats()

            # reset timer #
            if signum:
                # check repeat count #
                _checkRepeatCnt()

                # update timer #
                SysMgr.updateTimer()

        def _executeLoop(rdPipeList):
            tid = SysMgr.syscall("gettid")

            # main thread #
            if SysMgr.pid == tid:
                SysMgr.updateUptime()

                # update stats #
                _updateTaskInfo(
                    TaskAnalyzer.dbusData, DbusMgr.sentData, DbusMgr.recvData
                )

                # save initial stat of tasks #
                taskManager.reinitStats()

                # set timer #
                signal.signal(signal.SIGALRM, _printSummary)
                SysMgr.updateTimer()

                # set handler for exit #
                if mode == "top":
                    signal.signal(signal.SIGINT, SysMgr.exitHandler)
                    if SysMgr.outPath:
                        SysMgr.addExitFunc(_printSummary, [0, 0])
                    SysMgr.addExitFunc(SysMgr.stopHandler, [0, 0])

            while 1:
                if not rdPipeList:
                    return

                # multi-threaded loop #
                if threadingList:
                    # sibling thread #
                    if SysMgr.pid != tid:
                        _updateDataFromPipe(rdPipeList)
                    # main thread #
                    else:
                        try:
                            # check tracers #
                            while 1:
                                if not SysMgr.getChildList():
                                    SysMgr.printErr("no alive target task")
                                    sys.exit(0)
                                time.sleep(SysMgr.intervalEnable)
                        except SystemExit:
                            sys.exit(0)
                        except:
                            break
                # single-threaded loop #
                else:
                    _updateDataFromPipe(rdPipeList)

        def _updateData(data):
            tid, params, bus, service = data

            # convert string to dict #
            jsonData = UtilMgr.convStr2Dict(params)
            if not jsonData:
                return

            # check message #
            try:
                ctype = jsonData["name"]

                # check time #
                if jsonData["type"] != "enter":
                    return
            except SystemExit:
                sys.exit(0)
            except:
                return

            # check args #
            try:
                if ctype in ("sendmsg", "recvmsg"):
                    if isinstance(jsonData["args"]["msg"], dict):
                        msgList = jsonData["args"]["msg"]["msg_iov"]
                        _handleMsg(ctype, msgList, jsonData, data)
                elif ctype in ("sendmmsg", "recvmmsg"):
                    for idx, value in jsonData["args"]["msg"].items():
                        msgList = value["msg_iov"]
                        _handleMsg(ctype, msgList, jsonData, data)
                elif ctype == "read":
                    msgList = {
                        "0": {
                            "len": 0,
                            "data": jsonData["args"]["buf"],
                        }
                    }
                    _handleMsg(ctype, msgList, jsonData, data)
                else:
                    return
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printWarn(
                    "failed to handle %s for %s(%s)"
                    % ([jsonData], jsonData["comm"], jsonData["tid"]),
                    reason=True,
                )

        def _handleMsg(ctype, msgList, jsonData, data):
            if not isinstance(msgList, dict):
                return

            try:
                tid, params, bus, service = data

                mlist = {}
                cnt = 0
                gdmsg = 0

                gioObj = SysMgr.libgioObj
                libgObj = SysMgr.libgObj
                G_IO_ERROR_TYPE = DbusMgr.G_IO_ERROR_TYPE
                isTopMode = DbusMgr.isTopMode

                msgs = []
                for key, msg in sorted(msgList.items()):
                    msgs.append(msg)

                for idx, msg in enumerate(msgs):
                    # get message info #
                    length = msg["len"]
                    ecall = msg["data"]

                    if ctype == "read":
                        ecall = ecall.strip('"')
                        ecall = UtilMgr.decodeBase64(ecall)
                        if ecall[:7] == b"BEGIN\r\n":
                            call = ecall[7:]
                        else:
                            continue
                    else:
                        call = UtilMgr.decodeBase64(ecall)

                    # decode from base64 #
                    if isinstance(call, bytes):
                        call = call.decode("latin-1")

                    # check message sequence #
                    isFirst = isLast = False
                    if idx == 0:
                        isFirst = True
                    if idx == len(msgs) - 1:
                        isLast = True

                    # update message size #
                    if length == 0:
                        length = len(call)

                    # check message size #
                    if length == 0:
                        TaskAnalyzer.dbusData["totalErr"] += 1
                        continue

                    # recover data #
                    if len(call) > length:
                        call = call[:length]
                    elif len(call) < length:
                        call = call + ("\0" * (length - len(call)))

                    # check previous data #
                    if not tid in DbusMgr.prevData:
                        DbusMgr.prevData[tid] = {
                            "recvmsg": "",
                            "sendmsg": "",
                            "read": "",
                        }

                    # check direction #
                    if ctype.startswith("sendm"):
                        direction = "OUT"
                        data = DbusMgr.sentData
                        msgTable = DbusMgr.msgSentTable
                    else:
                        direction = "IN"
                        data = DbusMgr.recvData
                        msgTable = DbusMgr.msgRecvTable

                    # composite data #
                    if isLast:
                        if DbusMgr.prevData[tid][ctype]:
                            call = DbusMgr.prevData[tid][ctype] + call

                        if direction == "OUT":
                            DbusMgr.prevData[tid][ctype] = ""
                        else:
                            DbusMgr.prevData[tid][ctype] = call
                    else:
                        if isFirst:
                            if direction == "OUT":
                                DbusMgr.prevData[tid][ctype] = call
                            else:
                                DbusMgr.prevData[tid][ctype] += call
                        else:
                            DbusMgr.prevData[tid][ctype] += call

                        continue

                    # check message size #
                    if len(call) < 16:
                        continue

                    # cast bytes to void_p #
                    buf = c_char_p(call.encode("latin-1"))

                    errp = POINTER(DbusMgr.errObj)()

                    # check message size in header #
                    hsize = gioObj.g_dbus_message_bytes_needed(
                        buf, c_ulong(len(call)), byref(errp)
                    )
                    if errp:
                        SysMgr.printWarn(
                            (
                                "failed to get the length of %s D-Bus message"
                                " %s for %s(%s) because %s(%s)"
                            )
                            % (
                                direction,
                                [call],
                                jsonData["comm"],
                                jsonData["tid"],
                                G_IO_ERROR_TYPE[errp.contents.code],
                                errp.contents.message,
                            )
                        )
                        gioObj.g_error_free(byref(errp.contents))
                        TaskAnalyzer.dbusData["totalErr"] += 1
                        DbusMgr.prevData[tid][ctype] = ""
                        continue
                    elif hsize > len(call):
                        continue
                    # handle incoming data #
                    elif direction == "IN":
                        DbusMgr.prevData[tid][ctype] = ""

                    # free gdbus message object #
                    if gdmsg != 0:
                        libgObj.g_object_unref(gdmsg)
                        gdmsg = 0

                    # create GDBusMessage from bytes #
                    gdmsg = gioObj.g_dbus_message_new_from_blob(
                        buf, c_ulong(len(call)), 0, byref(errp)
                    )
                    # check error #
                    if not gdmsg and errp:
                        SysMgr.printWarn(
                            (
                                "failed to deserialize %s D-Bus"
                                " message %s for %s(%s) because %s(%s)"
                            )
                            % (
                                direction,
                                [call],
                                jsonData["comm"],
                                jsonData["tid"],
                                G_IO_ERROR_TYPE[errp.contents.code],
                                errp.contents.message,
                            )
                        )
                        gioObj.g_error_free(byref(errp.contents))
                        TaskAnalyzer.dbusData["totalErr"] += 1
                        continue

                    # get address of the message #
                    addr = c_ulong(gdmsg)

                    # get sender #
                    srcInfo = "??"
                    src = gioObj.g_dbus_message_get_sender(addr)
                    if src:
                        if isinstance(src, bytes):
                            src = src.decode()
                        srcInfo = src

                        if service:
                            if src in service:
                                srcInfo = service[src]
                            else:
                                service[src] = src
                                _updateServiceProc(bus, tid, service)
                                if src in service:
                                    srcInfo = service[src]

                    # get receiver #
                    desInfo = "??"
                    des = gioObj.g_dbus_message_get_destination(addr)
                    if des:
                        if isinstance(des, bytes):
                            des = des.decode()
                        desInfo = des

                        if service:
                            if des in service:
                                desInfo = service[des]
                            else:
                                service[des] = des
                                _updateServiceProc(bus, tid, service)
                                if des in service:
                                    desInfo = service[des]

                    # get message type #
                    try:
                        nrType = gioObj.g_dbus_message_get_message_type(addr)
                        mtype = DbusMgr.GDBusMessageType[nrType]
                    except SystemExit:
                        sys.exit(0)
                    except:
                        SysMgr.printWarn(
                            "failed to get type of GDBusMessage", reason=True
                        )
                        TaskAnalyzer.dbusData["totalErr"] += 1
                        continue

                    effectiveReply = False
                    if mtype == "RETURN":
                        # get reply-serial #
                        repSerial = gioObj.g_dbus_message_get_reply_serial(
                            c_ulong(gdmsg)
                        )
                        if repSerial in msgTable:
                            effectiveReply = True
                        else:
                            effectiveReply = False

                    # print message #
                    if not isTopMode or SysMgr.customCmd:
                        if len(jsonData["backtrace"]) > 2:
                            backtrace = (
                                "Backtrace: %s\n" % jsonData["backtrace"]
                            )
                        else:
                            backtrace = ""

                        if isTopMode or SysMgr.showAll:
                            addInfo = "\n%s%s" % (
                                gioObj.g_dbus_message_print(
                                    c_ulong(gdmsg), c_ulong(0)
                                ).decode(),
                                backtrace,
                            )
                        else:
                            """
                            path = gioObj.g_dbus_message_get_path(addr)
                            if not path:
                                path = b''
                            """

                            iface = gioObj.g_dbus_message_get_interface(addr)
                            if not iface:
                                iface = b""

                            member = gioObj.g_dbus_message_get_member(addr)
                            if not member:
                                member = b""

                            addInfo = " %s.%s" % (
                                iface.decode(),
                                member.decode(),
                            )
                            addInfo = UtilMgr.convColor(addInfo, "GREEN")

                        # get serial number #
                        serial = gioObj.g_dbus_message_get_serial(addr)
                        if not serial:
                            serial = b""

                        msgStr = "[%s] %.6f %s(%s) %3s %s->%s %s %sB%s" % (
                            DbusMgr.msgColorList[mtype],
                            jsonData["time"],
                            jsonData["comm"],
                            tid,
                            direction,
                            srcInfo,
                            desInfo,
                            serial,
                            UtilMgr.convSize2Unit(hsize),
                            addInfo,
                        )

                        # check skip condition #
                        if effectiveReply:
                            pass
                        elif SysMgr.customCmd and not UtilMgr.isValidStr(
                            msgStr, SysMgr.customCmd, inc=True, ignCap=True
                        ):
                            continue

                        # finish printing this message #
                        if not isTopMode:
                            SysMgr.printPipe(msgStr, flush=True)

                            # check log command #
                            SysMgr.checkLogCond(
                                msgStr, DbusMgr.watchList, DbusMgr.refilterList
                            )

                            continue

                    # set task default dict #
                    DbusMgr.sentData.setdefault(tid, {})
                    DbusMgr.recvData.setdefault(tid, {})

                    # return check #
                    if mtype == "RETURN":
                        if effectiveReply:
                            targetIf, prevTime = msgTable[repSerial]
                        else:
                            targetIf = prevTime = None

                        # handle method return #
                        if targetIf in data[tid] and prevTime:
                            lastData = data[tid][targetIf]
                            elapsed = jsonData["time"] - prevTime

                            if (
                                lastData["min"] == 0
                                or elapsed < lastData["min"]
                            ):
                                data[tid][targetIf]["min"] = elapsed

                            if elapsed > lastData["max"]:
                                data[tid][targetIf]["max"] = elapsed

                            data[tid][targetIf]["total"] += elapsed
                            data[tid][targetIf]["cnt"] += 1
                            data[tid][targetIf]["time"] = 0

                        continue

                    # get properties from message #
                    # path = gioObj.g_dbus_message_get_path(addr)
                    interface = gioObj.g_dbus_message_get_interface(addr)
                    member = gioObj.g_dbus_message_get_member(addr)
                    arg0 = gioObj.g_dbus_message_get_arg0(addr)
                    serial = gioObj.g_dbus_message_get_serial(addr)

                    # handle error message #
                    if mtype in ("ERROR", "INVALID"):
                        ename = gioObj.g_dbus_message_get_error_name(addr)
                        if not ename:
                            continue

                        mname = "[%6s] %3s %s: %s" % (
                            mtype,
                            direction,
                            ename.decode(),
                            arg0.decode(),
                        )
                        data[tid].setdefault(mname, dict(DbusMgr.taskInfo))
                        data[tid][mname]["cnt"] += 1
                        data[tid][mname]["err"] += 1
                        TaskAnalyzer.dbusData["totalErr"] += 1
                    # handle normal message #
                    else:
                        mname = "[%6s] %3s %s.%s" % (
                            mtype,
                            direction,
                            interface.decode(),
                            member.decode(),
                        )

                    # save serial number except for signal #
                    if mtype != "SIGNAL":
                        msgTable[serial] = (mname, jsonData["time"])

                    # initialize new interface #
                    data[tid].setdefault(mname, dict(DbusMgr.taskInfo))

                    # increase count #
                    cnt += 1
                    if mname not in mlist:
                        mlist[mname] = {"count": 1}
                    else:
                        mlist[mname]["count"] += 1

                    # increase size #
                    data[tid][mname]["size"] += hsize

                    # save timestamp #
                    data[tid][mname]["time"] = jsonData["time"]

                if cnt == 0:
                    return

                # acquire lock #
                if lock:
                    lock.acquire()

                # increase count #
                if tid not in TaskAnalyzer.dbusData:
                    TaskAnalyzer.dbusData[tid] = {"totalCnt": cnt}
                else:
                    TaskAnalyzer.dbusData[tid]["totalCnt"] += cnt

                TaskAnalyzer.dbusData["totalCnt"] += cnt

                # merge D-Bus interface #
                for name, value in mlist.items():
                    try:
                        TaskAnalyzer.dbusData[tid][name]["cnt"] += value[
                            "count"
                        ]
                    except SystemExit:
                        sys.exit(0)
                    except:
                        TaskAnalyzer.dbusData[tid][name] = {}
                        TaskAnalyzer.dbusData[tid][name]["cnt"] = value[
                            "count"
                        ]
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printWarn(
                    "failed to handle %s" % [jsonData], reason=True
                )
            finally:
                # free gdbus message object #
                if gdmsg != 0:
                    libgObj.g_object_unref(gdmsg)

                # release lock #
                if lock and lock.locked():
                    try:
                        lock.release()
                    except SystemExit:
                        sys.exit(0)
                    except:
                        pass

        def _updateDataFromPipe(rdPipeList):
            # merge dbus data #
            try:
                tid = comm = None

                # wait for event #
                [read, write, error] = selectObj.select(rdPipeList, [], [])

                # read messages through pipe connected to child processes #
                for robj in read:
                    tid = None
                    bus = None
                    service = None

                    # get tid of target #
                    try:
                        index = pipeList.index(robj)
                        tid = taskList[index]
                        bus = busList[index]
                        service = busServiceList[tid][bus]
                    except SystemExit:
                        sys.exit(0)
                    except:
                        pass

                    # handle data arrived #
                    while 1:
                        if SysMgr.condExit:
                            return

                        output = robj.readline()
                        if output == "\n":
                            continue
                        elif output == "":
                            try:
                                rdPipeList.remove(robj)
                            except SystemExit:
                                sys.exit(0)
                            except:
                                pass
                        elif output and len(output) > 0:
                            _updateData((tid, output, bus, service))

                        break
            except SystemExit:
                sys.exit(0)
            except:
                comm = SysMgr.getComm(tid, cache=True)

                if tid:
                    procInfo = " for %s(%s)" % (comm, tid)
                else:
                    procInfo = ""

                SysMgr.printWarn(
                    "failed to read data from pipe%s" % procInfo, reason=True
                )

                return

        def _updateServiceProc(bus, tid, serviceList, addr=None):
            """
            some case, hang up for the remote call with below error message.
            it is because the remote call is blocked by dbus-daemon that is also
            blocked by the tracer that is also blocked by the pipe for write
            that is full.

            "the remote application did not send a reply,
            the message bus security policy blocked the reply,
            the reply timeout expired, or the network connection was broken"
            """

            if not bus:
                return False

            # get all service list #
            services = DbusMgr.getBusService(bus, tid=tid, addr=None)
            if not services:
                return False

            # register process #
            for svc in services:
                # check skip condition #
                if svc in serviceList and svc != serviceList[svc]:
                    continue

                # get PID and update service info #
                pinfo = DbusMgr.getServiceProc(bus, svc)
                if pinfo:
                    serviceList[svc] = pinfo

            return True

        def _getDefaultTasks(comm, sibling=True):
            taskList = []
            tempList = SysMgr.getTids(comm, sibling=sibling)
            for tid in tempList:
                taskList.append(SysMgr.getTgid(tid))

                comm = SysMgr.getComm(tid, cache=True)
                if comm == "gdbus":
                    taskList.append(tid)

            return taskList

        # check essential json module #
        SysMgr.getPkg("json")

        # check permission #
        SysMgr.checkRootPerm()

        # check mode for returning list #
        if mode in ("getpidlist", "getunitlist", "getunitstat"):
            retList = True
        else:
            retList = False

        # check filter #
        pidList = {}
        unitList = {}
        taskList = []
        ret = SysMgr.selectTaskId() if not retList else None
        # selected processes #
        if ret:
            onlyDaemon = False
            if SysMgr.groupProcEnable:
                taskList += SysMgr.getTids(ret, sibling=True)
            else:
                taskList += _getDefaultTasks(ret)
        # new or daemon processes #
        elif not SysMgr.getOption("g") or retList:
            if SysMgr.hasMainArg() and not retList:
                onlyDaemon = False

                # get command #
                cmd = SysMgr.getMainArg()
                execCmd = UtilMgr.parseCommand(cmd)

                # execute command with a new process #
                pid = SysMgr.createProcess(execCmd, mute=True)
                if not pid:
                    sys.exit(0)

                # add TID to target list #
                taskList += [str(pid)]
            # daemon processes #
            else:
                onlyDaemon = True
                taskList += _getDefaultTasks("dbus-daemon")
                taskList += _getDefaultTasks("dbus-broker")
        # specific processes #
        else:
            onlyDaemon = False
            for val in SysMgr.filterGroup:
                if SysMgr.groupProcEnable:
                    taskList += SysMgr.getTids(val, sibling=True)
                else:
                    taskList += _getDefaultTasks(val)

        # check task list #
        if not taskList:
            SysMgr.printErr("failed to find task to analyze D-Bus message")
            sys.exit(-1)
        elif not onlyDaemon:
            if not (
                _getDefaultTasks("dbus-daemon")
                + _getDefaultTasks("dbus-broker")
            ):
                SysMgr.printErr("failed to find D-Bus daemon")
                if not SysMgr.forceEnable:
                    sys.exit(-1)
        else:
            # remove redundant tasks #
            taskList = UtilMgr.cleanItem(taskList)
            taskList.sort(key=int)
            SysMgr.printInfo(
                (
                    "only specific processes that are involved "
                    "in the process group [ %s ] are shown"
                )
                % SysMgr.getCommList(taskList)
            )

        # prepare D-Bus methods to analyze BLOB data #
        DbusMgr.prepareDbusMethods()

        # get select object #
        selectObj = SysMgr.getPkg("select")

        # get threading object #
        threadObj = SysMgr.getPkg("threading", False)
        if threadObj:
            lock = threadObj.Lock()
        else:
            lock = None

        # initialize task stat #
        DbusMgr.dbgObj = Debugger(SysMgr.pid, attach=False)
        DbusMgr.dbgObj.initValues()
        DbusMgr.dbgObj.getCpuUsage(system=True)
        DbusMgr.isTopMode = SysMgr.checkMode("dbustop")

        # define common list #
        busList = []
        pipeList = []
        busServiceList = {}
        gBusServiceList = {}
        threadingList = []
        environList = SysMgr.environList

        # update task filter #
        if not retList:
            SysMgr.filterGroup = taskList

            # get item for log filter #
            if "WATCHLOG" in environList:
                DbusMgr.watchList = environList["WATCHLOG"][0].split("+")
                DbusMgr.refilterList = environList.get("REFILTER")

            # initialize system stat #
            SysMgr.exceptCommFilter = True
            taskManager = TaskAnalyzer(onlyInstance=True)
            taskManager.saveSystemStat()
            SysMgr.sort = "D"
            SysMgr.processEnable = False
            SysMgr.cmdlineEnable = True

            # set target syscalls #
            if onlyDaemon:
                if SysMgr.showAll:
                    SysMgr.syscallList.append(SysMgr.getNrSyscall("sys_read"))
                if "INCINMSG" in environList:
                    SysMgr.syscallList.append(
                        SysMgr.getNrSyscall("sys_recvmsg")
                    )
                    SysMgr.syscallList.append(
                        SysMgr.getNrSyscall("sys_recvmmsg")
                    )
            else:
                SysMgr.syscallList.append(SysMgr.getNrSyscall("sys_recvmsg"))
                SysMgr.syscallList.append(SysMgr.getNrSyscall("sys_recvmmsg"))
            SysMgr.syscallList.append(SysMgr.getNrSyscall("sys_sendmsg"))
            SysMgr.syscallList.append(SysMgr.getNrSyscall("sys_sendmmsg"))

            # set colors for each message types #
            DbusMgr.msgColorList = {
                "INVALID": UtilMgr.convColor("INVALI", "RED", 6),
                "ERROR": UtilMgr.convColor("ERROR", "RED", 6),
                "METHOD": UtilMgr.convColor("METHOD", "CYAN", 6),
                "RETURN": UtilMgr.convColor("RETURN", "BLUE", 6),
                "SIGNAL": UtilMgr.convColor("SIGNAL", "PINK", 6),
            }

            # create a sync lock #
            syncLock = Debugger.getGlobalLock(SysMgr.pid, exit=False)
            if syncLock:
                lockf(syncLock, LOCK_EX)

        # os.environ["DBUS_SESSION_BUS_ADDRESS"] = "unix:path=/run/user/1000/bus"

        # create child processes to monitor each targets #
        for tid in taskList:
            cont = True

            # create pipe #
            rd, wr = os.pipe()

            # get cmdline for parent #
            try:
                ppidIdx = SysMgr.topInstance.ppidIdx
                ppid = taskManager.procData[tid]["stat"][ppidIdx]
                cmdline = SysMgr.getCmdline(ppid)
            except SystemExit:
                sys.exit(0)
            except:
                cmdline = ""

            # define variables #
            bus = None
            listen = None

            # get bus type for daemons #
            if onlyDaemon:
                cmdline += SysMgr.getCmdline(tid)
                if "--system " in cmdline or cmdline.endswith("--system"):
                    bus = "system"
                elif "--session " in cmdline or cmdline.endswith("--session"):
                    bus = "session"
                elif "--scope system" in cmdline:
                    bus = "system"
                elif "--scope user" in cmdline:
                    bus = "user"
                elif "--config-file=" in cmdline:
                    try:
                        cpath = cmdline.split("--config-file=", 1)[1]
                        pos = cpath.find(" --")
                        if pos > 0:
                            cpath = cpath[:pos]

                        with open(cpath, "r") as fd:
                            for item in fd.readlines():
                                item = item.strip()
                                if item.startswith("<listen>"):
                                    item = UtilMgr.lstrip(item, "<listen>")
                                    item = UtilMgr.rstrip(item, "</listen>")
                                    item = item.strip()
                                    listen = item
                                if item.startswith("<type>"):
                                    item = UtilMgr.lstrip(item, "<type>")
                                    item = UtilMgr.rstrip(item, "</type>")
                                    item = item.strip()
                                    bus = item
                    except SystemExit:
                        sys.exit(0)
                    except:
                        SysMgr.printWarn(
                            "failed to get D-Bus config", reason=True
                        )

                # add bus type #
                busList.append(bus)

            # get service list #
            if bus:
                services = DbusMgr.getBusService(bus, tid=tid, addr=listen)
            else:
                services = None

            # register services #
            introList = {}
            busServiceList.setdefault(tid, {})
            if services:
                busProcList = {}

                # register process #
                for idx, service in enumerate(services):
                    pinfo = DbusMgr.getServiceProc(bus, service)
                    busProcList[service] = pinfo
                    gBusServiceList.setdefault(service, pinfo)

                    # register methods and properties #
                    if mode == "printintro":
                        introList[service] = DbusMgr.getStats(
                            bus,
                            "introspect",
                            service,
                            procStr=pinfo,
                            path=SysMgr.customCmd[0]
                            if SysMgr.customCmd
                            else None,
                        )

                busServiceList[tid].setdefault(bus, busProcList)
            else:
                busServiceList[tid].setdefault(bus, {})

            # define process info #
            procStr = "%s(%s)" % (SysMgr.getComm(tid, cache=True), tid)

            # introspection #
            if mode == "printintro":
                for service, intro in introList.items():
                    DbusMgr.printIntrospection(tid, introList)

            # monitor #
            elif mode == "printmonitor":
                DbusMgr.runMonitor(bus)

            # signal #
            elif mode == "printsub":
                ret = DbusMgr.getStats(bus, "allmatch", procStr=procStr)
                if ret:
                    perProc, perSig = ret
                    DbusMgr.printSignalInfo(tid, perProc, perSig, busProcList)
                else:
                    SysMgr.printWarn(
                        "no subscription info for %s" % procStr, True
                    )

            # stat #
            elif mode == "printstat":
                ret = DbusMgr.getStats(bus, "stats")
                DbusMgr.printStatInfo(tid, ret)

            # sdinfo #
            elif mode in ("printsdinfo", "getunitlist"):
                if mode == "printsdinfo":
                    DbusMgr.printSdInfo(bus, procStr)
                else:
                    ret = DbusMgr.printSdInfo(bus, procStr, True)
                    if ret:
                        unitList.setdefault(procStr, {})
                        unitList[procStr].update(ret)

            # sdunit #
            elif mode in ("printsdunit", "getunitstat"):
                ret = DbusMgr.getUnitList(bus)
                if mode == "printsdunit":
                    DbusMgr.printUnitInfo(tid, ret, bus, procStr)
                else:
                    res = DbusMgr.printUnitInfo(tid, ret, bus, procStr, True)
                    unitList.setdefault(procStr, {})
                    unitList[procStr].update(res)

            # pidlist #
            elif mode == "getpidlist":
                if not pidList:
                    pidList = {k: None for k in SysMgr.getPidList()}

                for pid in pidList:
                    if not pid.isdigit():
                        continue
                    elif pidList[pid]:
                        continue

                    pidList[pid] = DbusMgr.getUnitByPID(bus, long(pid))

            else:
                cont = False

            # skip more processing #
            if cont:
                continue

            # create a new process #
            pid = SysMgr.createProcess()

            # parent #
            if pid > 0:
                os.close(wr)
                rdPipe = os.fdopen(rd, "r")
                SysMgr.setPipeSize(rdPipe)
                pipeList.append(rdPipe)

                # create a new worker thread #
                if threadObj:
                    tobj = threadObj.Thread(
                        target=_executeLoop, args=[[rdPipe]]
                    )
                    tobj.daemon = True
                    threadingList.append(tobj)
            # child #
            elif pid == 0:
                # redirect stdout to pipe #
                os.close(rd)
                wrPipe = os.fdopen(wr, "w")
                SysMgr.setPipeSize(wrPipe)
                os.dup2(wr, 1)

                # set SIGPIPE handler for termination of parent #
                SysMgr.setPipeHandler()

                # set environment for workers #
                sys.argv[1] = "strace"
                SysMgr.showAll = True
                SysMgr.intervalEnable = 0
                SysMgr.outPath = SysMgr.inputFile = SysMgr.printFd = None
                SysMgr.logEnable = False
                SysMgr.filterGroup = [tid]
                SysMgr.jsonEnable = True
                Debugger.dbusEnable = True
                SysMgr.streamEnable = True
                SysMgr.addOption("Q")
                SysMgr.customCmd = []

                # wait for parent to create all children #
                if syncLock:
                    for flag in (LOCK_EX, LOCK_UN):
                        lockf(syncLock, flag)

                # execute strace mode #
                SysMgr.doTrace("syscall", tid=tid)

                sys.exit(0)

            # error #
            else:
                sys.exit(-1)

        # check mode #
        if mode == "getpidlist":
            return pidList
        elif mode in ("getunitlist", "getunitstat"):
            return unitList
        elif mode in (
            "printintro",
            "printsub",
            "printstat",
            "printsdunit",
            "printsdinfo",
        ):
            return

        # release lock to start tracers #
        if syncLock:
            lockf(syncLock, LOCK_UN)

        # remove lock file #
        try:
            os.remove(syncLock.name)
        except:
            pass

        # start worker threads #
        for tobj in threadingList:
            tobj.start()

        # run event loop #
        _executeLoop(pipeList)


class DltAnalyzer(object):
    """Manager for DLT"""

    # define constant #
    DLT_HTYP_WEID = 0x04
    DLT_SIZE_WEID = DLT_ID_SIZE = 4
    DLT_MSIN_MSTP = 0x0E  # message type #
    DLT_MSIN_MSTP_SHIFT = 1  # shift right offset to get mstp value #
    DLT_DAEMON_TEXTSIZE = 10024
    DLT_FILTER_MAX = 30

    # define message color type #
    msgColorList = []

    # define message type #
    MSGTYPE = ["log", "app_trace", "nw_trace", "control"]

    # define log type #
    LOGINFO = ["", "fatal", "error", "warn", "info", "debug", "verb"]

    # define log level #
    LOGLEVEL = {
        "DEFAULT": -1,  # Default log level
        "OFF": 0x00,  # Log level off
        "FATAL": 0x01,  # fatal system error
        "ERROR": 0x02,  # error with impact to correct functionality
        "WARN": 0x03,  # warning, correct behaviour could not be ensured
        "INFO": 0x04,  # informational
        "DEBUG": 0x05,  # debug
        "VERBOSE": 0x06,  # highest grade of information
    }

    RETURNTYPE = {
        -7: "DLT_RETURN_LOGGING_DISABLED",
        -6: "DLT_RETURN_USER_BUFFER_FULL",
        -5: "DLT_RETURN_WRONG_PARAMETER",
        -4: "DLT_RETURN_BUFFER_FULL",
        -3: "DLT_RETURN_PIPE_FULL",
        -2: "DLT_RETURN_PIPE_ERROR",
        -1: "DLT_RETURN_ERROR",
        0: "DLT_RETURN_OK",
        1: "DLT_RETURN_TRUE",
    }

    SERVICEID = {
        "DLT_SERVICE_ID": 0x00,
        "DLT_SERVICE_ID_SET_LOG_LEVEL": 0x01,
        "DLT_SERVICE_ID_SET_TRACE_STATUS": 0x02,
        "DLT_SERVICE_ID_GET_LOG_INFO": 0x03,
        "DLT_SERVICE_ID_GET_DEFAULT_LOG_LEVEL": 0x04,
        "DLT_SERVICE_ID_STORE_CONFIG": 0x05,
        "DLT_SERVICE_ID_RESET_TO_FACTORY_DEFAULT": 0x06,
        "DLT_SERVICE_ID_SET_COM_INTERFACE_STATUS": 0x07,
        "DLT_SERVICE_ID_SET_COM_INTERFACE_MAX_BANDWIDTH": 0x08,
        "DLT_SERVICE_ID_SET_VERBOSE_MODE": 0x09,
        "DLT_SERVICE_ID_SET_MESSAGE_FILTERING": 0x0A,
        "DLT_SERVICE_ID_SET_TIMING_PACKETS": 0x0B,
        "DLT_SERVICE_ID_GET_LOCAL_TIME": 0x0C,
        "DLT_SERVICE_ID_USE_ECU_ID": 0x0D,
        "DLT_SERVICE_ID_USE_SESSION_ID": 0x0E,
        "DLT_SERVICE_ID_USE_TIMESTAMP": 0x0F,
        "DLT_SERVICE_ID_USE_EXTENDED_HEADER": 0x10,
        "DLT_SERVICE_ID_SET_DEFAULT_LOG_LEVEL": 0x11,
        "DLT_SERVICE_ID_SET_DEFAULT_TRACE_STATUS": 0x12,
        "DLT_SERVICE_ID_GET_SOFTWARE_VERSION": 0x13,
        "DLT_SERVICE_ID_MESSAGE_BUFFER_OVERFLOW": 0x14,
    }

    SERVICERESPONSE = {
        "DLT_SERVICE_RESPONSE_OK": 0x00,
        "DLT_SERVICE_RESPONSE_NOT_SUPPORTED": 0x01,
        "DLT_SERVICE_RESPONSE_ERROR": 0x02,
        "DLT_SERVICE_RESPONSE_PERM_DENIED": 0x03,
        "DLT_SERVICE_RESPONSE_WARNING": 0x04,
        "DLT_SERVICE_RESPONSE_LAST": 0x05,
    }

    # define list #
    pids = []
    procInfo = None
    dltData = {"cnt": 0}
    msgData = []
    dbgObj = None
    version = None
    totalTime = 0
    configData = {}

    @staticmethod
    def printSummary():
        # define summary data #
        total = 0
        msgs = {}
        convNum = UtilMgr.convNum

        # summarize messages #
        for intval in DltAnalyzer.msgData:
            total += intval.pop("cnt", None)

            for ecuid, ecudata in intval.items():
                # set default ecuid dict #
                msgs.setdefault(ecuid, {"cnt": 0})
                msgs[ecuid]["cnt"] += ecudata.pop("cnt", None)

                for appid, appdata in ecudata.items():
                    # set default appid dict #
                    msgs[ecuid].setdefault(appid, {"cnt": 0})
                    msgs[ecuid][appid]["cnt"] += appdata.pop("cnt", None)

                    for ctxid, ctxdata in appdata.items():
                        # set default ctxid dict #
                        msgs[ecuid][appid].setdefault(ctxid, {"cnt": 0})
                        msgs[ecuid][appid][ctxid]["cnt"] += ctxdata.pop(
                            "cnt", None
                        )

        # print menu #
        SysMgr.printPipe(
            "[DLT Summary Info] [Run: %.1f] [Total: %s]\n%s"
            % (DltAnalyzer.totalTime, convNum(total), twoLine)
        )
        SysMgr.printPipe(
            "{0:^20} | {1:^19} | {2:^19} |\n{3:1}".format(
                "ECU", "AP", "CONTEXT", twoLine
            ),
        )

        # traverse DLT table #
        dltCnt = 0
        for ecuId, ecuItem in sorted(
            msgs.items(),
            key=lambda x: x[1]["cnt"] if x[0] != "cnt" else 0,
            reverse=True,
        ):
            if ecuId == "cnt":
                continue

            ecuCnt = ecuItem["cnt"]
            ecuPer = (ecuCnt / float(total)) * 100
            ecuStr = "{0:4} {1:>8}({2:5.1f}%)".format(
                ecuId, convNum(ecuCnt), ecuPer
            )
            SysMgr.printPipe(ecuStr)
            dltCnt += 1

            for apId, apItem in sorted(
                ecuItem.items(),
                key=lambda x: x[1]["cnt"] if x[0] != "cnt" else 0,
                reverse=True,
            ):
                if apId == "cnt":
                    continue

                depth = len(ecuStr) * " "
                apCnt = apItem["cnt"]
                apPer = (apCnt / float(ecuCnt)) * 100
                apStr = "{0:1}{1:4} {2:>8}({3:5.1f}%)".format(
                    depth, apId, convNum(apCnt), apPer
                )
                SysMgr.printPipe(apStr)

                for ctxId, ctxItem in sorted(
                    apItem.items(),
                    key=lambda x: x[1]["cnt"] if x[0] != "cnt" else 0,
                    reverse=True,
                ):
                    if ctxId == "cnt":
                        continue

                    depth = len(apStr) * " "
                    ctxCnt = ctxItem["cnt"]
                    ctxPer = (ctxCnt / float(apCnt)) * 100
                    ctxStr = "{0:1}{1:4} {2:>8}({3:5.1f}%)".format(
                        depth, ctxId, convNum(ctxCnt), ctxPer
                    )
                    SysMgr.printPipe(ctxStr)

        if dltCnt == 0:
            SysMgr.printPipe("    None")

        SysMgr.printPipe(oneLine)

    @staticmethod
    def loadDaemonConfig(path=None):
        if not path:
            path = "/etc/dlt.conf"

        confDict = DltAnalyzer.loadConfig(path)
        if confDict:
            DltAnalyzer.configData = confDict

    @staticmethod
    def loadConfig(path):
        data = SysMgr.readFile(path, verb=False)
        if not data:
            return

        confDict = {}
        for l in data.split("\n"):
            l = l.strip()
            if not l or l.startswith("#"):
                continue

            try:
                k, v = l.split("=", 1)
                confDict[k.strip()] = v.strip()
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printWarn("failed to parse DLT config", reason=True)

        return confDict

    @staticmethod
    def printIntervalSummary(force=False):
        quitLoop = False
        convNum = UtilMgr.convNum

        # update CPU usage #
        if DltAnalyzer.dbgObj:
            # get CPU usages #
            mcpuStr, sysCpuStr = SysMgr.getCpuUsageStr(
                DltAnalyzer.dbgObj, SysMgr.uptimeDiff
            )

            # get memory usage for myself #
            rssStr = DltAnalyzer.dbgObj.getMemUsage()

            # get available memory for system #
            sysMemStr = SysMgr.getAvailMemInfo()

            procInfo = "%s(%s)" % (
                DltAnalyzer.dbgObj.comm,
                DltAnalyzer.dbgObj.pid,
            )
        else:
            procInfo = mcpuStr = rssStr = sysCpuStr = sysMemStr = "?"

        # print title #
        SysMgr.addPrint(
            (
                "[%s] [Time: %7.3f] [Interval: %.1f] [DLT: %.3f] [NrMsg: %s] "
                "[SYS: %s/%s] [%s: %s/%s]\n"
            )
            % (
                "DLT Info",
                SysMgr.uptime,
                SysMgr.uptimeDiff,
                SysMgr.dltTime,
                convNum(DltAnalyzer.dltData["cnt"]),
                sysCpuStr,
                sysMemStr,
                procInfo,
                mcpuStr,
                rssStr,
            ),
            force=force,
        )

        # update daemon stat #
        DltAnalyzer.procInfo.saveProcInstance()
        saved = False
        for pid in DltAnalyzer.pids:
            ret = DltAnalyzer.procInfo.saveProcData(None, pid)
            if ret:
                saved = True

        # print daemon stat #
        if saved:
            DltAnalyzer.procInfo.printTaskUsage()
            DltAnalyzer.procInfo.reinitStats()
        else:
            SysMgr.addPrint("%s\n" % twoLine, force=force)

        SysMgr.addPrint(
            "{0:^20} | {1:^19} | {2:^19} |\n{3:1}\n".format(
                "ECU", "AP", "CONTEXT", twoLine
            ),
            newline=2,
            force=force,
        )

        # traverse DLT table #
        dltCnt = 0
        for ecuId, ecuItem in sorted(
            DltAnalyzer.dltData.items(),
            key=lambda x: x[1]["cnt"] if x[0] != "cnt" else 0,
            reverse=True,
        ):
            if ecuId == "cnt":
                continue

            if quitLoop or (not force and SysMgr.checkCutCond()):
                break

            ecuCnt = ecuItem["cnt"]
            ecuPer = (ecuCnt / float(DltAnalyzer.dltData["cnt"])) * 100
            ecuStr = "{0:4} {1:>8}({2:5.1f}%)\n".format(
                ecuId, convNum(ecuCnt), ecuPer
            )
            SysMgr.addPrint(ecuStr, force=force)
            dltCnt += 1

            for apId, apItem in sorted(
                ecuItem.items(),
                key=lambda x: x[1]["cnt"] if x[0] != "cnt" else 0,
                reverse=True,
            ):
                if apId == "cnt":
                    continue

                if quitLoop or (not force and SysMgr.checkCutCond()):
                    quitLoop = True
                    break

                depth = len(ecuStr) * " "
                apCnt = apItem["cnt"]
                apPer = (apCnt / float(ecuCnt)) * 100
                apStr = "{0:1}{1:4} {2:>8}({3:5.1f}%)\n".format(
                    depth, apId, convNum(apCnt), apPer
                )
                SysMgr.addPrint(apStr, force=force)

                for ctxId, ctxItem in sorted(
                    apItem.items(),
                    key=lambda x: x[1]["cnt"] if x[0] != "cnt" else 0,
                    reverse=True,
                ):
                    if ctxId == "cnt":
                        continue

                    if quitLoop or (not force and SysMgr.checkCutCond()):
                        quitLoop = True
                        break

                    depth = len(apStr) * " "
                    ctxCnt = ctxItem["cnt"]
                    ctxPer = (ctxCnt / float(apCnt)) * 100
                    ctxStr = "{0:1}{1:4} {2:>8}({3:5.1f}%)\n".format(
                        depth, ctxId, convNum(ctxCnt), ctxPer
                    )
                    SysMgr.addPrint(ctxStr, force=force)

        if dltCnt == 0:
            SysMgr.addPrint("    None\n", force=force)

        if not quitLoop:
            SysMgr.addPrint("%s\n" % oneLine, force=force)

        SysMgr.printTopStats()

        # save DLT data #
        DltAnalyzer.totalTime += SysMgr.uptimeDiff
        DltAnalyzer.msgData.append(UtilMgr.deepcopy(DltAnalyzer.dltData))

        # initialize DLT data #
        DltAnalyzer.dltData = {"cnt": 0}

    @staticmethod
    def onAlarm(signum, frame):
        # check user input #
        SysMgr.waitUserInput(wait=0.000001, msg="DEFAULT")

        # update uptime #
        SysMgr.updateUptime()

        if DltAnalyzer.dltData["cnt"] or SysMgr.inWaitStatus:
            DltAnalyzer.printIntervalSummary()
        elif not SysMgr.condExit:
            SysMgr.printWarn("no DLT message received", True)

        if SysMgr.condExit:
            return

        # check term condition #
        SysMgr.progressCnt += 1
        if 0 < SysMgr.repeatCnt <= SysMgr.progressCnt:
            SysMgr.printWarn("terminated by timer\n", True)
            os.kill(SysMgr.pid, signal.SIGINT)

        # print progress #
        if SysMgr.repeatCnt:
            UtilMgr.printProgress(SysMgr.progressCnt, SysMgr.repeatCnt)

        SysMgr.updateTimer()

    @staticmethod
    def getMsgLogLevel(msg):
        DLT_MSIN_MTIN = 0xF0  # message type info #
        DLT_MSIN_MTIN_SHIFT = 4  # shift right offset to get mtin value #
        return (
            msg.extendedheader.contents.msin & DLT_MSIN_MTIN
        ) >> DLT_MSIN_MTIN_SHIFT

    @staticmethod
    def getTraceFileList():
        # load dlt config #
        if not DltAnalyzer.configData:
            DltAnalyzer.loadDaemonConfig()

        fileList = []
        for path in ("OfflineTraceDirectory", "OfflineLogstorageDirPath"):
            if not path in DltAnalyzer.configData:
                continue
            fileList.append(
                os.path.join(DltAnalyzer.configData[path], "*.dlt")
            )

        # return trace file list #
        return UtilMgr.getFileList(fileList, exceptDir=True, verb=False)

    @staticmethod
    def handleMessage(
        dltObj,
        msg,
        buf,
        mode,
        verb,
        buffered=False,
        cond=None,
        since=0,
        until=0,
        fname=None,
        printsrc=False,
    ):
        # check headers #
        if not msg.storageheader or not msg.extendedheader:
            if not SysMgr.isWindows:
                return

        uptime = msg.headerextra.tmsp / float(10000)

        # get timestamp #
        if "USEUPTIME" in SysMgr.environList:
            timestamp = uptime
        else:
            header = msg.storageheader.contents
            timestamp = header.seconds + float(header.microseconds) / 1000000

        # check since condition #
        if since and timestamp < since:
            return
        # check until condition #
        elif until and timestamp >= until:
            SysMgr.printWarn("all previous DLT logs are printed", True)
            sys.exit(0)

        # save and reset global filter #
        filterGroup = SysMgr.filterGroup

        # pick header info #
        try:
            ecuId = msg.storageheader.contents.ecu.decode()
        except SystemExit:
            sys.exit(0)
        except:
            ecuId = "?"

        try:
            apId = msg.extendedheader.contents.apid.decode()
        except SystemExit:
            sys.exit(0)
        except:
            apId = "?"

        try:
            ctxId = msg.extendedheader.contents.ctid.decode()
        except SystemExit:
            sys.exit(0)
        except:
            ctxId = "?"

        # summarizing #
        if mode == "top":
            # check filter #
            if filterGroup:
                skipFlag = True
                for cond in filterGroup:
                    if cond in (ecuId, apId, ctxId):
                        skipFlag = False
                        break
                if skipFlag:
                    return

            DltAnalyzer.dltData["cnt"] += 1

            # add ecuId #
            if not ecuId in DltAnalyzer.dltData:
                DltAnalyzer.dltData[ecuId] = {"cnt": 0}
            DltAnalyzer.dltData[ecuId]["cnt"] += 1

            # add apId #
            if not apId in DltAnalyzer.dltData[ecuId]:
                DltAnalyzer.dltData[ecuId][apId] = {"cnt": 0}
            DltAnalyzer.dltData[ecuId][apId]["cnt"] += 1

            # add ctxId #
            if not ctxId in DltAnalyzer.dltData[ecuId][apId]:
                DltAnalyzer.dltData[ecuId][apId][ctxId] = {"cnt": 0}
            DltAnalyzer.dltData[ecuId][apId][ctxId]["cnt"] += 1
        # printing #
        elif mode == "print":
            # get payload #
            ret = dltObj.dlt_message_payload(
                byref(msg), buf, DltAnalyzer.DLT_DAEMON_TEXTSIZE, 2, verb
            )

            try:
                string = buf.value
                string = string.decode("ascii", "ignore").strip()
            except SystemExit:
                sys.exit(0)
            except:
                string = [string]

            # check filter #
            if filterGroup:
                skipFlag = True
                for cond in filterGroup:
                    if cond in (ecuId, apId, ctxId) or cond in string:
                        skipFlag = False
                        break
                if skipFlag:
                    return

            # get message info #
            timeSec = msg.storageheader.contents.seconds
            timeUs = msg.storageheader.contents.microseconds
            mtime = "%.6f" % uptime

            # get log level #
            try:
                level = DltAnalyzer.getMsgLogLevel(msg)
                level = DltAnalyzer.msgColorList[level]
            except SystemExit:
                sys.exit(0)
            except:
                level = ""

            # get date time #
            ntime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(timeSec))

            # build full string #
            if SysMgr.jsonEnable:
                # get local date and time #
                ldate, ltime = ntime.split()

                # build string in JSON format #
                output = {
                    "localdate": ldate,
                    "localtime": ltime,
                    "seconds": UtilMgr.convStr2Time(
                        "%s.%s" % (ntime, timeUs),
                        timefmt="%Y-%m-%d %H:%M:%S.%f",
                        isSec=True,
                    ),
                    "mtime": mtime,
                    "ntime": ntime,
                    "ecuId": ecuId,
                    "apId": apId,
                    "ctxId": ctxId,
                    "level": level,
                    "message": string,
                    "source": fname if fname else "daemon",
                }

                output = UtilMgr.convDict2Str(
                    output, pretty=not SysMgr.streamEnable
                )
            else:
                # build default string #
                output = (
                    "{0:1}.{1:06d} {2:1} {3:4} {4:4} {5:4} {6:5} {7!s:1}"
                ).format(
                    ntime, timeUs, mtime, ecuId, apId, ctxId, level, string
                )

                # append file name #
                if printsrc:
                    fname = UtilMgr.convColor("[%s] " % fname, "YELLOW")
                    output = fname + output

            # print log #
            if buffered:
                SysMgr.addPrint(output, force=True, isList=True)
            else:
                SysMgr.printPipe(output, flush=True)

            # check exit condition #
            refilter = SysMgr.environList.get("REFILTER") if cond else None
            SysMgr.checkLogCond(string, cond, refilter)

    @staticmethod
    def doLogDlt(
        appid=b"GIDR", context=b"GIDR", msg=None, level=None, split=True
    ):
        # get ctypes object #
        SysMgr.importPkgItems("ctypes")

        class DltContext(Structure):
            _fields_ = [
                ("contextID", c_char * 4),
                ("log_level_pos", c_int32),
                ("log_level_ptr", POINTER(c_int8)),
                ("trace_status_ptr", POINTER(c_int8)),
                ("mcnt", c_uint8),
            ]

        # check dlt-daemon #
        if not "NOCHECKDAEMON" in SysMgr.environList and not DltAnalyzer.pids:
            DltAnalyzer.pids = SysMgr.getProcPids("dlt-daemon")
            if not DltAnalyzer.pids and not SysMgr.remoteServObj:
                SysMgr.printWarn("failed to find dlt-daemon process", True)

        DLT_USER_BUF_MAX_SIZE = 1380

        # set log level #
        try:
            if not level:
                level = "INFO"

            loglevel = DltAnalyzer.LOGLEVEL[level.upper()]
        except:
            loglevel = DltAnalyzer.LOGLEVEL["INFO"]

        # load DLT library #
        try:
            if not SysMgr.dltObj:
                SysMgr.dltObj = SysMgr.loadLib(SysMgr.libdltPath)
            if not SysMgr.dltObj:
                raise Exception("no DLT library")
            dltObj = SysMgr.dltObj
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.dltObj = None
            SysMgr.printWarn(
                "failed to find %s to log DLT" % SysMgr.libdltPath, True
            )
            return -1

        # register #
        if not SysMgr.dltCtx:
            ctx = DltContext()
            ret = dltObj.dlt_register_app(appid, "Guider".encode())
            if ret < 0:
                SysMgr.printErr("failed to register app '%s'" % appid)
                return -1

            ret = dltObj.dlt_register_context(
                byref(ctx), context, "Guider".encode()
            )
            if ret < 0:
                SysMgr.printErr("failed to register context '%s'" % context)
                return -1

            SysMgr.dltCtx = ctx

        # split by newline #
        if isinstance(msg, list):
            msgList = msg
        else:
            msgList = msg.strip().split("\n")

        # log #
        for msg in msgList:
            pos = 0
            while 1:
                if len(msg[pos:]) >= DLT_USER_BUF_MAX_SIZE:
                    end = DLT_USER_BUF_MAX_SIZE + pos
                else:
                    end = len(msg)

                ret = dltObj.dlt_log_string(
                    byref(SysMgr.dltCtx), loglevel, msg[pos:end].encode()
                )

                if end == len(msg):
                    break

                pos = end

        """
        # unregister #
        dltObj.dlt_unregister_context(byref(ctx))
        dltObj.dlt_unregister_app()
        """

        return 0

    @staticmethod
    def runDltReceiver(mode="top"):
        def _findNextHeader(path, filePos):
            with open(path, "rb") as fobj:
                lastPos = filePos
                fobj.seek(lastPos)
                buf = fobj.read(1024)
                while buf:
                    found = buf.find(b"DLT\x01")
                    if found != -1:
                        return lastPos + found
                    lastPos = fobj.tell()
                    buf = fobj.read(1024)
            return None

        def _setFilter(
            dltObj, dltFilter, dltFile, apid=None, ctid=None, init=True
        ):
            # initialize filter #
            if init and dltObj.dlt_filter_init(byref(dltFilter), verb) == -1:
                SysMgr.printErr("failed to initialize the DLTFilter object")
                return -1

            if (
                dltObj.dlt_filter_add(
                    byref(dltFilter), apid or b"", ctid or b"", verb
                )
                == -1
            ):
                SysMgr.printErr(
                    "failed to add %s and %s to the DLTFilter object"
                    % (apid, ctid)
                )
                return -1

            if dltFilter.counter >= DltAnalyzer.DLT_FILTER_MAX:
                SysMgr.printErr(
                    (
                        "failed to add %s and %s to the DLTFilter object "
                        "because maximum filter count %s exceed"
                    )
                    % (apid, ctid, DltAnalyzer.DLT_FILTER_MAX)
                )
                return -1

            return dltObj.dlt_file_set_filter(
                byref(dltFile), byref(dltFilter), verb
            )

        # get ctypes object #
        SysMgr.importPkgItems("ctypes")

        # define constant #
        DLT_HTYP_WEID = DltAnalyzer.DLT_HTYP_WEID
        DLT_ID_SIZE = DltAnalyzer.DLT_ID_SIZE
        DLT_FILTER_MAX = DltAnalyzer.DLT_FILTER_MAX

        class in_addr(Structure):
            _fields_ = [
                ("s_addr", c_ulong),
            ]

        class sockaddr_in(Structure):
            _fields_ = [
                ("sin_family", c_short),
                ("sin_port", c_ushort),
                ("sin_addr", in_addr),
                ("sin_zero", c_char * 8),
            ]

        class sockaddr_un(Structure):
            _fields_ = [
                ("sun_family", c_short),
                ("sun_path", c_char * 108),
            ]

        class DltContext(Structure):
            _fields_ = [
                ("contextID", c_char * 4),
                ("log_level_pos", c_int32),
                ("log_level_ptr", POINTER(c_int8)),
                ("trace_status_ptr", POINTER(c_int8)),
                ("mcnt", c_uint8),
            ]

        class DltReceiver(Structure):
            """
            typedef struct
             {
                 int32_t lastBytesRcvd;    /**< bytes received in last receive call */
                 int32_t bytesRcvd;        /**< received bytes */
                 int32_t totalBytesRcvd;   /**< total number of received bytes */
                 char *buffer;             /**< pointer to receiver buffer */
                 char *buf;                /**< pointer to position within receiver buffer */
                 char *backup_buf;     /** pointer to the buffer with partial messages if any **/
                 int fd;                   /**< connection handle */
                 DltReceiverType type;     /**< type of connection handle */
                 int32_t buffersize;       /**< size of receiver buffer */
                 struct sockaddr_in addr;  /**< socket address information */
             } DltReceiver;
            """

            _fields_ = [
                ("lastBytesRcvd", c_int32),
                ("bytesRcvd", c_int32),
                ("totalBytesRcvd", c_int32),
                ("buffer", POINTER(c_char)),
                ("buf", POINTER(c_char)),
                ("backup_buf", POINTER(c_char)),
                ("fd", c_int),
                ("type", c_int),
                ("buffersize", c_int32),
                ("addr", sockaddr_in),
            ]

        class DltClient(Structure):
            """
            typedef struct
            {
                DltReceiver receiver;  /**< receiver pointer to dlt receiver structure */
                int sock;              /**< sock Connection handle/socket */
                char *servIP;          /**< servIP IP adress/Hostname of TCP/IP interface */
                int port;              /**< Port for TCP connections (optional) */
                char *serialDevice;    /**< serialDevice Devicename of serial device */
                char *socketPath;      /**< socketPath Unix socket path */
                char ecuid[4];           /**< ECUiD */
                speed_t baudrate;      /**< baudrate Baudrate of serial interface, as speed_t */
                DltClientMode mode;    /**< mode DltClientMode */
            } DltClient;
            """

            _fields_ = [
                ("receiver", DltReceiver),
                ("sock", c_int),
                ("servIP", c_char_p),
                ("hostIP", c_char_p),
                ("port", c_int),
                ("serialDevice", c_char_p),
                ("socketPath", c_char_p),
                ("ecuid", c_char * DLT_ID_SIZE),
                ("baudrate", c_uint),
                ("mode", c_int),
                ("send_serial_header", c_int),
                ("resync_serial_header", c_int),
            ]

        class ContextIDsInfoType(Structure):
            """
            typedef struct
            {
                char context_id[DLT_ID_SIZE];
                int16_t log_level;
                int16_t trace_status;
                uint16_t len_context_description;
                char *context_description;
            } ContextIDsInfoType;
            """

            _fields_ = [
                ("context_id", c_char * DLT_ID_SIZE),
                ("log_level", c_int16),
                ("trace_status", c_int16),
                ("len_context_description", c_uint16),
                ("context_description", c_char_p),
            ]

        class AppIDsType(Structure):
            """
            typedef struct
             {
                 char app_id[DLT_ID_SIZE];
                 uint16_t count_context_ids;
                 ContextIDsInfoType *context_id_info; /**< holds info about a specific con id */
                 uint16_t len_app_description;
                 char *app_description;
             } AppIDsType;
            """

            _fields_ = [
                ("app_id", c_char * DLT_ID_SIZE),
                ("count_context_ids", c_uint16),
                ("context_id_info", POINTER(ContextIDsInfoType)),
                ("len_app_description", c_uint16),
                ("app_description", c_char_p),
            ]

        class LogInfoType(Structure):
            """
            typedef struct
            {
                uint16_t count_app_ids;
                AppIDsType *app_ids;            /**< holds info about a specific app id */
             } LogInfoType;
            """

            _fields_ = [
                ("count_app_ids", c_uint16),
                ("app_ids", POINTER(AppIDsType)),
            ]

        class DltServiceGetLogInfoResponse(Structure):
            """
            typedef struct
            {
                uint32_t service_id;            /**< service ID */
                uint8_t status;                 /**< type of request */
                LogInfoType log_info_type;      /**< log info type */
                char com[DLT_ID_SIZE];      /**< communication interface */
             } DltServiceGetLogInfoResponse;
            """

            _fields_ = [
                ("service_id", c_uint32),
                ("status", c_uint8),
                ("log_info_type", LogInfoType),
                ("com", c_char * DLT_ID_SIZE),
            ]

        class DltStorageHeader(Structure):
            """
            typedef struct
            {
                char pattern[DLT_ID_SIZE];        /**< This pattern should be DLT0x01 */
                uint32_t seconds;                    /**< seconds since 1.1.1970 */
                int32_t microseconds;            /**< Microseconds */
                char ecu[DLT_ID_SIZE];            /**< The ECU id is added, if it is not already in the DLT message itself */
            } PACKED DltStorageHeader;
            """

            _fields_ = [
                ("pattern", c_char * DLT_ID_SIZE),
                ("seconds", c_uint32),
                ("microseconds", c_int32),
                ("ecu", c_char * DLT_ID_SIZE),
            ]

            def __reduce__(self):
                return (
                    DltStorageHeader,
                    (self.pattern, self.seconds, self.microseconds, self.ecu),
                )

        class DltStandardHeader(BigEndianStructure):
            """
            typedef struct
            {
                uint8_t htyp;           /**< This parameter contains several information, see definitions below */
                uint8_t mcnt;           /**< The message counter is increased with each sent DLT message */
                uint16_t len;           /**< Length of the complete message, without storage header */
            } PACKED DltStandardHeader;
            """

            _fields_ = [
                ("htyp", c_uint8),
                ("mcnt", c_uint8),
                ("len", c_ushort),
            ]

            def __reduce__(self):
                return (DltStandardHeader, (self.htyp, self.mcnt, self.len))

        class DltExtendedHeader(Structure):
            """
            typedef struct
            {
                uint8_t msin;          /**< message info */
                uint8_t noar;          /**< number of arguments */
                char apid[DLT_ID_SIZE];          /**< application id */
                char ctid[DLT_ID_SIZE];          /**< context id */
            } PACKED DltExtendedHeader;
            """

            _fields_ = [
                ("msin", c_uint8),
                ("noar", c_uint8),
                ("apid", c_char * DLT_ID_SIZE),
                ("ctid", c_char * DLT_ID_SIZE),
            ]

            def __reduce__(self):
                return (
                    DltExtendedHeader,
                    (self.msin, self.noar, self.apid, self.ctid),
                )

        class DltStandardHeaderExtra(Structure):
            """
            typedef struct
            {
                char ecu[DLT_ID_SIZE];       /**< ECU id */
                uint32_t seid;     /**< Session number */
                uint32_t tmsp;     /**< Timestamp since system start in 0.1 milliseconds */
            } PACKED DltStandardHeaderExtra;
            """

            _fields_ = [
                ("ecu", c_char * DLT_ID_SIZE),
                ("seid", c_uint32),
                ("tmsp", c_uint32),
            ]

            def __reduce__(self):
                return (
                    DltStandardHeaderExtra,
                    (self.ecu, self.seid, self.tmsp),
                )

        class DLTMessage(Structure):
            """
            typedef struct sDltMessage
            {
                /* flags */
                int8_t found_serialheader;

                /* offsets */
                int32_t resync_offset;

                /* size parameters */
                int32_t headersize;  /**< size of complete header including storage header */
                int32_t datasize;  /**< size of complete payload */

                /* buffer for current loaded message */
                uint8_t headerbuffer[
                    sizeof(DltStorageHeader)+
                    sizeof(DltStandardHeader)+
                    sizeof(DltStandardHeaderExtra)+
                    sizeof(DltExtendedHeader)];
                    /**< buffer for loading complete header */
                uint8_t *databuffer;  /**< buffer for loading payload */
                int32_t databuffersize;

                /* header values of current loaded message */
                DltStorageHeader       *storageheader;  /**< pointer to storage header of current loaded header */
                DltStandardHeader      *standardheader; /**< pointer to standard header of current loaded header */
                DltStandardHeaderExtra headerextra;     /**< extra parameters of current loaded header */
                DltExtendedHeader      *extendedheader; /**< pointer to extended of current loaded header */
            } DltMessage;
            """

            _fields_ = [
                ("found_serialheader", c_int8),
                ("resync_offset", c_int32),
                ("headersize", c_int32),
                ("datasize", c_int32),
                (
                    "headerbuffer",
                    c_uint8
                    * (
                        sizeof(DltStorageHeader)
                        + sizeof(DltStandardHeader)
                        + sizeof(DltStandardHeaderExtra)
                        + sizeof(DltExtendedHeader)
                    ),
                ),
                ("databuffer", POINTER(c_uint8)),
                ("databuffersize", c_int32),
                ("storageheader", POINTER(DltStorageHeader)),
                ("standardheader", POINTER(DltStandardHeader)),
                ("headerextra", DltStandardHeaderExtra),
                ("extendedheader", POINTER(DltExtendedHeader)),
            ]

        class DLTFilter(Structure):
            """
            typedef struct
            {
                char apid[DLT_FILTER_MAX][DLT_ID_SIZE]; /**< application id */
                char ctid[DLT_FILTER_MAX][DLT_ID_SIZE]; /**< context id */
                int  counter;                           /**< number of filters */
            } DltFilter;
            """

            _fields_ = [
                ("apid", (c_char * DLT_ID_SIZE) * DLT_FILTER_MAX),
                ("ctid", (c_char * DLT_ID_SIZE) * DLT_FILTER_MAX),
                ("counter", c_int),
            ]

        class DLTFile(Structure):
            """
            typedef struct sDltFile
            {
                /* file handle and index for fast access */
                FILE *handle;      /**< file handle of opened DLT file */
                long *index;       /**< file positions of all DLT messages for fast access to file, only filtered messages */

                /* size parameters */
                int32_t counter;       /**< number of messages in DLT file with filter */
                int32_t counter_total; /**< number of messages in DLT file without filter */
                int32_t position;      /**< current index to message parsed in DLT file starting at 0 */
                long file_length;  /**< length of the file */
                long file_position; /**< current position in the file */

                /* error counters */
                int32_t error_messages; /**< number of incomplete DLT messages found during file parsing */

                /* filter parameters */
                DltFilter *filter;  /**< pointer to filter list. Zero if no filter is set. */
                int32_t filter_counter; /**< number of filter set */

                /* current loaded message */
                DltMessage msg;     /**< pointer to message */

            } DltFile;
            """

            _fields_ = [
                ("handle", POINTER(c_int)),
                ("index", POINTER(c_long)),
                ("counter", c_int32),
                ("counter_total", c_int32),
                ("position", c_int32),
                ("file_length", c_long),
                ("file_position", c_long),
                ("error_messages", c_int32),
                ("filter", POINTER(DLTFilter)),
                ("filter_counter", c_int32),
                ("msg", DLTMessage),
            ]

        # load DLT library #
        try:
            if "LIBDLT" in SysMgr.environList:
                libpath = SysMgr.environList["LIBDLT"][0]
                SysMgr.dltObj = SysMgr.loadLib(libpath, True)
            elif not SysMgr.dltObj:
                SysMgr.dltObj = SysMgr.loadLib(SysMgr.libdltPath)

            if not SysMgr.dltObj:
                raise Exception("no DLT library")

            dltObj = SysMgr.dltObj
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.dltObj = None
            SysMgr.printWarn(
                "failed to find %s to get DLT logs" % SysMgr.libdltPath,
                always=True,
                reason=True,
            )
            sys.exit(-1)

        # define verbose variable #
        if SysMgr.warnEnable:
            # set log level to DEBUG #
            if hasattr(dltObj, "dlt_log_set_level"):
                dltObj.dlt_log_set_level(LogMgr.LOG_DEBUG)

            verb = 1
        else:
            verb = 0

        # get socket object #
        socket = SysMgr.getPkg("socket")

        try:
            from socket import SOL_SOCKET, SO_RCVBUF, create_connection
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to import socket", True)
            sys.exit(-1)

        # define dlt functions #
        if not SysMgr.isWindows:
            dltObj.dlt_register_app.argtypes = [c_char_p, c_char_p]
            dltObj.dlt_register_app.restype = c_int

            dltObj.dlt_register_context.argtypes = [
                c_void_p,
                c_char_p,
                c_char_p,
            ]
            dltObj.dlt_register_context.restype = c_int

            dltObj.dlt_log_string.argtypes = [c_void_p, c_int, c_char_p]
            dltObj.dlt_log_string.restype = c_int

            dltObj.dlt_filter_init.argtypes = [c_void_p, c_int]
            dltObj.dlt_filter_init.restype = c_int

            dltObj.dlt_log_set_level.argtypes = [c_int]
            dltObj.dlt_log_set_level.restype = None

            dltObj.dlt_client_init.argtypes = [c_void_p, c_int]
            dltObj.dlt_client_init.restype = c_int

            dltObj.dlt_client_cleanup.argtypes = [c_void_p, c_int]
            dltObj.dlt_client_cleanup.restype = c_int

            dltObj.dlt_client_send_all_log_level.argtypes = [c_void_p, c_int8]
            dltObj.dlt_client_send_all_log_level.restype = c_int

            dltObj.dlt_client_get_log_info.argtypes = [c_void_p]
            dltObj.dlt_client_get_log_info.restype = c_int

            dltObj.dlt_client_main_loop.argtypes = [c_void_p, c_void_p, c_int]
            dltObj.dlt_client_main_loop.restype = c_int

        dltObj.dlt_filter_add.argtypes = [c_void_p, c_char_p, c_char_p, c_int]
        dltObj.dlt_filter_add.restype = c_int

        dltObj.dlt_file_set_filter.argtypes = [c_void_p, c_void_p, c_int]
        dltObj.dlt_file_set_filter.restype = c_int

        dltObj.dlt_file_init.argtypes = [c_void_p, c_int]
        dltObj.dlt_file_init.restype = c_int

        dltObj.dlt_file_open.argtypes = [c_void_p, c_char_p, c_int]
        dltObj.dlt_file_open.restype = c_int

        dltObj.dlt_file_read.argtypes = [c_void_p, c_int]
        dltObj.dlt_file_read.restype = c_int

        dltObj.dlt_file_message.argtypes = [c_void_p, c_int, c_int]
        dltObj.dlt_file_message.restype = c_int

        dltObj.dlt_file_free.argtypes = [c_void_p, c_int]
        dltObj.dlt_file_free.restype = c_int

        dltObj.dlt_receiver_init.argtypes = [c_void_p, c_int, c_int, c_int]
        dltObj.dlt_receiver_init.restype = c_int

        dltObj.dlt_get_version.argtypes = [c_char_p, c_int]
        dltObj.dlt_get_version.restype = None

        dltObj.dlt_message_init.argtypes = [c_void_p, c_int]
        dltObj.dlt_message_init.restype = c_int

        dltObj.dlt_message_read.argtypes = [
            c_void_p,
            c_void_p,
            c_uint,
            c_int,
            c_int,
        ]
        dltObj.dlt_message_read.restype = c_int

        dltObj.dlt_receiver_move_to_begin.argtypes = [c_void_p]
        dltObj.dlt_receiver_move_to_begin.restype = c_int

        dltObj.dlt_receiver_remove.argtypes = [c_void_p, c_int]
        dltObj.dlt_receiver_remove.restype = c_int

        dltObj.dlt_message_print_ascii.argtypes = [
            c_void_p,
            c_void_p,
            c_uint32,
            c_int,
        ]
        dltObj.dlt_message_print_ascii.restype = c_int

        dltObj.dlt_set_storageheader.argtypes = [c_void_p, c_char_p]
        dltObj.dlt_set_storageheader.restype = c_int

        dltObj.dlt_message_free.argtypes = [c_void_p, c_int]
        dltObj.dlt_message_free.restype = c_int

        # define default variables #
        msg = DLTMessage()
        dltFile = DLTFile()
        dltFilter = DLTFilter()
        buf = create_string_buffer(b"\000" * DltAnalyzer.DLT_DAEMON_TEXTSIZE)

        # initialize input path #
        flist = []
        if SysMgr.hasMainArg():
            args = SysMgr.getMainArg()
            for item in args.split(","):
                ret = UtilMgr.convPath(item.strip())
                flist += ret
            flist = UtilMgr.cleanItem(flist)
            if not flist:
                SysMgr.printErr("wrong path '%s' for DLT files" % args)
                sys.exit(-1)
        elif SysMgr.inputParam:
            for item in SysMgr.inputParam.split(","):
                ret = UtilMgr.convPath(item.strip())
                flist += ret
            flist = UtilMgr.cleanItem(flist)
            if not flist:
                SysMgr.printErr(
                    "wrong path '%s' for DLT files" % SysMgr.inputParam
                )
                sys.exit(-1)

        # check sort option #
        if SysMgr.findOption("S"):
            buffered = True
            SysMgr.streamEnable = False
        else:
            buffered = False

        # set colors for each message types #
        for item in DltAnalyzer.LOGINFO:
            if item in ("fatal", "error"):
                color = "RED"
            elif item == "info":
                color = "CYAN"
            elif item == "debug":
                color = "BLUE"
            elif item == "warn":
                color = "PINK"
            else:
                color = "DEFAULT"

            DltAnalyzer.msgColorList.append(
                UtilMgr.convColor(item, color, 5, "left")
            )

        # initialize task stat #
        try:
            DltAnalyzer.dbgObj = Debugger(SysMgr.pid, attach=False)
            DltAnalyzer.dbgObj.initValues()
            DltAnalyzer.dbgObj.getCpuUsage(system=True)
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn("failed to read CPU usage", reason=True)

        # get log level option #
        try:
            if SysMgr.customCmd:
                val = SysMgr.customCmd[0].upper()
                level = DltAnalyzer.LOGLEVEL[val]
            else:
                level = None
        except:
            SysMgr.printErr("failed to recognize log level", True)
            sys.exit(-1)

        # get level filtering option #
        fixLevel = "FIXLEVEL" in SysMgr.environList

        # get DLT version #
        try:
            vbuf = (c_char * 256)()
            dltObj.dlt_get_version(vbuf, c_int(255))
            vstr = memoryview(vbuf).tobytes().decode().split("\x00")[0]
            verstr = " ".join(vstr.split("\n"))

            SysMgr.printInfo(verstr)

            ver = UtilMgr.lstrip(verstr, "DLT Package Version: ")
            DltAnalyzer.version = tuple(
                map(int, ver[: ver.find(" ")].split("."))
            )
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn("failed to get DLT version", reason=True)

        # set new data structures (>=2.18.6) #
        legacy = True
        try:
            if not DltAnalyzer.version:
                raise Exception("no version")
            elif DltAnalyzer.version >= (2, 18, 6):
                legacy = False
                raise Exception("recent version")

            # dlt_receiver_init #
            dltObj.dlt_receiver_init.argtypes = [c_void_p, c_int, c_int]
            dltObj.dlt_receiver_init.restype = c_int

            def _popFields(struct, items):
                popList = []
                for idx, item in enumerate(struct._fields_):
                    if item[0] in items:
                        popList.append(idx)
                for idx in reversed(popList):
                    struct._fields_.pop(idx)

            # DltReceiver #
            _popFields(DltReceiver, ("addr", "type"))

            # DltClient #
            _popFields(
                DltClient,
                ("hostIP", "send_serial_header", "resync_serial_header"),
            )
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn(
                "failed to set legacy data structures", reason=True
            )

        # set flag and print mode #
        quitFlag = False
        if mode == "top":
            # set handler for exit #
            signal.signal(signal.SIGINT, SysMgr.exitHandler)
            if SysMgr.outPath:
                SysMgr.addExitFunc(DltAnalyzer.onAlarm, [0, 0])
            SysMgr.addExitFunc(SysMgr.stopHandler, [0, 0])

            SysMgr.printInfo("start collecting DLT logs... [ STOP(Ctrl+c) ]")
        elif mode == "print":
            quitFlag = SysMgr.findOption("Q")

            SysMgr.printInfo("start printing DLT logs... [ STOP(Ctrl+c) ]\n")

        # get item for log filter #
        if "WATCHLOG" in SysMgr.environList:
            watchcond = SysMgr.environList["WATCHLOG"][0].split("+")
        else:
            watchcond = None

        # save timestamp #
        SysMgr.updateUptime()

        # get times for tail and until option #
        if "USEUPTIME" in SysMgr.environList:
            current = UtilMgr.getClockTime(dlt=True)
            currentDiff = current - SysMgr.getUptime()
            useUptime = True
        else:
            current = time.time()
            currentDiff = current - time.time()
            useUptime = False
        since, until = SysMgr.getTimeValues(["TAIL", "UNTIL"], current)

        # initialize dlt-daemon info #
        origShowAll = SysMgr.showAll
        SysMgr.showAll = True
        SysMgr.cmdlineEnable = True
        procInfo = DltAnalyzer.procInfo = TaskAnalyzer(onlyInstance=True)
        for pid in DltAnalyzer.pids:
            procInfo.saveProcData(None, pid)
            procInfo.saveCmdlineData(None, pid)

        # convert since to DLT timestamp unit #
        if not since and mode != "print" and not origShowAll:
            since = current
        if since:
            if since < 0:
                since += current
            if False and since != current:
                since += currentDiff

        # convert until to DLT timestamp unit #
        if until:
            if until < 0:
                until += current
            if False and until != current:
                until += currentDiff
        elif quitFlag:
            until = current

        # load daemon configs #
        DltAnalyzer.loadDaemonConfig()

        # get trace file list #
        findMode = False
        targetFileList = []
        checkTraceFile = False
        traceFileList = DltAnalyzer.getTraceFileList()
        if "LASTFILE" in SysMgr.environList:
            checkTraceFile = True
            if traceFileList:
                flist = [traceFileList[-1]]
        elif "FINDFILE" in SysMgr.environList:
            checkTraceFile = True
            if since == until == 0:
                SysMgr.printErr("no time info")
                sys.exit(-1)
            elif traceFileList:
                traceFileList.reverse()
                flist = traceFileList
                findMode = True

                if since == 0:
                    since = current
                elif until == 0:
                    until = current
        elif "TRACEFILE" in SysMgr.environList:
            flist = traceFileList

        # check trace file list #
        if checkTraceFile and not traceFileList:
            SysMgr.printErr("failed to find trace file")
            sys.exit(-1)

        # get message count limit #
        msgLimit = UtilMgr.getEnvironNum("NRMSG", False, 0, False, True)
        fileLimit = UtilMgr.getEnvironNum("NRFILE", False, 0, False, True)

        # messages from file #
        if mode == "print" and flist:
            # set print mode #
            if "PRINTSUMMARY" in SysMgr.environList:
                printMode = "top"
                if not SysMgr.findOption("Q"):
                    SysMgr.streamEnable = False
                DltAnalyzer.procInfo = TaskAnalyzer(onlyInstance=True)
                action = "summarizing"
            else:
                printMode = mode
                action = "printing"

            # set print filename flag #
            if "PRINTPATH" in SysMgr.environList:
                printName = True
                hasFullPath = True
            elif "PRINTFILE" in SysMgr.environList:
                printName = True
                hasFullPath = False
            elif findMode:
                printName = False
                hasFullPath = True
            else:
                printName = False
                hasFullPath = False

            fileCnt = 0
            startTimeList = {}
            removeList = []

            for fidx, path in enumerate(flist):
                # init skip file flag #
                skipFile = False

                # check file type #
                if not os.path.isfile(path):
                    SysMgr.printWarn(
                        "failed to open '%s' because it is not file" % path,
                        True,
                    )
                    continue

                # decompress file #
                if "DECOMP" in SysMgr.environList:
                    removeItems = SysMgr.doDecompress(args=[path], verb=False)
                    if removeList:
                        removeList += removeItems
                        # TODO: support multiple compressed files #
                        path = removeList[0]

                # get file size #
                fsize = UtilMgr.getFileSizeStr(path)

                SysMgr.printInfo(
                    "start %s DLT logs from '%s'%s..." % (action, path, fsize)
                )

                # convert path string to utf-8 format #
                pathOrig = path if hasFullPath else os.path.basename(path)
                path = UtilMgr.encodeStr(path)

                # initialize file object #
                ret = dltObj.dlt_file_init(byref(dltFile), verb)
                if ret < 0:
                    SysMgr.printErr("failed to initialize a DLTFile object")

                # set filter #
                # _setFilter(dltObj, dltFilter, dltFile, apid=b"", ctid=b"", init=True)

                # open the file #
                ret = dltObj.dlt_file_open(byref(dltFile), path, verb)
                if ret != 0:
                    SysMgr.printErr("failed to open '%s'" % pathOrig)
                    continue
                elif dltFile.file_length == 0:
                    SysMgr.printErr(
                        "failed to read '%s' because size is 0" % pathOrig
                    )
                    continue

                # read the file #
                index = 0
                msgCnt = 0
                while dltFile.file_position < dltFile.file_length:
                    # read item #
                    ret = dltObj.dlt_file_read(byref(dltFile), verb)

                    # storage header corrupted #
                    if ret < 0:
                        nextHeaderPos = _findNextHeader(
                            path, dltFile.file_position
                        )
                        if nextHeaderPos is None:
                            SysMgr.printWarn(
                                "wrong DLT storage header from '%s'"
                                % pathOrig,
                                True,
                            )
                            break
                        elif dltFile.file_position == nextHeaderPos:
                            break
                        else:
                            dltFile.file_position = nextHeaderPos
                            continue
                    else:
                        msgCnt += 1

                    # check file scope #
                    if findMode:
                        if msgCnt == 1:
                            ret = dltObj.dlt_file_message(
                                byref(dltFile), 0, verb
                            )
                            if useUptime:
                                fileStart = (
                                    dltFile.msg.headerextra.tmsp / float(10000)
                                )
                            else:
                                header = dltFile.msg.storageheader.contents
                                fileStart = (
                                    header.seconds
                                    + float(header.microseconds) / 1000000
                                )
                            startTimeList[fidx] = fileStart

                            # scope exceed #
                            if until < fileStart:
                                skipFile = True

                            break

                    if findMode or SysMgr.outPath or printMode == "top":
                        # print progress #
                        UtilMgr.printProgress(
                            dltFile.file_position, dltFile.file_length
                        )

                    # read message #
                    ret = dltObj.dlt_file_message(byref(dltFile), index, verb)
                    index += 1
                    if ret < 0:
                        if ret in DltAnalyzer.RETURNTYPE:
                            errstr = (
                                " because of %s" % DltAnalyzer.RETURNTYPE[ret]
                            )
                        else:
                            errstr = ""

                        SysMgr.printWarn(
                            "failed to read %s message from '%s'%s"
                            % (index, pathOrig, errstr),
                            True,
                        )

                        if ret < -1:
                            break
                        else:
                            continue

                    # check log filter #
                    if level:
                        mlevel = DltAnalyzer.getMsgLogLevel(dltFile.msg)
                        if fixLevel:
                            if mlevel != level:
                                continue
                        else:
                            if mlevel > level:
                                continue

                    # print message #
                    DltAnalyzer.handleMessage(
                        dltObj,
                        dltFile.msg,
                        buf,
                        printMode,
                        verb,
                        buffered,
                        since=since,
                        until=until,
                        fname=pathOrig,
                        printsrc=printName,
                    )

                    # check message limit #
                    if msgLimit:
                        if msgCnt >= msgLimit:
                            break

                if findMode or SysMgr.outPath or printMode == "top":
                    UtilMgr.deleteProgress()

                # check skip file flag #
                if skipFile:
                    continue

                if findMode:
                    # start time #
                    ret = dltObj.dlt_file_message(byref(dltFile), 0, verb)

                    if useUptime:
                        fileStart = dltFile.msg.headerextra.tmsp / float(10000)
                    else:
                        header = dltFile.msg.storageheader.contents
                        fileStart = (
                            header.seconds
                            + float(header.microseconds) / 1000000
                        )

                    # end time #
                    if fidx == 0:
                        if "SETCURRENT" in SysMgr.environList:
                            try:
                                current = SysMgr.environList["SETCURRENT"][0]
                                fileEnd = float(current)
                            except SystemExit:
                                sys.exit(0)
                            except:
                                SysMgr.printErr(
                                    "failed to set current to '%s'" % current,
                                    True,
                                )
                                sys.exit(-1)
                        else:
                            fileEnd = current
                    else:
                        fileEnd = startTimeList[fidx - 1]

                    # check stop condition #
                    if fileEnd < since:
                        break

                    # get clock time #
                    clockTime = time.strftime(
                        "%Y-%m-%d %H:%M:%S",
                        time.localtime(
                            dltFile.msg.storageheader.contents.seconds
                        ),
                    )

                    # add to list #
                    targetFileList.append((pathOrig, fileStart, clockTime))

                    # check last boundary #
                    if fileStart < since and fileEnd > until:
                        break
                    else:
                        continue

                # check file limit #
                if fileLimit:
                    fileCnt += 1
                    if fileCnt >= fileLimit:
                        break

                # free file object #
                ret = dltObj.dlt_file_free(byref(dltFile), verb)
                if ret < 0:
                    SysMgr.printErr("failed to free a DLTFile object")

            # check no target file #
            if findMode:
                # print total CPU usage #
                try:
                    cpu, _, _ = SysMgr.getTotalUsage(SysMgr.pid)
                    cpustr = " and used CPU %s%% totally" % UtilMgr.convNum(
                        cpu
                    )
                except SystemExit:
                    sys.exit(0)
                except:
                    cpustr = ""

                if SysMgr.jsonEnable:
                    jsonList = {
                        "cpu": cpu,
                        "list": list(sorted(targetFileList)),
                        "start": since,
                        "end": until,
                    }

                    SysMgr.printPipe(
                        UtilMgr.convDict2Str(
                            jsonList, pretty=not SysMgr.findOption("Q")
                        )
                    )

                    sys.exit(0)

                if targetFileList:
                    SysMgr.printPipe(
                        "\nFound below files in %.3f ~ %.3f%s"
                        % (since, until, cpustr)
                    )
                    for i, f in enumerate(sorted(targetFileList)):
                        SysMgr.printPipe(
                            "[%3s] %s (%s / %.6f ~)"
                            % (
                                i,
                                UtilMgr.convColor(f[0], "YELLOW"),
                                f[2],
                                f[1],
                            )
                        )
                    sys.exit(0)
                else:
                    SysMgr.printErr(
                        "failed to find file in %.3f ~ %.3f%s"
                        % (since, until, cpustr)
                    )
                    sys.exit(-1)

            # print summary #
            if printMode == "top":
                SysMgr.printPipe()
                DltAnalyzer.dbgObj = None
                DltAnalyzer.printIntervalSummary(force=True)

            # handle buffered logs #
            if buffered:
                output = SysMgr.getPrintList(retStr=True, sort=True)
                SysMgr.clearPrint()
                SysMgr.printPipe(output, flush=True)

            # remove decompressed file #
            SysMgr.removeFiles(removeList)

            # finish file mode #
            return

        # check dlt-daemon #
        DltAnalyzer.pids = SysMgr.getProcPids("dlt-daemon")
        if not DltAnalyzer.pids and not SysMgr.remoteServObj:
            SysMgr.printWarn("failed to find dlt-daemon process", True)

        # set connection info #
        try:
            if SysMgr.remoteServObj:
                servIp = SysMgr.remoteServObj.ip
                port = long(SysMgr.remoteServObj.port)
                if port == SysMgr.defaultServPort:
                    servPort = 3490
                else:
                    servPort = port
            else:
                servIp = "127.0.0.1"
                servPort = 3490

            SysMgr.printInfo("use %s:%s as dlt-daemon" % (servIp, servPort))
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to get the address of dlt-daemon", True)
            sys.exit(-1)

        # get retry interval #
        retry = 0
        if "RETRYCONN" in SysMgr.environList:
            value = SysMgr.environList["RETRYCONN"][0]
            if value == "SET":
                retry = 1
            else:
                try:
                    retry = float(value) / 1000
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr(
                        "failed to convert %s to retry connection interval"
                        % value
                    )
                    sys.exit(-1)

        # connect to server #
        while 1:
            try:
                servIpStr = string_at(servIp.encode())
                connSock = create_connection((servIpStr, servPort), timeout=1)
                if not connSock:
                    raise Exception("no connection")

                # set blocking #
                connSock.setblocking(1)  # pylint: disable=no-member
                connSock.settimeout(1)  # pylint: disable=no-member

                # increase recv buffer #
                try:
                    connSock.setsockopt(SOL_SOCKET, SO_RCVBUF, 1 << 20)
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

                break
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr(
                    "failed to connect to dlt-daemon with %s:%s"
                    % (servIp, servPort),
                    True,
                )

                # retry #
                if retry:
                    time.sleep(retry)
                else:
                    sys.exit(-1)

        # initialize client #
        dltClient = DltClient()
        dltObj.dlt_client_init(byref(dltClient), verb)
        sockno = c_int(connSock.fileno())  # pylint: disable=no-member
        dltClient.sock = sockno
        dltClient.receiver.fd = sockno
        # dltObj.dlt_client_cleanup(byref(dltClient), verb)

        # change default log level #
        try:
            if level:
                dltObj.dlt_client_send_all_log_level(byref(dltClient), level)
        except:
            SysMgr.printErr("failed to set %s to default log level" % val)
            sys.exit(-1)

        # print log level #
        try:
            ret = dltObj.dlt_client_get_log_info(byref(dltClient))
            if ret == 0:
                resp = DltServiceGetLogInfoResponse()
                resp.service_id = DltAnalyzer.SERVICEID[
                    "DLT_SERVICE_ID_GET_LOG_INFO"
                ]
                resp.status = DltAnalyzer.SERVICERESPONSE[
                    "DLT_SERVICE_RESPONSE_ERROR"
                ]
                dltObj.dlt_client_main_loop(byref(dltClient), byref(resp), 0)
                appids = resp.log_info_type.count_app_ids
                for idx in xrange(appids):
                    app = resp.log_info_type.app_ids[idx]
                    for num in xrange(app.count_context_ids):
                        con = app.context_id_info[num]
                        SysMgr.printPipe(
                            "[%s] [%s] %s"
                            % (app.app_id, con.context_id, con.log_level)
                        )
        except:
            SysMgr.printWarn("failed to print log level", reason=True)

        # initialize receiver #
        dltReceiver = dltClient.receiver

        # initialize connection #
        try:
            nrConnSock = connSock.fileno()  # pylint: disable=no-member
            # pylint: disable-next=no-member
            RECVBUFSIZE = connSock.getsockopt(SOL_SOCKET, SO_RCVBUF)

            if legacy:
                ret = dltObj.dlt_receiver_init(
                    byref(dltReceiver), c_int(nrConnSock), c_int(RECVBUFSIZE)
                )
            else:
                ret = dltObj.dlt_receiver_init(
                    byref(dltReceiver),
                    c_int(nrConnSock),
                    c_int(0),  # 0: SOCKET / 1: UDP / 2: FD #
                    c_int(RECVBUFSIZE),
                )
            if ret < 0:
                SysMgr.printErr("failed to initialize DLT receiver")
                sys.exit(-1)
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to initialize connection", True)
            sys.exit(-1)

        # define receiver symbol #
        try:
            if hasattr(dltObj, "dlt_receiver_receive_socket"):
                dltObj.dlt_receiver_receive_socket.argtypes = [c_void_p]
                dltObj.dlt_receiver_receive_socket.restype = c_int
                dlt_receiver_receive = dltObj.dlt_receiver_receive_socket
                verbVal = c_int(0)
            elif hasattr(dltObj, "dlt_receiver_receive"):
                dltObj.dlt_receiver_receive.argtypes = [c_void_p, c_int]
                dltObj.dlt_receiver_receive.restype = c_int
                dlt_receiver_receive = dltObj.dlt_receiver_receive
                verbVal = c_int(1)
            else:
                raise Exception("no DLT receiver")
        except:
            SysMgr.printErr("failed to get dlt_receiver_receive symbol")
            sys.exit(-1)

        # save timestamp #
        SysMgr.updateUptime()

        # set timer #
        signal.signal(signal.SIGALRM, DltAnalyzer.onAlarm)
        SysMgr.updateTimer()

        # define status variables #
        tryCnt = 0
        read1stMsg = False

        while 1:
            try:
                # initialize the message buffer #
                ret = dltObj.dlt_message_init(byref(msg), verb)
                if ret < 0:
                    SysMgr.printErr(
                        "failed to initialize the DLT message buffer"
                    )
                    sys.exit(-1)

                # read DLT data from daemon #
                try:
                    ret = dlt_receiver_receive(byref(dltReceiver), verbVal)
                    if ret <= 0:
                        if quitFlag:
                            tryCnt += 1
                            if tryCnt > 5:
                                sys.exit(0)

                        time.sleep(0.1)
                        continue
                    else:
                        tryCnt = 0
                except SystemExit:
                    sys.exit(0)
                except:
                    sys.exit(-1)

                # check DLT data to be read #
                while 1:
                    ret = dltObj.dlt_message_read(
                        byref(msg),
                        cast(dltReceiver.buf, POINTER(c_uint8)),
                        c_uint(dltReceiver.bytesRcvd),
                        c_int(0),
                        c_int(verb),
                    )
                    if ret != 0:
                        """
                        handle error return
                        DLT_MESSAGE_ERROR_UNKNOWN (-1)
                        DLT_MESSAGE_ERROR_SIZE    (-2)
                        DLT_MESSAGE_ERROR_CONTENT (-3)
                        """

                        # move receiver buffer pointer to start of the buffer #
                        ret = dltObj.dlt_receiver_move_to_begin(
                            byref(dltReceiver)
                        )
                        if ret < 0:
                            SysMgr.printErr(
                                "failed to move the pointer of receiver buffer"
                            )
                            sys.exit(-1)

                        break

                    # get data size to be removed #
                    size = (
                        msg.headersize
                        + msg.datasize
                        - sizeof(DltStorageHeader)
                    )
                    if msg.found_serialheader:
                        size += DLT_ID_SIZE

                    # remove message from buffer #
                    if (
                        dltObj.dlt_receiver_remove(byref(dltReceiver), size)
                        < 0
                    ):
                        SysMgr.printErr("failed to remove data from buffer")
                        sys.exit(-1)

                    # check log filter #
                    if not read1stMsg:
                        read1stMsg = True
                        continue
                    elif level and DltAnalyzer.getMsgLogLevel(msg) > level:
                        continue

                    # print DLT message #
                    if verb:
                        dltObj.dlt_message_print_ascii(
                            byref(msg),
                            byref(buf),
                            c_uint32(msg.headersize),
                            c_int(verb),
                        )

                    # set storage info #
                    if msg.standardheader.contents.htyp & DLT_HTYP_WEID:
                        dltObj.dlt_set_storageheader(
                            msg.storageheader, msg.headerextra.ecu
                        )
                    else:
                        dltObj.dlt_set_storageheader(
                            msg.storageheader, c_char_p("".encode())
                        )

                    # handle a message #
                    DltAnalyzer.handleMessage(
                        dltObj,
                        msg,
                        buf,
                        mode,
                        verb,
                        cond=watchcond,
                        since=since,
                        until=until,
                    )

                    # check message limit #
                    if msgLimit:
                        msgCnt += 1
                        if msgCnt >= msgLimit:
                            return
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printWarn(
                    "failed to process DLT message", True, reason=True
                )
                continue

        # free message #
        dltObj.dlt_message_free(byref(msg), verb)


class Debugger(object):
    """Debugger for ptrace"""

    globalEvent = None
    gLockObj = None
    gLockPath = None
    dbgInstance = None
    tracerInstance = None
    dbusEnable = False
    RETSTR = None

    targetNums = {}
    ignoreSignals = {}
    cpuCond = -1
    pyElapsed = -1
    strSize = -1
    envFlags = {
        "COMPLETECALL": False,
        "CONTALONE": False,
        "CONVARG": False,
        "EXCEPTLD": False,
        "EXCEPTWAIT": False,
        "FIXEDBP": False,
        "FIXPYSTATE": False,
        "HIDESYM": False,
        "INCNATIVE": False,
        "INCOVERHEAD": False,
        "INCSECT": False,
        "INCTGINFO": False,
        "INTERCALL": False,
        "NOARG": False,
        "NOFILE": False,
        "UNMUTE": False,
        "NORETBT": False,
        "NOSAMPLECACHE": False,
        "NOSTRIP": False,
        "ONLYFAIL": False,
        "ONLYOK": False,
        "ONLYSYM": False,
        "ONLYWAIT": False,
        "PRINTARG": False,
        "PRINTDIFF": False,
        "PRINTDELAY": False,
        "PRINTFILE": False,
        "PRINTHIST": False,
        "PYSTACK": False,
        "SHOWADDR": False,
        "STDEV": False,
        "SYNCTASK": False,
        "TIMELINE": False,
        "TRACEBP": False,
        "WAITCLONE": False,
        "WAITWAKEUP": False,
    }

    def getSigStruct(self):
        class _sifields_sigfault_t(Union):
            _fields_ = (("_addr", c_void_p),)

        class _sifields_kill_t(Structure):
            _fields_ = (("pid", c_int), ("uid", c_ushort))

        class _sifields_sigchld_t(Structure):
            _fields_ = (
                ("pid", c_int),
                ("uid", c_ushort),
                ("status", c_int),
                ("utime", c_uint),
                ("stime", c_uint),
            )

        class _sifields_t(Union):
            _fields_ = (
                ("pad", c_char * (128 - 3 * sizeof(c_int))),
                # ("_kill", _sifields_kill_t),
                # ("_timer", _sifields_timer_t),
                ("_sigchld", _sifields_sigchld_t),
                ("_sigfault", _sifields_sigfault_t),
                # ("_rt", _sifields_rt_t),
                # ("_sigpoll", _sifields_sigpoll_t),
            )

        class siginfo(Structure):
            _fields_ = (
                ("si_signo", c_int),
                ("si_errno", c_int),
                ("si_code", c_int),
                ("_sifields", _sifields_t),
            )
            _anonymous_ = ("_sifields",)

        return siginfo()

    def getRegStruct(self):
        class user_regs_struct(Structure):
            def _getdict(struct):  # pylint: disable=no-self-argument
                return dict(
                    (field, getattr(struct, field))
                    for field, _ in struct._fields_
                )

            if self.arch == "aarch64":
                regs = tuple(("x%i" % reg, c_ulong) for reg in xrange(31))
                _fields_ = regs + (
                    ("sp", c_ulong),
                    ("pc", c_ulong),
                    ("ELP_mode", c_ulong),
                    ("RA_SIGN_STATE", c_ulong),
                )
            elif self.arch == "x64":
                _fields_ = (
                    ("r15", c_ulong),
                    ("r14", c_ulong),
                    ("r13", c_ulong),
                    ("r12", c_ulong),
                    ("rbp", c_ulong),
                    ("rbx", c_ulong),
                    ("r11", c_ulong),
                    ("r10", c_ulong),
                    ("r9", c_ulong),
                    ("r8", c_ulong),
                    ("rax", c_ulong),
                    ("rcx", c_ulong),
                    ("rdx", c_ulong),
                    ("rsi", c_ulong),
                    ("rdi", c_ulong),
                    ("orig_rax", c_ulong),
                    ("rip", c_ulong),
                    ("cs", c_ulong),
                    ("eflags", c_ulong),
                    ("rsp", c_ulong),
                    ("ss", c_ulong),
                    ("fs_base", c_ulong),
                    ("gs_base", c_ulong),
                    ("ds", c_ulong),
                    ("es", c_ulong),
                    ("fs", c_ulong),
                    ("gs", c_ulong),
                )
            elif self.arch == "riscv64":
                _fields_ = (
                    ("pc", c_ulong),
                    ("ra", c_ulong),
                    ("sp", c_ulong),
                    ("gp", c_ulong),
                    ("tp", c_ulong),
                    ("t0", c_ulong),
                    ("t1", c_ulong),
                    ("t2", c_ulong),
                    ("s0", c_ulong),
                    ("s1", c_ulong),
                    ("a0", c_ulong),
                    ("a1", c_ulong),
                    ("a2", c_ulong),
                    ("a3", c_ulong),
                    ("a4", c_ulong),
                    ("a5", c_ulong),
                    ("a6", c_ulong),
                    ("a7", c_ulong),
                    ("s2", c_ulong),
                    ("s3", c_ulong),
                    ("s4", c_ulong),
                    ("s5", c_ulong),
                    ("s6", c_ulong),
                    ("s7", c_ulong),
                    ("s8", c_ulong),
                    ("s9", c_ulong),
                    ("s10", c_ulong),
                    ("s11", c_ulong),
                    ("t3", c_ulong),
                    ("t4", c_ulong),
                    ("t5", c_ulong),
                    ("t6", c_ulong),
                )
            elif self.arch == "arm":
                _fields_ = tuple(("r%i" % reg, c_ulong) for reg in xrange(18))
            elif self.arch == "x86":
                _fields_ = (
                    ("ebx", c_ulong),
                    ("ecx", c_ulong),
                    ("edx", c_ulong),
                    ("esi", c_ulong),
                    ("edi", c_ulong),
                    ("ebp", c_ulong),
                    ("eax", c_ulong),
                    ("ds", c_ushort),
                    ("__ds", c_ushort),
                    ("es", c_ushort),
                    ("__es", c_ushort),
                    ("fs", c_ushort),
                    ("__fs", c_ushort),
                    ("gs", c_ushort),
                    ("__gs", c_ushort),
                    ("orig_eax", c_ulong),
                    ("eip", c_ulong),
                    ("cs", c_ushort),
                    ("__cs", c_ushort),
                    ("eflags", c_ulong),
                    ("esp", c_ulong),
                    ("ss", c_ushort),
                    ("__ss", c_ushort),
                )
            elif self.arch == "powerpc":
                _fields_ = (
                    ("gpr0", c_ulong),
                    ("gpr1", c_ulong),
                    ("gpr2", c_ulong),
                    ("gpr3", c_ulong),
                    ("gpr4", c_ulong),
                    ("gpr5", c_ulong),
                    ("gpr6", c_ulong),
                    ("gpr7", c_ulong),
                    ("gpr8", c_ulong),
                    ("gpr9", c_ulong),
                    ("gpr10", c_ulong),
                    ("gpr11", c_ulong),
                    ("gpr12", c_ulong),
                    ("gpr13", c_ulong),
                    ("gpr14", c_ulong),
                    ("gpr15", c_ulong),
                    ("gpr16", c_ulong),
                    ("gpr17", c_ulong),
                    ("gpr18", c_ulong),
                    ("gpr19", c_ulong),
                    ("gpr20", c_ulong),
                    ("gpr21", c_ulong),
                    ("gpr22", c_ulong),
                    ("gpr23", c_ulong),
                    ("gpr24", c_ulong),
                    ("gpr25", c_ulong),
                    ("gpr26", c_ulong),
                    ("gpr27", c_ulong),
                    ("gpr28", c_ulong),
                    ("gpr29", c_ulong),
                    ("gpr30", c_ulong),
                    ("gpr31", c_ulong),
                    ("nip", c_ulong),
                    ("msr", c_ulong),
                    ("orig_gpr3", c_ulong),
                    ("ctr", c_ulong),
                    ("link", c_ulong),
                    ("xer", c_ulong),
                    ("ccr", c_ulong),
                    ("mq", c_ulong),
                    ("trap", c_ulong),
                    ("dar", c_ulong),
                    ("dsisr", c_ulong),
                    ("result", c_ulong),
                )

        return user_regs_struct()

    def getFpItemRegs(self, ftype, num):
        try:
            ftype = {
                "float": c_float,
                "double": c_double,
                "long double": c_longdouble,
            }[ftype]
        except SystemExit:
            sys.exit(0)
        except:
            return None

        alignSize = sizeof(c_longdouble)

        # cast buffer to 16-byte long-double array #
        length = long(sizeof(self.fpregs) / alignSize)
        buf = cast(byref(self.fpregs), POINTER(c_longdouble * length)).contents

        # return target value #
        offset = addressof(buf) + (num * alignSize)
        count = long(alignSize / sizeof(ftype))
        return cast(offset, POINTER(ftype * count)).contents[0]

    def getFpRegStruct(self):
        class user_fpregs_struct(Structure):
            def _getdict(struct):  # pylint: disable=no-self-argument
                return dict(
                    (field, getattr(struct, field))
                    for field, _ in struct._fields_
                )

            if self.arch.endswith("64"):
                _fields_ = (
                    ("cwd", c_uint16),
                    ("swd", c_uint16),
                    ("ftw", c_uint16),
                    ("fop", c_uint16),
                    ("rip", c_uint64),
                    ("rdp", c_uint64),
                    ("mxcsr", c_uint32),
                    ("mxcr_mask", c_uint32),
                    ("st_space", c_uint32 * 32),
                    ("xmm_space", c_uint32 * 64),
                    ("padding", c_uint32 * 24),
                )
            else:
                _fields_ = (
                    ("cwd", c_ulong),
                    ("swd", c_ulong),
                    ("twd", c_ulong),
                    ("fip", c_ulong),
                    ("fcs", c_ulong),
                    ("foo", c_ulong),
                    ("fos", c_ulong),
                    ("st_space", c_ulong * 20),
                )

        return user_fpregs_struct()

    def updateBreakMode(self):
        # set break mode #
        if self.mode in ("break", "pybreak"):
            self.isBreakMode = True
        else:
            self.isBreakMode = False

    def updateCurrent(self):
        self.current = time.time()

    def __init__(self, pid=None, execCmd=None, attach=True, mode=None):
        self.pthreadid = 0
        self.comm = None
        self.mode = mode
        self.status = "enter"
        self.traceStatus = False
        self.runStatus = True
        self.attached = attach
        self.execCmd = execCmd
        self.arch = SysMgr.getArch()
        self.skipInst = 5
        self.indentLen = 20
        self.errmsg = ""
        self.syscall = ""
        self.bufferedStr = ""
        self.mapFd = None
        self.pmap = None
        self.prevPmap = None
        self.stackFd = None
        self.amap = []
        self.jmapFd = None
        self.jmapPath = None
        self.jmapSize = None
        self.jmapSymTable = []
        self.jmapAddrTable = []
        self.needMapScan = True
        self.watchLd = False
        self.initPtrace = False
        self.initWaitpid = False
        self.initPvr = False
        self.initPvw = False
        self.supportGetRegset = True
        self.supportSetRegset = True
        self.supportProcessVmRd = True
        self.supportProcessVmWr = False
        self.lastSig = None
        self.forked = False
        self.multi = False
        self.errCnt = 0
        self.sampleTime = 0
        self.sampleTimeOrig = 0
        self.sampleTimeMax = 0
        self.incrementalSampling = True
        self.startProfTime = False
        self.showStatus = True
        self.isMtraceMode = SysMgr.checkMode("mtrace")

        # init task number #
        self.myNum = 0
        self.childNum = 0

        # update break mode #
        self.updateBreakMode()

        # set character for word decoding #
        if ConfigMgr.wordSize == 4:
            self.decChar = "I"
        else:
            self.decChar = "Q"

        # define timestamp variables #
        self.current = 0
        self.dstart = 0
        self.dvalue = 0
        self.vdiff = 0
        self.interDiff = 0
        self.updateCurrent()

        self.args = []
        self.values = []
        self.fileList = []
        self.addrList = []
        self.callstack = []
        self.overlayfsList = []
        self.totalCall = 0
        self.syscallAddr = None
        self.syscallFound = True
        self.bpList = {}
        self.bpNewList = {}
        self.entryTime = {}
        self.entryContext = {}
        self.filterList = {}
        self.retCmdList = {}
        self.retFilterList = {}
        self.exceptBpList = {}
        self.targetBpList = {}
        self.targetBpFileList = {}
        self.exceptBpFileList = {}
        self.symbolCacheList = {}
        self.failedAddrList = {}
        self.ldInjected = False
        self.libcLoaded = False
        self.dftBpFileList = {}
        self.dftBpSymList = {
            "mmap": 0,
            "mmap64": 0,
            "munmap": 0,
            "prctl": 0,
            "pthread_setname_np": 0,
        }

        # TODO: Add getBacktrace_RISCV64 #
        self.backtrace = {
            "x86": self.getBacktrace_X86,
            "x64": self.getBacktrace_X86,
            "arm": self.getBacktrace_ARM,
            "aarch64": self.getBacktrace_AARCH64,
            "riscv64": None,
        }
        self.btList = None
        self.btStr = None
        self.btArgList = []
        self.btFilterList = []
        self.btIgnFilterList = []
        self.csBtList = None
        self.csBtSymList = None
        self.csContext = None
        self.prevBtList = None
        self.prevBtStr = None
        self.startAddr = None
        self.prevSym = None
        self.inBacktrace = False

        self.lockObj = None
        self.tempPage = None
        self.sockObj = None

        self.pyAddr = None
        self.pyFrameCache = {}
        self.readPyStr = None
        self.readPyFrame = None
        self.readPyState = None

        self.stack = None
        self.startStack = None
        self.endStack = None
        self.stackSize = 0

        # define timeline #
        self.timelineIdx = {}
        self.timelineData = {"time_unit": "us", "segments": []}
        self.timeDuration = UtilMgr.getEnvironNum(
            "DURATION", False, 100, False
        )

        # set ptrace indexes #
        plist = ConfigMgr.PTRACE_TYPE
        self.attachCmd = plist.index("PTRACE_ATTACH")
        self.peekCmd = plist.index("PTRACE_PEEKTEXT")
        self.pokeCmd = plist.index("PTRACE_POKEDATA")
        self.contCmd = plist.index("PTRACE_CONT")
        self.getregsCmd = plist.index("PTRACE_GETREGS")
        self.getfpregsCmd = plist.index("PTRACE_GETFPREGS")
        self.setregsCmd = plist.index("PTRACE_SETREGS")
        self.syscallCmd = plist.index("PTRACE_SYSCALL")
        self.sysemuCmd = plist.index("PTRACE_SYSEMU")
        self.singlestepCmd = plist.index("PTRACE_SINGLESTEP")
        self.seizeCmd = 0x4206
        self.interruptCmd = 0x4207
        self.listenCmd = 0x4208
        self.stopCmd = None

        # enable SEIZE and INTERRUPT commands #
        if False:
            self.attachCmd = self.seizeCmd
            self.stopCmd = self.interruptCmd

        # set ptrace event indexes #
        pelist = ConfigMgr.PTRACE_EVENT_TYPE
        self.sigExecFlag = (
            signal.SIGTRAP | pelist.index("PTRACE_EVENT_EXEC") << 8
        )
        self.sigCloneFlag = (
            signal.SIGTRAP | pelist.index("PTRACE_EVENT_CLONE") << 8
        )
        self.sigForkFlag = (
            signal.SIGTRAP | pelist.index("PTRACE_EVENT_FORK") << 8
        )
        self.sigVforkFlag = (
            signal.SIGTRAP | pelist.index("PTRACE_EVENT_VFORK") << 8
        )
        self.sigExitFlag = (
            signal.SIGTRAP | pelist.index("PTRACE_EVENT_EXIT") << 8
        )

        # set task indexes #
        clist = ConfigMgr.PROCSTAT_ATTR
        self.commIdx = clist.index("COMM")
        self.utimeIdx = clist.index("UTIME")
        self.stimeIdx = clist.index("STIME")
        self.rssIdx = clist.index("RSS")
        self.tkillIdx = SysMgr.getNrSyscall("sys_tkill")

        # set arch regs #
        self.arch = SysMgr.arch
        self.sysreg = ConfigMgr.SYSREG_LIST[self.arch]
        self.retreg = ConfigMgr.RET_LIST[self.arch]

        # set python reader functions #
        if ConfigMgr.wordSize == 4:
            self.readPyStr = self.readPyStr32
            self.readPyFrame = self.readPyFrame32
            self.readPyState = self.readPyState32
        else:
            self.readPyStr = self.readPyStr64
            self.readPyFrame = self.readPyFrame64
            self.readPyState = self.readPyState64

        # set breakpoint variables #
        if self.arch in ("aarch64", "arm"):
            self.brkInst = b"\xFE\xDE\xFF\xE7"
            self.prevInstOffset = 0
        elif self.arch == "riscv64":
            self.brkInst = b"\x73\x00\x10\x00"
            self.prevInstOffset = 1
        else:
            self.brkInst = b"\xCC"
            self.prevInstOffset = 1

        # set ignore list #
        self.ignoreItemList = list(
            map(lambda x: x.encode(), SysMgr.ignoreItemList)
        )

        # check ptrace scope #
        if Debugger.checkPtraceScope() < 0:
            raise Exception("no ptrace perm")

        # trace flags #
        self.traceEventList = [
            "PTRACE_O_TRACEEXEC",
            "PTRACE_O_TRACESYSGOOD",
            "PTRACE_O_TRACECLONE",
            "PTRACE_O_TRACEFORK",
            "PTRACE_O_TRACEVFORK",
            "PTRACE_O_TRACEEXIT",
        ]

        # load libc #
        SysMgr.loadLibcObj(exit=True)

        # define member classes #
        """
struct iovec {
    ptr_t iov_base; /* Starting address */
    size_t iov_len; /* Length in bytes */
};
        """

        class iovec(Structure):
            _fields_ = (("iov_base", c_void_p), ("iov_len", c_size_t))

        self.iovec = iovec
        self.iovec_ptr = iovec_ptr = POINTER(iovec)

        """
struct msghdr {
    void            *msg_name;  /* ptr to socket address structure */
    int             msg_namelen;    /* size of socket address structure */
    struct iov_iter msg_iter;   /* data */
    void            *msg_control;   /* ancillary data */
    size_t msg_controllen; /* ancillary data buffer length */
    unsigned int    msg_flags;  /* flags on received message */
    struct kiocb    *msg_iocb;  /* ptr to iocb for async requests */
};
        """

        class msghdr(Structure):
            _fields_ = (
                ("msg_name", c_void_p),
                ("msg_namelen", c_uint),
                ("msg_iov", iovec_ptr),
                ("msg_iovlen", c_size_t),
                ("msg_control", c_void_p),
                ("msg_controllen", c_size_t),
                ("msg_flags", c_int),
            )

        self.msghdr = msghdr
        self.msghdr_ptr = POINTER(msghdr)

        """
struct mmsghdr {
    struct msghdr msg_hdr;  /* Message header */
    unsigned int  msg_len;  /* Number of received bytes for header */
};
        """

        class mmsghdr(Structure):
            _fields_ = (("msg_hdr", msghdr), ("msg_len", c_uint))

        self.mmsghdr = mmsghdr
        self.mmsghdr_ptr = POINTER(mmsghdr)

        """
struct cmsghdr {
   size_t cmsg_len;    /* Data byte count, including header
                          (type is socklen_t in POSIX) */
   int    cmsg_level;  /* Originating protocol */
   int    cmsg_type;   /* Protocol-specific type */
/* followed by
  unsigned char cmsg_data[]; */
};
        """

        class cmsghdr(Structure):
            _fields_ = (
                ("cmsg_len", c_size_t),
                ("cmsg_level", c_int),
                ("cmsg_type", c_int),
            )

        self.cmsghdr = cmsghdr
        self.cmsghdr_ptr = POINTER(cmsghdr)

        """
struct io_sqring_offsets {
	__u32 head;
	__u32 tail;
	__u32 ring_mask;
	__u32 ring_entries;
	__u32 flags;
	__u32 dropped;
	__u32 array;
	__u32 resv1;
	__u64 resv2;
};
        """

        class io_sqring_offsets(Structure):
            _fields_ = (
                ("head", c_uint32),
                ("tail", c_uint32),
                ("ring_mask", c_uint32),
                ("ring_entries", c_uint32),
                ("flags", c_uint32),
                ("dropped", c_uint32),
                ("array", c_uint32),
                ("resv1", c_uint32),
                ("resv2", c_uint64),
            )

        """
struct io_cqring_offsets {
	__u32 head;
	__u32 tail;
	__u32 ring_mask;
	__u32 ring_entries;
	__u32 overflow;
	__u32 cqes;
	__u32 flags;
	__u32 resv1;
	__u64 resv2;
};
        """

        class io_cqring_offsets(Structure):
            _fields_ = (
                ("head", c_uint32),
                ("tail", c_uint32),
                ("ring_mask", c_uint32),
                ("ring_entries", c_uint32),
                ("overflow", c_uint32),
                ("cqes", c_uint32),
                ("flags", c_uint32),
                ("resv1", c_uint32),
                ("resv2", c_uint64),
            )

        """
struct io_uring_params {
	__u32 sq_entries;
	__u32 cq_entries;
	__u32 flags;
	__u32 sq_thread_cpu;
	__u32 sq_thread_idle;
	__u32 features;
	__u32 wq_fd;
	__u32 resv[3];
	struct io_sqring_offsets sq_off;
	struct io_cqring_offsets cq_off;
};
        """

        class io_uring_params(Structure):
            _fields_ = (
                ("sq_entries", c_uint32),
                ("cq_entries", c_uint32),
                ("flags", c_uint32),
                ("sq_thread_cpu", c_uint32),
                ("sq_thread_idle", c_uint32),
                ("features", c_uint32),
                ("wq_fd", c_uint32),
                ("resv", c_uint32 * 3),
                ("sq_off", io_sqring_offsets),
                ("cq_off", io_cqring_offsets),
            )

        self.io_uring_params = io_uring_params
        self.io_uring_params_ptr = POINTER(io_uring_params)

        """
typedef struct {
    uintptr_t absolute_pc;     /* absolute PC offset */
    uintptr_t stack_top;       /* top of stack for this frame */
    size_t stack_size;         /* size of this stack frame */
} backtrace_frame_t;
        """

        class backtrace_frame_t(Structure):
            _fields_ = (
                ("absolute_pc", c_uint),
                ("stack_top", c_uint),
                ("stack_size", c_size_t),
            )

        self.btframe = backtrace_frame_t
        self.btframe_ptr = POINTER(backtrace_frame_t)

        """
typedef struct {
    uintptr_t relative_pc;       /* relative frame PC offset from the start of the library,
                                    or the absolute PC if the library is unknown */
    uintptr_t relative_symbol_addr; /* relative offset of the symbol from the start of the
                                    library or 0 if the library is unknown */
    char* map_name;              /* executable or library name, or NULL if unknown */
    char* symbol_name;           /* symbol name, or NULL if unknown */
    char* demangled_name;        /* demangled symbol name, or NULL if unknown */
} backtrace_symbol_t;
        """

        class backtrace_symbol_t(Structure):
            _fields_ = (
                ("relative_pc", c_uint),
                ("relative_symbol_addr", c_uint),
                ("map_name", c_char_p),
                ("symbol_name", c_char_p),
                ("demangled_name", c_char_p),
            )

        self.btsym = backtrace_symbol_t
        self.btsym_ptr = POINTER(backtrace_symbol_t)

        # apply environment variables #
        self.applyEnviron()

        # running #
        self.isRunning = False
        if self.checkPid(pid) >= 0:
            self.pid = long(pid)
            self.isRunning = True

            if self.isInRun() is None:
                if self.comm:
                    procInfo = "%s(%s)" % (self.comm, pid)
                else:
                    procInfo = "the task with TID %s" % pid
                SysMgr.printErr("failed to find %s" % procInfo)

                sys.exit(-1)

            # update comm #
            if not self.comm:
                self.comm = SysMgr.getComm(self.pid, cache=True, save=True)

            if attach:
                if self.attach(verb=True) < 0:
                    sys.exit(-1)
        # execute #
        elif self.execCmd:
            self.execute(self.execCmd)
            if mode in ("signal", "kernel"):
                # wait for PTRACE_TRACEME of the child #
                time.sleep(0.1)

                # attach to the child #
                self.attach()
        # ready #
        else:
            self.pid = None

        # set variables for general register set #
        self.regs = self.getRegStruct()
        self.iovecObj = self.getIovec(self.regs)
        self.tempRegs = self.getRegStruct()
        self.tempIovecObj = self.getIovec(self.tempRegs)
        self.regsDict = None

        # set variables for general register set #
        self.fpregs = self.getFpRegStruct()
        self.iovecFpObj = self.getIovec(self.fpregs)
        self.tempFpRegs = self.getFpRegStruct()
        self.tempIovecFpObj = self.getIovec(self.tempFpRegs)
        self.fpRegsDict = None

        # set variables for special purpose #
        self.btRegs = self.getRegStruct()
        self.btIovecObj = self.getIovec(self.btRegs)
        self.sigObj = self.getSigStruct()

        # save singleton instance #
        if self.pid != SysMgr.pid:
            Debugger.dbgInstance = self

    def applyEnviron(self):
        # set filter condition for elapsed time #
        self.retTime = UtilMgr.getEnvironNum(
            "ELAPSED", False, 0.1, False, isFloat=True
        )

        # set target sequence #
        if not Debugger.targetNums and "TARGETNUM" in SysMgr.environList:
            for item in SysMgr.environList["TARGETNUM"]:
                try:
                    Debugger.targetNums.setdefault(long(item), None)
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr(
                        "failed to add '%s' target number list" % item, True
                    )
                    sys.exit(-1)

            SysMgr.printInfo(
                "set the target number list to [%s] for new threads"
                % ",".join(list(map(str, Debugger.targetNums)))
            )

        # apply color for return string #
        if not Debugger.RETSTR:
            Debugger.RETSTR = UtilMgr.convColor("[RET]", "OKBLUE")

        # update flags by environment variable #
        for item in list(Debugger.envFlags):
            if item in SysMgr.environList and not Debugger.envFlags[item]:
                Debugger.envFlags[item] = True

        # apply showing symbol flag #
        self.showStatus = not Debugger.envFlags["HIDESYM"]

        # print elapsed time for python #
        Debugger.pyElapsed = UtilMgr.getEnvironNum(
            "PYELAPSED", False, Debugger.pyElapsed, False, isFloat=True
        )

        # filter for CPU threshold #
        Debugger.cpuCond = UtilMgr.getEnvironNum(
            "CPUCOND", False, Debugger.cpuCond, False, isInt=True
        )

        # set watchld flag #
        self.watchLd = "WATCHLD" in SysMgr.environList

        # set string size #
        if Debugger.strSize == -1 and "STRSIZE" in SysMgr.environList:
            Debugger.strSize = UtilMgr.getEnvironNum("STRSIZE", isInt=True)

        # apply backtrace filters #
        self.btFilterList = SysMgr.environList.get("BTFILTER", [])
        self.btIgnFilterList = SysMgr.environList.get("IGNBTFILTER", [])

    def getIovec(self, reg):
        return self.iovec(iov_base=addressof(reg), iov_len=sizeof(reg))

    def __del__(self, stop=False):
        if not self.attached:
            return
        elif not self.isAlive():
            return

        # kill target children #
        if hasattr(self, "childList"):
            SysMgr.killChildren(sig=signal.SIGINT, children=self.childList)
            del self.childList

        # detach target #
        self.detach()

        # continue target #
        try:
            if stop:
                os.kill(self.pid, signal.SIGSTOP)
            else:
                SysMgr.syscall(self.tkillIdx, self.pid, signal.SIGCONT)
        except:
            SysMgr.printSigError(self.pid, "SIGCONT")
            return

    @staticmethod
    def loadElfList(path):
        try:
            with open(path, "r") as fd:
                fdList = fd.readlines()
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                "failed to open '%s' to get ELF file list" % path, True
            )
            sys.exit(0)

        for item in fdList:
            try:
                # get target file #
                target = item.split()
                if not target:
                    continue
                elif len(target) == 1:
                    target = target[0]
                else:
                    target = target[-1]

                # check validation #
                if not target:
                    continue
                elif not FileAnalyzer.isValidFile(target):
                    continue

                # load target file #
                target = target.strip("[]")
                ElfAnalyzer.getObject(target)

                # move loaded objects to preload list #
                tobj = ElfAnalyzer.cachedFiles.pop(target, None)
                if tobj:
                    ElfAnalyzer.preloadedFiles[target] = tobj
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr("failed to load ELF file '%s'" % target, True)

    @staticmethod
    def hookFunc(pid, hookList=[], mode="hook"):
        comm = SysMgr.getComm(pid)
        procInfo = "%s(%s)" % (comm, pid)

        # attach to target #
        try:
            dobj = Debugger(pid=pid, attach=False)
            dobj.initValues()
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to hook for %s" % procInfo, True)
            sys.exit(-1)

        # load libraries in advance #
        dobj.loadSymbols()
        if dobj.attach() < 0:
            sys.exit(-1)

        # register my instance #
        SysMgr.addExitFunc(Debugger.destroyDebugger, [dobj])

        SysMgr.printInfo("start gathering symbols for %s" % procInfo)

        # convert filter for print mode #
        if mode == "print":
            filterList = hookList
            hookList = []
        else:
            filterList = []

        # get symbol info #
        loadBin = {}
        hooks = []
        for item in hookList:
            symbols = item.split("#")

            # check parameter #
            if len(symbols) == 1:
                SysMgr.printErr("failed to recognize '%s'" % item)
                sys.exit(-1)

            # get symbols from string #
            oldSym = symbols[0]
            fpath = SysMgr.convFullPath(symbols[1].strip())
            if len(symbols) == 2:
                if fpath:
                    newSym = oldSym
                else:
                    newSym = symbols[1]
            elif len(symbols) == 3:
                newSym = symbols[2]
            else:
                SysMgr.printErr("failed to recognize '%s'" % item)
                sys.exit(-1)

            # load the library #
            if not fpath in loadBin:
                loadBin.setdefault(fpath, None)

                # remote load #
                ret = dobj.dlopen(fpath)
                if not ret:
                    errMsg = " because remote dlopen failed"
                else:
                    errMsg = ""

                # update and check target map #
                dobj.loadSymbols()
                if not fpath in dobj.pmap:
                    SysMgr.printErr(
                        "failed to find '%s' on memory map for %s%s"
                        % (fpath, procInfo, errMsg)
                    )

                    PageAnalyzer.printMemoryArea(pid, comm=comm, lastLine=True)

                    sys.exit(-1)

            # get target symbol info #
            oldSet = dobj.getAddrBySymbol(oldSym)
            if not oldSet:
                SysMgr.printWarn(
                    "failed to find '%s' info from %s" % (oldSym, procInfo)
                )
                continue

            # get hook symbol info #
            newSet = dobj.getAddrBySymbol(newSym, fpath)
            if not newSet:
                SysMgr.printErr(
                    "failed to find '%s' info in %s from %s"
                    % (newSym, fpath, procInfo)
                )
                continue

            # add a set to list #
            hooks.append([oldSet, newSet])

        # create a hook hash list #
        hookHash = {}
        for item in hooks:
            targetSym = item[0][0][1]
            hookHash[targetSym] = item

        # stop target #
        SysMgr.sendSignalProcs(signal.SIGSTOP, [pid], verb=False)

        # print context #
        dobj.printContext(regs=SysMgr.showAll, newline=True)

        # define link list #
        linkList = {}
        convColor = UtilMgr.convColor
        nrUpdated = 0

        for fpath, mapInfo in dobj.pmap.items():
            # skip same binary to prevent infinite recursive call #
            if not fpath.startswith("/"):
                continue

            # get ELF object #
            fcache = ElfAnalyzer.getObject(fpath)
            if not hasattr(fcache, "attr"):
                continue

            # get start address on map for the binary #
            if ElfAnalyzer.isRelocFile(fpath):
                vstart = mapInfo["vstart"]
            else:
                vstart = 0

            # get mapping info #
            for sym, attr in sorted(
                fcache.attr["dynsymTable"].items(),
                key=lambda x: x[1]["size"],
                reverse=False,
            ):
                # ignore original symbols #
                if mode == "hook" and attr["size"] > 0:
                    break
                # save link info #
                elif mode == "print" and sym and not hookHash:
                    # get relative type #
                    try:
                        rtype = ElfAnalyzer.RELOC_TYPE[attr["rtype"]]
                    except:
                        rtype = None

                    # original symbols #
                    if attr["size"] > 0:
                        origAddr = hex(vstart + attr["value"]).rstrip("L")
                        linkInfo = convColor("ORIGIN(%s)" % origAddr, "CYAN")

                        # add copy flag for reference variable #
                        """
                        When using dlopen() to load shared objects,
                        The address of variables with the same name
                        declared using the extern keyword can be different.
                        The default operation is to search the previously
                        defined symbol in the global area first.
                        But if the RTLD_DEEPBIND flag is set,
                        The shared objects with dependencies are searched first.
                        RTLD_DEEPBIND flag changes the search order for symbol
                        lookup in shared objects.
                        """
                        if rtype and rtype.endswith("_COPY"):
                            linkInfo += convColor("[COPY]", "RED")
                    # linked symbols #
                    elif attr["value"] > 0:
                        slotAddr = vstart + attr["value"]

                        # get reference address #
                        linkAddr = dobj.readWord(slotAddr)
                        if not isinstance(linkAddr, long):
                            linkAddr = UtilMgr.convWord2Str(linkAddr)

                        # get dereference address #
                        if fcache.isInPlt(linkAddr):
                            linkInfo = "PLT(%s)" % hex(linkAddr)
                        else:
                            linkData = dobj.getSymbolInfo(
                                linkAddr, onlyFunc=False, onlyExec=False
                            )
                            if linkData:
                                if UtilMgr.isNumber(
                                    linkData[2]
                                ) and fcache.isInPlt(long(linkData[2], 16)):
                                    linkInfo = convColor(
                                        "PLT(%s)" % linkData[2], "RED"
                                    )
                                else:
                                    linkSym = linkData[0]
                                    if linkData[0] != "??":
                                        linkSym = convColor(linkSym, "GREEN")
                                    linkInfo = "%s/%s[%s/%s]" % (
                                        linkSym,
                                        hex(linkAddr).rstrip("L"),
                                        convColor(linkData[1], "WARNING"),
                                        linkData[2],
                                    )
                            else:
                                linkInfo = convColor("ORIGIN", "CYAN")
                    else:
                        linkInfo = "NONE"

                    # register link info to list #
                    linkList.setdefault(fpath, [])
                    linkList[fpath].append(
                        (
                            convColor(sym, "YELLOW"),
                            attr["bind"],
                            attr["vis"],
                            attr["type"],
                            linkInfo,
                        )
                    )

                    continue

                # check target symbol #
                pureSymbol = sym.split("@")[0]
                if not pureSymbol in hookHash:
                    continue

                item = hookHash[pureSymbol]

                # hook info #
                hook = item[1][0]
                hookAddr, hookSym, hookBin = hook[:3]

                # target info #
                target = item[0][0]
                targetAddr, targetSym, targetBin = target[:3]

                # read original address for target #
                slotAddr = vstart + attr["value"]
                origAddr = dobj.readWord(slotAddr)

                mprotected = False
                while 1:
                    # write hook address for target #
                    if slotAddr % ConfigMgr.wordSize == 0:
                        ret = dobj.accessMem(dobj.pokeCmd, slotAddr, hookAddr)
                    else:
                        ret = dobj.writeMem(slotAddr, hookAddr)

                    # change access permission on the page #
                    if ret == -1:
                        if mprotected:
                            break
                        else:
                            dobj.mprotect(slotAddr)
                    else:
                        break

                # read updated address for verification #
                newAddr = dobj.readWord(slotAddr)

                # set symbol info #
                srcInfo = '"%s[%s@%s]"' % (targetSym, hex(targetAddr), fpath)
                desInfo = '"%s[%s@%s]"' % (hookSym, hex(hookAddr), hookBin)

                # check update result #
                if hookAddr == newAddr:
                    nrUpdated += 1
                    SysMgr.printInfo(
                        "updated %s to %s for %s"
                        % (srcInfo, desInfo, procInfo),
                        prefix=False,
                    )
                else:
                    SysMgr.printErr(
                        "failed to update %s to %s for %s"
                        % (srcInfo, desInfo, procInfo)
                    )

        # continue target #
        SysMgr.sendSignalProcs(signal.SIGCONT, [pid], verb=False)

        if not linkList:
            if not nrUpdated:
                SysMgr.printErr("no hooked function")
            return

        # set pager #
        if not SysMgr.findOption("Q"):
            SysMgr.streamEnable = False

        # print title for bind info #
        SysMgr.printPipe(
            "\n[Function Bind Info] [Target: %s]\n%s" % (procInfo, twoLine)
        )

        SysMgr.printPipe(
            "{0:6} {1:>7} {2:1} => {3:1}".format(
                "Path", "Type", "Sym[Bind/Vis]", "Link"
            )
        )

        path = None
        direct = convColor("=>", "BLUE")
        for path, links in linkList.items():
            SysMgr.printPipe("%s\n[%s]" % (oneLine, path))

            for item in sorted(links):
                source = "%s[%s/%s]" % (item[0], item[1], item[2])

                string = "{0:6} {1:>7} {2:1} {3:1} {4:1}".format(
                    " ", item[3], source, direct, item[4]
                )

                if filterList:
                    if UtilMgr.isValidStr(string, filterList):
                        SysMgr.printPipe(string)
                else:
                    SysMgr.printPipe(string)
        SysMgr.printPipe(oneLine)

    @staticmethod
    def getGlobalLock(name=None, size=0, exit=True):
        if Debugger.gLockObj:
            return Debugger.gLockObj

        # create a global lock based on file #
        try:
            SysMgr.importPkgItems("fcntl")

            if os.path.isdir(SysMgr.tmpPath):
                dirpath = SysMgr.tmpPath
            else:
                dirpath = SysMgr.cacheDirPath

            if not name:
                name = SysMgr.pid

            Debugger.gLockPath = "%s/guider_%s.lock" % (dirpath, name)
            Debugger.gLockObj = open(Debugger.gLockPath, "w")
            os.chmod(Debugger.gLockPath, 0o777)

            if size != 0:
                Debugger.gLockObj.truncate(size)

        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                "failed to create %s for lock" % Debugger.gLockPath, True
            )

            if exit and not SysMgr.forceEnable:
                sys.exit(-1)

        return Debugger.gLockObj

    def readArgs(self, syscall=False):
        arch = self.arch
        regs = self.regs

        if arch == "aarch64":
            ret = (
                regs.x0,
                regs.x1,
                regs.x2,
                regs.x3,
                regs.x4,
                regs.x5,
                regs.x6,
                regs.x7,
            )
        elif arch == "x64":
            ret = (
                regs.rdi,
                regs.rsi,
                regs.rdx,
                regs.r10 if syscall else regs.rcx,
                regs.r8,
                regs.r9,
            )
        elif arch == "riscv64":
            ret = (
                regs.a0,
                regs.a1,
                regs.a2,
                regs.a3,
                regs.a4,
                regs.a5,
                regs.a6,
            )
        elif arch == "arm":
            ret = (
                regs.r0,
                regs.r1,
                regs.r2,
                regs.r3,
                regs.r4,
                regs.r5,
                regs.r6,
            )
        elif arch == "x86":
            ret = (regs.ebx, regs.ecx, regs.edx, regs.esi, regs.edi, regs.ebp)
        elif arch == "powerpc":
            ret = (
                regs.gpr3,
                regs.gpr4,
                regs.gpr5,
                regs.gpr6,
                regs.gpr7,
                regs.gpr8,
            )
        else:
            return None

        return tuple(map(lambda x: -1 if c_int(x).value == -1 else x, ret))

    def writeArgs(self, argList):
        if not argList:
            return

        arch = self.arch

        # get regset #
        if arch == "aarch64":
            regs = ("x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7")
        elif arch == "x64":
            regs = (
                "rdi",
                "rsi",
                "rdx",
                "r10" if self.mode == "syscall" else "rcx",
                "r8",
                "r9",
            )
        elif arch == "riscv64":
            regs = ("a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7")
        elif arch == "arm":
            regs = ("r0", "r1", "r2", "r3", "r4", "r5", "r6")
        elif arch == "x86":
            regs = ("ebx", "ecx", "edx", "esi", "edi", "ebp")
        elif arch == "powerpc":
            regs = ("gpr3", "gpr4", "gpr5", "gpr6", "gpr7", "gpr8")
        else:
            return None

        for idx, val in enumerate(argList):
            if val is None or val == "":
                continue

            # set reg value #
            try:
                setattr(self.regs, regs[idx], val)
            except SystemExit:
                sys.exit(0)
            except:
                pass

    def setPid(self, pid):
        if self.checkPid(pid) < 0:
            SysMgr.printErr("failed to set PID %s" % pid)
            return -1

        self.pid = pid
        return 0

    def getPid(self):
        return self.pid

    def convRetArgs(self, argList):
        for idx, item in enumerate(deepcopy(argList)):
            if isinstance(item, str) and item.startswith("@"):
                try:
                    argList[idx] = self.retList[item[1:]]
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

        return argList

    def runDebugMode(self, sym=None, fname=None, args=[]):
        while 1:
            try:
                # print message #
                sys.stdout.write(
                    "\n[%s] input a command... [HELP/NEXT/EXIT] "
                    % (UtilMgr.convColor("debug", "PINK", 8))
                )
                sys.stdout.flush()

                # read command #
                cmd = sys.stdin.readline()[:-1]
                if cmd.strip().upper() in ("", "N", "NEXT"):
                    break

                # execute command #
                self.executeCmd([cmd], sym, fname, args, isExit=False)
            except SystemExit:
                sys.exit(0)
            except:
                pass

    def executeCmd(
        self, cmdList, sym=None, fname=None, args=[], force=False, isExit=True
    ):
        def _flushPrint(newline=True):
            if SysMgr.outPath:
                if SysMgr.showAll:
                    self.callPrint.append(SysMgr.bufferString[1:])

                # print to stdout #
                if SysMgr.streamEnable:
                    sys.stdout.write(SysMgr.bufferString)
            else:
                if self.bufferedStr:
                    SysMgr.bufferString += self.bufferedStr
                    self.bufferedStr = ""

                SysMgr.printPipe(
                    SysMgr.bufferString, flush=True, newline=newline
                )

            SysMgr.clearPrint()

        def _printCmdErr(cmdset, cmd):
            cmdsetList = {
                "acc": "NAME:VAR|REG|VAL",
                "add": "SYM;",
                "check": "VAR|NAME|ADDR|REG:OP(EQ/DF/INC/BT/LT):VAL:SIZE:EVT",
                "condexit": "",
                "debug": "",
                "dist": "NAME:VAR|REG|VAL",
                "dump": "NAME|ADDR:FILE",
                "event": "MESSAGE",
                "exec": "COMMAND",
                "exit": "",
                "fadvise": "ADDR:LENGTH:ADVISE",
                "getarg": "REG:REG",
                "getenv": "VAR",
                "getret": "CMD",
                "help": "",
                "hide": "",
                "hidden": "",
                "inter": "VAL",
                "interval": "FUNC",
                "jump": "SYMBOL|ADDR#ARG0#ARG1",
                "load": "PATH",
                "log": "MESSAGE",
                "madvise": "ADDR:LENGTH:ADVISE",
                "map": "",
                "mprotect": "ADDR:SIZE:PERM",
                "print": "VAR",
                "printaux": "",
                "pyfile": "PATH:SYNC",
                "pyscript": "PATH:FUNC:ARGS",
                "pystr": "CODE:SYNC",
                "rdfd": "FD",
                "rdmem": "VAR|ADDR|REG:SIZE",
                "remove": "SYM;",
                "repeat": "CNT",
                "ret": "VAL",
                "save": "VAR:VAL:TYPE",
                "setarg": "REG#VAL:REG#VAL",
                "setenv": "VAR#VAR|VAL",
                "setret": "VAL:CMD",
                "show": "",
                "start": "",
                "stop": "",
                "syscall": "SYSCALL#ARG0#ARG1",
                "thread": "CNT",
                "proctop": "PID|COMM",
                "proctopg": "PID|COMM",
                "thrtop": "PID|COMM",
                "thrtopg": "PID|COMM",
                "usercall": "FUNC|ADDR#ARG0#ARG1",
                "wrmem": "VAR|ADDR|REG:VAL:SIZE",
            }

            if cmd in cmdsetList:
                cmdformat = cmdsetList[cmd]
            else:
                cmdformat = ""

            if cmdformat:
                cmdformat = ":%s" % cmdformat

            SysMgr.printErr(
                "wrong command '%s', input in the format {%s%s}"
                % (cmdset, cmd, cmdformat)
            )
            sys.exit(-1)

        def _addPrint(string):
            if self.showStatus and not sym in self.hiddenList:
                SysMgr.addPrint(string)

        def _execFunc(func, *args):
            # get previous stats #
            if Debugger.envFlags["PRINTDIFF"]:
                prevTime, prevCpu, prevMem, prevIo = _getStats()

            ret = func(*args)

            # print diff stats #
            if Debugger.envFlags["PRINTDIFF"]:
                _printDiff(prevTime, prevCpu, prevMem, prevIo)

            return ret

        def _getStats():
            prevTime = time.time()
            prevCpu = self.getTotalCpuTick()
            prevMem = SysMgr.getMemStat(self.pid)
            prevIo = SysMgr.readFile(
                "%s/%s/io" % (SysMgr.procPath, self.pid), verb=False
            )
            return prevTime, prevCpu, prevMem, prevIo

        def _printDiff(prevTime, prevCpu, prevMem, prevIo):
            # time diff #
            diff = time.time() - prevTime

            # CPU diff #
            afterCpu = self.getTotalCpuTick()
            cpu = afterCpu[0] - prevCpu[0]
            if cpu:
                cpu = "+%d" % cpu

            prevMem = SysMgr.convMemStat(prevMem)
            afterMem = SysMgr.convMemStat(SysMgr.getMemStat(self.pid))

            # VSS diff #
            vss = UtilMgr.convSize2Unit(afterMem["vss"] - prevMem["vss"])
            if vss == "0":
                vss = 0
            elif not vss.startswith("-"):
                vss = "+" + vss

            # RSS diff #
            rss = UtilMgr.convSize2Unit(afterMem["rss"] - prevMem["rss"])
            if rss == "0":
                rss = 0
            elif not rss.startswith("-"):
                rss = "+" + rss

            # I/O diff #
            readIo = writeIo = 0
            if prevIo:

                def _getIoStats(iodata):
                    ioStats = {}
                    for line in iodata.split("\n"):
                        if not (
                            line.startswith("read_bytes:")
                            or line.startswith("write_bytes:")
                        ):
                            continue

                        name, val = line.split(None, 1)[:2]

                        ioStats[name[:-1]] = long(val)

                    return ioStats

                afterIo = SysMgr.readFile(
                    "%s/%s/io" % (SysMgr.procPath, self.pid)
                )
                if afterIo:
                    beforeIoStats = _getIoStats(prevIo)
                    afterIoStats = _getIoStats(afterIo)

                    def _getIoDiff(name):
                        diff = afterIoStats[name] - beforeIoStats[name]
                        if diff:
                            diff = "+%s" % UtilMgr.convSize2Unit(diff)
                        return diff

                    readIo = _getIoDiff("read_bytes")
                    writeIo = _getIoDiff("write_bytes")

            # print stat diff #
            statStr = "%s<stat> TIME: %.6f Sec" % (
                " " if self.multi else "\n" + " " * 11,
                diff,
            )
            if cpu:
                statStr += ", CPU: %s%%" % cpu
            if vss:
                statStr += ", VSS: %s" % vss
            if rss:
                statStr += ", RSS: %s" % rss
            if readIo:
                statStr += ", READ: %s" % readIo
            if writeIo:
                statStr += ", WRITE: %s" % writeIo
            self.bufferedStr += UtilMgr.convColor(statStr, "YELLOW")

        def _handleCmd(cmdset, cmd):
            repeat = True

            # check repeat #
            if cmd == "oneshot":
                repeat = False
                if len(cmdset) == 1:
                    return repeat
                cmdset = cmdval.split(":", 2)[1:]
                cmd = cmdset[0]

            convColor = UtilMgr.convColor

            # pick a command #
            cmdstr = convColor(
                "%s(%s):%s" % (self.comm, self.pid, cmd)
                if "INCTASK" in SysMgr.environList
                else cmd,
                "PINK",
                8,
            )

            def _getFunc():
                return "%s%s" % (
                    sym if sym else "",
                    "(%s)" % ",".join(list(map(hex, args))) if args else "()",
                )

            if cmd == "print":
                if SysMgr.showAll and not force:
                    pass
                elif len(cmdset) == 1:
                    self.printContext(newline=True, brief=True)
                else:
                    var = cmdset[1]
                    try:
                        data = self.retList[var]
                    except:
                        data = "N/A"

                    _addPrint("\n[%s] %s = %s" % (cmdstr, var, data))

            elif cmd == "ret":
                if len(cmdset) == 1:
                    _printCmdErr(cmdval, cmd)

                # get return value #
                ret = UtilMgr.convStr2Num(cmdset[1])
                if ret is None:
                    SysMgr.printErr(
                        "wrong return value %s for %s"
                        % (cmdset[1], _getFunc())
                    )
                    return repeat

                # get return address #
                wordSize = ConfigMgr.wordSize
                if self.lr:
                    targetAddr = self.lr
                else:
                    targetAddr = self.fp + wordSize
                    if targetAddr % wordSize == 0:
                        targetAddr = self.accessMem(self.peekCmd, targetAddr)
                    else:
                        targetAddr = self.readMem(targetAddr, retWord=True)

                retval = "0x%x" % ret
                _addPrint("\n[%s] %s(%s)" % (cmdstr, ret, retval))

                # set register values #
                self.setRet(ret)
                self.setPC(targetAddr)
                self.setRegs()
                self.updateRegs()

            elif cmd == "getret":
                if len(cmdset) > 1:
                    cmd = cmdset[1].split("$")
                else:
                    cmd = None

                # set breakpoint to return address #
                ret = self.setRetBp(sym, fname, cmd)
                if not ret:
                    SysMgr.printErr(
                        "failed to set breakpoint to return address for %s"
                        % _getFunc()
                    )
                    return repeat

            elif cmd == "setret":
                if len(cmdset) == 1:
                    _printCmdErr(cmdval, cmd)

                memset = cmdset[1].split(":", 1)
                if len(memset) > 1:
                    cmd = memset[1].split("$")
                else:
                    cmd = None

                # convert return type #
                try:
                    val = memset[0]
                    num = long(val, 16)
                except:
                    num = long(val)

                # inject the new breakpoint for return #
                if self.mode == "syscall":
                    setattr(self.regs, self.retreg, num)
                    self.setRegs()
                else:
                    ret = self.setRetBp(sym, fname, cmd)
                    if not ret:
                        SysMgr.printErr(
                            "failed to set breakpoint to return address for %s"
                            % _getFunc()
                        )
                        return repeat

                    # register a return value #
                    newSym = sym + Debugger.RETSTR
                    self.setRetList.setdefault(newSym, [])
                    self.setRetList[newSym].append(num)

                # print return value #
                _addPrint("\n[%s] %s" % (cmdstr, val))

            elif cmd == "setarg":
                if len(cmdset) == 1:
                    _printCmdErr(cmdval, cmd)

                # get argument info #
                nrMax = 0
                argStr = ""
                argSet = {}
                origArgs = args
                argList = cmdset[1].split(":")

                # convert args from variable list #
                argList = self.convRetArgs(argList)

                for item in argList:
                    idx, val = item.split("#")
                    idx = long(idx)
                    val = UtilMgr.convStr2Num(val)
                    argSet[idx] = val
                    if nrMax < idx:
                        nrMax = idx
                    argStr += "%s: %s(%s) -> %s(%s), " % (
                        idx,
                        hex(origArgs[idx]).rstrip("L"),
                        origArgs[idx],
                        hex(val).rstrip("L"),
                        val,
                    )

                # complete output string #
                if not argStr:
                    res = ""
                else:
                    res = argStr[: argStr.rfind(",")]

                # make a new argument list #
                argList = [None] * (nrMax + 1)
                for idx, val in argSet.items():
                    # convert args from variable list #
                    val = self.convRetArgs([val])

                    argList[long(idx)] = val[0]

                _addPrint("\n[%s] %s" % (cmdstr, res))

                # set register values #
                self.writeArgs(argList)
                self.setRegs()
                self.updateRegs()

            elif cmd == "getarg":
                if len(cmdset) == 1:
                    _printCmdErr(cmdval, cmd)

                # get argument info #
                argStr = ""
                argList = cmdset[1].split(":")

                # convert args from variable list #
                argList = self.convRetArgs(argList)

                for item in argList:
                    if not str(item):
                        continue

                    try:
                        val = args[long(item)]
                    except SystemExit:
                        sys.exit(0)
                    except:
                        val = "None"

                    # update return #
                    self.prevReturn = str(val)

                    argStr += "%s: %s(%s), " % (
                        item,
                        hex(val).rstrip("L"),
                        val,
                    )

                # complete output string #
                if not argStr:
                    res = ""
                else:
                    res = argStr[: argStr.rfind(",")]

                _addPrint("\n[%s] %s" % (cmdstr, res))

            elif cmd == "event":
                if len(cmdset) == 1:
                    _printCmdErr(cmdval, cmd)

                name = ":".join(cmdset[1:])

                SysMgr.writeEvent(
                    "EVENT_%s(%s)/%.6f" % (name, sym, self.vdiff), False
                )

                _addPrint("\n[%s] EVENT_%s" % (cmdstr, name))

            elif cmd == "fadvise":
                if len(cmdset) == 1:
                    _printCmdErr(cmdval, cmd)

                argList = cmdset[1].split(":")

                # convert args from variable list #
                argList = self.convRetArgs(argList)

                addr, length, advice = argList[:3]

                # get addr #
                if UtilMgr.isNumber(addr):
                    addr = UtilMgr.convStr2Num(addr)
                else:
                    SysMgr.printErr(
                        "wrong address '%s' for fadvise for %s"
                        % (addr, _getFunc())
                    )
                    sys.exit(-1)

                # get length #
                length = UtilMgr.convUnit2Size(length)

                # get advice #
                if UtilMgr.isNumber(advice):
                    advice = UtilMgr.convStr2Num(advice)
                else:
                    UtilMgr.makeReverseDict(
                        ConfigMgr.FADV_TYPE, ConfigMgr.FADV_TYPE_REVERSE
                    )

                    # convert string to number #
                    name = "FADV_" + advice.upper()
                    if name in ConfigMgr.FADV_TYPE_REVERSE:
                        advice = ConfigMgr.FADV_TYPE_REVERSE[name]

                # check advice #
                if UtilMgr.isString(advice):
                    SysMgr.printErr(
                        "failed to recognize '%s' as an advice for %s"
                        % (advice, _getFunc())
                    )
                    return repeat

                # get file and offset info #
                ret = self.getSymbolInfo(addr)
                if not ret:
                    SysMgr.printWarn("no symbol info for %s" % addr, True)
                    return repeat

                path, addr = ret[1:3]
                addr = long(addr, 16)

                # get file size #
                if length == 0:
                    length = UtilMgr.getFileSize(path, string=False)

                # convert page-aligned size #
                start, end, addr, length = UtilMgr.getPageAddrs(addr, length)

                # call fadvise #
                SysMgr.fadvise(path, pos=addr, size=length, advice=advice)

                _addPrint(
                    '\n[%s] fadvise("%s", %s, %s, %s)'
                    % (
                        cmdstr,
                        path,
                        hex(addr),
                        length,
                        ConfigMgr.FADV_TYPE[advice],
                    )
                )

            elif cmd == "printaux":
                auxList = self.getauxval()

                _addPrint("\n[%s]\n" % cmdstr)
                for name, val in sorted(auxList.items()):
                    _addPrint("{0:<30} {1:1}\n".format(name, val))

            elif cmd in ("madvise", "mprotect"):
                if len(cmdset) == 1:
                    _printCmdErr(cmdval, cmd)

                argList = cmdset[1].split(":")

                # convert args from variable list #
                argList = self.convRetArgs(argList)

                addr, length, advice = argList[:3]

                # get addr #
                if UtilMgr.isNumber(addr):
                    addr = UtilMgr.convStr2Num(addr)
                elif addr in ("heap", "stack"):
                    addr, end = FileAnalyzer.getMapAddr(
                        self.pid, "[%s]" % addr
                    )
                    addr = long(addr, 16)
                    if not length:
                        length = long(end, 16) - addr
                else:
                    SysMgr.printErr(
                        "wrong address '%s' from %s for %s"
                        % (addr, cmd, _getFunc())
                    )
                    sys.exit(-1)

                # get length #
                length = UtilMgr.convUnit2Size(length)

                if cmd == "madvise":
                    # get advice #
                    if UtilMgr.isNumber(advice):
                        advice = UtilMgr.convStr2Num(advice)
                    else:
                        UtilMgr.makeReverseDict(
                            ConfigMgr.MADV_TYPE, ConfigMgr.MADV_TYPE_REVERSE
                        )

                        # convert string to number #
                        name = "MADV_" + advice.upper()
                        if name in ConfigMgr.MADV_TYPE_REVERSE:
                            advice = ConfigMgr.MADV_TYPE_REVERSE[name]

                    # check advice #
                    if UtilMgr.isString(advice):
                        SysMgr.printErr(
                            "failed to recognize '%s' as an advice for %s"
                            % (advice, _getFunc())
                        )
                        return repeat

                    # convert page-aligned size #
                    start, end, addr, length = UtilMgr.getPageAddrs(
                        addr, length
                    )

                    # call madvise #
                    ret = self.madvise(addr, length, advice)
                elif cmd == "mprotect":
                    # call mprotect #
                    ret = self.mprotect(addr, length, advice)
                else:
                    return repeat

                _addPrint(
                    "\n[%s] %s = %s(%s, %s, %s)"
                    % (
                        cmdstr,
                        ret,
                        cmd,
                        hex(addr),
                        length,
                        ConfigMgr.MADV_TYPE[advice]
                        if cmd == "madvise"
                        else advice,
                    )
                )

            elif cmd in ("proctop", "proctopg", "thrtop", "thrtopg"):
                if len(cmdset) == 1:
                    target = "ALL"
                    filterOpt = ""
                else:
                    target = ":".join(cmdset[1:])
                    filterOpt = "-g %s" % target

                if cmd.startswith("proctop"):
                    gcmd = "top"
                elif cmd.startswith("thrtop"):
                    gcmd = "ttop"

                _addPrint("\n[%s] %s" % (cmdstr, target))
                _flushPrint()

                rcmd = ["GUIDER", gcmd, "-eTb", "-dL", "-R1", "-qFASTINIT"]
                if filterOpt:
                    rcmd += [filterOpt]
                if cmd.endswith("g"):
                    rcmd += ["-P"]

                SysMgr.execBgCmd(rcmd, mute=False)

            elif cmd == "rdmem":
                if len(cmdset) == 1:
                    _printCmdErr(cmdval, cmd)

                # get argument info #
                memset = cmdset[1].split(":")
                if len(memset) != 1 and len(memset) != 2:
                    _printCmdErr(cmdval, cmd)

                # convert args from variable list #
                memset = self.convRetArgs(memset)

                # get addr #
                addr = UtilMgr.convStr2Num(memset[0])
                if addr is None:
                    return repeat

                # get size #
                if len(memset) == 2:
                    fixed = True
                    size = UtilMgr.convStr2Num(memset[1], verb=False)
                    if size is None:
                        size = UtilMgr.convUnit2Size(memset[1])
                        if size is None:
                            return repeat
                else:
                    fixed = False
                    size = 0

                # convert address from registers #
                try:
                    addr = args[addr]
                except:
                    pass

                # get address #
                if UtilMgr.isNumber(addr):
                    addr = UtilMgr.convStr2Num(addr)
                    if addr is None:
                        return repeat
                else:
                    SysMgr.printErr(
                        "wrong addr %s for %s" % (addr, _getFunc())
                    )
                    return repeat

                # get memory value #
                if size:
                    ret = self.readMem(addr, size)
                else:
                    ret = self.readString(addr)
                if ret in (None, -1):
                    SysMgr.printErr(
                        "failed to read from %s for %s"
                        % (hex(addr).rstrip("L"), _getFunc())
                    )
                    return repeat

                # update return #
                try:
                    ret = ret.decode()
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

                self.retList[addr] = ret
                self.prevReturn = ret

                # strip garbage #
                if ret and not fixed:
                    try:
                        ret = ret.split("\x00")[0]
                        size = len(ret)
                    except SystemExit:
                        sys.exit(0)
                    except:
                        pass

                # convert to binary #
                try:
                    binary = struct.unpack("%sB" % len(ret), ret)
                    binstr = " [0x"
                    for item in binary:
                        binstr += "%x" % item
                    binstr += "]"
                except SystemExit:
                    sys.exit(0)
                except:
                    binstr = ""

                _addPrint(
                    "\n[%s] %s: %s(%sbyte)%s"
                    % (cmdstr, hex(addr).rstrip("L"), repr(ret), size, binstr)
                )

            elif cmd == "wrmem":
                if len(cmdset) == 1:
                    _printCmdErr(cmdval, cmd)

                # get argument info #
                memset = cmdset[1].split(":")
                if len(memset) != 2 and len(memset) != 3:
                    _printCmdErr(cmdval, cmd)

                # convert args from variable list #
                memset = self.convRetArgs(memset)

                # get addr #
                addr = UtilMgr.convStr2Num(memset[0])
                if addr is None:
                    return repeat

                # get value #
                val = memset[1].encode()

                # get size #
                if len(memset) == 3:
                    size = UtilMgr.convStr2Num(memset[2], verb=False)
                    if not size:
                        size = UtilMgr.convUnit2Size(memset[2])
                else:
                    size = len(val)

                # increase size #
                if len(val) < size:
                    val += b" " * (size - len(val))

                # convert address from registers #
                try:
                    addr = args[addr]
                except:
                    pass

                # get address #
                addr = UtilMgr.convStr2Num(addr)
                if addr is None:
                    return repeat

                _addPrint(
                    "\n[%s] %s: %s(%sbyte)"
                    % (cmdstr, hex(addr).rstrip("L"), repr(val[:size]), size)
                )

                # set register values #
                ret = self.writeMem(addr, val, size)
                if ret in (None, -1):
                    SysMgr.printErr(
                        "failed to write '%s' to %s for %s"
                        % (val.decode(), hex(addr).rstrip("L"), _getFunc())
                    )
                    return repeat

            elif cmd == "debug":
                self.runDebugMode(sym, fname, args)

            elif cmd == "check":
                cmds = ":".join(cmdset)
                ret = self.checkFilterCond(cmds, args, sym, fname)

                # broadcast event #
                if ret:
                    skip = False
                    params = cmdset[1].split(":")
                    if len(params) > 4:
                        SysMgr.broadcastEvent(params[4])
                else:
                    skip = True

                # change color for False #
                if ret:
                    ret = convColor(ret, "GREEN")
                else:
                    ret = convColor(ret, "RED")

                _addPrint("\n[%s] %s = %s" % (cmdstr, cmdset[1], ret))

                if skip:
                    raise UserWarning
                else:
                    return ret

            elif cmd == "setinter":
                if len(cmdset) == 1:
                    _printCmdErr(cmdval, cmd)

                # set timestamp #
                func = cmdset[1]

                if not func in self.intervalList:
                    # get function addr #
                    ret = self.getAddrBySymbol(func)
                    if not ret:
                        SysMgr.printErr(
                            "no function named %s for %s" % (func, _getFunc())
                        )
                        sys.exit(-1)

                    # check breakpoint #
                    addr = ret[0][0]
                    if not addr in self.bpList:
                        SysMgr.printErr(
                            "no breakpoint for %s for %s" % (func, _getFunc())
                        )
                        sys.exit(-1)

                    # set command for last function #
                    if not self.bpList[addr]["cmd"]:
                        self.bpList[addr]["cmd"] = []
                    if not "_interval" in self.bpList[addr]["cmd"]:
                        self.bpList[addr]["cmd"].append("getinter")

                    _addPrint(
                        "\n[%s] %s ~ %s(%s)"
                        % (cmdstr, sym, func, hex(addr).rstrip("L"))
                    )

                    self.intervalList.setdefault(func, {})

                self.intervalList[func][sym] = self.vdiff

            elif cmd == "getinter":
                if sym in self.intervalList:
                    for name, ts in self.intervalList[sym].items():
                        diff = self.vdiff - ts
                        diffstr = "%.6f" % diff

                        # convert time color #
                        if diff > self.retTime:
                            diffstr = UtilMgr.convColor(diffstr, "RED")
                        else:
                            diffstr = UtilMgr.convColor(diffstr, "CYAN")

                        _addPrint(
                            "\n[%s] %s -> %s ~ %s"
                            % (cmdstr, diffstr, name, sym)
                        )

            elif cmd == "inter":
                # get diff #
                if sym in self.interList:
                    val = self.vdiff - self.interList[sym]["time"]
                    self.interList[sym]["time"] = self.vdiff
                    self.interList[sym]["total"] += val
                else:
                    val = 0

                # accumulate values #
                self.interList.setdefault(
                    sym,
                    dict(
                        {
                            "time": self.vdiff,
                            "cnt": 0,
                            "total": val,
                            "min": float(val),
                            "max": float(val),
                        }
                    ),
                )

                self.interList[sym]["cnt"] += 1
                if (
                    self.interList[sym]["min"] == 0
                    or self.interList[sym]["min"] > val
                ):
                    self.interList[sym]["min"] = val
                if self.interList[sym]["max"] < val:
                    self.interList[sym]["max"] = val

                # get variables #
                cnt = self.interList[sym]["cnt"]
                total = self.interList[sym]["total"]
                avg = total / cnt
                vmin = self.interList[sym]["min"]
                vmax = self.interList[sym]["max"]

                # convert time color #
                itime = "%.6f" % val
                if val > self.retTime:
                    itime = UtilMgr.convColor(itime, "RED")

                _addPrint(
                    (
                        "\n[%s] %s {cnt: %s / avg: %.6f / "
                        "min: %.6f / max: %.6f / total %.6f}"
                    )
                    % (cmdstr, itime, convNum(cnt), avg, vmin, vmax, total)
                )

            elif cmd in ("acc", "dist"):
                if len(cmdset) == 1:
                    _printCmdErr(cmdval, cmd)

                # get argument info #
                memset = cmdset[1].split(":")

                # get name #
                name = memset[0]

                # convert args from variable list #
                memset = self.convRetArgs(memset)

                # get value #
                if len(memset) > 1:
                    data = memset[1]

                    # args #
                    if isinstance(data, (int, long)):
                        pass
                    elif data.isdigit() and long(data) < len(args):
                        data = args[long(data)]
                else:
                    data = "1"

                # convert value #
                val = UtilMgr.convStr2Num(data)
                if val is None:
                    return repeat

                # accumulate values #
                self.accList.setdefault(
                    name, dict({"cnt": 0, "total": 0, "min": val, "max": val})
                )

                self.accList[name]["cnt"] += 1
                self.accList[name]["total"] += val
                if self.accList[name]["min"] > val:
                    self.accList[name]["min"] = val
                if self.accList[name]["max"] < val:
                    self.accList[name]["max"] = val

                # get variables #
                cnt = self.accList[name]["cnt"]
                total = self.accList[name]["total"]
                avg = long(total / cnt)
                vmin = self.accList[name]["min"]
                vmax = self.accList[name]["max"]

                if cmd == "dist":
                    try:
                        # pylint: disable-next=used-before-assignment
                        idx = long(math.sqrt(val))
                    except SystemExit:
                        sys.exit(0)
                    except:
                        try:
                            # import math globally #
                            import math

                            idx = long(math.sqrt(val))
                        except SystemExit:
                            sys.exit(0)
                        except:
                            SysMgr.printErr(
                                "failed to import python package: math"
                            )
                            idx = 0
                    finally:
                        self.accList[name].setdefault("dist", {})
                        self.accList[name]["dist"].setdefault(idx, 0)
                        self.accList[name]["dist"][idx] += 1
                        dist = self.accList[name]["dist"]
                else:
                    dist = ""

                _addPrint(
                    (
                        "\n[%s] %s: %s(%s) "
                        "{cnt: %s / total: %s / avg: %s / "
                        "min: %s / max: %s} %s"
                    )
                    % (
                        cmdstr,
                        name,
                        hex(val).rstrip("L"),
                        val,
                        convNum(cnt),
                        convNum(total),
                        convNum(avg),
                        convNum(vmin),
                        convNum(vmax),
                        dist,
                    )
                )

            elif cmd == "pyscript":
                if len(cmdset) == 1:
                    _printCmdErr(cmdval, cmd)

                # get argument info #
                memset = cmdset[1].split(":")
                if len(memset) < 2:
                    _printCmdErr(cmdval, cmd)

                # get function items #
                path, func = memset[:2]
                if len(memset) > 2:
                    argset = memset[2:]
                else:
                    argset = []

                # call function #
                ret = _execFunc(UtilMgr.callPyFunc, path, func, argset)
                if res:
                    res = UtilMgr.convColor(res, "GREEN")
                else:
                    res = UtilMgr.convColor(res, "RED")

                output = "\n[%s] %s(%s)[%s] = %s" % (
                    cmdstr,
                    func,
                    argset,
                    path,
                    res,
                )
                _addPrint(output)

            elif cmd == "start":
                _addPrint("\n[%s]\n" % (cmdstr))
                _flushPrint(newline=False)

                # update status flag #
                self.startProfTime = self.vdiff

                # inject breakpoints #
                self.loadSymbols()
                self.updateBpList()

                repeat = False

            elif cmd == "show":
                if self.showStatus:
                    prevStr = ""
                else:
                    isRet = True if sym.endswith(Debugger.RETSTR) else False

                    (
                        tinfo,
                        diffstr,
                        arg,
                        argstr,
                        btstr,
                        indent,
                        symColor,
                    ) = self.getBpContext(sym, self.pc, self.readArgs(), isRet)

                    prevStr = " %s %s%s%s" % (
                        diffstr,
                        tinfo,
                        UtilMgr.convColor(sym, symColor),
                        argstr,
                    )

                # show tracing #
                self.showStatus = True

                _addPrint("\n[%s]%s" % (cmdstr, prevStr))
                _flushPrint(newline=False)

            elif cmd == "hidden":
                if not sym in self.hiddenList:
                    _addPrint("\n[%s]" % (cmdstr))
                    _flushPrint(newline=False)

                    # hide function #
                    self.hiddenList[sym] = True

            elif cmd == "hide":
                _addPrint("\n[%s]" % (cmdstr))
                _flushPrint(newline=False)

                # hide tracing #
                self.showStatus = False

            elif cmd == "repeat":
                if sym in self.repeatCntList:
                    pass
                elif len(cmdset) == 2:
                    cnt = long(cmdset[1]) + 1
                    self.repeatCntList.setdefault(sym, cnt)

                again = True

                # check repeat count #
                try:
                    self.repeatCntList[sym] -= 1
                    if self.repeatCntList[sym] == 0:
                        self.repeatCntList.pop(sym, None)
                        again = False
                    rstr = ": %s" % convNum(self.repeatCntList[sym])
                except SystemExit:
                    sys.exit(0)
                except:
                    rstr = ""

                # set breakpoint again #
                if again:
                    # save register set #
                    self.regList[sym] = self.getRegs(new=True)

                    # set a breakpoint at return address #
                    ret = self.setRetBp(sym, fname)
                    if not ret:
                        SysMgr.printErr(
                            "failed to set breakpoint to return address for %s"
                            % _getFunc()
                        )

                output = "\n[%s] %s%s" % (cmdstr, sym, rstr)
                _addPrint(output)

            elif cmd == "map":
                SysMgr.addPrint("\n[%s]" % cmdstr)
                _flushPrint(False)
                PageAnalyzer.printMemoryArea(
                    self.pid,
                    comm=self.comm,
                    lastLine=True,
                    showall=SysMgr.showAll,
                )

            elif cmd == "exec":
                if len(cmdset) == 1:
                    _printCmdErr(cmdval, cmd)

                _addPrint("\n[%s] %s\n" % (cmdstr, "; ".join(cmdset[1:])))
                _flushPrint(newline=False)

                # execute commands #
                for item in cmdset[1].split(";"):
                    command = item.strip()
                    if command.endswith("&"):
                        command = command[:-1]
                        wait = False
                    else:
                        wait = True

                    # convert variables #
                    for f, t in [("PID", str(self.pid))]:
                        command = command.replace(f, t)

                    param = command.split()
                    ret = _execFunc(SysMgr.execBgCmd, param, False, wait)

            elif cmd == "thread":
                ret = self.loadPyLib()
                if not ret:
                    return

                self.initPyLib()

                # init thread objects #
                origComm = self.comm
                origPid = self.pid
                self.remoteUsercall("PyEval_InitThreads")
                mainState = self.remoteUsercall("PyEval_SaveThread")
                gilState = self.remoteUsercall("PyGILState_Ensure")

                # create a new python thread #
                string = (
                    "import sys, time, threading\n"
                    "def func():\n"
                    "\twhile 1:\n"
                    "\t\ttime.sleep(1)\n"
                    "tobj = threading.Thread(target=func)\n"
                    "tobj.daemon = True\n"
                    "tobj.start();"
                )

                # get count #
                if len(cmdset) > 1:
                    cnt = long(cmdset[1])
                else:
                    cnt = 1

                for i in range(cnt):
                    ret = self.remotePyCall(string=string, wait=True)
                    if self.pid != origPid:
                        _addPrint(
                            "\n[%s] %s(%s) -> %s(%s) #%s"
                            % (
                                cmdstr,
                                origComm,
                                origPid,
                                self.comm,
                                self.pid,
                                i,
                            )
                        )
                        sys.exit(0)

                # release thread objects #
                if self.pid == origPid:
                    self.remoteUsercall("PyGILState_Release", [gilState])
                    self.remoteUsercall("PyEval_RestoreThread", [mainState])
                    # self.finishPyLib()

            elif cmd == "add":
                if len(cmdset) == 1:
                    _printCmdErr(cmdval, cmd)

                # get argument info #
                memset = cmdset[1].split(";")

                _addPrint("\n[%s] %s\n" % (cmdstr, cmdset[1]))
                _flushPrint(newline=False)

                # inject specific brea points #
                self.loadSymbols()
                self.injectBpList(memset)

            elif cmd == "remove":
                if len(cmdset) == 1:
                    _printCmdErr(cmdval, cmd)

                # get argument info #
                memset = cmdset[1].split(";")

                _addPrint("\n[%s] %s\n" % (cmdstr, cmdset[1]))
                _flushPrint(newline=False)

                self.removeAllBp(target=memset)

            elif cmd in ("pystr", "pyfile"):
                if len(cmdset) == 1:
                    _printCmdErr(cmdval, cmd)

                # get argument info #
                memset = cmdset[1].split(":")
                source = memset[0]
                if len(memset) > 1:
                    sync = memset[1]
                    if not sync:
                        sync = True
                    elif sync.upper() == "FALSE":
                        sync = False
                    else:
                        sync = True
                else:
                    sync = True

                # remove all breakpoints #
                self.removeAllBp(verb=False)

                ret = self.loadPyLib()
                if not ret:
                    return

                self.initPyLib()

                _addPrint("\n[%s] %s [sync=%s]" % (cmdstr, source, sync))

                # call python #
                if cmd == "pystr":
                    self.remotePyCall(string=source, wait=sync)
                elif cmd == "pyfile":
                    self.remotePyCall(script=source, wait=sync)

                self.finishPyLib()

                # inject all breakpoints again #
                self.loadSymbols()
                self.updateBpList(verb=False)

            elif cmd == "dump":
                if len(cmdset) == 1:
                    _printCmdErr(cmdval, cmd)

                # get argument info #
                memset = cmdset[1].split(":")
                if len(memset) != 2:
                    _printCmdErr(cmdval, cmd)

                # dump memory #
                meminfo, output = memset
                size = self.dumpMemory(meminfo, output, verb=False)
                if size == 0:
                    res = "fail"
                else:
                    res = "success"

                sizestr = UtilMgr.convSize2Unit(size)

                _addPrint(
                    "\n[%s] %s(%s)->%s (%s)"
                    % (cmdstr, meminfo, sizestr, output, res)
                )

            elif cmd == "save":
                if len(cmdset) == 1:
                    _printCmdErr(cmdval, cmd)

                cmdlist = cmdset[1].split(":")
                var = cmdlist[0]

                # set target data #
                if len(cmdlist) == 1:
                    data = self.prevReturn
                else:
                    data = cmdlist[1]
                    if not data:
                        data = self.prevReturn
                    elif data.startswith("@") and len(data) > 1:
                        vardata = self.retList.get(data[1:])
                        if vardata:
                            data = vardata

                    # convert type #
                    if len(cmdlist) == 3:
                        dtype = cmdlist[2]
                        if dtype == "arg":
                            data = args[long(data)]
                        elif dtype in ("float", "double"):
                            data = float(data)
                        elif dtype == "string":
                            data = str(data)

                self.retList[var] = data

                # convert to hex format #
                try:
                    hexData = "(%s)" % hex(long(data)).rstrip("L")
                except SystemExit:
                    sys.exit(0)
                except:
                    hexData = ""

                # convert data format #
                try:
                    data = repr(data)
                except:
                    pass

                output = "\n[%s] %s = %s%s" % (cmdstr, var, data, hexData)
                _addPrint(output)

            elif cmd == "load":
                if len(cmdset) == 1:
                    _printCmdErr(cmdval, cmd)

                # remove all breakpoints #
                self.removeAllBp(verb=False)

                # convert args from variable list #
                cmdset = self.convRetArgs(cmdset)

                # get function info #
                binary = cmdset[1]
                ret = _execFunc(self.dlopen, binary)
                if ret is None:
                    ret = "FAIL"
                else:
                    # update return #
                    self.prevReturn = str(ret)
                    ret = hex(ret).rstrip("L")

                output = "\n[%s] %s [%s]" % (cmdstr, binary, ret)
                _addPrint(output)
                _flushPrint(newline=False)

                # inject all breakpoints again #
                self.loadSymbols()
                self.updateBpList(verb=False)

            elif cmd == "syscall":
                if len(cmdset) == 1:
                    _printCmdErr(cmdval, cmd)

                # get function info #
                func = cmdset[1].split("#")
                val = func[0]
                if len(func) > 1:
                    argList = func[1:]

                    # convert args from variable list #
                    argList = self.convRetArgs(argList)

                    argStr = ", ".join(list(map(str, argList)))
                    argStr = "(%s)" % argStr
                else:
                    argList = []
                    argStr = "()"

                output = "\n[%s] %s%s" % (cmdstr, val, argStr)
                _addPrint(output)
                _flushPrint(newline=False)

                # remove a breakpoint for syscall #
                self.removeBp(self.getSyscallAddr(), lock=True)

                # call function #
                ret = _execFunc(self.remoteSyscall, val, argList)
                if ret is None:
                    ret = 0

                # update return #
                self.retList[val] = str(ret)
                self.prevReturn = str(ret)

                _addPrint(" = %s(%s)" % (hex(ret).rstrip("L"), ret))
                _flushPrint(newline=False)

                # inject a breakpoint for syscall again #
                self.injectBp(self.getSyscallAddr())

            elif cmd == "usercall":
                if len(cmdset) == 1:
                    _printCmdErr(cmdval, cmd)

                prevRet = None

                # handle C++ instance method #
                usercalls = cmdset[1].split("->")
                for usercall in usercalls:
                    # get function info #
                    func = usercall.split("#")
                    val = func[0]
                    if len(func) > 1:
                        argList = func[1:]

                        # convert args from variable list #
                        argList = self.convRetArgs(argList)

                        argStr = ", ".join(list(map(str, argList)))
                        argStr = "(%s)" % argStr
                    else:
                        argList = []
                        argStr = "()"

                    # add this address for C++ instance method #
                    if prevRet is not None:
                        argList.insert(0, prevRet)

                    # get address and file path #
                    path = ""
                    addr = UtilMgr.convStr2Num(val, verb=False)
                    if addr:
                        # get symbol #
                        ret = self.getSymbolInfo(addr)
                        if ret:
                            val = ret[0]
                            path = ret[1]
                    else:
                        # address #
                        ret = self.getAddrBySymbol(val, one=True)
                        if not ret:
                            SysMgr.printErr(
                                "no found %s for %s" % (val, _getFunc())
                            )
                            return repeat
                        addr = ret

                        # file path #
                        if addr:
                            ret = self.getSymbolInfo(addr)
                            if ret and ret[1]:
                                path = "/%s" % os.path.basename(ret[1])

                    output = "\n[%s] %s[0x%x%s]%s" % (
                        cmdstr,
                        val,
                        addr,
                        path,
                        argStr,
                    )

                    # check skip condition #
                    if sym == val or self.pc == addr:
                        skip = True
                        output = "%s (SKIP)" % output
                    else:
                        skip = False

                    # remove all breakpoints #
                    if not skip:
                        self.removeAllBp(verb=False)

                    _addPrint(output)
                    _flushPrint(newline=False)

                    if not skip:
                        # call function #
                        ret = _execFunc(self.remoteUsercall, addr, argList)
                        if ret is None:
                            ret = 0

                        # save return value for next C++ method call #
                        if len(usercalls) > 1:
                            prevRet = ret

                        # update return #
                        self.retList[val] = str(ret)
                        self.prevReturn = str(ret)

                        _addPrint(" = %s(%s)" % (hex(ret).rstrip("L"), ret))
                        _flushPrint(newline=False)

                        # inject all breakpoints again #
                        self.updateBpList(verb=False)

            elif cmd == "jump":
                if len(cmdset) == 1:
                    _printCmdErr(cmdval, cmd)

                # get function info #
                func = cmdset[1].split("#")
                val = func[0]
                if len(func) > 1:
                    argList = func[1:]

                    # convert args from variable list #
                    argList = self.convRetArgs(argList)

                    argStr = ", ".join(list(map(str, argList)))
                    argStr = "(%s)" % argStr
                else:
                    argList = []
                    argStr = "()"

                # convert arguments #
                argList, freelist = self.convRemoteArgs(argList)
                argList = list(map(long, argList))

                # get address #
                addr = UtilMgr.convStr2Num(val, verb=False)
                if addr is None:
                    ret = self.getAddrBySymbol(val, one=True)
                    if not ret:
                        SysMgr.printErr(
                            "no found %s for %s" % (val, _getFunc())
                        )
                        return repeat
                    addr = ret

                ret = self.getSymbolInfo(self.pc)
                if ret:
                    symbol = ret[0]
                else:
                    symbol = "??"

                output = "\n[%s] %s[0x%x] -> %s[0x%x]%s" % (
                    cmdstr,
                    symbol,
                    self.pc,
                    val,
                    addr,
                    args,
                )

                if sym == val or self.pc == addr:
                    skip = True
                    output = "%s (SKIP)" % output
                else:
                    skip = False

                _addPrint(output)

                # set register values #
                if not skip:
                    self.setPC(addr)
                    self.writeArgs(argList)
                    self.setRegs()
                    self.updateRegs()

            elif cmd == "rdfd":
                if len(cmdset) != 2 or not cmdset[1].isdigit():
                    _printCmdErr(cmdval, cmd)

                # get fd #
                arg = long(cmdset[1])
                fd = args[arg] if sym else arg

                # get path #
                try:
                    path = self.readFdPath(fd)
                    if Debugger.envFlags["PRINTFILE"]:
                        self.addFileAccess(sym, path)
                except:
                    path = "??"

                _addPrint("\n[%s] %s> %s" % (cmdstr, fd, path))
                _flushPrint(newline=False)

            elif cmd == "sleep":
                if len(cmdset) == 1:
                    val = 1
                else:
                    val = float(cmdset[1])

                _addPrint(
                    "\n[%s] %s sec" % (cmdstr, UtilMgr.convFloat2Str(val))
                )
                _flushPrint(newline=False)

                time.sleep(val)

                # add elapsed time #
                self.dvalue += val

            elif cmd == "setenv":
                if len(cmdset) == 1:
                    _printCmdErr(cmdval, cmd)

                # get env info #
                envs = cmdset[1].split("#")
                if len(envs) != 2:
                    _printCmdErr(cmdval, cmd)
                else:
                    val = envs[0]
                    argList = envs

                # convert args from variable list #
                argList = self.convRetArgs(argList)
                argStr = " = ".join(list(map(str, argList)))

                output = "\n[%s] %s" % (cmdstr, argStr)

                # remove all breakpoints #
                self.removeAllBp(verb=False)

                _addPrint(output)
                _flushPrint(newline=False)

                # call function #
                ret = self.setenv(argList[0], argList[1])
                if ret == 0:
                    res = "success"
                else:
                    res = "fail"

                # update return #
                self.retList[val] = str(ret)
                self.prevReturn = str(ret)

                _addPrint(" (%s)" % res)
                _flushPrint(newline=False)

                # inject all breakpoints again #
                self.updateBpList(verb=False)

            elif cmd == "getenv":
                if len(cmdset) == 1:
                    _printCmdErr(cmdval, cmd)

                # get env #
                val = cmdset[1]
                argList = [val]

                # convert args from variable list #
                argList = self.convRetArgs(argList)
                val = argList[0]

                output = "\n[%s] %s" % (cmdstr, val)

                # remove all breakpoints #
                self.removeAllBp(verb=False)

                _addPrint(output)
                _flushPrint(newline=False)

                # call function #
                ret = self.getenv(val)

                # update return #
                self.retList[val] = str(ret)
                self.prevReturn = str(ret)

                _addPrint(" = %s" % ret)
                _flushPrint(newline=False)

                # inject all breakpoints again #
                self.updateBpList(verb=False)

            elif cmd == "stop":
                if len(cmdset) > 1:
                    memset = cmdset[1].split(";")
                else:
                    memset = None

                _addPrint("\n[%s]\n" % (cmdstr))
                _flushPrint(newline=False)

                SysMgr.blockSignal(act="unblock")

                if memset:
                    SysMgr.sendSignalProcs(signal.SIGSTOP, memset)

                SysMgr.waitEvent(exit=True)

            elif cmd == "kill":
                _addPrint("\n[%s]\n" % (cmdstr))
                _flushPrint(newline=False)
                self.kill()
                sys.exit(0)

            elif cmd == "log":
                if len(cmdset) == 1:
                    _printCmdErr(cmdval, cmd)

                # get message #
                val = cmdset[1]

                _addPrint("\n[%s] %s" % (cmdstr, val))

            elif cmd == "condexit":
                if self.startProfTime:
                    diff = self.vdiff - self.startProfTime
                    diff = convColor("%.6f" % diff, "RED")
                    _addPrint("\n[%s] %s\n" % (cmdstr, diff))
                    sys.exit(0)

            elif cmd == "exit":
                _addPrint("\n[%s]\n" % (cmdstr))
                sys.exit(0)

            elif cmd == "help":
                _addPrint("\n[%s]\n" % (cmdstr))
                SysMgr.printHelp(force=True, isExit=False)

            else:
                raise Exception("no command supported")

            return repeat

        # check command list #
        if not isinstance(cmdList, list):
            return cmdList

        newCmdList = []

        convNum = UtilMgr.convNum

        # disable stream #
        origin = SysMgr.streamEnable
        if origin and SysMgr.outPath:
            SysMgr.streamEnable = False

        for cmdval in cmdList:
            # parse cmd set #
            cmdset = cmdval.split(":", 1)
            cmd = cmdset[0]

            # execute a command #
            try:
                repeat = _handleCmd(cmdset, cmd)
            except UserWarning:
                newCmdList.append(cmdval)
                break
            except SystemExit:
                _flushPrint()
                sys.exit(0)
            except:
                _flushPrint()
                SysMgr.printErr("failed to handle '%s' command" % cmd, True)
                if isExit:
                    sys.exit(-1)

            # re-register command #
            if repeat:
                newCmdList.append(cmdval)

        # recovery stream #
        SysMgr.streamEnable = origin

        _flushPrint(False)

        return newCmdList

    def setTraceme(self):
        # WARN: This requires CAP_SYS_PTRACE with PTRACE_TRACEME #
        cmd = ConfigMgr.PTRACE_TYPE.index("PTRACE_TRACEME")
        ret = self.ptrace(cmd)
        if ret != 0:
            if not self.comm:
                self.comm = SysMgr.getComm(self.pid, cache=True, save=True)
            SysMgr.printWarn(
                "failed to apply PTRACE_TRACEME for %s(%s) because %s"
                % (self.comm, self.pid, self.errmsg),
                True,
            )
        return ret

    def execute(self, execCmd, mute=True):
        # check mute flag #
        if Debugger.envFlags["UNMUTE"]:
            mute = False

        # create a new process #
        pid = SysMgr.createProcess()
        if pid == 0:
            # update pid #
            self.pid = os.getpid()

            # set tracee flag #
            self.setTraceme()

            # execute #
            # WARN: This requires CAP_SYS_PTRACE with PTRACE_TRACEME #
            SysMgr.executeProcess(cmd=execCmd, mute=mute)

            # execute fail #
            os._exit(0)
        else:
            self.pid = pid

    def removeBpFileByAddr(self, addr):
        fname = self.getFileFromMap(addr)
        if not fname:
            return

        fcache = ElfAnalyzer.getObject(fname)
        if not fcache:
            return

        for item in fcache.sortedAddrTable:
            self.removeBp(addr + item, lock=True)

    def removeAllBp(self, tgid=None, verb=True, target=[]):
        if not self.bpList:
            return
        # check fault flag from shared memory #
        elif self.getFaultFlag():
            return

        # get tgid #
        if not tgid:
            tgid = self.pid

        if verb:
            SysMgr.printStat(
                r"start removing %s breakpoints from %s(%s) process..."
                % (
                    UtilMgr.convNum(len(self.bpList)),
                    SysMgr.getComm(tgid, cache=True),
                    tgid,
                )
            )

        # remove all breakpoints #
        for idx, addr in enumerate(list(self.bpList)):
            if verb:
                UtilMgr.printProgress(idx, len(self.bpList))

            # check target filter #
            if target and self.bpList[addr]["symbol"]:
                skip = False
                sym = self.bpList[addr]["symbol"]

                for item in target:
                    if item.startswith("^"):
                        if UtilMgr.isValidStr(sym, [item[1:]]):
                            skip = True
                            break
                    else:
                        if not UtilMgr.isValidStr(sym, [item]):
                            skip = True
                            break

                if skip:
                    continue

            self.removeBp(addr)

        if verb:
            UtilMgr.deleteProgress()

    def removeBp(self, addr, lock=False):
        try:
            savedData = self.bpList[addr]["data"]
        except:
            SysMgr.printWarn(
                "no breakpoint with addr %s" % hex(addr).rstrip("L")
            )
            return None

        # lock between processes #
        if lock and addr in self.bpList:
            nrLock = self.bpList[addr]["number"]
            self.lock(nrLock)
        else:
            lock = False

        # write original data #
        if savedData and not savedData.startswith(self.brkInst):
            self.writeMem(addr, savedData, skipCheck=True)

        if lock:
            self.unlock(nrLock)

        # change breakpoint status #
        self.bpList[addr]["set"] = False

        data = self.bpList[addr]
        symbol = data["symbol"]
        filename = data["filename"]
        reins = data["reins"]

        if Debugger.envFlags["TRACEBP"]:
            SysMgr.printWarn(
                "removed the breakpoint %s(%s) by %s(%s)"
                % (hex(addr).rstrip("L"), symbol, self.comm, self.pid),
                True,
            )

        return (symbol, filename, reins)

    def convFilterValue(self, origCmdSet):
        cmdSet = []
        for cmd in origCmdSet:
            cmds = cmd.split(":")
            if (
                cmds[0] != "filter"
                or len(cmds) < 4
                or (cmds[1] and cmds[1][0] in ("*", "#"))
            ):
                cmdSet.append(cmd)
                continue

            val = cmds[3]
            if val.isdigit():
                val = long(val)
            else:
                try:
                    val = long(val, 16)
                except SystemExit:
                    sys.exit(0)
                except:
                    try:
                        val = float(val)
                    except SystemExit:
                        sys.exit(0)
                    except:
                        SysMgr.printErr(
                            "failed to recognize '%s' as a number" % val, True
                        )
                        sys.exit(-1)

            cmds[3] = str(val)
            newCmds = ":".join(cmds)
            cmdSet.append(newCmds)

        return cmdSet

    def injectDefaultBp(self):
        printld = "PRINTLD" in SysMgr.environList

        # add default breakpoints such as mmap #
        for lib in list(self.dftBpFileList):
            # add all symbols of loader #
            if (
                not self.isRunning
                and not self.ldInjected
                and os.path.basename(lib).startswith("ld-")
            ):
                ret = self.getAddrBySymbol("", binary=[lib], inc=True)

                for item in ret:
                    ldaddr, ldsym, ldlib = item
                    ret = self.injectBp(ldaddr, ldsym, fname=ldlib, reins=True)
                    if not printld and ret:
                        # register exceptional address #
                        self.exceptBpList[ldaddr] = 0

                self.ldInjected = True
                continue

            # add specific default symbols #
            for dsym in list(self.dftBpSymList):
                ret = self.getAddrBySymbol(dsym, binary=[lib])
                if not ret:
                    continue

                addr = ret[0][0]
                ret = self.injectBp(addr, dsym, fname=lib, reins=True)
                if not printld and ret:
                    # register exceptional address #
                    self.exceptBpList[addr] = 0

    def getBpList(self, symlist, binlist=None, verb=True):
        if not symlist:
            symlist.append("**")
        else:
            oldlist = list(symlist)
            symlist = []
            for sym in oldlist:
                if sym.startswith("|"):
                    symlist.append("**%s" % sym)
                else:
                    symlist.append(sym)

        cmdList = []
        newlist = []
        addrList = []

        # check start command #
        if self.startProfTime:
            for value in symlist:
                valueList = value.split("|")
                if len(valueList) > 1 and valueList[1].startswith("start"):
                    pass
                else:
                    newlist.append(value)
        else:
            for value in symlist:
                valueList = value.split("|")
                if len(valueList) > 1 and valueList[1].startswith("start"):
                    newlist.append(value)

        # update symbol list #
        if newlist:
            symlist = newlist

        # add breakpoints requested by user #
        for value in symlist:
            # parse symbol and commands #
            valueList = value.split("|")
            value = valueList[0]
            if len(valueList) > 1:
                # convert value to decimal #
                cmdSet = self.convFilterValue(valueList[1:])
            else:
                cmdSet = None

            # address #
            if UtilMgr.isNumber(value):
                try:
                    addr = long(value, 16)
                except:
                    addr = long(value)
                ret = self.getSymbolInfo(addr)
                addrList.append([addr, ret[0], ret[1]])
                cmdList.append(cmdSet)
                continue

            # symbol #
            symbol, inc, start, end = ElfAnalyzer.getFilterFlags(value)
            ret = self.getAddrBySymbol(
                symbol, binary=binlist, inc=inc, start=start, end=end
            )
            if ret:
                addrList += ret
                for _ in xrange(len(ret)):
                    cmdList.append(cmdSet)
                continue

            # execution mode #
            if self.execCmd or value == "" or value in self.dftBpSymList:
                continue

            # no binary on map #
            if binlist:
                if not (set(binlist) & set(self.pmap)):
                    SysMgr.printErr(
                        "failed to find '%s' on memory map for %s(%s)"
                        % (", ".join(binlist), self.comm, self.pid)
                    )

                    PageAnalyzer.printMemoryArea(
                        self.pid, comm=self.comm, lastLine=True
                    )

                    sys.exit(-1)

            # no symbol #
            SysMgr.printErr("failed to find address for symbol '%s'" % value)

            # quit #
            if not SysMgr.forceEnable:
                sys.exit(-1)

            return addrList, cmdList

        return addrList, cmdList

    def injectBpList(self, symList, binList=None, verb=True):
        exceptList = []
        if symList:
            newList = []
            for symbol in symList:
                if not symbol.startswith("^"):
                    newList.append(symbol)
                    continue
                exceptList.append(symbol[1:])
            symList = newList

        # get address list for breakpoints #
        addrList, cmdList = self.getBpList(symList, binList, verb)
        if not addrList:
            self.injectDefaultBp()
            return

        # get exceptional address list for breakpoints #
        exceptAddrList = []
        if exceptList:
            exceptList = self.getBpList(exceptList, binList, verb)[0]
            for item in exceptList:
                exceptAddrList.append(item[0])

        # print target process name #
        if verb:
            tgid = SysMgr.getTgid(self.pid)
            SysMgr.printStat(
                r"start injecting %s breakpoints for %s(%s)..."
                % (
                    UtilMgr.convNum(len(addrList)),
                    SysMgr.getComm(tgid, cache=True),
                    tgid,
                )
            )

        # check fixed bp option #
        if addrList and Debugger.envFlags["FIXEDBP"]:
            self.removeAllBp()

        # add new breakpoints #
        for idx, item in enumerate(addrList):
            UtilMgr.printProgress(idx, len(addrList))

            if not isinstance(item, list):
                continue

            addr, symbol, fname = item
            if addr in exceptAddrList:
                continue

            # inject a breakpoint #
            self.injectBp(
                addr, symbol, fname=fname, reins=True, cmd=cmdList[idx]
            )

            # remove the address from exception list #
            self.exceptBpList.pop(addr, None)

        UtilMgr.deleteProgress()

        # inject default breakpoints #
        if not Debugger.envFlags["FIXEDBP"]:
            self.injectDefaultBp()

        return True

    def checkFilterCond(self, filterCmd, args, sym=None, fname=None):
        def _printErr(cmd):
            SysMgr.printErr(
                "wrong command '%s', input in the format {%s:%s}"
                % (cmd, "filter|check", "ADDR|REG:OP:VAL:SIZE")
            )

        if not filterCmd:
            return True

        if not isinstance(filterCmd, list):
            filterCmd = [filterCmd]

        # set function string #
        def _getFunc():
            return "%s%s" % (
                sym if sym else "",
                "(%s)" % ",".join(list(map(hex, args))) if args else "()",
            )

        result = True
        for cmd in filterCmd:
            cmdset = cmd.split(":", 1)
            if len(cmdset) == 1:
                _printErr(cmd)
                result = False
                continue

            # get argument info #
            memset = cmdset[1].split(":")
            if len(memset) < 3:
                _printErr(cmd)
                result = False
                continue

            # handle return filter #
            if memset[0] in ("RETTIME", "RETVAL"):
                if cmdset[0] != "filter":
                    SysMgr.printErr(
                        "wrong command '%s' with '%s' for %s"
                        % (cmdset[0], memset[0], _getFunc())
                    )
                    sys.exit(-1)

                # check multiple return filter #
                if sym in self.retFilterList:
                    SysMgr.printWarn(
                        "return filter is overwritten for %s" % sym
                    )

                # get return command #
                if len(memset) > 2:
                    retcmd = ":".join(memset[3:])
                else:
                    retcmd = None

                # set return filter #
                self.retFilterList[sym] = [memset, None, retcmd]
                if self.isBreakMode:
                    ret = self.setRetBp(sym, fname, cmd)
                    if not ret:
                        SysMgr.printErr(
                            "failed to set breakpoint to return address for %s"
                            % _getFunc()
                        )

                # update filter result #
                if len(filterCmd) == 1:
                    result = False
                continue

            # convert args for previous return #
            memset = self.convRetArgs(memset)
            ref = False
            addr = memset[0]
            op = memset[1]
            val = memset[2]

            # convert 1st data #
            try:
                if addr[0] == "*":
                    ref = True
                    addr = long(addr[1:])
                    if addr < len(args):
                        addr = args[addr]
                elif addr[0] == "#":
                    addr = self.readFdPath(args[long(addr[1:])])
                elif addr.isdigit():
                    addr = long(addr)
                    if addr < len(args):
                        addr = args[addr]
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr(
                    "failed to convert %s to number in filter for %s"
                    % (addr, _getFunc())
                )
                continue

            if len(memset) == 4:
                size = long(memset[3])
            else:
                size = None

            # convert 1st value to number #
            if UtilMgr.isNumber(addr):
                try:
                    addr = long(addr, 16)
                except SystemExit:
                    sys.exit(0)
                except:
                    addr = long(addr)

            # convert 2nd value to number #
            if UtilMgr.isNumber(val):
                try:
                    val = long(val, 16)
                except SystemExit:
                    sys.exit(0)
                except:
                    val = long(val)

            # get value from memory #
            if ref:
                # fixed-length string #
                if size:
                    ret = self.readMem(addr, size)
                # null-terminated string #
                else:
                    ret = self.readString(addr)
                if ret in (None, -1):
                    SysMgr.printErr(
                        "failed to read from %s for %s" % (addr, _getFunc())
                    )
                    result = False
                    continue

                ret = ret.decode()
            # get value from register #
            else:
                ret = addr

            # check value #
            # == #
            if op.upper() == "EQ":
                if ref:
                    if ret[:size] != val:
                        result = False
                        continue
                else:
                    val = str(val)[:size]
                    ret = str(ret)[:size]

                    if ret != val:
                        result = False
                        continue
            # != #
            elif op.upper() == "DF":
                if ref:
                    if ret[:size] == val:
                        result = False
                        continue
                else:
                    val = str(val)[:size]
                    ret = str(ret)[:size]

                    if ret == val:
                        result = False
                        continue

            # in #
            elif op.upper() == "INC":
                if not val in str(ret)[:size]:
                    result = False
                    continue

            # <= or >= #
            elif op.upper() in ("BT", "LT"):
                if not UtilMgr.isNumber(val):
                    _printErr(cmd)
                    result = False
                    continue

                try:
                    val = long(val)
                except SystemExit:
                    sys.exit(0)
                except:
                    val = long(val, 16)

                if op.upper() == "BT" and ret <= val:
                    result = False
                    continue
                elif op.upper() == "LT" and ret >= val:
                    result = False
                    continue

            else:
                SysMgr.printErr(
                    "failed to recognize operator '%s' for filter for %s"
                    % (op, _getFunc())
                )
                result = False
                continue

        return result

    def loadInst(self, fname, offset):
        try:
            if fname == "vdso":
                fobj = SysMgr.getVDSO(elf=False)
            else:
                fobj = SysMgr.getCachedFd(fname)

            if not fobj:
                raise Exception("N/A")

            fobj.seek(offset)

            return fobj.read(ConfigMgr.wordSize)
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                "failed to read original data from %s" % fname, reason=True
            )
            sys.exit(-1)

    def injectBp(
        self,
        addr,
        sym=None,
        fname=None,
        size=1,
        reins=False,
        cmd=None,
        origWord=None,
    ):
        procInfo = "%s(%s)" % (self.comm, self.pid)

        # skip no symbol function #
        if sym:
            if Debugger.envFlags["ONLYSYM"] and sym.startswith("0x"):
                SysMgr.printWarn(
                    "skip injecting breakpoint for no symbol function %s" % sym
                )
                return False
            elif not Debugger.envFlags["INCSECT"] and sym.startswith("["):
                SysMgr.printWarn(
                    "skip injecting breakpoint for section %s" % sym
                )
                return False

        # get original instruction #
        if addr in self.bpList:
            if self.bpList[addr]["set"]:
                # update symbol for the breakpoint address #
                if sym:
                    self.bpList[addr]["symbol"] = sym

                # print fail message #
                if Debugger.envFlags["TRACEBP"]:
                    if not sym:
                        sym = self.bpList[addr]["symbol"]
                    SysMgr.printWarn(
                        (
                            "failed to inject a breakpoint to %s(%s) for %s "
                            "because it is already injected by this task"
                        )
                        % (hex(addr).rstrip("L"), sym, procInfo),
                        True,
                    )
                    return False

            # update flags #
            origWord = self.bpList[addr]["data"]
            if self.bpList[addr]["reins"] != reins:
                self.bpList[addr]["reins"] = reins
            self.bpList[addr]["set"] = True

            # update command #
            if cmd and not self.bpList[addr]["cmd"]:
                filterCmd = []
                newCmd = list(cmd)
                for item in list(newCmd):
                    if item.startswith("filter"):
                        filterCmd.append(item)
                        newCmd.remove(item)
                cmd = newCmd

                self.bpList[addr]["cmd"] = cmd
                self.bpList[addr]["filter"] = filterCmd

        # the new breakpoint #
        else:
            # read data #
            if origWord:
                pass
            elif addr % ConfigMgr.wordSize:
                origWord = self.readMem(addr)
            else:
                origWord = self.accessMem(self.peekCmd, addr)
                if origWord > 0:
                    origWord = UtilMgr.convWord2Str(origWord)
                else:
                    origWord = None

            # check data #
            if not origWord:
                return False
            elif origWord.startswith(self.brkInst):
                SysMgr.printWarn(
                    (
                        "failed to inject a breakpoint to %s(%s) for %s "
                        "because there is no original code"
                    )
                    % (hex(addr).rstrip("L"), sym, procInfo)
                )

                ret = self.getSymbolInfo(addr)
                fname = ret[1]
                try:
                    offset = long(ret[2], 16)
                except:
                    SysMgr.printErr(
                        (
                            "failed to inject a breakpoint to %s(%s) for %s"
                            % (hex(addr).rstrip("L"), sym, procInfo)
                        ),
                        reason=True,
                    )
                    return

                # load original data from storage #
                origWord = self.loadInst(fname, offset)

            # check filter command #
            filterCmd = []
            if cmd:
                newCmd = list(cmd)
                for item in list(newCmd):
                    if item.startswith("filter"):
                        filterCmd.append(item)
                        newCmd.remove(item)
                cmd = newCmd

            # register the breakpoint #
            self.bpList[addr] = {
                "data": origWord,
                "number": len(self.bpList),
                "symbol": sym,
                "reins": reins,
                "filename": fname,
                "cmd": cmd,
                "filter": filterCmd,
                "set": True,
            }

            # register the new breakpoint after fork #
            if self.forked:
                self.bpNewList[addr] = self.bpList[addr]

        # build trap instruction #
        if size == 1:
            inst = self.brkInst
        else:
            inst = self.brkInst * size

        # update symbol #
        if not sym:
            sym = self.bpList[addr]["symbol"]

        # check instructions whether it is already injected #
        if origWord.startswith(inst):
            if addr in self.bpList and self.bpList[addr]["data"] != inst:
                origWord = self.bpList[addr]["data"]
            else:
                SysMgr.printWarn(
                    (
                        "failed to inject a breakpoint to %s(%s) for %s "
                        "because it is already injected by another task"
                    )
                    % (hex(addr).rstrip("L"), sym, procInfo)
                )
                return False

        # inject trap code #
        # WARNING: this code may cause SIGTRAP fault for other tasks #
        ret = self.writeMem(addr, inst, skipCheck=True)
        if ret < 0:
            if not self.isAlive():
                reason = "because of target termination"
            else:
                reason = "because of remote write failure"

            SysMgr.printErr(
                "failed to inject a breakpoint to %s(%s) for %s %s"
                % (hex(addr).rstrip("L"), sym, procInfo, reason)
            )

            return False
        elif ret == 0 and Debugger.envFlags["TRACEBP"]:
            SysMgr.printWarn(
                "added the new breakpoint %s(%s)[%s] by %s"
                % (hex(addr).rstrip("L"), sym, fname, procInfo),
                True,
            )

        return True

    def attach(self, pid=None, verb=False, cont=False):
        if not pid:
            pid = self.pid

        if not self.comm:
            self.comm = SysMgr.getComm(self.pid, cache=True, save=True)

        if self.checkPid(pid) < 0:
            SysMgr.printWarn(
                "failed to attach %s(%s) to guider(%s) because of wrong pid"
                % (self.comm, pid, SysMgr.pid),
                verb,
            )
            return -1

        # recover SIGCHLD #
        signal.signal(signal.SIGCHLD, signal.SIG_DFL)

        doExit = False

        while 1:
            # attach to the thread #
            ret = self.ptrace(self.attachCmd)
            if ret == 0:
                self.attached = True

                if self.stopCmd:
                    if self.stop() == 0:
                        return 0
                    else:
                        return -1

                SysMgr.printWarn(
                    "attached %s(%s) to guider(%s)"
                    % (self.comm, pid, SysMgr.pid)
                )

                return 0

            # handle error #
            tracer = SysMgr.getTracerId(pid)
            if tracer > 0:
                reason = " because it is being traced by %s(%s)" % (
                    SysMgr.getComm(tracer),
                    tracer,
                )
            elif not SysMgr.isRoot():
                reason = " because there is no root permission"
                doExit = True
            else:
                reason = " because %s" % self.errmsg

                # get status #
                stat = self.getStatList(status=True)
                if stat in ("D"):
                    # get kernel stack #
                    if not hasattr(self, "kernelFd"):
                        self.kernelFd = None
                    kstack = self.getKernelStack(retstr=True)
                    if kstack:
                        kstack = kstack.replace("\n", "")
                    reason += ", status: '%s', stack: %s" % (stat, kstack)

                # check exit condition #
                if not self.isAlive():
                    doExit = True

            # print error message #
            warnMsg = "failed to attach %s(%s) to guider(%s)%s" % (
                self.comm,
                pid,
                SysMgr.pid,
                reason,
            )

            # get alive status #
            isAlive = self.isAlive()

            # add solution for docker to error message #
            if isAlive:
                if SysMgr.isKernelTask(self.pid):
                    warnMsg += ", it's a kernel task"
                elif tracer == 0:
                    warnMsg += (
                        "\n\tif you use docker then attach "
                        "'--cap-add=SYS_PTRACE --security-opt "
                        "seccomp=unconfined' option to the run command"
                    )

            # print error message #
            SysMgr.printWarn(warnMsg, verb)

            # check return #
            if doExit:
                sys.exit(-1)
            elif not cont:
                return -1
            elif isAlive:
                time.sleep(SysMgr.waitDelay)
                continue
            else:
                sys.exit(-1)

    def stop(self, pid=None, thread=True, check=False):
        if not pid:
            pid = self.pid

        try:
            # thread #
            if thread:
                # PTRACE_INTERRUPT #
                if self.stopCmd:
                    ret = self.ptrace(self.stopCmd)
                    if ret < 0:
                        SysMgr.printErr(
                            "failed to interrupt %s(%s)"
                            % (self.comm, self.pid)
                        )
                    return ret
                # SIGSTOP #
                else:
                    ret = SysMgr.syscall(self.tkillIdx, pid, signal.SIGSTOP)

                if not check:
                    return ret

                # wait for stop status #
                if not self.isStopped():
                    SysMgr.waitChild(self.pid)

                return ret
            # process #
            else:
                os.kill(pid, signal.SIGSTOP)

                return 0
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printSigError(pid, "SIGSTOP")
            return -1

    def getauxval(self):
        # get function address #
        symbol = "getauxval"
        func = self.getAddrBySymbol(symbol, one=True)
        if not func:
            return None

        # call getauxval #
        auxList = {}
        for attr, idx in sorted(ConfigMgr.AT_TYPE.items(), key=lambda e: e[1]):
            ret = self.remoteUsercall(func, [idx])
            if ret < 0:
                SysMgr.printErr(
                    "failed to call getauxval(%s) for %s(%s)"
                    % (attr, self.comm, self.pid)
                )
                return auxList

            auxList[attr] = hex(ret)

        return auxList

    def madvise(self, addr, length, advise):
        # get function address #
        symbol = "madvise"
        func = self.getAddrBySymbol(symbol, one=True)
        if not func:
            return None

        # convert page-aligned size #
        start, end, addr, length = UtilMgr.getPageAddrs(addr, length)

        # set args #
        args = [addr, length, advise]

        # call madvise #
        ret = self.remoteUsercall(func, args)
        if ret is None or ret < 0:
            SysMgr.printErr(
                "failed to call madvise(%s, %s, %s) for %s(%s)"
                % (hex(addr), length, advise, self.comm, self.pid)
            )
            return ret

        return ret

    def free(self, addr):
        # get function address #
        symbol = "free"
        func = self.getAddrBySymbol(symbol, one=True)
        if not func:
            return None

        # set args #
        args = [addr]

        # call free #
        ret = self.remoteUsercall(func, args)
        if ret < 0:
            SysMgr.printErr(
                "failed to free %s memory for %s(%s)"
                % (hex(addr), self.comm, self.pid)
            )
            return None

        return ret

    def convRemoteArgs(self, args):
        freelist = []
        for idx, item in enumerate(deepcopy(args)):
            if not isinstance(item, str):
                continue
            elif not item.isdigit() or (
                item.startswith('"') and item.endswith('"')
            ):
                try:
                    args[idx] = long(item, 16)
                except SystemExit:
                    sys.exit(0)
                except:
                    addr = self.calloc(string=item, temp=False)
                    if not addr:
                        sys.exit(-1)

                    args[idx] = long(addr)
                    freelist.append(addr)
            else:
                args[idx] = long(item)

        return args, freelist

    def calloc(self, size=None, string=None, temp=False):
        # get function address #
        symbol = "calloc"
        func = self.getAddrBySymbol(symbol, one=True)
        if not func:
            return None

        # check size #
        if not size:
            if string:
                string = string.strip('"')
                string = string.replace("\\n", "\n")
                size = len(string) + 1
            else:
                SysMgr.printErr(
                    (
                        "failed to alloc memory for %s(%s) "
                        "because there is no size"
                    )
                    % (self.comm, self.pid)
                )
                return None

        # use a temporary page #
        if temp or "STRPAGE" in SysMgr.environList:
            addr = self.getTempPage(size=size)
        else:
            addr = None

        # use a new memory segment #
        if not addr:
            # set args #
            args = [1, size]

            # call calloc #
            addr = self.remoteUsercall(func, args)
            if addr <= 0:
                SysMgr.printErr(
                    "failed to alloc %s bytes of memory for %s(%s)"
                    % (UtilMgr.convNum(size), self.comm, self.pid)
                )
                return None

        # copy string to memory #
        if addr and string:
            ret = self.writeMem(addr, string.encode(), skipCheck=True)
            if ret == -1:
                SysMgr.printErr(
                    "failed to write '%s' to %s" % (string, hex(addr))
                )
                return None

        return addr

    def getTempPage(self, size=4096):
        # get page-aligned size #
        PAGESIZE = SysMgr.PAGESIZE
        size = long((size + PAGESIZE - 1) / PAGESIZE) * PAGESIZE

        # alloc new pages #
        if not self.tempPage or size > 4096 or "STRPAGE" in SysMgr.environList:
            self.tempPage = self.mmap(size=size)
            if self.tempPage < 0:
                self.tempPage = None

        return self.tempPage

    def dlclose(self, addr):
        # get function address #
        func = "__libc_dlclose"

        # set args #
        args = [addr]

        # call dlclose #
        ret = self.remoteUsercall(func, args)

        return ret

    def getMapFilePathFast(self, fname):
        self.loadSymbols()
        if not self.pmap:
            return None

        for path in list(self.pmap):
            if os.path.basename(path).startswith(fname):
                return path

        return None

    def isPyLoaded(self):
        if self.pyLibPath:
            return True

        # check python program #
        try:
            exePath = SysMgr.getExeName(self.pid)
            exeName = os.path.basename(exePath)
            if exeName.startswith("python"):
                self.pyLibPath = exePath
                return True
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # check memory map #
        self.pyLibPath = self.getMapFilePathFast("libpython")
        if self.pyLibPath:
            return True
        else:
            return False

    def remotePyCall(self, string=None, script=None, wait=True):
        if string:
            """
            # set args #
            self.remoteUsercall(
                "PySys_SetArgvEx", [len(sys.argv), sys.argv, 0], wait=wait)

            # check import #
            pname = self.remoteUsercall(
                "PyUnicode_FromString", [string], wait=wait)
            pmodule = self.remoteUsercall(
                "PyImport_Import", [pname], wait=wait)

            # append system path #
            paths = SysMgr.getPyConfig('path')
            paths = list(map(lambda x: "'%s'" % x, list(paths.values())))
            pystr = "import sys; sys.path.append([%s]);" % ','.join(paths)
            string = pystr + string
            if not string.endswith(';'):
                string += ';'
            """

            # execute source #
            return self.remoteUsercall(
                "PyRun_SimpleString", [string], wait=wait
            )

        if script:
            path = os.path.expanduser(script)

            # convert path #
            if not path.startswith("/"):
                current = os.path.realpath(".")
                path = "%s/%s" % (current, path)

            # check file #
            if not os.path.exists(path):
                SysMgr.printErr("failed to access '%s'" % path)
                return None

            if sys.version_info >= (3, 0):
                # open script #
                fp = self.remoteUsercall("fopen64", [path, "r"])
                if not fp:
                    SysMgr.printErr("failed to call fopen64")
                    return

                # execute file #
                return self.remoteUsercall(
                    "PyRun_SimpleFile", [fp, path], wait=wait
                )
            else:
                # open script #
                po = self.remoteUsercall("PyFile_FromString", [path, "r"])
                if not po:
                    SysMgr.printErr("failed to call PyFile_FromString")
                    return
                fp = self.remoteUsercall("PyFile_AsFile", [po])
                if not fp:
                    SysMgr.printErr("failed to call PyFile_AsFile")
                    return

                # execute file #
                return self.remoteUsercall(
                    "PyRun_SimpleFileEx", [fp, path, 1], wait=wait
                )

    def initPyLib(self):
        if self.pyInit:
            return

        # initialization #
        self.remoteUsercall("Py_Initialize")

        # set argv #
        self.remoteUsercall("PySys_SetArgv", [0, 0])

        self.pyInit = True

    def finishPyLib(self):
        self.remoteUsercall("Py_Finalize")

    def loadPyLib(self):
        if self.isPyLoaded():
            return True

        # set environment #
        if "PYTHONHOME" in os.environ:
            self.setenv("PYTHONHOME", os.environ["PYTHONHOME"], False)

        # get libpython path #
        if "LIBPYTHON" in SysMgr.environList:
            pylib = SysMgr.environList["LIBPYTHON"]
        elif SysMgr.getPyLibPath():
            pylib = SysMgr.pyLibPath
        else:
            libName = SysMgr.getPyLibPath(load=False)
            platDir = SysMgr.getPyConfig(None, "LIBPL")
            pylib = "%s/%s.so" % (platDir, libName)
            if not os.path.exists(pylib):
                SysMgr.printErr("failed to get path for python library")
                return False

        if not isinstance(pylib, list):
            pylib = [pylib]

        # load the library #
        for lib in pylib:
            self.dlopen(lib)
            if self.isPyLoaded():
                return True

        SysMgr.printErr(
            "failed to load %s for %s(%s)"
            % (",".join(pylib), self.comm, self.pid)
        )
        return False

    def addFileAccess(self, func, path, cnt=1):
        if not self.pathData:
            self.pathData["total"] = {"total": 0}
        self.pathData["total"]["total"] += cnt

        if func in self.pathData:
            self.pathData[func]["total"] += cnt
        else:
            self.pathData[func] = {"total": cnt}

        if path in self.pathData[func]:
            self.pathData[func][path] += cnt
        else:
            self.pathData[func] = {
                "total": cnt,
                path: cnt,
            }

    def dlopen(self, fname, flags=None):
        # check fname #
        if not os.path.exists(fname):
            SysMgr.printErr(
                "failed to find %s for %s(%s)" % (fname, self.comm, self.pid)
            )
            return None

        # handle android #
        if SysMgr.isAndroid:
            for lpath in (SysMgr.libcObj._name, "libc.so"):
                libcPath = FileAnalyzer.getMapFilePath(
                    self.pid, lpath, self.mapFd
                )
                if libcPath:
                    break

            # get ELF object #
            fcache = ElfAnalyzer.getObject(libcPath)
            if not hasattr(fcache, "attr"):
                SysMgr.printErr(
                    "failed to find attr from the cache for %s" % libcPath
                )
                return None

            # get libc address #
            func = 0
            targetSym = "dlopen"
            if not self.pmap or not libcPath in self.pmap:
                self.loadSymbols()
            vstart = self.pmap[libcPath]["vstart"]

            # get mapping info #
            for sym, attr in sorted(
                fcache.attr["dynsymTable"].items(),
                key=lambda x: x[1]["size"],
                reverse=False,
            ):
                if attr["size"] > 0:
                    break
                elif sym != targetSym and sym.split("@")[0] != targetSym:
                    continue

                # read original address for target #
                slotAddr = vstart + attr["value"]
                if slotAddr % ConfigMgr.wordSize == 0:
                    func = self.accessMem(self.peekCmd, slotAddr)
                else:
                    func = self.readMem(slotAddr, retWord=True)
        else:
            # get function address #
            func = "__libc_dlopen_mode"
            if not self.getAddrBySymbol(func):
                func = "dlopen"
                if not self.getAddrBySymbol(func):
                    SysMgr.printErr(
                        "failed to find '%s' on memory map for %s(%s)"
                        % (func, self.comm, self.pid)
                    )

            """
            # alloc a memory segment for file name string #
            addr = self.getTempPage()
            if not addr:
                SysMgr.printErr("failed to allocate a new page")
                return None

            # copy file name string to the new page #
            fname += '\0'
            ret = self.writeMem(addr, fname.encode())
            if ret == -1:
                SysMgr.printErr(
                    "failed to write '%s' to %s" % (fname, hex(addr)))
                return None
            """

        # RTLD_LAZY | RTLD_GLOBAL #
        if not flags:
            flags = 0x00001 | 0x00100

        # set args #
        args = [fname, flags]

        # call dlopen #
        ret = self.remoteUsercall(func, args, inc=True)
        if ret:
            self.loadSymbols()

        return ret

    def getSyscallAddr(self):
        if not self.syscallAddr:
            self.syscallAddr = self.getAddrBySymbol("syscall", one=True)
            if not self.syscallAddr:
                self.syscallFound = False

        return self.syscallAddr

    def remoteUsercall(self, usercall, args=[], wait=True, inc=False):
        # convert arguments in advance to prevent nested remote calls #
        args, freelist = self.convRemoteArgs(args)

        # get target info #
        procInfo = "%s(%s)" % (self.comm, self.pid)

        # get original regset #
        if not self.updateRegs():
            return None

        # get backup regset #
        self.backupRegs()

        """
        # change access permission on a page pointed by PC #
        ret = self.mprotect(self.pc)
        if ret == -1:
            return None
        """

        # set usercall address #
        if isinstance(usercall, (int, long)):
            func = usercall
        elif isinstance(usercall, str):
            # get function address #
            func = self.getAddrBySymbol(usercall, inc=inc, one=True)
            if not func:
                SysMgr.printErr("failed to get address of '%s'" % usercall)
                return None
        else:
            SysMgr.printErr(
                "failed to recognize '%s' as a function for %s"
                % (usercall, procInfo),
                True,
            )
            return None
        setattr(self.regs, self.retreg, func)

        # set args #
        self.writeArgs(args)

        # set trap for return #
        if self.arch in ("arm", "aarch64"):
            self.setLR(0)

            # set CPSR for ARM #
            if func & 0x1:
                func &= ~1
                rval = 1 << 5
                if self.arch == "arm":
                    self.regs.r16 |= rval  # pylint: disable=no-member
                else:
                    self.regs.x16 |= rval  # pylint: disable=no-member
            else:
                rval = ~(1 << 5)
                if self.arch == "arm":
                    self.regs.r16 &= rval  # pylint: disable=no-member
                else:
                    self.regs.x16 &= rval  # pylint: disable=no-member
        elif self.arch == "x64":
            # align sp - wordSize to a multiple of 16
            wordSize = ConfigMgr.wordSize
            newSP = self.sp
            while 1:
                if (newSP - wordSize * 2) & 0xF == 0:
                    break
                newSP -= wordSize
            newSP -= wordSize
            self.setSP(newSP)
            self.writeMem(newSP, b"\x00" * wordSize)
        elif self.arch in ("x86", "riscv64"):
            # TODO: save all args to stack and install the trap finally #
            SysMgr.printErr(
                "failed to set trap for return because %s is not supported"
                % self.arch
            )
            sys.exit(-1)

        # update PC to target function addr #
        self.setPC(func)

        # apply register set #
        self.setRegs()

        # call function #
        self.cont(check=True)
        if not wait:
            return None

        while 1:
            # wait process #
            rid, ostat = self.waitpid()

            # handle clone event #
            if SysMgr.cloneEnable and self.isCloned(ostat):
                pid = self.handoverNewTarget()
                if pid == 0:
                    return None
                continue

            break

        # read regs to check results #
        if not self.updateRegs():
            return None

        # get return #
        retVal = self.getRet()

        # restore regs #
        self.setRegs(temp=True)
        self.restoreRegs()

        # free memory for string #
        for addr in freelist:
            self.free(addr)

        return retVal

    def remoteSyscall(self, syscall, args=[], verb=True):
        # check syscall function #
        if not self.syscallFound:
            return -1

        # get original regset #
        if not self.updateRegs():
            return -1

        # get backup regset #
        self.backupRegs()

        # set syscall number #
        if isinstance(syscall, (int, long)):
            sysid = syscall
        elif isinstance(syscall, str):
            syscall = syscall.lower()
            if not syscall.startswith("sys_"):
                syscall = "sys_%s" % syscall

            if syscall == "sys_mmap":
                sysid = ConfigMgr.getMmapId()
            else:
                try:
                    sysid = SysMgr.getNrSyscall(syscall)
                except:
                    SysMgr.printErr("failed to find %s" % syscall, True)
                    return -1
        else:
            SysMgr.printErr("failed to recognize syscall %s" % syscall, True)
            return -1
        setattr(self.regs, self.retreg, sysid)

        # convert arguments #
        args, freelist = self.convRemoteArgs(args)

        # set args #
        self.writeArgs([sysid] + args)

        # set PC to syscall function addr #
        addr = self.getSyscallAddr()
        if not addr:
            if verb:
                SysMgr.printErr(
                    "failed to find the address for syscall function"
                )
            return -1
        else:
            self.setPC(addr)

        # apply register set #
        self.setRegs()

        # execute syscall #
        self.ptrace(self.syscallCmd)
        self.waitpid()

        # read regs and change the 6th argument #
        if not self.updateRegs():
            return -1
        self.writeArgs(args)
        self.setRegs()

        # continue and stop at return #
        self.ptrace(self.syscallCmd)
        self.waitpid()

        # read regs to check results #
        if not self.updateRegs():
            return -1

        # get return #
        ret = self.getRet()

        # restore regs #
        self.setRegs(temp=True)
        self.restoreRegs()

        # free memory for string #
        for addr in freelist:
            self.free(addr)

        return ret

    def getenv(self, name):
        ret = self.remoteUsercall("getenv", [name])
        if ret:
            return self.readString(ret)

    def setenv(self, name, value, overwrite=True):
        if overwrite:
            overwrite = 1
        else:
            overwrite = 0

        ret = self.remoteUsercall("setenv", [name, value, overwrite])
        if ret == -1:
            procInfo = "%s(%s)" % (self.comm, self.pid)
            SysMgr.printErr(
                "failed to set %s(%s) environment variable for %s"
                % (name, value, procInfo)
            )
        return ret

    def mmap(self, size=4096, perm="rwx"):
        # set prot #
        prot = 0
        perm = perm.lower()
        if "r" in perm:
            prot |= 0x1
        if "w" in perm:
            prot |= 0x2
        if "x" in perm:
            prot |= 0x4

        # set flags #
        flags = 0x22

        return self.remoteSyscall(
            "mmap", [0, size, prot, flags, 0, 0], verb=False
        )

    def mprotect(self, maddr, size=0, perm="rwx"):
        # check size #
        if not size:
            size = SysMgr.PAGESIZE

        # align address #
        offset = maddr % SysMgr.PAGESIZE
        if offset > 0:
            maddr -= offset

        # set prot #
        prot = 0
        perm = perm.lower()
        if "r" in perm:
            prot |= 0x1
        if "w" in perm:
            prot |= 0x2
        if "x" in perm:
            prot |= 0x4

        ret = self.remoteUsercall("mprotect", [maddr, size, prot])
        if ret != 0:
            procInfo = "%s(%s)" % (self.comm, self.pid)
            SysMgr.printWarn(
                "failed to change access permission to %s page for %s"
                % (hex(maddr), procInfo)
            )
        return ret

    def kill(self):
        cmd = ConfigMgr.PTRACE_TYPE.index("PTRACE_KILL")
        ret = self.ptrace(cmd)
        if ret != 0:
            SysMgr.printWarn(
                "failed to kill %s(%s) because %s"
                % (self.comm, self.pid, self.errmsg)
            )
            return -1
        else:
            SysMgr.printWarn("killed %s(%s)" % (self.comm, self.pid))
            return 0

    def cont(self, pid=None, check=False, sig=0):
        if not pid:
            pid = self.pid

        # check target is running #
        try:
            os.kill(pid, 0)
        except SystemExit:
            sys.exit(0)
        except:
            if not self.isAlive():
                errMsg = (
                    "failed to continue %s(%s) because it is terminated"
                    % (self.comm, pid)
                )
                SysMgr.printWarn(errMsg)
                return -1

        # check ignoring signal #
        if sig and sig in Debugger.ignoreSignals:
            sig = 0

            # skip instructions caused the signal #
            if "SKIPSIGNAL" in SysMgr.environList:
                size = UtilMgr.getEnvironNum(
                    "SKIPSIGNAL", False, ConfigMgr.wordSize, False
                )
                self.updateRegs()
                self.setPC(self.pc + size)
                self.setRegs()

        # check target status #
        if check:
            cnt = 1000
            while 1:
                ret = self.ptrace(self.contCmd, 0, sig)
                if ret == 0:
                    return 0

                cnt -= 1
                if cnt < 0:
                    break
                elif not self.isAlive():
                    SysMgr.printWarn(
                        "failed to continue %s(%s) because it is terminated"
                        % (self.comm, pid)
                    )
                    return -1
                elif self.isInRun(("R")):
                    return 0

                time.sleep(0.001)

        # continue target thread #
        ret = self.ptrace(self.contCmd, 0, sig)
        if ret != 0:
            SysMgr.printWarn(
                "failed to continue %s(%s) because %s"
                % (self.comm, pid, self.errmsg)
            )
            return -1

        return 0

    def doDetach(self, pid, check=False):
        if not pid:
            pid = self.pid

        self.attached = False
        cmd = ConfigMgr.PTRACE_TYPE.index("PTRACE_DETACH")

        while 1:
            ret = self.ptrace(cmd, pid=pid)
            if ret != 0:
                SysMgr.printWarn(
                    "failed to detach %s(%s) from guider(%s) because %s"
                    % (self.comm, pid, SysMgr.pid, self.errmsg)
                )

                # check return #
                if not check:
                    return -1
                elif self.isAlive():
                    time.sleep(SysMgr.waitDelay)
                    continue
                else:
                    sys.exit(-1)
            else:
                SysMgr.printWarn(
                    "detached %s(%s) from guider(%s)"
                    % (self.comm, pid, SysMgr.pid)
                )
                return 0

    def detach(self, only=False, pid=None, check=False):
        if only:
            return self.doDetach(pid, check=check)

        if hasattr(self, "pid"):
            pid = self.pid
        else:
            return 0

        # kill target process executed #
        if self.execCmd:
            try:
                if "KEEPALIVE" in SysMgr.environList:
                    pass
                # kill process group #
                elif os.getpgid(pid) == pid:
                    os.killpg(pid, signal.SIGTERM)
                # kill target process #
                else:
                    os.kill(pid, signal.SIGKILL)
            except:
                SysMgr.printSigError(pid, "SIGKILL")

            return 0

        # check the process is running #
        try:
            os.kill(pid, 0)
        except:
            return -1

        return self.doDetach(pid, check=check)

    def checkPid(self, pid):
        if not pid:
            return -1
        elif not UtilMgr.isNumber(pid):
            return -1
        elif long(pid) <= 0:
            return -1
        else:
            return 0

    def accessMem(self, cmd, addr, data=0):
        wordSize = ConfigMgr.wordSize

        if addr < wordSize:
            SysMgr.printWarn(
                (
                    "failed to access memory address %s for %s(%s) "
                    "because of wrong address"
                )
                % (hex(addr).rstrip("L"), self.comm, self.pid)
            )
            return -1

        if addr % wordSize:
            SysMgr.printWarn(
                (
                    "failed to access memory address %s for %s(%s) "
                    "because of unaligned address"
                )
                % (hex(addr).rstrip("L"), self.comm, self.pid)
            )
            return -1

        return self.ptrace(cmd, addr, data)

    def writeMem(self, addr, data, size=0, skipCheck=False):
        ret = 0
        wordSize = ConfigMgr.wordSize

        if addr < wordSize:
            SysMgr.printWarn(
                "failed to write to %s memory because of wrong address"
                % hex(addr).rstrip("L")
            )
            return None

        # update size #
        if size == 0 or size > len(data):
            size = len(data)

        if self.supportProcessVmWr:
            try:
                # prepare process_vm_writev syscall #
                process_vm_writev = SysMgr.libcObj.process_vm_writev

                if not self.initPvw:
                    SysMgr.libcObj.process_vm_writev.restype = c_size_t
                    SysMgr.libcObj.process_vm_writev.argtypes = [
                        c_int,
                        self.iovec_ptr,
                        c_size_t,
                        self.iovec_ptr,
                        c_size_t,
                        c_ulong,
                    ]
                    self.initPvw = True

                # create params #
                pid = self.pid

                try:
                    lbuf = (c_char * size)()
                    memmove(byref(lbuf), data, len(data))
                    liov = (self.iovec * 1)()[0]
                    liov.iov_base = cast(lbuf, c_void_p)
                    liov.iov_len = size

                    riov = (self.iovec * 1)()[0]
                    riov.iov_base = c_void_p(addr)
                    riov.iov_len = size
                except SystemExit:
                    sys.exit(0)
                except:
                    return None

                # do syscall #
                ret = process_vm_writev(pid, liov, 1, riov, 1, 0)
                if c_long(ret).value == -1:
                    self.errmsg = SysMgr.getErrReason()
                    SysMgr.printWarn(
                        "failed to process_vm_writev for %s(%s) because %s"
                        % (self.comm, self.pid, self.errmsg)
                    )
                    raise Exception()
                return ret
            except SystemExit:
                sys.exit(0)
            except:
                self.supportProcessVmWr = False

        # check address alignment #
        offset = addr % wordSize

        # handle integer-type data #
        if not skipCheck:
            if UtilMgr.isNumber(data):
                if offset == 0:
                    if size == 0:
                        size = 1
                    for idx in xrange(size):
                        ret = self.accessMem(
                            self.pokeCmd, addr + (idx * wordSize), data
                        )
                        if ret < 0:
                            break
                    return ret
                else:
                    data = UtilMgr.convWord2Str(UtilMgr.convStr2Num(data))
                    if not data:
                        return -1

                    # converting integer-type data #
                    if 0 <= size <= 1:
                        size = wordSize
                    elif size > 1:
                        data = data * size
                        size *= wordSize

            # convert string to bytes #
            if UtilMgr.isString(data):
                data = UtilMgr.encodeStr(data)
            elif not isinstance(data, bytes):
                SysMgr.printErr(
                    (
                        "failed to recognize data to write because "
                        "%s type is not supported"
                    )
                    % type(data)
                )
                return -1

        # trim data #
        modWord = len(data) % wordSize
        if modWord > 0:
            data += b"0" * (wordSize - modWord)

        # back up data #
        origData = data[:size]
        origSize = size

        # handle not aligned part #
        if offset > 0:
            addr -= offset
            size += offset

        # read words from target address space #
        data = b""
        tempAddr = addr
        while size > 0:
            word = self.readMem(tempAddr)
            if word is None:
                return -1

            data += word

            size -= wordSize
            tempAddr += wordSize

        # update original data #
        fdata = data[:offset] + origData + data[offset + origSize :]

        # convert type from bytes to word #
        for idx in xrange(0, len(fdata), wordSize):
            data = UtilMgr.convStr2Word(fdata[idx : idx + wordSize])

            ret = self.accessMem(self.pokeCmd, addr + idx, data)
            if ret == -1:
                break

        return ret

    def updateFileList(self):
        fileList = SysMgr.getOption("T")
        if fileList:
            fileList = list(set(fileList.split(",")))
            fileList = SysMgr.convRealPath(fileList)

            for path in fileList:
                if path.startswith("^"):
                    self.exceptBpFileList[path[1:]] = 0
                else:
                    self.targetBpFileList[path] = 0
        return list(self.targetBpFileList)

    def updateBpList(self, verb=True):
        if not self.isBreakMode:
            return

        # update file list #
        fileList = self.updateFileList()

        # update symbol list #
        if not SysMgr.customCmd:
            funcFilter = []
        else:
            funcFilter = list(set(SysMgr.customCmd))
            self.targetBpList.update(dict.fromkeys(funcFilter, 0))

        # add per-process breakpoints #
        return self.injectBpList(
            symList=funcFilter, binList=fileList, verb=verb
        )

    def readMem(self, addr, size=0, retWord=False, verb=True, onlyBulk=False):
        wordSize = ConfigMgr.wordSize

        if not addr:
            return None

        if addr < wordSize:
            SysMgr.printWarn(
                (
                    "failed to read memory address %s for %s(%s) "
                    "because of wrong address"
                )
                % (hex(addr).rstrip("L"), self.comm, self.pid)
            )
            return None

        # check size #
        if not size:
            size = wordSize

        if self.supportProcessVmRd:
            try:
                if not self.initPvr:
                    if not hasattr(SysMgr.libcObj, "process_vm_readv"):
                        self.supportProcessVmRd = False
                        raise Exception("no suppport process_vm_readv")

                    SysMgr.libcObj.process_vm_readv.restype = c_size_t
                    SysMgr.libcObj.process_vm_readv.argtypes = [
                        c_int,
                        self.iovec_ptr,
                        c_size_t,
                        self.iovec_ptr,
                        c_size_t,
                        c_ulong,
                    ]
                    self.initPvr = True

                # prepare process_vm_readv syscall #
                process_vm_readv = SysMgr.libcObj.process_vm_readv

                # create params #
                pid = self.pid

                try:
                    lbuf = (c_char * size)()
                    liov = (self.iovec * 1)()[0]
                    liov.iov_base = cast(byref(lbuf), c_void_p)
                    liov.iov_len = size

                    riov = (self.iovec * 1)()[0]
                    riov.iov_base = c_void_p(addr)
                    riov.iov_len = size
                except SystemExit:
                    sys.exit(0)
                except:
                    return None

                # do syscall #
                ret = process_vm_readv(pid, liov, 1, riov, 1, 0)
                ret = c_long(ret).value
                if ret == 0:
                    return None
                elif ret == -1:
                    self.errmsg = SysMgr.getErrReason()
                    SysMgr.printWarn(
                        (
                            "failed to process_vm_readv(%s+%s) "
                            "for %s(%s) because %s"
                        )
                        % (
                            hex(addr),
                            UtilMgr.convNum(size),
                            self.comm,
                            self.pid,
                            self.errmsg,
                        )
                    )
                    raise Exception()

                data = memoryview(lbuf).tobytes()
                if retWord:
                    return UtilMgr.convStr2Word(data)
                else:
                    return data
            except SystemExit:
                sys.exit(0)
            except:
                if not hasattr(SysMgr.libcObj, "process_vm_readv"):
                    self.supportProcessVmRd = False

        if self.supportProcessVmRd and onlyBulk:
            return None

        # define return list #
        data = bytes()

        # handle not aligned part #
        offset = addr % wordSize
        if offset > 0:
            addr -= offset
            size += offset

        # read words from target address space #
        while size > 0:
            # read a word #
            word = self.accessMem(self.peekCmd, addr)
            if word == -1:
                # failed to read partial area #
                if data:
                    break

                # failed to read whole area #
                if verb:
                    SysMgr.printErr(
                        "failed to read memory address %s for %s(%s)"
                        % (hex(addr).rstrip("L"), self.comm, self.pid)
                    )

                return None

            if retWord and offset == 0:
                return word

            # convert a word to a byte string #
            word = UtilMgr.convWord2Str(word)
            if not word:
                return None

            if size < wordSize:
                data += word[:size]
            else:
                data += word

            size -= wordSize
            addr += wordSize

        # return data #
        ret = data[offset:]
        if retWord:
            return UtilMgr.convStr2Word(ret)
        else:
            return ret

    def readStrList(self, pos, maxsize=sys.maxsize):
        strList = []

        while 1:
            try:
                # read start address for a string #
                addr = self.readWord(pos)
                if not addr:
                    break

                # read a string #
                string = self.readString(addr)
                if not string:
                    break

                strList.append(string.decode())
                pos += ConfigMgr.wordSize
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printWarn(
                    "failed to read string from list for %s(%s)"
                    % (self.comm, self.pid),
                    reason=True,
                )
                break

        return strList

    def readString(self, addr, chunk=256, maxsize=sys.maxsize):
        cnt = 0
        ret = b""
        maxCnt = SysMgr.maxRdCnt
        while 1:
            string = self.readMem(addr, chunk)
            if not string:
                return ret

            # check read count #
            cnt += 1
            if cnt > maxCnt:
                SysMgr.printWarn(
                    "read %s time from %s for %s(%s)"
                    % (
                        UtilMgr.convNum(cnt),
                        hex(long(addr)),
                        self.comm,
                        self.pid,
                    ),
                    True,
                )
                maxCnt *= 2

            # check string size #
            if len(ret) > maxsize:
                SysMgr.printWarn(
                    "exceed maximum size %s to read string for %s(%s)"
                    % (UtilMgr.convSize2Unit(maxsize), self.comm, self.pid),
                    True,
                )
                return ret

            # read string from target #
            try:
                idx = string.index(b"\0")
                ret += string[:idx]
                return ret
            except SystemExit:
                sys.exit(0)
            except:
                if string:
                    ret += string

                if len(ret) > SysMgr.PAGESIZE:
                    return ret

    def readMultiMsgHdr(self, addr, vlen):
        msgInfo = {}
        for idx in xrange(vlen):
            offset = idx * sizeof(self.mmsghdr)
            # read msghdr structure #
            ret = self.readMem(addr + offset, sizeof(self.mmsghdr))
            if not ret:
                continue

            # cast struct mmsghdr #
            header = cast(ret, self.mmsghdr_ptr)

            # get msg info #
            # msglen = header.contents.msg_len
            msgaddr = addressof(header.contents.msg_hdr)
            ret = self.readMsgHdr(obj=msgaddr)
            if not ret:
                continue

            # add msghdr to list #
            msgInfo[idx] = ret

        if not msgInfo:
            return addr

        return msgInfo

    def readUringParam(self, addr):
        paddr = cast(addr, c_void_p).value

        pobj = self.readMem(addr, sizeof(self.io_uring_params))
        pobj = cast(pobj, self.io_uring_params_ptr)

        params = {
            "sq_entries": pobj.contents.sq_entries * 16,
            "cq_entries": pobj.contents.cq_entries * 16,
            "flags": pobj.contents.flags,
            "sq_thread_cpu": pobj.contents.sq_thread_cpu,
            "sq_thread_idle": pobj.contents.sq_thread_idle,
            "features": UtilMgr.getFlagString(
                pobj.contents.features, ConfigMgr.IORING_PARAM_FEATURES
            ),
            "wq_fd": pobj.contents.wq_fd,
            "sq_off": {
                "head": pobj.contents.sq_off.head,
                "tail": pobj.contents.sq_off.tail,
                "ring_mask": pobj.contents.sq_off.ring_mask,
                "ring_entries": pobj.contents.sq_off.ring_entries,
                "flags": pobj.contents.sq_off.flags,
                "dropped": pobj.contents.sq_off.dropped,
                "array": pobj.contents.sq_off.array,
            },
            "cq_off": {
                "head": pobj.contents.cq_off.head,
                "tail": pobj.contents.cq_off.tail,
                "ring_mask": pobj.contents.cq_off.ring_mask,
                "ring_entries": pobj.contents.cq_off.ring_entries,
                "overflow": pobj.contents.cq_off.overflow,
                "cqes": pobj.contents.cq_off.cqes,
                "flags": pobj.contents.cq_off.flags,
            },
        }

        return params

    def readIoVec(self, addr, cnt, baseAddr=False):
        iov = {}

        # get iov header info #
        iovaddr = cast(addr, c_void_p).value

        # get iov info #
        for idx in xrange(cnt):
            offset = idx * sizeof(self.iovec)

            iov[idx] = {}

            # get iov object #
            iovobj = self.readMem(iovaddr + offset, sizeof(self.iovec))
            iovobj = cast(iovobj, self.iovec_ptr)

            # get iov size #
            iovobjlen = long(iovobj.contents.iov_len)
            iov[idx]["iov_len"] = iovobjlen
            if iovobjlen == 0:
                iov[idx]["iov_base"] = ""
                continue

            # get base addr #
            if baseAddr:
                iov[idx]["iov_base"] = hex(iovobj.contents.iov_base)
                continue

            # strip data #
            if not SysMgr.outPath and not Debugger.envFlags["NOSTRIP"]:
                iovobjdata = "STRIP"
            else:
                # get iov data #
                iovobjbase = iovobj.contents.iov_base
                iovobjdata = self.readMem(iovobjbase, iovobjlen)

            iov[idx]["iov_base"] = iovobjdata

        return iov

    def readFdPath(self, fd):
        return os.readlink("%s/%s/fd/%s" % (SysMgr.procPath, self.pid, fd))

    def readTimespec(self, addr):
        if not addr:
            return None

        if sizeof(c_void_p) == sizeof(c_uint64):
            time_t = c_uint64
        else:
            time_t = c_uint32

        ret = self.readMem(addr, sizeof(time_t) * 2)
        if not ret:
            return None

        ret = struct.unpack("ll", ret)

        return " {tv_sec=%s, tv_nsec=%s}" % (ret[0], ret[1])

    def readPollFds(self, addr, nfds):
        if not addr:
            return None

        retList = []
        for n in xrange(nfds):
            ret = self.readMem(addr, 8)
            addr += 8
            if not ret:
                continue

            items = struct.unpack("ihh", ret)
            if not items:
                continue

            fd, reqe, rete = items
            path = self.readFdPath(fd)
            reqe = UtilMgr.getFlagString(reqe, ConfigMgr.EPOLL_EVENT_TYPE)
            rete = UtilMgr.getFlagString(rete, ConfigMgr.EPOLL_EVENT_TYPE)
            retList.append(
                "{fd=%s%s%s}"
                % (
                    path,
                    (", events=%s" % reqe) if reqe else "",
                    (", revents=%s" % rete) if rete else "",
                )
            )

        return "[%s]" % ", ".join(retList)

    def readMsgHdr(self, addr=None, obj=None):
        if not addr and not obj:
            return None

        # read msghdr structure #
        if not obj:
            ret = self.readMem(addr, sizeof(self.msghdr))
            if not ret:
                return addr
        else:
            ret = obj

        # cast struct msghdr #
        msginfo = {}
        header = cast(ret, self.msghdr_ptr)

        # get msg info #
        namelen = long(header.contents.msg_namelen)
        msginfo["msg_namelen"] = namelen
        if Debugger.dbusEnable or namelen == 0 or not header.contents.msg_name:
            msginfo["msg_name"] = "NULL"
        else:
            ret = self.readMem(header.contents.msg_name, namelen)
            if ret:
                ret = ret.decode("latin-1")
            msginfo["msg_name"] = ret

        # get iov header info #
        iovaddr = cast(header.contents.msg_iov, c_void_p).value
        iovlen = long(header.contents.msg_iovlen)

        if not iovaddr:
            msginfo["msg_iov"] = {}
        elif not SysMgr.showAll:
            msginfo["msg_iov"] = "0x{0:02x}".format(iovaddr)
        else:
            msginfo["msg_iov"] = {}

            # get iov info #
            for idx in xrange(iovlen):
                offset = idx * sizeof(self.iovec)

                # get iov object #
                iovobj = self.readMem(iovaddr + offset, sizeof(self.iovec))
                iovobj = cast(iovobj, self.iovec_ptr)

                # get iov size #
                iovobjlen = long(iovobj.contents.iov_len)
                if iovobjlen == 0:
                    continue

                # get iov data #
                iovobjbase = iovobj.contents.iov_base
                iovobjdata = self.readMem(iovobjbase, iovobjlen)
                if not iovobjdata:
                    continue

                msginfo["msg_iov"][idx] = {}

                # erase message #
                skip = False
                for ignoreName in SysMgr.ignoreItemList:
                    if not ignoreName in iovobjdata:
                        continue

                    iovobjdata = iovobjdata.replace(
                        ignoreName, b"\x00" * len(ignoreName)
                    )
                    self.writeMem(iovobjbase, iovobjdata)
                    skip = True
                    break
                if skip:
                    continue

                # encode to base64 #
                if not iovobjdata:
                    pass
                elif Debugger.dbusEnable:
                    iovobjdata = UtilMgr.encodeBase64(iovobjdata)
                    if sys.version_info >= (3, 0):
                        iovobjdata = iovobjdata.decode("latin-1")
                # strip data #
                elif not SysMgr.outPath and not Debugger.envFlags["NOSTRIP"]:
                    iovobjdata = "STRIP"

                # save size and data #
                msginfo["msg_iov"][idx]["len"] = iovobjlen
                msginfo["msg_iov"][idx]["data"] = iovobjdata

        # get msg_flags #
        flag = header.contents.msg_flags
        msginfo["msg_flags"] = flag

        # ignore cmsg info #
        if Debugger.dbusEnable:
            return msginfo

        # get socket object #
        if not self.sockObj:
            socket = SysMgr.getPkg("socket", False)
            if not socket:
                return msginfo
            else:
                self.sockObj = socket

        # get control info #
        control = header.contents.msg_control
        controllen = long(header.contents.msg_controllen)
        msginfo["msg_control"] = {}
        msginfo["msg_control"]["len"] = controllen

        if not SysMgr.showAll:
            msginfo["msg_control"]["addr"] = control
        elif controllen >= sizeof(self.cmsghdr):
            # read cmsghdr #
            control = self.readMem(header.contents.msg_control, controllen)
            controlobj = cast(control, self.cmsghdr_ptr)

            # check object #
            if not controlobj:
                return msginfo

            # cmsg_len #
            cmsglen = long(controlobj.contents.cmsg_len)
            msginfo["msg_control"]["cmsglen"] = cmsglen

            # cmsg_level #
            cmsglevel = controlobj.contents.cmsg_level
            if cmsglevel == self.sockObj.SOL_SOCKET:
                msginfo["msg_control"]["cmsg_level"] = "SOL_SOCKET"
            else:
                msginfo["msg_control"]["cmsg_level"] = cmsglevel

            # cmsg_type #
            try:
                cmsgtype = controlobj.contents.cmsg_type
                msginfo["msg_control"]["cmsg_type"] = ConfigMgr.CMSG_TYPE[
                    cmsgtype
                ]
            except:
                msginfo["msg_control"]["cmsg_type"] = cmsgtype

            # cmsg_data #
            datasize = controllen - cmsglen
            if datasize > 0:
                try:
                    cmsgdata = bytes()
                    cmsgdata = self.readMem(
                        header.contents.msg_control + sizeof(self.cmsghdr),
                        datasize,
                    )

                    if len(cmsgdata) == 4:
                        cmsgdata = struct.unpack("I", cmsgdata)[0]
                    elif len(cmsgdata) == 8:
                        cmsgdata = struct.unpack("Q", cmsgdata)[0]
                    else:
                        raise Exception()
                except:
                    pass
                finally:
                    msginfo["msg_control"]["cmsg_data"] = cmsgdata

        return msginfo

    def convSyscallParam(
        self,
        argtype,
        argname,
        value,
        seq=0,
        ref=True,
        argset={},
        buf=False,
        retval=None,
    ):
        syscall = self.syscall

        """
        TODO:
        1. convert a integer or mask values
        2. process_vm_readv
        """

        # handle syscalls #
        # TODO: apply hasing-based handling #
        if syscall in ("sendmsg", "recvmsg"):
            if ref and argname == "msg":
                try:
                    return self.readMsgHdr(value)
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printWarn(
                        "failed to get msghdr for %s" % syscall,
                        True,
                        reason=True,
                    )
                    return value
        elif syscall in ("sendmmsg", "recvmmsg"):
            if ref and argname == "vlen":
                try:
                    if "msg" in argset:
                        ret = self.readMultiMsgHdr(argset["msg"], value)
                        if ret != argset["msg"]:
                            self.changeArg("msg", ret)
                    return value
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printWarn(
                        "failed to get mmsghdr for %s" % syscall,
                        True,
                        reason=True,
                    )
                    return value
        elif syscall in ("open", "openat", "accept4"):
            if argname == "flags":
                return UtilMgr.getFlagString(
                    value, ConfigMgr.OPEN_TYPE, num="oct"
                )
        elif syscall == "execve":
            # ignore envp arg #
            if argname == "argv":
                strList = self.readStrList(value)
                if strList:
                    return " [%s] " % (", ".join(strList))
                else:
                    return value
        elif syscall.startswith("mmap") or syscall == "mprotect":
            if argname == "prot":
                return UtilMgr.getFlagString(value, ConfigMgr.PROT_TYPE)
            elif argname == "flags":
                return UtilMgr.getFlagString(value, ConfigMgr.MAP_TYPE)
        elif syscall.startswith("fcntl"):
            if argname == "cmd":
                return ConfigMgr.FCNTL_TYPE[value]
        elif syscall.startswith("futex"):
            if argname == "op":
                if value < len(ConfigMgr.FUTEX_TYPE):
                    return ConfigMgr.FUTEX_TYPE[value]

                # check _PRIVATE FLAG #
                value = value & 0xF
                if value < len(ConfigMgr.FUTEX_TYPE):
                    return ConfigMgr.FUTEX_TYPE[value] + "_PRIVATE"
            elif argname == "val3" and value == -1:
                return "FUTEX_BITSET_MATCH_ANY"
        elif syscall == "ptrace" and argname == "request":
            try:
                return ConfigMgr.PTRACE_TYPE[value]
            except SystemExit:
                sys.exit(0)
            except:
                return value
        elif syscall == "socketcall":
            if argname == "call":
                try:
                    return ConfigMgr.SOCKETCALL[value]
                except SystemExit:
                    sys.exit(0)
                except:
                    return value
            elif argname == "args":
                # TODO: handle socket call args #
                return value
        elif syscall == "access":
            if argname == "mode":
                return UtilMgr.getFlagString(value, ConfigMgr.PERM_TYPE)
        elif syscall == "socket":
            if argname == "type":
                return UtilMgr.getFlagString(value, ConfigMgr.SOCKET_TYPE)
        elif syscall == "clone":
            if argname == "flags":
                return UtilMgr.getFlagString(value, ConfigMgr.CLONE_TYPE)
        elif syscall in ("epoll_ctl", "epoll_wait", "epoll_pwait"):
            if argname == "op":
                try:
                    return ConfigMgr.EPOLL_CMD_TYPE[value]
                except SystemExit:
                    sys.exit(0)
                except:
                    return value
            elif ref and argname.startswith("event"):
                try:
                    value = self.readMem(value)
                    value = struct.unpack(self.decChar, value)[0]
                    return UtilMgr.getFlagString(
                        value, ConfigMgr.EPOLL_EVENT_TYPE
                    )
                except SystemExit:
                    sys.exit(0)
                except:
                    return value
        elif syscall == "bpf":
            if argname == "cmd":
                try:
                    return ConfigMgr.BPF_CMD[value]
                except SystemExit:
                    sys.exit(0)
                except:
                    return value
        elif syscall == "prctl":
            if argname == "option":
                try:
                    return ConfigMgr.PRCTL_TYPE[value]
                except SystemExit:
                    sys.exit(0)
                except:
                    return value
        elif syscall.startswith("madvise"):
            if argname == "advice":
                try:
                    return ConfigMgr.MADV_TYPE[c_int(value).value]
                except SystemExit:
                    sys.exit(0)
                except:
                    return value
        elif syscall.startswith("fadvise"):
            if argname == "advice":
                try:
                    return ConfigMgr.FADV_TYPE[c_int(value).value]
                except SystemExit:
                    sys.exit(0)
                except:
                    return value
        elif syscall == "mount":
            if argname == "flags":
                UtilMgr.makeReverseDict(
                    ConfigMgr.MOUNT_TYPE, ConfigMgr.MOUNT_TYPE_REVERSE
                )

                return UtilMgr.getFlagString(
                    value, ConfigMgr.MOUNT_TYPE_REVERSE
                )
        elif syscall.startswith("umount"):
            if argname == "flags":
                UtilMgr.makeReverseDict(
                    ConfigMgr.UMOUNT_TYPE, ConfigMgr.UMOUNT_TYPE_REVERSE
                )

                return UtilMgr.getFlagString(
                    value, ConfigMgr.UMOUNT_TYPE_REVERSE
                )
        elif syscall == "fanotify_init":
            if argname == "flags":
                return UtilMgr.getFlagString(value, ConfigMgr.FAN_INIT_TYPE)
            elif argname == "event_f_flags":
                return UtilMgr.getFlagString(value, ConfigMgr.OPEN_TYPE, "oct")
        elif syscall == "fanotify_mark":
            if argname == "flags":
                return UtilMgr.getFlagString(value, ConfigMgr.FAN_MARK_TYPE)
            elif argname == "mask":
                return UtilMgr.getFlagString(value, ConfigMgr.FAN_EVENT_TYPE)
        elif syscall == "ioctl":
            if argname == "cmd":
                UtilMgr.makeReverseDict(
                    ConfigMgr.IOCTL_TYPE, ConfigMgr.IOCTL_TYPE_REVERSE
                )

                try:
                    return ConfigMgr.IOCTL_TYPE_REVERSE[long(value)]
                except SystemExit:
                    sys.exit(0)
                except:
                    return value
        elif syscall in ("process_vm_readv", "process_vm_writev"):
            if not SysMgr.showAll or not argname == "riovcnt":
                return value

            # get params #
            argList = [["lvec", "liovcnt"], ["rvec", "riovcnt"]]

            # get iov info #
            for name, cnt in argList:
                if "ONLYADDR" in SysMgr.environList:
                    baseAddr = True
                else:
                    baseAddr = False if name == "lvec" else True

                vec = self.readIoVec(
                    argset[name], argset[cnt], baseAddr=baseAddr
                )
                self.changeArg(name, str(vec))

            return value
        elif syscall == "io_uring_enter":
            if argname == "flags":
                return UtilMgr.getFlagString(
                    value, ConfigMgr.IORING_ENTER_FLAG
                )
        elif syscall == "io_uring_register":
            if argname == "op":
                return ConfigMgr.IORING_REGISTER_OPCODE[value]
        elif syscall == "io_uring_setup":
            if argname == "p":
                return self.readUringParam(value)
        elif syscall == "arch_prctl" and argname == "code":
            try:
                return ConfigMgr.ARCH_CODE[long(value)]
            except SystemExit:
                sys.exit(0)
            except:
                return value

        if ref:
            # convert fd to name #
            if argname in ("fd", "sockfd"):
                try:
                    path = self.readFdPath(value)
                    if Debugger.envFlags["PRINTFILE"]:
                        self.addFileAccess(syscall, path)
                    return "%s>%s" % (value, path)
                except SystemExit:
                    sys.exit(0)
                except:
                    return str(value)

            # convert pointer to string #
            if argtype == "const char *" and (
                argname.endswith("name")
                or argname.endswith("path")
                or argname.endswith("type")
            ):
                addr = self.values[seq]
                # redundant call after execve #
                if addr == 0:
                    return value
                else:
                    ret = self.readString(addr)
                    if not ret:
                        ret = addr
                    return ret

            # convert iov #
            if argname in ("vlen", "nr_segs"):
                try:
                    # set vector name #
                    if argname == "vlen":
                        avec = argset["vec"]
                    else:
                        avec = argset["iov"]

                    baseAddr = "ONLYADDR" in SysMgr.environList

                    vec = self.readIoVec(avec, value, baseAddr=baseAddr)
                    if vec != avec:
                        self.changeArg("vec", str(vec))
                    return value
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printWarn(
                        "failed to read iovector for %s" % syscall,
                        True,
                        reason=True,
                    )
                    return value

            if argtype == "struct sockaddr *":
                try:
                    val = self.readMem(value, 4)
                    if not val:
                        return value
                    family, port = struct.unpack("HH", val)
                    sa_family = ConfigMgr.SA_FAMILY_TYPE[str(family)]
                    retList = {
                        "sa_family": sa_family,
                    }
                    if sa_family == "AF_UNIX":
                        retList["sun_path"] = self.readString(
                            value + 2, 108
                        ).decode()
                    else:
                        retList["sin_port"] = SysMgr.getPkg("socket").htons(
                            port
                        )

                    return retList
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printWarn(
                        "failed to read struct sockaddr for %s" % syscall,
                        True,
                        reason=True,
                    )
                    return value

            if argtype.startswith("struct pollfd *"):
                nfds = self.values[1]
                return self.readPollFds(value, nfds)

            if argtype.startswith("struct timespec *"):
                return self.readTimespec(value)

        if argname == "resource":
            return ConfigMgr.RLIMIT_TYPE[value]

        if argname == "domain":
            return ConfigMgr.SA_FAMILY_TYPE[str(value)]

        # convert position #
        if argname == "whence":
            return ConfigMgr.SEEK_TYPE[c_int(value).value]

        # convert at #
        if argname in ("dfd", "dirfd"):
            try:
                return ConfigMgr.FAT_TYPE[c_int(value).value]
            except:
                return "AT_FDCWD"

        # convert pointer to buffer #
        if buf and argname == "buf" and syscall in ConfigMgr.SYSCALL_REFBUF:
            # set try size #
            size = self.values[2]

            # handle read buffer #
            if self.isDeferCall(syscall):
                if retval == 0:
                    return ""
                elif Debugger.dbusEnable:
                    pass
                elif retval:
                    size = retval

            # set op length #
            if size > self.pbufsize:
                length = self.pbufsize
            else:
                length = size

            # set print length #
            if SysMgr.outPath:
                cutLen = sys.maxsize
            else:
                cutLen = length

            # read string from address #
            ret = self.readMem(value, length)
            if ret:
                value = ret

            try:
                if Debugger.dbusEnable:
                    return UtilMgr.encodeBase64(value)
                else:
                    return repr(value)[1:-1][:cutLen]
            except SystemExit:
                sys.exit(0)
            except:
                return repr(value)

        # convert socket flag #
        if argname == "flags" and value:
            if syscall.startswith("send") or syscall.startswith("recv"):
                return UtilMgr.getFlagString(value, ConfigMgr.MSG_TYPE)

        # convert signal number #
        if argname in ("signum", "sig") and argtype == "int":
            return ConfigMgr.SIG_LIST[c_int(value).value]

        # remove const prefix #
        if argtype.startswith("const "):
            argtype = argtype[6:]

        # TODO: handle pointer data type #
        if argtype[-1] == "*":
            return value

        return value

    @staticmethod
    def makeSvgString(callTree, callCnt):
        # create palette for colors #
        palette = Timeline.Config().PALETTE

        def _iterNode(
            array, target, callCnt, depth=0, pos=0, height=36, hsize=15
        ):
            for node, value in sorted(
                target.items(), key=lambda x: x[1]["cnt"], reverse=True
            ):
                # get stats #
                totalCnt = callCnt[0]
                cnt = value["cnt"]
                per = (cnt / float(totalCnt)) * 100

                # iterate children #
                if value["child"]:
                    _iterNode(
                        array,
                        value["child"],
                        callCnt,
                        depth + 1,
                        pos,
                        height + 16,
                    )

                # build tags #
                indent = "    "
                tagStr = "<g>\n"
                name = UtilMgr.convHtmlChar(node)
                color = palette[len(array) % len(palette)]
                tagStr += "%s<title>%s (%s samples, %.1f%%)</title>\n" % (
                    indent,
                    name,
                    UtilMgr.convNum(cnt),
                    per,
                )
                tagStr += (
                    '%s<rect x="%.4f%%" y="%s" width="%.4f%%" '
                    'height="%s" fill="%s"/>\n'
                ) % (indent, pos, height, per, hsize, color)
                tagStr += '%s<text x="%.4f%%" y="%.2f"></text>\n' % (
                    indent,
                    pos + 0.25,
                    height + 10.5,
                )
                tagStr += "</g>"

                # append tag #
                array.append(tagStr)

                pos += per

        tagList = []
        _iterNode(tagList, callTree, callCnt)
        return "\n".join(tagList)

    @staticmethod
    def drawFlame(
        inputFile=None,
        callList={},
        title="",
        suffix=False,
        outFile=None,
        getter=None,
    ):
        if not inputFile and not callList:
            SysMgr.printErr("no input for flame graph")
            sys.exit(-1)

        # convert input value type to list #
        fileName, inputName, inputList = UtilMgr.getInputNames(inputFile)

        # set output path #
        if inputList:
            outputPath = UtilMgr.getDrawOutputPath(
                fileName, "flamegraph", suffix=suffix
            )
        elif outFile:
            outputPath = outFile
            if not outputPath.endswith(".svg"):
                outputPath += ".svg"
        else:
            outputPath = "flamegraph.svg"

        # set verbose flag #
        if len(inputList) > 1:
            verb = True
        else:
            verb = False

        # get filter list #
        includeList, excludeList = SysMgr.getTargetSymList()

        # remove input file list if call samples exist #
        if callList:
            inputList = []

        isValidStr = UtilMgr.isValidStr
        filterGroup = SysMgr.filterGroup
        convNum = UtilMgr.convNum

        # set getter function #
        if getter:
            pass
        elif "MERGETOMBSTONE" in SysMgr.environList:
            getter = SysMgr.getBtStatsAndroid
        elif "MERGECRASH" in SysMgr.environList:
            getter = SysMgr.getBtStatsLinux1
        elif "MERGEPANIC" in SysMgr.environList:
            getter = SysMgr.getBtStatsKernel
        else:
            getter = Debugger.getCallStatsFile

        # set options #
        printFileList = "PRINTFILELIST" in SysMgr.environList

        # define stats #
        nrFiles = 0
        nrSamples = 0
        titleLines = []
        procList = {}
        threadList = {}
        taskList = {}
        titleList = {}

        def _addTitleItem(new, titleLines):
            titleLines.append(new)

        def _addTitle(title, titleLines):
            for new in titleLines:
                title += '<tspan x="0" dy="1.2em">%s</tspan>' % new
                if SysMgr.warnEnable and new.strip():
                    SysMgr.printWarn(new, newline=False)
            return title

        # load call samples #
        for fname in inputList:
            # get list for call samples #
            try:
                # get samples #
                samples, metas = getter(fname, verb=verb)

                # check sample #
                if not samples or not samples[0]:
                    SysMgr.printErr("no call sample for '%s'" % fname)
                    continue

                if isinstance(samples, dict):
                    samples = [samples]

                totalCnt = 0
                for si, sampleList in enumerate(samples):
                    ts = ""
                    addFileInfo = ""
                    metainfo = metas[si] if isinstance(metas, list) else metas

                    # merge stats #
                    if isinstance(metainfo, dict):
                        procName = metainfo.get("process", "")
                        threadName = metainfo.get("thread", "")

                        if procName:
                            procList[procName] = procList.get(procName, 0) + 1

                        if threadName:
                            taskName = "%s(%s)" % (threadName, procName)
                            threadList[threadName] = (
                                threadList.get(threadName, 0) + 1
                            )
                            threadInfo = "%s(%s)-" % (
                                threadName,
                                metainfo.get("tid"),
                            )
                        else:
                            threadInfo = ""
                            taskName = procName

                        if taskName:
                            taskList[taskName] = taskList.get(taskName, 0) + 1

                        ts = metainfo.get("time")

                        addFileInfo = " (Task: %s%s(%s)) (Time: %s)" % (
                            threadInfo,
                            procName,
                            metainfo.get("pid"),
                            ts,
                        )
                        metainfo = None
                    # check task filter #
                    elif metainfo and filterGroup:
                        try:
                            item = metainfo.split("[")[5].rsplit(":")[0]
                            comm, tid = item.rsplit("(", 1)
                        except SystemExit:
                            sys.exit(0)
                        except:
                            SysMgr.printErr(
                                "failed to parse '%s'" % metainfo, True
                            )
                            continue

                        # check filter #
                        if not tid[:-1] in filterGroup and not isValidStr(
                            comm
                        ):
                            continue

                    # add title info #
                    if metainfo:
                        _addTitleItem(metainfo, titleLines)

                    # merge samples #
                    for sample, cnt in sampleList.items():
                        # check filter #
                        if includeList and not isValidStr(sample, includeList):
                            continue
                        elif excludeList and isValidStr(sample, excludeList):
                            continue

                        # increase counts #
                        if sample in callList:
                            callList[sample] += cnt
                        else:
                            callList[sample] = cnt
                        totalCnt += cnt
                        nrSamples += cnt

                # add path info #
                if totalCnt:
                    nrFiles += 1
                    if printFileList:
                        pathInfo = "[%s] (NrSample: %s) (Size: %s)%s" % (
                            fname,
                            convNum(totalCnt),
                            UtilMgr.getFileSizeStr(fname).strip(" []"),
                            addFileInfo,
                        )
                        if ts in titleList:
                            titleList[str(nrFiles)] = pathInfo
                        else:
                            titleList[ts] = pathInfo
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr(
                    "failed to get call sample from '%s'" % fname, True
                )
                continue

        # add path info #
        for t, v in sorted(
            titleList.items(),
            key=lambda x: x[0] if isinstance(x[0], long) else 0,
        ):
            if v:
                _addTitleItem(v, titleLines)
        if titleList:
            _addTitleItem(" ", titleLines)

        # set stats from call sample input #
        if not inputList:
            nrSamples = sum(callList.values())

        # add stat info #
        maxDepth = (
            max([x.count(" <- ") for x in callList]) + 1 if callList else 0
        )
        _addTitleItem("*" * SysMgr.lineLength, titleLines)
        statInfo = (
            "[STAT] (NrFile: %s) (NrSample: %s) (NrStack: %s) "
            "(NrProc: %s) (NrThread: %s) (MaxDepth: %s)"
        ) % (
            convNum(nrFiles),
            convNum(nrSamples),
            convNum(len(callList)),
            convNum(len(procList)),
            convNum(len(threadList)),
            convNum(maxDepth),
        )
        _addTitleItem(statInfo, titleLines)

        # add task info #
        for tname, tlist in (
            ("PROC", procList),
            ("THREAD", threadList),
            ("TASK", taskList),
        ):
            taskInfo = ", ".join(
                [
                    "%s(%s)" % (n, convNum(v))
                    for n, v in sorted(
                        tlist.items(), key=lambda x: x[1], reverse=True
                    )
                ]
            )
            if taskInfo:
                _addTitleItem(("[%s] " % tname) + taskInfo, titleLines)

        # flush title #
        _addTitleItem("*" * SysMgr.lineLength, titleLines)
        if titleLines:
            title = _addTitle(title, titleLines)

        # handle WAIT status #
        exceptWait = "EXCEPTWAIT" in SysMgr.environList
        onlyWait = "ONLYWAIT" in SysMgr.environList
        if exceptWait and onlyWait:
            SysMgr.printErr(
                "both EXCEPTWAIT and ONLYWAIT can't be used togather"
            )
            sys.exit(0)
        elif exceptWait or onlyWait:
            newList = {}
            for item in callList:
                if exceptWait and item.startswith("WAIT{"):
                    continue
                elif onlyWait and not item.startswith("WAIT{"):
                    continue
                newList[item] = callList[item]
            callList = newList

        # check call samples #
        if not callList:
            SysMgr.printErr("no call sample to draw flame graph")
            sys.exit(-1)

        # convert list to tree for call samples #
        try:
            callTree, callCnt, depth = Debugger.convCallList2Tree(callList)
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                "failed to convert call samples from '%s'" % inputName, True
            )
            sys.exit(-1)

        # print text tree #
        if "PRINTTREE" in SysMgr.environList:

            def _printChilds(item, cur):
                if not item or not isinstance(item, dict):
                    return

                for n, v in item.items():
                    cnt = v.get("cnt")
                    if cnt:
                        per = cnt / float(callCnt[cur]) * 100
                        SysMgr.printPipe(
                            ("  " * cur)
                            + "%s(%d%%) " % (convNum(str(cnt)), per)
                            + n
                        )
                    child = v.get("child", {})
                    _printChilds(child, cur + 1)

            # print stats #
            SysMgr.printPipe("\n\n")
            for t in titleLines:
                SysMgr.printPipe(t)

            # print tree #
            SysMgr.printPipe("\n[Call Tree Info]\n%s" % twoLine)
            _printChilds(callTree, 0)
            SysMgr.printPipe(oneLine)

            return

        # make svg string #
        try:
            svgStr = Debugger.makeSvgString(callTree, callCnt)
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                "failed to make flame graph from '%s'" % inputName, True
            )
            sys.exit(-1)

        # write svg code to the file #
        try:
            UtilMgr.writeFlamegraph(outputPath, svgStr, title, depth)
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                "failed to save flame graph from '%s' to '%s'"
                % (inputName, SysMgr.outPath),
                True,
            )
            sys.exit(-1)

    @staticmethod
    def convCallList2Tree(callList):
        def _getObj():
            return dict(cnt=0, child={})

        tree = {}
        totals = {}
        pos = None
        maxLevel = 0

        # get filter #
        callFilter = SysMgr.environList.get("FILTER", [])

        keepRedundantCnt = "KEEPREDCNT" in SysMgr.environList

        # iterate calls #
        for sample, count in callList.items():
            level = 0
            pos = tree
            calls = reversed(sample.split(" <- "))

            # apply filter #
            if callFilter:
                if not UtilMgr.isValidStr(sample, callFilter):
                    continue

            # iterate functions #
            for call in calls:
                if not keepRedundantCnt:
                    # remove multiple info #
                    items = call.rsplit("] * ", 1)
                    if len(items) > 1:
                        call = "%s]" % items[0]

                pos.setdefault(call, _getObj())
                pos[call]["cnt"] += count
                pos = pos[call]["child"]
                totals.setdefault(level, 0)
                totals[level] += count
                level += 1

                # update max depth #
                if level >= maxLevel:
                    maxLevel = level

        return tree, totals, maxLevel

    @staticmethod
    def getCallStatsFile(logFile, verb=False):
        # get file handle #
        try:
            fd = UtilMgr.getTextLines(logFile, verb=verb, retfd=True)
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to read %s\n" % logFile)
            sys.exit(-1)

        stack = ""
        main = ""
        mainCnt = 0
        context = None
        samples = {}
        title = None

        # check backtrace stacks #
        onlybt = "ONLYBTSTACK" in SysMgr.environList

        for line in fd:
            # get start keyword #
            if isinstance(line, bytes):
                return samples, title
            elif line.startswith("[Top ") or line.startswith("[Trace "):
                if not " Summary]" in line or context:
                    return samples, title

                title = line.strip()
                context = "start"
                continue
            elif context is None:
                continue

            # split a line #
            sline = line.split("|")

            # last call #
            if len(sline) == 2:
                per = sline[0].strip()
                if not per[0].isdigit():
                    continue

                string = sline[1].lstrip()
                # backtraces #
                if string.startswith("<-"):
                    # parse backtraces #
                    last = string.split("<Cnt: ")
                    if not stack:
                        stack = main
                    if len(last) == 2:
                        stack = "%s %s" % (stack, last[0].strip())
                        count = long(last[1].strip().replace(",", "")[:-1])
                        samples.setdefault(stack, 0)
                        samples[stack] += count
                        stack = ""
                    else:
                        stack = "%s %s" % (stack, last[0].strip())
                # PC #
                else:
                    # no backtrace call #
                    if not onlybt and stack:
                        samples.setdefault(stack, 0)
                        samples[stack] += mainCnt

                    last = string.split("<Cnt: ")
                    stack = last[0].strip()
                    mainCnt = long(last[1].strip().replace(",", "")[:-1])
                    main = stack

            # backtrace #
            elif line.lstrip().startswith("<-"):
                last = line.split("<Cnt: ")
                if len(last) == 2:
                    stack = "%s %s" % (stack, last[0].strip())
                    count = long(last[1].strip().replace(",", "")[:-1])
                    samples.setdefault(stack, 0)
                    samples[stack] += count
                    stack = ""
                else:
                    stack = "%s %s" % (stack, last[0].strip())

            # no backtrace #
            elif not onlybt and stack:
                samples.setdefault(stack, 0)
                samples[stack] += mainCnt
                stack = ""

        return samples, title

    @staticmethod
    def updateCommFlag(flag=True):
        if not Debugger.globalEvent:
            return

        # set flag value #
        if flag is True:
            value = 1
        else:
            value = 0

        if sys.version_info < (3, 0, 0):
            value = bytes(value)

        # pylint: disable-next=unsupported-assignment-operation
        Debugger.globalEvent[0] = value

    @staticmethod
    def needUpdateComm():
        if not Debugger.globalEvent:
            return False

        # pylint: disable-next=unsubscriptable-object
        ret = Debugger.globalEvent[0]
        if ret in (b"1", 1):
            return True
        else:
            return False

    @staticmethod
    def setFaultFlag(flag=True):
        if not Debugger.globalEvent:
            return

        # set flag value #
        if flag is True:
            value = 1
        else:
            value = 0

        if sys.version_info < (3, 0, 0):
            value = bytes(value)

        # pylint: disable-next=unsupported-assignment-operation
        Debugger.globalEvent[1] = value

    @staticmethod
    def getFaultFlag():
        if not Debugger.globalEvent:
            return False

        # pylint: disable-next=unsubscriptable-object
        ret = Debugger.globalEvent[1]
        if ret in (b"1", 1):
            return True
        else:
            return False

    @staticmethod
    def onAlarm(signum, frame):
        if Debugger.dbgInstance:
            Debugger.dbgInstance.printIntervalSummary()

        SysMgr.updateTimer()

    def printIntervalSummary(self):
        def _resetStats():
            # merge syscall stats #
            for syscall in self.syscallStat:
                self.syscallTotalStat.setdefault(syscall, [0, 0])
                # update maximum elapsed time #
                if (
                    self.syscallTotalStat[syscall][1]
                    < self.syscallStat[syscall][1]
                ):
                    self.syscallTotalStat[syscall][1] = self.syscallStat[
                        syscall
                    ][1]
                self.syscallTotalStat[syscall][0] += self.syscallStat[syscall][
                    0
                ]

            # initialize syscall timetable #
            self.syscallStat = {}
            self.syscallInterStat = {}
            self.bpcallStat = {}
            self.bpcallInterStat = {}

            # reset data #
            self.totalCall = 0
            self.callTable = {}
            self.errCnt = 0
            SysMgr.clearPrint()

        def _checkInterval():
            if SysMgr.repeatCnt == 0:
                return

            # check uptime deadline #
            meetDeadline = (
                SysMgr.deadlineUptime > 0
                and SysMgr.deadlineUptime <= SysMgr.uptime
            )

            # update and check progress #
            SysMgr.progressCnt += 1
            if SysMgr.repeatCnt <= SysMgr.progressCnt or meetDeadline:
                SysMgr.printWarn("terminated by timer\n", True)
                sys.exit(0)

        def _printHist(items, title):
            itemList = UtilMgr.convList2Histo(items, mult=1000000)
            UtilMgr.printHist(itemList, title, "us", SysMgr.addPrint, "\n")

        def _finishPrint(self, needStop=False, term=False, flush=True):
            # flush print buffer #
            if flush:
                SysMgr.printTopStats()

            # check and update repeat count #
            _checkInterval()

            # reset stats #
            _resetStats()

            # stop target to return original status #
            if needStop:
                self.stop(check=True)

            # terminate tracing #
            if term:
                sys.exit(0)

        # define term variable #
        term = False

        # check master process #
        if (
            SysMgr.masterPid > 0
            and not Debugger.envFlags["CONTALONE"]
            and not SysMgr.isAlive(SysMgr.masterPid)
        ):
            SysMgr.printWarn(
                "terminated the master process for %s" % __module__
            )
            if self.callTable:
                term = True
            else:
                sys.exit(0)

        # update comm #
        origComm = self.comm
        self.comm = SysMgr.getComm(self.pid)
        if not self.comm:
            self.comm = origComm

        # check samples #
        if not self.callTable:
            if not self.traceStatus:
                SysMgr.printWarn(
                    "no sample data for %s(%s)" % (self.comm, self.pid),
                    False if SysMgr.masterPid > 0 else True,
                )

            # check and update repeat count #
            _checkInterval()

            # reset stats #
            _resetStats()

            return

        # check user input #
        SysMgr.waitUserInput(wait=0.000001, msg="DEFAULT")

        # define stop flag #
        needStop = False

        # update time #
        SysMgr.updateUptime()
        current = time.time()
        diff = current - self.last
        self.last = current

        # check comm filter for child #
        if Debugger.targetNums or (
            SysMgr.inputParam and self.execCmd and SysMgr.filterGroup
        ):
            # check filter #
            if SysMgr.filterGroup and UtilMgr.isValidStr(self.comm):
                pass
            # check thread sequence #
            elif self.myNum in Debugger.targetNums:
                pass
            # check break mode #
            elif self.mode == "break":
                # reset stats #
                _resetStats()

                # check term condition #
                if term:
                    sys.exit(0)
                else:
                    return
            else:
                # print status #
                SysMgr.printWarn(
                    "stopped tracing %s(%s) because it is not targeted"
                    % (self.comm, self.pid)
                )

                # disable alarm #
                self.status = "wait"
                signal.alarm(0)

                # disable file output #
                SysMgr.outPath = None

                # continue target #
                if self.isStopped():
                    self.cont()

                # reset stats #
                _resetStats()

                # check term condition #
                if term:
                    sys.exit(0)
                else:
                    return

        # get CPU usage for target #
        cpuUsage = self.getCpuUsage(system=True)
        ttime = cpuUsage[0] / diff
        utime = cpuUsage[1] / diff
        stime = cpuUsage[2] / diff
        ttimeStr = UtilMgr.convCpuColor(ttime, "%d%%" % ttime)
        cpuStr = "%s(U%d%%+S%d%%)" % (ttimeStr, utime, stime)

        # get CPU usage for system #
        ctime = 100 - (cpuUsage[3] / diff)
        ctime = ctime if ctime > 0 else 0
        sysCpuStr = "%d%%" % ctime
        sysCpuStr = UtilMgr.convCpuColor(ctime, sysCpuStr)

        # get memory usage for target #
        rssStr = self.getMemUsage()

        # get available memory for system #
        sysMemStr = SysMgr.getAvailMemInfo()

        # check CPU threshold #
        if Debugger.cpuCond > -1 and Debugger.cpuCond > ttime:
            # reset stats #
            _resetStats()

            # check term condition #
            if term:
                sys.exit(0)
            else:
                return

        # define variables #
        nrTotal = float(self.totalCall)
        convert = UtilMgr.convNum
        convColor = UtilMgr.convColor
        convSec = UtilMgr.convTime2Unit

        # set table name #
        if self.mode == "syscall":
            ctype = "Syscall"
            if Debugger.envFlags["INTERCALL"]:
                addInfo = "<Interval>"
            else:
                addInfo = "<Elapsed>"
            sampleStr = ""
        elif self.mode == "break":
            ctype = "Breakcall"
            addInfo = "[PATH] <Interval/Elapsed[RET]>"
            sampleStr = ""
        elif self.mode == "pycall":
            ctype = "Pycall"
            addInfo = "[PATH] <Sample>"
            sampleStr = " [Freq: %s]" % convSec(self.sampleTime)
        elif self.mode == "kernel":
            ctype = "Kernelcall"
            addInfo = "<Sample>"
            sampleStr = " [Freq: %s]" % convSec(self.sampleTime)
        else:
            ctype = "Usercall"
            addInfo = "[PATH] <Sample>"
            sampleStr = " [Freq: %s]" % convSec(self.sampleTime)

            # continue target to prevent too long freezing #
            if self.traceStatus and self.isAlive():
                try:
                    sig = 0
                    ret = os.waitpid(self.pid, os.WNOHANG)
                    if ret and ret[1] > 0:
                        sig = self.getStatus(ret[1])
                    if (
                        sig != signal.SIGSTOP
                        and self.cont(check=True, sig=sig) == 0
                    ):
                        needStop = True
                except:
                    pass

        # update resource usage for myself #
        cpuUsage = Debugger.tracerInstance.getCpuUsage()
        mttime = cpuUsage[0] / diff
        mcpuStr = "%d%%" % mttime
        mcpuStr = UtilMgr.convCpuColor(mttime, mcpuStr)
        mrssStr = Debugger.tracerInstance.getMemUsage()

        # add CPU time info #
        self.cpuUsageList.append([ttime, utime, stime, ctime])
        self.selfCpuUsageList.append(mttime)

        # calculate average for CPU usage #
        if not SysMgr.showAll and SysMgr.cpuEnable:
            floatTotalUsage = ttime / 100
        else:
            floatTotalUsage = 1

        # set comm #
        if self.comm:
            comm = self.comm
        else:
            comm = "??"

        # JSON-format stats #
        if SysMgr.jsonEnable:
            jsonData = {
                "type": ctype,
                "uptime": SysMgr.uptime,
                "interval": diff,
                "samples": self.totalCall,
                "nrError": self.errCnt,
                "sysCpu": sysCpuStr,
                "sysMem": sysMemStr,
                "comm": comm,
                "pid": self.pid,
                "cpu": cpuStr,
                "rss": rssStr,
                "tracerComm": Debugger.tracerInstance.comm,
                "tracerPid": Debugger.tracerInstance.pid,
                "tracerCpu": mcpuStr,
                "tracerRss": mrssStr,
                "stats": {},
            }
        # string-format stats #
        else:
            jsonData = {}

            # set error count #
            if self.errCnt:
                errstr = "[Err: %s] " % convColor(convert(self.errCnt), "RED")
            else:
                errstr = ""

            tginfo = "<-%s(%s)" % (self.tgcomm, self.tgid) if self.tgid else ""

            # print top stat #
            ret = SysMgr.addPrint(
                (
                    "[Top %s Info] [Time: %.3f] [Inter: %.3f] [Sample: %s] "
                    "%s[SYS: %s/%s] [%s(%s)%s: %s/%s] [%s(%s): %s/%s]%s \n%s\n"
                )
                % (
                    ctype,
                    SysMgr.uptime,
                    diff,
                    convert(self.totalCall),
                    errstr,
                    sysCpuStr,
                    sysMemStr,
                    comm,
                    self.pid,
                    tginfo,
                    cpuStr,
                    rssStr,
                    Debugger.tracerInstance.comm,
                    Debugger.tracerInstance.pid,
                    mcpuStr,
                    mrssStr,
                    sampleStr,
                    twoLine,
                ),
                newline=2,
            )
            if not ret:
                # flush print buffer #
                _finishPrint(self, needStop, term)
                return

            # print menu #
            ret = SysMgr.addPrint(
                "{0:^7} | {1:<144}\n{2:<1}\n".format(
                    "Usage", "Function %s" % addInfo, twoLine
                ),
                newline=2,
            )
            if not ret:
                # flush print buffer #
                _finishPrint(self, needStop, term)
                return

        # print functions #
        totalCnt = 0
        isBtPrinted = False
        for sym, value in sorted(
            self.callTable.items(), key=lambda x: x[1]["cnt"], reverse=True
        ):
            # get count #
            cnt = value["cnt"]
            cntstr = convert(cnt)
            addVal = ""

            # check percentage #
            try:
                per = (cnt / nrTotal) * 100 * floatTotalUsage
            except:
                break

            # SYSCALL #
            if self.mode == "syscall":
                try:
                    total, tmax = self.syscallStat[sym]
                    average = total / cnt
                except:
                    total = average = tmax = 0

                # color err #
                err = value["err"]
                if err > 0:
                    errstr = UtilMgr.convColor(convert(err), "RED")
                else:
                    errstr = convert(err)

                # handle stdev #
                if (
                    Debugger.envFlags["STDEV"]
                    and sym in self.syscallInterStat
                    and self.syscallInterStat[sym]
                ):
                    stdev = UtilMgr.getStdev(self.syscallInterStat[sym])
                    if Debugger.envFlags["PRINTHIST"]:
                        _printHist(self.syscallInterStat[sym], sym)
                else:
                    stdev = -1

                # merge stats #
                if SysMgr.jsonEnable:
                    jsonData["stats"][sym] = {
                        "count": cnt,
                        "totalTime": total,
                        "avgTime": average,
                        "maxTime": tmax,
                        "error": err,
                        "stdev": stdev,
                    }
                else:
                    avgtime = "%.6f" % average
                    maxtime = "%.6f" % tmax

                    # convert time color #
                    if average > self.retTime:
                        avgtime = UtilMgr.convColor(avgtime, "RED")
                    if tmax > self.retTime:
                        maxtime = UtilMgr.convColor(maxtime, "RED")

                    # convert stdev #
                    if stdev > -1:
                        stdevstr = ", Std: %.6f" % stdev
                    else:
                        stdevstr = ""

                    addVal = (
                        "<Cnt: %s, Tot: %.6f, Avg: %s, Max: %s, Err: %s%s>"
                        % (
                            convColor(cntstr, "YELLOW"),
                            total,
                            avgtime,
                            maxtime,
                            convColor(errstr, "RED") if err else errstr,
                            stdevstr,
                        )
                    )
            # BREAKPOINT #
            elif self.mode == "break":
                try:
                    prev, total, tmin, tmax = self.bpcallStat[sym]
                    average = total / cnt
                except:
                    prev = total = tmin = tmax = average = 0

                # handle stdev #
                if (
                    Debugger.envFlags["STDEV"]
                    and sym in self.bpcallInterStat
                    and self.bpcallInterStat[sym]
                ):
                    stdev = UtilMgr.getStdev(self.bpcallInterStat[sym])
                    if Debugger.envFlags["PRINTHIST"]:
                        _printHist(self.bpcallInterStat[sym], sym)
                else:
                    stdev = -1

                # merge stats #
                if SysMgr.jsonEnable:
                    jsonData["stats"][sym] = {
                        "path": value["path"],
                        "count": cnt,
                        "avgTime": average,
                        "minTime": tmin,
                        "maxTime": tmax,
                        "stdev": stdev,
                    }
                else:
                    avgtime = "%.6f" % average
                    maxtime = "%.6f" % tmax

                    # convert time color #
                    if average > self.retTime:
                        avgtime = UtilMgr.convColor(avgtime, "RED")
                    if tmax > self.retTime:
                        maxtime = UtilMgr.convColor(maxtime, "RED")

                    # convert stdev #
                    if stdev > -1:
                        stdevstr = ", Std: %.6f" % stdev
                    else:
                        stdevstr = ""

                    addVal = (
                        "[%s] <Cnt: %s, Avg: %s, Min: %.6f, Max: %s%s>"
                        % (
                            value["path"],
                            convColor(cntstr, "YELLOW"),
                            avgtime,
                            tmin,
                            maxtime,
                            stdevstr,
                        )
                    )
            # OTHERS #
            else:
                # merge stats #
                if SysMgr.jsonEnable:
                    jsonData["stats"][sym] = {
                        "path": value["path"],
                        "count": cnt,
                    }
                else:
                    addVal = "[%s] <Cnt: %s>" % (
                        value["path"],
                        convColor(cntstr, "YELLOW"),
                    )

            if not SysMgr.jsonEnable:
                # check cut condition #
                if SysMgr.checkCutCond():
                    break

                # convert path to symbol #
                if sym[0] == "/":
                    targetSym = "??"
                else:
                    targetSym = sym

                ret = SysMgr.addPrint(
                    "{0:>7} | {1:<144}\n".format(
                        convColor("%.1f%%" % per, "YELLOW", 7),
                        "%s %s" % (convColor(targetSym, "CYAN"), addVal),
                    )
                )
                if not ret:
                    break

            totalCnt += 1

            # print backtraces #
            if value["backtrace"]:
                if SysMgr.outPath:
                    self.btTable.setdefault(sym, dict())

                for bt, bcnt in sorted(
                    value["backtrace"].items(),
                    key=lambda x: x[1],
                    reverse=True,
                ):
                    # set percent #
                    bper = (bcnt / float(cnt)) * 100

                    # JSON format output #
                    if SysMgr.jsonEnable:
                        jsonData["stats"][sym].setdefault("backtraces", [])
                        jsonData["stats"][sym]["backtraces"].append(
                            {
                                "stack": UtilMgr.cleanItem(
                                    bt.replace("\n", "").split(" <- ")
                                ),
                                "count": bcnt,
                                "per": bper,
                            }
                        )
                        continue

                    # merge backtraces #
                    if SysMgr.outPath:
                        self.btTable[sym].setdefault(bt, 0)
                        self.btTable[sym][bt] += bcnt

                    nline = bt.count("\n") + 1
                    if SysMgr.checkCutCond(nline):
                        # flush print buffer #
                        _finishPrint(self, needStop, term)
                        return

                    ret = SysMgr.addPrint(
                        "{0:>17} | {1:<1} <Cnt: {2:1}>\n".format(
                            convColor("%.1f%%" % bper, "BLUE", 17),
                            bt,
                            convColor(convert(bcnt), "YELLOW"),
                        ),
                        newline=nline,
                    )
                    if not ret:
                        break

            # handle rest jobs #
            if SysMgr.jsonEnable:
                continue
            elif SysMgr.funcDepth > 0:
                isBtPrinted = True
                ret = SysMgr.addPrint("%s\n" % oneLine)
                if not ret:
                    break

        # print status #
        if SysMgr.jsonEnable:
            # stop target again #
            _finishPrint(self, needStop, term, False)

            # print JSON-format output #
            SysMgr.printPipe(
                UtilMgr.convDict2Str(jsonData, pretty=not SysMgr.streamEnable)
            )
        else:
            if ret:
                if totalCnt == 0:
                    SysMgr.addPrint("    None\n")

                if not isBtPrinted:
                    SysMgr.addPrint("%s\n" % oneLine)

            # flush print buffer #
            _finishPrint(self, needStop, term)

            # print progress #
            if ret and SysMgr.repeatCnt > 0:
                UtilMgr.printProgress(SysMgr.progressCnt, SysMgr.repeatCnt)

    def changeArg(self, name, value):
        for idx, item in enumerate(self.args):
            if item[1] == name:
                self.args[idx][2] = value
                return True
        return False

    def addArg(self, type, name, value):
        self.args.append([type, name, value])

    def clearArgs(self):
        self.args = []

    def getAnonRangeByOffset(self, offset, fname=None):
        if not fname in self.pmap:
            return

        vstart = self.pmap[fname]["vstart"]

        # get ELF object #
        fcache = ElfAnalyzer.getObject(fname)
        if not fcache:
            return [0, 0]

        ret = fcache.getAnonRangeByOffset(offset)
        if not ret:
            return [0, 0]

        # get start and end addresses #
        if ElfAnalyzer.isRelocFile(fname):
            start = vstart + ret[0]
            end = vstart + ret[1]
        else:
            start = ret[0]
            end = ret[1]

        return [start, end]

    def getRangeBySym(self, symbol, fname=None):
        if not fname in self.pmap:
            return

        vstart = self.pmap[fname]["vstart"]

        # get ELF object #
        fcache = ElfAnalyzer.getObject(fname)
        if not fcache:
            return [0, 0]

        ret = fcache.getRangeBySymbol(symbol)
        if not ret:
            return [0, 0]

        # get start and end addresses #
        if ElfAnalyzer.isRelocFile(fname):
            start = vstart + ret[0]
            end = vstart + ret[1]
        else:
            start = ret[0]
            end = ret[1]

        return [start, end]

    def updateProcMap(self, onlyExec=True, overlayInfo=None, saveAll=False):
        # get original map #
        try:
            mapstr = FileAnalyzer.procMapStrCache[self.pid]
        except:
            mapstr = None

        # update file-mapped memory map #
        self.pmap = FileAnalyzer.getProcMapInfo(
            self.pid, self.mapFd, onlyExec=onlyExec, saveAll=saveAll
        )
        if overlayInfo:
            for idx, path in enumerate(list(self.pmap)):
                newpath = UtilMgr.convOverlayPath(path, overlayInfo)
                if path != newpath:
                    self.pmap[newpath] = self.pmap.pop(path, None)

        # check map change #
        try:
            if mapstr == FileAnalyzer.procMapStrCache[self.pid]:
                return False
        except SystemExit:
            sys.exit(0)
        except:
            pass
        finally:
            self.prevPmap = self.pmap

        # get anonymous executable memory map #
        self.amap = FileAnalyzer.getAnonMapInfo(self.pid, self.mapFd)

        return True

    def getJITSymbolByOffset(self, offset):
        # check symbol table #
        if not self.jmapSymTable:
            return "??", "??", "??"

        try:
            if UtilMgr.isString(offset):
                try:
                    offset = long(offset, 16)
                except:
                    offset = long(offset)

            addrTable = self.jmapAddrTable
            symTable = self.jmapSymTable

            # get target index from address table #
            idx = UtilMgr.bisect_left(addrTable, offset) - 1
            if idx < 0:
                idx = 0

            while 1:
                if addrTable[idx] > offset:
                    return "??", "??", "??"

                size = symTable[idx][1]
                path = symTable[idx][2]

                # set symbol scope to it's size #
                maxAddr = addrTable[idx] + size

                if addrTable[idx] <= offset <= maxAddr:
                    return symTable[idx][0], size, path

                idx += 1
        except SystemExit:
            sys.exit(0)
        except:
            return "??", "??", "??"

    def loadJITSymbols(self):
        if not self.jmapPath:
            tgid = long(SysMgr.getTgid(self.pid))
            if not tgid:
                return False
            self.jmapPath = "%s/perf-%s.map" % (SysMgr.tmpPath, tgid)

        if not self.jmapFd:
            try:
                self.jmapFd = open(self.jmapPath, "r")
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printOpenWarn(self.jmapPath)
                return False

        # get map size #
        try:
            mapSize = os.fstat(self.jmapFd.fileno()).st_size
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn(
                "failed to get size for '%s' for %s(%s)"
                % (self.jmapPath, self.comm, self.pid)
            )
            return False

        # check map update and update JIT-compiled symbols #
        if mapSize != self.jmapSize:
            try:
                SysMgr.printWarn(
                    "start updating JIT-compiled symbols for %s(%s)"
                    % (self.comm, self.pid)
                )

                # update size #
                self.jmapSize = mapSize

                # reset symbol and address table #
                jmapTable = []
                self.jmapAddrTable = []
                self.jmapSymTable = []

                # parse and sort items #
                for item in self.jmapFd.readlines():
                    symInfo = item.rstrip().split(" ", 2)
                    if len(symInfo) == 3:
                        addr, size, sym = symInfo
                    else:
                        SysMgr.printWarn(
                            "failed to parse JIT-compiled symbol info '%s' "
                            "for %s(%s)" % (item, self.comm, self.pid)
                        )
                        continue

                    jmapTable.append([long(addr, 16), sym, long(size, 16)])

                # register symbols #
                for item in sorted(jmapTable, key=lambda x: x[0]):
                    addr, sym, size = item
                    self.jmapAddrTable.append(addr)
                    self.jmapSymTable.append([sym, size, "??"])
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printWarn(
                    "failed to load JIT-compiled symbols from '%s' for %s(%s)"
                    % (self.jmapPath, self.comm, self.pid),
                    reason=True,
                )
                return False

            return True

    def loadSymbols(self, onlyFunc=True, onlyExec=True):
        # set update flag #
        self.needMapScan = False

        if not self.pmap and not self.overlayfsList:
            self.overlayfsList = SysMgr.getOverlayfsInfo(self.pid)

        ret = self.updateProcMap(
            onlyExec=onlyExec, overlayInfo=self.overlayfsList
        )
        if not ret and self.fileList:
            return False

        # update overlayfs info #
        if not self.overlayfsList:
            self.overlayfsList = SysMgr.getOverlayfsInfo(self.pid)

        # update JIT-compiled symbols #
        if "JITSYM" in SysMgr.environList:
            self.loadJITSymbols()

        # check STOP condition #
        needStop = "STOPTARGET" in SysMgr.environList
        if needStop:
            self.stop()

        # get file list #
        targetFileList, exceptFileList = SysMgr.getTargetFileList()

        # register default libraries #
        for fpath in list(self.pmap):
            # update start address #
            startAddr = self.pmap[fpath]["vstart"]
            if self.startAddr is None:
                self.startAddr = startAddr
            elif self.startAddr > startAddr:
                self.startAddr = startAddr

            # update load status #
            fname = os.path.basename(fpath)
            if fname.startswith("ld-") or fname == "linker":
                self.dftBpFileList[fpath] = 0
            elif fname.startswith("libc-") or fname == "libc.so":
                self.libcLoaded = True
                self.dftBpFileList[fpath] = 0
            elif fname.startswith("libpthread"):
                self.dftBpFileList[fpath] = 0

        # load memory-mapped file objects #
        ret = False
        printLog = True
        prevRss = 0
        for mfile in [] if "LAZYCACHE" in SysMgr.environList else self.pmap:
            try:
                # check file cache #
                if mfile in ElfAnalyzer.cachedFiles:
                    continue
                # check file filter #
                if SysMgr.isExceptFile(mfile, targetFileList, exceptFileList):
                    continue
                # check file validation #
                elif not FileAnalyzer.isValidFile(mfile, special=True):
                    continue

                # load object #
                if mfile in ElfAnalyzer.preloadedFiles:
                    eobj = ElfAnalyzer.preloadedFiles[mfile]
                    ElfAnalyzer.cachedFiles[mfile] = eobj
                else:
                    eobj = ElfAnalyzer.getObject(
                        mfile, overlay=self.overlayfsList, log=printLog
                    )
                if eobj:
                    ret = True
                    eobj.mergeSymTable(onlyFunc=onlyFunc)
                    if printLog:
                        printLog = False

                continue

                # print ELF object size on RAM for test #
                curRss = Debugger.tracerInstance.getMemUsage(False)
                rssDiff = UtilMgr.convSize2Unit(curRss - prevRss)
                print(UtilMgr.convColor("[%s] %s" % (mfile, rssDiff), "CYAN"))
                prevRss = curRss
            except SystemExit:
                # continue target #
                if needStop:
                    self.cont()

                sys.exit(0)
            except:
                pass

        # update file and addr lists from memory map #
        self.fileList, self.addrList = self.getAddrLists()
        if not self.fileList:
            SysMgr.printWarn("failed to get file list on memory map")

        # continue target #
        if needStop:
            self.cont()

        return ret

    @staticmethod
    def getRealOffsetInfo(fileList, filePath):
        magicstr = SysMgr.magicStr
        totalDiff = 0

        origPath, number = filePath.split(magicstr)
        if not number:
            return origPath, fileList[origPath]["startAddr"], 0

        number = long(number)
        prevPath = curPath = None

        # walk to previous segments #
        while 1:
            # update prev path #
            if not prevPath:
                if number > 0:
                    prevPath = "%s%s%s" % (origPath, magicstr, number - 1)
                else:
                    if origPath in fileList:
                        prevPath = origPath
                    else:
                        prevPath = "%s%s" % (origPath, magicstr)
            else:
                prevPath = curPath

            curPath = "%s%s%s" % (origPath, magicstr, number)

            # add diff by hole #
            curStart = long(fileList[curPath]["vstart"])
            prevEnd = long(fileList[prevPath]["vend"])
            totalDiff += curStart - prevEnd

            if number == 0:
                break

            number -= 1

        # set startAddr and filePath to 1st segment's one #
        startAddr = fileList[origPath]["vstart"]
        filePath = origPath

        # disable diff by hole #
        totalDiff = 0

        return filePath, startAddr, totalDiff

    def getSymbolInfo(self, vaddr, onlyFunc=True, onlyExec=True):
        # get symbol info from cache list #
        if vaddr in self.symbolCacheList:
            return self.symbolCacheList[vaddr]

        # check exceptional cases #
        if not vaddr or vaddr < 0:
            return None
        elif not self.pid:
            SysMgr.printErr("failed to get PID to get symbol")
            return None
        elif vaddr in self.failedAddrList and self.failedAddrList[vaddr]:
            return None

        # open memory map file #
        if not self.mapFd:
            self.mapFd = FileAnalyzer.getMapFd(self.pid)
            if not self.mapFd:
                return None

        # scan process memory map #
        if self.needMapScan:
            if self.loadSymbols(onlyFunc=onlyFunc, onlyExec=onlyExec):
                self.updateBpList(verb=False)

        # get file name by address #
        fname = self.getFileFastFromMap(vaddr)
        if not fname:
            # set variable to rescan process map #
            self.needMapScan = True

            # print error message and return #
            if SysMgr.warnEnable:
                SysMgr.printWarn(
                    (
                        "failed to get symbol via %s for %s(%s) "
                        "because there is no file mapped to the address"
                    )
                    % (hex(vaddr).rstrip("L"), self.comm, self.pid)
                )

            return None

        # get real offset for memory hole #
        totalDiff = 0
        if SysMgr.magicStr in fname:
            fname, vstart, totalDiff = Debugger.getRealOffsetInfo(
                self.pmap, fname
            )
        else:
            vstart = self.pmap[fname]["vstart"]

        vend = self.pmap[fname]["vend"]

        # get offset in the file #
        offset = vaddr - vstart + totalDiff
        if offset < 0:
            # update anon map #
            if not self.amap:
                self.amap = FileAnalyzer.getAnonMapInfo(self.pid, self.mapFd)

            # check JIT-compiled code #
            for addrs in self.amap:
                if not addrs[0] <= vaddr <= addrs[1]:
                    continue

                # get JIT-compiled symbol #
                symInfo = self.getJITSymbolByOffset(vaddr)
                if symInfo[0] != "??":
                    # set path #
                    if symInfo[2] == "??":
                        path = "JIT"
                    else:
                        path = symInfo[2]

                    # return symbol info #
                    ret = [symInfo[0], path, "??", "??", "??", "??"]
                    self.symbolCacheList[vaddr] = ret
                    return ret

                # convert address #
                if SysMgr.showAll:
                    addr = hex(vaddr).rstrip("L")
                else:
                    addr = "JIT"

                # return #
                return [addr, "JIT", "??", "??", "??", "??"]

            # set variable to rescan process map #
            self.needMapScan = True

            # register failed address #
            if vaddr in self.failedAddrList:
                self.failedAddrList[vaddr] = True
            else:
                self.failedAddrList.setdefault(vaddr, False)

            # print error message and return #
            if SysMgr.warnEnable:
                SysMgr.printWarn(
                    (
                        "failed to get symbol via %s for %s(%s) "
                        "because of wrong offset"
                    )
                    % (hex(vaddr).rstrip("L"), self.comm, self.pid)
                )

            return None

        # remove suffix in file name #
        fname = fname.rsplit(SysMgr.magicStr, 1)[0]

        # get ELF object #
        fcache = ElfAnalyzer.getObject(fname)
        if not fcache:
            ret = [
                hex(offset) if Debugger.envFlags["SHOWADDR"] else "??",
                fname,
                "??",
                "??",
                "??",
                "??",
            ]
            self.symbolCacheList[vaddr] = ret
            return ret

        # check executable type #
        if not ElfAnalyzer.isRelocFile(fname):
            offset = vaddr

        # get native symbol #
        try:
            sym, size = fcache.getSymbolByOffset(offset, onlyFunc=onlyFunc)
            ret = [sym, fname, hex(offset).rstrip("L"), vstart, vend, size]
            self.symbolCacheList[vaddr] = ret
            return ret
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn(
                "failed to get symbol via %s for %s(%s)"
                % (hex(offset).rstrip("L"), self.comm, self.pid),
                reason=True,
            )

            # return #
            return [hex(offset).rstrip("L"), fname, "??", "??", "??", "??"]

    def getAddrLists(self):
        fileList = []
        addrList = []

        if not self.pmap:
            return [], []

        for f, item in sorted(self.pmap.items(), key=lambda x: x[1]["vend"]):
            fileList.append(f)
            addrList.append(item["vend"])

        return fileList, addrList

    def getFileFromMap(self, vaddr):
        if not self.pmap:
            return None

        try:
            vaddr = long(vaddr)
        except:
            return None

        for fname, value in self.pmap.items():
            if value["vstart"] == vaddr:
                return fname

        return None

    def getParamVal(self, argList, reverse=False):
        if not argList:
            return ""

        # get a arg set #
        params = argList.pop(0 if reverse else -1)
        if not params:
            return "()"

        return "(%s)" % ", ".join(
            [
                "%s=%s" % (arg[2], self.convParamVal(arg[1], arg[4]))
                for arg in params
            ]
        )

    def getFileFastFromMap(self, vaddr):
        try:
            idx = UtilMgr.bisect_left(self.addrList, vaddr)
            return self.fileList[idx]
        except SystemExit:
            sys.exit(0)
        except:
            return None

    def printContext(
        self,
        regs=True,
        bt=True,
        sig=True,
        deref=True,
        args=None,
        newline=False,
        brief=False,
    ):
        # check skip condition #
        if not regs and not bt:
            return
        elif not SysMgr.printEnable:
            return
        elif "NOCONTEXT" in SysMgr.environList:
            return

        if newline:
            prefix = "\n"
        else:
            prefix = ""

        try:
            # check group id #
            if not self.tgid:
                self.tgid = long(SysMgr.getTgid(self.pid))
                self.tgcomm = SysMgr.getComm(self.tgid)

            taskInfo = "%s(%s)<-%s(%s)" % (
                self.comm,
                self.pid,
                self.tgcomm,
                self.tgid,
            )
        except:
            taskInfo = "??(%s)" % self.pid

        isPrinted = False

        # update regs #
        if self.pc is None:
            self.updateRegs()

        # print register #
        if regs and not "NOREG" in SysMgr.environList:
            # set regsdict #
            self.regsDict = self.regs._getdict()

            if not isPrinted:
                SysMgr.addPrint(prefix, force=True)
                isPrinted = True

            SysMgr.addPrint(
                "%s\n\tRegister Info [%s]\n%s\n"
                % (twoLine, taskInfo, oneLine),
                force=True,
            )

            regstr = ""
            regstrline = ""
            for reg, val in sorted(self.regsDict.items()):
                rvalue = ""
                if deref and val:
                    rvalue = self.readMem(val, verb=False)
                    if rvalue:
                        try:
                            rvalue = '"%s"' % rvalue.decode("utf-8")
                            rvalue = re.sub("\W+", "", rvalue)
                        except SystemExit:
                            sys.exit(0)
                        except:
                            rvalue = hex(UtilMgr.convStr2Word(rvalue))
                            rvalue = rvalue.rstrip("L")

                # convert reference value #
                if rvalue:
                    rvalue = "[%s]" % rvalue
                else:
                    rvalue = ""

                # print register value #
                if brief:
                    newline = "%s(0x%x%s)" % (reg, val, rvalue)
                    if len(newline) + len(regstrline) >= min(
                        SysMgr.ttyCols, SysMgr.lineLength
                    ):
                        if regstr:
                            regstr = "%s\n%s" % (regstr, regstrline)
                        else:
                            regstr = regstrline
                        regstrline = newline
                    else:
                        if regstrline:
                            regstrline = "%s | %s" % (regstrline, newline)
                        else:
                            regstrline = newline
                else:
                    SysMgr.addPrint(
                        "%13s: 0x%x%s\n" % (reg, val, rvalue), force=True
                    )

            if brief:
                SysMgr.addPrint("%s\n%s\n" % (regstr, regstrline), force=True)

        # print signal #
        if sig and not "NOSIG" in SysMgr.environList:
            try:
                signame = ConfigMgr.SIG_LIST[long(self.lastSig)]

                if not isPrinted:
                    SysMgr.addPrint(prefix, force=True)
                    isPrinted = True

                SysMgr.addPrint(
                    "%s\n\tSignal Info [%s]\n%s\n"
                    % (twoLine, taskInfo, oneLine),
                    force=True,
                )
                SysMgr.addPrint(
                    "%s: %s\n" % (self.lastSig, signame), force=True
                )
            except SystemExit:
                sys.exit(0)
            except:
                pass

        # print backtrace #
        if bt and not "NOBT" in SysMgr.environList:
            # disable loading all symbols #
            lazyCache = SysMgr.environList.get("LAZYCACHE", None)
            SysMgr.environList["LAZYCACHE"] = lazyCache

            # get backtrace #
            backtrace = self.getBacktrace(cur=True, force=True)

            # recover lazycache flag #
            if not lazyCache:
                SysMgr.environList.pop("LAZYCACHE", None)

            if backtrace:
                if not isPrinted:
                    SysMgr.addPrint(prefix, force=True)
                    isPrinted = True

                SysMgr.addPrint(
                    "%s\n\tBacktrace Info [%s]\n%s\n"
                    % (twoLine, taskInfo, oneLine),
                    force=True,
                )

                # get args #
                if self.btArgList:
                    argList = list(self.btArgList[1:])
                    self.getRetAddr(
                        self.pc, argList, onlyArg=True, cur=self.isBreakMode
                    )
                    argList.insert(0, argList.pop())
                else:
                    argList = []

                for item in backtrace:
                    addr, sym, path = item

                    if addr:
                        # get offset #
                        try:
                            boffset = long(self.getSymbolInfo(addr)[2], 16)
                            bfaddr = ElfAnalyzer.getSymOffset(
                                sym, path, cache=True
                            )[0][0]
                            offset = boffset - bfaddr
                        except SystemExit:
                            sys.exit(0)
                        except:
                            offset = 0

                        addr = hex(addr).rstrip("L")
                    else:
                        offset = 0

                    # add arg info #
                    args = self.getParamVal(argList, reverse=True)

                    # set offset info #
                    if offset:
                        offset = "+%s" % offset
                    else:
                        offset = ""

                    # set address info #
                    if sym == addr:
                        addr = ""
                    else:
                        addr = "/%s" % addr

                    SysMgr.addPrint(
                        "%s%s%s%s[%s]\n" % (sym, args, offset, addr, path),
                        force=True,
                    )

        # print last line #
        if isPrinted:
            SysMgr.addPrint("%s\n" % oneLine, force=True)

        if SysMgr.outPath:
            self.callPrint.append(SysMgr.bufferString)
        else:
            SysMgr.doPrint(newline=False, clear=True)

        SysMgr.clearPrint()

    def setRet(self, val, temp=False, update=False):
        try:
            if temp:
                setattr(self.tempRegs, self.retreg, val)
            else:
                setattr(self.regs, self.retreg, val)

            if update:
                self.setRegs()
                self.updateRegs()

            return True
        except SystemExit:
            sys.exit(0)
        except:
            return False

    def getRet(self, temp=False):
        try:
            if temp:
                ret = getattr(self.tempRegs, self.retreg)
            else:
                ret = getattr(self.regs, self.retreg)

            if c_int(ret).value == -1:
                return -1
            else:
                return c_long(ret).value
        except SystemExit:
            sys.exit(0)
        except:
            return None

    def setSyscall(self, syscall):
        try:
            if UtilMgr.isNumber(syscall):
                nrSyscall = long(syscall)
            else:
                nrSyscall = SysMgr.getNrSyscall(syscall)

            # TODO: set syscall number to the specific register #
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to get syscall number", reason=True)
            sys.exit(-1)

        self.setRegs()

    def setNrSyscall(self, nr=None):
        try:
            # set invalid syscall number #
            if not nr:
                nr = 999
            return setattr(self.regs, self.sysreg, nr)
        except SystemExit:
            sys.exit(0)
        except:
            return None

    def getNrSyscall(self):
        try:
            return getattr(self.regs, self.sysreg)
        except SystemExit:
            sys.exit(0)
        except:
            return None

    def parseSyscallFilter(self):
        if hasattr(self, "ignSyscallList") or self.pid == SysMgr.pid:
            return

        # get filter string #
        syscallFilter = ",".join(SysMgr.environList.get("IGNSYSCALL", []))
        if not syscallFilter:
            self.ignSyscallList = {}
            return

        # init filter list #
        ignList = []

        # parse filter values #
        SysMgr.parseSyscallOption(syscallFilter, ignList, [])

        # get ignore list #
        self.ignSyscallList = {x: 0 for x in ignList}
        self.hideIgnSyscall = "HIDEIGNSYSCALL" in SysMgr.environList

    def changeSyscall(self, rep=None):
        try:
            self.setNrSyscall(rep)
            self.setRegs()
            self.ptrace(self.cmd)
            self.waitpid()
        except SystemExit:
            sys.exit(0)
        except:
            try:
                nameSyscall = ConfigMgr.sysList[long(rep)]
            except SystemExit:
                sys.exit(0)
            except:
                nameSyscall = "N/A"
            SysMgr.printErr(
                "failed to change syscall '%s' to %s(%s)"
                % (self.syscall, nameSyscall, rep),
                True,
            )

    def addCall(self, sym):
        self.callstack.append([self.sp, sym])

    def updateStack(self, sym):
        while 1:
            if not self.callstack:
                return
            elif (
                self.callstack[-1][0] > self.sp
                or self.callstack[-1][0] >= self.prevSp
                or self.callstack[-1][1] == sym
            ):
                self.callstack.pop()
            else:
                return

    def popSample(self, sym):
        if self.callList:
            self.callList.pop(-1)

        if sym in self.callTable:
            self.callTable[sym]["cnt"] -= 1
            if self.callTable[sym]["cnt"] == 0:
                self.callTable.pop(sym, None)

        if self.totalCall > 0:
            self.totalCall -= 1

    def addSample(
        self, sym, filename, realtime=False, bt=None, err=None, elapsed=None
    ):
        # check skip condition #
        if self.runStatus:
            if Debugger.envFlags["ONLYWAIT"]:
                return
        else:
            if Debugger.envFlags["EXCEPTWAIT"]:
                return

        # increase err count #
        if err:
            try:
                self.callTable[sym]["err"] += 1
            except:
                pass
            finally:
                return

        # add sample #
        if not realtime:
            self.callList.append([sym, self.current, filename])
            return

        self.totalCall += 1

        if Debugger.envFlags["ONLYSYM"] and bt and isinstance(bt[0], list):
            # remove anonymous symbol #
            while 1:
                if sym != "??":
                    break
                elif not bt:
                    break
                elif bt[0][1] == "??":
                    bt.pop(0)
                    continue
                else:
                    sym = bt[0][1]
                    filename = bt[0][2]
                    bt.pop(0)
                    break

            # remove contiguous symbol #
            while 1:
                if not bt or not SysMgr.showAll:
                    break
                elif sym == bt[0][1] and filename == bt[0][2]:
                    bt.pop(0)
                    continue
                break

        # convert anonymous symbol to filename to prevent call table corruption #
        if sym == "??":
            sym = filename

        # check wait status #
        if not self.runStatus and self.mode in ("sample", "kernel", "pycall"):
            sym = "WAIT{%s}" % sym

        # add backtrace #
        if bt:
            btStr = self.getBtStr(bt)
        else:
            btStr = None

        # add symbol table #
        if sym in self.callTable:
            self.callTable[sym]["cnt"] += 1
        else:
            self.callTable[sym] = {
                "cnt": 1,
                "path": filename,
                "err": 0,
                "backtrace": {},
                "elapsed": 0.0,
                "min": 0.0,
                "max": 0.0,
            }

        # increase count of callstack #
        if btStr:
            try:
                self.callTable[sym]["backtrace"][btStr] += 1
            except SystemExit:
                sys.exit(0)
            except:
                if sym in self.callTable:
                    self.callTable[sym]["backtrace"][btStr] = 1

        # calculate elapsed time #
        if elapsed:
            self.callTable[sym]["elapsed"] += elapsed
            if (
                self.callTable[sym]["min"] == 0
                or self.callTable[sym]["min"] > elapsed
            ):
                self.callTable[sym]["min"] = elapsed
            if (
                self.callTable[sym]["max"] == 0
                or self.callTable[sym]["max"] < elapsed
            ):
                self.callTable[sym]["max"] = elapsed

        # add file table #
        try:
            self.fileTable[filename]["cnt"] += 1
        except:
            self.fileTable[filename] = {"cnt": 1}

        if not SysMgr.outPath:
            return

        self.callList.append([sym, self.current, filename])

    def checkSymbol(self, sym, newline=False, exceptFilter=[]):
        if not SysMgr.customCmd or SysMgr.outPath:
            return

        # check filter #
        needNewline = False
        isPaused = False
        for cmd in SysMgr.customCmd:
            item = cmd.split("|")
            # skip #
            if not UtilMgr.isValidStr(sym, [item[0]]):
                continue
            # set pause flag #
            elif len(item) == 1:
                isPaused = True
                break

            for command in item[1:]:
                # check skip condition #
                if not exceptFilter:
                    pass
                elif UtilMgr.isValidStr(command, exceptFilter):
                    continue

                # execute commands #
                args = self.readArgs()
                self.executeCmd([command], sym, None, args)
                needNewline = True

        # check return #
        if not isPaused:
            if needNewline:
                SysMgr.printPipe()
            return

        sys.stdout.write("\n")

        if SysMgr.showAll:
            # print register set #
            self.printContext(newline=newline)

        # disable timer #
        signal.alarm(0)

        SysMgr.waitUserInput(
            wait=0, msg="\n%s() is detected! press ENTER to continue..." % sym
        )

        # enable timer #
        SysMgr.updateTimer()

    def getBtStr(self, bt, indent=None, maximum=0, force=False):
        if not force and self.btStr:
            return self.btStr
        elif not bt:
            self.btStr = ""
            return self.btStr

        btStr = ""
        prevSym = None
        prevFile = None
        cnt = 0

        noRedundantCnt = "NOREDCNT" in SysMgr.environList
        keepRedundantCall = "KEEPREDCALL" in SysMgr.environList

        # set default indentation #
        if indent is None:
            indentStr = " " * self.indentLen
            pos = self.indentLen
        else:
            indentStr = " " * indent
            pos = indent

        # set maximum depth #
        if maximum == 0:
            maximum = min(SysMgr.lineLength, SysMgr.ttyCols)

        # get arg list #
        if self.btArgList:
            argList = list(self.btArgList[1:])
        else:
            argList = []

        # get mode #
        if isinstance(bt[0], list):
            isUser = True
        else:
            isUser = False

        for item in bt:
            # user #
            if isUser:
                # get symbol and file info #
                if item[1] == item[2] == "??":
                    sym = hex(item[0]).rstrip("L")
                else:
                    sym = item[1]

                fname = item[2]
            # kernel #
            else:
                # skip no symbol item #
                if not item:
                    continue

                sample = item.split("] ", 1)
                sym = sample[1] if len(sample) > 1 else item
                fname = "KERNEL"

            # remove redundant symbols #
            if not keepRedundantCall and prevSym == sym and prevFile == fname:
                if not noRedundantCnt:
                    cnt += 1
                continue
            else:
                prevSym = sym
                prevFile = fname

            # check redundant symbols #
            if cnt > 0:
                cntStr = " * %s" % (cnt + 1)
                btStr += cntStr
                pos += len(cntStr)
                cnt = 0

            # add arg list #
            if argList:
                sym += self.getParamVal(argList, reverse=True)

            # build a new string #
            newStr = " <- %s[%s]" % (sym, fname)
            if btStr and len(newStr) + pos > maximum:
                newStr = "\n%s%s" % (indentStr, newStr)
                pos = len(newStr) - 1
            else:
                pos += len(newStr)

            # add a symbol to backtrace #
            btStr += newStr

        if btStr == "":
            self.btStr = "??"
        else:
            self.btStr = btStr

        return self.btStr

    def startSamplingKernel(self):
        # detach task #
        self.detach(only=True)

        def _checkAlive():
            if not self.isAlive():
                SysMgr.printErr("%s(%s) is terminated" % (self.comm, self.pid))
                sys.exit(0)

        while 1:
            # update sample time and wait #
            self.updateSampleTime()
            time.sleep(self.sampleTime)

            try:
                self.stackFd.seek(0)
                sample = self.stackFd.read()
                if sample:
                    sample = sample.split("\n")
                else:
                    _checkAlive()
            except SystemExit:
                sys.exit(0)
            except:
                # check alive #
                _checkAlive()

                if not self.stackFd:
                    self.stackFd = open(
                        "%s/%s/stack" % (SysMgr.procPath, self.pid)
                    )
                else:
                    SysMgr.printErr(
                        "failed to read kernel stack data for %s(%s)"
                        % (self.comm, self.pid),
                        True,
                    )
                    continue

                sample = self.stackFd.read()
                if sample:
                    sample = sample.split("\n")

            if sample:
                sym = sample[0].split("] ", 1)
                sym = sym[1] if len(sym) > 1 else sample[0]
                if len(sample) > 1:
                    bt = sample[1:]
                else:
                    bt = None
            else:
                sym = "N/A"
                bt = None

            # add sample #
            self.addSample(sym, "KERNEL", realtime=True, bt=bt)

            # check run status #
            self.runStatus = self.isInRun()

        sys.exit(0)

    def updateSampleTime(self):
        # wait for sampling time #
        if self.runStatus:
            self.sampleTime = self.sampleTimeOrig
        elif not self.multi or not self.incrementalSampling:
            self.sampleTime = self.sampleTimeOrig * 2
        else:
            # increase wait time by 2 #
            if self.sampleTimeMax > self.sampleTime:
                self.sampleTime *= 2
            # limit the maximum wait time #
            else:
                self.sampleTime = self.sampleTimeMax

    def checkInterval(self):
        # continue target thread #
        if self.cont(check=True) < 0:
            sys.exit(-1)

        self.traceStatus = False

        # update sample time and wait #
        self.updateSampleTime()
        time.sleep(self.sampleTime)

        # update run status #
        self.runStatus = self.isInRun()

        # stop target thread #
        if self.stop() < 0:
            sys.exit(-1)

        self.traceStatus = True

    def getBacktrace(self, limit=32, cur=False, force=False, native=False):
        restored = True

        try:
            if not force and self.btList:
                return self.btList
            # prevent recursive backtrace call #
            elif self.inBacktrace:
                return

            # set initial flags #
            self.inBacktrace = True

            # return special language callstack #
            if not native:
                # python callstack #
                if Debugger.envFlags["PYSTACK"]:
                    btList = self.handlePycall(retbt=True, cur=True)
                    if btList:
                        self.btList = btList
                        return self.btList

            # check libcorkscrew #
            if not SysMgr.libcorkscrewObj:
                SysMgr.initLibcorkscrew()

            # unwind stack using libcorkscrew #
            if SysMgr.libcorkscrewObj != -1:
                # get backtrace buffer #
                if not self.csBtList:
                    self.csBtList = (self.btframe * limit)()
                backtrace = self.csBtList

                libcc = SysMgr.libcorkscrewObj

                # get context #
                if not self.csContext:
                    self.csContext = libcc.load_ptrace_context(c_int(self.pid))
                context = self.csContext

                # get backtrace #
                frames = libcc.unwind_backtrace_ptrace(
                    c_int(self.pid),
                    context,
                    byref(backtrace),
                    c_size_t(0),
                    c_size_t(limit),
                )
                if frames <= 0:
                    return []

                """
                # pick addresses from structs #
                btList = []
                for idx in xrange(frames):
                    offset = idx * sizeof(self.btframe)
                    obj = cast(addressof(backtrace)+offset, self.btframe_ptr)
                    addr = obj.contents.absolute_pc
                    btList.append(addr)

                # convert addresses to symbols #
                self.btList = self.convAddrList(btList)
                """

                # get symbol buffer #
                if not self.csBtSymList:
                    self.csBtSymList = (self.btsym * limit)()
                btsyms = self.csBtSymList
                symsAddr = addressof(btsyms)

                # convert addresses to symbols #
                libcc.get_backtrace_symbols_ptrace(
                    context, byref(backtrace), c_size_t(frames), byref(btsyms)
                )

                # pick symbols from structs #
                self.btList = []
                for idx in xrange(frames):
                    offset = idx * sizeof(self.btsym)
                    obj = cast(symsAddr + offset, self.btsym_ptr).contents

                    # get symbol #
                    if obj.demangled_name:
                        symbol = obj.demangled_name.decode()
                    elif obj.symbol_name:
                        symbol = obj.symbol_name.decode()
                    else:
                        symbol = "??"

                    # get fname #
                    if obj.map_name:
                        fname = obj.map_name.decode()
                    else:
                        fname = "??"

                    self.btList.append([obj.relative_pc, symbol, fname])
            # unwind stack using DWARF #
            elif SysMgr.dwarfEnable:
                # init arg list for backtraces #
                argList = []
                self.btArgList = []

                # backup registers #
                self.backupRegs(bt=True)
                restored = False

                # load symbols from new objects #
                if not self.startAddr:
                    self.loadSymbols()

                # set start address #
                ip = self.pc
                if self.mode == "break":
                    ip -= self.prevInstOffset

                # add current symbol #
                if cur:
                    btList = [ip]
                else:
                    btList = []

                while 1:
                    # get return address #
                    raddr = self.getRetAddr(ip, argList)
                    if not raddr:
                        break

                    # add return addresses #
                    if isinstance(raddr, list):
                        btList += raddr
                    else:
                        btList.append(raddr)

                    # check max length #
                    if len(btList) >= limit:
                        break

                    # update IP #
                    ip = raddr

                # update backtrace args #
                self.btArgList = argList

                # convert addresses to symbols #
                self.btList = self.convAddrList(btList)

                # print backtrace for debugging #
                if False:
                    print("\n" + oneLine)
                    for item in self.btList:
                        print(item)
                    print(oneLine + "\n")

                # restore registers #
                self.restoreRegs(bt=True)
                restored = True
            # unwind stack using FP/SP #
            else:
                # load symbols from new objects #
                if not self.startAddr:
                    self.loadSymbols()

                self.btList = self.backtrace[SysMgr.arch](limit, cur)

            return self.btList
        except SystemExit:
            sys.exit(0)
        except:
            if self.pc:
                addr = " from %x" % self.pc
            else:
                addr = ""

            SysMgr.printWarn("failed to get backtrace%s" % addr, reason=True)

            if SysMgr.dwarfEnable and not restored:
                # restore registers #
                self.restoreRegs(bt=True)

            return []
        finally:
            self.inBacktrace = False

    def readWord(self, targetAddr):
        if not targetAddr:
            return None
        elif targetAddr % ConfigMgr.wordSize == 0:
            return self.accessMem(self.peekCmd, targetAddr)
        else:
            return self.readMem(targetAddr, retWord=True)

    def getWordFromStack(self, addr, update=False):
        if update:
            self.getStack()

        # verify address #
        if not self.startStack <= addr <= self.endStack + addr:
            return None

        offset = addr - self.startStack

        word = self.stack[offset : offset + ConfigMgr.wordSize]
        if ConfigMgr.wordSize == 8:
            return struct.unpack("Q", word)[0]
        else:
            return struct.unpack("I", word)[0]

    def getStack(self):
        if not self.pmap:
            return False

        stack = self.pmap["stack"]
        self.startStack = stack["vstart"]
        self.endStack = stack["vend"] + ConfigMgr.wordSize
        self.stackSize = self.endStack - self.startStack
        self.stack = self.readMem(self.startStack, self.stackSize)

        if self.stack:
            return True
        else:
            return False

    def convParamVal(self, typestr, val):
        if typestr == "char":
            try:
                return repr(c_char(val).value.decode())
            except SystemExit:
                sys.exit(0)
            except:
                return val
        elif typestr == "short":
            return c_short(val).value
        elif typestr == "float":
            return c_float(val).value
        elif typestr == "double":
            return c_double(val).value
        else:
            return val

    def getParamsInfo(self, dwarf, vaddr, faddr, cfa, cur):
        paramList = []
        if not "info" in dwarf or not faddr in dwarf["info"]:
            return paramList

        # frame base #
        base = dwarf["info"][faddr].get("frame", 0)

        # params #
        if "param" in dwarf["info"][faddr]:
            abbrevIdx = dwarf["info"][faddr]["abbrev"]
            abbrev = dwarf["abbrev"][abbrevIdx]
            updateFpRegs = False
            genRegNum = 0
            fpRegNum = 0

            for item in dwarf["info"][faddr]["param"]:
                # name #
                if "name" in abbrev[item]:
                    name = abbrev[item]["name"]
                else:
                    name = "??"

                # size #
                if "size" in abbrev[item]:
                    size = abbrev[item]["size"]
                else:
                    size = 0

                # type element #
                typeName = ""
                if "type" in abbrev[item]:
                    typeNum = abbrev[item]["type"]
                else:
                    typeNum = -1

                # get full type #
                if typeNum in ElfAnalyzer.cachedTypes:
                    (
                        typeName,
                        size,
                        decChar,
                        isAddr,
                    ) = ElfAnalyzer.cachedTypes[typeNum]
                else:
                    typeName = ""
                    typeNumOrig = typeNum

                    # build type name #
                    while 1:
                        if not typeNum in abbrev:
                            break

                        # add a type attribute #
                        if "name" in abbrev[typeNum]:
                            typeName = "%s %s" % (
                                typeName,
                                abbrev[typeNum]["name"],
                            )

                        # handle struct and union types #
                        """
                        if "member" in abbrev[typeNum]:
                            for member in abbrev[typeNum]["member"]:
                                abbrev[member]
                        """

                        # size #
                        if "size" in abbrev[typeNum]:
                            size = abbrev[typeNum]["size"]

                        if not "type" in abbrev[typeNum]:
                            break

                        typeNum = abbrev[typeNum]["type"]

                    # remove heading space #
                    typeName = typeName.lstrip()

                    # decide size and decoding type #
                    isAddr = False
                    if size == 0:
                        decChar = None
                    elif typeName.startswith("*"):
                        size = ConfigMgr.wordSize
                        decChar = self.decChar
                        isAddr = True
                    elif typeName in ConfigMgr.UNPACK_TYPE:
                        decChar = ConfigMgr.UNPACK_TYPE[typeName]
                    elif (
                        "unsigned" in typeName
                        and "U" + str(size) in ConfigMgr.UNPACK_TYPE
                    ):
                        decChar = ConfigMgr.UNPACK_TYPE["U" + str(size)]
                    elif "S" + str(size) in ConfigMgr.UNPACK_TYPE:
                        decChar = ConfigMgr.UNPACK_TYPE["S" + str(size)]
                    else:
                        decChar = None

                    # save type info #
                    ElfAnalyzer.cachedTypes[typeNumOrig] = [
                        typeName,
                        size,
                        decChar,
                        isAddr,
                    ]

                # location #
                paramVal = "??"
                if size == 0:
                    pass
                elif cur:
                    # handle float types #
                    if typeName in ("float", "double", "long double"):
                        # read float registers #
                        if not updateFpRegs:
                            if self.getFpRegs() != 0:
                                sys.exit(-1)
                            updateFpRegs = True
                        # TODO: handle long double (16 bytes) #
                        paramVal = self.getFpItemRegs(typeName, fpRegNum)
                        fpRegNum += 1
                    # handle general types #
                    else:
                        paramVal = self.readArgs()[genRegNum]
                        genRegNum += 1
                        if isAddr:
                            paramVal = hex(paramVal)
                elif "loc" in abbrev[item]:
                    # sec_offset #
                    # TODO: handle this attribute #
                    if isinstance(abbrev[item]["loc"], long):
                        paramVal = "??"
                    # exprloc(reg) #
                    # TODO: handle this attribute #
                    elif len(abbrev[item]["loc"]) == 1:
                        paramVal = "??"
                    # exprloc(offset) #
                    elif len(abbrev[item]["loc"]) == 2:
                        paramAddr = abbrev[item]["loc"][1]
                        paramVal = self.readMem(cfa + paramAddr, size)
                        try:
                            paramVal = struct.unpack(decChar, paramVal)[0]
                        except SystemExit:
                            sys.exit(0)
                        except:
                            SysMgr.printWarn(
                                "failed to unpack '%s' using '%s'"
                                % (paramVal, decChar),
                                reason=True,
                            )
                        if isAddr:
                            paramVal = hex(paramVal)

                # add a parameter info #
                paramList.append([vaddr, typeName, name, size, paramVal])

        return paramList

    def getRetAddr(self, vaddr, argList=[], onlyArg=False, cur=False):
        # get file name #
        fname = self.getFileFastFromMap(vaddr)
        if not fname:
            return None

        # get file offset #
        first = True
        while 1:
            ret = self.getSymbolInfo(vaddr)
            # try again after updating memory map #
            if not ret:
                if first:
                    first = False
                    self.loadSymbols()
                    continue
                else:
                    return None
            elif ret[1] == "JIT":
                # update named registers #
                self.updateNamedRegs()

                # return addr from FP #
                bts = self.backtrace[SysMgr.arch](cur=False)
                if bts:
                    return [item[0] for item in bts]
                else:
                    return None
            elif ret[2] == "??":
                return None
            else:
                break

        sym = ret[0]
        foffset = long(ret[2], 16)

        # get ELF object #
        fobj = ElfAnalyzer.getObject(fname)
        if not fobj:
            return None

        # check DWARF info #
        if "dwarf" not in fobj.attr or not fobj.attr["dwarf"]["CFAIndex"]:
            SysMgr.printWarn(
                "failed to find DWARF info for %s(%s) in %s"
                % (sym, hex(foffset), fname)
            )

            # update named registers #
            self.updateNamedRegs()

            # return addr from FP #
            bts = self.backtrace[SysMgr.arch](cur=False)
            if bts:
                return [item[0] for item in bts]
            else:
                return None

        # get function address from CFA index table #
        dwarf = fobj.attr["dwarf"]
        idx = UtilMgr.bisect_left(dwarf["CFAIndex"], foffset) - 1
        faddr = dwarf["CFAIndex"][idx]
        # TODO: check function scope from faddr #
        if not faddr in dwarf["CFATable"]:
            SysMgr.printWarn(
                "failed to find CFA table info for %s(%s) in %s"
                % (sym, hex(faddr), fname)
            )
            return None

        # get effective CFA rule #
        rule = None
        for line in dwarf["CFATable"][faddr]:
            if foffset < line["pc"]:
                break
            rule = line
        if not rule or not "cfa" in rule:
            SysMgr.printWarn(
                "failed to find CFA rule info for %s(%s) in %s"
                % (sym, hex(foffset), fname)
            )
            if vaddr != self.lr:
                return self.lr
            else:
                return None

        # define index for CFA member variables #
        regIdx = ElfAnalyzer.CFARule.REG
        offsetIdx = ElfAnalyzer.CFARule.OFFSET
        exprIdx = ElfAnalyzer.CFARule.EXPR

        # get CFA #
        cfaInfo = rule["cfa"]
        if cfaInfo[exprIdx]:
            return None
        reg = ConfigMgr.regList[cfaInfo[regIdx]]
        regval = getattr(self.regs, reg)
        offset = cfaInfo[offsetIdx]
        cfa = regval + offset

        # get parameter #
        if Debugger.envFlags["PRINTARG"]:
            paramList = self.getParamsInfo(dwarf, vaddr, faddr, cfa, cur)
            argList.append(paramList)

        # check only argument requirement #
        if onlyArg:
            return None

        # check CFA #
        if cfa == self.prevCfa:
            return None
        else:
            self.prevCfa = cfa

        """
        # check stack pointer #
        if cfa < self.sp:
            return None
        """

        # recover registers #
        argIdx = ElfAnalyzer.RegisterRule.ARG
        for num, value in rule.items():
            try:
                long(num)
                reg = ConfigMgr.regList[num]
                offset = value[argIdx]
                if offset is None:
                    continue
                rval = self.readWord(cfa + offset)
                setattr(self.regs, reg, rval)
            except SystemExit:
                sys.exit(0)
            except:
                pass

        # get CFA offset for return address #
        pcIdx = ConfigMgr.pcRegIndex[SysMgr.arch]

        # update stack pointer for previous context #
        self.updateNamedRegs()
        self.setSP(cfa)

        # use specific register #
        if pcIdx in rule:
            roffset = rule[pcIdx][argIdx]
            if roffset is None:
                return None

            # calculate address for return address #
            raddr = cfa + roffset
        # just use LR #
        elif self.arch in ("aarch64", "arm", "riscv64"):
            return self.lr
        # no more frame #
        else:
            return None

        # return next IP from stack #
        try:
            return self.readWord(raddr)
        except SystemExit:
            sys.exit(0)
        except:
            return None

    def getBacktrace_X86(self, limit=32, cur=False):
        nextFp = self.fp
        btList = []

        if cur and self.pc:
            btList.insert(0, self.pc)

        # update stack for target #
        readaheadStack = False
        if readaheadStack:
            self.getStack()

        # get 1st address from stack #
        targetAddr = self.sp
        if readaheadStack:
            value = self.getWordFromStack(targetAddr)
        else:
            value = self.readWord(targetAddr)

        if value > self.startAddr:
            btList.insert(0, value)

        while 1:
            if not nextFp or nextFp < self.sp:
                break

            # check max length #
            if len(btList) >= limit:
                break

            try:
                # get LR #
                targetAddr = nextFp + ConfigMgr.wordSize
                if targetAddr < self.startAddr:
                    break
                elif readaheadStack:
                    value = self.getWordFromStack(targetAddr)
                else:
                    value = self.readWord(targetAddr)

                # add call address #
                if value > 0:
                    try:
                        btList.append(long(value))
                    except SystemExit:
                        sys.exit(0)
                    except:
                        pass

                # get FP #
                if readaheadStack:
                    nextFp = self.getWordFromStack(targetAddr)
                else:
                    nextFp = self.readWord(nextFp)

                if nextFp < self.startAddr:
                    break
            except SystemExit:
                sys.exit(0)
            except:
                break

        return self.convAddrList(btList)

    def getBacktrace_ARM(self, limit=16, cur=False):
        nextFp = self.fp
        nextLr = self.lr

        if nextLr:
            btList = [nextLr]
        else:
            btList = []

        if cur and self.pc:
            btList.insert(0, self.pc)

        savedLr = nextLr

        while 1:
            # check max length #
            if len(btList) >= limit:
                break

            if not nextLr or not nextFp or nextLr & 0x1 or nextFp & 0x1:
                break

            try:
                # get LR #
                nextAddr = nextFp + ConfigMgr.wordSize
                if nextAddr < self.startAddr:
                    break
                else:
                    nextLr = self.readWord(nextAddr)

                # get FP #
                nextFp = self.readWord(nextFp)
                if nextFp < self.startAddr:
                    break
            except SystemExit:
                sys.exit(0)
            except:
                break

            # add address to list #
            if nextLr and savedLr != nextLr:
                btList.append(nextLr)
                savedLr = nextLr

        return self.convAddrList(btList)

    def getBacktrace_AARCH64(self, limit=32, cur=False):
        nextFp = self.fp
        nextLr = self.lr
        btList = [nextLr]

        if cur:
            btList.insert(0, self.pc)

        savedLr = nextLr

        # update stack for target #
        readaheadStack = False
        if readaheadStack:
            self.getStack()

        while 1:
            # check max length #
            if len(btList) >= limit:
                break

            if not nextLr or not nextFp or nextLr & 0x1 or nextFp & 0x1:
                break

            try:
                # get LR #
                nextAddr = nextFp + ConfigMgr.wordSize
                if nextAddr < self.startAddr:
                    break
                elif readaheadStack:
                    nextLr = self.getWordFromStack(nextAddr)
                else:
                    nextLr = self.readWord(nextAddr)

                # get FP #
                if nextFp < self.startAddr:
                    break
                elif readaheadStack:
                    nextFp = self.getWordFromStack(nextFp)
                else:
                    nextFp = self.readWord(nextFp)

                if nextFp <= 0:
                    break
            except SystemExit:
                sys.exit(0)
            except:
                break

            # add address to list #
            if savedLr != nextLr:
                btList.append(nextLr)
                savedLr = nextLr

        return self.convAddrList(btList)

    def convAddrList(self, btList):
        # get symbol and file from address #
        clist = []
        onlysym = Debugger.envFlags["ONLYSYM"]
        for addr in btList:
            res = self.getSymbolInfo(addr)
            if res:
                if res[0] == "??":
                    sym = res[2]
                else:
                    sym = res[0]

                # skip no symbol function #
                if onlysym and sym and sym.startswith("0x"):
                    continue

                clist.append([addr, sym, res[1]])
            elif not onlysym:
                clist.append([addr, "??", "??"])

        return clist

    def updateBpStat(self, sym, tdiff=0):
        # apply stat #
        try:
            prev, ttotal, tmin, tmax = self.bpcallStat[sym]

            if tdiff == 0:
                self.interDiff = tdiff = self.vdiff - prev
            ttotal += tdiff

            if tmax < tdiff:
                tmax = tdiff

            if tmin == 0 or tmin > tdiff:
                tmin = tdiff

            self.bpcallStat[sym] = [self.vdiff, ttotal, tmin, tmax]

            # save interval for stdev #
            if Debugger.envFlags["STDEV"]:
                self.bpcallInterStat.setdefault(sym, [])
                self.bpcallInterStat[sym].append(tdiff)
        except SystemExit:
            sys.exit(0)
        except:
            self.bpcallStat[sym] = [self.vdiff, 0, 0, 0]

    def getTraceBtStr(
        self,
        diffstr,
        tinfo,
        cont=True,
        cur=False,
        addBt=[],
        backtrace=[],
        python=False,
    ):
        def _getCommonPos(backtrace):
            # check contiguous tree presentation #
            commonPos = -1
            if not cont:
                return commonPos

            try:
                for item in reversed(self.prevStack):
                    # same pos at same level #
                    if item == backtrace[commonPos]:
                        commonPos -= 1
                        continue
                    # different item #
                    else:
                        break

                # out of stack #
                if backtrace[commonPos][1] == self.prevSym:
                    commonPos -= 1
            except SystemExit:
                sys.exit(0)
            except:
                pass

            return commonPos

        # get backtrace #
        if not backtrace:
            backtrace = self.getBacktrace(cur=cur)
            if addBt:
                commonIdx = 0
                for idx, item in enumerate(addBt):
                    if len(backtrace) > idx and item == backtrace[idx][0]:
                        commonIdx = idx + 1
                        continue
                    else:
                        break
                if commonIdx < len(addBt):
                    addBtList = self.convAddrList(addBt[commonIdx:])
                    backtrace = addBtList + backtrace

        # get indent info #
        depth = len(backtrace)
        diffindent = " " * len(diffstr)
        tinfoindent = " " * len(tinfo)

        # calculate common depth for previous stack #
        commonPos = _getCommonPos(backtrace)
        if commonPos == -1:
            commonPos = 0
            stack = backtrace
        else:
            if python:
                if SysMgr.pyFuncFilter:
                    commonPos += 1
            else:
                if SysMgr.customCmd:
                    commonPos += 1
            stack = backtrace[:commonPos]

        self.prevStack = backtrace

        # get arg list #
        if self.btArgList:
            argList = list(self.btArgList[1:])
            argList.append([])
        else:
            argList = []

        btStr = ""
        for sidx, item in enumerate(reversed(stack)):
            # get args #
            args = self.getParamVal(argList)

            # add context to string #
            if python:
                btStr += "\n%s %s%s%s%s [%s:%s]" % (
                    diffindent,
                    tinfoindent,
                    (sidx - (commonPos)) * "  ",
                    item[1],
                    args,
                    item[2],
                    item[0],
                )
            else:
                btStr += "\n%s %s%s%s%s/%s [%s]" % (
                    diffindent,
                    tinfoindent,
                    (sidx - (commonPos)) * "  ",
                    item[1],
                    args,
                    hex(item[0]).rstrip("L"),
                    item[2],
                )

        return btStr, depth

    def runExecMode(self):
        # stop target #
        if not self.isStopped():
            self.stop()
            self.waitpid()

        # read registers for target #
        if not self.updateRegs():
            sys.exit(-1)

        # print context #
        if (
            not "print" in SysMgr.customCmd
            and not "NOCONTEXT" in SysMgr.environList
        ):
            self.printContext(regs=SysMgr.showAll, newline=True)

        # read args #
        args = self.readArgs()

        # execute remote commands #
        for cmd in SysMgr.customCmd:
            self.executeCmd([cmd], args=args, force=True)

    def getBpContext(self, sym, addr, args, isRetBp):
        # set default symbol color #
        symColor = "GREEN"

        # convert args #
        if Debugger.envFlags["PRINTARG"]:
            argList = []
            self.getRetAddr(addr, argList, onlyArg=True, cur=True)
            if argList:
                # get a last args info #
                argstr = self.getParamVal(argList, reverse=True)
            else:
                argstr = ""
        # convert args #
        elif (
            Debugger.envFlags["CONVARG"]
            and sym.split("@", 1)[0] in ConfigMgr.SYSCALL_PROTOTYPES
        ):
            # convert args #
            self.syscall = sym.split("@", 1)[0]
            self.args = []
            self.updateSyscallArgs()

            # convert numbers #
            args = [
                hex(arg[2]).rstrip("L")
                if isinstance(arg[2], long)
                else str(arg[2])
                for arg in self.args
            ]
            argstr = "(%s)" % ",".join(args)

            # change symbol color #
            symColor = "OKBLUE"
        # build arguments string #
        elif Debugger.envFlags["NOARG"]:
            argstr = ""
        else:
            argstr = "(%s)" % ",".join(
                list(map(lambda x: hex(x).rstrip("L"), args))
            )

        # set task info #
        if self.multi:
            tinfo = "%s(%s) " % (self.comm, self.pid)
            # add thread group info #
            if self.tgid:
                tinfo = "%s(%s)/" % (self.tgcomm, self.tgid) + tinfo
        else:
            tinfo = ""

        # get diff time #
        diffstr = "%3.6f" % self.vdiff

        # build backtrace #
        if SysMgr.funcDepth > 0:
            # add return address to backtrace #
            addBt = [addr] if isRetBp else []

            # get backtrace tree #
            btstr, depth = self.getTraceBtStr(
                diffstr, tinfo, not SysMgr.showAll, addBt=addBt
            )

            indent = "  " * depth
        else:
            btstr = indent = ""

        return tinfo, diffstr, args, argstr, btstr, indent, symColor

    def printBpContext(self, sym, addr, fname, checkArg, origPC):
        # check return type #
        isRetBp = True if sym.endswith(Debugger.RETSTR) else False

        # skip loader functions #
        if (
            Debugger.envFlags["EXCEPTLD"]
            and fname
            and os.path.basename(fname).startswith("ld-")
        ):
            SysMgr.printWarn("skip printing %s in %s" % (sym, fname))
            return isRetBp

        # read args #
        args = self.readArgs()

        # check filter #
        filterRes = True
        filterCmd = self.bpList[addr]["filter"]
        if not self.checkFilterCond(filterCmd, args, sym, fname):
            if not Debugger.envFlags["COMPLETECALL"]:
                return isRetBp
            filterRes = False

        # top mode #
        if self.isRealtime:
            if SysMgr.funcDepth > 0:
                backtrace = self.getBacktrace()
            else:
                backtrace = None

            # handle return filter #
            if isRetBp:
                (
                    etime,
                    elapsed,
                    hasRetFilter,
                    skip,
                    retcmd,
                ) = self.handleRetBpFilter(sym)
                if skip:
                    return isRetBp

                # update elapsed time #
                diff = float(elapsed.lstrip("/"))
                self.updateBpStat(sym, diff)
            else:
                # update interval time #
                self.updateBpStat(sym)

            self.addSample(sym, fname, realtime=True, bt=backtrace)

            # add timeline segment #
            self.addTimelineInt(sym, self.btStr)

            return isRetBp

        # get context data #
        (
            tinfo,
            diffstr,
            args,
            argstr,
            btstr,
            indent,
            symColor,
        ) = self.getBpContext(sym, addr, args, isRetBp)

        # check backtrace filter #
        if self.btFilterList:
            if not UtilMgr.isValidStr(btstr, self.btFilterList):
                return isRetBp

        # print return value #
        jsonData = {}
        elapsed = callString = retstr = ""
        etime = cmds = retcmd = None
        skip = hasRetFilter = False
        convColor = UtilMgr.convColor

        # handle breakpoint for return #
        if isRetBp:
            # get original symbol #
            origSym = sym[: -len(Debugger.RETSTR)]

            try:
                (
                    etime,
                    elapsed,
                    hasRetFilter,
                    skip,
                    retcmd,
                ) = self.handleRetBpFilter(sym)
                if not elapsed:
                    raise Exception()

                # get context string and remove breakpoint for return #
                retstr = self.handleRetBp(sym, fname, addr)

                # add previous symbol info #
                if not skip:
                    # get previous symbol info #
                    prevSymInfo = self.getSymbolInfo(addr)
                    if Debugger.envFlags["NORETBT"]:
                        addStr = ""
                    elif prevSymInfo:
                        try:
                            prevAddr = hex(prevSymInfo[3]).rstrip("L")
                        except SystemExit:
                            sys.exit(0)
                        except:
                            prevAddr = prevSymInfo[3]

                        prevSym = prevSymInfo[0]

                        if Debugger.envFlags["NOFILE"]:
                            prevFname = ""
                        else:
                            prevFname = " [%s]" % prevSymInfo[1]

                        addStr = " -> %s/%s%s" % (prevSym, prevAddr, prevFname)
                    else:
                        addStr = ""

                    # convert elapsed color #
                    origElapsed = elapsed.lstrip("/")
                    if etime > self.retTime:
                        elapsed = convColor(elapsed, "RED")
                    else:
                        elapsed = convColor(elapsed, "CYAN")

                    # build combined output #
                    if (
                        Debugger.envFlags["COMPLETECALL"]
                        and origSym in self.entryContext
                    ):
                        # get entry context #
                        entryData = self.entryContext[origSym].pop()
                        if not self.entryContext[origSym]:
                            self.entryContext.pop(origSym, None)

                        # combine contexts for both entry and exit #
                        if SysMgr.jsonEnable:
                            args = entryData["args"]
                            args = args[1:-1].split(",") if args else []
                            jsonData = {
                                "context": "complete",
                                "time": entryData["time"],
                                "args": args,
                                "file": entryData["file"],
                                "task": tinfo
                                if tinfo
                                else "%s(%s)" % (self.comm, self.pid),
                                "symbol": origSym,
                                "return": retstr.lstrip("="),
                                "elapsed": origElapsed,
                                "caller": addStr.lstrip("-> "),
                            }
                        else:
                            # build file path #
                            if Debugger.envFlags["NOFILE"]:
                                fnameStr = ""
                            else:
                                fnameStr = " [%s]" % entryData["file"]

                            callString = "\n%s %s%s%s%s%s%s%s%s" % (
                                entryData["time"],
                                tinfo,
                                indent,
                                convColor(origSym, symColor),
                                entryData["args"],
                                convColor(fnameStr, "YELLOW"),
                                convColor(retstr, "PINK"),
                                elapsed,
                                addStr,
                            )

                        # add timeline segment #
                        numElapsed = float(origElapsed)
                        self.addTimelineRet(origSym, numElapsed, btstr)

                    # build JSON output #
                    elif SysMgr.jsonEnable:
                        jsonData = {
                            "context": "exit",
                            "time": diffstr,
                            "task": tinfo
                            if tinfo
                            else "%s(%s)" % (self.comm, self.pid),
                            "symbol": sym,
                            "return": retstr.lstrip("="),
                            "elapsed": origElapsed,
                            "caller": addStr.lstrip("-> "),
                        }
                    # build string output #
                    else:
                        callString = "\n%s %s%s%s%s%s%s" % (
                            diffstr,
                            tinfo,
                            indent,
                            sym,
                            convColor(retstr, "PINK"),
                            elapsed,
                            addStr,
                        )
            except SystemExit:
                sys.exit(0)
            except:
                skip = True
                elapsed = ""
                isRetBp = False

            # check command #
            if origSym in self.retCmdList:
                cmds = self.retCmdList[origSym]
        else:
            # save entry context #
            if (
                Debugger.envFlags["COMPLETECALL"]
                and self.bpList[addr]["cmd"]
                and "getret" in self.bpList[addr]["cmd"]
            ):
                self.entryContext.setdefault(sym, [])
                self.entryContext[sym].append(
                    {"time": diffstr, "args": argstr, "file": fname}
                )
            # build JSON output #
            elif SysMgr.jsonEnable:
                args = argstr[1:-1].split(",") if argstr else []
                jsonData = {
                    "context": "entry",
                    "time": diffstr,
                    "task": tinfo
                    if tinfo
                    else "%s(%s)" % (self.comm, self.pid),
                    "symbol": sym,
                    "args": args,
                    "file": fname,
                }
            # build string output #
            else:
                # build file path #
                if Debugger.envFlags["NOFILE"]:
                    fnameStr = ""
                else:
                    fnameStr = " [%s]" % fname

                callString = "\n%s %s%s%s%s%s/%s%s" % (
                    diffstr,
                    tinfo,
                    indent,
                    convColor(sym, symColor),
                    elapsed,
                    argstr,
                    hex(addr).rstrip("L"),
                    convColor(fnameStr, "YELLOW"),
                )

                # add timeline segment #
                self.addTimelineInt(sym, btstr)

        # check filter result #
        if not filterRes:
            return isRetBp

        # print output #
        if not self.showStatus or sym in self.hiddenList:
            pass
        elif jsonData:
            # add backtrace #
            if btstr:
                jsonData["backtrace"] = btstr.lstrip().split("\n")

            # print output #
            SysMgr.printPipe(
                UtilMgr.convDict2Str(jsonData, pretty=self.pretty), flush=True
            )
        elif callString:
            # update interval between calls #
            if Debugger.envFlags["INTERCALL"]:
                self.updateBpStat(sym)
                interdiffStr = " [%.6f]" % self.interDiff
                if self.interDiff > self.retTime:
                    callString += UtilMgr.convColor(interdiffStr, "RED")
                else:
                    callString += UtilMgr.convColor(interdiffStr, "CYAN")
                self.interDiff = 0

            # add backtrace #
            if btstr:
                callString = btstr + callString

            # add entry log to return filter #
            if sym in self.retFilterList:
                self.retFilterList[sym][1] = callString

            # task filter #
            if (
                self.execCmd
                and SysMgr.filterGroup
                and not UtilMgr.isValidStr(self.comm)
            ):
                pass
            # file output #
            elif SysMgr.outPath:
                self.addSample(sym, fname, realtime=True, elapsed=etime)

                # print history #
                self.callPrint.append(callString.rstrip())

                # print to stdout #
                if SysMgr.streamEnable:
                    sys.stdout.write(callString)
            # console output #
            else:
                SysMgr.printPipe(callString, newline=False, flush=True)

        # handle rest jobs #
        if jsonData or callString or Debugger.envFlags["COMPLETECALL"]:
            # handle repeat command #
            if isRetBp and origPC != self.pc:
                self.handleBp(True, checkArg)
                return isRetBp

            # check command #
            if self.bpList[addr]["cmd"]:
                self.bpList[addr]["cmd"] = self.executeCmd(
                    self.bpList[addr]["cmd"], sym=sym, fname=fname, args=args
                )

        # check skip condition #
        if skip:
            return isRetBp

        # execute commands #
        if cmds:
            self.executeCmd(cmds, sym, fname, args)
        if retcmd:
            self.executeCmd([retcmd], sym, fname, args)

        return isRetBp

    def handleBp(self, printStat=False, checkArg=None):
        # read registers for target #
        if not self.updateRegs():
            sys.exit(-1)

        # set rewind address #
        origPC = self.pc
        addr = self.pc - self.prevInstOffset
        origAddr = addr

        # get breakpoint addr #
        if addr not in self.bpList:
            # handle strange instructions such like ARM THUMB #
            if addr + 1 in self.bpList:
                addr += 1
            elif addr - 1 in self.bpList:
                addr -= 1
            elif self.loadSymbols():
                self.updateBpList(verb=False)

            # get symbol Info #
            ret = self.getSymbolInfo(addr)
            if ret:
                sym, fname, offset = ret[:3]
            else:
                SysMgr.printErr(
                    "failed to recognize address %s for %s(%s)"
                    % (hex(addr), self.comm, self.pid)
                )
                sys.exit(-1)

            # check offset #
            if not UtilMgr.isNumber(offset):
                SysMgr.printErr(
                    "failed to recognize address %s in %s for %s(%s)"
                    % (hex(addr), fname, self.comm, self.pid)
                )

                PageAnalyzer.printMemoryArea(
                    self.pid, comm=self.comm, lastLine=True
                )

                sys.exit(-1)
            else:
                offset = long(offset, 16)

            # load original data from storage #
            origWord = self.loadInst(fname, offset)

            # register this lost breakpoint #
            self.injectBp(addr, sym, fname=fname, origWord=origWord)

            # check memory map again #
            if addr not in self.bpList:
                SysMgr.printErr(
                    (
                        "failed to find %s in the breakpoint list "
                        "for %s(%s), update ELF caches in %s"
                    )
                    % (
                        hex(origAddr).rstrip("L"),
                        self.comm,
                        self.pid,
                        SysMgr.cacheDirPath,
                    )
                )

                sys.exit(-1)

        # pick breakpoint info #
        sym = self.bpList[addr]["symbol"]
        fname = self.bpList[addr]["filename"]
        isRetBp = False

        # check watchld to prevent missing new mapping #
        if self.watchLd and not self.libcLoaded:
            self.needMapScan = True

        # update memory map and load new ELF objects #
        if self.needMapScan or (
            not self.libcLoaded
            and (sym.startswith("__libc_") or sym.startswith("malloc"))
        ):
            # load symbols from new objects #
            if self.loadSymbols():
                self.updateBpList(verb=False)

        # check calls for memory map update #
        if sym.startswith("mmap"):
            self.needMapScan = True
        elif sym.startswith("munmap"):
            unmapAddr = self.readArgs()[0]
            self.removeBpFileByAddr(unmapAddr)
            self.needMapScan = True

        # update comm #
        if self.needUpdateComm():
            comm = SysMgr.getComm(self.pid, cache=True, save=True)
            if self.comm != comm:
                self.comm = comm
                Debugger.updateCommFlag(False)

        # check changing-comm calls #
        if sym == "pthread_setname_np":
            Debugger.updateCommFlag()
        elif sym == "prctl":
            param = self.readArgs()[0]
            if (
                param in ConfigMgr.PRCTL_TYPE
                and ConfigMgr.PRCTL_TYPE[param] == "PR_SET_NAME"
            ):
                Debugger.updateCommFlag()

        # set print flag #
        if self.mode != "break":
            printStat = False

        # print context info #
        if (
            printStat
            and not addr in self.exceptBpList
            and not fname in self.exceptBpFileList
            and (not self.targetBpFileList or fname in self.targetBpFileList)
        ):
            isRetBp = self.printBpContext(sym, addr, fname, checkArg, origPC)

        # apply register set to rewind IP #
        if self.pc == origPC:
            self.setPC(addr)
            self.setRegs()

        # save symbol #
        self.prevSym = sym

        # lock between processes #
        if addr in self.bpList:
            nrLock = self.bpList[addr]["number"]
        else:
            nrLock = -1
        self.lock(nrLock)

        # remove breakpoint #
        ret = self.removeBp(addr)
        if not ret:
            self.unlock(nrLock)
            return

        # check reinstall flag #
        reins = ret[2]
        if not reins:
            self.unlock(nrLock)
            return
        # check recursive call #
        elif isRetBp and sym.endswith(Debugger.RETSTR) in self.entryTime:
            self.unlock(nrLock)
            return

        # execute original instructions #
        if self.pc == origPC:
            # continue processing an instruction #
            if self.arch == "arm":
                # no PTRACE_SINGLESTEP on ARM #
                self.cont()
                self.stop()
            else:
                ret = self.ptrace(self.singlestepCmd)
                if ret != 0:
                    SysMgr.printWarn(
                        "failed to singlestep %s(%s) because %s"
                        % (self.comm, self.pid, self.errmsg)
                    )

            # check process #
            ret = self.waitpid()
            self.checkStat(ret, reason="of injection failure")

        # setup this breakpoint again #
        self.injectBp(addr, sym, fname=fname, reins=reins)

        # unlock between processes #
        self.unlock(nrLock)

        # handle pycall #
        if (
            self.mode == "pybreak"
            and sym.rstrip(Debugger.RETSTR) == SysMgr.pyCallFunc
        ):
            self.handlePyTrap(sym, fname, addr)

    def handleTrapEvent(self, stat):
        # check thread status #
        if self.status == "wait":
            return

        # check thread sequence #
        printEnable = not (
            Debugger.targetNums and not self.myNum in Debugger.targetNums
        )
        if not printEnable and not self.isBreakMode:
            return

        previous = self.status
        self.status = self.mode

        # usercall #
        if self.mode in ("sample", "inst"):
            self.handleUsercall()
        # breakcall #
        elif self.isBreakMode:
            printEnable = printEnable and SysMgr.printEnable

            # block signal #
            SysMgr.blockSignal(act="block")

            while 1:
                try:
                    self.handleBp(printStat=printEnable)
                    break
                except SystemExit:
                    sys.exit(0)
                except Exception as ex:
                    SysMgr.printWarn(
                        "failed to handle breakpoint for %s(%s)"
                        % (self.comm, self.pid),
                        True,
                        reason=True,
                    )
                    if type(ex).__name__ == "IOError":
                        sys.exit(-1)

            # continue target #
            if self.cont(check=True) < 0:
                sys.exit(-1)

            # unblock signal #
            SysMgr.blockSignal(act="unblock")
        # pycall #
        elif self.mode == "pycall":
            self.handlePycall()

        self.status = previous

    def handleExit(self):
        # update status #
        self.traceStatus = False

        # read return code #
        ret = self.getEventMsg()

        # get task info #
        tinfo = "%s(%s)" % (self.comm, self.pid)

        # get diff time #
        self.vdiff = self.current - self.dstart
        diffstr = "%3.6f" % self.vdiff

        try:
            retstr = "(%s)" % ConfigMgr.SIG_LIST[ret]
        except SystemExit:
            sys.exit(0)
        except:
            retstr = ""

        exitStr = UtilMgr.convColor(
            "+++ exited %s with %s%s +++" % (tinfo, ret, retstr), "RED"
        )

        exitStr = "\n%s %s" % (diffstr, exitStr)

        if SysMgr.outPath:
            # print history #
            if SysMgr.showAll:
                self.callPrint.append(exitStr.rstrip())

            # print to stdout #
            if SysMgr.streamEnable:
                sys.stdout.write("%s\n" % exitStr)
        else:
            SysMgr.printPipe(exitStr, flush=True)

    def handleSignal(self, sig, warn=False, taskinfo=False):
        if not SysMgr.printEnable:
            return

        # get mode #
        isSigMode = self.mode == "signal"

        # check signal filter #
        if isSigMode and SysMgr.customCmd:
            sigName = ConfigMgr.SIG_LIST[sig]
            sigList = list(map(str.upper, SysMgr.customCmd))

            # check exception condition #
            for item in sigList:
                if item.startswith("^"):
                    if sigName == item.lstrip("^"):
                        return
                    sigList.remove(item)

            # check target condition #
            if sigList and not sigName in sigList:
                return

        # get task info #
        if taskinfo or self.multi:
            tinfo = "%s(%s) " % (self.comm, self.pid)
        else:
            tinfo = ""

        # get signal name #
        try:
            name = ConfigMgr.SIG_LIST[sig]
            signame = UtilMgr.convColor(name, "GREEN", 7, "left")
        except SystemExit:
            sys.exit(0)
        except:
            name = sig
            signame = "UNKNOWN(%s)" % sig

        # get diff time #
        diff = self.vdiff
        if diff > 0:
            callString = "%3.6f %s[%s]" % (
                diff,
                tinfo,
                "{0:<7}".format(signame),
            )
        else:
            callString = "%s[%s]" % (tinfo, "{0:<7}".format(signame))

        # get signal info #
        isAlive = True
        callItems = []
        ret = self.getSigInfo()
        if ret == 0:
            # save signal #
            self.lastRxSig = sig

            if self.sigObj.si_errno > 0:
                errinfo = "errno=%s, " % UtilMgr.convColor(
                    self.sigObj.si_errno, "RED"
                )
                callItems.append(errinfo)

            # code #
            try:
                code = self.sigObj.si_code
                if self.sigObj.si_code in ConfigMgr.SI_CODE:
                    code = ConfigMgr.SI_CODE[code]
                elif name == "SIGCHLD":
                    code = ConfigMgr.SIGCHLD_CODE[code]
                elif name == "SIGTRAP":
                    code = ConfigMgr.SIGTRAP_CODE[code]
                elif name == "SIGSEGV":
                    code = ConfigMgr.SIGSEGV_CODE[code]
                elif name == "SIGILL":
                    code = ConfigMgr.SIGILL_CODE[code]
                elif name == "SIGFPE":
                    code = ConfigMgr.SIGFPE_CODE[code]
                else:
                    raise Exception("no signal code")
                callItems.append("code=%s" % code)
            except SystemExit:
                sys.exit(0)
            except:
                pass

            if self.sigObj._sifields:
                fields = self.sigObj._sifields._sigchld

                # pid #
                try:
                    pid = fields.pid
                    pidstr = str(pid)
                    if pid:
                        comm = SysMgr.getComm(pid, cache=True, save=True)
                        if comm:
                            pidstr = "%s(%s)" % (comm, pid)
                    callItems.append("pid=%s|addr=%s" % (pidstr, hex(pid)))
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

                # uid #
                try:
                    uid = str(fields.uid)
                    if SysMgr.sysInstance:
                        userData = SysMgr.sysInstance.userData
                        if uid in userData:
                            uid = "%s(%s)" % (userData[uid]["name"], uid)
                    callItems.append("uid=%s" % uid)
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

                # status #
                try:
                    if code == "CLD_EXITED":
                        status = fields.status
                    else:
                        status = ConfigMgr.SIG_LIST[fields.status]
                except:
                    status = fields.status
                callItems.append("status=%s" % status)

                # time #
                if fields.utime:
                    callItems.append("utime=%s" % fields.utime)
                if fields.stime:
                    callItems.append("stime=%s" % fields.stime)

            if callItems:
                callString += " {%s}" % ", ".join(callItems)
        else:
            # check delivered signal #
            if sig == self.lastRxSig:
                return

            # check alive #
            isAlive = self.isAlive()

        # check alive status #
        if not isAlive and not isSigMode:
            sys.exit(-1)

        # print context #
        if isSigMode and SysMgr.outPath:
            self.callList.append([name, self.current, str(sig)])
            if SysMgr.streamEnable:
                print(callString)
        else:
            if warn:
                SysMgr.printWarn(callString)
            else:
                SysMgr.printPipe(callString)

        # add timeline segment #
        self.addTimelineInt(name, callString)

        # print backtrace #
        if not self.isRealtime and SysMgr.funcDepth > 0:
            # read registers for target #
            if not self.updateRegs():
                sys.exit(-1)

            self.printContext(regs=False, sig=False)

        # handle watch command #
        if "WATCHLOG" in SysMgr.environList:
            # check exit condition #
            SysMgr.checkLogCond(
                name,
                SysMgr.environList["WATCHLOG"][0].split("+"),
                SysMgr.environList.get("REFILTER"),
            )

        # check alive status #
        if not isAlive:
            sys.exit(-1)

    def initPyEnv(self):
        if self.pyAddr:
            return True

        # check python version for target #
        if not self.checkPyVer():
            if SysMgr.forceEnable:
                return False
            else:
                sys.exit(-1)

        # set target info #
        procInfo = "%s(%s)" % (self.comm, self.pid)

        # get memory map by binary type #
        pyPath = FileAnalyzer.getMapFilePath(self.pid, "libpython")
        if not pyPath:
            pyPath = FileAnalyzer.getMapFilePath(self.pid, "python")
            if not pyPath:
                SysMgr.printErr(
                    "failed to find python binary for %s" % procInfo
                )
                sys.exit(-1)

        # remove anonymous symbol #
        Debugger.envFlags["ONLYSYM"] = True

        # check native function for python call #
        addr = self.getAddrBySymbol(SysMgr.pyCallFunc)
        if not addr:
            return False

        # get symbol for interpretr #
        pySym = ["_PyThreadState_Current", "_PyRuntime"]
        symbolInfo = SysMgr.getProcAddrBySymbol(
            self.pid, pySym, fileFilter=[pyPath]
        )
        if not symbolInfo:
            SysMgr.printErr(
                "failed to find '%s' symbol for %s" % (pySym, procInfo)
            )
            sys.exit(-1)
        elif len(symbolInfo) > 1:
            SysMgr.printErr(
                "found multiple symbols [ %s ] for %s"
                % (", ".join(list(symbolInfo)), procInfo)
            )
            sys.exit(-1)

        # set pthread ID for target task #
        threadList = SysMgr.getThreadList(self.pid)
        if len(threadList) == 1:
            self.pthreadid = -1
        else:
            self.pthreadid = self.remoteUsercall("pthread_self")

        # get address for interpretr #
        pySymbol = list(symbolInfo.values())[0]
        self.pyAddr = long(pySymbol[2], 16)

        # version >= 3.7 #
        if pySymbol[3] == "_PyRuntime":
            # get offset for PyinterpretrState head #
            if ConfigMgr.wordSize == 4:
                SysMgr.printErr(
                    "not supported yet for PyThreadState on 32-bit"
                )
                sys.exit(-1)
            else:
                self.pyAddr += 32

        return True

    def checkPyVer(self):
        # check exe #
        if not hasattr(self, "exe"):
            self.exe = SysMgr.getExeName(self.pid)

        # compare python binary #
        myExe = SysMgr.getExeName(SysMgr.pid)
        if myExe != self.exe:
            SysMgr.printErr(
                (
                    "different python executable '%s' for %s(%s) "
                    "and '%s' for %s(%s)"
                )
                % (
                    myExe,
                    SysMgr.comm,
                    SysMgr.pid,
                    self.exe,
                    self.comm,
                    self.pid,
                )
            )
            return False
        else:
            return True

    def readPyState32(self, addr):
        SysMgr.printErr("not implemented yet")
        sys.exit(-1)

    def readPyState64(self, addr):
        frameList = {}

        while 1:
            if sys.version_info >= (3, 11):
                # read gilstate.tstate_current from pyruntimestate #
                tstate_current = self.readMem(addr + 16)
                tstate_current = struct.unpack("Q", tstate_current)[0]

                PyThreadState = self.readMem(tstate_current, 200)
                if not PyThreadState:
                    break

                (
                    prevp,
                    nextp,
                    interp,
                    _initialized,
                    _static,
                    recursion_remaining,
                    recursion_limit,
                    recursion_headroom,
                    tracing,
                    tracing_what,
                    cframe,
                    c_profilefunc,
                    c_tracefunc,
                    c_profileobj,
                    c_traceobj,
                    curexc_type,
                    curexc_value,
                    curexc_traceback,
                    exc_info,
                    dictp,
                    gilstate_counter,
                    async_exc,
                    thread_id,
                    native_thread_id,
                    trash_delete_nesting,
                    trash_delete_later,
                    on_delete,
                    on_delete_data,
                ) = struct.unpack(
                    "QQQiiiiiiiQQQQQQQQQQiQLLiQQQ", PyThreadState
                )

                # get current_frame address from PyCframe #
                framep = self.readMem(cframe + 8, 8)
                framep = struct.unpack("Q", framep)[0]

            elif sys.version_info >= (3, 7):
                tstate_head = self.readMem(addr + 8)

                PyThreadState = struct.unpack("Q", tstate_head)[0]
                if PyThreadState == 0:
                    addr += 8
                    continue

                PyThreadState = self.readMem(PyThreadState, 216)
                if not PyThreadState:
                    break

                (
                    prevp,
                    nextp,
                    interp,
                    framep,
                    recursion_depth,
                    overflowed,
                    recursion_critical,
                    stackcheck_counter,
                    tracing,
                    use_tracing,
                    c_profilefunc,
                    c_tracefunc,
                    c_profileobj,
                    c_traceobj,
                    curexc_type,
                    curexc_value,
                    curexc_traceback,
                    exc_type,
                    exc_value,
                    exc_traceback,
                    previous_item,
                    exec_info,
                    dictp,
                    gilstate_counter,
                    async_exc,
                    thread_id,
                    trash_delete_nesting,
                    trash_delete_later,
                    on_delete,
                    on_delete_data,
                ) = struct.unpack(
                    "QQQQibbiiiQQQQQQQQQQQQQiQLiQQQ", PyThreadState
                )

            elif sys.version_info >= (3, 0):
                PyThreadState = self.readMem(addr, 192)
                if not PyThreadState:
                    break

                (
                    prevp,
                    nextp,
                    interp,
                    framep,
                    recursion_depth,
                    overflowed,
                    recursion_critical,
                    tracing,
                    use_tracing,
                    c_profilefunc,
                    c_tracefunc,
                    c_profileobj,
                    c_traceobj,
                    curexc_type,
                    curexc_value,
                    curexc_traceback,
                    exc_type,
                    exc_value,
                    exc_traceback,
                    dictp,
                    gilstate_counter,
                    async_exc,
                    thread_id,
                    trash_delete_nesting,
                    trash_delete_later,
                    on_delete,
                    on_delete_data,
                ) = struct.unpack("QQQQibbiiQQQQQQQQQQQiQliQQQ", PyThreadState)

            else:
                PyThreadState = self.readMem(addr, 168)
                if not PyThreadState:
                    break

                (
                    nextp,
                    interp,
                    framep,
                    recursion_depth,
                    tracing,
                    use_tracing,
                    c_profilefunc,
                    c_tracefunc,
                    c_profileobj,
                    c_traceobj,
                    curexc_type,
                    curexc_value,
                    curexc_traceback,
                    exc_type,
                    exc_value,
                    exc_traceback,
                    dictp,
                    tick_counter,
                    gilstate_counter,
                    async_exc,
                    thread_id,
                    trash_delete_nesting,
                    trash_delete_later,
                ) = struct.unpack("QQQiiiQQQQQQQQQQQiiQliQ", PyThreadState)

            # add frame address #
            frameList[thread_id] = framep

            if nextp == 0:
                break
            else:
                addr = nextp

        return frameList

    def readPyFrame32(self, addr):
        SysMgr.printErr("not implemented yet")
        sys.exit(-1)

    def readPyFrame64(self, addr):
        # read PyFrameObject #
        if sys.version_info >= (3, 11):
            """
            PyFrameObject = self.readMem(addr, 56)
            (
                ob_refcnt,
                ob_type,
                f_back,
                f_frame,
                f_trace,
                f_lineno,
                f_trace_lines,
                f_trace_opcodes,
                f_fast_as_locals,
                f_frame_data,
            ) = struct.unpack("IQQQQicccQ", PyFrameObject)
            """

            PyInterpreterFrameObject = self.readMem(addr, 80)
            (
                f_func,
                f_globals,
                f_builtins,
                f_locals,
                f_code,
                frame_obj,
                previous,
                prev_instr,
                stacktop,
                is_entry,
                owner,
                localsplus,
            ) = struct.unpack("QQQQQQQQibcQ", PyInterpreterFrameObject)

            f_back = previous

        elif sys.version_info >= (3, 10):
            PyFrameObject = self.readMem(addr, 104)
            (
                ob_refcnt,
                ob_type,
                ob_size,
                f_back,
                f_code,
                f_builtins,
                f_globals,
                f_locals,
                f_valuestack,
                f_trace,
                f_stackdepth,
                f_trace_lines,
                f_trace_opcodes,
                f_gen,
                f_lasti,
                f_lineno,
            ) = struct.unpack("IQQQQQQQQQiccQii", PyFrameObject)

        elif sys.version_info >= (3, 7):
            PyFrameObject = self.readMem(addr, 112)
            (
                ob_refcnt,
                ob_type,
                ob_size,
                f_back,
                f_code,
                f_builtins,
                f_globals,
                f_locals,
                f_valuestack,
                f_stacktop,
                f_trace,
                f_trace_lines,
                f_trace_opcodes,
                f_gen,
                f_lasti,
                f_lineno,
            ) = struct.unpack("IQQQQQQQQQQbbQii", PyFrameObject)

        else:
            PyFrameObject = self.readMem(addr, 128)
            (
                ob_refcnt,
                ob_type,
                ob_size,
                f_back,
                f_code,
                f_builtins,
                f_globals,
                f_locals,
                f_valuestack,
                f_stacktop,
                f_trace,
                f_exc_type,
                f_exc_value,
                f_exc_traceback,
                f_tstate,
                f_lasti,
                f_lineno,
            ) = struct.unpack("IQQQQQQQQQQQQQQii", PyFrameObject)

        # read PyCodeObject #
        if sys.version_info >= (3, 11, 0):
            PyCodeObject = self.readMem(f_code, 184)
            (
                ob_refcnt,
                ob_type,
                ob_size,
                co_consts,
                co_names,
                co_exceptiontable,
                co_flags,
                co_warmup,
                _co_linearray_entry_size,
                co_argcount,
                co_posonlyargcount,
                co_kwonlyargcount,
                co_stacksize,
                co_firstlineno,
                co_nlocalsplus,
                co_nlocals,
                co_nplaincellvars,
                co_ncellvars,
                co_nfreevars,
                co_localsplusnames,
                co_localspluskinds,
                co_filename,
                co_name,
                co_qualname,
                co_linetable,
                co_weakreflist,
                _co_code,
                _co_linearray,
                _co_firsttraceable,
                co_extra,
            ) = struct.unpack("IQQQQQIHHIIIIIIIIIIQQQQQQQQQIQ", PyCodeObject)

            # TODO: Add offset for lasti in _PyInterpreterFrame_GetLine() #
            f_lineno = co_firstlineno

        elif sys.version_info >= (3, 8, 0):
            PyCodeObject = self.readMem(f_code, 144)
            (
                ob_refcnt,
                ob_type,
                co_argcount,
                co_posonlyargcount,
                co_kwonlyargcount,
                co_nlocals,
                co_stacksize,
                co_flags,
                co_firstlineno,
                co_code,
                co_consts,
                co_names,
                co_varnames,
                co_freevars,
                co_cellvars,
                co_cell2args,
                co_filename,
                co_name,
                co_lnotab,
                co_zomebiframe,
                co_wearreflist,
            ) = struct.unpack("IQIIIIIIIQQQQQQQQQQQQ", PyCodeObject)

            if sys.version_info >= (3, 10, 0):
                f_lineno = co_firstlineno

        elif sys.version_info >= (3, 0):
            PyCodeObject = self.readMem(f_code, 136)
            (
                ob_refcnt,
                ob_type,
                co_argcount,
                co_kwonlyargcount,
                co_nlocals,
                co_stacksize,
                co_flags,
                co_firstlineno,
                co_code,
                co_consts,
                co_names,
                co_varnames,
                co_freevars,
                co_cellvars,
                co_cell2args,
                co_filename,
                co_name,
                co_lnotab,
                co_zomebiframe,
                co_wearreflist,
            ) = struct.unpack("IQIIIIIIQQQQQQQQQQQQ", PyCodeObject)

        else:
            PyCodeObject = self.readMem(f_code, 128)
            (
                ob_refcnt,
                ob_type,
                co_argcount,
                co_nlocals,
                co_stacksize,
                co_flags,
                co_code,
                co_consts,
                co_names,
                co_varnames,
                co_freevars,
                co_cellvars,
                co_filename,
                co_name,
                co_firstlineno,
                co_lnotab,
                co_zomebiframe,
                co_wearreflist,
            ) = struct.unpack("IQIIIIQQQQQQQQIQQQ", PyCodeObject)

        return f_back, f_lineno, f_code, co_name, co_filename

    def readPyStr32(self, addr):
        SysMgr.printErr("not implemented yet")
        sys.exit(-1)

    def readPyStr64(self, addr):
        PyStringObject = self.readMem(addr, 24)

        ob_refcnt, ob_type, ob_size = struct.unpack("IQQ", PyStringObject)

        if sys.version_info >= (3, 0):
            return self.readMem(addr + 48, ob_size).decode()
        else:
            return self.readMem(addr + 36, ob_size)

    def readPyStack(self, framep, depth):
        bt = []
        lastAddr = None
        lastName = None
        lastFile = None

        while 1:
            # read PyFrameObject #
            (
                f_back,
                f_lineno,
                f_code,
                co_name,
                co_filename,
            ) = self.readPyFrame(framep)

            # read context #
            if f_code in self.pyFrameCache:
                name, filename = self.pyFrameCache[f_code]
            else:
                # read name #
                name = self.readPyStr(co_name)

                # read filename #
                filename = self.readPyStr(co_filename)

            # cache frame #
            self.pyFrameCache[f_code] = [name, filename]

            # handle call info #
            if not lastName:
                lastAddr = f_lineno
                lastName = name
                lastFile = filename
            else:
                bt.append([f_lineno, name, filename])

            # check last frame #
            if f_back == 0 or depth == 0 or depth <= len(bt):
                break

            framep = f_back

        return bt, lastAddr, lastName, lastFile

    def handlePyTrap(self, sym, fname, addr):
        """
        PyObject* _Py_HOT_FUNCTION _PyEval_EvalFrameDefault(
            PyThreadState *tstate, _PyInterpreterFrame *frame, int throwflag)

        PyObject* _Py_HOT_FUNCTION_PyEval_EvalFrameDefault(
            PyThreadState *tstate, PyFrameObject *f, int throwflag)

        PyObject* PyEval_EvalFrame(PyFrameObject *f)
        """

        # set context flag #
        if Debugger.pyElapsed >= 0 and sym.endswith(Debugger.RETSTR):
            isRet = True
        else:
            isRet = False

        # return context #
        if isRet:
            self.handleRetBp(sym, fname, addr)
            if not self.prevPySym:
                return

            sym = sym.rstrip(Debugger.RETSTR)
            if not sym in self.entryTime:
                return
        # entry context #
        else:
            # get pointer to PyFrameObject #
            framep = self.readArgs()[1 if sys.version_info >= (3, 10) else 0]

            # read frames #
            try:
                bt, line, call, fname = self.readPyStack(
                    framep, SysMgr.funcDepth
                )

                # save context #
                self.prevPySym = call
                self.prevPyFile = fname
                self.prevPyLine = line
                self.prevPyBt = bt
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr(
                    "failed to get python stack for %s(%s)"
                    % (self.comm, self.pid),
                    True,
                )
                return

            # check filter #
            if SysMgr.pyFuncFilter and not UtilMgr.isValidStr(
                call, SysMgr.pyFuncFilter
            ):
                return

            # set the breakpoint for return #
            if Debugger.pyElapsed >= 0:
                self.setRetBp(sym, fname, ["getret"])

        # trace mode #
        if self.multi:
            tinfo = "%s(%s) " % (self.comm, self.pid)
        else:
            tinfo = ""

        # get diff time #
        diffstr = "%3.6f" % self.vdiff

        convColor = UtilMgr.convColor
        jsonData = {}
        callString = ""

        # build backtrace #
        if isRet:
            btstr = ""
            entry = self.entryTime[sym].pop()
            etime = self.vdiff - entry
            elapsed = "/%.6f" % etime
            if etime >= Debugger.pyElapsed:
                elapsed = convColor(elapsed, "CYAN")

            if self.prevPySym in self.prevPyIndent:
                indent = self.prevPyIndent[self.prevPySym].pop()
                if not self.prevPyIndent[self.prevPySym]:
                    self.prevPyIndent.pop(self.prevPySym, None)
            else:
                indent = ""

            # remove entry timestamp from list #
            if not self.entryTime[sym]:
                self.entryTime.pop(sym, None)

            sym += Debugger.RETSTR

            # build JSON output #
            if SysMgr.jsonEnable:
                jsonData = {
                    "time": diffstr,
                    "task": tinfo
                    if tinfo
                    else "%s(%s)" % (self.comm, self.pid),
                    "symbol": self.prevPySym,
                    "elapsed": elapsed.lstrip("/"),
                    "file": self.prevPyFile,
                    "line": self.prevPyLine,
                }
            # build string output #
            else:
                callString = "\n%s %s%s%s%s [%s:%s]" % (
                    diffstr,
                    tinfo,
                    indent,
                    self.prevPySym,
                    elapsed,
                    self.prevPyFile,
                    self.prevPyLine,
                )

            # add timeline segment #
            if (
                Debugger.envFlags["TIMELINE"]
                and not Debugger.envFlags["INTERCALL"]
            ):
                pySym = "%s[%s]" % (self.prevPySym, self.prevPyFile)
                pyBtStr = self.getBtStr(self.prevPyBt)
                self.addTimelineRet(pySym, etime, pyBtStr)

            # initialize previous symbol #
            call = self.prevPySym
            self.prevPySym = None
        else:
            if bt and SysMgr.funcDepth > 0:
                if SysMgr.showAll:
                    cont = False
                else:
                    cont = True

                # get backtrace tree #
                btstr, depth = self.getTraceBtStr(
                    diffstr, tinfo, cont, backtrace=bt, python=True
                )

                indent = "  " * depth

                # save indent #
                self.prevPyIndent.setdefault(call, [])
                self.prevPyIndent[call].append(indent)
            else:
                btstr = indent = ""

            elapsed = ""

            if SysMgr.jsonEnable:
                jsonData = {
                    "time": diffstr,
                    "task": tinfo
                    if tinfo
                    else "%s(%s)" % (self.comm, self.pid),
                    "symbol": call,
                    "file": fname,
                    "line": line,
                }
            else:
                # build current symbol string #
                callString = "\n%s %s%s%s%s [%s:%s]" % (
                    diffstr,
                    tinfo,
                    indent,
                    convColor(call, "GREEN"),
                    elapsed,
                    fname,
                    line,
                )

            # add timeline item #
            self.addTimelineInt(call, btstr)

        # add backtrace #
        if btstr:
            if jsonData:
                jsonData["backtrace"] = btstr.lstrip().split("\n")
            else:
                callString = btstr + callString

        # JSON output #
        if jsonData:
            SysMgr.printPipe(
                UtilMgr.convDict2Str(jsonData, pretty=self.pretty), flush=True
            )
        # file output #
        elif SysMgr.outPath:
            self.addSample(call, fname, realtime=True, elapsed=None)

            # print history #
            self.callPrint.append(callString.rstrip())

            # print to stdout #
            if SysMgr.streamEnable:
                sys.stdout.write(callString)
        # console output #
        else:
            SysMgr.printPipe(callString, newline=False, flush=True)

    def handlePycall(self, retbt=False, cur=False):
        # check init status #
        if not self.pyAddr and not self.initPyEnv():
            return

        # read native call stack for version >= 3.7 #
        nativeStack = None
        if sys.version_info >= (3, 7) or Debugger.envFlags["FIXPYSTATE"]:
            self.updateRegs()
            curSym = self.getSymbolInfo(self.pc)[0]
            if not curSym.startswith("_Py") and not curSym.startswith("Py"):
                nativeStack = self.getBacktrace(
                    SysMgr.funcDepth, cur=True, native=True
                )

        # change offset for PyThreadState #
        if self.pyThreadStateP:
            pyThreadStateP = self.pyThreadStateP
        else:
            if sys.version_info >= (3, 11):
                addr = self.pyAddr + 8
            else:
                addr = self.pyAddr

            # read address for PyThreadState #
            pyThreadStateP = self.readMem(addr)
            if not pyThreadStateP:
                return
            pyThreadStateP = struct.unpack("Q", pyThreadStateP)[0]

            # save and reuse pythreadstate #
            if Debugger.envFlags["FIXPYSTATE"]:
                self.pyThreadStateP = pyThreadStateP

        # read native call stack for version < 3.7 #
        if not pyThreadStateP and sys.version_info < (3, 7):
            self.handleUsercall()
            return

        # read PyThreadState #
        frameList = self.readPyState(pyThreadStateP)

        # TODO: get GIL usage by comparing thread_id with pthread_self() #
        # nrThread = len(frameList)

        # get top-level frame for target task #
        if self.pthreadid == -1:
            framep = list(frameList.values())[0]
        elif not self.pthreadid in frameList:
            self.handleUsercall()
            return
        else:
            framep = frameList[self.pthreadid]

        # check frame pointer #
        if not framep:
            return

        # read frames #
        bt, lastAddr, lastName, lastFile = self.readPyStack(
            framep, SysMgr.funcDepth
        )

        # add last frame to backtrace #
        if cur:
            bt.insert(0, [lastAddr, lastName, lastFile])

        # check filter #
        if SysMgr.pyFuncFilter and not UtilMgr.isValidStr(
            lastName, SysMgr.pyFuncFilter
        ):
            return

        # add last python call info #
        if nativeStack:
            bt.insert(0, [lastAddr, lastName, lastFile])

            # merge only native stack except for interpretr and python stack #
            if not Debugger.envFlags["INCNATIVE"]:
                lastIdx = 0
                for idx in xrange(len(nativeStack)):
                    lastIdx = idx
                    if nativeStack[idx][1] == SysMgr.pyCallFunc:
                        break
                nativeStack = nativeStack[:lastIdx]
                newStack = []
                for item in nativeStack:
                    sym = item[1]
                    if sym.startswith("_Py") or sym.startswith("Py"):
                        continue
                    newStack.append(item)
                nativeStack = newStack + bt
            # merge all native stack and python stack #
            else:
                for idx in xrange(len(nativeStack)):
                    if not bt:
                        break
                    if nativeStack[idx][1] == SysMgr.pyCallFunc:
                        nativeStack[idx] = bt.pop(0)

            # pick last call info #
            bt = nativeStack
            lastAddr, lastName, lastFile = bt.pop(0)

        # add a call stack sample #
        if retbt:
            return bt

        # add timeline segment #
        if Debugger.envFlags["TIMELINE"] and Debugger.envFlags["INTERCALL"]:
            pyBtStr = self.getBtStr(bt)
            self.addTimelineInt(lastName, pyBtStr)

        self.addSample(lastName, lastFile, realtime=True, bt=bt)

    def handleUsercall(self, update=True):
        # read registers for target #
        if update and not self.updateRegs():
            if not self.isAlive():
                sys.exit(-1)
            else:
                return

        # check previous function boundary #
        if not self.prevCallInfo or Debugger.envFlags["NOSAMPLECACHE"]:
            pass
        elif self.isRealtime:
            if self.pc == self.prevCallInfo[5]:
                self.addSample(
                    self.prevCallInfo[0],
                    self.prevCallInfo[1],
                    realtime=True,
                    bt=self.prevCallInfo[4],
                )
                return
        elif self.prevCallInfo[2] <= self.pc <= self.prevCallInfo[3]:
            if SysMgr.outPath:
                self.addSample(self.prevCallInfo[0], self.prevCallInfo[1])
            return

        # get symbol info #
        ret = self.getSymbolInfo(self.pc)
        if isinstance(ret, list):
            sym, fname, offset, fstart, fend, size = ret
        else:
            sym = ret
            fname = offset = "??"

        # get backtrace #
        if self.isRealtime and SysMgr.funcDepth > 0:
            backtrace = self.getBacktrace(SysMgr.funcDepth)
        else:
            backtrace = None

        # print unknown call address #
        if fname == "??":
            sym = hex(self.pc).rstrip("L")
            self.addSample(sym, fname, realtime=self.isRealtime, bt=backtrace)
            return

        # check contiguous unknown symbol #
        if sym == "??" and self.prevCallInfo:
            if self.prevCallInfo[0] == sym:
                return
            elif self.prevCallInfo[0].startswith("mmap"):
                # enable memory update flag #
                self.needMapScan = True

        # convert args #
        if Debugger.envFlags["PRINTARG"]:
            args = []
            self.getRetAddr(self.pc, args, onlyArg=True)
            if args:
                args = args[0]
            sym += "(%s)" % ", ".join(
                ["%s=%s" % (arg[2], arg[4]) for arg in args]
            )

        # update callstack #
        if self.isRealtime:
            direction = ""
            symstr = sym

            # save current call info as previous call #
            self.prevCallInfo = [sym, fname, 0, 0, backtrace, self.pc]

            self.addSample(sym, fname, realtime=True, bt=backtrace)
        else:
            # get filter addr #
            try:
                # symbol range #
                if sym != "??":
                    vstart, vend = self.getRangeBySym(sym, fname)
                # anon range #
                else:
                    vstart, vend = self.getAnonRangeByOffset(offset, fname)
            except SystemExit:
                sys.exit(0)
            except:
                vstart = vend = 0

            # save current call info as previous call #
            self.prevCallInfo = [sym, fname, vstart, vend, backtrace, self.pc]

            self.updateStack(sym)

            # check call relationship #
            if not self.sp or not self.prevSp:
                direction = "(??)"
                self.addCall(sym)
            elif sym.startswith(".plt"):
                direction = "(--)"
                self.addCall(sym)
            elif self.sp > self.prevSp:
                direction = "(<-)"
            else:
                direction = "(->)"
                self.addCall(sym)

            symstr = (" " * 4 * len(self.callstack)) + sym

            # get time diff #
            diff = self.vdiff

            # build call string #
            callString = "%3.6f %s %s [%s + %s] [%s]" % (
                diff,
                symstr,
                direction,
                fname,
                offset,
                hex(self.sp).rstrip("L"),
            )

            # backup callString #
            self.prevCallString = callString

            # file output #
            if SysMgr.outPath:
                self.addSample(sym, fname)

                # print history #
                self.callPrint.append(callString)
            # console output #
            else:
                SysMgr.printPipe(
                    "\n%s" % callString, newline=False, flush=True
                )

        # backup register #
        self.prevSp = self.sp

        # check symbol #
        if SysMgr.customCmd:
            onlySym = sym.split("@")[0]
            self.checkSymbol(onlySym)

    def updateSyscallArgs(self, ref=True, retval=None):
        proto = ConfigMgr.SYSCALL_PROTOTYPES

        # get argument values from register #
        regstr = self.readArgs(syscall=True)

        # check prototype #
        if self.syscall in proto:
            # get data types #
            self.rettype, formats = proto[self.syscall]

            # get values #
            self.values = [value for value, format in zip(regstr, formats)]

        else:
            SysMgr.printWarn("failed to get prototype for %s" % self.syscall)

            self.values = list(self.readArgs())
            formats = [("unsigned long", "value")] * len(self.values)

        argset = {}
        seq = 0
        for value, aformat in zip(regstr, formats):
            # get type and name for an argument #
            argtype, argname = aformat
            argset[argname] = value

            # convert argument value #
            try:
                value = self.convSyscallParam(
                    argtype,
                    argname,
                    value,
                    seq,
                    ref,
                    argset,
                    SysMgr.showAll,
                    retval,
                )
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printWarn(
                    "failed to convert %s's %s" % (self.syscall, argname),
                    reason=True,
                )

            if value is not None:
                self.addArg(argtype, argname, value)

            seq += 1

    def convSyscallArgs(self, retval=None):
        args = []

        if self.isRealtime and not SysMgr.showAll:
            ref = False
        else:
            ref = True

        # converting arguments #
        self.updateSyscallArgs(ref, retval=retval)

        # pick values from argument list #
        for idx, arg in enumerate(self.args):
            if arg[0].endswith("*"):
                # convert pointer to values #
                if UtilMgr.isString(arg[2]):
                    text = UtilMgr.decodeArg(arg[2])

                    # check output length #
                    if (
                        not (SysMgr.outPath or SysMgr.showAll)
                        and len(text) > self.pbufsize
                    ):
                        text = r'"%s..."' % text[: self.pbufsize]
                    else:
                        text = r'"%s"' % text.rstrip()
                else:
                    text = arg[2]
            elif arg[0].endswith("int") or arg[0].endswith("long"):
                try:
                    if arg[2].isdigit():
                        text = long(arg[2])
                    else:
                        text = arg[2]
                except SystemExit:
                    sys.exit(0)
                except:
                    text = arg[2]
            else:
                text = arg[2]

            # append an arg to list #
            args.append(text)

        return args

    def isDeferCall(self, name):
        return name in ConfigMgr.SYSCALL_DEFFERABLE

    def isDeferRetCall(self, name):
        return name in ConfigMgr.SYSCALL_DEFFERABLE_RET

    def addTimelineRet(self, sym, diff, bts, gid=-1, sid=-1):
        if not Debugger.envFlags["TIMELINE"] or Debugger.envFlags["INTERCALL"]:
            return

        # get symbol id #
        if sid == -1:
            if not sym in self.timelineIdx:
                self.timelineIdx[sym] = str(len(self.timelineIdx))
            symidx = self.timelineIdx[sym]
        else:
            symidx = sid

        # get group id #
        if gid == -1:
            grpidx = symidx
        else:
            grpidx = gid

        # convert time unit to us #
        startTime = self.vdiff - diff
        endTime = self.vdiff
        endTime *= 1000000
        startTime *= 1000000

        # add timeline data #
        self.timelineData["segments"].append(
            {
                "group": grpidx,
                "text": sym,
                "id": symidx,
                "state": "S",
                "time_start": startTime,
                "time_end": endTime,
                "info": bts.strip() if bts else "",
            }
        )

    def addTimelineInt(self, sym, bts, gid=-1, sid=-1):
        # add timeline segment #
        if (
            not Debugger.envFlags["TIMELINE"]
            or not Debugger.envFlags["INTERCALL"]
        ):
            return

        # get symbol id #
        if sid == -1:
            if not sym in self.timelineIdx:
                self.timelineIdx[sym] = str(len(self.timelineIdx))
            symidx = self.timelineIdx[sym]
        else:
            symidx = sid

        # get group id #
        if gid == -1:
            grpidx = symidx
        else:
            grpidx = gid

        # convert time unit to us #
        startTime = self.vdiff * 1000000

        # add timeline data #
        self.timelineData["segments"].append(
            {
                "group": grpidx,
                "text": sym,
                "id": symidx,
                "state": "S",
                "time_start": startTime,
                "time_end": startTime + self.timeDuration,
                "info": bts.strip() if bts else "",
            }
        )

    def handleSyscallOutput(self, args, defer=False, ignored=False):
        # get diff time #
        diff = self.vdiff

        # get backtrace #
        if SysMgr.funcDepth > 0:
            # get backtrace list #
            backtrace = self.getBacktrace(limit=SysMgr.funcDepth, cur=True)

            # convert list to string #
            bts = self.getBtStr(backtrace)
            if bts:
                if self.btFilterList:
                    if not UtilMgr.isValidStr(bts, self.btFilterList):
                        if not defer:
                            self.status = "skip"
                        else:
                            self.status = "enter"
                        self.clearArgs()
                        return False
                bts = "\n%s%s " % (" " * 20, bts)
        else:
            backtrace = None
            bts = ""

        # add timeline item #
        self.addTimelineInt(self.syscall, bts)

        # set filter flag #
        if Debugger.envFlags["ONLYOK"] or Debugger.envFlags["ONLYFAIL"]:
            filtered = True
        else:
            filtered = False

        # print context in JSON format #
        if SysMgr.jsonEnable and not self.isRealtime:
            # set context #
            jsonData = {
                "type": "enter",
                "time": self.current,
                "timediff": diff,
                "name": self.syscall,
                "tid": self.pid,
                "comm": self.comm,
                "backtrace": bts,
                "args": {},
            }

            # marshaling args #
            for idx, arg in enumerate(self.args):
                if args:
                    jsonData["args"][arg[1]] = args[idx]
                else:
                    jsonData["args"][arg[1]] = arg[2]

            # register entry context #
            if Debugger.envFlags["COMPLETECALL"]:
                self.entryContext[self.syscall] = jsonData
                return

            try:
                SysMgr.printPipe(
                    str(UtilMgr.convDict2Str(jsonData, pretty=self.pretty))
                )
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr(
                    "failed to convert %s to JSON for marshalling"
                    % [jsonData],
                    True,
                )
            return

        callString = ""

        if not self.isRealtime or SysMgr.showAll:
            # convert args to string ##
            if Debugger.envFlags["NOARG"]:
                argText = ""
            elif args:
                argText = ", ".join(
                    hex(arg).rstrip("L")
                    if isinstance(arg, (int, long))
                    else str(arg)
                    for arg in args
                )
            else:
                argText = ", ".join(str(arg[2]) for arg in self.args)

            # build call string #
            if defer:
                callString = "%s)%s" % (argText, bts)
            else:
                syscallColor = "RED" if ignored else "GREEN"
                syscall = UtilMgr.convColor(self.syscall, syscallColor)

                # get thread group info #
                if self.tgid:
                    tginfo = "%s(%s)/" % (self.tgcomm, self.tgid)
                else:
                    tginfo = ""

                callString = "%3.6f %s%s(%s) %s(%s)%s" % (
                    diff,
                    tginfo,
                    self.comm,
                    self.pid,
                    syscall,
                    argText,
                    bts,
                )

            # handle call string for enter #
            if filtered:
                if defer:
                    self.bufferedStr += callString
                else:
                    self.bufferedStr = callString

        # print call info #
        if self.isRealtime:
            self.addSample(self.syscall, "??", realtime=True, bt=backtrace)

            if filtered:
                return
        elif SysMgr.outPath:
            self.addSample(self.syscall, "??", bt=backtrace)

            if filtered:
                return

            # print to stdout #
            if SysMgr.streamEnable:
                callString = "%s" % callString[: self.pbufsize]
                sys.stdout.write(callString)
        else:
            if filtered:
                return

            # trim string #
            if not SysMgr.showAll and SysMgr.funcDepth == 0:
                callString = "%s " % callString[: self.pbufsize]

            if defer:
                prefix = ""
            else:
                prefix = "\n"

            SysMgr.printPipe(
                prefix + callString,
                newline=False,
                flush=True,
                trim=False,
            )

        # file output #
        if SysMgr.outPath and callString:
            if defer:
                callString = self.bufferedStr + callString
            self.callPrint.append(callString)

        # check symbol #
        if SysMgr.customCmd:
            self.checkSymbol(self.syscall, newline=True, exceptFilter=["ret*"])

    def handleDefSyscall(self):
        self.status = "exit"

        if self.getRegs(temp=True) != 0:
            sys.exit(-1)

        # set return value from register #
        retval = self.getRet(temp=True)

        # clear args #
        self.clearArgs()

        # get syscall number #
        nrSyscall = self.getNrSyscall()

        # check syscall condition #
        if nrSyscall >= len(ConfigMgr.sysList):
            return
        elif SysMgr.syscallList and not nrSyscall in SysMgr.syscallList:
            return
        elif (
            SysMgr.syscallExceptList and nrSyscall in SysMgr.syscallExceptList
        ):
            return

        # convert args #
        args = self.convSyscallArgs(retval)

        # print output #
        return self.handleSyscallOutput(args, defer=True)

    def updateSyscallStat(self, name, diff):
        try:
            # get times #
            ttotal, tmax = self.syscallStat[name]
            ttotal += diff

            # update maximum time #
            if tmax < diff:
                tmax = diff

            # update times #
            self.syscallStat[name] = [ttotal, tmax]

            # save interval for stdev #
            if Debugger.envFlags["STDEV"]:
                self.syscallInterStat.setdefault(name, [])
                self.syscallInterStat[name].append(diff)
        except SystemExit:
            sys.exit(0)
        except:
            self.syscallStat[name] = [diff, diff]

    def handleSyscall(self):
        # check thread status #
        if self.status == "wait":
            return

        # task filter #
        if (
            self.execCmd
            and SysMgr.filterGroup
            and not UtilMgr.isValidStr(self.comm)
        ):
            return

        # skip useless return processing #
        if self.status == "skip":
            self.status = "enter"
            return
        # check defer #
        elif self.status == "defer":
            ret = self.handleDefSyscall()
            if ret is False:
                return

        # ignore return #
        if Debugger.dbusEnable and self.status == "exit":
            self.status = "enter"
            self.clearArgs()
            return

        # read registers #
        if not self.updateRegs():
            sys.exit(-1)

        # get syscall number #
        nrSyscall = self.getNrSyscall()

        # check syscall condition #
        if nrSyscall >= len(ConfigMgr.sysList):
            if not Debugger.dbusEnable:
                SysMgr.printWarn("no support for syscall(%s)" % nrSyscall)
            return
        elif SysMgr.syscallList and not nrSyscall in SysMgr.syscallList:
            # self.cmd = self.sysemuCmd
            self.status = "skip"
            return
        elif (
            SysMgr.syscallExceptList and nrSyscall in SysMgr.syscallExceptList
        ):
            # self.cmd = self.sysemuCmd
            self.status = "skip"
            return

        # get syscall name #
        try:
            self.syscall = name = ConfigMgr.sysList[nrSyscall][4:]
        except SystemExit:
            sys.exit(0)
        except:
            if Debugger.dbusEnable:
                return
            SysMgr.printWarn("no support for syscall(%s)" % nrSyscall)
            self.syscall = name = "syscall<%s>" % nrSyscall

        # get diff time #
        diff = self.vdiff

        # define shortcut variables #
        convColor = UtilMgr.convColor

        # enter #
        if self.status == "enter":
            # ignore syscall #
            if self.ignSyscallList and nrSyscall in self.ignSyscallList:
                ignored = True

                # get backtrace #
                if SysMgr.funcDepth > 0 and self.btIgnFilterList:
                    # get backtrace list #
                    backtrace = self.getBacktrace(
                        limit=SysMgr.funcDepth, cur=True
                    )

                    # convert list to string #
                    bts = self.getBtStr(backtrace)

                    # check backtrace filter #
                    if UtilMgr.isValidStr(bts, self.btIgnFilterList):
                        ignored = True
                    else:
                        ignored = False

                if ignored:
                    self.changeSyscall()
                    self.clearArgs()
            else:
                ignored = False

            # check exit in mtrace mode #
            if self.isMtraceMode:
                if name.startswith("exit"):
                    SysMgr.printErr(
                        "%s(%s) is terminated" % (self.comm, self.pid)
                    )
                    return

            # check filter #
            for target in self.filterList:
                if not UtilMgr.isValidStr(name, [target]):
                    continue
                if not self.checkFilterCond(
                    self.filterList[target].split("|"), self.readArgs(), name
                ):
                    self.status = "skip"
                    return
                break

            # check hide condition #
            if ignored:
                if self.hideIgnSyscall:
                    return
            # set next status #
            else:
                self.status = "exit"

            # check wait condition #
            if self.wait:
                return

            # update interval between syscalls #
            if Debugger.envFlags["INTERCALL"]:
                try:
                    self.interDiff = self.vdiff - self.syscallTime[name]
                    self.updateSyscallStat(name, self.interDiff)
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

            args = []
            self.syscallTime[name] = self.vdiff

            # convert args in trace mode #
            if not self.isRealtime:
                if self.isDeferCall(name):
                    self.status = "defer"

                    # check return condition #
                    if SysMgr.jsonEnable:
                        return

                    # get thread group info #
                    if self.tgid:
                        tginfo = "%s(%s)/" % (self.tgcomm, self.tgid)
                    else:
                        tginfo = ""

                    # build call string #
                    callString = "%3.6f %s%s(%s) %s(" % (
                        diff,
                        tginfo,
                        self.comm,
                        self.pid,
                        convColor(name, "GREEN"),
                    )

                    # handle call string for enter #
                    if (
                        SysMgr.outPath
                        or Debugger.envFlags["ONLYOK"]
                        or Debugger.envFlags["ONLYFAIL"]
                        or self.btFilterList
                    ):
                        self.bufferedStr = callString
                    else:
                        SysMgr.printPipe(
                            "\n%s" % callString, newline=False, flush=True
                        )

                    return

                # convert args #
                args = self.convSyscallArgs()

            # print output #
            self.handleSyscallOutput(args, ignored=ignored)

            # check syscall condition #
            if SysMgr.syscallList:
                self.clearArgs()

            return

        # exit #
        elif self.status == "exit":
            callString = ""

            # set next status #
            self.status = "enter"

            # get diff #
            try:
                diff = self.vdiff - self.syscallTime[name]
            except SystemExit:
                sys.exit(0)
            except:
                diff = 0

            # execute commands #
            if SysMgr.customCmd:
                needUpdateRegs = False
                for cmd in SysMgr.customCmd:
                    item = cmd.split("|")
                    # skip #
                    if not UtilMgr.isValidStr(name, [item[0]]):
                        continue
                    elif len(item) == 1:
                        continue

                    # execute commands #
                    for command in item[1:]:
                        if not command.startswith("ret"):
                            continue
                        args = self.readArgs(syscall=True)
                        self.executeCmd([command], name, None, args)
                        needUpdateRegs = True

                # update final regset #
                if needUpdateRegs:
                    self.updateRegs()
                    SysMgr.printPipe()

            # get return value from register #
            retval = self.getRet()
            if retval < 0:
                # check wrong context using ENOSYS and syscall number #
                if retval == -38 and self.getNrSyscall() > 0:
                    self.handleSyscall()
                    return

            # check wait condition #
            if self.wait:
                if self.wait == name and retval == 0:
                    # unset wait condition #
                    self.wait = None
                return

            # convert error code #
            if retval and retval < 0:
                # increase error count #
                self.errCnt += 1

                # check exit condition for success #
                if Debugger.envFlags["ONLYOK"]:
                    self.clearArgs()
                    self.popSample(name)
                    return
                # print enter context #
                elif Debugger.envFlags["ONLYFAIL"]:
                    callString = "\n%s " % self.bufferedStr

                # get error string #
                try:
                    retstr = -1
                    errtype = ConfigMgr.ERR_TYPE[abs(retval + 1)]
                    err = "%s (%s)" % (errtype, os.strerror(abs(retval)))
                    self.addSample(name, "??", err=retval)
                except SystemExit:
                    sys.exit(0)
                except:
                    err = ""
            else:
                # handle mmap tracing #
                if self.isMtraceMode:
                    # get args #
                    args = self.readArgs(syscall=True)

                    # TODO: add mremap, munmap #

                    # handle only MAP_ANON #
                    if name == "mmap":
                        if args[4] == -1:
                            self.mmapList[retval] = {
                                "size": args[1],
                                "time": self.current,
                                "stack": self.prevBtStr,
                            }

                # check exit condition for fail #
                if Debugger.envFlags["ONLYFAIL"]:
                    self.clearArgs()
                    self.popSample(name)
                    return
                # print exit context #
                elif Debugger.envFlags["ONLYOK"]:
                    callString = "\n%s " % self.bufferedStr

                # build return string #
                try:
                    retstr = "%s(%s)" % (retval, hex(retval).rstrip("L"))
                except SystemExit:
                    sys.exit(0)
                except:
                    retstr = retval
                err = ""

                # change color for return #
                retstr = convColor(retstr, "WARNING")

            # add timeline segment #
            self.addTimelineRet(name, diff, self.prevBtStr)

            # update stats #
            if self.isRealtime:
                if not Debugger.envFlags["INTERCALL"]:
                    self.updateSyscallStat(name, diff)
                self.clearArgs()
                return
            # print context in JSON format #
            elif SysMgr.jsonEnable:
                # set context #
                jsonData = {
                    "type": "exit",
                    "time": self.current,
                    "timediff": diff,
                    "name": name,
                    "tid": self.pid,
                    "comm": self.comm,
                    "backtrace": self.prevBtStr,
                    "ret": str(retval),
                    "err": err,
                }

                # make complete context #
                if (
                    Debugger.envFlags["COMPLETECALL"]
                    and self.syscall in self.entryContext
                ):
                    entryData = self.entryContext.pop(self.syscall, None)
                    if entryData:
                        entryData.update(jsonData)
                        jsonData = entryData
                    jsonData["type"] = "complete"

                # print context #
                SysMgr.printPipe(
                    str(UtilMgr.convDict2Str(jsonData, pretty=self.pretty))
                )

                self.clearArgs()

                return

            # convert error color #
            if err:
                err = " " + convColor(err, "RED")

            # convert elapsed color #
            diffStr = " [%.6f]" % diff
            if diff > self.retTime:
                diffStr = convColor(diffStr, "RED")
            else:
                diffStr = convColor(diffStr, "CYAN")

            # add interval time #
            if Debugger.envFlags["INTERCALL"] and self.interDiff:
                interdiffStr = " [%.6f]" % self.interDiff
                if self.interDiff > self.retTime:
                    diffStr += convColor(interdiffStr, "RED")
                else:
                    diffStr += convColor(interdiffStr, "CYAN")
                self.interDiff = 0

            # print args after return #
            if SysMgr.showAll and self.isDeferRetCall(name):
                self.args = []
                self.updateSyscallArgs()

                # convert numbers #
                args = [
                    hex(arg[2]).rstrip("L")
                    if isinstance(arg[2], long)
                    else str(arg[2])
                    for arg in self.args
                ]
                argstr = " (%s)" % ", ".join(args)
                diffStr += argstr

            # add newline after backtrace #
            if self.btStr or self.prevBtStr:
                newline = "\n " + (" " * self.indentLen)
            else:
                newline = ""

            # build call string #
            callString = "%s%s= %s%s%s" % (
                callString,
                newline,
                retstr,
                err,
                diffStr,
            )

            # print call string #
            if SysMgr.outPath:
                if self.callPrint:
                    self.callPrint[-1] += callString
                else:
                    self.callPrint.append(callString)

                # update stats #
                if not Debugger.envFlags["INTERCALL"]:
                    self.updateSyscallStat(name, diff)

                # print to stdout #
                if SysMgr.streamEnable:
                    sys.stdout.write("%s\n" % callString)
            elif not self.isRealtime:
                SysMgr.printPipe(callString, newline=False, flush=True)

            # remove arg string #
            self.clearArgs()

        else:
            SysMgr.printErr("failed to recognize syscall %s's status" % name)

    def getKernelStack(self, retstr=False):
        # read kernel stack #
        stat = SysMgr.readProcStat(
            self.kernelFd,
            "%s/task/%s/stack" % (self.pid, self.pid),
            self,
            "kernelFd",
        )
        if not stat:
            return None

        # return full data #
        if retstr:
            return " <- ".join(stat)
        else:
            return stat

    def getStatList(self, retstr=False, status=False, proc=False):
        try:
            self.statFd.seek(0)
            stat = self.statFd.read()
        except SystemExit:
            sys.exit(0)
        except:
            try:
                if proc:
                    threadPath = ""
                else:
                    threadPath = "task/%s/" % self.pid

                statPath = "%s/%s/%sstat" % (
                    SysMgr.procPath,
                    self.pid,
                    threadPath,
                )
                self.statFd = open(statPath, "r")
                stat = self.statFd.read()
            except SystemExit:
                sys.exit(0)
            except:
                try:
                    SysMgr.printOpenWarn(statPath)
                except:
                    pass
                return None

        # return full data #
        if retstr:
            return stat

        try:
            statItem = stat.split(") ", 1)[1]
            if status:
                return statItem[0]
            else:
                return statItem.split()
        except SystemExit:
            sys.exit(0)
        except:
            return None

    def isAlive(self):
        stat = self.getStatList(status=True)
        if not stat:
            return None

        if stat == "Z":
            return False
        else:
            return True

    def isSigStopped(self):
        ret = os.waitpid(self.pid, os.WNOHANG)
        if os.WIFSTOPPED(ret[1]) and os.WSTOPSIG(ret[1]) == signal.SIGSTOP:
            return True
        else:
            return False

    def isStopped(self):
        stat = self.getStatList(status=True)
        if not stat:
            return None

        # check status #
        if stat in ("T", "t", "D"):
            return True
        else:
            return False

    def isInRun(self, stats=("R", "t")):
        stat = self.getStatList(status=True)
        if not stat:
            return None

        # check status #
        if stat in stats:
            return True
        else:
            return False

    def getMemUsage(self, unit=True):
        stat = self.getStatList(retstr=True)
        if not stat:
            SysMgr.printWarn(
                "failed to get memory usage for %s(%s)" % (self.comm, self.pid)
            )
            return "0"

        # convert string to list #
        statList = stat.split(") ", 1)[1].split()

        # get RSS size #
        try:
            byteSize = long(statList[self.rssIdx - 2]) << 12
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn(
                "failed to get memory usage for %s(%s)"
                % (self.pid, self.comm),
                reason=True,
            )
            byteSize = 0

        if not unit:
            return byteSize
        else:
            return UtilMgr.convSize2Unit(byteSize, True)

    def getTotalCpuTick(self):
        # get task CPU usage #
        stat = self.getStatList(retstr=True)
        if not stat:
            SysMgr.printWarn(
                "failed to get CPU usage for %s(%s)" % (self.comm, self.pid)
            )
            return [0, 0, 0]

        statList = stat.split(") ", 1)[1].split()

        # get total CPU time #
        utime = long(statList[self.utimeIdx - 2])
        stime = long(statList[self.stimeIdx - 2])
        ttime = utime + stime

        return [ttime, utime, stime]

    def getCpuUsage(self, system=False, proc=False):
        # get system CPU usage #
        if system:
            itime = SysMgr.getIdleTime()
            if itime and self.prevCpuStat and self.prevCpuStat[3]:
                nrCore = SysMgr.getNrCore()
                diff = itime - self.prevCpuStat[3]
                ctime = diff / float(nrCore)
            else:
                ctime = 0
        else:
            itime = ctime = 0

        # get task CPU usage #
        stat = self.getStatList(retstr=True, proc=proc)
        if not stat:
            SysMgr.printWarn(
                "failed to get CPU usage for %s(%s)" % (self.comm, self.pid)
            )
            self.prevCpuStat[3] = itime
            return [0, 0, 0, ctime]

        # check stat change #
        if self.prevStat == stat:
            self.prevCpuStat[3] = itime
            return [0, 0, 0, ctime]

        self.prevStat = stat

        # convert string to list #
        statList = stat.split(") ", 1)[1].split()
        if not statList:
            return [0, 0, 0, ctime]

        # get total CPU time #
        utime = long(statList[self.utimeIdx - 2])
        stime = long(statList[self.stimeIdx - 2])
        ttime = utime + stime

        # get previous CPU usage #
        prevUsage = self.prevCpuStat

        # update previous CPU usage #
        self.prevCpuStat = [ttime, utime, stime, itime]

        # get CPU diff #
        if prevUsage is None or ttime == 0:
            ret = [0, 0, 0, ctime]
        else:
            # return CPU times #
            ret = [
                ttime - prevUsage[0],
                utime - prevUsage[1],
                stime - prevUsage[2],
                ctime,
            ]

        return ret

    def printSymbolList(self, slist):
        if not slist or not SysMgr.warnEnable:
            return

        string = [
            "%s(%s@%s)" % (item[1], hex(item[0]).rstrip("L"), item[2])
            for item in slist
        ]

        SysMgr.printWarn("found multiple symbols [ %s ]" % ", ".join(string))

    def getAddrBySymbol(
        self,
        symbol,
        binary=None,
        inc=False,
        start=False,
        end=False,
        one=False,
        verb=False,
    ):
        # check cache #
        if (
            not "NOSYMCACHE" in SysMgr.environList
            and hasattr(self, "addrCacheList")
            and symbol in self.addrCacheList
        ):
            return self.addrCacheList[symbol]

        # check memory map #
        if not self.pmap:
            self.loadSymbols()

        addrList = []
        addrDict = {}

        def _getOneItem():
            # check libc symbol first #
            if SysMgr.libcObj:
                libcPath = SysMgr.libcObj._name
                for item in addrList:
                    if libcPath in item[2]:
                        return item[0]

            if addrList:
                ret = addrList[0][0]
                self.addrCacheList[symbol] = ret
                return ret

            return []

        # search symbols from all memory-mapped files #
        for mfile in list(self.pmap):
            if binary and not mfile in binary:
                continue

            # get ELF object #
            try:
                fcache = ElfAnalyzer.getObject(mfile)
                if not fcache:
                    continue
            except SystemExit:
                sys.exit(0)
            except:
                continue

            # get offset #
            offset = fcache.getOffsetBySymbol(
                symbol, inc=inc, start=start, end=end
            )
            if isinstance(offset, str):
                offset = long(offset, 16)

                if ElfAnalyzer.isRelocFile(mfile):
                    if offset in addrDict:
                        continue
                    addrList.append(
                        [self.pmap[mfile]["vstart"] + offset, symbol, mfile]
                    )
                else:
                    if offset in addrDict:
                        continue
                    addrList.append([offset, symbol, mfile])

                addrDict[offset] = True

                # return address for 1st item #
                if one:
                    ret = _getOneItem()
                    if ret:
                        return ret

                continue
            elif not isinstance(offset, list):
                continue

            for item in offset:
                sym = item[0]
                offset = long(item[1], 16)
                if ElfAnalyzer.isRelocFile(mfile):
                    if offset in addrDict:
                        continue

                    addrList.append(
                        [self.pmap[mfile]["vstart"] + offset, sym, mfile]
                    )
                else:
                    if offset in addrDict:
                        continue

                    addrList.append([offset, sym, mfile])

                addrDict[offset] = True

                # return address for 1st item #
                if one:
                    ret = _getOneItem()
                    if ret:
                        return ret

        # search JIT-compiled symbols #
        for idx, item in enumerate(self.jmapSymTable):
            target = item[0]
            if (
                (start and target.startswith(symbol))
                or (end and target.endswith(symbol))
                or (inc and symbol in target)
            ):
                addrList.append([self.jmapAddrTable[idx], target, "JIT"])

        # return address #
        if not addrList:
            if verb:
                SysMgr.printErr(
                    "failed to find %s symbol for %s(%s)"
                    % (symbol, self.comm, self.pid)
                )
            return addrList
        elif len(addrList) > 1:
            self.printSymbolList(addrList)

        return addrList

    def lock(self, pos=-1):
        # pylint: disable=undefined-variable
        if not self.lockObj:
            return False

        if pos > -1:
            lockf(self.lockObj, LOCK_EX, 1, pos, 0)
        else:
            lockf(self.lockObj, LOCK_EX)

        return True

    def unlock(self, pos=-1):
        # pylint: disable=undefined-variable
        if not self.lockObj:
            return False

        if pos > -1:
            lockf(self.lockObj, LOCK_UN, 1, pos, 0)
        else:
            lockf(self.lockObj, LOCK_UN)

        return True

    def handoverNewTarget(self, fork=False):
        # get tid of the child task #
        tid = self.getEventMsg()

        # give up the new tracee #
        if not SysMgr.isRoot():
            SysMgr.printWarn(
                (
                    "failed to trace a new cloned tracee "
                    "because there is no root permission"
                ),
                True,
            )

            # detach the new tracee #
            self.detach(only=True, pid=tid, check=True)

            return -1

        # stop tracees #
        self.stop()
        self.stop(pid=tid)

        # check memory map #
        if not self.pmap:
            # load symbols #
            if SysMgr.funcDepth > 0 or not self.mode in (
                "syscall",
                "signal",
                "kernel",
            ):
                self.loadSymbols()

        # check lock #
        if self.isBreakMode and not self.lockObj:
            self.lockObj = Debugger.getGlobalLock(self.pid, len(self.bpList))

        # check master process #
        if SysMgr.masterPid == 0:
            chMid = True
        else:
            chMid = False

        # print clone event info #
        SysMgr.printInfo(
            "%s(%s) is created by %s(%s) [%s]"
            % (
                self.comm,
                tid,
                self.comm,
                self.pid,
                UtilMgr.convNum(self.childNum),
            )
        )

        # create a pipe #
        if Debugger.envFlags["SYNCTASK"]:
            rd, wr = os.pipe()

        # check syscall mode #
        isSyscallMode = self.mode == "syscall"

        """
        create a new tracer to trace the child task.
        Guider trace a new tracee using a new tracer,
        this can't trace the child tracer of the parent tracer,
        because the child tracer is already attached to the parent tracer.
        """
        pid = SysMgr.createProcess(isDaemon=True, chMid=chMid, chPgid=True)
        # parent tracee #
        if pid > 0:
            # detach from the parent task #
            self.detach(only=True)

            # detach from the child task #
            self.detach(only=True, pid=tid, check=True)

            # attach to the parent task again #
            if self.attach(verb=True) < 0:
                sys.exit(-1)

            # update status to leave clone context #
            if isSyscallMode:
                self.status = "stop"

            # increase the number of children #
            self.childNum += 1

            # wait for tracer of child task #
            try:
                if Debugger.envFlags["SYNCTASK"]:
                    os.close(wr)
                    os.fdopen(rd, "r").read()
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr(
                    "failed to wait for tracer of %s(%s)" % (self.comm, tid),
                    reason=True,
                )

            # set new priority #
            if "TRACEESCHED" in SysMgr.environList:
                for cmd in SysMgr.environList["TRACEESCHED"]:
                    SysMgr.applyPriority("%s:%s" % (cmd, tid))

        # child tracee #
        elif pid == 0:
            # disable alarm #
            signal.alarm(0)

            # update the child PID #
            origPid = self.pid
            self.pid = tid

            # attach to the child task #
            while 1:
                if self.attach(verb=True) == 0:
                    break

                # check and wait for attach #
                if self.isAlive():
                    time.sleep(SysMgr.waitDelay)
                    continue
                else:
                    SysMgr.printErr(
                        "%s(%s) is terminated" % (self.comm, self.pid)
                    )
                    sys.exit(0)

            # initialize variables #
            self.initValues(fork)
            self.forked = True

            # increase the number of children #
            self.childNum += 1
            self.myNum = self.childNum

            # update status to leave clone context #
            if isSyscallMode:
                self.status = "enter"
            else:
                self.status = "skip"

            # notify to tracer of parent task #
            try:
                if Debugger.envFlags["SYNCTASK"]:
                    os.close(rd)
                    os.fdopen(wr, "w").write("0")
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr(
                    "failed to notify initialization to %s(%s)"
                    % (self.comm, origPid),
                    reason=True,
                )

            # restart alarm #
            signal.alarm(SysMgr.intervalEnable)

        # fork fail #
        else:
            return -1

        # set trace event #
        self.ptraceEvent(self.traceEventList)

        # set attributes for multiprocess #
        self.multi = True
        if not SysMgr.outPath:
            SysMgr.setStream()

        # check target status #
        if self.isSigStopped():
            # handle args and return values of clone syscall #
            if isSyscallMode and pid and SysMgr.isTraceMode():
                self.status = "defer"
                self.handleSyscall()
                self.status = "stop"

            # continue target #
            self.cont(check=True, sig=signal.SIGCONT)

        return pid

    def restartTrace(self, initStatus=None):
        # print exec event info #
        cmdline = SysMgr.getCmdline(self.pid)
        SysMgr.printInfo(
            '%s(%s) executed "%s"' % (self.comm, self.pid, cmdline)
        )

        # print previous context #
        Debugger.printSummary(self)

        # reset environment #
        SysMgr.exitFuncList = []
        SysMgr.closePrintFd()
        SysMgr.clearPrint()
        SysMgr.clearProcBuffer()

        # create a new controller #
        dobj = Debugger(pid=self.pid, attach=False, mode=self.mode)
        dobj.attached = True
        dobj.initValues()
        if cmdline:
            dobj.execCmd = cmdline.split()
            dobj.targetBpList = self.targetBpList
            dobj.targetBpFileList = self.targetBpFileList
            dobj.exceptBpFileList = self.exceptBpFileList

        # initialize variables #
        if dobj.mode == "break":
            dobj.isRunning = False
            ElfAnalyzer.cachedFiles = {}

        # apply original attribute #
        dobj.myNum = self.myNum
        dobj.childNum = self.childNum

        # load symbols and inject breakpoints #
        if SysMgr.funcDepth > 0 or not dobj.mode in (
            "syscall",
            "signal",
            "kernel",
        ):
            if dobj.loadSymbols():
                dobj.updateBpList()

        # continue target #
        if (
            not dobj.mode in ("syscall", "signal", "kernel")
            and dobj.isStopped()
        ):
            if dobj.cont(check=True) < 0:
                sys.exit(-1)

        # start new tracing #
        dobj.trace(mode=self.mode, multi=self.multi, initStatus=initStatus)

    def initValues(self, fork=True):
        # stat variables #
        self.pthreadid = 0
        self.comm = SysMgr.getComm(self.pid, cache=True)
        self.exe = SysMgr.getExeName(self.pid)
        self.start = self.last = time.time()
        self.statFd = None
        self.kernelFd = None
        self.prevStat = None
        self.pyLibPath = None
        self.pyInit = False
        self.pyThreadStateP = False
        self.prevSym = None
        self.prevPySym = None
        self.prevPyBt = None
        self.prevPyIndent = {}
        self.pretty = not SysMgr.findOption("Q")
        self.mmapList = {}
        self.pathData = {}

        # set syscall filters #
        self.parseSyscallFilter()

        # save tgid info #
        try:
            if not Debugger.envFlags["INCTGINFO"]:
                raise Exception()
            self.tgid = long(SysMgr.getTgid(self.pid))
            self.tgcomm = SysMgr.getComm(self.tgid)
        except SystemExit:
            sys.exit(0)
        except:
            self.tgid = 0
            self.tgcomm = ""

        # update CPU usage for target #
        if hasattr(self, "prevCpuStat"):
            self.getCpuUsage(system=True, proc=self.isMtraceMode)
        else:
            self.prevCpuStat = None

        # exec variable #
        if SysMgr.execEnable is None:
            self.execEnable = True
        else:
            self.execEnable = False

        # register variables #
        self.pc = None
        self.lr = None
        self.sp = None
        self.fp = None
        self.prevCallInfo = None
        self.prevSp = None
        self.prevDepth = 0
        self.prevCfa = 0

        # call variables #
        self.prevCallString = ""
        self.stack = []
        self.prevStack = []
        self.childList = []
        self.callList = []
        self.callTable = {}
        self.btTable = {}
        self.fileTable = {}
        self.callPrint = []
        self.cpuUsageList = []
        self.selfCpuUsageList = []
        self.syscallTime = {}
        self.syscallStat = {}
        self.syscallInterStat = {}
        self.syscallTotalStat = {}
        self.bpcallStat = {}
        self.bpcallInterStat = {}
        self.retList = {}
        self.accList = {}
        self.interList = {}
        self.intervalList = {}
        self.hiddenList = {}
        self.setRetList = {}
        self.regList = {}
        self.repeatCntList = {}
        self.symbolCacheList = {}
        self.addrCacheList = {}
        self.failedAddrList = {}
        self.prevReturn = -1
        self.startAddr = None
        self.errCnt = 0
        self.lastRxSig = 0

        # timestamp variables #
        self.updateCurrent()

        # python variables #
        self.pyAddr = None
        self.pyThreadStateP = None
        self.pyFrameCache = {}

        # make object for myself #
        if (
            not Debugger.tracerInstance
            or Debugger.tracerInstance.pid != SysMgr.pid
        ):
            Debugger.tracerInstance = Debugger(SysMgr.pid, attach=False)
            Debugger.tracerInstance.initValues()

        # update CPU usage for myself #
        Debugger.tracerInstance.getCpuUsage(proc=self.isMtraceMode)

    def handleRetBpFilter(self, sym):
        # get original symbol #
        try:
            origSym = sym[: -len(Debugger.RETSTR)]
        except SystemExit:
            sys.exit(0)
        except:
            origSym = sym

        # check entry time #
        if origSym not in self.entryTime:
            SysMgr.printWarn(
                "no entry time of %s for %s(%s)"
                % (origSym, self.comm, self.pid)
            )
            raise Exception("no entry time")

        # calculate elapsed time #
        skip = False
        hasRetFilter = False
        entry = self.entryTime[origSym].pop()
        etime = self.vdiff - entry
        elapsed = "/%.6f" % etime

        # remove entry timestamp from list #
        if not self.entryTime[origSym]:
            self.entryTime.pop(origSym, None)

        # check return filter #
        if not origSym in self.retFilterList:
            return etime, elapsed, hasRetFilter, skip, None

        # check condition #
        try:
            filters = self.retFilterList[origSym][0]
            target = filters[0]
            op = filters[1].upper()
            cond = float(filters[2])
            cmd = self.retFilterList[origSym][2]

            # set target value #
            if target == "RETTIME":
                val = etime
            elif target == "RETVAL":
                val = self.getRet()
            else:
                SysMgr.printErr(
                    "failed to recognize '%s' as a return filter" % target
                )
                sys.exit(-1)

            # compare values #
            if op == "EQ":
                if val != cond:
                    skip = True
            elif op == "DF":
                if val == cond:
                    skip = True
            elif op == "BT":
                if val <= cond:
                    skip = True
            elif op == "LT":
                if val >= cond:
                    skip = True
            else:
                SysMgr.printErr(
                    "failed to recognize '%s' in return filter for %s for %s"
                    % (op, origSym, "%s(%s)" % (self.comm, self.pid))
                )
                sys.exit(-1)

            hasRetFilter = True
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                "failed to check return filter for %s for %s(%s)"
                % (origSym, self.comm, self.pid),
                True,
            )

        # remove return filter #
        self.retFilterList.pop(origSym, None)

        return etime, elapsed, hasRetFilter, skip, cmd

    def handleRetBp(self, sym, fname, addr):
        try:
            # change return value #
            if sym in self.setRetList:
                newval = self.setRetList[sym].pop()
                if not self.setRetList[sym]:
                    self.setRetList.pop(sym, None)
                self.setRet(newval)
                self.setRegs()
                self.updateRegs()

            # get return value #
            retval = self.getRet()

            # check register set for repeat #
            try:
                origSym = sym[: -len(Debugger.RETSTR)]
            except:
                origSym = sym

            # update context #
            if origSym in self.regList:
                newObj = self.regList.pop(origSym, None)
                self.setRegs(newObj=newObj)
                self.updateRegs()

            # save return value #
            self.retList[origSym] = long(retval)
            self.prevReturn = str(retval)

            # remove breakpoint #
            if not origSym in self.entryTime:
                self.removeBp(addr, lock=True)

            return "=%s(%s)" % (hex(retval).rstrip("L"), retval)
        except SystemExit:
            sys.exit(0)
        except:
            errMsg = "failed to get return value for %s" % sym
            SysMgr.printWarn(errMsg, reason=True)

    def setRetBp(self, sym, fname, cmd=None):
        # get return address #
        try:
            if self.arch in ("aarch64", "arm"):
                pos = self.lr
            else:
                pos = self.getBacktrace(limit=2, cur=False)[0][0]
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn(
                "no backtrace for %s(%s)" % (sym, fname), reason=True
            )

            # to prevent useless error message #
            return True

        # add the new breakpoint for return #
        newSym = sym + Debugger.RETSTR
        ret = self.injectBp(pos, newSym, fname, reins=True, cmd=None)
        if not ret:
            return False

        # register the new breakpoint to per-thread list #
        if not pos in self.bpNewList and pos in self.bpList:
            self.bpNewList[pos] = self.bpList[pos]

        # register function entry time #
        # TODO: handle no return procedure such like PLT #
        self.entryTime.setdefault(sym, [])
        self.entryTime[sym].append(self.vdiff)

        # set command list #
        if cmd:
            self.retCmdList[sym] = cmd

        return True

    def waitForClone(self):
        if self.status == "ready":
            return

        # set trace event #
        self.ptraceEvent(self.traceEventList)

        SysMgr.printStat(
            "wait for clone by %s(%s)... [ STOP(Ctrl+c) ]"
            % (self.comm, self.pid)
        )

        while 1:
            self.cont()

            # wait process #
            rid, ostat = self.waitpid()

            # handle clone event #
            if self.checkCloned(ostat):
                # check clone/fork event #
                if self.isForked(ostat):
                    forked = True
                else:
                    forked = False

                # handle a new task #
                pid = self.handoverNewTarget(fork=forked)
                if pid > 0:
                    continue
                break

        self.stop()

    def checkStat(self, ret, reason=None):
        stat = self.getStatus(ret[1])
        if SysMgr.isTermSignal(stat) or (stat == -1 and not self.isAlive()):
            msg = "terminated %s(%s)" % (self.comm, self.pid)
            if reason:
                msg = "%s because %s" % (msg, reason)
            SysMgr.printErr(msg)
            sys.exit(0)

    def runEventLoop(self, initStatus=None):
        # set mode #
        sampleMode = pycallMode = signalMode = syscallMode = instMode = False
        if self.mode == "sample":
            sampleMode = True
        elif self.mode == "pycall":
            pycallMode = True
        elif self.mode == "signal":
            signalMode = True
        elif self.mode == "syscall":
            syscallMode = True
        elif self.mode == "inst":
            instMode = True
        elif self.mode == "kernel":
            self.startSamplingKernel()

        # timestamp variables #
        self.updateCurrent()

        # initialize dynamic time for tracing #
        self.dstart = time.time()

        # set update flag for time #
        if self.isBreakMode or syscallMode or SysMgr.isTraceMode():
            updateTime = True
        else:
            updateTime = False

        # set including overhead flag for time #
        incOverhead = True if Debugger.envFlags["INCOVERHEAD"] else False

        # define trap flag for syscall #
        syscallTrapFlag = signal.SIGTRAP | 0x80

        # set initial status #
        if initStatus:
            self.status = initStatus

        # enter trace loop #
        while 1:
            # save backtrace info #
            self.prevBtList = self.btList
            self.prevBtStr = self.btStr
            self.btList = self.btStr = None

            # set status #
            if self.status == "stop":
                self.status = "enter"
            elif self.status in ("ready", "wait"):
                pass
            else:
                # wait for sample calls #
                if sampleMode or pycallMode:
                    self.checkInterval()
                elif self.isBreakMode or signalMode:
                    pass
                # skip instructions for performance #
                elif instMode and self.skipInst > 0:
                    for _ in xrange(self.skipInst):
                        self.ptrace(self.cmd)
                # setup trap #
                else:
                    self.ptrace(self.cmd)

            try:
                # add tracing overhead to start time #
                if updateTime and not incOverhead:
                    overhead = time.time() - self.current
                    if Debugger.envFlags["PRINTDELAY"]:
                        SysMgr.printWarn(
                            "[OVERHEAD] %.6f sec" % overhead, True
                        )
                    self.dstart += overhead

                # wait for target to be stopped #
                rid, ostat = self.waitpid()

                # update time #
                self.updateCurrent()
                if updateTime:
                    delay = self.dstart - self.dvalue
                    delay = 0 if delay < 0 else delay
                    self.vdiff = self.current - delay

                # handle clone event #
                if (
                    not Debugger.dbusEnable
                    and (SysMgr.cloneEnable or self.isBreakMode)
                    and self.checkCloned(ostat)
                ):
                    # check fork event #
                    if self.isForked(ostat):
                        # skip handling fork in break mode #
                        if not SysMgr.cloneEnable:
                            self.cont()
                            continue
                        else:
                            forked = True
                    else:
                        forked = False

                    # handle clone event #
                    ret = self.handoverNewTarget(fork=forked)
                    # failure for handling clone #
                    if ret == -1:
                        self.cont()
                    # mute non-target tasks cloned in break mode #
                    elif not SysMgr.cloneEnable:
                        SysMgr.printEnable = False

                    # skip next step to prevent syscall context miss #
                    continue

                # handle exec event #
                elif self.isExeced(ostat):
                    if self.execEnable:
                        self.restartTrace("exit" if syscallMode else None)
                        sys.exit(0)
                    else:
                        SysMgr.printErr(
                            "terminated tracing %s(%s) because of exec"
                            % (self.comm, self.pid)
                        )
                    sys.exit(0)

                # get status of process #
                stat = self.getStatus(ostat)
                self.lastSig = stat

                # trap #
                if stat == signal.SIGTRAP:
                    # handle exit event #
                    if self.isExited(ostat):
                        self.handleExit()
                        self.cont()
                        SysMgr.callSigHandler(signal.SIGALRM)
                        continue

                    # after execve() #
                    elif self.status == "ready":
                        # initialize variables #
                        self.initValues()

                        # set events #
                        self.ptraceEvent(self.traceEventList)

                        SysMgr.printInfo(
                            "start profiling %s(%d)..." % (self.comm, self.pid)
                        )

                        # init python environment #
                        if self.mode in ("pycall", "pybreak"):
                            self.initPyEnv()

                        # set first command #
                        if self.cmd:
                            self.ptrace(self.cmd)

                        if self.isBreakMode:
                            # remove all breakpoints for new child process #
                            if self.forked:
                                self.removeAllBp()

                            # load symbols again #
                            self.loadSymbols()
                            self.updateBpList()

                            # continue target #
                            if self.cont(check=True) < 0:
                                sys.exit(-1)

                        # change status #
                        self.status = "enter"

                    # usercall / breakcall #
                    elif self.isBreakMode or sampleMode or instMode:
                        self.handleTrapEvent(ostat)

                    # wrong status for syscall #
                    elif syscallMode and self.status == "enter":
                        self.status = "skip"
                        self.ptraceEvent(self.traceEventList)
                        self.ptrace(self.cmd)
                        continue

                # exit #
                elif stat == -1:
                    # check alive status #
                    if self.isAlive():
                        continue

                    # print status #
                    SysMgr.printWarn(
                        "terminated %s(%s)" % (self.comm, self.pid)
                    )

                    # wait event #
                    if SysMgr.isTopMode() and self.totalCall:
                        SysMgr.waitEvent()

                    sys.exit(0)

                # signal #
                elif signalMode:
                    # handle signal #
                    self.handleSignal(stat)

                    # deliver signal #
                    self.cont(sig=stat)

                # breakpoint for ARM #
                elif stat == signal.SIGILL and self.isBreakMode:
                    self.handleTrapEvent(ostat)

                # syscall #
                elif stat == syscallTrapFlag:
                    # interpret syscall context #
                    if syscallMode:
                        self.handleSyscall()

                # STOP signal #
                elif stat == signal.SIGSTOP:
                    if sampleMode or pycallMode:
                        self.handleTrapEvent(ostat)
                        continue

                    # update status #
                    self.status = "stop"
                    SysMgr.printWarn(
                        "blocked %s(%s) because of %s"
                        % (self.comm, self.pid, ConfigMgr.SIG_LIST[stat])
                    )

                    # continue #
                    if self.isBreakMode:
                        if self.cont(check=True, sig=stat) < 0:
                            sys.exit(-1)
                    # set up trap again #
                    elif syscallMode:
                        self.ptraceEvent(self.traceEventList)
                        self.ptrace(self.cmd)

                # KILL / SEGV / ABRT signal #
                elif SysMgr.isTermSignal(stat):
                    # print a new line #
                    sys.stdout.write("\n")

                    # print signal info #
                    self.handleSignal(stat, taskinfo=syscallMode)

                    # print context info #
                    if SysMgr.showAll:
                        self.printContext(newline=True)

                    # deliver signal #
                    if self.cont(check=True, sig=stat) < 0:
                        sys.exit(-1)

                    # stop target for next syscall #
                    if syscallMode:
                        self.stop()

                    # set fault flag to shared memory #
                    self.setFaultFlag()

                # other #
                else:
                    # print signal info #
                    if SysMgr.warnEnable:
                        self.handleSignal(
                            stat, warn=True, taskinfo=syscallMode
                        )

                    # handle signal #
                    if sampleMode or pycallMode:
                        self.handleTrapEvent(ostat)

                    # deliver signal #
                    if self.cont(check=True, sig=stat) < 0:
                        sys.exit(-1)

                    # stop target for next syscall #
                    if syscallMode:
                        self.stop()
            except SystemExit:
                return
            except:
                if not self.isAlive():
                    SysMgr.printErr(
                        "terminated tracing %s(%s)" % (self.comm, self.pid)
                    )
                    return

                SysMgr.printWarn(
                    "detected %s(%s) with error" % (self.comm, self.pid),
                    reason=True,
                )

                # deliver signal #
                if self.isBreakMode:
                    if self.cont(check=True) < 0:
                        sys.exit(-1)

    def trace(
        self,
        mode,
        wait=None,
        multi=False,
        lock=None,
        bpList={},
        exceptBpList={},
        targetBpList={},
        targetBpFileList={},
        exceptBpFileList={},
        initStatus=None,
    ):
        # initialize variables #
        self.initValues()

        # apply common breakpoint list #
        if not self.targetBpList:
            self.targetBpList = targetBpList
        if not self.targetBpFileList:
            self.targetBpFileList = targetBpFileList
        if not self.exceptBpFileList:
            self.exceptBpFileList = exceptBpFileList

        # context variables #
        self.cmd = None
        self.wait = wait
        self.mode = mode
        self.multi = multi
        self.lockObj = lock

        # set maximum string size #
        if Debugger.strSize > -1:
            self.pbufsize = Debugger.strSize
        elif SysMgr.outPath or Debugger.dbusEnable:
            self.pbufsize = sys.maxsize
        else:
            self.pbufsize = SysMgr.ttyCols >> 1

        # update break mode #
        self.updateBreakMode()

        # disable extended ascii #
        SysMgr.encodeEnable = False

        # register my instance #
        SysMgr.addExitFunc(Debugger.destroyDebugger, [self])

        # check realtime mode #
        if SysMgr.isTopMode():
            self.isRealtime = True
        else:
            self.isRealtime = False

        # set tracing attribute #
        if self.isRealtime:
            # update CPU usage #
            self.getCpuUsage(system=True)
            Debugger.tracerInstance.getCpuUsage()

            # set alarm handler #
            signal.signal(signal.SIGALRM, Debugger.onAlarm)

            if self.mode in ("sample", "pycall", "kernel"):
                sampleTime = SysMgr.getOption("T")
                if sampleTime:
                    try:
                        self.sampleTime = UtilMgr.convUnit2Sec(sampleTime)
                    except SystemExit:
                        sys.exit(0)
                    except:
                        SysMgr.printErr("failed to set sampling rate", True)
                        sys.exit(-1)
                else:
                    self.sampleTime = 0.001

                # save original sampling rate #
                self.sampleTimeOrig = self.sampleTime

                if not self.multi:
                    SysMgr.printInfo(
                        "do sampling every %s"
                        % UtilMgr.convTime2Unit(self.sampleTime)
                    )

            # set default interval #
            if not SysMgr.findOption("R") and SysMgr.intervalEnable == 0:
                SysMgr.intervalEnable = 1

            # set maximum sampling rate #
            self.sampleTimeMax = float(SysMgr.intervalEnable) / 10

            # check disabling incremental sampling #
            self.incrementalSampling = not "FIXSAMPLING" in SysMgr.environList
        else:
            # set timer handler #
            if SysMgr.intervalEnable:
                signal.signal(signal.SIGALRM, SysMgr.alarmExitHandler)
            elif self.isMtraceMode:
                # define monitor handler #
                def _monitorResource(signum, frame):
                    try:
                        dobj = Debugger.dbgInstance

                        # check tgid #
                        if self.tgid != self.pid:
                            signal.alarm(SysMgr.intervalEnable)
                            return

                        # get CPU usages #
                        mcpuStr, sysCpuStr = SysMgr.getCpuUsageStr(
                            dobj, SysMgr.intervalEnable, proc=True
                        )

                        # get memory usages #
                        dobj.taskObj.saveProcInstance()
                        mstat = TaskAnalyzer.getMemStr(
                            dobj.taskObj, dobj.pid, stats=["rss", "pss", "uss"]
                        )

                        # get stats #
                        statStr = (
                            "%s(%s): CPU(%s)/%s, SYSTEM: CPU(%s)/AvailMem(%s)"
                            % (
                                dobj.comm,
                                dobj.pid,
                                mcpuStr.split("(", 1)[0],
                                mstat,
                                sysCpuStr,
                                SysMgr.getAvailMemInfo(),
                            )
                        )

                        # print resource usages #
                        SysMgr.printInfo(statStr, prefix=False)

                        signal.alarm(SysMgr.intervalEnable)
                    except SystemExit:
                        sys.exit(0)
                    except:
                        SysMgr.printWarn(
                            "failed to get stats for %s(%s)"
                            % (dobj.comm, dobj.pid),
                            True,
                            True,
                        )

                self.taskObj = SysMgr.initTaskMon(self.pid, update=False)
                signal.signal(signal.SIGALRM, _monitorResource)
                SysMgr.intervalEnable = 1
                signal.alarm(SysMgr.intervalEnable)

            # inst #
            if SysMgr.checkMode("utrace") and SysMgr.funcDepth > 0:
                # set sampling rate for instruction #
                self.skipInst = SysMgr.funcDepth

                SysMgr.printInfo(
                    "do sampling every %s instrunctions"
                    % UtilMgr.convNum(SysMgr.funcDepth)
                )

        # prepare environment for the running target #
        if self.isRunning:
            # check the process is running #
            try:
                os.kill(self.pid, 0)
            except SystemExit:
                sys.exit(0)
            except:
                ereason = SysMgr.getErrMsg()
                if ereason != "0":
                    SysMgr.printErr(
                        "failed to trace %s(%s) because %s"
                        % (self.comm, self.pid, ereason)
                    )
                sys.exit(-1)

            # init python environment #
            if self.mode in ("pycall", "pybreak"):
                self.initPyEnv()
            # load user symbols #
            elif SysMgr.funcDepth > 0 or mode not in (
                "syscall",
                "signal",
                "kernel",
            ):
                try:
                    self.loadSymbols()
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr("failed to load symbols", True)
                    sys.exit(-1)

            # print target task info #
            if SysMgr.printEnable:
                SysMgr.printInfo(
                    "start profiling %s(%d)..." % (self.comm, self.pid)
                )

            # check attach status #
            if not self.attached:
                if self.attach(verb=True) < 0:
                    sys.exit(-1)

            # set events #
            if not mode in ("kernel"):
                self.ptraceEvent(self.traceEventList)

            # handle current user symbol #
            if self.mode in ("inst", "sample") and not SysMgr.isTopMode():
                try:
                    self.handleUsercall()
                except SystemExit:
                    sys.exit(0)
                except:
                    return
        # set trap event type for the new target #
        else:
            if not mode in ("kernel"):
                self.ptraceEvent(self.traceEventList)
            self.status = "ready"

        # select trap command #
        if self.mode == "syscall":
            self.cmd = self.syscallCmd

            # get filter commands #
            newCustomCmd = []
            for item in SysMgr.customCmd:
                try:
                    syscall, value = item.split("|", 1)
                    if value.startswith("filter:"):
                        self.filterList[syscall] = value
                    else:
                        newCustomCmd.append(item)
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr("wrong syscall filter '%s'" % item)
                    sys.exit(-1)
            SysMgr.customCmd = newCustomCmd
        elif self.mode == "inst":
            self.cmd = self.singlestepCmd
            if self.arch == "arm":
                SysMgr.printErr("not supported on %s" % self.arch.upper())
                sys.exit(-1)
        elif self.mode in ("sample", "pycall"):
            self.cmd = None

            # set syscall command to wait for wakeup #
            if self.isRunning and Debugger.envFlags["WAITWAKEUP"]:
                SysMgr.printInfo(
                    "start waiting for %s(%s) awakened" % (self.comm, self.pid)
                )

                # sleep for syscall wakeup #
                for _ in range(2):
                    self.ptrace(self.syscallCmd)
                    self.waitpid()
        elif self.isBreakMode:
            if self.isRunning:
                # register breakpoint data #
                if bpList:
                    self.bpList = bpList
                if exceptBpList:
                    self.exceptBpList = exceptBpList

                # check thread status #
                stat = self.getStatList(status=True)
                if not stat:
                    SysMgr.printErr(
                        "terminated %s(%s)" % (self.comm, self.pid)
                    )
                elif stat == "S":
                    SysMgr.syscall(self.tkillIdx, self.pid, signal.SIGCONT)
        elif self.mode in ("signal", "kernel"):
            if self.isStopped():
                if self.cont(check=True) < 0:
                    sys.exit(-1)
        elif self.mode == "remote":
            Debugger.envFlags["PRINTDIFF"] = True
            for _ in xrange(SysMgr.intervalEnable + 1):
                self.runExecMode()
            SysMgr.printPipe()
            sys.exit(0)
        else:
            SysMgr.printErr(
                "failed to recognize '%s' mode for trace" % self.mode
            )
            sys.exit(-1)

        # register summary callback #
        SysMgr.addExitFunc(Debugger.printSummary, [self])

        # wait for task creation #
        if Debugger.envFlags["WAITCLONE"]:
            self.waitForClone()

        # set timer #
        if SysMgr.masterPid == 0 or SysMgr.printEnable:
            signal.alarm(SysMgr.intervalEnable)

        # run loop #
        self.runEventLoop(initStatus)

    @staticmethod
    def destroyDebugger(instance):
        def _printTimeline():
            if (
                Debugger.envFlags["TIMELINE"]
                and instance.timelineData["segments"]
            ):
                # get symbol list #
                ylist = {y: x for x, y in instance.timelineIdx.items()}

                # update name table #
                instance.timelineData["names"] = ylist

                # set output path #
                if SysMgr.outPath:
                    outPath = SysMgr.inputFile
                else:
                    outPath = "%s/guider_%s.svg" % (
                        SysMgr.tmpPath,
                        instance.pid,
                    )
                outPath = UtilMgr.getDrawOutputPath(outPath, "timeline")
                dataPath = outPath.replace(".svg", ".json")

                # backup data #
                SysMgr.backupFile(dataPath)

                # save data #
                UtilMgr.saveStr2File(
                    UtilMgr.convDict2Str(instance.timelineData, pretty=True),
                    dataPath,
                )

                # get data size #
                fsize = UtilMgr.getFileSizeStr(dataPath)

                SysMgr.printStat(
                    "wrote timeline data into '%s'%s" % (dataPath, fsize)
                )

                # backup output #
                SysMgr.backupFile(outPath)

                # check svgwrite object #
                try:
                    SysMgr.getPkg("svgwrite")
                except:
                    return

                # draw timeline #
                SysMgr.drawTimeline(
                    inputData=instance.timelineData,
                    outputPath=outPath,
                    yval=ylist,
                )

        Debugger.dbgInstance = None

        # check condition for breakpoint cleanup #
        if (
            not instance.pid
            or SysMgr.inputParam
            or not instance.bpList
            or not instance.isAlive()
        ):
            # print timeline #
            _printTimeline()

            # remove instance #
            instance.__del__()

            # return #
            return

        # stop target #
        if not instance.isStopped():
            instance.stop()
            instance.waitpid()

        # notify termination to master process #
        try:
            tgid = long(SysMgr.getTgid(instance.pid))
            if SysMgr.masterPid and tgid == instance.pid:
                os.kill(SysMgr.masterPid, signal.SIGINT)
        except:
            # return #
            return

        # make CPU priority lower #
        SysMgr.setPriority(SysMgr.pid, "C", 19, verb=False)

        try:
            # set max try count #
            cnt = 5

            while 1:
                # update register set #
                ret = instance.updateRegs()
                if ret:
                    break
                elif not instance.isAlive():
                    return

                # check count #
                cnt -= 1
                if cnt <= 0:
                    instance.__del__(stop=True)
                    return

                # wait a moment #
                time.sleep(SysMgr.waitDelay)
        except:
            Debugger.printSummary(instance)
            SysMgr.outPath = None

        # rewind IP from trap status #
        addr = instance.pc - instance.prevInstOffset
        if addr in instance.bpList:
            instance.setPC(addr)
            instance.setRegs()
            instance.removeBp(addr)
        else:
            # check alignment #
            if addr % ConfigMgr.wordSize == 0:
                origWord = instance.accessMem(instance.peekCmd, addr)
                origWord = UtilMgr.convWord2Str(origWord)
            else:
                origWord = instance.readMem(addr)

            # recover memory #
            if origWord and origWord.startswith(instance.brkInst):
                ret = instance.getSymbolInfo(addr)
                fname = ret[1]
                offset = long(ret[2], 16)
                inst = instance.loadInst(fname, offset)
                instance.writeMem(addr, inst, skipCheck=True)

        # remove all breakpoints for the thread group leader #
        if tgid == instance.pid:
            origPrintFlag = SysMgr.printEnable
            SysMgr.printEnable = True
            if not instance.execCmd:
                # block signal #
                SysMgr.blockSignal(act="block")

                instance.removeAllBp(tgid)

                # unblock signal #
                SysMgr.blockSignal(act="unblock")

            SysMgr.printEnable = origPrintFlag

        # remove new breakpoints for children after fork #
        for addr in list(instance.bpNewList):
            instance.removeBp(addr)

        instance.__del__(stop=True)

        # draw timeline segment #
        _printTimeline()

        # terminate immediately to avoid memory increase due to COW by GC #
        if tgid != instance.pid:
            Debugger.printSummary(instance)

            # release all resources #
            SysMgr.releaseResource()

            os._exit(0)

    @staticmethod
    def printCallHistory(instance, name=None):
        if not instance.callPrint:
            return

        try:
            elapsed = instance.callList[-1][1] - instance.start
        except:
            elapsed = instance.last - instance.start

        nrLine = UtilMgr.convNum(len(instance.callPrint))
        callStr = "\n".join(instance.callPrint)
        procInfo = "%s(%s)" % (instance.comm, instance.pid)

        SysMgr.printPipe(
            "\n[Trace %s History] [%s] [Time: %f] [Line: %s]\n%s\n%s\n%s"
            % (name, procInfo, elapsed, nrLine, twoLine, callStr, oneLine)
        )

    @staticmethod
    def printSummary(instance):
        def _printSystemStat():
            # save system info #
            SysMgr.saveSysStats(True)

        # check realtime mode #
        if not SysMgr.outPath or SysMgr.jsonEnable:
            return
        elif (
            not instance.callList and not "FORCESUMMARY" in SysMgr.environList
        ):
            SysMgr.printWarn(
                "no sample data to summarize for %s(%s)"
                % (instance.comm, instance.pid),
                True,
            )
            return

        # summarize samples after last tick #
        if instance.isRealtime and (
            SysMgr.repeatCnt == 0 or SysMgr.progressCnt < SysMgr.repeatCnt
        ):
            instance.printIntervalSummary()
        else:
            instance.last = time.time()

        callTable = {}
        fileTable = {}
        elapsedTable = []

        # define stop flag #
        needStop = False

        # check mode and define type variables #
        fattr = "File"
        attr = "Function"
        if instance.mode == "syscall":
            if instance.isMtraceMode:
                ctype = "Mmap"
            else:
                ctype = "Syscall"
            if Debugger.envFlags["INTERCALL"]:
                addInfo = "<Interval>"
            else:
                addInfo = "<Elapsed>"
        elif instance.mode == "break":
            ctype = "Breakcall"
            addInfo = "[Path] <Elapsed>"
        elif instance.mode == "pybreak":
            ctype = "Pycall"
            addInfo = "[Path] <Elapsed>"
        elif instance.mode == "signal":
            fattr = attr = ctype = "Signal"
            addInfo = "[Number] <Elapsed>"
        else:
            addInfo = "[Path]"
            if instance.mode == "pycall":
                ctype = "Pycall"
            else:
                ctype = "Usercall"

            # continue target to prevent too long freezing #
            if instance.traceStatus and instance.isAlive():
                try:
                    sig = 0
                    ret = os.waitpid(instance.pid, os.WNOHANG)
                    if ret and ret[1] > 0:
                        sig = instance.getStatus(ret[1])
                    if (
                        sig != signal.SIGSTOP
                        and instance.cont(check=True, sig=sig) == 0
                    ):
                        needStop = True
                except:
                    pass

        # check mode #
        if instance.isRealtime:
            mtype = "Top"
        else:
            mtype = "Trace"
        suffix = "\n"

        # update suffix of output file name #
        SysMgr.fileSuffix = "%s_%s_%s" % (
            instance.comm,
            instance.pid,
            SysMgr.pid,
        )

        # print logo #
        if not SysMgr.printFd:
            SysMgr.printLogo(big=True, onlyFile=True)

        # print System Info #
        _printSystemStat()

        SysMgr.printInfo(
            "start analyzing %ss of %s(%s)..."
            % (ctype, instance.comm, instance.pid)
        )

        nrTotal = float(len(instance.callList))

        # check incomplete break mode #
        if not Debugger.envFlags["COMPLETECALL"] and instance.mode == "break":
            isIncompleteBreakMode = True
        else:
            isIncompleteBreakMode = False

        # iterate the call sample list #
        for idx, item in enumerate(instance.callList):
            try:
                symbol, timestamp, filename = item

                # skip breakpoints for return #
                if isIncompleteBreakMode and symbol.endswith(Debugger.RETSTR):
                    nrTotal -= 1
                    continue

                # convert anonymous call to filename #
                if symbol == "??":
                    symbol = filename

                # add to symbol table #
                try:
                    callTable[symbol]["cnt"] += 1
                except:
                    callTable[symbol] = {
                        "cnt": 1,
                        "path": filename,
                    }

                UtilMgr.printProgress(idx, len(instance.callList))

                if instance.mode == "syscall":
                    continue

                # add to file table #
                try:
                    fileTable[filename]["cnt"] += 1
                except:
                    fileTable[filename] = {"cnt": 1}
            except SystemExit:
                UtilMgr.deleteProgress()

                # stop target to return original status #
                if needStop:
                    instance.stop(check=True)

                return
            except:
                pass

        if instance.callList:
            UtilMgr.deleteProgress()

        # print call table #
        convert = UtilMgr.convNum
        try:
            elapsed = instance.callList[-1][1] - instance.start
        except:
            elapsed = instance.last - instance.start

        # get sample info #
        try:
            maxSample = elapsed / instance.sampleTime
            perSample = "%.1f" % ((nrTotal / maxSample) * 100)
        except:
            perSample = "100"

        # get sampling period #
        if instance.sampleTime > 0:
            samplingStr = " [Freq: %s] " % UtilMgr.convTime2Unit(
                instance.sampleTime
            )
            freqStr = "(%s%%)" % perSample
        else:
            samplingStr = ""
            freqStr = ""

        # set task info #
        procInfo = "%s(%s)" % (instance.comm, instance.pid)
        mprocInfo = "%s(%s)" % (
            Debugger.tracerInstance.comm,
            Debugger.tracerInstance.pid,
        )

        # average CPU usage for target #
        nrCpuUsageSample = len(instance.cpuUsageList)
        if nrCpuUsageSample > 0:
            # calculate average CPU usage #
            ttime = utime = stime = ctime = 0
            for cpustat in instance.cpuUsageList:
                ttime += cpustat[0]
                utime += cpustat[1]
                stime += cpustat[2]
                ctime += cpustat[3]
            ttime /= float(nrCpuUsageSample)
            utime /= float(nrCpuUsageSample)
            stime /= float(nrCpuUsageSample)
            ctime /= float(nrCpuUsageSample)

            cpuStr = "%d%%(U%d%%+S%d%%)" % (ttime, utime, stime)
            cpuStr = "[%s: %s] " % (procInfo, cpuStr)

            sysStr = "[SYS: %d%%/%s] " % (ctime, SysMgr.getAvailMemInfo())
        else:
            sysStr = " "
            cpuStr = "[%s] " % procInfo

        # average CPU usage for myself #
        if instance.selfCpuUsageList:
            mttime = sum(instance.selfCpuUsageList)
            mttime /= float(len(instance.selfCpuUsageList))
            rssStr = Debugger.tracerInstance.getMemUsage()
            mStr = "[%s: %d%%/%s]" % (mprocInfo, mttime, rssStr)
        else:
            mStr = "[%s]" % mprocInfo

        # set error count #
        if instance.errCnt:
            errstr = "[Err: %s] " % convert(instance.errCnt)
        else:
            errstr = ""

        # set number values #
        nrSym = len(callTable)
        origNrTotal = nrTotal

        # print mtrace result #
        mtraceTotal = 0
        mtraceHistory = []
        mtraceStackMap = {}
        try:
            if not instance.isMtraceMode:
                raise ValueError

            # init values #
            nrTotal = 0
            instance.syscallTotalStat = instance.syscallStat = {}

            if not instance.mmapList:
                SysMgr.printWarn("no mmap call for %s" % procInfo)
            if not instance.tgid:
                try:
                    instance.tgid = long(SysMgr.getTgid(instance.pid))
                except:
                    if not instance.isAlive():
                        # print status #
                        SysMgr.printErr(
                            (
                                "failed to get memory map for %s "
                                "because it is already terminated"
                            )
                            % procInfo
                        )
                        raise Exception("target terminated")

            # get memory map #
            srcPath = "%s/%s/maps" % (SysMgr.procPath, instance.tgid)
            desPath = "%s/maps.%s" % (SysMgr.tmpPath, instance.tgid)
            # use the main map file #
            if os.path.exists(srcPath):
                amap = TaskAnalyzer.getProcAnonMap(
                    instance.tgid, onlyStart=True, total=True
                )
                SysMgr.copyFile(srcPath, desPath)
            # refer to last map file #
            elif os.path.exists(desPath):
                amap = TaskAnalyzer.getProcAnonMap(
                    instance.tgid, onlyStart=True, fpath=desPath, total=True
                )
            # no map file #
            else:
                raise Exception("no process map")

            sortedList = sorted(list(amap))
            onlyRes = "ONLYRES" in SysMgr.environList

            # update valid mmap items #
            for addr in list(instance.mmapList):
                # search valid vma #
                addrList = []
                for maddr in sortedList:
                    # check size #
                    if amap[maddr]["size"] == 0:
                        continue

                    # check inner scope #
                    offset = instance.mmapList[addr]["size"]
                    moffset = amap[maddr]["size"] << 10
                    mtotal = amap[maddr]["total"] << 10
                    if onlyRes and (
                        addr <= maddr <= maddr + moffset < addr + offset
                    ):
                        addrList.append([maddr, moffset >> 10])
                    elif not onlyRes and (
                        maddr <= addr <= addr + offset <= maddr + mtotal
                    ):
                        addrList.append([addr, offset >> 10])
                    elif maddr > addr + offset:
                        break
                if not addrList:
                    instance.mmapList.pop(addr, None)
                    continue

                # init size #
                data = instance.mmapList[addr]
                data["size"] = 0

                # accumulate size #
                for haddr, size in addrList:
                    # process data #
                    data["size"] += size

                    # convert stack #
                    stack = data["stack"]
                    if not stack:
                        stack = ""
                    stack = " <- ".join(
                        UtilMgr.cleanItem(
                            list(map(str.strip, stack.split("<-"))), False
                        )
                    )
                    instance.mmapList[addr]["stack"] = stack

            # summarize valid calls #
            stackDict = {}
            for k, v in instance.mmapList.items():
                msize = v["size"] << 10
                mtraceTotal += msize
                mstack = v["stack"]
                if not mstack in mtraceStackMap:
                    mtraceStackMap[mstack] = mstack
                mtraceHistory.append(
                    [v["time"], hex(k), msize, mtraceStackMap[mstack]]
                )

                try:
                    stack = v["stack"]
                    size = v["size"] << 10
                    stackDict[stack] += size
                except:
                    stackDict[stack] = size

            if not stackDict:
                SysMgr.printErr("no valid mmap call for %s" % procInfo)
                raise Exception("no valid mmap call")

            # init summary table #
            callTable = {"mmap": {"cnt": 0}}
            instance.btTable["mmap"] = {}

            # make summary table #
            for stack, size in stackDict.items():
                callTable["mmap"]["cnt"] += size
                nrTotal += size

                # apply indent to stack string #
                newStack = ""
                lineLength = SysMgr.lineLength
                ilen = tlen = 20
                for seq, call in enumerate(stack.split(" <- ")):
                    if seq > 0 and tlen + len(call) > lineLength:
                        newStack = "%s\n%s" % (newStack, " " * ilen)
                        tlen = ilen
                    newStack = "%s<- %s " % (newStack, call)
                    tlen += len(call) + 4
                instance.btTable["mmap"][newStack] = size

            # save the number of stack #
            nrSym = len(stackDict)
        except ValueError:
            pass
        except:
            SysMgr.printWarn(
                "failed to print mtrace results for %s" % procInfo, True, True
            )
            nrTotal = origNrTotal

        # print title #
        titleStr = (
            "\n[%s %s Summary] [Elapsed: %.3f]%s%s%s%s "
            "[Sample: %s%s] %s[Symbol: %s] %s"
        ) % (
            mtype,
            ctype,
            elapsed,
            samplingStr,
            sysStr,
            cpuStr,
            mStr,
            convert(long(nrTotal)),
            freqStr,
            errstr,
            convert(nrSym),
            suffix,
        )
        SysMgr.printPipe(titleStr)
        SysMgr.printPipe(
            "{3:1}{0:^7} | {1:<144}{2:1}{3:1}".format(
                "Usage", "%s %s" % (attr, addInfo), suffix, twoLine + suffix
            )
        )

        # print stat summary #
        tcnt = 0
        for sym, value in sorted(
            callTable.items(), key=lambda x: x[1]["cnt"], reverse=True
        ):
            cnt = value["cnt"]

            # get percentage #
            try:
                per = (cnt / nrTotal) * 100
            except:
                break

            # add stats #
            if instance.mode == "syscall":
                # add time stats #
                if sym in instance.syscallTotalStat:
                    vals = instance.syscallTotalStat[sym]
                    addVal = "<Tot: %.6f, Avg: %.6f, Max: %.6f> <Cnt: %s>" % (
                        vals[0],
                        vals[0] / float(cnt),
                        vals[1],
                        convert(cnt),
                    )
                elif sym in instance.syscallStat:
                    vals = instance.syscallStat[sym]
                    addVal = "<Tot: %.6f, Avg: %.6f, Max: %.6f> <Cnt: %s>" % (
                        vals[0],
                        vals[0] / float(cnt),
                        vals[1],
                        convert(cnt),
                    )
                else:
                    addVal = "<Cnt: %s>" % convert(cnt)
            elif instance.isBreakMode:
                addVal = "[%s] <Cnt: %s" % (
                    value["path"],
                    convert(cnt),
                )

                # set symbol #
                if sym.endswith(Debugger.RETSTR):
                    rsym = sym
                else:
                    rsym = sym + Debugger.RETSTR

                # add return stats #
                if (
                    rsym in instance.callTable
                    and "elapsed" in instance.callTable[rsym]
                    and instance.callTable[rsym]["elapsed"] > 0
                ):
                    val = instance.callTable[rsym]
                    addVal = (
                        "%s, Elapsed: %.6f, Avg: %.6f, Min: %.6f, Max: %.6f"
                        % (
                            addVal,
                            val["elapsed"],
                            val["elapsed"] / val["cnt"],
                            val["min"],
                            val["max"],
                        )
                    )

                    elapsedTable.append(val["elapsed"])

                addVal += ">"
            else:
                addVal = "[%s] <Cnt: %s>" % (
                    value["path"],
                    convert(cnt),
                )

            # convert path to anonymous symbol #
            if sym[0] == "/":
                targetSym = "??"
            else:
                targetSym = sym

            SysMgr.printPipe(
                "{0:>7} | {1:<144}{2:1}".format(
                    "%.1f%%" % per, "%s %s" % (targetSym, addVal), suffix
                )
            )

            # add backtraces #
            if sym in instance.btTable:
                for bt, btcnt in sorted(
                    instance.btTable[sym].items(),
                    key=lambda x: x[1],
                    reverse=True,
                ):
                    # print backtrace and percent #
                    bper = (btcnt / float(cnt)) * 100
                    ret = SysMgr.printPipe(
                        "{0:>17} | {1:<1} <Cnt: {2:1}>".format(
                            "%.1f%%" % bper, bt, convert(btcnt)
                        )
                    )

            tcnt += 1

        if tcnt == 0:
            SysMgr.printPipe("    None%s" % suffix)

        SysMgr.printPipe(oneLine + suffix)

        # print histo stats for elapsed time #
        if elapsedTable:
            elapsedTable = UtilMgr.convList2Histo(elapsedTable, mult=1000000)
            UtilMgr.printHist(elapsedTable, "Elapsed", "us")

        # print file table #
        if fileTable:
            SysMgr.printPipe(
                (
                    "\n[%s %s Summary] [Elapsed: %.3f]%s%s%s%s "
                    "[NrSamples: %s(%s%%)] [NrFiles: %s] %s"
                )
                % (
                    mtype,
                    fattr,
                    elapsed,
                    samplingStr,
                    sysStr,
                    cpuStr,
                    mStr,
                    convert(long(nrTotal)),
                    perSample,
                    convert(len(fileTable)),
                    suffix,
                )
            )

            # set attribute name #
            if instance.mode == "signal":
                attrname = "Number"
            else:
                attrname = "Path"

            SysMgr.printPipe(
                "{3:1}{0:^7} | {1:<144}{2:1}{3:1}".format(
                    "Usage", attrname, suffix, twoLine + suffix
                )
            )

            tcnt = 0
            for filename, value in sorted(
                fileTable.items(), key=lambda x: x[1]["cnt"], reverse=True
            ):
                try:
                    per = (value["cnt"] / nrTotal) * 100
                except:
                    break

                addVal = " <Cnt: %s>" % convert(value["cnt"])

                SysMgr.printPipe(
                    "{0:>7} | {1:<144}{2:1}".format(
                        "%.1f%%" % per, filename + addVal, suffix
                    )
                )

                tcnt += 1

            if tcnt == 0:
                SysMgr.printPipe("    None%s" % suffix)

            SysMgr.printPipe(oneLine + suffix)

        # print access files #
        if instance.envFlags["PRINTFILE"]:
            SysMgr.printPipe(
                "\n[Access File Summary] [NrFunc: %s] [NrAccess: %s] %s%s"
                % (
                    convert(len(instance.pathData) - 1),
                    convert(instance.pathData["total"]["total"]),
                    suffix,
                    twoLine,
                )
            )
            SysMgr.printPipe(
                "{0:10} {1:>10} {2:1}\n{3:1}".format(
                    "Func(Cnt)", "Cnt", "Path", oneLine
                )
            )

            for func, fval in sorted(
                instance.pathData.items(),
                key=lambda x: x[1]["total"],
                reverse=True,
            ):
                if func == "total":
                    continue
                SysMgr.printPipe(
                    "%s(%s)" % (func, convert(fval.pop("total", 0)))
                )
                for fpath, fstat in sorted(
                    fval.items(), key=lambda x: x[1], reverse=True
                ):
                    SysMgr.printPipe(
                        "{0:10} {1:>10} {2:1}".format(
                            " ", convert(fstat), fpath
                        )
                    )
                SysMgr.printPipe(oneLine)

        # print history #
        if instance.isMtraceMode:
            instance.printMtraceHistory(mtraceHistory, mtraceTotal)
        else:
            instance.printCallHistory(instance, ctype)

        # stop target to return original status #
        if needStop:
            instance.stop(check=True)

        try:
            SysMgr.printInfo(
                "saved the results into '%s' successfully"
                % SysMgr.printFd.name
            )
        except:
            pass

        # print detailed statistics #
        if SysMgr.procBuffer:
            SysMgr.printProcBuffer(suffix)

    def printMtraceHistory(self, history, totalSize):
        convSize = UtilMgr.convSize2Unit

        title = "Leakage History"

        try:
            tgid = self.tgid
        except SystemExit:
            sys.exit(0)
        except:
            tgid = self.pid

        proc = "%s(%s)" % (self.comm, tgid)
        startTime = SysMgr.getStartTime(tgid)

        SysMgr.printPipe(
            "\n[%s] [Process: %s] [Start: %s] [Total: %s] [Count: %s]\n%s"
            % (
                title,
                proc,
                startTime,
                convSize(totalSize, True),
                convSize(len(history), True),
                twoLine,
            )
        )

        SysMgr.printPipe(
            "{0:^16} | {1:^16} | {2:^6} |{3:^50}| {4:^53}\n{5:1}".format(
                "Time", "Addr", "Size", "Data", "Stack", oneLine
            )
        )
        if not history:
            SysMgr.printPipe("\tNone\n%s" % oneLine)
            return

        for item in history:
            timeval, addr, size, stack = item
            SysMgr.printPipe(
                "{0:>16} | {1:>16} | {2:>6} |{3:<50}| {4:<53}\n{5:1}".format(
                    round(timeval, 2),
                    addr,
                    size,
                    "",
                    stack,
                    oneLine,
                )
            )

    @staticmethod
    def checkPtraceScope():
        filePath = "%s/sys/kernel/yama/ptrace_scope" % SysMgr.procPath

        try:
            with open(filePath, "r") as fd:
                """
                0 - classic ptrace permissions: a process can PTRACE_ATTACH to any other
                process running under the same uid, as long as it is dumpable (i.e.
                did not transition uids, start privileged, or have called
                prctl(PR_SET_DUMPABLE...) already). Similarly, PTRACE_TRACEME is
                unchanged.

                1 - restricted ptrace: a process must have a predefined relationship
                with the inferior it wants to call PTRACE_ATTACH on. By default,
                this relationship is that of only its descendants when the above
                classic criteria is also met. To change the relationship, an
                inferior can call prctl(PR_SET_PTRACER, debugger, ...) to declare
                an allowed debugger PID to call PTRACE_ATTACH on the inferior.
                Using PTRACE_TRACEME is unchanged.

                2 - admin-only attach: only processes with CAP_SYS_PTRACE may use ptrace
                with PTRACE_ATTACH, or through children calling PTRACE_TRACEME.

                3 - no attach: no processes may use ptrace with PTRACE_ATTACH nor via
                PTRACE_TRACEME. Once set, this sysctl value cannot be changed.
                """
                perm = long(fd.readline()[:-1])
                if perm != 3:
                    return 0

                # error status #
                SysMgr.printErr(
                    "failed to use ptrace because it is not allowed, check %s"
                    % filePath
                )
                return -1
        except:
            return 0

    @staticmethod
    def dumpTaskMemory(pid, meminfo, output):
        SysMgr.checkRootPerm()

        # check ptrace scope #
        if Debugger.checkPtraceScope() < 0:
            return

        # dump memory #
        try:
            dobj = Debugger(pid=pid)
            if not dobj:
                raise Exception("N/A")
            ret = dobj.dumpMemory(meminfo, output)
            if ret == 0:
                raise Exception("N/A")
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                "failed to dump memory for %s(%s)"
                % (SysMgr.getComm(pid), pid),
                reason=True,
            )

    @staticmethod
    def pauseThreads(tlist):
        def _updateTargets(taskList):
            dlist = []
            tlist = list(taskList)

            for tid in tlist:
                if not SysMgr.isAlive(tid):
                    dlist.append(tid)

            for tid in list(set(dlist)):
                SysMgr.printWarn(
                    "terminated %s(%s)" % (taskList[tid], tid), True
                )
                taskList.pop(tid, None)

            return taskList

        # check thread list #
        if not tlist:
            return False

        SysMgr.checkRootPerm()

        # check ptrace scope #
        if Debugger.checkPtraceScope() < 0:
            return False

        # set alarm #
        signal.signal(signal.SIGALRM, SysMgr.onAlarm)
        signal.alarm(SysMgr.intervalEnable)

        taskList = {}
        lastTid = 0
        try:
            for tid in tlist:
                lastTid = long(tid)

                ret = SysMgr.createProcess(mute=True, chPgid=True)
                if ret > 0:
                    comm = SysMgr.getComm(tid)
                    taskList[tid] = comm
                    SysMgr.printInfo("paused %s(%s)" % (comm, lastTid))
                elif ret == 0:
                    dobj = Debugger(pid=lastTid)
                    SysMgr.waitEvent(ignChldSig=False)
                    dobj.__del__()
                    sys.exit(0)
                else:
                    SysMgr.printErr("failed to create a process")
                    sys.exit(-1)

            # wait for user event to continue threads #
            while 1:
                SysMgr.waitEvent(ignChldSig=False, exit=True)
                taskList = _updateTargets(taskList)
                SysMgr.updateChildList()
                if SysMgr.isNoChild():
                    break

            SysMgr.printErr("no target thread")
        except SystemExit:
            return
        except:
            SysMgr.printErr("failed to pause thread %s" % lastTid, True)
        finally:
            # send SIGCONT to targets #
            for tid in tlist:
                try:
                    SysMgr.syscall("tkill", tid, signal.SIGCONT)
                except:
                    pass

    def getSigInfo(self):
        PTRACE_GETSIGINFO = 0x4202
        ret = self.ptrace(PTRACE_GETSIGINFO, data=addressof(self.sigObj))
        return ret

    def setSigInfo(self):
        PTRACE_SETSIGINFO = 0x4203
        ret = self.ptrace(PTRACE_SETSIGINFO, data=addressof(self.sigObj))
        return ret

    def checkCloned(self, status):
        stat = status >> 8
        if stat in (self.sigCloneFlag, self.sigForkFlag, self.sigVforkFlag):
            return True
        else:
            return False

    def isForked(self, status):
        stat = status >> 8
        return stat in (self.sigForkFlag, self.sigVforkFlag)

    def isExited(self, status):
        return status >> 8 == self.sigExitFlag

    def isExeced(self, status):
        return status >> 8 == self.sigExecFlag

    def isCloned(self, status):
        return status >> 8 == self.sigCloneFlag

    def getEventMsg(self):
        PTRACE_GETEVENTMSG = 0x4201
        data = c_long(0)
        addr = addressof(data)

        self.ptrace(PTRACE_GETEVENTMSG, data=addr)

        return data.value

    def getStatus(self, status):
        ret = None

        # Process exited #
        if os.WIFEXITED(status):
            # code = os.WEXITSTATUS(status)
            ret = -1
        # Process killed by a signal #
        elif os.WIFSIGNALED(status):
            ret = os.WTERMSIG(status)
        # Invalid process status #
        elif not os.WIFSTOPPED(status):
            pass
        # Ptrace Event #
        elif status >> 8 == 0:
            ret = status >> 16
        # Process stopped by a signal #
        else:
            ret = os.WSTOPSIG(status)

        return ret

    def setPC(self, val):
        if self.arch in ("aarch64", "riscv64"):
            self.regs.pc = val
        elif self.arch == "x64":
            self.regs.rip = val
        elif self.arch == "arm":
            self.regs.r15 = val
        elif self.arch == "x86":
            self.regs.eip = val

    def setSP(self, val):
        if self.arch in ("aarch64", "riscv64"):
            self.regs.sp = val
        elif self.arch == "x64":
            self.regs.rsp = val
        elif self.arch == "arm":
            self.regs.r13 = val
        elif self.arch == "x86":
            self.regs.esp = val

    def setLR(self, val):
        if self.arch == "aarch64":
            self.regs.x30 = val
        elif self.arch == "arm":
            self.regs.r14 = val
        elif self.arch == "riscv64":
            self.regs.ra = val

    def setRegs(self, temp=False, newObj=None):
        # read registers #
        try:
            if not self.supportSetRegset:
                raise Exception("not support setregset")

            if newObj:
                addr = addressof(self.getIovec(newObj))
            elif temp:
                addr = addressof(self.tempIovecObj)
            else:
                addr = addressof(self.iovecObj)

            # PTRACE_SETREGSET #
            ret = self.ptrace(0x4205, ConfigMgr.NT_TYPE["NT_PRSTATUS"], addr)
            if ret != 0:
                raise Exception("setregset failure")
        except SystemExit:
            sys.exit(0)
        except:
            self.supportSetRegset = False

            if newObj:
                addr = addressof(newObj)
            elif temp:
                addr = addressof(self.tempRegs)
            else:
                addr = addressof(self.regs)

            cmd = self.setregsCmd

            ret = self.ptrace(cmd, 0, addr)

        # check ret value #
        if ret >= 0:
            return True

        if not self.isAlive():
            SysMgr.printErr("terminated %s(%s)" % (self.comm, self.pid))
            sys.exit(0)

        SysMgr.printErr(
            "failed to write remote registers for %s(%s) because %s"
            % (self.comm, self.pid, self.errmsg)
        )

        return False

    def restoreRegs(self, bt=False):
        if bt:
            target = self.btRegs
        else:
            target = self.tempRegs

        memmove(addressof(self.regs), addressof(target), sizeof(self.regs))

        self.updateNamedRegs()

    def backupRegs(self, bt=False):
        if bt:
            target = self.btRegs
        else:
            target = self.tempRegs

        memmove(addressof(target), addressof(self.regs), sizeof(self.regs))

    def getFpRegs(self, temp=False, new=False):
        if new:
            newObj = self.getFpRegStruct()

        # read registers #
        try:
            if not self.supportGetRegset:
                raise Exception("not support getregset")

            if new:
                addr = addressof(self.getIovec(newObj))
            elif temp:
                addr = addressof(self.tempIovecFpObj)
            else:
                addr = addressof(self.iovecFpObj)

            # PTRACE_GETREGSET #
            ret = self.ptrace(0x4204, ConfigMgr.NT_TYPE["NT_PRFPREG"], addr)
            if ret != 0:
                raise Exception("getregset failure")
        except SystemExit:
            sys.exit(0)
        except:
            self.supportGetRegset = False

            if new:
                addr = addressof(newObj)
            elif temp:
                addr = addressof(self.tempFpRegs)
            else:
                addr = addressof(self.fpregs)

            cmd = self.getfpregsCmd
            ret = self.ptrace(cmd, 0, addr)

        # handle error #
        if ret != 0:
            if not self.isAlive():
                SysMgr.printErr("terminated %s(%s)" % (self.comm, self.pid))
                sys.exit(0)

            errMsg = "failed to read float registers for %s(%s)" % (
                self.comm,
                self.pid,
            )

            # check state #
            if self.isStopped():
                SysMgr.printWarn("%s because %s" % (errMsg, self.errmsg))
            else:
                SysMgr.printWarn("%s because it is not stopped" % errMsg)

        # return result #
        if new:
            return newObj
        else:
            return ret

    def getRegs(self, temp=False, new=False):
        if new:
            newObj = self.getRegStruct()

        # read registers #
        try:
            if not self.supportGetRegset:
                raise Exception("not support getregset")

            if new:
                addr = addressof(self.getIovec(newObj))
            elif temp:
                addr = addressof(self.tempIovecObj)
            else:
                addr = addressof(self.iovecObj)

            # PTRACE_GETREGSET #
            ret = self.ptrace(0x4204, ConfigMgr.NT_TYPE["NT_PRSTATUS"], addr)
            if ret != 0:
                raise Exception("getregset failure")
        except SystemExit:
            sys.exit(0)
        except:
            self.supportGetRegset = False

            if new:
                addr = addressof(newObj)
            elif temp:
                addr = addressof(self.tempRegs)
            else:
                addr = addressof(self.regs)

            cmd = self.getregsCmd
            ret = self.ptrace(cmd, 0, addr)

        # handle error #
        if ret != 0:
            if not self.isAlive():
                SysMgr.printErr("terminated %s(%s)" % (self.comm, self.pid))
                sys.exit(0)

            errMsg = "failed to read registers for %s(%s)" % (
                self.comm,
                self.pid,
            )

            # check state #
            if self.isStopped():
                SysMgr.printWarn("%s because %s" % (errMsg, self.errmsg))
            else:
                SysMgr.printWarn("%s because it is not stopped" % errMsg)

        # return result #
        if new:
            return newObj
        else:
            return ret

    def updateNamedRegs(self):
        if self.arch == "aarch64":
            self.fp = self.regs.x29
            self.lr = self.regs.x30
            self.sp = self.regs.sp
            self.pc = self.regs.pc
        elif self.arch == "x64":
            # no use rbp as frame pointer #
            self.fp = self.regs.rbp
            self.sp = self.regs.rsp
            self.pc = self.regs.rip
        elif self.arch == "riscv64":
            self.fp = self.regs.s0
            self.lr = self.regs.ra
            self.sp = self.regs.sp
            self.pc = self.regs.pc
        elif self.arch == "arm":
            self.fp = self.regs.r11
            self.sp = self.regs.r13
            self.lr = self.regs.r14
            self.pc = self.regs.r15
        elif self.arch == "x86":
            self.fp = self.regs.ebp
            self.sp = self.regs.esp
            self.pc = self.regs.eip

    def updateRegs(self):
        if self.getRegs() != 0:
            return False

        self.updateNamedRegs()

        return True

    def dumpMemory(self, meminfo, output, verb=True):
        if meminfo in ("heap", "stack"):
            meminfo = "[%s]" % meminfo

        # get range info #
        self.updateProcMap()
        ret = FileAnalyzer.getMapAddr(self.pid, meminfo, self.mapFd)
        if not ret:
            ret = meminfo.split("-")
            if not ret:
                SysMgr.printErr(
                    "failed to search %s on memory map for %s(%s)"
                    % (meminfo, self.comm, self.pid)
                )
                return 0

        # convert range #
        start = UtilMgr.convStr2Num(ret[0], onlyHex=True)
        if not start:
            return 0
        end = UtilMgr.convStr2Num(ret[1], onlyHex=True)
        if not end:
            return 0
        size = end - start

        # print status #
        if verb:
            SysMgr.printInfo(
                "start dumping memory %s-%s [%s] for %s(%s)"
                % (
                    hex(start).rstrip("L"),
                    hex(start + size).rstrip("L"),
                    UtilMgr.convSize2Unit(size),
                    self.comm,
                    self.pid,
                )
            )

        # backup #
        SysMgr.backupFile(output)

        # open output file #
        try:
            fd = open(output, "wb")
            os.chmod(output, 0o777)
        except:
            SysMgr.printOpenErr(output)
            return 0

        # handle SAVEBITMAP variable #
        if "SAVEBITMAP" in SysMgr.environList:
            # get page-aligned size #
            pageSize = SysMgr.PAGESIZE
            tsize = long((size + pageSize - 1) / pageSize)

            # init bitmap #
            table = [0] * tsize

            # check present bit for each page #
            for idx in range(tsize):
                entry = PageAnalyzer.getPagemapEntry(
                    self.pid, start + (idx * pageSize)
                )
                res = PageAnalyzer.isPresent(entry)
                if res:
                    table[idx] = 1

            # write memory to file #
            btable = bytes(table)
            fd.write(btable)
            fd.close()

            # close output file for sync #
            if verb:
                # get output size #
                fsize = UtilMgr.getFileSizeStr(output)

                SysMgr.printStat(
                    "saved the bitmap data to '%s'%s successfully"
                    % (output, fsize)
                )

            return size

        # define buffer and chunk size #
        offset = start
        chunk = UtilMgr.convUnit2Size("10MB")
        total = 0

        # copy data from target memory #
        while size > 0:
            if size < chunk:
                chunk = size
                size = 0
            else:
                size -= chunk

            # read memory from target #
            buf = self.readMem(offset, chunk)

            # write memory to file #
            fd.write(buf)

            if verb:
                UtilMgr.printProgress(total, size)

            offset += chunk
            total += len(buf)

        if verb:
            UtilMgr.deleteProgress()

        # close output file for sync #
        if verb:
            SysMgr.printStat(
                "start writing dump data [%s] to %s"
                % (UtilMgr.convSize2Unit(total), output)
            )

        fd.close()

        return total

    def ptraceEvent(self, reqList):
        # define architect-independent constant #
        PTRACE_SETOPTIONS = 0x4200

        option = 0
        plist = ConfigMgr.PTRACE_EVENT_TYPE

        for req in reqList:
            if req == "PTRACE_O_TRACESYSGOOD":
                option |= 1
            elif req == "PTRACE_O_TRACEEXEC":
                option |= 1 << plist.index("PTRACE_EVENT_EXEC")
            elif req == "PTRACE_O_TRACEEXIT":
                option |= 1 << plist.index("PTRACE_EVENT_EXIT")
            elif req == "PTRACE_O_TRACESECCOMP":
                option |= 1 << plist.index("PTRACE_EVENT_SECCOMP")
            elif Debugger.dbusEnable:
                pass
            elif SysMgr.cloneEnable or self.isBreakMode:
                if req == "PTRACE_O_TRACEFORK":
                    option |= 1 << plist.index("PTRACE_EVENT_FORK")
                elif req == "PTRACE_O_TRACEVFORK":
                    option |= 1 << plist.index("PTRACE_EVENT_VFORK")
                elif req == "PTRACE_O_TRACECLONE":
                    option |= 1 << plist.index("PTRACE_EVENT_CLONE")
                elif req == "PTRACE_O_TRACEVFORKDONE":
                    option |= 1 << plist.index("PTRACE_EVENT_VFORK_DONE")

        return self.ptrace(PTRACE_SETOPTIONS, 0, option)

    def waitpid(self, pid=None):
        """
        # Don't wait on children of other threads in this group #
        __WNOTHREAD = 0x20000000

        # Wait on all children, regardless of type #
        __WALL = 0x40000000

        # Wait only on non-SIGCHLD children #
        __WCLONE = 0x80000000
        """

        try:
            # init variables #
            ret = 0
            status = c_uint(0)
            waitpid = SysMgr.libcObj.waitpid
            if not pid:
                pid = self.pid

            # type converting #
            if not self.initWaitpid:
                waitpid.argtypes = (c_int, POINTER(None), c_int)
                waitpid.restype = c_int
                self.initWaitpid = True

            while 1:
                # wait for children #
                try:
                    ret = waitpid(pid, pointer(status), 0x40000000)
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

                # check termination #
                if ret == -1:
                    if not self.isAlive():
                        sys.exit(0)

                break

            return ret, status.value
        except SystemExit:
            sys.exit(0)
        except Exception as ex:
            # handle exception error during exit #
            if type(ex).__name__ == "TypeError":
                sys.exit(-1)

            SysMgr.printWarn("failed to call waitpid", reason=True)

            return 0, 0

    def ptrace(self, req, addr=0, data=0, pid=None):
        if pid is None:
            pid = self.pid

        """
        # try to call native ptrace call #
        try:
            return SysMgr.guiderObj.ptrace(req, pid, addr, data)
        except SystemExit: sys.exit(0)
        except: pass
        """

        try:
            # type converting #
            if not self.initPtrace:
                SysMgr.libcObj.ptrace.argtypes = (
                    c_ulong,
                    c_ulong,
                    c_ulong,
                    c_ulong,
                )
                SysMgr.libcObj.ptrace.restype = c_ulong
                self.initPtrace = True

            # ptrace #
            ret = SysMgr.libcObj.ptrace(req, pid, addr, data)
            if not ret:
                return ret
            elif c_long(ret).value == -1:
                self.errmsg = SysMgr.getErrReason()
                raise Exception(self.errmsg)
            else:
                return ret
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn(
                "failed to call ptrace(%d, %d, %d, %d)"
                % (req, pid, addr, data),
                reason=True,
            )
            return -1


class EventAnalyzer(object):
    """Analyzer for event"""

    eventData = {}

    def __init__(self):
        pass

    def __del__(self):
        pass

    @staticmethod
    def addEvent(time, event):
        eventData = EventAnalyzer.eventData

        # ramdom event #
        if len(event.split(":")) == 1:
            name = event
            ID = None
        # sequential event #
        else:
            name = event.split(":")[0]
            ID = event.split(":")[1]

        try:
            eventData[name]
            """
            {'list': [ID, time, number],
            'summary': [ID, cnt, avr, min, max, first, last]}
            """
        except:
            eventData[name] = {"list": [], "summary": []}

        eventData[name]["list"].append(
            [
                ID,
                time,
                __builtins__.sum(t[0] == ID for t in eventData[name]["list"])
                + 1,
            ]
        )

        if (
            __builtins__.sum(id[0] == ID for id in eventData[name]["summary"])
            == 0
        ):
            eventData[name]["summary"].append([ID, 1, -1, -1, -1, time, time])
        else:
            for n in eventData[name]["summary"]:
                if n[0] == ID:
                    n[1] += 1
                    n[6] = time
                    break

    @staticmethod
    def printEventInfo():
        eventData = EventAnalyzer.eventData

        if eventData:
            SysMgr.printPipe(
                "\n[%s] [Total: %d]\n%s"
                % ("Event Info", len(eventData), twoLine)
            )
            try:
                EventAnalyzer.printEvent()
            except SystemExit:
                sys.exit(0)
            except:
                pass
            SysMgr.printPipe(twoLine)

    @staticmethod
    def printEvent():
        eventData = EventAnalyzer.eventData
        startTime = float(SysMgr.startTime)

        for key, value in sorted(
            eventData.items(), key=lambda x: float(x[1]["summary"][0][5])
        ):
            string = ""
            head = "%10s: [total: %s] [subEvent: %s] " % (
                key,
                len(eventData[key]["list"]),
                len(eventData[key]["summary"]),
            )
            for idx, n in enumerate(
                sorted(eventData[key]["summary"], key=lambda slist: slist[0])
            ):
                if idx == 0:
                    msg = head
                else:
                    msg = " " * len(head)

                if not n[0]:
                    n[0] = "MAIN"

                try:
                    string = (
                        "%s[%8s > cnt: %3d, avr: %3d, min: %3d,"
                        "max: %3d, first: %7.3f, last: %7.3f]"
                    ) % (
                        msg,
                        n[0],
                        n[1],
                        n[2],
                        n[3],
                        n[4],
                        float(n[5]) - startTime,
                        float(n[6]) - startTime,
                    )
                except:
                    pass

                SysMgr.printPipe("%s" % string)


class MemoryFile(object):
    """File object for memory region"""

    def __init__(self, addr=0, size=4096, name=None, data=None):
        self.pos = 0
        self.addr = addr
        self.size = size
        self.name = name
        self.fd = -1
        self.rd = -1
        self.wd = -1
        self.mem = bytearray(size)

        if addr:
            self.resize(size)

        if data:
            self.write(data)
            self.pos = 0

    def setpipe(self, default):
        rd, wr = os.pipe()
        self.rd = rd
        self.wr = wr

        # set default fd #
        if default == "wr":
            self.fd = wr
        elif default == "rd":
            self.fd = rd
        else:
            SysMgr.printErr("wrong default attr '%s' for pipe" % default)
            sys.exit(0)

    def flush(self):
        pass

    def fileno(self):
        return self.fd

    def resize(self, size):
        SysMgr.importPkgItems("ctypes")

        # copy more data #
        if self.addr:
            optr = self.addr
        # just increase anonymous buffer #
        else:
            orig = self.mem
            optr = (c_char * len(orig)).from_buffer(orig)

        # new #
        self.mem = bytearray(size)
        nptr = (c_char * size).from_buffer(self.mem)

        # memcpy #
        ret = memmove(nptr, optr, size)
        if ret < 0:
            if self.addr:
                SysMgr.printErr("failed to copy memory from %s" % self.addr)
            else:
                SysMgr.printErr("failed to increase memory to %s" % size)
        else:
            self.size = size

        return ret

    def getsize(self):
        return len(self.mem)

    def readline(self):
        # check EOF #
        if self.pos == self.size - 1:
            return ""
        data = self.mem[self.pos :].decode().split("\n", 1)[0]
        self.pos += len(data) + 1
        if self.pos >= self.size:
            self.pos = self.size - 1
        return data

    def readlines(self, hint=-1):
        data = self.read()
        data = data.decode().split("\n")
        if hint > 0:
            return data[:hint]
        else:
            return data

    def read(self, size=0, upscale=True):
        # update size #
        if not size:
            size = self.size - self.pos

        des = self.pos + size

        # read remaining data #
        if upscale:
            if self.pos + size > self.size:
                size = self.size - self.pos
        # WARN: upscaling buffer causes segmentation fault #
        else:
            if des > self.size:
                self.resize(des)

        segment = self.mem[self.pos : des]

        self.pos += size

        return bytes(segment)

    def write(self, buf):
        self.mem = self.mem[: self.pos] + buf + self.mem[self.pos + len(buf) :]
        self.pos += len(buf)
        self.size = len(self.mem)

    def tell(self):
        return self.pos

    def seek(self, offset, which=0):
        if which == 0:
            pos = offset
        elif which == 1:
            pos = self.pos + offset
        elif which == 2:
            pos = self.size + offset
        else:
            SysMgr.printErr("wrong which value '%s' for seek()" % which)
            sys.exit(-1)

        self.pos = pos

    def __iter__(self):
        return self

    def __next__(self):
        return self.readline()

        # old implementation #
        while 1:
            if self.pos == len(self.mem) - 1:
                raise StopIteration

            data = self.mem[self.pos :]
            try:
                data = data.decode()
            except:
                pass

            # find newline #
            pos = data.find("\n")

            # newline #
            if pos == 0:
                self.pos += 1
                return ""

            # EOF #
            if pos < 0:
                self.pos = len(self.mem) - 1
                return data

            # return a line #
            self.pos += pos
            return data[self.pos : self.pos + pos]

    def next(self):
        return self.__next__()


class ElfAnalyzer(object):
    """Analyzer for ELF object"""

    SHF_WRITE = 0x1
    SHF_ALLOC = 0x2
    SHF_EXECINSTR = 0x4
    SHF_MERGE = 0x10
    SHF_STRINGS = 0x20
    SHF_INFO_LINK = 0x40
    SHF_LINK_ORDER = 0x80
    SHF_OS_NONCONFORMING = 0x100
    SHF_GROUP = 0x200
    SHF_TLS = 0x400
    SHF_COMPRESSED = 0x800
    SHF_EXCLUDE = 0x80000000
    SHF_MASKPROC = 0xF0000000

    DT_VERSIONTAGNUM = 16

    rustChars = [
        [",", "$C$"],
        ["@", "$SP$"],
        ["*", "$BP$"],
        ["&", "$RF$"],
        ["<", "$LT$"],
        [">", "$GT$"],
        ["(", "$LP$"],
        [")", "$RP$"],
        [" ", "$u20$"],
        ["'", "$u27$"],
        ["[", "$u5b$"],
        ["]", "$u5d$"],
        ["~", "$u7e$"],
        ["{", "$u7b$"],
        ["}", "$u7d$"],
        ["::", ".."],
        ["-", "."],
    ]

    PT_FLAGS = {
        0: "None",
        1: "E",
        2: "W",
        3: "WE",
        4: "R",
        5: "RE",
        6: "RW",
        7: "RWE",
    }

    PT_TYPE = {
        0: "NULL",
        1: "LOAD",
        2: "DYNAMIC",
        3: "INTERP",
        4: "NOTE",
        5: "SHLIB",
        6: "PHDR",
        7: "TLS",
        8: "NUM",
        "LOOS": 0x60000000,
        "HIOS": 0x6FFFFFFF,
        0x60000000: "LOOS",
        0x6FFFFFFF: "HIOS",
        0x70000000: "LOPROC",
        0x7FFFFFFF: "HPROC",
        0x6474E550: "GNU_EH_FRAME",
        0x6474E551: "GNU_STACK",
        0x6474E552: "GNU_RELRO",
    }

    ST_TYPE = {
        0: "NOTYPE",
        1: "OBJECT",
        2: "FUNC",
        3: "SECTION",
        4: "FILE",
        5: "COMMON",
        6: "TLS",
        7: "NUM",
        10: "LOOS",
        12: "HIOS",
        13: "LOPROC",
        15: "HIPROC",
    }

    ST_BIND_TYPE = {
        0: "LOCAL",
        1: "GLOBAL",
        2: "WEAK",
        3: "NUM",
        10: "LOOS",
        12: "HIOS",
        13: "LOPROC",
        15: "HIPROC",
    }

    ST_VISIBILITY_TYPE = {
        0: "DEFAULT",
        1: "INTERNAL",
        2: "HIDDEN",
        3: "PROTECTED",
    }

    SH_TYPE = {
        0: "NULL",
        1: "PROGBITS",
        2: "SYMTAB",
        3: "STRTAB",
        4: "RELA",
        5: "HASH",
        6: "DYNAMIC",
        7: "NOTE",
        8: "NOBITS",
        9: "REL",
        10: "SHLIB",
        11: "DYNSYM",
        12: "NUM",
        14: "INIT_ARRAY",
        15: "FINI_ARRAY",
        16: "PREINIT_ARRAY",
        17: "GROUP",
        18: "SYMTAB_SHNDX",
        19: "RELR",
        20: "NUM",
        0x60000000: "LOOS",
        0x60000001: "ANDROID_REL",
        0x60000002: "ANDROID_RELA",
        0x6FFFFF00: "ANDROID_RELR",
        0x6FFF4C00: "LLVM_ODRTAB",
        0x6FFF4C01: "LLVM_LINKER_OPTIONS",
        0x6FFF4C02: "LLVM_CALL_GRAPH_PROFILE",
        0x6FFFFFFF: "HIOS",
        0x70000000: "LOPROC",
        0x7FFFFFFF: "HIPROC",
        0x80000000: "LOUSER",
        0xFFFFFFFF: "HIUSER",
        0x6FFFFFF5: "GNU_ATTRIBUTES",
        0x6FFFFFF6: "GNU_HASH",
        0x6FFFFFF7: "GNU_LIBLIST",
        0x6FFFFFF8: "CHECKSUM",
        0x6FFFFFFA: "LOSUNW",
        0x6FFFFFFA: "SUNW_move",
        0x6FFFFFFB: "SUNW_COMDAT",
        0x6FFFFFFC: "SUNW_syminfo",
        0x6FFFFFFD: "GNU_verdef",
        0x6FFFFFFE: "GNU_verneed",
        0x6FFFFFFF: "GNU_versym",
    }

    SHN_TYPE = {
        0: "SHN_UNDEF",  # Undefined section #
        0xFF00: "SHN_LORESERVE",  # Start of reserved indices #
        0xFF00: "SHN_LOPROC",  # Start of processor-specific #
        0xFF00: "SHN_BEFORE",  # Order section before all others #
        0xFF01: "SHN_AFTER",  # Order section after all others #
        0xFF1F: "SHN_HIPROC",  # End of processor-specific #
        0xFF20: "SHN_LOOS",  # Start of OS-specific #
        0xFF3F: "SHN_HIOS",  # End of OS-specific #
        0xFFF1: "SHN_ABS",  # Associated symbol is absolute #
        0xFFF2: "SHN_COMMON",  # Associated symbol is common #
        0xFFFF: "SHN_XINDEX",  # Index is in extra table. #
        0xFFFF: "SHN_HIRESERVE",  # End of reserved indices #
    }

    DT_TYPE = {
        0: "NULL",
        1: "NEEDED",
        2: "PLTRELSZ",
        3: "PLTGOT",
        4: "HASH",
        5: "STRTAB",
        6: "SYMTAB",
        7: "RELA",
        8: "RELASZ",
        9: "RELAENT",
        10: "STRSZ",
        11: "SYMENT",
        12: "INIT",
        13: "FINI",
        14: "SONAME",
        15: "RPATH",
        16: "SYMBOLIC",
        17: "REL",
        18: "RELSZ",
        19: "RELENT",
        20: "PLTREL",
        21: "DEBUG",
        22: "TEXTREL",
        23: "JMPREL",
        24: "BIND_NOW",
        25: "INIT_ARRAY",
        26: "FINI_ARRAY",
        27: "INIT_ARRAYSZ",
        28: "FINI_ARRAYSZ",
        29: "RUNPATH",
        30: "FLAGS",
        31: "ENCODING",
        32: "PREINIT_ARRAY",
        33: "PREINIT_ARRAYSZ",
        34: "NUM",
        0x36: "PROCNUM",
        0x60000000: "OLD_LOOS",
        0x6000000D: "LOOS",
        0x6FFFF000: "HIOS",
        0x70000000: "LOPROC",
        0x7FFFFFFF: "HIPROC",
        0x6FFFFD00: "VALRNGLO",
        0x6FFFFDF5: "GNU_PRELINKED",
        0x6FFFFDF6: "GNU_CONFLICTSZ",
        0x6FFFFDF7: "GNU_LIBLISTSZ",
        0x6FFFFDF8: "CHECKSUM",
        0x6FFFFDF9: "PLTPADSZ",
        0x6FFFFDFA: "MOVEENT",
        0x6FFFFDFB: "MOVESZ",
        0x6FFFFDFE: "SYMINSZ",
        0x6FFFFDFF: "SYMINENT",
        0x6FFFFEF5: "GNU_HASH",
        0x6FFFFEF6: "TLSDESC_PLT",
        0x6FFFFEF7: "TLSDESC_GOT",
        0x6FFFFEF8: "GNU_CONFLICT",
        0x6FFFFEF9: "GNU_LIBLIST",
        0x6FFFFEFA: "CONFIG",
        0x6FFFFEFB: "DEPAUDIT",
        0x6FFFFEFC: "AUDIT",
        0x6FFFFEFD: "PLTPAD",
        0x6FFFFEFE: "MOVETAB",
        0x6FFFFEFF: "SYMINFO",
        0x6FFFFFF0: "VERSYM",
        0x6FFFFFF9: "RELACOUNT",
        0x6FFFFFFA: "RELCOUNT",
        0x6FFFFFFB: "FLAGS_1",
        0x6FFFFFFC: "VERDEF",
        0x6FFFFFFD: "VERDEFNUM",
        0x6FFFFFFE: "VERNEED",
        0x6FFFFFFF: "VERNEEDNUM",
        0x7FFFFFFD: "AUXILIARY",
        0x7FFFFFFF: "FILTER",
    }

    DT_VERSYM = 0x6FFFFFF0
    DT_VERDEF = 0x6FFFFFFC
    DT_VERNEEDNUM = 0x6FFFFFFF

    EI_TYPE = {
        0: "None",
        1: "Relocatable",
        2: "Executable",
        3: "Shared-object",
        4: "Core",
        0xFF00: "Processor-specific",
        0xFFFF: "Processor-specific",
    }

    EI_OSABI = {
        0: "SYSV",
        1: "HPUX",
        2: "NETBSD",
        3: "LINUX",
        4: "HURD",
        6: "SOLARIS",
        7: "AIX",
        8: "IRIX",
        9: "FREEBSD",
        10: "TRU64",
        11: "MODESTO",
        12: "OPENBSD",
        13: "OPENVMS",
        14: "NSK",
        15: "AROS",
        16: "FENIXOS",
        17: "CLOUD",
        53: "SORTIX",
        64: "ARM_AEABI",
        97: "ARM",
        255: "STANDALONE",
    }

    DT_FLAGS = {
        0x1: "ORIGIN",
        0x2: "SYMBOLIC",
        0x4: "TEXTREL",
        0x8: "BIND_NOW",
        0x10: "STATIC_TLS",
    }

    DT_FLAGS_1 = {
        0x1: "NOW",
        0x2: "GLOBAL",
        0x4: "GROUP",
        0x8: "NODELETE",
        0x10: "LOADFLTR",
        0x20: "INITFIRST",
        0x40: "NOOPEN",
        0x80: "ORIGIN",
        0x100: "DIRECT",
        0x200: "TRANS",
        0x400: "INTERPOSE",
        0x800: "NODEFLIB",
        0x1000: "NODUMP",
        0x2000: "CONFALT",
        0x4000: "ENDFILTEE",
        0x8000: "DISPRELDNE",
        0x10000: "DISPRELPND",
        0x20000: "NODIRECT",
        0x40000: "IGNMULDEF",
        0x80000: "NOKSYMS",
        0x100000: "NOHDR",
        0x200000: "EDITED",
        0x400000: "NORELOC",
        0x800000: "SYMINTPOSE",
        0x1000000: "GLOBAUDIT",
        0x2000000: "SINGLETON",
        0x4000000: "STUB",
        0x8000000: "PIE",
    }

    RELOC_TYPE = {}

    RELOC_TYPE_x86 = {
        0: "R_386_NONE",
        1: "R_386_32",
        2: "R_386_PC32",
        3: "R_386_GOT32",
        4: "R_386_PLT32",
        5: "R_386_COPY",
        6: "R_386_GLOB_DAT",
        7: "R_386_JUMP_SLOT",
        8: "R_386_RELATIVE",
        9: "R_386_GOTOFF",
        10: "R_386_GOTPC",
        11: "R_386_32PLT",
        14: "R_386_TLS_TPOFF",
        15: "R_386_TLS_IE",
        16: "R_386_TLS_GOTIE",
        17: "R_386_TLS_LE",
        18: "R_386_TLS_GD",
        19: "R_386_TLS_LDM",
        20: "R_386_16",
        21: "R_386_PC16",
        22: "R_386_8",
        23: "R_386_PC8",
        24: "R_386_TLS_GD_32",
        25: "R_386_TLS_GD_PUSH",
        26: "R_386_TLS_GD_CALL",
        27: "R_386_TLS_GD_POP",
        28: "R_386_TLS_LDM_32",
        29: "R_386_TLS_LDM_PUSH",
        30: "R_386_TLS_LDM_CALL",
        31: "R_386_TLS_LDM_POP",
        32: "R_386_TLS_LDO_32",
        33: "R_386_TLS_IE_32",
        34: "R_386_TLS_LE_32",
        35: "R_386_TLS_DTPMOD32",
        36: "R_386_TLS_DTPOFF32",
        37: "R_386_TLS_TPOFF32",
        39: "R_386_TLS_GOTDESC",
        40: "R_386_TLS_DESC_CALL",
        41: "R_386_TLS_DESC",
        42: "R_386_IRELATIVE",
        200: "R_386_USED_BY_INTEL_200",
        250: "R_386_GNU_VTINHERIT",
        251: "R_386_GNU_VTENTRY",
    }

    RELOC_TYPE_x64 = {
        0: "R_X86_64_NONE",
        1: "R_X86_64_64",
        2: "R_X86_64_PC32",
        3: "R_X86_64_GOT32",
        4: "R_X86_64_PLT32",
        5: "R_X86_64_COPY",
        6: "R_X86_64_GLOB_DAT",
        7: "R_X86_64_JUMP_SLOT",
        8: "R_X86_64_RELATIVE",
        9: "R_X86_64_GOTPCREL",
        10: "R_X86_64_32",
        11: "R_X86_64_32S",
        12: "R_X86_64_16",
        13: "R_X86_64_PC16",
        14: "R_X86_64_8",
        15: "R_X86_64_PC8",
        16: "R_X86_64_DTPMOD64",
        17: "R_X86_64_DTPOFF64",
        18: "R_X86_64_TPOFF64",
        19: "R_X86_64_TLSGD",
        20: "R_X86_64_TLSLD",
        21: "R_X86_64_DTPOFF32",
        22: "R_X86_64_GOTTPOFF",
        23: "R_X86_64_TPOFF32",
        24: "R_X86_64_PC64",
        25: "R_X86_64_GOTOFF64",
        26: "R_X86_64_GOTPC32",
        27: "R_X86_64_GOT64",
        28: "R_X86_64_GOTPCREL64",
        29: "R_X86_64_GOTPC64",
        30: "R_X86_64_GOTPLT64",
        31: "R_X86_64_PLTOFF64",
        34: "R_X86_64_GOTPC32_TLSDESC",
        35: "R_X86_64_TLSDESC_CALL",
        36: "R_X86_64_TLSDESC",
        37: "R_X86_64_IRELATIVE",
        250: "R_X86_64_GNU_VTINHERIT",
        251: "R_X86_64_GNU_VTENTRY",
    }

    RELOC_TYPE_ARM = {
        0: "R_ARM_NONE",
        1: "R_ARM_PC24",
        2: "R_ARM_ABS32",
        3: "R_ARM_REL32",
        4: "R_ARM_LDR_PC_G0",
        5: "R_ARM_ABS16",
        6: "R_ARM_ABS12",
        7: "R_ARM_THM_ABS5",
        8: "R_ARM_ABS8",
        9: "R_ARM_SBREL32",
        10: "R_ARM_THM_CALL",
        11: "R_ARM_THM_PC8",
        12: "R_ARM_BREL_ADJ",
        13: "R_ARM_SWI24",
        14: "R_ARM_THM_SWI8",
        15: "R_ARM_XPC25",
        16: "R_ARM_THM_XPC22",
        17: "R_ARM_TLS_DTPMOD32",
        18: "R_ARM_TLS_DTPOFF32",
        19: "R_ARM_TLS_TPOFF32",
        20: "R_ARM_COPY",
        21: "R_ARM_GLOB_DAT",
        22: "R_ARM_JUMP_SLOT",
        23: "R_ARM_RELATIVE",
        24: "R_ARM_GOTOFF32",
        25: "R_ARM_BASE_PREL",
        26: "R_ARM_GOT_BREL",
        27: "R_ARM_PLT32",
        28: "R_ARM_CALL",
        29: "R_ARM_JUMP24",
        30: "R_ARM_THM_JUMP24",
        31: "R_ARM_BASE_ABS",
        32: "R_ARM_ALU_PCREL_7_0",
        33: "R_ARM_ALU_PCREL_15_8",
        34: "R_ARM_ALU_PCREL_23_15",
        35: "R_ARM_LDR_SBREL_11_0_NC",
        36: "R_ARM_ALU_SBREL_19_12_NC",
        37: "R_ARM_ALU_SBREL_27_20_CK",
        38: "R_ARM_TARGET1",
        39: "R_ARM_SBREL31",
        40: "R_ARM_V4BX",
        41: "R_ARM_TARGET2",
        42: "R_ARM_PREL31",
        43: "R_ARM_MOVW_ABS_NC",
        44: "R_ARM_MOVT_ABS",
        45: "R_ARM_MOVW_PREL_NC",
        46: "R_ARM_MOVT_PREL",
        47: "R_ARM_THM_MOVW_ABS_NC",
        48: "R_ARM_THM_MOVT_ABS",
        49: "R_ARM_THM_MOVW_PREL_NC",
        50: "R_ARM_THM_MOVT_PREL",
        51: "R_ARM_THM_JUMP19",
        52: "R_ARM_THM_JUMP6",
        53: "R_ARM_THM_ALU_PREL_11_0",
        54: "R_ARM_THM_PC12",
        55: "R_ARM_ABS32_NOI",
        56: "R_ARM_REL32_NOI",
        57: "R_ARM_ALU_PC_G0_NC",
        58: "R_ARM_ALU_PC_G0",
        59: "R_ARM_ALU_PC_G1_NC",
        60: "R_ARM_ALU_PC_G1",
        61: "R_ARM_ALU_PC_G2",
        62: "R_ARM_LDR_PC_G1",
        63: "R_ARM_LDR_PC_G2",
        64: "R_ARM_LDRS_PC_G0",
        65: "R_ARM_LDRS_PC_G1",
        66: "R_ARM_LDRS_PC_G2",
        67: "R_ARM_LDC_PC_G0",
        68: "R_ARM_LDC_PC_G1",
        69: "R_ARM_LDC_PC_G2",
        70: "R_ARM_ALU_SB_G0_NC",
        71: "R_ARM_ALU_SB_G0",
        72: "R_ARM_ALU_SB_G1_NC",
        73: "R_ARM_ALU_SB_G1",
        74: "R_ARM_ALU_SB_G2",
        75: "R_ARM_LDR_SB_G0",
        76: "R_ARM_LDR_SB_G1",
        77: "R_ARM_LDR_SB_G2",
        78: "R_ARM_LDRS_SB_G0",
        79: "R_ARM_LDRS_SB_G1",
        80: "R_ARM_LDRS_SB_G2",
        81: "R_ARM_LDC_SB_G0",
        82: "R_ARM_LDC_SB_G1",
        83: "R_ARM_LDC_SB_G2",
        84: "R_ARM_MOVW_BREL_NC",
        85: "R_ARM_MOVT_BREL",
        86: "R_ARM_MOVW_BREL",
        87: "R_ARM_THM_MOVW_BREL_NC",
        88: "R_ARM_THM_MOVT_BREL",
        89: "R_ARM_THM_MOVW_BREL",
        94: "R_ARM_PLT32_ABS",
        95: "R_ARM_GOT_ABS",
        96: "R_ARM_GOT_PREL",
        97: "R_ARM_GOT_BREL12",
        98: "R_ARM_GOTOFF12",
        99: "R_ARM_GOTRELAX",
        100: "R_ARM_GNU_VTENTRY",
        101: "R_ARM_GNU_VTINHERIT",
        102: "R_ARM_THM_JUMP11",
        103: "R_ARM_THM_JUMP8",
        104: "R_ARM_TLS_GD32",
        105: "R_ARM_TLS_LDM32",
        106: "R_ARM_TLS_LDO32",
        107: "R_ARM_TLS_IE32",
        108: "R_ARM_TLS_LE32",
        109: "R_ARM_TLS_LDO12",
        110: "R_ARM_TLS_LE12",
        111: "R_ARM_TLS_IE12GP",
        112: "R_ARM_PRIVATE_0",
        113: "R_ARM_PRIVATE_1",
        114: "R_ARM_PRIVATE_2",
        115: "R_ARM_PRIVATE_3",
        116: "R_ARM_PRIVATE_4",
        117: "R_ARM_PRIVATE_5",
        118: "R_ARM_PRIVATE_6",
        119: "R_ARM_PRIVATE_7",
        120: "R_ARM_PRIVATE_8",
        121: "R_ARM_PRIVATE_9",
        122: "R_ARM_PRIVATE_10",
        123: "R_ARM_PRIVATE_11",
        124: "R_ARM_PRIVATE_12",
        125: "R_ARM_PRIVATE_13",
        126: "R_ARM_PRIVATE_14",
        127: "R_ARM_PRIVATE_15",
        128: "R_ARM_ME_TOO",
        129: "R_ARM_THM_TLS_DESCSEQ16",
        130: "R_ARM_THM_TLS_DESCSEQ32",
        131: "R_ARM_THM_GOT_BREL12",
        140: "R_ARM_IRELATIVE",
    }

    RELOC_TYPE_AARCH64 = {
        256: "R_AARCH64_NONE",
        257: "R_AARCH64_ABS64",
        258: "R_AARCH64_ABS32",
        259: "R_AARCH64_ABS16",
        260: "R_AARCH64_PREL64",
        261: "R_AARCH64_PREL32",
        262: "R_AARCH64_PREL16",
        263: "R_AARCH64_MOVW_UABS_G0",
        264: "R_AARCH64_MOVW_UABS_G0_NC",
        265: "R_AARCH64_MOVW_UABS_G1",
        266: "R_AARCH64_MOVW_UABS_G1_NC",
        267: "R_AARCH64_MOVW_UABS_G2",
        268: "R_AARCH64_MOVW_UABS_G2_NC",
        269: "R_AARCH64_MOVW_UABS_G3",
        270: "R_AARCH64_MOVW_SABS_G0",
        271: "R_AARCH64_MOVW_SABS_G1",
        272: "R_AARCH64_MOVW_SABS_G2",
        273: "R_AARCH64_LD_PREL_LO19",
        274: "R_AARCH64_ADR_PREL_LO21",
        275: "R_AARCH64_ADR_PREL_PG_HI21",
        276: "R_AARCH64_ADR_PREL_PG_HI21_NC",
        277: "R_AARCH64_ADD_ABS_LO12_NC",
        278: "R_AARCH64_LDST8_ABS_LO12_NC",
        279: "R_AARCH64_TSTBR14",
        280: "R_AARCH64_CONDBR19",
        282: "R_AARCH64_JUMP26",
        283: "R_AARCH64_CALL26",
        284: "R_AARCH64_LDST16_ABS_LO12_NC",
        285: "R_AARCH64_LDST32_ABS_LO12_NC",
        286: "R_AARCH64_LDST64_ABS_LO12_NC",
        287: "R_AARCH64_MOVW_PREL_G0",
        288: "R_AARCH64_MOVW_PREL_G0_NC",
        289: "R_AARCH64_MOVW_PREL_G1",
        290: "R_AARCH64_MOVW_PREL_G1_NC",
        291: "R_AARCH64_MOVW_PREL_G2",
        292: "R_AARCH64_MOVW_PREL_G2_NC",
        293: "R_AARCH64_MOVW_PREL_G3",
        300: "R_AARCH64_MOVW_GOTOFF_G0",
        301: "R_AARCH64_MOVW_GOTOFF_G0_NC",
        302: "R_AARCH64_MOVW_GOTOFF_G1",
        303: "R_AARCH64_MOVW_GOTOFF_G1_NC",
        304: "R_AARCH64_MOVW_GOTOFF_G2",
        305: "R_AARCH64_MOVW_GOTOFF_G2_NC",
        306: "R_AARCH64_MOVW_GOTOFF_G3",
        307: "R_AARCH64_GOTREL64",
        308: "R_AARCH64_GOTREL32",
        309: "R_AARCH64_GOT_LD_PREL19",
        310: "R_AARCH64_LD64_GOTOFF_LO15",
        311: "R_AARCH64_ADR_GOT_PAGE",
        312: "R_AARCH64_LD64_GOT_LO12_NC",
        512: "R_AARCH64_TLSGD_ADR_PREL21",
        513: "R_AARCH64_TLSGD_ADR_PAGE21",
        514: "R_AARCH64_TLSGD_ADD_LO12_NC",
        515: "R_AARCH64_TLSGD_MOVW_G1",
        516: "R_AARCH64_TLSGD_MOVW_G0_NC",
        517: "R_AARCH64_TLSLD_ADR_PREL21",
        518: "R_AARCH64_TLSLD_ADR_PAGE21",
        519: "R_AARCH64_TLSLD_ADD_LO12_NC",
        520: "R_AARCH64_TLSLD_MOVW_G1",
        521: "R_AARCH64_TLSLD_MOVW_G0_NC",
        522: "R_AARCH64_TLSLD_LD_PREL19",
        523: "R_AARCH64_TLSLD_MOVW_DTPREL_G2",
        524: "R_AARCH64_TLSLD_MOVW_DTPREL_G1",
        525: "R_AARCH64_TLSLD_MOVW_DTPREL_G1_NC",
        526: "R_AARCH64_TLSLD_MOVW_DTPREL_G0",
        527: "R_AARCH64_TLSLD_MOVW_DTPREL_G0_NC",
        528: "R_AARCH64_TLSLD_ADD_DTPREL_HI12",
        529: "R_AARCH64_TLSLD_ADD_DTPREL_LO12",
        530: "R_AARCH64_TLSLD_ADD_DTPREL_LO12_NC",
        531: "R_AARCH64_TLSLD_LDST8_DTPREL_LO12",
        532: "R_AARCH64_TLSLD_LDST8_DTPREL_LO12_NC",
        533: "R_AARCH64_TLSLD_LDST16_DTPREL_LO12",
        534: "R_AARCH64_TLSLD_LDST16_DTPREL_LO12_NC",
        535: "R_AARCH64_TLSLD_LDST32_DTPREL_LO12",
        536: "R_AARCH64_TLSLD_LDST32_DTPREL_LO12_NC",
        537: "R_AARCH64_TLSLD_LDST64_DTPREL_LO12",
        538: "R_AARCH64_TLSLD_LDST64_DTPREL_LO12_NC",
        539: "R_AARCH64_TLSIE_MOVW_GOTTPREL_G1",
        540: "R_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC",
        541: "R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21",
        542: "R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC",
        543: "R_AARCH64_TLSIE_LD_GOTTPREL_PREL19",
        544: "R_AARCH64_TLSLE_MOVW_TPREL_G2",
        545: "R_AARCH64_TLSLE_MOVW_TPREL_G1",
        546: "R_AARCH64_TLSLE_MOVW_TPREL_G1_NC",
        547: "R_AARCH64_TLSLE_MOVW_TPREL_G0",
        548: "R_AARCH64_TLSLE_MOVW_TPREL_G0_NC",
        549: "R_AARCH64_TLSLE_ADD_TPREL_HI12",
        550: "R_AARCH64_TLSLE_ADD_TPREL_LO12",
        551: "R_AARCH64_TLSLE_ADD_TPREL_LO12_NC",
        552: "R_AARCH64_TLSLE_LDST8_TPREL_LO12",
        553: "R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC",
        554: "R_AARCH64_TLSLE_LDST16_TPREL_LO12",
        555: "R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC",
        556: "R_AARCH64_TLSLE_LDST32_TPREL_LO12",
        557: "R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC",
        558: "R_AARCH64_TLSLE_LDST64_TPREL_LO12",
        559: "R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC",
        1024: "R_AARCH64_COPY",
        1025: "R_AARCH64_GLOB_DAT",
        1026: "R_AARCH64_JUMP_SLOT",
        1027: "R_AARCH64_RELATIVE",
        1028: "R_AARCH64_TLS_DTPREL64",
        1029: "R_AARCH64_TLS_DTPMOD64",
        1030: "R_AARCH64_TLS_TPREL64",
        1031: "R_AARCH64_TLS_DTPREL32",
        1032: "R_AARCH64_TLS_DTPMOD32",
        1033: "R_AARCH64_TLS_TPREL32",
    }

    RELOC_TYPE_RISCV64 = {
        0: "R_RISCV_NONE",
        1: "R_RISCV_32",
        2: "R_RISCV_64",
        3: "R_RISCV_RELATIVE",
        4: "R_RISCV_COPY",
        5: "R_RISCV_JUMP_SLOT",
        6: "R_RISCV_TLS_DTPMOD32",
        7: "R_RISCV_TLS_DTPMOD64",
        8: "R_RISCV_TLS_DTPREL32",
        9: "R_RISCV_TLS_DTPREL64",
        10: "R_RISCV_TLS_TPREL32",
        11: "R_RISCV_TLS_TPREL64",
        16: "R_RISCV_BRANCH",
        17: "R_RISCV_JAL",
        18: "R_RISCV_CALL",
        19: "R_RISCV_CALL_PLT",
        20: "R_RISCV_GOT_HI20",
        21: "R_RISCV_TLS_GOT_HI20",
        22: "R_RISCV_TLS_GD_HI20",
        23: "R_RISCV_PCREL_HI20",
        24: "R_RISCV_PCREL_LO12_I",
        25: "R_RISCV_PCREL_LO12_S",
        26: "R_RISCV_HI20",
        27: "R_RISCV_LO12_I",
        28: "R_RISCV_LO12_S",
        29: "R_RISCV_TPREL_HI20",
        30: "R_RISCV_TPREL_LO12_I",
        31: "R_RISCV_TPREL_LO12_S",
        32: "R_RISCV_TPREL_ADD",
        33: "R_RISCV_ADD8",
        34: "R_RISCV_ADD16",
        35: "R_RISCV_ADD32",
        36: "R_RISCV_ADD64",
        37: "R_RISCV_SUB8",
        38: "R_RISCV_SUB16",
        39: "R_RISCV_SUB32",
        40: "R_RISCV_SUB64",
        41: "R_RISCV_GNU_VTINHERIT",
        42: "R_RISCV_GNU_VTENTRY",
        43: "R_RISCV_ALIGN",
        44: "R_RISCV_RVC_BRANCH",
        45: "R_RISCV_RVC_JUMP",
        46: "R_RISCV_RVC_LUI",
        47: "R_RISCV_GPREL_I",
        48: "R_RISCV_GPREL_S",
        49: "R_RISCV_TPREL_I",
        50: "R_RISCV_TPREL_S",
        51: "R_RISCV_RELAX",
        52: "R_RISCV_SUB6",
        53: "R_RISCV_SET6",
        54: "R_RISCV_SET8",
        55: "R_RISCV_SET1",
        56: "R_RISCV_SET32",
        57: "R_RISCV_32_PCREL",
    }

    EI_MACHINE_TYPE = {
        0: "no machine",
        1: "AT&T WE 32100",
        2: "SPARC",
        3: "Intel 80386",
        4: "Motorola 68000",
        5: "Motorola 88000",
        6: "Intel MCU",
        7: "Intel 80860",
        8: "MIPS I Architecture",
        9: "IBM System/370 Processor",
        10: "MIPS RS3000 Little-endian",
        11 - 14: "Reserved for future use",
        15: "Hewlett-Packard PA-RISC",
        16: "Reserved for future use",
        17: "Fujitsu VPP500",
        18: "Enhanced instruction set SPARC",
        19: "Intel 80960",
        20: "PowerPC",
        21: "64-bit PowerPC",
        22: "IBM System/390 Processor",
        23: "IBM SPU/SPC",
        24 - 35: "Reserved for future use",
        36: "NEC V800",
        37: "Fujitsu FR20",
        38: "TRW RH-32",
        39: "Motorola RCE",
        40: "ARM 32-bit architecture (AARCH32)",
        41: "Digital Alpha",
        42: "Hitachi SH",
        43: "SPARC Version 9",
        44: "Siemens TriCore embedded processor",
        45: "Argonaut RISC Core, Argonaut Technologies Inc.",
        46: "Hitachi H8/300",
        47: "Hitachi H8/300H",
        48: "Hitachi H8S",
        49: "Hitachi H8/500",
        50: "Intel IA-64 processor architecture",
        51: "Stanford MIPS-X",
        52: "Motorola ColdFire",
        53: "Motorola M68HC12",
        54: "Fujitsu MMA Multimedia Accelerator",
        55: "Siemens PCP",
        56: "Sony nCPU embedded RISC processor",
        57: "Denso NDR1 microprocessor",
        58: "Motorola Star*Core processor",
        59: "Toyota ME16 processor",
        60: "STMicroelectronics ST100 processor",
        61: "Advanced Logic Corp. TinyJ embedded processor family",
        62: "AMD x86-64 architecture",
        63: "Sony DSP Processor",
        64: "Digital Equipment Corp. PDP-10",
        65: "Digital Equipment Corp. PDP-11",
        66: "Siemens FX66 microcontroller",
        67: "STMicroelectronics ST9+ 8/16 bit microcontroller",
        68: "STMicroelectronics ST7 8-bit microcontroller",
        69: "Motorola MC68HC16 Microcontroller",
        70: "Motorola MC68HC11 Microcontroller",
        71: "Motorola MC68HC08 Microcontroller",
        72: "Motorola MC68HC05 Microcontroller",
        73: "Silicon Graphics SVx",
        74: "STMicroelectronics ST19 8-bit microcontroller",
        75: "Digital VAX",
        76: "Axis Communications 32-bit embedded processor",
        77: "Infineon Technologies 32-bit embedded processor",
        78: "Element 14 64-bit DSP Processor",
        79: "LSI Logic 16-bit DSP Processor",
        80: "Donald Knuth's educational 64-bit processor",
        81: "Harvard University machine-independent object files",
        82: "SiTera Prism",
        83: "Atmel AVR 8-bit microcontroller",
        84: "Fujitsu FR30",
        85: "Mitsubishi D10V",
        86: "Mitsubishi D30V",
        87: "NEC v850",
        88: "Mitsubishi M32R",
        89: "Matsushita MN10300",
        90: "Matsushita MN10200",
        91: "picoJava",
        92: "OpenRISC 32-bit embedded processor",
        93: "ARC International ARCompact processor (old spelling/synonym: EM_ARC_A5)",
        94: "Tensilica Xtensa Architecture",
        95: "Alphamosaic VideoCore processor",
        96: "Thompson Multimedia General Purpose Processor",
        97: "National Semiconductor 32000 series",
        98: "Tenor Network TPC processor",
        99: "Trebia SNP 1000 processor",
        100: "STMicroelectronics (www.st.com) ST200 microcontroller",
        101: "Ubicom IP2xxx microcontroller family",
        102: "MAX Processor",
        103: "National Semiconductor CompactRISC microprocessor",
        104: "Fujitsu F2MC16",
        105: "Texas Instruments embedded microcontroller msp430",
        106: "Analog Devices Blackfin (DSP) processor",
        107: "S1C33 Family of Seiko Epson processors",
        108: "Sharp embedded microprocessor",
        109: "Arca RISC Microprocessor",
        110: "Microprocessor series from PKU-Unity Ltd. and MPRC of Peking University",
        111: "eXcess: 16/32/64-bit configurable embedded CPU",
        112: "Icera Semiconductor Inc. Deep Execution Processor",
        113: "Altera Nios II soft-core processor",
        114: "National Semiconductor CompactRISC CRX microprocessor",
        115: "Motorola XGATE embedded processor",
        116: "Infineon C16x/XC16x processor",
        117: "Renesas M16C series microprocessors",
        118: "Microchip Technology dsPIC30F Digital Signal Controller",
        119: "Freescale Communication Engine RISC core",
        120: "Renesas M32C series microprocessors",
        121 - 130: "Reserved for future use",
        131: "Altium TSK3000 core",
        132: "Freescale RS08 embedded processor",
        133: "Analog Devices SHARC family of 32-bit DSP processors",
        134: "Cyan Technology eCOG2 microprocessor",
        135: "Sunplus S+core7 RISC processor",
        136: "New Japan Radio (NJR) 24-bit DSP Processor",
        137: "Broadcom VideoCore III processor",
        138: "RISC processor for Lattice FPGA architecture",
        139: "Seiko Epson C17 family",
        140: "The Texas Instruments TMS320C6000 DSP family",
        141: "The Texas Instruments TMS320C2000 DSP family",
        142: "The Texas Instruments TMS320C55x DSP family",
        143: "Texas Instruments Application Specific RISC Processor, 32bit fetch",
        144: "Texas Instruments Programmable Realtime Unit",
        145 - 159: "Reserved for future use",
        160: "",
        145 - 159: "Reserved for future use",
        160: "STMicroelectronics 64bit VLIW Data Signal Processor",
        161: "Cypress M8C microprocessor",
        162: "Renesas R32C series microprocessors",
        163: "NXP Semiconductors TriMedia architecture family",
        164: "QUALCOMM DSP6 Processor",
        165: "Intel 8051 and variants",
        166: "STMicroelectronics STxP7x family of configurable and extensible RISC processors",
        167: "Andes Technology compact code size embedded RISC processor family",
        168: "Cyan Technology eCOG1X family",
        168: "Cyan Technology eCOG1X family",
        169: "Dallas Semiconductor MAXQ30 Core Micro-controllers",
        170: "New Japan Radio (NJR) 16-bit DSP Processor",
        171: "M2000 Reconfigurable RISC Microprocessor",
        172: "Cray Inc. NV2 vector architecture",
        173: "Renesas RX family",
        174: "Imagination Technologies META processor architecture",
        175: "MCST Elbrus general purpose hardware architecture",
        176: "Cyan Technology eCOG16 family",
        177: "National Semiconductor CompactRISC CR16 16-bit microprocessor",
        178: "Freescale Extended Time Processing Unit",
        179: "Infineon Technologies SLE9X core",
        180: "Intel L10M",
        181: "Intel K10M",
        182: "Reserved for future Intel use",
        183: "ARM 64-bit architecture (AARCH64)",
        184: "Reserved for future ARM use",
        185: "Atmel Corporation 32-bit microprocessor family",
        186: "STMicroeletronics STM8 8-bit microcontroller",
        187: "Tilera TILE64 multicore architecture family",
        188: "Tilera TILEPro multicore architecture family",
        189: "Xilinx MicroBlaze 32-bit RISC soft processor core",
        190: "NVIDIA CUDA architecture",
        191: "Tilera TILE-Gx multicore architecture family",
        192: "CloudShield architecture family",
        193: "KIPO-KAIST Core-A 1st generation processor family",
        194: "KIPO-KAIST Core-A 2nd generation processor family",
        195: "Synopsys ARCompact V2",
        196: "Open8 8-bit RISC soft processor core",
        197: "Renesas RL78 family",
        198: "Broadcom VideoCore V processor",
        199: "Renesas 78KOR family",
        200: "Freescale 56800EX Digital Signal Controller (DSC)",
        201: "Beyond BA1 CPU architecture",
        202: "Beyond BA2 CPU architecture",
        203: "XMOS xCORE processor family",
        204: "Microchip 8-bit PIC(r) family",
        205: "Reserved by Intel",
        206: "Reserved by Intel",
        207: "Reserved by Intel",
        208: "Reserved by Intel",
        209: "Reserved by Intel",
        210: "KM211 KM32 32-bit processor",
        211: "KM211 KMX32 32-bit processor",
        212: "KM211 KMX16 16-bit processor",
        213: "KM211 KMX8 8-bit processor",
        214: "KM211 KVARC processor",
        215: "Paneve CDP architecture family",
        216: "Cognitive Smart Memory Processor",
        217: "Bluechip Systems CoolEngine",
        218: "Nanoradio Optimized RISC",
        219: "CSR Kalimba architecture family",
        220: "Zilog Z80",
        221: "Controls and Data Services VISIUMcore processor",
        222: "FTDI Chip FT32 high performance 32-bit RISC architecture",
        223: "Moxie processor family",
        224: "AMD GPU architecture",
        243: "RISC-V",
    }

    EI_OSABI = {
        0: "UNIX - System V",
        1: "UNIX - HP-UX",
        2: "UNIX - NetBSD",
        3: "UNIX - Linux",
        4: "UNIX - GNU/Hurd",
        6: "UNIX - Solaris",
        7: "UNIX - AIX",
        8: "UNIX - IRIX",
        9: "UNIX - FreeBSD",
        10: "UNIX - TRU64",
        11: "Novell - Modesto",
        12: "UNIX - OpenBSD",
        13: "VMS - OpenVMS",
        14: "HP - Non-Stop Kernel",
        15: "AROS",
        16: "Fenix OS",
        17: "Nuxi - CloudABI",
        53: "Sortix",
        64: "ARM - EABI",
        97: "ARM - ABI",
        102: "CellOS Lv-2",
        255: "Standalone App",
    }

    """
    python elftools constants
    refer to https://github.com/eliben/pyelftools

    Note that the first 3 instructions have the so-called "primary opcode"
    (as described in DWARFv3 7.23), so only their highest 2 bits take part
    in the opcode decoding. They are kept as constants with the low bits masked
    out, and the callframe module knows how to handle this.
    The other instructions use an "extended opcode" encoded just in the low 6
    bits, with the high 2 bits, so these constants are exactly as they would
    appear in an actual file.
    """
    DW_INL = {
        "DW_INL_not_inlined": 0,
        "DW_INL_inlined": 1,
        "DW_INL_declared_not_inlined": 2,
        "DW_INL_declared_inlined": 3,
    }
    DW_INL_MAP = {v: k for k, v in DW_INL.items()}

    DW_LANG = {
        "DW_LANG_C89": 0x0001,
        "DW_LANG_C": 0x0002,
        "DW_LANG_Ada83": 0x0003,
        "DW_LANG_C_plus_plus": 0x0004,
        "DW_LANG_Cobol74": 0x0005,
        "DW_LANG_Cobol85": 0x0006,
        "DW_LANG_Fortran77": 0x0007,
        "DW_LANG_Fortran90": 0x0008,
        "DW_LANG_Pascal83": 0x0009,
        "DW_LANG_Modula2": 0x000A,
        "DW_LANG_Java": 0x000B,
        "DW_LANG_C99": 0x000C,
        "DW_LANG_Ada95": 0x000D,
        "DW_LANG_Fortran95": 0x000E,
        "DW_LANG_PLI": 0x000F,
        "DW_LANG_ObjC": 0x0010,
        "DW_LANG_ObjC_plus_plus": 0x0011,
        "DW_LANG_UPC": 0x0012,
        "DW_LANG_D": 0x0013,
        "DW_LANG_Python": 0x0014,
        "DW_LANG_OpenCL": 0x0015,
        "DW_LANG_Go": 0x0016,
        "DW_LANG_Modula3": 0x0017,
        "DW_LANG_Haskell": 0x0018,
        "DW_LANG_C_plus_plus_03": 0x0019,
        "DW_LANG_C_plus_plus_11": 0x001A,
        "DW_LANG_OCaml": 0x001B,
        "DW_LANG_Rust": 0x001C,
        "DW_LANG_C11": 0x001D,
        "DW_LANG_Swift": 0x001E,
        "DW_LANG_Julia": 0x001F,
        "DW_LANG_Dylan": 0x0020,
        "DW_LANG_C_plus_plus_14": 0x0021,
        "DW_LANG_Fortran03": 0x0022,
        "DW_LANG_Fortran08": 0x0023,
        "DW_LANG_RenderScript": 0x0024,
        "DW_LANG_BLISS": 0x0025,
        "DW_LANG_Mips_Assembler": 0x8001,
        "DW_LANG_Upc": 0x8765,
        "DW_LANG_HP_Bliss": 0x8003,
        "DW_LANG_HP_Basic91": 0x8004,
        "DW_LANG_HP_Pascal91": 0x8005,
        "DW_LANG_HP_IMacro": 0x8006,
        "DW_LANG_HP_Assembler": 0x8007,
        "DW_LANG_GOOGLE_RenderScript": 0x8E57,
        "DW_LANG_BORLAND_Delphi": 0xB000,
    }
    DW_LANG_MAP = {v: k for k, v in DW_LANG.items()}

    DW_ATE = {
        "DW_ATE_void": 0x0,
        "DW_ATE_address": 0x1,
        "DW_ATE_boolean": 0x2,
        "DW_ATE_complex_float": 0x3,
        "DW_ATE_float": 0x4,
        "DW_ATE_signed": 0x5,
        "DW_ATE_signed_char": 0x6,
        "DW_ATE_unsigned": 0x7,
        "DW_ATE_unsigned_char": 0x8,
        "DW_ATE_imaginary_float": 0x9,
        "DW_ATE_packed_decimal": 0xA,
        "DW_ATE_numeric_string": 0xB,
        "DW_ATE_edited": 0xC,
        "DW_ATE_signed_fixed": 0xD,
        "DW_ATE_unsigned_fixed": 0xE,
        "DW_ATE_decimal_float": 0xF,
        "DW_ATE_UTF": 0x10,
        "DW_ATE_UCS": 0x11,
        "DW_ATE_ASCII": 0x12,
        "DW_ATE_lo_user": 0x80,
        "DW_ATE_hi_user": 0xFF,
        "DW_ATE_HP_float80": 0x80,
        "DW_ATE_HP_complex_float80": 0x81,
        "DW_ATE_HP_float128": 0x82,
        "DW_ATE_HP_complex_float128": 0x83,
        "DW_ATE_HP_floathpintel": 0x84,
        "DW_ATE_HP_imaginary_float80": 0x85,
        "DW_ATE_HP_imaginary_float128": 0x86,
    }
    DW_ATE_MAP = {v: k for k, v in DW_ATE.items()}

    DW_ACCESS = {
        "DW_ACCESS_public": 1,
        "DW_ACCESS_protected": 2,
        "DW_ACCESS_private": 3,
    }
    DW_ACCESS_MAP = {v: k for k, v in DW_ACCESS.items()}

    DW_VIS = {"DW_VIS_local": 1, "DW_VIS_exported": 2, "DW_VIS_qualified": 3}
    DW_VIS_MAP = {v: k for k, v in DW_VIS.items()}

    DW_VIRTUALITY = {
        "DW_VIRTUALITY_none": 0,
        "DW_VIRTUALITY_virtual": 1,
        "DW_VIRTUALITY_pure_virtual": 2,
    }
    DW_VIRTUALITY_MAP = {v: k for k, v in DW_VIRTUALITY.items()}

    DW_ID = {
        "DW_ID_case_sensitive": 0,
        "DW_ID_up_case": 1,
        "DW_ID_down_case": 2,
        "DW_ID_case_insensitive": 3,
    }
    DW_ID_MAP = {v: k for k, v in DW_ID.items()}

    DW_CC = {"DW_CC_normal": 0x1, "DW_CC_program": 0x2, "DW_CC_nocall": 0x3}
    DW_CC_MAP = {v: k for k, v in DW_CC.items()}

    DW_ORD = {"DW_ORD_row_major": 0, "DW_ORD_col_major": 1}
    DW_ORD_MAP = {v: k for k, v in DW_ORD.items()}

    DW_LNS = {
        "DW_LNS_copy": 0x01,
        "DW_LNS_advance_pc": 0x02,
        "DW_LNS_advance_line": 0x03,
        "DW_LNS_set_file": 0x04,
        "DW_LNS_set_column": 0x05,
        "DW_LNS_negate_stmt": 0x06,
        "DW_LNS_set_basic_block": 0x07,
        "DW_LNS_const_add_pc": 0x08,
        "DW_LNS_fixed_advance_pc": 0x09,
        "DW_LNS_set_prologue_end": 0x0A,
        "DW_LNS_set_epilogue_begin": 0x0B,
        "DW_LNS_set_isa": 0x0C,
    }
    DW_LNS_MAP = {v: k for k, v in DW_LNS.items()}

    DW_LNE = {
        "DW_LNE_end_sequence": 0x01,
        "DW_LNE_set_address": 0x02,
        "DW_LNE_define_file": 0x03,
        "DW_LNE_set_discriminator": 0x04,
        "DW_LNE_lo_user": 0x80,
        "DW_LNE_hi_user": 0xFF,
    }
    DW_LNE_MAP = {v: k for k, v in DW_LNE.items()}

    DW_AT = {
        "DW_AT_null": 0x00,
        "DW_AT_sibling": 0x01,
        "DW_AT_location": 0x02,
        "DW_AT_name": 0x03,
        "DW_AT_ordering": 0x09,
        "DW_AT_subscr_data": 0x0A,
        "DW_AT_byte_size": 0x0B,
        "DW_AT_bit_offset": 0x0C,
        "DW_AT_bit_size": 0x0D,
        "DW_AT_element_list": 0x0F,
        "DW_AT_stmt_list": 0x10,
        "DW_AT_low_pc": 0x11,
        "DW_AT_high_pc": 0x12,
        "DW_AT_language": 0x13,
        "DW_AT_member": 0x14,
        "DW_AT_discr": 0x15,
        "DW_AT_discr_value": 0x16,
        "DW_AT_visibility": 0x17,
        "DW_AT_import": 0x18,
        "DW_AT_string_length": 0x19,
        "DW_AT_common_reference": 0x1A,
        "DW_AT_comp_dir": 0x1B,
        "DW_AT_const_value": 0x1C,
        "DW_AT_containing_type": 0x1D,
        "DW_AT_default_value": 0x1E,
        "DW_AT_inline": 0x20,
        "DW_AT_is_optional": 0x21,
        "DW_AT_lower_bound": 0x22,
        "DW_AT_producer": 0x25,
        "DW_AT_prototyped": 0x27,
        "DW_AT_return_addr": 0x2A,
        "DW_AT_start_scope": 0x2C,
        "DW_AT_bit_stride": 0x2E,
        "DW_AT_stride_size": 0x2E,
        "DW_AT_upper_bound": 0x2F,
        "DW_AT_abstract_origin": 0x31,
        "DW_AT_accessibility": 0x32,
        "DW_AT_address_class": 0x33,
        "DW_AT_artificial": 0x34,
        "DW_AT_base_types": 0x35,
        "DW_AT_calling_convention": 0x36,
        "DW_AT_count": 0x37,
        "DW_AT_data_member_location": 0x38,
        "DW_AT_decl_column": 0x39,
        "DW_AT_decl_file": 0x3A,
        "DW_AT_decl_line": 0x3B,
        "DW_AT_declaration": 0x3C,
        "DW_AT_discr_list": 0x3D,
        "DW_AT_encoding": 0x3E,
        "DW_AT_external": 0x3F,
        "DW_AT_frame_base": 0x40,
        "DW_AT_friend": 0x41,
        "DW_AT_identifier_case": 0x42,
        "DW_AT_macro_info": 0x43,
        "DW_AT_namelist_item": 0x44,
        "DW_AT_priority": 0x45,
        "DW_AT_segment": 0x46,
        "DW_AT_specification": 0x47,
        "DW_AT_static_link": 0x48,
        "DW_AT_type": 0x49,
        "DW_AT_use_location": 0x4A,
        "DW_AT_variable_parameter": 0x4B,
        "DW_AT_virtuality": 0x4C,
        "DW_AT_vtable_elem_location": 0x4D,
        "DW_AT_allocated": 0x4E,
        "DW_AT_associated": 0x4F,
        "DW_AT_data_location": 0x50,
        "DW_AT_byte_stride": 0x51,
        "DW_AT_stride": 0x51,
        "DW_AT_entry_pc": 0x52,
        "DW_AT_use_UTF8": 0x53,
        "DW_AT_extension": 0x54,
        "DW_AT_ranges": 0x55,
        "DW_AT_trampoline": 0x56,
        "DW_AT_call_column": 0x57,
        "DW_AT_call_file": 0x58,
        "DW_AT_call_line": 0x59,
        "DW_AT_description": 0x5A,
        "DW_AT_binary_scale": 0x5B,
        "DW_AT_decimal_scale": 0x5C,
        "DW_AT_small": 0x5D,
        "DW_AT_decimal_sign": 0x5E,
        "DW_AT_digit_count": 0x5F,
        "DW_AT_picture_string": 0x60,
        "DW_AT_mutable": 0x61,
        "DW_AT_threads_scaled": 0x62,
        "DW_AT_explicit": 0x63,
        "DW_AT_object_pointer": 0x64,
        "DW_AT_endianity": 0x65,
        "DW_AT_elemental": 0x66,
        "DW_AT_pure": 0x67,
        "DW_AT_recursive": 0x68,
        "DW_AT_signature": 0x69,
        "DW_AT_main_subprogram": 0x6A,
        "DW_AT_data_bit_offset": 0x6B,
        "DW_AT_const_expr": 0x6C,
        "DW_AT_enum_class": 0x6D,
        "DW_AT_linkage_name": 0x6E,
        "DW_AT_string_length_bit_size": 0x6F,
        "DW_AT_string_length_byte_size": 0x70,
        "DW_AT_rank": 0x71,
        "DW_AT_str_offsets_base": 0x72,
        "DW_AT_addr_base": 0x73,
        "DW_AT_rnglists_base": 0x74,
        "DW_AT_dwo_name": 0x76,
        "DW_AT_reference": 0x77,
        "DW_AT_rvalue_reference": 0x78,
        "DW_AT_macros": 0x79,
        "DW_AT_call_all_calls": 0x7A,
        "DW_AT_call_all_source_calls": 0x7B,
        "DW_AT_call_all_tail_calls": 0x7C,
        "DW_AT_call_return_pc": 0x7D,
        "DW_AT_call_value": 0x7E,
        "DW_AT_call_origin": 0x7F,
        "DW_AT_call_parameter": 0x80,
        "DW_AT_call_pc": 0x81,
        "DW_AT_call_tail_call": 0x82,
        "DW_AT_call_target": 0x83,
        "DW_AT_call_target_clobbered": 0x84,
        "DW_AT_call_data_location": 0x85,
        "DW_AT_call_data_value": 0x86,
        "DW_AT_noreturn": 0x87,
        "DW_AT_alignment": 0x88,
        "DW_AT_export_symbols": 0x89,
        "DW_AT_deleted": 0x8A,
        "DW_AT_defaulted": 0x8B,
        "DW_AT_loclists_base": 0x8C,
        "DW_AT_MIPS_fde": 0x2001,
        "DW_AT_MIPS_loop_begin": 0x2002,
        "DW_AT_MIPS_tail_loop_begin": 0x2003,
        "DW_AT_MIPS_epilog_begin": 0x2004,
        "DW_AT_MIPS_loop_unroll_factor": 0x2005,
        "DW_AT_MIPS_software_pipeline_depth": 0x2006,
        "DW_AT_MIPS_linkage_name": 0x2007,
        "DW_AT_MIPS_stride": 0x2008,
        "DW_AT_MIPS_abstract_name": 0x2009,
        "DW_AT_MIPS_clone_origin": 0x200A,
        "DW_AT_MIPS_has_inlines": 0x200B,
        "DW_AT_MIPS_stride_byte": 0x200C,
        "DW_AT_MIPS_stride_elem": 0x200D,
        "DW_AT_MIPS_ptr_dopetype": 0x200E,
        "DW_AT_MIPS_allocatable_dopetype": 0x200F,
        "DW_AT_MIPS_assumed_shape_dopetype": 0x2010,
        "DW_AT_MIPS_assumed_size": 0x2011,
        "DW_AT_sf_names": 0x2101,
        "DW_AT_src_info": 0x2102,
        "DW_AT_mac_info": 0x2103,
        "DW_AT_src_coords": 0x2104,
        "DW_AT_body_begin": 0x2105,
        "DW_AT_body_end": 0x2106,
        "DW_AT_GNU_vector": 0x2107,
        "DW_AT_GNU_template_name": 0x2110,
        "DW_AT_GNU_odr_signature": 0x210F,
        "DW_AT_GNU_call_site_value": 0x2111,
        "DW_AT_GNU_call_site_data_value": 0x2112,
        "DW_AT_GNU_call_site_target": 0x2113,
        "DW_AT_GNU_call_site_target_clobbered": 0x2114,
        "DW_AT_GNU_tail_call": 0x2115,
        "DW_AT_GNU_all_tail_call_sites": 0x2116,
        "DW_AT_GNU_all_call_sites": 0x2117,
        "DW_AT_GNU_all_source_call_sites": 0x2118,
        "DW_AT_GNU_macros": 0x2119,
        "DW_AT_GNU_deleted": 0x211A,
        "DW_AT_GNU_dwo_id": 0x2131,
        "DW_AT_GNU_pubnames": 0x2134,
        "DW_AT_GNU_pubtypes": 0x2135,
        "DW_AT_GNU_discriminator": 0x2136,
        "DW_AT_LLVM_include_path": 0x3E00,
        "DW_AT_LLVM_config_macros": 0x3E01,
        "DW_AT_LLVM_isysroot": 0x3E02,
        "DW_AT_LLVM_tag_offset": 0x3E03,
        "DW_AT_APPLE_optimized": 0x3FE1,
        "DW_AT_APPLE_flags": 0x3FE2,
        "DW_AT_APPLE_isa": 0x3FE3,
        "DW_AT_APPLE_block": 0x3FE4,
        "DW_AT_APPLE_major_runtime_vers": 0x3FE5,
        "DW_AT_APPLE_runtime_class": 0x3FE6,
        "DW_AT_APPLE_omit_frame_ptr": 0x3FE7,
        "DW_AT_APPLE_property_name": 0x3FE8,
        "DW_AT_APPLE_property_getter": 0x3FE9,
        "DW_AT_APPLE_property_setter": 0x3FEA,
        "DW_AT_APPLE_property_attribute": 0x3FEB,
        "DW_AT_APPLE_objc_complete_type": 0x3FEC,
        "DW_AT_APPLE_property": 0x3FED,
    }
    DW_AT_MAP = {v: k for k, v in DW_AT.items()}

    DW_LANG = {
        "DW_LANG_C89": 0x0001,
        "DW_LANG_C": 0x0002,
        "DW_LANG_Ada83": 0x0003,
        "DW_LANG_C_plus_plus": 0x0004,
        "DW_LANG_Cobol74": 0x0005,
        "DW_LANG_Cobol85": 0x0006,
        "DW_LANG_Fortran77": 0x0007,
        "DW_LANG_Fortran90": 0x0008,
        "DW_LANG_Pascal83": 0x0009,
        "DW_LANG_Modula2": 0x000A,
        "DW_LANG_Java": 0x000B,
        "DW_LANG_C99": 0x000C,
        "DW_LANG_Ada95": 0x000D,
        "DW_LANG_Fortran95": 0x000E,
        "DW_LANG_PLI": 0x000F,
        "DW_LANG_ObjC": 0x0010,
        "DW_LANG_ObjC_plus_plus": 0x0011,
        "DW_LANG_UPC": 0x0012,
        "DW_LANG_D": 0x0013,
        "DW_LANG_Python": 0x0014,
        "DW_LANG_OpenCL": 0x0015,
        "DW_LANG_Go": 0x0016,
        "DW_LANG_Modula3": 0x0017,
        "DW_LANG_Haskell": 0x0018,
        "DW_LANG_C_plus_plus_03": 0x0019,
        "DW_LANG_C_plus_plus_11": 0x001A,
        "DW_LANG_OCaml": 0x001B,
        "DW_LANG_Rust": 0x001C,
        "DW_LANG_C11": 0x001D,
        "DW_LANG_Swift": 0x001E,
        "DW_LANG_Julia": 0x001F,
        "DW_LANG_Dylan": 0x0020,
        "DW_LANG_C_plus_plus_14": 0x0021,
        "DW_LANG_Fortran03": 0x0022,
        "DW_LANG_Fortran08": 0x0023,
        "DW_LANG_RenderScript": 0x0024,
        "DW_LANG_BLISS": 0x0025,
        "DW_LANG_Mips_Assembler": 0x8001,
        "DW_LANG_Upc": 0x8765,
        "DW_LANG_HP_Bliss": 0x8003,
        "DW_LANG_HP_Basic91": 0x8004,
        "DW_LANG_HP_Pascal91": 0x8005,
        "DW_LANG_HP_IMacro": 0x8006,
        "DW_LANG_HP_Assembler": 0x8007,
        "DW_LANG_GOOGLE_RenderScript": 0x8E57,
        "DW_LANG_BORLAND_Delphi": 0xB000,
    }
    DW_LANG_MAP = {v: k for k, v in DW_LANG.items()}

    DW_LNCT = {
        "DW_LNCT_path": 0x1,
        "DW_LNCT_directory_index": 0x2,
        "DW_LNCT_timestamp": 0x3,
        "DW_LNCT_size": 0x4,
        "DW_LNCT_MD5": 0x5,
        "DW_LNCT_lo_user": 0x2000,
        "DW_LNCT_hi_user": 0x3FFF,
    }
    DW_LNCT_MAP = {v: k for k, v in DW_LNCT.items()}

    DW_FORM = {
        "DW_FORM_null": 0x00,
        "DW_FORM_addr": 0x01,
        "DW_FORM_ref": 0x02,
        "DW_FORM_block2": 0x03,
        "DW_FORM_block4": 0x04,
        "DW_FORM_data2": 0x05,
        "DW_FORM_data4": 0x06,
        "DW_FORM_data8": 0x07,
        "DW_FORM_string": 0x08,
        "DW_FORM_block": 0x09,
        "DW_FORM_block1": 0x0A,
        "DW_FORM_data1": 0x0B,
        "DW_FORM_flag": 0x0C,
        "DW_FORM_sdata": 0x0D,
        "DW_FORM_strp": 0x0E,
        "DW_FORM_udata": 0x0F,
        "DW_FORM_ref_addr": 0x10,
        "DW_FORM_ref1": 0x11,
        "DW_FORM_ref2": 0x12,
        "DW_FORM_ref4": 0x13,
        "DW_FORM_ref8": 0x14,
        "DW_FORM_ref_udata": 0x15,
        "DW_FORM_indirect": 0x16,
        "DW_FORM_sec_offset": 0x17,
        "DW_FORM_exprloc": 0x18,
        "DW_FORM_flag_present": 0x19,
        "DW_FORM_strx": 0x1A,
        "DW_FORM_addrx": 0x1B,
        "DW_FORM_ref_sup4": 0x1C,
        "DW_FORM_strp_sup": 0x1D,
        "DW_FORM_data16": 0x1E,
        "DW_FORM_line_strp": 0x1F,
        "DW_FORM_ref_sig8": 0x20,
        "DW_FORM_implicit_const": 0x21,
        "DW_FORM_loclistx": 0x22,
        "DW_FORM_rnglistx": 0x23,
        "DW_FORM_ref_sup8": 0x24,
        "DW_FORM_strx1": 0x25,
        "DW_FORM_strx2": 0x26,
        "DW_FORM_strx3": 0x27,
        "DW_FORM_strx4": 0x28,
        "DW_FORM_addrx1": 0x29,
        "DW_FORM_addrx2": 0x2A,
        "DW_FORM_addrx3": 0x2B,
        "DW_FORM_addrx4": 0x2C,
        "DW_FORM_GNU_addr_index": 0x1F01,
        "DW_FORM_GNU_str_index": 0x1F02,
        "DW_FORM_GNU_ref_alt": 0x1F20,
        "DW_FORM_GNU_strp_alt": 0x1F21,
    }
    DW_FORM_MAP = {v: k for k, v in DW_FORM.items()}

    DW_TAG = {
        "DW_TAG_null": 0x00,
        "DW_TAG_array_type": 0x01,
        "DW_TAG_class_type": 0x02,
        "DW_TAG_entry_point": 0x03,
        "DW_TAG_enumeration_type": 0x04,
        "DW_TAG_formal_parameter": 0x05,
        "DW_TAG_imported_declaration": 0x08,
        "DW_TAG_label": 0x0A,
        "DW_TAG_lexical_block": 0x0B,
        "DW_TAG_member": 0x0D,
        "DW_TAG_pointer_type": 0x0F,
        "DW_TAG_reference_type": 0x10,
        "DW_TAG_compile_unit": 0x11,
        "DW_TAG_string_type": 0x12,
        "DW_TAG_structure_type": 0x13,
        "DW_TAG_subroutine_type": 0x15,
        "DW_TAG_typedef": 0x16,
        "DW_TAG_union_type": 0x17,
        "DW_TAG_unspecified_parameters": 0x18,
        "DW_TAG_variant": 0x19,
        "DW_TAG_common_block": 0x1A,
        "DW_TAG_common_inclusion": 0x1B,
        "DW_TAG_inheritance": 0x1C,
        "DW_TAG_inlined_subroutine": 0x1D,
        "DW_TAG_module": 0x1E,
        "DW_TAG_ptr_to_member_type": 0x1F,
        "DW_TAG_set_type": 0x20,
        "DW_TAG_subrange_type": 0x21,
        "DW_TAG_with_stmt": 0x22,
        "DW_TAG_access_declaration": 0x23,
        "DW_TAG_base_type": 0x24,
        "DW_TAG_catch_block": 0x25,
        "DW_TAG_const_type": 0x26,
        "DW_TAG_constant": 0x27,
        "DW_TAG_enumerator": 0x28,
        "DW_TAG_file_type": 0x29,
        "DW_TAG_friend": 0x2A,
        "DW_TAG_namelist": 0x2B,
        "DW_TAG_namelist_item": 0x2C,
        "DW_TAG_namelist_items": 0x2C,
        "DW_TAG_packed_type": 0x2D,
        "DW_TAG_subprogram": 0x2E,
        "DW_TAG_template_type_param": 0x2F,
        "DW_TAG_template_value_param": 0x30,
        "DW_TAG_thrown_type": 0x31,
        "DW_TAG_try_block": 0x32,
        "DW_TAG_variant_part": 0x33,
        "DW_TAG_variable": 0x34,
        "DW_TAG_volatile_type": 0x35,
        "DW_TAG_dwarf_procedure": 0x36,
        "DW_TAG_restrict_type": 0x37,
        "DW_TAG_interface_type": 0x38,
        "DW_TAG_namespace": 0x39,
        "DW_TAG_imported_module": 0x3A,
        "DW_TAG_unspecified_type": 0x3B,
        "DW_TAG_partial_unit": 0x3C,
        "DW_TAG_imported_unit": 0x3D,
        "DW_TAG_mutable_type": 0x3E,
        "DW_TAG_condition": 0x3F,
        "DW_TAG_shared_type": 0x40,
        "DW_TAG_type_unit": 0x41,
        "DW_TAG_rvalue_reference_type": 0x42,
        "DW_TAG_atomic_type": 0x47,
        "DW_TAG_call_site": 0x48,
        "DW_TAG_call_site_parameter": 0x49,
        "DW_TAG_skeleton_unit": 0x4A,
        "DW_TAG_immutable_type": 0x4B,
        "DW_TAG_lo_user": 0x4080,
        "DW_TAG_GNU_template_template_param": 0x4106,
        "DW_TAG_GNU_template_parameter_pack": 0x4107,
        "DW_TAG_GNU_formal_parameter_pack": 0x4108,
        "DW_TAG_GNU_call_site": 0x4109,
        "DW_TAG_GNU_call_site_parameter": 0x410A,
        "DW_TAG_APPLE_property": 0x4200,
        "DW_TAG_hi_user": 0xFFFF,
    }
    DW_TAG_MAP = {v: k for k, v in DW_TAG.items()}

    DW_EH_ENCODING_FLAGS = {
        "DW_EH_PE_absptr": 0x00,
        "DW_EH_PE_uleb128": 0x01,
        "DW_EH_PE_udata2": 0x02,
        "DW_EH_PE_udata4": 0x03,
        "DW_EH_PE_udata8": 0x04,
        "DW_EH_PE_signed": 0x08,
        "DW_EH_PE_sleb128": 0x09,
        "DW_EH_PE_sdata2": 0x0A,
        "DW_EH_PE_sdata4": 0x0B,
        "DW_EH_PE_sdata8": 0x0C,
        "DW_EH_PE_pcrel": 0x10,
        "DW_EH_PE_textrel": 0x20,
        "DW_EH_PE_datarel": 0x30,
        "DW_EH_PE_funcrel": 0x40,
        "DW_EH_PE_aligned": 0x50,
        "DW_EH_PE_indirect": 0x80,
        "DW_EH_PE_omit": 0xFF,
    }
    DW_EH_ENCODING_MAP = {v: k for k, v in DW_EH_ENCODING_FLAGS.items()}

    DW_INST = {
        "DW_CFA_advance_loc": 0b01000000,
        "DW_CFA_offset": 0b10000000,
        "DW_CFA_restore": 0b11000000,
        "DW_CFA_nop": 0x00,
        "DW_CFA_set_loc": 0x01,
        "DW_CFA_advance_loc1": 0x02,
        "DW_CFA_advance_loc2": 0x03,
        "DW_CFA_advance_loc4": 0x04,
        "DW_CFA_offset_extended": 0x05,
        "DW_CFA_restore_extended": 0x06,
        "DW_CFA_undefined": 0x07,
        "DW_CFA_same_value": 0x08,
        "DW_CFA_register": 0x09,
        "DW_CFA_remember_state": 0x0A,
        "DW_CFA_restore_state": 0x0B,
        "DW_CFA_def_cfa": 0x0C,
        "DW_CFA_def_cfa_register": 0x0D,
        "DW_CFA_def_cfa_offset": 0x0E,
        "DW_CFA_def_cfa_expression": 0x0F,
        "DW_CFA_expression": 0x10,
        "DW_CFA_offset_extended_sf": 0x11,
        "DW_CFA_def_cfa_sf": 0x12,
        "DW_CFA_def_cfa_offset_sf": 0x13,
        "DW_CFA_val_offset": 0x14,
        "DW_CFA_val_offset_sf": 0x15,
        "DW_CFA_val_expression": 0x16,
        "DW_CFA_GNU_args_size": 0x2E,
        "DW_CFA_low_user": 0x1C,
        "DW_CFA_MIPS_advance_loc8": 0x1D,
        "DW_CFA_GNU_window_save": 0x2D,
        "DW_CFA_GNU_args_size": 0x2E,
        "DW_CFA_GNU_negative_offset_extended": 0x2F,
        "DW_CFA_high_user": 0x3F,
        "DW_PRIMARY_MASK": 0b11000000,
        "DW_PRIMARY_ARG_MASK": 0b00111111,
    }
    DW_INST_MAP = {v: k for k, v in DW_INST.items()}

    DW_OPS_NAMES = {
        "DW_OP_addr": 0x03,
        "DW_OP_deref": 0x06,
        "DW_OP_const1u": 0x08,
        "DW_OP_const1s": 0x09,
        "DW_OP_const2u": 0x0A,
        "DW_OP_const2s": 0x0B,
        "DW_OP_const4u": 0x0C,
        "DW_OP_const4s": 0x0D,
        "DW_OP_const8u": 0x0E,
        "DW_OP_const8s": 0x0F,
        "DW_OP_constu": 0x10,
        "DW_OP_consts": 0x11,
        "DW_OP_dup": 0x12,
        "DW_OP_drop": 0x13,
        "DW_OP_over": 0x14,
        "DW_OP_pick": 0x15,
        "DW_OP_swap": 0x16,
        "DW_OP_rot": 0x17,
        "DW_OP_xderef": 0x18,
        "DW_OP_abs": 0x19,
        "DW_OP_and": 0x1A,
        "DW_OP_div": 0x1B,
        "DW_OP_minus": 0x1C,
        "DW_OP_mod": 0x1D,
        "DW_OP_mul": 0x1E,
        "DW_OP_neg": 0x1F,
        "DW_OP_not": 0x20,
        "DW_OP_or": 0x21,
        "DW_OP_plus": 0x22,
        "DW_OP_plus_uconst": 0x23,
        "DW_OP_shl": 0x24,
        "DW_OP_shr": 0x25,
        "DW_OP_shra": 0x26,
        "DW_OP_xor": 0x27,
        "DW_OP_bra": 0x28,
        "DW_OP_eq": 0x29,
        "DW_OP_ge": 0x2A,
        "DW_OP_gt": 0x2B,
        "DW_OP_le": 0x2C,
        "DW_OP_lt": 0x2D,
        "DW_OP_ne": 0x2E,
        "DW_OP_skip": 0x2F,
        "DW_OP_regx": 0x90,
        "DW_OP_fbreg": 0x91,
        "DW_OP_bregx": 0x92,
        "DW_OP_piece": 0x93,
        "DW_OP_deref_size": 0x94,
        "DW_OP_xderef_size": 0x95,
        "DW_OP_nop": 0x96,
        "DW_OP_push_object_address": 0x97,
        "DW_OP_call2": 0x98,
        "DW_OP_call4": 0x99,
        "DW_OP_call_ref": 0x9A,
        "DW_OP_form_tls_address": 0x9B,
        "DW_OP_call_frame_cfa": 0x9C,
        "DW_OP_bit_piece": 0x9D,
        "DW_OP_implicit_value": 0x9E,
        "DW_OP_stack_value": 0x9F,
        "DW_OP_implicit_pointer": 0xA0,
        "DW_OP_addrx": 0xA1,
        "DW_OP_constx": 0xA2,
        "DW_OP_entry_value": 0xA3,
        "DW_OP_const_type": 0xA4,
        "DW_OP_regval_type": 0xA5,
        "DW_OP_deref_type": 0xA6,
        "DW_OP_xderef_type": 0xA7,
        "DW_OP_convert": 0xA8,
        "DW_OP_reinterpret": 0xA9,
        "DW_OP_lo_user": 0xE0,
        "DW_OP_GNU_push_tls_address": 0xE0,
        "DW_OP_GNU_implicit_pointer": 0xF2,
        "DW_OP_GNU_entry_value": 0xF3,
        "DW_OP_GNU_const_type": 0xF4,
        "DW_OP_GNU_regval_type": 0xF5,
        "DW_OP_GNU_deref_type": 0xF6,
        "DW_OP_GNU_convert": 0xF7,
        "DW_OP_GNU_parameter_ref": 0xFA,
        "DW_OP_hi_user": 0xFF,
    }
    DW_OPS_NAMES.update(UtilMgr.genRangeDict("DW_OP_lit", 0, 31, 0x30))
    DW_OPS_NAMES.update(UtilMgr.genRangeDict("DW_OP_reg", 0, 31, 0x50))
    DW_OPS_NAMES.update(UtilMgr.genRangeDict("DW_OP_breg", 0, 31, 0x70))
    DW_OPS_NAMES_MAP = {v: k for k, v in DW_OPS_NAMES.items()}

    DW_UT = {
        "DW_UT_compile": 0x01,
        "DW_UT_type": 0x02,
        "DW_UT_partial": 0x03,
        "DW_UT_skeleton": 0x04,
        "DW_UT_split_compile": 0x05,
        "DW_UT_split_type": 0x06,
        "DW_UT_lo_user": 0x80,
        "DW_UT_hi_user": 0xFF,
    }
    DW_UT_MAP = {v: k for k, v in DW_UT.items()}

    DW_OPS_NOARGS = dict.fromkeys(
        [
            "DW_OP_deref",
            "DW_OP_dup",
            "DW_OP_drop",
            "DW_OP_over",
            "DW_OP_swap",
            "DW_OP_swap",
            "DW_OP_rot",
            "DW_OP_xderef",
            "DW_OP_abs",
            "DW_OP_and",
            "DW_OP_div",
            "DW_OP_minus",
            "DW_OP_mod",
            "DW_OP_mul",
            "DW_OP_neg",
            "DW_OP_not",
            "DW_OP_or",
            "DW_OP_plus",
            "DW_OP_shl",
            "DW_OP_shr",
            "DW_OP_shra",
            "DW_OP_xor",
            "DW_OP_eq",
            "DW_OP_ge",
            "DW_OP_gt",
            "DW_OP_le",
            "DW_OP_lt",
            "DW_OP_ne",
            "DW_OP_nop",
            "DW_OP_push_object_address",
            "DW_OP_form_tls_address",
            "DW_OP_call_frame_cfa",
            "DW_OP_stack_value",
            "DW_OP_GNU_push_tls_address",
        ]
        + ["DW_OP_lit%s" % idx for idx in xrange(32)]
        + ["DW_OP_reg%s" % idx for idx in xrange(32)]
    )

    DW_OPS_DEC_ARGS = set(
        [
            "DW_OP_const1u",
            "DW_OP_const1s",
            "DW_OP_const2u",
            "DW_OP_const2s",
            "DW_OP_const4u",
            "DW_OP_const4s",
            "DW_OP_const8u",
            "DW_OP_const8s",
            "DW_OP_constu",
            "DW_OP_consts",
            "DW_OP_pick",
            "DW_OP_plus_uconst",
            "DW_OP_bra",
            "DW_OP_skip",
            "DW_OP_fbreg",
            "DW_OP_piece",
            "DW_OP_deref_size",
            "DW_OP_xderef_size",
            "DW_OP_regx",
        ]
        + ["DW_OP_breg%s" % idx for idx in xrange(32)]
    )

    DW_OPS_2DEC_ARGS = {"DW_OP_bregx", "DW_OP_bit_piece"}

    DW_OPS_HEX_ARGS = {
        "DW_OP_addr",
        "DW_OP_call2",
        "DW_OP_call4",
        "DW_OP_call_ref",
    }

    DW_CFI_REGISTER_RULE_TYPE = dict(
        UNDEFINED="u",
        SAME_VALUE="s",
        OFFSET="c",
        VAL_OFFSET="v",
        REGISTER="",
        EXPRESSION="exp",
        VAL_EXPRESSION="vexp",
        ARCHITECTURAL="a",
    )

    class EHABIBytecodeDecoder(object):
        """
        refer to https://github.com/eliben/pyelftools
        Decoder of a sequence of ARM exception handler abi bytecode.

        Reference:
        https://github.com/llvm/llvm-project/blob/master/llvm/tools/llvm-readobj/ARMEHABIPrinter.h
        https://developer.arm.com/documentation/ihi0038/b/

        Accessible attributes:
            mnemonic_array:
                MnemonicItem array.

        Parameters:
            bytecode_array:
                Integer array, raw data of bytecode.
        """

        def __init__(self, bytecode_array, path=None):
            self.path = path
            self._bytecode_array = bytecode_array
            self._index = None
            self.mnemonic_array = []
            self.cfa_table = {}
            if bytecode_array:
                self._decode()

        def _getMnemonicItem(self, bytecode, mnemonic):
            return "%s ; %s" % (
                " ".join(["0x%02x" % x for x in bytecode]),
                mnemonic,
            )

        def _decode(self):
            # pylint: disable=too-many-function-args
            """Decode bytecode array, put result into mnemonic_array."""
            sp_opsstr = "vsp = vsp "
            sp_defstr = "vsp = r"
            pop_str = "pop {"

            fpIdx = 11
            ipIdx = 12
            spIdx = 13
            lrIdx = 14
            pcIdx = 15

            self._index = 0
            self.offset = 0

            while self._index < len(self._bytecode_array):
                for mask, value, handler in self.ring:
                    if (self._bytecode_array[self._index] & mask) != value:
                        continue

                    # decode code #
                    start_idx = self._index
                    mnemonic = handler(self)
                    end_idx = self._index

                    # convert code to string #
                    dstr = self._getMnemonicItem(
                        self._bytecode_array[start_idx:end_idx], mnemonic
                    )
                    self.mnemonic_array.append(dstr)

                    # convert to offset #
                    if mnemonic == "finish":
                        break

                    # apply CFA offset #
                    if mnemonic.startswith(sp_opsstr):
                        ops = mnemonic[len(sp_opsstr) :]
                        if ops[0] == "+":
                            offset = long(ops[1:])
                        else:
                            offset = -long(ops[1:])

                        # change offset #
                        if "cfa" in self.cfa_table:
                            cfaList = list(self.cfa_table["cfa"])
                            cfaList[1] += offset
                            self.cfa_table["cfa"] = tuple(cfaList)
                        else:
                            self.cfa_table["cfa"] = (spIdx, offset, None)
                    # define CFA #
                    elif mnemonic.startswith(sp_defstr):
                        reg = long(mnemonic[len(sp_defstr) :])
                        self.cfa_table["cfa"] = (reg, 0, None)
                    # set regs #
                    elif mnemonic.startswith(pop_str):
                        regs = mnemonic[len(pop_str) : -1].split(", ")
                        val = "OFFSET"
                        for reg in regs:
                            if reg == "lr":
                                self.cfa_table[lrIdx] = (val, self.offset)
                            elif reg == "fp":
                                self.cfa_table[fpIdx] = (val, self.offset)
                            elif reg == "sp":
                                self.cfa_table[spIdx] = (val, self.offset)
                            elif reg == "ip":
                                self.cfa_table[ipIdx] = (val, self.offset)
                            elif reg == "pc":
                                self.cfa_table[pcIdx] = (val, self.offset)
                            else:
                                reg = long(reg[1:])
                                self.cfa_table[reg] = (val, self.offset)

                            # only ARM 32bit supported #
                            self.offset += 4
                    else:
                        SysMgr.printWarn(
                            "unknown operation '%s' in %s"
                            % (mnemonic, self.path)
                        )

                    break

            # set default CFA #
            if not "cfa" in self.cfa_table:
                self.cfa_table["cfa"] = (spIdx, 0, None)

        def _decode_00xxxxxx(self):
            opcode = self._bytecode_array[self._index]
            self._index += 1
            return "vsp = vsp + %u" % (((opcode & 0x3F) << 2) + 4)

        def _decode_01xxxxxx(self):
            opcode = self._bytecode_array[self._index]
            self._index += 1
            return "vsp = vsp - %u" % (((opcode & 0x3F) << 2) + 4)

        gpr_register_names = (
            "r0",
            "r1",
            "r2",
            "r3",
            "r4",
            "r5",
            "r6",
            "r7",
            "r8",
            "r9",
            "r10",
            "fp",
            "ip",
            "sp",
            "lr",
            "pc",
        )

        def _calculate_xrange(self, start, count):
            return ((1 << (count + 1)) - 1) << start

        def _printGPR(self, gpr_mask):
            hits = [
                self.gpr_register_names[i]
                for i in xrange(32)
                if gpr_mask & (1 << i) != 0
            ]
            return "{%s}" % ", ".join(hits)

        def _print_registers(self, vfp_mask, prefix):
            hits = [
                prefix + str(i) for i in xrange(32) if vfp_mask & (1 << i) != 0
            ]
            return "{%s}" % ", ".join(hits)

        def _decode_1000iiii_iiiiiiii(self):
            op0 = self._bytecode_array[self._index]
            self._index += 1
            op1 = self._bytecode_array[self._index]
            self._index += 1
            gpr_mask = (op1 << 4) | ((op0 & 0x0F) << 12)
            if gpr_mask == 0:
                return "refuse to unwind"
            else:
                return "pop %s" % self._printGPR(gpr_mask)

        def _decode_10011101(self):
            self._index += 1
            return "reserved (ARM MOVrr)"

        def _decode_10011111(self):
            self._index += 1
            return "reserved (WiMMX MOVrr)"

        def _decode_1001nnnn(self):
            opcode = self._bytecode_array[self._index]
            self._index += 1
            return "vsp = r%u" % (opcode & 0x0F)

        def _decode_10100nnn(self):
            opcode = self._bytecode_array[self._index]
            self._index += 1
            return "pop %s" % self._printGPR(
                self._calculate_xrange(4, opcode & 0x07)
            )

        def _decode_10101nnn(self):
            opcode = self._bytecode_array[self._index]
            self._index += 1
            return "pop %s" % self._printGPR(
                self._calculate_xrange(4, opcode & 0x07) | (1 << 14)
            )

        def _decode_10110000(self):
            self._index += 1
            return "finish"

        def _decode_10110001_0000iiii(self):
            self._index += 1  # skip constant byte
            op1 = self._bytecode_array[self._index]
            self._index += 1
            if (op1 & 0xF0) != 0 or op1 == 0x00:
                return "spare"
            else:
                return "pop %s" % self._printGPR((op1 & 0x0F))

        def _decode_10110010_uleb128(self):
            self._index += 1  # skip constant byte
            uleb_buffer = [self._bytecode_array[self._index]]
            self._index += 1
            while self._bytecode_array[self._index] & 0x80 == 0:
                uleb_buffer.append(self._bytecode_array[self._index])
                self._index += 1
            value = 0
            for b in reversed(uleb_buffer):
                value = (value << 7) + (b & 0x7F)
            return "vsp = vsp + %u" % (0x204 + (value << 2))

        def _decode_10110011_sssscccc(self):
            # these two decoders are equal
            return self._decode_11001001_sssscccc()

        def _decode_101101nn(self):
            return self._spare()

        def _decode_10111nnn(self):
            opcode = self._bytecode_array[self._index]
            self._index += 1
            return "pop %s" % self._print_registers(
                self._calculate_xrange(8, opcode & 0x07), "d"
            )

        def _decode_11000110_sssscccc(self):
            self._index += 1  # skip constant byte
            op1 = self._bytecode_array[self._index]
            self._index += 1
            start = (op1 & 0xF0) >> 4
            count = (op1 & 0x0F) >> 0
            return "pop %s" % self._print_registers(
                self._calculate_xrange(start, count), "wR"
            )

        def _decode_11000111_0000iiii(self):
            self._index += 1  # skip constant byte
            op1 = self._bytecode_array[self._index]
            self._index += 1
            if (op1 & 0xF0) != 0 or op1 == 0x00:
                return "spare"
            else:
                return "pop %s" % self._print_registers(op1 & 0x0F, "wCGR")

        def _decode_11001000_sssscccc(self):
            self._index += 1  # skip constant byte
            op1 = self._bytecode_array[self._index]
            self._index += 1
            start = 16 + ((op1 & 0xF0) >> 4)
            count = (op1 & 0x0F) >> 0
            return "pop %s" % self._print_registers(
                self._calculate_xrange(start, count), "d"
            )

        def _decode_11001001_sssscccc(self):
            self._index += 1  # skip constant byte
            op1 = self._bytecode_array[self._index]
            self._index += 1
            start = (op1 & 0xF0) >> 4
            count = (op1 & 0x0F) >> 0
            return "pop %s" % self._print_registers(
                self._calculate_xrange(start, count), "d"
            )

        def _decode_11001yyy(self):
            return self._spare()

        def _decode_11000nnn(self):
            opcode = self._bytecode_array[self._index]
            self._index += 1
            return "pop %s" % self._print_registers(
                self._calculate_xrange(10, opcode & 0x07), "wR"
            )

        def _decode_11010nnn(self):
            # these two decoders are equal
            return self._decode_10111nnn()

        def _decode_11xxxyyy(self):
            return self._spare()

        def _spare(self):
            self._index += 1
            return "spare"

        ring = (
            (0xC0, 0x00, _decode_00xxxxxx),
            (0xC0, 0x40, _decode_01xxxxxx),
            (0xF0, 0x80, _decode_1000iiii_iiiiiiii),
            (0xFF, 0x9D, _decode_10011101),
            (0xFF, 0x9F, _decode_10011111),
            (0xF0, 0x90, _decode_1001nnnn),
            (0xF8, 0xA0, _decode_10100nnn),
            (0xF8, 0xA8, _decode_10101nnn),
            (0xFF, 0xB0, _decode_10110000),
            (0xFF, 0xB1, _decode_10110001_0000iiii),
            (0xFF, 0xB2, _decode_10110010_uleb128),
            (0xFF, 0xB3, _decode_10110011_sssscccc),
            (0xFC, 0xB4, _decode_101101nn),
            (0xF8, 0xB8, _decode_10111nnn),
            (0xFF, 0xC6, _decode_11000110_sssscccc),
            (0xFF, 0xC7, _decode_11000111_0000iiii),
            (0xFF, 0xC8, _decode_11001000_sssscccc),
            (0xFF, 0xC9, _decode_11001001_sssscccc),
            (0xC8, 0xC8, _decode_11001yyy),
            (0xF8, 0xC0, _decode_11000nnn),
            (0xF8, 0xD0, _decode_11010nnn),
            (0xC0, 0xC0, _decode_11xxxyyy),
        )

    class RegisterRule(object):
        """
        refer to https://github.com/eliben/pyelftools
        Register rules are used to find registers in call frames. Each rule
        consists of a type (enumeration following DWARFv3 section 6.4.1)
        and an optional argument to augment the type.
        """

        UNDEFINED = "UNDEFINED"
        SAME_VALUE = "SAME_VALUE"
        OFFSET = "OFFSET"
        VAL_OFFSET = "VAL_OFFSET"
        REGISTER = "REGISTER"
        EXPRESSION = "EXPRESSION"
        VAL_EXPRESSION = "VAL_EXPRESSION"
        ARCHITECTURAL = "ARCHITECTURAL"

        TYPE = 0
        ARG = 1

        def __init__(self, type, arg=None):
            pass

        def __new__(self, type, arg=None):
            return (type, arg)

    class CFARule(object):
        """
        refer to https://github.com/eliben/pyelftools
        A CFA rule is used to compute the CFA for each location. It either
        consists of a register+offset, or a DWARF expression.
        """

        REG = 0
        OFFSET = 1
        EXPR = 2

        def __init__(self, reg=None, offset=None, expr=None):
            pass

        def __new__(self, reg=None, offset=None, expr=None):
            return (reg, offset, expr)

    cachedFiles = {}
    cachedHdrFiles = {}
    preloadedFiles = {}
    cachedCFAs = {}
    cachedTypes = {}
    strippedFiles = {}
    failedFiles = {}
    relocTypes = {}
    cachedDemangleTable = {}
    overlayTable = {}

    @staticmethod
    def decompSect(fd, path, shname, sh_offset, sh_size):
        # check compression #
        if not UtilMgr.isZlibFile(path=path, fd=fd):
            return fd, sh_size

        # decompress section #
        decompSect, decompSize = UtilMgr.unzip(fd, shname)
        if not decompSize:
            return fd, sh_size

        # copy decompressed section to memory file #
        sh_size = decompSize
        fd = MemoryFile(size=sh_offset + sh_size, name=shname)
        fd.seek(sh_offset)
        fd.write(decompSect)
        fd.seek(sh_offset)

        return fd, sh_size

    @staticmethod
    def getCFARule(reg=None, offset=None, expr=None):
        key = "%s%s%s" % (reg, offset, expr)
        if not key in ElfAnalyzer.cachedCFAs:
            ElfAnalyzer.cachedCFAs[key] = ElfAnalyzer.CFARule(
                reg, offset, expr
            )
        return ElfAnalyzer.cachedCFAs[key]

    @staticmethod
    def ELF_ST_BIND(i):
        return (i) >> 4

    @staticmethod
    def ELF_ST_TYPE(i):
        return (i) & 0x0F

    @staticmethod
    def ELF_ST_INFO(b, t):
        return (b) << 4 + ((t) & 0x0F)

    @staticmethod
    def ELF_ST_VISIBILITY(i):
        return (i) & 0x3

    @staticmethod
    def ELF32_R_SYM(i):
        return (i) >> 8

    @staticmethod
    def ELF32_R_TYPE(i):
        return (i) & 0xFF

    @staticmethod
    def ELF32_R_INFO(s, t):
        return ((s) << 8) + ((t) & 0xFF)

    @staticmethod
    def ELF64_R_SYM(i):
        return (i) >> 32

    @staticmethod
    def ELF64_R_TYPE(i):
        return (i) & 0xFFFFFFFF

    @staticmethod
    def ELF64_R_INFO(s, type):
        return ((s) << 32) + ((s) & 0xFFFFFFFF)

    @staticmethod
    def DT_VERSIONTAGIDX(tag):
        return ElfAnalyzer.DT_VERNEEDNUM - (tag)

    @staticmethod
    def getHeader(path):
        if path not in ElfAnalyzer.cachedHdrFiles:
            try:
                ElfAnalyzer.cachedHdrFiles[path] = ElfAnalyzer(
                    path, onlyHdr=True
                )
            except:
                return None

        return ElfAnalyzer.cachedHdrFiles[path]

    @staticmethod
    def iteratePhdr():
        # load libc #
        SysMgr.loadLibcObj(exit=True)

        # define word size #
        if ConfigMgr.wordSize == 4:
            dlpi_addr_t = c_int32
        else:
            dlpi_addr_t = c_int64

        class dl_phdr_info(Structure):
            _fields_ = [
                ("dlpi_addr", dlpi_addr_t),
                ("dlpi_name", c_char_p),
                ("dlpi_phdr", c_void_p),
                ("dlpi_phnum", c_uint16),
                ("dlpi_adds", c_ulonglong),
                ("dlpi_subs", c_ulonglong),
                ("dlpi_tls_modid", c_size_t),
                ("dlpi_tls_data", c_void_p),
            ]

        callback_t = CFUNCTYPE(
            c_int, POINTER(dl_phdr_info), POINTER(c_size_t), c_char_p
        )

        # define dl_iterate_phdr #
        SysMgr.libcObj.dl_iterate_phdr.argtypes = [callback_t, c_char_p]
        SysMgr.libcObj.dl_iterate_phdr.restype = c_int

        def callback(info, size, data):
            if not info.contents.dlpi_name:
                return 0

            # 32-bit #
            if ConfigMgr.wordSize == 4:
                bufSize = 32
            else:
                bufSize = 56

            # create a buffer for Elfxx_Phdr #
            lbuf = (c_char * bufSize)()
            memmove(addressof(lbuf), info.contents.dlpi_phdr, sizeof(lbuf))

            # 32-bit #
            if ConfigMgr.wordSize == 4:
                (
                    p_type,
                    p_offset,
                    p_vaddr,
                    p_paddr,
                    p_filesz,
                    p_memsz,
                    p_flags,
                    p_align,
                ) = struct.unpack("IIIIIIII", lbuf)
            # 64-bit #
            else:
                (
                    p_type,
                    p_flags,
                    p_offset,
                    p_vaddr,
                    p_paddr,
                    p_filesz,
                    p_memsz,
                    p_align,
                ) = struct.unpack("IIQQQQQQ", lbuf)

            return 0

        # call function #
        SysMgr.libcObj.dl_iterate_phdr(callback_t(callback), None)

    @staticmethod
    def removeCacheFiles():
        try:
            for item in sorted(os.listdir(SysMgr.cacheDirPath)):
                SysMgr.removeFiles([os.path.join(SysMgr.cacheDirPath, item)])
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn(
                "failed to remove cache files from '%s'" % SysMgr.cacheDirPath,
                True,
                True,
            )

    @staticmethod
    def saveObject(obj, path):
        # check cache dir #
        SysMgr.mkdirs(SysMgr.cacheDirPath)

        # build cache path #
        cpath = "%s/%s" % (SysMgr.cacheDirPath, path.replace("/", "_"))

        return UtilMgr.saveObject2File(obj, cpath)

    @staticmethod
    def getCachedFilename(path):
        return "%s/%s" % (SysMgr.cacheDirPath, path.replace("/", "_"))

    @staticmethod
    def loadObject(path, virt=False):
        # ignore file cache #
        if "NOFILECACHE" in SysMgr.environList:
            return None

        # build cache path #
        cpath = ElfAnalyzer.getCachedFilename(path)

        # check modified time #
        try:
            otime = os.stat(path).st_ctime
            ctime = os.stat(cpath).st_ctime

            # check expired cache #
            if otime > ctime:
                SysMgr.printWarn(
                    "'%s' is more recent than the cache '%s'" % (path, cpath)
                )
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # load object from file #
        obj = UtilMgr.loadObjectFromFile(cpath)
        if not obj:
            return None

        # check expired condition #
        if not virt and not "FORCEFILECACHE" in SysMgr.environList:
            # binary size #
            dpath = obj.path
            if obj.fileSize != os.stat(dpath).st_size:
                return None

            # revision #
            if hasattr(obj, "revision"):
                if obj.revision != __revision__:
                    return None

        return obj

    @staticmethod
    def getObject(
        path, fobj=None, cache=True, overlay=None, log=False, virt=False
    ):
        # set cached list #
        if "FLUSHFILECACHE" in SysMgr.environList:
            cachedFiles = {}
        else:
            cachedFiles = ElfAnalyzer.cachedFiles

        # remove segment number #
        path = path.split(SysMgr.magicStr)[0]

        # save original path #
        origPath = path

        # convert file path on overlayfs #
        path = ElfAnalyzer.overlayTable.get(path, path)

        # immediate return for cached object #
        if cache and path in cachedFiles:
            return cachedFiles[path]

        # check overlayfs #
        if overlay and path.startswith("/") and not os.path.exists(path):
            path = UtilMgr.convOverlayPath(path, overlay)
            if path != origPath:
                ElfAnalyzer.overlayTable[origPath] = path
            if path in ElfAnalyzer.failedFiles:
                ElfAnalyzer.failedFiles.pop(path, None)

        # check black-list #
        if path in ElfAnalyzer.failedFiles:
            return None

        # check deleted files #
        if FileAnalyzer.isDeletedFile(path):
            ElfAnalyzer.failedFiles[path] = True
            SysMgr.printWarn(
                "failed to load %s because it is already deleted" % path
            )
            return None

        # check print flag for load messages #
        printMsg = not "NOLOADMSG" in SysMgr.environList

        def _printFilterWarn(obj, path):
            if not hasattr(obj, "filtered") or not obj.filtered:
                return
            SysMgr.printWarn(
                "symbol filter [ %s ] was applied to '%s'"
                % (", ".join(obj.filtered), path),
                True,
            )

        # load files #
        if not cache or not path in cachedFiles:
            # handle vDSO #
            if path == "vdso":
                fobj = SysMgr.getVDSO()
            # check exceptional case #
            elif not virt and not os.path.exists(path):
                return None

            # print load message #
            if printMsg:
                SysMgr.printInfo(
                    "load %s... " % path, prefix=False, suffix=False
                )

            # return a exceptional file object #
            if fobj and not virt:
                if printMsg:
                    SysMgr.printInfo("[done]", prefix=False, title=False)

                _printFilterWarn(fobj, path)

                return fobj

            # load archive files #
            def _loadArchives(fobj, newline=True):
                if hasattr(fobj, "archiveList"):
                    if not fobj.archiveList:
                        return
                else:
                    return

                if newline:
                    sys.stdout.write("\n")

                # load embedded objects #
                for i in fobj.archiveList:
                    mpath, size, offset = i
                    mdata = SysMgr.readFile(
                        path, size, byte=True, offset=offset
                    )
                    mfd = MemoryFile(size=size, name=mpath, data=mdata)
                    try:
                        ElfAnalyzer.getObject(mpath, mfd, virt=True)
                    except SystemExit:
                        sys.exit(0)
                    except:
                        SysMgr.printWarn(
                            "failed to load '%s'" % mpath, reason=True
                        )

                return True

            # try to load an object from cache #
            fobjOrig = fobj
            fobj = ElfAnalyzer.loadObject(path, virt=virt)
            if fobj:
                # load embedded objects #
                archived = _loadArchives(fobj)

                if SysMgr.dwarfEnable and (
                    not "dwarfEnabled" in fobj.attr
                    or not fobj.attr["dwarfEnabled"]
                ):
                    pass
                else:
                    cachedFiles[path] = fobj
                    cachedFiles[path].saved = True

                    if not archived and printMsg:
                        SysMgr.printInfo("[cached]", prefix=False, title=False)

                    _printFilterWarn(fobj, path)

                    return fobj

            raiseExcept = False

            # create a new object #
            try:
                archived = False
                elfObj = ElfAnalyzer(path, fd=fobjOrig if virt else None)
                if not elfObj or not elfObj.ret:
                    raiseExcept = True
                    raise Exception("no ELF format")

                cachedFiles[path] = elfObj

                # load embedded objects #
                archived = _loadArchives(elfObj)

                if not archived and printMsg:
                    SysMgr.printInfo("[done]", prefix=False, title=False)
            except SystemExit:
                sys.exit(0)
            except:
                ElfAnalyzer.failedFiles[path] = True

                if not archived and printMsg:
                    failLog = UtilMgr.convColor("[fail]", "RED")
                    SysMgr.printInfo(failLog, prefix=False, title=False)

                SysMgr.printWarn(
                    "failed to load '%s' as an ELF object" % path, reason=True
                )

                if raiseExcept:
                    raise Exception("wrong binary")
                else:
                    return None

        # save object cache to file #
        if not cachedFiles[path].saved:
            ElfAnalyzer.saveObject(cachedFiles[path], path)
            cachedFiles[path].saved = True

        _printFilterWarn(cachedFiles[path], path)

        return cachedFiles[path]

    @staticmethod
    def demangleJavaSym(symbol):
        return symbol

    @staticmethod
    def demangleRustSymPre(symbol):
        """
        refer to https://rust-lang.github.io/rfcs/2603-rust-symbol-name-mangling-v0.html

        Symbols begin with _R from v0.
        """

        if not symbol.startswith("_ZN"):
            return symbol, False

        # remove E. suffix #
        if "E." in symbol:
            symbol = symbol[: symbol.rfind("E.")]

        hash_prefix_len = 3
        hash_len = 16
        hashTotal = hash_prefix_len + hash_len

        # remove 17 prefix #
        if symbol[:-hash_len].endswith("17h"):
            symbol = "_Z" + symbol[3:-hashTotal]

        return symbol, True

    @staticmethod
    def demangleRustSym(symbol):
        """
        refer to https://elixir.bootlin.com/linux/latest/source/tools/perf/util/demangle-rust.c #

        Legacy Rust symbols always end with E.
        Legacy Rust symbols also always end with a path segment
        that encodes a 16 hex digit hash, i.e. '17h[a-f0-9]{16}'.
        This early check, before any parse_ident calls, should
        quickly filter out most C++ symbols unrelated to Rust.
        """

        # hash_prefix = "::h"
        hash_prefix_len = 3
        hash_len = 16
        hashTotal = hash_prefix_len + hash_len

        # check symbol #
        if len(symbol) <= hashTotal:
            return symbol

        # convert chars #
        for char in ElfAnalyzer.rustChars:
            symbol = symbol.replace(char[1], char[0])

        # check symbol #
        if len(symbol) <= hashTotal:
            return symbol

        if symbol[:-hash_len].endswith("::h"):
            symbol = symbol[:-hashTotal]

        return symbol

    @staticmethod
    def demangleSymbol(symbol, incArg=True):
        # pylint: disable=not-callable
        try:
            origSym = symbol
            symbol = symbol.replace("@@", "@")
        except SystemExit:
            sys.exit(0)
        except:
            return symbol

        if not SysMgr.demangleEnable:
            return symbol

        # remove android specific prefix #
        if SysMgr.isAndroid:
            symbol = UtilMgr.lstrip(symbol, "__dl_")

        # check mangling #
        if not symbol.startswith("_Z"):
            return symbol

        # check cache table #
        dsym = ElfAnalyzer.cachedDemangleTable.get(symbol)
        if dsym:
            return dsym

        # get ctypes object #
        if not SysMgr.importPkgItems("ctypes", False):
            SysMgr.printWarn(
                (
                    "failed to import python package: ctypes "
                    "to demangle symbol, so that disable demangle feature"
                ),
                True,
            )
            SysMgr.demangleEnable = False
            return symbol

        # try to demangle symbol #
        try:
            # load libc #
            ret = SysMgr.loadLibcObj()
            if not ret:
                return symbol

            # load demangle library #
            if not SysMgr.libdemangleObj:
                # update libllvm name #
                llvmPath = SysMgr.environList.get("LIBLLVM")
                if llvmPath:
                    SysMgr.libLLVMPath = llvmPath[0]

                    # load llvm library #
                    SysMgr.libLLVMObj = SysMgr.loadLib(SysMgr.libLLVMPath)
                    SysMgr.libdemangleObj = SysMgr.libLLVMObj

                # load standard library #
                if not SysMgr.libdemangleObj:
                    SysMgr.libdemangleObj = SysMgr.loadLib(
                        SysMgr.libdemanglePath
                    )

            # check standard library #
            if not SysMgr.libdemangleObj:
                SysMgr.printWarn(
                    (
                        "failed to load demangle object: %s "
                        "to demangle symbol, so that disable demangle feature"
                    )
                    % SysMgr.libdemanglePath,
                    True,
                )
                SysMgr.demangleEnable = False
                return symbol

            # declare demangler #
            if not SysMgr.demangleFunc:
                # llvm::itaniumDemangle() #
                llvmFunc = "_ZN4llvm15itaniumDemangleEPKcPcPmPi"
                if hasattr(SysMgr.libdemangleObj, llvmFunc):
                    SysMgr.demangleFunc = getattr(
                        SysMgr.libdemangleObj, llvmFunc
                    )
                # __cxa_demangle() #
                else:
                    SysMgr.demangleFunc = getattr(
                        SysMgr.libdemangleObj, "__cxa_demangle"
                    )

                # define return type for demangler #
                SysMgr.demangleFunc.restype = c_void_p

                # define arg types for free() #
                SysMgr.libcObj.free.argtypes = [c_void_p]

            # strip llvm info #
            symbol = symbol.split(".llvm.")[0]

            # remove suffixes for rust legacy #
            symbol, isRust = ElfAnalyzer.demangleRustSymPre(symbol)

            # remove optimization suffixes #
            symbol = symbol.split(".isra.")[0]
            symbol = symbol.split(".part.")[0]
            symbol = symbol.split(".constprop.")[0]

            # check version #
            if "@" in symbol:
                symbol, version = symbol.split("@")
                version = "@%s" % version
            else:
                version = ""

            # init variables #
            funcp = SysMgr.demangleFunc
            status = c_int()
            mSymbol = c_char_p(UtilMgr.encodeStr(symbol))

            # call to demangle symbol #
            ret = funcp(mSymbol, 0, 0, pointer(status))
            retc = cast(ret, c_char_p)

            # check 1st return status #
            origStat = status.value
            if origStat == 0:
                try:
                    dmSymbol = str(retc.value.decode())
                except SystemExit:
                    sys.exit(0)
                except:
                    dmSymbol = str(retc.value)
            else:
                # free demangled string array #
                SysMgr.libcObj.free(ret)

                # reinit variables #
                status = c_int()
                mSymbol = c_char_p(UtilMgr.encodeStr(origSym))

                # call to demangle symbol #
                ret = funcp(mSymbol, None, None, pointer(status))

                retc = cast(ret, c_char_p)

                # check 2nd return status #
                if status.value == 0:
                    try:
                        dmSymbol = str(retc.value.decode())
                    except:
                        dmSymbol = str(retc.value)
                else:
                    isRust = False
                    dmSymbol = symbol
                    if origStat == -1:
                        SysMgr.printWarn(
                            "failed to allocate memory to demangle symbol %s"
                            % origSym
                        )
                    elif origStat == -2:
                        SysMgr.printWarn(
                            "failed to demangle %s because of invalid name"
                            % origSym
                        )
                    elif origStat == -3:
                        SysMgr.printWarn(
                            "failed to demangle %s because of invalid args"
                            % origSym
                        )
                    else:
                        SysMgr.printWarn(
                            "failed to demangle %s because of unknown status %d"
                            % (origSym, origStat)
                        )

            # free demangled string array #
            SysMgr.libcObj.free(ret)

            # remove args info #
            if not incArg:
                try:
                    dmSymbol = dmSymbol.split("(", 1)[0]
                except:
                    pass

            # demangle symbols for rust #
            if isRust:
                dmSymbol = ElfAnalyzer.demangleRustSym(dmSymbol)

            # demangle symbols for java #
            """
            TODO: implement demangleJavaSym()
            dmSymbol = ElfAnalyzer.demangleJavaSym(dmSymbol)
            """

            demangledSym = dmSymbol + version
            ElfAnalyzer.cachedDemangleTable[origSym] = demangledSym
            return demangledSym
        except SystemExit:
            sys.exit(0)
        except:
            err = SysMgr.getErrMsg()
            SysMgr.printWarn(
                (
                    "failed to demangle symbol %s because %s "
                    "so that disable demangle feature"
                )
                % (symbol, err),
                True,
            )
            SysMgr.demangleEnable = False
            return origSym

    @staticmethod
    def getFilterFlags(symbol):
        inc = start = end = False

        if not symbol:
            inc = start = end = True
        elif symbol[0] == "*" == symbol[-1]:
            symbol = symbol.strip("*")
            inc = True
        elif symbol[0] == "*":
            symbol = symbol.strip("*")
            end = True
        elif symbol[-1] == "*":
            symbol = symbol.strip("*")
            start = True

        return symbol, inc, start, end

    @staticmethod
    def getSymOffset(
        symbol, binPath, objdumpPath=None, loadAddr=False, cache=False
    ):
        syms = []

        if not objdumpPath:
            offsets = None

            # get offset #
            try:
                binObj = ElfAnalyzer.getObject(binPath)
                if not binObj:
                    raise Exception("no binary")
                elif binObj.onlyFunc:
                    binObj = ElfAnalyzer.getObject(binPath, cache=cache)

                symbol, inc, start, end = ElfAnalyzer.getFilterFlags(symbol)

                offsets = binObj.getOffsetBySymbol(
                    symbol, inc=inc, start=start, end=end, onlyFunc=False
                )
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printWarn(
                    "failed to get offset for %s from %s" % (symbol, binPath),
                    reason=True,
                )
                return None

            if isinstance(offsets, str):
                offsets = [[symbol, offsets]]

            if isinstance(offsets, list):
                for item in offsets:
                    sym = item[0]
                    offset = long(item[1], 16)

                    # handle executable #
                    if loadAddr:
                        offset -= binObj.loadAddr

                    syms.append([offset, sym, binPath])

            return syms

        # get subprocess object #
        subprocess = SysMgr.getPkg("subprocess")

        args = [objdumpPath, "-C", "-F", "-d", binPath]

        SysMgr.printStat("start finding %s... [ STOP(Ctrl+c) ]" % (symbol))

        # start objdump process #
        try:
            proc = subprocess.Popen(
                args,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                bufsize=-1,
            )
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                "failed to execute %s to get address from binary" % objdumpPath
            )
            sys.exit(-1)

        while 1:
            try:
                # read a line from objdump process #
                line = proc.stdout.readline()
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr("failed to read output from objdump", True)

            # handle error #
            if not line:
                err = proc.stderr.read()
                if err:
                    try:
                        proc.terminate()
                    except:
                        pass

                    SysMgr.printErr(err[err.find(":") + 2 :])
                    sys.exit(-1)
                break

            # parse line to find offset of symbol #
            line = str(line)
            m = re.match(
                (
                    r"\s*(?P<addr>\S*)\s*\<(?P<symbol>.*)\>\s*\("
                    r"File Offset:\s*(?P<offset>\S*)\s*\)"
                ),
                line,
            )
            if not m:
                continue

            d = m.groupdict()
            if d["symbol"] == symbol:
                try:
                    proc.terminate()
                except:
                    pass
                return d["offset"]
            elif symbol in d["symbol"]:
                syms.append([d["symbol"], d["offset"]])

        # check similar list #
        if not syms:
            return None
        else:
            return syms

    @staticmethod
    def isRelocFile(path):
        if path in ElfAnalyzer.relocTypes:
            return ElfAnalyzer.relocTypes[path]

        try:
            eobj = ElfAnalyzer.getObject(path)
            if not eobj:
                raise Exception("no binary")

            # archive file #
            if hasattr(eobj, "archiveList") and eobj.archiveList:
                ElfAnalyzer.relocTypes[path] = True
                return True

            # ELF reloc file #
            etype = eobj.attr["elfHeader"]["type"]
            if etype in ("Relocatable", "Shared-object"):
                ElfAnalyzer.relocTypes[path] = True
                return True

            # not reloc file #
            ElfAnalyzer.relocTypes[path] = False
            return False
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn(
                "failed to check relocatable format for '%s'" % path,
                reason=True,
            )
            return False

        # check file name #
        exp = path.rsplit(".", 1)[-1]
        if exp in ("so", "ttf", "pak"):
            ElfAnalyzer.relocTypes[path] = True
            return True
        else:
            ElfAnalyzer.relocTypes[path] = False
            return False

    def mergeSymTable(self, force=False, onlyFunc=True, removeOrig=False):
        # check already merged #
        if not force and self.mergedSymTable:
            return

        # save func flag #
        self.onlyFunc = onlyFunc

        # check removing symbol tables flag #
        if "RMSYMTAB" in SysMgr.environList:
            removeOrig = True

        # merge sym tables #
        tempSymTable = UtilMgr.deepcopy(self.attr["symTable"])
        if removeOrig:
            self.attr["symTable"].clear()

        # merge dynsym tables #
        tempSymTable.update(self.attr["dynsymTable"])
        if removeOrig:
            self.attr["dynsymTable"].clear()

        # merge dwarf tables #
        tempSymTable.update(self.attr["dwarfTable"])
        if removeOrig:
            self.attr["dwarfTable"].clear()

        # save merged table #
        self.mergedSymTable = tempSymTable

        # add section symbols #
        for name, item in self.attr["sectionHeader"].items():
            if item["addr"] == 0:
                continue

            name = "[%s]" % name
            addr = hex(item["addr"])

            if addr in tempSymTable:
                tempSymTable[name] = tempSymTable[addr]
            else:
                tempSymTable[name] = {
                    "value": item["addr"],
                    "size": item["size"],
                    "type": "SECTION",
                    "bind": "GLOBAL",
                    "vis": "DEFAULT",
                    "ndx": 0,
                }

        mainSym = "?"
        prevAddr = None
        prevSize = 0
        prevLen = 0
        curLen = 0

        # set merge flag #
        mergeFlag = "MERGESYM" in SysMgr.environList

        # set merge flag #
        allFlag = "ALLSYM" in SysMgr.environList

        # sort and convert table #
        for idx, item in sorted(
            tempSymTable.items(), key=lambda e: e[1]["value"], reverse=False
        ):
            # skip useless symbol #
            if item["size"] == 0:
                continue

            # skip symbols except for function #
            if onlyFunc and not item["type"] in ("FUNC", "SECTION"):
                continue

            # update symbol length #
            prevLen = curLen
            curLen = idx.find("@")
            if curLen < 0:
                curLen = len(idx)

            # update representatives among symbols with the same address #
            if prevAddr == item["value"]:
                # merge all symbols #
                if mergeFlag:
                    mainSym += "/%s" % idx
                # handle all #
                elif allFlag:
                    pass
                # ignore address #
                elif idx.startswith("0x"):
                    pass
                # ignore _SYMBOL #
                elif idx.startswith("_") and not mainSym.startswith("_"):
                    pass
                # choose the shortest symbol #
                elif (
                    ("@" in idx and not "@" in mainSym)
                    or (not idx.startswith("_") and mainSym.startswith("_"))
                    or (curLen < prevLen)
                ):
                    mainSym = idx
                continue

            # register symbol #
            if prevAddr:
                self.sortedAddrTable.append(prevAddr)
                self.sortedSymTable.append([mainSym, prevSize])

            # update previous symbol info #
            mainSym = idx
            prevAddr = item["value"]
            prevSize = item["size"]
            prevLen = sys.maxsize

        # register last symbol #
        if prevAddr:
            self.sortedAddrTable.append(prevAddr)
            self.sortedSymTable.append([mainSym, prevSize])

        # except dwarf table #
        if (
            "dwarf" in self.attr
            and "EXCEPTDWARF" in SysMgr.environList
            and UtilMgr.isValidStr(
                self.path, SysMgr.environList["EXCEPTDWARF"]
            )
        ):
            del self.attr["dwarf"]

        # remove useless symbols after merge #
        if removeOrig:
            self.addrTable.clear()

    def getRangeBySymbol(self, symbol):
        # use unified symbol table #
        if symbol in self.mergedSymTable:
            val = self.mergedSymTable[symbol]
            return [val["value"], val["value"] + val["size"]]
        else:
            return None

        # use each symbol tables #
        if symbol in self.attr["symTable"]:
            val = self.attr["symTable"][symbol]
            return [val["value"], val["value"] + val["size"]]
        elif symbol in self.attr["dynsymTable"]:
            val = self.attr["dynsymTable"][symbol]
            return [val["value"], val["value"] + val["size"]]
        else:
            return None

    def getAnonRangeByOffset(self, offset):
        # check symbol table #
        if not self.sortedSymTable:
            self.mergeSymTable()

        try:
            if UtilMgr.isString(offset):
                try:
                    offset = long(offset, 16)
                except:
                    offset = long(offset)

            addrTable = self.sortedAddrTable
            symTable = self.sortedSymTable

            # get target index from address table #
            idx = UtilMgr.bisect_left(self.sortedAddrTable, offset) - 1
            if idx < 0:
                idx = 0

            if addrTable[idx] > offset:
                if idx > 0:
                    start = addrTable[idx - 1] + symTable[idx - 1][1] + 1
                    end = addrTable[idx] - 1
                else:
                    start = 0
                    end = addrTable[idx] - 1
            else:
                start = addrTable[idx] + symTable[idx][1] + 1
                end = addrTable[idx + 1] - 1

            return [start, end]
        except SystemExit:
            sys.exit(0)
        except:
            return [0, 0]

    def mergeArchiveTable(self):
        try:
            for item in self.archiveList:
                path, size, offset = item
                self.sortedAddrTable.append(offset)
                self.sortedSymTable.append([path, size])
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                "failed to merge archives for '%s'" % self.path, True
            )

    def getSymbolByOffset(self, offset, onlyFunc=True, reent=False):
        # check archives #
        if not reent and hasattr(self, "archiveList") and self.archiveList:
            if not self.sortedAddrTable:
                self.mergeArchiveTable()

            # get embedded file info #
            epath, esize = self.getSymbolByOffset(offset, onlyFunc, reent=True)
            if epath in self.archiveFile:
                eoffset = offset - self.archiveFile[epath]

                # get symbol info from embedded object #
                eobj = ElfAnalyzer.getObject(epath)
                ret = eobj.getSymbolByOffset(eoffset, onlyFunc)
                return (ret[0] + "@" + epath, ret[1])
            else:
                return "??@%s" % epath, "??"

        # check symbol table #
        if not self.sortedSymTable:
            self.mergeSymTable(onlyFunc=onlyFunc)

        # get symbol by offset #
        try:
            if UtilMgr.isString(offset):
                try:
                    offset = long(offset, 16)
                except:
                    offset = long(offset)

            addrTable = self.sortedAddrTable
            symTable = self.sortedSymTable

            # Add line info #
            addStr = ""
            if self.sortedAddrLineTable:
                try:
                    idx = (
                        UtilMgr.bisect_left(self.sortedAddrLineTable, offset)
                        - 1
                    )
                    if idx < 0:
                        idx = 0
                    sidx, line = self.sortedLineTable[idx]
                    sname = self.srcTable[sidx]
                    addStr = "{%s:%s}" % (sname, line)
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

            # get target index from address table #
            idx = UtilMgr.bisect_left(self.sortedAddrTable, offset) - 1
            if idx < 0:
                idx = 0

            while 1:
                if addrTable[idx] > offset:
                    return "??", "??"

                # set symbol scope to it's size #
                if True:
                    size = symTable[idx][1]
                    maxAddr = addrTable[idx] + size
                # set symbol scope to next one's start offset #
                else:
                    size = "??"
                    maxAddr = addrTable[idx + 1]

                if addrTable[idx] <= offset <= maxAddr:
                    return symTable[idx][0] + addStr, size

                idx += 1
        except SystemExit:
            sys.exit(0)
        except:
            return "??", "??"

    def getOffsetBySymbol(
        self, symbol, inc=False, start=False, end=False, onlyFunc=True
    ):
        # check archives #
        if hasattr(self, "archiveList") and self.archiveList:
            if not self.sortedSymTable:
                self.mergeArchiveTable()

            clist = []

            # get embedded file info #
            for idx, val in enumerate(self.sortedSymTable):
                try:
                    epath = val[0]

                    if not epath in self.archiveFile:
                        continue

                    # get base address #
                    baddr = self.sortedAddrTable[idx]

                    # get symbol info from embedded object #
                    eobj = ElfAnalyzer.getObject(epath)
                    ret = eobj.getOffsetBySymbol(
                        symbol, inc, start, end, onlyFunc
                    )

                    # get real address #
                    for item in ret:
                        raddr = hex(baddr + long(item[1], 16))
                        clist.append([item[0] + "@" + epath, raddr])
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printWarn(
                        "failed to get offset info from '%s'" % val[0],
                        reason=True,
                    )

            return clist

        # check symbol table #
        if not self.sortedSymTable:
            self.mergeSymTable(onlyFunc=onlyFunc)

        clist = []

        # get offset or symbol list #
        try:
            for idx, val in enumerate(self.sortedSymTable):
                target = val[0].split("@")[0]
                offset = hex(self.sortedAddrTable[idx]).rstrip("L")
                if (
                    (start and target.startswith(symbol))
                    or (end and target.endswith(symbol))
                    or (inc and symbol in target)
                ):
                    clist.append([val[0], offset])
                elif symbol in (val[0], target):
                    return offset
        except SystemExit:
            sys.exit(0)
        except:
            return None

        if not clist:
            return None
        else:
            return clist

    def getDebugSection(self, fd, sh_offset, sh_size, name):
        info = self.getCompressInfo(fd, sh_offset, name)
        if not info:
            return fd.read(sh_size), sh_size

        zlib = SysMgr.getPkg("zlib", False)
        if not zlib:
            return fd.read(sh_size), sh_size

        # parse compression info #
        if self.is32Bit:
            ctype, csize, caddralign = info
            reserved = 0
            headersize = 12
        else:
            ctype, reserved, csize, caddralign = info
            headersize = 24

        fd.seek(sh_offset + headersize)
        data = fd.read(sh_size - headersize)

        # ELFCOMPRESS_ZLIB = 1 #
        if ctype != 1:
            return data, len(data)

        # decompress ZLIB data #
        decompressor = zlib.decompressobj()
        decompressed = decompressor.decompress(data)
        decompressed += decompressor.flush()
        return decompressed, len(decompressed)

    def getCompressInfo(self, fd, pos=0, name=""):
        if (
            not name in self.attr["sectionHeader"]
            or not "C" in self.attr["sectionHeader"][name]["flag"]
        ):
            return None

        fd.seek(pos)

        if self.is32Bit:
            return struct.unpack("III", fd.read(12))
        else:
            return struct.unpack("IIQQ", fd.read(24))

    def getSectionInfo(self, fd, pos=0):
        fd.seek(pos)

        if self.is32Bit:
            return struct.unpack("IIIIIIIIII", fd.read(40))
        else:
            return struct.unpack("IIQQQQIIQQ", fd.read(64))

    def __del__(self):
        pass

    def getCFI(self, table, pos, size, prt=False):
        def _getBlockArgs(table, pos):
            size = struct.unpack("b", table[pos : pos + 1])[0]
            pos += 1

            args = []
            for _ in xrange(size):
                args.append(struct.unpack("B", table[pos : pos + 1])[0])
                pos += 1

            return args, pos

        # define shortcut variable for DWARF constants #
        DW = ElfAnalyzer.DW_INST
        DWM = ElfAnalyzer.DW_INST_MAP

        cfi = []

        def _decodeData(table, pos):
            return table[pos : pos + 1024].decode("latin-1")

        while pos < size:
            args = []

            opcode = struct.unpack("B", table[pos : pos + 1])[0]
            pos += 1

            primary = opcode & DW["DW_PRIMARY_MASK"]
            primaryArg = opcode & DW["DW_PRIMARY_ARG_MASK"]

            if primary == DW["DW_CFA_advance_loc"]:
                inst = "DW_CFA_advance_loc"
                args = [primaryArg]
            elif primary == DW["DW_CFA_offset"]:
                data = _decodeData(table, pos)
                arg, nsize = UtilMgr.decodeULEB128(data)
                pos += nsize

                inst = "DW_CFA_offset"
                args = [primaryArg, arg]
            elif primary == DW["DW_CFA_restore"]:
                inst = "DW_CFA_restore"
                args = [primaryArg]
            # primary == 0 and real opcode is extended
            elif opcode in (
                DW["DW_CFA_nop"],
                DW["DW_CFA_remember_state"],
                DW["DW_CFA_restore_state"],
                DW["DW_CFA_GNU_window_save"],
            ):
                inst = DWM[opcode]
                args = []
            elif opcode == DW["DW_CFA_set_loc"]:
                if self.is32Bit:
                    word = 4
                    val = struct.unpack("I", table[pos : pos + word])[0]
                else:
                    word = 8
                    val = struct.unpack("Q", table[pos : pos + word])[0]

                inst = DWM[opcode]
                args = [val]
            elif opcode == DW["DW_CFA_advance_loc1"]:
                val = struct.unpack("B", table[pos : pos + 1])[0]
                pos += 1

                inst = DWM[opcode]
                args = [val]
            elif opcode == DW["DW_CFA_advance_loc2"]:
                val = struct.unpack("H", table[pos : pos + 2])[0]
                pos += 2

                inst = DWM[opcode]
                args = [val]
            elif opcode == DW["DW_CFA_advance_loc4"]:
                val = struct.unpack("I", table[pos : pos + 4])[0]
                pos += 4

                inst = DWM[opcode]
                args = [val]
            elif opcode in (
                DW["DW_CFA_offset_extended"],
                DW["DW_CFA_register"],
                DW["DW_CFA_def_cfa"],
                DW["DW_CFA_val_offset"],
            ):
                data = _decodeData(table, pos)
                arg1, nsize = UtilMgr.decodeULEB128(data)
                pos += nsize
                data = _decodeData(table, pos)
                arg2, nsize = UtilMgr.decodeULEB128(data)
                pos += nsize

                inst = DWM[opcode]
                args = [arg1, arg2]
            elif opcode in (
                DW["DW_CFA_restore_extended"],
                DW["DW_CFA_undefined"],
                DW["DW_CFA_same_value"],
                DW["DW_CFA_def_cfa_register"],
                DW["DW_CFA_def_cfa_offset"],
                DW["DW_CFA_GNU_args_size"],
            ):
                inst = DWM[opcode]
                data = _decodeData(table, pos)
                arg, nsize = UtilMgr.decodeULEB128(data)
                args = [arg]
                pos += nsize
            elif opcode == DW["DW_CFA_def_cfa_offset_sf"]:
                inst = DWM[opcode]
                data = _decodeData(table, pos)
                arg, nsize = UtilMgr.decodeSLEB128(data)
                args = [arg]
                pos += nsize
            elif opcode == DW["DW_CFA_def_cfa_expression"]:
                inst = DWM[opcode]
                data = _decodeData(table, pos)
                args, pos = _getBlockArgs(table, pos)
            elif opcode in (
                DW["DW_CFA_expression"],
                DW["DW_CFA_val_expression"],
            ):
                inst = DWM[opcode]
                data = _decodeData(table, pos)
                arg1, nsize = UtilMgr.decodeULEB128(data)
                pos += nsize
                data = _decodeData(table, pos)
                arg2, pos = _getBlockArgs(table, pos)
                args = [arg1, arg2]
            elif opcode in (
                DW["DW_CFA_offset_extended_sf"],
                DW["DW_CFA_def_cfa_sf"],
                DW["DW_CFA_val_offset_sf"],
            ):
                data = _decodeData(table, pos)
                arg1, nsize = UtilMgr.decodeULEB128(data)
                pos += nsize
                data = _decodeData(table, pos)
                arg2, nsize = UtilMgr.decodeSLEB128(data)
                pos += nsize

                inst = DWM[opcode]
                args = [arg1, arg2]
            elif opcode == DW["DW_CFA_GNU_args_size"]:
                inst = DWM[opcode]
                data = _decodeData(table, pos)
                arg, nsize = UtilMgr.decodeULEB128(data)
                pos += nsize
                args = [arg]
            else:
                inst = None
                SysMgr.printErr(
                    "failed to recognize CFI opcode %s for %s"
                    % (hex(opcode), self.path)
                )
                break

            cfi.append([inst, opcode, args])

        return cfi

    def getString(self, strtable, start=0, retlen=False):
        if not strtable:
            return ""

        idx = start
        end = self.fileSize - start

        while 1:
            if strtable[idx : idx + 1] == b"\x00":
                break
            elif idx >= end:
                break

            idx += 1

        # pick symbol string #
        if start == idx:
            symbol = ""
        else:
            try:
                symbol = strtable[start:idx].decode()
            except SystemExit:
                sys.exit(0)
            except:
                symbol = strtable[start:idx]

        # return #
        if retlen:
            return symbol, idx - start
        else:
            return symbol

    def isInSection(self, section, addr):
        try:
            attr = self.attr["sectionHeader"][section]
            if attr["addr"] <= addr <= attr["addr"] + attr["size"]:
                return True
            else:
                return False
        except:
            SysMgr.printWarn("failed to check %s area" % section, reason=True)
            return None

    def isInPlt(self, addr):
        try:
            return self.isInSection(".plt", addr)
        except:
            return None

    def getSectionHeader(self, name):
        try:
            return self.attr["sectionHeader"][name]
        except:
            return None

    def __str__(self):
        self.__init__(self.path, debug=True, printer=True)
        return "\n".join(self.logList)

    def __init__(
        self,
        path=None,
        debug=False,
        onlyHdr=False,
        fd=None,
        size=sys.maxsize,
        incArg=False,
        printer=False,
        origPath=None,
    ):
        def _printer(item):
            if not hasattr(self, "logList"):
                self.logList = []
            self.logList.append(item)

        # set printer #
        if printer:
            printer = _printer
        else:
            printer = SysMgr.printPipe

        # set max line length #
        if SysMgr.outPath:
            maxLineLen = SysMgr.lineLength
        else:
            maxLineLen = min(SysMgr.lineLength, SysMgr.ttyCols)

        oneLineLoc = oneLine[:maxLineLen]
        twoLineLoc = twoLine[:maxLineLen]

        convNum = UtilMgr.convNum

        # structures #
        """
        #define EI_NIDENT 16

        /* Type for a 16-bit quantity.  */
        typedef uint16_t Elf32_Half;
        typedef uint16_t Elf64_Half;

        /* Types for signed and unsigned 32-bit quantities.  */
        typedef uint32_t Elf32_Word;
        typedef int32_t  Elf32_Sword;
        typedef uint32_t Elf64_Word;
        typedef int32_t  Elf64_Sword;

        /* Types for signed and unsigned 64-bit quantities.  */
        typedef uint64_t Elf32_Xword;
        typedef int64_t  Elf32_Sxword;
        typedef uint64_t Elf64_Xword;
        typedef int64_t  Elf64_Sxword;

        /* Type of addresses.  */
        typedef uint32_t Elf32_Addr;
        typedef uint64_t Elf64_Addr;

        /* Type of file offsets.  */
        typedef uint32_t Elf32_Off;
        typedef uint64_t Elf64_Off;

        /* Type for section indices, which are 16-bit quantities.  */
        typedef uint16_t Elf32_Section;
        typedef uint16_t Elf64_Section;

        /* Type for version symbol information.  */
        typedef Elf32_Half Elf32_Versym;
        typedef Elf64_Half Elf64_Versym;

        typedef struct
        {
          unsigned char e_ident[EI_NIDENT];     /* Magic number and other info */
          Elf32_Half    e_type;                 /* Object file type */
          Elf32_Half    e_machine;              /* Architecture */
          Elf32_Word    e_version;              /* Object file version */
          Elf32_Addr    e_entry;                /* Entry point virtual address */
          Elf32_Off     e_phoff;                /* Program header table file offset */
          Elf32_Off     e_shoff;                /* Section header table file offset */
          Elf32_Word    e_flags;                /* Processor-specific flags */
          Elf32_Half    e_ehsize;               /* ELF header size in bytes */
          Elf32_Half    e_phentsize;            /* Program header table entry size */
          Elf32_Half    e_phnum;                /* Program header table entry count */
          Elf32_Half    e_shentsize;            /* Section header table entry size */
          Elf32_Half    e_shnum;                /* Section header table entry count */
          Elf32_Half    e_shstrndx;             /* Section header string table index */
        } Elf32_Ehdr;

        typedef struct
        {
          unsigned char e_ident[EI_NIDENT];     /* Magic number and other info */
          Elf64_Half    e_type;                 /* Object file type */
          Elf64_Half    e_machine;              /* Architecture */
          Elf64_Word    e_version;              /* Object file version */
          Elf64_Addr    e_entry;                /* Entry point virtual address */
          Elf64_Off     e_phoff;                /* Program header table file offset */
          Elf64_Off     e_shoff;                /* Section header table file offset */
          Elf64_Word    e_flags;                /* Processor-specific flags */
          Elf64_Half    e_ehsize;               /* ELF header size in bytes */
          Elf64_Half    e_phentsize;            /* Program header table entry size */
          Elf64_Half    e_phnum;                /* Program header table entry count */
          Elf64_Half    e_shentsize;            /* Section header table entry size */
          Elf64_Half    e_shnum;                /* Section header table entry count */
          Elf64_Half    e_shstrndx;             /* Section header string table index */
        } Elf64_Ehdr;

        typedef struct
        {
          Elf32_Word    sh_name;                /* Section name (string tbl index) */
          Elf32_Word    sh_type;                /* Section type */
          Elf32_Word    sh_flags;               /* Section flags */
          Elf32_Addr    sh_addr;                /* Section virtual addr at execution */
          Elf32_Off     sh_offset;              /* Section file offset */
          Elf32_Word    sh_size;                /* Section size in bytes */
          Elf32_Word    sh_link;                /* Link to another section */
          Elf32_Word    sh_info;                /* Additional section information */
          Elf32_Word    sh_addralign;           /* Section alignment */
          Elf32_Word    sh_entsize;             /* Entry size if section holds table */
        } Elf32_Shdr;

        typedef struct
        {
          Elf64_Word    sh_name;                /* Section name (string tbl index) */
          Elf64_Word    sh_type;                /* Section type */
          Elf64_Xword   sh_flags;               /* Section flags */
          Elf64_Addr    sh_addr;                /* Section virtual addr at execution */
          Elf64_Off     sh_offset;              /* Section file offset */
          Elf64_Xword   sh_size;                /* Section size in bytes */
          Elf64_Word    sh_link;                /* Link to another section */
          Elf64_Word    sh_info;                /* Additional section information */
          Elf64_Xword   sh_addralign;           /* Section alignment */
          Elf64_Xword   sh_entsize;             /* Entry size if section holds table */
        } Elf64_Shdr;

        typedef struct
        {
          Elf32_Word    p_type;                 /* Segment type */
          Elf32_Off     p_offset;               /* Segment file offset */
          Elf32_Addr    p_vaddr;                /* Segment virtual address */
          Elf32_Addr    p_paddr;                /* Segment physical address */
          Elf32_Word    p_filesz;               /* Segment size in file */
          Elf32_Word    p_memsz;                /* Segment size in memory */
          Elf32_Word    p_flags;                /* Segment flags */
          Elf32_Word    p_align;                /* Segment alignment */
        } Elf32_Phdr;

        typedef struct
        {
          Elf64_Word    p_type;                 /* Segment type */
          Elf64_Word    p_flags;                /* Segment flags */
          Elf64_Off     p_offset;               /* Segment file offset */
          Elf64_Addr    p_vaddr;                /* Segment virtual address */
          Elf64_Addr    p_paddr;                /* Segment physical address */
          Elf64_Xword   p_filesz;               /* Segment size in file */
          Elf64_Xword   p_memsz;                /* Segment size in memory */
          Elf64_Xword   p_align;                /* Segment alignment */
        } Elf64_Phdr;

        typedef struct {
                Elf32_Sword d_tag;
                union {
                        Elf32_Word      d_val;
                        Elf32_Addr      d_ptr;
                        Elf32_Off       d_off;
                } d_un;
        } Elf32_Dyn;

        typedef struct {
                Elf64_Xword d_tag;
                union {
                        Elf64_Xword     d_val;
                        Elf64_Addr      d_ptr;
                } d_un;
        } Elf64_Dyn;

        typedef struct
        {
          Elf32_Word    st_name;                /* Symbol name (string tbl index) */
          Elf32_Addr    st_value;               /* Symbol value */
          Elf32_Word    st_size;                /* Symbol size */
          unsigned char st_info;                /* Symbol type and binding */
          unsigned char st_other;               /* Symbol visibility */
          Elf32_Section st_shndx;               /* Section index */
        } Elf32_Sym;

        typedef struct
        {
          Elf64_Word    st_name;                /* Symbol name (string tbl index) */
          unsigned char st_info;                /* Symbol type and binding */
          unsigned char st_other;               /* Symbol visibility */
          Elf64_Section st_shndx;               /* Section index */
          Elf64_Addr    st_value;               /* Symbol value */
          Elf64_Xword   st_size;                /* Symbol size */
        } Elf64_Sym;

        typedef struct
        {
          Elf32_Addr    r_offset;               /* Address */
          Elf32_Word    r_info;                 /* Relocation type and symbol index */
        } Elf32_Rel;

        /* I have seen two different definitions of the Elf64_Rel and
           Elf64_Rela structures, so we'll leave them out until Novell (or
           whoever) gets their act together.  */
        /* The following, at least, is used on Sparc v9, MIPS, and Alpha.  */

        typedef struct
        {
          Elf64_Addr    r_offset;               /* Address */
          Elf64_Xword   r_info;                 /* Relocation type and symbol index */
        } Elf64_Rel;

        /* Relocation table entry with addend (in section of type SHT_RELA).  */

        typedef struct
        {
          Elf32_Addr    r_offset;               /* Address */
          Elf32_Word    r_info;                 /* Relocation type and symbol index */
          Elf32_Sword   r_addend;               /* Addend */
        } Elf32_Rela;

        typedef struct
        {
          Elf64_Addr    r_offset;               /* Address */
          Elf64_Xword   r_info;                 /* Relocation type and symbol index */
          Elf64_Sxword  r_addend;               /* Addend */
        } Elf64_Rela;

        /* Version definition sections.  */

        typedef struct
        {
          Elf32_Half    vd_version;             /* Version revision */
          Elf32_Half    vd_flags;               /* Version information */
          Elf32_Half    vd_ndx;                 /* Version Index */
          Elf32_Half    vd_cnt;                 /* Number of associated aux entries */
          Elf32_Word    vd_hash;                /* Version name hash value */
          Elf32_Word    vd_aux;                 /* Offset in bytes to verdaux array */
          Elf32_Word    vd_next;                /* Offset in bytes to next verdef
                                                   entry */
        } Elf32_Verdef;

        typedef struct
        {
          Elf64_Half    vd_version;             /* Version revision */
          Elf64_Half    vd_flags;               /* Version information */
          Elf64_Half    vd_ndx;                 /* Version Index */
          Elf64_Half    vd_cnt;                 /* Number of associated aux entries */
          Elf64_Word    vd_hash;                /* Version name hash value */
          Elf64_Word    vd_aux;                 /* Offset in bytes to verdaux array */
          Elf64_Word    vd_next;                /* Offset in bytes to next verdef
                                                   entry */
        } Elf64_Verdef;

        /* Auxialiary version information.  */

        typedef struct
        {
          Elf32_Word    vda_name;               /* Version or dependency names */
          Elf32_Word    vda_next;               /* Offset in bytes to next verdaux
                                                   entry */
        } Elf32_Verdaux;

        typedef struct
        {
          Elf64_Word    vda_name;               /* Version or dependency names */
          Elf64_Word    vda_next;               /* Offset in bytes to next verdaux
                                                   entry */
        } Elf64_Verdaux;


        /* Version dependency section.  */

        typedef struct
        {
          Elf32_Half    vn_version;             /* Version of structure */
          Elf32_Half    vn_cnt;                 /* Number of associated aux entries */
          Elf32_Word    vn_file;                /* Offset of filename for this
                                                   dependency */
          Elf32_Word    vn_aux;                 /* Offset in bytes to vernaux array */
          Elf32_Word    vn_next;                /* Offset in bytes to next verneed
                                                   entry */
        } Elf32_Verneed;

        typedef struct
        {
          Elf64_Half    vn_version;             /* Version of structure */
          Elf64_Half    vn_cnt;                 /* Number of associated aux entries */
          Elf64_Word    vn_file;                /* Offset of filename for this
                                                   dependency */
          Elf64_Word    vn_aux;                 /* Offset in bytes to vernaux array */
          Elf64_Word    vn_next;                /* Offset in bytes to next verneed
                               entry */
        } Elf64_Verneed;

        /* Auxiliary needed version information.  */

        typedef struct
        {
          Elf32_Word    vna_hash;               /* Hash value of dependency name */
          Elf32_Half    vna_flags;              /* Dependency specific information */
          Elf32_Half    vna_other;              /* Unused */
          Elf32_Word    vna_name;               /* Dependency name string offset */
          Elf32_Word    vna_next;               /* Offset in bytes to next vernaux
                                                   entry */
        } Elf32_Vernaux;

        typedef struct
        {
          Elf64_Word    vna_hash;               /* Hash value of dependency name */
          Elf64_Half    vna_flags;              /* Dependency specific information */
          Elf64_Half    vna_other;              /* Unused */
          Elf64_Word    vna_name;               /* Dependency name string offset */
          Elf64_Word    vna_next;               /* Offset in bytes to next vernaux
                                                   entry */
        } Elf64_Vernaux;

        /* This holds instructions for unwinding frame at a particular PC location
           described by an FDE.  */
        typedef struct
        {
          /* This frame description covers PC values in [start, end).  */
          Dwarf_Addr start;
          Dwarf_Addr end;

          Dwarf_CFI *cache;

          /* Previous state saved by DW_CFA_remember_state, or .cie->initial_state,
             or NULL in an initial_state pseudo-frame.  */
          Dwarf_Frame *prev;

          /* The FDE that generated this frame state.  This points to its CIE,
             which has the return_address_register and signal_frame flag.  */
          struct dwarf_fde *fde;

          /* The CFA is unknown, is R+N, or is computed by a DWARF expression.
             A bogon in the CFI can indicate an invalid/incalculable rule.
             We store that as cfa_invalid rather than barfing when processing it,
             so callers can ignore the bogon unless they really need that CFA.  */
          enum { cfa_undefined, cfa_offset, cfa_expr, cfa_invalid } cfa_rule;
          union
          {
            Dwarf_Op offset;
            Dwarf_Block expr;
          } cfa_data;
          /* We store an offset rule as a DW_OP_bregx operation.  */
        #define cfa_val_reg cfa_data.offset.number
        #define cfa_val_offset  cfa_data.offset.number2

          size_t nregs;
          struct dwarf_frame_register regs[];
        } Dwarf_Frame;

        typedef GElf_Off Dwarf_Off;
        typedef Elf64_Off GElf_Off;

        typedef Elf64_Xword GElf_Xword;
        typedef uint64_t Elf64_Xword;

        typedef Elf64_Sxword GElf_Sxword;
        typedef int64_t  Elf64_Sxword;

        /* This describes one Common Information Entry read from a CFI section.
           Pointers here point into the DATA->d_buf block passed to dwarf_next_cfi.  */
        typedef struct
        {
          Dwarf_Off CIE_id;  /* Always DW_CIE_ID_64 in Dwarf_CIE structures.  */

          /* Instruction stream describing initial state used by FDEs.  If
             we did not understand the whole augmentation string and it did
             not use 'z', then there might be more augmentation data here
             (and in FDEs) before the actual instructions.  */
          const uint8_t *initial_instructions;
          const uint8_t *initial_instructions_end;

          Dwarf_Word code_alignment_factor;
          Dwarf_Sword data_alignment_factor;
          Dwarf_Word return_address_register;

          const char *augmentation; /* Augmentation string.  */

          /* Augmentation data, might be NULL.  The size is correct only if
             we understood the augmentation string sufficiently.  */
          const uint8_t *augmentation_data;
          size_t augmentation_data_size;
          size_t fde_augmentation_data_size;
        } Dwarf_CIE;

        /* This describes one Frame Description Entry read from a CFI section.
           Pointers here point into the DATA->d_buf block passed to dwarf_next_cfi.  */
        typedef struct
        {
          /* Section offset of CIE this FDE refers to.  This will never be
             DW_CIE_ID_64 in an FDE.  If this value is DW_CIE_ID_64, this is
             actually a Dwarf_CIE structure.  */
          Dwarf_Off CIE_pointer;

          /* We can't really decode anything further without looking up the CIE
             and checking its augmentation string.  Here follows the encoded
             initial_location and address_range, then any augmentation data,
             then the instruction stream.  This FDE describes PC locations in
             the byte range [initial_location, initial_location+address_range).
             When the CIE augmentation string uses 'z', the augmentation data is
             a DW_FORM_block (self-sized).  Otherwise, when we understand the
             augmentation string completely, fde_augmentation_data_size gives
             the number of bytes of augmentation data before the instructions.  */
          const uint8_t *start;
          const uint8_t *end;
        } Dwarf_FDE;
        """

        # define attributes #
        self.addrTable = {}
        self.ret = True
        self.path = path
        self.revision = __revision__
        self.attr = {}
        self.is32Bit = True
        self.saved = False
        self.sortedSymTable = []
        self.sortedAddrTable = []
        self.sortedLineTable = []
        self.sortedAddrLineTable = []
        self.srcTable = []
        self.srcIdxTable = {}
        self.mergedSymTable = {}
        self.cfaTableTitle = ""
        self.fileSize = size
        self.onlyFunc = False
        self.loadAddr = 0
        self.archiveMap = {}
        self.archiveFile = {}
        self.archiveList = []
        self.filtered = (
            SysMgr.environList["ELFSYMFILTER"]
            if "ELFSYMFILTER" in SysMgr.environList
            else None
        )

        # get target section info #
        targetSect = SysMgr.environList.get("TARGETSECT", ["*"])
        exceptSect = SysMgr.environList.get("EXCEPTSECT", [])

        def _isValidSect(name):
            if exceptSect and UtilMgr.isValidStr(name, exceptSect):
                return False
            elif UtilMgr.isValidStr(name, targetSect):
                return True
            else:
                return False

        # check archive file #
        if UtilMgr.isZipFile(path):
            self.archiveMap = UtilMgr.getZipFileMap(path, True)
            self.attr["zipMap"] = self.archiveMap
            pathMap = {}
            isTarget = _isValidSect("zip")
            printable = debug and isTarget

            if printable:
                printer(
                    (
                        "[Zip List] (Path: %s%s)\n%s\n"
                        "%16s(%16s) %16s %16s(%5s) %s\n%s"
                    )
                    % (
                        path,
                        UtilMgr.getFileSizeStr(path),
                        twoLineLoc,
                        "Offset",
                        "Hex",
                        "Size",
                        "Compressed",
                        "%",
                        "Path",
                        oneLineLoc,
                    )
                )

            for n, v in sorted(
                self.archiveMap.items(), key=lambda e: e[1]["offset"]
            ):
                try:
                    size = v["size"]
                    comp = v["compsize"]
                    offset = v["offset"]
                    ratio = (comp / float(size)) * 100
                except SystemExit:
                    sys.exit(0)
                except:
                    ratio = 0

                if printable:
                    printer(
                        "%16s(%16s) %16s %16s(%3.1f) %12s"
                        % (
                            convNum(offset),
                            hex(offset),
                            convNum(size),
                            convNum(comp),
                            ratio,
                            n,
                        )
                    )

                # add to archive list #
                mpath = os.path.realpath(path) + "@" + n.replace("/", "_")
                self.archiveList.append([mpath, size, offset])
                self.archiveFile[mpath] = offset
                pathMap[mpath] = n

            if printable:
                if not self.archiveMap:
                    printer("\tNone")
                printer(oneLineLoc)

            if printable or "INCZIPOBJ" in SysMgr.environList:
                # print embedded ELF objects #
                for i in self.archiveList:
                    mpath, size, offset = i
                    mdata = SysMgr.readFile(
                        path, size, byte=True, offset=offset
                    )
                    mfd = MemoryFile(size=size, name=mpath, data=mdata)
                    mobj = ElfAnalyzer(path=mpath, debug=debug, fd=mfd)
                    self.attr["zipMap"][pathMap[mpath]]["attr"] = mobj.attr

            # save file size #
            self.fileSize = os.stat(path).st_size

            return None

        # symbolize oat image #
        path = SysMgr.symbolizeOat(path, debug=debug, printer=printer)

        # link to debug object #
        if fd is None:
            debugPath = None
            for i in SysMgr.environList.get("REPELF", []):
                src, des = i.split(":", 1)
                if src == path:
                    debugPath = des
                    break

            # check debug file #
            if not debugPath and not "NODEBUG" in SysMgr.environList:
                dirname, filename = UtilMgr.getPath(path)
                # .debug #
                debugPath = "%s/.debug/%s" % (dirname, filename)
                if not os.path.isfile(debugPath):
                    # /usr/lib/debug/usr #
                    debugPath = SysMgr.debugDirPath + path
                    if not os.path.isfile(debugPath):
                        try:
                            # /usr/lib/debug/lib #
                            newPos = path.find("/", 2)
                            debugPath = SysMgr.debugDirPath + path[newPos:]
                            if not os.path.isfile(debugPath):
                                debugPath = None
                        except SystemExit:
                            sys.exit(0)
                        except:
                            pass

            # merge a debug file #
            if debugPath:
                SysMgr.printInfo("merge %s's debug symbols" % debugPath)

                dobj = ElfAnalyzer(debugPath, debug=debug, origPath=self.path)
                if dobj:
                    dobj.mergeSymTable()
                    self.addrTable.update(dobj.addrTable)
                    dobj.addrTable.clear()
                self.attr["symTable"] = UtilMgr.deepcopy(dobj.attr["symTable"])
                self.attr["dynsymTable"] = UtilMgr.deepcopy(
                    dobj.attr["dynsymTable"]
                )
                if "dwarf" in dobj.attr:
                    self.attr["dwarf"] = UtilMgr.deepcopy(dobj.attr["dwarf"])
                del dobj

            # check file #
            if not os.path.exists(path):
                raise Exception("no file")

            # open file #
            try:
                fd = open(path, "rb")
            except:
                if debug:
                    SysMgr.printOpenErr(path)
                else:
                    SysMgr.printOpenWarn(path)

                err = SysMgr.getErrMsg()
                raise Exception(err)

            # save file size #
            self.fileSize = os.stat(path).st_size

        # update absolute path #
        try:
            abspath = os.path.realpath(fd.name)
            if os.path.exists(abspath):
                self.path = abspath
        except:
            pass

        # define default file type #
        e_type = e_class = "dummpy"
        EI_NIDENT = 16

        # define err string #
        errStr = "failed to recognize '%s' as an ELF object because %s"

        # check size #
        if self.fileSize < EI_NIDENT:
            size = UtilMgr.convSize2Unit(self.fileSize)
            SysMgr.printWarn(
                errStr % (path, "it's size is just %s" % size), debug
            )
            self.ret = None
            return None

        # parse ELF header #
        ei_ident = struct.unpack("16B", fd.read(EI_NIDENT))
        (
            ei_mag0,
            ei_mag1,
            ei_mag2,
            ei_mag3,
            ei_class,
            ei_data,
            ei_version,
            ei_osabi,
            ei_abiversion,
            ei_pad,
        ) = ei_ident[:10]

        # check abi #
        e_osabi = ElfAnalyzer.EI_OSABI.get(ei_osabi)

        # check magic number #
        if (
            ei_mag0 != 0x7F
            and ei_mag1 != ord("E")
            and ei_mag2 != ord("L")
            and ei_mag3 != ord("F")
        ):
            SysMgr.printWarn(
                errStr % (path, "it is not the ELF object"), debug
            )
            self.ret = None
            return None

        # check 32/64-bit type #
        if ei_class == 1:
            self.is32Bit = True
            e_class = "32-bit objects"
            wordSize = 4
        elif ei_class == 2:
            self.is32Bit = False
            e_class = "64-bit objects"
            wordSize = 8
        else:
            SysMgr.printWarn(errStr % (path, "it is invalid class"), debug)
            self.ret = None
            return None

        # check data encoding (endian) #
        if ei_data == 1:
            e_data = "ELFDATA2LSB"
        elif ei_data == 2:
            e_data = "ELFDATA2MSB"
        else:
            SysMgr.printWarn(
                errStr % (path, "it is invalid for data encoding"), debug
            )
            self.ret = None
            return None

        # check file type #
        ei_type = struct.unpack("H", fd.read(2))[0]
        try:
            e_type = ElfAnalyzer.EI_TYPE[ei_type]
        except:
            e_type = "N/A"

        # check machine type #
        ei_machine = struct.unpack("H", fd.read(2))[0]
        if ei_machine in ElfAnalyzer.EI_MACHINE_TYPE:
            e_machine = ElfAnalyzer.EI_MACHINE_TYPE[ei_machine]
        else:
            e_machine = "Unknow machine"

        # update Program Table on arch #
        if e_machine.startswith("ARM"):
            ElfAnalyzer.PT_TYPE.update(
                {0x70000000: "ARCHEXT", 0x70000001: "EXIDX"}
            )

            if e_machine.startswith("ARM 32"):
                ElfAnalyzer.SH_TYPE.update(
                    {
                        0x70000001: "EXIDX",
                        0x70000002: "PREEMPTMAP",
                        0x70000003: "ATTRIBUTES",
                    }
                )

                ElfAnalyzer.RELOC_TYPE = ElfAnalyzer.RELOC_TYPE_ARM
                regList = ConfigMgr.REGS_ARM
            else:
                ElfAnalyzer.RELOC_TYPE = ElfAnalyzer.RELOC_TYPE_AARCH64
                regList = ConfigMgr.REGS_AARCH64
        elif e_machine.startswith("AMD x86-64") or e_machine.startswith(
            "Intel IA-64"
        ):
            ElfAnalyzer.PT_TYPE.update(
                {
                    0x60000012: "HP_OPT_ANOT",
                    0x60000013: "HP_HSL_ANOT",
                    0x60000014: "HP_STACK",
                    0x70000000: "ARCHEXT",
                    0x70000001: "UNWIND",
                }
            )

            ElfAnalyzer.SH_TYPE.update(
                {0x70000000: "EXT", 0x70000001: "UNWIND"}
            )

            ElfAnalyzer.RELOC_TYPE = ElfAnalyzer.RELOC_TYPE_x64
            regList = ConfigMgr.REGS_X64
        elif e_machine.startswith("Intel "):
            ElfAnalyzer.RELOC_TYPE = ElfAnalyzer.RELOC_TYPE_x86
            regList = ConfigMgr.REGS_X86
        elif e_machine.startswith("RISC-V"):
            ElfAnalyzer.RELOC_TYPE = ElfAnalyzer.RELOC_TYPE_RISCV64
            regList = ConfigMgr.REGS_RISCV64
        else:
            SysMgr.printErr("failed to recognize machine '%s'" % e_machine)

        # check version #
        ei_version = struct.unpack("I", fd.read(4))[0]
        if ei_version == 0:
            e_version = "illegal version"
        else:
            e_version = str(ei_version)

        # parse 32-bit ELF header #
        if self.is32Bit:
            (
                e_entry,
                e_phoff,
                e_shoff,
                e_flags,
                e_ehsize,
                e_phentsize,
                e_phnum,
                e_shentsize,
                e_shnum,
                e_shstrndx,
            ) = struct.unpack("IIIIHHHHHH", fd.read(28))
        # parse 64-bit ELF header #
        else:
            (
                e_entry,
                e_phoff,
                e_shoff,
                e_flags,
                e_ehsize,
                e_phentsize,
                e_phnum,
                e_shentsize,
                e_shnum,
                e_shstrndx,
            ) = struct.unpack("QQQIHHHHHH", fd.read(40))

        # save header info #
        self.attr.setdefault("elfHeader", {})
        self.attr["elfHeader"][
            "magic"
        ] = "%02x %02x %02x %02x %02x %02x %02x %02x" % (
            ei_mag0,
            ei_mag1,
            ei_mag2,
            ei_mag3,
            ei_class,
            ei_data,
            ei_version,
            ei_pad,
        )
        self.attr["elfHeader"]["class"] = e_class
        self.attr["elfHeader"]["data"] = e_data
        self.attr["elfHeader"]["type"] = e_type
        self.attr["elfHeader"]["machine"] = e_machine
        self.attr["elfHeader"]["version"] = e_version
        self.attr["elfHeader"]["osabi"] = e_osabi
        self.attr["elfHeader"]["abiversion"] = ei_abiversion
        self.attr["elfHeader"]["entry"] = e_entry
        self.attr["elfHeader"]["phoff"] = e_phoff
        self.attr["elfHeader"]["shoff"] = e_shoff
        self.attr["elfHeader"]["flags"] = e_flags
        self.attr["elfHeader"]["ehsize"] = e_ehsize
        self.attr["elfHeader"]["phentsize"] = e_phentsize
        self.attr["elfHeader"]["shnum"] = e_shnum
        self.attr["elfHeader"]["shentsize"] = e_shentsize
        self.attr["elfHeader"]["shnum"] = e_shnum
        self.attr["elfHeader"]["shstrndx"] = e_shstrndx

        # check onlyHdr flag #
        if onlyHdr:
            return None

        # print header info #
        if debug and _isValidSect("header"):
            printer(
                """\

[ELF Header]
%s
Path:                              %s
Magic:                             %s
Class:                             %s
Data:                              %s
Type:                              %s
Machine:                           %s
Version:                           %s
OS/ABI:                            %s
ABI Version:                       %s
Entry point address:               0x%x
Start of program headers:          %d (bytes into file)
Start of section headers:          %d (bytes into file)
Flags:                             0x%02x
Size of this header:               %d (bytes)
Size of program header:            %d (bytes)
Number of program headers:         %d
Size of section headers:           %d (bytes)
Number of section headers:         %d
Section header string table index: %d
%s
            """
                % (
                    twoLineLoc,
                    self.path,
                    self.attr["elfHeader"]["magic"],
                    e_class,
                    e_data,
                    e_type,
                    e_machine,
                    e_version,
                    e_osabi,
                    ei_abiversion,
                    e_entry,
                    e_phoff,
                    e_shoff,
                    e_flags,
                    e_ehsize,
                    e_phentsize,
                    e_shnum,
                    e_shentsize,
                    e_shnum,
                    e_shstrndx,
                    twoLineLoc,
                )
            )

        def _printStrSect(sh_name, strtab):
            # get symbol string #
            shname = self.getString(str_section, sh_name)

            # check target #
            isTarget = _isValidSect(shname)
            printable = debug and isTarget and "PRINTSTR" in SysMgr.environList

            if printable:
                printer("\n[%s Section]\n%s" % (shname, twoLineLoc))

                try:
                    SysMgr.printList(
                        list(
                            re.findall(
                                "[^\x00-\x1F\x7F-\xFF]{4,}", strtab.decode()
                            )
                        ),
                        printer=printer,
                    )
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

                printer(oneLineLoc)

        # parse string section #
        str_section = ""
        if e_shstrndx > 0:
            (
                sh_name,
                sh_type,
                sh_flags,
                sh_addr,
                sh_offset,
                sh_size,
                sh_link,
                sh_info,
                sh_addralign,
                sh_entsize,
            ) = self.getSectionInfo(fd, e_shoff + e_shentsize * e_shstrndx)
            fd.seek(sh_offset)
            str_section = fd.read(sh_size)
            _printStrSect(sh_name, str_section)

        # define program info #
        self.attr["progHeader"] = []
        e_notelist = {}

        # check target #
        isTarget = _isValidSect("program")
        printable = debug and isTarget

        # print program header title #
        if printable and e_phnum:
            printer(
                (
                    "[Program Headers]\n%s\n"
                    "%16s %10s %16s %16s %12s %12s %10s\n%s"
                )
                % (
                    twoLineLoc,
                    "Type",
                    "Offset",
                    "VirtAddr",
                    "PhysAddr",
                    "FileSize",
                    "MemSize",
                    "Flags",
                    twoLineLoc,
                )
            )

        # parse program sections #
        e_shinterpndx = -1
        PT_TYPE = ElfAnalyzer.PT_TYPE
        for i in xrange(e_phnum):
            fd.seek(e_phoff + e_phentsize * i)

            # 32-bit #
            if self.is32Bit:
                (
                    p_type,
                    p_offset,
                    p_vaddr,
                    p_paddr,
                    p_filesz,
                    p_memsz,
                    p_flags,
                    p_align,
                ) = struct.unpack("IIIIIIII", fd.read(32))
            # 64-bit #
            else:
                (
                    p_type,
                    p_flags,
                    p_offset,
                    p_vaddr,
                    p_paddr,
                    p_filesz,
                    p_memsz,
                    p_align,
                ) = struct.unpack("IIQQQQQQ", fd.read(56))

            # INTERP #
            if p_type == 3:
                e_shinterpndx = i

            # get flags #
            if p_flags in ElfAnalyzer.PT_FLAGS:
                flags = ElfAnalyzer.PT_FLAGS[p_flags]
            else:
                flags = ""

            # convert type #
            if p_type in PT_TYPE:
                typeval = PT_TYPE[p_type]
            else:
                typeval = p_type

            # save program info #
            self.attr["progHeader"].append(
                [typeval, p_offset, p_vaddr, p_paddr, p_filesz, p_memsz, flags]
            )

            # convert type for print #
            if p_type in PT_TYPE:
                typestr = PT_TYPE[p_type]
            elif PT_TYPE["LOOS"] <= p_type <= PT_TYPE["HIOS"]:
                typestr = "LOOS+0x%lx" % (p_type - PT_TYPE["LOOS"])
            else:
                typestr = "<unknown>"

            # save load address #
            if typestr == "LOAD" and flags == "RE":
                self.loadAddr = p_vaddr
            elif typestr == "NOTE":
                e_notelist.setdefault(p_offset, p_filesz)

            if not debug:
                continue

            # print program header #
            if printable:
                printer(
                    "%16s 0x%08x 0x%014x 0x%014x 0x%010x 0x%010x %010s"
                    % (
                        typestr,
                        p_offset,
                        p_vaddr,
                        p_paddr,
                        p_filesz,
                        p_memsz,
                        flags,
                    )
                )

        if debug and e_phnum and isTarget:
            printer(oneLineLoc)

        if e_shinterpndx >= 0:
            fd.seek(e_phoff + e_phentsize * e_shinterpndx)

            # 32-bit #
            if self.is32Bit:
                (
                    p_type,
                    p_offset,
                    p_vaddr,
                    p_paddr,
                    p_filesz,
                    p_memsz,
                    p_flags,
                    p_align,
                ) = struct.unpack("IIIIIIII", fd.read(32))
            # 64-bit #
            else:
                (
                    p_type,
                    p_flags,
                    p_offset,
                    p_vaddr,
                    p_paddr,
                    p_filesz,
                    p_memsz,
                    p_align,
                ) = struct.unpack("IIQQQQQQ", fd.read(56))

            # move pos #
            fd.seek(p_offset + p_filesz)

        # initialize indexes #
        e_sharmidx = -1
        e_shdbgabbrev = -1
        e_shdbgdata = -1
        e_shdbgframe = -1
        e_shdbginfo = -1
        e_shdbglink = -1
        e_shdbgaltlink = -1
        e_shdbgloc = -1
        e_shdbgranges = -1
        e_shdbgline = -1
        e_shdbglinestr = -1
        e_shdbgstr = -1
        e_shdynamic = -1
        e_shdynstr = -1
        e_shdynsym = -1
        e_shehframe = -1
        e_shehframehdr = -1
        e_shframe = -1
        e_shnotelist = {}
        e_shrelalist = []
        e_shandrelalist = []
        e_shrellist = []
        e_shandrellist = []
        e_shstrndx = -1
        e_shsymndx = -1
        e_shverdef = -1
        e_shverneed = -1
        e_shversym = -1

        # define section info #
        self.attr.setdefault("sectionHeader", {})

        # check target #
        isTarget = _isValidSect("section")
        printable = debug and isTarget

        # print section header title #
        if printable and e_shnum > 0:
            printer(
                (
                    "\n[Section Headers]\n%s\n"
                    "[NR] %50s%15s%12s%12s%13s(%5s)%8s%5s%5s%7s%6s\n%s"
                )
                % (
                    twoLineLoc,
                    "Name",
                    "Type",
                    "Address",
                    "Offset",
                    "Size",
                    "%",
                    "EntSize",
                    "Flag",
                    "Link",
                    "Info",
                    "Align",
                    twoLineLoc,
                )
            )

        # get export filter #
        exportFilter = (
            SysMgr.environList["EXPORTSECT"]
            if "EXPORTSECT" in SysMgr.environList
            else []
        )

        # parse section headers #
        for i in xrange(e_shnum):
            (
                sh_name,
                sh_type,
                sh_flags,
                sh_addr,
                sh_offset,
                sh_size,
                sh_link,
                sh_info,
                sh_addralign,
                sh_entsize,
            ) = self.getSectionInfo(fd, e_shoff + e_shentsize * i)

            # check flags #
            f = ""
            if sh_flags & ElfAnalyzer.SHF_WRITE:
                f += "W"
            if sh_flags & ElfAnalyzer.SHF_ALLOC:
                f += "A"
            if sh_flags & ElfAnalyzer.SHF_EXECINSTR:
                f += "X"
            if sh_flags & ElfAnalyzer.SHF_MERGE:
                f += "M"
            if sh_flags & ElfAnalyzer.SHF_STRINGS:
                f += "S"
            if sh_flags & ElfAnalyzer.SHF_INFO_LINK:
                f += "I"
            if sh_flags & ElfAnalyzer.SHF_LINK_ORDER:
                f += "L"
            if sh_flags & ElfAnalyzer.SHF_OS_NONCONFORMING:
                f += "O"
            if sh_flags & ElfAnalyzer.SHF_GROUP:
                f += "G"
            if sh_flags & ElfAnalyzer.SHF_TLS:
                f += "T"
            if sh_flags & ElfAnalyzer.SHF_COMPRESSED:
                f += "C"
            if sh_flags & ElfAnalyzer.SHF_EXCLUDE:
                f += "E"
            if sh_flags & ElfAnalyzer.SHF_MASKPROC:
                f += "P"

            # get symbol string #
            symbol = self.getString(str_section, sh_name)

            stype = (
                ElfAnalyzer.SH_TYPE[sh_type]
                if sh_type in ElfAnalyzer.SH_TYPE
                else sh_type
            )

            self.attr["sectionHeader"][symbol] = {
                "type": stype,
                "addr": sh_addr,
                "offset": sh_offset,
                "size": sh_size,
                "entSize": sh_entsize,
                "flag": f,
                "link": sh_link,
                "info": sh_info,
                "align": sh_addralign,
            }

            # print section header #
            if printable:
                try:
                    size_per = (sh_size / float(self.fileSize)) * 100
                except:
                    size_per = 0

                size_str = "%s(%5.1f)" % (convNum(sh_size), size_per)

                printer(
                    "[%02d] %50s%15s%12s%12s%20s%8s%5s%5s%7s%6s"
                    % (
                        i,
                        symbol,
                        ElfAnalyzer.SH_TYPE[sh_type]
                        if sh_type in ElfAnalyzer.SH_TYPE
                        else hex(sh_type),
                        "0x%x" % sh_addr,
                        "0x%x" % sh_offset,
                        size_str,
                        sh_entsize,
                        f,
                        sh_link,
                        sh_info,
                        sh_addralign,
                    )
                )

                # export section to file #
                try:
                    if (
                        not exportFilter
                        or not symbol
                        or (
                            exportFilter[0] != "SET"
                            and not UtilMgr.isValidStr(symbol, exportFilter)
                        )
                    ):
                        raise ValueError("no export filter")

                    # make output file name #
                    if SysMgr.outPath:
                        secFileName = (
                            os.path.realpath(
                                os.path.join(
                                    SysMgr.outPath, os.path.basename(path)
                                )
                            )
                            + symbol
                        )
                    else:
                        secFileName = os.path.realpath(path + symbol)

                    # extract section to file #
                    curPos = fd.tell()
                    fd.seek(sh_offset)
                    secData = fd.read(sh_size)
                    SysMgr.writeFile(secFileName, secData, byte=True)

                    SysMgr.printInfo(
                        "exported '%s' section of '%s' to '%s'"
                        % (symbol, path, secFileName)
                    )
                    fd.seek(curPos)
                except ValueError:
                    pass
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr(
                        "failed to write '%s' section of '%s' to '%s'"
                        % (symbol, path, secFileName)
                    )
                    sys.exit(0)

            # get header index #
            if symbol == ".symtab":
                e_shsymndx = i
            elif symbol == ".strtab":
                e_shstrndx = i
            elif symbol == ".dynsym":
                e_shdynsym = i
            elif symbol == ".dynstr":
                e_shdynstr = i
            elif symbol == ".dynamic":
                e_shdynamic = i
            elif symbol == ".eh_frame":
                e_shehframe = i
            elif symbol in (
                ".debug_frame",
                ".zdebug_frame",
                ".debug_frame.dwo",
            ):
                e_shdbgframe = i
            elif symbol in (".debug_str", ".zdebug_str", ".debug_str.dwo"):
                e_shdbgstr = i
            elif symbol in (
                ".debug_line_str",
                ".zdebug_line_str",
                ".debug_line_str.dwo",
            ):
                e_shdbglinestr = i
            elif symbol in (".debug_line", ".zdebug_line", ".debug_line.dwo"):
                e_shdbgline = i
            elif symbol in (".debug_info", ".zdebug_info", ".debug_info.dwo"):
                e_shdbginfo = i
            elif symbol in (".debug_loc", ".zdebug_loc", ".debug_loc.dwo"):
                e_shdbgloc = i
            elif symbol in (
                ".debug_ranges",
                ".zdebug_ranges",
                ".debug_ranges.dwo",
            ):
                e_shdbgranges = i
            elif symbol in (
                ".debug_abbrev",
                ".zdebug_abbrev",
                ".debug_abbrev.dwo",
            ):
                e_shdbgabbrev = i
            elif symbol == ".eh_frame_hdr":
                e_shehframehdr = i
            elif symbol == ".gnu_debugdata":
                e_shdbgdata = i
            elif symbol == ".gnu_debuglink":
                e_shdbglink = i
            elif symbol == ".gnu_debugaltlink":
                e_shdbgaltlink = i
            elif symbol.startswith(".note."):
                e_shnotelist.setdefault(symbol, i)
            elif stype == "GNU_versym":
                e_shversym = i
            elif stype == "GNU_verdef":
                e_shverdef = i
            elif stype == "GNU_verneed":
                e_shverneed = i
            elif stype == "REL":
                e_shrellist.append(i)
            elif stype == "ANDROID_REL":
                e_shandrellist.append(i)
            elif stype == "RELA":
                e_shrelalist.append(i)
            elif stype == "ANDROID_RELA":
                e_shandrelalist.append(i)
            elif stype == "EXIDX":
                e_sharmidx = i
            elif stype == "PLTREL":
                pass
            elif stype == "JMPREL":
                pass

        if debug and e_shnum > 0 and isTarget:
            printer(oneLineLoc)

        # define versym info #
        self.attr["versymList"] = []

        # parse .gnu.version table #
        if e_shversym >= 0:
            # get .gnu.version section info #
            (
                sh_name,
                sh_type,
                sh_flags,
                sh_addr,
                sh_offset,
                sh_size,
                sh_link,
                sh_info,
                sh_addralign,
                sh_entsize,
            ) = self.getSectionInfo(fd, e_shoff + e_shentsize * e_shversym)

            # read .gnu.version data #
            fd.seek(sh_offset)
            versym_section = fd.read(sh_size)

            for i in xrange(long(sh_size / sh_entsize)):
                target = versym_section[i * sh_entsize : (i + 1) * sh_entsize]
                symidx = struct.unpack("H", target)[0]
                self.attr["versymList"].append(symidx)

        # define .dynsym info #
        self.attr.setdefault("dynsymTable", {})
        self.attr.setdefault("dynsymList", [""])  # STN_UNDEF == 0
        self.attr.setdefault("versionTable", {})

        # parse .dynsym table #
        if (
            e_shdynsym >= 0
            and e_shdynstr >= 0
            and self.attr["sectionHeader"][".dynsym"]["type"] != "NOBITS"
            and self.attr["sectionHeader"][".dynstr"]["type"] != "NOBITS"
        ):
            # get .dynstr section info #
            (
                sh_name,
                sh_type,
                sh_flags,
                sh_addr,
                sh_offset,
                sh_size,
                sh_link,
                sh_info,
                sh_addralign,
                sh_entsize,
            ) = self.getSectionInfo(fd, e_shoff + e_shentsize * e_shdynstr)

            # read .dynstr data #
            fd.seek(sh_offset)
            dynstr_section = fd.read(sh_size)
            try:
                dynstr_section_decoded = dynstr_section.decode()
            except SystemExit:
                sys.exit(0)
            except:
                dynstr_section_decoded = dynstr_section

            lastnull = 0
            dynsymTable = {}
            for i, s in enumerate(dynstr_section_decoded):
                if s == "\0":
                    try:
                        dynsymTable[lastnull] = dynstr_section[
                            lastnull:i
                        ].decode()
                    except SystemExit:
                        sys.exit(0)
                    except:
                        dynsymTable[lastnull] = dynstr_section[lastnull:i]
                    lastnull = i + 1

            # parse .gnu.version_d table #
            if e_shverdef >= 0:
                # get .gnu.version_d section info #
                (
                    sh_name,
                    sh_type,
                    sh_flags,
                    sh_addr,
                    sh_offset,
                    sh_size,
                    sh_link,
                    sh_info,
                    sh_addralign,
                    sh_entsize,
                ) = self.getSectionInfo(fd, e_shoff + e_shentsize * e_shverdef)

                # read .gnu.version_d data #
                fd.seek(sh_offset)
                verdef_section = fd.read(sh_size)

                # get verdef values #
                vdidx = 1
                offset = 0
                entsize = 20
                sentsize = 8
                for idx in xrange(sh_info):
                    target = verdef_section[offset : offset + entsize]
                    (
                        vd_version,
                        vd_flags,
                        vd_ndx,
                        vd_cnt,
                        vd_hash,
                        vd_aux,
                        vd_next,
                    ) = struct.unpack("HHHHIII", target)

                    # get verdef strings #
                    soffset = offset + vd_aux
                    for vidx in xrange(vd_cnt):
                        starget = verdef_section[soffset : soffset + sentsize]
                        vda_name, vda_next = struct.unpack("II", starget)

                        if vidx == 0:
                            self.attr["versionTable"][vdidx] = self.getString(
                                dynstr_section, vda_name
                            )

                            vdidx += 1

                        soffset += vda_next

                    offset += vd_next

            # parse .gnu.version_r table #
            if e_shverneed >= 0:
                # get .gnu.version_r section info #
                (
                    sh_name,
                    sh_type,
                    sh_flags,
                    sh_addr,
                    sh_offset,
                    sh_size,
                    sh_link,
                    sh_info,
                    sh_addralign,
                    sh_entsize,
                ) = self.getSectionInfo(
                    fd, e_shoff + e_shentsize * e_shverneed
                )

                # read .gnu.version_r data #
                fd.seek(sh_offset)
                verneed_section = fd.read(sh_size)

                # get verneed values #
                offset = 0
                entsize = 16
                for idx in xrange(sh_info):
                    target = verneed_section[offset : offset + entsize]
                    (
                        vn_version,
                        vn_cnt,
                        vn_file,
                        vn_aux,
                        vn_next,
                    ) = struct.unpack("HHIII", target)

                    # get verneed strings #
                    soffset = offset + entsize
                    for vidx in xrange(vn_cnt):
                        starget = verneed_section[soffset : soffset + entsize]
                        (
                            vna_hash,
                            vna_flags,
                            vna_other,
                            vna_name,
                            vna_next,
                        ) = struct.unpack("IHHII", starget)

                        self.attr["versionTable"][vna_other] = self.getString(
                            dynstr_section, vna_name
                        )

                        soffset += entsize

                    offset += vn_next

            # get .dynsym section info #
            (
                sh_name,
                sh_type,
                sh_flags,
                sh_addr,
                sh_offset,
                sh_size,
                sh_link,
                sh_info,
                sh_addralign,
                sh_entsize,
            ) = self.getSectionInfo(fd, e_shoff + e_shentsize * e_shdynsym)

            # read .dynsym data #
            fd.seek(sh_offset)
            dynsym_section = fd.read(sh_size)

            # get symbol string #
            shname = self.getString(str_section, sh_name)

            # check target #
            isTarget = _isValidSect(shname)
            printable = debug and isTarget

            nrItems = long(sh_size / sh_entsize) if isTarget else 0

            # print .dynsym table title #
            if printable:
                printer(
                    (
                        "\n[%s Section] (nrItem: %s)\n%s\n"
                        "%04s %16s%15s%10s%10s%10s%10s %30s\n%s"
                    )
                    % (
                        shname,
                        convNum(nrItems),
                        twoLineLoc,
                        "Num",
                        "Value",
                        "Size",
                        "Type",
                        "Bind",
                        "Vis",
                        "Ndx",
                        "Name",
                        twoLineLoc,
                    )
                )

            if nrItems == 0 and isTarget:
                printer("    None")

            printCnt = 0

            for i in xrange(nrItems):
                if not SysMgr.pipeForPager:
                    UtilMgr.printProgress(i, nrItems)

                target = dynsym_section[i * sh_entsize : (i + 1) * sh_entsize]
                # 32-bit #
                if self.is32Bit:
                    (
                        st_name,
                        st_value,
                        st_size,
                        st_info,
                        st_other,
                        st_shndx,
                    ) = struct.unpack("IIIBBH", target)
                # 64-bit #
                else:
                    (
                        st_name,
                        st_info,
                        st_other,
                        st_shndx,
                        st_value,
                        st_size,
                    ) = struct.unpack("IBBHQQ", target)

                # get symbol string #
                symbol = self.getString(dynstr_section, st_name)

                # convert manged string #
                if symbol:
                    symbol = ElfAnalyzer.demangleSymbol(symbol)

                # concatenate symbol with it's required version #
                try:
                    symIdx = len(self.attr["dynsymList"])
                    vsIdx = self.attr["versymList"][symIdx]
                    if symbol:
                        symbol = "%s@%s" % (
                            symbol,
                            self.attr["versionTable"][vsIdx],
                        )
                    else:
                        symbol = ""
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

                # check symbol filter #
                if self.filtered:
                    if not UtilMgr.isValidStr(symbol, self.filtered):
                        continue

                # add symbol to table #
                self.attr["dynsymTable"][symbol] = {
                    "value": st_value,
                    "size": st_size,
                    "type": ElfAnalyzer.ST_TYPE[
                        ElfAnalyzer.ELF_ST_TYPE(st_info)
                    ],
                    "bind": ElfAnalyzer.ST_BIND_TYPE[
                        ElfAnalyzer.ELF_ST_BIND(st_info)
                    ],
                    "vis": ElfAnalyzer.ST_VISIBILITY_TYPE[
                        ElfAnalyzer.ELF_ST_VISIBILITY(st_other)
                    ],
                    "ndx": st_shndx,
                }

                # add address-symbol mapping info #
                if SysMgr.dwarfEnable:
                    self.addrTable[st_value] = symbol

                # register symbol to dynamic symbol list #
                self.attr["dynsymList"].append(symbol)

                # get index #
                if st_shndx in ElfAnalyzer.SHN_TYPE:
                    if ElfAnalyzer.SHN_TYPE[st_shndx] == "SHN_UNDEF":
                        st_shndx = "UND"
                    elif ElfAnalyzer.SHN_TYPE[st_shndx] == "SHN_ABS":
                        st_shndx = "ABS"

                # print .dynsym table #
                if printable:
                    # apply filter #
                    if SysMgr.filterGroup:
                        if not UtilMgr.isValidStr(symbol):
                            continue

                    printer(
                        "%04d %016x%15s%10s%10s%10s%10s %s"
                        % (
                            i,
                            st_value,
                            convNum(st_size),
                            ElfAnalyzer.ST_TYPE[
                                ElfAnalyzer.ELF_ST_TYPE(st_info)
                            ],
                            ElfAnalyzer.ST_BIND_TYPE[
                                ElfAnalyzer.ELF_ST_BIND(st_info)
                            ],
                            ElfAnalyzer.ST_VISIBILITY_TYPE[
                                ElfAnalyzer.ELF_ST_VISIBILITY(st_other)
                            ],
                            st_shndx,
                            symbol,
                        )
                    )

                    printCnt += 1

            if printable:
                if printCnt == 0:
                    printer("    None")
                printer(oneLineLoc)

        # define .sym info #
        self.attr.setdefault("symTable", {})

        # parse .symtab table #
        if (
            e_shsymndx >= 0
            and e_shstrndx >= 0
            and self.attr["sectionHeader"][".symtab"]["type"] != "NOBITS"
            and self.attr["sectionHeader"][".strtab"]["type"] != "NOBITS"
        ):
            # get .symtab section info #
            (
                sh_name,
                sh_type,
                sh_flags,
                sh_addr,
                sh_offset,
                sh_size,
                sh_link,
                sh_info,
                sh_addralign,
                sh_entsize,
            ) = self.getSectionInfo(fd, e_shoff + e_shentsize * e_shstrndx)

            # read .strtab data #
            fd.seek(sh_offset)
            strtab_section = fd.read(sh_size)

            # get .symtab section info #
            (
                sh_name,
                sh_type,
                sh_flags,
                sh_addr,
                sh_offset,
                sh_size,
                sh_link,
                sh_info,
                sh_addralign,
                sh_entsize,
            ) = self.getSectionInfo(fd, e_shoff + e_shentsize * e_shsymndx)

            # read .symtab data #
            fd.seek(sh_offset)
            sym_section = fd.read(sh_size)

            # get symbol string #
            shname = self.getString(str_section, sh_name)

            # check target #
            isTarget = _isValidSect(shname)
            printable = debug and isTarget

            nrItems = long(sh_size / sh_entsize) if isTarget else 0

            # parse .sym table title #
            if printable:
                printer(
                    (
                        "\n[%s Section] (nrItem: %s)\n%s\n"
                        "%04s %16s%15s%10s%10s%10s%10s%30s\n%s"
                    )
                    % (
                        shname,
                        convNum(nrItems),
                        twoLineLoc,
                        "Num",
                        "Value",
                        "Size",
                        "Type",
                        "Bind",
                        "Vis",
                        "Ndx",
                        "Name",
                        twoLineLoc,
                    )
                )

            if nrItems == 0 and isTarget:
                printer("    None")

            printCnt = 0

            for i in xrange(nrItems):
                if not SysMgr.pipeForPager:
                    UtilMgr.printProgress(i, nrItems)

                target = sym_section[i * sh_entsize : (i + 1) * sh_entsize]
                if self.is32Bit:
                    (
                        st_name,
                        st_value,
                        st_size,
                        st_info,
                        st_other,
                        st_shndx,
                    ) = struct.unpack("IIIBBH", target)
                # 64-bit #
                else:
                    (
                        st_name,
                        st_info,
                        st_other,
                        st_shndx,
                        st_value,
                        st_size,
                    ) = struct.unpack("IBBHQQ", target)

                # get symbol string #
                symbol = self.getString(strtab_section, st_name)

                # convert manged string #
                if symbol:
                    symbol = ElfAnalyzer.demangleSymbol(symbol)

                # check symbol filter #
                if self.filtered:
                    if not UtilMgr.isValidStr(symbol, self.filtered):
                        continue

                # change duplicated symbol #
                if symbol in self.attr["symTable"]:
                    symbol = symbol + "#%s" % hex(st_value)

                self.attr["symTable"][symbol] = {
                    "value": st_value,
                    "size": st_size,
                    "type": ElfAnalyzer.ST_TYPE[
                        ElfAnalyzer.ELF_ST_TYPE(st_info)
                    ],
                    "bind": ElfAnalyzer.ST_BIND_TYPE[
                        ElfAnalyzer.ELF_ST_BIND(st_info)
                    ],
                    "vis": ElfAnalyzer.ST_VISIBILITY_TYPE[
                        ElfAnalyzer.ELF_ST_VISIBILITY(st_other)
                    ],
                    "ndx": st_shndx,
                }

                # add address-symbol mapping info #
                if SysMgr.dwarfEnable:
                    self.addrTable[st_value] = symbol

                # get index #
                if st_shndx in ElfAnalyzer.SHN_TYPE:
                    if ElfAnalyzer.SHN_TYPE[st_shndx] == "SHN_UNDEF":
                        st_shndx = "UND"
                    elif ElfAnalyzer.SHN_TYPE[st_shndx] == "SHN_ABS":
                        st_shndx = "ABS"

                # parse .sym table #
                if printable:
                    # apply filter #
                    if SysMgr.filterGroup:
                        if not UtilMgr.isValidStr(symbol):
                            continue

                    printer(
                        "%04d %016x%15s%10s%10s%10s%10s %s"
                        % (
                            i,
                            st_value,
                            convNum(st_size),
                            ElfAnalyzer.ST_TYPE[
                                ElfAnalyzer.ELF_ST_TYPE(st_info)
                            ],
                            ElfAnalyzer.ST_BIND_TYPE[
                                ElfAnalyzer.ELF_ST_BIND(st_info)
                            ],
                            ElfAnalyzer.ST_VISIBILITY_TYPE[
                                ElfAnalyzer.ELF_ST_VISIBILITY(st_other)
                            ],
                            st_shndx,
                            symbol,
                        )
                    )

                    printCnt += 1

            if printable:
                if printCnt == 0:
                    printer("    None")
                printer(oneLineLoc)
        else:
            ElfAnalyzer.strippedFiles[path] = True
            SysMgr.printWarn(
                "failed to get static symbol of %s (stripped)" % path
            )

        # parse REL table #
        for rellist in (e_shrellist, e_shandrellist):
            for idx in rellist:
                (
                    sh_name,
                    sh_type,
                    sh_flags,
                    sh_addr,
                    sh_offset,
                    sh_size,
                    sh_link,
                    sh_info,
                    sh_addralign,
                    sh_entsize,
                ) = self.getSectionInfo(fd, e_shoff + e_shentsize * idx)

                # get symbol string #
                shname = self.getString(str_section, sh_name)

                # check target #
                isTarget = _isValidSect(shname)
                printable = debug and isTarget

                nrItems = long(sh_size / sh_entsize) if isTarget else 0

                if printable:
                    printer(
                        "\n[%s Section] (nrItem: %s)\n%s\n%16s %16s %32s %16s %s\n%s"
                        % (
                            shname,
                            convNum(nrItems),
                            twoLineLoc,
                            "Offset",
                            "Info",
                            "Type",
                            "Sym.Value",
                            "Sym.Name",
                            twoLineLoc,
                        )
                    )

                fd.seek(sh_offset)

                if nrItems == 0 and isTarget:
                    printer("    None")

                printCnt = 0
                for i in xrange(nrItems):
                    # 32-bit #
                    if self.is32Bit:
                        sh_offset, sh_info = struct.unpack("II", fd.read(8))

                        rsym = ElfAnalyzer.ELF32_R_SYM(sh_info)
                        rtype = ElfAnalyzer.ELF32_R_TYPE(sh_info)
                    # 64-bit #
                    else:
                        sh_offset, sh_info = struct.unpack("QQ", fd.read(16))

                        rsym = ElfAnalyzer.ELF64_R_SYM(sh_info)
                        rtype = ElfAnalyzer.ELF64_R_TYPE(sh_info)

                    try:
                        RTYPE = ElfAnalyzer.RELOC_TYPE[rtype]
                    except:
                        RTYPE = rtype

                    try:
                        symbol = self.attr["dynsymList"][rsym + 1]
                    except:
                        symbol = rsym

                    # convert manged string #
                    if symbol:
                        symbol = ElfAnalyzer.demangleSymbol(symbol)

                    # check symbol filter #
                    if self.filtered:
                        if not UtilMgr.isValidStr(symbol, self.filtered):
                            continue

                    # update address on dynsym table #
                    if symbol in self.attr["dynsymTable"]:
                        if self.attr["dynsymTable"][symbol]["value"] == 0:
                            self.attr["dynsymTable"][symbol][
                                "value"
                            ] = sh_offset

                    # get address of symbol string #
                    if symbol in self.attr["dynsymTable"]:
                        saddr = self.attr["dynsymTable"][symbol]["value"]
                    else:
                        saddr = 0

                    if printable:
                        # apply filter #
                        if SysMgr.filterGroup:
                            if not UtilMgr.isValidStr(symbol):
                                continue

                        printer(
                            "%016x %016x %32s %016x %s"
                            % (sh_offset, sh_info, RTYPE, saddr, symbol)
                        )

                        printCnt += 1

                if printable:
                    if printCnt == 0:
                        printer("    None")
                    printer(oneLineLoc)

        # parse RELA table #
        relaTable = {}
        for relalist in (e_shrelalist, e_shandrelalist):
            for idx in relalist:
                (
                    sh_name,
                    sh_type,
                    sh_flags,
                    sh_addr,
                    sh_offset,
                    sh_size,
                    sh_link,
                    sh_info,
                    sh_addralign,
                    sh_entsize,
                ) = self.getSectionInfo(fd, e_shoff + e_shentsize * idx)

                # get symbol string #
                shname = self.getString(str_section, sh_name)

                # make a new rela table #
                # TODO: apply rela table info to each section #
                relaTable[shname] = {}

                # check target #
                isTarget = _isValidSect(shname)
                printable = debug and isTarget

                nrItems = long(sh_size / sh_entsize) if isTarget else 0

                if printable:
                    printer(
                        "\n[%s Section] (nrItem: %s)\n%s\n%16s %16s %32s %16s %s\n%s"
                        % (
                            shname,
                            convNum(nrItems),
                            twoLineLoc,
                            "Offset",
                            "Info",
                            "Type",
                            "Sym.Value",
                            "Sym.Name + Addend",
                            twoLineLoc,
                        )
                    )

                # set pos #
                fd.seek(sh_offset)

                if nrItems == 0 and isTarget:
                    printer("    None")

                printCnt = 0

                for i in xrange(nrItems):
                    try:
                        # 32-bit #
                        if self.is32Bit:
                            sh_offset, sh_info, sh_addend = struct.unpack(
                                "III", fd.read(12)
                            )

                            rsym = ElfAnalyzer.ELF32_R_SYM(sh_info)
                            rtype = ElfAnalyzer.ELF32_R_TYPE(sh_info)
                        # 64-bit #
                        else:
                            sh_offset, sh_info, sh_addend = struct.unpack(
                                "QQQ", fd.read(24)
                            )

                            rsym = ElfAnalyzer.ELF64_R_SYM(sh_info)
                            rtype = ElfAnalyzer.ELF64_R_TYPE(sh_info)
                    except SystemExit:
                        sys.exit(0)
                    except:
                        SysMgr.printWarn(
                            "failed to read item in %s" % shname, reason=True
                        )
                        break

                    try:
                        RTYPE = ElfAnalyzer.RELOC_TYPE[rtype]
                    except:
                        RTYPE = rtype

                    if RTYPE == "R_X86_64_RELATIVE":
                        val = " " * 16
                    else:
                        val = "%016x" % 0

                    try:
                        symbol = self.attr["dynsymList"][rsym + 1]
                    except:
                        symbol = rsym

                    # convert manged string #
                    if symbol:
                        symbol = ElfAnalyzer.demangleSymbol(symbol)

                    # check symbol filter #
                    if self.filtered:
                        if not isinstance(symbol, str):
                            continue
                        if not UtilMgr.isValidStr(symbol, self.filtered):
                            continue

                    # add rela info #
                    relaTable[shname][sh_offset] = [symbol, sh_addend]

                    # update address on dynsym table #
                    # TODO: resolve string table name of each .rela table #
                    if symbol in self.attr["dynsymTable"]:
                        if self.attr["dynsymTable"][symbol]["value"] == 0:
                            self.attr["dynsymTable"][symbol][
                                "value"
                            ] = sh_offset

                        self.attr["dynsymTable"][symbol]["rtype"] = rtype

                    if symbol:
                        symbol = "%s + " % symbol

                    if printable:
                        # apply filter #
                        if SysMgr.filterGroup:
                            if not UtilMgr.isValidStr(symbol):
                                continue

                        printer(
                            "%016x %016x %32s %s %s"
                            % (
                                sh_offset,
                                sh_info,
                                RTYPE,
                                val,
                                "%s%x" % (symbol, sh_addend),
                            )
                        )

                        printCnt += 1

                if printable:
                    if printCnt == 0:
                        printer("    None")
                    printer(oneLineLoc)

        # set DWARF Flag #
        if SysMgr.dwarfEnable:
            self.attr["dwarfEnabled"] = True
        else:
            self.attr["dwarfEnabled"] = False

        # check frame section #
        sectionHeader = self.attr["sectionHeader"]
        if (
            ".eh_frame" in sectionHeader
            and sectionHeader[".eh_frame"]["type"] != "NOBITS"
        ):
            frameSectName = "eh_frame"
            e_shframe = e_shehframe
        elif (
            ".debug_frame" in sectionHeader
            and sectionHeader[".debug_frame"]["type"] != "NOBITS"
        ):
            # TODO: need to implement more for DWARF v4 #
            frameSectName = "debug_frame"
            e_shframe = e_shdbgframe
        elif (
            ".zdebug_frame" in sectionHeader
            and sectionHeader[".zdebug_frame"]["type"] != "NOBITS"
        ):
            frameSectName = "debug_frame"
            e_shframe = e_shdbgframe
        else:
            frameSectName = ""

        # check frame section #
        self.attr["dwarfTable"] = {}
        if SysMgr.dwarfEnable and e_shframe >= 0 and frameSectName:

            def _getEncType(encoding):
                if encoding == ENC_FLAGS["DW_EH_PE_omit"]:
                    SysMgr.printErr(
                        "failed to decode initial location for FDE"
                    )
                    return None, None, None

                basicEnc = encoding & 0x0F
                encMod = encoding & 0xF0

                # get format #
                DW_EH_ENCODING_MAP = ElfAnalyzer.DW_EH_ENCODING_MAP
                if basicEnc in DW_EH_ENCODING_MAP:
                    encFormat = DW_EH_ENCODING_MAP[basicEnc]
                else:
                    encFormat = None

                return basicEnc, encMod, encFormat

            def _decodeAddr(addr, shaddr, offset, encoding):
                if encoding == ENC_FLAGS["DW_EH_PE_absptr"]:
                    pass
                elif encoding == ENC_FLAGS["DW_EH_PE_pcrel"]:
                    addr += shaddr + offset
                elif encoding == ENC_FLAGS["DW_EH_PE_datarel"]:
                    addr += shaddr
                else:
                    SysMgr.printErr(
                        "failed to recognize modifier %x" % encoding
                    )
                    sys.exit(-1)
                return addr

            def _decodeData(encFormat, fd):
                if encFormat == "DW_EH_PE_sdata4":
                    val = struct.unpack("i", fd.read(4))[0]
                elif encFormat == "DW_EH_PE_sdata8":
                    val = struct.unpack("q", fd.read(8))[0]
                elif encFormat == "DW_EH_PE_sdata2":
                    val = struct.unpack("h", fd.read(2))[0]
                elif encFormat in ("DW_EH_PE_uleb128", "DW_EH_PE_sleb128"):
                    data = UtilMgr.readLEB128(fd)
                    data = data.decode("latin-1")
                    if encFormat == "DW_EH_PE_uleb128":
                        val, nsize = UtilMgr.decodeULEB128(data)
                    else:
                        val, nsize = UtilMgr.decodeSLEB128(data)
                elif encFormat == "DW_EH_PE_absptr":
                    if self.is32Bit:
                        val = struct.unpack("I", fd.read(4))[0]
                    else:
                        val = struct.unpack("Q", fd.read(8))[0]
                elif encFormat == "DW_EH_PE_udata4":
                    val = struct.unpack("I", fd.read(4))[0]
                elif encFormat == "DW_EH_PE_udata8":
                    val = struct.unpack("Q", fd.read(8))[0]
                elif encFormat == "DW_EH_PE_udata2":
                    val = struct.unpack("H", fd.read(2))[0]
                else:
                    SysMgr.printErr(
                        "failed to recognize basic encoding %s" % encFormat
                    )
                    sys.exit(-1)

                return val

            def _getAugData(string, table, pos, size):
                if size == 0:
                    return {}, "", ""

                augdata = table[pos : pos + size]

                # parse data #
                augpos = 0
                augdict = {}
                for idx, char in enumerate(string):
                    if augpos >= len(augdata):
                        break

                    try:
                        data = ord(augdata[augpos])
                    except SystemExit:
                        sys.exit(0)
                    except:
                        data = augdata[augpos]

                    if char == "z":
                        augdict["length"] = size
                    elif char == "L":
                        augdict["lsdaEncoding"] = data
                        augpos += 1
                    elif char == "R":
                        augdict["fdeEncoding"] = data
                        augpos += 1
                    elif char == "S":
                        augpos += 1
                    elif char == "P":
                        # get basic encoding #
                        augdict["personality"] = dict(encoding=data)
                        basicEnc = data & 0x0F
                        augpos += 1

                        # get format #
                        DW_EH_ENCODING_MAP = ElfAnalyzer.DW_EH_ENCODING_MAP
                        if basicEnc in DW_EH_ENCODING_MAP:
                            encFormat = DW_EH_ENCODING_MAP[basicEnc]
                        else:
                            encFormat = None
                        augdict["personality"]["format"] = encFormat

                        # decoding #
                        if encFormat == "DW_EH_PE_sdata4":
                            data = augdata[augpos : augpos + 4]
                            func = struct.unpack("i", data)[0]
                            augpos += 4
                        elif encFormat == "DW_EH_PE_sdata8":
                            data = augdata[augpos : augpos + 8]
                            func = struct.unpack("q", data)[0]
                            augpos += 8
                        elif encFormat == "DW_EH_PE_sdata2":
                            data = augdata[augpos : augpos + 2]
                            func = struct.unpack("h", data)[0]
                            augpos += 2
                        elif encFormat in (
                            "DW_EH_PE_uleb128",
                            "DW_EH_PE_sleb128",
                        ):
                            data = augdata[augpos:].decode("latin-1")
                            if encFormat == "DW_EH_PE_uleb128":
                                func, nsize = UtilMgr.decodeULEB128(data)
                            else:
                                func, nsize = UtilMgr.decodeSLEB128(data)
                            augpos += nsize
                        elif encFormat == "DW_EH_PE_absptr":
                            if self.is32Bit:
                                data = augdata[augpos : augpos + 4]
                                func = struct.unpack("I", data)[0]
                                augpos += 4
                            else:
                                data = augdata[augpos : augpos + 8]
                                func = struct.unpack("Q", data)[0]
                                augpos += 8
                        elif encFormat == "DW_EH_PE_udata4":
                            data = augdata[augpos : augpos + 4]
                            func = struct.unpack("I", data)[0]
                            augpos += 4
                        elif encFormat == "DW_EH_PE_udata8":
                            data = augdata[augpos : augpos + 8]
                            func = struct.unpack("Q", data)[0]
                            augpos += 8
                        elif encFormat == "DW_EH_PE_udata2":
                            data = augdata[augpos : augpos + 2]
                            func = struct.unpack("H", data)[0]
                            augpos += 2
                        else:
                            SysMgr.printErr(
                                "failed to recognize encoding format %x"
                                % basicEnc
                            )
                            sys.exit(-1)

                        augdict["personality"]["func"] = func
                    elif char == "e" and string[idx + 1] == "h":
                        pass
                    elif char == "h" and string[idx - 1] == "e":
                        augpos += 1
                    else:
                        SysMgr.printErr(
                            'failed to recognize augmentation "%s"' % char
                        )
                        sys.exit(-1)

                adstr = UtilMgr.convStr2Bytes(augdata)

                return augdict, adstr.strip(), augdata

            def _decodeCFI(self, entry, cfi, cie, offset):
                def _add2Order(regnum):
                    """
                    DW_CFA_restore and others remove registers from curLine,
                    but they stay in reg_order. Avoid duplicates.
                    """
                    if regnum not in regOrder:
                        regOrder.append(regnum)

                RegisterRule = ElfAnalyzer.RegisterRule
                regIdx = ElfAnalyzer.CFARule.REG
                offsetIdx = ElfAnalyzer.CFARule.OFFSET
                # exprIdx = ElfAnalyzer.CFARule.EXPR
                getRule = ElfAnalyzer.getCFARule

                copy = SysMgr.getPkg("copy", False)

                # CIE #
                if entry == "CIE":
                    myObj = cie
                    curLine = dict(pc=0, cfa=getRule(reg=None, offset=0))
                    regOrder = []
                # FDE #
                else:
                    myObj = self.attr["dwarf"]["FDE"][offset]
                    cieTable = cie["table"]
                    if cieTable:
                        cieLastLine = copy.copy(cieTable[-1])
                        curLine = copy.copy(cieLastLine)
                    else:
                        curLine = dict(cfa=getRule(reg=None, offset=0))
                    curLine["pc"] = myObj["initLoc"]
                    regOrder = copy.copy(cie["regOrder"])

                table = []

                # stack for DW_CFA_{remember|restore}_state instructions #
                lineStack = []

                """
                Throughout this loop, curLine is the current line.
                Some instructions add it to the table,
                but most instructions just update it
                without adding it to the table.
                """
                for instr in cfi:
                    name = instr[0]
                    args = instr[2]

                    if name == "DW_CFA_set_loc":
                        table.append(copy.copy(curLine))
                        curLine["pc"] = args[0]
                    elif name in (
                        "DW_CFA_advance_loc1",
                        "DW_CFA_advance_loc2",
                        "DW_CFA_advance_loc4",
                        "DW_CFA_advance_loc",
                    ):
                        table.append(copy.copy(curLine))
                        curLine["pc"] += args[0] * cie["caf"]
                    elif name == "DW_CFA_def_cfa":
                        curLine["cfa"] = getRule(reg=args[0], offset=args[1])
                    elif name == "DW_CFA_def_cfa_sf":
                        curLine["cfa"] = getRule(
                            reg=args[0], offset=args[1] * cie["daf"]
                        )
                    elif name == "DW_CFA_def_cfa_register":
                        curLine["cfa"] = getRule(
                            reg=args[0], offset=curLine["cfa"][offsetIdx]
                        )
                    elif name == "DW_CFA_def_cfa_offset":
                        curLine["cfa"] = getRule(
                            reg=curLine["cfa"][regIdx], offset=args[0]
                        )
                    elif name == "DW_CFA_def_cfa_offset_sf":
                        curLine["cfa"] = getRule(
                            reg=curLine["cfa"][regIdx],
                            offset=args[0] * cie["daf"],
                        )
                    elif name == "DW_CFA_def_cfa_expression":
                        curLine["cfa"] = getRule(expr=args[0])
                    elif name == "DW_CFA_undefined":
                        _add2Order(args[0])
                        curLine[args[0]] = RegisterRule(RegisterRule.UNDEFINED)
                    elif name == "DW_CFA_same_value":
                        _add2Order(args[0])
                        curLine[args[0]] = RegisterRule(
                            RegisterRule.SAME_VALUE
                        )
                    elif name in (
                        "DW_CFA_offset",
                        "DW_CFA_offset_extended",
                        "DW_CFA_offset_extended_sf",
                    ):
                        _add2Order(args[0])
                        curLine[args[0]] = RegisterRule(
                            RegisterRule.OFFSET, args[1] * cie["daf"]
                        )
                    elif name in ("DW_CFA_val_offset", "DW_CFA_val_offset_sf"):
                        _add2Order(args[0])
                        curLine[args[0]] = RegisterRule(
                            RegisterRule.VAL_OFFSET, args[1] * cie["daf"]
                        )
                    elif name == "DW_CFA_register":
                        _add2Order(args[0])
                        curLine[args[0]] = RegisterRule(
                            RegisterRule.REGISTER, args[1]
                        )
                    elif name == "DW_CFA_expression":
                        _add2Order(args[0])
                        curLine[args[0]] = RegisterRule(
                            RegisterRule.EXPRESSION, args[1]
                        )
                    elif name == "DW_CFA_val_expression":
                        _add2Order(args[0])
                        curLine[args[0]] = RegisterRule(
                            RegisterRule.VAL_EXPRESSION, args[1]
                        )
                    elif name in ("DW_CFA_restore", "DW_CFA_restore_extended"):
                        _add2Order(args[0])
                        if entry != "FDE":
                            SysMgr.printErr(
                                "%s instruction must be in a FDE" % name
                            )
                        if args[0] in cieLastLine:
                            curLine[args[0]] = cieLastLine[args[0]]
                        else:
                            curLine.pop(args[0], None)
                    elif name == "DW_CFA_remember_state":
                        lineStack.append(UtilMgr.deepcopy(curLine))
                    elif name == "DW_CFA_restore_state":
                        pc = curLine["pc"]
                        curLine = lineStack.pop()
                        curLine["pc"] = pc
                    elif name == "DW_CFA_nop":
                        pass
                    else:
                        """
                        SysMgr.printWarn(
                            'skipped to update current line by %s' % name)
                        """
                        self.nrSkipUpdate += 1

                """
                The current line is appended to the table after
                all instructions have ended, if there were instructions.
                """
                if curLine["cfa"][regIdx] is not None or len(curLine) > 2:
                    table.append(curLine)

                # save result #
                myObj["table"] = table
                myObj["regOrder"] = regOrder

                return table, regOrder

            def _makeCFATable(self, entry, offset, regList, prt=False):
                def _getCFARule(cfa):
                    regIdx = ElfAnalyzer.CFARule.REG
                    offsetIdx = ElfAnalyzer.CFARule.OFFSET
                    exprIdx = ElfAnalyzer.CFARule.EXPR

                    if cfa[exprIdx]:
                        return "exp"
                    else:
                        return "%s%+d" % (regList[cfa[regIdx]], cfa[offsetIdx])

                def _getRegRule(reg):
                    typeIdx = ElfAnalyzer.RegisterRule.TYPE
                    argIdx = ElfAnalyzer.RegisterRule.ARG

                    s = ElfAnalyzer.DW_CFI_REGISTER_RULE_TYPE[reg[typeIdx]]

                    if reg[typeIdx] in ("OFFSET", "VAL_OFFSET"):
                        s += "%+d" % reg[argIdx]
                    elif reg[typeIdx] == "REGISTER":
                        s += regList[reg[argIdx]]

                    return s

                myObj = self.attr["dwarf"][entry][offset]
                table = myObj["table"]
                regOrder = myObj["regOrder"]

                if "initLoc" in myObj:
                    initLoc = myObj["initLoc"]
                else:
                    initLoc = 0

                # get return address register #
                if "rar" in myObj:
                    rar = myObj["rar"]
                else:
                    rar = myObj["CIE"]["rar"]

                # register to CFA table #
                if initLoc > 0:
                    self.attr["dwarf"]["CFAIndex"].append(initLoc)
                    self.attr["dwarf"]["CFATable"][initLoc] = table

                if prt:
                    # remove return address register #
                    try:
                        regOrder.remove(rar)
                    except SystemExit:
                        sys.exit(0)
                    except:
                        pass

                    # define default title #
                    if not self.cfaTableTitle:
                        self.cfaTableTitle = "{0:^16} {1:<10}".format(
                            "LOC", "CFA"
                        )

                    # copy default title #
                    s = str(self.cfaTableTitle)

                    # add reg name #
                    for regnum in regOrder:
                        try:
                            s += "%-6s" % regList[regnum]
                        except SystemExit:
                            sys.exit(0)
                        except:
                            SysMgr.printWarn(
                                "failed to get register name", reason=True
                            )

                    s += "%-6s\n" % "ra"
                    regOrder.append(rar)

                    # mark line #
                    s = "." * len(s) + "\n" + s

                # add decoded CFA lines #
                for line in table:
                    pc = line["pc"]

                    if not prt:
                        continue

                    # pc #
                    s += "%016x" % pc

                    # cfa #
                    if line["cfa"]:
                        cfa = line["cfa"]
                    else:
                        cfa = "u"
                    s += " %-10s" % _getCFARule(cfa)

                    # reg #
                    for regnum in regOrder:
                        if regnum in line:
                            reginfo = _getRegRule(line[regnum])
                        else:
                            reginfo = "u"
                        s += "%-6s" % reginfo

                    s += "\n"

                if prt:
                    printer(s)

            def _printCFIs(cfi, cie=None, pc=None, regList=None):
                def _convRegName(arg, regList):
                    try:
                        return regList[arg]
                    except:
                        SysMgr.printWarn(
                            "failed to get register name", reason=True
                        )
                        return None

                s = ""
                for inst in cfi:
                    name = inst[0]
                    args = inst[2]

                    if name in (
                        "DW_CFA_offset",
                        "DW_CFA_offset_extended",
                        "DW_CFA_offset_extended_sf",
                        "DW_CFA_val_offset",
                        "DW_CFA_val_offset_sf",
                    ):
                        s += " %s: %s at cfa%+d\n" % (
                            name,
                            _convRegName(args[0], regList),
                            args[1] * cie["daf"],
                        )
                    elif name in (
                        "DW_CFA_restore",
                        "DW_CFA_restore_extended",
                        "DW_CFA_undefined",
                        "DW_CFA_same_value",
                        "DW_CFA_def_cfa_register",
                    ):
                        s += " %s: %s\n" % (
                            name,
                            _convRegName(args[0], regList),
                        )
                    elif name == "DW_CFA_register":
                        s += " %s: %s in %s" % (
                            name,
                            _convRegName(args[0], regList),
                            _convRegName(args[1], regList),
                        )
                    elif name == "DW_CFA_set_loc":
                        pc = args[0]
                        s += " %s: %08x\n" % (name, pc)
                    elif name in (
                        "DW_CFA_advance_loc1",
                        "DW_CFA_advance_loc2",
                        "DW_CFA_advance_loc4",
                        "DW_CFA_advance_loc",
                    ):
                        factoredOffset = args[0] * cie["caf"]
                        s += " %s: %s to %08x\n" % (
                            name,
                            factoredOffset,
                            factoredOffset + pc,
                        )
                        pc += factoredOffset
                    elif name in (
                        "DW_CFA_remember_state",
                        "DW_CFA_restore_state",
                        "DW_CFA_nop",
                    ):
                        s += " %s\n" % name
                    elif name == "DW_CFA_def_cfa":
                        s += " %s: %s ofs %s\n" % (
                            name,
                            _convRegName(args[0], regList),
                            args[1],
                        )
                    elif name == "DW_CFA_def_cfa_sf":
                        s += " %s: %s ofs %s\n" % (
                            name,
                            _convRegName(args[0], regList),
                            args[1] * cie["daf"],
                        )
                    elif name in (
                        "DW_CFA_def_cfa_offset",
                        "DW_CFA_GNU_args_size",
                    ):
                        s += " %s: %s\n" % (name, args[0])
                    elif name in ("DW_CFA_def_cfa_offset_sf"):
                        s += " %s: %s\n" % (name, args[0] * cie["daf"])
                    elif name == "DW_CFA_def_cfa_expression":
                        """
                        expr_dumper = ExprDumper(entry.structs)
                        s += ' %s (%s)\n' % \
                            (name, expr_dumper.dump_expr(args[0]))
                        """
                        s += " %s: %s\n" % (name, args)
                    elif name == "DW_CFA_expression":
                        """
                        expr_dumper = ExprDumper(entry.structs)
                        s += ' %s: %s (%s)\n' % (
                            name, _convRegName(args[0], regList),
                                 expr_dumper.dump_expr(args[1]))
                        """
                        s += " %s: %s\n" % (name, args)
                    else:
                        s += " %s: <??>\n" % name

                printer(s.rstrip("\n"))

            (
                sh_name,
                sh_type,
                sh_flags,
                sh_addr,
                sh_offset,
                sh_size,
                sh_link,
                sh_info,
                sh_addralign,
                sh_entsize,
            ) = self.getSectionInfo(fd, e_shoff + e_shentsize * e_shframe)

            self.attr.setdefault("dwarf", {})
            self.attr["dwarf"]["CIE"] = {}
            self.attr["dwarf"]["FDE"] = {}
            self.attr["dwarf"].setdefault("general", {})
            self.attr["dwarf"].setdefault("CFAIndex", [])
            self.attr["dwarf"].setdefault("CFATable", {})
            ENC_FLAGS = ElfAnalyzer.DW_EH_ENCODING_FLAGS
            nrCIE = nrFDE = 0

            # get symbol string #
            shname = self.getString(str_section, sh_name)

            # check target #
            isTarget = _isValidSect(shname)
            printable = debug and isTarget

            if printable:
                printer("\n[%s Section]\n%s" % (shname, twoLineLoc))

            # set position #
            fd.seek(sh_offset)

            # decompress section #
            origFd = fd
            fd, sh_size = ElfAnalyzer.decompSect(
                fd, path, shname, sh_offset, sh_size
            )
            isCompressed = fd != origFd

            # initialize the number of decode error #
            self.nrSkipUpdate = 0

            def _decodeData2(table, pos):
                return table[pos : pos + 1024].decode("latin-1")

            while 1:
                if not isTarget:
                    break

                # offset #
                offset = fd.tell() - sh_offset
                if offset >= sh_size:
                    break

                # length #
                size = struct.unpack("I", fd.read(4))[0]
                # terminator #
                if size == 0:
                    break
                # extended length 8 bytes are needed #
                elif size == 0xFFFFFFFF:
                    size = struct.unpack("Q", fd.read(8))[0]

                # format #
                dwarfFormat = 64 if size == 0xFFFFFFFF else 32

                # initial length #
                # initLenField = 4 if dwarfFormat == 32 else 12

                # start position #
                startPos = fd.tell()

                # CIE ID #
                cid = struct.unpack("I", fd.read(4))[0]

                # check CIE #
                if frameSectName == "eh_frame":
                    isCIE = cid == 0
                else:
                    isCIE = (
                        dwarfFormat == 32 and cid == 0xFFFFFFFF
                    ) or cid == 0xFFFFFFFFFFFFFFFF

                # -------------------- CIE -------------------- #
                if isCIE:
                    entry = "CIE"
                    nrCIE += 1
                    initLoc = 0

                    # version #
                    ver = struct.unpack("B", fd.read(1))[0]

                    # data #
                    dataSize = size - 5
                    table = fd.read(dataSize)

                    # Augmentation String #
                    if frameSectName == "eh_frame":
                        augstr = self.getString(table)
                        pos = len(augstr) + 1
                    else:
                        augstr = ""
                        pos = 1

                    # ehdata #
                    if "eh" in augstr:
                        if self.is32Bit:
                            ehdata = fd.read(4)
                        else:
                            ehdata = fd.read(8)

                    # size for address and segment in DWARF v4 #
                    if ver >= 4:
                        addrSize = struct.unpack("B", table[pos : pos + 1])[0]
                        pos += 1
                        segmentSize = struct.unpack("B", table[pos : pos + 1])[
                            0
                        ]
                        pos += 1
                    else:
                        addrSize = segmentSize = -1

                    # Call Alignment Factor #
                    data = _decodeData2(table, pos)
                    caf, nsize = UtilMgr.decodeULEB128(data)
                    pos += nsize

                    # Data Alignment Factor #
                    data = _decodeData2(table, pos)
                    daf, nsize = UtilMgr.decodeSLEB128(data)
                    pos += nsize

                    # Return Address Register #
                    data = _decodeData2(table, pos)
                    rar, nsize = UtilMgr.decodeULEB128(data)
                    pos += nsize

                    # Augmentation Size #
                    if "z" in augstr:
                        data = _decodeData2(table, pos)
                        augsize, nsize = UtilMgr.decodeULEB128(data)
                        pos += nsize
                    else:
                        augsize = 0

                    # Augmentation Data #
                    if frameSectName == "eh_frame":
                        augdict, augdatastr, augdata = _getAugData(
                            augstr, table, pos, augsize
                        )
                        pos += augsize
                    else:
                        augdict = {}
                        augdatastr = ""
                        augdata = ""

                    # decode Call Frame Instructions #
                    cfi = self.getCFI(table, pos, dataSize)

                    # save info #
                    cie = self.attr["dwarf"]["CIE"][offset] = {
                        "offset": offset,
                        "length": size,
                        "id": cid,
                        "version": ver,
                        "addrsize": addrSize,
                        "segmentsize": segmentSize,
                        "caf": caf,
                        "daf": daf,
                        "rar": rar,
                        "augsize": augsize,
                        "augstr": augstr,
                        "augdict": augdict,
                        "augdata": augdata,
                        "augdatastr": augdatastr,
                        "cfi": cfi,
                        "table": [],
                    }

                    # print info #
                    if printable:
                        printStr = "\n%08x %016x %08x CIE\n" % (
                            offset,
                            size,
                            cid,
                        )
                        printStr += " %-22s %s\n" % ("Version:", ver)
                        printStr += ' %-22s "%s"\n' % ("Augmentation:", augstr)
                        if addrSize >= 0:
                            printStr += " %-22s %s\n" % (
                                "Pointer Size:",
                                addrSize,
                            )
                        if segmentSize >= 0:
                            printStr += " %-22s %s\n" % (
                                "Segment Size:",
                                segmentSize,
                            )
                        printStr += " %-22s %x\n" % (
                            "Code alignment factor:",
                            caf,
                        )
                        printStr += " %-22s %x\n" % (
                            "Data alignment factor:",
                            daf,
                        )
                        printStr += " %-22s %d\n" % (
                            "Return address column:",
                            rar,
                        )
                        if augdatastr:
                            printStr += " %-22s %s\n\n" % (
                                "Augmentation data: ",
                                augdatastr,
                            )
                        printer(printStr)

                # -------------------- FDE -------------------- #
                else:
                    entry = "FDE"
                    nrFDE += 1

                    # CIE pointer #
                    ciePtr = cid

                    # CIE #
                    try:
                        if frameSectName == "eh_frame":
                            cieOffset = offset + dwarfFormat // 8 - ciePtr
                            cie = self.attr["dwarf"]["CIE"][cieOffset]
                        elif frameSectName == "debug_frame":
                            cieOffset = 0
                            cie = self.attr["dwarf"]["CIE"][ciePtr]
                    except:
                        SysMgr.printWarn(
                            "failed to get CIE info for FDE", reason=True
                        )
                        continue

                    # check encoding #
                    if frameSectName == "eh_frame":
                        augstr = cie["augstr"]
                        augdict = cie["augdict"]
                        augdatastr = None

                        if not "fdeEncoding" in augdict:
                            SysMgr.printErr(
                                "failed to find FDE encoding data from CIE %x"
                                % cie["id"]
                            )
                            sys.exit(-1)
                    else:
                        augstr = ""
                        augdict = {}
                        augdatastr = None

                    # get offset #
                    curOffset = fd.tell() - sh_offset

                    # get encoding #
                    if frameSectName == "eh_frame":
                        encoding = cie["augdict"]["fdeEncoding"]
                        basicEnc, encMod, encFormat = _getEncType(encoding)
                        if basicEnc is None:
                            continue
                    else:
                        encoding = ""
                        basicEnc = ""
                        encMod = ENC_FLAGS["DW_EH_PE_absptr"]
                        encFormat = "DW_EH_PE_absptr"

                    # get function address #
                    initLoc = _decodeData(encFormat, fd)

                    # convert function address #
                    initLoc = _decodeAddr(initLoc, sh_addr, curOffset, encMod)

                    # Range Length #
                    lenSize = _decodeData(encFormat, fd)

                    # Augmentation Size #
                    if "z" in augstr:
                        augsize = UtilMgr.readLEB128(fd)
                        if not augsize:
                            augsize = 0
                            nsize = 1
                        else:
                            augsize, nsize = UtilMgr.decodeULEB128(augsize)
                    else:
                        augsize = 0

                    # Augmentation Data #
                    if (
                        augsize == 0
                        and "personality" in augdict
                        and "lsdaEncoding" in augdict
                    ):
                        # get encoding format #
                        encFormat = augdict["personality"]["format"]

                        # decode data for size #
                        curPos = fd.tell()
                        data = _decodeData(encFormat, fd)
                        datasize = fd.tell() - curPos
                        fd.seek(curPos)

                        # load data #
                        augdata = fd.read(datasize)
                        augdatastr = UtilMgr.convStr2Bytes(augdata)

                    # read remain part #
                    remain = fd.tell() - startPos
                    loadSize = size - remain
                    if loadSize > 0:
                        table = fd.read(loadSize)

                        # decode Call Frame Instructions #
                        cfi = self.getCFI(table, pos=0, size=len(table))
                    # wrong FDE #
                    else:
                        fd.seek(loadSize, 1)
                        cfi = []

                    # save FDE info #
                    self.attr["dwarf"]["FDE"][offset] = {
                        "offset": offset,
                        "length": size,
                        "id": cid,
                        "CIE": cie,
                        "initLoc": initLoc,
                        "lenSize": lenSize,
                        "cfi": cfi,
                        "table": [],
                    }

                    if initLoc in self.addrTable and self.addrTable[initLoc]:
                        symbol = self.addrTable[initLoc]
                        if self.filtered and not UtilMgr.isValidStr(
                            symbol, self.filtered
                        ):
                            continue
                        self.attr["dwarf"]["FDE"][offset]["symbol"] = symbol
                    else:
                        if self.filtered:
                            continue

                        symbol = ""

                        # register stripped function info #
                        self.attr["dwarfTable"][hex(initLoc)] = {
                            "value": initLoc,
                            "size": lenSize,
                            "type": "FUNC",
                        }

                    # print info #
                    if printable:
                        # apply filter #
                        if SysMgr.filterGroup:
                            if not UtilMgr.isValidStr(symbol):
                                continue

                        # type info #
                        printStr = "\n%08x %016x %08x FDE cie=%08x " % (
                            offset,
                            size,
                            cid,
                            cieOffset,
                        )

                        # address info #
                        printStr += "pc=%016x..%016x" % (
                            initLoc,
                            initLoc + lenSize,
                        )

                        # symbol info #
                        if symbol:
                            printStr += " sym=%s" % symbol

                        printStr += "\n"

                        # augmentation info #
                        if augdatastr:
                            printStr += " %-22s %s\n\n" % (
                                "Augmentation data: ",
                                augdatastr,
                            )

                        # print line #
                        printer(printStr)

                # decode instructions to make CFA table #
                _decodeCFI(self, entry, cfi, cie, offset)

                # print CFI #
                if printable:
                    _printCFIs(cfi, cie, initLoc, regList)

                # make CFA table #
                _makeCFATable(self, entry, offset, regList, prt=printable)

            # print the number of instructions for skip-update-current-line #
            if self.nrSkipUpdate > 0:
                SysMgr.printWarn(
                    (
                        "skipped to update current line "
                        "by %s DWARF instructions for %s"
                    )
                    % (convNum(self.nrSkipUpdate), self.path),
                    True,
                )

            # sort address list for CFA #
            self.attr["dwarf"]["CFAIndex"].sort()

            # add general info #
            self.attr["dwarf"]["general"]["nrCIE"] = nrCIE
            self.attr["dwarf"]["general"]["nrFDE"] = nrFDE

            # remove useless data #
            for name in list(self.attr["dwarf"]):
                if not name in ("CFAIndex", "CFATable", "info", "abbrev"):
                    del self.attr["dwarf"][name]

            if printable:
                printer(
                    "\n< Total CIE: %s / FDE: %s >\n%s"
                    % (convNum(nrCIE), convNum(nrFDE), oneLineLoc)
                )

            # recover original fd #
            if isCompressed:
                fd = origFd

        # check .eh_frame_hdr section #
        if (
            SysMgr.dwarfEnable
            and e_shehframehdr >= 0
            and self.attr["sectionHeader"][".eh_frame_hdr"]["type"] != "NOBITS"
        ):
            (
                sh_name,
                sh_type,
                sh_flags,
                sh_addr,
                sh_offset,
                sh_size,
                sh_link,
                sh_info,
                sh_addralign,
                sh_entsize,
            ) = self.getSectionInfo(fd, e_shoff + e_shentsize * e_shehframehdr)

            self.attr.setdefault("dwarf", {})
            self.attr["dwarf"]["hdr"] = {}
            ENC_FLAGS = ElfAnalyzer.DW_EH_ENCODING_FLAGS

            # get symbol string #
            shname = self.getString(str_section, sh_name)

            # check target #
            isTarget = _isValidSect(shname)
            printable = debug and isTarget

            if printable:
                printer("\n[%s Section]\n%s" % (shname, twoLineLoc))

            # set position #
            fd.seek(sh_offset)

            # version #
            ver = struct.unpack("B", fd.read(1))[0]

            # eh_frame_ptr_enc #
            ehframePtrEnc = struct.unpack("B", fd.read(1))[0]
            basicEnc, efpEncMod, efpEncFormat = _getEncType(ehframePtrEnc)

            # fde_count_enc #
            fdeCntEnc = struct.unpack("B", fd.read(1))[0]
            basicEnc, fcEncMod, fcEncFormat = _getEncType(fdeCntEnc)

            # table_enc #
            tableEnc = struct.unpack("B", fd.read(1))[0]
            basicEnc, tEncMod, tEncFormat = _getEncType(tableEnc)

            # eh_frame_ptr #
            ehframePtr = _decodeData(efpEncFormat, fd) + sh_offset + 4

            # fde_count #
            fdeCnt = _decodeData(fcEncFormat, fd) if isTarget else 0

            # print summary #
            if printable:
                printer(
                    (
                        "eh_frame pointer: %016x, FDE count: %s\n%s"
                        % (ehframePtr, convNum(fdeCnt), oneLineLoc)
                    )
                )

                # print menu #
                printer(
                    "{0:^5} {1:^16} {2:^16}".format(
                        "IDX", "FUNC ADDR", "FDE ADDR"
                    )
                )

            # table #
            printCnt = 0
            for idx in xrange(fdeCnt):
                curPos = fd.tell() - sh_offset

                # address for the function #
                initLoc = _decodeData(tEncFormat, fd)
                initLoc = _decodeAddr(initLoc, sh_addr, curPos, tEncMod)

                # address for the FDE #
                addr = _decodeData(tEncFormat, fd)
                addr = _decodeAddr(addr, sh_addr, curPos, tEncMod)

                if printable:
                    output = "%05s %016x %016x" % (idx, initLoc, addr)

                    # apply filter #
                    if SysMgr.filterGroup:
                        if not UtilMgr.isValidStr(output):
                            continue

                    printer(output)

                    printCnt += 1

            if printable:
                if printCnt == 0:
                    printer("%s\n    None" % oneLineLoc)
                printer(oneLineLoc)

        # check .ARM.IDX section #
        if SysMgr.dwarfEnable and e_sharmidx >= 0:
            # parse section header #
            (
                sh_name,
                sh_type,
                sh_flags,
                sh_addr,
                sh_offset,
                sh_size,
                sh_link,
                sh_info,
                sh_addralign,
                sh_entsize,
            ) = self.getSectionInfo(fd, e_shoff + e_shentsize * e_sharmidx)

            # refer to https://github.com/eliben/pyelftools #
            def _expandPrel31(address, place):
                location = address & 0x7FFFFFFF
                if location & 0x04000000:
                    location |= 0xFFFFFFFF80000000
                return location + place & 0xFFFFFFFFFFFFFFFF

            # refer to https://github.com/eliben/pyelftools #
            def _decodeEntry(
                self,
                idx,
                foffset,
                personality=None,
                bytecode=None,
                tableoffset=None,
                debug=False,
            ):
                # decode code #
                dobj = ElfAnalyzer.EHABIBytecodeDecoder(bytecode, self.path)

                printable = debug and isTarget

                if printable:
                    printer("Entry %s:" % idx)

                # get table offset #
                if personality == -1:
                    toffset = "[cantunwind]"
                elif foffset in self.addrTable:
                    toffset = self.addrTable[foffset]
                # fixed address code #
                elif foffset + self.loadAddr in self.addrTable:
                    foffset += self.loadAddr
                    toffset = self.addrTable[foffset]
                # THUMB code #
                elif foffset + 1 in self.addrTable:
                    toffset = self.addrTable[foffset + 1]
                elif tableoffset:
                    toffset = "@%s" % hex(tableoffset).rstrip("L")
                else:
                    toffset = "N/A"

                # register to CFA table #
                if foffset != 1:
                    self.attr["dwarf"]["CFAIndex"].append(foffset)
                    self.attr["dwarf"]["CFATable"][foffset] = [dobj.cfa_table]
                    self.attr["dwarf"]["CFATable"][foffset][0]["pc"] = foffset

                    # register stripped function info #
                    if (
                        not foffset in self.addrTable
                        or not self.addrTable[foffset]
                    ):
                        self.attr["dwarfTable"][hex(foffset)] = {
                            "value": foffset,
                            "size": 1,
                            "type": "FUNC",
                        }

                if printable:
                    printer(
                        " Function offset %s: %s"
                        % (hex(foffset).rstrip("L"), toffset)
                    )

                if personality == -1:
                    if printable:
                        printer("\n")
                    return

                if printable:
                    printer(" Compact model index: %s" % personality)

                    # print op code #
                    for line in dobj.mnemonic_array:
                        printer(" %s" % line)

                    printer("\n")

            self.attr.setdefault("dwarf", {})
            self.attr["dwarf"].setdefault("general", {})
            self.attr["dwarf"].setdefault("CFAIndex", [])
            self.attr["dwarf"].setdefault("CFATable", {})

            # define entry size #
            EHABI_INDEX_ENTRY_SIZE = 8

            # get symbol string #
            shname = self.getString(str_section, sh_name)

            # check target #
            isTarget = _isValidSect(shname)
            printable = debug and isTarget

            # get the number of item #
            nrItems = long(sh_size / EHABI_INDEX_ENTRY_SIZE) if isTarget else 0

            if printable:
                printer(
                    "\n[%s Section] (nrItem: %s)\n%s\n"
                    % (shname, convNum(nrItems), twoLineLoc)
                )

            for idx in xrange(nrItems):
                # read value #
                fd.seek(sh_offset + idx * EHABI_INDEX_ENTRY_SIZE)
                data = fd.read(EHABI_INDEX_ENTRY_SIZE)
                word0, word1 = struct.unpack("II", data)

                # check corruption #
                if word0 & 0x80000000 != 0:
                    SysMgr.printWarn(
                        "corrupted ARM exception handler entry: %x" % idx
                    )
                    continue

                foffset = _expandPrel31(
                    word0, sh_offset + (idx * EHABI_INDEX_ENTRY_SIZE)
                )

                if word1 == 1:
                    # 0x1 means cannot unwind #
                    _decodeEntry(self, idx, foffset, -1, debug=debug)
                    continue

                elif word1 & 0x80000000 == 0:
                    # highest bit is zero, point to .ARM.extab data
                    eh_table_offset = _expandPrel31(
                        word1, sh_offset + idx * EHABI_INDEX_ENTRY_SIZE + 4
                    )

                    # read value #
                    fd.seek(eh_table_offset)
                    data = fd.read(long(EHABI_INDEX_ENTRY_SIZE / 2))
                    word0 = struct.unpack("I", data)[0]

                    if word0 & 0x80000000 == 0:
                        # highest bit is one, generic model #
                        personality = _expandPrel31(word0, eh_table_offset)
                        _decodeEntry(
                            self, idx, foffset, personality, debug=debug
                        )

                    # highest bit is one, arm compact model #
                    # highest half must be 0b1000 for compact model #
                    if word0 & 0x70000000 != 0:
                        SysMgr.printWarn(
                            "corrupted ARM exception handler entry: %x" % idx
                        )
                        _decodeEntry(self, idx, 1)
                        continue

                    per_index = (word0 >> 24) & 0x7F
                    if per_index == 0:
                        # arm compact model 0 #
                        opcode = [
                            (word0 & 0xFF0000) >> 16,
                            (word0 & 0xFF00) >> 8,
                            word0 & 0xFF,
                        ]

                        _decodeEntry(
                            self, idx, foffset, per_index, opcode, debug=debug
                        )
                    elif per_index in (1, 2):
                        # arm compact model 1/2 #
                        more_word = (word0 >> 16) & 0xFF
                        opcode = [(word0 >> 8) & 0xFF, (word0 >> 0) & 0xFF]

                        fd.seek(eh_table_offset + 4)

                        for i in xrange(more_word):
                            # read value #
                            data = fd.read(long(EHABI_INDEX_ENTRY_SIZE / 2))
                            r = struct.unpack("I", data)[0]
                            opcode.append((r >> 24) & 0xFF)
                            opcode.append((r >> 16) & 0xFF)
                            opcode.append((r >> 8) & 0xFF)
                            opcode.append((r >> 0) & 0xFF)

                        _decodeEntry(
                            self,
                            idx,
                            foffset,
                            per_index,
                            opcode,
                            eh_table_offset,
                            debug=debug,
                        )
                    else:
                        SysMgr.printWarn(
                            "unknown ARM compact model %d at entry: %x"
                            % (per_index, idx)
                        )
                        _decodeEntry(self, idx, 1)
                else:
                    # highest bit is one, compact model must be 0 #
                    if word1 & 0x7F000000 != 0:
                        SysMgr.printWarn(
                            "corrupted ARM compact model entry: %x" % idx
                        )
                        continue

                    opcode = [
                        (word1 & 0xFF0000) >> 16,
                        (word1 & 0xFF00) >> 8,
                        word1 & 0xFF,
                    ]

                    _decodeEntry(self, idx, foffset, 0, opcode, debug=debug)

            # sort address list for CFA #
            self.attr["dwarf"]["CFAIndex"].sort()

            if printable:
                printer(oneLineLoc)

        def _readNoteSection(fd, offset, size):
            if not size:
                printer("\tNone\n%s\n" % oneLine)
                return

            # set position #
            fd.seek(sh_offset)

            while 1:
                # read meta-data #
                namesz, descsz, ntype = struct.unpack("III", fd.read(12))

                # get name #
                if namesz > 0:
                    # 4-byte alignment #
                    nameszRemain = namesz % 4
                    if nameszRemain > 0:
                        namesz = namesz + 4 - nameszRemain

                    # read name #
                    name = fd.read(namesz).rstrip(b"\x00").decode()
                else:
                    name = "N/A"

                # get description #
                if descsz > 0:
                    # 4-byte alignment #
                    descszRemain = descsz % 4
                    if descszRemain > 0:
                        descsz = descsz + 4 - descszRemain

                    # read description #
                    desc = fd.read(descsz)

                    # convert description to bytes #
                    descstr = UtilMgr.convStr2Bytes(desc)
                else:
                    descstr = "N/A"

                printer(
                    "%20s %16s [type:%x] %s"
                    % (name, hex(descsz), ntype, descstr)
                )

                # align pos by 4 byte #
                pos = fd.tell()
                posRemain = pos % 4
                if posRemain > 0:
                    fd.seek(pos + 4 - posRemain)

                if sh_offset + size <= fd.tell():
                    break

            printer(oneLineLoc)

        # check note sections #
        if debug and _isValidSect(".note"):
            for sh_offset, size in e_notelist.items():
                # print note section title #
                printer(
                    "\n[Note %s Section]\n%s\n%20s %16s %s\n%s"
                    % (
                        hex(sh_offset).rstrip("L"),
                        twoLineLoc,
                        "Owner",
                        "Data size",
                        "Description",
                        twoLineLoc,
                    )
                )

                _readNoteSection(fd, sh_offset, size)

            # check .note sections #
            for name, idx in e_shnotelist.items():
                (
                    sh_name,
                    sh_type,
                    sh_flags,
                    sh_addr,
                    sh_offset,
                    sh_size,
                    sh_link,
                    sh_info,
                    sh_addralign,
                    sh_entsize,
                ) = self.getSectionInfo(fd, e_shoff + e_shentsize * idx)

                # get symbol string #
                shname = self.getString(str_section, sh_name)

                # print .note section title #
                printer(
                    "\n[%s Section]\n%s\n%20s %16s %s\n%s"
                    % (
                        shname,
                        twoLineLoc,
                        "Owner",
                        "Data size",
                        "Description",
                        twoLineLoc,
                    )
                )

                _readNoteSection(fd, sh_offset, sh_size)

        # check debug option #
        if not "DEBUGINFO" in SysMgr.environList or not SysMgr.dwarfEnable:
            debuginfo = False
        elif "DEBUGINFO" in SysMgr.environList:
            if (
                SysMgr.environList["DEBUGINFO"][0] == "SET"
                or self.path in SysMgr.environList["DEBUGINFO"]
                or origPath in SysMgr.environList["DEBUGINFO"]
            ):
                debuginfo = True
            else:
                debuginfo = False
        else:
            debuginfo = True

        # define hot functions #
        unpack = lambda s, d: struct.unpack(s, d)[0]
        decULEB = lambda table, pos: UtilMgr.decodeULEB128(
            table[pos : pos + 64].decode("latin-1")
        )
        decSLEB = lambda table, pos: UtilMgr.decodeSLEB128(
            table[pos : pos + 64].decode("latin-1")
        )

        # save debug_line_str section #
        if debuginfo and e_shdbglinestr >= 0:
            (
                sh_name,
                sh_type,
                sh_flags,
                sh_addr,
                sh_offset,
                sh_size,
                sh_link,
                sh_info,
                sh_addralign,
                sh_entsize,
            ) = self.getSectionInfo(fd, e_shoff + e_shentsize * e_shdbglinestr)

            # get symbol string #
            shname = self.getString(str_section, sh_name)

            # set position #
            fd.seek(sh_offset)

            # decompress section #
            origFd = fd
            fd, sh_size = ElfAnalyzer.decompSect(
                fd, path, shname, sh_offset, sh_size
            )
            isCompressed = fd != origFd

            # parse debug_line_str section #
            dbglinestr_section, sh_size = self.getDebugSection(
                fd,
                sh_offset,
                sh_size,
                shname,
            )

            _printStrSect(sh_name, dbglinestr_section)

            # recover original fd #
            if isCompressed:
                fd = origFd
        else:
            dbglinestr_section = ""

        # define _decodeOp #
        def _decodeOp(value, dwarfFormat):
            if not value:
                return None, None, ""

            # check opcode #
            op = value[0]
            if not op in ElfAnalyzer.DW_OPS_NAMES_MAP:
                return None, None, ""

            opcode = ElfAnalyzer.DW_OPS_NAMES_MAP[op]

            printable = debug and isTarget

            # print data #
            if printable:
                verbStr = "%s byte block: " % len(value)
                verbStr += " ".join(list(map(hex, value)))
            else:
                verbStr = None

            # convert value type to bytes #
            if opcode in ElfAnalyzer.DW_OPS_NOARGS:
                vals = 0
            else:
                vals = bytes(value[1:])

            # decode value #
            opval = None
            if vals == 0:
                pass
            elif opcode == "DW_OP_addr":
                if dwarfFormat == 32:
                    sig = "I"
                    sz = 4
                else:
                    sig = "Q"
                    sz = 8
                opval = unpack(sig, vals[:sz])
            elif opcode in (
                "DW_OP_addrx",
                "DW_OP_constu",
                "DW_OP_plus_uconst",
                "DW_OP_regx",
                "DW_OP_piece",
                "DW_OP_GNU_convert",
            ):
                opval, nsize = UtilMgr.decodeULEB128(vals)
            elif opcode in ("DW_OP_const1u", "DW_OP_pick"):
                opval = unpack("B", vals[:1])
            elif opcode in (
                "DW_OP_const1s",
                "DW_OP_deref_size",
                "DW_OP_xderef_size",
            ):
                opval = unpack("b", vals[:1])
            elif opcode in ("DW_OP_const2u", "DW_OP_call2"):
                opval = unpack("H", vals[:2])
            elif opcode in ("DW_OP_const2s", "DW_OP_bra", "DW_OP_skip"):
                opval = unpack("h", vals[:2])
            elif opcode in ("DW_OP_const4u", "DW_OP_call4"):
                opval = unpack("I", vals[:4])
            elif opcode == "DW_OP_const4s":
                opval = unpack("i", vals[:4])
            elif opcode == "DW_OP_const8u":
                opval = unpack("Q", vals[:8])
            elif opcode == "DW_OP_const8s":
                opval = unpack("q", vals[:8])
            elif opcode in ("DW_OP_call_ref", "DW_OP_GNU_parameter_ref"):
                # TODO: implement more #
                if dwarfFormat == 32:
                    sig = "I"
                    sz = 4
                else:
                    sig = "Q"
                    sz = 8
                opval = unpack(sig, vals[:sz])
            elif opcode in (
                "DW_OP_fbreg",
                "DW_OP_consts",
            ) or opcode.startswith("DW_OP_breg"):
                vals = vals.decode("latin-1")
                opval, nsize = UtilMgr.decodeSLEB128(vals)
            elif opcode in ("DW_OP_GNU_entry_value", "DW_OP_entry_value"):
                osize, nsize = UtilMgr.decodeULEB128(vals)
                olist = vals[nsize : nsize + osize]
                for b in olist:
                    b = b if isinstance(b, long) else ord(b)
                    if not b in ElfAnalyzer.DW_OPS_NAMES_MAP:
                        break
                    opval = ElfAnalyzer.DW_OPS_NAMES_MAP[b]
                    # TODO: implement nested code parser #
                    break
            elif opcode == "DW_OP_implicit_value":
                osize, nsize = UtilMgr.decodeULEB128(vals)
                # TODO: read blob of osize #
            elif opcode == "DW_OP_bregx":
                try:
                    osize, nsize = UtilMgr.decodeULEB128(vals)
                    osize, nsize = UtilMgr.decodeSLEB128(vals[osize:])
                except SystemExit:
                    sys.exit(0)
                except:
                    pass
            else:
                # TODO: implement more opcodes #
                """
                elif opcode == 'DW_OP_bit_piece':
                elif opcode == 'DW_OP_GNU_regval_type':
                elif opcode == 'DW_OP_GNU_implicit_pointer':
                elif opcode == 'DW_OP_GNU_const_type':
                elif opcode == 'DW_OP_GNU_deref_type':
                """
                SysMgr.printWarn(
                    (
                        "failed to decode '%s' "
                        "because there is no implementation"
                    )
                    % opcode
                )

            # print data expression #
            if printable:
                if opval and not opcode in ElfAnalyzer.DW_OPS_DEC_ARGS:
                    try:
                        opval = hex(opval)
                    except SystemExit:
                        sys.exit(0)
                    except:
                        pass

                verbStr += " (%s%s)" % (
                    opcode,
                    ": %s" % opval if opval else "",
                )

            return opcode, opval, verbStr

        # define _decodeForm #
        def _decodeForm(
            table, pos, form, addrSize, dwarfFormat, addStr="", refTable=None
        ):
            opcode = opval = 0

            # addr #
            if form == 0x1:
                sig = "I" if addrSize == 4 else "Q"
                value = unpack(sig, table[pos : pos + addrSize])
                pos += addrSize
            # addrx/udata/ref_udata/indirect/loclistx/rnglistx #
            # TODO: resolve indirect #
            elif form in (0x1B, 0x0F, 0x15, 0x16, 0x22, 0x23):
                value, nsize = decULEB(table, pos)
                pos += nsize
            # addrx1/data1/strx1/flag/ref1 #
            elif form in (0x25, 0x0B, 0x25, 0x0C, 0x11):
                value = unpack("B", table[pos : pos + 1])
                pos += 1
            # addrx2/data2/strx2/ref2 #
            elif form in (0x26, 0x05, 0x26, 0x12):
                value = unpack("H", table[pos : pos + 2])
                pos += 2
            # addrx3 #
            elif form == 0x27:
                value = unpack("3B", table[pos : pos + 3])
                pos += 3
            # addrx4/data4/strx4/ref/ref4 #
            elif form in (0x28, 0x06, 0x28, 0x02, 0x13):
                value = unpack("I", table[pos : pos + 4])
                pos += 4
            # data8/ref8/ref_sig8 #
            elif form in (0x07, 0x14, 0x20):
                value = unpack("Q", table[pos : pos + 8])
                pos += 8
            # block/block1/block2/block4 #
            elif form in (0x09, 0x0A, 0x03, 0x04):
                # block1 #
                if form == 0x0A:
                    sig = "B"
                    bsize = 1
                # block2 #
                elif form == 0x03:
                    sig = "H"
                    bsize = 2
                # block4 #
                elif form == 0x04:
                    sig = "I"
                    bsize = 4

                # block #
                if form == 0x9:
                    sz, nsize = decULEB(table, pos)
                    pos += nsize
                else:
                    # get size #
                    sz = unpack(sig, table[pos : pos + bsize])
                    pos += bsize

                # get data #
                value = table[pos : pos + sz]
                pos += sz
                value = struct.unpack("B" * sz, value)

                # decode data #
                opcode, opval, verbStr = _decodeOp(value, dwarfFormat)
                if verbStr:
                    addStr += verbStr
            # sdata #
            elif form == 0x0D:
                value, nsize = decSLEB(table, pos)
                pos += nsize
            # string #
            elif form == 0x08:
                # convert string #
                value, sz = self.getString(table[pos:], retlen=True)
                pos += sz + 1
            # strp/strp_sup/sec_offset/line_strp/strp_alt/ref_alt #
            elif form in (0x0E, 0x1D, 0x17, 0x1F, 0x1F21, 0x1F20):
                if refTable and pos in refTable:
                    value = refTable[pos][1]
                    pos += 4 if dwarfFormat == 32 else 8
                elif dwarfFormat == 32:
                    value = unpack("I", table[pos : pos + 4])
                    pos += 4
                else:
                    value = unpack("Q", table[pos : pos + 8])
                    pos += 8

                # strp #
                if form == 0x0E:
                    value = self.getString(dbgstr_section, value)
                # line_strp #
                elif form == 0x1F:
                    value = self.getString(dbglinestr_section, value)
            # strx3 #
            elif form == 0x27:
                value = unpack("3B", table[pos : pos + 3])
                pos += 3
            # ref_addr #
            elif form == 0x10:
                if ver == 2:
                    sig = "I" if addrSize == 4 else "Q"
                    value = unpack(sig, table[pos : pos + addrSize])
                    pos += addrSize
                elif dwarfFormat == 32:
                    value = unpack("I", table[pos : pos + 4])
                    pos += 4
                else:
                    value = unpack("Q", table[pos : pos + 8])
                    pos += 8
            # exprloc #
            elif form == 0x18:
                # get size #
                sz, nsize = decULEB(table, pos)
                pos += nsize

                # get data #
                value = table[pos : pos + sz]
                pos += sz
                value = list(map(long, struct.unpack("B" * sz, value)))

                # decode data #
                opcode, opval, verbStr = _decodeOp(value, dwarfFormat)
                if verbStr:
                    addStr += verbStr
            # flag_present #
            elif form == 0x19:
                value = 1
            # implicit_const #
            elif form == 0x21:
                if len(attr) > 2:
                    value = attr[2]
                else:
                    value = 0
            else:
                SysMgr.printErr(
                    "not implemented %s(0x%x) decoder"
                    % (
                        ElfAnalyzer.DW_FORM_MAP[form]
                        if form in ElfAnalyzer.DW_FORM_MAP
                        else form,
                        form,
                    ),
                )
                sys.exit(-1)

            return value, pos, addStr, opcode, opval

        # check debug_str section #
        if debuginfo and e_shdbgstr >= 0:
            (
                sh_name,
                sh_type,
                sh_flags,
                sh_addr,
                sh_offset,
                sh_size,
                sh_link,
                sh_info,
                sh_addralign,
                sh_entsize,
            ) = self.getSectionInfo(fd, e_shoff + e_shentsize * e_shdbgstr)

            # get symbol string #
            shname = self.getString(str_section, sh_name)

            # set position #
            fd.seek(sh_offset)

            # decompress section #
            origFd = fd
            fd, sh_size = ElfAnalyzer.decompSect(
                fd, path, shname, sh_offset, sh_size
            )
            isCompressed = fd != origFd

            # parse debug_str section #
            dbgstr_section, sh_size = self.getDebugSection(
                fd,
                sh_offset,
                sh_size,
                shname,
            )

            _printStrSect(sh_name, dbgstr_section)

            # recover original fd #
            if isCompressed:
                fd = origFd
        else:
            dbgstr_section = ""

        # check debug_abbrev section #
        if debuginfo and e_shdbgabbrev >= 0:
            (
                sh_name,
                sh_type,
                sh_flags,
                sh_addr,
                sh_offset,
                sh_size,
                sh_link,
                sh_info,
                sh_addralign,
                sh_entsize,
            ) = self.getSectionInfo(fd, e_shoff + e_shentsize * e_shdbgabbrev)

            # get symbol string #
            shname = self.getString(str_section, sh_name)

            # check target #
            isTarget = _isValidSect(shname)
            printable = debug and isTarget

            if printable:
                printer("\n[%s Section]\n%s" % (shname, twoLineLoc))

            # init variables #
            idx = 0
            typeDict = [{}]
            abbrevDict = [{}]
            self.attr.setdefault("dwarf", {})
            self.attr["dwarf"]["info"] = {}
            self.attr["dwarf"]["abbrev"] = typeDict
            attrDict = {
                "DW_AT_byte_size": "size",
                "DW_AT_encoding": "encoding",
                "DW_AT_name": "name",
                "DW_AT_type": "type",
                "DW_AT_sibling": "sibling",
                "DW_AT_data_member_location": "offset",
                "DW_AT_const_value": "value",
                "DW_AT_frame_base": "frame",
                "DW_AT_location": "loc",
                "DW_AT_low_pc": "addr",
                "DW_AT_decl_file": "file",
                "DW_AT_decl_line": "line",
            }

            # set position #
            fd.seek(sh_offset)

            # decompress section #
            origFd = fd
            fd, sh_size = ElfAnalyzer.decompSect(
                fd, path, shname, sh_offset, sh_size
            )
            isCompressed = fd != origFd

            # parse debug_abbrev section #
            pos = 0
            table, sh_size = self.getDebugSection(
                fd, sh_offset, sh_size, shname
            )

            while 1:
                if not isTarget:
                    break

                # check final end #
                if pos + 1 >= sh_size:
                    break

                # abbrev_code #
                abbrevCode, nsize = decULEB(table, pos)
                pos += nsize

                # create a new table #
                if abbrevCode == 0:
                    abbrevDict.append({})
                    typeDict.append({})
                    idx += 1
                    continue

                # tag #
                tag, nsize = decULEB(table, pos)
                pos += nsize

                # children_flag #
                childFlag = struct.unpack("B", table[pos : pos + 1])[0]
                pos += 1

                abbrevDict[idx][abbrevCode] = {
                    "tag": tag,
                    "child": childFlag,
                    "attrs": [],
                }

                while 1:
                    # name data #
                    name, nsize = decULEB(table, pos)
                    pos += nsize

                    # form data #
                    form, nsize = decULEB(table, pos)
                    pos += nsize

                    # check end #
                    if name == form == 0:
                        break

                    spec = [name, form]

                    # DW_FORM_implicit_const #
                    if form == 0x21:
                        val, nsize = decSLEB(table, pos)
                        pos += nsize
                        spec.append(val)

                    abbrevDict[idx][abbrevCode]["attrs"].append(spec)

            if printable:
                printStr = "%4s     %s   [%s]\n" % ("Num", "TAG", "Child")
                printStr += "%s\n" % twoLineLoc

                for idx in xrange(len(abbrevDict)):
                    printStr += "\n"
                    for name, value in abbrevDict[idx].items():
                        if value["child"]:
                            children = "has children"
                        else:
                            children = "no children"

                        # tag name #
                        if value["tag"] in ElfAnalyzer.DW_TAG_MAP:
                            tag = ElfAnalyzer.DW_TAG_MAP[value["tag"]]
                        else:
                            tag = None

                        printStr += "%4s     %s   [%s]\n" % (
                            name,
                            tag,
                            children,
                        )

                        for item in value["attrs"]:
                            # name #
                            if item[0] in ElfAnalyzer.DW_AT_MAP:
                                name = ElfAnalyzer.DW_AT_MAP[item[0]]
                            else:
                                name = "N/A"

                            # form #
                            if item[1] in ElfAnalyzer.DW_FORM_MAP:
                                form = ElfAnalyzer.DW_FORM_MAP[item[1]]
                            else:
                                form = "N/A"

                            printStr += "%4s %-20s %s\n" % (" ", name, form)

                printer(printStr)
                printer(oneLineLoc)

            # recover original fd #
            if isCompressed:
                fd = origFd

        # parse debug_info section #
        stmt_list = []
        if debuginfo and e_shdbginfo >= 0:
            (
                sh_name,
                sh_type,
                sh_flags,
                sh_addr,
                sh_offset,
                sh_size,
                sh_link,
                sh_info,
                sh_addralign,
                sh_entsize,
            ) = self.getSectionInfo(fd, e_shoff + e_shentsize * e_shdbginfo)

            # get symbol string #
            shname = self.getString(str_section, sh_name)

            # check target #
            isTarget = _isValidSect(shname)
            printable = debug and isTarget

            if printable:
                printer("\n[%s Section]\n%s" % (shname, twoLineLoc))

            # set position #
            fd.seek(sh_offset)

            # decompress section #
            origFd = fd
            fd, sh_size = ElfAnalyzer.decompSect(
                fd, path, shname, sh_offset, sh_size
            )
            isCompressed = fd != origFd

            # parse debug_info section #
            pos = 0
            idx = -1
            error = False
            lstrip = UtilMgr.lstrip
            table, sh_size = self.getDebugSection(
                fd, sh_offset, sh_size, shname
            )

            while 1:
                if not isTarget:
                    break

                # check final end #
                if pos >= sh_size:
                    break

                # print progress #
                UtilMgr.printProgress(pos, sh_size)

                # pos variables #
                parentPos = None
                posStack = []

                # length #
                size = unpack("I", table[pos : pos + 4])
                pos += 4
                addrPos = 4
                # extended length 8 bytes are needed #
                if size == 0xFFFFFFFF:
                    size = unpack("Q", table[pos : pos + 8])
                    pos += 8
                    addrPos += 8

                # format #
                dwarfFormat = 64 if size == 0xFFFFFFFF else 32

                # data #
                startPos = pos
                endPos = startPos + size

                # version #
                ver = unpack("H", table[pos : pos + 2])
                pos += 2

                if ver >= 5:
                    # unit type #
                    unitType = unpack("B", table[pos : pos + 1])
                    pos += 1

                    # address size #
                    addrSize = unpack("B", table[pos : pos + 1])
                    pos += 1

                    # debug_abbrev_offset #
                    if dwarfFormat == 32:
                        dao = unpack("I", table[pos : pos + 4])
                        pos += 4
                    else:
                        dao = unpack("Q", table[pos : pos + 8])
                        pos += 8
                else:
                    # unit type #
                    unitType = None

                    # debug_abbrev_offset #
                    if dwarfFormat == 32:
                        dao = unpack("I", table[pos : pos + 4])
                        pos += 4
                    else:
                        dao = unpack("Q", table[pos : pos + 8])
                        pos += 8

                    # address size #
                    addrSize = unpack("B", table[pos : pos + 1])
                    pos += 1

                # print #
                if printable:
                    printStr = "\nCompilation Unit @ offset 0x%x\n" % (
                        startPos - addrPos
                    )
                    printStr += "%-13s: 0x%x (%s-Bit)\n" % (
                        "Length",
                        size,
                        dwarfFormat,
                    )
                    printStr += "%-13s: %s\n" % ("Version", ver)
                    printStr += "%-13s: 0x%x\n" % ("Abbrev Offset", dao)
                    printStr += "%-13s: %s\n" % ("Pointer Size", addrSize)
                    if unitType:
                        addStr = (
                            ElfAnalyzer.DW_UT_MAP[unitType]
                            if unitType in ElfAnalyzer.DW_UT_MAP
                            else ""
                        )
                        addStr = " (%s)" % addStr if addStr else ""
                        printStr += "%-13s: %s%s\n" % (
                            "Unit Type",
                            unitType,
                            addStr,
                        )
                    printer(printStr)
                    printStr = "\n"

                # update variables #
                depth = 0
                if idx + 1 < len(abbrevDict):
                    idx += 1

                while 1:
                    # abbrev_code #
                    abbrevCode, nsize = decULEB(table, pos)
                    origPos = pos
                    pos += nsize

                    # check end #
                    if abbrevCode == 0:
                        depth -= 1
                        if posStack:
                            parentPos = posStack.pop()
                        else:
                            parentPos = None

                        # print #
                        if printable:
                            printer(
                                " <%s><%x>: Abbrev Number: %s"
                                % (depth + 1, origPos, abbrevCode)
                            )

                        # check depth #
                        if depth == 0:
                            break
                        else:
                            continue

                    try:
                        if not abbrevCode in abbrevDict[idx]:
                            raise Exception("no table")

                        # get a DIE #
                        value = abbrevDict[idx][abbrevCode]
                        tagid = value["tag"]
                        tag = ElfAnalyzer.DW_TAG_MAP[tagid]
                        child = value["child"]
                        attrs = value["attrs"]

                        # print abbrev number #
                        if printable:
                            printStr = (
                                " <%s><%x>: Abbrev Number: %s (%s)\n"
                                % (depth, origPos, abbrevCode, tag)
                            )

                        # increase depth #
                        if child:
                            depth += 1
                            if parentPos:
                                posStack.append(parentPos)
                            parentPos = origPos

                        # register variable type #
                        typeAttr = typeDict[idx][origPos] = {"type": tagid}

                        # register member/formal_parameter to parent #
                        if tagid in (0x0D, 0x05, 0x34):
                            # set name #
                            if tagid == 0x0D:
                                arg = "member"
                            elif tagid == 0x05:
                                arg = "param"
                            elif tagid == 0x34:
                                arg = "var"

                            # register value #
                            typeAttr["parent"] = parentPos
                            if parentPos in typeDict[idx]:
                                parent = typeDict[idx][parentPos]
                                parent.setdefault(arg, [])
                                parent[arg].append(origPos)

                        # get data from FORM attributes #
                        for attr in attrs:
                            addStr = ""

                            # name #
                            at = attr[0]
                            if at in ElfAnalyzer.DW_AT_MAP:
                                name = ElfAnalyzer.DW_AT_MAP[at]
                            else:
                                name = "Unknown AT"

                            # form #
                            form = attr[1]
                            if form in ElfAnalyzer.DW_FORM_MAP:
                                formstr = ElfAnalyzer.DW_FORM_MAP[form]
                            elif form in ElfAnalyzer.DW_AT_MAP:
                                formstr = ElfAnalyzer.DW_AT_MAP[form]
                            else:
                                raise Exception("no form")

                            # save original pos #
                            origPos = pos

                            # decode form #
                            value, pos, addStr, opcode, opval = _decodeForm(
                                table, pos, form, addrSize, dwarfFormat, addStr
                            )

                            # add variable attributes #
                            if typeAttr and name in attrDict:
                                typeAttr[attrDict[name]] = value
                                # subprogram #
                                if tagid == 0x2E and at == 0x11:
                                    dwarfInfo = self.attr["dwarf"]["info"]
                                    dwarfInfo[value] = typeAttr
                                    dwarfInfo[value]["abbrev"] = idx
                                # pointer type #
                                elif tagid == 0x0F:
                                    typeAttr["name"] = "*"
                                # const type #
                                elif tagid == 0x26:
                                    typeAttr["name"] = "const"
                                # location #
                                elif at == 0x40:
                                    typeAttr["frame"] = opcode
                                # location #
                                elif at == 0x02:
                                    # exprloc #
                                    if form == 0x18:
                                        typeAttr["loc"] = [opcode, opval]
                                    # sec_offset #
                                    elif form == 0x17:
                                        typeAttr["loc"] = value

                            # stmt_list #
                            if at == 0x10:
                                stmt_list.append(value)

                            # print #
                            if printable:
                                # type/sibling/abstract_origin #
                                if at in (0x49, 0x01, 0x31):
                                    try:
                                        addStr += "<0x%x>" % value
                                    except SystemExit:
                                        sys.exit(0)
                                    except:
                                        pass
                                # low_pc/high_pc/ranges/location #
                                elif at in (0x11, 0x12, 0x55, 0x02):
                                    try:
                                        addStr += hex(value)
                                    except SystemExit:
                                        sys.exit(0)
                                    except:
                                        pass
                                # language #
                                elif at == 0x13:
                                    if value in ElfAnalyzer.DW_LANG_MAP:
                                        lang = ElfAnalyzer.DW_LANG_MAP[value]
                                        addStr += lstrip(lang, "DW_LANG_")
                                # encoding #
                                elif at == 0x3E:
                                    if value in ElfAnalyzer.DW_ATE_MAP:
                                        enc = ElfAnalyzer.DW_ATE_MAP[value]
                                        addStr += "%s (%s)" % (
                                            value,
                                            lstrip(enc, "DW_ATE_"),
                                        )

                                helper = addStr if addStr else value

                                # demangle symbols #
                                if name in ("DW_AT_linkage_name"):
                                    helper = ElfAnalyzer.demangleSymbol(helper)

                                printStr += "    <%x>   %-18s: %s" % (
                                    origPos,
                                    name,
                                    helper,
                                )
                                printer(printStr)
                                printStr = ""
                    except SystemExit:
                        sys.exit(0)
                    except:
                        error = True
                        SysMgr.printErr(
                            "failed to get %s(0x%x) attributes from debug_info"
                            % (abbrevCode, abbrevCode),
                            True,
                        )
                        break

                    # check end #
                    if pos >= endPos:
                        break

                # check error #
                if error:
                    break

            # print progress #
            UtilMgr.deleteProgress()

            if printable:
                printer("%s\n" % oneLineLoc)
                if "DEBUGINFODICT" in SysMgr.environList:
                    SysMgr.printPipe(
                        UtilMgr.convDict2Str(typeDict, pretty=True)
                    )

            # recover original fd #
            if isCompressed:
                fd = origFd

        # check debug_line section #
        if debuginfo and e_shdbgline >= 0:
            (
                sh_name,
                sh_type,
                sh_flags,
                sh_addr,
                sh_offset,
                sh_size,
                sh_link,
                sh_info,
                sh_addralign,
                sh_entsize,
            ) = self.getSectionInfo(fd, e_shoff + e_shentsize * e_shdbgline)

            # get symbol string #
            shname = self.getString(str_section, sh_name)

            # check target #
            isTarget = _isValidSect(shname)
            printable = debug and isTarget

            # set position #
            fd.seek(sh_offset)

            # decompress section #
            origFd = fd
            fd, sh_size = ElfAnalyzer.decompSect(
                fd, path, shname, sh_offset, sh_size
            )
            isCompressed = fd != origFd

            # parse debug_line section #
            pos = 0
            table, sh_size = self.getDebugSection(
                fd, sh_offset, sh_size, shname
            )

            # get dwarfFormat #
            try:
                dwarfFormat
            except:
                dwarfFormat = 32

            # print warn #
            if not stmt_list:
                SysMgr.printWarn(
                    "no stmt_list info from .debug_info section", True
                )

            DW_LNS = ElfAnalyzer.DW_LNS
            DW_LNE = ElfAnalyzer.DW_LNE

            contents = []
            lineTable = []

            if printable:
                printer(
                    ("\n[%s Section] (nrItem: %s) (nrStmt: %s)\n%s\n")
                    % (
                        shname,
                        convNum(nrItems),
                        convNum(len(stmt_list)),
                        twoLine,
                    )
                )

                printer(
                    "{0:<40} {1:>7} {2:>18} {3:>5} {4:<1}\n{5:1}".format(
                        "File", "Line", "Addr", "Stmt", "Function", oneLine
                    )
                )

            for stmt in set(stmt_list):
                if not isTarget:
                    break

                pos = stmt

                # initial length #
                initLen = unpack("I", table[pos : pos + 4])
                pos += 4

                # version #
                ver = unpack("H", table[pos : pos + 2])
                pos += 2

                # address size #
                if ver >= 5:
                    addrSize = unpack("B", table[pos : pos + 1])
                    pos += 1
                else:
                    addrSize = wordSize

                # segment selector size #
                if ver >= 5:
                    segSelectSize = unpack("B", table[pos : pos + 1])
                    pos += 1
                else:
                    segSelectSize = 0

                # header length #
                if dwarfFormat == 32:
                    headLen = unpack("I", table[pos : pos + 4])
                    pos += 4
                    initLenFieldSize = 4
                else:
                    headLen = unpack("Q", table[pos : pos + 8])
                    pos += 8
                    initLenFieldSize = 12

                # mininum instruction length #
                minInstLen = unpack("B", table[pos : pos + 1])
                pos += 1

                # maximum operations per instruction #
                if ver >= 4:
                    maxOpPerInstr = unpack("B", table[pos : pos + 1])
                    pos += 1
                else:
                    maxOpPerInstr = 1

                # default is stmt #
                defstmt = unpack("B", table[pos : pos + 1])
                pos += 1

                # line base #
                linebase = unpack("b", table[pos : pos + 1])
                pos += 1

                # line range #
                linerange = unpack("B", table[pos : pos + 1])
                pos += 1

                # opcode base #
                opbase = unpack("B", table[pos : pos + 1])
                pos += 1

                # standard opcode #
                opbaselen = opbase - 1
                stdopcode = struct.unpack(
                    "%sB" % opbaselen, table[pos : pos + opbaselen]
                )
                pos += opbaselen

                # define path variables for v5 #
                dirs = []
                ldirs = []
                filenames = []
                lfilenames = []

                relaSect = ".rela.debug_line"
                refTable = (
                    relaTable[relaSect] if relaSect in relaTable else None
                )

                if ver >= 5:
                    # directory entry format #
                    defcnt = unpack("B", table[pos : pos + 1])
                    pos += 1

                    dirFormat = []
                    for i in xrange(defcnt):
                        # content type #
                        ctype, nsize = decULEB(table, pos)
                        pos += nsize
                        if not ctype in ElfAnalyzer.DW_LNCT_MAP:
                            SysMgr.printErr(
                                "wrong value '%s' for DW_LNCT" % ctype
                            )
                            break

                        # form #
                        form, nsize = decULEB(table, pos)
                        pos += nsize
                        if not form in ElfAnalyzer.DW_FORM_MAP:
                            SysMgr.printErr(
                                "wrong value '%s' for DW_FORM" % form
                            )
                            break

                        dirFormat.append(
                            [
                                ElfAnalyzer.DW_LNCT_MAP[ctype],
                                ElfAnalyzer.DW_FORM_MAP[form],
                            ]
                        )

                    # directories #
                    dircnt, nsize = decULEB(table, pos)
                    pos += nsize

                    for i in xrange(dircnt):
                        dirdata = []
                        for d in dirFormat:
                            # decode form #
                            value, pos, addStr, opcode, opval = _decodeForm(
                                table,
                                pos,
                                ElfAnalyzer.DW_FORM[d[1]],
                                addrSize,
                                dwarfFormat,
                                refTable=refTable,
                            )
                            dirdata.append(value)
                        dirs.append(dirdata[0])

                    # file name entry format #
                    defcnt = unpack("B", table[pos : pos + 1])
                    pos += 1

                    filenameFormat = []
                    for i in xrange(defcnt):
                        # content type #
                        ctype, nsize = decULEB(table, pos)
                        pos += nsize
                        if not ctype in ElfAnalyzer.DW_LNCT_MAP:
                            SysMgr.printErr(
                                "wrong value '%s' for DW_LNCT" % ctype
                            )
                            break

                        # form #
                        form, nsize = decULEB(table, pos)
                        pos += nsize
                        if not form in ElfAnalyzer.DW_FORM_MAP:
                            SysMgr.printErr(
                                "wrong value '%s' for DW_FORM" % form
                            )
                            break

                        filenameFormat.append(
                            [
                                ElfAnalyzer.DW_LNCT_MAP[ctype],
                                ElfAnalyzer.DW_FORM_MAP[form],
                            ]
                        )

                    # file names #
                    filecnt, nsize = decULEB(table, pos)
                    pos += nsize

                    for i in xrange(filecnt):
                        fdata = []
                        for d in filenameFormat:
                            # decode form #
                            value, pos, addStr, opcode, opval = _decodeForm(
                                table,
                                pos,
                                ElfAnalyzer.DW_FORM[d[1]],
                                addrSize,
                                dwarfFormat,
                                refTable=refTable,
                            )
                            fdata.append(value)
                        filenames.append(fdata)
                else:
                    # legacy directories #
                    while 1:
                        offset = table[pos:].find(b"\x00")
                        if offset < 0:
                            break

                        dirstr = table[pos : pos + offset]
                        pos += offset + 1

                        if dirstr[:-1] == b"":
                            break

                        ldirs.append(dirstr)

                    # legacy file names #
                    while 1:
                        offset = table[pos:].find(b"\x00")
                        if offset < 0:
                            break

                        fname = table[pos : pos + offset]
                        pos += offset + 1

                        if fname[:-1] == b"":
                            break

                        dindex, nsize = decULEB(table, pos)
                        pos += nsize

                        mtime, nsize = decULEB(table, pos)
                        pos += nsize

                        length, nsize = decULEB(table, pos)
                        pos += nsize

                        lfilenames.append([fname, dindex, mtime, length])

                def _resetLineState():
                    return {
                        "addr": 0,
                        "file": 1,
                        "line": 1,
                        "column": 0,
                        "opindex": 0,
                        "isstmt": defstmt,
                        "basicBlock": False,
                        "endSequence": False,
                        "prologueEnd": False,
                        "epilogueBegin": False,
                        "isa": 0,
                        "discriminator": 0,
                    }

                lineState = _resetLineState()

                entries = []

                def _addEntryNewState(cmd, args, isExtended=False):
                    entries.append(
                        (cmd, isExtended, args, UtilMgr.deepcopy(lineState))
                    )
                    lineState["discriminator"] = 0
                    lineState["basicBlock"] = False
                    lineState["prologueEnd"] = False
                    lineState["epilogueBegin"] = False

                def _addEntryOldState(cmd, args, isExtended=False):
                    entries.append((cmd, isExtended, args, None))

                # calculate the end offset for this program using DW_AT_stmt_list of CompileUnits in .debug_info #
                endOffset = stmt + initLen + initLenFieldSize

                # decode line table #
                while pos < endOffset:
                    # opcode #
                    op = unpack("B", table[pos : pos + 1])
                    pos += 1

                    # special #
                    if op >= opbase:
                        adjop = op - opbase
                        opadv = adjop // linerange
                        addrAddend = minInstLen * (
                            (lineState["opindex"] + opadv) // maxOpPerInstr
                        )
                        lineState["addr"] += addrAddend
                        lineState["opindex"] = (
                            lineState["opindex"] + opadv
                        ) % maxOpPerInstr
                        lineAddend = linebase + (adjop % linerange)
                        lineState["line"] += lineAddend
                        _addEntryNewState(
                            op, [lineAddend, addrAddend, lineState["opindex"]]
                        )
                    # extended #
                    elif op == 0:
                        instlen, nsize = decULEB(table, pos)
                        pos += nsize

                        # extended opcode #
                        exop = unpack("B", table[pos : pos + 1])
                        pos += 1

                        # handle opcode #
                        if exop == DW_LNE["DW_LNE_end_sequence"]:
                            lineState["endSequence"] = True
                            lineState["isstmt"] = 0
                            _addEntryNewState(exop, [], isExtended=True)
                            lineState = _resetLineState()
                        elif exop == DW_LNE["DW_LNE_set_address"]:
                            sig = "I" if addrSize == 4 else "Q"
                            operand = unpack(sig, table[pos : pos + addrSize])
                            pos += addrSize
                            lineState["addr"] = operand
                            _addEntryOldState(exop, [operand], isExtended=True)
                        elif exop == DW_LNE["DW_LNE_define_file"]:
                            # TODO: verify this part #
                            offset = table[pos:].find(b"\x00")

                            fname = table[pos : pos + offset]
                            pos += offset + 1

                            dindex, nsize = decULEB(table, pos)
                            pos += nsize

                            mtime, nsize = decULEB(table, pos)
                            pos += nsize

                            length, nsize = decULEB(table, pos)
                            pos += nsize

                            operand = [fname, dindex, mtime, length]

                            lfilenames.append(operand)

                            _addEntryOldState(exop, [operand], isExtended=True)
                        elif exop == DW_LNE["DW_LNE_set_discriminator"]:
                            operand, nsize = decULEB(table, pos)
                            pos += nsize
                            lineState["discriminator"] = operand
                        else:
                            # unknown opcode, seek forward #
                            pos -= instlen - 1
                    # standard #
                    else:
                        if op == DW_LNS["DW_LNS_copy"]:
                            _addEntryNewState(op, [])
                        elif op == DW_LNS["DW_LNS_advance_pc"]:
                            operand, nsize = decULEB(table, pos)
                            pos += nsize
                            addrAddend = operand * minInstLen
                            lineState["addr"] += addrAddend
                            _addEntryOldState(op, [addrAddend])
                        elif op == DW_LNS["DW_LNS_advance_line"]:
                            operand, nsize = decSLEB(table, pos)
                            pos += nsize
                            lineState["line"] += operand
                        elif op == DW_LNS["DW_LNS_set_file"]:
                            operand, nsize = decULEB(table, pos)
                            pos += nsize
                            lineState["file"] = operand
                            _addEntryOldState(op, [operand])
                        elif op == DW_LNS["DW_LNS_set_column"]:
                            operand, nsize = decULEB(table, pos)
                            pos += nsize
                            lineState["column"] = operand
                            _addEntryOldState(op, [operand])
                        elif op == DW_LNS["DW_LNS_negate_stmt"]:
                            lineState["isstmt"] = not lineState["isstmt"]
                            _addEntryOldState(op, [])
                        elif op == DW_LNS["DW_LNS_const_add_pc"]:
                            adjop = 255 - opbase
                            addrAddend = (adjop // linerange) * minInstLen
                            lineState["addr"] += addrAddend
                            _addEntryOldState(op, [addrAddend])
                        elif op == DW_LNS["DW_LNS_fixed_advance_pc"]:
                            operand = unpack("H", table[pos : pos + 2])
                            pos += 2
                            lineState["addr"] += operand
                            _addEntryOldState(op, [operand])
                        elif op == DW_LNS["DW_LNS_set_prologue_end"]:
                            lineState["prologueEnd"] = True
                            _addEntryOldState(op, [])
                        elif op == DW_LNS["DW_LNS_set_epilogue_begin"]:
                            lineState["epilogueBegin"] = True
                            _addEntryOldState(op, [])
                        elif op == DW_LNS["DW_LNS_set_isa"]:
                            operand, nsize = decULEB(table, pos)
                            pos += nsize
                            lineState["isa"] = operand
                            _addEntryOldState(op, [operand])
                        else:
                            SysMgr.printErr(
                                "failed to parse line program opcode '%s'" % op
                            )

                contents.append(
                    {
                        "dirs": dirs,
                        "ldirs": ldirs,
                        "filenames": filenames,
                        "lfilenames": lfilenames,
                        "entries": entries,
                    }
                )

                if filenames:
                    filelist = filenames
                    dirlist = dirs
                    vindex = 0
                else:
                    filelist = lfilenames
                    dirlist = ldirs
                    vindex = -1

                # print line info #
                for e in entries:
                    try:
                        cmd, extended, args, state = e
                    except SystemExit:
                        sys.exit(0)
                    except:
                        SysMgr.printWarn("failed to get line info", True, True)
                        continue

                    # new file #
                    if state is None:
                        if cmd == DW_LNS["DW_LNS_set_file"]:
                            didx = args[0] + vindex
                            fname, dindex = filelist[didx][:2]
                            if isinstance(fname, bytes):
                                fname = fname.decode()

                            if printable:
                                if dindex == 0:
                                    printer(
                                        "\n{0:<1}".format("./%s:[++]" % fname)
                                    )
                                else:
                                    dname = dirlist[dindex - 1]
                                    if isinstance(dname, bytes):
                                        dname = dname.decode()
                                    printer(
                                        "\n{0:<1}".format(
                                            "%s/%s:" % (dname, fname)
                                        )
                                    )
                        elif cmd == DW_LNE["DW_LNE_define_file"]:
                            # TODO: implement more #
                            pass
                    # line number #
                    else:
                        fname = filelist[state["file"] + vindex][0]
                        if isinstance(fname, bytes):
                            fname = fname.decode()
                        line = (
                            state["line"] if not state["endSequence"] else "-"
                        )
                        addr = state["addr"]
                        maxop = (
                            " "
                            if (ver < 4 or maxOpPerInstr == 1)
                            else state["opIndex"]
                        )
                        isstmt = (
                            "x"
                            if state["isstmt"] and not state["endSequence"]
                            else " "
                        )
                        lineTable.append([addr, line, fname])

                        if printable:
                            # get function name #
                            try:
                                f = self.getSymbolByOffset(addr)[0]
                            except SystemExit:
                                sys.exit(0)
                            except:
                                f = ""

                            printer(
                                "{0:<40} {1:>7} {2:>18} {3:>5} {4:<1}".format(
                                    fname, convNum(line), hex(addr), isstmt, f
                                )
                            )

                    if cmd == DW_LNS["DW_LNS_copy"]:
                        pass

                if printable:
                    if entries:
                        printer("%s\n" % oneLine)

            if printable:
                if not stmt_list:
                    printer("\tNone\n%s\n" % oneLine)

            for x in sorted(lineTable, key=lambda x: x[0]):
                addr, line, fname = x

                # get source index #
                try:
                    sidx = self.srcIdxTable[fname]
                except SystemExit:
                    sys.exit(0)
                except:
                    sidx = len(self.srcTable)
                    self.srcIdxTable[fname] = sidx
                    self.srcTable.append(fname)

                self.sortedAddrLineTable.append(addr)
                self.sortedLineTable.append([sidx, line])

            # recover original fd #
            if isCompressed:
                fd = origFd

        # remove useless data #
        del self.attr["dynsymList"]
        del self.attr["versymList"]

        # decompress gnu_debugdata section #
        try:
            secName = ".gnu_debugdata"
            if e_shdbgdata < 0 or not secName in self.attr["sectionHeader"]:
                raise ValueError("no " + secName)

            vals = self.attr["sectionHeader"][secName]
            doffset = vals["offset"]
            dsize = vals["size"]

            # read section #
            ddata = SysMgr.readFile(path, dsize, byte=True, offset=doffset)

            # decompress data #
            newpath = path + (" (%s)" % secName)
            mdata = SysMgr.getPkg("lzma", False).decompress(ddata)
            dfd = MemoryFile(name=newpath, data=mdata)

            # parse ELF data #
            dobj = ElfAnalyzer(path=newpath, debug=debug, fd=dfd)

            for dt in ("symTable", "dynsymTable", "dwarfTable"):
                if not dt in dobj.attr:
                    continue

                if not dt in self.attr:
                    self.attr[dt] = {}

                self.attr[dt].update(dobj.attr[dt])
        except ValueError:
            pass
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to parse %s" % secName, True)

        # print gnu_debuglink section #
        for secName, secIdx in (
            (".gnu_debuglink", e_shdbglink),
            (".gnu_debugaltlink", e_shdbgaltlink),
        ):
            try:
                if secIdx < 0 or not secName in self.attr["sectionHeader"]:
                    continue

                jname = secName.lstrip(".")
                self.attr.setdefault(jname, {})

                # read section #
                vals = self.attr["sectionHeader"][secName]
                doffset = vals["offset"]
                dsize = vals["size"]
                ddata = SysMgr.readFile(path, dsize, byte=True, offset=doffset)

                # get path and checksum #
                dpath = ddata.split(b"\x00")[0].decode("utf-8")

                self.attr[jname] = {
                    "offset": doffset,
                    "size": dsize,
                    "path": dpath,
                }

                if printable:
                    printer("\n[%s Section]\n%s" % (secName, twoLineLoc))
                    printer("Debug info file: %s" % dpath)
                    lenData = len(dpath.encode("utf-8"))
                    if secName == ".gnu_debuglink":
                        dchecksum = ddata[lenData + 1 :][-4:]
                        printer("CRC value: 0x%s" % dchecksum[::-1].hex())
                    elif secName == ".gnu_debugaltlink":
                        dbuildid = UtilMgr.convStr2Bytes(ddata[lenData + 1 :])
                        printer("Build-ID: 0x%s" % dbuildid)
                    printer(oneLineLoc)
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr("failed to parse %s" % secName, True)

        # check dynamic section #
        if e_shdynamic < 0:
            return None

        # parse dynamic section #
        (
            sh_name,
            sh_type,
            sh_flags,
            sh_addr,
            sh_offset,
            sh_size,
            sh_link,
            sh_info,
            sh_addralign,
            sh_entsize,
        ) = self.getSectionInfo(fd, e_shoff + e_shentsize * e_shdynamic)

        fd.seek(sh_offset + sh_size)

        # get symbol string #
        shname = self.getString(str_section, sh_name)

        # check target #
        isTarget = _isValidSect(shname)
        printable = debug and isTarget

        nrItems = long(sh_size / sh_entsize) if isTarget else 0

        if printable:
            printer(
                ("\n[%s Section] (nrItem: %s)\n%s\n%16s %20s %1s\n%s")
                % (
                    shname,
                    convNum(nrItems),
                    twoLineLoc,
                    "Tag",
                    "Type",
                    "Value",
                    twoLineLoc,
                )
            )

            if nrItems == 0 and isTarget:
                printer("    None")

        nrPrint = 0
        for i in xrange(nrItems):
            fd.seek(sh_offset + i * sh_entsize)
            dyndata = fd.read(sh_entsize)
            if not dyndata:
                break

            d_tag, d_un = struct.unpack(
                "II" if self.is32Bit else "QQ", dyndata
            )

            # NULL termination #
            if d_tag == d_un == 0:
                break

            if not debug or not isTarget:
                continue

            if d_tag in ElfAnalyzer.DT_TYPE:
                if ElfAnalyzer.DT_TYPE[d_tag] in ("NEEDED", "SONAME", "RPATH"):
                    printer(
                        "%016x %20s %1s"
                        % (
                            d_tag,
                            ElfAnalyzer.DT_TYPE[d_tag],
                            dynsymTable[d_un],
                        )
                    )
                elif ElfAnalyzer.DT_TYPE[d_tag] in (
                    "STRSZ",
                    "RELSZ",
                    "RELENT",
                    "PLTRELSZ",
                    "VERDEFNUM",
                    "VERNEEDNUM",
                    "RELCOUNT",
                ):
                    printer(
                        "%016x %20s %1s"
                        % (d_tag, ElfAnalyzer.DT_TYPE[d_tag], d_un)
                    )
                else:
                    printer(
                        "%016x %20s %1s"
                        % (d_tag, ElfAnalyzer.DT_TYPE[d_tag], hex(d_un))
                    )
            else:
                printer("%016x %20s %1s" % (d_tag, d_tag, hex(d_un)))

            nrPrint += 1

        if printable:
            if nrPrint == 0 and isTarget:
                printer("    None")

            printer("%s\n" % oneLineLoc)


class TaskAnalyzer(object):
    """Analyzer for thread profiling"""

    reportData = {}
    fileIntData = {}
    fileNameTable = {}
    dirLastData = {}
    lifeIntData = {}
    lifeProcData = {}
    lifecycleData = {}
    procTotData = {}
    procIntData = []
    procEventData = []
    procTreeData = {}
    procPrioData = {}
    dbusData = {"totalCnt": 0, "totalErr": 0}
    dbgObj = None
    tmpObj = None

    # request type #
    requestType = ["LOG", "EVENT", "PRINT", "REPORT", "THRESHOLD"]

    init_procTotData = {
        "comm": "",
        "ppid": 0,
        "nrThreads": 0,
        "pri": "",
        "startIdx": 0,
        "cpu": 0,
        "cpuMax": 0,
        "cpuMin": -1,
        "cpuAvg": 0,
        "dly": 0,
        "dlyMax": 0,
        "dlyMin": -1,
        "dlyAvg": 0,
        "initMem": 0,
        "lastMem": 0,
        "memDiff": 0,
        "minMem": 0,
        "maxMem": 0,
        "minVss": 0,
        "maxVss": 0,
        "blk": 0,
        "blkrd": 0,
        "blkwr": 0,
    }

    init_procIntData = {
        "cpu": 0,
        "cpuMax": 0,
        "cpuMin": -1,
        "cpuAvg": 0,
        "dly": 0,
        "dlyMax": 0,
        "dlyMin": -1,
        "dlyAvg": 0,
        "mem": 0,
        "memDiff": 0,
        "blk": 0,
        "blkrd": 0,
        "blkwr": 0,
        "die": False,
    }

    @staticmethod
    def checkFilter(comm, pid):
        # check no filter #
        if not SysMgr.filterGroup:
            return True

        # remove * for new tasks #
        comm = comm.lstrip("*")

        filterGroup = SysMgr.filterGroup

        # check exclusion condition #
        exCondCnt = 0
        for idx in filterGroup:
            if idx.startswith("^"):
                exCondCnt += 1
                cond = idx[1:]
                if pid == cond or UtilMgr.isValidStr(comm, [cond]):
                    return False

        # check all excusion condition #
        if exCondCnt == len(filterGroup):
            return True

        # check filter #
        if UtilMgr.isValidStr(comm) or pid in filterGroup:
            return True

    @staticmethod
    def getSummaryData(fname, incHdr=False, update=False):
        # load file #
        if fname:
            SysMgr.reloadFileBuffer(fname)

        # recognize data #
        header = None
        start = end = -1
        reverse = True
        for idx, item in enumerate(SysMgr.procBuffer):
            if "Top Summary Info" in item:
                if reverse:
                    header = SysMgr.procBuffer[:idx]
                reverse = False

            if update:
                if "Top PSS Info" in item:
                    SysMgr.pssEnable = True
                elif "Top USS Info" in item:
                    SysMgr.ussEnable = True

            if start == -1 and "[Top Info] " in item:
                start = idx
            if start >= 0 and not "[Top Info]" in item:
                end = idx
                break

        # check data #
        if start == end == -1:
            SysMgr.printErr("failed to recognize %s" % fname)
            return []

        # split header #
        if not header and start:
            header = SysMgr.procBuffer[: start - 1]

        # split interval data #
        SysMgr.procBuffer = SysMgr.procBuffer[start:end]

        # reverse sequence #
        if reverse:
            SysMgr.procBuffer.reverse()

        # trim interval data #
        if "TRIMIDX" in SysMgr.environList:
            try:
                SysMgr.procBuffer = SysMgr.getTrimVals(SysMgr.procBuffer)
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr(
                    "failed to trim interval data from '%s'" % fname, True
                )
                sys.exit(0)

        if incHdr:
            return header, SysMgr.procBuffer
        else:
            return SysMgr.procBuffer

    @staticmethod
    def extractEmbeddedFile(fname, ffilter):
        # load file #
        data = SysMgr.readFile(fname)

        # find embedded file section #
        name = "[ File Dump ]"
        pos = data.find(name)
        if pos < 0:
            return

        # split lines #
        lines = data[pos:].split("\n")

        path = None
        for l in lines:
            if l.startswith(name):
                fname = l[l.find("(") + 1 : l.find(" [")]

                # check file name #
                if not UtilMgr.isValidStr(fname, ffilter):
                    path = None
                    continue

                path = fname.replace("/", "_")
                path = os.path.join(".", path)
            elif l in (oneLine, twoLine, ""):
                continue
            elif path:
                SysMgr.printInfo(
                    "extract '%s [%s]'" % (path, UtilMgr.convSize2Unit(len(l)))
                )
                decoded = UtilMgr.decodeBase64(l)
                if decoded:
                    l = decoded
                SysMgr.writeFile(path, UtilMgr.decodeBase64(l), byte=True)

    @staticmethod
    def doSumReport(flist):
        targetChar = SysMgr.environList.get("CLASSIFYGROUP", ["CPU"])[
            0
        ].upper()
        if targetChar == "MEM":
            SysMgr.rssEnable = True
        elif targetChar == "BLK":
            pass
        else:
            targetChar = "CPU"
        taskGroup = {}
        classGroup = {}

        def _printSummary(fdata, onlyTotal, onlySummary, header, path=None):
            if not SysMgr.jsonEnable:
                SysMgr.printLogo(big=True, onlyFile=True)

            # update proc buffer #
            SysMgr.procBuffer = fdata

            # print header first #
            if header:
                if SysMgr.jsonEnable:
                    jsonData = SysMgr.parseInfoData(header)
                    if jsonData:
                        SysMgr.jsonData.update(jsonData)
                else:
                    SysMgr.printPipe(header)

            # print summary #
            try:
                # backup original values #
                origFd = SysMgr.printFd
                origStreamEnable = SysMgr.streamEnable
                origJsonEnable = SysMgr.jsonEnable
                fd = None

                if SysMgr.jsonEnable:
                    # create an on-memory file for temporary output #
                    SysMgr.printFd = MemoryFile(name="buf")

                    SysMgr.jsonEnable = False
                    SysMgr.streamEnable = True

                # summarize data #
                TaskAnalyzer.printIntervalUsage(
                    onlyTotal, onlySummary, current=False
                )

                SysMgr.jsonEnable = origJsonEnable
                SysMgr.streamEnable = origStreamEnable

                if SysMgr.jsonEnable:
                    # convert data to list #
                    fd = SysMgr.printFd
                    fd.seek(0)

                    # restore print fd #
                    SysMgr.printFd = origFd
                else:
                    raise ValueError("no json")

                # change merged file name #
                if fd:
                    path = [", ".join(path)]
                    fd.name = path[0]

                # add summarized stats #
                for p in path:
                    stats, memstat = TaskAnalyzer.getStatsFile(p, handle=fd)
                    if not stats:
                        continue
                    SysMgr.jsonData.update(stats)
                    SysMgr.jsonData["procMem"] = memstat

                # convert string to list #
                jsonData = SysMgr.jsonData
                for item in jsonData:
                    if not item.endswith("ProcUsage"):
                        continue

                    jsonSubData = jsonData[item]
                    for proc, stats in jsonSubData.items():
                        for k in stats:
                            if not k in ("usage") and not k.endswith("Usage"):
                                continue

                            try:
                                target = jsonSubData[proc][k].split("\n")
                                target = " ".join(target).split()
                                target = UtilMgr.cleanItem(target, False)
                                target = list(map(long, target))
                                SysMgr.jsonData[item][proc][k] = target
                            except SystemExit:
                                sys.exit(0)
                            except:
                                SysMgr.printWarn(
                                    "failed to convert '%s' to list"
                                    % jsonSubData[proc][k],
                                    True,
                                    True,
                                )

                # classify task groups #
                if "CLASSIFYGROUP" in SysMgr.environList:

                    def _getEnvNum(n, v):
                        return UtilMgr.getEnvironNum(
                            n, False, v, verb=False, isInt=True
                        )

                    cur = 0
                    nrTop = _getEnvNum("NRCLASSTOP", 5)
                    resGroup = {}
                    maxSize = SysMgr.maxSize
                    isValidStr = UtilMgr.isValidStr

                    # set target group #
                    if targetChar == "CPU":
                        tRes = "cpuProcUsage"
                        tSysRes = "cpuUsage"
                        tVal = "maximum"
                    elif targetChar == "MEM":
                        tRes = "memProcUsage"
                        tSysRes = "memFree"
                        tVal = "maxRss"
                    elif targetChar == "BLK":
                        tRes = "blkProcUsage"
                        tSysRes = "blkWait"
                        tVal = "total"
                    else:
                        tRes = None
                        tSysRes = None
                    tResAvg = sum(jsonData[tSysRes]) / len(jsonData[tSysRes])

                    # get filters #
                    incFilter = SysMgr.environList.get("INCFILTER")
                    exFilter = SysMgr.environList.get("EXCFILTER")
                    taskOutFilter = SysMgr.environList.get("TASKOUTFILTER", [])
                    if taskOutFilter:
                        for n in range(len(taskOutFilter)):
                            if isinstance(taskOutFilter[n], list):
                                break
                            taskOutFilter[n] = taskOutFilter[n].rsplit(":", 2)
                            taskOutFilter[n][1] = taskOutFilter[n][1].upper()
                            taskOutFilter[n][2] = long(taskOutFilter[n][2])

                    # apply uptime filter #
                    uptimeBt = _getEnvNum("UPTIMEBT", 0)
                    if (
                        uptimeBt
                        and min(jsonData.get("timeline", [0])) < uptimeBt
                    ):
                        return
                    uptimeLt = _getEnvNum("UPTIMELT", maxSize)
                    if min(jsonData.get("timeline", [0])) > uptimeLt:
                        return

                    # get task filter #
                    taskBt = _getEnvNum("CLASSTASKBT", 0)
                    taskLt = _getEnvNum("CLASSTASKLT", maxSize)

                    localTaskGroup = {}

                    for n, v in sorted(
                        jsonData[tRes].items(),
                        key=lambda x: x[1][tVal] if tVal in x[1] else 0,
                        reverse=True,
                    ):
                        # apply default filter #
                        if (
                            n.startswith("/")
                            or n == "[ TOTAL ]"
                            or not tVal in v
                        ):
                            continue

                        # apply comm filter #
                        comm = n.rsplit("(", 1)[0].strip("*")
                        if incFilter and not isValidStr(comm, incFilter):
                            continue
                        elif exFilter and isValidStr(comm, exFilter):
                            continue

                        # apply usage filter #
                        val = v[tVal]
                        if not (taskBt <= val <= taskLt):
                            break
                        for uf in taskOutFilter:
                            if not isValidStr(comm, [uf[0]]):
                                continue
                            if uf[1] == "BT":
                                if val < uf[2]:
                                    return
                            elif uf[1] == "LT":
                                if val > uf[2]:
                                    return
                            else:
                                SysMgr.printErr(
                                    "wrong task usage filter %s" % uf
                                )
                                sys.exit(0)

                        # save local task stats #
                        resGroup[comm] = resGroup.get(
                            comm, {"count": 0, "total": 0}
                        )
                        resGroup[comm]["count"] += 1
                        resGroup[comm]["total"] += val

                        # save global task info #
                        localTaskGroup[comm] = localTaskGroup.get(
                            comm, {"count": 0, "usage": [], "maxPrio": "I20"}
                        )
                        localTaskGroup[comm]["count"] += 1
                        localTaskGroup[comm]["usage"].append(val)

                        prio = (
                            jsonData.get("cpuProcPrio", {})
                            .get(n, {})
                            .get("usage")
                        )
                        if prio:
                            prioList = list(map(SysMgr.convSchedPrio, prio))
                            prioMax = min(
                                filter(lambda x: x is not None, prioList)
                            )
                            localTaskGroup[comm]["maxPrio"] = prio[
                                prioList.index(prioMax)
                            ]

                        # check top number #
                        cur += 1
                        if cur >= nrTop:
                            break

                    # get total stats and apply filters #
                    taskTotal = sum([x["total"] for x in resGroup.values()])
                    taskTotalBt = _getEnvNum("CLASSALLTASKBT", 0)
                    taskTotalLt = _getEnvNum("CLASSALLTASKLT", maxSize)
                    if not (taskTotalBt <= taskTotal <= taskTotalLt):
                        return
                    sysTotalBt = _getEnvNum("CLASSSYSBT", 0)
                    sysTotalLt = _getEnvNum("CLASSSYSLT", maxSize)
                    if not (sysTotalBt <= tResAvg <= sysTotalLt):
                        return

                    # accumulate task stats #
                    for tn, tv in localTaskGroup.items():
                        if not tn in taskGroup:
                            taskGroup[tn] = {
                                "count": 0,
                                "usage": [],
                                "sysTotal": [],
                                "maxPrio": "I20",
                            }
                        taskGroup[tn]["count"] += tv["count"]
                        taskGroup[tn]["usage"] += tv["usage"]
                        taskGroup[tn]["sysTotal"].append(tResAvg)
                        prio = tv.get("maxPrio")
                        if prio:
                            prioMax = SysMgr.convSchedPrio(prio)
                            curPrioMax = SysMgr.convSchedPrio(
                                taskGroup[tn]["maxPrio"]
                            )
                            if curPrioMax > prioMax:
                                taskGroup[tn]["maxPrio"] = prio

                    # get class name #
                    className = "+".join(sorted(resGroup))

                    # apply class name filter #
                    classInc = SysMgr.environList.get("CLASSINCFILTER", [])
                    classExc = SysMgr.environList.get("CLASSEXCFILTER", [])
                    if classInc and not isValidStr(className, classInc):
                        return
                    elif classExc and isValidStr(className, classExc):
                        return

                    # save class stats #
                    if not className in classGroup:
                        classGroup[className] = {}
                    classInfo = classGroup[className]
                    if not "taskTotal" in classInfo:
                        classInfo["taskTotal"] = []
                    if not "sysTotal" in classInfo:
                        classInfo["sysTotal"] = []
                    classInfo["count"] = classInfo.get("count", 0) + 1
                    classInfo["taskTotal"].append(taskTotal)
                    classInfo["sysTotal"].append(tResAvg)

                    # print raw data #
                    if "PRINTRAW" in SysMgr.environList:
                        SysMgr.streamEnable = True
                        SysMgr.printPipe(fdata)

                    return

                # print data in JSON format #
                SysMgr.printPipe(
                    UtilMgr.convDict2Str(
                        jsonData, pretty=not SysMgr.streamEnable
                    )
                )
            except ValueError:
                pass
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr(
                    "failed to print interval summary", reason=True
                )
                return

            # close all files #
            SysMgr.closeAllForPrint()

        if not flist:
            SysMgr.printErr("no input file")
            sys.exit(-1)

        # get ONLYTOTAL flag #
        onlyTotal = "ONLYTOTAL" in SysMgr.environList

        # get ONLYSUMMARY flag #
        onlySummary = "ONLYSUMMARY" in SysMgr.environList

        classifyGroup = "CLASSIFYGROUP" in SysMgr.environList
        if classifyGroup:
            SysMgr.jsonEnable = True

        # get NOMERGE flag #
        merge = not ("NOMERGE" in SysMgr.environList or classifyGroup)

        # get NOHEADER flag #
        incHdr = not "NOHEADER" in SysMgr.environList

        # merge files #
        fdata = []
        if "REVERSEFILE" in SysMgr.environList:
            flist.reverse()
        else:
            flist = sorted(flist)

        # get max file count #
        maxFileCnt = UtilMgr.getEnvironNum(
            "MAXFILECNT", False, SysMgr.maxSize, verb=False, isInt=True
        )

        convNum = UtilMgr.convNum

        nrTotal = 0
        header = None
        for fidx, fname in enumerate(flist):
            fname = os.path.realpath(fname)

            # update output file #
            SysMgr.inputFile = fname

            # get file size #
            fsize = UtilMgr.getFileSizeStr(fname)

            SysMgr.printStat(
                "start loading '%s'%s (%s/%s)"
                % (fname, fsize, convNum(fidx + 1), convNum(len(flist)))
            )

            # summarize data #
            ret = TaskAnalyzer.getSummaryData(fname, incHdr, update=True)
            if ret:
                nrTotal += 1
                if nrTotal >= maxFileCnt:
                    break
            else:
                continue

            if SysMgr.jsonEnable:
                SysMgr.jsonData["source"] = fname
                SysMgr.jsonData["sourceSize"] = fsize.strip(" []")

            # get header and data #
            if ret and incHdr:
                header, data = ret
            else:
                header = None
                data = ret

            # reorder buffer #
            SysMgr.reorderProcBuffer(data)

            # reverse data sequence #
            if "REVERSESAMPLE" in SysMgr.environList:
                data.reverse()

            # export embedded file data #
            if "EXTRACTFILE" in SysMgr.environList:
                TaskAnalyzer.extractEmbeddedFile(
                    fname, SysMgr.environList["EXTRACTFILE"]
                )

            # no merge #
            if not merge:
                _printSummary(data, onlyTotal, onlySummary, header, [fname])
                continue

            # remove redundant data #
            newData = []
            for item in data:
                if item in fdata:
                    continue
                newData.append(item)

            # merge data #
            fdata = newData + fdata

        if merge:
            _printSummary(
                fdata,
                onlyTotal,
                onlySummary,
                header if len(flist) == 1 else None,
                path=flist,
            )

        if classGroup:
            # print class stats #
            classTotal = sum([v["count"] for v in classGroup.values()])
            SysMgr.printPipe(
                (
                    "\n[Class Group Info] (Target: %s) (NrTotal: %s) "
                    "(NrSample: %s) (NrSamplePer: %s%%) (NrGroup: %s)\n%s"
                )
                % (
                    targetChar,
                    convNum(nrTotal),
                    convNum(classTotal),
                    long(classTotal / float(nrTotal) * 100),
                    convNum(len(classGroup)),
                    twoLine,
                )
            )
            SysMgr.printPipe(
                "{0:>5} ({1:>3}) {2:>5} {3:>5} {4:>5} {5:>5} {6:1}\n{7:1}".format(
                    "Cnt", "%", "Avg", "Min", "Max", "Sys", "Group", oneLine
                )
            )
            for n, v in sorted(
                classGroup.items(), key=lambda x: x[1]["count"], reverse=True
            ):
                SysMgr.printPipe(
                    "{0:>5} ({1:>3}) {2:>5} {3:>5} {4:>5} {5:>5} {6:1}".format(
                        v["count"],
                        long(v["count"] / float(classTotal) * 100),
                        long(sum(v["taskTotal"]) / len(v["taskTotal"])),
                        long(min(v["taskTotal"])),
                        long(max(v["taskTotal"])),
                        long(sum(v["sysTotal"]) / len(v["sysTotal"])),
                        n,
                    )
                )
            SysMgr.printPipe(oneLine)

            # print task stats #
            classIncFilter = SysMgr.environList.get("CLASSINCFILTER", [])
            classExcFilter = SysMgr.environList.get("CLASSEXCFILTER", [])
            procTotal = sum([v["count"] for v in taskGroup.values()])
            SysMgr.printPipe(
                (
                    "\n[Class Task Info] (Target: %s) (NrTotal: %s) "
                    "(NrSample: %s) (NrSamplePer: %s%%) (NrProc: %s)\n%s"
                )
                % (
                    targetChar,
                    convNum(nrTotal),
                    convNum(classTotal),
                    long(classTotal / float(nrTotal) * 100),
                    convNum(len(taskGroup)),
                    twoLine,
                )
            )
            SysMgr.printPipe(
                "{0:>5} ({1:>3}) {2:>5} {3:>5} {4:>5} {5:>5} {6:>4} {7:1}\n{8:1}".format(
                    "Cnt",
                    "%",
                    "Avg",
                    "Min",
                    "Max",
                    "Sys",
                    "Prio",
                    "Task",
                    oneLine,
                )
            )
            for n, v in sorted(
                taskGroup.items(), key=lambda x: x[1]["count"], reverse=True
            ):
                if classIncFilter and not UtilMgr.isValidStr(
                    n, classIncFilter
                ):
                    continue
                elif classExcFilter and UtilMgr.isValidStr(n, classExcFilter):
                    continue

                SysMgr.printPipe(
                    "{0:>5} ({1:>3}) {2:>5} {3:>5} {4:>5} {5:>5} {6:>4} {7:1}".format(
                        v["count"],
                        long(v["count"] / float(classTotal) * 100),
                        long(sum(v["usage"]) / len(v["usage"])),
                        long(min(v["usage"])),
                        long(max(v["usage"])),
                        long(sum(v["sysTotal"]) / len(v["sysTotal"])),
                        v["maxPrio"],
                        n,
                    )
                )
            SysMgr.printPipe(oneLine)

    @staticmethod
    def doDiffReports(flist):
        def _getProcName(pinfo):
            merge = not "NOMERGE" in SysMgr.environList
            if merge:
                return pinfo.rsplit("(", 1)[0]
            else:
                return "-".join(pinfo.rstrip(")").rsplit("(", 1))

        # enable RSS flag #
        SysMgr.rssEnable = True

        # get stats from files #
        flist = UtilMgr.getFileList(flist, exceptDir=True)
        if not flist:
            SysMgr.printErr("no input for PATH")
            sys.exit(-1)

        # get diff target #
        fromStart = "FROMSTART" in SysMgr.environList

        # define variable and table #
        nrFiles = len(flist)
        unionCpuList = {}
        unionGpuList = {}
        unionRssList = {}
        unionSdList = {}
        unionSdBootList = {}
        statFileList = {}
        sortStd = ""

        # get diff type #
        if SysMgr.cpuAvgEnable:
            item = "average"
            diffType = "AvgDiff"
        else:
            item = "total"
            diffType = "TotDiff"

        # parse stats from multiple files #
        for idx, lfile in enumerate(flist):
            try:
                gstats, _ = TaskAnalyzer.getStatsFile(lfile, applyOpt=False)
                if not gstats:
                    continue
            except SystemExit:
                sys.exit(0)
            except:
                sys.exit(-1)

            # save all stats in a file #
            statFileList[lfile] = gstats

            # define proc usage #
            cpuProcUsage = gstats["cpuProcUsage"]
            memProcUsage = gstats["memProcUsage"]
            gpuProcUsage = gstats["gpuProcUsage"] = {}

            # define systemd list #
            sdUnitList = gstats.get("systemdInfo", {})

            # get total CPU info #
            cpuUsage = gstats["cpuUsage"]
            if not cpuUsage:
                break
            cpuProcUsage["TOTAL"] = {
                "usage": cpuUsage,
                "average": sum(cpuUsage) / float(len(cpuUsage)),
                "minimum": min(cpuUsage),
                "maximum": max(cpuUsage),
            }

            # get total GPU info #
            gpuProcUsage = gstats["gpuUsage"]

            # get total free info #
            memFree = gstats["memFree"]
            memProcUsage["FREE"] = {
                "rssUsage": memFree,
                "average": sum(memFree) / len(memFree),
                "minRss": min(memFree),
                "maxRss": max(memFree),
            }

            # remove * characters #
            for pinfo in sorted(cpuProcUsage):
                if pinfo.startswith("*"):
                    cpuProcUsage[pinfo[1:]] = cpuProcUsage.pop(pinfo)
                else:
                    break

            # merge tasks having same name #
            prevTask = None
            for pinfo in sorted(cpuProcUsage):
                pname = _getProcName(pinfo)

                # convert usage string to list #
                try:
                    cpuProcUsage[pinfo]["usage"] = list(
                        map(long, cpuProcUsage[pinfo]["usage"].split())
                    )
                except:
                    pass

                # register the first task #
                if prevTask != pname:
                    prevTask = pname

                    target = cpuProcUsage[pinfo]
                    cpuProcUsage.setdefault(pname, target)
                    target["cnt"] = 1
                    target["total"] = sum(target["usage"])
                    target["average"] = target["total"] / float(
                        len(target["usage"])
                    )
                    if "(" in pinfo:
                        cpuProcUsage.pop(pinfo)

                    continue

                # merge tasks #
                target = cpuProcUsage[pname]
                usage = target["usage"]
                target["usage"] = list(
                    map(sum, zip(*[usage, cpuProcUsage[pinfo]["usage"]]))
                )

                # update stats #
                target["cnt"] += 1
                usage = target["usage"]
                target["minimum"] = min(usage)
                target["maximum"] = max(usage)
                target["total"] = sum(usage)
                target["average"] = target["total"] / float(len(usage))

                # pop this task #
                if "(" in pinfo:
                    cpuProcUsage.pop(pinfo)

            # iterate CPU list #
            for pinfo, value in sorted(cpuProcUsage.items()):
                if not cpuProcUsage:
                    break

                pname = _getProcName(pinfo)

                # register comm #
                unionCpuList.setdefault("TOTAL", 0)
                unionCpuList.setdefault(pname, 0)

                # save diff itself #
                if idx > 0:
                    targetList = {}
                    prevIdx = 0 if fromStart else idx - 1
                    prevProcList = statFileList[flist[prevIdx]]["cpuProcUsage"]

                    for proc, pval in prevProcList.items():
                        if _getProcName(proc) == pname:
                            targetList.setdefault(pname, pval)

                    # get diff #
                    if not targetList:
                        value["diff"] = value[item]
                    elif len(targetList) == 1:
                        target = targetList.popitem()[1]
                        value["diff"] = value[item] - target[item]
                    else:
                        pass

            # set diff to the union list if this file is latest one #
            if nrFiles == 1:
                cpuStats = statFileList[lfile]["cpuProcUsage"]
                for pname in list(unionCpuList):
                    try:
                        unionCpuList[pname] = cpuStats[pname]["average"]
                    except SystemExit:
                        sys.exit(0)
                    except:
                        pass
            elif idx == nrFiles - 1 and nrFiles > 1:
                prevProcList = statFileList[flist[-2]]["cpuProcUsage"]
                lastProcList = statFileList[flist[-1]]["cpuProcUsage"]
                for pname, value in unionCpuList.items():
                    if pname in lastProcList:
                        try:
                            unionCpuList[pname] = (
                                lastProcList[pname]["average"]
                                - prevProcList[pname]["average"]
                            )
                        except:
                            unionCpuList[pname] = lastProcList[pname][
                                "average"
                            ]
                    elif pname in prevProcList:
                        unionCpuList[pname] = -(prevProcList[pname]["average"])

            # remove * characters #
            for pinfo in sorted(gpuProcUsage):
                if pinfo.startswith("*"):
                    gpuProcUsage[pinfo[1:]] = gpuProcUsage.pop(pinfo)
                else:
                    break

            # merge tasks having same name #
            prevTask = None
            for pinfo in sorted(gpuProcUsage):
                pname = _getProcName(pinfo)

                # convert usage string to list #
                try:
                    gusage = list(map(long, gpuProcUsage[pinfo].split()))
                    gpuProcUsage[pinfo] = {"usage": gusage}
                    gstats["gpuProcUsage"][pinfo] = gpuProcUsage[pinfo]
                except:
                    pass

                # register the first task #
                if prevTask != pname:
                    prevTask = pname

                    target = gpuProcUsage[pinfo]
                    target["cnt"] = 1
                    target["minimum"] = min(target["usage"])
                    target["maximum"] = max(target["usage"])
                    target["total"] = sum(target["usage"])
                    target["average"] = target["total"] / float(
                        len(target["usage"])
                    )

                    gpuProcUsage.setdefault(pname, target)

                    if "(" in pinfo:
                        gpuProcUsage.pop(pinfo)

                    continue

                # merge tasks #
                target = gpuProcUsage[pname]
                usage = target["usage"]
                target["usage"] = list(
                    map(sum, zip(*[usage, gpuProcUsage[pinfo]["usage"]]))
                )

                # update stats #
                target["cnt"] += 1
                usage = target["usage"]
                target["minimum"] = min(usage)
                target["maximum"] = max(usage)
                target["total"] = sum(usage)
                target["average"] = target["total"] / float(len(usage))

                # pop this task #
                if "(" in pinfo:
                    gpuProcUsage.pop(pinfo)

            # iterate GPU list #
            for pinfo, value in gpuProcUsage.items():
                pname = _getProcName(pinfo)

                # register comm #
                unionGpuList.setdefault(pname, 0)

                # save diff itself #
                if idx > 0:
                    targetList = {}
                    prevIdx = 0 if fromStart else idx - 1
                    prevProcList = statFileList[flist[prevIdx]]["gpuProcUsage"]

                    for proc, pval in prevProcList.items():
                        if _getProcName(proc) == pname:
                            targetList.setdefault(proc, pval)

                    # get diff #
                    if not targetList:
                        value["diff"] = value[item]
                    elif len(targetList) == 1:
                        target = targetList.popitem()[1]
                        value["diff"] = value[item] - target[item]
                    else:
                        pass

                # set diff to the union list if this file is latest one #
                if nrFiles == 1:
                    gpuStats = statFileList[lfile]["gpuProcUsage"]
                    for pname in list(unionGpuList):
                        try:
                            unionGpuList[pname] = gpuStats[pname]["average"]
                        except SystemExit:
                            sys.exit(0)
                        except:
                            pass
                elif idx == nrFiles and nrFiles > 1:
                    unionGpuList[pname] = value["diff"]

            # remove * characters #
            for pinfo in sorted(memProcUsage):
                if pinfo.startswith("*"):
                    memProcUsage[pinfo[1:]] = memProcUsage.pop(pinfo)
                else:
                    break

            # merge tasks having same name #
            prevTask = None
            for pinfo in sorted(memProcUsage):
                pname = _getProcName(pinfo)

                # convert usage string to list #
                try:
                    rssList = memProcUsage[pinfo]["rssUsage"]
                    if isinstance(rssList, str):
                        rssList = rssList.split()
                    memProcUsage[pinfo]["rssUsage"] = list(map(long, rssList))
                except:
                    continue

                # register the first task #
                if prevTask != pname:
                    prevTask = pname

                    target = memProcUsage[pinfo]
                    memProcUsage.setdefault(pname, target)
                    rss = target["rssUsage"]
                    target["cnt"] = 1
                    target["minRss"] = min(rss)
                    target["maxRss"] = max(rss)
                    target["avgRss"] = sum(rss) / len(rss)
                    if "(" in pinfo:
                        memProcUsage.pop(pinfo)

                    continue

                # merge tasks #
                target = memProcUsage[pname]
                rss = target["rssUsage"]
                target["rssUsage"] = list(
                    map(sum, zip(*[rss, memProcUsage[pinfo]["rssUsage"]]))
                )

                # update stats #
                target["cnt"] += 1
                rss = target["rssUsage"]
                target["minRss"] = min(rss)
                target["maxRss"] = max(rss)
                target["avgRss"] = sum(rss) / len(rss)

                # pop this task #
                if "(" in pinfo:
                    memProcUsage.pop(pinfo)

            # iterate RSS list #
            for pinfo, value in memProcUsage.items():
                pname = _getProcName(pinfo)

                # register comm #
                unionRssList.setdefault("FREE", 0)
                unionRssList.setdefault(pname, 0)

                # set stat #
                if pname == "FREE":
                    stat = "minRss"
                else:
                    stat = "maxRss"

                # save diff itself #
                if idx > 0:
                    targetList = {}
                    prevIdx = 0 if fromStart else idx - 1
                    prevProcList = statFileList[flist[prevIdx]]["memProcUsage"]

                    for proc, pval in prevProcList.items():
                        if _getProcName(proc) == pname:
                            targetList.setdefault(proc, pval)

                    # get diff by average #
                    try:
                        if not targetList:
                            if stat in value:
                                value["diff"] = value[stat]
                        elif len(targetList) == 1:
                            target = targetList.popitem()[1]
                            if stat in value:
                                value["diff"] = value[stat] - target[stat]
                        else:
                            pass
                    except:
                        continue

            # set diff to the union list if this file is latest one #
            if nrFiles == 1:
                memStats = statFileList[lfile]["memProcUsage"]
                for pname in list(unionRssList):
                    try:
                        unionRssList[pname] = memStats[pname]["maxRss"]
                    except SystemExit:
                        sys.exit(0)
                    except:
                        pass
            elif idx == nrFiles - 1 and nrFiles > 1:
                prevProcList = statFileList[flist[-2]]["memProcUsage"]
                lastProcList = statFileList[flist[-1]]["memProcUsage"]
                for pname, value in unionRssList.items():
                    # set stat #
                    if pname == "FREE":
                        stat = "minRss"
                    else:
                        stat = "maxRss"

                    if pname in lastProcList:
                        try:
                            unionRssList[pname] = (
                                lastProcList[pname][stat]
                                - prevProcList[pname][stat]
                            )
                        except:
                            unionRssList[pname] = lastProcList[pname][stat]
                    elif pname in prevProcList:
                        unionRssList[pname] = -(prevProcList[pname][stat])

            # init systemd varianles #
            INITSTATS = (
                "activating",
                "activated",
                "activation",
                "deactivated",
            )
            eventFilter = SysMgr.environList.get("UNITFILTER", [])
            eventExFilter = SysMgr.environList.get("UNITEXFILTER", [])
            sortByName = False
            sortStd = SysMgr.environList.get("SORT", ["STARTDIFF"])[0].upper()
            if sortStd == "INITDIFF":
                sortStd = "initDiff"
            elif sortStd == "START":
                sortStd = "activating"
            elif sortStd == "NAME":
                sortByName = True
                sortStd = "activating"
            else:
                sortStd = "startDiff"

            # iterate systemd list #
            bootStat = {}
            for bus, v in sdUnitList.items():
                # get boot info #
                bstats = {}
                bootInfo = v.get("systemd", {})
                for bn, bs in bootInfo.items():
                    bstats[bn] = long(bs) / 1000000.0
                if bstats:
                    bootStat["EARLY"] = bstats
                    unionSdBootList.update(bstats)

                # per unit #
                for unit, val in v.items():
                    if not isinstance(val, dict) or not "Boot" in val:
                        continue

                    # check filter #
                    if eventFilter and not UtilMgr.isValidStr(
                        unit, eventFilter
                    ):
                        continue
                    elif eventExFilter and UtilMgr.isValidStr(
                        unit, eventExFilter
                    ):
                        continue

                    # get stats #
                    stats = {}
                    bval = val["Boot"]
                    for name in INITSTATS:
                        stats[name] = long(bval[name]) / 1000000.0
                    stats["actDiff"] = (
                        (stats["activated"] - stats["activating"])
                        if stats["activated"]
                        else 0
                    )

                    # save stats #
                    bootStat.update({unit: stats})

                    # register unit #
                    unionSdList.setdefault(unit, 0)

                    # save diff itself #
                    if idx > 0:
                        prevIdx = 0 if fromStart else idx - 1
                        prevSdList = statFileList[flist[prevIdx]].get(
                            "systemdInfo", {}
                        )
                        prevSdList = prevSdList.get("BOOT", {}).get(unit, {})
                        # get init diff #
                        if prevSdList:
                            stats["startDiff"] = (
                                stats["activating"] - prevSdList["activating"]
                            )
                            stats["initDiff"] = (
                                stats["actDiff"] - prevSdList["actDiff"]
                            )
                        else:
                            stats["startDiff"] = stats["activating"]
                            stats["initDiff"] = stats["actDiff"]

                    # set diff to the union list if this file is latest one #
                    if nrFiles == 1:
                        for uname in list(unionSdList):
                            try:
                                unionSdList[uname] = bootStat[uname][sortStd]
                            except SystemExit:
                                sys.exit(0)
                            except:
                                if sortStd == "startDiff":
                                    esortStd = "activating"
                                elif sortStd == "initDiff":
                                    esortStd = "actDiff"
                                try:
                                    unionSdList[uname] = bootStat[uname][
                                        esortStd
                                    ]
                                except SystemExit:
                                    sys.exit(0)
                                except:
                                    pass
                    elif idx == nrFiles - 1:
                        if prevSdList.get("activating"):
                            unionSdList[unit] = bootStat[unit][sortStd]
                        else:
                            unionSdList[unit] = SysMgr.maxSize

            # update boot stats #
            sdUnitList["BOOT"] = bootStat

        # define shortcut functions #
        convNum = UtilMgr.convNum
        convColor = UtilMgr.convColor

        jsonData = {}

        # define printer #
        if SysMgr.jsonEnable:

            def _printer(string):
                return

            if not SysMgr.outPath:
                SysMgr.setStream()
        else:

            def _printer(string):
                SysMgr.printPipe(string)

        # define color description #
        colors = "(Color: %s|%s|%s)" % (
            convColor("Increased", "RED"),
            convColor("Decreased", "GREEN"),
            convColor("Removed", "WARNING"),
        )

        # -------------------- CPU -------------------- #

        emptyCpuStat = "%7s(%2s)(%5s/%7s/%5s/%6s) |" % (
            "-",
            "-",
            "-",
            "-",
            "-",
            "-",
        )
        menuStat = "%7s(%2s)(%5s/%7s/%5s/%6s) |" % (
            diffType,
            "Nr",
            "Min",
            "Avg",
            "Max",
            "Tot",
        )
        lenCpuStat = len(emptyCpuStat)

        # print file names #
        totalBuf = ""
        menuBuf = "{0:^16} | ".format("Task")
        printBuf = "{0:^16} | ".format("File")
        for fname in flist:
            fpath = fname[-lenCpuStat + 1 :]
            if "ONLYFILE" in SysMgr.environList:
                fpath = os.path.basename(fpath)

            printBuf = ("{0:1} {1:^%d}|" % (lenCpuStat - 1)).format(
                printBuf, fpath
            )
            menuBuf = ("{0:1} {1:^%d}" % len(menuStat)).format(
                menuBuf, menuStat
            )
        lineLen = len(menuBuf)
        oneLineStr = "-" * lineLen
        twoLineStr = "=" * lineLen
        jsonData["cpu"] = {}

        # print CPU diff #
        _printer(
            "\n[Diff CPU Info] (NrTask: %s) %s\n%s"
            % (convNum(len(unionCpuList) - 2), colors, twoLineStr)
        )

        printBuf = "%s\n%s\n%s\n%s" % (
            printBuf,
            oneLineStr,
            menuBuf,
            twoLineStr,
        )
        _printer(printBuf)

        # get error range #
        errnum = UtilMgr.getEnvironNum(
            "CPUERRRANGE", False, 0, False, isFloat=True
        )
        nrPrint = 0

        for pname, value in sorted(
            unionCpuList.items(), key=lambda e: float(e[1]), reverse=True
        ):
            if pname == "[ TOTAL ]":
                continue
            elif pname == "TOTAL":
                printBuf = "%16s | " % "[CPU/AVG]"
            else:
                printBuf = "%16s | " % pname[-16:]

            totalerr = 0
            for idx, fname in enumerate(flist):
                try:
                    prevCpuProcList = statFileList[flist[idx - 1]][
                        "cpuProcUsage"
                    ]
                except:
                    prevCpuProcList = {}

                cpuProcList = statFileList[fname]["cpuProcUsage"]
                jsonData["cpu"].setdefault(pname, {})

                # no target process in this file #
                if not pname in cpuProcList:
                    if idx > 0 and pname in prevCpuProcList:
                        diff = "%6.1f%%" % -(prevCpuProcList[pname][item])
                        diff = convColor(diff, "WARNING", 6)
                        printBuf = "%s %6s%s" % (
                            printBuf,
                            diff,
                            emptyCpuStat[7:],
                        )
                    else:
                        diff = "-"
                        printBuf = "%s %s" % (printBuf, emptyCpuStat)

                    continue

                cpuProcStat = cpuProcList[pname]
                if not "diff" in cpuProcStat:
                    diff = "-"
                elif cpuProcStat["diff"] > 0:
                    diff = "{0:>6}%".format(
                        "%6s"
                        % ("+%s" % convNum(cpuProcStat["diff"], isFloat=True))
                    )
                    diff = convColor(diff, "RED", 6)
                    totalerr += cpuProcStat["diff"]
                elif cpuProcStat["diff"] < 0:
                    diff = "{0:>6}%".format(
                        "%6s"
                        % (
                            "-%s"
                            % convNum(abs(cpuProcStat["diff"]), isFloat=True)
                        )
                    )
                    diff = convColor(diff, "GREEN", 6)
                    totalerr += abs(cpuProcStat["diff"])
                else:
                    diff = "0"

                total = convNum(cpuProcStat["total"])

                jsonData["cpu"][pname][fname] = {
                    "diff": diff.strip(" %"),
                    "total": total,
                    "cnt": cpuProcStat["cnt"],
                    "min": cpuProcStat["minimum"],
                    "avg": cpuProcStat["average"],
                    "max": cpuProcStat["maximum"],
                }

                newStat = "%7s(%2d)(%4s%%/%6.1f%%/%4s%%/%5s%%) |" % (
                    diff,
                    cpuProcStat["cnt"],
                    cpuProcStat["minimum"],
                    cpuProcStat["average"],
                    cpuProcStat["maximum"],
                    total,
                )

                printBuf = "%s %s" % (printBuf, newStat)

            if pname == "TOTAL":
                totalBuf = printBuf
            elif errnum and totalerr < errnum:
                continue
            else:
                SysMgr.addPrint(printBuf + "\n", force=True)
                nrPrint += 1

        if totalBuf:
            _printer("%s\n%s" % (totalBuf, oneLineStr))

        if SysMgr.jsonEnable:
            SysMgr.clearPrint()
        else:
            SysMgr.doPrint(newline=False, clear=True)

        if len(unionCpuList) < 2 or nrPrint == 0:
            _printer("    None")

        _printer(oneLineStr)

        # -------------------- GPU -------------------- #

        emptyGpuStat = "%7s(%2s)(%5s/%7s/%5s/%6s) |" % (
            "-",
            "-",
            "-",
            "-",
            "-",
            "-",
        )
        menuStat = "%7s(%2s)(%5s/%7s/%5s/%6s) |" % (
            diffType,
            "Nr",
            "Min",
            "Avg",
            "Max",
            "Tot",
        )
        lenGpuStat = len(emptyCpuStat)

        # print file names #
        menuBuf = "{0:^24} | ".format("Task")
        printBuf = "{0:^24} | ".format("File")
        for fname in flist:
            fpath = fname[-lenGpuStat + 1 :]
            if "ONLYFILE" in SysMgr.environList:
                fpath = os.path.basename(fpath)

            printBuf = ("{0:1} {1:^%d}|" % (len(emptyGpuStat) - 1)).format(
                printBuf, fpath
            )
            menuBuf = ("{0:1} {1:^%d}" % len(menuStat)).format(
                menuBuf, menuStat
            )
        lineLen = len(menuBuf)
        oneLineStr = "-" * lineLen
        twoLineStr = "=" * lineLen
        jsonData["gpu"] = {}

        printBuf = "%s\n%s\n%s\n%s" % (
            printBuf,
            oneLineStr,
            menuBuf,
            twoLineStr,
        )

        # print GPU diff #
        _printer("\n[Diff GPU Info] %s\n%s" % (colors, twoLineStr))
        _printer(printBuf)

        # get error range #
        errnum = UtilMgr.getEnvironNum(
            "GPUERRRANGE", False, 0, False, isFloat=True
        )
        nrPrint = 0

        for pname, value in sorted(
            unionGpuList.items(), key=lambda e: float(e[1]), reverse=True
        ):
            totalerr = 0
            printBuf = "%24s | " % pname[-24:]
            for idx, fname in enumerate(flist):
                try:
                    prevGpuProcList = statFileList[flist[idx - 1]][
                        "gpuProcUsage"
                    ]
                except:
                    prevGpuProcList = {}

                gpuProcList = statFileList[fname]["gpuProcUsage"]
                jsonData["gpu"].setdefault(pname, {})

                # no target process in this file #
                if not pname in gpuProcList:
                    if idx > 0 and pname in prevGpuProcList:
                        diff = "%6.1f%%" % -(prevGpuProcList[pname][item])
                        diff = convColor(diff, "WARNING", 6)
                        printBuf = "%s %6s%s" % (
                            printBuf,
                            diff,
                            emptyGpuStat[7:],
                        )
                    else:
                        diff = "-"
                        printBuf = "%s %s" % (printBuf, emptyGpuStat)

                    continue

                gpuProcStat = gpuProcList[pname]
                if not "diff" in gpuProcStat:
                    diff = "-"
                elif gpuProcStat["diff"] > 0:
                    diff = "{0:>6}%".format(
                        "%6s"
                        % ("+%s" % convNum(gpuProcStat["diff"], isFloat=True))
                    )
                    diff = convColor(diff, "RED", 6)
                    totalerr += gpuProcStat["diff"]
                elif gpuProcStat["diff"] < 0:
                    diff = "{0:>6}%".format(
                        "%6s"
                        % (
                            "-%s"
                            % convNum(abs(gpuProcStat["diff"]), isFloat=True)
                        )
                    )
                    diff = convColor(diff, "GREEN", 6)
                    totalerr += abs(gpuProcStat["diff"])
                else:
                    diff = "0"

                total = convNum(gpuProcStat["total"])

                jsonData["gpu"][pname][fname] = {
                    "diff": diff.strip(" %"),
                    "total": total,
                    "cnt": gpuProcStat["cnt"],
                    "min": gpuProcStat["minimum"],
                    "avg": gpuProcStat["average"],
                    "max": gpuProcStat["maximum"],
                }

                newStat = "%7s(%2d)(%4s%%/%6.1f%%/%4s%%/%5s%%) |" % (
                    diff,
                    gpuProcStat["cnt"],
                    gpuProcStat["minimum"],
                    gpuProcStat["average"],
                    gpuProcStat["maximum"],
                    total,
                )

                printBuf = "%s %s" % (printBuf, newStat)

            if errnum and totalerr < errnum:
                continue

            SysMgr.addPrint(printBuf + "\n", force=True)
            nrPrint += 1

        if SysMgr.jsonEnable:
            SysMgr.clearPrint()
        else:
            SysMgr.doPrint(newline=False, clear=True)

        if not unionGpuList or nrPrint == 0:
            _printer("    None")

        _printer(oneLineStr)

        # -------------------- Memory -------------------- #

        emptyRssStat = "%7s(%2s)(%7s/%7s/%7s) |" % ("-", "-", "-", "-", "-")
        menuStat = "%7s(%2s)(%7s/%7s/%7s) |" % (
            "Diff",
            "Nr",
            "Min",
            "Avg",
            "Max",
        )
        lenRssStat = len(emptyRssStat)

        totalBuf = ""
        menuBuf = "{0:^16} | ".format("Task")
        printBuf = "{0:^16} | ".format("File")
        for fname in flist:
            fpath = fname[-lenRssStat + 1 :]
            if "ONLYFILE" in SysMgr.environList:
                fpath = os.path.basename(fpath)

            printBuf = ("{0:1} {1:^%d}|" % (len(emptyRssStat) - 1)).format(
                printBuf, fpath
            )
            menuBuf = ("{0:1} {1:^%d}" % len(menuStat)).format(
                menuBuf, menuStat
            )
        lineLen = len(menuBuf)
        oneLineStr = "-" * lineLen
        twoLineStr = "=" * lineLen
        jsonData["mem"] = {}

        # print memory diff #
        _printer(
            "\n[Diff Mem Info] (NrTask: %s) %s\n%s"
            % (convNum(len(unionRssList) - 1), colors, twoLineStr)
        )

        printBuf = "%s\n%s\n%s\n%s" % (
            printBuf,
            oneLineStr,
            menuBuf,
            twoLineStr,
        )
        _printer(printBuf)

        # get error range #
        errnum = UtilMgr.getEnvironNum(
            "MEMERRRANGE", False, 0, False, isFloat=True
        )
        nrPrint = 0

        for pname, value in sorted(
            unionRssList.items(), key=lambda e: long(e[1]), reverse=True
        ):
            if pname == "FREE":
                printBuf = "%16s | " % "[FREE]"
            else:
                printBuf = "%16s | " % pname[-16:]

            totalerr = 0
            for idx, fname in enumerate(flist):
                try:
                    prevRssProcList = statFileList[flist[idx - 1]][
                        "memProcUsage"
                    ]
                except:
                    prevRssProcList = {}

                rssProcList = statFileList[fname]["memProcUsage"]
                jsonData["mem"].setdefault(pname, {})

                # no target process in this file #
                if not pname in rssProcList:
                    if idx > 0 and pname in prevRssProcList:
                        diff = "%6sM" % -(prevRssProcList[pname]["maxRss"])
                        diff = convColor(diff, "WARNING", 7)
                        printBuf = "%s %7s%s" % (
                            printBuf,
                            diff,
                            emptyRssStat[7:],
                        )
                    else:
                        printBuf = "%s %s" % (printBuf, emptyRssStat)
                    continue

                rssProcStat = rssProcList[pname]
                if not "diff" in rssProcStat:
                    diff = "-"
                elif rssProcStat["diff"] > 0:
                    diff = "{0:>6}M".format(
                        "+%s" % convNum(rssProcStat["diff"])
                    )
                    diff = convColor(diff, "RED", 6)
                    totalerr += rssProcStat["diff"]
                elif rssProcStat["diff"] < 0:
                    diff = "{0:>6}M".format(
                        "-%s" % convNum(abs(rssProcStat["diff"]))
                    )
                    diff = convColor(diff, "GREEN", 6)
                    totalerr += abs(rssProcStat["diff"])
                else:
                    diff = "0"

                jsonData["mem"][pname][fname] = {
                    "diff": diff.strip(" M"),
                    "cnt": rssProcStat["cnt"],
                    "min": rssProcStat["minRss"],
                    "avg": rssProcStat["avgRss"],
                    "max": rssProcStat["maxRss"],
                }

                newStat = "%7s(%2s)(%6sM/%6sM/%6sM) |" % (
                    diff,
                    convNum(rssProcStat["cnt"]),
                    convNum(rssProcStat["minRss"]),
                    convNum(rssProcStat["avgRss"]),
                    convNum(rssProcStat["maxRss"]),
                )

                printBuf = "%s %s" % (printBuf, newStat)

            if pname == "FREE":
                totalBuf = printBuf
            elif errnum and totalerr < errnum:
                continue
            else:
                SysMgr.addPrint(printBuf + "\n", force=True)
                nrPrint += 1

        if totalBuf:
            _printer("%s\n%s" % (totalBuf, oneLineStr))

        if SysMgr.jsonEnable:
            SysMgr.clearPrint()
        else:
            SysMgr.doPrint(newline=False, clear=True)

        if len(unionRssList) < 2 or nrPrint == 0:
            _printer("    None")

        _printer(oneLineStr)

        # -------------------- SYSTEMD -------------------- #

        emptySdStat = "%5s(%5s)/%5s(%5s) |" % (
            "-",
            "-",
            "-",
            "-",
        )
        menuStat = "%5s(%5s)/%5s(%5s) |" % (
            "Strt",
            "Diff",
            "Init",
            "Diff",
        )
        lenSdStat = len(emptySdStat)

        # print file names #
        totalBuf = ""
        menuBuf = ""
        printBuf = ""
        for fname in flist:
            fpath = fname[-lenSdStat + 1 :]
            if "ONLYFILE" in SysMgr.environList:
                fpath = os.path.basename(fpath)

            printBuf = ("{0:1} {1:^%d}|" % (lenSdStat - 1)).format(
                printBuf, fpath
            )
            menuBuf = ("{0:1} {1:^%d}" % len(menuStat)).format(
                menuBuf, menuStat
            )
        menuBuf += " {0:<64} ".format("Unit")
        printBuf += " {0:<64} ".format("File")
        lineLen = len(menuBuf)
        oneLineStr = "-" * lineLen
        twoLineStr = "=" * lineLen
        jsonData["sd"] = {}

        # print SYSTEMD diff #
        _printer(
            "\n[Diff SYSTEMD Info] (NrUnit: %s) %s\n%s"
            % (convNum(len(unionSdList)), colors, twoLineStr)
        )

        printBuf = "%s\n%s\n%s\n%s" % (
            printBuf,
            oneLineStr,
            menuBuf,
            twoLineStr,
        )
        _printer(printBuf)

        # get error range #
        errnum = UtilMgr.getEnvironNum(
            "SDERRRANGE", False, 0, False, isFloat=True
        )
        nrPrint = 0

        # get reverse type #
        if sortStd.endswith("Diff") and nrFiles > 1:
            reverse = True
        else:
            reverse = False

        # print early stats #
        for pname, value in sorted(
            unionSdBootList.items(), key=lambda e: float(e[1])
        ):
            printBuf = " "

            totalerr = 0
            for idx, fname in enumerate(flist):
                try:
                    if idx == 0:
                        raise Exception()
                    prevSdList = statFileList[flist[idx - 1]]["systemdInfo"][
                        "BOOT"
                    ]["EARLY"]
                except:
                    prevSdList = {}

                try:
                    sdList = statFileList[fname]["systemdInfo"]["BOOT"][
                        "EARLY"
                    ]
                except:
                    sdList = {}

                jsonData["sd"].setdefault(pname, {})

                # no target unit in this file #
                if not pname in sdList:
                    printBuf = "%s %s" % (
                        printBuf,
                        convColor(emptySdStat, "WARNING"),
                    )
                    continue

                actDiff = (
                    ("%5.1f" % float(sdList[pname])) if sdList[pname] else "-"
                )
                if pname in prevSdList:
                    initDiff = float(value) - float(prevSdList[pname])
                    if initDiff:
                        initDiffStr = "%5.1f" % round(initDiff, 2)
                        if initDiff > 0:
                            initDiff = convColor(initDiffStr, "RED")
                        elif initDiff < 0:
                            initDiff = convColor(initDiffStr, "GREEN")
                    else:
                        initDiff = "-"
                    diff = "%5s(%5s)/%5s(%5s) |" % (
                        "-",
                        "-",
                        actDiff,
                        initDiff,
                    )
                else:
                    diff = "%5s(%5s)/%5s(%5s) |" % ("-", "-", actDiff, "-")

                jsonData["sd"][pname][fname] = sdList

                printBuf = "%s %s" % (printBuf, diff)

            if errnum and totalerr < errnum:
                continue
            else:
                printBuf += " %-64s " % pname
                SysMgr.addPrint(printBuf + "\n", force=True)
                nrPrint += 1
        if nrPrint:
            SysMgr.addPrint(oneLineStr + "\n", force=True)

        # print unit stats #
        totalerr = 0
        for pname, value in sorted(
            unionSdList.items(),
            key=lambda e: e[0] if sortByName else float(e[1]),
            reverse=reverse,
        ):
            printBuf = " "

            totalerr = 0
            for idx, fname in enumerate(flist):
                try:
                    if idx == 0:
                        raise Exception()
                    prevSdList = statFileList[flist[idx - 1]]["systemdInfo"][
                        "BOOT"
                    ]
                except:
                    prevSdList = {}

                try:
                    sdList = statFileList[fname]["systemdInfo"]["BOOT"]
                except:
                    sdList = {}

                jsonData["sd"].setdefault(pname, {})

                # no target unit in this file #
                if not pname in sdList:
                    printBuf = "%s %s" % (
                        printBuf,
                        convColor(emptySdStat, "WARNING"),
                    )
                    continue

                sdUnitStat = sdList[pname]
                start = sdUnitStat["activating"]
                if start:
                    if "startDiff" in sdUnitStat:
                        startDiff = "%5.1f" % round(sdUnitStat["startDiff"], 2)
                        if sdUnitStat["startDiff"] > 0:
                            startDiff = convColor(startDiff, "RED")
                        elif sdUnitStat["startDiff"] < 0:
                            startDiff = convColor(startDiff, "GREEN")

                        if sortStd == "startDiff":
                            totalerr += abs(sdUnitStat["startDiff"])
                    else:
                        startDiff = "-"
                    actDiff = sdUnitStat["actDiff"]
                    if "initDiff" in sdUnitStat:
                        initDiff = "%5.1f" % round(sdUnitStat["initDiff"], 2)
                        if sdUnitStat["initDiff"] > 0:
                            initDiff = convColor(initDiff, "RED")
                        elif sdUnitStat["initDiff"] < 0:
                            initDiff = convColor(initDiff, "GREEN")

                        if sortStd != "startDiff":
                            totalerr += abs(sdUnitStat["initDiff"])
                    else:
                        initDiff = "-"
                    diff = "%5.1f(%5s)/%5.1f(%5s) |" % (
                        start,
                        startDiff,
                        actDiff,
                        initDiff,
                    )
                else:
                    diff = emptySdStat

                jsonData["sd"][pname][fname] = sdUnitStat

                printBuf = "%s %s" % (printBuf, diff)

            if errnum and totalerr < errnum:
                continue
            else:
                printBuf += " %-64s " % pname
                SysMgr.addPrint(printBuf + "\n", force=True)
                nrPrint += 1

        if totalBuf:
            _printer("%s\n%s" % (totalBuf, oneLineStr))

        if SysMgr.jsonEnable:
            SysMgr.clearPrint()
        else:
            SysMgr.doPrint(newline=False, clear=True)

        if len(unionCpuList) < 2 or nrPrint == 0:
            _printer("    None")

        _printer(oneLineStr)

        # -------------------- JSON -------------------- #

        if SysMgr.jsonEnable:
            SysMgr.printPipe(
                UtilMgr.convDict2Str(
                    jsonData, pretty=not SysMgr.findOption("Q")
                )
            )

    def __init__(self, fpath=None, onlyInstance=False):
        # init variables #
        self.tmpCnt = 0
        self.figure = None
        self.execEnable = False if SysMgr.execEnable is None else True

        # thread mode #
        if fpath:
            self.initThreadData()

            self.init_threadData = {
                "comm": "",
                "usage": 0.0,
                "cpuRank": 0,
                "yield": 0,
                "cpuWait": 0.0,
                "pri": "?",
                "reqRdBlock": 0,
                "readBlock": 0,
                "ioRank": 0,
                "irq": 0.0,
                "reclaimWait": 0.0,
                "reclaimCnt": 0,
                "ptid": "-" * 5,
                "new": " ",
                "die": " ",
                "preempted": 0,
                "preemption": 0,
                "start": 0.0,
                "stop": 0.0,
                "ioRdWait": 0.0,
                "readQueueCnt": 0,
                "readStart": 0.0,
                "maxRuntime": 0.0,
                "coreSchedCnt": 0,
                "longRunCore": -1,
                "dReclaimWait": 0.0,
                "dReclaimStart": 0.0,
                "migrate": 0,
                "dReclaimCnt": 0,
                "ftxMax": 0.0,
                "ftxLockCnt": 0,
                "ftxEnter": 0.0,
                "ftxLock": 0.0,
                "ftxTotal": 0.0,
                "ftxWaitCnt": 0,
                "ftxProcess": 0.0,
                "ftxLockMax": 0.0,
                "ftxStat": "?",
                "ftxLSwitch": 0,
                "ftxBlockTotal": 0.0,
                "ftxLBlockTotal": 0.0,
                "ftxBlock": 0.0,
                "ftxLBlock": 0.0,
                "ftxBlockCnt": 0,
                "ftxEnt": None,
                "lastStatus": "N",
                "offCnt": 0,
                "offTime": 0.0,
                "waitStartAsParent": 0.0,
                "nrAllocPages": 0,
                "nrPages": 0,
                "reclaimedPages": 0,
                "waitPid": 0,
                "remainKmem": 0,
                "wasteKmem": 0,
                "childList": None,
                "kernelPages": 0,
                "readBlockCnt": 0,
                "writeBlock": 0,
                "writeBlockCnt": 0,
                "tgid": "-" * 5,
                "cachePages": 0,
                "userPages": 0,
                "lastOff": 0.0,
                "maxPreempted": 0.0,
                "anonReclaimedPages": 0,
                "lastIdleStatus": 0,
                "createdTime": 0.0,
                "waitChild": 0.0,
                "waitParent": 0.0,
                "customEvent": None,
                "userEvent": None,
                "kernelEvent": None,
                "blkCore": 0,
                "lockWait": 0.0,
                "lockTime": 0.0,
                "lockCnt": 0,
                "tryLockCnt": 0,
                "lastLockTime": 0.0,
                "lastLockWait": 0.0,
                "reqWrBlock": 0,
                "writeQueueCnt": 0,
                "writeBlockCnt": 0,
                "writeStart": 0.0,
                "ioWrWait": 0.0,
                "awriteBlock": 0,
                "awriteBlockCnt": 0,
                "schedLatency": 0.0,
                "schedReady": 0.0,
                "lastNrSyscall": -1,
                "schedBlock": 0.0,
                "nrSyscall": 0,
                "lastCore": 0,
            }

            self.init_irqData = {
                "name": None,
                "usage": 0.0,
                "start": 0.0,
                "max": 0.0,
                "min": 0.0,
                "maxPeriod": 0.0,
                "minPeriod": 0.0,
                "count": 0,
            }

            self.init_intData = {
                "time": 0.0,
                "firstLogTime": 0.0,
                "cpuPer": 0.0,
                "totalUsage": 0.0,
                "totalMemUsage": 0,
                "brUsage": 0,
                "totalBrUsage": 0,
                "irqUsage": 0.0,
                "kmemUsage": 0,
                "totalKmemUsage": 0,
                "coreSchedCnt": 0,
                "totalCoreSchedCnt": 0,
                "preempted": 0.0,
                "totalBwUsage": 0,
                "totalPreempted": 0.0,
                "new": " ",
                "die": " ",
                "bwUsage": 0,
                "cpuUsage": 0.0,
                "memUsage": 0,
            }

            self.init_eventData = {
                "count": 0,
                "start": 0.0,
                "usage": 0.0,
                "max": 0.0,
                "min": 0.0,
                "maxPeriod": 0.0,
                "minPeriod": 0.0,
            }

            self.init_kmallocData = {
                "tid": "0",
                "caller": "0",
                "ptr": "0",
                "req": 0,
                "alloc": 0,
                "time": "0",
                "waste": 0,
                "core": 0,
            }

            self.wakeupData = {
                "tid": "0",
                "nr": "0",
                "ret": "0",
                "time": "0",
                "args": "0",
                "valid": 0,
                "from": "0",
                "to": "0",
                "corrupt": "0",
            }

            self.allocPageData = {}

            self.init_syscallInfo = {
                "usage": 0.0,
                "last": 0.0,
                "count": 0,
                "max": 0.0,
                "min": 0.0,
                "err": 0,
            }

            self.init_binderInfo = {
                "usage": 0.0,
                "last": 0.0,
                "count": 0,
                "max": 0.0,
                "min": 0.0,
            }

            self.init_pageData = {
                "tid": "0",
                "page": "0",
                "flags": "0",
                "type": "0",
                "time": "0",
            }

            self.init_lastJob = {
                "job": "0",
                "time": "0",
                "tid": "0",
                "prevWakeupTid": "0",
            }

            self.init_preemptData = {"usage": 0.0, "count": 0, "max": 0.0}

            self.init_wqData = {
                "name": None,
                "usage": 0.0,
                "start": 0.0,
                "max": -1.0,
                "min": -1.0,
                "maxPeriod": -1.0,
                "minPeriod": -1.0,
                "avgPeriod": 0.0,
                "scount": 0,
                "rcount": 0,
                "avg": 0.0,
                "task": None,
            }

            self.finishTime = "0"
            self.lastCore = "0"
            self.lastTidPerCore = {}
            self.lastEvent = "0"
            self.backupData = {}
            self.nrSchedLoss = 0
            self.timelineIdx = {}
            self.timelineData = {"time_unit": "us", "segments": []}

        # top mode #
        else:
            self.init_procData = {
                "isMain": False,
                "tids": None,
                "stat": None,
                "io": None,
                "alive": False,
                "runtime": 0.0,
                "changed": True,
                "created": False,
                "new": False,
                "majflt": 0,
                "ttime": 0,
                "cttime": 0,
                "utime": 0,
                "stime": 0,
                "btime": 0,
                "rw": 0,
                "taskPath": None,
                "statm": None,
                "mainID": "",
                "maps": None,
                "status": None,
            }

            self.init_cpuData = {
                "user": 0,
                "nice": 0,
                "system": 0,
                "idle": 0,
                "iowait": 0,
                "wait": 0,
                "irq": 0,
                "softirq": 0,
                "steal": 0,
                "guest": 0,
                "guest_nice": 0,
            }

            self.nrThread = 0
            self.nrPrevThread = 0
            self.nrProcess = 0
            self.nrPrevProcess = 0
            self.nrAbnormalTask = 0
            self.nrNewTask = 0
            self.nrDieTask = 0
            self.nrContextSwitch = 0
            self.nrFd = 0
            self.printTick = 0
            self.maxPid = SysMgr.pid
            self.procData = {}
            self.procFdData = {}
            self.prevProcData = {}
            self.fixedProcData = {}
            self.cgroupData = {}
            self.prevCgroupData = {}
            self.nsData = {}
            self.nsProcData = {}
            self.binderData = {}
            self.fileData = {}
            self.cpuData = {}
            self.gpuData = {}
            self.prevCpuData = {}
            self.oldCpuData = {}
            self.irqData = {}
            self.prevIrqData = {}
            self.irqInfo = {}
            self.prevIrqInfo = {}
            self.zoneData = {}
            self.prevZoneData = {}
            self.slabData = {}
            self.ksmData = {}
            self.buddyData = {}
            self.dmabufData = {}
            self.vmallocData = {}
            self.memData = {}
            self.prevMemData = {}
            self.gpuMemData = {}
            self.vmData = {}
            self.prevVmData = {}
            self.stackTable = {}
            self.prevSwaps = None
            self.abnormalTasks = {}
            self.intervalData = {}
            self.intervalFileData = {}
            self.intervalPeakData = {}
            self.customPeakData = {}
            self.fdinfoData = {}
            self.targetEventDrawn = False

            # set index of attributes #
            self.majfltIdx = ConfigMgr.PROCSTAT_ATTR.index("MAJFLT")
            self.utimeIdx = ConfigMgr.PROCSTAT_ATTR.index("UTIME")
            self.stimeIdx = ConfigMgr.PROCSTAT_ATTR.index("STIME")
            self.cutimeIdx = ConfigMgr.PROCSTAT_ATTR.index("CUTIME")
            self.cstimeIdx = ConfigMgr.PROCSTAT_ATTR.index("CSTIME")
            self.btimeIdx = ConfigMgr.PROCSTAT_ATTR.index("DELAYBLKTICK")
            self.swapIdx = ConfigMgr.PROCSTAT_ATTR.index("NSWAP")
            self.commIdx = ConfigMgr.PROCSTAT_ATTR.index("COMM")
            self.ppidIdx = ConfigMgr.PROCSTAT_ATTR.index("PPID")
            self.nrthreadIdx = ConfigMgr.PROCSTAT_ATTR.index("NRTHREAD")
            self.prioIdx = ConfigMgr.PROCSTAT_ATTR.index("PRIORITY")
            self.policyIdx = ConfigMgr.PROCSTAT_ATTR.index("POLICY")
            self.vssIdx = ConfigMgr.PROCSTAT_ATTR.index("VSIZE")
            self.rssIdx = ConfigMgr.PROCSTAT_ATTR.index("RSS")
            self.scodeIdx = ConfigMgr.PROCSTAT_ATTR.index("STARTCODE")
            self.ecodeIdx = ConfigMgr.PROCSTAT_ATTR.index("ENDCODE")
            self.statIdx = ConfigMgr.PROCSTAT_ATTR.index("STATE")
            self.starttimeIdx = ConfigMgr.PROCSTAT_ATTR.index("STARTTIME")
            self.sidIdx = ConfigMgr.PROCSTAT_ATTR.index("SESSIONID")
            self.pgrpIdx = ConfigMgr.PROCSTAT_ATTR.index("PGRP")
            self.shrIdx = ConfigMgr.STATM_TYPE.index("SHR")
            self.statCnt = len(ConfigMgr.PROCSTAT_ATTR)

            # initialize netlink socket #
            try:
                SysMgr.initNetlink()
            except:
                SysMgr.printWarn("failed to initialize netlink", reason=True)

            # check task stream flag #
            if "TASKSTREAM" in SysMgr.environList:
                self.taskStreamEnable = True
                SysMgr.streamEnable = True
            else:
                self.taskStreamEnable = False

            # check to return just instance #
            if onlyInstance:
                return

            # set target type #
            if SysMgr.processEnable:
                targets = "processes"
            else:
                targets = "threads"

            if SysMgr.graphEnable:
                # draw images based on statistics #
                if SysMgr.inputParam:
                    # FLAME GRAPH MODE #
                    if SysMgr.checkMode("drawflame", True):
                        drawFunc = Debugger.drawFlame
                    elif SysMgr.checkMode("drawbitmap", True):
                        drawFunc = TaskAnalyzer.drawBitmap
                    elif SysMgr.checkMode("drawviolin", True):
                        drawFunc = TaskAnalyzer.drawViolin
                    elif SysMgr.checkMode("drawhist", True):
                        drawFunc = TaskAnalyzer.drawHist
                    # OTHER DRAW MODE #
                    else:
                        drawFunc = self.drawStats

                    # draw #
                    if "NOMERGE" in SysMgr.environList:
                        for fpath in list(SysMgr.inputParam):
                            drawFunc(fpath)
                    else:
                        drawFunc(SysMgr.inputParam)

                    sys.exit(0)
                # no path for statistics #
                else:
                    SysMgr.printErr(
                        (
                            "wrong option used, "
                            "use also -I option to load statistics data"
                        )
                    )
                    sys.exit(-1)

            # set system maximum fd number #
            SysMgr.setMaxFd()

            # set default interval #
            if SysMgr.intervalEnable == 0:
                SysMgr.intervalEnable = 1

            # apply filter from 1st argument #
            if not SysMgr.filterGroup and SysMgr.hasMainArg():
                value = SysMgr.getMainArg()
                value = UtilMgr.splitString(value)
                SysMgr.filterGroup = UtilMgr.cleanItem(value)

            # remove wrong filter #
            if SysMgr.filterGroup:
                for idx, val in enumerate(SysMgr.filterGroup):
                    if not val:
                        SysMgr.filterGroup.pop(idx)

                taskList = ", ".join(SysMgr.filterGroup)

                if SysMgr.fileTopEnable:
                    pass
                elif SysMgr.groupProcEnable:
                    SysMgr.printInfo(
                        (
                            "only specific %s that are involved "
                            "in the process group [ %s ] are shown"
                        )
                        % (targets, taskList)
                    )
                else:
                    SysMgr.printInfo(
                        "only specific %s [ %s ] are shown"
                        % (targets, taskList)
                    )

            # set threshold config #
            if not "NOTHRESHOLD" in SysMgr.environList:
                SysMgr.applyThreshold()

            # set log buffer size #
            if SysMgr.bufferSize < 0:
                # default unlimited #
                SysMgr.bufferSize = 0

            if SysMgr.outPath:
                # print buffer size #
                if SysMgr.bufferSize == 0:
                    SysMgr.printWarn("buffer size is unlimited", True)
                else:
                    SysMgr.printInfo(
                        "limit buffer size to [%s]"
                        % UtilMgr.convSize2Unit(SysMgr.bufferSize)
                    )

                SysMgr.printStat(
                    r"start profiling... [ STOP(Ctrl+c), SAVE(Ctrl+\) ]"
                )

            # apply print condition via resource threshold #
            TaskAnalyzer.applyPrintCond()

            # file mode #
            if SysMgr.fileTopEnable:
                try:
                    self.runFileTop()
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr("failed to monitor files", reason=True)
            # DLT mode #
            elif SysMgr.dltTopEnable:
                DltAnalyzer.runDltReceiver(mode="top")
            # D-Bus mode #
            elif SysMgr.dbusTopEnable:
                DbusMgr.runDbusSnooper(mode="top")
            # irq #
            elif SysMgr.checkMode("irqtop"):
                try:
                    self.runIrqTop()
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr("failed to monitor irqs", reason=True)
            # slab mode #
            elif SysMgr.checkMode("slabtop"):
                try:
                    self.runSlabTop()
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr("failed to monitor slab", reason=True)
            # vmalloc mode #
            elif SysMgr.checkMode("vtop"):
                try:
                    self.runVmallocTop()
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr("failed to monitor vmalloc", reason=True)
            # cgroup mode #
            elif SysMgr.checkMode("cgtop"):
                try:
                    self.runCgTop()
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr("failed to monitor cgroup", reason=True)
                    sys.exit(-1)
            # others #
            else:
                # set network config #
                if not SysMgr.findOption("x"):
                    NetworkMgr.setServerNetwork(
                        None, None, reuse=False, weakPort=True
                    )

            # print system general info in advance #
            if SysMgr.outPath and SysMgr.pipeEnable and SysMgr.exitFlag:
                SysMgr.printLogo(big=True)
                SysMgr.saveSysStats(True)
                SysMgr.printPipe("\n")

            # request service to remote server #
            self.requestService()

            # task top mode #
            try:
                if SysMgr.isLinux:
                    self.runTaskTop()
                else:
                    self.runTaskTopGen()
            except KeyboardInterrupt:
                SysMgr.stopHandler()
                sys.exit(0)
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr("failed to monitor tasks", reason=True)

            # terminate top mode #
            sys.exit(0)

        # -------------------- THREAD MODE -------------------- #
        # change default CPU property #
        SysMgr.cpuEnable = False

        # initialize preempt thread list #
        if SysMgr.preemptGroup:
            if SysMgr.findOption("i"):
                SysMgr.printErr("-i option is already used")
                sys.exit(-1)
            elif SysMgr.findOption("g"):
                SysMgr.printErr("-g option is already used")
                sys.exit(-1)

            for index in SysMgr.preemptGroup:
                """
                preempted state
                [preemptBit, threadList, startTime, core, totalUsage] #
                """
                self.preemptData.append([False, {}, 0.0, 0, 0.0])

        # read trace data #
        lines = TaskAnalyzer.readTraceData(fpath)

        # save trace data and stop analysis #
        if SysMgr.outputFile:
            SysMgr.saveTraceData(lines)
            sys.exit(0)

        # get process tree #
        SysMgr.getProcTreeInfo()

        # start parsing logs #
        SysMgr.printStat("start analyzing... [ STOP(Ctrl+c) ]")
        SysMgr.totalLine = len(lines)

        for idx, log in enumerate(lines):
            if self.stopFlag:
                # accumulated data #
                if "EVENT_START" in log:
                    self.stopFlag = False
                else:
                    continue

            # skip blank line #
            if not log.strip():
                continue

            # parse a line #
            time = self.parse(log)
            UtilMgr.printProgress(idx, SysMgr.totalLine)

            # save last job per core #
            self.lastJob.setdefault(self.lastCore, dict(self.init_lastJob))

            self.lastJob[self.lastCore]["job"] = self.lastEvent
            self.lastJob[self.lastCore]["time"] = self.finishTime

        if lines:
            UtilMgr.deleteProgress()

        # print the number of missed sched data #
        if self.nrSchedLoss:
            SysMgr.printWarn(
                "a total of %s sched data is missing"
                % UtilMgr.convNum(self.nrSchedLoss),
                True,
            )

        # update finish time #
        if self.finishTime == "0":
            self.finishTime = time

        # update anonymous comm #
        for idx, val in self.threadData.items():
            if val["comm"] == "<...>":
                val["comm"] = "?"

        stime = float(SysMgr.startTime)
        if hasattr(self, "trimStart"):
            stime += self.trimStart

        # add consumed time of jobs not finished yet to each threads #
        for idx, val in self.lastTidPerCore.items():
            # apply core off time #
            nrCore = long(idx)
            coreId = "0[%s]" % idx
            if (
                self.threadData[coreId]["coreSchedCnt"] == 0
                and self.threadData[coreId]["offTime"] == 0
            ):
                # define time delta #
                start_delta = 0
                stop_delta = long((float(self.finishTime) - stime) * 1000000)

                # add timeline data #
                self.timelineData["segments"].append(
                    {
                        "group": nrCore,
                        "text": "OFF",
                        "id": nrCore,
                        "state": "OFF",
                        "time_start": start_delta,
                        "time_end": stop_delta,
                    }
                )

            elif self.threadData[coreId]["lastOff"] > 0:
                self.threadData[coreId]["usage"] += (
                    float(self.finishTime) - self.threadData[coreId]["start"]
                )

                # define time delta #
                startTime = self.threadData[coreId]["lastOff"]
                start_delta = long((float(startTime) - stime) * 1000000)
                stop_delta = long((float(self.finishTime) - stime) * 1000000)

                # add timeline data #
                self.timelineData["segments"].append(
                    {
                        "group": nrCore,
                        "text": "OFF",
                        "id": nrCore,
                        "state": "OFF",
                        "time_start": start_delta,
                        "time_end": stop_delta,
                    }
                )

            if self.threadData[val]["lastStatus"] == "S":
                continue

            self.threadData[val]["usage"] += float(self.finishTime) - float(
                self.threadData[val]["start"]
            )

        # add lock time of jobs not finished yet to each threads #
        if SysMgr.lockEnable:
            for idx, item in self.threadData.items():
                if item["ftxEnter"] > 0:
                    # elapsed time #
                    wtime = float(self.finishTime) - item["ftxEnter"]
                    item["ftxTotal"] += wtime
                    if item["ftxMax"] < wtime:
                        item["ftxMax"] = wtime

                    # CPU time #
                    if item["start"] > item["ftxEnter"]:
                        ctime = float(self.finishTime) - item["start"]
                        item["ftxProcess"] += ctime
                    elif item["ftxBlock"] == 0 == item["ftxLBlock"]:
                        ctime = float(self.finishTime) - item["ftxEnter"]
                        item["ftxProcess"] += ctime

                    # wait time #
                    if item["ftxBlock"] > 0:
                        wtime = float(self.finishTime) - item["ftxBlock"]
                        item["ftxBlockTotal"] += wtime
                    if item["ftxLBlock"] > 0:
                        wtime = float(self.finishTime) - item["ftxLBlock"]
                        item["ftxLBlockTotal"] += wtime

                if not "futexObj" in item:
                    continue

                # lock time #
                for obj, time in item["futexObj"].items():
                    ltime = float(self.finishTime) - time
                    item["ftxLock"] += ltime
                    if item["ftxLockMax"] < ltime:
                        item["ftxLockMax"] = ltime

        # add block waiting time of jobs not finished yet to each threads #
        if SysMgr.blockEnable:
            # waiting for read #
            for idx, item in sorted(
                self.threadData.items(),
                key=lambda e: e[1]["readStart"],
                reverse=True,
            ):
                if item["readStart"] > 0:
                    waitTime = float(self.finishTime) - item["readStart"]
                    item["ioRdWait"] += waitTime
                    self.threadData[item["blkCore"]]["ioRdWait"] += waitTime
                    item["readStart"] = 0
                else:
                    break

            # waiting for synchronous write #
            for idx, item in sorted(
                self.threadData.items(),
                key=lambda e: e[1]["writeStart"],
                reverse=True,
            ):
                # cancel to add blocking time for write because async write #
                break
                """
                if item['writeStart'] > 0:
                    waitTime = float(self.finishTime) - item['writeStart']
                    item['ioWrWait'] += waitTime
                    self.threadData[item['blkCore']]['ioWrWait'] += waitTime
                    item['writeStart'] = 0
                else:
                    break
                """

            # warn uncompleted block request #
            if self.ioData:
                SysMgr.printWarn(
                    "failed to handle %s block requests"
                    % UtilMgr.convNum(len(self.ioData))
                )

        # calculate usage of threads in last interval #
        self.handleIntData(self.finishTime)

        if not self.threadData:
            SysMgr.printErr("no recognized data in %s" % SysMgr.inputFile)
            sys.exit(-1)

        self.totalTime = round(float(self.finishTime) - stime, 7)

        # apply filter #
        if SysMgr.filterGroup:
            # make parent list #
            plist = {}
            if SysMgr.groupProcEnable:
                for key, value in self.threadData.items():
                    tgid = value["tgid"]
                    if tgid == "0" or tgid.startswith("-"):
                        continue
                    if key in SysMgr.filterGroup or UtilMgr.isValidStr(
                        value["comm"]
                    ):
                        plist[tgid] = 0

            # filter segments #
            segments = self.timelineData["segments"]
            self.timelineData["segments"] = []
            for item in segments:
                # no tid #
                if not item["id"] in self.threadData:
                    continue
                # other progress group #
                elif SysMgr.isExceptTarget(
                    item["id"], self.threadData, plist=plist
                ):
                    continue

                # add segment #
                self.timelineData["segments"].append(item)

            # filter thread data #
            for key in list(self.threadData):
                # except for cores #
                if key.startswith("0["):
                    continue

                # except for process group #
                if not SysMgr.isExceptTarget(
                    key, self.threadData, plist=plist
                ):
                    continue

                # remove thread #
                try:
                    self.threadData.pop(key, None)
                except:
                    continue

        # add empty timeline data for drawing cores #
        for nrCore in SysMgr.perCoreDrawList:
            self.timelineData["segments"].append(
                {
                    "group": nrCore,
                    "text": "OFF",
                    "id": nrCore,
                    "state": "OFF",
                    "time_start": 0,
                    "time_end": 0,
                }
            )

    def __del__(self):
        pass

    def runIrqTop(self):
        # create an object #
        tobj = TaskAnalyzer(onlyInstance=True)

        # run loop #
        while 1:
            # update uptime #
            SysMgr.updateUptime()

            # save timestamp #
            prevTime = time.time()

            # check repeat count #
            SysMgr.checkProgress()

            # save irqs #
            tobj.saveIrqs(full=True)
            if tobj.prevIrqData:
                tobj.printIrqs()

            # write user command #
            SysMgr.runProfCmd("AFTER")

            # get delayed time #
            delayTime = time.time() - prevTime
            if delayTime > SysMgr.intervalEnable:
                waitTime = 0.000001
            else:
                waitTime = SysMgr.intervalEnable - delayTime

            # wait for next tick #
            if not SysMgr.waitUserInput(waitTime):
                time.sleep(waitTime)

    def runVmallocTop(self):
        # run loop #
        while 1:
            # save timestamp #
            prevTime = time.time()

            # check repeat count #
            SysMgr.checkProgress()

            # print vmalloc info #
            SysMgr.doPrintVmalloc(True)

            # write user command #
            SysMgr.runProfCmd("AFTER")

            # get delayed time #
            delayTime = time.time() - prevTime
            if delayTime > SysMgr.intervalEnable:
                waitTime = 0.000001
            else:
                waitTime = SysMgr.intervalEnable - delayTime

            # wait for next tick #
            if not SysMgr.waitUserInput(waitTime):
                time.sleep(waitTime)

    def runSlabTop(self):
        SysMgr.addEnvironVar("ONLYSLAB")

        # run loop #
        while 1:
            # save timestamp #
            prevTime = time.time()

            # check repeat count #
            SysMgr.checkProgress()

            # print slab info #
            SysMgr.doPrintSlab(True)

            # write user command #
            SysMgr.runProfCmd("AFTER")

            # get delayed time #
            delayTime = time.time() - prevTime
            if delayTime > SysMgr.intervalEnable:
                waitTime = 0.000001
            else:
                waitTime = SysMgr.intervalEnable - delayTime

            # wait for next tick #
            if not SysMgr.waitUserInput(waitTime):
                time.sleep(waitTime)

    def runCgTop(self):
        # check cgroup path #
        cgroupPath = SysMgr.sysInstance.getCgroupPath()
        if not cgroupPath:
            SysMgr.printCgroupList()
            SysMgr.printErr("failed to access cgroup filesystem")
            sys.exit(-1)

        # set target subsystems #
        disOpt = SysMgr.getOption("d")
        if not disOpt:
            disOpt = ""
        targetGroup = [
            "cpu" if not "c" in disOpt else None,
            "cpuacct" if not "c" in disOpt else None,
            "memory" if not "m" in disOpt else None,
            "blkio" if not "b" in disOpt else None,
        ]

        # remove None items #
        targetGroup = list(set(targetGroup))
        if None in targetGroup:
            targetGroup.remove(None)

        # check mandatory subsystem #
        for item in list(targetGroup):
            if not SysMgr.checkCgroup(item):
                targetGroup.remove(item)
        if not targetGroup:
            if SysMgr.sysInstance.getCgroupPath(
                "cgroup2", force=True, save=False
            ):
                SysMgr.printWarn("no mounted cgroup v1 subsystem", True)
            else:
                SysMgr.printErr("no mounted cgroup subsystem")
                sys.exit(-1)

        # sort target subsystems #
        targetGroup.sort()

        SysMgr.printInfo(
            "enabled cgroup subsystems [ %s ]"
            % (", ".join(targetGroup) if targetGroup else "cgroup2")
        )

        # run loop #
        while 1:
            # collect system stats as soon as possible #
            self.saveSystemStat(saveProc=False)

            # save cgroup stats #
            self.saveCgroupStat(targetGroup)

            # save timestamp #
            prevTime = time.time()

            if self.prevCpuData:
                # print system status #
                self.printSystemStat(idIndex=True, targetList=["cgroup"])

            # check repeat count #
            SysMgr.checkProgress()

            # reset system status #
            self.reinitStats()

            # write user command #
            SysMgr.runProfCmd("AFTER")

            # get delayed time #
            delayTime = time.time() - prevTime
            if delayTime > SysMgr.intervalEnable:
                waitTime = 0.000001
            else:
                waitTime = SysMgr.intervalEnable - delayTime

            # wait for next tick #
            if not SysMgr.waitUserInput(waitTime):
                time.sleep(waitTime)

    def runFileTop(self):
        def _getFilter(init=False):
            procFilter = []
            fileFilter = []

            if not SysMgr.filterGroup:
                return [procFilter, fileFilter]

            newFilter = ",".join(SysMgr.filterGroup)
            newFilter = newFilter.split(":", 1)

            # proc filter #
            for pval in newFilter[0].split(","):
                if pval != "":
                    procFilter.append(pval)

            # file filter #
            if len(newFilter) > 1:
                for fval in newFilter[1].split(","):
                    if fval != "":
                        fileFilter.append(fval)

            if init and len(procFilter) > 0:
                SysMgr.printInfo(
                    "only specific processes including [ %s ] are shown"
                    % ", ".join(procFilter)
                )

            if init and len(fileFilter) > 0:
                SysMgr.printInfo(
                    "only specific files including [ %s ] are shown"
                    % ", ".join(fileFilter)
                )

            return [procFilter, fileFilter]

        if SysMgr.isLinux:
            # check root permission #
            SysMgr.checkRootPerm()

            # check proc access #
            if not os.path.isdir(SysMgr.procPath):
                SysMgr.printErr("failed to access to procfs")
                sys.exit(-1)

            # initialize task stat #
            TaskAnalyzer.dbgObj = Debugger(SysMgr.pid, attach=False)
            TaskAnalyzer.dbgObj.initValues()
            TaskAnalyzer.dbgObj.getCpuUsage(system=True)

            # execute commands #
            if SysMgr.inputParam:
                watchList = []
                for cmd in SysMgr.inputParam.split(","):
                    pid = SysMgr.createProcess(cmd.split(), mute=True)
                    if pid > 0:
                        watchList.append(str(pid))

                # apply new filter #
                SysMgr.filterGroup = watchList

        # import select package in the foreground #
        if not SysMgr.outPath:
            SysMgr.getPkg("select", False)

        prevFilter = []

        # set initial repeat value #
        SysMgr.progressCnt = 1

        # get prof flag #
        printFlag = "PRINTDELAY" in SysMgr.environList

        while 1:
            # save timestamp #
            prevTime = time.time()

            # update proc and file filter #
            if prevFilter != SysMgr.filterGroup:
                nowFilter = _getFilter(True)
                prevFilter = SysMgr.filterGroup
            else:
                nowFilter = _getFilter()

            # collect file stats as soon as possible #
            if printFlag:
                UtilMgr.saveTime()
            self.saveFileStat(nowFilter)
            if printFlag:
                UtilMgr.printTime("saveFileStat")

            # print system status #
            if printFlag:
                UtilMgr.saveTime()
            self.printFileStat(nowFilter)
            if printFlag:
                UtilMgr.printTime("printFileStat")

            # flush socket caches #
            SysMgr.udpListCache = {}
            SysMgr.tcpListCache = {}
            SysMgr.udsListCache = {}

            # check repeat count #
            SysMgr.checkProgress()

            # reset and save proc instance #
            if printFlag:
                UtilMgr.saveTime()
            self.saveProcInstance()
            if printFlag:
                UtilMgr.printTime("saveProcInstance")

            # reset system status #
            self.reinitStats()

            # write user command #
            SysMgr.runProfCmd("AFTER")

            # get delayed time #
            delayTime = time.time() - prevTime
            if delayTime > SysMgr.intervalEnable:
                waitTime = 0.000001
            else:
                waitTime = SysMgr.intervalEnable - delayTime

            # wait for next tick #
            if not SysMgr.waitUserInput(waitTime, msg="DEFAULT"):
                time.sleep(waitTime)

    def runTaskTopGen(self):
        # import select package in the foreground #
        if not SysMgr.outPath:
            SysMgr.getPkg("select", False)

        # get prof flag #
        printFlag = "PRINTDELAY" in SysMgr.environList

        # run loop #
        while 1:
            # collect system stats as soon as possible #
            if printFlag:
                UtilMgr.saveTime()
            self.saveSystemStatGen()
            if printFlag:
                UtilMgr.printTime("saveSystemStatGen")

            # save timestamp #
            prevTime = time.time()

            # handle first tick for printing total resource #
            if SysMgr.totalEnable and not self.prevCpuData:
                SysMgr.progressCnt += 1
                self.reinitStats()
                self.saveSystemStatGen()

            # print stats #
            if self.prevCpuData:
                # save timestamp #
                if printFlag:
                    UtilMgr.saveTime()

                # print system status #
                self.printSystemStatGen(idIndex=True)

                # report system status for elastic stack
                if SysMgr.elasticEnable:
                    self.reportSystemStatElastic()
                # report system status #
                elif SysMgr.reportEnable:
                    self.reportSystemStat()

                # print elapsed time #
                if printFlag:
                    UtilMgr.printTime("printSystemStatGen")

            # check repeat count #
            SysMgr.checkProgress()

            # reset system status #
            self.reinitStats()

            # get delayed time #
            delayTime = time.time() - prevTime
            if delayTime > SysMgr.intervalEnable:
                waitTime = 0.000001
            else:
                waitTime = SysMgr.intervalEnable - delayTime

            # wait for next tick #
            if not SysMgr.waitUserInput(waitTime):
                time.sleep(waitTime)

    def runTaskTop(self):
        if not os.path.isdir(SysMgr.procPath):
            SysMgr.printErr("failed to access procfs")
            sys.exit(-1)

        # import select package in the foreground #
        if not SysMgr.outPath:
            SysMgr.getPkg("select", False)

        # initialize perf events #
        SysMgr.initSystemPerfEvents()

        # execute commands #
        if SysMgr.inputParam:
            watchList = []
            for cmd in SysMgr.inputParam.split(","):
                pid = SysMgr.createProcess(cmd.split(), mute=True)
                if pid > 0:
                    watchList.append(str(pid))

            # apply new filter #
            SysMgr.filterGroup = watchList
            if not SysMgr.processEnable:
                SysMgr.groupProcEnable = True

            # add termination condition #
            SysMgr.addEnvironVar("EXITCONDTERM", watchList)

        # get prof flag #
        printFlag = "PRINTDELAY" in SysMgr.environList

        # get printcg flag #
        if "PRINTCG" in SysMgr.environList:
            # check cgroup path #
            cgroupPath = SysMgr.sysInstance.getCgroupPath()
            if not cgroupPath:
                SysMgr.printCgroupList()
                SysMgr.printErr("failed to access cgroup filesystem")
                sys.exit(-1)

            # check mandatory subsystem #
            SysMgr.checkCgroup()

            # set target subsystems #
            targetGroup = SysMgr.environList["PRINTCG"][0]
            if targetGroup == "SET":
                targetGroup = ["cpu", "cpuacct"]
            elif targetGroup.upper() == "ALL":
                targetGroup = []
            else:
                targetGroup = UtilMgr.cleanItem(targetGroup.split("+"))
                if "cpu" in targetGroup:
                    targetGroup += ["cpuacct"]
                targetGroup = list(set(targetGroup))

            # remove sort option #
            if not SysMgr.sort and not "cpu" in targetGroup:
                SysMgr.sort = False

            targetList = ["task", "cgroup"]

            printCg = True
        else:
            targetList = ["task"]

            printCg = False

        # get D-Bus unit list #
        if SysMgr.dbusUnitEnable:
            DbusMgr.pidUnitList = DbusMgr.runDbusSnooper(mode="getpidlist")

        # run loop #
        while 1:
            if SysMgr.remoteServObj:
                # receive response from server #
                ret = SysMgr.localServObj.recvfrom()

                # handle response from server #
                self.handleServerResponse(ret)

                continue

            # handle skip condition #
            if SysMgr.skipCnt:
                SysMgr.skipCnt -= 1
                self.reinitStats()
                time.sleep(SysMgr.intervalEnable)
                continue

            # collect system stats as soon as possible #
            if printFlag:
                UtilMgr.saveTime()
            self.saveSystemStat()
            if printFlag:
                UtilMgr.printTime("saveSystemStat")

            # save cgroup stats #
            if printCg:
                self.saveCgroupStat(targetGroup)
                if printFlag:
                    UtilMgr.printTime("saveCgroupStat")

            # save timestamp #
            prevTime = time.time()

            # handle first tick for printing total resource #
            if SysMgr.totalEnable and not self.prevCpuData:
                SysMgr.progressCnt += 1
                self.reinitStats()
                self.saveSystemStat()

            # print stats #
            if self.prevCpuData:
                # save timestamp #
                if printFlag:
                    UtilMgr.saveTime()

                # print system status #
                self.printSystemStat(idIndex=True, targetList=targetList)

                # check termination condition #
                self.checkTermCond()

                # report system status for elastic stack
                if SysMgr.elasticEnable:
                    self.reportSystemStatElastic()
                # report system status #
                elif SysMgr.reportEnable:
                    self.reportSystemStat()

                # print elapsed time #
                if printFlag:
                    UtilMgr.printTime("printSystemStat")

            # check repeat count #
            SysMgr.checkProgress()

            # reset system status #
            self.reinitStats()

            # write user command #
            SysMgr.runProfCmd("AFTER")

            # get delayed time #
            delayTime = time.time() - prevTime
            if delayTime > SysMgr.intervalEnable:
                waitTime = 0.000001
            else:
                waitTime = SysMgr.intervalEnable - delayTime

            # get stack of threads #
            if SysMgr.stackEnable and self.stackTable:
                self.sampleStack(waitTime)
                SysMgr.waitUserInput(0.000001)
            # wait for next tick #
            elif not SysMgr.waitUserInput(waitTime):
                time.sleep(waitTime)

            # check request from client #
            self.checkServer()

    def saveCgroupInstance(self):
        del self.prevCgroupData
        self.prevCgroupData = self.cgroupData
        self.cgroupData = {}
        SysMgr.topInstance = self
        SysMgr.procInstance = self.procData

    def saveProcInstance(self):
        # close fds for terminated tasks #
        for pid in set(self.prevProcData) - set(self.procData):
            try:
                os.close(self.procFdData.pop(pid, None))
            except SystemExit:
                sys.exit(0)
            except:
                pass

        self.prevProcData = self.procData
        self.procData = {}
        self.fixedProcData = {}
        SysMgr.topInstance = self
        SysMgr.procInstance = self.procData

    def reinitStats(self):
        self.oldCpuData = self.prevCpuData
        self.prevCpuData = self.cpuData
        self.cpuData = {}
        self.fileData = {}
        self.abnormalTasks = {}
        self.nsData = {}
        self.nsProcData = {}
        self.binderData = {}
        self.nrPrevThread = self.nrThread
        self.nrPrevProcess = self.nrProcess
        self.nrThread = 0
        self.nrProcess = 0
        self.nrFd = 0
        SysMgr.jsonData = {}

    @staticmethod
    def applyPrintCond():
        for env in list(SysMgr.printCond):
            if not env in SysMgr.environList:
                continue

            # apply condition number #
            SysMgr.printCond[env] = UtilMgr.getEnvironNum(env)
            if SysMgr.printCond[env]:
                SysMgr.reportEnable = True

    @staticmethod
    def getStatsFile(
        logFile, handle=None, applyOpt=True, onlyStart=False, verb=False
    ):
        path = None
        infoBuf = None
        chartStats = {}
        timeline = []
        eventList = []
        eventData = []

        cpuUsage = []
        nrCore = []
        nrTask = []
        memFree = []
        memAnon = []
        memCache = []
        memKernel = []
        swapUsage = []
        reclaimBg = []
        reclaimDr = []
        blkWait = []
        blkRead = []
        blkWrite = []
        netRead = []
        netWrite = []
        gpuUsage = {}
        cpuProcUsage = {}
        cpuProcDelay = {}
        cpuProcPrio = {}
        memProcUsage = {}
        blkProcUsage = {}
        storageUsage = {}
        networkUsage = {}

        # get file handle #
        try:
            if handle:
                fd = handle
            else:
                fd = UtilMgr.getTextLines(logFile, verb=verb, retfd=True)
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to read '%s'\n" % logFile)
            sys.exit(-1)

        # summarize details in advance #
        if "TOPSUM" in SysMgr.environList:
            # load whole file #
            SysMgr.reloadFileBuffer(path=fd.name, fd=fd if handle else None)

            # get new stats #
            idx = 0
            for idx, item in enumerate(SysMgr.procBuffer):
                if item.lstrip("\n ").startswith("[Top Info]"):
                    break
            SysMgr.procBuffer = SysMgr.procBuffer[idx:]

            # backup print fd #
            origFd = SysMgr.printFd

            # create an on-memory file for temporary output #
            SysMgr.printFd = MemoryFile(name="buf")

            # summarize file data #
            TaskAnalyzer.printIntervalUsage(current=False)

            # convert data to list #
            fd = SysMgr.printFd
            fd.seek(0)

            # restore print fd #
            SysMgr.printFd = origFd

        # get file size #
        totalSize = 0
        if not handle:
            fsize = UtilMgr.getFileSizeStr(logFile)
            SysMgr.printStat(r"start processing '%s'%s..." % (logFile, fsize))

        # context variable #
        context = None
        totalRam = None
        startTime = 0

        # get env flags #
        envList = SysMgr.environList
        nocgcpu = "NOCGCPU" in envList
        nocgthrottle = "NOCGTHROTTLE" in envList
        nocgmem = "NOCGMEM" in envList
        nocgblk = "NOCGBLK" in envList
        norecfile = "NORECFILE" in envList
        recfileFilter = envList.get("RECFILEFILTER")
        lifeEvent = "LIFEEVENT" in SysMgr.environList

        convSize = UtilMgr.convUnit2Size

        curSize = 0
        for idx, line in enumerate(fd):
            # check EOF #
            if not line and fd.size <= curSize:
                break

            # increase size #
            curSize += len(line) + 1
            UtilMgr.printProgress(curSize, totalSize)

            # get system info #
            try:
                if not SysMgr.sysinfoBuffer and line.startswith(
                    "[System General Info]"
                ):
                    infoBuf = ""
                elif infoBuf is not None:
                    if line.startswith("["):
                        # apply launch option #
                        SysMgr.sysinfoBuffer = infoBuf
                        if applyOpt:
                            SysMgr.applyLaunchOption()
                        infoBuf = None
                        continue
                    elif line.startswith("=") or line.startswith(" "):
                        continue
                    else:
                        if not line.endswith("\n"):
                            line += "\n"
                        infoBuf += line
                        continue
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr(
                    "failed to read data from '%s'" % fd.name, True
                )
                break

            # split line #
            sline = line.split("|")
            slen = len(sline)

            # get context #
            if line.startswith("[Top "):
                pid = 0
                maxVss = 0
                maxRss = 0
                pname = None
                gname = None
                intervalList = None

                contextList = line.split()

                # termination #
                if len(contextList) > 10:
                    strPos = line.find("[RAM")
                    sline = line[strPos:].split()

                    try:
                        totalRam = convSize(sline[1][:-1])
                    except:
                        pass

                    try:
                        totalSwap = convSize(sline[3][:-1])
                    except:
                        totalSwap = None

                    break

                # change context #
                context = contextList[1]

            # Summary #
            if context == "Summary":
                nrStatistics = 15

                if slen < nrStatistics:
                    continue

                try:
                    idx = long(sline[0])
                except:
                    continue

                try:
                    tick = long(float(sline[1].split("-")[1]))
                    timeline.append(tick)
                    if not startTime and tick:
                        startTime = tick
                        if onlyStart:
                            return {"start": tick}, {}
                except:
                    timeline.append(0)

                eventList.append(list())

                try:
                    cpuUsage.append(long(sline[2]))
                except:
                    cpuUsage.append(0)

                try:
                    memStat = sline[3].split("/")

                    if len(memStat) > 4:
                        raise Exception()

                    memFree.append(long(memStat[0]))
                    memAnon.append(long(memStat[1]))
                    memCache.append(long(memStat[2]))
                    try:
                        memKernel.append(long(memStat[3]))
                    except:
                        memKernel.append(0)
                except:
                    # for backward compatibility #
                    try:
                        memFree.append(long(sline[3]))
                    except:
                        memFree.append(0)
                    memAnon.append(0)
                    memCache.append(0)
                    memKernel.append(0)

                try:
                    blkWait.append(long(sline[5]))
                except:
                    blkWait.append(0)

                try:
                    swapUsage.append(long(sline[6]))
                except:
                    swapUsage.append(0)

                try:
                    reclaim = sline[7].strip().split("/")
                    reclaimBg.append(long(reclaim[0]) << 2)
                    reclaimDr.append(long(reclaim[1]) << 2)
                except:
                    reclaimBg.append(0)
                    reclaimDr.append(0)

                try:
                    blkUsage = sline[4].split("/")
                    blkRead.append(long(blkUsage[0]) << 10)
                    blkWrite.append(long(blkUsage[1]) << 10)
                except:
                    blkRead.append(0)
                    blkWrite.append(0)

                try:
                    nrTask.append(sline[11])
                except:
                    nrTask.append("")

                try:
                    nrCore.append(long(sline[12]))
                except:
                    nrCore.append(0)

                try:
                    netstat = sline[13].strip().split("/")

                    if netstat[0] == "-":
                        raise Exception()

                    if netstat[0][-1] == "T":
                        netRead.append(long(netstat[0][:-1]) << 30)
                    elif netstat[0][-1] == "G":
                        netRead.append(long(netstat[0][:-1]) << 20)
                    elif netstat[0][-1] == "M":
                        netRead.append(long(netstat[0][:-1]) << 10)
                    elif netstat[0][-1] == "K":
                        netRead.append(long(netstat[0][:-1]))
                    else:
                        netRead.append(0)

                    if netstat[0][-1] == "T":
                        netWrite.append(long(netstat[1][:-1]) << 30)
                    elif netstat[0][-1] == "G":
                        netWrite.append(long(netstat[1][:-1]) << 20)
                    elif netstat[1][-1] == "M":
                        netWrite.append(long(netstat[1][:-1]) << 10)
                    elif netstat[1][-1] == "K":
                        netWrite.append(long(netstat[1][:-1]))
                    else:
                        netWrite.append(0)
                except:
                    netRead.append(0)
                    netWrite.append(0)

            # CPU / DELAY #
            elif context in ("CPU", "Delay", "Prio"):
                if slen == 3:
                    m = re.match(r"\s*(?P<comm>.+)\(\s*(?P<pid>[0-9]+)", line)
                    if not m:
                        continue

                    d = m.groupdict()
                    comm = d["comm"].strip()

                    if SysMgr.filterGroup:
                        if not TaskAnalyzer.checkFilter(comm, d["pid"]):
                            intervalList = None
                        else:
                            pid = d["pid"]
                            pname = "%s(%s)" % (comm, pid)

                            intervalList = sline[2]
                    else:
                        pid = d["pid"]
                        pname = "%s(%s)" % (comm, pid)

                        intervalList = sline[2]
                elif slen == 2:
                    if intervalList:
                        intervalList += sline[1]
                elif intervalList:
                    if context == "CPU":
                        statList = cpuProcUsage
                        convFunc = long
                        hasTotal = True
                    elif context == "Delay":
                        statList = cpuProcDelay
                        convFunc = long
                        hasTotal = True
                    else:
                        statList = cpuProcPrio
                        convFunc = lambda e: e
                        hasTotal = False

                    # save previous info #
                    statList[pname] = {}
                    statList[pname]["pid"] = pid

                    # get lifecycle info #
                    start = -1
                    finish = -1
                    zombie = -1
                    intervalList = intervalList.split()
                    for idx, item in enumerate(list(intervalList)):
                        if item.startswith("+"):
                            start = idx
                        elif item.startswith("-"):
                            finish = idx
                        elif item.startswith("z"):
                            zombie = idx
                        else:
                            continue
                        intervalList[idx] = item[1:]

                    # convert interval list #
                    cpuList = list(map(convFunc, intervalList))
                    intervalList = " ".join(intervalList)

                    # save interval list #
                    statList[pname]["usage"] = intervalList
                    intervalList = None

                    # update lifecycle #
                    if start > -1:
                        statList[pname]["start"] = start
                    if finish > -1:
                        statList[pname]["finish"] = finish
                    if zombie > -1:
                        statList[pname]["zombie"] = zombie

                    # check total stat #
                    if not hasTotal:
                        statList[pname]["usage"] = cpuList
                        continue

                    # calculate total usage of tasks filtered #
                    if TaskAnalyzer.checkFilter(comm, pid):
                        tname = "[ TOTAL ]"
                        if not tname in statList:
                            statList[tname] = {}

                            pstats = statList[pname]["usage"].split()
                            ftotal = list(map(long, pstats))

                            statList[tname]["count"] = 1
                        else:
                            tstats = statList[tname]["usage"].split()
                            ftotal = list(map(long, tstats))

                            for idx in xrange(len(ftotal)):
                                ftotal[idx] += cpuList[idx]

                            statList[tname]["count"] += 1

                        usageList = " ".join(list(map(str, ftotal)))
                        statList[tname]["usage"] = usageList
                        statList[tname]["minimum"] = min(ftotal)
                        statList[tname]["average"] = sum(ftotal) / len(ftotal)
                        statList[tname]["maximum"] = max(ftotal)

                    # update statistics #
                    if not cpuList:
                        statList[pname]["minimum"] = 0
                        statList[pname]["average"] = 0
                        statList[pname]["maximum"] = 0
                    else:
                        statList[pname]["minimum"] = min(cpuList)
                        statList[pname]["average"] = sum(cpuList) / len(
                            cpuList
                        )
                        statList[pname]["maximum"] = max(cpuList)

            # GPU #
            elif context == "GPU":
                if slen == 3:
                    gname = sline[0].strip()
                    intervalList = sline[2]
                elif slen == 2:
                    if intervalList:
                        intervalList += sline[1]
                elif intervalList and gname != "GPU":
                    # save previous info #
                    gpuUsage[gname] = intervalList
                    intervalList = None

                    """
                    gpuUsage[gname] = {}

                    gpuUsage[gname]['usage'] = intervalList
                    gpuList = list(map(long, intervalList.split()))

                    if not gpuList:
                        gpuUsage[gname]['minimum'] = 0
                        gpuUsage[gname]['average'] = 0
                        gpuUsage[gname]['maximum'] = 0
                    else:
                        gpuUsage[pname]['minimum'] = min(gpuList)
                        gpuUsage[pname]['average'] = \
                            sum(gpuList) / len(gpuList)
                        gpuUsage[pname]['maximum'] = max(gpuList)
                    """

            # VSS #
            elif context == "VSS":
                if slen == 3:
                    m = re.match(r"\s*(?P<comm>.+)\(\s*(?P<pid>[0-9]+)", line)
                    if not m:
                        continue

                    d = m.groupdict()
                    comm = d["comm"].strip("^ ")

                    if not SysMgr.filterGroup:
                        pid = d["pid"]
                        pname = "%s(%s)" % (comm, pid)
                        maxVss = long(sline[1])
                        intervalList = sline[2]
                        continue

                    if not TaskAnalyzer.checkFilter(comm, d["pid"]):
                        intervalList = None
                    else:
                        pid = d["pid"]
                        pname = "%s(%s)" % (comm, pid)
                        maxVss = long(sline[1])
                        intervalList = sline[2]
                elif slen == 2:
                    if intervalList:
                        intervalList += sline[1]
                elif intervalList:
                    # save previous info #
                    try:
                        memProcUsage[pname]
                    except:
                        memProcUsage[pname] = {}
                        memProcUsage[pname]["pid"] = pid

                    memProcUsage[pname]["maxVss"] = maxVss
                    memProcUsage[pname]["vssUsage"] = intervalList
                    intervalList = None

            # RSS / PSS / USS #
            elif (
                SysMgr.rssEnable or SysMgr.pssEnable or SysMgr.ussEnable
            ) and context in ("RSS", "PSS", "USS"):
                if slen == 3:
                    m = re.match(r"\s*(?P<comm>.+)\(\s*(?P<pid>[0-9]+)", line)
                    if not m:
                        continue

                    d = m.groupdict()
                    comm = d["comm"].strip("^ ")

                    if not SysMgr.filterGroup:
                        pid = d["pid"]
                        pname = "%s(%s)" % (comm, pid)
                        maxRss = long(sline[1])
                        intervalList = sline[2]
                        continue

                    if not TaskAnalyzer.checkFilter(comm, d["pid"]):
                        intervalList = None
                    else:
                        pid = d["pid"]
                        pname = "%s(%s)" % (comm, pid)
                        maxRss = long(sline[1])
                        intervalList = sline[2]
                elif slen == 2:
                    if intervalList:
                        intervalList += sline[1]
                elif intervalList:
                    # save previous info #
                    try:
                        memProcUsage[pname]
                    except:
                        memProcUsage[pname] = {}
                        memProcUsage[pname]["pid"] = pid

                    memProcUsage[pname]["maxRss"] = maxRss
                    memProcUsage[pname]["rssUsage"] = intervalList
                    intervalList = None

            # Block #
            elif context == "Block":
                if slen == 3:
                    m = re.match(r"\s*(?P<comm>.+)\(\s*(?P<pid>[0-9]+)", line)
                    if not m:
                        continue

                    d = m.groupdict()
                    comm = d["comm"].strip()

                    if not SysMgr.filterGroup:
                        pid = d["pid"]
                        pname = "%s(%s)" % (comm, pid)

                        try:
                            total = long(sline[1])
                        except:
                            total = sline[1]

                        intervalList = sline[2]
                        continue

                    if not TaskAnalyzer.checkFilter(comm, d["pid"]):
                        intervalList = None
                    else:
                        pid = d["pid"]
                        pname = "%s(%s)" % (comm, pid)

                        try:
                            total = long(sline[1])
                        except:
                            total = sline[1]

                        intervalList = sline[2]
                elif slen == 2:
                    if intervalList:
                        intervalList += sline[1]
                elif intervalList:
                    # save previous info #
                    blkProcUsage[pname] = {}
                    blkProcUsage[pname]["pid"] = pid
                    blkProcUsage[pname]["total"] = total
                    blkProcUsage[pname]["usage"] = intervalList
                    intervalList = None

            # Storage #
            elif context == "Storage":
                if slen == 3:
                    sname = sline[0].strip()
                    intervalList = sline[2]
                elif slen == 2:
                    if intervalList:
                        intervalList += sline[1]
                elif intervalList and not sname in ("Device", "Storage"):
                    # define arrays #
                    storageUsage.setdefault(sname, {})
                    busyList = []
                    readList = []
                    writeList = []
                    freeList = []

                    # convert previous stats #
                    for item in intervalList.split():
                        if item == "0":
                            busy = read = write = free = 0
                        else:
                            busy, read, write, free = item.split("/")
                        busyList.append(long(busy))
                        readList.append(convSize(read) >> 10)
                        writeList.append(convSize(write) >> 10)
                        freeList.append(convSize(free) >> 10)

                    # save previous info #
                    storageUsage[sname]["busy"] = busyList
                    storageUsage[sname]["read"] = readList
                    storageUsage[sname]["write"] = writeList
                    storageUsage[sname]["free"] = freeList
                    intervalList = None

            # Network #
            elif context == "Network":
                if slen == 3:
                    sname = sline[0].strip()
                    intervalList = sline[2]
                elif slen == 2:
                    if intervalList:
                        intervalList += sline[1]
                elif intervalList and not sname in ("Device", "Network"):
                    # define arrays #
                    networkUsage.setdefault(sname, {})
                    recvList = []
                    tranList = []

                    # convert previous stats e
                    for item in intervalList.split():
                        if item == "0":
                            recv = tran = 0
                        else:
                            recv, tran = item.split("/")
                        recvList.append(convSize(recv) >> 10)
                        tranList.append(convSize(tran) >> 10)

                    # save previous info #
                    networkUsage[sname]["recv"] = recvList
                    networkUsage[sname]["tran"] = tranList
                    intervalList = None

            # Memory #
            elif context == "Memory":
                if slen != 13:
                    continue

                m = re.match(r"\s*(?P<comm>.+)\(\s*(?P<pid>[0-9]+)", sline[0])
                if m:
                    d = m.groupdict()
                    pid = d["pid"]
                    comm = d["comm"].strip()
                    pname = "%s(%s)" % (comm, pid)
                    chartStats[pname] = {}

                try:
                    chartStats[pname][sline[1].strip()] = list(
                        map(lambda x: long(x.replace(",", "")), sline[2:-1])
                    )
                except:
                    pass

            # Cgroup.cpu #
            elif context in (
                "Cgroup.CPU",
                "Cgroup.Throttle",
                "Cgroup.Mem",
                "Cgroup.Blk",
            ):
                if context == "Cgroup.CPU":
                    if nocgcpu:
                        continue
                    targetDict = cpuProcUsage
                elif context == "Cgroup.Throttle":
                    if nocgthrottle:
                        continue
                    targetDict = cpuProcDelay
                elif context == "Cgroup.Mem":
                    if nocgmem:
                        continue
                    targetDict = memProcUsage
                else:
                    if nocgblk:
                        continue
                    targetDict = blkProcUsage

                pid = 0

                if slen == 3:
                    if not sline[0].startswith("Cgroup"):
                        comm = sline[0].strip()

                        if SysMgr.filterGroup:
                            if not TaskAnalyzer.checkFilter(comm, pid):
                                intervalList = None
                            else:
                                pname = "%s" % comm

                                intervalList = sline[2]
                        else:
                            pname = "%s" % comm

                            intervalList = sline[2]
                elif slen == 2:
                    if intervalList:
                        intervalList += sline[1]
                elif intervalList:
                    # save previous info #
                    targetDict[pname] = {}
                    targetDict[pname]["pid"] = pid

                    # get lifecycle info #
                    intervalList = intervalList.split()

                    statNames = {
                        "min": "minimum",
                        "avg": "average",
                        "max": "maximum",
                        "usage": "usage",
                    }

                    if context in ("Cgroup.CPU", "Cgroup.Throttle"):
                        rlist = list(map(float, intervalList))
                        rlist = list(map(long, rlist))
                    elif context == "Cgroup.Blk":
                        for idx in range(len(intervalList)):
                            rd, wr = intervalList[idx].split("/")
                            rd = convSize(rd) >> 20
                            wr = convSize(wr) >> 20
                            intervalList[idx] = "%s/%s" % (rd, wr)
                        rlist = intervalList
                    elif context == "Cgroup.Mem":
                        rlist = list(map(convSize, intervalList))
                        rlist = list(map(lambda v: long(v) >> 20, rlist))

                        statNames = {
                            "min": "minRss",
                            "avg": "avgRss",
                            "max": "maxRss",
                            "usage": "rssUsage",
                        }

                    intervalList = list(map(str, rlist))
                    intervalList = " ".join(intervalList)
                    targetDict[pname][statNames["usage"]] = intervalList
                    intervalList = None

                    if context == "Cgroup.Blk":
                        continue

                    # update statistics #
                    tdict = targetDict[pname]
                    if not rlist:
                        tdict[statNames["min"]] = 0
                        tdict[statNames["avg"]] = 0
                        tdict[statNames["max"]] = 0
                    else:
                        tdict[statNames["min"]] = min(rlist)
                        tdict[statNames["avg"]] = sum(rlist) / len(rlist)
                        tdict[statNames["max"]] = max(rlist)

            # Event #
            elif context == "Event":
                if slen != 4:
                    continue

                try:
                    ltime = long(float(sline[0]))
                    utime = float(sline[1])
                    dtime = float(sline[2])
                    event = sline[3].strip()
                    eventData.append([ltime, utime, dtime, event])
                except:
                    pass

            # File #
            elif context == "File":
                if norecfile:
                    continue
                elif slen != 2:
                    if line.startswith("[Top File"):
                        for item in line.strip().split("("):
                            if not item.startswith("Path:"):
                                continue
                            path = UtilMgr.lstrip(item, "Path: ").strip(")")
                    continue
                elif recfileFilter and not UtilMgr.isValidStr(
                    path, recfileFilter
                ):
                    continue

                try:
                    utime = float(sline[0])
                    idx = timeline.index(long(utime))
                    dtime = 0

                    if not path in TaskAnalyzer.fileNameTable:
                        TaskAnalyzer.fileNameTable[path] = "#%s" % len(
                            TaskAnalyzer.fileNameTable
                        )
                    name = TaskAnalyzer.fileNameTable[path]

                    event = "%s> %s" % (name, sline[1].strip())

                    eventList[idx].insert(0, event)
                except:
                    pass

            # Life #
            elif context == "Life":
                if slen != 8:
                    continue

                if not lifeEvent:
                    continue

                try:
                    task = sline[0].strip()
                    start = sline[1].strip()
                    end = sline[2].strip()
                    prio = sline[6].strip()

                    for s, char in ((start, "+"), (end, "-")):
                        try:
                            st = UtilMgr.convTime2Sec(s)
                            if not st:
                                raise Exception("no time")

                            stidx = UtilMgr.bisect_left(timeline, st)
                            eventList[stidx].append(
                                "[%s] %s [%s] <%s>" % (char, task, s, prio)
                            )
                        except:
                            pass
                except:
                    pass

        # set event timeline #
        try:
            for evt in eventData:
                ltime, utime, dtime, event = evt
                idx = timeline.index(ltime)
                eventList[idx].append(
                    "%s [%.3f / +%.2fs]" % (event, utime, dtime)
                )
        except:
            pass

        if curSize:
            UtilMgr.deleteProgress()

        # verify mandatory stat #
        if not totalRam:
            SysMgr.printErr("failed to find statistics data in '%s'" % logFile)
            return {}, {}
        elif not timeline:
            SysMgr.printErr("failed to find interval data in '%s'" % logFile)
            return {}, {}

        # parse sysinfo #
        try:
            sysinfo = {}
            for item in SysMgr.sysinfoBuffer.split("\n"):
                item = item.split(" ", 1)
                if len(item) != 2:
                    continue
                sysinfo[item[0]] = item[1].strip()
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn("failed to parse sysinfo", True, True)

        # get indexes for trim #
        if {"TRIM", "TRIMIDX"} & set(envList):
            vals = SysMgr.getTrimVals(timeline)
            if not vals:
                SysMgr.printErr("no item in timeline")
                sys.exit(0)

            imin = timeline.index(vals[0])
            imax = timeline.index(vals[-1])

            # trim intervals #
            for name, value in cpuProcUsage.items():
                value["usage"] = value["usage"].split()[imin:imax]
                value["usage"] = " ".join(value["usage"])

            for name, value in blkProcUsage.items():
                value["usage"] = value["usage"].split()[imin:imax]
                value["usage"] = " ".join(value["usage"])

            for name, value in memProcUsage.items():
                if "vssUsage" in value:
                    value["vssUsage"] = value["vssUsage"].split()[imin:imax]
                    value["vssUsage"] = " ".join(value["vssUsage"])
                if "rssUsage" in value:
                    value["rssUsage"] = value["rssUsage"].split()[imin:imax]
                    value["rssUsage"] = " ".join(value["rssUsage"])

            for name, value in gpuUsage.items():
                value = value.split()[imin:imax]
                gpuUsage[name] = " ".join(value)

            for name, dev in storageUsage.items():
                for item, value in dev.items():
                    storageUsage[name][item] = value[imin:imax]

            for name, dev in networkUsage.items():
                for item, value in dev.items():
                    networkUsage[name][item] = value[imin:imax]
        else:
            # set range index #
            imin = 0
            imax = len(timeline)

        # check valid interval #
        if not timeline[imin:imax]:
            SysMgr.printErr("no data in the range %s:%s" % (imin, imax))
            return {}, {}

        # get systemd info #
        systemdList = []
        systemdParsing = False
        for line in fd:
            # check EOF #
            if not line:
                break

            if systemdParsing:
                if line.startswith("*") and "Kernel Message" in line:
                    break
                else:
                    systemdList.append(line)
            else:
                if line.startswith("*") and "Systemd Info" in line:
                    systemdParsing = True
        try:
            systemdInfo = {}
            if systemdList:
                systemdInfo = UtilMgr.convStr2Dict("".join(systemdList))
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to get systemd info", True)

        # set graph argument list #
        graphStats = {
            "timeline": timeline[imin:imax],
            "eventList": eventList[imin:imax],
            "cpuUsage": cpuUsage[imin:imax],
            "cpuProcUsage": cpuProcUsage,
            "cpuProcDelay": cpuProcDelay,
            "cpuProcPrio": cpuProcPrio,
            "blkWait": blkWait[imin:imax],
            "blkProcUsage": blkProcUsage,
            "blkRead": blkRead[imin:imax],
            "blkWrite": blkWrite[imin:imax],
            "netRead": netRead[imin:imax],
            "netWrite": netWrite[imin:imax],
            "memFree": memFree[imin:imax],
            "memAnon": memAnon[imin:imax],
            "memCache": memCache[imin:imax],
            "memKernel": memKernel[imin:imax],
            "memProcUsage": memProcUsage,
            "gpuUsage": gpuUsage,
            "totalRam": totalRam,
            "swapUsage": swapUsage[imin:imax],
            "totalSwap": totalSwap,
            "reclaimBg": reclaimBg[imin:imax],
            "reclaimDr": reclaimDr[imin:imax],
            "storageUsage": storageUsage,
            "networkUsage": networkUsage,
            "nrCore": nrCore[imin:imax],
            "nrTask": list(map(lambda x: x.strip(), nrTask[imin:imax])),
            "graphTitle": "Guider Perf Graph",
            "start": startTime,
            "sysinfo": sysinfo,
            "systemdInfo": systemdInfo,
        }

        return graphStats, chartStats

    def getAvgStats(self, flist, stats):
        # pylint: disable=undefined-variable

        # make file index table #
        fileIdxList = {}
        for idx, fname in enumerate(flist):
            fileIdxList[fname] = idx

        avgList = {}

        for name, value in stats.items():
            # set graph title #
            if name == "graphTitle":
                avgList.setdefault(name, "Guider Average Graph")
                continue
            elif name.endswith("sysinfo"):
                continue

            try:
                fname, sname = name.split(":", 1)
            except:
                SysMgr.printErr(
                    "failed to get file name from '%s" % name, True
                )
                sys.exit(-1)

            if sname.endswith("List"):
                continue
            elif sname.endswith("ProcUsage"):
                avgList.setdefault(sname, {})

                for proc, pvalue in value.items():
                    pname = proc.split("(", 1)[0].lstrip("*")
                    avgList[sname].setdefault(pname, [0] * len(flist))
                    if sname == "cpuProcUsage":
                        usage = pvalue["average"]
                    elif sname == "memProcUsage":
                        if "rssUsage" in pvalue:
                            mname = "rssUsage"
                        else:
                            mname = "vssUsage"
                        mlist = list(map(long, pvalue[mname].split()))
                        usage = max(mlist)
                    else:
                        continue

                    # add average usage #
                    avgList[sname][pname][fileIdxList[fname]] += usage
            elif isinstance(value, list):
                if not value:
                    usage = 0
                elif isinstance(value[0], (int, long)):
                    usage = round(sum(value) / len(value), 1)
                else:
                    continue

                avgList.setdefault(sname, [0] * len(flist))
                avgList[sname][fileIdxList[fname]] = usage
            elif isinstance(value, dict):
                avgList.setdefault(sname, {})

                for item, val in value.items():
                    if UtilMgr.isString(val):
                        val = list(map(long, val.split()))
                        usage = round(sum(val) / len(val), 1)
                    else:
                        continue

                    avgList[sname].setdefault(item, [0] * len(flist))
                    avgList[sname][item][fileIdxList[fname]] = usage
            else:
                avgList.setdefault(sname, value)

        return avgList

    @staticmethod
    def drawHist(
        inputFile=[],
        statList=[],
        nameList=[],
        outFile=None,
        posInfo=[],
        subNameList=[],
        gname=None,
        yper=True,
        msg=None,
        yunit="",
        inLabel=False,
        xrangeList=[],
        addLabel=False,
        cumulative=False,
    ):
        # pylint: disable=undefined-variable

        """
        function call example:
        - statList: value list
        - nameList: plot name list
        - posInfo: position info list (grid, pos, rowspan, colspan)

        drawHist(
            statList = [       # bar data #
                [
                    range(1, 10),
                    range(5, 10),
                    range(9, 20),
                    range(10, 20),
                ],
                [
                    range(1, 10),
                    range(5, 10),
                    range(9, 20),
                    range(10, 20),
                ],
            ],
            nameList = [       # bar name #
                [
                    "test1",
                    "test2",
                    "test3",
                    "test4",
                ],
                [
                    "test1",
                    "test2",
                    "test3",
                    "test4",
                ],
            ],
            subNameList = [    # box name #
                "group1",
                "group2",
                "group3",
            ],
            posInfo = [        # box position of stat #
                (
                    [(3, 3), (0, 0), 1, 1],
                    [(3, 3), (1, 1), 1, 1],
                    [(3, 3), (2, 2), 1, 1],
                    [(3, 3), (0, 0), 1, 1],
                ),
                (
                    [(3, 3), (0, 0), 1, 1],
                    [(3, 3), (1, 1), 1, 1],
                    [(3, 3), (2, 2), 1, 1],
                    [(3, 3), (0, 0), 1, 1],
                ),
            ],
            xrangeList = [     # set xrange list #
                "10:100:10",
                "",
                ":200",
                "::50",
            ],
        )
        """

        # check input #
        if not inputFile and not statList:
            SysMgr.printErr("no input for histogram")
            sys.exit(-1)

        # convert input value type to list #
        fileName, inputName, inputList = UtilMgr.getInputNames(inputFile)

        # initialize environment for drawing #
        TaskAnalyzer.initDrawEnv()

        # draw base #
        figObj = TaskAnalyzer.drawFigure()
        renderer = figObj.canvas.get_renderer()

        # parse stats #
        if inputFile:
            totalStat = {}

            # get target list #
            if "TARGETDRAW" in SysMgr.environList:
                targetList = ",".join(SysMgr.environList["TARGETDRAW"]).split(
                    ","
                )
            else:
                targetList = ["*"]

            for fname in inputList:
                graphStats, chartStats = TaskAnalyzer.getStatsFile(
                    logFile=fname,
                    applyOpt=True,
                )

                # merge stats #
                for n, v in graphStats.items():
                    # check system stat #
                    if not UtilMgr.isValidStr(n, targetList) or not v:
                        continue

                    # handle storage and network usage #
                    if n in ("storageUsage", "networkUsage"):
                        slist = {
                            "read": [],
                            "write": [],
                            "recv": [],
                            "tran": [],
                        }
                        for val in v.values():
                            for key, sv in val.items():
                                if not key in slist:
                                    continue

                                if not slist[key]:
                                    slist[key] = sv
                                    continue

                                slist[key] = [
                                    sum(x) for x in zip(slist[key], sv)
                                ]

                        # set name #
                        if slist["read"]:
                            name = "storageUsage"
                        else:
                            name = "networkUsage"

                        for key, sv in slist.items():
                            if not sv:
                                continue

                            key = name + key[0].upper() + key[1:]

                            # handle not merged list #
                            if "EACHFILE" in SysMgr.environList:
                                totalStat["%s@%s" % (key, name)] = sv
                                continue

                            if not key in totalStat:
                                totalStat[key] = []

                            totalStat[key] += sv

                        continue
                    # handle gpu usage #
                    elif n == "gpuUsage":
                        nv = []
                        for val in v.values():
                            nv += list(map(long, val.strip().split()))
                        v = nv

                    # TODO: handle process stats with -g filter option #

                    # check type #
                    if not isinstance(v, list):
                        continue

                    # handle not merged list #
                    if "EACHFILE" in SysMgr.environList:
                        totalStat["%s@%s" % (n, fname)] = v
                        continue

                    if not n in totalStat:
                        totalStat[n] = []

                    totalStat[n] += v

            # split statDict to statList and nameList #
            for n, v in totalStat.items():
                if n.startswith("cpuUsage"):
                    if sum(v) == 0:
                        continue

                    posInfo.append([(2, 2), (0, 0), 1, 1])
                elif n.startswith("memFree"):
                    if sum(v) == 0:
                        continue

                    posInfo.append([(2, 2), (0, 1), 1, 1])
                elif n.startswith("blkRead"):
                    if sum(v) == 0:
                        continue

                    posInfo.append([(2, 2), (1, 0), 1, 2])
                elif n.startswith("storageUsage"):
                    if sum(v) == 0:
                        continue

                    posInfo.append([(2, 2), (1, 1), 1, 2])
                else:
                    continue

                name = "System" + n[0].upper() + n[1:]
                nameList.append(name)
                subNameList.append(name)
                statList.append(v)

        # check stat #
        if not statList:
            SysMgr.printErr("no stat for histogram")
            sys.exit(-1)

        # set frame #
        fig, ax = subplots()

        # draw text #
        if msg:
            fig.text(
                0,
                0.99,
                msg,
                color="b",
                fontsize=2,
            )

        # set title ##
        if not gname:
            gname = "Guider Histogram"

        # draw title #
        ax.xaxis.set_major_locator(MaxNLocator(integer=True))
        suptitle(gname, fontsize=8)

        # init values #
        convNum = UtilMgr.convNum
        posList = {}
        nrBins = 20
        newSubNameList = []
        addLabel = addLabel or "ADDLABEL" in SysMgr.environList

        # convert types to long #
        def convLong(arr):
            if isinstance(arr, list) or isinstance(arr, range):
                return [convLong(item) for item in arr]
            else:
                return long(arr)

        statList = convLong(list(statList))

        # convert input types #
        if not isinstance(statList[0][0], list):
            statList = [statList]
        if not isinstance(nameList[0], list):
            nameList = [nameList]
        try:
            posInfo[0][0][0][0]
        except SystemExit:
            sys.exit(0)
        except:
            posInfo = [posInfo]

        # set pos info #
        try:
            if not posInfo:
                posList = [0]
                raise ValueError("no pos info")

            # merge data by pos info #
            for pidx, posSet in enumerate(posInfo):
                for idx, pos in enumerate(posSet):
                    posStr = str(pos)
                    if posStr in posList:
                        posList[posStr][1].append(list(statList[pidx][idx]))
                        posList[posStr][2].append(nameList[pidx][idx])
                    else:
                        posList[posStr] = [
                            pos,
                            [list(statList[pidx][idx])],
                            [nameList[pidx][idx]],
                        ]
        except ValueError:
            pass
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to parse pos info", True)
            return

        # update subNameList #
        try:
            for idx in range(len(posList)):
                newSubNameList.append(subNameList[idx])
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn("wrong index of subNameList used", True)

        # check subNameList count #
        if len(newSubNameList) != len(posList):
            SysMgr.printWarn(
                "counts of posInfo(%s) and subNameList(%s) are different"
                % (convNum(len(posList)), convNum(len(newSubNameList))),
                True,
            )

        # define random color getter #
        def _getColor():
            try:
                random = SysMgr.getPkg("random", False)
                return (random.random(), random.random(), random.random())
            except SystemExit:
                sys.exit(0)
            except:
                return None

        # define min/max getter #
        def _getStat(a, m, f):
            try:
                l = 0 if f == max else sys.maxsize
                for n in a:
                    l = f(m, l, _getStat(n, m, f))
                return l
            except SystemExit:
                sys.exit(0)
            except:
                return f(m, l, a)

        # define tick and label applyer #
        def _applyTickLabel(items, ax, maxVal):
            # set dimension #
            try:
                xticks(items, fontsize=TaskAnalyzer.xfsize)
                rmax = max(items)

                newList = []
                for idx, name in enumerate(items):
                    name = UtilMgr.convNum(long(name))
                    if idx & 1:
                        name = "\n%s" % name
                    newList.append(name)
                items = newList

                # update labels #
                nitems = items[:]
                last = UtilMgr.convNum(max(maxVal, rmax) + 1)
                last = "\n" + last if nitems[-1].startswith("\n") else last
                nitems[-1] = last
                ax.set_xticklabels(nitems)

                # draw grids #
                ax.xaxis.grid(
                    True, linestyle="-", linewidth="0.01", color="black"
                )
                ax.yaxis.grid(True, linestyle="-", linewidth="0.1", color="w")

                # draw ticks #
                gca().xaxis.set_ticks_position("both")
                tick_params(
                    axis="x",
                    which="both",
                    direction="in",
                    width=0.2,
                    pad=1,
                    color="black",
                )
            except:
                SysMgr.printWarn("failed to apply xticks", True, True)

            # apply formatter #
            try:
                # convert label units #
                ytickLabel = ax.get_yticks().tolist()
                ytickLabel = list(map(long, ytickLabel))
                ax.set_yticks(ytickLabel)
                ytickLabel = [convNum(val) for val in ytickLabel]
                ax.set_yticklabels(ytickLabel)
            except:
                pass

        # draw plots for each position #
        for index, pos in enumerate(posList):
            if not posInfo:
                continue

            if index == 0:
                ax.remove()

            posData, statData, nameList = posList[pos]

            ax = subplot2grid(
                posData[0],
                posData[1],
                rowspan=posData[2],
                colspan=posData[3],
            )
            ax.xaxis.set_major_locator(MaxNLocator(integer=True))
            subplots_adjust(
                top=0.90,
                bottom=0.05,
                left=0.05,
                right=0.95,
                hspace=0.4,
                wspace=0.3,
            )
            wbox = ax.get_window_extent(renderer=renderer)

            # draw title #
            ax.xaxis.set_major_locator(MaxNLocator(integer=True))
            name = (
                newSubNameList[index] if len(newSubNameList) > index else " "
            )
            ax.set_title(name, fontsize=5)

            # set style #
            [x.set_linewidth(0.2) for x in ax.spines.values()]
            tick_params(direction="in", length=2, width=0.5)

            # get min/max stats #
            minVal = _getStat(statData, sys.maxsize, min)
            maxVal = _getStat(statData, 0, max)

            # get bins #
            diff = long(maxVal - minVal)
            diff = 1 if diff == 0 else diff
            nrBins = min(diff, nrBins)
            nrBins = 1 if nrBins == 0 else nrBins
            nrTicks = long(diff / nrBins)
            bins = range(minVal, maxVal + 1 + nrTicks, nrTicks)

            # draw histogram #
            h, b, p = hist(
                statData,
                bins=bins,
                density=False,
                color=[_getColor() for _ in range(len(statData))],
                alpha=0.7,
                cumulative=cumulative,
                histtype="bar",
                stacked=False,
            )
            """
            histtype = "bar" | "barstacked" | "step" | "stepfilled"
            """

            # draw label #
            TaskAnalyzer.drawLabel(
                [
                    "%s(%s)" % (nameList[i], convNum(len(s)))
                    for i, s in enumerate(statData)
                ],
                draw=True,
                anchor=(1, 1) if inLabel else (1.1, 1.1),
                fontsize=2,
                handlelength=None if len(statData) > 1 else 0,
            )

            # update bars #
            try:
                minTick = 0
                maxTick = 0

                totalVal = 0
                for i, subp in enumerate(p):
                    # c = _getColor()
                    try:
                        height = subp.get_height()
                        if totalVal == 0:
                            totalVal = sum([x.get_height() for x in p])
                        if yper:
                            per = height / totalVal * 100
                            subp.set_height(per)
                            t = per
                        else:
                            t = height
                        maxTick = max(maxTick, t)
                        # subp.set_facecolor(c)
                    except SystemExit:
                        sys.exit(0)
                    except:
                        totalVal = 0
                        for si, bar in enumerate(subp):
                            height = bar.get_height()
                            if totalVal == 0:
                                totalVal = sum([x.get_height() for x in subp])
                            if yper:
                                per = height / totalVal * 100
                                bar.set_height(per)
                                t = per
                            else:
                                t = height
                            maxTick = max(maxTick, t)
                            # bar.set_facecolor(c)
            except SystemExit:
                sys.exit(0)
            except:
                pass

            # convert types #
            items = list(map(long, b))
            try:
                long(h[0])
                h = [h]
            except SystemExit:
                sys.exit(0)
            except:
                pass

            # set xrange #
            try:
                if len(xrangeList) > index:
                    xrangeval = xrangeList[index]
                    xvals = xrangeval.split(":")
                elif "XRANGE" in SysMgr.environList:
                    xvals = SysMgr.environList["XRANGE"][0].split(":")
                else:
                    raise ValueError()

                # set min #
                minidx = -1
                xmin = (
                    long(xvals[0])
                    if len(xvals) > 0 and xvals[0].isdigit()
                    else min(items)
                )
                for mi, ms in enumerate(items):
                    if ms >= xmin:
                        minidx = 0 if mi == 0 else mi - 1
                        break
                minidx = len(items) - 1 if minidx == -1 else minidx

                # set max #
                maxidx = -1
                xmax = (
                    long(xvals[1])
                    if len(xvals) > 1 and xvals[1].isdigit()
                    else max(items)
                )
                for mi, ms in enumerate(items):
                    if ms >= xmax:
                        maxidx = 0 if mi == 0 else mi
                        break
                maxidx = len(items) - 1 if maxidx == -1 else maxidx

                # set interval #
                if len(xvals) > 2 and xvals[2].isdigit():
                    xint = long(xvals[2])
                else:
                    xint = 0

                # update range #
                if xint:
                    items = list(range(xmin, xmax + xint, xint))
                else:
                    items = items[minidx : maxidx + 1]
            except ValueError:
                pass
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr("failed to apply custom xrange", True)
            finally:
                xmin = min(items)
                xmax = max(items)

                # limit xrange #
                xlim(xmin, xmax)

            # apply ticks and labels #
            _applyTickLabel(items, ax, maxVal if maxVal <= xmax else xmax)

            # get max ytick #
            vmaxTick = 0
            for c, v in enumerate(h):
                for i in range(len(b) - 1):
                    # check range #
                    if not (xmin <= b[i] < xmax):
                        continue

                    try:
                        num = long(v[i])
                    except SystemExit:
                        sys.exit(0)
                    except:
                        num = long(v)

                    if num == 0:
                        continue

                    # get per #
                    try:
                        width = p[c][i].get_width()
                        if yper:
                            per = p[c][i].get_height()
                    except SystemExit:
                        sys.exit(0)
                    except:
                        width = p[i].get_width()
                        if yper:
                            per = p[i].get_height()

                    # update max value #
                    if yper:
                        vmaxTick = max(vmaxTick, per)
                    else:
                        per = num / float(totalVal) * 100
                        vmaxTick = max(vmaxTick, num)

            # draw sample counts #
            for c, v in enumerate(h):
                for i in range(len(b) - 1):
                    # check range #
                    if not (xmin <= b[i] < xmax):
                        continue

                    try:
                        num = long(v[i])
                    except SystemExit:
                        sys.exit(0)
                    except:
                        num = long(v)

                    if num == 0:
                        continue

                    # get per #
                    try:
                        width = p[c][i].get_width()
                        if yper:
                            per = p[c][i].get_height()
                    except SystemExit:
                        sys.exit(0)
                    except:
                        width = p[i].get_width()
                        if yper:
                            per = p[i].get_height()

                    # get text #
                    cn = convNum(num)
                    cp = convNum(per, True)
                    if cp == "0.0":
                        cp = "0"
                    if yper:
                        t = "%s%% (%s)" % (cp, cn)
                    else:
                        t = "%s (%s%%)" % (cn, cp)
                    if addLabel:
                        t += " [%s]" % nameList[c]

                    # set font size #
                    fontsize = min(width / (xmax - xmin) * 140, 3)

                    # set text #
                    y = per if yper else num
                    if y > (vmaxTick / 2):
                        va = "top"
                    else:
                        va = "bottom"

                    # draw text #
                    tobj = text(
                        b[i] + ((c + 0.5) * width),
                        y,
                        t,
                        ha="left",
                        va=va,
                        color="black",
                        fontsize=fontsize,
                        rotation=-90,
                        rotation_mode=None,
                    )
                    bbox = tobj.get_window_extent(renderer=renderer)
                    if bbox.height > wbox.height / 2:
                        tobj.remove()

                        # resize text #
                        tobj = text(
                            b[i] + ((c + 0.5) * width),
                            y,
                            t,
                            ha="left",
                            va=va,
                            color="black",
                            fontsize=fontsize / 2,
                            rotation=-90,
                            rotation_mode=None,
                        )

            # set default ytick unit #
            if not yunit and yper:
                yunit = "%"

            # update yticks #
            TaskAnalyzer.drawYticks(
                ax, vmaxTick, adjust=True, ymin=minTick, yunit=yunit
            )

        # save map to file #
        return TaskAnalyzer.saveImage(fileName, "hist", outFile=outFile)

    @staticmethod
    def drawBitmap(inputFile, unit=4096):
        # pylint: disable=undefined-variable

        # check file #
        if isinstance(inputFile, list):
            if len(inputFile) > 1:
                SysMgr.printErr("no support multiple files")
                sys.exit(0)
            inputFile = inputFile[0]

        # load bitmap from file #
        try:
            table = list(open(inputFile, "rb").read())
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                "failed to read bitmap from '%s'" % inputFile, True
            )
            sys.exit(0)

        # initialize environment for drawing #
        TaskAnalyzer.initDrawEnv()

        # set frame #
        _, ax = subplots()

        # set title #
        suptitle("Guider Bitmap", fontsize=8)

        # set size of table #
        num = long(len(table) ** (1 / 2))

        # convert 0 values #
        table = [3 if not val else val for val in table]

        # get stats and build info string #
        nrTotal = len(table)
        nrExist = table.count(1)
        nrEmpty = table.count(3)
        convNum = UtilMgr.convNum
        info = (
            "- Unit: %s\n" "- Total: %s\n" "- Used: %s\n" "- Unused: %s\n"
        ) % (
            convNum(unit),
            convNum(nrTotal * unit),
            convNum(nrExist * unit),
            convNum(nrEmpty * unit),
        )

        # create a new bitmap #
        bitmap = []
        prevPos = 0
        for pos in xrange(num, len(table), num):
            bitmap.append(table[prevPos:pos])
            prevPos = pos

        # attach remaining bits #
        if prevPos < len(table) - 1:
            last = table[prevPos : len(table)]
            last += [2] * (num - len(last))
            bitmap.append(last)

        # draw base #
        figObj = TaskAnalyzer.drawFigure()

        # draw bitmap #
        ax.imshow(
            bitmap,
            cmap="RdGy",
            aspect="auto",
            interpolation="nearest",
            extent=[0, num, num, 0],
        )

        # set font size #
        fontsize = UtilMgr.getEnvironNum(
            "FONTSIZE", False, 3, False, isInt=True
        )

        # set ticks #
        ax.set_xticks(xrange(0, num, 1))
        ax.set_yticks(xrange(0, num, 1))
        ax.grid(color="w", linewidth=0.1)

        # set font size #
        xticks(fontsize=fontsize)
        yticks(fontsize=fontsize)

        # draw bitmap info #
        figObj.text(0, 1, info, va="top", ha="left", size=3)

        # save map to file #
        return TaskAnalyzer.saveImage(inputFile, "bitmap")

    @staticmethod
    def drawFigure():
        # pylint: disable=undefined-variable
        # ticklabel_format(useOffset=False)
        locator_params(axis="x", nbins=30)
        obj = figure(num=1, figsize=(10, 10), facecolor="b", edgecolor="k")
        obj.subplots_adjust(left=0.06, top=0.95, bottom=0.04, hspace=0.3)
        return obj

    @staticmethod
    def drawName(ax, name, fontsize=20):
        # pylint: disable=undefined-variable
        xticks = ax.get_xticks().tolist()
        yticks = ax.get_yticks().tolist()

        if xticks:
            x = xticks[long(len(xticks) * 1 / 2)] - fontsize / len(name)
        else:
            x = 0

        if yticks:
            y = yticks[long(len(yticks) * 1 / 2)]
        else:
            y = 0

        annotate(
            name,
            xy=(x, y),
            xytext=(x, y),
            fontsize=fontsize,
            fontweight="bold",
            color="gray",
            alpha=0.1,
        )

    @staticmethod
    def drawYticks(
        ax,
        ymax,
        fontsize=0,
        adjust=True,
        unit=10,
        ymin=-1,
        margin=True,
        yunit="",
    ):
        # pylint: disable=undefined-variable

        # set font size #
        if not fontsize and hasattr(TaskAnalyzer, "xfsize"):
            fontsize = TaskAnalyzer.xfsize
        else:
            fontsize = 5

        if "YRANGE" in SysMgr.environList:
            yminval, ymaxval = SysMgr.environList["YRANGE"][0].split(":")
            # use only one time for first graph #
            if not "KEEPYRANGE" in SysMgr.environList:
                SysMgr.environList.pop("YRANGE", None)

            # set ymin #
            try:
                yminval = long(yminval)
            except:
                yminval = 0
            finally:
                if yminval:
                    ax.set_ylim(bottom=long(yminval))
                    ymin = long(yminval)
                else:
                    ymin = long(min(ax.get_yticks().tolist()))
                    if ymin < 0:
                        ymin = 0
                        ax.set_ylim(bottom=0)

            # set ymax #
            try:
                ymaxval = long(ymaxval)
            except:
                ymaxval = 0
            finally:
                if ymaxval:
                    ax.set_ylim(top=long(ymaxval))
                    ymax = long(ymaxval)
                else:
                    ymax = long(min(ax.get_yticks().tolist()))

            # adjust ticks #
            if adjust:
                ymin = long(ymin)
                ymax = long(ymax)

                inc = long(ymax / unit)
                if inc == 0:
                    inc = 1

                yticks(xrange(ymin, long(ymax + inc), inc), fontsize=fontsize)
            else:
                yticks(fontsize=fontsize)
        else:
            if ymax is None:
                ylist = ax.get_yticks().tolist()
                ymax = long(max(ylist))

            if margin:
                ymaxval = ymax + int(ymax / unit)
            else:
                ymaxval = ymax

            if ymaxval > 0:
                ylim([0, ymaxval])

            # check ymin #
            if ymin < 0:
                ylist = ax.get_yticks().tolist()
                ymin = long(min(ylist))
            else:
                yminval = ymin - int(ymin / unit)
                ax.set_ylim(bottom=yminval)

            # adjust ymin #
            if ymin < 0:
                ymin = 0
                ax.set_ylim(bottom=0)

            # adjust yticks #
            if adjust:
                ymin = long(ymin)
                ymax = long(ymax)

                inc = long(ymax / unit)
                if inc == 0:
                    inc = 1

                yticks(xrange(ymin, long(ymax + inc), inc), fontsize=fontsize)
            else:
                yticks(fontsize=fontsize)

        # apply formatter #
        try:
            ytickLabel = ax.get_yticks().tolist()
            ytickLabel = list(map(long, ytickLabel))

            # convert label units #
            ytickLabel = [UtilMgr.convNum(val) + yunit for val in ytickLabel]

            # remove redundant values #
            last = None
            newLabel = []
            for n in ytickLabel:
                if n == last:
                    v = ""
                else:
                    last = v = n
                newLabel.append(v)
            ytickLabel = newLabel

            # apply formatter #
            ax.set_yticks(ax.get_yticks().tolist())
            ax.set_yticklabels(ytickLabel)
        except:
            pass

    @staticmethod
    def drawLabel(
        labelList,
        draw=True,
        anchor=(1.12, 0.75),
        loc="upper right",
        fontsize=None,
        markerfirst=False,
        handlelength=None,
    ):
        # pylint: disable=undefined-variable

        # check labels #
        if not labelList:
            return

        if not fontsize:
            fontsize = UtilMgr.getEnvironNum("LABELSIZE", False, 3.5, False)

        # set legend position #
        if SysMgr.matplotlibVersion >= 1.2:
            if labelList:
                res = legend(
                    labelList,
                    bbox_to_anchor=anchor,
                    fontsize=fontsize,
                    loc=loc,
                    markerfirst=markerfirst,
                    handlelength=handlelength,
                )
            elif draw:
                res = legend(
                    bbox_to_anchor=anchor,
                    fontsize=fontsize,
                    loc=loc,
                    markerfirst=markerfirst,
                    handlelength=handlelength,
                )
            else:
                res = None
        else:
            if labelList:
                res = legend(
                    labelList,
                    bbox_to_anchor=anchor,
                    loc=loc,
                    markerfirst=markerfirst,
                    handlelength=handlelength,
                )
            elif draw:
                res = legend(
                    bbox_to_anchor=anchor,
                    loc=loc,
                    markerfirst=markerfirst,
                    handlelength=handlelength,
                )
            else:
                res = None

        # set zorder #
        if res:
            res.set_zorder(1)

    @staticmethod
    def getProcThrInfo(pid, tid, tcomm):
        # get process info #
        pcomm = SysMgr.commCache[pid] if pid in SysMgr.commCache else "?"
        pinfo = "%s(%s)" % (pcomm, pid)
        if "ONLYPROC" in SysMgr.environList:
            return pinfo, pid

        # get thread info #
        tinfo = "%s(%s)" % (tcomm, tid)

        return pinfo + "->" + tinfo, tid

    @staticmethod
    def drawTimelineItems():
        origInputFile = SysMgr.inputFile
        origInterval = SysMgr.intervalEnable

        # check svgwrite object #
        SysMgr.getPkg("svgwrite")

        # prepare for timeline segment #
        SysMgr.graphEnable = False
        SysMgr.intervalEnable = 0
        tobj = TaskAnalyzer(origInputFile)
        outputPath = UtilMgr.prepareForImageFile(SysMgr.inputFile, "timeline")

        # initialize environment for drawing #
        TaskAnalyzer.initDrawEnv()

        # check absolute timeline option #
        if "ABSTIME" in SysMgr.environList:
            start = float(SysMgr.startTime)
        else:
            start = 0

        # minimize system info for annotation #
        if SysMgr.sysinfoBuffer:
            annotation = SysMgr.sysinfoBuffer
            annotation = annotation.replace("%s\n" % twoLine, "")
        else:
            annotation = None

        # update name table #
        itemInfoList = {}
        taskList = {x: None for x in tobj.threadData.keys()}
        if "PERTASK" in SysMgr.environList:
            tobj.timelineData["names"] = {
                y: x for x, y in tobj.timelineIdx.items()
            }

            # get task usage #
            for key, value in tobj.threadData.items():
                if not key in tobj.timelineIdx:
                    continue

                # calculate total task usage percentage #
                try:
                    usagePercent = (
                        value["usage"] / float(tobj.totalTime)
                    ) * 100
                except:
                    usagePercent = 0

                itemInfoList[key] = "%s: %d%%" % (value["comm"], usagePercent)
        else:
            # get core usage #
            for key, value in tobj.threadData.items():
                if not key.startswith("0["):
                    continue

                try:
                    coreId = key[2:-1]
                except:
                    continue

                # calculate total core usage percentage #
                try:
                    idle = value["usage"] / float(tobj.totalTime)
                    usagePercent = long(100 - (idle * 100))
                except:
                    usagePercent = 0

                itemInfoList[coreId] = "Core%s: %d%%" % (coreId, usagePercent)

        # draw timeline segment #
        SysMgr.drawTimeline(
            inputData=tobj.timelineData,
            outputPath=outputPath,
            taskList=taskList,
            start=start,
            annotation=annotation,
            yval=itemInfoList,
        )

        # draw resource graph #
        SysMgr.graphEnable = True
        SysMgr.intervalEnable = origInterval
        TaskAnalyzer(origInputFile).printUsage()

    @staticmethod
    def initDrawEnv(dpi=0, size=None, fontsize=None):
        # pylint: disable=import-error

        # get matplotlib object #
        matplotlib = SysMgr.getPkg("matplotlib", False)
        if not matplotlib:
            SysMgr.printPipWarn("matplotlib", "matplotlib")
            sys.exit(-1)

        from matplotlib.ticker import MaxNLocator

        SysMgr.matplotlibVersion = float(
            ".".join(matplotlib.__version__.split(".")[:2])
        )

        matplotlib.use("Agg")

        # get pylab object #
        SysMgr.importPkgItems("pylab")

        # set dpi #
        if not dpi and "DPI" in SysMgr.environList:
            dpi = long(SysMgr.environList["DPI"])
        matplotlib.rcParams["figure.dpi"] = (
            dpi if dpi else SysMgr.matplotlibDpi
        )

        # set resolution #
        if not size:
            if "RESOLUTION" in SysMgr.environList:
                size = list(
                    map(long, SysMgr.environList["RESOLUTION"][0].split("*"))
                )
            elif SysMgr.matplotlibSize:
                size = SysMgr.matplotlibSize
        if size:
            matplotlib.rcParams["figure.figsize"] = size

        # set font size #
        if fontsize:
            TaskAnalyzer.xfsize = TaskAnalyzer.lfsize = fontsize
        elif "FONTSIZE" in SysMgr.environList:
            fontsize = UtilMgr.getEnvironNum("FONTSIZE")
            TaskAnalyzer.xfsize = TaskAnalyzer.lfsize = fontsize
        else:
            TaskAnalyzer.xfsize = UtilMgr.getEnvironNum(
                "XFONTSIZE", False, 4, False
            )
            TaskAnalyzer.lfsize = UtilMgr.getEnvironNum(
                "LFONTSIZE", False, 3, False
            )

        # set variables for stacked plots #
        if SysMgr.checkMode("drawstack", True):
            SysMgr.addEnvironVar("STACKEDPLOT")
            SysMgr.addEnvironVar("TOTALLABEL")

        return matplotlib

    def drawStats(
        self,
        flist=[],
        fdlist=[],
        outFile=None,
        outFd=None,
        onlyGraph=False,
        onlyChart=False,
        applyOpt=True,
    ):
        def _printMemUsage(signum=None, frame=None):
            pid = SysMgr.pid
            comm = SysMgr.getComm(pid)

            try:
                mlist = SysMgr.getMemStat(pid)
                mstat = SysMgr.convMemStat(mlist)
                vss = UtilMgr.convSize2Unit(mstat["vss"], isInt=True, unit="M")
                rss = UtilMgr.convSize2Unit(mstat["rss"], isInt=True, unit="M")
                avl = SysMgr.getAvailMemInfo(unit="M")
                SysMgr.printWarn(
                    "System's AvailableMemory(%s), %s(%s)'s VSS(%s) & RSS(%s)"
                    % (avl, comm, pid, vss, rss),
                    True,
                )
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr(
                    "failed to get memory usage for %s(%s)" % (pid, comm), True
                )

            signal.alarm(SysMgr.intervalEnable)

        # convert to list #
        if not isinstance(flist, list):
            flist = [flist]
        if not isinstance(fdlist, list):
            fdlist = [fdlist]

        # set target #
        if fdlist:
            targets = fdlist
        else:
            targets = flist

        # get stats from a single file #
        if len(targets) == 1:
            logFile = targets[0]

            # parse stats #
            graphStats, chartStats = TaskAnalyzer.getStatsFile(
                logFile=logFile if flist else None,
                handle=logFile if fdlist else None,
                applyOpt=applyOpt,
            )
        # get stats from multiple files for comparison #
        else:
            logFile = SysMgr.outFilePath

            # define integrated stats #
            graphStats = {}
            chartStats = {}
            timeList = {}

            # get only start flag #
            concatenated = "CONCATENATE" in SysMgr.environList

            # parse stats from multiple files #
            for lfile in targets:
                try:
                    gstats, _ = TaskAnalyzer.getStatsFile(
                        lfile, onlyStart=concatenated, applyOpt=applyOpt
                    )
                    if not gstats:
                        continue
                except SystemExit:
                    sys.exit(0)
                except:
                    continue

                # merge stats for concatenation temporally #
                if concatenated:
                    try:
                        timeList[gstats["start"]] = lfile
                    except SystemExit:
                        sys.exit(0)
                    except:
                        pass
                    continue

                # merge stats #
                for key, val in gstats.items():
                    fname = os.path.basename(lfile)
                    if key == "graphTitle":
                        graphStats[key] = val
                    else:
                        graphStats["%s:%s" % (fname, key)] = val

            # concatenate stats in order #
            if concatenated:
                # set alarm for printing meory usage #
                if not SysMgr.intervalEnable:
                    SysMgr.intervalEnable = 1
                signal.signal(signal.SIGALRM, _printMemUsage)
                signal.alarm(SysMgr.intervalEnable)

                mergedData = []
                for stime, path in sorted(
                    timeList.items(),
                    key=lambda e: e[0],
                    reverse=True,
                ):
                    mergedData += TaskAnalyzer.getSummaryData(path)

                # save merged data #
                SysMgr.procBuffer = mergedData

                # set temporary output path for merged data #
                SysMgr.printFd = None
                SysMgr.pipeForPager = None
                origOutput = SysMgr.outPath
                if SysMgr.outPath:
                    SysMgr.outPath = os.path.dirname(origOutput)
                    if not SysMgr.outPath:
                        SysMgr.outPath = "."
                else:
                    SysMgr.outPath = "."
                SysMgr.outPath = os.path.join(
                    SysMgr.outPath, "merged_%s.out" % SysMgr.pid
                )

                # print summary #
                try:
                    SysMgr.printStat("start summarizing...")

                    # get only total flag #
                    onlyTotal = "ONLYTOTAL" in SysMgr.environList

                    # set ignore flags #
                    checkMode = SysMgr.checkMode
                    addEnv = SysMgr.addEnvironVar

                    # check processor #
                    if not any(
                        [checkMode("draw", True), checkMode("drawcpu", True)]
                    ):
                        addEnv("NOCPUSUMMARY")
                        addEnv("NOGPUSUMMARY")

                    # check delay #
                    if not checkMode("drawdelay", True):
                        addEnv("NODELAYSUMMARY")

                    # check vss #
                    if not any(
                        [
                            checkMode("drawvss", True),
                            checkMode("drawleak", True),
                        ]
                    ):
                        addEnv("NOVSSSUMMARY")

                    # check rss #
                    if not checkMode("drawrss", True):
                        addEnv("NORSSSUMMARY")

                    # check io #
                    if not any(
                        [checkMode("draw", True), checkMode("drawio", True)]
                    ):
                        addEnv("NOBLOCKSUMMARY")
                        addEnv("NOSTORAGESUMMARY")
                        addEnv("NONETSUMMARY")

                    # ignore life info #
                    addEnv("NOLIFESUMMARY")

                    # print interval info for drawing #
                    TaskAnalyzer.printIntervalUsage(
                        onlyTotal=onlyTotal, onlySummary=True, current=False
                    )
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr(
                        "failed to print interval summary", reason=True
                    )

                # clean up #
                targetFile = SysMgr.printFd.name
                del SysMgr.procBuffer
                SysMgr.procBuffer = []
                SysMgr.printFd = None
                SysMgr.outPath = origOutput

                try:
                    gstats, _ = TaskAnalyzer.getStatsFile(targetFile)
                    if not gstats:
                        sys.exit(-1)
                except SystemExit:
                    sys.exit(0)
                except:
                    sys.exit(-1)

                # merge stats #
                graphStats = gstats

        # initialize environment for drawing #
        TaskAnalyzer.initDrawEnv()

        # draw average graphs #
        if SysMgr.isDrawAvgMode():
            try:
                # convert pull path to file name #
                fnameList = [os.path.basename(fname) for fname in flist]
                if len(fnameList) < 2:
                    raise Exception("of a single input file")
                graphStats = self.getAvgStats(fnameList, graphStats)
                graphStats["fileList"] = fnameList
                self.drawAvgGraph(graphStats, logFile, outFile=outFile)
            except SystemExit:
                return
            except:
                SysMgr.printErr("failed to draw history graph", True)
            finally:
                return

        # get log events #
        logEvents = SysMgr.getLogEvents()

        # apply resource usage filter #
        removeItems = []
        for filterName, field in (
            ("CPUFILTER", "cpuUsage"),
            ("MEMFREEFILTER", "memFree"),
            ("MEMANONFILTER", "memAnon"),
            ("MEMCACHEFILTER", "memCache"),
            ("SWAPFILTER", "swapUsage"),
            ("NETRDFILTER", "netRead"),
            ("NETWRFILTER", "netWrite"),
            ("BLKRDFILTER", "blkRead"),
            ("BLKWRFILTER", "blkWrite"),
            ("NRCOREFILTER", "nrCore"),
        ):
            if not filterName in SysMgr.environList:
                continue

            _resChecker = UtilMgr.getSizeFilterFunc(filterName)
            removeItems = []
            for name in list(graphStats):
                if not name.endswith(field):
                    continue

                # check resource average #
                target = graphStats[name]
                if isinstance(target, list):
                    avg = sum(target) / len(target)
                    if _resChecker(avg):
                        continue
                elif isinstance(target, dict):
                    res = True
                    for subname, subtarget in target.items():
                        avg = sum(subtarget) / len(subtarget)
                        if not _resChecker(avg):
                            res = False
                            break
                    if not res:
                        continue
                else:
                    continue

                # skip drawing #
                filterInfo = SysMgr.environList[filterName][0]
                SysMgr.printWarn(
                    "skip drawing because of %s %s(%s)"
                    % (long(avg), filterName, filterInfo),
                    True,
                )

                # remove item #
                graphStats.pop(name, None)
                if name != field:
                    removeItems.append(UtilMgr.rstrip(name, field))

        # remove redundant items by filter #
        if removeItems:
            for name in list(graphStats):
                for item in removeItems:
                    graphStats.pop(name, None)

        # draw graphs #
        try:
            if graphStats and not onlyChart:
                ret = self.drawGraph(
                    deepcopy(graphStats),
                    logFile,
                    outFile=outFile,
                    outFd=outFd,
                    logEvents=logEvents,
                )
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to draw graph", True)
            return

        # draw charts #
        try:
            if chartStats and not onlyGraph:
                # draw detailed memory chart #
                ret = self.drawProcChart(chartStats, logFile, outFile=outFile)

                # draw system memory chart #
                if graphStats and SysMgr.pssEnable:
                    ret = self.drawSmemChart(
                        graphStats, logFile, outFile=outFile
                    )
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to draw chart", True)
            return

    def drawSmemChart(self, data, logFile, outFile=None):
        # pylint: disable=undefined-variable

        if not data:
            return

        def _make_autopct(values):
            def _autopct(pct):
                return ""

                total = sum(values)
                val = long(round(pct * total / 100.0)) << 20
                val = UtilMgr.convSize2Unit(val, False)
                usage = "{v:s} ({p:.0f}%)".format(p=pct, v=val)
                string = "{s:1} {l:1}{d:1}".format(
                    s=usage, d=details[self.tmpCnt], l=""
                )
                self.tmpCnt += 1
                return string

            return _autopct

        # set title #
        suptitle("Guider System Memory Chart", fontsize=8)

        # init variables #
        self.tmpCnt = 0
        summary = 0
        total = data["totalRam"] >> 20

        labels = []
        sizes = []
        details = []

        colors = [
            "skyblue",
            "pink",
            "lightgreen",
            "lightcoral",
            "gold",
            "yellowgreen",
        ]

        # get PSS value #
        for proc in list(data["memProcUsage"].keys()):
            mstats = data["memProcUsage"][proc]
            if not "rssUsage" in mstats:
                mstats["rss"] = 0
                continue

            # get pss #
            pss = long(mstats["rssUsage"].strip().split()[-1])
            mstats["rss"] = pss
            summary += pss

        # add available memory #
        availMem = data["memFree"][-1]
        summary += availMem
        data["memProcUsage"]["[Available]"] = {"rss": availMem}

        # add other memory #
        data["memProcUsage"]["[Other]"] = {"rss": total - summary}

        # get filters #
        incFilter = SysMgr.environList.get("INCFILTER")
        exFilter = SysMgr.environList.get("EXCFILTER")

        # add proc memory #
        for proc, mstats in sorted(
            data["memProcUsage"].items(),
            key=lambda x: long(x[1]["rss"]),
            reverse=True,
        ):
            visible = True
            # check filters #
            if incFilter and not UtilMgr.isValidStr(proc, incFilter):
                visible = False
            elif exFilter and UtilMgr.isValidStr(proc, exFilter):
                visible = False

            pss = mstats["rss"]
            sizes.append(pss)

            if visible:
                per = "%.1f" % ((pss / float(total)) * 100)
                labels.append(
                    proc
                    + " - %s(%s%%)" % (UtilMgr.convSize2Unit(pss << 20), per)
                )
                details.append(proc)
            else:
                labels.append("")
                details.append("")

        # convert labels to tuple #
        labels = tuple(labels)

        # find and mark index of max value #
        explode = [0] * len(sizes)
        for i in range(len(explode)):
            if incFilter or exFilter:
                if labels[i]:
                    explode[i] = 0.2
            elif labels[i].startswith("[Available]") or labels[i].startswith(
                "[Other]"
            ):
                continue
            else:
                explode[i] = 0.2

        ax = subplot2grid((1, 1), (0, 0), rowspan=1, colspan=1)
        ax.xaxis.set_major_locator(MaxNLocator(integer=True))

        # draw chart #
        patches, texts, autotexts = pie(
            sizes,
            explode=explode,
            labels=labels,
            colors=colors,
            autopct=_make_autopct(sizes),
            shadow=True,
            startangle=180 if SysMgr.matplotlibVersion >= 1.2 else None,
            pctdistance=0.8,
            labeldistance=1,
            textprops={"horizontalalignment": "left"},
        )

        # print total size in legend #
        legends = legend(
            patches,
            labels,
            loc="upper right",
            shadow=True,
            fontsize=self.xfsize if SysMgr.matplotlibVersion >= 1.2 else None,
        )

        # remove anonymous labels #
        for text, color in zip(legends.get_texts(), legends.legendHandles):
            if text.get_text() == "":
                text.set_visible(False)
                color.set_visible(False)

        # set font size #
        for idx, val in enumerate(texts):
            val.set_fontsize(self.xfsize / 2)
            autotexts[idx].set_fontsize(self.lfsize)
        axis("equal")
        tight_layout()

        # draw image #
        figure(
            num=1, figsize=(10, 10), facecolor="b", edgecolor="k"
        ).subplots_adjust(left=0, top=0.9, bottom=0.02, hspace=0.1, wspace=0.1)

        # save to file #
        return TaskAnalyzer.saveImage(logFile, "sysmem_chart", outFile=outFile)

    def drawProcChart(self, data, logFile, outFile=None):
        # pylint: disable=undefined-variable

        if not data:
            return

        def _make_autopct(values):
            def _autopct(pct):
                total = sum(values)
                val = long(round(pct * total / 100.0)) << 20
                val = UtilMgr.convSize2Unit(val, True)
                usage = "{v:s} ({p:.0f}%)".format(p=pct, v=val)
                line = "=" * 7
                string = "{s:1}\n{l:1}{d:1}".format(
                    s=usage, d=self.details[self.tmpCnt], l=line
                )
                self.tmpCnt += 1
                return string

            return _autopct

        SysMgr.printStat(r"start drawing charts...")

        # init variables #
        seq = 0
        self.tmpCnt = 0

        height = (
            long(len(data) / 2)
            if len(data) & 1 != 0
            else long(len(data) / 2 + 1)
        )

        colors = [
            "pink",
            "lightgreen",
            "skyblue",
            "lightcoral",
            "gold",
            "yellowgreen",
        ]

        propList = {
            "count": 0,
            "vmem": 1,
            "rss": 2,
            "pss": 3,
            "swap": 4,
            "huge": 5,
            "locked": 6,
            "pdirty": 7,
            "sdirty": 8,
        }

        convSize = UtilMgr.convSize2Unit

        # set title #
        suptitle("Guider Process Memory Chart", fontsize=8)

        for idx, item in sorted(
            data.items(),
            key=lambda e: e[1]["[TOTAL]"][propList["rss"]]
            + e[1]["[TOTAL]"][propList["swap"]],
            reverse=True,
        ):
            labels = []
            sizes = []
            explode = []
            self.details = []
            self.tmpCnt = 0
            tprop = item["[TOTAL]"]

            if tprop[propList["count"]] == 0:
                continue

            for prop, value in item.items():
                if (
                    prop == "[TOTAL]"
                    or value[propList["rss"]] == 0 == value[propList["swap"]]
                ):
                    continue

                # add label of property and its property count #
                labels.append("%s(%s)" % (prop, value[propList["count"]]))

                sizes.append(value[propList["rss"]] + value[propList["swap"]])

                # set private dirty size #
                pdrt = convSize(value[propList["pdirty"]] << 10, True)

                # set shared dirty size #
                sdrt = convSize(value[propList["sdirty"]] << 10, True)

                # set rss size #
                rss = convSize(value[propList["rss"]] << 20, True)

                # set swap size #
                swap = convSize(value[propList["swap"]] << 20, True)

                # set locked size #
                locked = convSize(value[propList["locked"]] << 10, True)

                self.details.append(
                    (
                        "\n- RSS  : %5s \n- SWAP : %5s \n%s\n"
                        "- LOCK : %5s \n- PDRT : %5s \n- SDRT : %5s"
                    )
                    % (rss, swap, "=" * 7, locked, pdrt, sdrt)
                )

            # convert labels to tuple #
            labels = tuple(labels)

            # find and mark index of max value #
            explode = [0] * len(sizes)
            explode[sizes.index(max(sizes))] = 0.03

            # set size and position of this chart #
            try:
                ypos = seq >> 1
                xpos = seq - (ypos << 1)
                ax = subplot2grid(
                    (height, 2), (ypos, xpos), rowspan=1, colspan=1
                )
                ax.xaxis.set_major_locator(MaxNLocator(integer=True))
            except SystemExit:
                sys.exit(0)
            except:
                continue

            # get property of process #
            line = "_" * len(idx) * 1

            rss = tprop[propList["rss"]]
            swap = tprop[propList["swap"]]
            total = convSize((rss + swap) << 20)

            rss = convSize(rss << 20)
            swap = convSize(swap << 20)
            vmem = convSize(tprop[propList["vmem"]] << 20)
            pss = convSize(tprop[propList["pss"]] << 20)
            lock = convSize(tprop[propList["locked"]] << 10)
            dirty = tprop[propList["pdirty"]] + tprop[propList["sdirty"]]
            dirty = convSize(dirty << 10)

            totalList = [
                (
                    "\n%s\n%s\n\n- %-5s: %7s\n- %-5s: %7s\n- %-5s: %7s\n%s\n\n"
                    "- %-5s: %7s\n- %-5s: %7s\n- %-5s: %7s\n- %-5s: %7s"
                )
                % (
                    "[%s] %s" % (str(seq + 1), idx),
                    line,
                    "TOTAL",
                    total,
                    "RSS",
                    rss,
                    "SWAP",
                    swap,
                    line,
                    "VSS",
                    vmem,
                    "PSS",
                    pss,
                    "LOCK",
                    lock,
                    "DIRTY",
                    dirty,
                )
            ]

            # draw chart #
            patches, texts, autotexts = pie(
                sizes,
                explode=explode,
                labels=labels,
                colors=colors,
                autopct=_make_autopct(sizes),
                shadow=True,
                startangle=50 if SysMgr.matplotlibVersion >= 1.2 else None,
                pctdistance=0.5,
            )

            # set font size #
            for idx, val in enumerate(texts):
                val.set_fontsize(self.xfsize + 1)
                autotexts[idx].set_fontsize(self.lfsize)
            axis("equal")

            # print total size in legend #
            legend(
                patches,
                totalList,
                loc="lower right",
                shadow=True,
                fontsize=self.xfsize
                if SysMgr.matplotlibVersion >= 1.2
                else None,
                handlelength=0,
                bbox_to_anchor=(1.2, 0.01),
            )

            seq += 1

        # draw image #
        figure(
            num=1, figsize=(10, 10), facecolor="b", edgecolor="k"
        ).subplots_adjust(left=0, top=0.9, bottom=0.02, hspace=0.1, wspace=0.1)

        # save to file #
        return TaskAnalyzer.saveImage(
            logFile, "procmem_chart", outFile=outFile
        )

    def drawLayout(
        self,
        graphStats,
        _drawCpu=None,
        _drawMem=None,
        _drawIo=None,
        _drawPrio=None,
        _drawEvent=None,
        logEvents=None,
    ):
        pos = 0
        total = 0
        layoutDict = {}
        layoutList = []
        layout = SysMgr.layout.split(",")

        # sum size of graph boxes #
        for idx, graph in enumerate(layout):
            try:
                if len(graph.split(":")) == 1:
                    target = graph
                    size = 1
                else:
                    (target, size) = graph.split(":")

                # check duplicated graph #
                try:
                    layoutDict[target]
                    SysMgr.printErr(
                        "failed to draw graph "
                        "because %s graph is duplicated" % target
                    )
                    sys.exit(-1)
                except SystemExit:
                    sys.exit(0)
                except:
                    layoutDict[target] = True

                size = long(size)
                if size <= 0:
                    raise Exception("wrong size")

                total += size
                layoutList.append([target, long(size)])
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr(
                    "failed to draw graph "
                    "because graph format [TYPE:SIZE] is wrong"
                )
                sys.exit(-1)

        for idx, item in enumerate(layoutList):
            target = item[0].strip()
            size = item[1]

            # convert size to proportion #
            size = long((size / float(total)) * 6)

            try:
                xtype = len(layoutList) - idx

                targetc = target.upper()

                if targetc == "CPU" or targetc.startswith("C"):
                    _drawCpu(graphStats, xtype, pos, size)
                elif targetc == "DELAY" or targetc.startswith("D"):
                    _drawCpu(graphStats, xtype, pos, size, delay=True)
                elif targetc == "MEM" or targetc.startswith("M"):
                    _drawMem(graphStats, xtype, pos, size)
                elif targetc == "VSS" or targetc.startswith("V"):
                    SysMgr.vssEnable = True
                    _drawMem(graphStats, xtype, pos, size)
                elif targetc == "RSS" or targetc.startswith("R"):
                    SysMgr.rssEnable = True
                    _drawMem(graphStats, xtype, pos, size)
                elif targetc == "IO" or targetc.startswith("I"):
                    if _drawIo:
                        _drawIo(graphStats, xtype, pos, size)
                elif targetc == "PRIO" or targetc.startswith("P"):
                    _drawPrio(graphStats, xtype, pos, size)
                else:
                    SysMgr.printErr(
                        "failed to draw graph "
                        "because '%s' is not recognized" % target
                    )
                    sys.exit(-1)

                if _drawEvent and idx == 0:
                    # draw events on graphs #
                    _drawEvent(graphStats, logEvents)

                pos += size
            except SystemExit:
                sys.exit(0)
            except:
                err = SysMgr.getErrMsg()
                raise Exception(err)

    def drawBottom(self, xtype, ax, timeline, nrTask, effectProcList):
        # pylint: disable=undefined-variable

        if xtype == 1:
            # convert tick type to integer #
            try:
                xtickLabel = ax.get_xticks().tolist()
                xtickLabel = list(map(long, xtickLabel))

                # apply relative time #
                if "RELTIME" in SysMgr.environList:
                    try:
                        start = xtickLabel[0]
                        xtickLabel = [val - start for val in xtickLabel]
                    except:
                        pass

                # apply time format #
                if not "NOTIMEFORMAT" in SysMgr.environList:
                    try:
                        xtickLabel = list(map(UtilMgr.convTime, xtickLabel))
                    except:
                        pass

                for idx, item in enumerate(list(xtickLabel)):
                    if idx & 1:
                        xtickLabel[idx] = "\n%s" % item

                xname = "\nTIME(Sec)"
            except SystemExit:
                sys.exit(0)
            except:
                pass
        elif xtype == 3:
            # draw the number of running tasks #
            try:
                xtickLabel = ax.get_xticks().tolist()
                xlim([xtickLabel[0], xtickLabel[-1]])
                if sum(effectProcList) == 0:
                    for seq, cnt in enumerate(xtickLabel):
                        xtickLabel[seq] = "?"
                else:
                    for seq, cnt in enumerate(xtickLabel):
                        try:
                            xtickLabel[seq] = UtilMgr.convNum(
                                effectProcList[timeline.index(long(cnt))]
                            )
                        except SystemExit:
                            sys.exit(0)
                        except:
                            xtickLabel[seq] = " "

                xname = "\nRUN(NR)"
            except SystemExit:
                sys.exit(0)
            except:
                pass
        elif xtype == 2:
            # draw the number of total tasks (proc/thread) #
            try:
                xtickLabel = ax.get_xticks().tolist()
                xlim([xtickLabel[0], xtickLabel[-1]])
                for seq, cnt in enumerate(xtickLabel):
                    try:
                        val = nrTask[timeline.index(long(cnt))]
                        if seq & 1:
                            xtickLabel[seq] = "\n%s" % val
                        else:
                            xtickLabel[seq] = val
                    except SystemExit:
                        sys.exit(0)
                    except:
                        xtickLabel[seq] = " "

                xname = "\nTASK(NR)"
            except SystemExit:
                sys.exit(0)
            except:
                pass
        else:
            return

        # apply xtick labels #
        try:
            ax.set_xticks(ax.get_xticks())
            ax.set_xticklabels(xtickLabel)
            lastIdx = len(ax.get_xticks().tolist())
            xtickLabel[lastIdx - 1] += xname.expandtabs()
            xtickLabel[lastIdx - 1] = xtickLabel[lastIdx - 1].lstrip("\n")
            ax.set_xticklabels(xtickLabel)
        except SystemExit:
            sys.exit(0)
        except:
            pass

    def drawUserEvent(self, mode=None):
        # pylint: disable=undefined-variable

        if not "EVENT" in SysMgr.environList:
            return

        for item in SysMgr.environList["EVENT"]:
            try:
                values = item.split(":")
                if not (3 <= len(values) <= 5):
                    SysMgr.printErr(
                        (
                            "failed to recognize %s in "
                            "X:Y:NAME:[CPU|MEM|IO]:[BOX|CIRCLE|LARROW|RARROW]"
                            "format"
                        )
                        % item
                    )
                    sys.exit(-1)

                # get values #
                if len(values) == 5:
                    x, y, name, resource, feature = values
                elif len(values) == 4:
                    x, y, name, resource = values
                    feature = "none"
                elif len(values) == 3:
                    x, y, name = values
                    feature = "none"
                    resource = "none"

                # check type #
                if mode and mode.upper() != resource.upper():
                    continue

                # get feature #
                if feature.upper() == "CIRCLE":
                    feature = "Circle,pad=1"
                elif feature.upper() == "LARROW":
                    feature = "LArrow,pad=1"
                elif feature.upper() == "RARROW":
                    feature = "RArrow,pad=1"
                else:
                    feature = "Roundtooth,pad=1"

                # draw text #
                text(
                    long(x),
                    long(y),
                    name,
                    style="italic",
                    fontsize=self.lfsize * 2,
                    color="green",
                    fontweight="bold",
                    bbox=dict(boxstyle=feature, facecolor="gold", alpha=0.7),
                )
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr("failed to draw events", reason=True)
                sys.exit(-1)

    def drawBoundary(self, gtype, labelList):
        # pylint: disable=undefined-variable

        if not SysMgr.boundaryLine:
            return

        try:
            boundaryList = list(
                map(UtilMgr.convUnit2Size, SysMgr.boundaryLine)
            )
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to set boundary line", True)
            sys.exit(-1)

        # draw boundary graph #
        for boundary in boundaryList:
            if gtype == "io":
                bl = boundary >> 10
            elif gtype == "mem":
                bl = boundary >> 20
            else:
                bl = boundary

            try:
                axhline(y=bl, linewidth=1, linestyle="--", color="black")

                labelList.append(
                    "[ Boundary %s ]" % UtilMgr.convSize2Unit(boundary)
                )
            except SystemExit:
                sys.exit(0)
            except:
                continue

    def drawGraph(
        self, graphStats, logFile, outFile=None, outFd=None, logEvents=[]
    ):
        # pylint: disable=undefined-variable

        # ==================== DEFINE PART ====================#
        def _getTextAlign(idx, timeline):
            if idx < len(timeline) / 4:
                return "left"
            elif idx > len(timeline) / 4 * 3:
                return "right"
            else:
                return "center"

        def _getYrange(ax):
            yrange = ax.get_yticks().tolist()
            return yrange[0], yrange[-1]

        def _drawStack(
            timeline, stackedStats, stackedTexts, self, ax, acc=True
        ):
            if not stackedStats:
                return

            acc = acc and "ACCUMULATE" in SysMgr.environList
            hasLabel = not "NOLABEL" in SysMgr.environList

            # change stats to be accumulated #
            origStackedStats = deepcopy(stackedStats)
            accStackedStats = deepcopy(stackedStats)
            for stats in (accStackedStats, stackedStats if acc else []):
                for n in range(len(stats)):
                    if not stats[n]:
                        continue

                    for m in range(len(stats[n])):
                        if m == 0:
                            continue
                        stats[n][m] += stats[n][m - 1]

            # draw base #
            figObj = TaskAnalyzer.drawFigure()
            renderer = figObj.canvas.get_renderer()
            wbox = ax.get_window_extent(renderer=renderer)
            totalHeight = float(
                sum([(sets[-1] if sets else 0) for sets in accStackedStats])
            )
            boxHeight = wbox.height / 10

            # draw stackplot #
            stackplot(timeline, *stackedStats)

            # draw texts #
            xposList = {}
            for i, s in enumerate(stackedStats):
                # get x index #
                xpos = max(s)
                if xpos == 0:
                    continue
                elif acc:
                    try:
                        xpos = long(len(s) * 0.7)
                    except SystemExit:
                        sys.exit(0)
                    except:
                        xpos = len(s) - 1
                elif xpos > 10:
                    xpos = len(s) - s[::-1].index(xpos) - 1
                else:
                    xpos = s.index(xpos)

                # update x pos #
                xpos = xpos - 1 if xpos == len(s) - 1 else xpos

                # get y pos #
                ypos = sum([sets[xpos] for sets in stackedStats[:i]])
                ypos += s[xpos] * 0.5

                # get x pos index #
                xpos = timeline[xpos]

                # get name #
                xname = stackedTexts[i]

                # add xpos count #
                if not xpos in xposList:
                    xposList[xpos] = []
                xposList[xpos].append(xname)
                newline = "" + ("\n" * (len(xposList[xpos]) - 1))

                if not hasLabel:
                    continue

                # resize text #
                fontsize = sum(origStackedStats[i]) / totalHeight * boxHeight

                # draw text #
                tobj = text(
                    xpos,
                    ypos,
                    newline + "<- " + xname,
                    fontsize=fontsize
                    if fontsize < self.lfsize
                    else self.lfsize,
                    fontweight="normal",
                )

        def _drawEvent(graphStats, logEvents):
            # get minimum timeline #
            timeline = None
            for key, val in graphStats.items():
                if not key.endswith("timeline"):
                    continue
                elif not timeline:
                    timeline = val
                elif len(timeline) > len(val):
                    timeline = val
            lent = len(timeline)

            # get first and last uptime #
            try:
                firstEvt = logEvents[0]["seconds"]
                lastEvt = logEvents[-1]["seconds"]
            except:
                firstEvt = lastEvt = -1

            # get exclude event list #
            exEventList = SysMgr.environList.get("EXEVENT", None)

            # get include event list #
            inEventList = SysMgr.environList.get("INEVENT", None)

            # get print event time flag #
            incTime = not "NOEVTTIME" in SysMgr.environList

            # define a function for merging log events #
            def _addLogEvents(
                epochTimes, eventList, logEvents, epochDelta, logEventInfo
            ):
                for item in logEvents:
                    try:
                        name = item["name"]
                        eventTime = item["seconds"]

                        # get index #
                        idx = UtilMgr.bisect_left(epochTimes, eventTime) - 1
                        if idx < 0 or eventTime > epochTimes[-1]:
                            continue

                        # get interval and upate last #
                        if "LAST" in logEventInfo:
                            fromPrevEvt = eventTime - logEventInfo["LAST"]
                            if name in logEventInfo:
                                fromSameEvt = eventTime - logEventInfo[name]
                            else:
                                fromSameEvt = 0
                        else:
                            fromPrevEvt = eventTime - epochTimes[0]
                            fromSameEvt = 0

                        # update last info #
                        logEventInfo[name] = eventTime
                        logEventInfo["LAST"] = eventTime

                        # add to event list #
                        eventList[idx].append(
                            "%s [%f / +%.2fs|+%.2fs]"
                            % (
                                name,
                                eventTime - epochDelta,
                                fromSameEvt,
                                fromPrevEvt,
                            )
                        )
                    except SystemExit:
                        sys.exit(0)
                    except:
                        SysMgr.printErr("failed to merge log events", True)

            # get xticks interval #
            try:
                xtickLabel = gca().get_xticks().tolist()
                xint = xtickLabel[1] - xtickLabel[0]
            except:
                xint = 1

            # start loop #
            for key, val in graphStats.items():
                if not key.endswith("timeline"):
                    continue

                # get filename #
                res = key.rsplit(":", 1)
                if len(res) > 1:
                    fname = "%s:" % res[0]
                    prefix = "[%s] " % res[0]
                else:
                    fname = ""
                    prefix = ""

                # get event table #
                eventList = graphStats["%seventList" % fname][:lent]

                # define log event info table #
                logEventInfo = {}

                # get epochDelta info #
                try:
                    epochDelta = float(
                        graphStats["%ssysinfo" % fname]["StartTimeEpoch"]
                    )
                except:
                    epochDelta = 0

                # add log events to event list #
                if firstEvt != -1:
                    epochTimes = [epochDelta + i for i in val]
                    if epochTimes[-1] < firstEvt or lastEvt < epochTimes[0]:
                        pass
                    else:
                        _addLogEvents(
                            epochTimes,
                            eventList,
                            logEvents,
                            epochDelta,
                            logEventInfo,
                        )

                accHeight = 0
                lastEventTime = 0

                # draw user events #
                for tm, evts in enumerate(eventList):
                    if not evts:
                        continue

                    # filter inclusive events #
                    if inEventList:
                        newEvts = []
                        for evt in evts:
                            if not UtilMgr.isValidStr(evt, inEventList):
                                continue
                            newEvts.append(evt)
                        evts = newEvts

                    # filter exclusive events #
                    if exEventList:
                        newEvts = []
                        for evt in evts:
                            if UtilMgr.isValidStr(evt, exEventList):
                                continue
                            newEvts.append(evt)
                        evts = newEvts

                    # accumuate heights in an interval #
                    axHeight = (
                        gca()
                        .get_window_extent()
                        .transformed(self.figure.dpi_scale_trans.inverted())
                        .height
                    )
                    maxHeight = ylim()[-1]

                    for seq, evt in enumerate(evts):
                        evtbox = prefix + evt

                        # get x position #
                        try:
                            evtname, remains = evtbox.rsplit("[", 1)
                            uptime, remains = remains.split("/", 1)
                            xpos = float(uptime.strip())
                            if incTime:
                                evtbox = "%s [%s" % (
                                    evtname.rstrip(),
                                    remains.strip(),
                                )
                            else:
                                evtbox = evtname.strip()
                        except SystemExit:
                            sys.exit(0)
                        except:
                            xpos = timeline[tm]

                        # update height for new interval #
                        if not lastEventTime or xpos - lastEventTime >= xint:
                            accHeight = 0
                            lastEventTime = xpos

                        # draw a event box #
                        try:
                            ypos = maxHeight - (
                                maxHeight * accHeight / axHeight
                            )

                            ret = text(
                                xpos,
                                ypos,
                                evtbox,
                                fontsize=self.lfsize,
                                verticalalignment="top",
                                style="italic",
                                bbox={
                                    "facecolor": "green",
                                    "alpha": 0.5,
                                    "pad": 1,
                                },
                                ha="left",
                            )

                            accHeight += (
                                ret.get_window_extent(
                                    self.figure.canvas.get_renderer()
                                )
                                .transformed(
                                    self.figure.dpi_scale_trans.inverted()
                                )
                                .height
                                * 2
                            )

                            axvline(
                                x=xpos,
                                linewidth=1,
                                linestyle="--",
                                color="green",
                            )
                        except SystemExit:
                            sys.exit(0)
                        except:
                            pass

        def _getPathEffect(lw=2):
            try:
                pe = SysMgr.getPkg("matplotlib.patheffects", False)
                return [
                    pe.Stroke(linewidth=lw, foreground="black"),
                    pe.Normal(),
                ]
            except SystemExit:
                sys.exit(0)
            except:
                return []

        def _getPlotAttr(idx):
            try:
                lw = 0.7
                pe = []

                if not "STROKE" in SysMgr.environList:
                    pass
                elif UtilMgr.isValidStr(idx, SysMgr.environList["STROKE"]):
                    lw = 1
                    pe = _getPathEffect()
            except:
                pass

            return lw, pe

        def _drawAct(activating, activated, deactivated, y, name, fsize):
            if activating == 0:
                return

            # update times #
            if activated == 0:
                activated = activating

            elist = (
                [activating, activated, deactivated]
                if deactivated
                else [activating, activated]
            )
            lolims = [False] * len(elist)
            lolims[1] = True
            uplims = [False] * len(elist)
            if len(uplims) == 3:
                uplims[2] = True

            # draw error bar #
            errorbar(
                elist,
                [y] * len(elist),
                yerr=1,
                color="black",
                ecolor="black",
                elinewidth=0.3,
                linewidth=0.5,
                capsize=0.4,
                capthick=0.2,
                lolims=lolims,
                uplims=uplims,
            )

            # check highlight unit #
            unitHigh = SysMgr.environList.get("HIGHLIGHTUNIT")
            if unitHigh and UtilMgr.isValidStr(name, unitHigh):
                color = "red"
            else:
                color = "orange"

            # draw name text #
            text(
                activating,
                y,
                name + "(%s) " % UtilMgr.convNum(activating, True),
                fontsize=fsize,
                verticalalignment="top",
                style="italic",
                bbox={
                    "boxstyle": "round",
                    "facecolor": color,
                    "edgecolor": "none",
                    "alpha": 0.3,
                    "pad": 0.3,
                },
                ha="right",
                va="center",
            )

            # draw activated text #
            if activated:
                text(
                    activated,
                    y,
                    "\n~%s" % UtilMgr.convNum(activated - activating, True),
                    fontsize=fsize,
                    verticalalignment="top",
                    ha="center",
                    va="center",
                )

            # draw deactivated text #
            if deactivated:
                text(
                    deactivated,
                    y,
                    "~%s\n" % UtilMgr.convNum(deactivated - activating, True),
                    color="red",
                    fontsize=fsize,
                    verticalalignment="top",
                    ha="right",
                    va="center",
                )

        def _drawUnitEvent(graphStats, timeline, ax):
            # check skip option #
            if "NOUNITEVT" in SysMgr.environList:
                return

            # get values #
            minPos = timeline[0]
            maxPos = timeline[-1]
            nrVpart = UtilMgr.getEnvironNum("NRVPART", False, 5, False, True)
            nrPart = long(len(timeline) / nrVpart)
            if nrPart == 0:
                nrPart = 1
            ntimeline = [timeline[n] for n in range(0, len(timeline), nrPart)]
            posList = [[] for _ in range(len(ntimeline))]
            ymin, ymax = _getYrange(ax)
            eventFilter = SysMgr.environList.get("UNITFILTER", [])
            eventExFilter = SysMgr.environList.get("UNITEXFILTER", [])

            # get font height #
            fsize = self.lfsize / 2
            renderer = self.figure.canvas.get_renderer()
            wh = ax.get_window_extent(renderer=renderer).height
            fh = (
                text(0, 0, " ", fontsize=fsize)
                .get_window_extent(renderer=renderer)
                .height
            )
            h = ((ymax - ymin) / (wh / fh)) * 1.5

            # per file #
            for n, v in graphStats.items():
                if not n.endswith("systemdInfo"):
                    continue

                prefix = UtilMgr.rstrip(n, "systemdInfo")

                # per daemon #
                for dn, dv in v.items():
                    # per unit #
                    for unit, val in sorted(
                        dv.items(),
                        key=lambda k: long(k[1]["Boot"]["activating"])
                        if "Boot" in k[1]
                        else 0,
                    ):
                        try:
                            if not val["Boot"]["activating"]:
                                continue
                        except SystemExit:
                            sys.exit(0)
                        except:
                            continue

                        # check filter #
                        if unit.endswith(".target"):
                            continue
                        elif eventFilter and not UtilMgr.isValidStr(
                            unit, eventFilter
                        ):
                            continue
                        elif eventExFilter and UtilMgr.isValidStr(
                            unit, eventExFilter
                        ):
                            continue

                        # get x pos #
                        boot = val["Boot"]
                        x = long(boot["activating"]) / 1000000.0
                        if not minPos <= x <= maxPos:
                            continue

                        # get y pos #
                        posIdx = UtilMgr.bisect_left(ntimeline, x)
                        targetPos = posList[posIdx]
                        targetPos.append(unit)
                        height = len(targetPos) * h

                        unit = prefix + unit

                        # draw event #
                        _drawAct(
                            x,
                            long(boot["activated"]) / 1000000.0,
                            long(boot["deactivated"]) / 1000000.0,
                            ymax - height,
                            unit,
                            fsize,
                        )

        def _drawTargetEvent(graphStats, timeline, ax):
            # check skip option #
            if "NOTARGETEVT" in SysMgr.environList:
                return

            # get values #
            minPos = timeline[0]
            maxPos = timeline[-1]
            nrVpart = UtilMgr.getEnvironNum("NRVPART", False, 5, False, True)
            nrPart = long(len(timeline) / nrVpart)
            if nrPart == 0:
                nrPart = 1
            ntimeline = [timeline[n] for n in range(0, len(timeline), nrPart)]
            posList = [[] for _ in range(len(ntimeline))]
            ymin, ymax = _getYrange(ax)
            eventFilter = SysMgr.environList.get("TARGETFILTER", [])
            eventExFilter = SysMgr.environList.get("TARGETEXFILTER", [])

            # per file #
            for n, v in graphStats.items():
                if not n.endswith("systemdInfo"):
                    continue

                prefix = UtilMgr.rstrip(n, "systemdInfo")

                # per daemon #
                for dn, dv in v.items():
                    # get boot info #
                    bootInfo = dv.get("systemd", {})
                    earlyStats = ""
                    for bn, bs in sorted(
                        bootInfo.items(), key=lambda k: long(k[1])
                    ):
                        bs = long(bs) / 1000000.0
                        if not bs:
                            continue
                        if long(bs) <= minPos:
                            targetPos = posList[0]
                            targetPos.append(bn)
                            earlyStats += "\n" if earlyStats else ""
                            earlyStats += "%s: %s" % (
                                bn,
                                UtilMgr.convNum(bs, True),
                            )

                    # draw early stats #
                    if earlyStats:
                        text(
                            minPos,
                            ymax,
                            earlyStats,
                            fontsize=self.lfsize / 2,
                            verticalalignment="top",
                            color="black",
                            style="italic",
                            bbox={
                                "boxstyle": "round",
                                "facecolor": "red",
                                "edgecolor": "none",
                                "alpha": 0.3,
                                "pad": 0.3,
                            },
                            ha="left",
                            va="bottom",
                        )

                    # per unit #
                    for unit, val in sorted(
                        dv.items(),
                        key=lambda k: long(k[1]["Boot"]["activating"])
                        if "Boot" in k[1]
                        else 0,
                        reverse=True,
                    ):
                        try:
                            if not val["Boot"]["activating"]:
                                raise Exception()
                        except SystemExit:
                            sys.exit(0)
                        except:
                            break

                        # check filter #
                        if not unit.endswith(".target"):
                            continue
                        elif eventFilter and not UtilMgr.isValidStr(
                            unit, eventFilter
                        ):
                            continue
                        elif eventExFilter and UtilMgr.isValidStr(
                            unit, eventExFilter
                        ):
                            continue

                        # get x pos #
                        x = long(val["Boot"]["activating"]) / 1000000.0
                        if not minPos <= x <= maxPos:
                            continue

                        # draw line #
                        axvline(
                            x,
                            ymin,
                            ymax,
                            color="purple",
                            linewidth=0.5,
                            linestyle="--",
                        )

                        unit = prefix + unit

                        # draw text #
                        posIdx = UtilMgr.bisect_left(ntimeline, x)
                        targetPos = posList[posIdx]
                        targetPos.append(unit)
                        hstr = "\n\n " * len(targetPos)
                        text(
                            x,
                            ymax,
                            hstr + unit,
                            fontsize=self.lfsize,
                            verticalalignment="top",
                            color="purple",
                            style="italic",
                            ha="left",
                            va="center",
                        )

        def _drawCpu(graphStats, xtype, pos, size, delay=False):
            # draw title #
            ax = subplot2grid((6, 1), (pos, 0), rowspan=size, colspan=1)
            ax.xaxis.set_major_locator(MaxNLocator(integer=True))
            suptitle(graphStats["graphTitle"], fontsize=8)

            # define common label list #
            ymax = 0
            labelList = []

            # get minimum timeline #
            timeline = None
            for key, val in graphStats.items():
                if not key.endswith("timeline"):
                    continue
                elif not timeline:
                    timeline = val
                elif len(timeline) > len(val):
                    timeline = val
            lent = len(timeline)

            conv = UtilMgr.convNum
            hasLabel = not "NOLABEL" in SysMgr.environList

            # get stacked plot option #
            stacked = "STACKEDPLOT" in SysMgr.environList
            stackedStats = []
            stackedTexts = []

            # start loop #
            cnt = 0
            for key, val in graphStats.items():
                if not key.endswith("timeline"):
                    continue

                res = key.rsplit(":", 1)
                if len(res) > 1:
                    fname = "%s:" % res[0]
                    prefix = "[%s] " % res[0]
                else:
                    fname = ""
                    prefix = ""

                cpuUsage = graphStats["%scpuUsage" % fname][:lent]
                cpuProcUsage = graphStats["%scpuProcUsage" % fname]
                cpuProcDelay = graphStats["%scpuProcDelay" % fname]
                blkWait = graphStats["%sblkWait" % fname][:lent]
                blkProcUsage = graphStats["%sblkProcUsage" % fname]
                gpuUsage = graphStats["%sgpuUsage" % fname]
                nrCore = graphStats["%snrCore" % fname]
                if nrCore:
                    maxCore = max(nrCore)
                else:
                    maxCore = 1
                coreStr = ""

                # convert total CPU usage by core number #
                if not SysMgr.cpuAvgEnable:
                    cpuUsage = [maxCore * i for i in cpuUsage]

                totalLabel = "TOTALLABEL" in SysMgr.environList

                # set visible total usage flag #
                if SysMgr.showAll or not SysMgr.filterGroup:
                    isVisibleTotal = True
                else:
                    isVisibleTotal = False

                # add boundary line #
                self.drawBoundary("cpu", labelList)

                # draw user event #
                self.drawUserEvent("cpu")

                # System Processor usage #
                if not delay and isVisibleTotal and SysMgr.gpuEnable:
                    # ------------------ Total GPU usage ------------------ #
                    for gpu, stat in gpuUsage.items():
                        # get stats #
                        stat = list(map(long, stat.split()))[:lent]

                        # accumulate stats #
                        if "ACCUMULATE" in SysMgr.environList:
                            for n in range(len(stat)):
                                if n == 0:
                                    continue
                                stat[n] += stat[n - 1]

                        try:
                            if min(stat) == max(stat):
                                continue
                        except:
                            pass

                        # set color #
                        if prefix:
                            gcolor = None
                        else:
                            gcolor = "olive"

                        # draw total GPU graph #
                        plot(
                            timeline,
                            stat,
                            c=gcolor,
                            linestyle="-",
                            linewidth=0.3,
                            marker=".",
                            markersize=0.3,
                            path_effects=_getPathEffect(),
                            solid_capstyle="round",
                        )

                        totalUsage = sum(stat)
                        try:
                            avgUsage = round(totalUsage / len(stat), 1)
                        except:
                            avgUsage = 0

                        maxUsage = max(stat)
                        maxIdx = stat.index(maxUsage)

                        if not totalLabel:
                            labelStat = conv(avgUsage, True)
                        else:
                            labelStat = conv(totalUsage)

                        labelList.append(
                            "%s%s[ %s ] - %s%%"
                            % (coreStr, prefix, gpu, labelStat)
                        )
                        coreStr = ""

                        for idx in (
                            idx
                            for idx, usage in enumerate(stat)
                            if usage == maxUsage
                        ):
                            if idx != 0 and stat[idx] == stat[idx - 1]:
                                continue
                            text(
                                timeline[idx],
                                stat[maxIdx],
                                "%s Max_%d%% | Avg_%d%% | Total_%s%%"
                                % (
                                    prefix,
                                    maxUsage,
                                    avgUsage,
                                    conv(totalUsage),
                                ),
                                fontsize=self.lfsize + 1,
                                color="olive",
                                fontweight="bold",
                                bbox=dict(
                                    boxstyle="round",
                                    facecolor="wheat",
                                    alpha=0.3,
                                ),
                                ha=_getTextAlign(idx, timeline),
                            )
                            break

                    # ------------------ Total CPU usage ------------------ #
                    if (
                        not "NOLOADPLOT" in SysMgr.environList
                        and sum(blkWait) > 0
                    ):
                        # get stats #
                        for idx, item in enumerate(blkWait):
                            blkWait[idx] += cpuUsage[idx]

                    for targetUsage, color, name in (
                        (blkWait, "pink", "CPU(%s)+IOWAIT" % conv(maxCore)),
                        (cpuUsage, "red", "CPU(%s)" % conv(maxCore)),
                    ):
                        # accumulate stats #
                        origTargetUsage = targetUsage[:]
                        if "ACCUMULATE" in SysMgr.environList:
                            for n in range(len(targetUsage)):
                                if n == 0:
                                    continue
                                targetUsage[n] += targetUsage[n - 1]

                        # set color #
                        if prefix:
                            color = None

                        # draw total CPU + IOWAIT graph #
                        plot(
                            timeline,
                            targetUsage,
                            c=color,
                            linestyle="-",
                            linewidth=1,
                            marker=".",
                            markersize=1,
                            path_effects=_getPathEffect(),
                            solid_capstyle="round",
                        )

                        # draw text #
                        maxUsage = max(targetUsage)
                        maxIdx = targetUsage.index(maxUsage)

                        # update the maximum ytick #
                        if ymax < maxUsage:
                            ymax = maxUsage

                        # get average #
                        totalUsage = sum(origTargetUsage)
                        try:
                            avgUsage = round(
                                totalUsage / len(origTargetUsage), 1
                            )
                        except:
                            avgUsage = 0

                        # draw text #
                        for idx in (
                            idx
                            for idx, usage in enumerate(targetUsage)
                            if usage == maxUsage
                        ):
                            if (
                                idx != 0
                                and targetUsage[idx] == targetUsage[idx - 1]
                            ):
                                continue
                            text(
                                timeline[idx],
                                targetUsage[maxIdx],
                                "%s Max_%d%% | Avg_%.1f%% | Total_%s%%"
                                % (
                                    prefix,
                                    maxUsage,
                                    avgUsage,
                                    conv(totalUsage),
                                ),
                                fontsize=self.lfsize + 1,
                                color=color,
                                fontweight="bold",
                                bbox=dict(
                                    boxstyle="round",
                                    facecolor="wheat",
                                    alpha=0.3,
                                ),
                                ha=_getTextAlign(idx, timeline),
                            )
                            break

                        # add label #
                        if totalLabel:
                            labelStat = conv(totalUsage)
                            statName = "Total"
                        else:
                            labelStat = conv(avgUsage, True)
                            statName = "Average"

                        labelList.append(
                            "%s%s[ %s %s ] - %s%%"
                            % (coreStr, prefix, name, statName, labelStat)
                        )

                        coreStr = ""

                # ------------------- Process CPU usage ------------------- #
                # total Process CPU usage filtered #
                if (
                    not delay
                    and SysMgr.filterGroup
                    and "[ TOTAL ]" in cpuProcUsage
                    and cpuProcUsage["[ TOTAL ]"]["count"] > 1
                    and not "NOTOTAL" in SysMgr.environList
                ):
                    totalUsage = cpuProcUsage["[ TOTAL ]"]["usage"].split()
                    totalUsage = list(map(long, totalUsage))[:lent]

                    # draw total graph #
                    plot(
                        timeline,
                        totalUsage,
                        "-",
                        c="green",
                        linestyle="-.",
                        linewidth=1,
                        marker=".",
                        markersize=1,
                        path_effects=_getPathEffect(),
                        solid_capstyle="round",
                    )

                    totalSumUsage = sum(totalUsage)
                    try:
                        avgUsage = round(totalSumUsage / len(totalUsage), 1)
                    except:
                        avgUsage = 0

                    if not totalLabel:
                        labelStat = conv(avgUsage, True)
                    else:
                        labelStat = conv(totalSumUsage)

                    maxUsage = max(totalUsage)
                    maxIdx = totalUsage.index(maxUsage)

                    labelList.append(
                        "%s[ TOTAL ] - %s%%" % (prefix, labelStat)
                    )

                    # update the maximum ytick #
                    if ymax < maxUsage:
                        ymax = maxUsage

                    for idx in (
                        idx
                        for idx, usage in enumerate(totalUsage)
                        if usage == maxUsage
                    ):
                        if idx != 0 and totalUsage[idx] == totalUsage[idx - 1]:
                            continue

                        if not hasLabel:
                            break

                        text(
                            timeline[idx],
                            totalUsage[maxIdx],
                            "%s Max_%d%%|Avg_%.1f%%|Total_%s%%"
                            % (
                                prefix,
                                maxUsage,
                                avgUsage,
                                conv(totalSumUsage),
                            ),
                            fontsize=self.lfsize + 1,
                            color="green",
                            fontweight="bold",
                            bbox=dict(
                                boxstyle="round", facecolor="wheat", alpha=0.3
                            ),
                            ha=_getTextAlign(idx, timeline),
                        )
                        break

                # define top variable #
                tcnt = 0

                # check delay option #
                if delay:
                    targetList = cpuProcDelay
                else:
                    targetList = cpuProcUsage

                targetList.pop("[ TOTAL ]", None)

                # get max cpu condition #
                maxCpuCond = UtilMgr.getEnvironNum(
                    "MAXCPUCOND", False, 0, False, True
                )

                # get avg cpu condition #
                avgCpuCond = UtilMgr.getEnvironNum(
                    "AVGCPUCOND", False, 0, False, True
                )

                # get min cpu condition #
                if "MINCPUCOND" in SysMgr.environList:
                    minCpuCond = UtilMgr.getEnvironNum(
                        "MINCPUCOND", False, 0, False, True
                    )
                else:
                    minCpuCond = 0

                # Process CPU usage #
                for idx, item in sorted(
                    targetList.items(),
                    key=lambda e: e[1]["average"],
                    reverse=True,
                ):
                    # check option #
                    if not SysMgr.cpuEnable:
                        break

                    # check skip condition #
                    if (
                        not SysMgr.showAll
                        and not SysMgr.filterGroup
                        and item["maximum"] == 0
                    ):
                        continue

                    # check max cpu condition #
                    if item["maximum"] < maxCpuCond:
                        continue

                    # check min cpu condition #
                    if item["minimum"] < minCpuCond:
                        continue

                    usage = item["usage"].split()
                    usage = list(map(long, usage))[:lent]
                    cpuUsage = list(usage)

                    totalUsage = sum(cpuUsage)
                    try:
                        avgUsage = round(totalUsage / len(cpuUsage), 1)
                    except:
                        avgUsage = 0

                    # check avg cpu condition #
                    if avgUsage < avgCpuCond:
                        continue

                    # check top number #
                    if SysMgr.nrTop:
                        if tcnt >= SysMgr.nrTop:
                            break
                        else:
                            tcnt += 1

                    if not SysMgr.blockEnable:
                        # merge CPU usage and wait time for processes #
                        try:
                            blkUsage = blkProcUsage[idx]["usage"].split()
                            blkUsage = list(map(long, blkUsage))
                            for interval, value in enumerate(blkUsage):
                                usage[interval] += value
                        except SystemExit:
                            sys.exit(0)
                        except:
                            pass

                    # increase effectProcList count #
                    for seq, cnt in enumerate(usage):
                        if cnt > 0:
                            effectProcList[seq] += 1

                    # update the maximum ytick #
                    maxusage = max(usage)
                    if ymax < maxusage:
                        ymax = maxusage

                    color = None
                    if stacked:
                        stackedStats.append(cpuUsage)
                    else:
                        # get plot attr #
                        lw, pe = _getPlotAttr(idx)

                        # draw plot and get color #
                        color = plot(
                            timeline, usage, "-", linewidth=lw, path_effects=pe
                        )[0].get_color()

                    # get stats #
                    margin = self.getMargin()
                    maxIdx = usage.index(maxusage)
                    maxCpuPer = str(cpuUsage[maxIdx])
                    if idx in blkProcUsage and not SysMgr.blockEnable:
                        maxBlkPer = blkUsage[maxIdx]
                    else:
                        maxBlkPer = 0

                    # get blocking time #
                    try:
                        if maxBlkPer > 0:
                            maxBlkPerStr = "+%s%%" % maxBlkPer
                        else:
                            maxBlkPerStr = ""
                    except:
                        maxBlkPerStr = ""

                    # check lifecycle event #
                    life = ""
                    if (
                        "start" in item
                        and "finish" in item
                        and item["start"] < len(timeline)
                        and item["finish"] < len(timeline)
                    ):
                        life += "|Run_%s~%s" % (
                            timeline[item["start"]],
                            timeline[item["finish"]],
                        )
                    elif "start" in item and item["start"] < len(timeline):
                        life += "|Run_%s~" % timeline[item["start"]]
                    elif "finish" in item and item["finish"] < len(timeline):
                        life += "|Run_~%s" % timeline[item["finish"]]

                    # make text #
                    maxPer = "[Max_%s%%%s|Avg_%s%%|Total_%s%%%s]" % (
                        maxCpuPer,
                        maxBlkPerStr,
                        avgUsage,
                        conv(totalUsage),
                        life,
                    )

                    # mark text at peek #
                    ilabel = "%s%s%s" % (prefix, idx, maxPer)

                    if stacked:
                        stackedTexts.append(ilabel)
                    elif hasLabel:
                        text(
                            timeline[maxIdx],
                            usage[maxIdx] + margin,
                            ilabel,
                            fontsize=self.lfsize - 1,
                            color=color,
                            fontweight="normal",
                            rotation=35,
                            ha=_getTextAlign(maxIdx, timeline),
                        )

                    if not totalLabel:
                        labelStat = conv(avgUsage, True)
                    else:
                        labelStat = conv(totalUsage)

                    labelList.append("%s%s - %s%%" % (prefix, idx, labelStat))

                # draw stacked plots #
                if stacked:
                    _drawStack(timeline, stackedStats, stackedTexts, self, ax)

            """
            ylabel('CPU + I/O', fontsize=5)
            ax.yaxis.set_label_coords(-0.05,0.5)
            """

            # draw label #
            TaskAnalyzer.drawLabel(labelList, draw=True, anchor=(1.12, 1.05))

            grid(which="both", linestyle=":", linewidth=0.2)
            tick_params(direction="in")

            # update xticks #
            xticks(fontsize=self.xfsize)
            if len(timeline) > 1:
                xlim([timeline[0], timeline[-1]])

            # adjust yticks #
            ylist = ax.get_yticks().tolist()

            # set ymin #
            ymin = long(min(ylist))
            if ymin < 0:
                ymin = 0
            elif ymin == 0:
                try:
                    ax.set_ylim(bottom=0)
                except:
                    pass

            # update yticks #
            if stacked:
                try:
                    ymax = max(ymax, max(sum(y) for y in zip(*stackedStats)))
                except:
                    ymax = 0
                margin = False
            else:
                if ymax < 100:
                    ymax = 100
                margin = True
            TaskAnalyzer.drawYticks(ax, ymax, margin=margin)

            # add % unit to each value #
            try:
                ytickLabel = ax.get_yticks().tolist()
                ytickLabel = list(map(long, ytickLabel))

                # convert label units #
                ytickLabel = ["%s%%" % conv(val) for val in ytickLabel]

                ax.set_yticklabels(ytickLabel)
            except SystemExit:
                sys.exit(0)
            except:
                pass

            # set name #
            if delay:
                name = "Delay"
            else:
                name = "CPU"

            # draw name #
            TaskAnalyzer.drawName(ax, name)

            # draw base #
            self.figure = TaskAnalyzer.drawFigure()

            # draw systemd events #
            if not self.targetEventDrawn:
                _drawTargetEvent(graphStats, timeline, ax)
                _drawUnitEvent(graphStats, timeline, ax)
                self.targetEventDrawn = True

            self.drawBottom(xtype, ax, timeline, nrTask, effectProcList)

        def _drawPrio(graphStats, xtype, pos, size):
            # draw title #
            ax = subplot2grid((6, 1), (pos, 0), rowspan=size, colspan=1)
            ax.xaxis.set_major_locator(MaxNLocator(integer=True))
            suptitle(graphStats["graphTitle"], fontsize=8)

            # define common label list #
            labelList = []
            hasLabel = not "NOLABEL" in SysMgr.environList

            # get minimum timeline #
            timeline = None
            for key, val in graphStats.items():
                if not key.endswith("timeline"):
                    continue
                elif not timeline:
                    timeline = val
                elif len(timeline) > len(val):
                    timeline = val
            lent = len(timeline)

            # get max condition #
            if "MAXPRICOND" in SysMgr.environList:
                maxPriCond = abs(
                    SysMgr.convSchedPrio(
                        SysMgr.environList["MAXPRICOND"][0].upper()
                    )
                    - 39
                )
            else:
                maxPriCond = 0

            # get min condition #
            if "MINPRICOND" in SysMgr.environList:
                minPriCond = abs(
                    SysMgr.convSchedPrio(
                        SysMgr.environList["MINPRICOND"][0].upper()
                    )
                    - 39
                )
            else:
                minPriCond = 0

            # start loop #
            for key, val in graphStats.items():
                if not key.endswith("timeline"):
                    continue

                res = key.rsplit(":", 1)
                if len(res) > 1:
                    fname = "%s:" % res[0]
                    prefix = "[%s] " % res[0]
                else:
                    fname = ""
                    prefix = ""

                cpuProcPrio = graphStats["%scpuProcPrio" % fname]

                # add boundary line #
                self.drawBoundary("pri", labelList)

                # draw user event #
                self.drawUserEvent("pri")

                # Convert process priority #
                for val in list(cpuProcPrio):
                    usage = cpuProcPrio[val]["usage"][:lent]
                    usage = list(map(SysMgr.convSchedPrio, usage))
                    usage = list(
                        map(
                            lambda e: 0 if e is None else abs(long(e) - 39),
                            usage,
                        )
                    )
                    cpuProcPrio[val]["usage"] = usage
                    eusage = []
                    for v in usage:
                        if v:
                            eusage.append(v)
                    if eusage:
                        maxUsage = max(eusage)
                        minUsage = min(eusage)
                    else:
                        maxUsage = minUsage = 0
                    cpuProcPrio[val]["maxUsage"] = maxUsage
                    cpuProcPrio[val]["minUsage"] = minUsage

                # Process priority #
                for idx, item in sorted(
                    cpuProcPrio.items(),
                    key=lambda e: e[1]["maxUsage"],
                    reverse=True,
                ):
                    # check condition #
                    if maxPriCond and maxPriCond < item["maxUsage"]:
                        continue
                    elif minPriCond and minPriCond > item["maxUsage"]:
                        continue

                    usage = item["usage"]

                    # get plot attr #
                    lw, pe = _getPlotAttr(idx)

                    # remove 0 #
                    eusage = []
                    prevVal = None
                    for i, v in enumerate(usage):
                        if v is not None:
                            prevVal = v
                        else:
                            if "start" in item and v < item["start"]:
                                v = None
                            elif "finish" in item and v > item["finish"]:
                                v = None
                            elif prevVal is None:
                                v = None
                            else:
                                v = prevVal
                        eusage.append(v)

                    # draw plot and get color #
                    color = plot(
                        timeline, eusage, "-", linewidth=lw, path_effects=pe
                    )[0].get_color()

                    # get stats #
                    maxusage = item["maxUsage"]
                    margin = self.getMargin()
                    maxIdx = usage.index(maxusage)
                    maxPri = SysMgr.convSchedPrio(maxusage, True)
                    minPri = SysMgr.convSchedPrio(item["minUsage"], True)

                    # check lifecycle event #
                    life = ""
                    if (
                        "start" in item
                        and "finish" in item
                        and item["start"] < len(timeline)
                        and item["finish"] < len(timeline)
                    ):
                        life += "|Run_%s~%s" % (
                            timeline[item["start"]],
                            timeline[item["finish"]],
                        )
                    elif "start" in item and item["start"] < len(timeline):
                        life += "|Run_%s~" % timeline[item["start"]]
                    elif "finish" in item and item["finish"] < len(timeline):
                        life += "|Run_~%s" % timeline[item["finish"]]

                    # make text #
                    maxStr = "[Min_%s|Max_%s|%s]" % (
                        minPri,
                        maxPri,
                        life,
                    )

                    # mark text at peek #
                    ilabel = "%s%s%s" % (prefix, idx, maxStr)

                    if hasLabel:
                        text(
                            timeline[maxIdx],
                            usage[maxIdx] + margin,
                            ilabel,
                            fontsize=self.lfsize - 1,
                            color=color,
                            fontweight="normal",
                            rotation=35,
                            ha=_getTextAlign(maxIdx, timeline),
                        )

                    labelList.append("%s%s - %4s" % (prefix, idx, maxPri))

            # draw label #
            TaskAnalyzer.drawLabel(labelList, draw=True, anchor=(1.12, 1.05))

            grid(which="both", linestyle=":", linewidth=0.2)
            tick_params(direction="in")

            # update xticks #
            xticks(fontsize=self.xfsize)
            if len(timeline) > 1:
                xlim([timeline[0], timeline[-1]])

            # adjust yticks #
            ylist = ax.get_yticks().tolist()

            # set ymin #
            ymin = long(min(ylist))
            if ymin < 0:
                ymin = 0
            elif ymin == 0:
                try:
                    ax.set_ylim(bottom=0)
                except:
                    pass

            # set ymax #
            TaskAnalyzer.drawYticks(ax, 140, unit=14)

            # draw name #
            TaskAnalyzer.drawName(ax, "Prio")

            # convert priority units #
            try:
                ytickLabel = ax.get_yticks().tolist()
                ytickLabel = list(map(long, ytickLabel))

                # convert label units #
                ytickLabel = [
                    SysMgr.convSchedPrio(val, True) for val in ytickLabel
                ]

                ax.set_yticklabels(ytickLabel)
            except SystemExit:
                sys.exit(0)
            except:
                pass

            # draw watermark #
            try:
                # RT #
                axhline(y=40, linewidth=1, linestyle="--", color="black")

                # NORMAL - #
                axhline(y=19, linewidth=1, linestyle="--", color="black")
            except SystemExit:
                sys.exit(0)
            except:
                pass

            # draw base #
            self.figure = TaskAnalyzer.drawFigure()

            # draw systemd events #
            if not self.targetEventDrawn:
                _drawTargetEvent(graphStats, timeline, ax)
                _drawUnitEvent(graphStats, timeline, ax)
                self.targetEventDrawn = True

            self.drawBottom(xtype, ax, timeline, nrTask, effectProcList)

        def _drawIo(graphStats, xtype, pos, size):
            def __drawSystemIo(
                statList,
                color,
                ymax,
                name,
                stacked,
                stackedStats,
                stackedTexts,
                ioFilter,
            ):
                if not statList:
                    return -1, ymax
                elif ioFilter and not UtilMgr.isValidStr(name, ioFilter):
                    return -1, ymax

                usage = list(map(long, statList))[:lent]
                origUsage = usage[:]

                # disable stacked #
                if SysMgr.showAll:
                    stacked = False

                    # accumulate stats #
                    if "ACCUMULATE" in SysMgr.environList:
                        for n in range(len(usage)):
                            if n == 0:
                                continue
                            usage[n] += usage[n - 1]
                        statList = usage

                # update the maximum ytick #
                maxUsage = max(usage)
                if ymax < maxUsage:
                    ymax = maxUsage

                minIdx = usage.index(min(usage))
                maxIdx = usage.index(max(usage))

                minval = "%s%s%s" % (
                    prefix,
                    name,
                    convSize(usage[minIdx] << 10),
                )
                maxsize = convSize(usage[maxIdx] << 10)
                totalsize = convSize(long(sum(origUsage)) << 10)
                maxval = "%s%s%s" % (prefix, name, maxsize)
                lastval = "%s%s%s" % (prefix, name, convSize(usage[-1] << 10))

                # set color #
                if prefix:
                    rcolor = None
                else:
                    rcolor = color

                # draw plots #
                if stacked:
                    stackedStats.append(statList)
                elif usage[minIdx] == usage[maxIdx] == 0:
                    line = plot(
                        timeline,
                        statList,
                        "-",
                        c=rcolor,
                        linewidth=0.1,
                        alpha=0.1,
                    )
                else:
                    pe = _getPathEffect(lw=1)
                    line = plot(
                        timeline,
                        statList,
                        linestyle="-",
                        c=rcolor,
                        linewidth=0.7,
                        marker=".",
                        markersize=1,
                        path_effects=pe,
                    )

                if stacked:
                    color = None
                else:
                    color = line[0].get_color()

                # draw texts #
                if stacked:
                    pass
                elif usage[minIdx] > 0:
                    text(
                        timeline[minIdx],
                        usage[minIdx],
                        minval,
                        fontsize=self.lfsize,
                        color=color,
                        fontweight="bold",
                        ha=_getTextAlign(minIdx, timeline),
                        rotation=35,
                    )
                if stacked:
                    pass
                elif usage[minIdx] != usage[maxIdx] and usage[maxIdx] > 0:
                    text(
                        timeline[maxIdx],
                        usage[maxIdx],
                        maxval,
                        fontsize=self.lfsize,
                        color=color,
                        fontweight="bold",
                        ha=_getTextAlign(maxIdx, timeline),
                        rotation=35,
                    )
                if stacked:
                    stackedTexts.append(maxval)
                elif usage[-1] > 0:
                    text(
                        timeline[-1],
                        usage[-1],
                        lastval,
                        rotation=35,
                        fontsize=self.lfsize,
                        color=color,
                        fontweight="bold",
                        ha="right",
                    )

                return totalsize, ymax

            # define common label list #
            ymax = 0
            labelList = []

            # set convert size #
            convSize = UtilMgr.convSize2Unit

            # get stacked plot option #
            stacked = "STACKEDPLOT" in SysMgr.environList
            stackedStats = []
            stackedTexts = []

            # draw title #
            ax = subplot2grid((6, 1), (pos, 0), rowspan=size, colspan=1)
            ax.xaxis.set_major_locator(MaxNLocator(integer=True))
            suptitle(graphStats["graphTitle"], fontsize=8)

            # get minimum timeline #
            timeline = None
            for key, val in graphStats.items():
                if not key.endswith("timeline"):
                    continue
                elif not timeline:
                    timeline = val
                elif len(timeline) > len(val):
                    timeline = val
            lent = len(timeline)

            # set visible total usage flag #
            if SysMgr.showAll or not SysMgr.filterGroup:
                isVisibleTotal = True
            else:
                isVisibleTotal = False

            # set label variable #
            hasLabel = not "NOLABEL" in SysMgr.environList

            # add boundary line #
            self.drawBoundary("io", labelList)

            # draw user event #
            self.drawUserEvent("io")

            # get I/O filter #
            ioFilter = SysMgr.environList.get("IOFILTER", [])
            ioProcFilter = SysMgr.environList.get("IOPROCFILTER", [])

            # start loop #
            for key, val in graphStats.items():
                if not key.endswith("timeline"):
                    continue

                res = key.rsplit(":", 1)
                if len(res) > 1:
                    fname = "%s:" % res[0]
                    prefix = "[%s] " % res[0]
                else:
                    fname = ""
                    prefix = ""

                blkRead = graphStats["%sblkRead" % fname][:lent]
                blkWrite = graphStats["%sblkWrite" % fname][:lent]
                blkProcUsage = graphStats["%sblkProcUsage" % fname]
                netRead = graphStats["%snetRead" % fname][:lent]
                netWrite = graphStats["%snetWrite" % fname][:lent]
                reclaimBg = graphStats["%sreclaimBg" % fname][:lent]
                reclaimDr = graphStats["%sreclaimDr" % fname][:lent]
                storageUsage = graphStats["%sstorageUsage" % fname]
                networkUsage = graphStats["%snetworkUsage" % fname]

                if isVisibleTotal:
                    for color, stats, name, lname in (
                        ("purple", blkRead, "[BR]", "Block Read"),
                        ("darkgreen", blkWrite, "[BW]", "Block Write"),
                        ("pink", reclaimBg, "[RB]", "Reclaim BG"),
                        ("red", reclaimDr, "[RF]", "Reclaim FG"),
                        ("orange", netRead, "[NI]", "Network In"),
                        ("cyan", netWrite, "[NO]", "Network Out"),
                    ):
                        color = None if fname else color
                        totalsize, ymax = __drawSystemIo(
                            stats,
                            color,
                            ymax,
                            name,
                            stacked,
                            stackedStats,
                            stackedTexts,
                            ioFilter,
                        )
                        if totalsize != -1:
                            labelList.append(
                                "%s%s - %s" % (prefix, lname, totalsize)
                            )

                # check device enable flag #
                diskEnable = False
                networkEnable = False
                enableList = SysMgr.getOption("e")
                if enableList:
                    if "d" in enableList:
                        diskEnable = True
                    if "n" in enableList:
                        networkEnable = True

                # System Network Usage #
                for idx, item in networkUsage.items():
                    if not isVisibleTotal or not networkEnable:
                        break

                    rdUsage = item["recv"][:lent]
                    wrUsage = item["tran"][:lent]

                    # no network usage #
                    if not rdUsage and not wrUsage:
                        continue

                    margin = self.getMargin()

                    for usage, ptype, name in (
                        (wrUsage, "[NO]", "Outbound"),
                        (rdUsage, "[NI]", "Inbound"),
                    ):
                        # get stat #
                        minIdx = usage.index(min(usage))
                        maxIdx = usage.index(max(usage))

                        # update the maximum ytick #
                        maxUsage = max(usage)
                        if ymax < maxUsage:
                            ymax = maxUsage

                        maxsize = convSize(usage[maxIdx] << 10)
                        totalsize = convSize(long(sum(usage)) << 10)
                        maxval = "%s%s(%s)%s" % (prefix, ptype, idx, maxsize)
                        lastval = "%s%s(%s)%s" % (
                            prefix,
                            ptype,
                            idx,
                            convSize(usage[-1] << 10),
                        )

                        addLabel = True
                        if usage[minIdx] == usage[maxIdx] == 0:
                            addLabel = False
                        elif ioFilter and (
                            not UtilMgr.isValidStr(ptype, ioFilter)
                            or not UtilMgr.isValidStr(idx, ioFilter)
                        ):
                            addLabel = False
                        elif stacked:
                            stackedStats.append(usage)
                            stackedTexts.append(maxval)
                        else:
                            color = plot(timeline, usage, "-", linewidth=0.7)[
                                0
                            ].get_color()
                            if usage[maxIdx] > 0:
                                text(
                                    timeline[maxIdx],
                                    usage[maxIdx] + margin,
                                    maxval,
                                    rotation=35,
                                    fontsize=self.lfsize,
                                    color=color,
                                    fontweight="bold",
                                    ha=_getTextAlign(maxIdx, timeline),
                                )
                            if usage[-1] > 0:
                                text(
                                    timeline[-1],
                                    usage[-1] + margin,
                                    lastval,
                                    rotation=35,
                                    fontsize=self.lfsize,
                                    color=color,
                                    fontweight="bold",
                                    ha="right",
                                )

                        if addLabel:
                            labelList.append(
                                "%s%s %s - %s" % (prefix, idx, name, totalsize)
                            )

                # System Storage Usage #
                for idx, item in storageUsage.items():
                    if not isVisibleTotal or not diskEnable:
                        break

                    busyUsage = item["busy"][:lent]
                    freeUsage = item["free"][:lent]
                    rdUsage = item["read"][:lent]
                    wrUsage = item["write"][:lent]

                    # no storage usage #
                    if len(rdUsage) == len(wrUsage) == len(freeUsage) == 0:
                        continue

                    margin = self.getMargin()

                    for usage, ptype, name in (
                        (wrUsage, "[BW]", "Write"),
                        (rdUsage, "[BR]", "Read"),
                    ):
                        # get stats #
                        minIdx = usage.index(min(usage))
                        maxIdx = usage.index(max(usage))

                        # update the maximum ytick #
                        maxUsage = max(usage)
                        if ymax < maxUsage:
                            ymax = maxUsage

                        maxsize = convSize(usage[maxIdx] << 10)
                        totalsize = convSize(long(sum(usage)) << 10)
                        busyval = busyUsage[maxIdx]
                        maxval = "%s%s(%s)%s[%s%%]" % (
                            prefix,
                            ptype,
                            idx,
                            maxsize,
                            busyval,
                        )
                        lastval = "%s%s(%s)%s" % (
                            prefix,
                            ptype,
                            idx,
                            convSize(usage[-1] << 10),
                        )

                        addLabel = True
                        if usage[minIdx] == usage[maxIdx] == 0:
                            addLabel = False
                        elif ioFilter and (
                            not UtilMgr.isValidStr(ptype, ioFilter)
                            or not UtilMgr.isValidStr(idx, ioFilter)
                        ):
                            addLabel = False
                        elif stacked:
                            stackedStats.append(usage)
                            stackedTexts.append(maxval)
                        else:
                            color = plot(timeline, usage, "-", linewidth=0.7)[
                                0
                            ].get_color()

                            if usage[maxIdx] > 0:
                                text(
                                    timeline[maxIdx],
                                    usage[maxIdx] + margin,
                                    maxval,
                                    rotation=35,
                                    fontsize=self.lfsize,
                                    color=color,
                                    fontweight="bold",
                                    ha=_getTextAlign(maxIdx, timeline),
                                )

                            if usage[-1] > 0:
                                text(
                                    timeline[-1],
                                    usage[-1] + margin,
                                    lastval,
                                    fontsize=self.lfsize,
                                    color=color,
                                    fontweight="bold",
                                    ha="right",
                                    rotation=35,
                                )

                        if addLabel:
                            labelList.append(
                                "%s%s %s - %s" % (prefix, idx, name, totalsize)
                            )

                # Process I/O usage #
                wrFilter = (
                    UtilMgr.isValidStr("BW", ioFilter) if ioFilter else True
                )
                rdFilter = (
                    UtilMgr.isValidStr("BR", ioFilter) if ioFilter else True
                )
                if not SysMgr.showAll or (not wrFilter and not rdFilter):
                    blkProcUsage = {}
                for idx, item in sorted(
                    blkProcUsage.items(),
                    key=lambda x: x[1]["total"]
                    if isinstance(x[1]["total"], long)
                    else sum(list(map(long, x[1]["total"].split("/")))),
                    reverse=True,
                ):
                    # check skip condition #
                    if ioProcFilter and not UtilMgr.isValidStr(
                        idx, ioProcFilter
                    ):
                        continue

                    rdUsage = []
                    wrUsage = []
                    usage = item["usage"].split()[:lent]

                    # divide I/O graph #
                    for item in usage:
                        io = item.split("/")
                        if len(io) == 2:
                            rdUsage.append(long(io[0]) << 10)
                            wrUsage.append(long(io[1]) << 10)
                        else:
                            rdUsage.append(0)
                            wrUsage.append(0)

                    # no I/O usage #
                    if len(rdUsage) == len(wrUsage) == 0:
                        continue

                    margin = self.getMargin()

                    for rwFilter, usage, ptype, name in (
                        (wrFilter, wrUsage, "[BW]", "Write"),
                        (rdFilter, rdUsage, "[BR]", "Read"),
                    ):
                        # check filter #
                        if rwFilter:
                            minIdx = usage.index(min(usage))
                            maxIdx = usage.index(max(usage))

                            # update the maximum ytick #
                            maxUsage = max(usage)
                            if ymax < maxUsage:
                                ymax = maxUsage

                            maxsize = convSize(usage[maxIdx] << 10)
                            totalsize = convSize(long(sum(usage)) << 10)
                            maxval = "%s%s%s[%s]" % (
                                prefix,
                                ptype,
                                idx,
                                maxsize,
                            )
                            lastval = "%s%s%s[%s]" % (
                                prefix,
                                ptype,
                                convSize(usage[-1] << 10),
                                idx,
                            )

                            # get plot attr #
                            lw, pe = _getPlotAttr(idx)

                        addLabel = True
                        if not rwFilter or usage[minIdx] == usage[maxIdx] == 0:
                            addLabel = False
                        elif stacked:
                            stackedStats.append(usage)
                            stackedTexts.append(maxval)
                        else:
                            color = plot(
                                timeline,
                                usage,
                                "-",
                                linewidth=lw,
                                path_effects=pe,
                            )[0].get_color()

                            if usage[maxIdx] > 0 and hasLabel:
                                text(
                                    timeline[maxIdx],
                                    usage[maxIdx] + margin,
                                    maxval,
                                    fontsize=self.lfsize,
                                    color=color,
                                    fontweight="normal",
                                    rotation=35,
                                    ha=_getTextAlign(maxIdx, timeline),
                                )
                            if usage[-1] > 0 and hasLabel:
                                text(
                                    timeline[-1],
                                    usage[-1] + margin,
                                    lastval,
                                    fontsize=self.lfsize,
                                    color=color,
                                    fontweight="normal",
                                    rotation=35,
                                    ha="right",
                                )

                        if addLabel:
                            labelList.append(
                                "%s%s%s - %s" % (prefix, idx, ptype, totalsize)
                            )

            """
            ylabel('I/O', fontsize=5)
            ax.yaxis.set_label_coords(-0.05,0.5)
            """

            # draw stacked plots #
            if stacked:
                _drawStack(timeline, stackedStats, stackedTexts, self, ax)

            # draw label #
            TaskAnalyzer.drawLabel(labelList, draw=False, anchor=(1.12, 1.05))

            grid(which="both", linestyle=":", linewidth=0.2)
            tick_params(direction="in")

            # update yticks #
            if stacked:
                try:
                    ymax = max(ymax, max(sum(y) for y in zip(*stackedStats)))
                except:
                    ymax = 0
                margin = False
            else:
                margin = True
            TaskAnalyzer.drawYticks(ax, ymax, margin=margin)

            # update xticks #
            xticks(fontsize=self.xfsize)
            if len(timeline) > 1:
                xlim([timeline[0], timeline[-1]])

            try:
                ticklabel_format(useOffset=False)
            except:
                pass

            # draw name #
            TaskAnalyzer.drawName(ax, "I/O")

            # draw base #
            self.figure = TaskAnalyzer.drawFigure()

            # convert tick type to integer #
            try:
                # ax.get_xaxis().set_visible(False)
                ytickLabel = ax.get_yticks().tolist()
                ytickLabel = list(map(long, ytickLabel))

                # convert label units #
                ytickLabel = [convSize(val << 10) for val in ytickLabel]

                # remove redundant ticks #
                lastTick = ""
                tempLabelList = list(ytickLabel)
                for idx, ytick in enumerate(tempLabelList):
                    if lastTick == ytick:
                        ytickLabel[idx] = ""
                    else:
                        lastTick = ytick
                ax.set_yticklabels(ytickLabel)

                # hide yticks #
                if ytickLabel[-1] == "0":
                    ax.set_ylim(top=1)
                    ax.get_yaxis().set_visible(False)
            except SystemExit:
                sys.exit(0)
            except:
                pass

            # draw systemd events #
            if not self.targetEventDrawn:
                _drawTargetEvent(graphStats, timeline, ax)
                _drawUnitEvent(graphStats, timeline, ax)
                self.targetEventDrawn = True

            self.drawBottom(xtype, ax, timeline, nrTask, effectProcList)

        def _drawMem(graphStats, xtype, pos, size):
            def __drawSystemMem(statList, color, ymax, name):
                usage = list(map(long, statList))
                minIdx = usage.index(min(usage))
                maxIdx = usage.index(max(usage))

                if usage[minIdx] == usage[maxIdx] == 0:
                    return None, ymax

                # update the maximum ytick #
                maxusage = max(usage)
                if ymax < maxusage:
                    ymax = maxusage

                minval = "%s%s%s" % (
                    prefix,
                    name,
                    convSize(usage[minIdx] << 20),
                )
                maxsize = convSize(usage[maxIdx] << 20)
                maxval = "%s%s%s" % (prefix, name, maxsize)
                lastsize = convSize(usage[-1] << 20)
                lastval = "%s%s%s" % (prefix, name, lastsize)

                # set color #
                if prefix:
                    fcolor = None
                else:
                    fcolor = color

                pe = _getPathEffect()

                line = plot(
                    timeline,
                    usage,
                    "-",
                    c=fcolor,
                    linewidth=0.7,
                    path_effects=pe,
                    solid_capstyle="round",
                )

                color = line[0].get_color()

                if usage[minIdx] > 0:
                    text(
                        timeline[minIdx],
                        usage[minIdx],
                        minval,
                        fontsize=self.lfsize,
                        color=color,
                        fontweight="bold",
                        ha=_getTextAlign(minIdx, timeline),
                    )
                if usage[minIdx] != usage[maxIdx] and usage[maxIdx] > 0:
                    text(
                        timeline[maxIdx],
                        usage[maxIdx],
                        maxval,
                        fontsize=self.lfsize,
                        color=color,
                        fontweight="bold",
                        ha=_getTextAlign(maxIdx, timeline),
                    )
                if usage[-1] > 0:
                    text(
                        timeline[-1],
                        usage[-1],
                        lastval,
                        fontsize=self.lfsize,
                        color=color,
                        fontweight="bold",
                        ha="right",
                    )

                return lastsize, ymax

            # define common label list #
            ymax = 0
            labelList = []

            # set convert size #
            convSize = UtilMgr.convSize2Unit

            # set label variable #
            hasLabel = not "NOLABEL" in SysMgr.environList

            # draw title #
            ax = subplot2grid((6, 1), (pos, 0), rowspan=size, colspan=1)
            ax.xaxis.set_major_locator(MaxNLocator(integer=True))
            suptitle(graphStats["graphTitle"], fontsize=8)

            # get minimum timeline #
            timeline = None
            for key, val in graphStats.items():
                if not key.endswith("timeline"):
                    continue
                elif not timeline:
                    timeline = val
                elif len(timeline) > len(val):
                    timeline = val
            lent = len(timeline)

            # add boundary line #
            self.drawBoundary("mem", labelList)

            # draw user event #
            self.drawUserEvent("mem")

            # define top variable #
            tcnt = 0

            # get stacked plot option #
            stacked = "STACKEDPLOT" in SysMgr.environList
            stackedStats = []
            stackedTexts = []

            # start loop #
            for key, val in graphStats.items():
                if not key.endswith("timeline"):
                    continue

                # get prefix #
                res = key.rsplit(":", 1)
                if len(res) > 1:
                    fname = "%s:" % res[0]
                    prefix = "[%s] " % res[0]
                else:
                    fname = ""
                    prefix = ""

                totalRam = graphStats["%stotalRam" % fname]
                totalSwap = graphStats["%stotalSwap" % fname]

                if "NOMEMFREEPLOT" in SysMgr.environList:
                    memFree = [0]
                else:
                    memFree = graphStats["%smemFree" % fname][:lent]

                if "NOMEMANONPLOT" in SysMgr.environList:
                    memAnon = [0]
                else:
                    memAnon = graphStats["%smemAnon" % fname][:lent]

                if "NOMEMCACHEPLOT" in SysMgr.environList:
                    memCache = [0]
                else:
                    memCache = graphStats["%smemCache" % fname][:lent]

                try:
                    if "NOMEMKERNELPLOT" in SysMgr.environList:
                        raise Exception()
                    memKernel = graphStats["%smemKernel" % fname][:lent]
                except:
                    memKernel = [0]

                if "NOMEMPROCPLOT" in SysMgr.environList:
                    memProc = {}
                else:
                    memProc = graphStats["%smemProcUsage" % fname]

                if "NOMEMSWAPPLOT" in SysMgr.environList:
                    swapUsage = [0]
                else:
                    swapUsage = graphStats["%sswapUsage" % fname][:lent]

                # get margin #
                margin = self.getMargin()

                def __drawMemPlots(ymax, stacked=False):
                    # set target function #
                    if stacked:
                        stackedStats.append(memFree)
                        stackedStats.append(memAnon)
                        stackedStats.append(memCache)
                        stackedStats.append(memKernel)
                        stackedStats.append(swapUsage)
                        __drawSysMem = lambda a, b, c, d: (
                            convSize(a[-1] << 20) if a else None,
                            max(a) + c,
                        )
                    else:
                        __drawSysMem = __drawSystemMem

                    # System Available(Free) Memory #
                    lastsize, ymax = __drawSysMem(
                        memFree, "blue", ymax, "[RA]"
                    )
                    if lastsize is not None:
                        if totalRam:
                            label = (
                                "%s< RAM Total > - %s\n%sRAM Available - %s"
                                % (
                                    prefix,
                                    convSize(totalRam),
                                    prefix,
                                    lastsize,
                                )
                            )
                            labelList.append(label)
                        else:
                            labelList.append(
                                "%sRAM Available - %s" % (prefix, lastsize)
                            )

                    # System User(Anon) Memory #
                    lastsize, ymax = __drawSysMem(
                        memAnon, "skyblue", ymax, "[RU]"
                    )
                    if lastsize is not None:
                        labelList.append(
                            "%sRAM User - %s" % (prefix, lastsize)
                        )

                    # System Cache Memory #
                    lastsize, ymax = __drawSysMem(
                        memCache, "darkgray", ymax, "[RC]"
                    )
                    if lastsize is not None:
                        labelList.append(
                            "%sRAM Cache - %s" % (prefix, lastsize)
                        )

                    # System Kernel Memory #
                    lastsize, ymax = __drawSysMem(
                        memKernel, "purple", ymax, "[RK]"
                    )
                    if lastsize is not None:
                        labelList.append(
                            "%sRAM Kernel - %s" % (prefix, lastsize)
                        )

                    # System Swap Memory #
                    lastsize, ymax = __drawSysMem(
                        swapUsage, "orange", ymax, "[RS]"
                    )
                    if lastsize is not None:
                        if totalSwap:
                            label = (
                                "%s< Swap Total > - %s\n%sSwap Usage - %s"
                                % (
                                    prefix,
                                    convSize(totalSwap),
                                    prefix,
                                    lastsize,
                                )
                            )
                            labelList.append(label)
                        else:
                            labelList.append(
                                "%sSwap Usage - %s" % (prefix, lastsize)
                            )

                    # draw stacked plots #
                    if stacked:
                        _drawStack(
                            timeline,
                            stackedStats,
                            labelList,
                            self,
                            ax,
                            acc=False,
                        )

                    return ymax

                # draw system memory #
                if "MEMSYSPLOT" in SysMgr.environList:
                    ymax = __drawMemPlots(ymax)

                isSysplot = False

                # Process VSS #
                if SysMgr.vssEnable:
                    # get max vss condition #
                    maxVssCond = UtilMgr.getEnvironNum(
                        "MAXVSSCOND", False, 0, False, True
                    )

                    for key, item in sorted(
                        memProc.items(),
                        key=lambda e: 0
                        if not "maxVss" in e[1]
                        else e[1]["maxVss"],
                        reverse=True,
                    ):
                        usage = list(map(long, item["vssUsage"].split()))[
                            :lent
                        ]

                        # update the maximum ytick #
                        maxusage = max(usage)
                        if ymax < maxusage:
                            ymax = maxusage

                        # check max vss conditino #
                        if maxusage < maxVssCond:
                            continue

                        try:
                            minIdx = usage.index(min(usage))
                        except:
                            minIdx = 0
                        try:
                            maxIdx = usage.index(item["maxVss"])
                        except:
                            maxIdx = 0

                        if usage[minIdx] == usage[maxIdx] == 0:
                            continue

                        # check top number #
                        if SysMgr.nrTop:
                            if tcnt >= SysMgr.nrTop:
                                break
                            else:
                                tcnt += 1

                        key = prefix + key
                        minval = "%s [%s]" % (
                            key,
                            convSize(usage[minIdx] << 20),
                        )
                        maxsize = convSize(usage[maxIdx] << 20)
                        maxval = "%s [%s]" % (key, maxsize)
                        lastval = "%s [%s]" % (key, convSize(usage[-1] << 20))

                        # ignore 0 #
                        usage = list(
                            map(lambda x: x if x != 0 else None, usage)
                        )

                        # get plot attr #
                        lw, pe = _getPlotAttr(key)

                        # get color #
                        color = plot(
                            timeline, usage, "-", linewidth=lw, path_effects=pe
                        )[0].get_color()

                        if usage[minIdx] and hasLabel:
                            text(
                                timeline[minIdx],
                                usage[minIdx] + margin,
                                minval,
                                color=color,
                                fontsize=self.lfsize,
                                rotation=35,
                                ha=_getTextAlign(minIdx, timeline),
                            )
                        if (
                            usage[minIdx] != usage[maxIdx]
                            and usage[maxIdx]
                            and hasLabel
                        ):
                            text(
                                timeline[maxIdx],
                                usage[maxIdx] + margin,
                                maxval,
                                color=color,
                                fontsize=self.lfsize,
                                rotation=35,
                                ha=_getTextAlign(maxIdx, timeline),
                            )
                        if usage[-1] and hasLabel:
                            text(
                                timeline[-1],
                                usage[-1] + margin,
                                lastval,
                                color=color,
                                fontsize=self.lfsize,
                                rotation=35,
                                ha="right",
                            )

                        labelList.append("%s [VSS] - %s" % (key, maxsize))

                # Process Leak #
                elif SysMgr.leakEnable:
                    # get VSS diffs #
                    for key, item in sorted(
                        memProc.items(),
                        key=lambda e: 0
                        if not "maxVss" in e[1]
                        else e[1]["maxVss"],
                        reverse=True,
                    ):
                        usage = list(map(long, item["vssUsage"].split()))[
                            :lent
                        ]

                        # get maximum value #
                        try:
                            maxVss = max(usage)
                        except:
                            maxVss = 0

                        if maxVss == 0:
                            item["vssDiff"] = 0
                            continue

                        # get index for maximum/minimum values greater than 0 #
                        try:
                            first = next(val for val in usage if val > 0)
                            last = next(
                                val for val in reversed(usage) if val > 0
                            )

                            if long(first) >= long(last):
                                item["vssDiff"] = 0
                                continue
                        except:
                            pass

                        # get minimum value #
                        try:
                            minVss = min(x for x in usage if x != 0)
                        except:
                            minVss = 0

                        diff = maxVss - minVss
                        item["vssDiff"] = diff

                    # draw leakage plots #
                    for key, item in sorted(
                        memProc.items(),
                        key=lambda e: e[1]["vssDiff"],
                        reverse=True,
                    ):
                        if item["vssDiff"] == 0:
                            break

                        usage = list(map(long, item["vssUsage"].split()))[
                            :lent
                        ]

                        # update the maximum ytick #
                        maxusage = max(usage)
                        if ymax < maxusage:
                            ymax = maxusage

                        # get minimum value #
                        try:
                            minIdx = usage.index(min(usage))
                        except:
                            minIdx = 0

                        # get maximum value #
                        try:
                            maxIdx = usage.index(item["maxVss"])
                        except:
                            maxIdx = 0

                        if usage[minIdx] == usage[maxIdx] == 0:
                            continue

                        # check top number #
                        if SysMgr.nrTop:
                            if tcnt >= SysMgr.nrTop:
                                break
                            else:
                                tcnt += 1

                        key = prefix + key
                        minval = "%s [%s]" % (
                            key,
                            convSize(usage[minIdx] << 20),
                        )
                        diffsize = convSize(item["vssDiff"] << 20)
                        lastval = "%s [%s/+%s]" % (
                            key,
                            convSize(usage[maxIdx] << 20),
                            diffsize,
                        )

                        # ignore 0 #
                        usage = list(
                            map(lambda x: x if x != 0 else None, usage)
                        )

                        # get plot attr #
                        lw, pe = _getPlotAttr(key)

                        # get color #
                        color = plot(
                            timeline, usage, "-", linewidth=lw, path_effects=pe
                        )[0].get_color()

                        if usage[minIdx] and hasLabel:
                            text(
                                timeline[minIdx],
                                usage[minIdx] - margin,
                                minval,
                                color=color,
                                fontsize=self.lfsize,
                                rotation=35,
                                ha=_getTextAlign(minIdx, timeline),
                            )
                        if (
                            usage[minIdx] != usage[maxIdx]
                            and usage[maxIdx]
                            and hasLabel
                        ):
                            text(
                                timeline[maxIdx],
                                usage[maxIdx] + margin,
                                lastval,
                                color=color,
                                fontsize=self.lfsize,
                                rotation=35,
                                ha=_getTextAlign(maxIdx, timeline),
                            )

                        labelList.append("%s [LEAK] - %s" % (key, diffsize))

                # Process RSS #
                elif SysMgr.rssEnable or SysMgr.pssEnable or SysMgr.ussEnable:
                    # get max rss condition #
                    maxRssCond = UtilMgr.getEnvironNum(
                        "MAXRSSCOND", False, 0, False, True
                    )

                    if stacked:
                        if not "MEMSYSPLOT" in SysMgr.environList:
                            ymax = __drawMemPlots(ymax)

                    for key, item in sorted(
                        memProc.items(),
                        key=lambda e: 0
                        if not "maxRss" in e[1]
                        else e[1]["maxRss"],
                        reverse=True,
                    ):
                        try:
                            rssList = item["rssUsage"].split()
                            usage = list(map(long, rssList))[:lent]
                        except SystemExit:
                            sys.exit(0)
                        except:
                            continue

                        # update the maximum ytick #
                        maxusage = max(usage)
                        if ymax < maxusage:
                            ymax = maxusage

                        # check max rss condition #
                        if maxusage < maxRssCond:
                            continue

                        try:
                            minIdx = usage.index(min(usage))
                        except SystemExit:
                            sys.exit(0)
                        except:
                            minIdx = 0

                        try:
                            maxIdx = usage.index(item["maxRss"])
                        except SystemExit:
                            sys.exit(0)
                        except:
                            maxIdx = 0

                        # check top number #
                        if SysMgr.nrTop:
                            if tcnt >= SysMgr.nrTop:
                                break
                            else:
                                tcnt += 1

                        if usage[minIdx] == usage[maxIdx] == 0:
                            continue

                        key = prefix + key
                        minval = "%s [%s]" % (
                            key,
                            convSize(usage[minIdx] << 20),
                        )
                        maxsize = convSize(usage[maxIdx] << 20)
                        maxval = "%s [%s]" % (key, maxsize)
                        lastsize = convSize(usage[-1] << 20)
                        lastval = "%s [%s]" % (key, lastsize)

                        color = None
                        if stacked:
                            stackedStats.append(usage)
                        else:
                            # ignore 0 #
                            usage = list(
                                map(lambda x: x if x != 0 else None, usage)
                            )

                            # get plot attr #
                            lw, pe = _getPlotAttr(key)

                            # get color #
                            color = plot(
                                timeline,
                                usage,
                                "-",
                                linewidth=lw,
                                path_effects=pe,
                            )[0].get_color()

                        # add text #
                        if stacked:
                            stackedTexts.append(maxval)
                        elif hasLabel:
                            minv = usage[minIdx]
                            maxv = usage[maxIdx]

                            # text at min #
                            if minv:
                                text(
                                    timeline[minIdx],
                                    minv + margin,
                                    minval,
                                    color=color,
                                    fontsize=self.lfsize,
                                    rotation=35,
                                    ha=_getTextAlign(minIdx, timeline),
                                )

                            # text at max #
                            if maxv and minv != maxv:
                                text(
                                    timeline[maxIdx],
                                    maxv + margin,
                                    maxval,
                                    color=color,
                                    fontsize=self.lfsize,
                                    rotation=35,
                                    ha=_getTextAlign(maxIdx, timeline),
                                )

                            # text at last #
                            if usage[-1]:
                                text(
                                    timeline[-1],
                                    usage[-1] + margin,
                                    lastval,
                                    color=color,
                                    fontsize=self.lfsize,
                                    rotation=35,
                                    ha="right",
                                )

                        # set memory type #
                        if SysMgr.pssEnable:
                            mem = "PSS"
                        elif SysMgr.ussEnable:
                            mem = "USS"
                        else:
                            mem = "RSS"

                        labelList.append("%s [%s] - %s" % (key, mem, maxsize))

                    # draw stacked plots #
                    if stacked:
                        _drawStack(
                            timeline,
                            stackedStats,
                            stackedTexts,
                            self,
                            ax,
                            acc=False,
                        )

                # System #
                else:
                    isSysplot = True
                    if not "MEMSYSPLOT" in SysMgr.environList:
                        ymax = __drawMemPlots(ymax, stacked=True)

            """
            ylabel('MEMORY', fontsize=5)
            ax.yaxis.set_label_coords(-0.05,0.5)
            """

            # draw label #
            labelYpos = 0.75 if isSysplot else 1
            TaskAnalyzer.drawLabel(
                labelList, draw=True, anchor=(1.12, labelYpos)
            )

            grid(which="both", linestyle=":", linewidth=0.2)
            tick_params(direction="in")

            # adjust yticks #
            ylist = ax.get_yticks().tolist()

            # set ymin #
            ymin = long(min(ylist))
            if ymin < 0:
                ymin = 0
            elif ymin == 0:
                try:
                    ax.set_ylim(bottom=0)
                except:
                    pass

            # update yticks #
            if stacked:
                try:
                    ymax = max(ymax, max(sum(y) for y in zip(*stackedStats)))
                except:
                    ymax = 0
                margin = False
            else:
                if ymax < 100:
                    ymax = 100
                margin = True

            TaskAnalyzer.drawYticks(ax, ymax, margin=margin)

            try:
                # ax.get_xaxis().set_visible(False)
                ytickLabel = ax.get_yticks().tolist()
                ytickLabel = list(map(long, ytickLabel))

                # convert label units #
                ytickLabel = [convSize(val << 20) for val in ytickLabel]

                # remove redundant ticks #
                lastTick = ""
                tempLabelList = list(ytickLabel)
                for idx, ytick in enumerate(tempLabelList):
                    if lastTick == ytick:
                        ytickLabel[idx] = ""
                    else:
                        lastTick = ytick

                ax.set_yticklabels(ytickLabel)
            except SystemExit:
                sys.exit(0)
            except:
                pass

            xticks(fontsize=self.xfsize)
            if len(timeline) > 1:
                xlim([timeline[0], timeline[-1]])

            # draw name #
            TaskAnalyzer.drawName(ax, "MEM")

            # draw base #
            self.figure = TaskAnalyzer.drawFigure()

            # draw systemd events #
            if not self.targetEventDrawn:
                _drawTargetEvent(graphStats, timeline, ax)
                _drawUnitEvent(graphStats, timeline, ax)
                self.targetEventDrawn = True

            self.drawBottom(xtype, ax, timeline, nrTask, effectProcList)

        # ==================== BODY PART ====================#

        SysMgr.printStat(r"start drawing graphs...")

        """
        initialize list that count the number of process
        using resource more than 1% #
        """

        # get timeline #
        if "timeline" in graphStats:
            timeline = graphStats["timeline"]
        else:
            timeline = []
            for key, val in graphStats.items():
                if key.endswith("timeline") and len(val) > len(timeline):
                    timeline = val

        # get nrCore #
        if "nrCore" in graphStats:
            nrCore = graphStats["nrCore"]
        else:
            nrCore = []
            for key, val in graphStats.items():
                if key.endswith("nrCore") and len(val) > len(nrCore):
                    nrCore = val

        # get nrTask #
        if "nrTask" in graphStats:
            nrTask = graphStats["nrTask"]
        else:
            nrTask = []
            for key, val in graphStats.items():
                if key.endswith("nrTask") and len(val) > len(nrTask):
                    nrTask = val

        # get effectProcList #
        effectProcList = [0] * len(timeline)

        # draw plots #
        if not SysMgr.layout:
            _drawCpu(graphStats, 3, 0, 4)
            _drawEvent(graphStats, logEvents)
            _drawIo(graphStats, 2, 4, 1)
            _drawMem(graphStats, 1, 5, 1)
        else:
            self.drawLayout(
                graphStats,
                _drawCpu,
                _drawMem,
                _drawIo,
                _drawPrio,
                _drawEvent,
                logEvents,
            )

        # draw system info #
        try:
            if (
                not SysMgr.sysinfoBuffer
                or len(SysMgr.sysinfoBuffer) == 0
                or "NOSYSINFO" in SysMgr.environList
            ):
                raise ValueError()

            if not SysMgr.origArgs:
                SysMgr.origArgs = ["None"]

            # add draw command #
            drawCmdStr = "{0:20} # {1:<100}".format(
                "DrawCmd", " ".join(SysMgr.origArgs)
            )
            if not drawCmdStr in SysMgr.sysinfoBuffer:
                SysMgr.sysinfoBuffer = (
                    "<System Info>\n" + SysMgr.sysinfoBuffer[:-1] + drawCmdStr
                )

            # add file event info #
            try:
                if TaskAnalyzer.fileNameTable:
                    SysMgr.sysinfoBuffer += "\n\n<File Info>"
                for path, name in sorted(
                    TaskAnalyzer.fileNameTable.items(),
                    key=lambda e: long(e[1].lstrip("#")),
                ):
                    SysMgr.sysinfoBuffer += "\n%s: %s" % (name, path)

            except SystemExit:
                sys.exit(0)
            except:
                pass

            # draw figure text #
            self.figure.text(
                0, 1, SysMgr.sysinfoBuffer, va="top", ha="left", size=2
            )
        except ValueError:
            pass
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn("failed to write system info", True, reason=True)

        # remove stats to free memory #
        graphStats.clear()

        # save to file #
        return TaskAnalyzer.saveImage(
            logFile, "graph", outFile=outFile, fd=outFd if outFd else None
        )

    def getMargin(self):
        # pylint: disable=undefined-variable

        ytick = yticks()[0]
        if len(ytick) > 1:
            margin = (ytick[1] - ytick[0]) / 10
        else:
            margin = 0

        return margin

    @staticmethod
    def drawStack(
        statList,
        inputFile=[],
        xnames=[],
        labels=[],
        gname=None,
        colors=[],
        outFile=None,
    ):
        # pylint: disable=undefined-variable

        if not inputFile and not statList:
            SysMgr.printErr("no input for stack plot")
            sys.exit(-1)

        # convert input value type to list #
        fileName, inputName, inputList = UtilMgr.getInputNames(inputFile)

        # initialize environment for drawing #
        TaskAnalyzer.initDrawEnv()

        # parse stats #
        if inputFile:
            SysMgr.printErr("not implemented yet")
            sys.exit(-1)

        # check stat #
        if not statList:
            SysMgr.printErr("no stat for stack plot")
            sys.exit(-1)

        # set frame #
        _, ax = subplots()

        if not gname:
            gname = "Guider Stack Plot"

        # draw title #
        ax.xaxis.set_major_locator(MaxNLocator(integer=True))
        suptitle(gname, fontsize=8)

        # get colors #
        random = SysMgr.getPkg("random")
        if not colors:
            colors = [
                (random.random(), random.random(), random.random())
                for _ in range(len(statList))
            ]

        # draw plots #
        try:
            stackplot(xnames, *statList, labels=labels, colors=colors)
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to draw stack plot", True)
            sys.exit(-1)

        # save to file #
        TaskAnalyzer.saveImage(fileName, "stack_plot", outFile=outFile)

    @staticmethod
    def drawViolin(
        inputFile=[],
        statList=[],
        nameList=[],
        xname=[],
        yname=[],
        outFile=None,
        posInfo=[],
        subNameList=[],
        gname=None,
    ):
        # pylint: disable=undefined-variable

        """
        function call example:
        - statList: value list
        - nameList: plot name list
        - posInfo: position info list (grid, pos, rowspan, colspan)
        - subNameList: subplot(grid) name list

        drawViolin(
            statList = [
                range(1, 10),
                range(5, 10),
                range(9, 20),
                range(10, 20),
            ],
            nameList = [
                "test1",
                "test2",
                "test3",
                "test4",
            ],
            posInfo = (
                [(3, 3), (0, 0), 1, 1],
                [(3, 3), (1, 1), 1, 1],
                [(3, 3), (2, 2), 1, 1],
                [(3, 3), (0, 0), 1, 1],
            ),
            subNameList = [
                "subtest1",
                "subtest2",
                "subtest3",
                "subtest1",
            ],
        )
        """

        # check input #
        if not inputFile and not statList:
            SysMgr.printErr("no input for violin plot")
            sys.exit(-1)

        # convert input value type to list #
        fileName, inputName, inputList = UtilMgr.getInputNames(inputFile)

        # initialize environment for drawing #
        TaskAnalyzer.initDrawEnv()

        # draw base #
        TaskAnalyzer.drawFigure()

        # parse stats #
        if inputFile:
            totalStat = {}

            # get target list #
            if "TARGETDRAW" in SysMgr.environList:
                targetList = ",".join(SysMgr.environList["TARGETDRAW"]).split(
                    ","
                )
            else:
                targetList = ["*"]

            for fname in inputList:
                graphStats, chartStats = TaskAnalyzer.getStatsFile(
                    logFile=fname,
                    applyOpt=True,
                )

                # merge stats #
                for n, v in graphStats.items():
                    # check system stat #
                    if not UtilMgr.isValidStr(n, targetList) or not v:
                        continue

                    # handle storage and network usage #
                    if n in ("storageUsage", "networkUsage"):
                        slist = {
                            "read": [],
                            "write": [],
                            "recv": [],
                            "tran": [],
                        }
                        for val in v.values():
                            for key, sv in val.items():
                                if not key in slist:
                                    continue

                                if not slist[key]:
                                    slist[key] = sv
                                    continue

                                slist[key] = [
                                    sum(x) for x in zip(slist[key], sv)
                                ]

                        # set name #
                        if slist["read"]:
                            name = "storageUsage"
                        else:
                            name = "networkUsage"

                        for key, sv in slist.items():
                            if not sv:
                                continue

                            key = name + key[0].upper() + key[1:]

                            # handle not merged list #
                            if "EACHFILE" in SysMgr.environList:
                                totalStat["%s@%s" % (key, name)] = sv
                                continue

                            if not key in totalStat:
                                totalStat[key] = []

                            totalStat[key] += sv

                        continue
                    # handle gpu usage #
                    elif n == "gpuUsage":
                        nv = []
                        for val in v.values():
                            nv += list(map(long, val.strip().split()))
                        v = nv

                    # TODO: handle process stats with -g filter option #

                    # check type #
                    if not isinstance(v, list):
                        continue

                    # handle not merged list #
                    if "EACHFILE" in SysMgr.environList:
                        totalStat["%s@%s" % (n, fname)] = v
                        continue

                    if not n in totalStat:
                        totalStat[n] = []

                    totalStat[n] += v

            # split statDict to statList and nameList #
            for n, v in totalStat.items():
                if (
                    n.startswith("cpuUsage")
                    or n.startswith("gpuUsage")
                    or n.startswith("blkWait")
                ):
                    if sum(v) == 0:
                        continue

                    posInfo.append([(2, 2), (0, 0), 1, 1])
                    subNameList.append("Processor")
                    yname.append("%")
                elif (
                    n.startswith("memFree")
                    or n.startswith("memAnon")
                    or n.startswith("memCache")
                ):
                    if sum(v) == 0:
                        continue

                    posInfo.append([(2, 2), (0, 1), 1, 1])
                    subNameList.append("RAM")
                    yname.append("MB")
                elif (
                    n.startswith("blkRead")
                    or n.startswith("blkWrite")
                    or n.startswith("netRead")
                    or n.startswith("netWrite")
                    or n.startswith("networkUsage")
                    or n.startswith("swapUsage")
                    or n.startswith("reclaimBg")
                    or n.startswith("reclaimDr")
                    or n.startswith("storageUsage")
                ):
                    if sum(v) == 0:
                        continue

                    posInfo.append([(2, 2), (1, 0), 1, 2])
                    subNameList.append("I/O")
                    yname.append("KB")
                else:
                    continue

                nameList.append("System" + n[0].upper() + n[1:])
                statList.append(v)

        # check stat #
        if not statList:
            SysMgr.printErr("no stat for violin plot")
            sys.exit(-1)

        convNum = UtilMgr.convNum
        posList = {}
        newSubNameList = []
        newXnameList = []
        newYnameList = []

        # init subNameList #
        if not subNameList:
            subNameList = [""] * len(posInfo)

        # set pos info #
        try:
            if not posInfo:
                posList = [0]
                newXnameList = xname
                newXnameList = yname
                raise ValueError("no pos info")

            # merge data by pos info #
            for idx, pos in enumerate(posInfo):
                posStr = str(pos)
                if posStr in posList:
                    posList[posStr][1].append(list(statList[idx]))
                    posList[posStr][2].append(nameList[idx])
                else:
                    posList[posStr] = [
                        pos,
                        [list(statList[idx])],
                        [nameList[idx]],
                    ]

                    # add subName #
                    if idx < len(subNameList):
                        newSubNameList.append(subNameList[idx])
                    else:
                        raise Exception("no subNameList")

                    if len(xname) > idx:
                        newXnameList.append(xname[idx])
                    if len(yname) > idx:
                        newYnameList.append(yname[idx])
        except ValueError:
            pass
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to parse pos info", True)
            return

        # set frame #
        _, ax = subplots()

        if not gname:
            gname = "Guider Violin Plot"

        # draw title #
        ax.xaxis.set_major_locator(MaxNLocator(integer=True))
        suptitle(gname, fontsize=8)

        # define scatter function #
        def _drawScatter(v, statList, doScatter):
            random = SysMgr.getPkg("random")
            for xi, xv in enumerate(v["bodies"]):
                xv.set_facecolor(
                    (random.random(), random.random(), random.random())
                )
                xv.set_edgecolor("yellow")
                xv.set_alpha(0.5)

                pval = xv.get_paths()[0].vertices[:, 0]
                start = pval.min()
                end = pval.max()
                itemCnt = len(statList[xi])
                inc = (end - start) / itemCnt

                # skip scatter #
                if not doScatter:
                    continue

                # get range #
                xtickRange = UtilMgr.getRange(start, end, inc)

                # apply jitter #
                xtickRange = [
                    val + random.uniform(-0.1, 0.1) for val in xtickRange
                ]

                # draw scatter #
                scatter(xtickRange[:itemCnt], statList[xi], s=0.1, alpha=0.5)

            # change line widths #
            for vn, vv in v.items():
                try:
                    vv.set_linewidths(0.5)
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

        # define label maker function #
        def _makeLabel(nameList, totalLabel):
            for idx, name in enumerate(nameList):
                target = statList[idx]
                cnt = len(target)
                avg = convNum(long(sum(target) / cnt))
                cnt = convNum(cnt)
                minv = convNum(min(target))
                maxv = convNum(max(target))
                stdev = UtilMgr.getStdev(target)
                totalLabel.append(
                    "Avg(%s) Std(%.1f) Cnt(%s) Min(%s) Max(%s) [%s]"
                    % (avg, stdev, cnt, minv, maxv, name)
                )

        # define label converter function #
        def _convNameLabel(fileList):
            newList = []
            for idx, name in enumerate(fileList):
                if idx & 1:
                    name = "\n%s" % name
                newList.append(name)
            return newList

        # define tick and label apply function #
        def _applyTickLabel(items, nameList, xname, yname, index, ax):
            # set dimension #
            try:
                xticks(items, fontsize=TaskAnalyzer.xfsize)
                ax.set_xticklabels(_convNameLabel(nameList))
                if len(xname) > index:
                    ax.set_xlabel(xname[index])
                    ax.xaxis.label.set_size(5)
                if len(yname) > index:
                    ax.set_ylabel(yname[index])
                    ax.yaxis.label.set_size(5)
                ax.grid(color="w", linewidth=0.1)
            except:
                pass

            # apply formatter #
            try:
                # convert label units #
                ytickLabel = ax.get_yticks().tolist()
                ytickLabel = list(map(long, ytickLabel))
                ytickLabel = [convNum(val) for val in ytickLabel]
                ax.set_yticklabels(ytickLabel)
            except:
                pass

        # check NOSCATTER flag #
        doScatter = not "NOSCATTER" in SysMgr.environList

        for index, pos in enumerate(posList):
            if posInfo:
                if index == 0:
                    ax.remove()

                posData, statList, nameList = posList[pos]

                ax = subplot2grid(
                    posData[0],
                    posData[1],
                    rowspan=posData[2],
                    colspan=posData[3],
                )
                ax.xaxis.set_major_locator(MaxNLocator(integer=True))
                subplots_adjust(
                    top=0.90,
                    bottom=0.05,
                    left=0.05,
                    right=0.95,
                    hspace=0.4,
                    wspace=0.3,
                )

                # draw title #
                ax.xaxis.set_major_locator(MaxNLocator(integer=True))
                ax.set_title(newSubNameList[index], fontsize=5)

                # set style #
                [x.set_linewidth(0.2) for x in ax.spines.values()]
                tick_params(direction="in", length=2, width=0.5)

                # sort values #
                if "SORT" in SysMgr.environList:
                    for s in statList:
                        s.sort()

                # set range values #
                minVal = min([min(s) for s in statList])
                maxVal = max([max(s) for s in statList])
                items = range(len(statList))

                # draw plots #
                v = ax.violinplot(statList, positions=items, showmeans=True)

                # draw scatters #
                _drawScatter(v, statList, doScatter)

                # make stats for label #
                totalLabel = []
                _makeLabel(nameList, totalLabel)

                # draw label #
                TaskAnalyzer.drawLabel(totalLabel, draw=True, anchor=(1.12, 1))

                # update yticks #
                TaskAnalyzer.drawYticks(ax, maxVal, adjust=True, ymin=minVal)

                # apply ticks and labels #
                _applyTickLabel(
                    items, nameList, newXnameList, newYnameList, index, ax
                )

                continue

            # set temporary names #
            if not nameList:
                nameList = [chr(c) for c in range(ord("A"), ord("Z") + 1)]
                nameList = nameList[: len(statList)]

            # set range values #
            minVal = min([min(s) for s in statList])
            maxVal = max([max(s) for s in statList])
            items = range(len(statList))

            # draw plots #
            v = ax.violinplot(statList, positions=items, showmeans=True)

            # draw scatters #
            _drawScatter(v, statList, doScatter)

            # make stats for label #
            totalLabel = []
            _makeLabel(nameList, totalLabel)

            # draw label #
            TaskAnalyzer.drawLabel(totalLabel, draw=True, anchor=(1.12, 1))

            # update yticks #
            TaskAnalyzer.drawYticks(ax, maxVal)

            # apply ticks and labels #
            _applyTickLabel(
                items, nameList, newXnameList, newYnameList, index, ax
            )

            break

        # save to file #
        TaskAnalyzer.saveImage(fileName, "violin_plot", outFile=outFile)

    def drawAvgGraph(self, graphStats, logFile, outFile=None, outFd=None):
        def _getTextAlign(idx, timeline):
            if idx < len(timeline) / 4:
                return "left"
            elif idx > len(timeline) / 4 * 3:
                return "right"
            else:
                return "center"

        def _getPathEffect(lw=2):
            try:
                pe = SysMgr.getPkg("matplotlib.patheffects", False)
                return [
                    pe.Stroke(linewidth=lw, foreground="black"),
                    pe.Normal(),
                ]
            except SystemExit:
                sys.exit(0)
            except:
                return []

        def _getPlotAttr(idx):
            try:
                lw = 0.7
                pe = []

                if not "STROKE" in SysMgr.environList:
                    pass
                elif UtilMgr.isValidStr(idx, SysMgr.environList["STROKE"]):
                    lw = 1
                    pe = _getPathEffect()
            except:
                pass

            return lw, pe

        def _convNameLabel(fileList):
            newList = []
            for idx, name in enumerate(fileList):
                if idx & 1:
                    name = "\n%s" % name
                newList.append(name)
            return newList

        def _drawAvgCpu(graphStats, xtype, pos, size):
            # pylint: disable=undefined-variable

            # draw title #
            ax = subplot2grid((6, 1), (pos, 0), rowspan=size, colspan=1)
            ax.xaxis.set_major_locator(MaxNLocator(integer=True))
            suptitle("Guider Average Graph", fontsize=8)

            # define common label list #
            ymax = 0
            labelList = []

            # create new timeline #
            timeline = xrange(len(graphStats["timeline"]))
            lent = len(timeline)

            cpuUsage = graphStats["cpuUsage"]
            cpuProcUsage = graphStats["cpuProcUsage"]
            blkWait = graphStats["blkWait"]
            blkProcUsage = graphStats["blkProcUsage"]
            try:
                gpuUsage = graphStats["gpuUsage"]
            except:
                gpuUsage = {}
            nrCore = graphStats["nrCore"]
            maxCore = max(nrCore)
            conv = UtilMgr.convNum

            # convert total CPU usage by core number #
            if not SysMgr.cpuAvgEnable:
                cpuUsage = [maxCore * i for i in cpuUsage]

            # set visible total usage flag #
            if SysMgr.showAll or not SysMgr.filterGroup:
                isVisibleTotal = True
            else:
                isVisibleTotal = False

            # add boundary line #
            self.drawBoundary("cpu", labelList)

            # draw user event #
            self.drawUserEvent("cpu")

            # get plot path effect #
            pe = _getPathEffect()

            # -------------------- Total GPU usage -------------------- #
            if isVisibleTotal and SysMgr.gpuEnable:
                for gpu, stat in gpuUsage.items():
                    if UtilMgr.isString(stat):
                        stat = list(map(long, stat.split()))[:lent]
                    else:
                        stat = stat[:lent]

                    try:
                        if min(stat) == max(stat):
                            continue
                    except:
                        pass

                    # draw total GPU graph #
                    plot(
                        timeline,
                        stat,
                        "-",
                        c="olive",
                        linestyle="-",
                        linewidth=2,
                        marker=".",
                        markersize=1,
                        path_effects=pe,
                        solid_capstyle="round",
                    )

                    maxUsage = max(stat)
                    maxIdx = stat.index(maxUsage)
                    labelList.append("[ %s ] - %s%%" % (gpu, maxUsage))

                    margin = self.getMargin()

                    for idx, usage in enumerate(stat):
                        if usage == 0:
                            continue
                        elif idx == maxIdx:
                            name = "[ %s ] - %d%%" % (gpu, usage)
                        else:
                            name = "%d%%" % usage

                        text(
                            timeline[idx],
                            usage + margin,
                            name,
                            fontsize=self.lfsize + 1,
                            color="olive",
                            fontweight="bold",
                            bbox=dict(
                                boxstyle="round", facecolor="wheat", alpha=0.3
                            ),
                            ha=_getTextAlign(idx, timeline),
                        )

            # -------------------- Total CPU usage -------------------- #
            if isVisibleTotal:
                for idx, item in enumerate(blkWait):
                    blkWait[idx] += cpuUsage[idx]

                for targetUsage, color, name in (
                    (
                        blkWait,
                        "pink",
                        "CPU(%s)+IOWAIT Average" % conv(maxCore),
                    ),
                    (cpuUsage, "red", "CPU(%s) Average" % conv(maxCore)),
                ):
                    # draw plots #
                    plot(
                        timeline,
                        targetUsage,
                        "-",
                        c=color,
                        linestyle="-",
                        linewidth=2,
                        marker=".",
                        markersize=1,
                        path_effects=pe,
                        solid_capstyle="round",
                    )

                    # add label #
                    maxUsage = max(targetUsage)
                    maxIdx = targetUsage.index(maxUsage)
                    labelList.append("[ %s ] - %.1f%%" % (name, maxUsage))

                    # update the maximum ytick #
                    if ymax < maxUsage:
                        ymax = maxUsage

                    margin = self.getMargin()

                    for idx, usage in enumerate(targetUsage):
                        if usage == 0:
                            continue
                        elif idx == maxIdx:
                            name = "[ %s ] - %d%%" % (name, usage)
                        else:
                            name = "%d%%" % usage

                        text(
                            timeline[idx],
                            usage + margin,
                            name,
                            fontsize=self.lfsize + 1,
                            color=color,
                            fontweight="bold",
                            bbox=dict(
                                boxstyle="round", facecolor="wheat", alpha=0.3
                            ),
                            ha=_getTextAlign(idx, timeline),
                        )

            # -------------------- Process CPU usage -------------------- #
            # Total Process CPU usage filtered #
            if (
                "[ TOTAL ]" in cpuProcUsage
                and len(cpuProcUsage) > 2
                and not "NOTOTAL" in SysMgr.environList
            ):
                totalUsage = cpuProcUsage["[ TOTAL ]"]
                totalUsage = list(map(long, totalUsage))[:lent]

                # draw total graph #
                plot(
                    timeline,
                    totalUsage,
                    "-",
                    c="green",
                    linestyle="-",
                    linewidth=2,
                    marker=".",
                    markersize=1,
                    path_effects=pe,
                    solid_capstyle="round",
                )

                maxUsage = max(totalUsage)
                maxIdx = totalUsage.index(maxUsage)
                prefix = "TOTAL"
                labelList.append("[ %s ] - %d%%" % (prefix, maxUsage))

                margin = self.getMargin()

                # update the maximum ytick #
                if ymax < maxUsage:
                    ymax = maxUsage

                for idx, usage in enumerate(totalUsage):
                    if usage == 0:
                        continue
                    elif idx == maxIdx:
                        name = "[ %s ] - %d%%" % (prefix, usage)
                    else:
                        name = "%d%%" % usage

                    text(
                        timeline[idx],
                        usage + margin,
                        name,
                        fontsize=self.lfsize + 1,
                        color="green",
                        fontweight="bold",
                        bbox=dict(
                            boxstyle="round", facecolor="wheat", alpha=0.3
                        ),
                        ha=_getTextAlign(idx, timeline),
                    )

            # get max cpu condition #
            maxCpuCond = UtilMgr.getEnvironNum(
                "MAXCPUCOND", False, 0, False, True
            )

            # get avg cpu condition #
            avgCpuCond = UtilMgr.getEnvironNum(
                "AVGCPUCOND", False, 0, False, True
            )

            # get min cpu condition #
            if "MINCPUCOND" in SysMgr.environList:
                minCpuCond = UtilMgr.getEnvironNum(
                    "MINCPUCOND", False, 0, False, True
                )
            else:
                minCpuCond = 0

            # define top variable #
            tcnt = 0

            # Process CPU usage #
            for idx, item in sorted(
                cpuProcUsage.items(),
                key=lambda e: sum(list(map(long, e[1]))),
                reverse=True,
            ):
                if not SysMgr.cpuEnable:
                    break

                if idx == "[ TOTAL ]":
                    continue

                usage = list(map(long, item))[:lent]
                cpuUsage = list(usage)

                totalUsage = sum(cpuUsage)

                try:
                    avgUsage = round(totalUsage / len(cpuUsage), 1)
                except:
                    avgUsage = 0

                # check avg cpu condition #
                if avgUsage < avgCpuCond:
                    continue

                # update the maximum ytick #
                maxusage = max(usage)
                if ymax < maxusage:
                    ymax = maxusage

                # check max cpu condition #
                if maxusage < maxCpuCond:
                    continue

                # check min cpu condition #
                if min(usage) < minCpuCond:
                    continue

                # check top number #
                if SysMgr.nrTop:
                    if tcnt >= SysMgr.nrTop:
                        break
                    else:
                        tcnt += 1

                if not SysMgr.blockEnable:
                    # merge CPU usage and wait time for processes #
                    try:
                        blkUsage = blkProcUsage[idx]
                        blkUsage = list(map(long, blkUsage))
                        for interval, value in enumerate(blkUsage):
                            usage[interval] += value
                    except SystemExit:
                        sys.exit(0)
                    except:
                        pass

                # get plot attr #
                lw, pe = _getPlotAttr(idx)

                maxIdx = usage.index(maxusage)
                color = plot(timeline, usage, "-", path_effects=pe)[
                    0
                ].get_color()

                margin = self.getMargin()

                maxCpuPer = str(cpuUsage[maxIdx])

                """
                if idx in blkProcUsage and not SysMgr.blockEnable:
                    maxBlkPer = str(blkUsage[maxIdx])
                else:
                    maxBlkPer = "0"
                """

                for pidx, usage in enumerate(cpuUsage):
                    if usage == 0:
                        continue

                    if (
                        "[ TOTAL ]" in cpuProcUsage
                        and len(cpuProcUsage) > 2
                        and cpuProcUsage["[ TOTAL ]"][pidx] == usage
                    ):
                        continue

                    if pidx == maxIdx:
                        name = "%s - %d%%" % (idx, usage)
                    else:
                        name = "%d%%" % usage

                    text(
                        timeline[pidx],
                        usage + margin,
                        name,
                        fontsize=self.lfsize + 1,
                        color=color,
                        fontweight="bold",
                        ha=_getTextAlign(maxIdx, timeline),
                    )

                labelList.append("%s - %s%%" % (idx, maxCpuPer))

            cpuProcUsage.pop("[ TOTAL ]", None)

            # draw label #
            TaskAnalyzer.drawLabel(labelList, draw=True, anchor=(1.12, 1.05))

            grid(which="both", linestyle=":", linewidth=0.2)
            tick_params(direction="in")

            # update ymax #
            if SysMgr.funcDepth > 0:
                ymax = SysMgr.funcDepth

            # set xticks attributes #
            ax.set_xticklabels(_convNameLabel(graphStats["fileList"]))
            xticks(fontsize=self.xfsize)
            if len(timeline) > 1:
                xlim([timeline[0], timeline[-1]])

            # update yticks #
            TaskAnalyzer.drawYticks(ax, ymax)

            # add % unit to each value #
            try:
                # convert label units #
                ytickLabel = ax.get_yticks().tolist()
                ytickLabel = list(map(long, ytickLabel))
                ytickLabel = ["%s%%" % val for val in ytickLabel]
                ytickLabel[0] = ""
                ax.set_yticklabels(ytickLabel)

                # remove space between axis and label #
                tick_params(pad=1)
            except SystemExit:
                sys.exit(0)
            except:
                pass

            # draw name #
            TaskAnalyzer.drawName(ax, "CPU")

            # draw base #
            self.figure = TaskAnalyzer.drawFigure()

        def _drawAvgMem(graphStats, xtype, pos, size):
            # pylint: disable=undefined-variable
            def __drawSystemMem(statList, color, ymax):
                usage = list(map(long, statList))
                minIdx = usage.index(min(usage))
                maxIdx = usage.index(max(usage))

                if usage[minIdx] == usage[maxIdx] == 0:
                    return None, ymax

                # update the maximum ytick #
                maxUsage = max(usage)
                if ymax < maxUsage:
                    ymax = maxUsage

                # get plot path effect #
                pe = _getPathEffect()

                plot(
                    timeline,
                    usage,
                    "-",
                    c=color,
                    linewidth=2,
                    path_effects=pe,
                    solid_capstyle="round",
                )

                for pidx, value in enumerate(usage):
                    if value == 0:
                        continue
                    size = convSize2Unit(value << 20)
                    text(
                        timeline[pidx],
                        value,
                        size,
                        color=color,
                        fontsize=self.lfsize + 1,
                        ha=_getTextAlign(maxIdx, timeline),
                    )

                lastsize = UtilMgr.convSize2Unit(usage[-1] << 20)

                return lastsize, ymax

            # define common label list #
            ymax = 0
            labelList = []

            # set convert size #
            convSize2Unit = UtilMgr.convSize2Unit

            # draw title #
            ax = subplot2grid((6, 1), (pos, 0), rowspan=size, colspan=1)
            ax.xaxis.set_major_locator(MaxNLocator(integer=True))
            suptitle(graphStats["graphTitle"], fontsize=8)

            # add boundary line #
            self.drawBoundary("mem", labelList)

            # draw user event #
            self.drawUserEvent("mem")

            # define top variable #
            tcnt = 0

            # create new timeline #
            timeline = xrange(len(graphStats["timeline"]))
            lent = len(timeline)

            totalRam = graphStats["totalRam"]
            memFree = graphStats["memFree"]
            memAnon = graphStats["memAnon"]
            memCache = graphStats["memCache"]
            memProc = graphStats["memProcUsage"]
            totalSwap = graphStats["totalSwap"]
            swapUsage = graphStats["swapUsage"]

            # get margin #
            margin = self.getMargin()

            # Process VSS #
            if SysMgr.vssEnable:
                # get max vss condition #
                maxVssCond = UtilMgr.getEnvironNum(
                    "MAXVSSCOND", False, 0, False, True
                )

                for key, item in sorted(
                    memProc.items(),
                    key=lambda e: sum(list(map(long, e[1]))),
                    reverse=True,
                ):
                    usage = list(map(long, item))[:lent]

                    # update the maximum ytick #
                    maxUsage = max(usage)
                    if ymax < maxUsage:
                        ymax = maxUsage
                    maxIdx = usage.index(maxUsage)

                    # check max vss conditino #
                    if maxUsage < maxVssCond:
                        continue

                    # check top number #
                    if SysMgr.nrTop:
                        if tcnt >= SysMgr.nrTop:
                            break
                        else:
                            tcnt += 1

                    # get plot attr #
                    lw, pe = _getPlotAttr(key)

                    # get color #
                    color = plot(
                        timeline, usage, "-", linewidth=1, path_effects=pe
                    )[0].get_color()

                    lastUsage = "?"
                    for pidx, value in enumerate(usage):
                        if value == 0:
                            continue

                        lastUsage = size = convSize2Unit(value << 20)

                        if pidx == maxIdx:
                            name = "%s - %s" % (key, size)
                        else:
                            name = size

                        text(
                            timeline[pidx],
                            value + margin,
                            name,
                            color=color,
                            fontsize=self.lfsize + 1,
                            ha=_getTextAlign(maxIdx, timeline),
                        )

                    labelList.append("%s [VSS] - %s" % (key, lastUsage))

            # Process RSS #
            elif SysMgr.rssEnable or SysMgr.pssEnable or SysMgr.ussEnable:
                # get max rss condition #
                maxRssCond = UtilMgr.getEnvironNum(
                    "MAXRSSCOND", False, 0, False, True
                )

                for key, item in sorted(
                    memProc.items(),
                    key=lambda e: sum(list(map(long, e[1]))),
                    reverse=True,
                ):
                    usage = list(map(long, item))[:lent]

                    # update the maximum ytick #
                    maxUsage = max(usage)
                    if ymax < maxUsage:
                        ymax = maxUsage
                    maxIdx = usage.index(maxUsage)

                    # check max rss conditino #
                    if maxUsage < maxRssCond:
                        continue

                    # check top number #
                    if SysMgr.nrTop:
                        if tcnt >= SysMgr.nrTop:
                            break
                        else:
                            tcnt += 1

                    # get plot attr #
                    lw, pe = _getPlotAttr(key)

                    # get color #
                    color = plot(
                        timeline, usage, "-", linewidth=1, path_effects=pe
                    )[0].get_color()

                    lastUsage = "?"
                    for pidx, value in enumerate(usage):
                        if value == 0:
                            continue

                        lastUsage = size = convSize2Unit(value << 20)

                        if pidx == maxIdx:
                            name = "%s - %s" % (key, size)
                        else:
                            name = size

                        text(
                            timeline[pidx],
                            value + margin,
                            name,
                            color=color,
                            fontsize=self.lfsize + 1,
                            ha=_getTextAlign(maxIdx, timeline),
                        )

                    # set memory type #
                    if SysMgr.pssEnable:
                        mem = "PSS"
                    elif SysMgr.ussEnable:
                        mem = "USS"
                    else:
                        mem = "RSS"

                    labelList.append("%s [%s] - %s" % (key, mem, lastUsage))

            # System #
            else:
                # System Available(Free) Memory #
                lastsize, ymax = __drawSystemMem(memFree, "blue", ymax)
                if lastsize is not None:
                    if totalRam:
                        label = "< RAM Total > - %s\nRAM Available - %s" % (
                            convSize2Unit(totalRam),
                            lastsize,
                        )
                        labelList.append(label)
                    else:
                        labelList.append("RAM Available - %s" % lastsize)

                # System User(Anon) Memory #
                lastsize, ymax = __drawSystemMem(memAnon, "skyblue", ymax)
                if lastsize is not None:
                    labelList.append("RAM User - %s" % lastsize)

                # System Cache Memory #
                lastsize, ymax = __drawSystemMem(memCache, "darkgray", ymax)
                if lastsize is not None:
                    labelList.append("RAM Cache - %s" % lastsize)

                # System Swap Memory #
                lastsize, ymax = __drawSystemMem(swapUsage, "orange", ymax)
                if lastsize is not None:
                    if totalSwap:
                        label = "< Swap Total > - %s\nSwap Usage - %s" % (
                            convSize2Unit(totalSwap),
                            lastsize,
                        )
                        labelList.append(label)
                    else:
                        labelList.append("Swap Usage - %s" % lastsize)

            """
            ylabel('MEMORY', fontsize=5)
            ax.yaxis.set_label_coords(-0.05,0.5)
            """

            # draw label #
            TaskAnalyzer.drawLabel(labelList, draw=True, anchor=(1.12, 0.75))

            grid(which="both", linestyle=":", linewidth=0.2)
            tick_params(direction="in")

            # update yticks #
            TaskAnalyzer.drawYticks(ax, ymax)

            try:
                # ax.get_xaxis().set_visible(False)
                ytickLabel = ax.get_yticks().tolist()
                ytickLabel = list(map(long, ytickLabel))

                # convert label units #
                ytickLabel = [convSize2Unit(val << 20) for val in ytickLabel]

                # remove redundant ticks #
                lastTick = ""
                tempLabelList = list(ytickLabel)
                for idx, ytick in enumerate(tempLabelList):
                    if lastTick == ytick:
                        ytickLabel[idx] = ""
                    else:
                        lastTick = ytick
                ytickLabel[0] = ""
                ax.set_yticklabels(ytickLabel)

                # remove space between axis and label #
                tick_params(pad=1)
            except SystemExit:
                sys.exit(0)
            except:
                pass

            # set xticks attributes #
            ax.set_xticklabels(_convNameLabel(graphStats["fileList"]))
            xticks(fontsize=self.xfsize)
            if len(timeline) > 1:
                xlim([timeline[0], timeline[-1]])

            # draw name #
            TaskAnalyzer.drawName(ax, "MEM")

            # draw base #
            self.figure = TaskAnalyzer.drawFigure()

        SysMgr.printStat(r"start drawing average graphs...")

        # draw All #
        if SysMgr.checkMode("drawavg", True):
            if not SysMgr.layout:
                _drawAvgCpu(graphStats, 3, 0, 4)
                _drawAvgMem(graphStats, 1, 4, 2)
            else:
                self.drawLayout(graphStats, _drawAvgCpu, _drawAvgMem)

        # draw CPU #
        elif SysMgr.checkMode("drawcpuavg", True):
            _drawAvgCpu(graphStats, 3, 0, 6)
        # draw Memory #
        else:
            _drawAvgMem(graphStats, 3, 0, 6)

        # remove stats to free memory #
        graphStats.clear()

        # save to file #
        return TaskAnalyzer.saveImage(
            logFile, "graph", outFile=outFile, fd=outFd if outFd else None
        )

    @staticmethod
    def saveImage(logFile, itype="", outFile=None, fd=None):
        # get pylab object #
        SysMgr.importPkgItems("pylab")

        try:
            outFd = None

            # get output file name #
            if fd:
                outFd = fd
            else:
                outFd = UtilMgr.prepareForImageFile(logFile, itype, outFile)

            # save graph #
            # pylint: disable-next=undefined-variable
            savefig(outFd, dpi=SysMgr.matplotlibDpi)
            clf()  # pylint: disable=undefined-variable

            fname = outFd.name if hasattr(outFd, "name") else outFd

            # get output size #
            fsize = UtilMgr.getFileSizeStr(fname)

            SysMgr.printStat(
                "wrote %s image into '%s'%s" % (itype, fname, fsize)
            )
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                "failed to draw image to '%s'" % outFd.name
                if hasattr(outFd, "name")
                else outFd,
                True,
            )
            return

    def sampleStack(self, period):
        def _findNthStr(s, x, n, i=0):
            i = s.find(x, i)
            if n == 1 or i == -1:
                return i
            else:
                return _findNthStr(s, x, n - 1, i + len(x))

        start = time.time()

        while 1:
            for idx in list(self.stackTable):
                item = self.stackTable[idx]

                # read stack #
                try:
                    item["fd"].seek(0)
                    stack = item["fd"].read()
                except SystemExit:
                    sys.exit(0)
                except:
                    self.stackTable.pop(idx, None)
                    continue

                # cut stack length #
                try:
                    if SysMgr.funcDepth > 0:
                        nth = _findNthStr(stack, "\n", SysMgr.funcDepth)
                        stack = stack[:nth]
                except:
                    pass

                # count sampled stack #
                try:
                    item["total"] += 1
                    item["stack"][stack] += 1
                except SystemExit:
                    sys.exit(0)
                except:
                    item["stack"][stack] = 1

            if time.time() - start >= period:
                return

            # set 1ms as sampling rate #
            time.sleep(0.001)

    def getRunTaskNum(self):
        return len(self.threadData) - SysMgr.nrCore

    def printCreationTree(self, tid, loc):
        try:
            childList = self.threadData[tid]["childList"]
        except:
            return

        threadName = "%s(%s)" % (self.threadData[tid]["comm"], tid)

        if self.threadData[tid]["createdTime"] > 0:
            threadName += " /%2.3f/" % (
                self.threadData[tid]["createdTime"] - float(SysMgr.startTime)
            )

        if self.threadData[tid]["usage"] > 0:
            threadName += " <%2.3f>" % (self.threadData[tid]["usage"])

        if self.threadData[tid]["childList"]:
            threadName += " |%d|" % (len(self.threadData[tid]["childList"]))

        if self.threadData[tid]["waitChild"] > 0:
            threadName += " {%1.3f}" % (self.threadData[tid]["waitChild"])

        if self.threadData[tid]["waitParent"] > 0:
            threadName += " [%1.3f]" % (self.threadData[tid]["waitParent"])

        # set new position of line #
        newLoc = loc + 5

        if self.threadData[tid]["die"] == " ":
            life = "+ "
        else:
            life = "- "

        SysMgr.printPipe(" " * loc + life + threadName)

        if childList:
            for thread in childList:
                self.printCreationTree(thread, newLoc)

    @staticmethod
    def getCoreId(string):
        try:
            offset = string.rfind("/")
            if offset >= 0:
                return long(string[offset + 1 :])
            else:
                return -1
        except:
            return -1

    def printComInfo(self):
        convNum = UtilMgr.convNum

        # print thread tree by creation #
        if SysMgr.showAll and self.nrNewTask > 0:
            SysMgr.clearPrint()
            SysMgr.printPipe(
                (
                    "\n[Thread Creation Info] [Alive: +] [Die: -] "
                    "[CreatedTime: //] [ChildCount: ||] "
                    "[CpuUsage: <>] [WaitForChildren: {}] "
                    "[WaitOfParent: []]\n%s"
                )
                % twoLine
            )

            cnt = 0
            for key, value in sorted(
                self.threadData.items(),
                key=lambda e: e[1]["waitChild"],
                reverse=True,
            ):
                # print tree from root threads #
                if value["childList"] and value["new"] == " ":
                    cnt += 1
                    self.printCreationTree(key, 0)
            if cnt == 0:
                SysMgr.printPipe("    None")
            SysMgr.printPipe(oneLine)

        # print signal traffic #
        if SysMgr.showAll and len(self.sigData) > 0:
            lineLen = 130
            SysMgr.clearPrint()
            SysMgr.printPipe("\n[Thread Signal Info]")
            SysMgr.printPipe(twoLine[:lineLen])
            SysMgr.printPipe(
                (
                    "{0:^6} {1:>10} {2:>40}({3:>7}) {4:^10} "
                    "{5:>40}({6:>7}) |\n{7:1}"
                ).format(
                    "TYPE",
                    "TIME",
                    "SENDER",
                    "TID",
                    "SIGNAL",
                    "RECEIVER",
                    "TID",
                    twoLine[:lineLen],
                )
            )

            cnt = 0
            for val in self.sigData:
                try:
                    signal = ConfigMgr.SIG_LIST[int(val[4])]
                except:
                    signal = "SIG_%s" % val[4]

                stype = val[0]
                stime = val[1]
                stid = val[2]
                rtid = val[3]

                # skip useless signal log #
                if (
                    (stid and stid[0] == "0") or stid not in self.threadData
                ) and (
                    (rtid and rtid[0] == "0") or rtid not in self.threadData
                ):
                    continue

                try:
                    scomm = self.threadData[stid]["comm"]
                except:
                    scomm = "?"

                try:
                    rcomm = self.threadData[rtid]["comm"]
                except:
                    rcomm = "?"

                if stype == "SEND":
                    if stid.startswith("0["):
                        stid = 0

                    SysMgr.printPipe(
                        (
                            "{0:^6} {1:>10.6f} {2:>40}({3:>7}) "
                            "{4:^10} {5:>40}({6:>7}) |"
                        ).format(
                            stype, stime, scomm, stid, signal, rcomm, rtid
                        )
                    )

                    cnt += 1
                elif val[0] == "RECV":
                    SysMgr.printPipe(
                        (
                            "{0:^6} {1:>10.6f} {2:>40} {3:>7}  "
                            "{4:^10} {5:>40}({6:>7}) |"
                        ).format(stype, stime, " ", " ", signal, rcomm, rtid)
                    )

                    cnt += 1
            if cnt == 0:
                SysMgr.printPipe("    None")
            SysMgr.printPipe(oneLine[:lineLen])

        # print workqueue information #
        if self.wqData:
            totalCnt = 0
            totalUsage = 0.0

            title = (
                "{0:^32} {1:>12} {2:>10} {3:>10} "
                "{4:>10} {5:>10} {6:>10} {7:>10} {8:>6} |\n"
            ).format(
                "Name",
                "Count",
                "Usage",
                "ProcAvg",
                "ProcMax",
                "ProcMin",
                "InterMax",
                "InterMin",
                "NrTask",
            )

            lenLine = len(title) - 1

            SysMgr.printPipe(
                (
                    "\n[Thread Workqueue Info] [Elapsed: %.3f] "
                    "(Unit: Sec/NR)\n%s"
                )
                % (float(self.totalTime), twoLine[:lenLine])
            )

            SysMgr.printPipe(title + twoLine[:lenLine])

            SysMgr.clearPrint()

            # merge by name #
            wqData = {}
            for _, item in self.wqData.items():
                name = item["name"]

                # change default stat value -1 to 0 #
                for key in list(item):
                    if item[key] == -1:
                        item[key] = 0

                if not name in wqData:
                    wqData[name] = UtilMgr.deepcopy(item)
                    continue

                target = wqData[name]

                # change default stat value -1 to 0 #
                for key in list(target):
                    if target[key] == -1:
                        target[key] = 0

                target["scount"] += item["scount"]
                target["rcount"] += item["rcount"]
                target["usage"] += item["usage"]
                target["task"].update(item["task"])

                if target["max"] < item["max"]:
                    target["max"] = item["max"]
                if target["min"] > item["min"]:
                    target["min"] = item["min"]
                if target["maxPeriod"] < item["maxPeriod"]:
                    target["maxPeriod"] = item["maxPeriod"]
                if target["minPeriod"] > item["minPeriod"]:
                    target["minPeriod"] = item["minPeriod"]

            # print summary #
            for _, item in sorted(
                wqData.items(), key=lambda e: e[1]["usage"], reverse=True
            ):
                totalCnt += item["scount"]
                totalUsage += item["usage"]
                tasks = convNum(len(item["task"]))
                try:
                    avg = item["usage"] / item["rcount"]
                except:
                    avg = 0
                SysMgr.addPrint(
                    (
                        "{0:<32} {1:>12} {2:>10.6f} {3:>10.6f} "
                        "{4:>10.6f} {5:>10.6f} {6:>10.6f} {7:>10.6f} {8:>6} |\n"
                    ).format(
                        item["name"],
                        convNum(item["scount"]),
                        item["usage"],
                        avg,
                        item["max"],
                        item["min"],
                        item["maxPeriod"],
                        item["minPeriod"],
                        tasks,
                    )
                )

            totalStr = "%s# WORKQUEUE(%s) / Total(%6.3f) / Cnt(%s)" % (
                "",
                convNum(len(wqData)),
                totalUsage,
                convNum(totalCnt),
            )
            SysMgr.printPipe(
                totalStr + " " * (lenLine - len(totalStr) - 1) + "|"
            )
            SysMgr.printPipe((" " * (lenLine - 1)) + "|")
            SysMgr.doPrint(clear=True)
            SysMgr.printPipe(oneLine[:lenLine])

        # print interrupt information #
        if self.irqData:
            totalCnt = 0
            totalUsage = 0.0

            title = (
                "{0:^16} {1:<62} {2:>12} {3:>10} {4:>10} "
                "{5:>10} {6:>10} {7:>10} |\n"
            ).format(
                "IRQ",
                "Name",
                "Count",
                "Usage",
                "ProcMax",
                "ProcMin",
                "InterMax",
                "InterMin",
            )

            lenLine = len(title) - 1

            SysMgr.printPipe(
                ("\n[Thread IRQ Info] [Elapsed: %.3f] (Unit: Sec/NR)\n%s")
                % (float(self.totalTime), twoLine[:lenLine])
            )

            SysMgr.printPipe(title + twoLine[:lenLine])

            SysMgr.clearPrint()

            # print irq list #
            irqList = [
                irq for irq in list(self.irqData) if irq.startswith("irq")
            ]
            for key in sorted(irqList, key=lambda e: int(e.split("/")[1])):
                totalCnt += self.irqData[key]["count"]
                totalUsage += self.irqData[key]["usage"]
                SysMgr.addPrint(
                    (
                        "{0:>16} {1:<62} {2:>12} {3:>10.6f} {4:>10.6f} "
                        "{5:>10.6f} {6:>10.6f} {7:>10.6f}\n"
                    ).format(
                        key,
                        " | ".join(list(self.irqData[key]["name"])),
                        convNum(self.irqData[key]["count"]),
                        self.irqData[key]["usage"],
                        self.irqData[key]["max"],
                        self.irqData[key]["min"],
                        self.irqData[key]["maxPeriod"],
                        self.irqData[key]["minPeriod"],
                    )
                )

            # print softirq list #
            sirqList = [
                irq for irq in list(self.irqData) if irq.startswith("softirq")
            ]
            for key in sorted(sirqList, key=lambda e: int(e.split("/")[1])):
                totalCnt += self.irqData[key]["count"]
                totalUsage += self.irqData[key]["usage"]
                SysMgr.addPrint(
                    (
                        "{0:>16} {1:<62} {2:>12} {3:>10.6f} {4:>10.6f} "
                        "{5:>10.6f} {6:>10.6f} {7:>10.6f} |\n"
                    ).format(
                        key,
                        " | ".join(list(self.irqData[key]["name"])),
                        convNum(self.irqData[key]["count"]),
                        self.irqData[key]["usage"],
                        self.irqData[key]["max"],
                        self.irqData[key]["min"],
                        self.irqData[key]["maxPeriod"],
                        self.irqData[key]["minPeriod"],
                    )
                )

            totalStr = "%s# IRQ(%s) / Total(%6.3f) / Cnt(%s)" % (
                "",
                convNum(len(self.irqData)),
                totalUsage,
                convNum(totalCnt),
            )
            SysMgr.printPipe(
                totalStr + " " * (lenLine - len(totalStr) - 1) + "|"
            )
            SysMgr.printPipe((" " * (lenLine - 1)) + "|")
            SysMgr.doPrint(clear=True)
            SysMgr.printPipe(oneLine[:lenLine])

    def printEventInfo(self):
        convNum = UtilMgr.convNum

        # pick up event info from thread info #
        for key, value in sorted(self.threadData.items()):
            if value["customEvent"]:
                self.customInfo[key] = value["customEvent"]
            if value["userEvent"]:
                self.userInfo[key] = value["userEvent"]
            if value["kernelEvent"]:
                self.kernelInfo[key] = value["kernelEvent"]

        # define event printer #
        def _printEvent(items, info, data, name):
            if not items:
                return

            SysMgr.clearPrint()
            SysMgr.printPipe("\n[Thread %s Event Info]\n%s" % (name, twoLine))
            SysMgr.printPipe(
                (
                    "{0:^32} {1:>16}({2:>7}) {3:>10} {4:>10} "
                    "{5:>10} {6:>10} {7:>10} {8:>10}\n{9:1}"
                ).format(
                    "Event",
                    "COMM",
                    "TID",
                    "Usage",
                    "Count",
                    "ProcMax",
                    "ProcMin",
                    "InterMax",
                    "InterMin",
                    twoLine,
                )
            )

            # set sort value #
            if name == "Trace":
                sortval = "count"
            else:
                sortval = "usage"

            newLine = False
            for idx, val in sorted(
                items.items(),
                key=lambda e: e[1][sortval],
                reverse=True,
            ):
                if newLine:
                    SysMgr.printPipe()
                else:
                    newLine = True
                SysMgr.printPipe(
                    (
                        "{0:<32} {1:>16}({2:>7}) {3:>10.6f} {4:>10} {5:>10.6f} "
                        "{6:>10.6f} {7:>10.6f} {8:>10.6f}"
                    ).format(
                        idx,
                        "TOTAL",
                        "-",
                        0 if name == "Trace" else val["usage"],
                        convNum(val["count"]),
                        val["max"] if "max" in val else 0,
                        val["min"] if "min" in val else 0,
                        val["maxPeriod"],
                        val["minPeriod"],
                    )
                )

                for key, value in sorted(
                    info.items(),
                    key=lambda e: 0 if not idx in e[1] else e[1][idx][sortval],
                    reverse=True,
                ):
                    try:
                        value[idx]
                        self.threadData[key]["comm"]
                    except:
                        continue

                    SysMgr.printPipe(
                        (
                            "{0:<32} {1:>16}({2:>7}) {3:>10.6f} {4:>10} "
                            "{5:>10.6f} {6:>10.6f} {7:>10.6f} {8:>10.6f}"
                        ).format(
                            " ",
                            self.threadData[key]["comm"][:16],
                            key,
                            0 if name == "Trace" else value[idx]["usage"],
                            convNum(value[idx]["count"]),
                            value[idx]["max"] if "max" in value[idx] else 0,
                            value[idx]["min"] if "max" in value[idx] else 0,
                            value[idx]["maxPeriod"],
                            value[idx]["minPeriod"],
                        )
                    )

                SysMgr.printPipe(oneLine)

            # print user event history #
            if not SysMgr.showAll or len(data) == 0:
                return

            # set title menu #
            if name == "Trace":
                caller = "ARGS"
                etype = elapsed = args = " "
            else:
                etype = "TYPE"
                caller = "CALLER"
                elapsed = "ELAPSED"
                args = "ARGS"

            SysMgr.clearPrint()
            SysMgr.printPipe(
                "\n[Thread %s Event History]\n%s" % (name, twoLine)
            )
            SysMgr.printPipe(
                (
                    "{0:^32} {1:>6} {2:^10} {3:>16}({4:>7}) "
                    "{5:^16} {6:>10} {7:1} \n{8:1}"
                ).format(
                    "EVENT",
                    etype,
                    "TIME",
                    "COMM",
                    "TID",
                    caller,
                    elapsed,
                    args,
                    twoLine,
                )
            )

            cnt = 0
            callTable = {}
            for val in data:
                elapsed = "-"

                if name == "User":
                    etype, ename, ecomm, etid, etime, _, eargs = val[:8]
                elif name == "Kernel":
                    etype, ename, eaddr, ecomm, etid, etime, _, eargs = val[:8]
                elif name == "Trace":
                    ename, ecomm, etid, etime, _ = val[:5]
                    etype = eargs = elapsed = " "
                else:
                    continue

                skipFlag = False
                for fval in SysMgr.filterGroup:
                    if SysMgr.isValidTid(etid, fval) or fval in ecomm:
                        skipFlag = False
                        break
                    skipFlag = True

                if skipFlag:
                    continue
                elif etype == "ENTER":
                    cid = "%s%s" % (ename, etid)
                    callTable[cid] = etime
                elif etype == "EXIT":
                    cid = "%s%s" % (ename, etid)
                    try:
                        elapsed = "%.6f" % (etime - callTable[cid])
                    except:
                        pass

                cnt += 1
                args = (" ".join(eargs.split(" arg"))).replace("=", ">")
                SysMgr.printPipe(
                    (
                        "{0:<32} {1:>6} {2:>10.6f} {3:>16}({4:>7}) "
                        "{5:<16} {6:>10} {7:1}"
                    ).format(
                        ename,
                        etype,
                        etime,
                        ecomm[:16],
                        etid,
                        _,
                        elapsed,
                        args.lstrip(),
                    )
                )
            if cnt == 0:
                SysMgr.printPipe("    None")
            SysMgr.printPipe(oneLine)

        # print trace event info #
        _printEvent(
            self.customEventInfo,
            self.customInfo,
            self.customEventData,
            "Trace",
        )

        # print user event info #
        _printEvent(
            self.userEventInfo, self.userInfo, self.userEventData, "User"
        )

        # print kernel event info #
        _printEvent(
            self.kernelEventInfo,
            self.kernelInfo,
            self.kernelEventData,
            "Kernel",
        )

    def checkSysInterval(self, res):
        if not SysMgr.maxIntervalTick:
            return
        elif not res in SysMgr.thrTarget:
            return
        return True

    def addPeakInterval(self, key, value, inter):
        name = "%s_%s" % (key, inter)
        self.intervalPeakData.setdefault(name, [])
        self.intervalPeakData[name].append(value)
        mod = len(self.intervalPeakData[name]) - inter
        if mod > 0:
            self.intervalPeakData[name] = self.intervalPeakData[name][mod:]

    def addSysInterval(self, res, key, value, check=False):
        if check and not self.checkSysInterval(res):
            return

        # TODO: filter only system resource #

        self.intervalData.setdefault(key, [])
        self.intervalData[key].append(value)
        mod = len(self.intervalData[key]) - long(SysMgr.maxIntervalTick)
        if mod > 0:
            self.intervalData[key] = self.intervalData[key][mod:]

    def addProcInterval(self, res, pid, target, key, value):
        if not self.checkSysInterval(res):
            return
        elif not SysMgr.taskThrEnable:
            return
        elif not SysMgr.thrTarget[res]:
            return

        try:
            target.setdefault(key, self.prevProcData[pid][key])
        except SystemExit:
            sys.exit(0)
        except:
            target.setdefault(key, [])

        target[key].append(value)
        mod = len(target[key]) - SysMgr.maxIntervalTick
        if mod > 0:
            target[key] = target[key][mod:]

    def printUsage(self):
        # print system resource usage #
        self.printResourceUsage()

        # print communication info #
        self.printComInfo()

        # print event info #
        self.printEventInfo()

        # print page info #
        self.printPageInfo()

        # print block usage #
        self.printBlockInfo()

        # print fs usage #
        self.printFsInfo()

        # print f2fs usage #
        self.printF2fsInfo()

        # print open history #
        self.printOpenInfo()

        # print resource usage for threads on timeline #
        self.printIntervalInfo()

        # print kernel module info #
        self.printModuleInfo()

        # print cpu freq info #
        self.printFreqInfo()

        # print dependency info #
        self.printDepInfo()

        # print lock usage #
        self.printFutexInfo()
        self.printFlockInfo()

        # print syscall usage #
        self.printSyscallInfo()

        # print LMK info #
        self.printLMKInfo()

        # print binder usage #
        self.printBinderInfo()

        # print kernel messages #
        self.printConsoleInfo()

    def printResourceUsage(self):
        # pylint: disable=undefined-variable,import-error

        title = "Thread Info"

        SysMgr.printLogo(big=True)

        # print system information #
        SysMgr.printInfoBuffer()

        convSize = UtilMgr.convSize2Unit
        convNum = UtilMgr.convNum

        # check trace event #
        if not (SysMgr.cpuEnable or SysMgr.memEnable or SysMgr.blockEnable):
            return

        # group threads #
        if "PROCGROUP" in SysMgr.environList:
            title = "Process Info"
            newThreadData = {}
            for t, d in self.threadData.items():
                tgid = d["tgid"]
                if tgid == "-----":
                    newThreadData[t] = d
                    continue
                elif not tgid in newThreadData:
                    newThreadData[tgid] = d
                    if tgid in SysMgr.commCache:
                        newThreadData[tgid]["comm"] = SysMgr.commCache[tgid]
                    continue

                if tgid == t:
                    newThreadData[tgid]["comm"] = d["comm"]

                for i, a in d.items():
                    if isinstance(a, (long, float)):
                        newThreadData[tgid][i] += a
            self.threadData = newThreadData

        # print menu #
        SysMgr.printPipe(
            (
                "[%s] [%s: %0.3f] [%s: %0.3f] [ActiveThread: %s] "
                "[ContextSwitch: %s] [LogSize: %s] (Unit: Sec/MB/NR)\n%s"
            )
            % (
                title,
                "Elapsed",
                round(float(self.totalTime), 7),
                "Start",
                round(float(SysMgr.startTime), 7),
                convNum(self.getRunTaskNum()),
                convNum(self.cxtSwitch),
                convSize(SysMgr.logSize),
                twoLine,
            )
        )

        lastAField = "{0:_^17}|{1:_^16}".format("Mem Info", "Process")
        lastBField = "%3s|%3s|%4s(%2s)" % ("Rcl", "Wst", "DRcl", "Nr")

        SysMgr.printPipe(
            "{0:_^34}|{1:_^35}|{2:_^22}|{3:_^26}|{4:_^34}|".format(
                title, "CPU Info", "SCHED Info", "BLOCK Info", lastAField
            )
        )

        SysMgr.printPipe("{0:^34}|{0:^35}|{0:^22}|{0:^26}|{0:^34}|".format(""))

        # set field name #
        if SysMgr.latEnable:
            field = "Latc"
        else:
            field = "Blck"

        SysMgr.printPipe(
            (
                "%16s(%6s/%6s)|%2s|%5s(%5s)|%5s|%6s|%3s|%5s|"
                "%5s|%5s|%5s|%4s|%5s(%3s/%4s)|%5s(%3s)|"
                "%4s(%3s/%3s/%3s)|%s|\n%s"
            )
            % (
                "Name",
                "TID",
                "PID",
                "LF",
                "Usage",
                "%",
                "Prmt",
                field,
                "Pri",
                "IRQ",
                "Yld",
                " Lose",
                "Steal",
                "Mig",
                "Read",
                "MB",
                "Cnt",
                "Write",
                "MB",
                "Sum",
                "Usr",
                "Buf",
                "Ker",
                lastBField,
                twoLine,
            )
        )

        # initialize swapper thread per core #
        for n in xrange(SysMgr.maxCore + 1):
            try:
                if SysMgr.perCoreList and n not in SysMgr.perCoreList:
                    continue

                coreId = "0[%s]" % n
                self.threadData[coreId]
            except:
                self.threadData[coreId] = dict(self.init_threadData)
                self.threadData[coreId]["comm"] = "swapper/" + str(n)
                self.threadData[coreId]["usage"] = 0

        # sort by size of I/O usage and convert read blocks to MB size #
        for key, value in sorted(
            self.threadData.items(),
            key=lambda e: e[1]["readBlock"],
            reverse=True,
        ):
            if value["readBlock"] > 0:
                value["readBlock"] = (
                    value["readBlock"] * SysMgr.blockSize
                ) >> 20
            if value["writeBlock"] > 0:
                value["writeBlock"] = (
                    value["writeBlock"] * SysMgr.blockSize
                ) >> 20
            if value["awriteBlock"] > 0:
                value["awriteBlock"] = (
                    value["awriteBlock"] * SysMgr.PAGESIZE
                ) >> 20

        # set precise flag #
        precise = "PRECISE" in SysMgr.environList

        # print total information after sorting by CPU usage #
        count = 0
        SysMgr.clearPrint()
        for key, value in sorted(
            self.threadData.items(),
            key=lambda e: TaskAnalyzer.getCoreId(e[1]["comm"]),
            reverse=False,
        ):
            if not key.startswith("0["):
                # convert priority #
                try:
                    prio = long(value["pri"]) - 120
                    if prio >= -20:
                        value["pri"] = str(prio)
                    elif prio == -121:
                        value["pri"] = "D99"
                    else:
                        value["pri"] = "R%2s" % abs(prio + 21)
                except:
                    pass

                continue

            # change the name of swapper thread to CORE #
            value["comm"] = value["comm"].replace("swapper", "CORE")

            # modify idle time if this core is not woke up ever #
            if value["usage"] == 0 == value["coreSchedCnt"]:
                value["usage"] = self.totalTime

            # calculate total core usage percentage #
            try:
                idle = float(value["usage"]) / float(self.totalTime)
                usagePercent = 100 - (round(idle, 7) * 100)
            except:
                usagePercent = 0

            if value["lastOff"] > 0:
                value["offTime"] += float(self.finishTime) - value["lastOff"]

            if SysMgr.powerEnable:
                if precise:
                    offTime = "%5.6f" % value["offTime"]
                else:
                    offTime = "%5.2f" % value["offTime"]
                offCnt = str(value["offCnt"])
            else:
                offTime = "-"
                offCnt = "-"

            if SysMgr.cpuEnable:
                if precise:
                    cpuTime = "%5.6f" % (self.totalTime - value["usage"])
                else:
                    cpuTime = "%5.2f" % (self.totalTime - value["usage"])

                cpuPer = "%5.1f" % usagePercent

                if SysMgr.latEnable:
                    schedVal = value["schedLatency"]
                else:
                    schedVal = value["schedBlock"]

                if precise:
                    schedLatency = "%5.6f" % schedVal
                else:
                    schedLatency = "%5.2f" % schedVal

                yieldCnt = "%5s" % convSize(value["yield"])
                preemptedCnt = "%5s" % convSize(value["preempted"])
                preemptionCnt = "%5s" % convSize(value["preemption"])
                migrateCnt = "%4s" % convSize(value["migrate"])
            else:
                cpuTime = "-"
                cpuPer = "-"
                schedLatency = "-"
                yieldCnt = "-"
                preemptedCnt = "-"
                preemptionCnt = "-"
                migrateCnt = "-"

            if SysMgr.irqEnable:
                if precise:
                    irqTime = "%5.6f" % value["irq"]
                else:
                    irqTime = "%5.2f" % value["irq"]
            else:
                irqTime = "-"

            if SysMgr.blockEnable:
                if precise:
                    ioRdWait = "%5.6f" % value["ioRdWait"]
                else:
                    ioRdWait = "%5.2f" % value["ioRdWait"]
                readBlock = "%3d" % value["readBlock"]
                readBlockCnt = "%4d" % value["readBlockCnt"]
                if precise:
                    ioWrWait = "%5.6f" % value["ioWrWait"]
                else:
                    ioWrWait = "%5.2f" % value["ioWrWait"]
                writeBlock = "%3d" % (
                    value["writeBlock"] + value["awriteBlock"]
                )
            else:
                ioRdWait = "-"
                readBlock = "-"
                readBlockCnt = "-"
                ioWrWait = "-"
                writeBlock = "-"

            if SysMgr.memEnable:
                usedMem = "%4d" % (
                    (value["nrPages"] >> 8) + (value["remainKmem"] >> 20)
                )
                userMem = "%3d" % (value["userPages"] >> 8)
                cacheMem = "%3d" % (value["cachePages"] >> 8)
                kernelMem = "%3d" % (
                    (value["kernelPages"] >> 8) + (value["remainKmem"] >> 20)
                )
                reclaimedMem = "%3d" % (value["reclaimedPages"] >> 8)
                wastedMem = "%3d" % (value["wasteKmem"] >> 20)
                if precise:
                    dreclaimedTime = "%4.6f" % value["dReclaimWait"]
                else:
                    dreclaimedTime = "%4.2f" % value["dReclaimWait"]
                dreclaimedCnt = "%2d" % value["dReclaimCnt"]
            else:
                usedMem = "-"
                userMem = "-"
                cacheMem = "-"
                kernelMem = "-"
                reclaimedMem = "-"
                wastedMem = "-"
                dreclaimedTime = "-"
                dreclaimedCnt = "-"

            lastField = "%3s|%3s|%4s(%2s)" % (
                reclaimedMem,
                wastedMem,
                dreclaimedTime,
                dreclaimedCnt,
            )

            SysMgr.addPrint(
                (
                    "%16s(%6s/%6s)|%s%s|%5s(%5s)|%5s|%6s|%3s|%5s|"
                    "%5s|%5s|%5s|%4s|%5s(%3s/%4s)|%5s(%3s)|"
                    "%4s(%3s/%3s/%3s)|%s|\n"
                )
                % (
                    value["comm"],
                    "-" * 5,
                    "-" * 5,
                    "-",
                    "-",
                    cpuTime,
                    cpuPer,
                    offTime,
                    schedLatency,
                    offCnt,
                    irqTime,
                    yieldCnt,
                    preemptedCnt,
                    preemptionCnt,
                    migrateCnt,
                    ioRdWait,
                    readBlock,
                    readBlockCnt,
                    ioWrWait,
                    writeBlock,
                    usedMem,
                    userMem,
                    cacheMem,
                    kernelMem,
                    lastField,
                )
            )
            count += 1

        SysMgr.printPipe("%s# %s: %d\n" % ("", "CPU", count))
        SysMgr.doPrint()
        SysMgr.printPipe(oneLine)

        # apply sort value automatically #
        if not SysMgr.sort:
            if SysMgr.cpuEnable:
                pass
            elif SysMgr.memEnable:
                SysMgr.sort = "m"
            elif SysMgr.blockEnable:
                SysMgr.sort = "b"

        # set sort value #
        if SysMgr.sort == "m":
            sortedThreadData = sorted(
                self.threadData.items(),
                key=lambda e: e[1]["nrPages"],
                reverse=True,
            )
        elif SysMgr.sort == "b":
            sortedThreadData = sorted(
                self.threadData.items(),
                key=lambda e: e[1]["readBlock"]
                + e[1]["writeBlock"]
                + e[1]["awriteBlock"],
                reverse=True,
            )
        else:
            # set CPU usage as default #
            sortedThreadData = sorted(
                self.threadData.items(),
                key=lambda e: e[1]["usage"],
                reverse=True,
            )

        # set total CPU variables #
        totalCpuTime = 0
        totalPrtTime = 0
        totalSchedLatency = 0
        totalYieldCnt = 0
        totalPreemptedCnt = 0
        totalPreemptionCnt = 0
        totalMigrateCnt = 0

        # set total irq variables #
        totalIrqTime = 0

        # set total I/O variables #
        totalIoRdWait = 0
        totalReadBlock = 0
        totalReadBlockCnt = 0
        totalIoWrWait = 0
        totalWriteBlock = 0

        # set total mem variables #
        totalUsedMem = 0
        totalUserMem = 0
        totalCacheMem = 0
        totalKernelMem = 0
        totalReclaimedMem = 0
        totalWastedMem = 0
        totalDreclaimedTime = 0
        totalDreclaimedCnt = 0

        # define variables for tasks #
        normCnt = 0
        newCnt = 0
        dieCnt = 0
        normThreadString = ""
        newThreadString = ""
        dieThreadString = ""

        # print thread information after sorting by time of CPU usage #
        count = 0
        SysMgr.clearPrint()
        for key, value in sortedThreadData:
            if key.startswith("0["):
                continue

            try:
                usagePercent = (
                    round(value["usage"] / float(self.totalTime), 7) * 100
                )
            except:
                usagePercent = 0

            # set break condition #
            if SysMgr.sort == "m":
                breakCond = value["nrPages"]
            elif SysMgr.sort == "b":
                breakCond = (
                    value["readBlock"]
                    + value["writeBlock"]
                    + value["awriteBlock"]
                )
            else:
                breakCond = usagePercent

            if (
                breakCond < 1
                and not SysMgr.showAll
                and SysMgr.filterGroup == []
            ):
                break

            if SysMgr.cpuEnable:
                if precise:
                    cpuTime = "%5.6f" % value["usage"]
                else:
                    cpuTime = "%5.2f" % value["usage"]

                totalCpuTime += value["usage"]

                cpuPer = "%5.1f" % usagePercent

                if SysMgr.latEnable:
                    schedVal = value["schedLatency"]
                else:
                    schedVal = value["schedBlock"]

                if precise:
                    prtTime = "%5.6f" % value["cpuWait"]
                    schedLatency = "%5.6f" % schedVal
                else:
                    prtTime = "%5.2f" % value["cpuWait"]
                    schedLatency = "%5.2f" % schedVal

                totalPrtTime += value["cpuWait"]
                totalSchedLatency += value["schedLatency"]

                pri = value["pri"]

                yieldCnt = "%5s" % convSize(value["yield"])
                totalYieldCnt += value["yield"]

                preemptedCnt = "%5s" % convSize(value["preempted"])
                totalPreemptedCnt += value["preempted"]

                preemptionCnt = "%5s" % convSize(value["preemption"])
                totalPreemptionCnt += value["preemption"]

                migrateCnt = "%4s" % convSize(value["migrate"])
                totalMigrateCnt += value["migrate"]
            else:
                cpuTime = "-"
                cpuPer = "-"
                prtTime = "-"
                schedLatency = "-"
                pri = "-"
                yieldCnt = "-"
                preemptedCnt = "-"
                preemptionCnt = "-"
                migrateCnt = "-"

                totalCpuPer = "-"
                totalCpuTime = "-"
                totalPrtTime = "-"
                totalSchedLatency = "-"
                totalYieldCnt = "-"
                totalPreemptedCnt = "-"
                totalPreemptionCnt = "-"
                totalMigrateCnt = "-"

            if SysMgr.irqEnable:
                if precise:
                    irqTime = "%5.6f" % value["irq"]
                else:
                    irqTime = "%5.2f" % value["irq"]
                totalIrqTime += value["irq"]
            else:
                irqTime = "-"
                totalIrqTime = "-"

            if SysMgr.blockEnable:
                if precise:
                    ioRdWait = "%5.6f" % value["ioRdWait"]
                else:
                    ioRdWait = "%5.2f" % value["ioRdWait"]
                totalIoRdWait += value["ioRdWait"]

                readBlock = "%3d" % value["readBlock"]
                totalReadBlock += value["readBlock"]

                readBlockCnt = "%4d" % value["readBlockCnt"]
                totalReadBlockCnt += value["readBlockCnt"]

                if precise:
                    ioWrWait = "%5.6f" % value["ioWrWait"]
                else:
                    ioWrWait = "%5.2f" % value["ioWrWait"]
                totalIoWrWait += value["ioWrWait"]

                writeBlock = "%3d" % (
                    value["writeBlock"] + value["awriteBlock"]
                )
                totalWriteBlock += value["writeBlock"] + value["awriteBlock"]
            else:
                ioRdWait = "-"
                readBlock = "-"
                readBlockCnt = "-"
                ioWrWait = "-"
                writeBlock = "-"

                totalIoRdWait = "-"
                totalReadBlock = "-"
                totalReadBlockCnt = "-"
                totalIoWrWait = "-"
                totalWriteBlock = "-"

            if SysMgr.memEnable:
                usedMem = (value["nrPages"] >> 8) + (value["remainKmem"] >> 20)
                totalUsedMem += usedMem
                usedMem = "%4d" % usedMem

                userMem = "%3d" % (value["userPages"] >> 8)
                totalUserMem += value["userPages"] >> 8

                cacheMem = "%3d" % (value["cachePages"] >> 8)
                totalCacheMem += value["cachePages"] >> 8

                kernelMem = (value["kernelPages"] >> 8) + (
                    value["remainKmem"] >> 20
                )
                totalKernelMem += kernelMem
                kernelMem = "%3d" % kernelMem

                reclaimedMem = "%3d" % (value["reclaimedPages"] >> 8)
                totalReclaimedMem += value["reclaimedPages"] >> 8

                wastedMem = "%3d" % (value["wasteKmem"] >> 20)
                totalWastedMem += value["wasteKmem"] >> 20

                if precise:
                    dreclaimedTime = "%4.6f" % value["dReclaimWait"]
                else:
                    dreclaimedTime = "%4.2f" % value["dReclaimWait"]
                totalDreclaimedTime += value["dReclaimWait"]

                dreclaimedCnt = "%2d" % value["dReclaimCnt"]
                totalDreclaimedCnt += value["dReclaimCnt"]
            else:
                usedMem = "-"
                userMem = "-"
                cacheMem = "-"
                kernelMem = "-"
                reclaimedMem = "-"
                wastedMem = "-"
                dreclaimedTime = "-"
                dreclaimedCnt = "-"

                totalUsedMem = "-"
                totalUserMem = "-"
                totalCacheMem = "-"
                totalKernelMem = "-"
                totalReclaimedMem = "-"
                totalWastedMem = "-"
                totalDreclaimedTime = "-"
                totalDreclaimedCnt = "-"

            # set last field #
            if SysMgr.savedProcComm:
                if value["tgid"] in SysMgr.savedProcComm:
                    comm = SysMgr.savedProcComm[value["tgid"]]
                elif value["tgid"] in SysMgr.commCache:
                    comm = SysMgr.commCache[value["tgid"]]
                elif key == value["tgid"]:
                    comm = value["comm"]
                else:
                    comm = "?"
                lastField = "{0:>16}".format(comm)[:16]
            else:
                lastField = "%3s|%3s|%4s(%2s)" % (
                    reclaimedMem,
                    wastedMem,
                    dreclaimedTime,
                    dreclaimedCnt,
                )

            if value["new"] != " ":
                newCnt += 1
                taskInfo = "%16s(%6s/%6s)" % (
                    value["comm"],
                    key,
                    value["ptid"],
                )
                newThreadString += (
                    "%s|%s%s|%5s(%5s)|%5s|%6s|%3s|%5s|"
                    "%5s|%5s|%5s|%4s|%5s(%3s/%4s)|%5s(%3s)|"
                    "%4s(%3s/%3s/%3s)|%s|\n"
                ) % (
                    taskInfo[:31],
                    value["new"],
                    value["die"],
                    cpuTime,
                    cpuPer,
                    prtTime,
                    schedLatency,
                    pri,
                    irqTime,
                    yieldCnt,
                    preemptedCnt,
                    preemptionCnt,
                    migrateCnt,
                    ioRdWait,
                    readBlock,
                    readBlockCnt,
                    ioWrWait,
                    writeBlock,
                    usedMem,
                    userMem,
                    cacheMem,
                    kernelMem,
                    lastField,
                )

            if value["die"] != " ":
                dieCnt += 1
                taskInfo = "%16s(%6s/%6s)" % (
                    value["comm"],
                    key,
                    value["ptid"],
                )
                dieThreadString += (
                    "%s|%s%s|%5s(%5s)|%5s|%6s|%3s|%5s|"
                    "%5s|%5s|%5s|%4s|%5s(%3s/%4s)|%5s(%3s)|"
                    "%4s(%3s/%3s/%3s)|%s|\n"
                ) % (
                    taskInfo[:31],
                    value["new"],
                    value["die"],
                    cpuTime,
                    cpuPer,
                    prtTime,
                    schedLatency,
                    pri,
                    irqTime,
                    yieldCnt,
                    preemptedCnt,
                    preemptionCnt,
                    migrateCnt,
                    ioRdWait,
                    readBlock,
                    readBlockCnt,
                    ioWrWait,
                    writeBlock,
                    usedMem,
                    userMem,
                    cacheMem,
                    kernelMem,
                    lastField,
                )

            normCnt += 1
            taskInfo = "%16s(%6s/%6s)" % (value["comm"], key, value["tgid"])
            normThreadString += (
                "%s|%s%s|%5s(%5s)|%5s|%6s|%3s|%5s|"
                "%5s|%5s|%5s|%4s|%5s(%3s/%4s)|%5s(%3s)|"
                "%4s(%3s/%3s/%3s)|%s|\n"
            ) % (
                taskInfo[:31],
                value["new"],
                value["die"],
                cpuTime,
                cpuPer,
                prtTime,
                schedLatency,
                pri,
                irqTime,
                yieldCnt,
                preemptedCnt,
                preemptionCnt,
                migrateCnt,
                ioRdWait,
                readBlock,
                readBlockCnt,
                ioWrWait,
                writeBlock,
                usedMem,
                userMem,
                cacheMem,
                kernelMem,
                lastField,
            )

        # build total usage string #
        try:
            totalCpuPer = "%5.1f" % (
                (totalCpuTime / float(self.totalTime)) * 100
            )
            if precise:
                totalCpuTime = "%5.6f" % totalCpuTime
                totalPrtTime = "%5.6f" % totalPrtTime
                totalSchedLatency = "%5.6f" % totalSchedLatency
            else:
                totalCpuTime = "%5.2f" % totalCpuTime
                totalPrtTime = "%5.2f" % totalPrtTime
                totalSchedLatency = "%5.2f" % totalSchedLatency
            totalYieldCnt = "%5s" % convSize(totalYieldCnt)
            totalPreemptedCnt = "%5s" % convSize(totalPreemptedCnt)
            totalPreemptionCnt = "%5s" % convSize(totalPreemptionCnt)
            totalMigrateCnt = "%4s" % convSize(totalMigrateCnt)
        except:
            pass

        try:
            if precise:
                totalIrqTime = "%5.6f" % totalIrqTime
            else:
                totalIrqTime = "%5.2f" % totalIrqTime
        except:
            pass

        try:
            if precise:
                totalIoRdWait = "%5.6f" % totalIoRdWait
            else:
                totalIoRdWait = "%5.2f" % totalIoRdWait
            totalReadBlock = "%3d" % totalReadBlock
            totalReadBlockCnt = "%4d" % totalReadBlockCnt
            if precise:
                totalIoWrWait = "%5.6f" % totalIoWrWait
            else:
                totalIoWrWait = "%5.2f" % totalIoWrWait
            totalWriteBlock = "%3d" % totalWriteBlock
        except:
            pass

        try:
            totalUsedMem = "%4d" % totalUsedMem
            totalUserMem = "%3d" % totalUserMem
            totalCacheMem = "%3d" % totalCacheMem
            totalKernelMem = "%3d" % totalKernelMem
            totalReclaimedMem = "%3d" % totalReclaimedMem
            totalWastedMem = "%3d" % totalWastedMem
            totalDreclaimedCnt = "%2d" % totalDreclaimedCnt
            if precise:
                totalDreclaimedTime = "%4.6f" % totalDreclaimedTime
            else:
                totalDreclaimedTime = "%4.2f" % totalDreclaimedTime
        except:
            pass

        lastField = "%3s|%3s|%4s(%2s)" % (
            totalReclaimedMem,
            totalWastedMem,
            totalDreclaimedTime,
            totalDreclaimedCnt,
        )

        # print TOTAL information #
        SysMgr.printPipe(
            (
                "%31s|%s%s|%5s(%5s)|%5s|%6s|%3s|%5s|"
                "%5s|%5s|%5s|%4s|%5s(%3s/%4s)|%5s(%3s)|%4s(%3s/%3s/%3s)|%s|"
            )
            % (
                "{0:>29}".format("[ TOTAL ]"),
                " ",
                " ",
                totalCpuTime,
                totalCpuPer,
                totalPrtTime,
                totalSchedLatency,
                "-",
                totalIrqTime,
                totalYieldCnt,
                totalPreemptedCnt,
                totalPreemptionCnt,
                totalMigrateCnt,
                totalIoRdWait,
                totalReadBlock,
                totalReadBlockCnt,
                totalIoWrWait,
                totalWriteBlock,
                totalUsedMem,
                totalUserMem,
                totalCacheMem,
                totalKernelMem,
                lastField,
            )
        )

        # print normal thread info #
        if normCnt > 0:
            SysMgr.printPipe(
                "%s# %s: %s\n%s\n%s"
                % ("", "Hot", convNum(normCnt), normThreadString, oneLine)
            )
        else:
            SysMgr.printPipe("    None\n%s" % oneLine)

        # print new thread info #
        if newCnt > 0:
            SysMgr.printPipe(
                "%s# %s: %s\n%s\n%s"
                % ("", "New", convNum(newCnt), newThreadString, oneLine)
            )

        # print die thread info #
        if dieCnt > 0:
            SysMgr.printPipe(
                "%s# %s: %s\n%s\n%s"
                % ("", "Die", convNum(dieCnt), dieThreadString, oneLine)
            )

        # print thread preempted information after sorting by time of CPU usage #
        for index, tid in enumerate(SysMgr.preemptGroup):
            count = 0

            try:
                self.threadData[tid]
            except:
                SysMgr.printErr('failed to find "%s" thread' % tid)
                continue

            SysMgr.clearPrint()
            for key, value in sorted(
                self.preemptData[index][1].items(),
                key=lambda e: e[1]["usage"],
                reverse=True,
            ):
                count += 1
                stats = self.preemptData[index]

                if float(stats[4]) == 0:
                    break

                if precise:
                    SysMgr.addPrint(
                        "%16s(%6s/%6s)|%s%s|%5.6f(%5s)\n"
                        % (
                            self.threadData[key]["comm"],
                            key,
                            "0",
                            self.threadData[key]["new"],
                            self.threadData[key]["die"],
                            value["usage"],
                            "%.6f" % ((value["usage"] / stats[4]) * 100),
                        )
                    )
                else:
                    SysMgr.addPrint(
                        "%16s(%6s/%6s)|%s%s|%5.2f(%5s)\n"
                        % (
                            self.threadData[key]["comm"],
                            key,
                            "0",
                            self.threadData[key]["new"],
                            self.threadData[key]["die"],
                            value["usage"],
                            "%.2f" % ((value["usage"] / stats[4]) * 100),
                        )
                    )

            SysMgr.printPipe(
                "# %s: %s(%s) [TOTAL: %.3f] [TASKS: %s]\n"
                % (
                    "PRT",
                    self.threadData[tid]["comm"],
                    tid,
                    self.preemptData[index][4],
                    UtilMgr.convNum(count),
                )
            )

            SysMgr.doPrint()
            SysMgr.printPipe(oneLine)

        # print histo stats for runtime #
        if "runtime" in self.statData:
            runtimeStats = UtilMgr.convList2Histo(
                self.statData["runtime"], mult=1000000
            )
            UtilMgr.printHist(runtimeStats, "Sched_runtime", "us")

        # print histo stats for preemption #
        if "prttime" in self.statData:
            prttimeStats = UtilMgr.convList2Histo(
                self.statData["prttime"], mult=1000000
            )
            UtilMgr.printHist(prttimeStats, "Sched_preempted", "us")

        # print histo stats for latency #
        if "schedlat" in self.statData:
            latStats = UtilMgr.convList2Histo(
                self.statData["schedlat"], mult=1000000
            )
            UtilMgr.printHist(latStats, "Sched_latency", "us")

        # print histo stats for block #
        if "schedblock" in self.statData:
            latStats = UtilMgr.convList2Histo(
                self.statData["schedblock"], mult=1000000
            )
            UtilMgr.printHist(latStats, "Sched_block", "us")

        # prepare to draw graph #
        if not SysMgr.isRecordMode() and SysMgr.graphEnable:
            # check interval value #
            if SysMgr.intervalEnable == 0:
                SysMgr.printErr("use -i option if you want to draw graph")
                SysMgr.graphEnable = False
                return

            # get matplotlib object #
            matplotlib = SysMgr.getPkg("matplotlib", False)
            if not matplotlib:
                SysMgr.printPipWarn("matplotlib", "matplotlib")
                sys.exit(-1)
            from matplotlib.ticker import MaxNLocator

            SysMgr.matplotlibVersion = float(
                ".".join(matplotlib.__version__.split(".")[:2])
            )

            # set dpi #
            matplotlib.rcParams["figure.dpi"] = SysMgr.matplotlibDpi

            # set resolution #
            if SysMgr.matplotlibSize:
                matplotlib.rcParams["figure.figsize"] = SysMgr.matplotlibSize

            # set backend #
            matplotlib.use("Agg")

            # get pylab object #
            SysMgr.importPkgItems("pylab")

            rc("legend", fontsize=5)
            rcParams.update({"font.size": 8})
        else:
            SysMgr.graphEnable = False

    def printFreqInfo(self):
        if len(self.freqData) <= 0:
            return

        # print frequency info #
        lineLen = 116
        SysMgr.printPipe("\n[Thread Frequency Info]\n%s" % twoLine[:lineLen])
        SysMgr.printPipe(
            "{0:^16}|{1:^6}|{2:^16}|{3:^16}| {4:<56}|\n{5:1}".format(
                "Time",
                "Core",
                "FreqMin",
                "FreqMax",
                "Process->Thread",
                twoLine[:lineLen],
            )
        )

        printCnt = 0
        for value in self.freqData:
            ftime, task, fmin, fmax, fcore = value
            SysMgr.printPipe(
                "{0:>16}|{1:>6}|{2:>16}|{3:>16}| {4:<56}|\n{5:1}".format(
                    ftime,
                    fcore,
                    UtilMgr.convNum(fmin),
                    UtilMgr.convNum(fmax),
                    task,
                    twoLine[:lineLen],
                )
            )

        if printCnt == 0:
            SysMgr.printPipe("    None\n%s" % oneLine[:lineLen])
        else:
            SysMgr.printPipe("%s" % oneLine[:lineLen])

    def printModuleInfo(self):
        if len(self.moduleData) <= 0:
            return

        moduleTable = {}
        init_moduleData = {
            "startTime": 0.0,
            "loadCnt": 0,
            "elapsed": 0.0,
            "freeCnt": 0,
            "refCnt": 0,
            "getCnt": 0,
            "putCnt": 0,
        }

        # print module history #
        lineLen = 102
        SysMgr.clearPrint()
        SysMgr.addPrint("\n[Thread Module History]\n", force=True)
        SysMgr.addPrint("%s\n" % twoLine[:lineLen], force=True)
        SysMgr.addPrint(
            "{3:>16} ({4:^7})|{0:^6}|{1:^12}|{2:^32}|{5:^12}|{6:^8}|\n".format(
                "Type", "Time", "Module", "Comm", "TID", "Elapsed", "RefCnt"
            ),
            force=True,
        )
        SysMgr.addPrint("%s\n" % twoLine[:lineLen], force=True)

        printCnt = 0
        for val in self.moduleData:
            event = val[0]
            tid = val[1]
            time = val[2]
            module = val[3]

            current = float(time) - float(SysMgr.startTime)

            try:
                comm = self.threadData[tid]["comm"]
            except:
                continue

            moduleTable.setdefault(module, dict(init_moduleData))

            startTime = float(moduleTable[module]["startTime"])

            # module probe #
            if event == "load":
                moduleTable[module]["startTime"] = time
                moduleTable[module]["loadCnt"] += 1

                moduleTable[module]["refCnt"] = 1
                refCnt = moduleTable[module]["refCnt"]

                SysMgr.addPrint(
                    (
                        "{3:>16} ({4:>7})|{0:^6}|{1:12.6f}|"
                        "{2:^32}|{5:>12}|{6:>8}|\n"
                    ).format("LOAD", current, module, comm, tid, "", refCnt),
                    force=True,
                )
                printCnt += 1

            # module remove #
            elif event == "free":
                if startTime > 0:
                    lifetime = "%.6f" % (float(time) - startTime)
                else:
                    lifetime = ""

                moduleTable[module]["freeCnt"] += 1
                moduleTable[module]["refCnt"] = 0
                refCnt = moduleTable[module]["refCnt"]

                SysMgr.addPrint(
                    (
                        "{3:>16} ({4:>7})|{0:^6}|{1:12.6f}|"
                        "{2:^32}|{5:>12}|{6:^8}|\n"
                    ).format(
                        "FREE", current, module, comm, tid, lifetime, refCnt
                    ),
                    force=True,
                )
                printCnt += 1

            # module refcount increase #
            elif event == "get":
                moduleTable[module]["getCnt"] += 1
                moduleTable[module]["refCnt"] = val[4]
                refCnt = moduleTable[module]["refCnt"]

                SysMgr.addPrint(
                    (
                        "{3:>16} ({4:>7})|{0:^6}|{1:12.6f}|"
                        "{2:^32}|{5:>12}|{6:^8}|\n"
                    ).format("GET", current, module, comm, tid, "", refCnt),
                    force=True,
                )
                printCnt += 1

            # module refcount decrease #
            elif event == "put":
                moduleTable[module]["putCnt"] += 1
                moduleTable[module]["refCnt"] = val[4]
                refCnt = moduleTable[module]["refCnt"]

                if startTime > 0:
                    elapsed = float(time) - startTime
                    moduleTable[module]["elapsed"] += elapsed
                    moduleTable[module]["startTime"] = 0
                    elapsed = "%.6f" % elapsed
                else:
                    elapsed = ""

                SysMgr.addPrint(
                    (
                        "{3:>16} ({4:>7})|{0:^6}|{1:12.6f}|"
                        "{2:^32}|{5:>12}|{6:^8}|\n"
                    ).format(
                        "PUT", current, module, comm, tid, elapsed, refCnt
                    ),
                    force=True,
                )
                printCnt += 1

        if printCnt == 0:
            SysMgr.addPrint("    None\n%s\n" % oneLine[:lineLen], force=True)
        else:
            SysMgr.addPrint("%s\n" % oneLine[:lineLen], force=True)

        # print module info #
        lineLen = 90
        SysMgr.printPipe("\n[Thread Module Info]\n%s" % twoLine[:lineLen])
        SysMgr.printPipe(
            "{0:^32}|{1:^10}|{2:^12}|{3:^10}|{4:^10}|{5:^10}|\n{6:1}".format(
                "Module",
                "LoadCnt",
                "LoadTime",
                "FreeCnt",
                "GetCnt",
                "PutCnt",
                twoLine[:lineLen],
            )
        )

        printCnt = 0
        for module, value in moduleTable.items():
            elapsed = "%.6f" % value["elapsed"]
            SysMgr.printPipe(
                "{0:^32}|{1:>10}|{2:>12}|{3:>10}|{4:>10}|{5:>10}|".format(
                    module,
                    value["loadCnt"],
                    elapsed,
                    value["freeCnt"],
                    value["getCnt"],
                    value["putCnt"],
                )
            )
            printCnt += 1

        if printCnt == 0:
            SysMgr.printPipe("    None\n%s" % oneLine[:lineLen])
        else:
            SysMgr.printPipe("%s" % oneLine[:lineLen])

        SysMgr.doPrint(clear=True)

    def printDepInfo(self):
        if not SysMgr.depEnable:
            return

        SysMgr.printPipe("\n[Thread Dependency Info]\n%s" % twoLine)
        SysMgr.printPipe(
            "\t%5s/%4s \t%32s(%7s) -> %32s(%7s) \t%5s\n%s"
            % ("Total", "Inter", "From", "TID", "To", "TID", "Event", twoLine)
        )
        SysMgr.printPipe("%s# %s: %d\n" % ("", "Dep", len(self.depData)))

        for icount in xrange(len(self.depData)):
            SysMgr.addPrint(self.depData[icount] + "\n", force=True)

        SysMgr.doPrint(clear=True)
        SysMgr.printPipe(oneLine)

    def printFutexInfo(self):
        SysMgr.clearPrint()

        if not self.futexData:
            return

        convNum = UtilMgr.convNum

        outputCnt = 0
        SysMgr.printPipe(
            (
                "\n[Thread Futex Lock Info] [Elapsed: %.3f] "
                "(Unit: Sec/NR) (LBlock: LOCK_PI)\n%s"
            )
            % (float(self.totalTime), twoLine)
        )
        SysMgr.printPipe(
            (
                "{0:>16}({1:>7}/{2:>7}) {3:>9} {4:>9} {5:>9} "
                "{6:>8} {7:>10} {8:>10} {9:>10} {10:>8} {11:>8} "
                "{12:>10} {13:>8} {14:>10}\n{15:1}"
            ).format(
                "Name",
                "TID",
                "PID",
                "Elapsed",
                "Process",
                "Block",
                "NrBlock",
                "CallMax",
                "Lock",
                "LockMax",
                "NrLock",
                "NrWait",
                "LBlock",
                "NrLBlock",
                "LastStat",
                twoLine,
            )
        )

        tinfo = {
            "ftxTotal": 0,
            "ftxMax": 0,
            "ftxLock": 0,
            "ftxLockMax": 0,
            "ftxLockCnt": 0,
            "ftxWaitCnt": 0,
            "ftxProcess": 0,
            "ftxBlockTotal": 0,
            "ftxLBlockTotal": 0,
            "ftxBlockCnt": 0,
            "ftxLSwitch": 0,
        }

        # print futex info of threads #
        for key, value in sorted(
            self.threadData.items(),
            key=lambda e: e[1]["ftxLockCnt"] + e[1]["ftxWaitCnt"],
            reverse=True,
        ):
            if key.startswith("0["):
                continue
            elif value["ftxTotal"] == 0:
                break

            if value["ftxEnter"] == 0:
                status = "Running"
            else:
                status = "Wait"

            pid = value["tgid"]

            # set thread info #
            ftxTotal = "%.3f" % float(value["ftxTotal"])
            ftxMax = "%.3f" % float(value["ftxMax"])
            ftxLock = "%.3f" % float(value["ftxLock"])
            ftxLockMax = "%.3f" % float(value["ftxLockMax"])
            ftxLockCall = convNum(value["ftxLockCnt"])
            ftxWaitCall = convNum(value["ftxWaitCnt"])

            # set total info #
            tinfo["ftxTotal"] += value["ftxTotal"]
            tinfo["ftxLock"] += value["ftxLock"]
            tinfo["ftxLockCnt"] += value["ftxLockCnt"]
            tinfo["ftxWaitCnt"] += value["ftxWaitCnt"]
            if tinfo["ftxMax"] == 0 or tinfo["ftxMax"] < value["ftxMax"]:
                tinfo["ftxMax"] = value["ftxMax"]
            if (
                tinfo["ftxLockMax"] == 0
                or tinfo["ftxLockMax"] < value["ftxLockMax"]
            ):
                tinfo["ftxLockMax"] = value["ftxLockMax"]

            if SysMgr.cpuEnable:
                ftxProcess = "%.3f" % float(value["ftxProcess"])
                ftxBlock = "%.3f" % float(value["ftxBlockTotal"])
                ftxLBlock = "%.3f" % float(value["ftxLBlockTotal"])
                ftxBlockCall = convNum(value["ftxBlockCnt"])
                ftxLSwitch = convNum(value["ftxLSwitch"])

                tinfo["ftxProcess"] += value["ftxProcess"]
                tinfo["ftxBlockTotal"] += value["ftxBlockTotal"]
                tinfo["ftxLBlockTotal"] += value["ftxLBlockTotal"]
                tinfo["ftxBlockCnt"] += value["ftxBlockCnt"]
                tinfo["ftxLSwitch"] += value["ftxLSwitch"]
            else:
                ftxProcess = tinfo["ftxProcess"] = "-"
                ftxBlock = tinfo["ftxBlockTotal"] = "-"
                ftxLBlock = tinfo["ftxLBlockTotal"] = "-"
                ftxBlockCall = tinfo["ftxBlockCnt"] = "-"
                ftxLSwitch = tinfo["ftxLSwitch"] = "-"

            futexInfo = (
                "{0:>16}({1:>7}/{2:>7}) {3:>9} {4:>9} {5:>9} "
                + "{6:>8} {7:>10} {8:>10} {9:>10} {10:>8} "
                + "{11:>8} {12:>10} {13:>8} {14:>10}"
            ).format(
                value["comm"],
                key,
                pid,
                ftxTotal,
                ftxProcess,
                ftxBlock,
                ftxBlockCall,
                ftxMax if value["ftxMax"] else "-",
                ftxLock if value["ftxLock"] else "-",
                ftxLockMax if value["ftxLockMax"] else "-",
                ftxLockCall if value["ftxLockCnt"] else "-",
                ftxWaitCall if value["ftxWaitCnt"] else "-",
                ftxLBlock if value["ftxLBlockTotal"] else "-",
                ftxLSwitch if value["ftxLSwitch"] else "-",
                status,
            )

            SysMgr.addPrint("%s\n%s\n" % (futexInfo, oneLine), force=True)
            outputCnt += 1

        if outputCnt == 0:
            SysMgr.printPipe("    None\n%s" % oneLine)
        else:
            # print total info #
            tinfo["ftxTotal"] = "%.3f" % tinfo["ftxTotal"]
            tinfo["ftxMax"] = "%.3f" % tinfo["ftxMax"]
            tinfo["ftxLock"] = "%.3f" % tinfo["ftxLock"]
            tinfo["ftxLockMax"] = "%.3f" % tinfo["ftxLockMax"]
            tinfo["ftxLockCnt"] = convNum(tinfo["ftxLockCnt"])
            tinfo["ftxWaitCnt"] = convNum(tinfo["ftxWaitCnt"])

            # convert format for total info #
            for item in ("ftxProcess", "ftxBlockTotal", "ftxLBlockTotal"):
                if tinfo[item] != "-":
                    tinfo[item] = "%.3f" % tinfo[item]
            for item in ("ftxBlockCnt", "ftxLSwitch"):
                if tinfo[item] != "-":
                    tinfo[item] = convNum(tinfo[item])

            def _convItem(name):
                try:
                    val = float(tinfo[name].replace(",", ""))
                    return tinfo[name] if val else "-"
                except SystemExit:
                    sys.exit(0)
                except:
                    return "-"

            totalFutexInfo = (
                "{0:>33} {1:>9} {2:>9} {3:>9} "
                "{4:>8} {5:>10} {6:>10} {7:>10} {8:>8} "
                "{9:>8} {10:>10} {11:>8} {12:>10}"
            ).format(
                "[ TOTAL ]",
                tinfo["ftxTotal"],
                tinfo["ftxProcess"],
                tinfo["ftxBlockTotal"],
                tinfo["ftxBlockCnt"],
                _convItem("ftxMax"),
                _convItem("ftxLock"),
                _convItem("ftxLockMax"),
                _convItem("ftxLockCnt"),
                _convItem("ftxWaitCnt"),
                _convItem("ftxLBlockTotal"),
                _convItem("ftxLSwitch"),
                "-",
            )

            SysMgr.printPipe("%s\n%s" % (totalFutexInfo, oneLine))

            # print thread info #
            SysMgr.doPrint(clear=True)

        if not SysMgr.showAll:
            return

        SysMgr.printPipe(
            "\n[Thread Futex Lock History] (Unit: Sec/NR)\n%s" % twoLine
        )
        SysMgr.printPipe(
            (
                "{0:>12} {1:>16}({2:>7}/{3:>7}) {4:>4} {5:^24} "
                "{6:^10} {7:>12} {8:>16} {9:>16} {10:>16}\n{11:1}"
            ).format(
                "Time",
                "Name",
                "Tid",
                "Pid",
                "Core",
                "Operation",
                "Type",
                "Elapsed",
                "Target",
                "Value",
                "Timer",
                twoLine,
            )
        )

        cnt = 0
        prevCnt = -1
        for icount in xrange(len(self.futexData)):
            try:
                value = self.futexData[icount]

                if value[1] == -1:
                    continue

                atime = float(value[1])
                stime = "%.6f" % (atime - float(SysMgr.startTime))

                comm = self.threadData[value[0]]["comm"]
                tid = "(%7s/%7s)" % (
                    value[0],
                    self.threadData[value[0]]["tgid"],
                )
                core = value[2]

                try:
                    if prevCnt < 0:
                        raise Exception("no previous item")

                    if self.futexData[prevCnt][2] == value[2]:
                        core = ""

                    if self.futexData[prevCnt][0] == value[0]:
                        tid = comm = "."
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

                nextCnt = icount + 1
                if (
                    icount + 1 <= len(self.futexData)
                    and self.futexData[nextCnt][0] == value[0]
                    and self.futexData[icount][4].startswith("ENT")
                    and self.futexData[nextCnt][4].endswith("RET")
                ):
                    otype = "{0:^10}".format("ALL")
                    elapsed = self.futexData[nextCnt][5]
                    self.futexData[nextCnt][1] = -1
                else:
                    otype = value[4]
                    elapsed = value[5]

                # convert error code #
                try:
                    ret = long(value[7])
                    if ret < 0:
                        try:
                            ret = "%s" % ConfigMgr.ERR_TYPE[abs(ret + 1)]
                        except:
                            pass
                except:
                    ret = value[7]

                SysMgr.printPipe(
                    (
                        "{0:>12} {1:>16}{2:>17} {3:>4} {4:<24} "
                        + "{5:>10} {6:>12} {7:>16} {8:>16} {9:>16}"
                    ).format(
                        stime,
                        comm,
                        tid,
                        core,
                        value[3],
                        otype,
                        elapsed,
                        value[6],
                        ret,
                        value[8],
                    )
                )

                prevCnt = icount
                cnt += 1
            except:
                pass

        if cnt == 0:
            SysMgr.printPipe("    None")
        SysMgr.printPipe(oneLine)

    def printFlockInfo(self):
        SysMgr.clearPrint()

        if not self.flockData:
            return

        outputCnt = 0
        SysMgr.printPipe(
            "\n[Thread File Lock Info] (Unit: Sec/NR)\n%s" % twoLine
        )
        SysMgr.printPipe(
            (
                "{0:>16}({1:>6})\t{2:>12}\t{3:>12}\t" "{4:>10}\t{5:>10}\n{6:1}"
            ).format(
                "Name", "TID", "Wait", "Lock", "nrTryLock", "nrLocked", twoLine
            )
        )

        for key, value in sorted(
            self.threadData.items(),
            key=lambda e: e[1]["lockWait"],
            reverse=True,
        ):
            if key.startswith("0["):
                continue
            elif (
                value["lockWait"]
                == value["lockTime"]
                == value["tryLockCnt"]
                == value["lockCnt"]
                == 0
            ):
                continue

            lockInfo = (
                "{0:>16}({1:>6})\t{2:>12}\t{3:>12}\t{4:>10}\t{5:>10}".format(
                    value["comm"],
                    key,
                    "%.3f" % float(value["lockWait"]),
                    "%.3f" % float(value["lockTime"]),
                    value["tryLockCnt"],
                    value["lockCnt"],
                )
            )
            SysMgr.printPipe("%s\n%s" % (lockInfo, oneLine))
            outputCnt += 1

        if outputCnt == 0:
            SysMgr.printPipe("    None\n%s" % oneLine)

        if not SysMgr.showAll:
            return

        SysMgr.printPipe(
            "\n[Thread File Lock History] (Unit: Sec/NR)\n%s" % twoLine
        )
        SysMgr.printPipe(
            (
                "{0:>16}({1:>6}) {2:>10} {3:>4} {4:>10} "
                "{5:>16} {6:>16} {7:>20}\n{8:1}"
            ).format(
                "Name",
                "TID",
                "Time",
                "Core",
                "Type",
                "Device",
                "Inode",
                "Context",
                twoLine,
            )
        )

        cnt = 0
        prevCnt = -1
        for icount in xrange(len(self.flockData)):
            try:
                if not self.flockData[icount][0] in self.threadData:
                    continue

                pos = self.flockData[icount][4].rfind("0x")
                dev = self.flockData[icount][4][:pos]
                inode = self.flockData[icount][4][pos:]
                atime = float(self.flockData[icount][1])
                time = "%.3f" % (atime - float(SysMgr.startTime))

                if (
                    prevCnt > -1
                    and self.flockData[prevCnt][0] == self.flockData[icount][0]
                ):
                    tid = comm = "."
                else:
                    comm = self.threadData[self.flockData[icount][0]]["comm"]
                    tid = "(%6s)" % self.flockData[icount][0]

                SysMgr.printPipe(
                    (
                        "{0:>16}{1:>8} {2:>10} {3:>4} {4:>10} "
                        "{5:>16} {6:>16} {7:>20}"
                    ).format(
                        comm,
                        tid,
                        time,
                        self.flockData[icount][2],
                        self.flockData[icount][3],
                        dev,
                        inode,
                        self.flockData[icount][5],
                    )
                )

                prevCnt = icount
                cnt += 1
            except:
                continue

        if cnt == 0:
            SysMgr.printPipe("    None")
        SysMgr.printPipe(oneLine)

    def printBinderInfo(self):
        if not self.binderServerData:
            return

        conv = UtilMgr.convNum

        # stats #
        for item in ("Server", "Client"):
            if item == "Server":
                dataList = self.binderServerData
                opposite = "Client"
            else:
                dataList = self.binderCliData
                opposite = "Server"

            # server #
            outputCnt = 0
            SysMgr.printPipe(
                "\n[Thread Binder %s Info] [Elapsed: %.3f] (Unit: Sec/NR)\n%s"
                % (item, float(self.totalTime), twoLine)
            )

            SysMgr.printPipe(
                (
                    "{0:>42} {1:>12} {2:>12} {3:>12} {4:>12} {5:>12} "
                    "{6:>27} {7:>12}({8:>4})\n{9:1}"
                ).format(
                    item,
                    "Elapsed",
                    "Count",
                    "Min",
                    "Max",
                    "Avg",
                    "%s[CODE]" % opposite,
                    "Count",
                    "Per",
                    twoLine,
                )
            )

            for key, value in sorted(
                dataList.items(), key=lambda e: e[1]["usage"], reverse=True
            ):
                try:
                    avg = "%.6f" % (value["usage"] / value["count"])
                except:
                    avg = "-"

                # total stats #
                SysMgr.printPipe(
                    "{0:>42} {1:>12} {2:>12} {3:>12} {4:>12} {5:>12}".format(
                        key,
                        "%.6f" % value["usage"],
                        conv(value["count"]),
                        "%.6f" % value["min"],
                        "%.6f" % value["max"],
                        avg,
                    )
                )

                outputCnt += 1

                if not SysMgr.showAll or not "others" in value:
                    continue

                # opposite-side stats #
                for ckey, cvalue in sorted(
                    value["others"].items(),
                    key=lambda e: e[1]["count"],
                    reverse=True,
                ):
                    # get values #
                    totalCnt = cvalue["count"]
                    codeList = cvalue["call"]

                    try:
                        per = (totalCnt / float(value["count"])) * 100
                    except:
                        per = 0

                    SysMgr.printPipe(
                        "\n{0:>92} {1:>42} {2:>12}({3:>3}%)".format(
                            " ", ckey, conv(totalCnt), "%d" % per
                        )
                    )

                    # code stats #
                    for code, callCnt in sorted(
                        codeList.items(), key=lambda e: e[1], reverse=True
                    ):
                        codestr = "[%4s]" % code

                        try:
                            cper = (callCnt / float(totalCnt)) * 100
                        except:
                            cper = 0

                        SysMgr.printPipe(
                            "{0:>92} {1:>42} {2:>12}({3:>3}%)".format(
                                " ", codestr, conv(callCnt), "%d" % cper
                            )
                        )

                SysMgr.printPipe(oneLine)

            if outputCnt == 0:
                SysMgr.printPipe("    None\n%s" % oneLine)
            elif not SysMgr.showAll:
                SysMgr.printPipe(oneLine)

        if not SysMgr.showAll:
            return

        # history #
        SysMgr.printPipe(
            "\n[Thread Binder History] (Unit: Sec/NR)\n%s" % twoLine
        )
        SysMgr.printPipe(
            (
                "{0:>10} {1:^8} {7:>10} {3:^45} -> {4:^45} {2:>10} "
                "{5:>8} {6:>8}\n{8:1}"
            ).format(
                "Time",
                "Type",
                "TranID",
                "Sender",
                "Receiver",
                "Flags",
                "Code",
                "Elapsed",
                twoLine,
            )
        )

        outputCnt = 0
        for item in self.binderData:
            # get values #
            (
                stime,
                reply,
                tranid,
                sender,
                receiver,
                flags,
                code,
                diff,
                oneway,
            ) = item

            # type #
            if reply:
                ttype = "    REP"
            elif oneway:
                ttype = "REQ/ONE"
            else:
                ttype = "REQ    "

            # diff #
            if diff:
                diff = "%.6f" % diff
            else:
                diff = " "

            SysMgr.printPipe(
                (
                    "{0:>10} {1:>8} {7:>10} {3:>45} -> {4:>45} {2:>10} "
                    "{5:>8} {6:>8}"
                ).format(
                    "%.6f" % stime,
                    ttype,
                    tranid,
                    sender,
                    receiver,
                    flags,
                    code,
                    diff,
                )
            )

            outputCnt += 1

        if outputCnt == 0:
            SysMgr.printPipe("    None\n%s" % oneLine)
        else:
            SysMgr.printPipe(oneLine)

    def printSyscallInfo(self):
        if not self.syscallData:
            return

        convNum = UtilMgr.convNum

        lineLen = 141
        outputCnt = 0
        SysMgr.printPipe(
            "\n[Thread Syscall Info] [Elapsed: %.3f] (Unit: Sec/NR)\n%s"
            % (float(self.totalTime), twoLine[:lineLen])
        )
        SysMgr.printPipe(
            (
                "{0:>16}({1:>7}) {2:>30}({3:>3}) {4:>12} {5:>12} "
                "{6:>12} {7:>12} {8:>12} {9:>12} |\n{10:1}"
            ).format(
                "Name",
                "TID",
                "Syscall",
                "SID",
                "Elapsed",
                "Count",
                "Error",
                "Min",
                "Max",
                "Avg",
                twoLine[:lineLen],
            )
        )

        totalInfo = {}

        for key, value in sorted(
            self.threadData.items(),
            key=lambda e: e[1]["nrSyscall"],
            reverse=True,
        ):
            threadInfo = ""
            syscallInfo = ""

            # skip swapper #
            if key.startswith("0["):
                continue

            try:
                if not value["syscallInfo"]:
                    continue

                threadInfo = "%16s(%7s)" % (value["comm"], key)
            except:
                continue

            for sysId, val in sorted(
                value["syscallInfo"].items(),
                key=lambda e: e[1]["usage"],
                reverse=True,
            ):
                if val["count"] == 0:
                    continue

                # apply syscall filter #
                if (
                    SysMgr.syscallList
                    and long(sysId) not in SysMgr.syscallList
                ):
                    continue

                # print per-thread syscall table #
                try:
                    val["average"] = "%.6f" % (val["usage"] / val["count"])
                    syscall = ConfigMgr.sysList[int(sysId)][4:]
                    syscallInfo = (
                        "{0:1} {1:>30}({2:>3}) {3:>12} "
                        "{4:>12} {5:>12} {6:>12} {7:>12} {8:>12} |\n"
                    ).format(
                        syscallInfo + (" " * len(threadInfo)),
                        syscall,
                        sysId,
                        "%.6f" % val["usage"],
                        convNum(val["count"]),
                        convNum(val["err"]),
                        "%.6f" % val["min"],
                        "%.6f" % val["max"],
                        val["average"],
                    )
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

                # add total info #
                try:
                    if not sysId in totalInfo:
                        totalInfo[sysId] = {}
                        totalInfo[sysId]["usage"] = 0
                        totalInfo[sysId]["count"] = 0
                        totalInfo[sysId]["err"] = 0
                        totalInfo[sysId]["min"] = 0
                        totalInfo[sysId]["max"] = 0
                        totalInfo[sysId]["average"] = 0

                    totalInfo[sysId]["usage"] += val["usage"]
                    totalInfo[sysId]["count"] += val["count"]
                    totalInfo[sysId]["err"] += val["err"]

                    if (
                        totalInfo[sysId]["min"] == 0
                        or totalInfo[sysId]["min"] > val["min"]
                    ):
                        totalInfo[sysId]["min"] = val["min"]

                    if (
                        totalInfo[sysId]["max"] == 0
                        or totalInfo[sysId]["max"] < val["max"]
                    ):
                        totalInfo[sysId]["max"] = val["max"]

                    totalInfo[sysId]["average"] = (
                        totalInfo[sysId]["usage"] / totalInfo[sysId]["count"]
                    )
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

            if syscallInfo != "":
                outputCnt += 1
                SysMgr.addPrint("{0:140}|\n".format(threadInfo), force=True)
                SysMgr.addPrint(
                    "%s%s\n" % (syscallInfo, oneLine[:lineLen]), force=True
                )

        if outputCnt == 0:
            SysMgr.printPipe(
                "{0:140}|\n{1:1}".format("    None", oneLine[:lineLen])
            )
        else:
            totalStrInfo = "{0:>25}{1:115}|".format("[ TOTAL ]", " ")
            SysMgr.printPipe(totalStrInfo)

            # print total info #
            syscallInfo = ""
            for sysId, val in sorted(
                totalInfo.items(), key=lambda e: e[1]["usage"], reverse=True
            ):
                try:
                    syscall = ConfigMgr.sysList[int(sysId)][4:]
                except:
                    continue

                syscallInfo = (
                    "{0:1} {1:>30}({2:>3}) {3:>12} "
                    "{4:>12} {5:>12} {6:>12} {7:>12} {8:>12} |"
                ).format(
                    " " * 25,
                    syscall,
                    sysId,
                    "%.6f" % val["usage"],
                    convNum(val["count"]),
                    convNum(val["err"]),
                    "%.6f" % val["min"],
                    "%.6f" % val["max"],
                    "%.6f" % val["average"],
                )

                SysMgr.printPipe(syscallInfo)
            SysMgr.printPipe("%s" % oneLine[:lineLen])

            # print thread info #
            SysMgr.doPrint(clear=True)

        if not SysMgr.showAll:
            return

        SysMgr.printPipe(
            "\n[Thread Syscall History] (Unit: Sec/NR)\n%s" % twoLine
        )
        SysMgr.printPipe(
            (
                "{0:>10} {1:>16}({2:>7}) {3:>4} {4:>18} {5:>3} "
                "{6:>5} {7:>10} {8:>16} {9:<1}\n{10:1}"
            ).format(
                "Time",
                "Name",
                "TID",
                "Core",
                "Syscall",
                "SID",
                "Type",
                "Elapsed",
                "Return",
                "Arguments",
                twoLine,
            )
        )

        # remove calls of unavailable threads #
        for icount in xrange(len(self.syscallData)):
            try:
                self.threadData[self.syscallData[icount][2]]
            except SystemExit:
                sys.exit(0)
            except:
                try:
                    del self.syscallData[icount]
                except SystemExit:
                    sys.exit(0)
                except:
                    break

        cnt = 0
        nrErr = 0
        prevCnt = -1
        proto = ConfigMgr.SYSCALL_PROTOTYPES
        startTime = float(SysMgr.startTime)
        for icount in xrange(len(self.syscallData)):
            try:
                prevData = self.syscallData[prevCnt]
                nowData = self.syscallData[icount]

                if nowData[1] == -1 or not nowData[2] in self.threadData:
                    continue

                if len(self.syscallData) > icount + 1:
                    nextData = self.syscallData[icount + 1]
                else:
                    nextData = None

                try:
                    syscall = ConfigMgr.sysList[int(nowData[4])]
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printWarn(
                        "failed to recognize syscall %s for number %s"
                        % (nowData[0], nowData[4]),
                        True,
                    )
                    continue

                if nowData[0] == "ENT":
                    # all #
                    if (
                        nextData
                        and nextData[0] == "RET"
                        and nowData[2] == nextData[2]
                        and nowData[4] == nextData[4]
                    ):
                        eventType = "{0:^5}".format("ALL")
                        eventTime = float(nowData[1]) - startTime
                        elapsed = "%6.6f" % (
                            float(nextData[1]) - float(nowData[1])
                        )
                        param = nowData[5]
                        ret = nextData[5]
                        nextData[1] = -1
                    else:
                        eventType = "{0:<5}".format(nowData[0])
                        eventTime = float(nowData[1]) - startTime
                        elapsed = " " * 8
                        param = nowData[5]
                        ret = " "

                    # trim real arguments #
                    try:
                        call = syscall[4:]
                        nrArgs = len(proto[call][1])
                        if nrArgs <= 0:
                            raise ValueError()

                        paramlist = param[1:-1].split(",")[:nrArgs]

                        # convert values #
                        for idx, args in enumerate(proto[call][1]):
                            val = paramlist[idx]

                            # check type #
                            if "*" in args[0]:
                                paramlist[idx] = "0x%s" % val.strip()
                                continue
                            if (
                                not "int" in args[0]
                                and not "short" in args[0]
                                and not "long" in args[0]
                            ):
                                paramlist[idx] = "0x%s" % val.strip()
                                continue

                            # type casting #
                            if "unsigned" in args[0]:
                                paramlist[idx] = str(long(val, 16))
                                continue

                            val = long(val, 16)
                            if "short" in args[0]:
                                paramlist[idx] = -(val & 0x8000) | (
                                    val & 0x7FFF
                                )
                            elif "int" in args[0]:
                                paramlist[idx] = -(val & 0x80000000) | (
                                    val & 0x7FFFFFFF
                                )

                        param = "(%s)" % ", ".join(list(map(str, paramlist)))
                    except SystemExit:
                        sys.exit(0)
                    except ValueError:
                        param = " "
                    except:
                        SysMgr.printWarn(
                            "failed to analyze syscall info", reason=True
                        )
                        param = " "
                        nrErr += 1

                elif nowData[0] == "RET":
                    eventType = nowData[0]
                    eventTime = float(nowData[1]) - startTime
                    param = " "
                    ret = nowData[5]

                    try:
                        elapsed = "%6.6f" % nowData[6]
                    except SystemExit:
                        sys.exit(0)
                    except:
                        elapsed = " " * 8
                        nrErr += 1

                try:
                    # convert error code #
                    nrRet = long(ret)
                    if nrRet < 0:
                        ret = ConfigMgr.ERR_TYPE[abs(nrRet) - 1]
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

                if prevCnt > -1 and prevData[2] == nowData[2]:
                    tid = comm = "."
                else:
                    comm = self.threadData[nowData[2]]["comm"]
                    tid = "(%7s)" % nowData[2]

                if prevCnt > -1 and prevData[3] == nowData[3]:
                    core = ""
                else:
                    core = nowData[3]

                SysMgr.printPipe(
                    (
                        "{0:>10} {1:>16}{2:>9} {3:>4} {4:>18} {5:>3} "
                        "{6:>5} {7:>10} {8:>16} {9:<1}"
                    ).format(
                        "%.6f" % eventTime,
                        comm,
                        tid,
                        core,
                        syscall[4:],
                        nowData[4],
                        eventType,
                        elapsed,
                        ret,
                        param,
                    )
                )

                prevCnt = icount
                cnt += 1
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr("failed to analyze syscall info", True)

        if cnt == 0:
            SysMgr.printPipe("    None")
        SysMgr.printPipe(oneLine)

        # print total error #
        if nrErr:
            SysMgr.printWarn(
                "failed to parse a total of %s syscalls" % convNum(nrErr), True
            )

    def printLMKInfo(self):
        if not self.lmkData:
            return

        SysMgr.printPipe("\n[Thread LMK Info] (Unit: KB)\n%s" % twoLine)
        SysMgr.printPipe(
            "%10s %16s %10s %10s %10s %10s\n%s"
            % ("Time", "Name", "TID", "free", "file", "minfree", twoLine)
        )

        startTime = float(SysMgr.startTime)

        cnt = 0
        for msg in self.lmkData:
            try:
                SysMgr.printPipe(
                    "%10.3f %16s %10s %10s %10s %10s"
                    % (
                        round(float(msg[0]) - startTime, 7),
                        msg[1],
                        msg[2],
                        msg[3],
                        msg[4],
                        msg[5],
                    )
                )
                cnt += 1
            except:
                continue

        if cnt == 0:
            SysMgr.printPipe("    None")
        SysMgr.printPipe(twoLine)

    def printConsoleInfo(self):
        if not self.consoleData or not SysMgr.showAll:
            return

        SysMgr.printPipe("\n[Thread Message Info]\n%s" % twoLine)
        SysMgr.printPipe(
            "%16s %7s %4s %10s %30s\n%s"
            % ("Name", "TID", "Core", "Time", "Console message", twoLine)
        )

        startTime = float(SysMgr.startTime)

        cnt = 0
        for msg in self.consoleData:
            try:
                SysMgr.printPipe(
                    "%16s %7s %4s %10.3f %s"
                    % (
                        self.threadData[msg[0]]["comm"],
                        msg[0],
                        msg[1],
                        round(float(msg[2]) - startTime, 7),
                        msg[3],
                    )
                )
                cnt += 1
            except:
                continue

        if cnt == 0:
            SysMgr.printPipe("    None")
        SysMgr.printPipe(twoLine)

    def printPageInfo(self):
        # check pages #
        if not self.allocPageData:
            return

        orderTable = list(sorted(self.allocPageData))
        orders = " ".join(
            ["{0:>5}".format(UtilMgr.convNum(order)) for order in orderTable]
        )
        SysMgr.printPipe("\n[Thread Page Info]\n%s" % twoLine)
        SysMgr.printPipe(
            "{0:^25} {1:>1}\n{2:1}".format("Thread / Order", orders, twoLine)
        )

        # print total pages #
        totalInfo = "{0:^25} ".format("TOTAL")
        for order in orderTable:
            totalInfo += "{0:>5} ".format(
                UtilMgr.convNum(self.allocPageData[order])
            )
        SysMgr.printPipe(totalInfo)
        SysMgr.printPipe(oneLine)

        # print task pages #
        for tid, value in sorted(
            self.threadData.items(),
            key=lambda e: e[1]["nrAllocPages"],
            reverse=True,
        ):
            if tid[0] == "0":
                continue
            if not "orderPages" in value:
                continue

            comm = self.threadData[tid]["comm"]
            taskInfo = "{0:>25} ".format("%s(%s)" % (comm, tid))
            for order in orderTable:
                if order in value["orderPages"]:
                    addval = UtilMgr.convNum(value["orderPages"][order])
                else:
                    addval = " "
                taskInfo += "{0:>5} ".format(addval)
            SysMgr.printPipe(taskInfo)

        SysMgr.printPipe(oneLine)

    def printBlockInfo(self):
        def _printBlkUsage(cid, data, opt, tcnt):
            convSize = UtilMgr.convSize2Unit
            convNum = UtilMgr.convNum

            for num, val in sorted(
                data.items(), key=lambda e: e[1], reverse=True
            ):
                if tcnt == 0:
                    pass
                else:
                    cid = " "

                try:
                    mountInfo = SysMgr.savedMountTree
                    fs = mountInfo[num]["filesystem"]
                    mountdata = mountInfo[num]["mount"]
                    if mountdata == "-":
                        dev = mountInfo[num]["dev"]
                    else:
                        dev = mountdata
                except SystemExit:
                    sys.exit(0)
                except:
                    dev = "?"
                    fs = "?"

                try:
                    seqPer = round((val[3] / float(val[0])) * 100, 1)
                except SystemExit:
                    sys.exit(0)
                except:
                    seqPer = "?"

                size = convSize(val[0])
                seqSize = convSize(val[3])
                seqString = "%s(%5.1f)" % (seqSize, seqPer)

                if tcnt > 0:
                    SysMgr.printPipe()

                SysMgr.printPipe(
                    (
                        "{0:>25} {1:>5} {2:>8} {3:>20} {4:>25} {5:^12} {6:1}"
                    ).format(cid, opt, num, size, seqString, fs, dev)
                )

                opt = ""

                # print per-operation size statistics #
                for optSize, cnt in sorted(val[5].items()):
                    start = convSize(optSize, True)
                    end = convSize((optSize << 1) - 1024, True)
                    SysMgr.printPipe(
                        (
                            "{0:^25} {0:^8} {0:^5} {1:>20} "
                            "{2:>25} {0:^12} {0:<20}"
                        ).format(
                            "", "[%7s - %7s]" % (start, end), convNum(cnt)
                        )
                    )

                tcnt += 1

            return tcnt

        # check block option #
        if not SysMgr.blockEnable:
            return

        SysMgr.printPipe("\n[Thread Block Info] (Unit: NR)\n%s" % twoLine)
        SysMgr.printPipe(
            "{0:^25} {1:>5} {2:>8} {3:>20} {4:>25} {5:^12} {6:^51} |".format(
                "ID",
                "OPT",
                "NrDev",
                "TOTAL",
                "SEQUENTIAL(    %)",
                "FS",
                "PATH",
            )
        )
        SysMgr.printPipe(
            (
                "{0:^25} {1:>5} {2:>8} {3:>20} {4:>25} "
                "{5:^12} {6:^51} |\n{7:1}"
            ).format("", "", "", "[ACCESS]", "COUNT", "", "", twoLine)
        )

        tcnt = 0
        totalStr = "{0:^25}".format("TOTAL")

        # total read #
        if self.blockTable[0]:
            tcnt = _printBlkUsage(totalStr, self.blockTable[0], "READ", tcnt)

        # total write #
        if self.blockTable[1]:
            tcnt = _printBlkUsage(totalStr, self.blockTable[1], "WRITE", tcnt)

        if tcnt > 0:
            SysMgr.printPipe(oneLine)
        else:
            SysMgr.printPipe("    None")
            SysMgr.printPipe(oneLine)

        # sort threads by read size #
        for tid, data in sorted(
            self.blockTable[2].items(), key=lambda e: e[1][2], reverse=True
        ):
            tcnt = 0

            # task info #
            if tid in self.threadData:
                comm = self.threadData[tid]["comm"][:16]
            else:
                comm = "??"
            cid = "%s(%s)" % (comm, tid)

            # thread read #
            if data[0]:
                tcnt = _printBlkUsage(cid, data[0], "READ", tcnt)

            # thread write #
            if data[1]:
                tcnt = _printBlkUsage(cid, data[1], "WRITE", tcnt)

            if tcnt > 0:
                SysMgr.printPipe(oneLine)

    def printOpenInfo(self):
        if not self.openData:
            return

        # print menu #
        SysMgr.printPipe("\n[Thread Open History] (Unit: NR)\n%s" % twoLine)
        SysMgr.printPipe(
            "{0:^10} {1:>25} {2:>6} {3:>6} {4:>1}\n{5:1}".format(
                "Time", "Task(Comm)", "Flags", "Mode", "Name", twoLine
            )
        )

        # print history #
        for item in self.openData:
            atime, tid, path, flags, mode = item
            if not tid in self.threadData:
                continue

            comm = self.threadData[tid]["comm"]
            taskInfo = "%s(%s)" % (comm, tid)

            SysMgr.printPipe(
                "{0:>10.6f} {1:>25} {2:>6} {3:>6} {4:>1}".format(
                    atime, taskInfo, flags, mode, path
                )
            )

        SysMgr.printPipe(twoLine)

    def printF2fsInfo(self):
        if not self.f2fsTable:
            return

        # print menu #
        SysMgr.printPipe("\n[Thread F2FS Info] (Unit: NR)\n%s" % twoLine)
        SysMgr.printPipe(
            ("{0:>25} {1:>7} {2:>8} {3:>12} {4:>12} {5:1}\n{6:1}").format(
                "Name(TID)", "OPT", "Size", "Count", "Time", "PATH", twoLine
            )
        )

        convSize = UtilMgr.convSize2Unit
        convNum = UtilMgr.convNum

        for tid, ops in self.f2fsTable.items():
            if tid == 0:
                name = "TOTAL"
            else:
                name = "%s(%s)" % (self.threadData[tid]["comm"], tid)

            SysMgr.printPipe("{0:>25}".format(name))

            for idx, op in enumerate(ops):
                opname = "READ" if idx == 0 else "WRITE"
                if not op:
                    continue

                SysMgr.printPipe(
                    "{0:>25} {1:>7} {2:>8} {3:>12} {4:>12}".format(
                        " ",
                        opname,
                        convSize(op["size"]),
                        convNum(op["count"]),
                        convNum(op["time"], True, 3),
                    )
                )

                for path, stats in sorted(
                    op["path"].items(), key=lambda x: x[0]
                ):
                    SysMgr.printPipe(
                        "{0:>25} {1:>7} {2:>8} {3:>12} {4:>12} {5:1}".format(
                            " ",
                            " ",
                            convSize(stats["size"]),
                            convNum(op["count"]),
                            convNum(op["time"], True, 3),
                            path,
                        )
                    )

            SysMgr.printPipe(oneLine)

    def printFsInfo(self):
        # get readahead items #
        (
            raPath,
            raMin,
            raAllowList,
            raDenyList,
            raAddList,
        ) = FileAnalyzer.getReadaheadItems()

        # check fs data #
        if raAddList:
            pass
        elif not self.fsTable or not self.fsTable[0]:
            if raPath:
                SysMgr.printWarn("no filesystem data for readahead list", True)
            return

        # print menu #
        SysMgr.printPipe("\n[Thread FS Info] (Unit: NR)\n%s" % twoLine)
        SysMgr.printPipe(
            (
                "{0:^25} {1:>7} {2:>8} {3:>12} {4:>12} "
                "{5:>12} {6:<75}\n{7:1}"
            ).format(
                "ID", "OPT", "NrDev", "INODE", "Size", "FS", "PATH", twoLine
            )
        )

        convSize = UtilMgr.convSize2Unit

        # merge target inode list #
        inodeList = {}
        if self.fsTable[0]:
            for value in self.fsTable[0].values():
                for dev, inodes in value.items():
                    if dev in inodeList:
                        inodeList[dev].update(inodes)
                    else:
                        inodeList[dev] = inodes

        # make target inode filter #
        fileInfo = {}
        inodeInfo = {}
        inodeCache = {}
        inodeFilter = []
        mountInfo = SysMgr.savedMountTree
        for did in list(inodeList):
            inodeFilter += list(inodeList[did])
        inodeFilter = list(set(inodeFilter))

        # get target inode info #
        if inodeFilter and "CONVINODE" in SysMgr.environList:
            # get scan dir #
            if not "SET" in SysMgr.environList["CONVINODE"]:
                targetDir = os.path.realpath(
                    SysMgr.environList["CONVINODE"][0]
                )
                # check dir #
                if not os.path.isdir(targetDir):
                    SysMgr.printErr(
                        "wrong dir path '%s' for inode scan" % targetDir
                    )
                    sys.exit(-1)
            else:
                targetDir = "/"

            SysMgr.printStat(
                r"start traversing inodes from '%s'..." % targetDir
            )

            exceptFilter = SysMgr.environList.get("EXCEPTFILE", [])

            # get inode info #
            inodeInfo = UtilMgr.getInodes(
                targetDir,
                inodeFilter=inodeFilter,
                exceptFilter=exceptFilter,
                fileAttr=fileInfo,
            )

        # TOTAL #
        idstr = "TOTAL"
        for op, data in sorted(
            self.fsTable[0].items(), key=lambda e: str(e[1]), reverse=True
        ):
            opSize = 0
            devStr = ""

            for did, item in sorted(data.items()):
                try:
                    fs = mountInfo[did]["filesystem"]
                    mountdata = mountInfo[did]["mount"]
                    if mountdata == "-":
                        dev = mountInfo[did]["dev"]
                    else:
                        dev = mountdata
                except SystemExit:
                    sys.exit(0)
                except:
                    dev = "?"
                    fs = "?"

                totalSize = 0
                inodeStr = ""

                for inode, cnt in sorted(
                    item.items(), key=lambda e: e[1], reverse=True
                ):
                    # convert page to size #
                    realSize = cnt << 12
                    totalSize += realSize
                    size = convSize(realSize)

                    # convert inode to path #
                    """
                    use a below command to convert inode to path
                    # debugfs -R 'ncheck INODE' DEVNODE_PATH
                    """
                    # set file name #
                    if did in inodeInfo and inode in inodeInfo[did]:
                        path = inodeInfo[did][inode]
                        if path in fileInfo:
                            fsize = convSize(fileInfo[path].st_size)
                            path = "%s[%s]" % (path, fsize)
                    # search candidate files for incorrect device ID #
                    else:
                        path = " "
                        pathList = []
                        for devid in sorted(list(inodeInfo)):
                            if not inode in inodeInfo[devid]:
                                continue
                            cpath = inodeInfo[devid][inode]
                            if cpath in fileInfo:
                                fsize = convSize(fileInfo[cpath].st_size)
                                cpath = "%s[%s]" % (cpath, fsize)
                            pathList.append(cpath)
                        if pathList:
                            path = " | ".join(pathList)

                    # cache inode #
                    cacheId = "%s#%s" % (did, inode)
                    inodeCache[cacheId] = path

                    # build final string #
                    inodeStr += (
                        "{0:^25} {1:>7} {2:>8} {3:>12} {4:>12} {5:>12} {6:<1}\n"
                    ).format(" ", " ", " ", inode, size, " ", path)

                opSize += totalSize

                devStr += (
                    "{0:^25} {1:>7} {2:>8} {3:>12} {4:>12} " "{5:>12} {6:<1}\n"
                ).format("", "", did, "", convSize(totalSize), fs, dev)

                devStr += inodeStr

            SysMgr.printPipe(
                "{0:^25} {1:>7} {2:>8} {3:>12} {4:>12} ".format(
                    idstr, op, " ", " ", convSize(opSize)
                )
            )
            SysMgr.printPipe(devStr)
            idstr = ""

        if not self.fsTable or not self.fsTable[0]:
            SysMgr.printPipe("    None\n")
        SysMgr.printPipe(oneLine)

        # THREAD #
        previnfo = ""
        for tid, size in sorted(
            self.fsTable[2].items(), key=lambda e: e[1], reverse=True
        ):
            # check thread data #
            if not tid in self.fsTable[1]:
                continue

            # define thread info #
            if tid in self.threadData:
                tinfo = "%s(%s)" % (self.threadData[tid]["comm"][:16], tid)
            else:
                tinfo = "??(%s)" % (tid)

            for op, data in sorted(
                self.fsTable[1][tid].items(),
                key=lambda e: str(e[1]),
                reverse=True,
            ):
                opSize = 0
                devStr = ""

                for did, item in sorted(data.items()):
                    try:
                        fs = mountInfo[did]["filesystem"]
                        mountdata = mountInfo[did]["mount"]
                        if mountdata == "-":
                            dev = mountInfo[did]["dev"]
                        else:
                            dev = mountdata
                    except SystemExit:
                        sys.exit(0)
                    except:
                        dev = "?"
                        fs = "?"

                    totalSize = 0
                    inodeStr = ""

                    for inode, cnt in sorted(
                        item.items(), key=lambda e: e[1], reverse=True
                    ):
                        # convert page to size #
                        realSize = cnt << 12
                        totalSize += realSize
                        size = convSize(realSize)

                        # get file name from inode cache #
                        cacheId = "%s#%s" % (did, inode)
                        path = inodeCache[cacheId]

                        # build final string #
                        inodeStr += (
                            "{0:^25} {1:>7} {2:>8} {3:>12} {4:>12} "
                            "{5:>12} {6:<1}\n"
                        ).format(" ", " ", " ", inode, size, " ", path)

                    opSize += totalSize

                    devStr += (
                        "{0:>25} {1:>7} {2:>8} {3:>12} {4:>12} {5:>12} {6:<1}\n"
                    ).format("", "", did, "", convSize(totalSize), fs, dev)

                    devStr += inodeStr

                # update previous thread info #
                if previnfo == tinfo:
                    tinfo = ""
                previnfo = tinfo

                SysMgr.printPipe(
                    "{0:>25} {1:>7} {2:>8} {3:>12} {4:>12} ".format(
                        tinfo, op, " ", " ", convSize(opSize)
                    )
                )
                SysMgr.printPipe(devStr)
            SysMgr.printPipe(oneLine)

        # check return condition #
        if raPath and self.fsData[0]:
            pass
        elif raAddList:
            pass
        elif not SysMgr.showAll:
            return
        elif not self.fsData[0] and not self.fsData[1]:
            return

        # define readahead list #
        pathConvList = {}
        readaheadList = []

        # print menu #
        SysMgr.printPipe(
            "\n[Thread FS History] (Type: Read) (Unit: Byte)\n%s" % twoLine
        )
        SysMgr.printPipe(
            (
                "{0:>8} {1:>25} {2:>7} {3:>10} {4:>16} "
                "{5:>12} {6:<1}\n{7:1}"
            ).format(
                "Time",
                "Task",
                "Dev",
                "Inode",
                "Offset",
                "Size",
                "Path",
                twoLine,
            )
        )

        skipFiles = {}
        for item in self.fsData[0]:
            tid, atime, dev, inode, offset, size = item

            # set task info #
            if not tid in self.threadData:
                continue
            comm = self.threadData[tid]["comm"]
            taskInfo = "%s(%s)" % (comm, tid)

            # get file path #
            cacheId = "%s#%s" % (dev, inode)
            if cacheId in inodeCache:
                path = inodeCache[cacheId]
            else:
                path = ""

            SysMgr.printPipe(
                "{0:>8} {1:>25} {2:>7} {3:>10} {4:>16} {5:>12} {6:<1}".format(
                    atime, taskInfo, dev, inode, offset, size, path
                )
            )

            # check readahead list #
            if raPath and path.strip():
                if path in pathConvList:
                    path, idx = pathConvList[path]
                # skip implicit files #
                elif "|" in path:
                    continue
                else:
                    origPath = path
                    pathConvList[origPath] = [
                        path[: path.rfind("[")],
                        len(pathConvList),
                    ]
                    path, idx = pathConvList[origPath]

                # check allow list #
                if raAllowList and not UtilMgr.isValidStr(path, raAllowList):
                    skip = True
                # check deny list #
                elif raDenyList and UtilMgr.isValidStr(path, raDenyList):
                    skip = True
                else:
                    skip = False

                # check skip condition #
                if skip:
                    skipFiles.setdefault(path, None)
                    continue

                # add to readahead list #
                readaheadList.append([idx, offset, size])

        # print no item result #
        if not self.fsData[0]:
            SysMgr.printPipe("    None\n")
        SysMgr.printPipe(oneLine)

        if not raPath:
            return

        # print skip files #
        for path in sorted(list(skipFiles)):
            SysMgr.printWarn("skipped adding '%s' to readahead list" % path)

        # make readahead list file #
        readaheadList, raSummary = FileAnalyzer.makeReadaheadFile(
            raPath, readaheadList, pathConvList, raMin, raAddList
        )

        # print readahead list info #
        SysMgr.printPipe(
            FileAnalyzer.getReadaheadListStr(readaheadList, raSummary)
        )

    def printEventIntervalInfo(self):
        # timeline #
        titleLine = "%16s(%7s/%7s):" % ("Name", "TID", "PID")
        maxLineLen = SysMgr.lineLength
        titleLineLen = len(titleLine)
        intervalEnable = SysMgr.intervalEnable

        # define event printer #
        def _printEvents(eventList, target, event):
            for idx, val in sorted(
                eventList.items(),
                key=lambda e: e[1][target],
                reverse=True,
            ):
                for key, value in sorted(
                    self.threadData.items(),
                    key=lambda e: 0 if not idx in e[1] else e[1][idx]["count"],
                    reverse=True,
                ):
                    if not value[event] or not idx in value[event]:
                        continue
                    timeLine = ""
                    timeLineLen = titleLineLen
                    lval = long(float(self.totalTime) / intervalEnable) + 1
                    for icount in xrange(lval):
                        newFlag = " "
                        dieFlag = " "

                        if timeLineLen + 4 > maxLineLen:
                            timeLine += "\n" + (" " * (titleLineLen + 1))
                            timeLineLen = titleLineLen + 4
                        else:
                            timeLineLen += 4

                        try:
                            self.intData[icount][key][event]
                        except:
                            timeLine += "%3d " % 0
                            continue

                        nowVal = self.intData[icount][key]

                        try:
                            prevVal = self.intData[icount - 1][key]
                        except:
                            prevVal = nowVal

                        if icount > 0:
                            try:
                                if nowVal["new"] != prevVal["new"]:
                                    newFlag = nowVal["new"]
                            except:
                                newFlag = nowVal["new"]
                            try:
                                if nowVal["die"] != prevVal["die"]:
                                    dieFlag = nowVal["die"]
                            except:
                                dieFlag = nowVal["die"]
                        else:
                            newFlag = nowVal["new"]
                            dieFlag = nowVal["die"]

                        if idx in nowVal[event]:
                            res = str(nowVal[event][idx]["count"])
                        else:
                            SysMgr.printWarn(
                                "no %s event in %s list" % (idx, event), True
                            )
                            res = "0"

                        timeLine += "%4s" % (newFlag + res + dieFlag)

                    if (
                        idx not in value or value[idx]["count"] == 0
                    ) and not SysMgr.showAll:
                        break

                    SysMgr.addPrint(
                        "%16s(%7s/%7s): "
                        % (
                            self.threadData[key]["comm"]
                            if key in self.threadData
                            else "?",
                            key,
                            self.threadData[key]["tgid"]
                            if key in self.threadData
                            else "?",
                        )
                        + timeLine
                        + "\n"
                    )

                SysMgr.printPipe("%s# %s\n" % ("", "%s(Cnt)" % idx))
                SysMgr.doPrint(clear=True)
                SysMgr.printPipe(oneLine)

        # custom event usage on timeline #
        SysMgr.clearPrint()
        if SysMgr.customEventList:
            _printEvents(self.customEventInfo, "count", "customEvent")

        # user event usage on timeline #
        SysMgr.clearPrint()
        if SysMgr.userEventList:
            _printEvents(self.userEventInfo, "usage", "userEvent")

        # kernel event usage on timeline #
        SysMgr.clearPrint()
        if SysMgr.kernelEventList:
            _printEvents(self.kernelEventInfo, "usage", "kernelEvent")

    def printIntervalInfo(self):
        # pylint: disable=undefined-variable,import-error

        if SysMgr.intervalEnable <= 0 or not (
            SysMgr.cpuEnable or SysMgr.memEnable or SysMgr.blockEnable
        ):
            return

        # print title #
        intervalEnable = SysMgr.intervalEnable
        SysMgr.printPipe(
            "\n[Thread Interval Info] [Start: %s] (Unit: %s Sec)\n%s"
            % (round(float(SysMgr.startTime), 7), intervalEnable, twoLine)
        )

        # graph list #
        cpuUsageList = []
        cpuThrLabelList = []
        cpuThrUsageList = []
        ioLabelList = []
        ioUsageList = []

        # timeline #
        timeLine = ""
        titleLine = "%16s(%7s/%7s):" % ("Name", "TID", "PID")
        maxLineLen = SysMgr.lineLength
        timeLineLen = titleLineLen = len(titleLine)
        startTime = float(SysMgr.startTime)
        if hasattr(self, "trimStart"):
            startTime += self.trimStart
        lval = long(float(self.totalTime) / intervalEnable) + 2
        for icount in xrange(1, lval):
            checkEvent = " "
            cnt = icount - 1

            # check suspend event #
            for val in self.suspendData:
                if (
                    startTime + cnt * intervalEnable
                    < float(val[0])
                    < startTime + ((cnt + 1) * intervalEnable)
                ):
                    if val[1] == "S":
                        checkEvent = "!"
                    elif val[1] == "F":
                        checkEvent = "^"
                    else:
                        checkEvent = ">"

            # check mark event #
            for val in self.markData:
                if (
                    startTime + cnt * intervalEnable
                    < float(val)
                    < startTime + ((cnt + 1) * intervalEnable)
                ):
                    checkEvent = "v"

            if timeLineLen + 4 > maxLineLen:
                timeLine += "\n" + (" " * (titleLineLen + 1))
                timeLineLen = titleLineLen + 4
            else:
                timeLineLen += 4

            # print timeline #
            if icount * intervalEnable <= float(self.totalTime):
                timeLine += "%s%2d " % (checkEvent, icount * intervalEnable)
            else:
                timeLine += "%s%.2f " % (checkEvent, self.totalTime)

        SysMgr.printPipe("%s %s\n%s" % (titleLine, timeLine, twoLine))
        SysMgr.clearPrint()

        # total CPU usage on timeline #
        cpuStr = ""
        lval = long(float(self.totalTime) / intervalEnable) + 1
        cpuAvgUsage = [0] * lval
        cpuCnt = 0
        for key, value in sorted(
            self.threadData.items(),
            key=lambda e: TaskAnalyzer.getCoreId(e[1]["comm"]),
            reverse=False,
        ):
            if not SysMgr.cpuEnable:
                break
            elif not key.startswith("0["):
                continue

            coreId = key[2:-1]
            cpuCnt += 1

            timeLine = ""
            timeLineLen = titleLineLen
            for icount in xrange(lval):
                try:
                    # revise core usage in DVFS system #
                    if self.threadData[key]["coreSchedCnt"] == 0 and (
                        self.threadData[key]["offCnt"] > 0
                        or not coreId in self.lastTidPerCore
                        or self.lastTidPerCore[coreId] == 0
                    ):
                        raise Exception("core off")
                    else:
                        per = 100 - self.intData[icount][key]["cpuPer"]
                        timeLine += "%3d " % per
                        cpuAvgUsage[icount] += per
                except SystemExit:
                    sys.exit(0)
                except:
                    timeLine += "%3s " % "0"

                if timeLineLen + 4 >= maxLineLen:
                    timeLine += "\n" + (" " * (titleLineLen + 1))
                    timeLineLen = titleLineLen + 4
                else:
                    timeLineLen += 4

            cpuStr += (
                "%16s(%7s/%7s): " % (value["comm"][:16], "-", "-")
                + timeLine
                + "\n"
            )

            # make CPU usage list for graph #
            if SysMgr.graphEnable and SysMgr.cpuEnable:
                timeLine = timeLine.replace("N", "")
                timeLine = timeLine.replace("D", "")
                timeLine = timeLine.replace("F", "")
                timeLineData = [int(n) for n in timeLine.split()]
                cpuUsageList.append(timeLineData)

        # average CPU usage on timeline #
        if SysMgr.cpuEnable:
            timeLine = ""
            timeLineLen = titleLineLen
            for icount, per in enumerate(cpuAvgUsage):
                try:
                    timeLine += "%3d " % (per / cpuCnt)
                except:
                    timeLine += "%3s " % "0"

                if timeLineLen + 4 >= maxLineLen:
                    timeLine += "\n" + (" " * (titleLineLen + 1))
                    timeLineLen = titleLineLen + 4
                else:
                    timeLineLen += 4

            # print final CPU usage #
            cpuAvgStr = (
                "%16s(%7s/%7s): " % ("CPU/AVG", "-", "-") + timeLine + "\n"
            ) + cpuStr
            SysMgr.addPrint(cpuAvgStr)

        # total memory usage on timeline #
        timeLine = ""
        timeLineLen = titleLineLen
        for icount in xrange(lval):
            if timeLineLen + 4 > maxLineLen:
                timeLine += "\n" + (" " * (titleLineLen + 1))
                timeLineLen = titleLineLen + 4
            else:
                timeLineLen += 4

            try:
                timeLine += "%3d " % (
                    (self.intData[icount]["toTal"]["totalMem"] >> 8)
                    + (self.intData[icount]["toTal"]["totalKmem"] >> 20)
                )
            except:
                timeLine += "%3d " % (0)

        if SysMgr.memEnable:
            SysMgr.addPrint(
                "\n%16s(%7s/%7s): " % ("MEM", "-", "-") + timeLine + "\n"
            )
            if SysMgr.graphEnable:
                timeLineData = [int(n) for n in timeLine.split()]
                ioUsageList.append(timeLineData)
                ioLabelList.append("RAM Usage")

        # total block usage on timeline #
        if SysMgr.blockEnable:
            # total block read usage on timeline #
            brtotal = 0
            timeLine = ""
            timeLineLen = titleLineLen
            for icount in xrange(lval):
                if timeLineLen + 4 > maxLineLen:
                    timeLine += "\n" + (" " * (titleLineLen + 1))
                    timeLineLen = titleLineLen + 4
                else:
                    timeLineLen += 4

                try:
                    timeLine += "%3d " % (
                        (
                            self.intData[icount]["toTal"]["totalBr"]
                            * SysMgr.blockSize
                        )
                        >> 20
                    )
                    brtotal += self.intData[icount]["toTal"]["totalBr"]
                except:
                    timeLine += "%3d " % (0)

            if brtotal > 0:
                SysMgr.addPrint(
                    "\n%16s(%7s/%7s): " % ("BLK_RD", "-", "-")
                    + timeLine
                    + "\n"
                )
                if SysMgr.graphEnable:
                    timeLineData = [int(n) for n in timeLine.split()]
                    ioUsageList.append(timeLineData)
                    ioLabelList.append("Block Read")

            # total block write usage on timeline #
            bwtotal = 0
            timeLine = ""
            timeLineLen = titleLineLen
            for icount in xrange(lval):
                if timeLineLen + 4 > maxLineLen:
                    timeLine += "\n" + (" " * (titleLineLen + 1))
                    timeLineLen = titleLineLen + 4
                else:
                    timeLineLen += 4

                try:
                    timeLine += "%3d " % (
                        (
                            self.intData[icount]["toTal"]["totalBw"]
                            * SysMgr.blockSize
                        )
                        >> 20
                    )
                    bwtotal += self.intData[icount]["toTal"]["totalBw"]
                except:
                    timeLine += "%3d " % (0)

            if bwtotal > 0:
                if brtotal == 0:
                    SysMgr.addPrint("\n")
                SysMgr.addPrint(
                    "%16s(%7s/%7s): " % ("BLK_WR", "0", "-----")
                    + timeLine
                    + "\n"
                )
                if SysMgr.graphEnable:
                    timeLineData = [int(n) for n in timeLine.split()]
                    ioUsageList.append(timeLineData)
                    ioLabelList.append("Block Write")

        # define event printer #
        def _printEventInterval(eventInfo, name):
            newLine = True
            for evt, value in sorted(
                eventInfo.items(),
                key=lambda e: e[1]["count"],
                reverse=True,
            ):
                timeLine = ""
                timeLineLen = titleLineLen
                for i in xrange(lval):
                    if timeLineLen + 4 > maxLineLen:
                        timeLine += "\n" + (" " * (titleLineLen + 1))
                        timeLineLen = titleLineLen + 4
                    else:
                        timeLineLen += 4

                    try:
                        intval = self.intData[i]["toTal"][name][evt]["count"]
                        timeLine += "%3d " % intval
                    except:
                        timeLine += "%3d " % 0

                if newLine:
                    SysMgr.addPrint("\n")
                    newLine = False

                SysMgr.addPrint(
                    "%16s(%7s/%7s): " % (evt[: SysMgr.commLen], "-", "-")
                    + timeLine
                    + "\n"
                )

        # total custom event usage on timeline #
        _printEventInterval(self.customEventInfo, "customEvent")

        # total user event usage on timeline #
        _printEventInterval(self.userEventInfo, "userEvent")

        # total kernel event usage on timeline #
        _printEventInterval(self.kernelEventInfo, "kernelEvent")

        # print buffered info #
        SysMgr.printPipe("%s# %s\n" % ("", "Total(%/MB/Cnt)"))
        SysMgr.doPrint()
        SysMgr.printPipe(oneLine)
        SysMgr.clearPrint()

        if SysMgr.graphEnable:
            # get matplotlib object #
            matplotlib = SysMgr.getPkg("matplotlib", False)
            if not matplotlib:
                SysMgr.printPipWarn("matplotlib", "matplotlib")
                sys.exit(-1)
            from matplotlib.ticker import MaxNLocator

            SysMgr.matplotlibVersion = float(
                ".".join(matplotlib.__version__.split(".")[:2])
            )

            # set backend #
            matplotlib.pyplot.switch_backend("agg")

            # get pylab object #
            SysMgr.importPkgItems("pylab")

        # draw I/O graph #
        if SysMgr.graphEnable and len(ioUsageList) > 0:
            timelen = len(ioUsageList[0])
            ax = subplot2grid((6, 1), (5, 0), rowspan=1, colspan=1)
            ax.xaxis.set_major_locator(MaxNLocator(integer=True))
            ax.yaxis.set_major_locator(MaxNLocator(integer=True))

            timeline = list(
                xrange(
                    intervalEnable,
                    (timelen + 1) * intervalEnable,
                    intervalEnable,
                )
            )
            timeline[-1] = self.totalTime

            for idx, item in enumerate(ioUsageList):
                minIdx = item.index(min(item))
                maxIdx = item.index(max(item))
                nrColor = long(idx) % 3
                if nrColor == 0:
                    color = "blue"
                elif nrColor == 1:
                    color = "red"
                else:
                    color = "green"

                plot(timeline, item, "-", c=color)

                margin = self.getMargin()

                if minIdx > 0:
                    minUsage = str(item[minIdx])
                    text(
                        minIdx + 1,
                        item[minIdx] - margin,
                        minUsage,
                        fontsize=self.lfsize + 1,
                        color=color,
                        fontweight="bold",
                    )
                if maxIdx > 0:
                    maxUsage = str(item[maxIdx])
                    text(
                        maxIdx + 1,
                        item[maxIdx] - margin,
                        maxUsage,
                        fontsize=self.lfsize + 1,
                        color=color,
                        fontweight="bold",
                    )

            # draw label #
            TaskAnalyzer.drawLabel(ioLabelList, draw=True, anchor=(1.1, 1))

            # add % unit to each value #
            try:
                ytickLabel = ax.get_yticks().tolist()
                ytickLabel = list(map(long, ytickLabel))

                # convert label units #
                convNum = UtilMgr.convSize2Unit
                ytickLabel = [convNum(val << 20, True) for val in ytickLabel]

                ax.set_yticklabels(ytickLabel)
            except:
                pass

            grid(which="both", linestyle=":", linewidth=0.2)
            yticks(fontsize=self.xfsize)
            xticks(fontsize=self.xfsize)

            # draw base #
            TaskAnalyzer.drawFigure()

        # CPU usage on timeline #
        for key, value in sorted(
            self.threadData.items(), key=lambda e: e[1]["usage"], reverse=True
        ):
            if key.startswith("0["):
                continue

            timeLine = ""
            timeLineLen = titleLineLen
            for icount in xrange(lval):
                newFlag = " "
                dieFlag = " "

                if timeLineLen + 4 > maxLineLen:
                    timeLine += "\n" + (" " * (titleLineLen + 1))
                    timeLineLen = titleLineLen + 4
                else:
                    timeLineLen += 4

                try:
                    self.intData[icount][key]
                except:
                    timeLine += "%3d " % 0
                    continue

                nowVal = self.intData[icount][key]

                try:
                    prevVal = self.intData[icount - 1][key]
                except:
                    prevVal = nowVal

                if icount > 0:
                    try:
                        if nowVal["new"] != prevVal["new"]:
                            newFlag = nowVal["new"]
                    except:
                        newFlag = nowVal["new"]
                    try:
                        if nowVal["die"] != prevVal["die"]:
                            dieFlag = nowVal["die"]
                    except:
                        dieFlag = nowVal["die"]
                else:
                    newFlag = self.intData[icount][key]["new"]
                    dieFlag = self.intData[icount][key]["die"]

                # Do not use 100% because of output format #
                cpuPer = str(long(self.intData[icount][key]["cpuPer"]))
                if cpuPer == "100":
                    cpuPer = "99"

                timeLine += "%4s" % (newFlag + cpuPer + dieFlag)

            try:
                pid = SysMgr.savedProcTree[key]
            except:
                pid = value["tgid"]

            SysMgr.addPrint(
                "%16s(%7s/%7s): " % (value["comm"][:16], key, pid)
                + timeLine
                + "\n"
            )

            if SysMgr.graphEnable and SysMgr.cpuEnable:
                timeLine = timeLine.replace("N", "")
                timeLine = timeLine.replace("D", "")
                timeLine = timeLine.replace("F", "")
                cpuThrUsageList.append([int(n) for n in timeLine.split()])
                tinfo = "%s(%s)" % (value["comm"], key)
                cpuThrLabelList.append(tinfo)

            if (
                not SysMgr.showAll
                and (value["usage"] / float(self.totalTime)) * 100 < 1
            ):
                break

        # draw CPU graph #
        if SysMgr.graphEnable and len(cpuUsageList) > 0:
            timelen = len(cpuUsageList[0])
            ax = subplot2grid((6, 1), (0, 0), rowspan=5, colspan=1)
            ax.xaxis.set_major_locator(MaxNLocator(integer=True))
            ax.yaxis.set_major_locator(MaxNLocator(integer=True))

            # CPU total usage #
            totalCpuUsage = None
            for item in cpuUsageList:
                if totalCpuUsage is None:
                    totalCpuUsage = item
                    continue

                totalCpuUsage = list(map(int.__add__, totalCpuUsage, item))

            avgCpuUsage = [x / len(cpuUsageList) for x in totalCpuUsage]

            timeline = list(
                xrange(
                    intervalEnable,
                    (timelen + 1) * intervalEnable,
                    intervalEnable,
                )
            )
            timeline[-1] = self.totalTime

            plot(
                timeline,
                avgCpuUsage,
                ".-",
                linewidth=3,
                solid_capstyle="round",
            )

            # CPU usage of threads #
            for idx, item in enumerate(cpuThrUsageList):
                maxIdx = item.index(max(item))

                color = plot(timeline, item, "-")[0].get_color()

                margin = self.getMargin()

                maxCpuPer = str(item[maxIdx])
                label = "%s[max: %s%%]" % (cpuThrLabelList[idx], maxCpuPer)
                text(
                    maxIdx + 1,
                    item[maxIdx] + margin,
                    label,
                    fontsize=self.lfsize,
                    color=color,
                    fontweight="bold",
                )

            # draw label #
            totalLabel = [" CPU Average "] + cpuThrLabelList
            TaskAnalyzer.drawLabel(totalLabel, draw=True, anchor=(1.12, 1))

            # add % unit to each value #
            try:
                ytickLabel = ax.get_yticks().tolist()
                ymax = max(ytickLabel)
                ylim([0, ymax + long(ymax / 10)])
                inc = long(ymax / 10)
                if inc == 0:
                    inc = 1
                yticks(xrange(0, long(ymax + inc), inc), fontsize=5)
            except:
                pass

            suptitle("Guider Thread Graph", fontsize=8)

            grid(which="both", linestyle=":", linewidth=0.2)
            yticks(fontsize=self.xfsize)
            xticks(fontsize=self.xfsize)

            # draw base #
            TaskAnalyzer.drawFigure()

        if SysMgr.cpuEnable:
            SysMgr.printPipe("%s# %s\n" % ("", "CPU(%)"))
            SysMgr.doPrint()
            SysMgr.printPipe(oneLine)

        # preempted units on timeline #
        SysMgr.clearPrint()
        for key, value in sorted(
            self.threadData.items(),
            key=lambda e: e[1]["cpuWait"],
            reverse=True,
        ):
            if (
                value["cpuWait"] / float(self.totalTime)
            ) * 100 < 1 and not SysMgr.showAll:
                break
            elif key.startswith("0["):
                continue

            timeLine = ""
            timeLineLen = titleLineLen
            for icount in xrange(lval):
                newFlag = " "
                dieFlag = " "

                if timeLineLen + 4 > maxLineLen:
                    timeLine += "\n" + (" " * (titleLineLen + 1))
                    timeLineLen = titleLineLen + 4
                else:
                    timeLineLen += 4

                try:
                    self.intData[icount][key]
                except:
                    timeLine += "%3d " % 0
                    continue

                nowVal = self.intData[icount][key]

                try:
                    prevVal = self.intData[icount - 1][key]
                except:
                    prevVal = nowVal

                if icount > 0:
                    try:
                        if nowVal["new"] != prevVal["new"]:
                            newFlag = self.intData[icount][key]["new"]
                    except:
                        newFlag = nowVal["new"]
                    try:
                        if nowVal["die"] != prevVal["die"]:
                            dieFlag = nowVal["die"]
                    except:
                        dieFlag = nowVal["die"]
                else:
                    newFlag = nowVal["new"]
                    dieFlag = nowVal["die"]

                # Do not use 100% because of output format #
                totalPrt = nowVal["preempted"] / float(intervalEnable)
                prtPer = str(long(totalPrt * 100))
                if prtPer == "100":
                    prtPer = "99"

                timeLine += "%4s" % (newFlag + prtPer + dieFlag)

            try:
                pid = SysMgr.savedProcTree[key]
            except:
                pid = value["tgid"]

            SysMgr.addPrint(
                "%16s(%7s/%7s): " % (value["comm"][:16], key, pid)
                + timeLine
                + "\n"
            )

        if SysMgr.bufferString:
            SysMgr.printPipe("%s# %s\n" % ("", "Delay(%)"))
            SysMgr.doPrint()
            SysMgr.printPipe(oneLine)

        # memory usage on timeline #
        SysMgr.clearPrint()
        if SysMgr.memEnable:
            for key, value in sorted(
                self.threadData.items(),
                key=lambda e: e[1]["nrPages"],
                reverse=True,
            ):
                if (
                    not SysMgr.showAll
                    and (value["nrPages"] >> 8) + (value["remainKmem"] >> 20)
                    < 1
                ):
                    break
                elif key.startswith("0["):
                    continue

                timeLine = ""
                timeLineLen = titleLineLen
                for icount in xrange(lval):
                    newFlag = " "
                    dieFlag = " "

                    if timeLineLen + 4 > maxLineLen:
                        timeLine += "\n" + (" " * (titleLineLen + 1))
                        timeLineLen = titleLineLen + 4
                    else:
                        timeLineLen += 4

                    try:
                        self.intData[icount][key]
                    except:
                        timeLine += "%3d " % 0
                        continue

                    nowVal = self.intData[icount][key]

                    try:
                        prevVal = self.intData[icount - 1][key]
                    except:
                        prevVal = nowVal

                    if icount > 0:
                        try:
                            if nowVal["new"] != prevVal["new"]:
                                newFlag = self.intData[icount][key]["new"]
                        except:
                            newFlag = nowVal["new"]
                        try:
                            if nowVal["die"] != prevVal["die"]:
                                dieFlag = nowVal["die"]
                        except:
                            dieFlag = nowVal["die"]
                    else:
                        newFlag = nowVal["new"]
                        dieFlag = nowVal["die"]

                    memUsage = self.intData[icount][key]["memUsage"] >> 8
                    kmemUsage = self.intData[icount][key]["kmemUsage"] >> 20
                    timeLine += "%4s" % (
                        newFlag + str(memUsage + kmemUsage) + dieFlag
                    )

                try:
                    pid = SysMgr.savedProcTree[key]
                except:
                    pid = value["tgid"]

                SysMgr.addPrint(
                    "%16s(%7s/%7s): " % (value["comm"][:16], key, pid)
                    + timeLine
                    + "\n"
                )

            if SysMgr.bufferString:
                SysMgr.printPipe("%s# %s\n" % ("", "MEM(MB)"))
                SysMgr.doPrint()
                SysMgr.printPipe(oneLine)

        # block read usage on timeline #
        SysMgr.clearPrint()
        if SysMgr.blockEnable:
            for key, value in sorted(
                self.threadData.items(),
                key=lambda e: e[1]["reqRdBlock"],
                reverse=True,
            ):
                if value["readBlock"] < 1 and not SysMgr.showAll:
                    break
                elif key.startswith("0["):
                    continue

                timeLine = ""
                timeLineLen = titleLineLen
                for icount in xrange(lval):
                    newFlag = " "
                    dieFlag = " "

                    if timeLineLen + 4 > maxLineLen:
                        timeLine += "\n" + (" " * (titleLineLen + 1))
                        timeLineLen = titleLineLen + 4
                    else:
                        timeLineLen += 4

                    try:
                        self.intData[icount][key]
                    except:
                        timeLine += "%3d " % 0
                        continue

                    nowVal = self.intData[icount][key]

                    try:
                        prevVal = self.intData[icount - 1][key]
                    except:
                        prevVal = nowVal

                    if icount > 0:
                        try:
                            if nowVal["new"] != prevVal["new"]:
                                newFlag = self.intData[icount][key]["new"]
                        except:
                            newFlag = nowVal["new"]
                        try:
                            if nowVal["die"] != prevVal["die"]:
                                dieFlag = nowVal["die"]
                        except:
                            dieFlag = nowVal["die"]
                    else:
                        newFlag = nowVal["new"]
                        dieFlag = nowVal["die"]

                    timeLine += "%4s" % (
                        newFlag
                        + str(
                            long(
                                (
                                    self.intData[icount][key]["brUsage"]
                                    * SysMgr.blockSize
                                )
                                >> 20
                            )
                        )
                        + dieFlag
                    )

                try:
                    pid = SysMgr.savedProcTree[key]
                except:
                    pid = value["tgid"]

                SysMgr.addPrint(
                    "%16s(%7s/%7s): " % (value["comm"][:16], key, pid)
                    + timeLine
                    + "\n"
                )

            if SysMgr.bufferString:
                SysMgr.printPipe("%s# %s\n" % ("", "BLK_RD(MB)"))
                SysMgr.doPrint()
                SysMgr.printPipe(oneLine)

        # block write usage on timeline #
        SysMgr.clearPrint()
        if SysMgr.blockEnable:
            for key, value in sorted(
                self.threadData.items(),
                key=lambda e: e[1]["reqWrBlock"] + (e[1]["awriteBlock"] << 3),
                reverse=True,
            ):
                if (
                    value["reqWrBlock"] + (value["awriteBlock"] << 3) < 1
                    and not SysMgr.showAll
                ):
                    break
                elif key.startswith("0["):
                    continue

                timeLine = ""
                timeLineLen = titleLineLen
                for icount in xrange(lval):
                    newFlag = " "
                    dieFlag = " "

                    if timeLineLen + 4 > maxLineLen:
                        timeLine += "\n" + (" " * (titleLineLen + 1))
                        timeLineLen = titleLineLen + 4
                    else:
                        timeLineLen += 4

                    try:
                        self.intData[icount][key]
                    except:
                        timeLine += "%3d " % 0
                        continue

                    nowVal = self.intData[icount][key]

                    try:
                        prevVal = self.intData[icount - 1][key]
                    except:
                        prevVal = nowVal

                    if icount > 0:
                        try:
                            if nowVal["new"] != prevVal["new"]:
                                newFlag = self.intData[icount][key]["new"]
                        except:
                            newFlag = nowVal["new"]
                        try:
                            if nowVal["die"] != prevVal["die"]:
                                dieFlag = nowVal["die"]
                        except:
                            dieFlag = nowVal["die"]
                    else:
                        newFlag = nowVal["new"]
                        dieFlag = nowVal["die"]

                    timeLine += "%4s" % (
                        newFlag
                        + str(
                            long(
                                (
                                    self.intData[icount][key]["bwUsage"]
                                    * SysMgr.blockSize
                                )
                                >> 20
                            )
                        )
                        + dieFlag
                    )

                try:
                    pid = SysMgr.savedProcTree[key]
                except:
                    pid = value["tgid"]

                SysMgr.addPrint(
                    "%16s(%7s/%7s): "
                    % (value["comm"][:16], key, value["tgid"])
                    + timeLine
                    + "\n"
                )

            if SysMgr.bufferString:
                SysMgr.printPipe("%s# %s\n" % ("", "BLK_WR(MB)"))
                SysMgr.doPrint()
                SysMgr.printPipe(oneLine)

        # event usage on timeline #
        self.printEventIntervalInfo()

        # save graph #
        if SysMgr.graphEnable and (
            len(cpuUsageList) > 0 or len(ioUsageList) > 0
        ):
            return TaskAnalyzer.saveImage(SysMgr.inputFile, "graph")

    def getNetworkUsage(self, prev, now):
        if not now or prev == now:
            return (0, 0)

        nowIn = nowOut = prevIn = prevOut = 0

        idx = -1

        for line in now:
            idx += 1
            if not line:
                continue
            elif not isinstance(line, str):
                continue
            elif not line.startswith("IpExt"):
                continue

            try:
                if SysMgr.netInIndex < 0:
                    SysMgr.netInIndex = line.split().index("InOctets")

                nowStat = line.split()
                nowIn = long(nowStat[SysMgr.netInIndex])
                nowOut = long(nowStat[SysMgr.netInIndex + 1])

                if SysMgr.totalEnable:
                    prevIn = prevOut = 0
                else:
                    prevStat = prev[idx].split()
                    prevIn = long(prevStat[SysMgr.netInIndex])
                    prevOut = long(prevStat[SysMgr.netInIndex + 1])

                inDiff = nowIn - prevIn
                outDiff = nowOut - prevOut

                return (inDiff, outDiff)
            except SystemExit:
                sys.exit(0)
            except:
                continue

        return (0, 0)

    @staticmethod
    def printThreadTree():
        orig = SysMgr.processEnable

        try:
            SysMgr.processEnable = False

            # save task info #
            obj = TaskAnalyzer(onlyInstance=True)
            obj.saveProcStat()

            # set color flag #
            color = SysMgr.checkMode("filerec")

            # print task tree #
            TaskAnalyzer.printProcTree(
                instance=obj,
                printFunc=SysMgr.infoBufferPrint,
                color=color,
                newline=True,
            )
        except SystemExit:
            sys.exit(0)
        except:
            pass
        finally:
            SysMgr.processEnable = orig

    @staticmethod
    def readTraceData(fname):
        try:
            # get fd #
            fr = SysMgr.getCompFd(fname)
            if fr:
                lines = []
                tlines = fr.read().decode().split("\n")
                for item in tlines:
                    if not item:
                        continue
                    lines.append("%s\n" % item)

                return lines
            else:
                if sys.version_info >= (3, 0, 0):
                    with open(fname, "r", encoding="latin-1") as fr:
                        return fr.readlines()
                else:
                    with open(fname, "r") as fr:
                        return fr.readlines()
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printOpenErr(fname)
            sys.exit(-1)

    @staticmethod
    def setLastField(option):
        SysMgr.affinityEnable = False
        SysMgr.attrEnable = False
        SysMgr.wchanEnable = False
        SysMgr.sigHandlerEnable = False
        SysMgr.oomEnable = False
        SysMgr.ioschedEnable = False
        SysMgr.cgroupRootEnable = False

        if option == "iosched":
            SysMgr.ioschedEnable = True
        elif option == "affinity":
            SysMgr.affinityEnable = True
        elif option == "wchan":
            SysMgr.wchanEnable = True
        elif option == "signal":
            SysMgr.sigHandlerEnable = True
        elif option == "oom":
            SysMgr.oomEnable = True
        elif option == "attr":
            SysMgr.attrEnable = True
        elif option == "cgroup":
            SysMgr.cgroupRootEnable = True
        else:
            SysMgr.printErr("failed to set '%s' as a last field" % option)

    @staticmethod
    def parseProcLine(index, procLine):
        TA = TaskAnalyzer
        procIntData = TA.procIntData[index]

        # time #
        if "time" not in procIntData:
            m = re.match(
                (
                    r".+\[Time:\s*(?P<time>[0-9]+.[0-9]+)\].+"
                    r"\[Inter\S*:\s*(?P<inter>[0-9]+.[0-9]+)\].+"
                    r"\[Ctxt:\s*(?P<nrCtxt>[0-9]+)\].+"
                    r"\[IRQ:\s*(?P<nrIrq>[0-9]+)\].+"
                    r"\[Core:\s*(?P<nrCore>[0-9]+)\].+"
                    r"\[Task:\s*(?P<nrProc>[0-9]+)"
                    r"/(?P<nrThread>[0-9]+)"
                ),
                procLine,
            )
            if m:
                d = m.groupdict()
                procIntData["time"] = d["time"]
                procIntData["inter"] = d["inter"]
                procIntData["nrCtxt"] = d["nrCtxt"]
                procIntData["nrIrq"] = d["nrIrq"]
                procIntData["nrCore"] = d["nrCore"]
                procIntData["nrProc"] = d["nrProc"]
                procIntData["nrThread"] = d["nrThread"]
            return

        # shortcut #
        convUnit2Size = UtilMgr.convUnit2Size

        # split stats #
        tokens = procLine.split("|")

        # total #
        if "total" not in procIntData and tokens[0].startswith("Total"):
            if len(tokens) < 2:
                return

            # parse CPU & BLOCK stat #
            m = re.match(
                (
                    r"\s*(?P<cpu>\-*[0-9]+)\s*%\s*\(\s*"
                    r"(?P<user>\-*[0-9]+)\s*\/s*\s*"
                    r"(?P<kernel>\-*[0-9]+)\s*\/s*\s*"
                    r"(?P<block>\-*[0-9]+)"
                ),
                tokens[1],
            )
            if not m:
                return

            d = m.groupdict()

            # CPU stat #
            cpu = long(d["cpu"])

            # sum total CPU usage #
            TA.procTotData["total"]["cpu"] += cpu

            # get total max CPU usage #
            if TA.procTotData["total"]["cpuMax"] < cpu:
                TA.procTotData["total"]["cpuMax"] = cpu

            # get total min CPU usage #
            if TA.procTotData["total"]["cpuMin"] < 0:
                TA.procTotData["total"]["cpuMin"] = cpu
            elif TA.procTotData["total"]["cpuMin"] > cpu:
                TA.procTotData["total"]["cpuMin"] = cpu

            procIntData["total"] = dict(TA.init_procIntData)

            # save CPU usage on this interval #
            try:
                procIntData["total"]["cpu"] = cpu
            except:
                procIntData["total"]["cpu"] = 0

            # save blkwait on this interval #
            try:
                procIntData["total"]["blkwait"] = long(d["block"])
            except:
                procIntData["total"]["blkwait"] = 0

            # parse MEM stat #
            m = re.match(
                (
                    r"\s*(?P<free>[0-9]+)\s*\(\s*(?P<freePer>-*[0-9]+)\s*"
                    r"/\s*(?P<anon>[0-9]+)\s*/\s*(?P<cache>[0-9]+)\s*"
                    r"/\s*(?P<kernel>[0-9]+)"
                ),
                tokens[2],
            )
            if not m:
                return

            d = m.groupdict()

            # MEM stats #
            freeMem = long(d["free"])
            freeMemPer = long(d["freePer"])
            anonMem = long(d["anon"])
            cacheMem = long(d["cache"])
            kernelMem = long(d["kernel"])

            if TA.procTotData["total"]["initMem"] == 0:
                TA.procTotData["total"]["initMem"] = freeMem

            TA.procTotData["total"]["lastMem"] = freeMem

            # set minimum free memory #
            if (
                TA.procTotData["total"]["minMem"] == 0
                or TA.procTotData["total"]["minMem"] > freeMem
            ):
                TA.procTotData["total"]["minMem"] = freeMem
            # set maximum free memory #
            if TA.procTotData["total"]["maxMem"] < freeMem:
                TA.procTotData["total"]["maxMem"] = freeMem

            procIntData["total"]["mem"] = freeMem
            procIntData["total"]["memper"] = freeMemPer
            procIntData["total"]["anonmem"] = anonMem
            procIntData["total"]["cachemem"] = cacheMem
            procIntData["total"]["kernelmem"] = kernelMem

            try:
                procIntData["total"]["blk"] = tokens[5]
            except:
                procIntData["total"]["blk"] = "-"

            # parse swap stat #
            m = re.match(r"\s*(?P<swap>\-*[0-9]+)", tokens[3])
            if not m:
                return

            d = m.groupdict()

            # swap stat #
            procIntData["total"]["swap"] = long(d["swap"])

            try:
                procIntData["total"]["rclm"] = tokens[4].strip()
            except:
                procIntData["total"]["rclm"] = "-"

            try:
                procIntData["total"]["nrFlt"] = long(tokens[6])
            except:
                procIntData["total"]["nrFlt"] = "-"

            try:
                procIntData["total"]["netIO"] = tokens[11].strip()
            except:
                procIntData["total"]["netIO"] = "-"

            return

        # GPU #
        elif len(tokens) == 5:
            m = re.match(
                r"\s*(?P<gpu>.+)\s*\(\s*(?P<usage>[0-9]+)\s*%\)", tokens[0]
            )
            if m:
                d = m.groupdict()

                gpu = d["gpu"].strip()
                usage = long(d["usage"])

                procIntData["total"].setdefault("gpu", {})

                TA.procTotData["total"].setdefault("gpu", {})
                totalData = TA.procTotData["total"]["gpu"]

                try:
                    totalData[gpu]["usage"] += usage

                    if totalData[gpu]["min"] > usage:
                        totalData[gpu]["min"] = usage
                    elif totalData[gpu]["max"] < usage:
                        totalData[gpu]["max"] = usage
                except:
                    totalData[gpu] = {}
                    totalData[gpu]["usage"] = usage
                    totalData[gpu]["min"] = usage
                    totalData[gpu]["max"] = usage

                try:
                    procIntData["total"]["gpu"][gpu] = usage
                except:
                    pass

            return

        # storage #
        elif len(tokens) == 12 and tokens[0][0] == "/":
            procIntData["total"].setdefault("storage", {})
            TA.procTotData["total"].setdefault("storage", {})

            try:
                # get device name #
                dev = tokens[0].strip()
                dev = dev[dev.rfind("/") + 1 :]

                procIntData["total"]["storage"].setdefault(dev, {})
                indexData = procIntData["total"]["storage"][dev]

                TA.procTotData["total"]["storage"].setdefault(dev, {})
                totalData = TA.procTotData["total"]["storage"][dev]

                # get busy time and average queue-length #
                busy = convUnit2Size(tokens[1].strip()[:-1])
                avq = tokens[2].strip()

                # get storage stats in MB #
                read = convUnit2Size(tokens[3].strip())
                write = convUnit2Size(tokens[4].strip())

                freestat = tokens[5].strip().split("(")
                freeDiff = convUnit2Size(freestat[1][:-1].strip())

                # busy #
                try:
                    indexData["busy"] = busy
                    totalData["busy"] += busy
                except:
                    totalData["busy"] = busy

                # avq #
                try:
                    indexData["avq"] = avq
                    totalData["avq"] += avq
                except:
                    totalData["avq"] = avq

                # read #
                try:
                    indexData["read"] = read
                    totalData["read"] += read
                except:
                    totalData["read"] = read

                # write #
                try:
                    indexData["write"] = write
                    totalData["write"] += write
                except:
                    totalData["write"] = write

                # freediff #
                try:
                    indexData["free"] = freeDiff
                    totalData["free"] += freeDiff
                except:
                    totalData["free"] = freeDiff
            except SystemExit:
                sys.exit(0)
            except:
                pass

            return

        # network #
        elif len(tokens) == 13 and not tokens[0].startswith("Total"):
            # check condition #
            if tokens[0].strip() in ("ID", "Dev"):
                return

            procIntData["total"].setdefault("netdev", {})
            TA.procTotData["total"].setdefault("netdev", {})

            try:
                # get device name #
                dev = tokens[0].strip()

                procIntData["total"]["netdev"].setdefault(dev, {})
                TA.procTotData["total"]["netdev"].setdefault(dev, {})

                # get storage stats in MB #
                recv = convUnit2Size(tokens[2].strip())
                tran = convUnit2Size(tokens[7].strip())

                # recv #
                try:
                    procIntData["total"]["netdev"][dev]["recv"] = recv
                    TA.procTotData["total"]["netdev"][dev]["recv"] += recv
                except:
                    TA.procTotData["total"]["netdev"][dev]["recv"] = recv

                # tran #
                try:
                    procIntData["total"]["netdev"][dev]["tran"] = tran
                    TA.procTotData["total"]["netdev"][dev]["tran"] += tran
                except:
                    TA.procTotData["total"]["netdev"][dev]["tran"] = tran
            except:
                pass

            return

        # cgroup #
        elif len(tokens) == 10 and "/" in tokens[8]:
            tokens = UtilMgr.cleanItem(tokens, False)
            if len(tokens) != 9:
                return

            try:
                system, proc, task, cpu, thr, mem, read, write, psi = tokens
            except:
                SysMgr.printWarn(
                    "failed to parse cgroup line '%s'" % tokens, reason=True
                )
                return

            # TODO: Add psi stats #

            # cpu, throttle, memory, read, write #
            for target in (
                "cgroup.cpu",
                "cgroup.throttle",
                "cgroup.mem",
                "cgroup.read",
                "cgroup.write",
            ):
                # set usage #
                try:
                    if target == "cgroup.cpu":
                        if cpu == "-":
                            usage = 0
                        else:
                            usage = float(cpu)
                    elif target in (
                        "cgroup.mem",
                        "cgroup.read",
                        "cgroup.write",
                    ):
                        if target == "cgroup.mem":
                            res = mem
                        elif target == "cgroup.read":
                            res = read
                        else:
                            res = write

                        if res == "-":
                            usage = 0
                        else:
                            usage = UtilMgr.convUnit2Size(res)
                    else:
                        if thr == "-":
                            usage = 0
                        else:
                            usage = long(thr)
                except:
                    return

                TA.procTotData["total"].setdefault(target, {})
                totalData = TA.procTotData["total"][target]

                # set stats #
                try:
                    if system == "memory":
                        totalData[system]["usage"] = usage
                    else:
                        totalData[system]["usage"] += usage

                    if totalData[system]["min"] > usage:
                        totalData[system]["min"] = usage
                    elif totalData[system]["max"] < usage:
                        totalData[system]["max"] = usage
                except:
                    totalData[system] = {}
                    totalData[system]["usage"] = usage
                    totalData[system]["min"] = usage
                    totalData[system]["max"] = usage

                try:
                    procIntData["total"].setdefault(target, {})
                    procIntData["total"][target][system] = usage
                except:
                    pass

            return

        # WARN: each tab's the number of tokens #
        # task(10), GPU(5), storage(12), network(13), cgroup(10)

        # check return condition #
        if "ONLYTOTAL" in SysMgr.environList:
            return
        elif procLine in (oneLine, twoLine):
            return

        # process #
        m = re.match(
            (
                r"\s*(?P<comm>.+) \(\s*(?P<pid>[0-9]+)\/\s*(?P<ppid>[0-9]+)"
                r"\/\s*(?P<nrThreads>[0-9]+)\/(?P<pri>.{4})\)\|"
                r"\s*(?P<cpu>\S+)\(.+/.+/(?P<dly>.+)\)\|\s*"
                r"(?P<vss>[0-9]+)\(\s*(?P<rss>[0-9]+)\/\s*"
                r"(?P<text>.+)\/\s*(?P<shm>.+)\/\s*(?P<swap>.+)\)\|\s*"
                r"(?P<blk>\S+)\(\s*(?P<blkrd>.+)\/\s*(?P<blkwr>.+)\/\s*"
                r"(?P<nrflt>.+)\)\|\s*(?P<sid>.+)\|\s*(?P<user>.+)\|\s*"
                r"(?P<fd>.+)\|\s*(?P<life>.+)\|\s*(?P<parent>.+)\|"
            ),
            procLine,
        )
        if not m:
            return

        # check excepting for shm #
        if "EXCEPTSHM" in SysMgr.environList and (
            SysMgr.isDrawMode() or SysMgr.checkMode("topsum")
        ):
            exceptShm = True
        else:
            exceptShm = False

        # define task variables #
        d = m.groupdict()
        pid = d["pid"]
        ppid = d["ppid"]
        comm = d["comm"].strip("*")

        def _getPrio(d):
            return d["pri"].replace(" ", "")

        # save tree info #
        if "PSTREE" in SysMgr.environList:
            # save relation #
            if not ppid in TaskAnalyzer.procTreeData:
                TaskAnalyzer.procTreeData[ppid] = {}
            TaskAnalyzer.procTreeData[ppid][pid] = 0

            # save priority #
            if not pid in TaskAnalyzer.procPrioData:
                TaskAnalyzer.procPrioData[pid] = {}
            TaskAnalyzer.procPrioData[pid][_getPrio(d)] = 0

            # save comm #
            SysMgr.commCache[pid] = comm
            SysMgr.commCache[ppid] = d["parent"].rsplit("(")[0].strip("*")

        # check filter #
        if SysMgr.filterGroup:
            if (
                not SysMgr.groupProcEnable
                and not UtilMgr.isValidStr(pid)
                and not UtilMgr.isValidStr(comm)
            ):
                return

        try:
            # ignore special processes #
            if len(comm) > 2 and comm[0] == "[" and comm[2] == "]":
                # define real comm #
                rcomm = comm[3:]

                # initialize lifecycle data #
                if rcomm not in TA.lifecycleData:
                    TA.lifecycleData[rcomm] = [0] * 8
                TA.lifeIntData.setdefault(pid, {})
                TA.lifeIntData[pid].setdefault(index, [])
                lifecycleData = TA.lifecycleData[rcomm]

                # died process #
                if comm[1] == "-":
                    lifecycleData[1] += 1

                    # get index #
                    if index == 0:
                        idx = 0
                    else:
                        idx = index - 1

                    if not pid in TA.procIntData[idx]:
                        TA.procIntData[idx][pid] = dict(TA.init_procIntData)

                    TA.procIntData[idx][pid]["die"] = True
                    TA.lifeIntData[pid][index].append("FINISH")

                    # convert lifetime #
                    ts = UtilMgr.convTime2Sec(d["life"])

                    # end time #
                    ets = TA.procIntData[idx]["time"]

                    # start time #
                    if ts:
                        if index:
                            sts = long(float(TA.procIntData[idx]["time"])) - ts
                        else:
                            start = long(float(TA.procIntData[0]["time"]))
                            sts = start - SysMgr.intervalEnable - ts
                    else:
                        sts = -2

                    TA.lifeProcData.setdefault(
                        pid,
                        {
                            "START": -2,
                            "FINISH": -2,
                            "comm": rcomm,
                            "parent": d["parent"],
                            "user": d["user"],
                        },
                    )

                    TA.lifeProcData[pid]["comm"] = rcomm
                    TA.lifeProcData[pid]["START"] = sts
                    TA.lifeProcData[pid]["FINISH"] = ets
                    TA.lifeProcData[pid]["prio"] = _getPrio(d)
                    if TA.lifeProcData[pid]["user"] == "-":
                        TA.lifeProcData[pid]["user"] = d["user"]
                # created process #
                elif comm[1] == "+":
                    lifecycleData[0] += 1
                    TA.lifeIntData[pid][index].append("START")

                    ts = UtilMgr.convTime2Sec(d["life"])
                    if ts:
                        ts = long(float(TA.procIntData[index]["time"])) - ts
                    else:
                        ts = TA.procIntData[index]["time"]

                    TA.lifeProcData.setdefault(
                        pid,
                        {
                            "START": -2,
                            "FINISH": -2,
                            "comm": rcomm,
                            "parent": d["parent"],
                            "user": d["user"],
                        },
                    )

                    TA.lifeProcData[pid]["comm"] = rcomm
                    TA.lifeProcData[pid]["START"] = ts
                    if TA.lifeProcData[pid]["user"] == "-":
                        TA.lifeProcData[pid]["user"] = d["user"]
                    TA.lifeProcData[pid]["prio"] = _getPrio(d)
                # zomebie process #
                elif comm[1].upper() == "Z":
                    lifecycleData[2] += 1
                    TA.lifeIntData[pid][index].append("ZOMBIE")
                # stopped process #
                elif comm[1] == "T":
                    lifecycleData[3] += 1
                # traced process #
                elif comm[1] == "t":
                    lifecycleData[4] += 1
                # wait process #
                elif comm[1].upper() == "D":
                    lifecycleData[5] += 1
                # waking process #
                elif comm[1].upper() == "W":
                    lifecycleData[6] += 1
                # parked process #
                elif comm[1].upper() == "P":
                    lifecycleData[7] += 1

                return
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # update comm for life data #
        if pid in TA.lifeProcData:
            TA.lifeProcData[pid]["comm"] = comm
            if TA.lifeProcData[pid]["user"] == "-":
                TA.lifeProcData[pid]["user"] = d["user"]

        # check pid in list #
        if pid not in TA.procTotData:
            TA.procTotData[pid] = dict(TA.init_procTotData)
            TA.procTotData[pid]["startIdx"] = index

        cpu = long(float(d["cpu"]))

        try:
            dly = long(d["dly"])
        except:
            dly = 0

        blk = long(float(d["blk"]))

        try:
            blkrd = long(d["blkrd"])
            blkwr = long(d["blkwr"])

            SysMgr.blockEnable = True
        except SystemExit:
            sys.exit(0)
        except:
            blkrd = blkwr = 0

        # save process info #
        TA.procTotData[pid]["comm"] = d["comm"]
        TA.procTotData[pid]["ppid"] = ppid
        TA.procTotData[pid]["nrThreads"] = d["nrThreads"]
        TA.procTotData[pid]["pri"] = d["pri"]

        # save CPU and delay time of process #
        for res, name in ((cpu, "cpu"), (dly, "dly")):
            TA.procTotData[pid][name] += res

            nameMax = name + "Max"
            if TA.procTotData[pid][nameMax] < res:
                TA.procTotData[pid][nameMax] = res

            nameMin = name + "Min"
            if index > 0 and TA.procTotData[pid][nameMin] < 0:
                TA.procTotData[pid][nameMin] = 0
            elif TA.procTotData[pid][nameMin] < 0:
                TA.procTotData[pid][nameMin] = res
            elif TA.procTotData[pid][nameMin] > res:
                TA.procTotData[pid][nameMin] = res

        # save block usage of process #
        TA.procTotData[pid]["blk"] += blk
        TA.procTotData[pid]["blkrd"] += blkrd
        TA.procTotData[pid]["blkwr"] += blkwr

        # set vss #
        vss = long(d["vss"])
        if TA.procTotData[pid]["minVss"] >= vss:
            TA.procTotData[pid]["minVss"] = vss
        if TA.procTotData[pid]["maxVss"] < vss:
            TA.procTotData[pid]["maxVss"] = vss

        # update gpumem stat #
        try:
            gpumem = long(d["sid"])

            if (
                not SysMgr.gpuMemGetters
                and not "GPUMEMSUMMARY" in SysMgr.environList
            ):
                raise OSError()

            if "gpumem" in TA.procTotData[pid]:
                TA.procTotData[pid]["gpumem"] = max(
                    TA.procTotData[pid]["gpumem"], gpumem
                )
            else:
                TA.procTotData[pid]["gpumem"] = gpumem
        except OSError:
            pass
        except SystemExit:
            sys.exit(0)
        except:
            gpumem = 0

        # set rss #
        rss = long(d["rss"])
        if "GPUMEMSUM2" in SysMgr.environList:
            rss += gpumem

        if exceptShm:
            try:
                rss -= long(d["text"])
                rss -= long(d["shm"])
            except SystemExit:
                sys.exit(0)
            except:
                pass
            finally:
                if rss < 0:
                    rss = 0

        # update mem stats #
        TA.procTotData[pid]["minMem"] = min(TA.procTotData[pid]["minMem"], rss)
        TA.procTotData[pid]["maxMem"] = max(TA.procTotData[pid]["maxMem"], rss)

        # set init mem #
        if TA.procTotData[pid]["initMem"] == 0:
            TA.procTotData[pid]["initMem"] = rss
            TA.procTotData[pid]["lastMem"] = rss

        # save process stats on this interval #
        if pid not in procIntData:
            procIntData[pid] = dict(TA.init_procIntData)
            procIntData[pid]["cpu"] = cpu
            procIntData[pid]["dly"] = dly
            procIntData[pid]["pri"] = d["pri"]
            procIntData[pid]["vss"] = vss
            procIntData[pid]["gpumem"] = gpumem
            procIntData[pid]["blk"] = blk
            procIntData[pid]["blkrd"] = blkrd
            procIntData[pid]["blkwr"] = blkwr
            procIntData[pid]["mem"] = rss
            procIntData[pid]["memDiff"] = rss - TA.procTotData[pid]["lastMem"]
            TA.procTotData[pid]["lastMem"] = rss

    @staticmethod
    def summarizeInterval():
        if "total" not in TaskAnalyzer.procTotData:
            TaskAnalyzer.procTotData["total"] = dict(
                TaskAnalyzer.init_procTotData
            )

        cleanChar = "CLEANCHAR" in SysMgr.environList

        idx = 0
        for val in reversed(SysMgr.procBuffer):
            if len(TaskAnalyzer.procIntData) < idx + 1:
                TaskAnalyzer.procIntData.append({})

            # parse interval items #
            for line in val.split("\n"):
                if cleanChar:
                    line = UtilMgr.removeColor(line)
                    line = SysMgr.convExtAscii(line, True)

                TaskAnalyzer.parseProcLine(idx, line)

            # skip useless tick #
            if not TaskAnalyzer.procIntData[-1]:
                TaskAnalyzer.procIntData.pop()
                continue

            idx += 1
            UtilMgr.printProgress(idx, len(SysMgr.procBuffer))

        # skip if no tick #
        if idx == 0:
            return

        UtilMgr.deleteProgress()

        # calculate final stat #
        for pid, val in TaskAnalyzer.procTotData.items():
            val["cpuAvg"] = round(val["cpu"] / float(idx), 1)
            val["dlyAvg"] = round(val["dly"] / float(idx), 1)
            val["memDiff"] = val["lastMem"] - val["initMem"]

    @staticmethod
    def printFileTable():
        if not SysMgr.fileInstance:
            return

        convNum = UtilMgr.convNum

        nrEvent = nrSocket = nrDevice = nrPipe = nrProc = nrFile = 0
        for filename in list(SysMgr.fileInstance):
            # increase type count per process #
            if filename.startswith("anon"):
                nrEvent += 1
            elif filename.startswith("socket"):
                nrSocket += 1
            elif filename.startswith("/dev"):
                nrDevice += 1
            elif filename.startswith("pipe"):
                nrPipe += 1
            elif filename.startswith(SysMgr.procPath):
                nrProc += 1
            else:
                nrFile += 1

        SysMgr.printPipe(
            (
                "\n[Top File Table] [TOTAL: %s] [FILE: %s] [EVENT: %s] "
                "[SOCKET: %s] [DEV: %s] [PIPE: %s] [PROC: %s]\n"
            )
            % (
                convNum(len(SysMgr.fileInstance)),
                convNum(nrFile),
                convNum(nrEvent),
                convNum(nrSocket),
                convNum(nrDevice),
                convNum(nrPipe),
                convNum(nrProc),
            )
        )
        SysMgr.printPipe(
            "{2:1}\n{0:^5} | {1:^144} |\n{3:1}\n".format(
                "REF", "FILE", twoLine, oneLine
            )
        )

        for filename, value in sorted(
            SysMgr.fileInstance.items(), key=lambda e: long(e[1]), reverse=True
        ):
            SysMgr.printPipe("{0:>5} | {1:<144} |\n".format(value, filename))

        if not SysMgr.fileInstance:
            SysMgr.printPipe("\tN/A\n")

        SysMgr.printPipe("%s\n" % oneLine)

    @staticmethod
    def printTimeline():
        if SysMgr.jsonEnable:
            _printer = lambda x: x
            jsonData = SysMgr.jsonData["summary"] = []
        else:
            _printer = SysMgr.printPipe
            jsonData = {}

        _printer("\n[Top Summary Info]\n%s\n" % twoLine)

        # check available memory type #
        memTitle = "Mem%s/User/Cache" % (
            "Free" if SysMgr.freeMemEnable else "Avl"
        )

        _printer(
            (
                "{0:^5} | {1:^27} | {2:^3} | {3:^18} | {4:^7} | {5:^3} | "
                "{6:^4} | {7:^9} | {8:^5} | {9:^6} | {10:^6} | {11:^8} | "
                "{12:^4} | {13:^8} |\n"
            ).format(
                "IDX",
                "Interval",
                "CPU",
                memTitle,
                "BlkRW",
                "Blk",
                "SWAP",
                "NrPgRclm",
                "NrFlt",
                "NrCtx",
                "NrIRQ",
                "NrTask",
                "Core",
                "NetIO",
            )
        )
        _printer("%s\n" % twoLine)

        pCnt = 0
        for idx, val in list(enumerate(TaskAnalyzer.procIntData)):
            if not val or not "time" in val:
                continue

            if idx == 0:
                before = "%.3f" % (float(val["time"]) - float(val["inter"]))
            elif "time" in TaskAnalyzer.procIntData[idx - 1]:
                before = TaskAnalyzer.procIntData[idx - 1]["time"]
            else:
                continue

            if "total" not in val:
                continue

            ts = val["total"]

            if SysMgr.jsonEnable:
                rclmBg, rclmFg = ts["rclm"].split("/")
                blkRd, blkWr = ts["blk"].split("/")
                netIn, netOut = ts["netIO"].split("/")
                jsonData.append(
                    {
                        "index": idx + 1,
                        "interval": [before, val["time"]],
                        "cpu": ts["cpu"],
                        "memAvail": ts["mem"],
                        "memAnon": ts["anonmem"],
                        "memCache": ts["cachemem"],
                        "memKernel": ts["kernelmem"],
                        "blkRd": blkRd.strip(),
                        "blkWr": blkWr.strip(),
                        "blkWait": ts["blkwait"],
                        "swapUsage": ts["swap"],
                        "rclmBg": rclmBg.strip(),
                        "rclmFg": rclmFg.strip(),
                        "fault": ts["nrFlt"],
                        "nrCtxt": val["nrCtxt"],
                        "nrIrq": val["nrIrq"],
                        "nrProc": val["nrProc"],
                        "nrThread": val["nrThread"],
                        "nrCore": val["nrCore"],
                        "netIn": netIn.strip(),
                        "netOut": netOut.strip(),
                    }
                )
                continue

            task = "%s/%s" % (val["nrProc"], val["nrThread"])

            if "INCKERNELMEM" in SysMgr.environList:
                kmemstat = "/%s" % ts["kernelmem"]
            else:
                kmemstat = ""

            _printer(
                (
                    "{0:>5} | {1:>12} - {2:>12} | {3:>3} | {4:^18} | "
                    "{5:^7} | {6:>3} | {7:>4} | {8:^9} | {9:>5} | {10:>6} | "
                    "{11:>6} | {12:>8} | {13:^4} | {14:^8} |\n"
                ).format(
                    idx + 1,
                    before,
                    val["time"],
                    ts["cpu"],
                    "%s/%s/%s%s"
                    % (ts["mem"], ts["anonmem"], ts["cachemem"], kmemstat),
                    ts["blk"],
                    ts["blkwait"],
                    ts["swap"],
                    ts["rclm"],
                    ts["nrFlt"],
                    val["nrCtxt"],
                    val["nrIrq"],
                    task,
                    val["nrCore"],
                    ts["netIO"],
                )
            )
            pCnt += 1

        if not TaskAnalyzer.procIntData or pCnt == 0:
            _printer("    None\n")

        _printer("%s\n" % oneLine)

    @staticmethod
    def checkSummaryFilter(res):
        if not "SUMMARYFILTER" in SysMgr.environList:
            return True
        elif res in SysMgr.environList["SUMMARYFILTER"]:
            return True
        else:
            return False

    @staticmethod
    def printEventInterval():
        if not TaskAnalyzer.procEventData:
            return
        elif not TaskAnalyzer.checkSummaryFilter("EVENT"):
            return

        # remove invalid events #
        try:
            raise Exception("ignore")

            initTime = TaskAnalyzer.procIntData[0]["time"]

            eventList = list(TaskAnalyzer.procEventData)
            for event in eventList:
                # skip unbounded events #
                if float(initTime) > event[0]:
                    del TaskAnalyzer.procEventData[0]
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # check events #
        if not TaskAnalyzer.procEventData:
            return

        if SysMgr.jsonEnable:
            _printer = lambda x: x
            jsonData = SysMgr.jsonData["event"] = []
        else:
            _printer = SysMgr.printPipe
            jsonData = {}

        # print title #
        _printer("\n[Top Event Info] (Unit: %%)\n%s\n" % twoLine)
        _printer(
            ("{0:^12} | {1:^12} | {2:^12} | {3:1}\n{4:1}\n").format(
                "Timeline", "Realtime", "Interval", "Event", twoLine
            )
        )

        procEventData = TaskAnalyzer.procEventData
        for idx, event in enumerate(sorted(procEventData, key=lambda e: e[2])):
            name = event[1]
            time = "%.2f" % float(event[0])
            rtime = float(event[2])
            rtimestr = "%.2f" % rtime

            try:
                # from previous event #
                if len(procEventData) > 1 and idx > 0:
                    diff = rtime - float(procEventData[idx - 1][2])
                # from start #
                else:
                    diff = rtime - SysMgr.startTime
            except SystemExit:
                sys.exit(0)
            except:
                diff = 0

            # check diff #
            if diff < 0:
                diff = 0

            diff = "%.2f" % diff
            _printer(
                ("{0:>12} | {1:>12} | {2:>12} | {3:1}\n").format(
                    time, rtimestr, diff, name
                )
            )

            if SysMgr.jsonEnable:
                jsonData.append(
                    {
                        "name": name,
                        "tick": time,
                        "time": rtimestr,
                        "interval": diff,
                    }
                )

        _printer("%s\n" % oneLine)

    @staticmethod
    def printCpuInterval(attr="cpu"):
        def _conv(val):
            return val

        sortr = True
        if attr == "cpu":
            res = "CPU"
            name = vname = "cpu"
            sortv = name
        elif attr == "dly":
            res = "Delay"
            name = vname = "dly"
            sortv = name
        elif attr == "pri":
            res = "Prio"
            name = "cpu"
            vname = "pri"
            sortv = "maxPri"
            sortr = False

            def _conv(val):
                try:
                    return val.replace(" ", "")
                except SystemExit:
                    sys.exit(0)
                except:
                    return val

        else:
            return

        # check skip flag #
        uname = res.upper()
        if ("NO%sSUMMARY" % uname) in SysMgr.environList:
            return
        elif not TaskAnalyzer.checkSummaryFilter(uname):
            return

        if SysMgr.jsonEnable:
            _printer = lambda x: x
        else:
            _printer = SysMgr.printPipe

        TA = TaskAnalyzer

        # set comm and pid size #
        pd = SysMgr.pidDigit
        cl = 26 - (pd * 2)

        _printer(
            "\n[Top %s Info] (Unit: %%) (New: +) (Die: -)\n%s\n"
            % (res, twoLine)
        )

        if SysMgr.processEnable:
            idName = "PID"
            pidName = "PPID"
        else:
            idName = "TID"
            pidName = "PID"

        # Print menu #
        procInfo = (
            "{0:>{cl}} ({1:>{pd}}/{2:>{pd}}/{3:>4}/{4:>4})| {5:^17} |".format(
                "COMM",
                idName,
                pidName,
                "Nr",
                "Pri",
                "Min/Avg/Max/Tot",
                cl=cl,
                pd=pd,
            )
        )
        procInfoLen = len(procInfo)
        maxLineLen = SysMgr.lineLength
        margin = 5

        # Print timeline #
        TA.printTimelineInterval(margin, procInfoLen, procInfo, 1)

        value = TA.procTotData["total"]
        cpuInfo = "%d/%.1f/%d/%d" % (
            value["cpuMin"] if value["cpuMin"] > 0 else 0,
            value["cpuAvg"],
            value["cpuMax"],
            value["cpu"],
        )

        # Print total usage #
        procInfo = (
            "{0:>{cl}} ({1:>{pd}}/{2:>{pd}}/{3:>4}/{4:>4})| {5:^17} |".format(
                "[CPU/AVG]", "-", "-", "-", "-", cpuInfo, cl=cl, pd=pd
            )
        )
        procInfoLen = len(procInfo)
        maxLineLen = SysMgr.lineLength

        timeLine = ""
        lineLen = len(procInfo)
        for idx in xrange(len(TA.procIntData)):
            if lineLen + margin > maxLineLen:
                timeLine += "\n" + (" " * (procInfoLen - 1)) + "| "
                lineLen = len(procInfo)

            if "total" in TA.procIntData[idx]:
                usage = TA.procIntData[idx]["total"]["cpu"]
            else:
                usage = 0

            timeLine += "{0:>6} ".format(usage)
            lineLen += margin + 2

        _printer(("{0:1} {1:1}\n{2:1}\n").format(procInfo, timeLine, oneLine))

        if attr == "pri":
            try:
                TA.procTotData["total"]["maxPri"] = 0

                for data in TA.procIntData:
                    for pid, stat in data.items():
                        # skip non-task data #
                        if (
                            not pid.isdigit()
                            or not pid in TA.procTotData
                            or not "pri" in stat
                        ):
                            continue

                        # update max prio value #
                        TA.procTotData[pid].setdefault("maxPri", 40)
                        nrSched = SysMgr.convSchedPrio(stat["pri"])
                        TA.procTotData[pid]["maxPri"] = min(
                            TA.procTotData[pid]["maxPri"],
                            0 if nrSched is None else nrSched,
                        )
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printWarn("failed to sort by prio", True, True)

        # Print usage of processes #
        for pid, value in sorted(
            TA.procTotData.items(), key=lambda e: e[1][sortv], reverse=sortr
        ):
            if pid == "total":
                continue

            resInfo = "%d/%.1f/%d/%d" % (
                value[name + "Min"] if value[name + "Min"] > 0 else 0,
                value[name + "Avg"],
                value[name + "Max"],
                value[name],
            )

            procInfo = (
                "{0:>{cl}} ({1:>{pd}}/{2:>{pd}}/{3:>4}/{4:>4})| {5:^17} |"
            ).format(
                value["comm"][:cl],
                pid,
                value["ppid"],
                value["nrThreads"],
                value["pri"],
                resInfo,
                cl=cl,
                pd=pd,
            )
            procInfoLen = len(procInfo)
            maxLineLen = SysMgr.lineLength

            timeLine = ""
            lineLen = len(procInfo)
            total = 0
            for idx in xrange(len(TA.procIntData)):
                if lineLen + margin > maxLineLen:
                    timeLine += "\n" + (" " * (procInfoLen - 1)) + "| "
                    lineLen = len(procInfo)

                if pid in TA.procIntData[idx]:
                    try:
                        usage = _conv(TA.procIntData[idx][pid][vname])
                    except SystemExit:
                        sys.exit(0)
                    except:
                        usage = 0
                    total += TA.procIntData[idx][pid][name]
                else:
                    usage = 0

                lflag = ""
                if pid in TA.lifeIntData and idx in TA.lifeIntData[pid]:
                    for item in TA.lifeIntData[pid][idx]:
                        if item == "START":
                            lflag += "+"
                        elif item == "FINISH":
                            lflag += "-"

                # append lifecycle flag to usage #
                usage = lflag + str(usage)

                timeLine += "{0:>6} ".format(usage)
                lineLen += margin + 2

            # skip process used no value #
            if total == 0:
                continue

            _printer(
                ("{0:1} {1:1}\n{2:1}\n").format(procInfo, timeLine, oneLine)
            )

    @staticmethod
    def printDlyInterval():
        TaskAnalyzer.printCpuInterval(attr="dly")

    @staticmethod
    def printGpuInterval():
        # check skip flag #
        if "NOGPUSUMMARY" in SysMgr.environList:
            return
        elif not TaskAnalyzer.checkSummaryFilter("GPU"):
            return

        TA = TaskAnalyzer

        # Check GPU data #
        if "gpu" not in TA.procTotData["total"]:
            return

        if SysMgr.jsonEnable:
            _printer = lambda x: x
        else:
            _printer = SysMgr.printPipe

        _printer("\n[Top GPU Info] (Unit: %%)\n%s\n" % twoLine)

        # Print menu #
        gpuInfo = "{0:>23} | {1:^17} |".format("GPU", "Min/Avg/Max/Tot")
        gpuInfoLen = len(gpuInfo)
        maxLineLen = SysMgr.lineLength
        margin = 5

        # Print timeline #
        TA.printTimelineInterval(margin, gpuInfoLen, gpuInfo, 1)

        # Print GPU usage #
        for gpu, stat in TA.procTotData["total"]["gpu"].items():
            try:
                avg = stat["usage"] / float(len(TA.procIntData))
            except:
                avg = 0

            # get stats #
            stats = "%d/%.1f/%d/%d" % (
                stat["min"] if stat["min"] > 0 else 0,
                avg,
                stat["max"],
                stat["usage"],
            )

            gpuInfo = "{0:>23} | {1:^17} |".format(gpu, stats)
            gpuInfoLen = len(gpuInfo)
            maxLineLen = SysMgr.lineLength

            timeLine = ""
            lineLen = len(gpuInfo)
            total = 0
            margin = 5
            for idx in xrange(len(TA.procIntData)):
                if lineLen + margin > maxLineLen:
                    timeLine += "\n" + (" " * (gpuInfoLen - 1)) + "| "
                    lineLen = len(gpuInfo)

                try:
                    usage = TA.procIntData[idx]["total"]["gpu"][gpu]
                    total += usage
                except:
                    usage = 0

                timeLine += "{0:>6} ".format(usage)
                lineLen += 7

            _printer(
                ("{0:1} {1:1}\n{2:1}\n").format(gpuInfo, timeLine, oneLine)
            )

    @staticmethod
    def printRssInterval():
        # check skip flag #
        if "NORSSSUMMARY" in SysMgr.environList:
            return
        elif not TaskAnalyzer.checkSummaryFilter("RSS"):
            return

        TA = TaskAnalyzer

        # set comm and pid size #
        pd = SysMgr.pidDigit
        cl = 26 - (pd * 2)

        # check memory type #
        if SysMgr.pssEnable:
            mtype = "PSS"
        elif SysMgr.ussEnable:
            mtype = "USS"
        else:
            mtype = "RSS"

        if SysMgr.jsonEnable:
            _printer = lambda x: x
        else:
            _printer = SysMgr.printPipe

        _printer(
            "\n[Top %s Info] (Unit: MB) (Change: ^)\n%s\n" % (mtype, twoLine)
        )

        if SysMgr.processEnable:
            idName = "PID"
            pidName = "PPID"
        else:
            idName = "TID"
            pidName = "PID"

        # Print menu #
        procInfo = (
            "{0:>{cl}} ({1:>{pd}}/{2:>{pd}}/{3:>4}/{4:>4})|{5:^6} |".format(
                "COMM", idName, pidName, "Nr", "Pri", " Max", cl=cl, pd=pd
            )
        )
        procInfoLen = len(procInfo)
        maxLineLen = SysMgr.lineLength
        margin = 5

        # Print timeline #
        TA.printTimelineInterval(margin, procInfoLen, procInfo, 1)

        # Print total free memory #
        value = TA.procTotData["total"]
        procInfo = (
            "{0:>{cl}} ({1:>{pd}}/{2:>{pd}}/{3:>4}/{4:>4})|{5:>6} |".format(
                "[FREE/MIN]", "-", "-", "-", "-", value["minMem"], cl=cl, pd=pd
            )
        )
        procInfoLen = len(procInfo)
        maxLineLen = SysMgr.lineLength

        timeLine = ""
        lineLen = len(procInfo)
        for idx in xrange(len(TA.procIntData)):
            if lineLen + margin > maxLineLen:
                timeLine += "\n" + (" " * (procInfoLen - 1)) + "| "
                lineLen = len(procInfo)

            if "total" in TA.procIntData[idx]:
                usage = TA.procIntData[idx]["total"]["mem"]
            else:
                usage = 0

            timeLine += "{0:>6} ".format(usage)
            lineLen += 7

        _printer(("{0:1} {1:1}\n{2:1}\n").format(procInfo, timeLine, oneLine))

        # Print rss of processes #
        for pid, value in sorted(
            TA.procTotData.items(),
            key=lambda e: 0 if not "maxMem" in e[1] else e[1]["maxMem"],
            reverse=True,
        ):
            if pid == "total" or value["maxMem"] == 0:
                continue

            procInfo = (
                "{0:>{cl}} ({1:>{pd}}/{2:>{pd}}/" "{3:>4}/{4:>4})|{5:>6} |"
            ).format(
                value["comm"][:cl],
                pid,
                value["ppid"],
                value["nrThreads"],
                value["pri"],
                value["maxMem"],
                cl=cl,
                pd=pd,
            )
            procInfoLen = len(procInfo)
            maxLineLen = SysMgr.lineLength

            prev = 0
            timeLine = ""
            minRss = maxRss = 0
            lineLen = len(procInfo)
            intData = TA.procIntData
            for idx in xrange(len(intData)):
                if lineLen + margin > maxLineLen:
                    timeLine += "\n" + (" " * (procInfoLen - 1)) + "| "
                    lineLen = len(procInfo)

                # process is shown #
                if pid in intData[idx]:
                    if intData[idx][pid]["die"]:
                        try:
                            usage = intData[idx][pid]["mem"]
                        except:
                            prev = usage = 0
                    else:
                        usage = intData[idx][pid]["mem"]
                        if usage == 0 and prev > 0:
                            usage = prev
                        else:
                            prev = usage
                # process was shown previously #
                elif prev > 0:
                    try:
                        # process was terminated #
                        if intData[idx - 1][pid]["die"]:
                            prev = usage = 0
                        # process is alive #
                        else:
                            usage = prev
                    except:
                        usage = prev
                else:
                    prev = usage = 0

                if usage == 0:
                    pass
                else:
                    if minRss == 0 or minRss > usage:
                        minRss = usage
                    if maxRss == 0 or maxRss < usage:
                        maxRss = usage

                timeLine += "{0:>6} ".format(usage)
                lineLen += 7

            # mark change #
            if maxRss - minRss > 0:
                try:
                    procInfo = procInfo.replace(" (", "^(", 1)
                except:
                    pass

            _printer(
                "{0:1} {1:1}\n{2:1}\n".format(procInfo, timeLine, oneLine)
            )

    @staticmethod
    def printPrioInterval():
        TaskAnalyzer.printCpuInterval(attr="pri")

    @staticmethod
    def printVssInterval():
        # check skip flag #
        if "NOVSSSUMMARY" in SysMgr.environList:
            return
        elif not TaskAnalyzer.checkSummaryFilter("VSS"):
            return

        TA = TaskAnalyzer

        # set comm and pid size #
        pd = SysMgr.pidDigit
        cl = 26 - (pd * 2)

        if SysMgr.jsonEnable:
            _printer = lambda x: x
        else:
            _printer = SysMgr.printPipe

        _printer("\n[Top VSS Info] (Unit: MB) (Change: ^)\n%s\n" % twoLine)

        if SysMgr.processEnable:
            idName = "PID"
            pidName = "PPID"
        else:
            idName = "TID"
            pidName = "PID"

        # Print menu #
        procInfo = (
            "{0:>{cl}} ({1:>{pd}}/{2:>{pd}}/{3:>4}/{4:>4})|{5:^6} |".format(
                "COMM", idName, pidName, "Nr", "Pri", " Max", cl=cl, pd=pd
            )
        )
        procInfoLen = len(procInfo)
        maxLineLen = SysMgr.lineLength
        margin = 5

        # Print timeline #
        TA.printTimelineInterval(margin, procInfoLen, procInfo, 1)

        # Print total free memory #
        value = TA.procTotData["total"]
        procInfo = (
            "{0:>{cl}} ({1:>{pd}}/{2:>{pd}}/{3:>4}/{4:>4})|{5:>6} |".format(
                "[FREE/MIN]", "-", "-", "-", "-", value["minMem"], cl=cl, pd=pd
            )
        )
        procInfoLen = len(procInfo)
        maxLineLen = SysMgr.lineLength

        timeLine = ""
        lineLen = len(procInfo)
        for idx in xrange(len(TA.procIntData)):
            if lineLen + margin > maxLineLen:
                timeLine += "\n" + (" " * (procInfoLen - 1)) + "| "
                lineLen = len(procInfo)

            if "total" in TA.procIntData[idx]:
                usage = TA.procIntData[idx]["total"]["mem"]
            else:
                usage = 0

            timeLine += "{0:>6} ".format(usage)
            lineLen += 7

        _printer("{0:1} {1:1}\n{2:1}\n".format(procInfo, timeLine, oneLine))

        # Print vss of processes #
        for pid, value in sorted(
            TA.procTotData.items(),
            key=lambda e: 0 if not "maxVss" in e[1] else e[1]["maxVss"],
            reverse=True,
        ):
            if pid == "total" or value["maxVss"] == 0:
                continue

            procInfo = (
                "{0:>{cl}} ({1:>{pd}}/{2:>{pd}}/" "{3:>4}/{4:>4})|{5:>6} |"
            ).format(
                value["comm"][:cl],
                pid,
                value["ppid"],
                value["nrThreads"],
                value["pri"],
                value["maxVss"],
                cl=cl,
                pd=pd,
            )
            procInfoLen = len(procInfo)
            maxLineLen = SysMgr.lineLength

            prev = 0
            timeLine = ""
            minVss = maxVss = 0
            lineLen = len(procInfo)
            intData = TA.procIntData
            for idx in xrange(len(intData)):
                if lineLen + margin > maxLineLen:
                    timeLine += "\n" + (" " * (procInfoLen - 1)) + "| "
                    lineLen = len(procInfo)

                # process is shown #
                if pid in intData[idx]:
                    if intData[idx][pid]["die"]:
                        try:
                            usage = intData[idx][pid]["vss"]
                        except:
                            prev = usage = 0
                    else:
                        usage = intData[idx][pid]["vss"]
                        if usage == 0 and prev > 0:
                            usage = prev
                        else:
                            prev = usage
                # process was shown previously #
                elif prev > 0:
                    try:
                        # process was terminated #
                        if intData[idx - 1][pid]["die"]:
                            prev = usage = 0
                        # process is alive #
                        else:
                            usage = prev
                    except:
                        usage = prev
                else:
                    prev = usage = 0

                if usage == 0:
                    pass
                else:
                    if minVss == 0 or minVss > usage:
                        minVss = usage
                    if maxVss == 0 or maxVss < usage:
                        maxVss = usage

                timeLine += "{0:>6} ".format(usage)
                lineLen += 7

            # mark change #
            if maxVss - minVss > 0:
                try:
                    procInfo = procInfo.replace(" (", "^(", 1)
                except:
                    pass

            _printer(
                "{0:1} {1:1}\n{2:1}\n".format(procInfo, timeLine, oneLine)
            )

    @staticmethod
    def printGpuMemInterval():
        # check skip flag #
        if "NOGPUMEMSUMMARY" in SysMgr.environList:
            return
        elif not TaskAnalyzer.checkSummaryFilter("GPUMEM"):
            return

        TA = TaskAnalyzer

        # set comm and pid size #
        pd = SysMgr.pidDigit
        cl = 26 - (pd * 2)

        if SysMgr.jsonEnable:
            _printer = lambda x: x
        else:
            _printer = SysMgr.printPipe

        _printer("\n[Top GPUMEM Info] (Unit: MB)\n%s\n" % twoLine)

        if SysMgr.processEnable:
            idName = "PID"
            pidName = "PPID"
        else:
            idName = "TID"
            pidName = "PID"

        # Print menu #
        procInfo = (
            "{0:>{cl}} ({1:>{pd}}/{2:>{pd}}/{3:>4}/{4:>4})| {5:^5} |".format(
                "COMM", idName, pidName, "Nr", "Pri", " Max", cl=cl, pd=pd
            )
        )
        procInfoLen = len(procInfo)
        maxLineLen = SysMgr.lineLength
        margin = 5

        # Print timeline #
        TA.printTimelineInterval(margin, procInfoLen, procInfo, 1)

        # Print gpumem usage of processes #
        itemCnt = 0
        for pid, value in sorted(
            TA.procTotData.items(),
            key=lambda e: e[1]["gpumem"] if "gpumem" in e[1] else 0,
            reverse=True,
        ):
            if pid == "total":
                continue
            elif not value.get("gpumem"):
                break

            procInfo = (
                "{0:>{cl}} ({1:>{pd}}/{2:>{pd}}/" "{3:>4}/{4:>4})| {5:>5} |"
            ).format(
                value["comm"][:cl],
                pid,
                value["ppid"],
                value["nrThreads"],
                value["pri"],
                value["gpumem"],
                cl=cl,
                pd=pd,
            )
            procInfoLen = len(procInfo)
            maxLineLen = SysMgr.lineLength

            timeLine = ""
            lineLen = len(procInfo)
            for idx in xrange(len(TA.procIntData)):
                if lineLen + margin > maxLineLen:
                    timeLine += "\n" + (" " * (procInfoLen - 1)) + "| "
                    lineLen = len(procInfo)

                if pid in TA.procIntData[idx]:
                    usage = TA.procIntData[idx][pid]["gpumem"]
                else:
                    usage = "-"

                timeLine += "{0:>6} ".format(usage)
                lineLen += 7

            _printer(
                ("{0:1} {1:1}\n{2:1}\n").format(procInfo, timeLine, oneLine)
            )
            itemCnt += 1

        if itemCnt == 0:
            _printer("    None\n%s\n" % oneLine)

    @staticmethod
    def printBlkInterval():
        # check skip flag #
        if "NOBLOCKSUMMARY" in SysMgr.environList:
            return
        elif not TaskAnalyzer.checkSummaryFilter("BLOCK"):
            return

        TA = TaskAnalyzer

        # set comm and pid size #
        pd = SysMgr.pidDigit
        cl = 26 - (pd * 2)

        if SysMgr.jsonEnable:
            _printer = lambda x: x
        else:
            _printer = SysMgr.printPipe

        _printer("\n[Top Block Info] (Unit: %%)\n%s\n" % twoLine)

        if SysMgr.processEnable:
            idName = "PID"
            pidName = "PPID"
        else:
            idName = "TID"
            pidName = "PID"

        # Print menu #
        procInfo = (
            "{0:>{cl}} ({1:>{pd}}/{2:>{pd}}/{3:>4}/{4:>4})| {5:^5} |".format(
                "COMM", idName, pidName, "Nr", "Pri", " Sum", cl=cl, pd=pd
            )
        )
        procInfoLen = len(procInfo)
        maxLineLen = SysMgr.lineLength
        margin = 5

        # Print timeline #
        TA.printTimelineInterval(margin, procInfoLen, procInfo, 1)

        # Print block usage of processes #
        itemCnt = 0
        for pid, value in sorted(
            TA.procTotData.items(), key=lambda e: e[1]["blk"], reverse=True
        ):
            if (
                pid == "total"
                or value["blk"] == value["blkrd"] == value["blkwr"] == 0
            ):
                continue

            if SysMgr.blockEnable:
                bstat = "%s/%s" % (value["blkrd"], value["blkwr"])
            else:
                bstat = value["blk"]

            procInfo = (
                "{0:>{cl}} ({1:>{pd}}/{2:>{pd}}/" "{3:>4}/{4:>4})| {5:>5} |"
            ).format(
                value["comm"][:cl],
                pid,
                value["ppid"],
                value["nrThreads"],
                value["pri"],
                bstat,
                cl=cl,
                pd=pd,
            )
            procInfoLen = len(procInfo)
            maxLineLen = SysMgr.lineLength

            timeLine = ""
            lineLen = len(procInfo)
            for idx in xrange(len(TA.procIntData)):
                if lineLen + margin > maxLineLen:
                    timeLine += "\n" + (" " * (procInfoLen - 1)) + "| "
                    lineLen = len(procInfo)

                if pid in TA.procIntData[idx]:
                    res = TA.procIntData[idx][pid]
                    if SysMgr.blockEnable:
                        if res["blkrd"] or res["blkwr"]:
                            usage = "%s/%s" % (res["blkrd"], res["blkwr"])
                        else:
                            usage = 0
                    else:
                        usage = res["blk"]
                else:
                    usage = 0

                timeLine += "{0:>6} ".format(usage)
                lineLen += 7

            _printer(
                ("{0:1} {1:1}\n{2:1}\n").format(procInfo, timeLine, oneLine)
            )
            itemCnt += 1

        if itemCnt == 0:
            _printer("    None\n%s\n" % oneLine)

    @staticmethod
    def printStorageInterval():
        # check skip flag #
        if "NOSTORAGESUMMARY" in SysMgr.environList:
            return
        elif not TaskAnalyzer.checkSummaryFilter("STORAGE"):
            return

        if "NOSTORAGEINFO" in SysMgr.environList:
            return

        TA = TaskAnalyzer

        convSize2Unit = UtilMgr.convSize2Unit

        if SysMgr.jsonEnable:
            _printer = lambda x: x
        else:
            _printer = SysMgr.printPipe

        _printer("\n[Top Storage Info] (Unit: %%/MB)\n%s\n" % twoLine)

        # Print menu #
        storageInfo = "{0:>16} | {1:^21} |".format(
            "Device", "Busy/Read/Write/Free"
        )
        storageInfoLen = len(storageInfo)
        maxLineLen = SysMgr.lineLength
        margin = 21

        # Print timeline #
        TA.printTimelineInterval(margin, storageInfoLen, storageInfo)

        # Check storage data #
        if "storage" not in TA.procTotData["total"]:
            _printer("    None\n%s\n" % oneLine)
            return

        # Print storage usage #
        for dev, val in TA.procTotData["total"]["storage"].items():
            if dev == "[ TOTAL ]":
                continue

            try:
                total = "%s/%s/%s/%s" % (
                    "%.1f" % (val["busy"] / len(TA.procIntData)),
                    convSize2Unit(val["read"], True),
                    convSize2Unit(val["write"], True),
                    convSize2Unit(val["free"], True),
                )
            except SystemExit:
                sys.exit(0)
            except:
                continue

            storageInfo = "{0:>16} | {1:^21} |".format(dev, total)
            storageInfoLen = len(storageInfo)
            maxLineLen = SysMgr.lineLength

            timeLine = ""
            lineLen = len(storageInfo)
            for idx in xrange(len(TA.procIntData)):
                if lineLen + margin > maxLineLen:
                    timeLine += "\n" + (" " * (storageInfoLen - 1)) + "| "
                    lineLen = len(storageInfo)

                try:
                    stats = TA.procIntData[idx]["total"]["storage"][dev]
                    if (
                        stats["busy"]
                        == stats["read"]
                        == stats["write"]
                        == stats["free"]
                        == 0
                    ):
                        usage = "0"
                    else:
                        usage = "%s/%s/%s/%s" % (
                            stats["busy"],
                            convSize2Unit(stats["read"], True),
                            convSize2Unit(stats["write"], True),
                            convSize2Unit(stats["free"], True),
                        )
                except SystemExit:
                    sys.exit(0)
                except:
                    usage = "0"

                timeLine += "{0:>21} ".format(usage)
                lineLen += margin

            _printer(
                ("{0:1} {1:1}\n{2:1}\n").format(storageInfo, timeLine, oneLine)
            )

    @staticmethod
    def printNetworkInterval():
        # check skip flag #
        if "NONETSUMMARY" in SysMgr.environList:
            return
        elif not TaskAnalyzer.checkSummaryFilter("NETWORK"):
            return

        TA = TaskAnalyzer

        convSize2Unit = UtilMgr.convSize2Unit

        if SysMgr.jsonEnable:
            _printer = lambda x: x
        else:
            _printer = SysMgr.printPipe

        _printer("\n[Top Network Info] (Unit: Byte)\n%s\n" % twoLine)

        # Print menu #
        networkInfo = "{0:>16} | {1:^21} |".format("Device", "In/Out")
        networkInfoLen = len(networkInfo)
        maxLineLen = SysMgr.lineLength
        margin = 21

        # Print timeline #
        TA.printTimelineInterval(margin, networkInfoLen, networkInfo)

        # Check network data #
        if "netdev" not in TA.procTotData["total"]:
            _printer("    None\n%s\n" % oneLine)
            return

        # Print network usage #
        for dev, val in TA.procTotData["total"]["netdev"].items():
            if dev == "[ TOTAL ]":
                continue

            try:
                total = "%s/%s" % (
                    convSize2Unit(val["recv"], True),
                    convSize2Unit(val["tran"], True),
                )
            except:
                continue

            networkInfo = "{0:>16} | {1:^21} |".format(dev, total)
            networkInfoLen = len(networkInfo)
            maxLineLen = SysMgr.lineLength

            timeLine = ""
            lineLen = len(networkInfo)
            for idx in xrange(len(TA.procIntData)):
                if lineLen + margin > maxLineLen:
                    timeLine += "\n" + (" " * (networkInfoLen - 1)) + "| "
                    lineLen = len(networkInfo)

                try:
                    stats = TA.procIntData[idx]["total"]["netdev"][dev]
                    if stats["recv"] == stats["tran"] == 0:
                        usage = "0"
                    else:
                        usage = "%s/%s" % (
                            convSize2Unit(stats["recv"], True),
                            convSize2Unit(stats["tran"], True),
                        )
                except:
                    usage = "0"

                timeLine += "{0:>21} ".format(usage)
                lineLen += margin

            _printer(
                ("{0:1} {1:1}\n{2:1}\n").format(networkInfo, timeLine, oneLine)
            )

    @staticmethod
    def printTimelineInterval(margin, length, title, more=0):
        if SysMgr.jsonEnable:
            return

        timeLine = ""
        lineLen = length
        maxLineLen = SysMgr.lineLength

        if more > 0:
            spaces = more * 2
        else:
            spaces = more

        for i in xrange(1, len(TaskAnalyzer.procIntData) + 1):
            if lineLen + margin > maxLineLen:
                timeLine += "\n" + (" " * (length - 1)) + "| "
                lineLen = length

            timeLine += "{0:>{margin}} ".format(i, margin=margin + more)
            lineLen += margin + spaces

        SysMgr.printPipe(
            ("{0:1} {1:1}\n{2:1}\n").format(title, timeLine, twoLine)
        )

    @staticmethod
    def printCgCpuInterval(attr="CPU"):
        if not TaskAnalyzer.checkSummaryFilter("CGCPU"):
            return

        # check skip flag #
        if attr == "CPU":
            if "NOCGCPUSUMMARY" in SysMgr.environList:
                return
            name = "cgroup.cpu"
            jname = "cpuCgroupUsage"
        elif attr == "Throttle":
            if "NOCGTHROTTLESUMMARY" in SysMgr.environList:
                return
            name = "cgroup.throttle"
            jname = "throttleCgroupUsage"

        TA = TaskAnalyzer

        if SysMgr.jsonEnable:
            _printer = lambda x: x
            jsonData = SysMgr.jsonData[jname] = {}
        else:
            _printer = SysMgr.printPipe
            jsonData = {}

        _printer("\n[Top Cgroup.%s Info] (Unit: %%)\n%s\n" % (attr, twoLine))

        # Print menu #
        cpuInfo = "{0:<48} | {1:^21} |".format("Cgroup", "Min/Avg/Max/Tot")
        cpuInfoLen = len(cpuInfo)
        margin = 5

        # Print timeline #
        TA.printTimelineInterval(margin, cpuInfoLen, cpuInfo, 1)

        # Check CPU data #
        if name not in TA.procTotData["total"]:
            _printer("    None\n%s\n" % oneLine)
            return

        # Print CPU usage #
        nrPrint = 0
        for group, val in sorted(
            TA.procTotData["total"][name].items(),
            key=lambda e: e[1]["usage"],
            reverse=True,
        ):
            total = long(val["usage"])
            minval = long(val["min"])
            maxval = long(val["max"])

            if total == 0:
                continue

            try:
                avg = total / len(TaskAnalyzer.procIntData)
            except:
                avg = 0

            usagestr = "%s/%.1f/%s/%s" % (minval, avg, maxval, total)

            cgroupInfo = "{0:<48} | {1:^21} |".format(group, usagestr)
            cgroupInfoLen = len(cgroupInfo)
            maxLineLen = SysMgr.lineLength

            timeLine = ""
            lineLen = len(cgroupInfo)
            usageList = []
            for idx in xrange(len(TA.procIntData)):
                if lineLen + margin > maxLineLen:
                    timeLine += "\n" + (" " * (cgroupInfoLen - 1)) + "| "
                    lineLen = len(cgroupInfo)

                try:
                    usage = TA.procIntData[idx]["total"][name][group]
                except:
                    usage = 0

                usageList.append(usage)
                timeLine += "{0:>6} ".format(usage)
                lineLen += margin + 2

            _printer(
                ("{0:1} {1:1}\n{2:1}\n").format(cgroupInfo, timeLine, oneLine)
            )

            try:
                if not SysMgr.jsonEnable:
                    raise Exception()

                jsonData[group] = {
                    "usage": usageList,
                    "minimum": minval,
                    "maximum": maxval,
                    "average": avg,
                }
            except SystemExit:
                sys.exit(0)
            except:
                pass

            nrPrint += 1

        if nrPrint == 0:
            _printer("    None\n%s\n" % oneLine)

    @staticmethod
    def printFileInterval():
        # check skip flag #
        if "NOFILESUMMARY" in SysMgr.environList:
            return
        elif not TaskAnalyzer.fileIntData:
            return
        elif not TaskAnalyzer.checkSummaryFilter("FILE"):
            return

        if SysMgr.jsonEnable:
            _printer = lambda x: x
            jsonData = SysMgr.jsonData["file"] = []
        else:
            _printer = SysMgr.printPipe
            jsonData = {}

        for path, data in sorted(TaskAnalyzer.fileIntData.items()):
            _printer(
                "\n[Top File Info] (Path: %s)\n%s\n"
                % (os.path.realpath(path), twoLine)
            )

            # Print menu #
            _printer("{0:^16} | {1:1}\n{2:1}".format("Time", "Data", oneLine))

            for ltime, content in sorted(
                data.items(), key=lambda x: float(x[0])
            ):
                _printer("{0:>16.1f} | {1:1}".format(float(ltime), content))

            _printer("%s\n" % oneLine)

    @staticmethod
    def printLifeHistory():
        # check skip flag #
        if "NOLIFESUMMARY" in SysMgr.environList:
            return
        elif not TaskAnalyzer.checkSummaryFilter("LIFE"):
            return

        TA = TaskAnalyzer

        if SysMgr.jsonEnable:
            _printer = lambda x: x
            jsonData = SysMgr.jsonData["procLife"] = {}
        else:
            _printer = SysMgr.printPipe
            jsonData = {}

        _printer("\n[Top Life Info]\n%s\n" % twoLine)

        # set target #
        if SysMgr.processEnable:
            target = "Process"
            parent = "Parent"
        else:
            target = "Thread"
            parent = "Process"

        # Print menu #
        _printer(
            (
                "{0:^32} | {1:^16} | {2:^16} | {3:^16} | "
                "{4:^32} | {5:^18} | {6:^4} |\n{7:1}"
            ).format(
                target,
                "START",
                "END",
                "DURATION",
                parent,
                "USER",
                "PRIO",
                oneLine,
            )
        )

        nrPrint = 0
        procIntData = TaskAnalyzer.procIntData

        # Print times #
        for pid, value in sorted(
            TA.lifeProcData.items(), key=lambda x: long(x[0])
        ):
            # check filter #
            if SysMgr.filterGroup and (
                not UtilMgr.isValidStr(pid)
                and not UtilMgr.isValidStr(value["comm"])
            ):
                continue

            # name #
            name = "%s(%s)" % (value["comm"], pid)

            # start #
            try:
                start = value["START"]
                if start == -2:
                    start = "<-"
                else:
                    start = float(start)
            except SystemExit:
                sys.exit(0)
            except:
                start = "x"

            # end #
            try:
                end = value["FINISH"]
                if end == -2:
                    end = "-"
                elif end == -1:
                    startTime = float(procIntData[0]["time"])
                    end = float(startTime - SysMgr.intervalEnable)
                else:
                    end = float(end)
            except:
                end = "x"

            # duration #
            try:
                duration = end - start
                durations = str(duration).split(".")
                s = long(durations[0])
                f = durations[1][:1]
                duration = "%s.%s" % (UtilMgr.convTime(s), f)
            except:
                duration = " "

            # convert start #
            try:
                start = UtilMgr.convTime(long(start))
            except:
                pass

            # convert end #
            try:
                end = UtilMgr.convTime(long(end))
            except:
                pass

            _printer(
                (
                    "{0:>32} | {1:>16} | {2:>16} | {3:>16} | "
                    "{4:>32} | {5:>18} | {6:>4} |"
                ).format(
                    name,
                    start,
                    end,
                    duration,
                    value["parent"],
                    value["user"],
                    value["prio"],
                )
            )

            if SysMgr.jsonEnable:
                jsonData[name] = {
                    "start": start,
                    "end": end,
                    "duration": duration,
                    "parent": value["parent"],
                    "user": value["user"],
                    "prio": value["prio"],
                }

            nrPrint += 1

        if not nrPrint:
            _printer("    None")

        _printer("%s\n" % oneLine)

    @staticmethod
    def printCgBlkInterval():
        # check skip flag #
        if "NOCGBLKSUMMARY" in SysMgr.environList:
            return
        elif not TaskAnalyzer.checkSummaryFilter("CGBLOCK"):
            return

        TA = TaskAnalyzer

        convSize2Unit = UtilMgr.convSize2Unit

        if SysMgr.jsonEnable:
            _printer = lambda x: x
            jsonData = SysMgr.jsonData["blkCgroupUsage"] = {}
        else:
            _printer = SysMgr.printPipe
            jsonData = {}

        _printer("\n[Top Cgroup.Blk Info] (Unit: Byte)\n%s\n" % twoLine)

        # Print menu #
        blkInfo = "{0:<48} | {1:^15} |".format("Cgroup", "Read/Write")
        blkInfoLen = len(blkInfo)
        margin = 14

        # Print timeline #
        TA.printTimelineInterval(margin, blkInfoLen, blkInfo, 1)

        totalData = TA.procTotData["total"]

        # Check blkio data #
        if "cgroup.read" not in totalData:
            _printer("    None\n%s\n" % oneLine)
            return

        # Print blkio usage #
        nrPrint = 0
        for group, val in sorted(
            totalData["cgroup.read"].items(),
            key=lambda e: e[1]["max"],
            reverse=True,
        ):
            rdTotal = val["usage"]
            wrTotal = totalData["cgroup.write"][group]["usage"]
            if rdTotal == wrTotal == 0:
                continue

            rdTotal = convSize2Unit(rdTotal)
            wrTotal = convSize2Unit(wrTotal)
            usagestr = "%s/%s" % (rdTotal, wrTotal)

            cgroupInfo = "{0:<48} | {1:^15} |".format(group, usagestr)
            cgroupInfoLen = len(cgroupInfo)
            maxLineLen = SysMgr.lineLength

            timeLine = ""
            lineLen = len(cgroupInfo)
            usageList = []
            for idx in xrange(len(TA.procIntData)):
                if lineLen + margin > maxLineLen:
                    timeLine += "\n" + (" " * (cgroupInfoLen - 1)) + "| "
                    lineLen = len(cgroupInfo)

                try:
                    rd = TA.procIntData[idx]["total"]["cgroup.read"][group]
                    rd = convSize2Unit(rd)
                    wr = TA.procIntData[idx]["total"]["cgroup.write"][group]
                    wr = convSize2Unit(wr)
                    usage = "%s/%s" % (rd, wr)
                except:
                    usage = "0/0"

                usageList.append(usage)
                timeLine += "{0:>15} ".format(usage)
                lineLen += margin + 2

            _printer(
                ("{0:1} {1:1}\n{2:1}\n").format(cgroupInfo, timeLine, oneLine)
            )

            try:
                if not SysMgr.jsonEnable:
                    raise Exception()

                jsonData[group] = {
                    "usage": usageList,
                    "rdTotal": rdTotal,
                    "wrTotal": wrTotal,
                }
            except SystemExit:
                sys.exit(0)
            except:
                pass

            nrPrint += 1

        if nrPrint == 0:
            _printer("    None\n%s\n" % oneLine)

    @staticmethod
    def printCgMemInterval():
        # check skip flag #
        if "NOCGMEMSUMMARY" in SysMgr.environList:
            return
        elif not TaskAnalyzer.checkSummaryFilter("CGMEM"):
            return

        TA = TaskAnalyzer

        convSize2Unit = UtilMgr.convSize2Unit

        if SysMgr.jsonEnable:
            _printer = lambda x: x
            jsonData = SysMgr.jsonData["memCgroupUsage"] = {}
        else:
            _printer = SysMgr.printPipe
            jsonData = {}

        _printer("\n[Top Cgroup.Mem Info] (Unit: Byte)\n%s\n" % twoLine)

        # Print menu #
        memInfo = "{0:<48} | {1:^15} |".format("Cgroup", "Min/Max")
        memInfoLen = len(memInfo)
        margin = 6

        # Print timeline #
        TA.printTimelineInterval(margin, memInfoLen, memInfo, 1)

        # Check Memory data #
        if "cgroup.mem" not in TA.procTotData["total"]:
            _printer("    None\n%s\n" % oneLine)
            return

        # Print Memory usage #
        nrPrint = 0
        for group, val in sorted(
            TA.procTotData["total"]["cgroup.mem"].items(),
            key=lambda e: e[1]["max"],
            reverse=True,
        ):
            if val["max"] == 0:
                continue

            minval = convSize2Unit(val["min"])
            maxval = convSize2Unit(val["max"])
            usagestr = "%s/%s" % (minval, maxval)

            cgroupInfo = "{0:<48} | {1:^15} |".format(group, usagestr)
            cgroupInfoLen = len(cgroupInfo)
            maxLineLen = SysMgr.lineLength

            timeLine = ""
            lineLen = len(cgroupInfo)
            usageList = []
            for idx in xrange(len(TA.procIntData)):
                if lineLen + margin > maxLineLen:
                    timeLine += "\n" + (" " * (cgroupInfoLen - 1)) + "| "
                    lineLen = len(cgroupInfo)

                try:
                    usage = TA.procIntData[idx]["total"]["cgroup.mem"][group]
                    usage = convSize2Unit(usage)
                except:
                    usage = 0

                usageList.append(usage)
                timeLine += "{0:>7} ".format(usage)
                lineLen += margin + 2

            _printer(
                ("{0:1} {1:1}\n{2:1}\n").format(cgroupInfo, timeLine, oneLine)
            )

            try:
                if not SysMgr.jsonEnable:
                    raise Exception()

                jsonData[group] = {
                    "usage": usageList,
                    "minimum": minval,
                    "maximum": maxval,
                }
            except SystemExit:
                sys.exit(0)
            except:
                pass

            nrPrint += 1

        if nrPrint == 0:
            _printer("    None\n%s\n" % oneLine)

    @staticmethod
    def printIntervalUsage(onlyTotal=False, onlySummary=False, current=True):
        def _printMenu(title):
            stars = "*" * long((long(SysMgr.lineLength) - len(title)) / 2)
            SysMgr.printPipe("\n\n\n\n%s%s%s\n\n" % (stars, title, stars))

        def _printProcTree(root, treeList, depth, done):
            commCache = SysMgr.commCache
            prioData = TaskAnalyzer.procPrioData
            lifeData = TaskAnalyzer.lifeProcData
            for i, c in sorted(treeList.items(), key=lambda x: long(x[0])):
                if i in done:
                    continue
                else:
                    done[i] = 0

                indent = " " * depth * 5
                char = "->" if depth == 0 else "|-"
                start = "\n" if depth == 0 else ""
                comm = commCache[i] if i in commCache else "?"
                prio = (
                    "<%s>" % ",".join(list(prioData[i].keys()))
                    if i in prioData
                    else ""
                )
                user = "[%s]" % lifeData[i]["user"] if i in lifeData else ""

                try:
                    if not i in lifeData:
                        raise Exception("no life data")
                    lifeTime = lifeData[i]
                    life = float(lifeTime["FINISH"]) - float(lifeTime["START"])
                    lifes = str(life).split(".")
                    s = long(lifes[0])
                    f = lifes[1][:3]
                    runtime = "{%s.%s}" % (UtilMgr.convTime(s), f)
                except SystemExit:
                    sys.exit(0)
                except:
                    runtime = ""

                SysMgr.printPipe(
                    "%s%s%s %s(%s)%s%s%s"
                    % (start, indent, char, comm, i, prio, user, runtime)
                )

                if isinstance(c, dict):
                    sub = c
                elif i in root:
                    sub = root[i]
                else:
                    continue

                _printProcTree(root, sub, depth + 1, done)

        def _initData():
            # initialize parsing buffer #
            TaskAnalyzer.lifeIntData = {}
            TaskAnalyzer.lifeProcData = {}
            TaskAnalyzer.lifecycleData = {}
            TaskAnalyzer.procTotData = {}
            TaskAnalyzer.procIntData = []
            TaskAnalyzer.procTreeData = {}
            TaskAnalyzer.procPrioData = {}

            # shrink heap #
            SysMgr.shrinkHeap()

        # check skip condition #
        try:
            if SysMgr.printFd:
                if (
                    SysMgr.printFd.name == SysMgr.nullPath
                    or SysMgr.outPath == "NUL"
                ):
                    return
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # ftop mode #
        if SysMgr.fileTopEnable:
            TaskAnalyzer.printFileTable()
        # check skip condition #
        elif SysMgr.jsonEnable and current:
            pass
        # dbustop mode #
        elif SysMgr.dbusTopEnable:
            DbusMgr.printSummary()
        # dlttop mode #
        elif SysMgr.dltTopEnable:
            DltAnalyzer.printSummary()
        # top mode #
        else:
            # build summary interval table #
            TaskAnalyzer.summarizeInterval()

            # print interval info #
            TaskAnalyzer.printTimeline()

            # TODO: support JSON format for below functions #

            if not onlyTotal:
                TaskAnalyzer.printEventInterval()
                TaskAnalyzer.printCpuInterval()
                TaskAnalyzer.printDlyInterval()
                TaskAnalyzer.printPrioInterval()
                TaskAnalyzer.printGpuInterval()
                TaskAnalyzer.printGpuMemInterval()
                TaskAnalyzer.printVssInterval()
                TaskAnalyzer.printRssInterval()
                TaskAnalyzer.printBlkInterval()
                TaskAnalyzer.printStorageInterval()
                TaskAnalyzer.printNetworkInterval()
                TaskAnalyzer.printCgCpuInterval()
                TaskAnalyzer.printCgCpuInterval("Throttle")
                TaskAnalyzer.printCgMemInterval()
                TaskAnalyzer.printCgBlkInterval()
                TaskAnalyzer.printLifeHistory()
                TaskAnalyzer.printFileInterval()

        if SysMgr.jsonEnable:
            # initialize global data #
            _initData()

            # quit #
            return

        # set target #
        target = "Process" if SysMgr.processEnable else "Thread"

        # print only summary #
        if onlySummary:
            # print only last detailed statistic #
            _printMenu(" Detailed Statistics ")
            SysMgr.printPipe(SysMgr.procBuffer[:1])

            # print lifecycle info #
            _printMenu(" %s Lifecycle " % target)
            TaskAnalyzer.printProcLifecycle()

            # initialize global data #
            _initData()

            # quit #
            return

        # print interval info #
        TaskAnalyzer.printMemAnalysis()

        # print detailed statistics #
        _printMenu(" Detailed Statistics ")
        if not SysMgr.procBuffer:
            SysMgr.printPipe("\n    None")
        else:
            SysMgr.printPipe(SysMgr.procBuffer)

        # print lifecycle info #
        _printMenu(" %s Lifecycle " % target)
        TaskAnalyzer.printProcLifecycle()

        # print process tree #
        _printMenu(" %s Tree " % target)
        TaskAnalyzer.printProcTree()

        # print Leak hint #
        _printMenu(" Leak Hint ")
        TaskAnalyzer.printLeakHint()

        # print D-Bus info #
        if "PRINTSYSTEMD" in SysMgr.environList:
            origJsonEnable = SysMgr.jsonEnable
            SysMgr.jsonEnable = True
            try:
                SysMgr.streamEnable = True
                dbusDict = DbusMgr.runDbusSnooper(mode="getunitlist")
                dbusStr = UtilMgr.convDict2Str(dbusDict, pretty=False)
                _printMenu(" Systemd Info ")
                SysMgr.printPipe(dbusStr)
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr("failed to save systemd info", True)
            SysMgr.jsonEnable = origJsonEnable

        # print kernel messages for current system #
        try:
            if current:
                _printMenu(" Kernel Message ")
                SysMgr.printPipe(LogMgr.getKmsg(SysMgr.kmsgLine))
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn("failed to save kernel message", reason=True)

        # file dump #
        _printMenu(" File Data ")
        ret = SysMgr.dumpFileData()
        if not ret:
            SysMgr.printPipe("\n    None")

        # print tree info #
        if TaskAnalyzer.procTreeData:
            _printMenu(" Relation Tree ")
            _printProcTree(
                TaskAnalyzer.procTreeData, TaskAnalyzer.procTreeData, 0, {}
            )
            if not TaskAnalyzer.procTreeData:
                SysMgr.printPipe("\n    None")
            SysMgr.printPipe("\n")

        # initialize global data #
        _initData()

    @staticmethod
    def printLeakHint():
        if (
            not SysMgr.isLinux
            or not SysMgr.isTopMode()
            or not SysMgr.topInstance
        ):
            SysMgr.printPipe("\n    None")
            return

        convNum = UtilMgr.convNum
        convSize = UtilMgr.convSize2Unit

        for pid in tuple(SysMgr.procInstance):
            path = "%s/%s" % (SysMgr.procPath, pid)
            SysMgr.topInstance.updateOOMScore(path, pid)
            SysMgr.topInstance.saveProcStatusData(path, pid)

        if SysMgr.processEnable:
            pidType = "PID"
            ppidType = "PPID"
        else:
            pidType = "TID"
            ppidType = "PID"

        SysMgr.printPipe(
            (
                "\n{0:1}\n{1:>16}({2:>7}/{3:>7}) "
                "{4:>8} {5:>8} {6:>8} {7:>12} {8:>20}\n{9:^1}\n"
            ).format(
                twoLine,
                "Name",
                pidType,
                ppidType,
                "VSS",
                "RSS",
                "SHM",
                "OOM_SCORE",
                "LifeTime",
                oneLine,
            )
        )

        cnt = 0
        commIdx = SysMgr.topInstance.commIdx
        ppidIdx = SysMgr.topInstance.ppidIdx
        vssIdx = SysMgr.topInstance.vssIdx
        rssIdx = SysMgr.topInstance.rssIdx
        shrIdx = SysMgr.topInstance.shrIdx
        starttimeIdx = SysMgr.topInstance.starttimeIdx

        for pid, val in sorted(
            SysMgr.procInstance.items(),
            key=lambda x: long(x[1]["oomScore"] if "oomScore" in x[1] else 0),
            reverse=True,
        ):
            if "oomScore" not in val or val["oomScore"] == 0:
                break

            # skip sibling threads #
            if not val["isMain"]:
                continue

            stat = val["stat"]
            statm = val["statm"]
            comm = stat[commIdx].strip("()")
            runtime = SysMgr.uptime - (float(val["stat"][starttimeIdx]) / 100)

            if SysMgr.processEnable:
                ppid = stat[ppidIdx]
            else:
                ppid = val["mainID"]

            try:
                vss = convSize(long(stat[vssIdx]))
            except SystemExit:
                sys.exit(0)
            except:
                vss = "-"

            try:
                rss = convSize(long(stat[rssIdx]) << 12)
            except SystemExit:
                sys.exit(0)
            except:
                rss = "-"

            try:
                shm = convSize(long(statm[shrIdx]) << 12)
            except SystemExit:
                sys.exit(0)
            except:
                shm = "-"

            SysMgr.printPipe(
                (
                    "{0:>16}({1:>7}/{2:>7}) "
                    "{3:>8} {4:>8} {5:>8} {6:>12} {7:>20}\n"
                ).format(
                    comm,
                    pid,
                    ppid,
                    vss,
                    rss,
                    shm,
                    convNum(val["oomScore"]),
                    UtilMgr.convTime(runtime),
                )
            )

            cnt += 1

        if cnt == 0:
            SysMgr.printPipe("    None\n")

        SysMgr.printPipe(oneLine)

    @staticmethod
    def printProcLifecycle():
        if not TaskAnalyzer.lifecycleData:
            SysMgr.printPipe("\n    None")
            return

        SysMgr.printPipe(
            (
                "\n{0:1}\n{1:>16} {2:>15} {3:>15} {4:>15} "
                "{5:>15} {6:>15} {7:>15} {8:>15} {9:>15}\n{10:1}\n"
            ).format(
                twoLine,
                "Name",
                "Created",
                "Terminated",
                "Zombie",
                "Stopped",
                "Traced",
                "Waiting",
                "Waking",
                "Parked",
                oneLine,
            )
        )

        try:
            for comm, event in sorted(
                TaskAnalyzer.lifecycleData.items(),
                key=lambda e: e[1][0] + e[1][1],
                reverse=True,
            ):
                if comm in ("^START", "^FINISH"):
                    continue

                # convert 0 to '-' #
                for idx, value in enumerate(event):
                    if value == 0:
                        event[idx] = "-"

                SysMgr.printPipe(
                    (
                        "{0:>16} {1:>15} {2:>15} {3:>15} {4:>15} "
                        "{5:>15} {6:>15} {7:>15} {8:>15}\n"
                    ).format(
                        comm,
                        event[0],
                        event[1],
                        event[2],
                        event[3],
                        event[4],
                        event[5],
                        event[6],
                        event[7],
                    )
                )
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn("failed to print lifecycle of tasks", True, True)

        SysMgr.printPipe(oneLine)

    @staticmethod
    def getDescendantList(pids, instance=None):
        taskList = []

        try:
            # convert to list #
            if UtilMgr.isNumber(pids):
                pids = [pids]

            # convert items to integer #
            pids = list(map(str, pids))
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                "failed to convert items to get descendants", reason=True
            )
            return taskList

        # get proc instance #
        if instance:
            obj = instance
        else:
            obj = TaskAnalyzer(onlyInstance=True)

            if SysMgr.isLinux:
                obj.saveSystemStat()
            else:
                obj.saveSystemStatGen()

        # get task tree #
        try:
            procTree = TaskAnalyzer.getProcTreeFromList(obj.procData)
        except SystemExit:
            sys.exit(0)
        except:
            return taskList

        # get nodes in tree #
        def _getTreeNodes(root, pids, enable, taskList):
            initStatus = enable
            for pid, children in sorted(
                root.items(), key=lambda x: long(x[0])
            ):
                enable = initStatus

                if enable:
                    taskList.append(pid)
                elif pid in pids:
                    enable = True

                _getTreeNodes(children, pids, enable, taskList)

        # get descendant list #
        _getTreeNodes(procTree, pids, False, taskList)

        return taskList

    @staticmethod
    def printProcTree(
        instance=None,
        title=False,
        printFunc=None,
        targets=None,
        color=True,
        maxLine=0,
        lastLine=True,
        newline=False,
    ):
        if instance:
            instOrig = instance
            instance = instance.procData
        elif SysMgr.procInstance:
            instOrig = None
            instance = SysMgr.procInstance

        if not printFunc:
            printFunc = SysMgr.printPipe

        lastLine = oneLine if lastLine else ""

        if not instance:
            printFunc("\n    None\n" + lastLine)
            return

        # define index variables #
        ATTR = ConfigMgr.PROCSTAT_ATTR
        startIdx = ATTR.index("STARTTIME")
        utimeIdx = ATTR.index("UTIME")
        stimeIdx = ATTR.index("STIME")
        stateIdx = ATTR.index("STATE")
        nrthreadIdx = ATTR.index("NRTHREAD")

        # get cpu flag #
        curCpu = "CURCPU" in SysMgr.environList

        # get memory flag #
        addMem = "ADDMEM" in SysMgr.environList

        # get IO flag #
        addIO = "ADDIO" in SysMgr.environList

        # get cmdline flag #
        printCmdline = False
        if "CMDLINE" in SysMgr.environList:
            cmdlineLen = SysMgr.environList["CMDLINE"][0]
            if cmdlineLen == "SET":
                printCmdline = sys.maxsize
            elif cmdlineLen.isdigit():
                printCmdline = long(cmdlineLen)
            else:
                printCmdline = False

        # get nostat flag #
        nostat = "NOSTAT" in SysMgr.environList

        # get task tree #
        try:
            procTree = TaskAnalyzer.getProcTreeFromList(instance)
        except SystemExit:
            sys.exit(0)
        except:
            printFunc("\n    None\n" + lastLine)
            return

        # set target #
        if SysMgr.processEnable:
            target = "Process"
        else:
            target = "Thread"

        # get network info #
        try:
            netinfo = " (%s/%s)" % (
                NetworkMgr.getHostName(),
                NetworkMgr.getPublicIp(),
            )
        except SystemExit:
            sys.exit(0)
        except:
            netinfo = ""

        # print nodes in tree #
        def _printTreeNodes(root, depth, targets, enable, current, pindent=""):
            # check depth #
            if 0 < SysMgr.funcDepth <= depth:
                return "", 0

            cpu = 0
            treestr = ""
            initStatus = enable
            convColor = UtilMgr.convColor
            convSize = UtilMgr.convSize2Unit
            cutChild = "CUTCHILD" in SysMgr.environList

            nodes = sorted(root.items(), key=lambda x: long(x[0]))
            for pid, children in nodes:
                enable = initStatus
                indent = ""
                isLast = depth != 0 and nodes[-1][0] == pid
                isValid = True

                # get comm #
                try:
                    comm = ""
                    if printCmdline:
                        comm = SysMgr.getCmdline(pid)[:printCmdline]
                    if not comm:
                        comm = instance[pid]["comm"]

                    if SysMgr.filterGroup:
                        if (
                            UtilMgr.isValidStr(comm, ignCap=True)
                            or pid in SysMgr.filterGroup
                        ):
                            comm = convColor(comm, "RED")
                            if initStatus and cutChild:
                                isValid = False
                except SystemExit:
                    sys.exit(0)
                except:
                    comm = "?"

                # init indent string #
                if depth == 0:
                    indent = ""
                else:
                    token = " " if isLast else "|"
                    indent = pindent + ("%s%s" % (" " * 5, token))

                # get sub-tasks for filter #
                if not enable and targets:
                    if not str(pid) in targets and not UtilMgr.isValidStr(
                        comm, targets
                    ):
                        treestr += _printTreeNodes(
                            children,
                            depth + 1,
                            targets,
                            enable,
                            current,
                            indent,
                        )[0]
                        continue
                    else:
                        enable = True

                # get runtime #
                try:
                    if nostat:
                        raise Exception()

                    if SysMgr.isLinux:
                        runtime = long(instance[pid]["stat"][startIdx]) / 100
                        runtime = SysMgr.uptime - runtime
                    else:
                        runtime = long(instance[pid]["starttime"])
                        runtime = current - runtime

                    runtimestr = UtilMgr.convTime(runtime, True)
                except SystemExit:
                    sys.exit(0)
                except:
                    runtime = "?"
                    runtimestr = "?"

                # get CPU time #
                try:
                    if nostat:
                        raise Exception()

                    if SysMgr.isLinux:
                        utime = long(instance[pid]["stat"][utimeIdx])
                        stime = long(instance[pid]["stat"][stimeIdx])
                        ttime = (utime + stime) / 100
                    else:
                        utime = long(instance[pid]["utime"] * 100)
                        stime = long(instance[pid]["stime"] * 100)
                        ttime = utime + stime

                    # save total cpu usage #
                    if curCpu:
                        instance[pid]["utime"] = utime
                        instance[pid]["stime"] = stime
                        instance[pid]["ttime"] = ttime

                    ttimestr = UtilMgr.convTime(ttime, True)
                    if ttime > 0:
                        ttimestr = convColor(ttimestr, "YELLOW")
                except SystemExit:
                    sys.exit(0)
                except:
                    ttime = 0
                    ttimestr = "?"

                # get CPU time percent by runtime #
                try:
                    # ignore tasks used CPU lesser than 1% #
                    if nostat or not ttime:
                        raise Exception()

                    # calculate current cpu usage #
                    if curCpu:
                        cpuPer = ttime - instOrig.prevProcData[pid]["ttime"]
                        cpuPer = cpuPer * 100 / SysMgr.uptimeDiff
                    else:
                        cpuPer = round(ttime / float(runtime) * 100, 1)

                    if cpuPer <= 0:
                        raise Exception()

                    nrCpuPer = cpuPer
                    cpuPer = "%5.1f" % cpuPer
                    cpuPer = convColor(cpuPer, "RED", 5)
                except SystemExit:
                    sys.exit(0)
                except:
                    nrCpuPer = 0
                    cpuPer = "%5s" % 0

                # get user info #
                try:
                    if not printUserInfo:
                        raise ValueError("lo user info")

                    instOrig.saveProcStatusData(instance[pid]["taskPath"], pid)
                    uid = instance[pid]["status"]["Uid"].split(None, 1)[0]
                    uname = convColor(
                        SysMgr.sysInstance.userData[uid]["name"], "GREEN"
                    )
                    userInfo = "[%s]" % uname
                except ValueError:
                    userInfo = ""
                except SystemExit:
                    sys.exit(0)
                except:
                    userInfo = ""

                # get sub-tasks #
                treestrret, ctime = _printTreeNodes(
                    children, depth + 1, targets, enable, current, indent
                )
                if not isValid:
                    treestrret = ""
                if curCpu and (not SysMgr.showAll and not targets):
                    if not nrCpuPer and not ctime:
                        continue

                    cpu += nrCpuPer
                    cpu += ctime
                    if not ctime:
                        treestrret = ""

                # get memory stats #
                if not nostat and addMem and instOrig:
                    if SysMgr.isRoot():
                        stats = ["rss", "pss", "uss"]
                    else:
                        stats = ["rss"]

                    # backup task data #
                    backupData = instOrig.procData.pop(pid, None)

                    memstr = " " + TaskAnalyzer.getMemStr(
                        instOrig,
                        pid,
                        stats=stats,
                        color="CYAN",
                        verb=False,
                    )

                    # recover task data #
                    instOrig.procData[pid] = backupData
                else:
                    memstr = ""

                # get I/O stats #
                try:
                    if (
                        nostat
                        or not addIO
                        or (
                            not SysMgr.totalEnable
                            and not instOrig.prevProcData[pid]["io"]
                        )
                    ):
                        raise Exception()

                    read = instOrig.procData[pid]["io"]["read_bytes"]
                    write = instOrig.procData[pid]["io"]["write_bytes"]

                    # get diff #
                    if (
                        not SysMgr.totalEnable
                        and instOrig.prevProcData[pid]["io"]
                    ):
                        read -= instOrig.prevProcData[pid]["io"]["read_bytes"]
                        write -= instOrig.prevProcData[pid]["io"][
                            "write_bytes"
                        ]

                    if not read and not write:
                        raise Exception()

                    iostr = " IO(%s/%s)" % (
                        convColor(convSize(read), "WARNING") if read else read,
                        convColor(convSize(write), "WARNING")
                        if write
                        else write,
                    )
                except SystemExit:
                    sys.exit(0)
                except:
                    iostr = ""

                # build stat string #
                if nostat:
                    stats = ""
                else:
                    stats = "%3s%%(%s/%s)%s%s " % (
                        cpuPer,
                        ttimestr,
                        runtimestr,
                        memstr,
                        iostr,
                    )

                # get state #
                try:
                    if nostat:
                        raise Exception()
                    state = instance[pid]["stat"][stateIdx]
                    if state in ("D", "t", "T", "I", "x", "X", "Z"):
                        state = convColor(state, "RED")
                    state = "<%s>" % state
                except SystemExit:
                    sys.exit(0)
                except:
                    state = ""

                # get nrThread #
                try:
                    if nostat:
                        raise Exception()
                    nrThread = instance[pid]["stat"][nrthreadIdx]
                    nrThread = "{%s}" % nrThread
                except SystemExit:
                    sys.exit(0)
                except:
                    nrThread = ""

                # add proc info #
                procInfo = "%s(%s)%s%s%s" % (
                    comm,
                    pid,
                    state,
                    nrThread,
                    userInfo,
                )
                treestr += "%s- %-22s %s" % (
                    (indent[:-1] + "|") if isLast else indent,
                    procInfo,
                    stats,
                )

                # add child info #
                nrChild = len(children)
                if nrChild > 0:
                    treestr = treestr.rstrip() + " {%s}" % nrChild

                # add child nodes #
                treestr += "\n%s" % treestrret

            return treestr, cpu

        # set initial switch #
        if targets:
            enable = False
        else:
            enable = True

        # set color flag #
        origColor = SysMgr.colorEnable
        if not color and SysMgr.colorEnable:
            SysMgr.colorEnable = False

        # get current #
        current = time.time()

        # get string for tree #
        finalstr = _printTreeNodes(procTree, 0, targets, enable, current)[0]
        if finalstr:
            finalstr = "%s\n%s" % (finalstr.strip("\n"), oneLine)
        else:
            finalstr = "    None\n" + oneLine

        title = "%s[%s Tree Info]" % ("\n" if newline else "", target)

        printUserInfo = "USERINFO" in SysMgr.environList
        if printUserInfo:
            try:
                SysMgr.sysInstance.saveUserInfo()
            except SystemExit:
                sys.exit(0)
            except:
                pass

        diffStr = " (Interval: %.3f)" % SysMgr.uptimeDiff

        # get system usage #
        if TaskAnalyzer.dbgObj:
            convColor = UtilMgr.convColor
            convSize = UtilMgr.convSize2Unit

            # CPU #
            mcpuStr, sysCpuStr = SysMgr.getCpuUsageStr(
                TaskAnalyzer.dbgObj,
                SysMgr.uptimeDiff,
            )

            # Available Memory #
            sysAvailMemStr = convColor(SysMgr.getAvailMemInfo(), "YELLOW")

            # I/O #
            try:
                vmData = instOrig.vmData
                prevVmData = instOrig.prevVmData
                pgInMemDiff = vmData["pgpgin"]
                pgOutMemDiff = vmData["pgpgout"]

                if not SysMgr.totalEnable:
                    pgInMemDiff -= prevVmData["pgpgin"]
                    pgOutMemDiff -= prevVmData["pgpgout"]

                blkRdStr = convSize(pgInMemDiff << 10)
                blkWrStr = convSize(pgOutMemDiff << 10)
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printWarn("failed to get block stats", reason=True)
                blkRdStr = blkWrStr = "?"
            sysBlockStr = "%s/%s" % (blkRdStr, blkWrStr)
            if not blkRdStr in ("0", "?") or not blkWrStr in ("0", "?"):
                sysBlockStr = convColor(sysBlockStr, "YELLOW")

            # Network #
            try:
                (netIn, netOut) = instOrig.getNetworkUsage(
                    SysMgr.prevNetstat, SysMgr.netstat
                )
                netIn = convSize(netIn)
                netOut = convSize(netOut)
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printWarn("failed to get network stats", reason=True)
                netIn = netOut = "?"
            sysNetStr = "%s/%s" % (netIn, netOut)
            if not netIn in ("0", "?") or not netOut in ("0", "?"):
                sysNetStr = convColor(sysNetStr, "YELLOW")

            # build final stat string #
            sysStr = " (CPU:%s|MemAvl:%s|Block:%s|Net:%s) " % (
                sysCpuStr,
                sysAvailMemStr,
                sysBlockStr,
                sysNetStr,
            )
        else:
            sysStr = ""

        # get my usage #
        try:
            mprocInfo = "%s(%s)" % (
                TaskAnalyzer.dbgObj.comm,
                TaskAnalyzer.dbgObj.pid,
            )
            cur = instOrig.procData[str(SysMgr.pid)]["ttime"]
            prev = instOrig.prevProcData[str(SysMgr.pid)]["ttime"]
            mttime = long((cur - prev) * 100 / SysMgr.uptimeDiff)
            mttime = convColor(str(mttime) + "%", "YELLOW")
            rssStr = TaskAnalyzer.dbgObj.getMemUsage()
            rssStr = convColor(rssStr, "YELLOW")
            mStr = "(%s: %s/%s)" % (mprocInfo, mttime, rssStr)
        except SystemExit:
            sys.exit(0)
        except:
            mStr = ""

        # print title #
        printFunc(
            ("%s (Uptime: %s)%s%s%s%s\n%s\n" "  %-24s %4s(%s/%s) {%s}\n%s")
            % (
                title,
                UtilMgr.convTime(SysMgr.uptime),
                diffStr,
                netinfo,
                sysStr,
                mStr,
                twoLine,
                "Name(ID)<STATUS>{NRTHREAD}",
                ("Current_" if curCpu else "Total_") + "Per",
                "CPUTIME",
                "RUNTIME",
                "SUB",
                lastLine,
            )
        )

        # recover color flag #
        SysMgr.colorEnable = origColor

        if maxLine:
            finalstr = "\n".join(finalstr.split("\n", maxLine)[:maxLine])

        # print tree #
        printFunc(finalstr)

    @staticmethod
    def printMemAnalysis():
        if not SysMgr.isLinux or not SysMgr.procInstance:
            return

        statList = ConfigMgr.PROCSTAT_ATTR
        convNum = UtilMgr.convNum

        # set comm and pid size #
        pd = SysMgr.pidDigit
        cl = 26 - (pd * 2)

        SysMgr.printPipe(
            "\n[Top Memory Details] (Unit: MB/KB/NR)\n%s\n" % twoLine
        )

        if SysMgr.processEnable:
            idType = "PID"
            pidType = "PPID"
        else:
            idType = "TID"
            pidType = "PID"

        # Print menu #
        SysMgr.printPipe(
            (
                "{0:^{cl}} ({1:^{pd}}/{2:^{pd}}) | {3:^8} | "
                "{4:^5} | {5:^6} | {6:^6} | {7:^6} | {8:^6} | {9:^6} | "
                "{10:^10} | {11:^12} | {12:^12} | {13:^12} |\n{14}\n"
            ).format(
                "COMM",
                idType,
                pidType,
                "Type",
                "Cnt",
                "VSS/M",
                "RSS/M",
                "PSS/M",
                "SWAP/M",
                "HUGE/M",
                "LOCK/K",
                "PDRT/K",
                "SDRT/K",
                "NOPM/K",
                twoLine,
                cl=cl,
                pd=pd,
            )
        )

        cnt = 1
        limitProcCnt = SysMgr.nrProcMemReport
        commIdx = statList.index("COMM")
        ppidIdx = statList.index("PPID")

        try:
            sortedList = sorted(
                SysMgr.procInstance.items(),
                key=lambda e: long(e[1]["stat"][statList.index("RSS")]),
                reverse=True,
            )
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn(
                "failed to get memory details because of sort error"
            )
            SysMgr.printPipe("    None\n%s\n" % oneLine)
            return

        for key, value in sortedList:
            # check filter #
            if SysMgr.filterGroup:
                skip = True
                for item in SysMgr.filterGroup:
                    if key == item or item in value["stat"][commIdx]:
                        skip = False
                        break
                if skip:
                    continue

            # only print memory details of top 4 processes #
            if cnt > limitProcCnt:
                break

            # get memory details #
            value["maps"] = None
            TaskAnalyzer.saveProcSmaps(value["taskPath"], key)
            if not value["maps"]:
                continue

            cnt += 1

            totalCnt = 0
            totalVmem = 0
            totalRss = 0
            totalPss = 0
            totalSwap = 0
            totalHuge = 0
            totalLock = 0
            totalPdirty = 0
            totalSdirty = 0
            totalRef = 0
            totalNone = 0

            procInfo = " "
            procDetails = ""

            if SysMgr.processEnable:
                ppid = value["stat"][ppidIdx]
            else:
                ppid = value["mainID"]

            for idx, item in sorted(value["maps"].items(), reverse=True):
                if not item:
                    continue

                totalCnt += item["count"]

                try:
                    vmem = item["Size:"] >> 10
                    totalVmem += vmem
                except SystemExit:
                    sys.exit(0)
                except:
                    vmem = 0

                try:
                    rss = item["Rss:"] >> 10
                    totalRss += rss
                except SystemExit:
                    sys.exit(0)
                except:
                    rss = 0

                try:
                    pss = item["Pss:"] >> 10
                    totalPss += pss
                except SystemExit:
                    sys.exit(0)
                except:
                    pss = 0

                try:
                    swap = item["Swap:"] >> 10
                    totalSwap += swap
                except SystemExit:
                    sys.exit(0)
                except:
                    swap = 0

                try:
                    huge = item["AnonHugePages:"] >> 10
                    totalHuge += huge
                except SystemExit:
                    sys.exit(0)
                except:
                    huge = 0

                try:
                    lock = item["Locked:"]
                    totalLock += lock
                except SystemExit:
                    sys.exit(0)
                except:
                    lock = 0

                try:
                    pdirty = item["Private_Dirty:"]
                    totalPdirty += pdirty
                except SystemExit:
                    sys.exit(0)
                except:
                    pdirty = 0

                try:
                    sdirty = item["Shared_Dirty:"]
                    totalSdirty += sdirty
                except SystemExit:
                    sys.exit(0)
                except:
                    sdirty = 0

                try:
                    ref = item["Referenced:"]
                    totalRef += ref
                except SystemExit:
                    sys.exit(0)
                except:
                    ref = 0

                try:
                    none = item["NOPM"]
                    totalNone += none
                except SystemExit:
                    sys.exit(0)
                except:
                    none = 0

                procDetails += (
                    "{0:>30} | {1:>8} | {2:>5} | {3:>6} | "
                    "{4:>6} | {5:>6} | {6:>6} | {7:>6} | {8:>10} | "
                    "{9:>12} | {10:>12} | {11:>12} |\n"
                ).format(
                    procInfo,
                    idx,
                    convNum(item["count"]),
                    convNum(vmem),
                    convNum(rss),
                    convNum(pss),
                    convNum(swap),
                    convNum(huge),
                    convNum(lock),
                    convNum(pdirty),
                    convNum(sdirty),
                    convNum(none),
                )

            procInfo = "{0:>{cl}} ({1:>{pd}}/{2:>{pd}})".format(
                value["stat"][commIdx].strip("()")[:cl],
                key,
                ppid,
                cl=cl,
                pd=pd,
            )

            SysMgr.printPipe(
                (
                    "{0:>30} | {1:>8} | {2:>5} | {3:>6} | "
                    "{4:>6} | {5:>6} | {6:>6} | {7:>6} | {8:>10} | "
                    "{9:>12} | {10:>12} | {11:>12} |\n{12}"
                ).format(
                    procInfo,
                    "[TOTAL]",
                    convNum(totalCnt),
                    convNum(totalVmem),
                    convNum(totalRss),
                    convNum(totalPss),
                    convNum(totalSwap),
                    convNum(totalHuge),
                    convNum(totalLock),
                    convNum(totalPdirty),
                    convNum(totalSdirty),
                    convNum(totalNone),
                    procDetails,
                )
            )

            SysMgr.printPipe("%s\n" % oneLine)

        if cnt == 1:
            SysMgr.printPipe("    None\n%s\n" % oneLine)

    @staticmethod
    def setTraceAttr(line):
        for f in (
            SysMgr.traceTgidFormat1,
            SysMgr.traceTgidFormat2,
            SysMgr.traceNoTgidFormat,
        ):
            m = re.match(f, line)
            if not m:
                continue

            if f == SysMgr.traceNoTgidFormat:
                SysMgr.tgidEnable = False

            d = m.groupdict()
            SysMgr.startTime = d["time"]
            return float(d["time"])

        return None

    @staticmethod
    def getInitTime(fname, verb=True):
        fd = None
        printer = SysMgr.printErr if verb else lambda *x: None
        exit = sys.exit if verb else lambda x: x

        if SysMgr.isRecordMode():
            compressor = None
        else:
            try:
                fd = open(fname, "rb")
            except SystemExit:
                exit(0)
            except:
                SysMgr.printOpenErr(fname)
                exit(-1)

            # check compression #
            fd = SysMgr.getCompFd(fname)
            if not fd:
                SysMgr.compressEnable = False

        while 1:
            start = end = -1

            # make delay for some logs not written immediately #
            if SysMgr.isRecordMode():
                try:
                    time.sleep(0.1)
                except:
                    exit(0)

                # check repeat count #
                if SysMgr.progressCnt >= SysMgr.repeatInterval:
                    return 0

            # update fd #
            try:
                if SysMgr.isRecordMode():
                    verb = False
                else:
                    verb = True

                if fd:
                    if verb:
                        # get output size #
                        fsize = UtilMgr.getFileSizeStr(fname)

                        SysMgr.printStat(
                            r"start checking '%s'%s..." % (fname, fsize)
                        )
                else:
                    try:
                        fd = UtilMgr.getTextLines(
                            fname, verb, retfd=True, load=False
                        )
                    except SystemExit:
                        exit(0)
                    except:
                        printer("failed to read '%s'\n" % fname, reason=True)
                        exit(-1)
            except SystemExit:
                exit(0)
            except:
                SysMgr.printOpenErr(fname)
                exit(-1)

            # check decodable load #
            try:
                fd.read(1)
                fd.seek(0)
            except SystemExit:
                exit(0)
            except:
                printer("failed to read '%s'\n" % fname)
                if SysMgr.forceEnable:
                    return 0
                exit(-1)

            # verify log buffer #
            buf = []
            for idx, line in enumerate(fd):
                # decode line #
                try:
                    line = line.decode("utf-8")
                except SystemExit:
                    exit(0)
                except:
                    pass

                # check system info #
                if end == -1:
                    buf.append(line)
                if not SysMgr.recordStatus:
                    if line.startswith(SysMgr.magicStr):
                        if start == -1:
                            start = idx
                        elif end == -1:
                            end = idx
                            SysMgr.sysinfoBuffer = "".join(
                                buf[start + 1 : end]
                            )
                        continue

                # parsing and set attributes #
                etime = TaskAnalyzer.setTraceAttr(line)
                if etime:
                    return etime

                # check other mode #
                if line.startswith("[Top "):
                    return 0

            # check record status #
            if SysMgr.isDrawMode():
                return 0
            elif not SysMgr.recordStatus:
                printer("failed to read '%s' because there is no log" % fname)
                exit(-1)

    def saveBlkOpt(self, tid, comm, opt, major, minor, addr, size):
        def _getBlkOptSize(size):
            idx = size.bit_length() - 1
            return 1 << idx

        def _applyBlkOpt(targetTable, addr, size, blkSize, blkOffset, did):
            # per-device size stat #
            try:
                targetTable[did][0] += size
                targetTable[did][1] += 1

                # sequential operation #
                if targetTable[did][2] == addr:
                    targetTable[did][3] += size
                    targetTable[did][4] += 1

                targetTable[did][2] = blkOffset
            except SystemExit:
                sys.exit(0)
            except:
                sizeTable = {}
                targetTable[did] = [size, 1, blkOffset, size, 1, sizeTable]

            # per-operation size stat #
            try:
                targetTable[did][5][blkSize] += 1
            except:
                targetTable[did][5][blkSize] = 1

        # apply filter #
        if SysMgr.isExceptTarget(tid, self.threadData, comm):
            return

        # total block info #
        readTable = self.blockTable[0]
        writeTable = self.blockTable[1]
        taskTable = self.blockTable[2]
        # [totalSize, totalCnt, lastBlk, seqSize, seqCnt, sizeTable] #

        addr = long(addr)
        size = long(size)

        # get real block size for operation #
        if size > 1:
            blkOffset = addr + (size >> 9)
        else:
            # convert page to real size #
            size = SysMgr.PAGESIZE
            blkOffset = addr + 1

        blkSize = _getBlkOptSize(size)
        did = "%s:%s" % (major, minor)

        # revise real minor number by address #
        mntTree = SysMgr.savedMountTree
        if not did in mntTree or minor == "0":
            for mid, val in mntTree.items():
                try:
                    if did == mid:
                        continue

                    if minor == "0" and did in mntTree:
                        devPath = mntTree[did]["dev"]
                        if (
                            mntTree[mid]["dev"].startswith(devPath)
                            and val["start"] <= addr <= val["end"]
                        ):
                            did = mid
                            break

                    if (
                        mid.split(":")[0] == major
                        and val["start"] <= addr <= val["end"]
                    ):
                        # update device ID #
                        minor = mid.split(":")[1]
                        did = "%s:%s" % (major, minor)
                        break
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

        # map data to total table #
        if opt == "R":
            targetTable = readTable
        elif opt == "W":
            targetTable = writeTable
        else:
            SysMgr.printWarn("failed to recognize block operation '%s'" % opt)
            return

        # apply total block info #
        _applyBlkOpt(targetTable, addr, size, blkSize, blkOffset, did)

        # init task stat #
        if tid not in taskTable:
            taskTable[tid] = [{}, {}, 0]

        # map data to task table #
        if opt == "R":
            targetTable = taskTable[tid][0]
        elif opt == "W":
            targetTable = taskTable[tid][1]
        else:
            SysMgr.printWarn("failed to recognize block operation '%s'" % opt)
            return

        # increase task I/O size #
        taskTable[tid][2] += size

        # apply thread block info #
        _applyBlkOpt(targetTable, addr, size, blkSize, blkOffset, did)

    def handleIntData(self, itime):
        if SysMgr.intervalEnable == 0:
            return

        intervalEnable = SysMgr.intervalEnable

        itime = float(itime)
        intervalCnt = float(SysMgr.intervalNow + intervalEnable)
        startTime = float(SysMgr.startTime)
        if hasattr(self, "trimStart"):
            startTime += self.trimStart
        elapsed = itime - startTime

        if not elapsed > intervalCnt and self.finishTime == "0":
            return

        SysMgr.intervalNow += intervalEnable

        # check change of all threads #
        for key, value in sorted(
            self.threadData.items(), key=lambda e: e[1]["usage"], reverse=True
        ):
            index = long(SysMgr.intervalNow / intervalEnable) - 1
            nextIndex = long(SysMgr.intervalNow / intervalEnable)

            try:
                self.intData[index]
            except SystemExit:
                sys.exit(0)
            except:
                self.intData.append(dict())

            try:
                self.intData[index]["toTal"]
            except SystemExit:
                sys.exit(0)
            except:
                self.intData[index]["toTal"] = {
                    "totalBr": 0,
                    "totalBw": 0,
                    "totalMem": 0,
                    "totalKmem": 0,
                }

                # make total custom event list #
                if SysMgr.customEventList:
                    self.intData[index]["toTal"]["customEvent"] = {}
                    for evt in self.customEventInfo:
                        self.intData[index]["toTal"]["customEvent"][
                            evt
                        ] = dict(self.init_eventData)

                # make user event list #
                if SysMgr.userEventList:
                    self.intData[index]["toTal"]["userEvent"] = {}
                    for evt in self.userEventInfo:
                        self.intData[index]["toTal"]["userEvent"][evt] = dict(
                            self.init_eventData
                        )

                # make kernel event list #
                if SysMgr.kernelEventList:
                    self.intData[index]["toTal"]["kernelEvent"] = {}
                    for evt in self.kernelEventInfo:
                        self.intData[index]["toTal"]["kernelEvent"][
                            evt
                        ] = dict(self.init_eventData)

            # set thread in this interval #
            self.intData[index].setdefault(key, dict(self.init_intData))

            # define thread alias in this interval #
            curIntval = self.intData[index][key]

            # save start time in this interval #
            curIntval["firstLogTime"] = itime

            # make interval list #
            try:
                self.intData[nextIndex]
            except SystemExit:
                sys.exit(0)
            except:
                self.intData.append({})

            # set thread in next interval #
            self.intData[nextIndex].setdefault(key, dict(self.init_intData))

            # define shortcut variable for thread data #
            tdata = self.threadData[key]

            # save total usage in this interval #
            curIntval["totalUsage"] = float(tdata["usage"])
            curIntval["totalPreempted"] = float(tdata["cpuWait"])
            curIntval["totalCoreSchedCnt"] = long(tdata["coreSchedCnt"])
            curIntval["totalBrUsage"] = long(tdata["reqRdBlock"])
            curIntval["totalBwUsage"] = long(tdata["writeBlock"]) + (
                long(tdata["awriteBlock"]) << 3
            )
            curIntval["totalMemUsage"] = long(tdata["nrPages"])
            curIntval["totalKmemUsage"] = long(tdata["remainKmem"])

            # add core time not calculated yet in this interval #
            for idx, val in self.lastTidPerCore.items():
                if self.threadData[val]["lastStatus"] == "S":
                    # apply core off time #
                    coreId = "0[%s]" % idx
                    if self.threadData[coreId]["lastOff"] > 0:
                        diff = itime - self.threadData[coreId]["start"]
                        self.threadData[coreId]["usage"] += diff
                        self.intData[index][coreId]["totalUsage"] += diff
                        self.threadData[coreId]["start"] = itime
                    continue

                curIntval["totalUsage"] += itime - float(
                    self.threadData[val]["start"]
                )

            # mark life flag #
            if tdata["new"] != " ":
                curIntval["new"] = tdata["new"]
            if tdata["die"] != " ":
                curIntval["die"] = tdata["die"]

            # initialize custom event list #
            if SysMgr.customEventList:
                curIntval["customEvent"] = {}
                curIntval["totalCustomEvent"] = {}
                for evt in self.customEventInfo:
                    curIntval["customEvent"][evt] = dict(self.init_eventData)
                    curIntval["totalCustomEvent"][evt] = dict(
                        self.init_eventData
                    )
                    try:
                        curIntval["totalCustomEvent"][evt]["count"] = tdata[
                            "customEvent"
                        ][evt]["count"]
                    except SystemExit:
                        sys.exit(0)
                    except:
                        pass

            # initialize user event list #
            if SysMgr.userEventList:
                curIntval["userEvent"] = {}
                curIntval["totalUserEvent"] = {}
                for evt in self.userEventInfo:
                    curIntval["userEvent"][evt] = dict(self.init_eventData)
                    curIntval["totalUserEvent"][evt] = dict(
                        self.init_eventData
                    )
                    try:
                        curIntval["totalUserEvent"][evt]["count"] = tdata[
                            "userEvent"
                        ][evt]["count"]

                        curIntval["totalUserEvent"][evt]["usage"] = tdata[
                            "userEvent"
                        ][evt]["usage"]
                    except SystemExit:
                        sys.exit(0)
                    except:
                        pass

            # initialize kernel event list #
            if SysMgr.kernelEventList:
                curIntval["kernelEvent"] = {}
                curIntval["totalKernelEvent"] = {}
                for evt in self.kernelEventInfo:
                    curIntval["kernelEvent"][evt] = dict(self.init_eventData)
                    curIntval["totalKernelEvent"][evt] = dict(
                        self.init_eventData
                    )
                    try:
                        curIntval["totalKernelEvent"][evt]["count"] = tdata[
                            "kernelEvent"
                        ][evt]["count"]

                        curIntval["totalKernelEvent"][evt]["usage"] = tdata[
                            "kernelEvent"
                        ][evt]["usage"]
                    except SystemExit:
                        sys.exit(0)
                    except:
                        pass

            # first interval #
            if SysMgr.intervalNow == intervalEnable:
                curIntval["cpuUsage"] = float(tdata["usage"])
                curIntval["preempted"] = float(tdata["cpuWait"])
                curIntval["coreSchedCnt"] = float(tdata["coreSchedCnt"])
                curIntval["brUsage"] = long(tdata["reqRdBlock"])
                curIntval["bwUsage"] = long(tdata["writeBlock"]) + (
                    long(tdata["awriteBlock"]) << 3
                )
                curIntval["memUsage"] = long(tdata["nrPages"])
                curIntval["kmemUsage"] = long(tdata["remainKmem"])

            # later intervals #
            else:
                try:
                    self.intData[index - 1][key]
                except SystemExit:
                    sys.exit(0)
                except:
                    self.intData[index - 1][key] = dict(self.init_intData)

                # define thread alias in previous interval #
                prevIntval = self.intData[index - 1][key]

                # calculate resource usage in this interval #
                curIntval["cpuUsage"] += (
                    curIntval["totalUsage"] - prevIntval["totalUsage"]
                )
                curIntval["preempted"] += (
                    curIntval["totalPreempted"] - prevIntval["totalPreempted"]
                )
                curIntval["coreSchedCnt"] = (
                    curIntval["totalCoreSchedCnt"]
                    - prevIntval["totalCoreSchedCnt"]
                )
                curIntval["brUsage"] = (
                    curIntval["totalBrUsage"] - prevIntval["totalBrUsage"]
                )
                curIntval["bwUsage"] = (
                    curIntval["totalBwUsage"] - prevIntval["totalBwUsage"]
                )
                curIntval["memUsage"] = (
                    curIntval["totalMemUsage"] - prevIntval["totalMemUsage"]
                )
                curIntval["kmemUsage"] = (
                    curIntval["totalKmemUsage"] - prevIntval["totalKmemUsage"]
                )

            # calculate custom event usage in this interval #
            if "totalCustomEvent" in curIntval:
                for evt in list(curIntval["totalCustomEvent"]):
                    try:
                        curIntval["customEvent"][evt]["count"] = (
                            curIntval["totalCustomEvent"][evt]["count"]
                            - prevIntval["totalCustomEvent"][evt]["count"]
                        )
                    except SystemExit:
                        sys.exit(0)
                    except:
                        curIntval["customEvent"][evt]["count"] = curIntval[
                            "totalCustomEvent"
                        ][evt]["count"]

                    self.intData[index]["toTal"]["customEvent"][evt][
                        "count"
                    ] += curIntval["customEvent"][evt]["count"]

            # calculate user event usage in this interval #
            if "totalUserEvent" in curIntval:
                for evt in list(curIntval["totalUserEvent"]):
                    try:
                        curIntval["userEvent"][evt]["count"] = (
                            curIntval["totalUserEvent"][evt]["count"]
                            - prevIntval["totalUserEvent"][evt]["count"]
                        )

                        curIntval["userEvent"][evt]["usage"] = (
                            curIntval["totalUserEvent"][evt]["usage"]
                            - prevIntval["totalUserEvent"][evt]["usage"]
                        )
                    except SystemExit:
                        sys.exit(0)
                    except:
                        curIntval["userEvent"][evt]["count"] = curIntval[
                            "totalUserEvent"
                        ][evt]["count"]

                        curIntval["userEvent"][evt]["usage"] = curIntval[
                            "totalUserEvent"
                        ][evt]["usage"]

                    self.intData[index]["toTal"]["userEvent"][evt][
                        "count"
                    ] += curIntval["userEvent"][evt]["count"]

                    self.intData[index]["toTal"]["userEvent"][evt][
                        "usage"
                    ] += curIntval["userEvent"][evt]["usage"]

            # calculate kernel event usage in this interval #
            if "totalKernelEvent" in curIntval:
                for evt in list(curIntval["totalKernelEvent"]):
                    try:
                        curIntval["kernelEvent"][evt]["count"] = (
                            curIntval["totalKernelEvent"][evt]["count"]
                            - prevIntval["totalKernelEvent"][evt]["count"]
                        )

                        curIntval["kernelEvent"][evt]["usage"] = (
                            curIntval["totalKernelEvent"][evt]["usage"]
                            - prevIntval["totalKernelEvent"][evt]["usage"]
                        )
                    except SystemExit:
                        sys.exit(0)
                    except:
                        curIntval["kernelEvent"][evt]["count"] = curIntval[
                            "totalKernelEvent"
                        ][evt]["count"]

                        curIntval["kernelEvent"][evt]["usage"] = curIntval[
                            "totalKernelEvent"
                        ][evt]["usage"]

                    self.intData[index]["toTal"]["kernelEvent"][evt][
                        "count"
                    ] += curIntval["kernelEvent"][evt]["count"]

                    self.intData[index]["toTal"]["kernelEvent"][evt][
                        "usage"
                    ] += curIntval["kernelEvent"][evt]["usage"]

            # fix CPU usage exceed this interval #
            self.thisInterval = intervalEnable
            if (
                curIntval["cpuUsage"] > intervalEnable
                or self.finishTime != "0"
            ):
                ftime = float(self.intData[index - 1][key]["firstLogTime"])

                # first interval #
                if index == 0:
                    self.thisInterval = itime - startTime
                # normal intervals #
                elif ftime > 0:
                    self.thisInterval = itime - ftime
                # long time running intervals #
                else:
                    for idx in xrange(index - 1, -1, -1):
                        if ftime > 0:
                            self.thisInterval = itime - ftime
                            break
                    if self.thisInterval != intervalEnable:
                        self.thisInterval = itime - startTime

                # recalculate previous intervals if no context switching since profile start #
                remainTime = curIntval["cpuUsage"]
                if curIntval["cpuUsage"] > self.thisInterval:
                    for idx in xrange(
                        long(curIntval["cpuUsage"] / intervalEnable), -1, -1
                    ):
                        try:
                            self.intData[idx][key]
                        except SystemExit:
                            sys.exit(0)
                        except:
                            if not idx in self.intData:
                                continue
                            self.intData[idx][key] = dict(self.init_intData)
                        try:
                            self.intData[idx - 1][key]
                        except SystemExit:
                            sys.exit(0)
                        except:
                            if not idx - 1 in self.intData:
                                continue
                            self.intData[idx - 1][key] = dict(
                                self.init_intData
                            )
                        prevIntervalData = self.intData[idx - 1][key]

                        # make previous intervals of core there was no context switching #
                        longRunCore = tdata["longRunCore"]
                        if longRunCore >= 0:
                            longRunCoreId = "0[%s]" % longRunCore
                            try:
                                self.intData[idx][longRunCoreId]
                            except SystemExit:
                                sys.exit(0)
                            except:
                                self.intData[idx][longRunCoreId] = dict(
                                    self.init_intData
                                )

                        if remainTime >= intervalEnable:
                            remainTime = (
                                long(remainTime / intervalEnable)
                                * intervalEnable
                            )
                            prevIntervalData["cpuUsage"] = intervalEnable
                            prevIntervalData["cpuPer"] = 100
                        else:
                            if prevIntervalData["cpuUsage"] > remainTime:
                                remainTime = prevIntervalData["cpuUsage"]
                            else:
                                prevIntervalData["cpuUsage"] = remainTime
                            prevIntervalData["cpuPer"] = (
                                remainTime / intervalEnable
                            ) * 100

                        remainTime -= intervalEnable

            # add remainter of CPU usage exceed interval in this interval to previous interval #
            if (
                SysMgr.intervalNow - intervalEnable > 0
                and self.thisInterval > intervalEnable
            ):
                diff = self.thisInterval - intervalEnable
                if prevIntval["cpuUsage"] + diff > intervalEnable:
                    diff = intervalEnable - prevIntval["cpuUsage"]

                prevIntval["cpuUsage"] += diff
                prevIntval["cpuPer"] = (
                    prevIntval["cpuUsage"] / intervalEnable
                ) * 100

            # calculate percentage of CPU usage of this thread in this interval #
            if self.thisInterval > 0:
                curIntval["cpuPer"] = (
                    curIntval["cpuUsage"] / self.thisInterval
                ) * 100
            else:
                curIntval["cpuPer"] = 0

            # revise thread interval usage in DVFS system #
            if curIntval["cpuPer"] > 100:
                curIntval["cpuPer"] = 100
            elif curIntval["cpuPer"] < 0:
                curIntval["cpuPer"] = 0

            # fix preempted time exceed this interval #
            if (
                curIntval["preempted"] > intervalEnable
                and curIntval["preempted"] > self.thisInterval
            ):
                # recalculate previous intervals if no context switching since profile start #
                remainTime = curIntval["preempted"]
                for idx in xrange(index + 1, -1, -1):
                    try:
                        self.intData[idx][key]
                    except SystemExit:
                        sys.exit(0)
                    except:
                        self.intData[idx][key] = dict(self.init_intData)

                    try:
                        self.intData[idx - 1][key]
                    except SystemExit:
                        sys.exit(0)
                    except:
                        self.intData[idx - 1][key] = dict(self.init_intData)

                    if remainTime >= intervalEnable:
                        self.intData[idx - 1][key][
                            "preempted"
                        ] = intervalEnable
                    else:
                        self.intData[idx - 1][key]["preempted"] += remainTime

                    remainTime -= intervalEnable
                    if remainTime <= 0:
                        break

            # calculate total block usage in this interval #
            self.intData[index]["toTal"]["totalBr"] += self.intData[index][
                key
            ]["brUsage"]
            self.intData[index]["toTal"]["totalBw"] += self.intData[index][
                key
            ]["bwUsage"]

            """
            calculate total memory usage in this interval \
            except for core(swapper) threads because its already calculated
            """
            if key.startswith("0["):
                continue

            self.intData[index]["toTal"]["totalMem"] += self.intData[index][
                key
            ]["memUsage"]
            self.intData[index]["toTal"]["totalKmem"] += self.intData[index][
                key
            ]["kmemUsage"]

    def initThreadData(self):
        self.threadData = {}
        self.irqData = {}
        self.wqData = {}
        self.ioData = {}
        self.reclaimData = {}
        self.pageTable = {}
        self.kmemTable = {}
        self.blockTable = [{}, {}, {}]
        self.fsTable = [{}, {}, {}]
        self.fsData = [[], []]
        self.f2fsTable = {}
        self.f2fsData = []
        self.moduleData = []
        self.freqData = []
        self.intData = []
        self.depData = []
        self.sigData = []
        self.lmkData = []
        self.lockTable = {}
        self.flockData = []
        self.futexData = []
        self.customEventData = []
        self.userEventData = []
        self.kernelEventData = []
        self.syscallData = []
        self.binderData = []
        self.lastJob = {}
        self.preemptData = []
        self.suspendData = []
        self.markData = []
        self.consoleData = []
        self.statData = {}
        self.binderTranData = {}
        self.binderServerData = {}
        self.binderCliData = {}
        self.openData = []

        self.customEventInfo = {}
        self.userEventInfo = {}
        self.kernelEventInfo = {}

        self.customInfo = {}
        self.userInfo = {}
        self.kernelInfo = {}

        self.stopFlag = False
        self.totalTime = 0
        self.totalTimeOld = 0
        self.cxtSwitch = 0
        self.nrNewTask = 0
        self.thisInterval = 0

        self.finishTime = "0"
        self.lastCore = "0"
        self.lastTidPerCore = {}
        self.lastEvent = "0"

        # check custom interval #
        if (
            not "TRIM" in SysMgr.environList
            or hasattr(self, "trimStart")
            or hasattr(self, "trimStop")
        ):
            return

        # set custom interval #
        try:
            start, end = SysMgr.environList["TRIM"][0].split(":")
            if start.strip():
                self.trimStart = float(start.strip())
            if end.strip():
                self.trimStop = float(end.strip())
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to parse TRIM variable", True)
            sys.exit(-1)

    def handleUserEvent(self, event, time):
        # initialize TaskAnalyzer data #
        if event == "START":
            self.initThreadData()

            SysMgr.startTime = time

            # initialize preempt thread list #
            for index in SysMgr.preemptGroup:
                self.preemptData.append([False, {}, 0.0, 0, 0.0])
        # finish data processing #
        elif event == "STOP":
            SysMgr.totalLine = SysMgr.curLine
            self.finishTime = time
            self.stopFlag = True
        # restart data processing #
        elif event == "RESTART":
            # backup data #
            for item in dir(self):
                # skip some objects #
                if (
                    item.startswith("_")
                    or item.startswith("init_")
                    or item.startswith("backupData")
                ):
                    continue

                # skip callable objects #
                obj = getattr(self, item)
                if hasattr(obj, "__call__"):
                    continue

                self.backupData[item] = obj

            startTime = float(SysMgr.startTime)
            self.totalTimeOld = round(float(time) - startTime, 7)

            self.initThreadData()

            SysMgr.startTime = time
        # save mark event #
        elif event == "MARK":
            self.markData.append(time)

        # add event #
        EventAnalyzer.addEvent(time, event)

    def addTimelineData(self, group, text, task, state, start, end):
        # convert group id from core to task #
        if "PERTASK" in SysMgr.environList:
            if not task in self.timelineIdx:
                self.timelineIdx[task] = task
            group = self.timelineIdx[task]

        # add timeline data #
        self.timelineData["segments"].append(
            {
                "group": group,
                "text": text,
                "id": task,
                "state": state,
                "time_start": start,
                "time_end": end,
            }
        )

    def parse(self, string):
        def _printEventWarning(func):
            SysMgr.printWarn(
                "failed to recognize '%s' event at line %d"
                % (func, SysMgr.curLine)
            )

        SysMgr.curLine += 1

        m = SysMgr.getTraceItem(string)
        if not m:
            # handle modified type of event #
            m = SysMgr.getTraceItem(string)
            if m:
                d = m.groupdict()
                comm = d["comm"]
                core = str(long(d["core"]))
                func = d["func"]
                etc = d["etc"]
                time = d["time"]

                if "tracing_mark_write" in func:
                    m = re.match(r"^.+EVENT_(?P<event>\S+)", etc)
                    if m:
                        d = m.groupdict()

                        self.handleUserEvent(d["event"], time)
            return

        # get thread info #
        d = m.groupdict()
        comm = d["comm"]
        core = str(long(d["core"]))
        if d["func"][-1] == ":":
            func = d["func"][:-1]
        else:
            func = d["func"]
        etc = d["etc"]
        time = d["time"]
        ftime = float(time)
        stime = float(SysMgr.startTime)
        allTime = ftime - stime

        SysMgr.logSize += len(string)

        # set time for trim #
        if "ABSTIME" in SysMgr.environList:
            checkTime = ftime
        else:
            checkTime = allTime

        # check trim range #
        if (
            hasattr(self, "trimStart")
            and self.trimStart > 0
            and checkTime < self.trimStart
        ):
            return time
        elif (
            hasattr(self, "trimStop")
            and self.trimStop > 0
            and checkTime > self.trimStop
        ):
            return time

        # check skip condition #
        if (
            SysMgr.perCoreList
            and long(core) not in SysMgr.perCoreList
            and (func != "console" and func != "tracing_mark_write")
        ):
            return time
        elif (
            SysMgr.countEnable
            and SysMgr.repeatCnt * SysMgr.intervalEnable <= allTime
        ):
            self.stopFlag = True
            return time

        self.lastCore = core
        self.lastEvent = func

        if SysMgr.maxCore < long(core):
            SysMgr.maxCore = long(core)

        # make core ID #
        coreId = "0[%s]" % core
        if long(d["thread"]) == 0:
            thread = coreId
            comm = comm.replace("<idle>", "swapper/" + core)
        else:
            thread = d["thread"]

        # group by tgid #
        if "PROCGROUP" in SysMgr.environList:
            thread = d.get("tgid", thread)

        # update comm #
        if comm == "<...>":
            if thread in SysMgr.commCache:
                comm = SysMgr.commCache[thread]
            elif thread in self.threadData and self.threadData[thread]["comm"]:
                comm = self.threadData[thread]["comm"]

        # make core thread entity in advance for total irq per core #
        try:
            self.threadData[coreId]
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.nrCore += 1
            self.threadData[coreId] = dict(self.init_threadData)
            self.threadData[coreId]["comm"] = "swapper/%s" % core

        # make thread entity #
        self.threadData.setdefault(thread, dict(self.init_threadData))
        if comm[0] != "<":
            self.threadData[thread]["comm"] = comm

        # update last core #
        self.threadData[thread]["lastCore"] = core

        # define shortcut variable #
        threadData = self.threadData[thread]

        # set tgid #
        tgid = d.get("tgid", "-")
        try:
            if tgid == "0" or tgid.startswith("-"):
                raise Exception("no tgid")
            tgid = threadData["tgid"] = tgid
        except:
            try:
                tgid = threadData["tgid"] = SysMgr.savedProcTree[thread]
            except:
                tgid = "N/A"

        # calculate usage of threads had been running longer than periodic interval #
        if SysMgr.intervalEnable > 0:
            for key, value in sorted(self.lastTidPerCore.items()):
                try:
                    coreId = "0[%s]" % key
                    tid = self.lastTidPerCore[key]

                    # check CPU idle status #
                    if self.threadData[coreId]["lastStatus"] == "R":
                        self.threadData[coreId]["usage"] += (
                            ftime - self.threadData[coreId]["start"]
                        )
                        self.threadData[coreId]["start"] = ftime
                        continue

                    # check status of thread running on this core #
                    if self.threadData[tid]["lastStatus"] != "R":
                        continue

                    usage = ftime - float(self.threadData[tid]["start"])
                    if usage > allTime:
                        usage = allTime

                    self.threadData[tid]["usage"] += usage
                    self.threadData[tid]["start"] = ftime
                except SystemExit:
                    sys.exit(0)
                except:
                    continue

        # calculate usage of threads in interval #
        self.handleIntData(time)

        # define flag for special events #
        handleSpecialEvents = False

        if func == "sched_switch":
            m = re.match(
                (
                    r"^\s*prev_comm=(?P<prev_comm>.*)\s+"
                    r"prev_pid=(?P<prev_pid>[0-9]+)\s+"
                    r"prev_prio=(?P<prev_prio>\S+)\s+"
                    r"prev_state=(?P<prev_state>\S+)\s+==>\s+"
                    r"next_comm=(?P<next_comm>.*)\s+"
                    r"next_pid=(?P<next_pid>[0-9]+)\s+"
                    r"next_prio=(?P<next_prio>\S+)"
                ),
                etc,
            )
            if not m:
                _printEventWarning(func)
                return time

            d = m.groupdict()

            SysMgr.cpuEnable = True

            self.cxtSwitch += 1

            """
            # proc states in TASK_REPORT #
            R: running",      /* 0x00 */
            S: sleeping",     /* 0x01 */
            D: disk sleep",   /* 0x02 */
            T: stopped",      /* 0x04 */
            t: tracing stop", /* 0x08 */
            X: dead",     /* 0x10 */
            Z: zombie",       /* 0x20 */
            P: parked",       /* 0x40 */
            I: idle",     /* 0x80 */

            # kernel states #
            R: Runnable
            S: Sleeping
            D: Uninterruptible Sleep
            T: Stopped
            t: Traced
            X: Exit (Dead)
            Z: Exit (Zombie)
            x: Task Dead
            I: Task Dead
            K: Wake Kill
            W: Waking
            P: Parked
            N: No Load
            +: (Preempted)
            """

            prev_comm = d["prev_comm"]
            prev_pid = d["prev_pid"]
            prev_id = prev_pid

            # update prev comm #
            if prev_comm == "<...>" and prev_id in SysMgr.commCache:
                prev_comm = SysMgr.commCache[prev_id]

            coreId = "0[%s]" % core

            if long(d["prev_pid"]) == 0:
                prev_id = coreId
            else:
                prev_id = prev_pid

            next_comm = d["next_comm"]
            next_pid = d["next_pid"]

            if long(d["next_pid"]) == 0:
                next_id = coreId
            else:
                next_id = next_pid

            # check missed sched data #
            if (
                core in self.lastTidPerCore
                and self.lastTidPerCore[core] != prev_id
            ):
                SysMgr.printWarn(
                    "no context for %s(%s) on Core/%s"
                    % (prev_comm, prev_id, core)
                )
                self.nrSchedLoss += 1

            self.lastTidPerCore[core] = next_id

            # update prev comm #
            if next_comm == "<...>" and next_id in SysMgr.commCache:
                next_comm = SysMgr.commCache[next_id]

            # check CPU wakeup #
            if self.threadData[coreId]["lastOff"] > 0:
                diff = ftime - self.threadData[coreId]["lastOff"]
                self.threadData[coreId]["offTime"] += diff
                self.threadData[coreId]["lastOff"] = 0

            # initialize thread data #
            self.threadData.setdefault(prev_id, dict(self.init_threadData))
            self.threadData.setdefault(next_id, dict(self.init_threadData))

            # initialize core data #
            try:
                self.threadData[coreId]
            except:
                self.threadData[coreId] = dict(self.init_threadData)
                self.threadData[coreId]["comm"] = "swapper/%s" % core

            if (
                self.wakeupData["valid"] > 0
                and self.wakeupData["tid"] == prev_id
            ):
                self.wakeupData["valid"] -= 1

            # update comm #
            self.threadData[prev_id]["comm"] = prev_comm
            self.threadData[next_id]["comm"] = next_comm

            # update anonymous comm #
            if self.threadData[prev_id]["comm"] == "<...>":
                self.threadData[prev_id]["comm"] = prev_comm
            if self.threadData[next_id]["comm"] == "<...>":
                self.threadData[next_id]["comm"] = next_comm

            # update status #
            prev_state = d["prev_state"][0]

            # check event loss #
            if (
                self.threadData[prev_id]["lastStatus"] != "R"
                and self.threadData[coreId]["coreSchedCnt"] > 0
            ):
                self.threadData[prev_id]["start"] = ftime

            # write current time #
            self.threadData[prev_id]["stop"] = ftime
            self.threadData[next_id]["start"] = ftime
            self.threadData[next_id]["waitStartAsParent"] = 0.0

            # define time #
            prev_start = self.threadData[prev_id]["start"]
            prev_stop = self.threadData[prev_id]["stop"]
            next_start = self.threadData[next_id]["start"]
            next_stop = self.threadData[next_id]["stop"]

            # update the CPU scheduling priority for thread to highest one #
            prevPri = self.threadData[prev_id]["pri"]
            if prevPri == "?" or long(prevPri) > long(d["prev_prio"]):
                self.threadData[prev_id]["pri"] = d["prev_prio"]
            nextPri = self.threadData[next_id]["pri"]
            if nextPri == "?" or long(nextPri) > long(d["next_prio"]):
                self.threadData[next_id]["pri"] = d["next_prio"]

            # update CPU time by futex #
            if self.threadData[prev_id]["ftxEnter"] > 0:
                fstart = self.threadData[prev_id]["ftxEnter"]

                if prev_start > fstart:
                    tstart = prev_start
                else:
                    tstart = fstart

                fstime = ftime - tstart
                self.threadData[prev_id]["ftxProcess"] += fstime
                self.threadData[prev_id]["ftxBlock"] = ftime
                self.threadData[prev_id]["ftxBlockCnt"] += 1

                opt = "{0:^24}".format("BLOCK")
                otype = "{0:<10}".format("ENT")
                fstime = "%.6f" % fstime
                self.futexData.append(
                    [prev_id, time, core, opt, otype, fstime, "", "", ""]
                )

            # save block time with lock by futex #
            try:
                if self.threadData[prev_id]["futexObj"]:
                    self.threadData[prev_id]["ftxLBlock"] = ftime
                    self.threadData[prev_id]["ftxLSwitch"] += 1

                    # remove previous BLOCK enter event #
                    if self.futexData and self.futexData[-1][1] == time:
                        del self.futexData[-1]

                    opt = "{0:^24}".format("LOCK_BLOCK")
                    otype = "{0:<10}".format("ENT")
                    locks = ", ".join(self.threadData[prev_id]["futexObj"])
                    self.futexData.append(
                        [prev_id, time, core, opt, otype, "", locks, "", ""]
                    )
            except SystemExit:
                sys.exit(0)
            except:
                pass

            # update total block time with lock by futex #
            if self.threadData[next_id]["ftxLBlock"] > 0:
                cstop = self.threadData[next_id]["ftxLBlock"]
                btime = ftime - cstop
                self.threadData[next_id]["ftxLBlockTotal"] += btime
                self.threadData[next_id]["ftxLBlock"] = 0

                opt = "{0:^24}".format("LOCK_BLOCK")
                otype = "{0:>10}".format("RET")
                try:
                    locks = ", ".join(self.threadData[next_id]["futexObj"])
                except:
                    locks = ""
                btime = "%.6f" % btime
                self.futexData.append(
                    [next_id, time, core, opt, otype, btime, locks, "", ""]
                )

            # save block time by futex #
            if self.threadData[next_id]["ftxBlock"] > 0:
                cstop = self.threadData[next_id]["ftxBlock"]
                btime = ftime - cstop
                self.threadData[next_id]["ftxBlockTotal"] += btime
                self.threadData[next_id]["ftxBlock"] = 0

                opt = "{0:^24}".format("BLOCK")
                otype = "{0:>10}".format("RET")
                btime = "%.6f" % btime
                self.futexData.append(
                    [next_id, time, core, opt, otype, btime, "", "", ""]
                )

            # calculate running time of previous thread #
            diff = 0
            if prev_start == 0:
                """calculate runtime of previous thread started
                before starting to profile"""
                if self.threadData[coreId]["coreSchedCnt"] == 0:
                    if hasattr(self, "trimStart"):
                        prev_start = stime + self.trimStart
                        diff = prev_stop - prev_start
                    else:
                        diff = allTime
                    self.threadData[prev_id]["usage"] = diff
                # it is possible that log was loss #
                else:
                    pass
            else:
                diff = prev_stop - prev_start
                if diff >= 0:
                    self.threadData[prev_id]["usage"] += diff

                    if self.threadData[prev_id]["maxRuntime"] < diff:
                        self.threadData[prev_id]["maxRuntime"] = diff
                else:
                    SysMgr.printWarn(
                        "usage time of %s(%s) is negative(%f) at line %d"
                        % (prev_comm, prev_id, diff, SysMgr.curLine)
                    )

            # add timeline stats #
            if not prev_id.startswith("0["):
                # add runtime to list for histogram #
                self.statData.setdefault("runtime", [])
                self.statData["runtime"].append(diff)

                # add timestamps to list for timeline #
                if prev_start == 0:
                    start_delta = 0
                else:
                    start_delta = long((float(prev_start) - stime) * 1000000)

                stop_delta = long((float(prev_stop) - stime) * 1000000)

                # update comm #
                if prev_comm == "<...>":
                    tcomm = "??"
                else:
                    tcomm = prev_comm

                # make target info #
                ptinfo, thd = TaskAnalyzer.getProcThrInfo(tgid, prev_id, tcomm)

                # add timeline data #
                self.addTimelineData(
                    long(core),
                    ptinfo,
                    thd,
                    prev_state,
                    start_delta,
                    stop_delta,
                )

            if diff > long(SysMgr.intervalEnable):
                self.threadData[prev_id]["longRunCore"] = long(core)

            # update core info #
            self.threadData[coreId]["coreSchedCnt"] += 1
            self.lastTidPerCore[core] = next_id

            # calculate preempted time of threads blocked #
            for index, value in enumerate(SysMgr.preemptGroup):
                preemptData = self.preemptData[index]
                if preemptData[0] and preemptData[3] == core:
                    try:
                        preemptData[1][prev_id]
                    except:
                        preemptData[1][prev_id] = dict(self.init_preemptData)

                    preemptData[1][prev_id]["usage"] += diff
                    preemptData[4] += diff

            # set sched status #
            if prev_state == "R":
                # except for core sched event #
                if prev_id != coreId:
                    self.threadData[prev_id]["preempted"] += 1
                    self.threadData[coreId]["preempted"] += 1

                # except for core sched event #
                if next_id != coreId:
                    self.threadData[next_id]["preemption"] += 1
                    self.threadData[coreId]["preemption"] += 1

                self.threadData[prev_id]["lastStatus"] = "P"

                # enable preempted bit #
                try:
                    index = SysMgr.preemptGroup.index(prev_id)
                except:
                    index = -1

                if index >= 0:
                    self.preemptData[index][0] = True
                    try:
                        self.preemptData[index][1][next_id]
                    except:
                        self.preemptData[index][1][next_id] = dict(
                            self.init_preemptData
                        )

                    self.preemptData[index][2] = ftime
                    self.preemptData[index][3] = core

            elif prev_state in ("S", "D", "t", "T", "I", "x", "X", "Z"):
                # increase yield count except for core sched event #
                if prev_id != coreId:
                    self.threadData[prev_id]["yield"] += 1
                    self.threadData[coreId]["yield"] += 1

                # update status #
                if prev_state == "D":
                    self.threadData[prev_id]["lastStatus"] = "D"
                else:
                    self.threadData[prev_id]["stop"] = 0
                    self.threadData[prev_id]["lastStatus"] = "S"

            else:
                self.threadData[prev_id]["stop"] = 0
                self.threadData[prev_id]["lastStatus"] = prev_state

                SysMgr.printWarn(
                    "%s(%s)'s sched state is '%s' at line %d"
                    % (prev_comm, prev_id, prev_state, SysMgr.curLine)
                )

            # calculate sched latency of next thread #
            if (
                not next_id.startswith("0[")
                and self.threadData[next_id]["schedReady"] > 0
            ):
                schedLat = ftime - self.threadData[next_id]["schedReady"]
                self.threadData[next_id]["schedLatency"] += schedLat
                self.threadData[coreId]["schedLatency"] += schedLat
                self.threadData[next_id]["schedReady"] = 0

                self.statData.setdefault("schedlat", [])
                self.statData["schedlat"].append(schedLat)

            # calculate preempted time of next thread #
            if next_stop == 0:
                # no stop time of next thread because of some reasons #
                pass
            # set sched status of next thread #
            elif self.threadData[next_id]["lastStatus"] == "D":
                blockTime = next_start - next_stop
                self.threadData[next_id]["schedBlock"] += blockTime
                self.threadData[coreId]["schedBlock"] += blockTime

                # calculate sched block of next thread #
                if not next_id.startswith("0["):
                    self.statData.setdefault("schedblock", [])
                    self.statData["schedblock"].append(blockTime)

            # set sched status of next thread #
            elif self.threadData[next_id]["lastStatus"] == "P":
                preemptedTime = next_start - next_stop

                if preemptedTime >= 0:
                    self.threadData[next_id]["cpuWait"] += preemptedTime
                else:
                    SysMgr.printWarn(
                        "preempted time of %s(%s) is %f at line %s"
                        % (
                            next_comm,
                            next_id,
                            preemptedTime,
                            UtilMgr.convNum(SysMgr.curLine),
                        )
                    )

                if preemptedTime > self.threadData[next_id]["maxPreempted"]:
                    self.threadData[next_id]["maxPreempted"] = preemptedTime

                try:
                    nextIdx = SysMgr.preemptGroup.index(next_id)
                    self.preemptData[nextIdx][0] = False
                except:
                    pass

                # add preempted time to list for histogram #
                if not next_id.startswith("0["):
                    self.statData.setdefault("prttime", [])
                    self.statData["prttime"].append(preemptedTime)

            self.threadData[next_id]["lastStatus"] = "R"

        elif func == "irq_handler_entry":
            m = re.match(r"^\s*irq=(?P<irq>[0-9]+)\s+name=(?P<name>\S+)", etc)
            if not m:
                _printEventWarning(func)
                return time

            d = m.groupdict()

            irqId = "irq/%s" % (d["irq"])

            # make irq list #
            try:
                self.irqData[irqId]
            except:
                self.irqData[irqId] = dict(self.init_irqData)
                self.irqData[irqId]["name"] = {}

            self.irqData[irqId]["name"].setdefault(d["name"], 0)

            # make per-thread irq list #
            threadData.setdefault("irqList", {})
            threadData["irqList"].setdefault(irqId, dict(self.init_irqData))
            threadData["irqList"][irqId]["name"] = d["name"]

            # update period per thread #
            if threadData["irqList"][irqId]["start"] > 0:
                diff = ftime - threadData["irqList"][irqId]["start"]
                if (
                    diff > threadData["irqList"][irqId]["maxPeriod"]
                    or threadData["irqList"][irqId]["maxPeriod"] <= 0
                ):
                    threadData["irqList"][irqId]["maxPeriod"] = diff
                if (
                    diff < threadData["irqList"][irqId]["minPeriod"]
                    or threadData["irqList"][irqId]["minPeriod"] <= 0
                ):
                    threadData["irqList"][irqId]["minPeriod"] = diff

            # update period #
            if self.irqData[irqId]["start"] > 0:
                diff = ftime - self.irqData[irqId]["start"]
                if (
                    diff > self.irqData[irqId]["maxPeriod"]
                    or self.irqData[irqId]["maxPeriod"] <= 0
                ):
                    self.irqData[irqId]["maxPeriod"] = diff
                if (
                    diff < self.irqData[irqId]["minPeriod"]
                    or self.irqData[irqId]["minPeriod"] <= 0
                ):
                    self.irqData[irqId]["minPeriod"] = diff

            self.irqData[irqId]["start"] = ftime
            self.irqData[irqId]["count"] += 1
            threadData["irqList"][irqId]["start"] = ftime
            threadData["irqList"][irqId]["count"] += 1

        elif func == "irq_handler_exit":
            m = re.match(r"^\s*irq=(?P<irq>[0-9]+)\s+ret=(?P<return>\S+)", etc)
            if not m:
                _printEventWarning(func)
                return time

            d = m.groupdict()

            irqId = "irq/%s" % (d["irq"])

            # make list #
            try:
                self.irqData[irqId]
                threadData["irqList"][irqId]
            except:
                return time

            # update usage #
            if threadData["irqList"][irqId]["start"] > 0:
                diff = ftime - threadData["irqList"][irqId]["start"]
                threadData["irqList"][irqId]["usage"] += diff
                threadData["irq"] += diff
                self.irqData[irqId]["usage"] += diff

                # add CPU usage of this thread to core usage #
                if coreId != thread:
                    self.threadData[coreId]["irq"] += diff

                # update period per thread #
                if (
                    diff > threadData["irqList"][irqId]["max"]
                    or threadData["irqList"][irqId]["max"] <= 0
                ):
                    threadData["irqList"][irqId]["max"] = diff
                if (
                    diff < threadData["irqList"][irqId]["min"]
                    or threadData["irqList"][irqId]["min"] <= 0
                ):
                    threadData["irqList"][irqId]["min"] = diff

                threadData["irqList"][irqId]["start"] = 0

            # update period #
            if self.irqData[irqId]["start"] > 0:
                diff = ftime - self.irqData[irqId]["start"]
                if (
                    diff > self.irqData[irqId]["max"]
                    or self.irqData[irqId]["max"] <= 0
                ):
                    self.irqData[irqId]["max"] = diff
                if (
                    diff < self.irqData[irqId]["min"]
                    or self.irqData[irqId]["min"] <= 0
                ):
                    self.irqData[irqId]["min"] = diff

                self.irqData[irqId]["start"] = 0

        elif func == "softirq_entry":
            m = re.match(
                r"^\s*vec=(?P<vector>[0-9]+)\s+\[action=(?P<action>\S+)\]", etc
            )
            if not m:
                _printEventWarning(func)
                return time

            d = m.groupdict()

            irqId = "softirq/%s" % (d["vector"])

            # make irq list #
            try:
                self.irqData[irqId]
            except:
                self.irqData[irqId] = dict(self.init_irqData)
                self.irqData[irqId]["name"] = {}

            self.irqData[irqId]["name"].setdefault(d["action"], 0)

            # make per-thread irq list #
            try:
                threadData["irqList"][irqId]
            except:
                threadData["irqList"] = {}
            try:
                threadData["irqList"][irqId]
            except:
                threadData["irqList"][irqId] = dict(self.init_irqData)
                threadData["irqList"][irqId]["name"] = d["action"]

            # update period per thread #
            if threadData["irqList"][irqId]["start"] > 0:
                diff = ftime - threadData["irqList"][irqId]["start"]
                if (
                    diff > threadData["irqList"][irqId]["maxPeriod"]
                    or threadData["irqList"][irqId]["maxPeriod"] <= 0
                ):
                    threadData["irqList"][irqId]["maxPeriod"] = diff
                if (
                    diff < threadData["irqList"][irqId]["minPeriod"]
                    or threadData["irqList"][irqId]["minPeriod"] <= 0
                ):
                    threadData["irqList"][irqId]["minPeriod"] = diff

            # update period #
            if self.irqData[irqId]["start"] > 0:
                diff = ftime - self.irqData[irqId]["start"]
                if (
                    diff > self.irqData[irqId]["maxPeriod"]
                    or self.irqData[irqId]["maxPeriod"] <= 0
                ):
                    self.irqData[irqId]["maxPeriod"] = diff
                if (
                    diff < self.irqData[irqId]["minPeriod"]
                    or self.irqData[irqId]["minPeriod"] <= 0
                ):
                    self.irqData[irqId]["minPeriod"] = diff

            self.irqData[irqId]["start"] = ftime
            self.irqData[irqId]["count"] += 1
            threadData["irqList"][irqId]["start"] = ftime
            threadData["irqList"][irqId]["count"] += 1

        elif func == "softirq_exit":
            m = re.match(
                r"^\s*vec=(?P<vector>[0-9]+)\s+\[action=(?P<action>\S+)\]", etc
            )
            if not m:
                _printEventWarning(func)
                return time

            d = m.groupdict()

            irqId = "softirq/%s" % (d["vector"])

            # make list #
            try:
                self.irqData[irqId]
                threadData["irqList"][irqId]
            except:
                return time

            # update usage #
            if threadData["irqList"][irqId]["start"] > 0:
                diff = ftime - threadData["irqList"][irqId]["start"]
                threadData["irqList"][irqId]["usage"] += diff
                threadData["irq"] += diff
                self.irqData[irqId]["usage"] += diff

                # add CPU usage of this thread to core usage #
                if coreId != thread:
                    self.threadData[coreId]["irq"] += diff

                # update period per thread #
                if (
                    diff > threadData["irqList"][irqId]["max"]
                    or threadData["irqList"][irqId]["max"] <= 0
                ):
                    threadData["irqList"][irqId]["max"] = diff
                if (
                    diff < threadData["irqList"][irqId]["min"]
                    or threadData["irqList"][irqId]["min"] <= 0
                ):
                    threadData["irqList"][irqId]["min"] = diff

                threadData["irqList"][irqId]["start"] = 0

            # update period #
            if self.irqData[irqId]["start"] > 0:
                diff = ftime - self.irqData[irqId]["start"]
                if (
                    diff > self.irqData[irqId]["max"]
                    or self.irqData[irqId]["max"] <= 0
                ):
                    self.irqData[irqId]["max"] = diff
                if (
                    diff < self.irqData[irqId]["min"]
                    or self.irqData[irqId]["min"] <= 0
                ):
                    self.irqData[irqId]["min"] = diff

                self.irqData[irqId]["start"] = 0

        elif func == "sched_migrate_task":
            m = re.match(
                (
                    r"^\s*comm=(?P<comm>.*)\s+pid=(?P<pid>[0-9]+)\s+"
                    r"prio=(?P<prio>[0-9]+)\s+orig_cpu=(?P<orig_cpu>[0-9]+)\s+"
                    r"dest_cpu=(?P<dest_cpu>[0-9]+)"
                ),
                etc,
            )
            if not m:
                _printEventWarning(func)
                return time

            d = m.groupdict()

            pid = d["pid"]

            self.threadData.setdefault(pid, dict(self.init_threadData))
            self.threadData[pid]["comm"] = d["comm"]
            self.threadData[pid]["migrate"] += 1

            self.threadData.setdefault(coreId, dict(self.init_threadData))
            self.threadData[coreId]["migrate"] += 1

            # update core data for preempted info #
            try:
                index = SysMgr.preemptGroup.index(thread)
            except:
                index = -1

            if index >= 0:
                self.preemptData[index][3] = core

        elif func == "mm_page_alloc":
            m = re.match(
                (
                    r"^\s*page=\s*(?P<page>\S+)\s+pfn=(?P<pfn>[0-9]+)\s+"
                    r"order=(?P<order>[0-9]+)\s+"
                    r"migratetype=(?P<mt>[0-9]+)\s+gfp_flags=(?P<flags>\S+)"
                ),
                etc,
            )
            if not m:
                _printEventWarning(func)
                return time

            d = m.groupdict()

            SysMgr.memEnable = True

            # check whether it is huge page #
            if d["page"] == "(null)":
                page = "huge"
            else:
                page = d["page"]

            pfn = long(d["pfn"])
            flags = d["flags"]
            order = long(d["order"])
            nr = pow(2, order)

            # register page order #
            threadData.setdefault("orderPages", {})
            threadData["orderPages"].setdefault(order, 0)
            threadData["orderPages"][order] += 1
            self.allocPageData.setdefault(order, 0)
            self.allocPageData[order] += 1

            # accumulate pages allocated #
            threadData["nrAllocPages"] += nr
            self.threadData[coreId]["nrAllocPages"] += nr
            threadData["nrPages"] += nr
            self.threadData[coreId]["nrPages"] += nr

            if "NOFS" in flags or "GFP_WRITE" in flags or "0x1000000" in flags:
                pageType = "CACHE"
                threadData["cachePages"] += nr
                self.threadData[coreId]["cachePages"] += nr
            elif "USER" in flags:
                pageType = "USER"
                threadData["userPages"] += nr
                self.threadData[coreId]["userPages"] += nr
            else:
                pageType = "KERNEL"
                threadData["kernelPages"] += nr
                self.threadData[coreId]["kernelPages"] += nr

            # make PTE in page table #
            for cnt in xrange(nr):
                pfnv = pfn + cnt

                try:
                    # this allocated page is not freed #
                    if self.pageTable[pfnv] == {}:
                        raise Exception("double page allocation")
                    else:
                        threadData["nrPages"] -= 1
                        self.threadData[coreId]["nrPages"] -= 1
                except:
                    self.pageTable[pfnv] = dict(self.init_pageData)

                self.pageTable[pfnv]["tid"] = thread
                self.pageTable[pfnv]["page"] = page
                self.pageTable[pfnv]["flags"] = flags
                self.pageTable[pfnv]["type"] = pageType
                self.pageTable[pfnv]["time"] = time

        elif func in ("mm_page_free", "mm_page_free_direct"):
            m = re.match(
                (
                    r"^\s*page=(?P<page>\S+)\s+pfn=(?P<pfn>[0-9]+)"
                    r"\s+order=(?P<order>[0-9]+)"
                ),
                etc,
            )
            if not m:
                _printEventWarning(func)
                return time

            d = m.groupdict()

            SysMgr.memEnable = True

            page = d["page"]
            pfn = long(d["pfn"])
            order = long(d["order"])
            nr = pow(2, order)

            for cnt in xrange(nr):
                pfnv = pfn + cnt

                try:
                    owner = self.pageTable[pfnv]["tid"]
                    self.threadData[owner]["nrPages"] -= 1
                    self.threadData[coreId]["nrPages"] -= 1

                    if thread != owner:
                        self.threadData[owner]["reclaimedPages"] += 1
                        self.threadData[coreId]["reclaimedPages"] += 1

                    if self.pageTable[pfnv]["type"] == "CACHE":
                        self.threadData[owner]["cachePages"] -= 1
                        self.threadData[coreId]["cachePages"] -= 1
                    elif self.pageTable[pfnv]["type"] == "USER":
                        self.threadData[owner]["userPages"] -= 1
                        self.threadData[coreId]["userPages"] -= 1
                    elif self.pageTable[pfnv]["type"] == "KERNEL":
                        self.threadData[owner]["kernelPages"] -= 1
                        self.threadData[coreId]["kernelPages"] -= 1

                    self.pageTable.pop(pfnv)
                except:
                    # this page is allocated before starting profile #
                    threadData["anonReclaimedPages"] += 1
                    self.threadData[coreId]["anonReclaimedPages"] += 1

        elif func == "mm_filemap_delete_from_page_cache":
            m = re.match(
                (
                    r"^\s*dev (?P<major>[0-9]+):(?P<minor>[0-9]+) .+"
                    r"page=(?P<page>\S+)\s+pfn=(?P<pfn>[0-9]+)"
                ),
                etc,
            )
            if not m:
                _printEventWarning(func)
                return time

            d = m.groupdict()

            SysMgr.memEnable = True

            pfn = long(d["pfn"])

            try:
                owner = self.pageTable[pfn]["tid"]

                # attribute of page is changed to file #
                if self.pageTable[pfn]["type"] == "USER":
                    self.threadData[owner]["userPages"] -= 1
                    self.threadData[coreId]["userPages"] -= 1
                    self.threadData[owner]["cachePages"] += 1
                    self.threadData[coreId]["cachePages"] += 1
                elif self.pageTable[pfn]["type"] == "KERNEL":
                    self.threadData[owner]["kernelPages"] -= 1
                    self.threadData[coreId]["kernelPages"] -= 1
                    self.threadData[owner]["cachePages"] += 1
                    self.threadData[coreId]["cachePages"] += 1

                self.pageTable[pfn]["type"] = "CACHE"
            except:
                return time

        elif func == "kmalloc":
            m = re.match(
                (
                    r"^\s*call_site=(?P<caller>\S+).*ptr=(?P<ptr>\S+)\s+"
                    r"bytes_req=(?P<req>[0-9]+)\s+"
                    r"bytes_alloc=(?P<alloc>[0-9]+)\s+"
                    r"gfp_flags=(?P<flags>\S+)"
                ),
                etc,
            )
            if not m:
                _printEventWarning(func)
                return time

            d = m.groupdict()

            SysMgr.memEnable = True

            caller = d["caller"]
            ptr = d["ptr"]
            req = long(d["req"])
            alloc = long(d["alloc"])

            self.kmemTable.setdefault(ptr, dict(self.init_kmallocData))

            self.kmemTable[ptr]["tid"] = thread
            self.kmemTable[ptr]["caller"] = caller
            self.kmemTable[ptr]["req"] = req
            self.kmemTable[ptr]["alloc"] = alloc
            self.kmemTable[ptr]["waste"] = alloc - req
            self.kmemTable[ptr]["core"] = coreId

            threadData["remainKmem"] += alloc
            threadData["wasteKmem"] += alloc - req
            self.threadData[coreId]["remainKmem"] += alloc
            self.threadData[coreId]["wasteKmem"] += alloc - req

        elif func == "kfree":
            m = re.match(
                r"^\s*call_site=(?P<caller>\S+)\s+ptr=\s*(?P<ptr>\S+)", etc
            )
            if not m:
                _printEventWarning(func)
                return time

            d = m.groupdict()

            SysMgr.memEnable = True

            caller = d["caller"]
            ptr = d["ptr"]

            try:
                pageObj = self.kmemTable[ptr]
                self.threadData[pageObj["tid"]]["remainKmem"] -= pageObj[
                    "alloc"
                ]

                self.threadData[pageObj["core"]]["remainKmem"] -= pageObj[
                    "alloc"
                ]

                self.threadData[pageObj["tid"]]["wasteKmem"] -= pageObj[
                    "waste"
                ]

                self.threadData[pageObj["core"]]["wasteKmem"] -= pageObj[
                    "waste"
                ]

                self.kmemTable.pop(ptr)
            except:
                """
                this allocated object is not logged or \
                this object is allocated before starting profile
                """
                return time

        elif func in ("sched_wakeup", "sched_wakeup_new"):
            m = re.match(
                (
                    r"^\s*comm=(?P<comm>.*)\s+pid=(?P<pid>[0-9]+)\s+"
                    r"prio=(?P<prio>[0-9]+)\s+"
                ),
                etc,
            )
            if not m:
                _printEventWarning(func)
                return time

            d = m.groupdict()

            target_comm = d["comm"]
            pid = d["pid"]

            # update prev comm #
            if target_comm == "<...>" and pid in SysMgr.commCache:
                target_comm = SysMgr.commCache[pid]

            # skip self-wakeup #
            if thread == pid:
                return time

            self.threadData.setdefault(pid, dict(self.init_threadData))
            self.threadData[pid]["comm"] = target_comm
            self.threadData[pid]["schedReady"] = ftime

            if self.wakeupData["tid"] == "0":
                self.wakeupData["time"] = allTime
            elif thread[0] == "0" or pid == "0":
                return time
            elif self.wakeupData["valid"] > 0 and (
                self.wakeupData["from"] != self.wakeupData["tid"]
                or self.wakeupData["to"] != pid
            ):
                if (
                    self.wakeupData["valid"] == 1
                    and self.wakeupData["corrupt"] == "0"
                ):
                    try:
                        kicker = self.threadData[self.wakeupData["tid"]][
                            "comm"
                        ]
                    except:
                        kicker = "NULL"

                    kicker_pid = self.wakeupData["tid"]
                else:
                    kicker = threadData["comm"]
                    kicker_pid = thread

                ntime = round(allTime, 7)
                self.depData.append(
                    "\t%.3f/%.3f \t%40s(%7s) -> %40s(%7s) \t%s"
                    % (
                        ntime,
                        round(ntime - float(self.wakeupData["time"]), 7),
                        kicker,
                        kicker_pid,
                        target_comm,
                        pid,
                        "kick",
                    )
                )

                self.wakeupData["time"] = allTime
                self.wakeupData["from"] = self.wakeupData["tid"]
                self.wakeupData["to"] = pid

        elif func == "do_sys_open":
            m = re.match(r'^\s*"(?P<path>.*)" (?P<flags>.+) (?P<mode>.+)', etc)
            if not m:
                _printEventWarning(func)
                return time

            d = m.groupdict()

            self.openData.append(
                [allTime, thread, d["path"], d["flags"], d["mode"]]
            )

        elif func == "sys_enter":
            m = re.match(r"^\s*NR (?P<nr>[0-9]+) (?P<args>.+)", etc)
            if not m:
                _printEventWarning(func)
                return time

            d = m.groupdict()

            nr = long(d["nr"])
            nrstr = str(nr)
            args = d["args"]
            td = threadData

            # update futex lock stat #
            if nr == SysMgr.getNrSyscall("sys_futex"):
                n = re.match(
                    (
                        r"^\s*(?P<uaddr>\S+), (?P<op>\S+), "
                        r"(?P<val>\S+), (?P<timer>\S+),"
                    ),
                    d["args"],
                )
                if n:
                    l = n.groupdict()

                    FUTEX_CMD_MASK = ~(128 | 256)
                    # FUTEX_PRIVATE_FLAG: 128, FUTEX_CLOCK_REALTIME: 256 #
                    maskedOp = long(l["op"], base=16) & FUTEX_CMD_MASK

                    addr = l["uaddr"][1:]
                    flist = ConfigMgr.FUTEX_TYPE

                    try:
                        op = flist[maskedOp]
                    except:
                        op = l["op"]

                    # check recursive entry caused by log loss #
                    if td["ftxEnter"] > 0:
                        SysMgr.printWarn(
                            (
                                "failed to find return of %s for thread %s "
                                "at %s\n\tso report the results "
                                "may differ from actual"
                            )
                            % (td["ftxEnt"], thread, SysMgr.curLine)
                        )

                    # futex operation #
                    td["ftxEnt"] = op

                    # futex object address #
                    td["futexCandObj"] = addr

                    # try to lock #
                    if maskedOp == flist.index(
                        "FUTEX_LOCK_PI"
                    ) or maskedOp == flist.index("FUTEX_TRYLOCK_PI"):
                        td["ftxStat"] = "L"
                        td["ftxLockCnt"] += 1

                        # remove already unlocked futex #
                        try:
                            td["futexObj"].pop(addr, None)
                        except:
                            pass
                    # wait #
                    elif maskedOp in (
                        flist.index("FUTEX_WAIT"),
                        flist.index("FUTEX_WAIT_REQUEUE_PI"),
                        flist.index("FUTEX_WAIT_BITSET"),
                    ):
                        td["ftxStat"] = "W"
                        td["ftxWaitCnt"] += 1
                    # try to unlock #
                    elif maskedOp == flist.index("FUTEX_UNLOCK_PI"):
                        td["ftxStat"] = "U"
                    else:
                        td["ftxStat"] = "?"

                    td["ftxEnter"] = ftime
                    otype = "{0:<10}".format("ENT")
                    self.futexData.append(
                        [
                            thread,
                            time,
                            core,
                            op,
                            otype,
                            "",
                            addr,
                            l["val"],
                            l["timer"],
                        ]
                    )

            if self.wakeupData["tid"] == "0":
                self.wakeupData["time"] = allTime

            # write syscall #
            if nr == SysMgr.getNrSyscall("sys_write"):
                self.wakeupData["tid"] = thread
                self.wakeupData["nr"] = nrstr
                self.wakeupData["args"] = args

                if not (
                    self.wakeupData["valid"] > 0
                    and (
                        self.wakeupData["tid"] == thread
                        and self.wakeupData["from"] == comm
                    )
                ):
                    self.wakeupData["valid"] += 1

                    if self.wakeupData["valid"] > 1:
                        self.wakeupData["corrupt"] = "1"
                    else:
                        self.wakeupData["corrupt"] = "0"

            # register syscall #
            try:
                td["syscallInfo"]
            except:
                td["syscallInfo"] = {}
            try:
                td["syscallInfo"][nrstr]
            except:
                td["syscallInfo"][nrstr] = dict(self.init_syscallInfo)

            # save syscall info #
            td["nrSyscall"] += 1
            td["lastNrSyscall"] = nr
            td["syscallInfo"][nrstr]["count"] += 1
            td["syscallInfo"][nrstr]["last"] = ftime

            # save syscall history #
            if SysMgr.syscallList:
                try:
                    if not nr in SysMgr.syscallList:
                        raise Exception()

                    self.syscallData.append(
                        ["ENT", time, thread, core, nrstr, args]
                    )
                except SystemExit:
                    sys.exit(0)
                except:
                    pass
            else:
                self.syscallData.append(
                    ["ENT", time, thread, core, nrstr, args]
                )

        elif func == "sys_exit":
            m = re.match(r"^\s*NR (?P<nr>\S+) = (?P<ret>.+)", etc)
            if not m:
                _printEventWarning(func)
                return time

            d = m.groupdict()

            nr = long(d["nr"])
            nrstr = str(nr)
            ret = d["ret"]
            td = threadData

            # handle wrong syscall number #
            if nr < 0 <= td["lastNrSyscall"]:
                nr = td["lastNrSyscall"]

            # update futex lock stat #
            if nr == SysMgr.getNrSyscall("sys_futex"):
                lockEnter = td["ftxEnter"]
                lockStat = td["ftxStat"]

                # futex call status #
                if lockEnter > 0:
                    # elapsed time #
                    futexTime = ftime - lockEnter

                    if futexTime > td["ftxMax"]:
                        td["ftxMax"] = futexTime

                    td["ftxTotal"] += futexTime
                    td["ftxEnter"] = 0

                    # update CPU time by futex #
                    if td["start"] > lockEnter:
                        ctime = ftime - td["start"]
                        td["ftxProcess"] += ctime
                    elif td["ftxBlock"] == 0 == td["ftxLBlock"]:
                        ctime = ftime - lockEnter
                        td["ftxProcess"] += ctime

                    # handle lock object #
                    if lockStat in ("L", "U") and ret[0] == "0":
                        # target object #
                        try:
                            candObj = td["futexCandObj"]
                        except:
                            candObj = None

                        # lock context #
                        if lockStat == "L":
                            # register lock object #
                            try:
                                td["futexObj"][candObj] = ftime
                            except:
                                td["futexObj"] = {}
                                td["futexObj"][candObj] = ftime
                        # unlock context #
                        elif lockStat == "U":
                            # remove lock object #
                            try:
                                lockStart = td["futexObj"][candObj]
                                td["futexObj"].pop(candObj, None)
                            except:
                                lockStart = 0

                            # calculate lock time #
                            if lockStart > 0:
                                ltime = ftime - lockStart
                                td["ftxLock"] += ltime
                                if td["ftxLockMax"] < ltime:
                                    td["ftxLockMax"] = ltime

                    futexTime = "%.6f" % futexTime
                else:
                    td["ftxStat"] = "?"
                    futexTime = ""

                if td["ftxEnt"]:
                    op = td["ftxEnt"]
                    td["ftxEnt"] = None
                else:
                    op = ""

                otype = "{0:>10}".format("RET")

                # add futex data #
                self.futexData.append(
                    [
                        thread,
                        time,
                        core,
                        op,
                        otype,
                        futexTime,
                        "",
                        d["ret"],
                        "",
                    ]
                )

            try:
                if not SysMgr.depEnable:
                    raise Exception("skip dependency analysis")
                elif (
                    nr == SysMgr.getNrSyscall("sys_write")
                    and self.wakeupData["valid"] > 0
                ):
                    self.wakeupData["valid"] -= 1
                elif SysMgr.arch != "aarch64" and nr in (
                    SysMgr.getNrSyscall("sys_poll"),
                    SysMgr.getNrSyscall("sys_select"),
                    SysMgr.getNrSyscall("sys_epoll_wait"),
                ):
                    if (
                        self.lastJob[core]["job"]
                        in ("sched_switch", "sched_wakeup", "sched_wakeup_new")
                        and self.lastJob[core]["prevWakeupTid"] != thread
                    ):
                        ttime = allTime
                        itime = ttime - float(self.wakeupData["time"])
                        self.depData.append(
                            "\t%.3f/%.3f \t%40s %7s     %40s(%7s) \t%s"
                            % (
                                round(ttime, 7),
                                round(itime, 7),
                                " ",
                                " ",
                                td["comm"],
                                thread,
                                "wakeup",
                            )
                        )

                        self.wakeupData["time"] = allTime
                        self.lastJob[core]["prevWakeupTid"] = thread
                elif (
                    SysMgr.arch == "arm"
                    and nr == SysMgr.getNrSyscall("sys_recv")
                ) or nr in (
                    SysMgr.getNrSyscall("sys_recvfrom"),
                    SysMgr.getNrSyscall("sys_recvmsg"),
                    SysMgr.getNrSyscall("sys_recvmmsg"),
                ):
                    if self.lastJob[core]["prevWakeupTid"] != thread:
                        ttime = allTime
                        itime = ttime - float(self.wakeupData["time"])
                        self.depData.append(
                            "\t%.3f/%.3f \t%40s %7s     %40s(%7s) \t%s"
                            % (
                                round(ttime, 7),
                                round(itime, 7),
                                " ",
                                " ",
                                td["comm"],
                                thread,
                                "recv",
                            )
                        )

                        self.wakeupData["time"] = allTime
                        self.lastJob[core]["prevWakeupTid"] = thread
            except SystemExit:
                sys.exit(0)
            except:
                pass

            # register syscall #
            try:
                td["syscallInfo"]
            except:
                td["syscallInfo"] = {}
            try:
                td["syscallInfo"][nrstr]
            except:
                td["syscallInfo"][nrstr] = dict(self.init_syscallInfo)

            # save syscall usage #
            diff = ""
            sysItem = td["syscallInfo"][nrstr]

            # add syscall timeline #
            if not "NOSYSCALL" in SysMgr.environList:
                if sysItem["last"] > 0:
                    start_delta = long(
                        (float(sysItem["last"]) - stime) * 1000000
                    )
                    stop_delta = long((float(ftime) - stime) * 1000000)
                else:
                    # start_delta = 0
                    stop_delta = long((float(ftime) - stime) * 1000000)
                    start_delta = long((float(ftime) - stime) * 1000000)

                # make context string #
                try:
                    ptinfo, thd = TaskAnalyzer.getProcThrInfo(
                        tgid, thread, comm
                    )
                    text = "%s->%s" % (ptinfo, ConfigMgr.sysList[nr])
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printWarn("wrong syscall(%s)" % nr)
                    return time

                # add timeline data #
                self.addTimelineData(
                    long(core),
                    text,
                    thd,
                    "SYSCALL",
                    start_delta,
                    stop_delta,
                )

            # update syscall stat #
            if sysItem["last"] > 0:
                diff = ftime - sysItem["last"]
                td["syscallInfo"][nrstr]["usage"] += diff
                td["syscallInfo"][nrstr]["last"] = 0

                if sysItem["max"] == 0 or sysItem["max"] < diff:
                    td["syscallInfo"][nrstr]["max"] = diff
                if sysItem["min"] <= 0 or sysItem["min"] > diff:
                    td["syscallInfo"][nrstr]["min"] = diff

                if ret[0] == "-":
                    td["syscallInfo"][nrstr]["err"] += 1

            # save syscall history #
            if SysMgr.syscallList:
                try:
                    if not long(nrstr) in SysMgr.syscallList:
                        raise Exception()

                    self.syscallData.append(
                        ["RET", time, thread, core, nrstr, ret, diff]
                    )
                except:
                    pass
            else:
                self.syscallData.append(
                    ["RET", time, thread, core, nrstr, ret, diff]
                )

        elif func == "signal_generate":
            m = re.match(
                (
                    r"^\s*sig=(?P<sig>[0-9]+) errno=(?P<err>[0-9]+) "
                    r"code=(?P<code>.*) comm=(?P<comm>.*) "
                    r"pid=(?P<pid>[0-9]+)"
                ),
                etc,
            )
            if not m:
                _printEventWarning(func)
                return time

            d = m.groupdict()

            sig = d["sig"]
            target_comm = d["comm"]
            pid = d["pid"]
            ttime = allTime

            # update prev comm #
            if target_comm == "<...>" and pid in SysMgr.commCache:
                target_comm = SysMgr.commCache[pid]

            self.depData.append(
                "\t%.3f/%.3f \t%40s(%7s) -> %40s(%7s) \t%s(%s)"
                % (
                    round(ttime, 7),
                    round(ttime - float(self.wakeupData["time"]), 7),
                    threadData["comm"],
                    thread,
                    target_comm,
                    pid,
                    "sigsend",
                    sig,
                )
            )

            self.sigData.append(("SEND", ttime, thread, pid, sig))

            self.wakeupData["time"] = ttime

            try:
                # SIGCHLD #
                std = self.threadData[pid]
                if sig == str(signal.SIGCHLD):
                    if std["waitStartAsParent"] > 0:
                        if std["waitPid"] == 0 or std["waitPid"] == long(
                            thread
                        ):
                            diff = ftime - std["waitStartAsParent"]
                            threadData["waitParent"] = diff
                            std["waitChild"] += diff
                elif sig == str(signal.SIGSEGV):
                    std["die"] = "F"
            except:
                return time

        elif func == "signal_deliver":
            m = re.match(
                (
                    r"^\s*sig=(?P<sig>[0-9]+) errno=(?P<err>[0-9]+) "
                    r"code=(?P<code>.*) sa_handler=(?P<handler>.*) "
                    r"sa_flags=(?P<flags>.*)"
                ),
                etc,
            )
            if not m:
                _printEventWarning(func)
                return time

            d = m.groupdict()

            sig = d["sig"]
            flags = d["flags"]

            ttime = allTime
            itime = ttime - float(self.wakeupData["time"])
            self.depData.append(
                "\t%.3f/%.3f \t%40s %7s     %40s(%7s) \t%s(%s)"
                % (
                    round(ttime, 7),
                    round(itime, 7),
                    "",
                    "",
                    threadData["comm"],
                    thread,
                    "sigrecv",
                    sig,
                )
            )

            self.sigData.append(("RECV", ttime, None, thread, sig))

            self.wakeupData["time"] = ttime

        elif func in ("block_bio_queue", "block_bio_remap"):
            m = re.match(
                (
                    r"^\s*(?P<major>[0-9]+),(?P<minor>[0-9]+)\s*"
                    r"(?P<operation>\S+)\s*(?P<address>\S+)\s+\+\s+"
                    r"(?P<size>[0-9]+)"
                ),
                etc,
            )
            if not m:
                _printEventWarning(func)
                return time

            d = m.groupdict()

            SysMgr.blockEnable = True

            opt = d["operation"]
            bio = "%s/%s/%s/%s" % (
                d["major"],
                d["minor"],
                d["operation"][0],
                d["address"],
            )
            bsize = long(d["size"])

            # skip redundant operation #
            if func == "block_bio_queue" and bio in self.ioData:
                return time

            self.ioData[bio] = {
                "thread": thread,
                "time": ftime,
                "major": d["major"],
                "minor": d["minor"],
                "address": long(d["address"]),
                "size": bsize,
            }

            self.saveBlkOpt(
                thread,
                comm,
                opt[0],
                d["major"],
                d["minor"],
                d["address"],
                SysMgr.blockSize * bsize,
            )

            # read operations #
            if opt[0] == "R":
                threadData["reqRdBlock"] += bsize
                threadData["readQueueCnt"] += 1
                threadData["readBlockCnt"] += 1
                threadData["blkCore"] = coreId
                self.threadData[coreId]["readBlockCnt"] += 1

                if threadData["readStart"] == 0:
                    threadData["readStart"] = ftime
            # synchronous write operation #
            elif opt == "WS":
                threadData["reqWrBlock"] += bsize
                threadData["writeQueueCnt"] += 1
                threadData["writeBlockCnt"] += 1
                threadData["blkCore"] = coreId
                self.threadData[coreId]["writeBlockCnt"] += 1

                if threadData["writeStart"] == 0:
                    threadData["writeStart"] = ftime

        elif func == "block_rq_complete":
            m = re.match(
                (
                    r"^\s*(?P<major>[0-9]+),(?P<minor>[0-9]+)\s*"
                    r"(?P<operation>\S+)\s*\(.*\)\s*(?P<address>\S+)\s+\+\s+"
                    r"(?P<size>[0-9]+)"
                ),
                etc,
            )
            if not m:
                _printEventWarning(func)
                return time

            d = m.groupdict()

            address = d["address"]
            size = d["size"]
            opt = d["operation"]

            bio = "%s/%s/%s/%s" % (
                d["major"],
                d["minor"],
                opt[0],
                d["address"],
            )

            bioStart = long(address)
            bioEnd = long(address) + long(size)

            for key, request in sorted(
                self.ioData.items(),
                key=lambda e: e[1]["address"],
                reverse=False,
            ):
                # skip different requests with device number #
                if (
                    request["major"] != d["major"]
                    or request["minor"] != d["minor"]
                ):
                    continue

                rBioEnd = request["address"] + request["size"]

                # skip irrelevant requests #
                if not (
                    bioStart <= request["address"] < bioEnd
                    or bioStart < rBioEnd <= bioEnd
                ):
                    continue

                # remove bio request in table #
                self.ioData.pop(key, None)

                matchBlock = 0

                if bioStart < request["address"]:
                    matchStart = request["address"]
                else:
                    matchStart = bioStart

                if bioEnd > rBioEnd:
                    matchEnd = rBioEnd
                else:
                    matchEnd = bioEnd

                # simple case #
                if matchStart == request["address"]:
                    matchBlock = matchEnd - request["address"]
                    request["size"] = rBioEnd - matchEnd
                    request["address"] = matchEnd

                    if request["size"] > 0:
                        try:
                            mbio = "%s/%s/%s/%s" % (
                                request["major"],
                                request["minor"],
                                opt[0],
                                request["address"] + request["size"],
                            )

                            request["size"] += self.ioData[mbio]["size"]

                            # remove bio request in table #
                            self.ioData.pop(mbio, None)
                        except:
                            pass

                        # recreate partial ioData uncompleted #
                        bio = "%s/%s/%s/%s" % (
                            request["major"],
                            request["minor"],
                            opt[0],
                            request["address"],
                        )
                        self.ioData[bio] = request
                # complex case #
                elif matchStart > request["address"]:
                    if matchEnd == request["address"] + request["size"]:
                        matchBlock = matchEnd - matchStart
                        request["size"] = matchStart - request["address"]

                        # recreate partial ioData uncompleted #
                        bio = "%s/%s/%s/%s" % (
                            request["major"],
                            request["minor"],
                            opt[0],
                            request["address"],
                        )
                        self.ioData[bio] = request
                    else:
                        continue
                else:
                    continue

                # just ignore error ;( #
                if bioEnd < request["address"] + request["size"]:
                    pass

                reqThd = request["thread"]
                tcomm = self.threadData[reqThd]["comm"]
                lastCore = long(self.threadData[reqThd]["lastCore"])

                # READ #
                if opt[0] == "R":
                    self.threadData[reqThd]["readBlock"] += matchBlock
                    self.threadData[coreId]["readBlock"] += matchBlock

                    if request["size"] != 0:
                        continue

                    if self.threadData[reqThd]["readQueueCnt"] > 0:
                        self.threadData[reqThd]["readQueueCnt"] -= 1

                    """
                    if error of size and time of block read is big then \
                    consider inserting below conditions
                    # self.threadData[reqThd]['readQueueCnt'] == 0 #
                    """
                    if self.threadData[reqThd]["readStart"] > 0:
                        startTime = self.threadData[reqThd]["readStart"]
                        waitTime = ftime - startTime
                        self.threadData[coreId]["ioRdWait"] += waitTime
                        self.threadData[reqThd]["ioRdWait"] += waitTime
                        self.threadData[reqThd]["readStart"] = 0
                        workload = UtilMgr.convSize2Unit(
                            matchBlock * SysMgr.blockSize, True
                        )

                        start_delta = long(
                            (float(startTime) - stime) * 1000000
                        )
                        stop_delta = long((float(ftime) - stime) * 1000000)
                        ptinfo, thd = TaskAnalyzer.getProcThrInfo(
                            tgid, reqThd, tcomm
                        )
                        text = "%s | RD[%s]" % (ptinfo, workload)

                        # add timeline data #
                        self.addTimelineData(
                            long(lastCore),
                            text,
                            thd,
                            "RD",
                            start_delta,
                            stop_delta,
                        )

                # WRITE #
                elif opt == "WS":
                    self.threadData[reqThd]["writeBlock"] += matchBlock
                    self.threadData[coreId]["writeBlock"] += matchBlock

                    if thread != reqThd or request["size"] != 0:
                        continue

                    if self.threadData[reqThd]["writeQueueCnt"] > 0:
                        self.threadData[reqThd]["writeQueueCnt"] -= 1

                    """
                    if error of size and time of block read is big then \
                    consider inserting below conditions
                    # self.threadData[reqThd]['writeQueueCnt'] == 0 #
                    """
                    if self.threadData[reqThd]["writeStart"] > 0:
                        startTime = self.threadData[reqThd]["writeStart"]
                        waitTime = ftime - startTime
                        self.threadData[coreId]["ioWrWait"] += waitTime
                        self.threadData[reqThd]["ioWrWait"] += waitTime
                        self.threadData[reqThd]["writeStart"] = 0
                        workload = UtilMgr.convSize2Unit(
                            matchBlock * SysMgr.blockSize, True
                        )

                        start_delta = long(
                            (float(startTime) - stime) * 1000000
                        )
                        stop_delta = long((float(ftime) - stime) * 1000000)
                        ptinfo, thd = TaskAnalyzer.getProcThrInfo(
                            tgid, reqThd, tcomm
                        )
                        text = "%s | WR[%s]" % (ptinfo, workload)

                        # add timeline data #
                        self.addTimelineData(
                            long(lastCore),
                            text,
                            thd,
                            "WR",
                            start_delta,
                            stop_delta,
                        )

        elif func in ("f2fs_dataread_start", "f2fs_datawrite_start"):
            m = re.match(
                (
                    r"^\s*entry_name (?P<ename>.*), offset (?P<offset>.*), "
                    r"bytes (?P<bytes>.*), cmdline (?P<cmdline>.*), "
                    r"pid (?P<pid>.*), i_size (?P<isize>.*), ino (?P<ino>.*)"
                ),
                etc,
            )
            if not m:
                _printEventWarning(func)
                return time

            d = m.groupdict()

            idx = 0 if func == "f2fs_dataread_start" else 1
            size = long(d["bytes"])
            path = d["ename"]

            for taskid in (0, thread):
                if not taskid in self.f2fsTable:
                    self.f2fsTable[taskid] = [{}, {}]

                if not self.f2fsTable[taskid][idx]:
                    self.f2fsTable[taskid][idx] = {
                        "path": dict(),
                        "size": 0,
                        "count": 0,
                        "time": 0,
                        "stamp": 0,
                        "access": None,
                    }

                if not path in self.f2fsTable[taskid][idx]["path"]:
                    self.f2fsTable[taskid][idx]["path"][path] = {
                        "size": 0,
                        "count": 0,
                        "time": 0,
                        "stamp": 0,
                    }

                self.f2fsTable[taskid][idx]["path"][path]["size"] += size
                self.f2fsTable[taskid][idx]["path"][path]["count"] += 1
                self.f2fsTable[taskid][idx]["size"] += size
                self.f2fsTable[taskid][idx]["count"] += 1
                self.f2fsTable[taskid][idx]["access"] = path
                self.f2fsTable[taskid][idx]["stamp"] = time

        elif func in ("f2fs_dataread_end", "f2fs_datawrite_end"):
            m = re.match(
                (
                    r"^\s*ino (?P<ino>.*), offset (?P<offset>.*), "
                    r"bytes (?P<bytes>.*)"
                ),
                etc,
            )
            if not m:
                _printEventWarning(func)
                return time

            d = m.groupdict()

            idx = 0 if func == "f2fs_dataread_end" else 1

            try:
                last = self.f2fsTable[thread][idx]["stamp"]
                if last == 0:
                    return time

                self.f2fsTable[thread][idx]["stamp"] = 0
                diff = float(time) - float(last)

                for taskid in (0, thread):
                    self.f2fsTable[taskid][idx]["time"] += diff
                    path = self.f2fsTable[taskid][idx]["access"]
                    self.f2fsTable[taskid][idx]["path"][path]["time"] += diff
            except SystemExit:
                sys.exit(0)
            except:
                pass

        elif func == "mm_filemap_add_to_page_cache":
            m = re.match(
                (
                    r"^\s*dev\s+(?P<major>[0-9]+):(?P<minor>[0-9]+)\s*"
                    r"ino (?P<ino>\S+)\s+page=(?P<page>.+)\s+"
                    r"pfn=(?P<pfn>.+)\s+ofs=(?P<ofs>.+)"
                ),
                etc,
            )
            if not m:
                _printEventWarning(func)
                return time

            d = m.groupdict()

            SysMgr.blockEnable = True

            inode = str(long(d["ino"], 16))
            did = "%s:%s" % (d["major"], d["minor"])
            ofs = long(d["ofs"])
            ops = "READ"

            # total read #
            self.fsTable[0].setdefault(ops, {})
            self.fsTable[0][ops].setdefault(did, {})
            self.fsTable[0][ops][did].setdefault(inode, 0)
            self.fsTable[0][ops][did][inode] += 1

            # thread read #
            self.fsTable[1].setdefault(thread, {})
            self.fsTable[1][thread].setdefault(ops, {})
            self.fsTable[1][thread][ops].setdefault(did, {})
            self.fsTable[1][thread][ops][did].setdefault(inode, 0)
            self.fsTable[1][thread][ops][did][inode] += 1

            # thread total #
            self.fsTable[2].setdefault(thread, 0)
            self.fsTable[2][thread] += 1

            # add history #
            if SysMgr.showAll or "RALIST" in SysMgr.environList:
                access = "%.3f" % allTime

                # merge with previous event #
                if self.fsData[0]:
                    lastData = self.fsData[0][-1]
                    if (
                        lastData[0] == thread
                        and lastData[1] == access
                        and lastData[2] == did
                        and lastData[3] == inode
                        and lastData[4] + lastData[5] == ofs
                    ):
                        lastData[5] += SysMgr.PAGESIZE
                        return time

                # append new event #
                self.fsData[0].append(
                    [thread, access, did, inode, ofs, SysMgr.PAGESIZE]
                )

        elif func == "writeback_dirty_page":
            m = re.match(
                (
                    r"^\s*bdi\s+(?P<major>[0-9]+):(?P<minor>[0-9]+):\s*"
                    r"ino=(?P<ino>\S+)\s+index=(?P<index>\S+)"
                ),
                etc,
            )
            if not m:
                _printEventWarning(func)
                return time

            d = m.groupdict()

            SysMgr.blockEnable = True

            inode = d["ino"]
            idx = d["index"]
            did = "%s:%s" % (d["major"], d["minor"])
            bid = inode + idx
            ops = "WRITE"

            threadData["awriteBlock"] += 1
            threadData["awriteBlockCnt"] += 1
            self.threadData[coreId]["awriteBlock"] += 1
            self.threadData[coreId]["awriteBlockCnt"] += 1

            # total write #
            self.fsTable[0].setdefault(ops, {})
            self.fsTable[0][ops].setdefault(did, {})
            self.fsTable[0][ops][did].setdefault(inode, 0)
            self.fsTable[0][ops][did][inode] += 1

            # thread write #
            self.fsTable[1].setdefault(thread, {})
            self.fsTable[1][thread].setdefault(ops, {})
            self.fsTable[1][thread][ops].setdefault(did, {})
            self.fsTable[1][thread][ops][did].setdefault(inode, 0)
            self.fsTable[1][thread][ops][did][inode] += 1

            # thread total #
            self.fsTable[2].setdefault(thread, 0)
            self.fsTable[2][thread] += 1

            self.saveBlkOpt(thread, comm, "W", d["major"], d["minor"], bid, 1)

        elif func == "wbc_writepage":
            m = re.match(
                (
                    r"^\s*bdi\s+(?P<major>[0-9]+):(?P<minor>[0-9]+):\s*"
                    r"towrt=(?P<towrt>\S+)\s+skip=(?P<skip>\S+)"
                ),
                etc,
            )
            if not m:
                _printEventWarning(func)
                return time

            d = m.groupdict()

            if d["skip"] == "0":
                SysMgr.blockEnable = True

                threadData["awriteBlock"] += 1
                threadData["awriteBlockCnt"] += 1
                self.threadData[coreId]["awriteBlock"] += 1
                self.threadData[coreId]["awriteBlockCnt"] += 1

                self.saveBlkOpt(
                    thread, comm, "W", d["major"], d["minor"], d["towrt"], 1
                )

        elif func == "mm_vmscan_wakeup_kswapd":
            try:
                self.reclaimData[thread]
            except:
                self.reclaimData[thread] = {"start": 0.0}

            if self.reclaimData[thread]["start"] <= 0:
                self.reclaimData[thread]["start"] = ftime

            threadData["reclaimCnt"] += 1

        elif func == "mm_vmscan_kswapd_sleep":
            popList = []

            for key, value in self.reclaimData.items():
                self.threadData.setdefault(key, dict(self.init_threadData))
                self.threadData[key]["comm"] = comm

                self.threadData[key]["reclaimWait"] += ftime - float(
                    value["start"]
                )

                popList.append(key)

            for key in popList:
                self.reclaimData.pop(key, None)

        elif func == "mm_vmscan_direct_reclaim_begin":
            if threadData["dReclaimStart"] <= 0:
                threadData["dReclaimStart"] = ftime

            threadData["dReclaimCnt"] += 1
            self.threadData[coreId]["dReclaimCnt"] += 1

        elif func == "mm_vmscan_direct_reclaim_end":
            m = re.match(r"^\s*nr_reclaimed=(?P<nr>[0-9]+)", etc)
            if not m:
                _printEventWarning(func)
                return time

            d = m.groupdict()

            if threadData["dReclaimStart"] > 0:
                threadData["dReclaimWait"] += (
                    ftime - threadData["dReclaimStart"]
                )
                self.threadData[coreId]["dReclaimWait"] += (
                    ftime - threadData["dReclaimStart"]
                )

            threadData["dReclaimStart"] = 0

        elif func == "lowmemory_kill":
            m = re.match(
                (
                    r"^\s*(?P<comm>.+) \((?P<pid>[0-9]+)\), "
                    r"page cache (?P<other_file>.+) \(limit (?P<minfree>.+)\), "
                    r"free (?P<other_free>.+)"
                ),
                etc,
            )
            if not m:
                _printEventWarning(func)
                return time

            d = m.groupdict()

            """
            other_free: NR_FREE_PAGES - totalreserve_pages
            other_file: NR_FILE_PAGES - NR_SHMEM
            minfree: threshold condition (other_free < minfree && other_file < minfree)
            """

            convNum = UtilMgr.convNum
            other_free = convNum(d["other_free"].upper().rstrip("KB"))
            other_file = convNum(d["other_file"].upper().rstrip("KB"))
            minfree = convNum(d["minfree"].upper().rstrip("KB"))

            self.lmkData.append(
                [time, d["comm"], d["pid"], other_free, other_file, minfree]
            )

        elif func == "task_newtask":
            m = re.match(r"^\s*pid=(?P<pid>[0-9]+)\s+comm=(?P<comm>\S+)", etc)
            if not m:
                _printEventWarning(func)
                return time

            d = m.groupdict()

            pid = d["pid"]

            try:
                data = self.threadData[pid]
                SysMgr.printWarn(
                    (
                        "failed to handle a new task %s(%s) "
                        "because it is already exist"
                    )
                    % (data["comm"], pid)
                )
            except SystemExit:
                sys.exit(0)
            except:
                self.threadData[pid] = dict(self.init_threadData)
                self.threadData[pid]["comm"] = d["comm"]
                self.threadData[pid]["ptid"] = thread
                self.threadData[pid]["new"] = "N"
                self.threadData[pid]["createdTime"] = ftime

                if not threadData["childList"]:
                    threadData["childList"] = []

                threadData["childList"].append(pid)
                self.nrNewTask += 1

        elif func == "sched_process_fork":
            m = re.match(
                (
                    r"^\s*comm=(?P<comm>.*)\s+pid=(?P<pid>[0-9]+)\s+"
                    r"child_comm=(?P<child_comm>.*)\s+"
                    r"child_pid=(?P<child_pid>[0-9]+)"
                ),
                etc,
            )
            if not m:
                _printEventWarning(func)
                return time

            d = m.groupdict()

            cpid = d["child_pid"]
            ccomm = d["child_comm"]

            if not cpid in self.threadData:
                self.threadData[cpid] = dict(self.init_threadData)
                self.threadData[cpid]["comm"] = ccomm
                self.threadData[cpid]["ptid"] = thread
                self.threadData[cpid]["new"] = "N"
                self.threadData[cpid]["createdTime"] = ftime

                if not threadData["childList"]:
                    threadData["childList"] = []

                threadData["childList"].append(cpid)
                self.nrNewTask += 1

        elif func == "task_rename":
            m = re.match(
                (
                    r"^\s*pid=(?P<pid>[0-9]+)\s+oldcomm=(?P<oldcomm>.*)\s+"
                    r"newcomm=(?P<newcomm>.*)\s+oom_score_adj"
                ),
                etc,
            )
            if not m:
                _printEventWarning(func)
                return time

            d = m.groupdict()

            pid = d["pid"]
            newcomm = d["newcomm"]

            try:
                self.threadData[pid]
            except:
                self.threadData[pid] = dict(self.init_threadData)
                self.threadData[pid]["comm"] = newcomm
                self.threadData[pid]["ptid"] = thread

            self.threadData[pid]["comm"] = newcomm

        elif func == "locks_get_lock_context":
            m = re.match(
                (
                    r"^\s*dev=(?P<dev>.+)\s+ino=(?P<ino>.+)"
                    r"\s+type=(?P<type>.+)\s+ctx=(?P<ctx>.+)"
                ),
                etc,
            )
            if not m:
                _printEventWarning(func)
                return time

            d = m.groupdict()

            fid = "%s%s" % (d["dev"], d["ino"])
            ltype = d["type"]
            ctx = d["ctx"]

            # save lock data #
            self.flockData.append([thread, time, core, ltype, fid, ctx])

            # unlock #
            if ltype == "F_UNLCK":
                try:
                    if self.lockTable[fid]["owner"] == thread:
                        threadData["lockTime"] += (
                            ftime - self.lockTable[fid]["time"]
                        )
                        threadData["lockCnt"] += 1
                except:
                    self.lockTable[fid] = {}
                    threadData["lockCnt"] += 1

                # initialize lock data #
                self.lockTable[fid]["owner"] = None
                self.lockTable[fid]["time"] = 0
                self.lockTable[fid]["type"] = None
            # try to lock #
            else:
                threadData["tryLockCnt"] += 1

                try:
                    # get lock #
                    if not self.lockTable[fid]["owner"]:
                        self.lockTable[fid]["owner"] = thread
                        self.lockTable[fid]["time"] = ftime
                        self.lockTable[fid]["type"] = ltype
                        threadData["lastLockTime"] = ftime

                        # add wait time to get lock #
                        if threadData["lastLockWait"] > 0:
                            llw = threadData["lastLockWait"]
                            threadData["lockWait"] += ftime - llw

                            threadData["lastLockWait"] = 0
                    # wait lock #
                    else:
                        # add wait time to get lock #
                        if threadData["lastLockWait"] > 0:
                            llw = threadData["lastLockWait"]
                            threadData["lockWait"] += ftime - llw

                        threadData["lastLockWait"] = ftime
                except:
                    # no lock #
                    self.lockTable[fid] = {}
                    self.lockTable[fid]["owner"] = thread
                    self.lockTable[fid]["time"] = ftime
                    self.lockTable[fid]["type"] = ltype
                    threadData["lastLockTime"] = ftime

        elif func == "sched_process_exit":
            m = re.match(r"^\s*comm=(?P<comm>.*)\s+pid=(?P<pid>[0-9]+)", etc)
            if not m:
                _printEventWarning(func)
                return time

            d = m.groupdict()

            pid = d["pid"]

            try:
                self.threadData[pid]
            except:
                self.threadData[pid] = dict(self.init_threadData)
                self.threadData[pid]["comm"] = d["comm"]
                self.threadData[pid]["die"] = "D"

            if self.threadData[pid]["die"] != "F":
                self.threadData[pid]["die"] = "D"

        elif func == "sched_process_wait":
            m = re.match(r"^\s*comm=(?P<comm>.*)\s+pid=(?P<pid>[0-9]+)", etc)
            if not m:
                _printEventWarning(func)
                return time

            d = m.groupdict()

            threadData["waitStartAsParent"] = ftime
            threadData["waitPid"] = long(d["pid"])

        elif func == "suspend_resume":
            SysMgr.powerEnable = True

            state = None

            if "suspend_enter" in etc and "begin" in etc:
                state = "S"
            elif "machine_suspend" in etc and "end" in etc:
                state = "F"
            # Complete a PM transition for all non-sysdev devices #
            elif "dpm_resume_user" in etc and "end" in etc:
                state = "R"

            if state:
                self.suspendData.append([time, state])

        elif func == "net_dev_xmit":
            m = re.match(
                (
                    r"^\s*dev=(?P<dev>.*)\s+skbaddr=(?P<addr>.*)"
                    r"\s+len=(?P<len>.*)\s+rc=(?P<rc>.*)"
                ),
                etc,
            )
            if not m:
                _printEventWarning(func)
                return time

            d = m.groupdict()

        elif func == "module_load":
            m = re.match(r"^\s*(?P<module>.*)\s+(?P<address>.*)", etc)
            if not m:
                _printEventWarning(func)
                return time

            d = m.groupdict()

            module = d["module"]
            address = d["address"]

            self.moduleData.append(["load", thread, time, module, address])

        elif func == "module_free":
            m = re.match(r"^\s*(?P<module>.*)", etc)
            if not m:
                _printEventWarning(func)
                return time

            d = m.groupdict()

            module = d["module"]

            self.moduleData.append(["free", thread, time, module, None])

        elif func == "module_put":
            m = re.match(
                (
                    r"^\s*(?P<module>.*)\s+call_site=(?P<site>.*)\s+"
                    r"refcnt=(?P<refcnt>[0-9]+)"
                ),
                etc,
            )
            if not m:
                _printEventWarning(func)
                return time

            d = m.groupdict()

            module = d["module"]
            refcnt = long(d["refcnt"])

            self.moduleData.append(["put", thread, time, module, refcnt])

        elif func == "module_get":
            m = re.match(
                (
                    r"^\s*(?P<module>.*)\s+call_site=(?P<site>.*)\s+"
                    r"refcnt=(?P<refcnt>[0-9]+)"
                ),
                etc,
            )
            if not m:
                _printEventWarning(func)
                return time

            d = m.groupdict()

            module = d["module"]
            refcnt = long(d["refcnt"])

            self.moduleData.append(["get", thread, time, module, refcnt])

        elif func == "binder_transaction_received":
            m = re.match(r"^\s*transaction=(?P<tranid>-*[0-9]+)", etc)
            if not m:
                _printEventWarning(func)
                return time

            d = m.groupdict()

            tranid = d["tranid"]
            if tranid in self.binderTranData:
                self.threadData[thread]["binderTranId"] = tranid

        elif func == "binder_transaction":
            m = re.match(
                (
                    r"^\s*transaction=(?P<tranid>-*[0-9]+)\s+"
                    r"dest_node=(?P<dnode>[0-9]+)\s+"
                    r"dest_proc=(?P<dproc>[0-9]+)\s+"
                    r"dest_thread=(?P<dthread>[0-9]+)\s+"
                    r"reply=(?P<reply>[0-9]+)\s+"
                    r"flags=(?P<flags>.*)\s+"
                    r"code=(?P<code>.*)"
                ),
                etc,
            )
            if not m:
                _printEventWarning(func)
                return time

            d = m.groupdict()

            # get info #
            commCache = SysMgr.commCache
            tranid = d["tranid"]
            reply = d["reply"]
            flags = d["flags"]
            code = d["code"]

            # convert index string to number for method #
            try:
                code = long(code, 16)
            except:
                code = code

            # convert flags string to number for call type #
            try:
                oneway = 0
                flags = long(flags, 16)
                oneway = 0x1 & flags
                flags = hex(flags)
            except:
                pass

            # sender thread #
            sthr = "%s(%s)" % (comm, thread)

            # sender process #
            if tgid in commCache:
                sproc = "%s(%s)" % (commCache[tgid], tgid)
            else:
                sproc = "??(%s)" % tgid

            # sender #
            if sthr == sproc:
                sender = sthr
            else:
                sender = "%s@%s" % (sthr, sproc)

            # receiver thread #
            dthread = d["dthread"]
            if dthread == "0":
                rthr = ""
            elif dthread in commCache:
                rthr = "%s(%s)" % (commCache[dthread], dthread)
            else:
                rthr = "??(%s)" % dthread

            # receiver process #
            dproc = d["dproc"]
            if dproc in commCache:
                rproc = "%s(%s)" % (commCache[dproc], dproc)
            else:
                rproc = "??(%s)" % dproc

            # receiver #
            if rthr == rproc:
                receiver = rthr
            elif not rthr:
                receiver = rproc
            else:
                receiver = "%s@%s" % (rthr, rproc)

            err = False
            reason = ""

            # sub stat update function #
            def _updateBinderStat(binderStat, ctask, code):
                binderStat.setdefault("others", {})
                binderStat["others"].setdefault(ctask, {})
                binderStat["others"][ctask].setdefault("count", 0)
                binderStat["others"][ctask]["count"] += 1
                binderStat["others"][ctask].setdefault("call", {})
                binderStat["others"][ctask]["call"].setdefault(code, 0)
                binderStat["others"][ctask]["call"][code] += 1

            # request #
            if reply == "0":
                self.binderTranData.setdefault(
                    tranid,
                    {
                        "reqtime": ftime,
                        "receiver": dproc,
                        "sender": sproc,
                        "code": code,
                    },
                )

                # handle oneway calls that have no reply #
                if oneway:
                    # update stats #
                    types = ["Server", "Client"]
                    for item in types:
                        if item == "Server":
                            dataList = self.binderServerData
                            task = sproc
                            ctask = rproc
                        else:
                            dataList = self.binderCliData
                            task = rproc
                            ctask = sproc

                        # update my stats #
                        dataList.setdefault(task, dict(self.init_binderInfo))
                        binderStat = dataList[task]
                        binderStat["count"] += 1
                        binderStat["last"] = ftime

                        # update sub stats #
                        _updateBinderStat(binderStat, ctask, code)

                # add to history #
                if SysMgr.showAll:
                    self.binderData.append(
                        [
                            allTime,
                            False,
                            tranid,
                            sender,
                            receiver,
                            flags,
                            code,
                            None,
                            oneway,
                        ]
                    )
            # response #
            elif "binderTranId" in self.threadData[thread]:
                reqTranId = self.threadData[thread]["binderTranId"]
                if reqTranId in self.binderTranData:
                    reqTime = self.binderTranData[reqTranId]["reqtime"]
                    origCode = self.binderTranData[reqTranId]["code"]
                    diff = ftime - reqTime
                    self.binderTranData[reqTranId]["diff"] = diff

                    # update stats #
                    types = ["Server", "Client"]
                    for item in types:
                        if item == "Server":
                            dataList = self.binderServerData
                            task = sproc
                            ctask = rproc
                        else:
                            dataList = self.binderCliData
                            task = rproc
                            ctask = sproc

                        # update my stats #
                        dataList.setdefault(task, dict(self.init_binderInfo))
                        binderStat = dataList[task]
                        binderStat["count"] += 1
                        binderStat["usage"] += diff
                        binderStat["last"] = ftime
                        dataList[task]["max"] = max(binderStat["max"], diff)
                        if binderStat["min"] <= 0 or binderStat["min"] > diff:
                            dataList[task]["min"] = diff

                        # update sub stats #
                        _updateBinderStat(binderStat, ctask, origCode)
                else:
                    err = True
                    diff = None

                # add to history #
                if SysMgr.showAll:
                    self.binderData.append(
                        [
                            allTime,
                            True,
                            tranid,
                            sender,
                            receiver,
                            "",
                            "",
                            diff,
                            None,
                        ]
                    )
            else:
                err = True

            # print error line #
            if err:
                reason = " because there is no transaction ID for request"
                SysMgr.printWarn(
                    (
                        "failed to recognize binder transaction "
                        "for the below line%s\n%s"
                    )
                    % (reason, string.strip())
                )

        elif func == "cpu_idle":
            m = re.match(
                r"^\s*state=(?P<state>[0-9]+)\s+cpu_id=(?P<cpu_id>[0-9]+)", etc
            )
            if not m:
                _printEventWarning(func)
                return time

            SysMgr.powerEnable = True

            d = m.groupdict()

            tid = "0[%s]" % d["cpu_id"]
            state = long(d["state"])

            # no change #
            if self.threadData[tid]["lastIdleStatus"] == state:
                return time

            # update status #
            self.threadData[tid]["lastIdleStatus"] = state

            # check wakeup state #
            isWakeuped = state == 4294967295 or state == -1

            # wakeup #
            if isWakeuped:
                # update off time from start #
                if (
                    self.threadData[tid]["coreSchedCnt"] == 0
                    and self.threadData[tid]["offTime"] == 0
                ):
                    self.threadData[tid]["offTime"] = allTime
                    start_delta = 0
                # start to sleep #
                elif self.threadData[tid]["lastOff"] > 0:
                    startTime = self.threadData[tid]["lastOff"]
                    start_delta = long((float(startTime) - stime) * 1000000)
                    self.threadData[tid]["offTime"] += (
                        ftime - self.threadData[tid]["lastOff"]
                    )
                    self.threadData[tid]["lastOff"] = 0.0
                # undefined #
                else:
                    return time

                stop_delta = long((float(ftime) - stime) * 1000000)

                if "ONLYPROC" in SysMgr.environList:
                    thd = tgid
                else:
                    thd = thread

                # add timeline data #
                self.addTimelineData(
                    long(core), "OFF", thd, "OFF", start_delta, stop_delta
                )

            # sleep #
            else:
                self.threadData[tid]["offCnt"] += 1
                self.threadData[tid]["lastOff"] = ftime

        elif func == "cpu_frequency":
            # TODO: calculate power consumption for DVFS system #
            SysMgr.powerEnable = True
            return time

        elif func == "cpu_frequency_limits":
            m = re.match(
                (
                    r"^\s*min=(?P<min>[0-9]+) "
                    r"max=(?P<max>[0-9]+) cpu_id=(?P<cpu_id>.+)"
                ),
                etc,
            )
            if not m:
                _printEventWarning(func)
                return time

            SysMgr.powerEnable = True

            d = m.groupdict()

            ptinfo, thd = TaskAnalyzer.getProcThrInfo(tgid, thread, comm)

            self.freqData.append(
                [ftime, ptinfo, d["min"], d["max"], d["cpu_id"]]
            )

        elif func == "workqueue_queue_work":
            m = re.match(
                (
                    r"^\s*work struct=(?P<struct>.+) "
                    r"function=(?P<function>.+) workqueue=(?P<wq>.+) "
                    r"req_cpu=(?P<rcpu>.+) cpu=(?P<ecpu>.+)"
                ),
                etc,
            )
            if not m:
                _printEventWarning(func)
                return time

            d = m.groupdict()
            struct = d["struct"]
            function = d["function"]

            # register workqueue #
            try:
                self.wqData[struct]
            except:
                self.wqData[struct] = dict(self.init_wqData)
                self.wqData[struct]["name"] = function
                self.wqData[struct]["task"] = {}

        elif func == "workqueue_execute_start":
            m = re.match(
                (
                    r"^\s*work struct (?P<struct>.+): "
                    r"function (?P<function>.+)"
                ),
                etc,
            )
            if not m:
                _printEventWarning(func)
                return time

            d = m.groupdict()
            struct = d["struct"]
            function = d["function"]

            if not struct in self.wqData:
                return time

            # update period #
            if self.wqData[struct]["start"] > 0:
                diff = ftime - self.wqData[struct]["start"]
                if (
                    diff > self.wqData[struct]["maxPeriod"]
                    or self.wqData[struct]["maxPeriod"] < 0
                ):
                    self.wqData[struct]["maxPeriod"] = diff
                if (
                    diff < self.wqData[struct]["minPeriod"]
                    or self.wqData[struct]["minPeriod"] < 0
                ):
                    self.wqData[struct]["minPeriod"] = diff

            self.wqData[struct]["start"] = ftime
            self.wqData[struct]["scount"] += 1
            self.wqData[struct]["task"].setdefault(thread, None)

        elif func == "workqueue_execute_end":
            m = re.match(r"^\s*work struct (?P<struct>.+)", etc)
            if not m:
                _printEventWarning(func)
                return time

            d = m.groupdict()
            struct = d["struct"]

            if not struct in self.wqData:
                return time
            elif self.wqData[struct]["start"] == 0:
                return

            # update usage #
            diff = ftime - self.wqData[struct]["start"]
            self.wqData[struct]["usage"] += diff

            # update stat #
            diff = ftime - self.wqData[struct]["start"]
            if (
                diff > self.wqData[struct]["max"]
                or self.wqData[struct]["max"] < 0
            ):
                self.wqData[struct]["max"] = diff
            if (
                diff < self.wqData[struct]["min"]
                or self.wqData[struct]["min"] < 0
            ):
                self.wqData[struct]["min"] = diff

            self.wqData[struct]["rcount"] += 1

        elif func == "console":
            m = re.match(
                r"^\s*\[\s*(?P<time>\S+)\s*\]\s+EVENT_(?P<event>\S+)", etc
            )
            if m:
                d = m.groupdict()

                self.handleUserEvent(d["event"], time)

                return time

            # process CPU shutdown event #
            m = re.match(
                (
                    r"^\s*\[\s*(?P<time>\S+)\s*\]\s+"
                    r"CPU(?P<core>[0-9]+)\: shutdown"
                ),
                etc,
            )
            if m:
                ed = m.groupdict()

                try:
                    # set status of thread #
                    lastTid = self.lastTidPerCore[ed["core"]]
                    self.threadData[lastTid]["stop"] = float(ed["time"])
                    self.threadData[lastTid]["lastStatus"] = "S"

                    # set status of core #
                    scoreId = "0[%s]" % ed["core"]
                    self.threadData[scoreId]["offCnt"] += 1
                    self.threadData[scoreId]["lastOff"] = float(ed["time"])
                    self.threadData[scoreId]["start"] = float(ed["time"])
                    self.threadData[scoreId]["lastStatus"] = "R"
                except:
                    pass

            # save console log #
            self.consoleData.append([d["thread"], core, time, etc])

        elif func in ("tracing_mark_write", "0"):
            m = re.match(r"^.+EVENT_(?P<event>\S+)", etc)
            if not m:
                _printEventWarning(func)
                return time

            d = m.groupdict()

            self.handleUserEvent(d["event"], time)

            start_delta = stop_delta = long((float(ftime) - stime) * 1000000)

            ptinfo, thd = TaskAnalyzer.getProcThrInfo(tgid, thread, comm)
            event = "%s->%s" % (ptinfo, d["event"])

            # add timeline data #
            self.addTimelineData(
                long(core),
                event,
                thd,
                "EVENT_MARK",
                start_delta,
                stop_delta,
            )

        else:
            handleSpecialEvents = True

        # custom event #
        if any(
            [
                True
                for event in SysMgr.customEventList
                if func.startswith(event)
            ]
        ):
            self.customEventData.append(
                [func, comm, thread, allTime, etc.strip()]
            )

            eventName = "customEvent"

            # make event list #
            if not threadData[eventName]:
                threadData[eventName] = {}

            threadData[eventName].setdefault(func, dict(self.init_eventData))

            self.customEventInfo.setdefault(func, dict(self.init_eventData))

            threadData[eventName][func]["count"] += 1
            self.customEventInfo[func]["count"] += 1

            # define eventObj #
            eventObj = threadData[eventName][func]

            # get interval #
            interDiff = 0
            if eventObj["start"] > 0:
                interDiff = ftime - eventObj["start"]

            # update period of thread #
            if interDiff > eventObj["maxPeriod"] or eventObj["maxPeriod"] == 0:
                threadData[eventName][func]["maxPeriod"] = interDiff
            if interDiff < eventObj["minPeriod"] or eventObj == 0:
                threadData[eventName][func]["minPeriod"] = interDiff

            # update period of system #
            if (
                interDiff > self.customEventInfo[func]["maxPeriod"]
                or self.customEventInfo[func]["maxPeriod"] == 0
            ):
                self.customEventInfo[func]["maxPeriod"] = interDiff
            if (
                interDiff < self.customEventInfo[func]["minPeriod"]
                or self.customEventInfo[func]["minPeriod"] == 0
            ):
                self.customEventInfo[func]["minPeriod"] = interDiff

            threadData[eventName][func]["start"] = ftime

            handleSpecialEvents = True

            # add timeline data #
            ptinfo, thd = TaskAnalyzer.getProcThrInfo(tgid, thread, comm)
            event = "%s->%s" % (ptinfo, func)
            start_delta = stop_delta = long((float(ftime) - stime) * 1000000)
            self.addTimelineData(
                long(core),
                event,
                thd,
                eventName,
                start_delta,
                stop_delta,
            )

        # check special event flag #
        if not handleSpecialEvents:
            return time

        for item in (
            (
                SysMgr.userEventList,
                "userEvent",
                self.userEventInfo,
                self.userEventData,
            ),
            (
                SysMgr.kernelEventList,
                "kernelEvent",
                self.kernelEventInfo,
                self.kernelEventData,
            ),
        ):
            eventList, eventName, eventInfo, eventData = item

            for name in eventList:
                # convert real symbol #
                if not func.startswith(name):
                    if name != "@SYM":
                        continue

                    found = False
                    for suffix, prefix, evt in (
                        ("_USER_enter", "USER_", "userEvent"),
                        ("_USER_exit", "USER_", "userEvent"),
                        ("_KERNEL_enter", "KERNEL_", "kernelEvent"),
                        ("_KERNEL_exit", "KERNEL_", "kernelEvent"),
                    ):
                        if evt != eventName:
                            continue
                        elif not func.endswith(suffix):
                            continue

                        name = UtilMgr.rstrip(func, suffix)
                        func = func.replace(prefix, "")

                        found = True
                        break

                    if not found:
                        continue

                if not threadData[eventName]:
                    threadData[eventName] = {}

                threadData[eventName].setdefault(
                    name, dict(self.init_eventData)
                )

                eventInfo.setdefault(name, dict(self.init_eventData))

                # define eventObj #
                eventObj = threadData[eventName][name]

                if func == "%s_enter" % name:
                    isSaved = True

                    # user enter #
                    if eventName == "userEvent":
                        m = re.match(r"^\s*\((?P<addr>.+)\)(?P<args>.+)", etc)
                        if m:
                            d = m.groupdict()
                            eventData.append(
                                [
                                    "ENTER",
                                    name,
                                    comm,
                                    thread,
                                    allTime,
                                    "",
                                    d["args"],
                                ]
                            )
                        else:
                            m = re.match(r"^\s*\((?P<addr>.+)\)", etc)
                            if m:
                                d = m.groupdict()
                                eventData.append(
                                    [
                                        "ENTER",
                                        name,
                                        comm,
                                        thread,
                                        allTime,
                                        "",
                                        "",
                                    ]
                                )
                            else:
                                isSaved = False
                                SysMgr.printWarn(
                                    "failed to recognize '%s' user event" % etc
                                )
                    # kernel enter #
                    else:
                        m = re.match(
                            (
                                r"^\s*\((?P<name>.+)\+(?P<offset>.+) "
                                r"<(?P<addr>.+)>\)(?P<args>.*)"
                            ),
                            etc,
                        )
                        if m:
                            d = m.groupdict()
                            eventData.append(
                                [
                                    "ENTER",
                                    name,
                                    d["addr"],
                                    comm,
                                    thread,
                                    allTime,
                                    "",
                                    d["args"],
                                ]
                            )
                        else:
                            m = re.match(
                                r"^\s*\((?P<name>.+)\+(?P<offset>.+)\)(?P<args>.*)",
                                etc,
                            )
                            if m:
                                d = m.groupdict()
                                eventData.append(
                                    [
                                        "ENTER",
                                        name,
                                        "",
                                        comm,
                                        thread,
                                        allTime,
                                        "",
                                        d["args"],
                                    ]
                                )
                            else:
                                isSaved = False
                                SysMgr.printWarn(
                                    "failed to recognize '%s' kernel event"
                                    % etc
                                )

                    if not isSaved:
                        continue

                    # get interval #
                    interDiff = 0
                    if eventObj["start"] > 0:
                        interDiff = ftime - eventObj["start"]

                    threadData[eventName][name]["count"] += 1
                    threadData[eventName][name]["start"] = ftime

                    # update period of thread #
                    if (
                        interDiff > eventObj["maxPeriod"]
                        or eventObj["maxPeriod"] == 0
                    ):
                        threadData[eventName][name]["maxPeriod"] = interDiff
                    if (
                        interDiff < eventObj["minPeriod"]
                        or eventObj["minPeriod"] == 0
                    ):
                        threadData[eventName][name]["minPeriod"] = interDiff

                    eventInfo[name]["count"] += 1

                    # update period of system #
                    if (
                        interDiff > eventInfo[name]["maxPeriod"]
                        or eventInfo[name]["maxPeriod"] == 0
                    ):
                        eventInfo[name]["maxPeriod"] = interDiff
                    if (
                        interDiff < eventInfo[name]["minPeriod"]
                        or eventInfo[name]["minPeriod"] == 0
                    ):
                        eventInfo[name]["minPeriod"] = interDiff

                elif func == "%s_exit" % name:
                    isSaved = True

                    # user exit #
                    if eventName == "userEvent":
                        m = re.match(
                            r"^\s*\((?P<addr>.+) <- (?P<caller>.+)\)(?P<args>.+)",
                            etc,
                        )
                        if m:
                            d = m.groupdict()
                            eventData.append(
                                [
                                    "EXIT",
                                    name,
                                    comm,
                                    thread,
                                    allTime,
                                    d["caller"],
                                    d["args"],
                                ]
                            )
                        else:
                            m = re.match(
                                r"^\s*\((?P<addr>.+) <- (?P<caller>.+)\)",
                                etc,
                            )
                            if m:
                                d = m.groupdict()
                                eventData.append(
                                    [
                                        "EXIT",
                                        name,
                                        comm,
                                        thread,
                                        allTime,
                                        d["caller"],
                                        "",
                                    ]
                                )
                            else:
                                isSaved = False
                                SysMgr.printWarn(
                                    "failed to recognize '%s' user event" % etc
                                )

                    # kernel exit #
                    else:
                        m = re.match(
                            (
                                r"^\s*\((?P<caller>.+)\+(?P<offset>.+)"
                                r" <(?P<caddr>.+)> <- "
                                r"(?P<name>.+) <(?P<addr>.+)>\)(?P<args>.*)"
                            ),
                            etc,
                        )
                        if m:
                            d = m.groupdict()
                            eventData.append(
                                [
                                    "EXIT",
                                    name,
                                    d["addr"],
                                    comm,
                                    thread,
                                    allTime,
                                    d["caller"],
                                    d["args"],
                                    d["caddr"],
                                ]
                            )
                        else:
                            m = re.match(
                                (
                                    r"^\s*\((?P<caller>.+)\+(?P<offset>.+) <- "
                                    r"(?P<name>.+)\)(?P<args>.*)"
                                ),
                                etc,
                            )
                            if m:
                                d = m.groupdict()
                                eventData.append(
                                    [
                                        "EXIT",
                                        name,
                                        "",
                                        comm,
                                        thread,
                                        allTime,
                                        d["caller"],
                                        d["args"],
                                        "",
                                    ]
                                )
                            else:
                                isSaved = False
                                SysMgr.printWarn(
                                    "failed to recognize '%s' kernel event"
                                    % etc
                                )

                    if not isSaved:
                        continue

                    # get usage #
                    if eventObj["start"] <= 0:
                        continue

                    usage = ftime - eventObj["start"]
                    threadData[eventName][name]["usage"] += usage
                    eventInfo[name]["usage"] += usage

                    # update usage of thread #
                    if usage > eventObj["max"] or eventObj["max"] == 0:
                        threadData[eventName][name]["max"] = usage
                    if usage < eventObj["min"] or eventObj["min"] == 0:
                        threadData[eventName][name]["min"] = usage

                    # update usage of system #
                    if (
                        usage > eventInfo[name]["max"]
                        or eventInfo[name]["max"] == 0
                    ):
                        eventInfo[name]["max"] = usage
                    if (
                        usage < eventInfo[name]["min"]
                        or eventInfo[name]["min"] == 0
                    ):
                        eventInfo[name]["min"] = usage

                    # add timeline data #
                    ptinfo, thd = TaskAnalyzer.getProcThrInfo(
                        tgid, thread, comm
                    )
                    event = "%s->%s" % (ptinfo, name)
                    stop_delta = long((float(ftime) - stime) * 1000000)
                    start_delta = long(
                        (float(eventObj["start"]) - stime) * 1000000
                    )
                    self.addTimelineData(
                        long(core),
                        event,
                        thd,
                        eventName,
                        start_delta,
                        stop_delta,
                    )

        # return time #
        return time

    def getSchedInfo(self, stat):
        schedPolicy = ConfigMgr.SCHED_POLICY[long(stat[self.policyIdx])]
        nrPrio = long(stat[self.prioIdx])
        if schedPolicy in ("C", "I", "B"):
            schedValue = "%3d" % (nrPrio - 20)
        else:
            schedValue = "%3d" % (abs(nrPrio + 1))
        return schedPolicy, schedValue

    def compareThreadData(self):
        # TODO: compare stats with items in self.backupData #
        for key, value in sorted(
            self.threadData.items(), key=lambda e: e[1]["usage"], reverse=True
        ):
            pass

    def printFileStat(self, filters):
        # update uptime #
        SysMgr.updateUptime()

        convNum = UtilMgr.convNum
        convSize = UtilMgr.convSize2Unit
        convUnit = UtilMgr.convUnit2Size
        convColor = UtilMgr.convColor

        # get size filter #
        try:
            condBig = convUnit(SysMgr.environList.get("BT", [0])[0])
            condLess = convUnit(SysMgr.environList.get("LT", [0])[0])
            sizeFilter = condBig or condLess
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to get size filter", True)
            sys.exit(0)

        # print CPU usage #
        if SysMgr.isLinux:
            # get diff #
            diff = SysMgr.uptimeDiff
            if diff == 0:
                diff = 0.01

            # get CPU usage for myself #
            mcpuStr, sysCpuStr = SysMgr.getCpuUsageStr(
                TaskAnalyzer.dbgObj, diff
            )

            # get available memory for system #
            sysMemStr = SysMgr.getAvailMemInfo()
        else:
            mcpuStr = sysCpuStr = sysMemStr = "?"

        # get system fd handle stats #
        sysFds = SysMgr.getNrSysFdHandle()
        try:
            curFd = sysFds[0]
            maxFd = sysFds[2]
            fdUsage = "%d" % ((curFd / float(maxFd)) * 100)
            sysFdStr = "%s(%s%%/%s)" % (
                convNum(curFd),
                fdUsage,
                convSize(maxFd, True),
            )
        except SystemExit:
            sys.exit(0)
        except:
            curFd = maxFd = 0
            sysFdStr = "?"

        # get the number of sockets #
        SysMgr.getSocketAddrList([])
        SysMgr.getUdsList([])

        # print menu #
        if SysMgr.jsonEnable:
            SysMgr.jsonData = {
                "uptime": SysMgr.uptime,
                "uptimeDiff": SysMgr.uptimeDiff,
                "nrProcess": self.nrProcess,
                "nrOpenFd": self.nrFd,
                "nrCurFdHandle": curFd,
                "nrMaxFdHandle": maxFd,
                "nrUDPSock": SysMgr.nrUDPSock,
                "nrTCPSock": SysMgr.nrTCPSock,
                "nrTCPConn": SysMgr.nrTCPConn,
                "nrUDSSock": SysMgr.nrUDSSock,
                "nrFile": len(self.fileData),
                "comm": SysMgr.comm,
                "pid": SysMgr.pid,
                "procs": {},
            }
        else:
            SysMgr.addPrint(
                convColor(
                    (
                        "[Top File Info] [Time: %7.3f] [Inter: %.3f]"
                        " [Proc: %s] [SYS: %s/%s] [%s(%s): %s]\n"
                    )
                    % (
                        SysMgr.uptime,
                        SysMgr.uptimeDiff,
                        convNum(self.nrProcess),
                        sysCpuStr,
                        sysMemStr,
                        SysMgr.comm,
                        SysMgr.pid,
                        mcpuStr,
                    ),
                    "BOLD",
                )
            )

            SysMgr.addPrint(
                convColor(
                    (
                        "%s[Handle: %s] [FD: %s] [File: %s] [UDP: %s] "
                        "[TCP: %s] [TCP(ESTABLISHED): %s] [UDS: %s]\n"
                    )
                    % (
                        " " * 16,
                        sysFdStr,
                        convNum(self.nrFd),
                        convNum(len(self.fileData)),
                        convNum(SysMgr.nrUDPSock),
                        convNum(SysMgr.nrTCPSock),
                        convNum(SysMgr.nrTCPConn),
                        convNum(SysMgr.nrUDSSock),
                    ),
                    "BOLD",
                )
            )

            SysMgr.addPrint(
                "%s\n" % twoLine
                + (
                    "{0:>16} ({1:^7}/{2:^7}/{3:^4}/{4:>4})|{5:^6}|"
                    "{6:^101}|\n{7:1}\n"
                ).format(
                    "Process", "ID", "PID", "Nr", "Pri", "FD", "Path", oneLine
                ),
                newline=3,
            )

        # set sort value #
        if SysMgr.sort == "p":
            sortedProcData = sorted(
                self.procData.items(), key=lambda e: long(e[0])
            )
        else:
            # set the number of files opened as default #
            sortedProcData = sorted(
                self.procData.items(),
                key=lambda e: len(e[1]["fdList"]),
                reverse=True,
            )

        # get filters #
        procFilter, fileFilter = filters

        # print process info #
        procCnt = 0
        for idx, value in sortedProcData:
            # linux #
            if SysMgr.isLinux:
                stat = value["stat"]
                comm = stat[self.commIdx].strip("()")
                pid = stat[self.ppidIdx]
                nrThread = stat[self.nrthreadIdx]

                # get sched info #
                schedPolicy, schedValue = self.getSchedInfo(stat)
                sched = schedPolicy + schedValue
            # other OS #
            else:
                comm = value["comm"]
                pid = value["ppid"]
                nrThread = value["nrThread"]
                sched = str(value["nice"])

            procInfo = ("{0:>16} ({1:>7}/{2:>7}/{3:>4}/{4:>4})").format(
                comm[:16], idx, pid, nrThread, sched
            )

            procInfoLen = len(procInfo)

            # summary info #
            if "fdInfo" in value:
                details = "   ".join(
                    [
                        "%6s: %5s" % (fd, path)
                        for fd, path in sorted(
                            value["fdInfo"].items(),
                            key=lambda e: long(e[1]),
                            reverse=True,
                        )
                    ]
                )
            else:
                details = " "

            # convert the number of fds #
            nrFd = len(value["fdList"])
            if nrFd > SysMgr.fdHighThr:
                nrFd = convColor(nrFd, "RED", 6)
            elif nrFd == 0:
                break

            procInfo = "%s|%s\n" % (
                procInfo,
                "{0:>6}| {1:<100}|".format(nrFd, details),
            )

            # print only per-process summary #
            fdCnt = 0
            if "ONLYPROC" in SysMgr.environList or (
                not SysMgr.showAll and not SysMgr.filterGroup
            ):
                if procInfo != "":
                    if SysMgr.jsonEnable:
                        ret = True
                        SysMgr.jsonData["procs"][idx] = {
                            "comm": comm,
                            "pid": pid,
                            "nrThread": nrThread,
                            "nrFd": len(value["fdList"]),
                        }
                    else:
                        ret = SysMgr.addPrint(procInfo)

                    procInfo = ""
                    if not ret:
                        break

                fdCnt += 1
                procCnt += 1

                continue

            # print fd info #
            for fd, path in sorted(
                value["fdList"].items(), key=lambda e: long(e[0]), reverse=True
            ):
                # get additional info #
                try:
                    if path.startswith("socket"):
                        obj = path.split("[")[1][:-1]

                        addr = SysMgr.getSocketAddrList([obj])
                        if addr:
                            path = "%s (%s)" % (path, addr[0])
                            raise Exception("skip UDS socket")

                        uds = SysMgr.getUdsList([obj])
                        if uds:
                            path = "%s (%s)" % (path, uds[0])
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

                # apply filter #
                if fileFilter:
                    if not UtilMgr.isValidStr(path, fileFilter):
                        continue

                # read pos and permission #
                try:
                    if SysMgr.isLinux:
                        if not path.startswith("/"):
                            if sizeFilter:
                                continue
                            raise ValueError()
                    else:
                        raise ValueError()

                    attr = ""
                    fdinfoPath = "%s/%s/fdinfo/%s" % (SysMgr.procPath, idx, fd)
                    with open(fdinfoPath, "r") as infofd:
                        for item in infofd.readlines():
                            name, val = item.split(":", 1)
                            val = val.strip()

                            if name == "pos":
                                attr += "%s" % val
                            elif name == "flags":
                                perm = long(val, 8)
                                perm = UtilMgr.getFlagString(
                                    perm, ConfigMgr.OPEN_TYPE, num="oct"
                                )
                                attr += ", %s" % perm
                            elif name in ("size", "exp_name", "count", "ino"):
                                try:
                                    if name != "size":
                                        raise Exception()

                                    val = long(val)
                                    if name == "size":
                                        val = convSize(val, True, "K")
                                except SystemExit:
                                    sys.exit(0)
                                except:
                                    pass
                                attr += ", %s=%s" % (name, val)

                    # append attributes #
                    if attr:
                        # add size #
                        try:
                            size = os.stat(path).st_size
                            if condBig and size < condBig:
                                continue
                            elif condLess and size > condLess:
                                continue

                            if size:
                                size = convSize(size)
                                attr += ", size=%s" % (size)
                        except SystemExit:
                            sys.exit(0)
                        except:
                            if sizeFilter:
                                continue

                        path = "%s (%s)" % (path, attr)
                    elif sizeFilter:
                        continue
                except ValueError:
                    pass
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printWarn(
                        "failed to read attributes from %s" % fdinfoPath,
                        reason=True,
                    )

                if procInfo != "":
                    if SysMgr.jsonEnable:
                        ret = True
                        SysMgr.jsonData["procs"][idx] = {
                            "comm": comm,
                            "pid": pid,
                            "nrThread": nrThread,
                            "nrFd": len(value["fdList"]),
                            "fdList": value["fdList"],
                        }
                    else:
                        ret = SysMgr.addPrint(procInfo)

                    # save cmdline #
                    if SysMgr.isLinux:
                        # save cmdline #
                        self.saveCmdlineData(None, idx)

                    # print cmdline #
                    if "cmdline" in value:
                        if SysMgr.jsonEnable:
                            SysMgr.jsonData["procs"][idx]["cmdline"] = value[
                                "cmdline"
                            ]
                        else:
                            # print stat #
                            SysMgr.addPrint(
                                ("{0:>1}|{1:>6}| {2:<100}|\n").format(
                                    " " * procInfoLen, "CMD", value["cmdline"]
                                )
                            )

                    procInfo = ""
                    if not ret:
                        break

                # print stat #
                if not SysMgr.jsonEnable:
                    SysMgr.addPrint(
                        ("{0:>1}|{1:>6}| {2:<100}|\n").format(
                            " " * procInfoLen, fd, path
                        )
                    )

                fdCnt += 1

            if fdCnt > 0:
                procCnt += 1
                if SysMgr.jsonEnable:
                    ret = True
                else:
                    ret = SysMgr.addPrint("%s\n" % oneLine)
                if not ret:
                    break

        # add last line #
        if "ONLYPROC" in SysMgr.environList:
            ret = SysMgr.addPrint("%s\n" % oneLine)

        # print total stats #
        if SysMgr.jsonEnable:
            pass
        elif procCnt == 0:
            text = "{0:^16}".format("None")
            frame = "%s%s|" % (text, " " * (SysMgr.lineLength - len(text) - 1))

            SysMgr.addPrint("{0:1}\n{1:1}\n".format(frame, oneLine))
        elif not SysMgr.showAll and not SysMgr.filterGroup:
            SysMgr.addPrint("{0:1}\n".format(oneLine))

        # print buffer #
        SysMgr.printTopStats()

    def saveFdinfoData(self, pid, filterList=[]):
        try:
            # skip kernel thread #
            if SysMgr.isKernelTask(pid):
                return

            dirpath = "%s/%s/fdinfo" % (SysMgr.procPath, pid)
            for fd in os.listdir(dirpath):
                fdpath = os.path.join(dirpath, fd)

                data = SysMgr.readFile(fdpath, verb=False)
                if not data:
                    continue
                elif filterList and not UtilMgr.isValidStr(
                    data, filterList, inc=True
                ):
                    continue

                if not pid in self.fdinfoData:
                    self.fdinfoData[pid] = {}
                if not fd in self.fdinfoData[pid]:
                    self.fdinfoData[pid][fd] = {}

                for item in data.split("\n"):
                    name, val = item.split(":", 1)
                    val = val.strip()
                    self.fdinfoData[pid][fd][name] = val
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn(
                "failed to get fdinfo data for %s(%s)"
                % (pid, self.procData[pid]["comm"]),
                reason=True,
            )

    def saveFileStat(self, filters):
        # save proc and file instance #
        SysMgr.topInstance = self
        SysMgr.procInstance = self.procData
        SysMgr.fileInstance = self.fileData

        procFilter = filters[0]

        # get process list #
        if procFilter:
            pids = SysMgr.convTaskList(procFilter, isThread=True, inc=True)
            newPids = []
            if SysMgr.isLinux:
                for pid in pids:
                    ret = SysMgr.getTgid(pid)
                    if ret:
                        newPids.append(ret)
            else:
                self.saveProcStatGen(["open_files"], list(map(str, pids)))
                newPids += pids
            pids = list(set(newPids))
        else:
            if SysMgr.isLinux:
                pids = SysMgr.getPidList()
            else:
                self.saveProcStatGen(["open_files"])
                pids = list(self.procData)

        # remove myself info #
        try:
            pids.remove(str(SysMgr.pid))
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # get thread list #
        for pid in pids:
            try:
                long(pid)
                self.nrProcess += 1
            except:
                continue

            if SysMgr.isLinux:
                # save stat of process #
                procPath = "%s/%s" % (SysMgr.procPath, pid)
                self.saveProcData(procPath, pid)

                # save file info per process #
                try:
                    fdlistPath = "%s/fd" % (procPath)
                    fdlist = os.listdir(fdlistPath)
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printOpenWarn(fdlistPath)
                    continue
            else:
                try:
                    fdlist = self.procData[pid]["open_files"]
                except SystemExit:
                    sys.exit(0)
                except:
                    continue

            # save fd info of process #
            for fd in fdlist:
                try:
                    if SysMgr.isLinux:
                        long(fd)
                    else:
                        filename, fd = fd
                        fdPath = "N/A"
                except SystemExit:
                    sys.exit(0)
                except:
                    continue

                try:
                    self.nrFd += 1

                    if SysMgr.isLinux:
                        # add file info into fdList #
                        fdPath = "%s/%s" % (fdlistPath, fd)
                        filename = os.readlink(fdPath)

                    self.procData[pid]["fdList"][fd] = filename

                    # increase reference count of file #
                    try:
                        self.fileData[filename] += 1
                    except:
                        self.fileData[filename] = 1

                    # initialize fdinfo per process #
                    try:
                        self.procData[pid]["fdInfo"]
                    except:
                        self.procData[pid]["fdInfo"] = {}
                        self.procData[pid]["fdInfo"]["EVENT"] = 0
                        self.procData[pid]["fdInfo"]["SOCKET"] = 0
                        self.procData[pid]["fdInfo"]["DEVICE"] = 0
                        self.procData[pid]["fdInfo"]["PIPE"] = 0
                        self.procData[pid]["fdInfo"]["NORMAL"] = 0
                        self.procData[pid]["fdInfo"]["PROC"] = 0

                    # increase type count per process #
                    if SysMgr.isLinux:
                        if filename.startswith("anon"):
                            self.procData[pid]["fdInfo"]["EVENT"] += 1
                        elif filename.startswith("socket"):
                            self.procData[pid]["fdInfo"]["SOCKET"] += 1
                        elif filename.startswith("/dev"):
                            self.procData[pid]["fdInfo"]["DEVICE"] += 1
                        elif filename.startswith("pipe"):
                            self.procData[pid]["fdInfo"]["PIPE"] += 1
                        elif filename.startswith(SysMgr.procPath):
                            self.procData[pid]["fdInfo"]["PROC"] += 1
                        else:
                            self.procData[pid]["fdInfo"]["NORMAL"] += 1
                except SystemExit:
                    sys.exit(0)
                except:
                    self.nrFd -= 1
                    SysMgr.printOpenWarn(fdPath)

    def saveVmallocInfo(self):
        self.vmallocData = {}

        # read slab buf #
        vmBuf = SysMgr.readProcStat(
            SysMgr.vmallocFd, "vmallocinfo", SysMgr, "vmallocFd"
        )
        if not vmBuf:
            return

        # parse slab items #
        for line in vmBuf[2:]:
            try:
                # get items #
                items = line.strip().split()
                addr, size, alloc = items[:3]
                alloc = alloc.split("+", 1)[0]
                others = items[2:]

                # get type #
                if "vmalloc" in others:
                    mtype = "vmalloc"
                elif "ioremap" in others:
                    mtype = "ioremap"
                else:
                    mtype = others[-1]

                # add chunk size #
                name = "%s [%s]" % (alloc, mtype.strip("()"))
                self.vmallocData.setdefault(name, {"size": 0})
                self.vmallocData[name]["size"] += long(size)
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printWarn("failed to parse vmalloc info", reason=True)

    def saveDmabufInfo(self):
        if not "DMABUF" in SysMgr.environList:
            return

        self.dmabufData = SysMgr.readDmabufStats()

    def saveBuddyInfo(self):
        if not SysMgr.memEnable and not "ONLYBUDDY" in SysMgr.environList:
            return

        self.buddyData = SysMgr.readBuddyInfo()

    def saveKSMInfo(self):
        if not SysMgr.memEnable and not "ONLYKSM" in SysMgr.environList:
            return

        dpath = "/sys/kernel/mm/ksm"
        if not os.path.exists(dpath):
            return

        self.ksmData = {}

        # read KSM data #
        try:
            for item in sorted(os.listdir(dpath)):
                # check type #
                fpath = os.path.join(dpath, item)
                if os.path.isdir(fpath):
                    continue

                value = SysMgr.readFile(fpath)

                self.ksmData[item] = value
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to get KSM stats", True)

    def saveSlabInfo(self):
        if not SysMgr.memEnable and not "ONLYSLAB" in SysMgr.environList:
            return

        self.slabData = {}

        # read slab buf #
        slabBuf = SysMgr.readProcStat(
            SysMgr.slabFd, "slabinfo", SysMgr, "slabFd"
        )
        if not slabBuf:
            return False

        # parse slab items #
        for line in slabBuf[2:]:
            try:
                items = line.strip().split()[:6]
                name = items[0]
                actobj, totobj, objsize, objperslab, pageperslab = list(
                    map(long, items[1:])
                )

                self.slabData[name] = {
                    "size": objsize,
                    "active": actobj,
                    "actsize": actobj * objsize,
                    "total": totobj,
                    "totsize": totobj * objsize,
                    "ops": objperslab,
                    "pps": pageperslab,
                }
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printWarn("failed to parse slab info", reason=True)

        return True

    def saveVmInfo(self):
        if not SysMgr.memEnable and not "ONLYVM" in SysMgr.environList:
            return

        # read swappiness #
        try:
            SysMgr.swappiness = long(
                SysMgr.readProcStat(
                    SysMgr.swappinessFd,
                    "sys/vm/swappiness",
                    SysMgr,
                    "swappinessFd",
                )[0].rstrip()
            )
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # read vmpressure #
        try:
            SysMgr.vmpressure = long(
                SysMgr.readProcStat(
                    SysMgr.vmpressureFd,
                    "sys/vm/vfs_cache_pressure",
                    SysMgr,
                    "vmpressureFd",
                )[0].rstrip()
            )
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # read overcommit_memory #
        try:
            SysMgr.overcommit = long(
                SysMgr.readProcStat(
                    SysMgr.overcommitFd,
                    "sys/vm/overcommit_memory",
                    SysMgr,
                    "overcommitFd",
                )[0].rstrip()
            )
        except SystemExit:
            sys.exit(0)
        except:
            pass

    def saveZoneInfo(self):
        if not SysMgr.memEnable and not "ONLYZONE" in SysMgr.environList:
            return

        # read zone buf #
        memBuf = SysMgr.readProcStat(
            SysMgr.zoneFd, "zoneinfo", SysMgr, "zoneFd"
        )
        if not memBuf:
            return

        self.prevZoneData = self.zoneData
        self.zoneData = {}

        zone = None
        for line in memBuf:
            zl = line.split()
            item = zl[0]
            if item == "Node":
                zone = "%s-%s" % (zl[1][:-1], zl[3])
                self.zoneData[zone] = {}
            elif item == "pages" and zl[1] == "free":
                self.zoneData[zone]["free"] = long(zl[2])
            elif item in (
                "min",
                "low",
                "high",
                "spanned",
                "present",
                "managed",
            ):
                self.zoneData[zone][item] = long(zl[1])
            else:
                continue

    def printIrqs(self):
        diffList = {}

        # calculate diffs #
        for irq, total in self.irqData.items():
            if irq in self.prevIrqData:
                prevTotal = self.prevIrqData[irq]
                diffStats = [
                    a - b
                    for a, b in zip(
                        self.irqInfo[irq]["stats"],
                        self.prevIrqInfo[irq]["stats"],
                    )
                ]
            else:
                prevTotal = 0
                diffStats = self.irqInfo[irq]["stats"]

            diffList[irq] = {"total": total - prevTotal, "diff": diffStats}

        # get total count #
        totalCnt = sum([x["total"] for x in diffList.values()])

        # print title #
        SysMgr.addPrint(
            "[Top IRQ Info] [Time: %.6f] [NrIRQs: %s] [Total: %s] \n%s\n"
            % (
                SysMgr.uptime,
                UtilMgr.convNum(len(self.irqData)),
                UtilMgr.convNum(totalCnt),
                twoLine,
            ),
            newline=2,
        )
        title = "{0:>7} {1:>7} ".format("IRQ", "TOTAL")
        title += " ".join(
            [
                "{0:>5}".format("CPU%s" % core)
                for core in range(len(list(diffList.values())[0]["diff"]))
            ]
        )
        title += " Info"
        SysMgr.addPrint(title + "\n" + twoLine + "\n")

        # print per-core stats #
        for irq, data in sorted(
            diffList.items(), key=lambda e: e[1]["total"], reverse=True
        ):
            statstr = "{0:>7} {1:>7} ".format(
                irq, UtilMgr.convNum(data["total"])
            )
            statstr += " ".join(
                ["{0:>5}".format(stat) for stat in data["diff"]]
            )
            statstr += " %s\n" % (self.irqInfo[irq]["info"])
            SysMgr.addPrint(statstr)

        if len(diffList) == 0:
            SysMgr.addPrint("    None\n")
        SysMgr.addPrint("%s\n" % oneLine, force=True)

        SysMgr.printTopStats()

    def saveIrqs(self, full=False):
        # read irq buf #
        irqBuf = SysMgr.readProcStat(
            SysMgr.irqFd, "interrupts", SysMgr, "irqFd"
        )

        # read softirq buf #
        try:
            sirqBuf = SysMgr.readProcStat(
                SysMgr.softirqFd, "softirqs", SysMgr, "softirqFd"
            )
            irqBuf += sirqBuf[1:]
        except SystemExit:
            sys.exit(0)
        except:
            pass

        if not irqBuf:
            return

        # save previous data #
        self.prevIrqData = self.irqData
        self.irqData = {}
        self.prevIrqInfo = self.irqInfo
        self.irqInfo = {}

        # get cpu count #
        cpuCnt = len(irqBuf.pop(0).split())

        for line in irqBuf:
            try:
                irqList = line.split()
                name = irqList[0][:-1]
                irqSum = sum(list(map(long, irqList[1 : cpuCnt + 1])))
                if irqSum > 0:
                    self.irqData[name] = irqSum

                if not full:
                    continue

                self.irqInfo[name] = {
                    "stats": list(map(long, irqList[1 : cpuCnt + 1])),
                    "info": " ".join(irqList[cpuCnt + 1 :]),
                }
            except SystemExit:
                sys.exit(0)
            except:
                pass

    def saveCgroupStat(self, targetGroup=[]):
        def _getStats(root, path, sub, dftDepth, isVer2):
            # register subsystem #
            root.setdefault(sub, {})

            # get options #
            repComm = "REPCOMM" in SysMgr.environList
            repCmdline = "REPCMDLINE" in SysMgr.environList
            skipNoTask = "SKIPNOTASK" in SysMgr.environList

            for dirpath, subdirs, subfiles in path:
                # check depth #
                if SysMgr.funcDepth > 0:
                    depth = dirpath.count("/") - dftDepth
                    if depth >= SysMgr.funcDepth:
                        del subdirs[:]
                        continue

                # check no task group #
                if skipNoTask and "tasks" in subfiles:
                    taskfile = os.path.join(dirpath, "tasks")
                    fd = SysMgr.getCachedFd(taskfile, "r")
                    fd.seek(0)
                    stat = fd.read()
                    if not stat:
                        continue

                # update subfiles #
                for item in subfiles:
                    # check file #
                    if not item in ConfigMgr.CGROUP_STAT:
                        continue

                    # save stat #
                    try:
                        # convert name #
                        dpath = dirpath
                        if dpath in SysMgr.cgroupPathList:
                            dpath = ""
                        else:
                            for cpath in SysMgr.cgroupPathList:
                                dpath = UtilMgr.lstrip(dpath, cpath)
                                if dpath != dirpath:
                                    break

                        if not dpath:
                            dpath = "/"

                        subfile = os.path.join(dirpath, item)

                        # read stat #
                        fd = SysMgr.getCachedFd(subfile, "r")
                        try:
                            fd.seek(0)
                            stat = fd.read()
                        except SystemExit:
                            sys.exit(0)
                        except:
                            continue

                        # save stat #
                        root[sub].setdefault(dpath, {})
                        root[sub][dpath][item] = stat

                        # save pid list for group naming #
                        if (
                            isVer2 or repComm or repCmdline
                        ) and item == "cgroup.procs":
                            root[sub][dpath][item + "List"] = stat
                    except SystemExit:
                        sys.exit(0)
                    except:
                        SysMgr.printWarn(
                            "failed to read stat from %s" % subfile, True, True
                        )

        # reset and save cgroup instance #
        self.saveCgroupInstance()

        # save stats #
        for sub, val in sorted(SysMgr.cgroupPathList.items()):
            # check subsystem #
            if sub in self.cgroupData:
                continue

            # gather stats #
            isVer2 = False
            if val.startswith("cgroup2"):
                isVer2 = True
            elif not targetGroup:
                pass
            else:
                target = set(targetGroup) & set(val.split(","))
                if not target:
                    continue

            # get default depth #
            dftDepth = sub.count("/")

            _getStats(
                self.cgroupData,
                os.walk(sub),
                sub[sub.rfind("/", 1) + 1 :],
                dftDepth,
                isVer2,
            )

    def saveBinderData(self):
        # get path #
        try:
            spath = os.path.join(SysMgr.getDebugfsPath(), "binder/stats")
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn("failed to get path of binder stats", True, True)
            return

        # load stats #
        data = SysMgr.readFile(spath)
        if not data:
            return

        # parse stats #
        pid = None
        this = {}
        for l in data.split("\n"):
            try:
                if l.startswith(" "):
                    v = l.lstrip().split(":", 1)
                    if len(v) > 1:
                        n, v = v
                    else:
                        n, v = v[0].rsplit(" ", 1)
                    this[n] = v.strip()
                elif l.startswith("proc "):
                    pid = l.split(None, 1)[1]
                    this = self.binderData[pid] = {}
                elif l.startswith("context "):
                    this["context"] = l.split(None, 1)[1]
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printWarn("failed to get binder stat", True, True)

    def saveFileData(self):
        if not "RECFILE" in SysMgr.environList:
            return

        self.reportData.setdefault("file", {})

        # get size #
        size = UtilMgr.getEnvironNum("RECFILESIZE", False, 0, False, True)

        # save file data for this interval #
        for path in SysMgr.environList["RECFILE"]:
            TaskAnalyzer.fileIntData.setdefault(path, {})

            if os.path.isdir(path):
                curData = SysMgr.magicStr + "|".join(os.listdir(path))
                if path in TaskAnalyzer.dirLastData:
                    if TaskAnalyzer.dirLastData[path] == curData:
                        continue

                    # get item string #
                    lastData = set(
                        UtilMgr.lstrip(
                            TaskAnalyzer.dirLastData[path], SysMgr.magicStr
                        ).split("|")
                    )
                    TaskAnalyzer.dirLastData[path] = curData
                    curData = set(
                        UtilMgr.lstrip(curData, SysMgr.magicStr).split("|")
                    )

                    # get item list #
                    newItems = curData - lastData
                    delItems = lastData - curData

                    # build strings #
                    curData = ""
                    if newItems:
                        curData += "[+]%s " % ",".join(newItems)
                    if delItems:
                        curData += "[-]%s " % ",".join(delItems)
                else:
                    TaskAnalyzer.dirLastData[path] = curData
                    continue
            else:
                # update size #
                if size < 0:
                    tail = True
                    size = abs(size)
                else:
                    tail = False
                    if size == 0:
                        size = -1

                curData = repr(SysMgr.readFile(path, size, tail)).strip("'")
                if TaskAnalyzer.fileIntData[path]:
                    lastData = sorted(
                        TaskAnalyzer.fileIntData[path].items(),
                        key=lambda e: float(e[0]),
                    )[-1][1]
                    if lastData == curData:
                        continue

            TaskAnalyzer.fileIntData[path][SysMgr.uptime] = curData

    def saveProcStat(self):
        # get process list #
        if SysMgr.fixedProcList:
            if SysMgr.processEnable:
                pids = list(SysMgr.fixedProcList)
            else:
                pids = []
                for tid in SysMgr.fixedProcList:
                    pid = SysMgr.getPpid(tid, "Tgid")
                    if pid:
                        pids.append(pid)
        else:
            pids = SysMgr.getPidList()

        # reset and save proc instance #
        self.saveProcInstance()

        # get thread list #
        for pid in pids:
            try:
                nrPid = long(pid)
            except SystemExit:
                sys.exit(0)
            except:
                continue

            if self.maxPid < nrPid:
                self.maxPid = nrPid

            self.nrProcess += 1

            # set process path #
            procPath = "%s/%s" % (SysMgr.procPath, pid)

            # save info per process #
            if SysMgr.processEnable:
                if SysMgr.exceptCommFilter and not pid in SysMgr.filterGroup:
                    continue

                # save stat of process #
                ret = self.saveProcData(procPath, pid)

                # calculate number of threads #
                if pid in self.procData:
                    self.nrThread += long(
                        self.procData[pid]["stat"][self.nrthreadIdx]
                    )

                continue

            # set thread group path #
            taskPath = "%s/task" % procPath

            # save per-thread data #
            try:
                tids = os.listdir(taskPath)
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printOpenWarn(taskPath)
                continue

            for tid in tids:
                try:
                    nrTid = long(tid)
                except SystemExit:
                    sys.exit(0)
                except:
                    continue

                # check fixed tasks and main tasks #
                if SysMgr.fixedProcList:
                    if tid in SysMgr.fixedProcList:
                        pass
                    elif tid != pid:
                        continue

                if self.maxPid < nrTid:
                    self.maxPid = nrTid

                if SysMgr.exceptCommFilter and not tid in SysMgr.filterGroup:
                    continue

                self.nrThread += 1

                # set thread path #
                threadPath = "%s/%s" % (taskPath, tid)

                # save stat of thread #
                ret = self.saveProcData(threadPath, tid, pid)
                if not ret:
                    if tid in self.procData:
                        self.procData.pop(tid, None)
                    continue

                # main thread #
                if pid == tid:
                    self.procData[tid]["isMain"] = True
                    self.procData[tid]["tids"] = []
                    continue

                # sibling thread #
                try:
                    self.procData[pid]["tids"].append(tid)
                except:
                    self.procData[pid] = dict(self.init_procData)
                    self.procData[pid]["tids"] = []
                    self.procData[pid]["tids"].append(tid)

    def saveSystemStatGen(self):
        # get psutil object #
        psutil = SysMgr.getPkg("psutil")

        """
        python psutil APIs
        refer to https://psutil.readthedocs.io/en/latest
        """

        # update uptime #
        SysMgr.updateUptime()

        # CPU total #
        cpuStat = psutil.cpu_times(percpu=False)
        if cpuStat:
            self.cpuData["all"] = {
                "user": cpuStat[0],
                "nice": cpuStat[1],
                "system": cpuStat[2],
                "idle": cpuStat[3],
                "iowait": 0,
                "irq": 0,
                "softirq": 0,
            }

        # CPU cores #
        if SysMgr.nrCore == 0:
            nrCore = psutil.cpu_count(logical=True)
            if nrCore:
                SysMgr.nrCore = psutil.cpu_count(logical=True)
        cpuStat = psutil.cpu_times(percpu=True)
        if not cpuStat:
            cpuStat = []
        for idx, cpu in enumerate(cpuStat):
            self.cpuData[idx] = {}
            # linux #
            if SysMgr.isLinux:
                self.cpuData[idx]["user"] = cpu[0] * 100
                self.cpuData[idx]["nice"] = cpu[1] * 100
                self.cpuData[idx]["system"] = cpu[2] * 100
                self.cpuData[idx]["idle"] = cpu[3] * 100
                self.cpuData[idx]["iowait"] = cpu[4] * 100
                self.cpuData[idx]["irq"] = cpu[5] * 100
                self.cpuData[idx]["softirq"] = cpu[6] * 100
            # MacOS #
            elif SysMgr.isDarwin:
                self.cpuData[idx]["user"] = cpu[0] * 100
                self.cpuData[idx]["nice"] = cpu[1] * 100
                self.cpuData[idx]["system"] = cpu[2] * 100
                self.cpuData[idx]["idle"] = cpu[3] * 100
                self.cpuData[idx]["iowait"] = 0
                self.cpuData[idx]["irq"] = 0
                self.cpuData[idx]["softirq"] = 0
            # windows #
            else:
                self.cpuData[idx]["user"] = cpu[0] * 100
                self.cpuData[idx]["nice"] = 0
                self.cpuData[idx]["system"] = cpu[1] * 100
                self.cpuData[idx]["idle"] = cpu[2] * 100
                self.cpuData[idx]["iowait"] = 0
                self.cpuData[idx]["irq"] = cpu[3] * 100
                self.cpuData[idx]["softirq"] = 0

        # CPU events #
        cpuEvents = psutil.cpu_stats()
        self.cpuData["ctxt"] = {"ctxt": cpuEvents[0]}
        self.cpuData["intr"] = {"intr": cpuEvents[1]}
        self.cpuData["softirq"] = {"softirq": cpuEvents[2]}
        self.cpuData["syscall"] = {"syscall": cpuEvents[3]}

        # load #
        try:
            SysMgr.loadavg = psutil.getloadavg()
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # memory #
        try:
            self.prevVmData = self.vmData
            self.vmData = {}
            self.prevMemData = self.memData
            self.memData = {}

            mem = psutil.virtual_memory()
            self.memData["MemTotal"] = self.vmData["total"] = mem[0] >> 10
            self.memData["MemAvailable"] = self.vmData["available"] = (
                mem[1] >> 10
            )
            self.vmData["percent"] = mem[2]
            self.vmData["used"] = mem[3]
            self.memData["MemFree"] = self.vmData["free"] = mem[4] >> 10
            self.vmData["active"] = mem[5]
            self.vmData["inactive"] = mem[6]
            self.vmData["wired"] = mem[7]
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # swap #
        try:
            swap = psutil.swap_memory()
            self.memData["SwapTotal"] = self.vmData["swapTotal"] = (
                swap[0] >> 10
            )
            self.vmData["swapUsed"] = swap[1] >> 10
            self.vmData["swapFree"] = swap[2] >> 10
            self.vmData["swapPer"] = swap[3]
            self.vmData["pswpin"] = swap[4] >> 10
            self.vmData["pswpout"] = swap[5] >> 10
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # disk #
        if SysMgr.diskEnable:
            SysMgr.sysInstance.updateStorageInfoGen()

        # network #
        SysMgr.prevNetstat = SysMgr.netstat
        netStat = psutil.net_io_counters(pernic=False, nowrap=True)
        if netStat:
            SysMgr.netstat = [netStat[1], netStat[0]]
        if SysMgr.networkEnable:
            SysMgr.sysInstance.updateNetworkInfoGen()

        # temperature #
        try:
            pass
            # temp = psutil.sensors_temperatures(fahrenheit=False)
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # fan #
        try:
            pass
            # fan = psutil.sensors_fans()
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # battery #
        try:
            battery = psutil.sensors_battery()
            if battery:
                SysMgr.battery = battery
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # users #
        """
        try:
            user = psutil.users()
        except SystemExit:
            sys.exit(0)
        except:
            pass
        """

        # check system-only monitoring #
        if not SysMgr.taskEnable:
            return

        # process #
        self.saveProcStatGen()

    def saveProcStatGen(self, addList=[], filterList=None):
        # get psutil object #
        psutil = SysMgr.getPkg("psutil")

        # get the number of processes #
        pidList = sorted(psutil.pids())
        self.cpuData["processes"] = {"processes": 0}
        self.cpuData["processes"]["processes"] = len(pidList)
        self.nrProcess = len(pidList)
        SysMgr.maxPid = pidList[-1]

        # get current time #
        current = time.time()

        # reset and save proc instance #
        self.saveProcInstance()

        attrs = [
            "pid",
            "name",
            "ppid",
            "exe",
            "nice",
            "num_threads",
            "cpu_times",
            "memory_info",
            "num_ctx_switches",
            "cmdline",
            "create_time",
            "username",
        ] + addList

        # append num_fds field #
        if SysMgr.isLinux or SysMgr.isDarwin:
            attrs.append("num_fds")

        if filterList is None:
            pass
        else:
            filterList = SysMgr.filterGroup

        # define shortcut #
        now = self.procData
        prev = self.prevProcData

        # psutil.Process(pid=os.getpid())
        # psutil.pid_exists(pid=os.getpid())
        procs = psutil.process_iter(attrs=attrs, ad_value=None)
        for proc in procs:
            procInfo = proc.info
            pid = procInfo["pid"]
            comm = procInfo["name"]

            # check skip condition #
            if pid == 0:
                continue
            elif filterList:
                if not str(pid) in filterList and not UtilMgr.isValidStr(
                    comm, filterList
                ):
                    # increase total thread count #
                    if "num_threads" in procInfo and procInfo["num_threads"]:
                        self.nrThread += procInfo["num_threads"]
                    continue

            # info #
            now[pid] = dict(self.init_procData)
            now[pid]["mainID"] = pid
            now[pid]["comm"] = comm
            now[pid]["ppid"] = procInfo["ppid"]
            now[pid]["exe"] = procInfo["exe"]
            now[pid]["starttime"] = procInfo["create_time"]
            now[pid]["runtime"] = current - procInfo["create_time"]
            now[pid]["fdList"] = {}

            # cmdline #
            if procInfo["cmdline"]:
                now[pid]["cmdline"] = " ".join(procInfo["cmdline"])
            else:
                now[pid]["cmdline"] = None

            # priority #
            if not procInfo["nice"]:
                now[pid]["nice"] = " "
            else:
                if SysMgr.isWindows:
                    try:
                        # pylint: disable-next=bad-str-strip-call
                        pri = str(procInfo["nice"]).strip("Priority.")
                        now[pid]["nice"] = ConfigMgr.SCHED_POLICY_WINDOWS[pri]
                    except SystemExit:
                        sys.exit(0)
                    except:
                        now[pid]["nice"] = " "
                else:
                    now[pid]["nice"] = procInfo["nice"]

            # thread count #
            if "num_threads" in procInfo and procInfo["num_threads"]:
                now[pid]["nrThread"] = procInfo["num_threads"]
                self.nrThread += procInfo["num_threads"]
            else:
                now[pid]["nrThread"] = 1
                self.nrThread += 1

            # file count #
            if "num_fds" in procInfo and procInfo["num_fds"]:
                now[pid]["nrFds"] = procInfo["num_fds"]
            else:
                now[pid]["nrFds"] = 0

            # user #
            if "username" in procInfo:
                now[pid]["user"] = procInfo["username"]
            else:
                now[pid]["user"] = ""

            # CPU #
            if "cpu_times" in procInfo and procInfo["cpu_times"]:
                utime = now[pid]["utime"] = procInfo["cpu_times"][0]
                if not utime:
                    utime = 0
                stime = now[pid]["stime"] = procInfo["cpu_times"][1]
                if not stime:
                    stime = 0

                if pid in prev:
                    now[pid]["utimeDiff"] = utime - prev[pid]["utime"]
                    now[pid]["stimeDiff"] = stime - prev[pid]["stime"]
                    now[pid]["ttimeDiff"] = (
                        now[pid]["utimeDiff"] + now[pid]["stimeDiff"]
                    )
                else:
                    now[pid]["utimeDiff"] = utime
                    now[pid]["stimeDiff"] = stime
                    now[pid]["ttimeDiff"] = utime + stime
            else:
                now[pid]["utime"] = now[pid]["utimeDiff"] = 0
                now[pid]["stime"] = now[pid]["stimeDiff"] = 0
                now[pid]["ttime"] = now[pid]["ttimeDiff"] = 0

            # Memory #
            if "memory_info" in procInfo and procInfo["memory_info"]:
                now[pid]["rss"] = procInfo["memory_info"][0] >> 20  # MB #
                now[pid]["vss"] = procInfo["memory_info"][1] >> 30  # GB #
                nrpgflt = now[pid]["nrpgflt"] = procInfo["memory_info"][2]
                if pid in prev:
                    now[pid]["nrpgfltDiff"] = nrpgflt - prev[pid]["nrpgflt"]
                else:
                    now[pid]["nrpgfltDiff"] = nrpgflt
            else:
                now[pid]["rss"] = 0
                now[pid]["vss"] = 0
                now[pid]["nrpgfltDiff"] = now[pid]["nrpgflt"] = 0

            # Context switch #
            if "num_ctx_switches" in procInfo and procInfo["num_ctx_switches"]:
                nryield = now[pid]["yield"] = procInfo["num_ctx_switches"][0]
                nrpreempted = now[pid]["preempted"] = procInfo[
                    "num_ctx_switches"
                ][1]

                if pid in prev:
                    now[pid]["yieldDiff"] = nryield - prev[pid]["yield"]
                    now[pid]["preemptedDiff"] = (
                        nrpreempted - prev[pid]["preempted"]
                    )
                else:
                    now[pid]["yieldDiff"] = nryield
                    now[pid]["preemptedDiff"] = nrpreempted
            else:
                now[pid]["yield"] = now[pid]["yieldDiff"] = 0
                now[pid]["preempted"] = now[pid]["preemptedDiff"] = 0

            if "open_files" in attrs and procInfo["open_files"]:
                now[pid]["open_files"] = procInfo["open_files"]

            # TODO: proc.io_counters() on windows #

            continue

            # control #
            proc.is_running()
            proc.send_signal(0)
            proc.suspend()
            proc.resume()
            proc.terminate()
            proc.kill()
            proc.wait()

    def saveVmData(self):
        # vmstat list from https://access.redhat.com/solutions/406773 #
        self.prevVmData = self.vmData
        self.vmData = {}
        vmBuf = SysMgr.readProcStat(
            SysMgr.vmstatFd, "vmstat", SysMgr, "vmstatFd"
        )
        if vmBuf:
            for line in vmBuf:
                vmList = line.split()
                self.vmData[vmList[0]] = long(vmList[1])

    def saveSystemStat(self, saveProc=True):
        # update uptime #
        SysMgr.updateUptime()

        # read CPU buf #
        cpuBuf = SysMgr.readProcStat(
            SysMgr.statFd, "stat", SysMgr, "statFd", True
        )

        # stat list from http://man7.org/linux/man-pages/man5/proc.5.html #
        if cpuBuf:
            for line in cpuBuf:
                statList = line.split()
                cpuId = statList[0]
                if cpuId == "cpu":
                    if not "all" in self.cpuData:
                        self.cpuData["all"] = {
                            "user": long(statList[1]),
                            "nice": long(statList[2]),
                            "system": long(statList[3]),
                            "idle": long(statList[4]),
                            "iowait": long(statList[5]),
                            "irq": long(statList[6]),
                            "softirq": long(statList[7]),
                            "steal": long(statList[8]),
                        }
                elif cpuId.startswith("cpu"):
                    if not long(cpuId[3:]) in self.cpuData:
                        self.cpuData[int(cpuId[3:])] = {
                            "user": long(statList[1]),
                            "nice": long(statList[2]),
                            "system": long(statList[3]),
                            "idle": long(statList[4]),
                            "iowait": long(statList[5]),
                            "irq": long(statList[6]),
                            "softirq": long(statList[7]),
                            "steal": long(statList[8]),
                        }
                elif not cpuId in self.cpuData:
                    self.cpuData[cpuId] = {cpuId: long(statList[1])}

            # set the number of core #
            SysMgr.nrCore = 0
            for idx in list(self.cpuData):
                try:
                    SysMgr.maxCore = long(idx)
                    SysMgr.nrCore += 1
                except SystemExit:
                    sys.exit(0)
                except:
                    continue

        # save mem info #
        memBuf = SysMgr.getMemInfo()
        if memBuf:
            self.prevMemData = self.memData
            self.memData = {}

            for line in memBuf:
                memList = line.split()
                self.memData[memList[0][:-1]] = long(memList[1])

        # save irq info #
        if SysMgr.irqEnable:
            self.saveIrqs()

        # save memory info #
        if not "NOSYSMEM" in SysMgr.environList:
            self.saveBuddyInfo()
            self.saveKSMInfo()
            self.saveSlabInfo()
            self.saveVmInfo()
            self.saveZoneInfo()
            self.saveDmabufInfo()

        # save GPU memory info #
        self.gpuMemData, _ = SysMgr.getGpuMem()

        # read vmstat buf #
        self.saveVmData()

        # read swap buf #
        swapBuf = SysMgr.readProcStat(SysMgr.swapFd, "swaps", SysMgr, "swapFd")
        # get swap usage if it changed #
        if self.prevSwaps != swapBuf and swapBuf:
            swapTotal = 0
            swapUsed = 0

            for line in swapBuf:
                swapList = line.split()
                # swapList = [Filename, Type, Size, Used, Priority] #
                try:
                    swapTotal += long(swapList[2])
                    swapUsed += long(swapList[3])
                except:
                    continue

            self.vmData["swapTotal"] = swapTotal
            self.vmData["swapUsed"] = swapUsed

            self.prevSwaps = swapBuf
        else:
            try:
                self.vmData["swapTotal"] = self.prevVmData["swapTotal"]
                self.vmData["swapUsed"] = self.prevVmData["swapUsed"]
            except:
                self.vmData["swapTotal"] = 0
                self.vmData["swapUsed"] = 0

        # save diskstats #
        SysMgr.updateDiskStats()

        # save netstat #
        try:
            SysMgr.netstatFd.seek(0)
            SysMgr.prevNetstat = SysMgr.netstat
            SysMgr.netstat = SysMgr.netstatFd.readlines()
        except SystemExit:
            sys.exit(0)
        except:
            try:
                netstatPath = "%s/%s" % (SysMgr.procPath, "net/netstat")
                SysMgr.netstatFd = open(netstatPath, "r")
                SysMgr.netstat = SysMgr.netstatFd.readlines()
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printOpenWarn(netstatPath)

        # read loadavg buf #
        try:
            SysMgr.loadavg = SysMgr.readProcStat(
                SysMgr.loadavgFd, "loadavg", SysMgr, "loadavgFd"
            )[0]
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # save battery #
        SysMgr.updateBatInfo()

        # collect perf data #
        if SysMgr.perfEnable:
            SysMgr.collectSystemPerfData()

        # save GPU stat #
        self.saveGpuData()

        # save PSI stat #
        if SysMgr.psiEnable:
            SysMgr.prevPsiData = SysMgr.psiData
            SysMgr.psiData = SysMgr.readPsiStats()

        # save file data #
        if self.prevMemData:
            self.saveFileData()

        # save binder stats #
        if "BINDERSTAT" in SysMgr.environList:
            self.saveBinderData()

        # check system-only monitoring #
        if not SysMgr.taskEnable:
            return

        # save task stats #
        if saveProc:
            self.saveProcStat()

        # save namespace leader tasks #
        for ns, data in self.nsData.items():
            if not data:
                continue
            self.nsProcData[ns] = {}
            for nid, proc in data.items():
                try:
                    tid = [
                        tid
                        for tid, start in sorted(
                            proc.items(), key=lambda e: long(e[1])
                        )
                    ][0]
                    self.nsProcData[ns][nid] = "%s(%s)" % (
                        self.procData[tid]["comm"],
                        tid,
                    )
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

    @staticmethod
    def getProcTreeFromList(procInstance, incKernel=True):
        procTree = {}
        ppidIdx = ConfigMgr.PROCSTAT_ATTR.index("PPID")

        # get a relation list to track ancestors of process #
        def _getRelationList(item, procInstance):
            tmpid = item
            relationList = []

            while 1:
                try:
                    if not tmpid in relationList:
                        relationList.insert(0, tmpid)

                    # add main thread ID #
                    if not procInstance[item]["isMain"]:
                        mainid = procInstance[item]["mainID"]
                        if not mainid in relationList:
                            relationList.insert(0, mainid)

                    # add parent process ID #
                    orig = tmpid
                    if SysMgr.isLinux:
                        tmpid = procInstance[tmpid]["stat"][ppidIdx]
                    else:
                        tmpid = procInstance[tmpid]["ppid"]

                    if long(tmpid) == 0 or orig == tmpid:
                        return relationList
                except SystemExit:
                    sys.exit(0)
                except:
                    return relationList

        # add items in relation list to tree #
        def _addItemsToList(relationList, procTree):
            nodePointer = procTree
            for item in relationList:
                try:
                    nodePointer[item]
                except:
                    nodePointer[item] = {}
                nodePointer = nodePointer[item]

        # make dictionary for tree #
        if SysMgr.isLinux:
            vssIdx = ConfigMgr.PROCSTAT_ATTR.index("VSIZE")
            startIdx = ConfigMgr.PROCSTAT_ATTR.index("STARTTIME")

            for pid, item in sorted(
                procInstance.items(),
                key=lambda e: long(e[1]["stat"][startIdx]),
            ):
                # check kernel thread #
                if not incKernel:
                    if procInstance[pid]["stat"][vssIdx] == "0":
                        continue

                # get parent ID #
                ppid = procInstance[pid]["stat"][ppidIdx]

                # add task #
                if ppid == "0":
                    procTree[pid] = {}
                else:
                    relationList = _getRelationList(pid, procInstance)
                    _addItemsToList(relationList, procTree)
        else:
            for pid, item in sorted(
                procInstance.items(), key=lambda e: long(e[1]["starttime"])
            ):
                ppid = procInstance[pid]["ppid"]

                # add task #
                if ppid == "0":
                    procTree[pid] = {}
                else:
                    relationList = _getRelationList(pid, procInstance)
                    _addItemsToList(relationList, procTree)

        return procTree

    @staticmethod
    def readProcMemStats(path, tid, retPss=True, retShr=False):
        # check root permission #
        if not SysMgr.isRoot():
            return 0, 0

        buf = ""
        fpath = "%s/%s" % (path, "smaps_rollup")
        if not os.path.exists(fpath):
            fpath = "%s/%s" % (path, "smaps")

        # get both PSS and SHARED #
        if retPss and retShr:
            with open(fpath, "r") as fd:
                buf = fd.readlines()
                if not buf:
                    return 0, 0

            pss = shared = 0
            for line in buf:
                try:
                    if retPss and line.startswith("Pss:"):
                        val = line.split()[1]
                        if val != "0":
                            pss += long(val)
                    elif retShr and (
                        line.startswith("Shared_Clean:")
                        or line.startswith("Shared_Dirty:")
                    ):
                        val = line.split()[1]
                        if val != "0":
                            shared += long(val)
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printWarn(
                        "failed to get memory stats for the task having TID %s"
                        % tid,
                        reason=True,
                    )

            return pss, shared

        # set re string #
        if retPss:
            restr = "\nPss:\s+"
        elif retShr:
            restr = "\nShared_Clean:\s+|\nShared_Dirty:\s+"
        else:
            return 0, 0

        # read maps data #
        target = 0
        try:
            buf = None
            with open(fpath, "r") as fd:
                buf = fd.read()
        except SystemExit:
            sys.exit(0)
        except:
            if not buf:
                return 0, 0

        # parse target sizes #
        for line in re.split(restr, buf):
            if line.startswith("0"):
                continue

            try:
                target += long(line.split(None, 1)[0])
            except SystemExit:
                sys.exit(0)
            except:
                pass

        # return stat #
        if retPss:
            return target, 0
        else:
            return 0, target

    @staticmethod
    def getProcAnonMap(
        tid,
        onlyStart=False,
        target="Anonymous",
        cache=False,
        fpath=None,
        total=False,
    ):
        # check root permission #
        if not SysMgr.isRoot():
            return {}

        # make path #
        if not fpath:
            fpath = "%s/%s/smaps" % (SysMgr.procPath, tid)

        # check cache #
        if cache:
            cachedMap = PageAnalyzer.mapFileCache.get(fpath)
            if cachedMap:
                return cachedMap

        try:
            with open(fpath, "r") as fd:
                buf = fd.readlines()
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printOpenWarn(fpath)
            return {}

        # check buf #
        if not buf:
            return {}

        maps = {}
        current = {}

        for line in buf:
            try:
                # attr #
                if line[0].isupper():
                    if not current:
                        continue
                    elif total and line.startswith("Size"):
                        current["total"] += long(line.split()[1])
                    elif line.startswith(target):
                        current["size"] += long(line.split()[1])
                # map #
                else:
                    tmplist = line.split(" ", 1)
                    addr = tmplist[0]
                    if onlyStart:
                        addr = long(addr.split("-", 1)[0], 16)
                    current = maps[addr] = {
                        "attr": tmplist[1],
                        "size": 0,
                        "total": 0,
                    }
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printWarn(
                    "failed to parse map '%s'" % line, reason=True
                )

        # save cache #
        if cache:
            PageAnalyzer.mapFileCache[fpath] = maps

        return maps

    @staticmethod
    def saveProcSmaps(
        path, tid, mini=False, vmflags=[], procData=None, rollup=False
    ):
        # check root permission #
        if not SysMgr.isRoot():
            return

        # make path #
        if not path:
            path = "%s/%s" % (SysMgr.procPath, tid)

        buf = ""
        mtype = ""
        stable = {}
        ftable = {}
        isInaccessible = False

        fpath = None
        if rollup:
            fpath = "%s/%s" % (path, "smaps_rollup")
            fpath = fpath if os.path.exists(fpath) else None
        if not fpath:
            fpath = "%s/%s" % (path, "smaps")

        ptable = {
            "ANON": {},
            "HEAP": {},
            "FILE": {},
            "STACK": {},
            "OTHER": {},
            "SHM": {},
            "FLAG": {},
        }

        checklist = (
            "Size:",
            "Rss:",
            "Pss:",
            "Shared_Clean:",
            "Shared_Dirty:",
        )
        if not mini:
            checklist += (
                "Private_Dirty:",
                "Referenced:",
                "AnonHugePages:",
                "Swap:",
                "Locked:",
            )
        if vmflags:
            checklist += ("VmFlags:",)

        # get global procData #
        if not procData:
            procData = SysMgr.procInstance

        # share the map table for main thread #
        try:
            ppid = procData[tid]["mainID"]
            pmaps = procData[ppid].get("maps")
            if pmaps:
                procData[tid]["maps"] = pmaps
                return
        except SystemExit:
            sys.exit(0)
        except:
            pass

        try:
            procData[tid]["maps"] = ptable
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn("failed to find the process with PID %s" % tid)
            return

        try:
            with open(fpath, "r") as fd:
                buf = fd.readlines()
        except SystemExit:
            sys.exit(0)
        except:
            procData[tid]["maps"] = None
            SysMgr.printOpenWarn(fpath)
            return

        # check buf #
        if not buf:
            return

        lastDict = {}

        for line in buf:
            tmplist = line.split()

            # memory map info #
            if not line[0].isupper():
                perm = tmplist[1]

                """
                range = tmplist[0]
                offset = tmplist[2]
                devid = tmplist[3]
                inode = tmplist[4]
                """

                if len(tmplist) > 5:
                    ptype = tmplist[5]
                else:
                    ptype = ""

                # shared memory #
                if perm[3] == "s":
                    mtype = "SHM"
                    stable[ptype] = 0
                # file-mapped memory #
                elif ptype.startswith("/"):
                    mtype = "FILE"
                    ftable[ptype] = 0
                # heap #
                elif ptype.startswith("[heap]"):
                    mtype = "HEAP"
                # anonymous memory #
                elif ptype == "" or ptype.startswith("[anon"):
                    mtype = "ANON"
                # stack memory #
                elif ptype.startswith("[stack"):
                    mtype = "STACK"
                else:
                    mtype = "OTHER"

                # check inaccessible area #
                isInaccessible = perm.startswith("---")

                try:
                    ptable[mtype]["count"] += 1
                except:
                    ptable[mtype]["count"] = 1
            # memory detail info #
            else:
                # handle vmflags #
                if vmflags:
                    if tmplist[0] == "VmFlags:":
                        if not set(vmflags) & set(tmplist[1:]):
                            continue

                        for item in ("Rss:", "Pss:", "Size:"):
                            if not item in lastDict:
                                continue

                            # save size #
                            ptable["FLAG"].setdefault(item, 0)
                            ptable["FLAG"].setdefault("count", 0)
                            ptable["FLAG"]["count"] += 1
                            ptable["FLAG"][item] += long(lastDict[item])

                        continue
                    else:
                        lastDict[tmplist[0]] = tmplist[1]

                # skip 0 size attributes #
                if tmplist[-2] == "0":
                    continue

                # skip useless attributes #
                prop = tmplist[0]
                if not prop in checklist:
                    continue

                try:
                    val = long(tmplist[1])

                    try:
                        ptable[mtype][prop] += val
                    except:
                        ptable[mtype][prop] = val

                    if isInaccessible:
                        try:
                            ptable[mtype]["NOPM"] += val
                        except:
                            ptable[mtype]["NOPM"] = val
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

        # save the number of mapping #
        ptable["FILE"]["count"] = len(ftable)
        ptable["SHM"]["count"] = len(stable)

        # share the map table for main thread #
        try:
            ppid = procData[tid]["mainID"]
            procData[ppid]["maps"] = procData[tid]["maps"]
        except:
            pass

    def saveProcWchanData(self, path, tid):
        if not SysMgr.isRoot():
            self.procData[tid]["wchan"] = "EPERM"
            return

        wchanBuf = self.saveTaskData(path, tid, "wchan")

        try:
            if wchanBuf[0] == "0":
                self.procData[tid]["wchan"] = "RUNNING"
            else:
                self.procData[tid]["wchan"] = wchanBuf[0]
        except SystemExit:
            sys.exit(0)
        except:
            self.procData[tid]["wchan"] = ""

    def saveGpuData(self):
        try:
            if not SysMgr.gpuEnable or not self.gpuCoreList:
                return
        except:
            self.gpuCoreList = {}
            self.gpuNameList = {}

        devList = ["/sys/devices", "/sys/class"]

        # initialize candidates for GPU devices #
        if not self.gpuCoreList:
            candList = self.gpuCoreList
            for devPath in devList:
                # check permission #
                if not os.access(devPath, os.R_OK) or not os.path.isdir(
                    devPath
                ):
                    continue

                for targetDir in os.listdir(devPath):
                    path = "%s/%s" % (devPath, targetDir)
                    if path in candList:
                        continue

                    # get node list #
                    if os.access(path, os.R_OK) and os.path.isdir(path):
                        nodes = os.listdir(path)
                    else:
                        continue

                    # NVIDIA tegra #
                    if "devfreq" in nodes:
                        candList[path] = {}
                        self.gpuNameList[path] = "NVIDIA"

                    # QUALCOMM #
                    if "kgsl-3d0" in nodes:
                        name = "%s/%s/devfreq" % (path, "kgsl-3d0")
                        candList[name] = {}
                        self.gpuNameList[name] = "QUALCOMM"

        # no GPU supported #
        if not self.gpuCoreList:
            SysMgr.gpuEnable = False
            return

        # read GPU stat from list #
        for cand, value in self.gpuCoreList.items():
            try:
                target = None

                # set GPU name #
                gpuName = self.gpuNameList[cand]

                # save device info for NVIDIA #
                if gpuName.startswith("NVIDIA"):
                    devName = cand[cand.rfind("/") + 1 :]
                    target = "%s/%s" % (gpuName, devName)
                    self.gpuData[target] = {}
                    nodePath = "%s/devfreq/%s" % (cand, devName)

                    if not "uevent" in value:
                        self.gpuCoreList[cand]["uevent"] = open(
                            "%s/uevent" % cand, "r"
                        )
                    fd = self.gpuCoreList[cand]["uevent"]
                    fd.seek(0)

                    for item in fd.readlines():
                        attr, value = item[:-1].split("=")
                        self.gpuData[target][attr] = value

                    # save GPU device load #
                    if not "load" in value:
                        self.gpuCoreList[cand]["load"] = open(
                            "%s/load" % cand, "r"
                        )
                    fd = self.gpuCoreList[cand]["load"]
                    fd.seek(0)
                    self.gpuData[target]["CUR_LOAD"] = (
                        long(fd.readline()[:-1]) / 10
                    )

                # save device info for QUALCOMM #
                elif gpuName.startswith("QUALCOMM"):
                    realCand = cand.rstrip("/devfreq")
                    devName = realCand[realCand.rfind("/") + 1 :]
                    target = "%s/%s" % (gpuName, devName)
                    self.gpuData[target] = {}
                    nodePath = cand

                    # save GPU device load #
                    value = {}
                    try:
                        self.gpuCoreList[cand]["load"] = open(
                            "%s/gpu_busy_percentage" % realCand, "r"
                        )
                        fd = self.gpuCoreList[cand]["load"]
                        fd.seek(0)
                        self.gpuData[target]["CUR_LOAD"] = long(
                            fd.readline()[:-3]
                        )
                    except SystemExit:
                        sys.exit(0)
                    except:
                        self.gpuCoreList[cand]["load"] = open(
                            "%s/gpu_load" % cand, "r"
                        )
                        fd = self.gpuCoreList[cand]["load"]
                        fd.seek(0)
                        self.gpuData[target]["CUR_LOAD"] = long(
                            fd.readline()[:-1]
                        )

                # save current clock of GPU device #
                if not "curfreq" in value:
                    self.gpuCoreList[cand]["curfreq"] = open(
                        "%s/cur_freq" % nodePath, "r"
                    )
                fd = self.gpuCoreList[cand]["curfreq"]
                fd.seek(0)
                self.gpuData[target]["CUR_FREQ"] = (
                    long(fd.readline()[:-1]) / 1000000
                )

                # save min clock of GPU device #
                if not "minfreq" in value:
                    self.gpuCoreList[cand]["minfreq"] = open(
                        "%s/min_freq" % nodePath, "r"
                    )
                fd = self.gpuCoreList[cand]["minfreq"]
                fd.seek(0)
                self.gpuData[target]["MIN_FREQ"] = (
                    long(fd.readline()[:-1]) / 1000000
                )

                # save max clock of GPU device #
                if not "maxfreq" in value:
                    self.gpuCoreList[cand]["maxfreq"] = open(
                        "%s/max_freq" % nodePath, "r"
                    )
                fd = self.gpuCoreList[cand]["maxfreq"]
                fd.seek(0)
                self.gpuData[target]["MAX_FREQ"] = (
                    long(fd.readline()[:-1]) / 1000000
                )
            except SystemExit:
                sys.exit(0)
            except:
                pass

    def saveProcSchedData(self, path, tid):
        self.procData[tid]["execTime"] = 0
        self.procData[tid]["waitTime"] = 0

        if SysMgr.minStatEnable or not SysMgr.schedstatEnable:
            return

        try:
            schedBuf = self.saveTaskData(path, tid, "schedstat")
            if not schedBuf:
                if not os.path.exists("%s/self/schedstat" % SysMgr.procPath):
                    SysMgr.schedstatEnable = False
                return

            SCHED_POLICY = schedBuf[0].split()
            self.procData[tid]["execTime"] = float(SCHED_POLICY[0])
            self.procData[tid]["waitTime"] = float(SCHED_POLICY[1])
            self.procData[tid]["nrSlice"] = float(SCHED_POLICY[2])
        except SystemExit:
            sys.exit(0)
        except:
            return

    def isKernelThread(self, tid):
        if self.procData[tid]["stat"][self.vssIdx] == "0":
            return True
        else:
            return False

    def saveExeData(self, path, tid):
        if not SysMgr.exeEnable:
            return

        exePath = self.procData[tid].get("exe")
        if exePath:
            return exePath

        if not path:
            path = "%s/%s" % (SysMgr.procPath, tid)

        # check kernel thread #
        if self.isKernelThread(tid):
            self.procData[tid]["exe"] = ""
            return

        # check main thread to remove redundant operation #
        isThreadMode = SysMgr.checkMode("ttop")
        if isThreadMode:
            mainID = self.procData[tid]["mainID"]
            if mainID in self.procData and "exe" in self.procData[mainID]:
                self.procData[tid]["exe"] = self.procData[mainID]["exe"]
                return

        # save exe info #
        self.procData[tid]["exe"] = SysMgr.getExeName(tid)

        if isThreadMode and mainID in self.procData:
            self.procData[mainID]["exe"] = self.procData[tid]["exe"]

    def saveCmdlineData(self, path, tid):
        if not SysMgr.cmdlineEnable:
            return

        cmdline = self.procData[tid].get("cmdlin")
        if cmdline:
            return cmdline

        if not path:
            path = "%s/%s" % (SysMgr.procPath, tid)

        # check kernel thread #
        if self.isKernelThread(tid):
            self.procData[tid]["cmdline"] = ""
            return

        # check main thread to remove redundant operation #
        isThreadMode = SysMgr.checkMode("ttop")
        if isThreadMode:
            mainID = self.procData[tid]["mainID"]
            if mainID in self.procData and "cmdline" in self.procData[mainID]:
                self.procData[tid]["cmdline"] = self.procData[mainID][
                    "cmdline"
                ]
                return

        # save cmdline info #
        self.procData[tid]["cmdline"] = SysMgr.getCmdline(tid)

        if isThreadMode and mainID in self.procData:
            self.procData[mainID]["cmdline"] = self.procData[tid]["cmdline"]

    def saveTaskData(self, path, tid, name, decode=True, retry=True):
        buf = []

        try:
            fd = "%sFd" % name
            self.procData[tid][fd] = self.prevProcData[tid][fd]
            while 1:
                self.prevProcData[tid][fd].seek(0)
                buf = self.procData[tid][fd].readlines()
                # retry to read for shared descriptor #
                if buf or not retry:
                    break
        except SystemExit:
            sys.exit(0)
        except:
            try:
                newPath = "%s/%s" % (path, name)
                newFd = self.procData[tid][fd] = open(newPath, "rb")
                buf = newFd.readlines()

                # fd resource is about to run out #
                if SysMgr.maxKeepFd < newFd.fileno():
                    newFd.close()
                    self.procData[tid][fd] = None
                    self.reclaimFds()
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printOpenWarn(newPath)

        # decode data #
        if decode and sys.version_info >= (3, 0):
            buf = list(map(lambda x: x.decode(), buf))

        return buf

    def saveCgroupData(self, path, tid):
        if not SysMgr.cgroupEnable and not SysMgr.cgroupRootEnable:
            return

        # save cgroup data #
        cgBuf = self.saveTaskData(path, tid, "cgroup")

        # get cgroup filter #
        cgFilter = SysMgr.environList.get("TASKCGFILTER", [])

        # get cgroup root #
        try:
            self.procData[tid]["cgroupRoot"] = cgBuf[-1].split(":")[2].rstrip()
        except SystemExit:
            sys.exit(0)
        except:
            return

        if not SysMgr.cgroupEnable:
            return

        cstr = ""
        indentlen = 39
        linelen = indentlen + 3
        for item in cgBuf:
            clist = item[:-1].split(":")
            if len(clist) != 3 or clist[-1] == "/":
                continue
            elif cgFilter and not UtilMgr.isValidStr(clist[1], cgFilter):
                continue

            new = "%s:%s, " % (clist[1], clist[2])

            if cstr and linelen + len(new) > SysMgr.ttyCols:
                indent = "\n{0:>{ilen}} | ".format(" ", ilen=indentlen)
                linelen = len(indent) + len(new)
            else:
                indent = ""
                linelen += len(new)

            cstr = "%s%s%s" % (cstr, indent, new)

        if cstr:
            self.procData[tid]["cgroup"] = cstr[:-2]

    def saveProcStatusData(self, path, tid, force=False):
        if SysMgr.minStatEnable or not tid in self.procData:
            return

        isKernelThread = self.isKernelThread(tid)

        # PID/status #
        stat = "status"
        # no memory and context switch for kernel threads in process mode #
        if not force and SysMgr.processEnable and isKernelThread:
            pass
        elif not self.procData[tid][stat]:
            statusBuf = self.saveTaskData(path, tid, stat, False)
            if not self.procData[tid]["status"]:
                self.procData[tid]["status"] = {}

            # check status change #
            self.procData[tid]["statusOrig"] = statusBuf
            if (
                tid in self.prevProcData
                and "statusOrig" in self.prevProcData[tid]
                and self.prevProcData[tid]["statusOrig"] == statusBuf
            ):
                self.procData[tid]["status"] = self.prevProcData[tid]["status"]
            else:
                # decode items #
                if sys.version_info >= (3, 0):
                    statusBuf = list(map(lambda x: x.decode(), statusBuf))

                for line in statusBuf:
                    try:
                        statusList = line.split(":", 1)
                        self.procData[tid]["status"][
                            statusList[0]
                        ] = statusList[1].strip()
                    except SystemExit:
                        sys.exit(0)
                    except:
                        pass

        # PID/statm #
        stat = "statm"
        if not self.procData[tid][stat]:
            mainID = self.procData[tid]["mainID"]

            # kernel thread #
            if isKernelThread:
                pass
            # sibling thread #
            elif (
                mainID in self.procData
                and stat in self.procData[mainID]
                and self.procData[mainID][stat]
            ):
                self.procData[tid][stat] = self.procData[mainID][stat]
            # main thread #
            else:
                statmBuf = self.saveTaskData(path, tid, stat)
                if statmBuf:
                    self.procData[tid][stat] = statmBuf[0].split()
                    if mainID in self.procData:
                        self.procData[mainID][stat] = self.procData[tid][stat]

    def saveProcData(self, path, tid, pid=None):
        def _getStatBuf(self, path, tid):
            fd = os.open(path, os.O_RDONLY)
            self.procData[tid]["statFd"] = fd
            statBuf = os.read(fd, 1024)
            self.procFdData[tid] = fd

            if tid in self.prevProcData:
                self.prevProcData[tid]["alive"] = True

            # fd resource is about to run out #
            if SysMgr.maxKeepFd < fd:
                os.close(fd)
                self.procData[tid]["statFd"] = None
                self.reclaimFds()

            return statBuf

        # make path #
        if not path:
            path = "%s/%s" % (SysMgr.procPath, tid)

        # initialize task #
        if not tid in self.procData:
            if not pid:
                pid = tid
            self.procData[tid] = dict(self.init_procData)
            self.procData[tid]["mainID"] = pid
            self.procData[tid]["taskPath"] = path
            self.procData[tid]["fdList"] = {}

        statPath = "%s/%s" % (path, "stat")

        # save stat data #
        try:
            try:
                prevFd = self.prevProcData[tid]["statFd"]
                self.procData[tid]["statFd"] = prevFd
                while 1:
                    os.lseek(prevFd, 0, 0)
                    statBuf = os.read(prevFd, 1024)
                    # retry to read for shared descriptor #
                    if statBuf:
                        break
                self.prevProcData[tid]["alive"] = True
            except SystemExit:
                sys.exit(0)
            except:
                statBuf = _getStatBuf(self, statPath, tid)
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printOpenWarn(statPath)
            self.procData.pop(tid, None)
            self.abnormalTasks[pid] = "?"
            return False

        # check stat change #
        self.procData[tid]["statOrig"] = statBuf
        if (
            tid in self.prevProcData
            and "statOrig" in self.prevProcData[tid]
            and self.prevProcData[tid]["statOrig"] == statBuf
        ):
            self.procData[tid]["stat"] = self.prevProcData[tid]["stat"]
            del self.prevProcData[tid]["statOrig"]
            self.procData[tid]["changed"] = False
        else:
            # convert string to list #
            statList = statBuf.decode().split()
            if len(statList) + 1 < self.statCnt:
                SysMgr.printWarn(
                    "failed to read stat for the task having TID %s" % tid
                )
                self.procData.pop(tid, None)
                return False

            # merge comm parts that splited by space #
            statList = SysMgr.mergeCommStat(statList, self.commIdx)

            # convert type of values #
            self.procData[tid]["stat"] = statList
            statList[self.majfltIdx] = long(statList[self.majfltIdx])
            statList[self.utimeIdx] = long(statList[self.utimeIdx])
            statList[self.stimeIdx] = long(statList[self.stimeIdx])
            statList[self.btimeIdx] = long(statList[self.btimeIdx])
            statList[self.cutimeIdx] = long(statList[self.cutimeIdx])
            statList[self.cstimeIdx] = long(statList[self.cstimeIdx])

        # check task status #
        statData = self.procData[tid]["stat"]
        tstat = statData[self.statIdx]
        if not tstat in "SRI":
            self.abnormalTasks[tid] = tstat

        # set comm #
        comm = self.procData[tid]["comm"] = statData[self.commIdx].strip("()")

        # change sched priority #
        for item in SysMgr.schedFilter:
            target = str(item[2])
            if not target:
                target = tid
            elif tid != target and not target in comm:
                continue

            # set deadline sched #
            if item[0].upper() == "D":
                runtime, deadline, period = SysMgr.getDeadlineArgs(item[1])

                SysMgr.setDeadlinePriority(
                    long(tid), runtime, deadline, period
                )
            # set other scheds #
            else:
                SysMgr.setPriority(long(tid), item[0], long(item[1]))

        # change CPU affinity #
        if SysMgr.affinityFilter:
            alist = list(SysMgr.affinityFilter)
            for idx, item in enumerate(alist):
                val = item[0]
                if not val:
                    target = tid
                elif tid != val and not val in comm:
                    continue

                try:
                    mask = item[1]
                    if val == tid or val in comm:
                        SysMgr.setAffinity(mask, [tid])

                    flag = item[2]
                    if flag != "CONT":
                        SysMgr.affinityFilter.remove(item)
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

        # send signal #
        if SysMgr.killFilter:
            slist = list(SysMgr.killFilter)
            for idx, item in enumerate(slist):
                val = item[0]
                sig = item[1]
                flag = item[2]
                if tid != val and not val in comm:
                    continue

                try:
                    os.kill(long(tid), sig)
                    SysMgr.printInfo(
                        "sent %s to %s(%s)"
                        % (ConfigMgr.SIG_LIST[sig], comm, tid)
                    )
                    if flag != "CONT":
                        SysMgr.killFilter.remove(item)
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

        # save I/O data #
        if SysMgr.blockEnable:
            ioBuf = self.saveTaskData(path, tid, "io")
            # check I/O support in procfs #
            if not ioBuf and not os.path.isfile(
                "%s/self/io" % SysMgr.procPath
            ):
                SysMgr.printWarn(
                    "failed to use bio event, please check kernel configs"
                )
                SysMgr.blockEnable = False
            self.procData[tid]["ioData"] = ioBuf

            # check I/O data #
            if (
                tid in self.prevProcData
                and "ioData" in self.prevProcData[tid]
                and self.prevProcData[tid]["ioData"] == ioBuf
            ):
                self.procData[tid]["io"] = self.prevProcData[tid]["io"]
            else:
                self.procData[tid]["io"] = {}

                for line in ioBuf:
                    if not (
                        line.startswith("read_bytes:")
                        or line.startswith("write_bytes:")
                    ):
                        continue

                    name, val = line.split(None, 1)[:2]

                    # save stats #
                    self.procData[tid]["io"][name[:-1]] = long(val)

        # save perf fds #
        if (
            SysMgr.perfGroupEnable
            and tid in self.prevProcData
            and "perfFds" in self.prevProcData[tid]
        ):
            self.procData[tid]["perfFds"] = self.prevProcData[tid]["perfFds"]

        # save oom_score #
        if SysMgr.oomEnable:
            self.updateOOMScore(path, tid)

        # save namespace #
        if SysMgr.nsEnable:
            self.updateNamespace(path, tid)

        # register to fixed task list #
        if SysMgr.fixedTaskList:
            if tid in SysMgr.fixedTaskList or UtilMgr.isValidStr(
                comm, SysMgr.fixedTaskList
            ):
                self.fixedProcData[tid] = self.procData[tid]

        return True

    def updateNamespace(self, path, tid):
        if not SysMgr.isLinux:
            return

        nsPath = "%s/%s" % (path, "ns")
        try:
            self.procData[tid]["ns"] = ""
            for items in os.walk(nsPath):
                for node in items[2]:
                    value = os.readlink(os.path.join(items[0], node))[
                        len(node) + 1 :
                    ]

                    if not value:
                        continue

                    # update global info #
                    value = value[1:-1]
                    self.nsData.setdefault(node, {})
                    self.nsData[node].setdefault(value, {})
                    self.nsData[node][value].setdefault(
                        tid, self.procData[tid]["stat"][self.starttimeIdx]
                    )

                    # update task info #
                    if not SysMgr.nsAllEnable:
                        continue
                    self.procData[tid]["ns"] += "%s:%s, " % (node, value)
        except SystemExit:
            sys.exit(0)
        except:
            comm = self.procData[tid]["stat"][self.commIdx].strip("()")
            SysMgr.printWarn(
                "failed to read namespace value for %s(%s)" % (comm, tid),
                reason=True,
            )

    def updateOOMScore(self, path, tid):
        if not SysMgr.isLinux:
            return

        # check main thread to remove redundant operation #
        if not SysMgr.processEnable:
            mainID = self.procData[tid]["mainID"]
            if mainID in self.procData:
                mainData = self.procData[mainID]
                if "oomScore" in self.procData[mainID]:
                    self.procData[tid]["oomScore"] = mainData["oomScore"]
                    self.procData[tid]["oomFd"] = mainData["oomFd"]
                    return

        try:
            self.prevProcData[tid]["oomFd"].seek(0)
            self.procData[tid]["oomFd"] = self.prevProcData[tid]["oomFd"]
            self.procData[tid]["oomScore"] = long(
                self.procData[tid]["oomFd"].readline()
            )
        except SystemExit:
            sys.exit(0)
        except:
            try:
                oomPath = "%s/%s" % (path, "oom_score")
                oomFd = self.procData[tid]["oomFd"] = open(oomPath, "r")
                self.procData[tid]["oomScore"] = long(
                    self.procData[tid]["oomFd"].readline()
                )

                # fd resource is about to run out #
                if SysMgr.maxKeepFd < oomFd.fileno():
                    oomFd.close()
                    self.procData[tid]["oomFd"] = None
                    self.reclaimFds()
                elif not SysMgr.processEnable and mainID in self.procData:
                    mainData = self.procData[tid]
                    self.procData[mainID]["oomScore"] = mainData["oomScore"]
                    self.procData[mainID]["oomFd"] = mainData["oomFd"]
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printOpenWarn(oomPath)

                self.procData.pop(tid, None)

    def reclaimFds(self, nrReq=64):
        nrRclm = 0
        for pid, val in sorted(self.procData.items(), key=lambda x: int(x[0])):
            for item in val:
                if not item.endswith("Fd"):
                    continue

                # close file descriptors #
                try:
                    if isinstance(val[item], (int, long)):
                        os.close(val[item])
                    else:
                        val[item].close()

                    val[item] = None
                    nrRclm += 1
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

            # return the number of closed files #
            if nrRclm >= nrReq:
                return nrRclm

    def printSystemUsage(self):
        # define shortcut #
        convColor = UtilMgr.convColor
        convCpuColor = UtilMgr.convCpuColor
        convSize = UtilMgr.convSize2Unit
        removeColor = UtilMgr.removeColor
        lineLength = SysMgr.lineLength

        vmData = self.vmData
        prevVmData = self.prevVmData
        memData = self.memData
        prevMemData = self.prevMemData
        failedStat = []

        kbunit = "KBUNIT" in SysMgr.environList
        kbshift = 0 if kbunit else 10

        # total memory #
        try:
            totalMem = memData["MemTotal"] >> kbshift
        except SystemExit:
            sys.exit(0)
        except:
            totalMem = 0
            failedStat.append("MemTotal")

        # free memory #
        try:
            freeMem = memData["MemFree"] >> kbshift
            freeMemDiff = freeMem - (prevMemData["MemFree"] >> kbshift)
            freeMemPer = long(((totalMem - freeMem) / float(totalMem)) * 100)
        except SystemExit:
            sys.exit(0)
        except:
            freeMem = freeMemDiff = freeMemPer = 0
            failedStat.append("MemFree")

        # available memory #
        try:
            if SysMgr.freeMemEnable:
                raise Exception()

            # assume MemAvailable #
            if not "MemAvailable" in memData:
                memData["MemAvailable"] = (
                    memData["MemFree"]
                    + memData["Inactive(file)"]
                    + memData["SReclaimable"]
                    + memData["SwapCached"]
                )

            availMem = memData["MemAvailable"] >> kbshift
            prevAvailMem = prevMemData.get("MemAvailable", 0)
            if prevAvailMem:
                availMemDiff = availMem - (prevAvailMem >> kbshift)
            else:
                availMemDiff = 0

            availMemPer = long(((totalMem - availMem) / float(totalMem)) * 100)
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.freeMemEnable = True
            availMem = availMemDiff = availMemPer = 0

        # anonymous memory #
        try:
            # actAnonMem = vmData["nr_active_anon"] >> 8
            # inactAnonMem = vmData["nr_inactive_anon"] >> 8
            pgAnon = vmData["nr_anon_pages"]
            anonMemDiff = pgAnon - prevVmData["nr_anon_pages"]
            if kbunit:
                totalAnonMem = pgAnon << 2
                anonMemDiff << 2
            else:
                totalAnonMem = pgAnon >> 8
                anonMemDiff = anonMemDiff >> 8
        except SystemExit:
            sys.exit(0)
        except:
            # actAnonMem = inactAnonMem = 0
            totalAnonMem = anonMemDiff = 0
            failedStat.append("MemAnon")

        # file memory #
        try:
            # actFileMem = vmData["nr_active_file"] >> 8
            # inactFileMem = vmData["nr_inactive_file"] >> 8
            pgFile = vmData["nr_file_pages"]
            fileMemDiff = pgFile - prevVmData["nr_file_pages"]
            if kbunit:
                totalFileMem = pgFile << 2
                fileMemDiff = fileMemDiff << 2
            else:
                totalFileMem = pgFile >> 8
                fileMemDiff = fileMemDiff >> 8
        except SystemExit:
            sys.exit(0)
        except:
            # actFileMem = inactFileMem = 0
            totalFileMem = fileMemDiff = 0
            failedStat.append("MemFile")

        # dirty memory #
        try:
            pgDirty = vmData["nr_dirty"]

            """
            dirtyRatio = \
                long((vmData['nr_dirty'] / \
                float(vmData['nr_dirty_threshold'])) * 100)
            dirtyBgRatio = \
                long((vmData['nr_dirty'] / \
                float(vmData['nr_dirty_background_threshold'])) * 100)
            """
        except SystemExit:
            sys.exit(0)
        except:
            pgDirty = 0
            failedStat.append("MemDirty")

        # slab memory #
        try:
            slabReclmDiff = (
                vmData["nr_slab_reclaimable"]
                - prevVmData["nr_slab_reclaimable"]
            )

            slabUnReclmDiff = (
                vmData["nr_slab_unreclaimable"]
                - prevVmData["nr_slab_unreclaimable"]
            )

            totalSlabMem = (
                vmData["nr_slab_reclaimable"] + vmData["nr_slab_unreclaimable"]
            )

            slabMemDiff = slabReclmDiff + slabUnReclmDiff

            if kbunit:
                totalSlabMem = totalSlabMem << 2
                slabMemDiff = slabMemDiff << 2
            else:
                totalSlabMem = totalSlabMem >> 8
                slabMemDiff = slabMemDiff >> 8
        except SystemExit:
            sys.exit(0)
        except:
            totalSlabMem = slabMemDiff = 0
            failedStat.append("MemSlab")

        # sum cached mems #
        totalCacheMem = totalFileMem + totalSlabMem

        # kernel memory #
        try:
            totalKernelMem = totalMem - (
                totalAnonMem + totalCacheMem + freeMem
            )
        except SystemExit:
            sys.exit(0)
        except:
            totalKernelMem = 0

        # fault #
        try:
            nrMajFault = vmData["pgmajfault"]
            nrTotalFault = vmData["pgfault"]
            if not SysMgr.totalEnable:
                nrMajFault -= prevVmData["pgmajfault"]
                nrTotalFault -= prevVmData["pgfault"]
            nrMinFault = nrTotalFault - nrMajFault
        except SystemExit:
            sys.exit(0)
        except:
            nrMajFault = nrTotalFault = nrMinFault = 0
            failedStat.append("MemFault")

        # paged in/out from/to disk #
        try:
            pgInMemDiff = vmData["pgpgin"]
            pgOutMemDiff = vmData["pgpgout"]

            if not SysMgr.totalEnable:
                pgInMemDiff -= prevVmData["pgpgin"]
                pgOutMemDiff -= prevVmData["pgpgout"]

            pgInMemDiff = pgInMemDiff >> kbshift
            pgOutMemDiff = pgOutMemDiff >> kbshift
        except SystemExit:
            sys.exit(0)
        except:
            pgInMemDiff = pgOutMemDiff = 0
            failedStat.append("MemPg")

        # swap memory #
        try:
            swapTotal = vmData["swapTotal"] >> kbshift
            swapUsage = vmData["swapUsed"] >> kbshift
            if swapTotal:
                swapUsagePer = long((swapUsage / float(swapTotal)) * 100)
            else:
                swapUsagePer = 0
            swapUsageDiff = (
                prevVmData["swapUsed"] - vmData["swapUsed"]
            ) >> kbshift
            swapInMem = vmData["pswpin"] - prevVmData["pswpin"]
            swapOutMem = vmData["pswpout"] - prevVmData["pswpout"]
            if kbunit:
                swapInMem = swapInMem << 2
                swapOutMem = swapOutMem << 2
            else:
                swapInMem = swapInMem >> 8
                swapOutMem = swapOutMem >> 8
        except SystemExit:
            sys.exit(0)
        except:
            swapTotal = swapUsage = 0
            swapUsageDiff = swapUsagePer = swapInMem = swapOutMem = 0
            failedStat.append("MemSwap")

        # add swap device interval #
        if self.checkSysInterval("swap"):
            self.addSysInterval("swap", "usage", swapUsage)
            self.addSysInterval("swap", "usagePer", swapUsagePer)
            self.addSysInterval("swap", "usageDiff", swapUsageDiff)
            self.addSysInterval("swap", "swapin", swapInMem)
            self.addSysInterval("swap", "swapout", swapOutMem)

        # background reclaim #
        try:
            # init variable #
            pgRclmBg = 0

            if SysMgr.totalEnable:
                prevData = None
            else:
                prevData = prevVmData

            # accumulate the number of pages #
            for name in list(ConfigMgr.BGRECLAIMSTAT):
                try:
                    prevStat = prevData[name]
                except:
                    prevStat = 0

                try:
                    pgRclmBg += vmData[name] - prevStat
                except:
                    ConfigMgr.BGRECLAIMSTAT.remove(name)

            # calculate the count #
            """
            try:
                nrBgReclaim = (
                    vmData["pageoutrun"] - prevVmData["pageoutrun"]
                )
            except SystemExit:
                sys.exit(0)
            except:
                nrBgReclaim = 0
            """
        except SystemExit:
            sys.exit(0)
        except:
            pgRclmBg = 0
            # nrBgReclaim = 0
            failedStat.append("MemBgReclaim")

        # direct reclaim #
        try:
            pgRclmFg = 0

            if SysMgr.totalEnable:
                prevData = None
            else:
                prevData = prevVmData

            # accumulate the number of pages #
            for name in list(ConfigMgr.FGRECLAIMSTAT):
                try:
                    prevStat = prevData[name]
                except:
                    prevStat = 0

                try:
                    pgRclmFg += vmData[name] - prevStat
                except:
                    ConfigMgr.FGRECLAIMSTAT.remove(name)

            # calculate the count #
            """
            try:
                nrDrReclaim = (
                    vmData["allocstall"] - prevVmData["allocstall"]
                )
            except SystemExit:
                sys.exit(0)
            except:
                nrDrReclaim = 0
            """
        except SystemExit:
            sys.exit(0)
        except:
            pgRclmFg = 0
            # nrDrReclaim = 0
            failedStat.append("MemFgReclaim")

        # mlock #
        try:
            pgMlock = vmData["nr_mlock"]
            # mappedMem = vmData['nr_mapped'] >> 8
        except:
            pgMlock = 0
            failedStat.append("MemMlock")

        # pending #
        try:
            nrBlocked = self.cpuData["procs_blocked"]["procs_blocked"]
        except:
            nrBlocked = 0
            failedStat.append("NrBlocked")

        # add block interval #
        if self.checkSysInterval("block"):
            self.addSysInterval("block", "read", pgInMemDiff)
            self.addSysInterval("block", "write", pgOutMemDiff)
            self.addSysInterval("block", "nrMajFlt", nrMajFault)
            self.addSysInterval("block", "nrTask", nrBlocked)

        # cma mem #
        try:
            if not "CmaTotal" in memData:
                raise Exception("no cma")

            cmaTotalMem = memData["CmaTotal"]

            if "CmaFree" in memData:
                cmaFreeMem = memData["CmaFree"]
            else:
                cmaFreeMem = 0
            if "CmaDeviceAlloc" in memData:
                cmaDevMem = memData["CmaDeviceAlloc"]
            else:
                cmaDevMem = 0
        except SystemExit:
            sys.exit(0)
        except:
            cmaTotalMem = cmaFreeMem = cmaDevMem = 0

        """
        try:
            shMem = vmData['nr_shmem'] >> 8
            pageTableMem = vmData['nr_page_table_pages'] >> 8
            kernelStackMem = vmData['nr_kernel_stack'] * 8 >> 10
        except:
            failedStat.append('MemShm')
        """

        # add memory interval #
        if self.checkSysInterval("mem"):
            self.addSysInterval("mem", "free", freeMem)
            self.addSysInterval("mem", "available", availMem)
            self.addSysInterval("mem", "anon", totalAnonMem)
            self.addSysInterval("mem", "file", totalFileMem)
            self.addSysInterval("mem", "pg", pgDirty)
            self.addSysInterval("mem", "slab", totalSlabMem)
            self.addSysInterval("mem", "cache", totalCacheMem)
            self.addSysInterval("mem", "kernelmem", totalKernelMem)
            self.addSysInterval("mem", "nrMinFlt", nrMinFault)
            self.addSysInterval("mem", "pgRclmBg", pgRclmBg)
            self.addSysInterval("mem", "pgRclmFg", pgRclmFg)
            self.addSysInterval("mem", "pgMlock", pgMlock)
            self.addSysInterval("mem", "cmaFree", cmaFreeMem)
            self.addSysInterval("mem", "cmaDev", cmaDevMem)

        # check available memory type #
        if SysMgr.freeMemEnable:
            memTitle = "MemFre"
        else:
            memTitle = "MemAvl"

        # get iowait time #
        # iowait = SysMgr.getIowaitTime()

        # print failed stats #
        if failedStat:
            SysMgr.printWarn("failed to get %s stats" % ", ".join(failedStat))

        titleStr = (
            "{0:^7}|{1:>5}({2:^3}/{3:^3}/{4:^3}/{5:^3})|"
            "{6:>6}({7:>3}/{8:>6}/{9:>6}/{10:>5})|"
            "{11:>5}({12:>4}/{13:>3}/{14:>3})|{15:^11}|{16:^7}|{17:^7}|"
            "{18:^3}|{19:^8}|{20:^7}|{21:^8}|{22:^12}|\n"
        ).format(
            "ID",
            "CPU",
            "Usr",
            "Ker",
            "Blk",
            "IRQ",
            memTitle,
            "Per",
            "User",
            "Cache",
            "Kern",
            "Swap",
            "Per",
            "In",
            "Out",
            "PgRclm",
            "BlkRW",
            "NrFlt",
            "Blk",
            "NrSIRQ",
            "PgMlk",
            "PgDirt",
            "NetIO",
        )

        # print system status menu #
        SysMgr.addPrint(
            "%s\n%s%s\n" % (twoLine, titleStr, oneLine),
            newline=3,
        )

        # set interval #
        if SysMgr.totalEnable:
            interval = 1
        else:
            interval = SysMgr.uptimeDiff
            if interval == 0:
                return

        # get event count #
        nrVars = []
        for var in ("ctxt", "intr", "softriq"):
            try:
                res = self.cpuData[var][var] - self.prevCpuData[var][var]
                if res < 0:
                    res = 0
            except SystemExit:
                sys.exit(0)
            except:
                res = 0
            finally:
                nrVars.append(res)
        nrCtxSwc, nrIrq, nrSoftIrq = nrVars

        # check cpu data #
        if not self.cpuData.get("all") or not self.prevCpuData.get("all"):
            if self.oldCpuData and not SysMgr.procBuffer:
                # recover last cpu data #
                self.cpuData = self.prevCpuData
                self.prevCpuData = self.oldCpuData
                self.oldCpuData = {}
            else:
                SysMgr.printErr("failed to get system CPU stat")
                return

        # set maximum CPU usage #
        if SysMgr.cpuAvgEnable:
            nrCore = SysMgr.nrCore
            maxUsage = 100
        else:
            nrCore = 1
            maxUsage = 100 * SysMgr.nrCore

        # initialize accumulated CPU values #
        userUsage = kerUsage = ioUsage = irqUsage = idleUsage = stealUsage = 0
        coreStats = {}

        # get total CPU usage #
        for idx in self.cpuData:
            try:
                nrIdx = long(idx)
                nowData = self.cpuData[nrIdx]

                # check previous data #
                if not nrIdx in self.prevCpuData:
                    coreStat = (
                        "{0:<7}|{1:>5}({2:^3}/{3:^3}/{4:^3}/{5:^3})|".format(
                            "Core/" + str(idx), "- %", "-", "-", "-", "-"
                        )
                    )
                    SysMgr.addPrint("%s\n" % coreStat)
                    continue

                # set previous stat #
                if SysMgr.totalEnable:
                    prevData = self.init_cpuData
                else:
                    prevData = self.prevCpuData[nrIdx]

                coreStats[idx] = {}

                # -------------------- SIMPLE STAT -------------------- #
                userStat = nowData["user"] - prevData["user"]
                niceStat = nowData["nice"] - prevData["nice"]
                userCoreUsage = long((userStat + niceStat) / interval)

                kerCoreUsage = long(
                    (nowData["system"] - prevData["system"]) / interval
                )

                irqStat = nowData["irq"] - prevData["irq"]
                softirqStat = nowData["softirq"] - prevData["softirq"]
                irqCoreUsage = long((irqStat + softirqStat) / interval)

                ioCoreUsage = long(
                    (nowData["iowait"] - prevData["iowait"]) / interval
                )

                idleCoreUsage = long(
                    (nowData["idle"] - prevData["idle"]) / interval
                )

                try:
                    coreStealUsage = long(
                        (nowData["steal"] - prevData["steal"]) / interval
                    )
                    stealUsage += coreStealUsage
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

                # -------------------- REVISED STAT -------------------- #
                if SysMgr.totalEnable:
                    scale = 1
                else:
                    # get scaled factor #
                    totalStat = (
                        userCoreUsage
                        + kerCoreUsage
                        + ioCoreUsage
                        + irqCoreUsage
                        + idleCoreUsage
                    )
                    if totalStat:
                        scale = 100 / float(totalStat)
                    else:
                        scale = 1

                # get CPU stats #
                coreStats[idx]["user"] = long(userCoreUsage * scale)
                userUsage += coreStats[idx]["user"]
                coreStats[idx]["kernel"] = long(kerCoreUsage * scale)
                kerUsage += coreStats[idx]["kernel"]
                coreStats[idx]["io"] = long(ioCoreUsage * scale)
                ioUsage += coreStats[idx]["io"]
                coreStats[idx]["irq"] = long(irqCoreUsage * scale)
                irqUsage += coreStats[idx]["irq"]
                coreStats[idx]["idle"] = long(idleCoreUsage * scale)
                idleUsage += coreStats[idx]["idle"]
                try:
                    coreStats[idx]["steal"] = coreStealUsage
                except SystemExit:
                    sys.exit(0)
                except:
                    coreStats[idx]["steal"] = 0

                # check core filter #
                if SysMgr.perCoreList and not idx in SysMgr.perCoreList:
                    coreStats.pop(idx, None)
            except SystemExit:
                sys.exit(0)
            except:
                pass
                # SysMgr.printWarn("failed to calculate CPU(%s) usage" % idx, reason=True)

        # divide total CPU usage by the number of cores #
        userUsage = long(userUsage / nrCore)
        kerUsage = long(kerUsage / nrCore)
        ioUsage = long(ioUsage / nrCore)
        irqUsage = long(irqUsage / nrCore)
        idleUsage = long(idleUsage / nrCore)
        SysMgr.cpuStealUsage = stealUsage = long(stealUsage)

        # get total usage #
        if SysMgr.totalEnable:
            totalUsage = userUsage + kerUsage + irqUsage
        elif idleUsage < maxUsage:
            totalUsage = maxUsage - idleUsage - ioUsage
        else:
            totalUsage = 0

        # add CPU interval #
        SysMgr.cpuUsage = totalUsage
        if self.checkSysInterval("cpu"):
            self.addSysInterval("cpu", "total", totalUsage)
            self.addSysInterval("cpu", "user", userUsage)
            self.addSysInterval("cpu", "kernel", kerUsage)
            self.addSysInterval("cpu", "iowait", ioUsage)
            self.addSysInterval("cpu", "irq", irqUsage)
            self.addSysInterval("cpu", "steal", stealUsage)

        # add block interval #
        self.addSysInterval("block", "ioWait", ioUsage)

        # get network usage in bytes #
        if SysMgr.isLinux:
            (netIn, netOut) = self.getNetworkUsage(
                SysMgr.prevNetstat, SysMgr.netstat
            )
        else:
            netIn = SysMgr.netstat[0] - SysMgr.prevNetstat[0]
            netOut = SysMgr.netstat[1] - SysMgr.prevNetstat[1]

        # add network interval #
        if self.checkSysInterval("net"):
            self.addSysInterval("net", "inbound", netIn)
            self.addSysInterval("net", "outbound", netOut)

        # convert network usage #
        try:
            netIO = "%s/%s" % (convSize(netIn, True), convSize(netOut, True))
        except SystemExit:
            sys.exit(0)
        except:
            netIO = "-/-"

        # check available memory #
        if availMem == 0:
            availMem = freeMem
            availMemDiff = freeMemDiff
            availMemPer = freeMemPer

        # add memory interval #
        SysMgr.memAvail = availMem

        # convert color for CPU usage #
        totalUsageStr = r"%3s %%" % totalUsage
        totalUsageStr = convCpuColor(totalUsage, totalUsageStr)

        # convert color for mem available #
        availMemStr = r"%6s" % availMem
        if availMemPer == 0:
            pass
        elif availMemPer >= SysMgr.memAvailPerThr:
            availMemStr = convColor(availMemStr, "RED")
        else:
            availMemStr = convColor(availMemStr, "YELLOW")

        # convert color for block #
        ioUsageStr = r"%3s" % ioUsage
        if ioUsage > 0:
            ioUsageStr = convColor(ioUsageStr, "RED")

        # convert color for swap usage #
        swapUsageStr = r"%5s" % swapUsage
        if swapUsagePer == 0:
            pass
        elif swapUsagePer >= SysMgr.swapPerThr:
            swapUsageStr = convColor(swapUsageStr, "RED")
        else:
            swapUsageStr = convColor(swapUsageStr, "YELLOW")

        # convert color for reclaim stats #
        pgRclmStr = r"%s/%s" % (pgRclmBg, pgRclmFg)
        pgRclmStr = r"{0:^11}".format(pgRclmStr)
        if pgRclmBg > 0 or pgRclmFg > 0:
            pgRclmStr = convColor(pgRclmStr, "RED")

        # convert color for I/O stats #
        pgIOMemDiffStr = r"%s/%s" % (pgInMemDiff, pgOutMemDiff)
        pgIOMemDiffStr = r"{0:^7}".format(pgIOMemDiffStr)
        if pgInMemDiff > 0 or pgOutMemDiff > 0:
            pgIOMemDiffStr = convColor(pgIOMemDiffStr, "RED")

        # convert color for majfault stat #
        nrMajFaultStr = r"%5s " % nrMajFault
        if nrMajFault > 0:
            nrMajFaultStr = convColor(nrMajFaultStr, "RED", 7)

        # convert color for network stats #
        if not netIO in ("-/-", "0/0"):
            netIO = r"{0:^12}".format(netIO)
            netIO = convColor(netIO, "YELLOW")

        # make total stat string #
        totalCoreStat = (
            "{0:<7}|{1:>5}({2:>3}/{3:>3}/{4:>3}/{5:>3})|"
            "{6:>6}({7:>3}/{8:>6}/{9:>6}/{10:>5})|"
            "{11:>5}({12:>4}/{13:>3}/{14:>3})|{15:^11}|{16:^7}|"
            "{17:^7}|{18:^3}|{19:^8}|{20:^7}|{21:^8}|{22:^12}|\n"
        ).format(
            "Total",
            totalUsageStr,
            userUsage,
            kerUsage,
            ioUsageStr,
            irqUsage,
            availMemStr,
            availMemPer,
            totalAnonMem,
            totalCacheMem,
            totalKernelMem,
            swapUsageStr,
            swapUsagePer,
            swapInMem,
            swapOutMem,
            pgRclmStr,
            pgIOMemDiffStr,
            "%5s" % nrMajFaultStr,
            nrBlocked,
            "%6s" % nrSoftIrq,
            "%5s" % pgMlock,
            "%6s" % pgDirty,
            netIO,
        )

        SysMgr.addPrint(totalCoreStat)

        # print bar graphs of CPU, memory, swap #
        try:
            if SysMgr.barGraphEnable is False:
                raise Exception()

            nrItems = 3
            margin = 25

            lenLine = long(lineLength / nrItems - (nrItems * 3))
            lenLine -= lenLine % 2
            mspace = " " * max(0, (lineLength - (lenLine * nrItems) - margin))

            totalUsagePer = totalUsage / 100
            if SysMgr.totalEnable:
                totalUsagePer /= SysMgr.uptime
            coreGraph = "#" * long(lenLine * totalUsagePer)
            coreGraph += " " * (lenLine - len(coreGraph))
            coreGraph = convCpuColor(totalUsagePer, coreGraph)

            memGraph = "#" * long(lenLine * availMemPer / 100)
            memGraph += " " * (lenLine - len(memGraph))
            memGraph = convCpuColor(availMemPer, memGraph)
            memName = mspace + "MEM"

            swapGraph = "#" * long(lenLine * swapUsagePer / 100)
            swapGraph += " " * (lenLine - len(swapGraph))
            swapGraph = convCpuColor(swapUsagePer, swapGraph)
            swapName = mspace + "SWAP"

            SysMgr.addPrint(
                "%s\nCPU [%s] %s [%s] %s [%s]|\n"
                % (oneLine, coreGraph, memName, memGraph, swapName, swapGraph)
            )
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # print bar graphs of PSI #
        try:
            if SysMgr.barGraphEnable != True or not SysMgr.psiData:
                raise Exception()

            nrItems = 3
            margin = 27

            lenLine = long(lineLength / nrItems - (nrItems * 3))
            lenLine -= lenLine % 2
            mspace = " " * max(0, (lineLength - (lenLine * nrItems) - margin))

            cpuPsi = SysMgr.psiData["cpu"]["some"]["diff"]
            coreGraph = "#" * long(lenLine * cpuPsi / 100)
            coreGraph += " " * (lenLine - len(coreGraph))
            coreGraph = convColor(coreGraph, "RED")

            memPsi = SysMgr.psiData["memory"]["some"]["diff"]
            memGraph = "#" * long(lenLine * memPsi / 100)
            memGraph += " " * (lenLine - len(memGraph))
            memGraph = convColor(memGraph, "RED")
            memName = mspace + "MEM"

            ioPsi = SysMgr.psiData["io"]["some"]["diff"]
            ioGraph = "#" * long(lenLine * ioPsi / 100)
            ioGraph += " " * (lenLine - len(ioGraph))
            ioGraph = convColor(ioGraph, "RED")
            ioName = mspace + "I/O"

            SysMgr.addPrint(
                "%s\nPSI> CPU [%s] %s [%s] %s [%s]|\n"
                % (oneLine, coreGraph, memName, memGraph, ioName, ioGraph)
            )
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # get CPU/GPU temperature #
        coreTempData = {}
        if SysMgr.isLinux and (SysMgr.cpuEnable or SysMgr.gpuEnable):
            tempDirList = []
            tempPath = "/sys/class/hwmon"

            try:
                self.tempFdList
            except:
                self.tempFdList = {}

            try:
                for item in os.listdir(tempPath):
                    devPath = "%s/%s/name" % (tempPath, item)
                    if os.path.isfile(devPath):
                        tempDirList.append(devPath)

                    devPath = "%s/%s/device/name" % (tempPath, item)
                    if os.path.isfile(devPath):
                        tempDirList.append(devPath)
            except SystemExit:
                sys.exit(0)
            except:
                pass

            tempPath = []
            for tempDir in tempDirList:
                try:
                    if tempDir in self.tempFdList:
                        fd = self.tempFdList[tempDir]
                        fd.seek(0)
                    else:
                        fd = open(tempDir, "r")
                        self.tempFdList[tempDir] = fd

                    if fd.readline()[:-1] == "coretemp":
                        tempPath.append(os.path.dirname(tempDir))
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

            # /sys/class/hwmon #
            for hwPath in tempPath:
                try:
                    hwdirs = os.listdir(hwPath)
                except SystemExit:
                    sys.exit(0)
                except:
                    continue

                tempDirList = [
                    "%s/%s" % (hwPath, item.replace("input", "label"))
                    for item in hwdirs
                    if item.endswith("_input")
                ]

                phyId = 0
                tempData = {}

                for tempDir in tempDirList:
                    try:
                        if tempDir in self.tempFdList:
                            lfd = self.tempFdList[tempDir]
                            lfd.seek(0)
                        else:
                            lfd = open(tempDir, "r")
                            self.tempFdList[tempDir] = lfd

                        name = lfd.readline()[:-1]
                        if name.startswith("Physical id ") or name.startswith(
                            "Package id "
                        ):
                            phyId = name[name.rfind(" ") + 1 :]
                        elif name.startswith("Core "):
                            coreId = name[name.rfind(" ") + 1 :]

                            tempDir = tempDir.replace("label", "input")
                            if tempDir in self.tempFdList:
                                tfd = self.tempFdList[tempDir]
                                tfd.seek(0)
                            else:
                                tfd = open(tempDir, "r")
                                self.tempFdList[tempDir] = tfd

                            tempData[coreId] = long(tfd.readline()[:-4])
                    except SystemExit:
                        sys.exit(0)
                    except:
                        pass

                for idx, val in sorted(
                    tempData.items(), key=lambda x: long(x[0])
                ):
                    coreTempData["%s-%s" % (phyId, idx)] = val

            # /sys/class/thermal #
            tempPath = "/sys/class/thermal"
            try:
                tempDirList = [
                    "%s/%s" % (tempPath, item)
                    for item in os.listdir(tempPath)
                    if item.startswith("thermal_zone")
                ]
            except SystemExit:
                sys.exit(0)
            except:
                tempDirList = []

            for tempDir in sorted(tempDirList):
                try:
                    ctype = None

                    with open("%s/type" % tempDir, "r") as fd:
                        ctype = fd.readline()[:-1]

                    with open("%s/temp" % tempDir, "r") as fd:
                        if "CPU" in ctype:
                            coreTempData["CPU"] = long(fd.readline()[:-4])
                        elif "GPU" in ctype:
                            coreTempData["GPU"] = long(fd.readline()[:-4])
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

        shortBarGraph = (
            SysMgr.barGraphEnable is None and SysMgr.nrCore > SysMgr.NRMANYCORE
        ) or SysMgr.barGraphEnable is False

        # print CPU per-core stats #
        perCoreStats = {}
        disableList = SysMgr.getOption("d")
        disableCore = disableList and "c" in disableList
        isReport = SysMgr.reportEnable or SysMgr.jsonEnable
        if not disableCore and (SysMgr.cpuEnable or isReport):
            shortCoreStats = ""
            lenCoreStat = 0

            if self.cpuData:
                SysMgr.addPrint("%s\n" % oneLine)

            freqPath = "/sys/devices/system/cpu/cpu"

            # set max cols #
            maxCols = min(lineLength, SysMgr.ttyCols)

            # get frequency #
            if not SysMgr.isLinux:
                try:
                    freqList = SysMgr.getPkg("psutil").cpu_freq(percpu=True)
                    if freqList and len(freqList) == 1:
                        freqList = [freqList[0]] * len(self.cpuData)
                except SystemExit:
                    sys.exit(0)
                except:
                    freqList = []

            # get max core digit #
            cd = len(str(SysMgr.maxCore))

            # traverse core files #
            for idx in sorted(
                self.cpuData,
                key=lambda x: long(x) if UtilMgr.isNumber(x) else -1,
            ):
                try:
                    if idx == -1 or not idx in coreStats:
                        continue
                    elif SysMgr.checkCutCond():
                        return

                    # init per-core dict #
                    cid = None
                    perCoreStats[idx] = {}

                    # get per-core stats #
                    userCoreUsage = coreStats[idx]["user"]
                    kerCoreUsage = coreStats[idx]["kernel"]
                    ioCoreUsage = coreStats[idx]["io"]
                    irqCoreUsage = coreStats[idx]["irq"]
                    idleCoreUsage = coreStats[idx]["idle"]
                    stealUsage = coreStats[idx]["steal"]

                    # get total usage #
                    if SysMgr.totalEnable:
                        totalCoreUsage = (
                            userCoreUsage + kerCoreUsage + irqCoreUsage
                        )
                    elif idleCoreUsage < 100:
                        totalCoreUsage = 100 - idleCoreUsage - ioCoreUsage
                    else:
                        totalCoreUsage = 0

                    # limit total core usage in each modes #
                    if not SysMgr.totalEnable:
                        if userCoreUsage > 100:
                            userCoreUsage = 100
                        if kerCoreUsage > 100:
                            kerCoreUsage = 100

                    # set per-core stats #
                    perCoreStats[idx]["user"] = userCoreUsage
                    perCoreStats[idx]["kernel"] = kerCoreUsage
                    perCoreStats[idx]["iowait"] = ioCoreUsage
                    perCoreStats[idx]["irq"] = irqCoreUsage
                    perCoreStats[idx]["idle"] = idleCoreUsage
                    perCoreStats[idx]["steal"] = stealUsage
                    perCoreStats[idx]["total"] = totalCoreUsage

                    # apply color #
                    totalCoreUsageStr = convCpuColor(totalCoreUsage, size=3)

                    if shortBarGraph and not "CORESTAT" in SysMgr.environList:
                        lenLine = 20
                        coreGraph = "#" * long(lenLine * totalCoreUsage / 100)
                        coreGraph += " " * (lenLine - len(coreGraph))
                        coreGraph = convCpuColor(totalCoreUsage, coreGraph)

                        coreStat = ("Core/{0:<{cd}}|{1:>5}[{2:1}] ").format(
                            idx,
                            "%s %%" % totalCoreUsageStr,
                            coreGraph,
                            cd=cd,
                        )
                    else:
                        coreStat = (
                            "Core/{0:<{cd}}|{1:>5}"
                            "({2:^3}/{3:^3}/{4:^3}/{5:^3}) "
                        ).format(
                            idx,
                            "%s %%" % totalCoreUsageStr,
                            userCoreUsage,
                            kerCoreUsage,
                            ioCoreUsage,
                            irqCoreUsage,
                            cd=cd,
                        )
                except SystemExit:
                    sys.exit(0)
                except:
                    continue

                # save frequency #
                if (
                    not SysMgr.reportEnable
                    and not SysMgr.jsonEnable
                    and not SysMgr.barGraphEnable
                    and SysMgr.nrCore > SysMgr.NRMANYCORE
                ):
                    pass
                elif not SysMgr.isLinux:
                    try:
                        cid = None
                        curFreq = minFreq = maxFreq = None
                        perCoreStats[idx]["id"] = None
                        freq = freqList[idx]
                        if len(freq) > 0:
                            curFreq = long(freq[0]) << 10
                        if len(freq) > 1:
                            minFreq = long(freq[1]) << 10
                        if len(freq) > 2:
                            maxFreq = long(freq[2]) << 10
                    except SystemExit:
                        sys.exit(0)
                    except:
                        pass
                else:
                    # set default path #
                    defPath = "%s%s/cpufreq" % (freqPath, idx)

                    # get current frequency #
                    """
                    if the core has been suspended,
                    reading current frequency will take quite some time.
                    """
                    try:
                        prevCurFd = self.prevCpuData[idx]["curFd"]
                        prevCurFd.seek(0)
                        curFreq = long(prevCurFd.readline()[:-1])
                        self.cpuData[idx]["curFd"] = prevCurFd
                        perCoreStats[idx]["curFreq"] = curFreq
                    except SystemExit:
                        sys.exit(0)
                    except:
                        infoPath = "%s/cpuinfo_cur_freq" % defPath
                        scalingPath = "%s/scaling_cur_freq" % defPath

                        if os.path.isfile(infoPath):
                            curPath = infoPath
                        elif os.path.isfile(scalingPath):
                            curPath = scalingPath
                        else:
                            curPath = None

                        try:
                            newCurFd = open(curPath, "r")
                            self.cpuData[idx]["curFd"] = newCurFd
                            curFreq = long(newCurFd.readline()[:-1])
                            perCoreStats[idx]["curFreq"] = curFreq
                        except SystemExit:
                            sys.exit(0)
                        except:
                            curFreq = None

                    # get min frequency #
                    try:
                        prevMinFd = self.prevCpuData[idx]["minFd"]
                        prevMinFd.seek(0)
                        minFreq = long(prevMinFd.readline()[:-1])
                        self.cpuData[idx]["minFd"] = prevMinFd
                        perCoreStats[idx]["minFreq"] = minFreq
                    except SystemExit:
                        sys.exit(0)
                    except:
                        infoPath = "%s/cpuinfo_min_freq" % defPath
                        scalingPath = "%s/scaling_min_freq" % defPath

                        if os.path.isfile(infoPath):
                            minPath = infoPath
                        elif os.path.isfile(scalingPath):
                            minPath = scalingPath
                        else:
                            minPath = None

                        try:
                            newMinFd = open(minPath, "r")
                            self.cpuData[idx]["minFd"] = newMinFd
                            minFreq = long(newMinFd.readline()[:-1])
                            perCoreStats[idx]["minFreq"] = minFreq
                        except SystemExit:
                            sys.exit(0)
                        except:
                            minFreq = None

                    # get max frequency #
                    try:
                        prevMaxFd = self.prevCpuData[idx]["maxFd"]
                        prevMaxFd.seek(0)
                        maxFreq = long(prevMaxFd.readline()[:-1])
                        self.cpuData[idx]["maxFd"] = prevMaxFd
                        perCoreStats[idx]["maxFreq"] = maxFreq
                    except SystemExit:
                        sys.exit(0)
                    except:
                        infoPath = "%s/cpuinfo_max_freq" % defPath
                        scalingPath = "%s/scaling_max_freq" % defPath

                        if os.path.isfile(infoPath):
                            maxPath = infoPath
                        elif os.path.isfile(scalingPath):
                            maxPath = scalingPath
                        else:
                            maxPath = None

                        try:
                            newMaxFd = open(maxPath, "r")
                            self.cpuData[idx]["maxFd"] = newMaxFd
                            maxFreq = long(newMaxFd.readline()[:-1])
                            perCoreStats[idx]["maxFreq"] = maxFreq
                        except SystemExit:
                            sys.exit(0)
                        except:
                            maxFreq = None

                    # get current governor #
                    try:
                        self.prevCpuData[idx]["govFd"].seek(0)
                        gov = self.prevCpuData[idx]["govFd"].readline()[:-1]
                        self.cpuData[idx]["govFd"] = self.prevCpuData[idx][
                            "govFd"
                        ]
                        perCoreStats[idx]["governor"] = gov
                    except SystemExit:
                        sys.exit(0)
                    except:
                        govPath = "%s/scaling_governor" % defPath

                        try:
                            self.cpuData[idx]["govFd"] = open(govPath, "r")
                            gov = self.cpuData[idx]["govFd"].readline()[:-1]
                            perCoreStats[idx]["governor"] = gov
                        except:
                            gov = None

                    # get package ID #
                    try:
                        # get core ID #
                        if (
                            idx in self.prevCpuData
                            and "cidFd" in self.prevCpuData[idx]
                        ):
                            fd = self.prevCpuData[idx]["cidFd"]
                            fd.seek(0)
                            coreId = long(fd.readline()[:-1])
                            self.cpuData[idx]["cidFd"] = fd
                        else:
                            cidPath = "%s%s/topology/core_id" % (freqPath, idx)
                            newCidFd = open(cidPath, "r")

                            self.cpuData[idx]["cidFd"] = newCidFd
                            coreId = long(newCidFd.readline()[:-1])

                        if coreId < 0:
                            coreId = "?"

                        # get package ID #
                        if (
                            idx in self.prevCpuData
                            and "pidFd" in self.prevCpuData[idx]
                        ):
                            fd = self.prevCpuData[idx]["pidFd"]
                            fd.seek(0)
                            phyId = long(fd.readline()[:-1])
                            self.cpuData[idx]["pidFd"] = fd
                        else:
                            pidPath = "%s%s/topology/physical_package_id" % (
                                freqPath,
                                idx,
                            )
                            newPidFd = open(pidPath, "r")

                            self.cpuData[idx]["pidFd"] = newPidFd
                            phyId = long(newPidFd.readline()[:-1])

                        if phyId < 0:
                            phyId = "?"

                        # make core ID #
                        cid = "%s-%s" % (phyId, coreId)
                        perCoreStats[idx]["id"] = cid

                        # add core temperature #
                        if cid in coreTempData:
                            perCoreStats[idx]["temp"] = coreTempData[cid]
                        else:
                            perCoreStats[idx]["temp"] = 0
                    except SystemExit:
                        sys.exit(0)
                    except:
                        perCoreStats[idx]["id"] = None

                    # add interval data #
                    if self.checkSysInterval("core"):
                        for sname, sval in (
                            ("user", userCoreUsage),
                            ("kernel", kerCoreUsage),
                            ("iowait", ioCoreUsage),
                            ("irq", irqCoreUsage),
                            ("steal", stealUsage),
                            ("total", totalCoreUsage),
                            ("curFreq", curFreq),
                            ("minFreq", minFreq),
                            ("maxFreq", maxFreq),
                            ("governor", gov),
                        ):
                            if sval is None:
                                continue
                            self.addSysInterval(
                                "core", "core%s:%s" % (idx, sname), sval
                            )

                # merge frequency info #
                try:
                    coreFreq = ""

                    # frequency #
                    if curFreq:
                        coreFreq = "%d Mhz" % (long(curFreq) >> 10)
                    # cache #
                    else:
                        sysObj = SysMgr.sysInstance
                        if not sysObj.cpuCacheData:
                            sysObj.saveCpuCacheInfo(True)
                        try:
                            coreFreq = sysObj.cpuCacheData[str(idx)]
                        except SystemExit:
                            sys.exit(0)
                        except:
                            coreFreq = "? Mhz"

                    if minFreq and maxFreq:
                        coreFreq = "%s [%d-%d]" % (
                            coreFreq,
                            long(minFreq) >> 10,
                            long(maxFreq) >> 10,
                        )

                    coreFreq = "%20s|" % coreFreq
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

                # merge temperature info #
                try:
                    coreFreq = "{0:^6} | {1:>3} C | {2:<1}".format(
                        cid, coreTempData[cid], coreFreq
                    )
                except SystemExit:
                    sys.exit(0)
                except:
                    try:
                        coreFreq = "{0:^6} | {1:>3} C | {2:<1}".format(
                            cid, coreTempData["CPU"], coreFreq
                        )
                    except SystemExit:
                        sys.exit(0)
                    except:
                        if cid:
                            coreFreq = "{0:^6} | {1:>3} C | {2:<1}".format(
                                cid, "?", coreFreq
                            )
                        else:
                            coreFreq = "%3s C | %s" % ("?", coreFreq)

                # merge governor info #
                try:
                    coreFreq = "{0:^13} | {1:>1}".format(gov, coreFreq)
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

                # print final stats #
                try:
                    # get real length without ansi for core stat #
                    if lenCoreStat == 0:
                        lenCoreStat = len(removeColor(coreStat))

                    # use short core stats for many-core system #
                    if shortBarGraph:
                        shortCoreStats += coreStat
                        coreFactor = long(maxCols / lenCoreStat)
                        if (
                            idx + 1
                        ) % coreFactor == 0 or idx + 1 == SysMgr.nrCore:
                            lenShortStats = len(removeColor(shortCoreStats))
                            if lenShortStats < lineLength:
                                shortCoreStats = (
                                    shortCoreStats[:-1]
                                    + (" " * (lineLength - lenShortStats))
                                    + "| "
                                )
                            SysMgr.addPrint(shortCoreStats[:-1] + "\n")
                            shortCoreStats = ""

                        raise Exception()

                    # get length of string #
                    lenFreq = len(coreFreq)
                    lenLine = lineLength - lenCoreStat - lenFreq - 2

                    # print bar graph of per-core usage #
                    if totalCoreUsage == 0:
                        coreGraph = " " * lenLine
                    else:
                        if SysMgr.totalEnable:
                            totalCoreUsage /= SysMgr.uptime
                        coreGraph = "#" * long(lenLine * totalCoreUsage / 100)
                        coreGraph += " " * (lenLine - len(coreGraph))
                        coreGraph = convCpuColor(totalCoreUsage, coreGraph)

                    SysMgr.addPrint(
                        "%s%s| %s\n" % (coreStat, coreGraph, coreFreq)
                    )
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

            # print remain core stats #
            if shortCoreStats:
                SysMgr.addPrint(shortCoreStats[:-1] + "\n")

        # print GPU stats #
        gpuStats = {}
        gpuTempData = {}
        if SysMgr.isLinux and SysMgr.gpuEnable:
            if self.gpuData:
                SysMgr.addPrint("%s\n" % oneLine)

            for idx, value in self.gpuData.items():
                try:
                    if SysMgr.checkCutCond():
                        return

                    totalGpuUsage = long(value["CUR_LOAD"])

                    # save temperature #
                    if "TEMP" in value:
                        gpuTempData[idx] = value["TEMP"]
                    elif "GPU" in coreTempData:
                        gpuTempData[idx] = coreTempData["GPU"]

                    # convert color for GPU usage #
                    totalGpuUsageStr = "%s %%" % totalGpuUsage
                    if SysMgr.colorEnable and totalGpuUsage > 0:
                        totalGpuUsageStr = r"{0:>5}".format(totalGpuUsageStr)
                        totalGpuUsageStr = convCpuColor(
                            totalGpuUsage, totalGpuUsageStr
                        )

                    coreStat = "{0:<23}({1:>5})|".format(
                        idx[:23], totalGpuUsageStr
                    )

                    gpuStats[idx] = totalGpuUsage

                    # set frequency info #
                    try:
                        coreFreq = "%d Mhz" % value["CUR_FREQ"]
                    except SystemExit:
                        sys.exit(0)
                    except:
                        coreFreq = "? Mhz"
                    if (
                        "MIN_FREQ" in value
                        and "MAX_FREQ" in value
                        and value["MIN_FREQ"] > 0
                        and value["MAX_FREQ"] > 0
                    ):
                        coreFreq = "%s [%d-%d]" % (
                            coreFreq,
                            value["MIN_FREQ"],
                            value["MAX_FREQ"],
                        )
                    coreFreq = "%20s|" % coreFreq

                    # set memory info #
                    try:
                        memUsage = None
                        if (
                            self.gpuMemData
                            and not "GPUTEMP" in SysMgr.environList
                        ):
                            memUsage = convSize(
                                self.gpuMemData["0"]["size"],
                                isInt=True,
                                unit="M",
                            )
                    except SystemExit:
                        sys.exit(0)
                    except:
                        pass

                    # additional info #
                    try:
                        # GPU memory #
                        if memUsage:
                            coreFreq = "{0:^6}| {1:1}".format(
                                memUsage, coreFreq
                            )
                        # GPU temperature #
                        else:
                            coreFreq = "%3s C | %s" % (value["TEMP"], coreFreq)
                    except SystemExit:
                        sys.exit(0)
                    except:
                        # GPU temperature #
                        try:
                            coreFreq = "%3s C | %s" % (
                                coreTempData["GPU"],
                                coreFreq,
                            )
                        except SystemExit:
                            sys.exit(0)
                        except:
                            coreFreq = "%3s C | %s" % ("?", coreFreq)

                    lenCore = len(coreStat)
                    lenFreq = len(coreFreq)
                    lenLine = lineLength - lenCore - lenFreq - 2

                    # print bar graph for GPU usage #
                    if totalGpuUsage > 0:
                        coreGraph = "#" * long(lenLine * totalGpuUsage / 100)
                        coreGraph += " " * (lenLine - len(coreGraph))
                        origCoreGraph = coreGraph
                        coreGraph = convCpuColor(totalGpuUsage, coreGraph)
                        coreGraph += " " * (
                            len(coreGraph) - len(origCoreGraph)
                        )
                    else:
                        coreGraph = " " * lenLine

                    SysMgr.addPrint(
                        "%s%s| %s\n" % (coreStat, coreGraph, coreFreq)
                    )
                except SystemExit:
                    sys.exit(0)
                except:
                    continue

        # CPU core temperature average #
        coreTempData.pop("GPU", None)
        if coreTempData:
            coreTempAvg = sum(coreTempData.values()) / len(coreTempData)
        else:
            coreTempAvg = 0
        self.addSysInterval("cpu", "temp", coreTempAvg, True)

        # update peak list #
        peakUptime = UtilMgr.getEnvironNum(
            "PEAKUPTIMECOND", False, 0, False, isInt=True
        )
        peakRuntime = UtilMgr.getEnvironNum(
            "PEAKRUNTIMECOND", False, 0, False, isInt=True
        )
        if (
            (peakUptime == 0 == peakRuntime)
            or (peakUptime and SysMgr.uptime >= peakUptime)
            or (peakRuntime and SysMgr.getRuntime(sec=True) >= peakRuntime)
        ):
            # CPU usage #
            SysMgr.maxCpuUsage = max(SysMgr.maxCpuUsage, totalUsage)

            # CPU steal #
            SysMgr.maxCpuSteal = max(SysMgr.maxCpuSteal, stealUsage)

            # GPU #
            try:
                SysMgr.maxGpuUsage = max(
                    SysMgr.maxGpuUsage, max(gpuStats.values())
                )
            except SystemExit:
                sys.exit(0)
            except:
                pass

            # GPUMEM #
            try:
                SysMgr.maxGpuMemUsage = max(
                    SysMgr.maxGpuMemUsage, self.gpuMemData["0"]["size"] >> 20
                )
            except SystemExit:
                sys.exit(0)
            except:
                pass

            # MEM #
            SysMgr.minMemAvail = min(SysMgr.minMemAvail, availMem)

            # SWAP #
            SysMgr.maxSwapPer = max(SysMgr.maxSwapPer, swapUsagePer)

            # BLKRD #
            SysMgr.maxBlkRd = max(SysMgr.maxBlkRd, pgInMemDiff)

            # BLKWR #
            SysMgr.maxBlkWr = max(SysMgr.maxBlkWr, pgOutMemDiff)

            # NETIN #
            SysMgr.maxNetIn = max(
                SysMgr.maxNetIn, netIn >> (10 if kbunit else 20)
            )

            # NETOUT #
            SysMgr.maxNetOut = max(
                SysMgr.maxNetOut, netOut >> (10 if kbunit else 20)
            )

            # LOAD #
            try:
                loads = list(map(float, SysMgr.loadavg.split()[:3]))
                load1, load5, load15 = loads
                SysMgr.maxLoad1m = max(SysMgr.maxLoad1m, load1)
                SysMgr.maxLoad5m = max(SysMgr.maxLoad5m, load5)
                SysMgr.maxLoad15m = max(SysMgr.maxLoad5m, load15)
            except SystemExit:
                sys.exit(0)
            except:
                loads = []

            # STORAGE #
            try:
                storageData = SysMgr.sysInstance.getStorageInfo()
                SysMgr.minDiskFree = min(
                    SysMgr.minDiskFree, storageData["total"]["free"]
                )
            except SystemExit:
                sys.exit(0)
            except:
                pass

            # CONTEXTSWITCH #
            SysMgr.maxNrContextSwitch = max(
                SysMgr.maxNrContextSwitch, self.nrContextSwitch
            )

            # IRQ #
            SysMgr.maxNrIrq = max(SysMgr.maxNrIrq, SysMgr.nrIrq)

            # PSI #
            try:
                psiData = SysMgr.psiData
                SysMgr.maxPsiCpu = max(
                    SysMgr.maxPsiCpu, psiData["cpu"]["some"]["diff"]
                )
                SysMgr.maxPsiMem = max(
                    SysMgr.maxPsiMem, psiData["memory"]["full"]["diff"]
                )
                SysMgr.maxPsiIo = max(
                    SysMgr.maxPsiIo, psiData["io"]["full"]["diff"]
                )
            except SystemExit:
                sys.exit(0)
            except:
                pass

            # FD #
            try:
                SysMgr.maxNrFd = max(
                    SysMgr.maxNrFd, SysMgr.sysInstance.openFileData["cur"]
                )
            except SystemExit:
                sys.exit(0)
            except:
                pass

            # PROCESS #
            SysMgr.maxNrProcess = max(SysMgr.maxNrProcess, self.nrProcess)

            # CORE #
            SysMgr.maxNrCore = max(SysMgr.maxNrCore, SysMgr.nrCore)

            # THREAD #
            SysMgr.maxNrThread = max(SysMgr.maxNrThread, self.nrThread)

            # TASKABNORMAL #
            SysMgr.maxNrTaskAbnormal = max(
                SysMgr.maxNrTaskAbnormal, self.nrAbnormalTask
            )

            # TASKNEW #
            SysMgr.maxNrTaskNew = max(SysMgr.maxNrTaskNew, self.nrNewTask)

            # TASKDIE #
            SysMgr.maxNrTaskDie = max(SysMgr.maxNrTaskDie, self.nrDieTask)

            # INTERVAL #
            SysMgr.maxInterval = max(SysMgr.maxInterval, SysMgr.uptimeDiff)

        # update peak list #
        if "PEAKTICKLIST" in SysMgr.environList:
            peakList = SysMgr.getPeakList(False)
            for tick in SysMgr.environList["PEAKTICKLIST"]:
                try:
                    tick = long(tick)
                    for n, p in peakList.items():
                        if p < 0:
                            continue
                        self.addPeakInterval(n, p, tick)
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printWarn(
                        "failed to update peak tick(%s) list" % tick,
                        True,
                        True,
                    )

        # check report option #
        if not SysMgr.reportEnable and not SysMgr.jsonEnable:
            return

        # initialize report data #
        """
        - jsonData: just data to be kept in JSON format
        - reportData: serializable data to be reported outside in JSON format
        """
        self.reportData = {}

        # times #
        self.reportData["timestamp"] = SysMgr.uptime
        self.reportData["datetime"] = UtilMgr.getTime()
        self.reportData["utctime"] = UtilMgr.getTime(utc=True)

        # system info #
        if not SysMgr.sysInstance.uname:
            SysMgr.sysInstance.saveUnameInfo()
        if SysMgr.sysInstance.uname:
            if len(SysMgr.sysInstance.uname) == 4:
                osinfo, host, kernel, release = SysMgr.sysInstance.uname
            elif len(SysMgr.sysInstance.uname) == 5:
                (
                    osinfo,
                    host,
                    kernel,
                    release,
                    machine,
                ) = SysMgr.sysInstance.uname
            else:
                osinfo = host = kernel = release = None
        else:
            osinfo = host = kernel = release = None

        # system #
        if "system" in SysMgr.jsonData:
            self.reportData["system"] = SysMgr.jsonData["system"]
        else:
            self.reportData["system"] = {}
        self.reportData["system"].update(
            {
                "pid": SysMgr.pid,
                "uptime": SysMgr.uptime,
                "interval": interval,
                "nrIrq": nrIrq,
                "nrSoftIrq": nrSoftIrq,
                "os": osinfo,
                "host": host,
                "kernel": kernel,
                "release": release,
            }
        )

        # load #
        try:
            self.reportData["system"]["load1m"] = loads[0]
            self.reportData["system"]["load5m"] = loads[1]
            self.reportData["system"]["load15m"] = loads[2]
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # fd #
        try:
            self.reportData["fd"] = {}

            sysFds = SysMgr.getNrSysFdHandle()
            self.reportData["fd"]["curFd"] = sysFds[0]
            self.reportData["fd"]["maxFd"] = sysFds[2]
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # sock #
        try:
            self.reportData["sock"] = {}

            # check skip condition #
            if "NOSOCKPROF" in SysMgr.environList:
                raise Exception("no socket profilng")

            # flush socket caches #
            SysMgr.udpListCache = {}
            SysMgr.tcpListCache = {}
            SysMgr.udsListCache = {}

            # get the number of sockets #
            SysMgr.getSocketAddrList([])
            SysMgr.getUdsList([])

            self.reportData["sock"]["nrUDPSock"] = SysMgr.nrUDPSock
            self.reportData["sock"]["nrTCPSock"] = SysMgr.nrTCPSock
            self.reportData["sock"]["nrTCPConn"] = SysMgr.nrTCPConn
            self.reportData["sock"]["nrUDSSock"] = SysMgr.nrUDSSock

            # add socket interval #
            if self.checkSysInterval("sock"):
                self.addSysInterval("sock", "nrUDPSock", SysMgr.nrUDPSock)
                self.addSysInterval("sock", "nrTCPSock", SysMgr.nrTCPSock)
                self.addSysInterval("sock", "nrTCPConn", SysMgr.nrTCPConn)
                self.addSysInterval("sock", "nrUDSSock", SysMgr.nrUDSSock)
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # peak #
        self.reportData["peak"] = SysMgr.getPeakList()

        # threshold event count #
        self.reportData["thresholdEvents"] = SysMgr.thrEvtCntList

        # CPU #
        self.reportData["cpu"] = {
            "total": totalUsage,
            "idle": idleUsage,
            "user": userUsage,
            "kernel": kerUsage,
            "irq": irqUsage,
            "iowait": ioUsage,
            "nrCore": SysMgr.nrCore,
            "perCore": perCoreStats,
            "steal": stealUsage,
            "temp": coreTempAvg,
        }

        # GPU #
        self.reportData["gpu"] = gpuStats

        # GPU memory #
        self.reportData["gpumem"] = self.gpuMemData

        # GPU temperature #
        self.reportData["gputemp"] = gpuTempData

        # memory #
        self.reportData["mem"] = {
            "total": totalMem,
            "free": freeMem,
            "available": availMem,
            "anon": totalAnonMem,
            "file": totalFileMem,
            "slab": totalSlabMem,
            "cache": totalCacheMem,
            "kernel": totalKernelMem,
            "freeDiff": freeMemDiff,
            "availableDiff": availMemDiff,
            "anonDiff": anonMemDiff,
            "fileDiff": fileMemDiff,
            "slabDiff": slabMemDiff,
            "pgDirty": pgDirty,
            "pgRclmBg": pgRclmBg,
            "pgRclmFg": pgRclmFg,
            "nrMinFlt": nrMinFault,
            "pgMlock": pgMlock,
            "oomKill": SysMgr.nrOOMKill,
        }

        # CMA #
        try:
            self.reportData["mem"]["cmaTotal"] = cmaTotalMem
            self.reportData["mem"]["cmaFree"] = cmaFreeMem
            self.reportData["mem"]["cmaDev"] = cmaDevMem
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # swap #
        self.reportData["swap"] = {
            "total": swapTotal,
            "usage": swapUsage,
            "usagePer": swapUsagePer,
            "usageDiff": swapUsageDiff,
            "swapin": swapInMem,
            "swapout": swapOutMem,
        }

        # block #
        self.reportData["block"] = {
            "read": pgInMemDiff,
            "write": pgOutMemDiff,
            "ioWait": ioUsage,
            "nrMajFlt": nrMajFault,
            "nrTask": nrBlocked,
        }

        # task #
        self.reportData["task"] = {
            "nrBlocked": nrBlocked,
            "nrProc": self.nrProcess,
            "nrThread": self.nrThread,
            "nrCtx": nrCtxSwc,
        }

        # network #
        self.reportData["net"] = {"inbound": netIn, "outbound": netOut}

        # MAC #
        if not SysMgr.sysInstance.macAddr:
            SysMgr.sysInstance.saveMacAddr()
        if SysMgr.sysInstance.macAddr:
            macAddr = SysMgr.sysInstance.macAddr
            macStr = "%s_%s" % (macAddr[0], macAddr[1])
            self.reportData["net"]["macip"] = macStr
            self.reportData["net"]["mac"] = macAddr[0]
            self.reportData["net"]["ip"] = macAddr[1]

        # network device #
        if SysMgr.networkEnable:
            SysMgr.sysInstance.updateNetworkInfo()

            for dev, value in sorted(SysMgr.sysInstance.networkInfo.items()):
                # check value #
                if not "rdiff" in value or not "tdiff" in value:
                    continue

                self.reportData["net"][dev] = {}
                reportData = self.reportData["net"][dev]

                reportData["ipaddr"] = value["ipaddr"]

                rdiff = value["rdiff"]
                tdiff = value["tdiff"]

                reportData["recv"] = {
                    "bytes": rdiff[0],
                    "packets": rdiff[1],
                    "errs": rdiff[2],
                    "drop": rdiff[3],
                    "fifo": rdiff[4],
                    "frame": rdiff[5],
                    "compressed": rdiff[6],
                    "multicast": rdiff[7],
                }

                reportData["trans"] = {
                    "bytes": tdiff[0],
                    "packets": tdiff[1],
                    "errs": tdiff[2],
                    "drop": tdiff[3],
                    "fifo": tdiff[4],
                    "frame": tdiff[5],
                    "compressed": tdiff[6],
                    "multicast": tdiff[7],
                }

                # add network device interval #
                if self.checkSysInterval("net"):
                    self.addSysInterval("net", dev + ":recv", rdiff[0])
                    self.addSysInterval("net", dev + ":trans", tdiff[0])

        # storage #
        if SysMgr.diskEnable:
            SysMgr.sysInstance.updateStorageInfo()

            # copy storage data into report data structure #
            self.reportData["storage"] = UtilMgr.deepcopy(
                SysMgr.sysInstance.storageData
            )

            prevStorageData = SysMgr.sysInstance.prevStorageData

            monItems = ("usagePer", "free", "favail", "readtime", "writetime")

            # calculate diff of read /write on each devices #
            for dev, value in sorted(self.reportData["storage"].items()):
                # get read size on this interval #
                try:
                    value["read"] -= prevStorageData[dev]["read"]
                except SystemExit:
                    sys.exit(0)
                except:
                    value["read"] = 0

                # get write size on this interval #
                try:
                    value["write"] -= prevStorageData[dev]["write"]
                except SystemExit:
                    sys.exit(0)
                except:
                    value["write"] = 0

                # get readtime on this interval #
                try:
                    value["readtime"] -= prevStorageData[dev]["readtime"]
                except SystemExit:
                    sys.exit(0)
                except:
                    value["readtime"] = 0

                # get writetime on this interval #
                try:
                    value["writetime"] -= prevStorageData[dev]["writetime"]
                except SystemExit:
                    sys.exit(0)
                except:
                    value["writetime"] = 0

                # get iotime on this interval #
                try:
                    value["iotime"] -= prevStorageData[dev]["iotime"]
                except SystemExit:
                    sys.exit(0)
                except:
                    value["iotime"] = 0

                # get iowtime on this interval #
                try:
                    value["iowtime"] -= prevStorageData[dev]["iowtime"]
                except SystemExit:
                    sys.exit(0)
                except:
                    value["iowtime"] = 0

                # get avq on this interval #
                try:
                    value["avq"] = value["iowtime"] / value["iotime"]
                except SystemExit:
                    sys.exit(0)
                except:
                    value["avq"] = 0

                # add storage device interval #
                if self.checkSysInterval("storage"):
                    for mon in monItems:
                        if mon in value:
                            self.addSysInterval(
                                "storage", dev + ":" + mon, value[mon]
                            )
        else:
            self.reportData["storage"] = {}

        # PMU #
        if "PMU" in SysMgr.jsonData:
            self.reportData["PMU"] = SysMgr.jsonData["PMU"]

        # battery #
        if SysMgr.battery:
            try:
                self.reportData["battery"] = {
                    "per": SysMgr.battery[0],
                    "left": UtilMgr.convTime(SysMgr.battery[1]),
                    "plugged": "true" if SysMgr.battery[2] else "false",
                }
            except SystemExit:
                sys.exit(0)
            except:
                pass

        # custom #
        if "REPORTFUNC" in SysMgr.environList:
            for item in SysMgr.environList["REPORTFUNC"]:
                # get function info #
                try:
                    values = item.split(":")
                    path, func = values[:2]
                    if len(values) > 2:
                        args = values[2:]
                    else:
                        args = []
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr(
                        "failed to parse function info from '%s'" % item, True
                    )
                    sys.exit(-1)

                # call function #
                ret = UtilMgr.callPyFunc(path, func, args)
                try:
                    self.reportData.setdefault("custom", {})
                    fname = "%s(%s)" % (func, args)
                    self.reportData["custom"][fname] = ret
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printErr(
                        "failed to get custom data from '%s'" % item, True
                    )

        # apply report data to global data #
        if SysMgr.jsonEnable:
            SysMgr.jsonData.update(self.reportData)

    def setProcUsage(self):
        interval = SysMgr.uptimeDiff
        if interval == 0:
            interval = 0.1

        # set memory size shift factor #
        if "KBUNIT" in SysMgr.environList:

            def _memFactorPG(stat):
                return stat << 2

        else:

            def _memFactorPG(stat):
                return stat >> 8

        for pid, value in self.procData.items():
            try:
                # rss #
                value["rss"] = _memFactorPG(long(value["stat"][self.rssIdx]))

                # add RSS interval #
                self.addProcInterval("mem", pid, value, "rssInt", value["rss"])

                # define now data #
                nowData = value["stat"]

                # update runtime #
                value["runtime"] = long(
                    SysMgr.uptime - (float(nowData[self.starttimeIdx]) / 100)
                )

                # use total stat #
                if SysMgr.totalEnable:
                    raise Exception()

                # define prev data #
                prevData = self.prevProcData[pid]["stat"]
                if self.prevProcData[pid]["created"]:
                    value["created"] = True
                    if not value["comm"].startswith("*"):
                        value["comm"] = "*%s" % (value["comm"])

                # update I/O #
                if value["io"]:
                    value["read"] = (
                        value["io"]["read_bytes"]
                        - self.prevProcData[pid]["io"]["read_bytes"]
                    )
                    value["write"] = (
                        value["io"]["write_bytes"]
                        - self.prevProcData[pid]["io"]["write_bytes"]
                    )
                    value["rw"] = value["read"] + value["write"]

                # check stat change #
                if not value["changed"]:
                    value["utime"] = value["stime"] = value["ttime"] = 0
                    value["btime"] = value["cttime"] = 0

                    # add CPU interval #
                    self.addProcInterval(
                        "cpu", pid, value, "cpuInt", value["ttime"]
                    )

                    continue

                # major fault #
                value["majflt"] = (
                    nowData[self.majfltIdx] - prevData[self.majfltIdx]
                )

                # utime #
                utick = nowData[self.utimeIdx] - prevData[self.utimeIdx]
                value["utime"] = long(utick / interval)
                if (
                    value["utime"] >= 100
                    and value["stat"][self.nrthreadIdx] == "1"
                ):
                    value["utime"] = 100

                # stime #
                stick = nowData[self.stimeIdx] - prevData[self.stimeIdx]
                value["stime"] = long(stick / interval)
                if (
                    value["stime"] >= 100
                    and value["stat"][self.nrthreadIdx] == "1"
                ):
                    value["stime"] = 100

                # ttime #
                value["ttime"] = utick + stick
                if SysMgr.floatEnable:
                    value["ttime"] = round(value["ttime"] / interval, 1)
                else:
                    value["ttime"] = long(value["ttime"] / interval)
                if (
                    value["ttime"] >= 100
                    and value["stat"][self.nrthreadIdx] == "1"
                ):
                    value["ttime"] = 100
                elif value["ttime"] == 0:
                    value["ttime"] = 0

                # add CPU interval #
                self.addProcInterval(
                    "cpu", pid, value, "cpuInt", value["ttime"]
                )

                # child utime #
                cutick = nowData[self.cutimeIdx] - prevData[self.cutimeIdx]
                if SysMgr.floatEnable:
                    cutime = round(cutick / interval, 1)
                else:
                    cutime = long(cutick / interval)

                # child stime #
                cstick = nowData[self.cstimeIdx] - prevData[self.cstimeIdx]
                if SysMgr.floatEnable:
                    cstime = round(cstick / interval, 1)
                else:
                    cstime = long(cstick / interval)

                # child ttime #
                value["cttime"] = cutime + cstime

                # btime #
                btick = nowData[self.btimeIdx] - prevData[self.btimeIdx]
                if SysMgr.floatEnable:
                    value["btime"] = round(btick / interval, 1)
                    if value["btime"] == 0:
                        value["btime"] = 0
                else:
                    value["btime"] = long(btick / interval)

                if (
                    value["ttime"] + value["btime"] > 100
                    and value["stat"][self.nrthreadIdx] == "1"
                ):
                    value["btime"] = 100 - value["ttime"]
            except SystemExit:
                sys.exit(0)
            except:
                # set flags for new task #
                if SysMgr.totalEnable and pid in self.prevProcData:
                    pass
                else:
                    value["new"] = True
                    value["created"] = True

                    # update comm #
                    value["comm"] = "*%s" % value["comm"].lstrip("*")

                if SysMgr.totalEnable:
                    interval = 1

                value["majflt"] = nowData[self.majfltIdx]

                # user time #
                value["utime"] = long(nowData[self.utimeIdx] / interval)
                if (
                    not SysMgr.totalEnable
                    and value["utime"] >= 100
                    and value["stat"][self.nrthreadIdx] == "1"
                ):
                    value["utime"] = 100
                if SysMgr.floatEnable:
                    utick = round(nowData[self.utimeIdx] / interval, 1)
                else:
                    utick = value["utime"]

                # system time #
                value["stime"] = long(nowData[self.stimeIdx] / interval)
                if (
                    not SysMgr.totalEnable
                    and value["stime"] >= 100
                    and value["stat"][self.nrthreadIdx] == "1"
                ):
                    value["stime"] = 100
                if SysMgr.floatEnable:
                    stick = round(nowData[self.stimeIdx] / interval, 1)
                else:
                    stick = value["stime"]

                # total time #
                value["ttime"] = utick + stick
                if (
                    not SysMgr.totalEnable
                    and value["ttime"] >= 100
                    and value["stat"][self.nrthreadIdx] == "1"
                ):
                    value["ttime"] = 100

                # child time #
                if SysMgr.floatEnable:
                    cutime = round(nowData[self.cutimeIdx] / interval, 1)
                    cstime = round(nowData[self.cstimeIdx] / interval, 1)
                else:
                    cutime = long(nowData[self.cutimeIdx] / interval)
                    cstime = long(nowData[self.cstimeIdx] / interval)
                value["cttime"] = cutime + cstime

                # block time #
                if SysMgr.floatEnable:
                    value["btime"] = round(
                        nowData[self.btimeIdx] / interval, 1
                    )
                else:
                    value["btime"] = long(nowData[self.btimeIdx] / interval)
                if (
                    not SysMgr.totalEnable
                    and value["ttime"] + value["btime"] > 100
                    and value["stat"][self.nrthreadIdx] == "1"
                ):
                    value["btime"] = 100 - value["ttime"]

                if value["io"]:
                    value["read"] = value["io"]["read_bytes"]
                    value["write"] = value["io"]["write_bytes"]
                    value["rw"] = value["read"] + value["write"]

            # check delayacct_blkio_ticks error #
            if not SysMgr.totalEnable and value["btime"] >= 100:
                value["btime"] = 0

    def getMemDetails(self, idx, maps, vss=0):
        rss = 0
        sss = 0
        pss = 0
        wssTotal = 0
        memBuf = []

        if not maps:
            return [], 0, 0, 0

        convSize = UtilMgr.convSize2Unit

        for key, item in sorted(maps.items()):
            tmpstr = ""

            if not item or item["count"] == 0:
                continue

            # set add flag #
            add = key != "FLAG"

            try:
                prop = "Size:"
                tmpstr = "%s%s%7s / " % (
                    tmpstr,
                    "VSS:",
                    convSize(item[prop] << 10),
                )
            except:
                tmpstr = "%s%s%7s / " % (tmpstr, "VSS:", 0)

            try:
                prop = "Rss:"
                tmpstr = "%s%s%7s / " % (
                    tmpstr,
                    prop.upper(),
                    convSize(item[prop] << 10),
                )
                if add:
                    rss += item[prop]
            except:
                tmpstr = "%s%s%7s / " % (tmpstr, prop.upper(), 0)

            try:
                prop = "Pss:"
                tmpstr = "%s%s%7s / " % (
                    tmpstr,
                    prop.upper(),
                    convSize(item[prop] << 10),
                )
                if add:
                    pss += item[prop]
            except:
                tmpstr = "%s%s%7s / " % (tmpstr, prop.upper(), 0)

            try:
                prop = "Swap:"
                tmpstr = "%s%s%7s / " % (
                    tmpstr,
                    prop.upper(),
                    convSize(item[prop] << 10),
                )
            except:
                tmpstr = "%s%s%7s / " % (tmpstr, prop.upper(), 0)

            try:
                prop = "AnonHugePages:"
                tmpstr = "%s%s:%5s / " % (
                    tmpstr,
                    "HUGE",
                    convSize(item[prop] << 10, True),
                )
            except:
                tmpstr = "%s%s:%5s / " % (tmpstr, "HUGE", 0)

            try:
                prop = "Locked:"
                tmpstr = "%s%s%6s / " % (
                    tmpstr,
                    "LOCK:",
                    convSize(item[prop] << 10, True),
                )
            except:
                tmpstr = "%s%s%6s / " % (tmpstr, "LOCK:", 0)

            try:
                prop = "Shared_Clean:"
                if add:
                    sss += item[prop]
            except:
                pass

            try:
                prop = "Shared_Dirty:"
                if add:
                    sss += item[prop]
                tmpstr = "%s%s:%7s / " % (
                    tmpstr,
                    "SDRT",
                    convSize(item[prop] << 10),
                )
            except:
                tmpstr = "%s%s:%7s / " % (tmpstr, "SDRT", 0)

            try:
                prop = "Private_Dirty:"
                tmpstr = "%s%s:%7s" % (
                    tmpstr,
                    "PDRT",
                    convSize(item[prop] << 10),
                )
            except:
                tmpstr = "%s%s:%7s" % (tmpstr, "PDRT", 0)

            """
            try:
                prop = 'NOPM'
                tmpstr = "%s%s:%5s" % \
                    (tmpstr, prop, convSize(item[prop] << 10, True))
            except:
                tmpstr = "%s%s:%5s" % (tmpstr, prop, 0)
            """

            mtype = "MEM(%s/%s)" % (key, item["count"])
            memBuf.append([key, "{0:>39} | {1:1}|\n".format(mtype, tmpstr)])

            if SysMgr.wssEnable:
                # get current WSS size #
                try:
                    wssNum = item["Referenced:"] << 10
                    if add:
                        wssTotal += wssNum
                    wss = convSize(wssNum, False)
                except:
                    wss = 0

                # get previous WSS history #
                try:
                    self.procData[idx]["wss"] = self.prevProcData[idx]["wss"]
                except:
                    self.procData[idx].setdefault("wss", {})

                    # clear reference bits #
                    SysMgr.clearPageRefs(idx, val="1")

                # update WSS history #
                try:
                    history = self.procData[idx]["wss"][key]
                    self.procData[idx]["wss"][key] = "%s -> %7s" % (
                        history,
                        wss,
                    )
                except SystemExit:
                    sys.exit(0)
                except:
                    initVal = "[%s]" % UtilMgr.convColor(wss, "RED", 7)
                    self.procData[idx]["wss"][key] = initVal

        # update uss #
        uss = rss - sss
        wssTotal = wssTotal >> 10

        # update history for wss #
        if SysMgr.wssEnable:
            unitItems = (
                ("VSS", vss),
                ("RSS", rss),
                ("PSS", pss),
                ("USS", uss),
                ("WSS", wssTotal),
            )

            for item in unitItems:
                name = item[0]
                unit = UtilMgr.convSize2Unit(item[1] << 10)
                if name in self.procData[idx]["wss"]:
                    history = self.procData[idx]["wss"][name]
                    self.procData[idx]["wss"][name] = "%s -> %7s" % (
                        history,
                        unit,
                    )
                else:
                    initVal = "[%s]" % UtilMgr.convColor(unit, "RED", 7)
                    self.procData[idx]["wss"][name] = initVal

        # erase memory info #
        if not SysMgr.memEnable:
            memBuf = []

        return memBuf, rss, pss, uss

    def printDefaultUsage(self, title):
        try:
            nrNewThreads = (
                self.cpuData["processes"]["processes"]
                - self.prevCpuData["processes"]["processes"]
            )
        except SystemExit:
            sys.exit(0)
        except:
            nrNewThreads = 0

        try:
            if SysMgr.isLinux:
                loadlist = SysMgr.loadavg.split()[:3]
            else:
                loadlist = list(SysMgr.loadavg)

            for idx, load in enumerate(loadlist):
                loadlist[idx] = str("%d" % float(load))

            loadavg = "/".join(loadlist)
        except SystemExit:
            sys.exit(0)
        except:
            loadavg = "?"

        try:
            steal = SysMgr.cpuStealUsage
            if steal:
                stealstr = " [Steal: %d] " % steal
            else:
                stealstr = " "
        except SystemExit:
            sys.exit(0)
        except:
            stealstr = " "
            steal = 0

        try:
            oomKill = long(self.vmData["oom_kill"])
            if SysMgr.nrTotalOOMKill == -1:
                SysMgr.nrOOMKill = 0
            else:
                SysMgr.nrOOMKill = oomKill - SysMgr.nrTotalOOMKill
            SysMgr.nrTotalOOMKill = oomKill
            if oomKill:
                oomstr = " [OOM: %d] " % SysMgr.nrOOMKill
            else:
                oomstr = " "
        except SystemExit:
            sys.exit(0)
        except:
            oomstr = " "
            oomKill = 0

        try:
            self.nrContextSwitch = nrCtxt = (
                self.cpuData["ctxt"]["ctxt"] - self.prevCpuData["ctxt"]["ctxt"]
            )

            if nrCtxt < 0:
                nrCtxt = 0
        except SystemExit:
            sys.exit(0)
        except:
            nrCtxt = 0

        try:
            nrTermThreads = abs(
                self.nrThread - nrNewThreads - self.nrPrevThread
            )
        except SystemExit:
            sys.exit(0)
        except:
            nrTermThreads = 0

        try:
            SysMgr.nrIrq = nrIrq = (
                self.cpuData["intr"]["intr"] - self.prevCpuData["intr"]["intr"]
            )

            if nrIrq < 0:
                nrIrq = 0
        except SystemExit:
            sys.exit(0)
        except:
            nrIrq = 0

        try:
            memTotal = UtilMgr.convSize2Unit(self.memData["MemTotal"] << 10)
            if SysMgr.memTotal < 0:
                SysMgr.memTotal = self.memData["MemTotal"] >> 10
        except SystemExit:
            sys.exit(0)
        except:
            memTotal = 0

        try:
            swapTotal = UtilMgr.convSize2Unit(self.memData["SwapTotal"] << 10)
        except SystemExit:
            sys.exit(0)
        except:
            swapTotal = 0

        try:
            if SysMgr.battery:
                battery = " [Bat: %d%%/%s/%s]" % (
                    SysMgr.battery[0],
                    UtilMgr.convTime(SysMgr.battery[1]),
                    "+" if SysMgr.battery[2] else "-",
                )
            else:
                battery = ""
        except SystemExit:
            sys.exit(0)
        except:
            battery = ""

        # dlt time #
        if SysMgr.dltTime:
            dlttime = " [DLT: %.3f]" % SysMgr.dltTime
        else:
            dlttime = ""

        # print stats #
        SysMgr.addPrint(
            UtilMgr.convColor(
                (
                    "%s [Time: %7.3f] [Inter: %.1f]%s [Ctxt: %d] "
                    "[Life: +%d/-%d]%s[IRQ: %d]%s[Core: %d] [Task: %d/%d] "
                    "[Load: %s] [RAM: %s] [Swap: %s]%s\n"
                )
                % (
                    title,
                    SysMgr.uptime,
                    SysMgr.uptimeDiff,
                    dlttime,
                    nrCtxt,
                    nrNewThreads,
                    nrTermThreads,
                    oomstr,
                    nrIrq,
                    stealstr,
                    SysMgr.nrCore,
                    self.nrProcess,
                    self.nrThread,
                    loadavg,
                    memTotal,
                    swapTotal,
                    battery,
                ),
                "BOLD",
            )
        )

        # add JSON stats #
        if SysMgr.jsonEnable:
            SysMgr.jsonData.setdefault("system", {})
            jsonData = SysMgr.jsonData["system"]

            jsonData["uptime"] = SysMgr.uptime
            jsonData["startTime"] = SysMgr.startTime
            jsonData["interval"] = SysMgr.uptimeDiff
            jsonData["nrCtxt"] = nrCtxt
            jsonData["nrNewThreads"] = nrNewThreads
            jsonData["nrTermThreads"] = nrTermThreads
            jsonData["nrProcess"] = self.nrProcess
            jsonData["nrThreads"] = self.nrThread

            if oomstr:
                jsonData["oomKill"] = oomKill

            if battery:
                try:
                    jsonData["battery"] = {
                        "per": SysMgr.battery[0],
                        "left": UtilMgr.convTime(SysMgr.battery[1]),
                        "plugged": "true" if SysMgr.battery[2] else "false",
                    }
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

    def printDmabufUsage(self, nrIndent):
        if not self.dmabufData:
            return

        databuf = ""
        edata = "%s %-10s" % (" " * nrIndent, " ")
        data = "%s [%-6s > " % (" " * nrIndent, "DMABUF")
        curline = str(data)

        # merge stats #
        stats = {"total": 0}
        for v in self.dmabufData.values():
            name = v.get("exporter_name")
            if not name:
                continue
            stats.setdefault(name, 0)
            size = long(v.get("size", 0))
            stats[name] += size
            stats["total"] += size

        for name, size in sorted(stats.items()):
            if not size:
                continue

            size = UtilMgr.convSize2Unit(size)

            # apply color #
            if name == "total":
                size = UtilMgr.convColor(size, "YELLOW")

            # add stats in a line #
            item = "%s: %s" % (name, size)

            if len(item) + len(curline) >= SysMgr.ttyCols:
                databuf += "%s\n" % curline.rstrip(", ")
                curline = str(edata)

            curline += "%s, " % item

        # check last line #
        if curline != data:
            databuf += "%s]" % curline.rstrip(", \n")
        databuf = databuf.rstrip(", \n")
        if not databuf:
            return

        SysMgr.addPrint("%s\n" % databuf, newline=databuf.count("\n") + 1)

    def printMemoryUsage(self, nrIndent):
        if (
            not self.memData
            or not SysMgr.memEnable
            or "NOSYSMEM" in SysMgr.environList
        ) and not "ONLYSYSMEM" in SysMgr.environList:
            return

        databuf = ""
        edata = "%s %-10s" % (" " * nrIndent, " ")
        data = "%s [%-5s > " % (" " * nrIndent, "SYSTEM")
        curline = str(data)

        colorList = ["Active", "Inactive", "Cached"]

        for name, value in sorted(self.memData.items()):
            if not value:
                continue

            size = UtilMgr.convSize2Unit(value << 10)

            # add stats in a line #
            item = "%-15s %7s" % ("%s:" % name, size)
            if len(item) + len(curline) >= SysMgr.ttyCols:
                databuf += "%s\n" % curline.rstrip(", ")
                curline = str(edata)

            # set color #
            if name in colorList or name.endswith("Free"):
                item = "%-15s %7s" % (
                    "%s:" % name,
                    UtilMgr.convColor(size, "YELLOW", 7),
                )

            curline += "%s, " % item

        # check last line #
        if curline != data:
            databuf += "%s]" % curline.rstrip(", \n")
        databuf = databuf.rstrip(", \n")

        SysMgr.addPrint("%s\n" % databuf, newline=databuf.count("\n") + 1)

    def printBuddyUsage(self, nrIndent):
        if not self.buddyData:
            return

        databuf = ""
        edata = "%s %-9s" % (" " * nrIndent, " ")
        data = "%s [%-5s > " % (" " * nrIndent, "BUDDY")
        curline = str(data)
        nrLine = 1

        for node, items in sorted(self.buddyData.items()):
            for zone, orders in sorted(items.items()):
                orders = "_".join(list(map(UtilMgr.convNum, orders)))
                # add stats in a line #
                item = "%s: %s" % (zone, orders)
                if (
                    SysMgr.ttyCols
                    and len(item) + len(curline) >= SysMgr.ttyCols
                ):
                    databuf += "%s\n" % curline.rstrip(", ")
                    curline = str(edata)
                    nrLine += 1
                curline += "%s / " % item

        # check last line #
        if curline != data:
            databuf += "%s]" % curline.rstrip(", \n/")
        databuf = databuf.rstrip(", \n")
        databuf = databuf.replace(", ", ",")
        if not databuf:
            return

        SysMgr.addPrint("%s\n" % databuf, newline=databuf.count("\n") + 1)

    def printKSMStat(self, nrIndent):
        if not self.ksmData:
            return

        databuf = ""
        edata = "%s %-8s" % (" " * nrIndent, " ")
        data = "%s [%-4s > " % (" " * nrIndent, "KSM")
        curline = str(data)
        nrLine = 1
        rmColor = UtilMgr.removeColor
        convColor = UtilMgr.convColor
        convSize = UtilMgr.convSize2Unit
        total = 0

        # get total pages #
        for name, value in sorted(self.ksmData.items()):
            # accumulate pages #
            if name in ("pages_shared", "pages_sharing", "pages_unshared"):
                value = long(value)
                total += value

        self.ksmData["pages_total"] = total

        for name, value in sorted(self.ksmData.items()):
            # convert value #
            if name.startswith("pages_") and not name.endswith("_scan"):
                value = convSize(long(value) << 12)
            else:
                value = UtilMgr.convNum(value)

            # apply color #
            if name in ("pages_shared", "pages_sharing"):
                value = convColor(value, "GREEN")
            elif name in (
                "max_page_sharing",
                "full_scans",
                "pages_unshared",
                "pages_total",
            ):
                value = convColor(value, "YELLOW")
            elif name == "run":
                value = convColor(value, "WARNING")

            # add stats in a line #
            item = "%s: %s" % (name, value)
            if len(rmColor(item)) + len(rmColor(curline)) >= SysMgr.ttyCols:
                databuf += "%s\n" % curline.rstrip(", ")
                curline = str(edata)
                nrLine += 1

            curline += "%s, " % item

        # check last line #
        if curline != data:
            databuf += "%s]" % curline.rstrip(", \n")
        databuf = databuf.rstrip(", \n")

        SysMgr.addPrint("%s\n" % databuf, newline=databuf.count("\n") + 1)

    def printSlabUsage(self, nrIndent):
        if not self.slabData:
            return

        databuf = ""
        edata = "%s %-8s" % (" " * nrIndent, " ")
        data = "%s [%-4s > " % (" " * nrIndent, "SLAB")
        curline = str(data)
        nrLine = 1

        # get cutline #
        if "NRSLAB" in SysMgr.environList:
            cutLine = UtilMgr.getEnvironNum("NRSLAB")
        else:
            cutLine = 3

        # get target object #
        if "TOTALSLAB" in SysMgr.environList:
            target = "totsize"
        else:
            target = "actsize"

        for name, items in sorted(
            self.slabData.items(), key=lambda x: x[1][target], reverse=True
        ):
            # add stats in a line #
            item = "%s: %s" % (name, UtilMgr.convSize2Unit(items[target]))
            if len(item) + len(curline) >= SysMgr.ttyCols:
                if nrLine >= cutLine:
                    break

                databuf += "%s\n" % curline.rstrip(", ")
                curline = str(edata)
                nrLine += 1

            curline += "%s, " % item

        # check last line #
        if curline != data:
            databuf += "%s]" % curline.rstrip(", \n")
        databuf = databuf.rstrip(", \n")
        if not databuf:
            return

        SysMgr.addPrint("%s\n" % databuf, newline=databuf.count("\n") + 1)

    def printVmInfo(self, nrIndent):
        if (
            not SysMgr.memEnable or "NOSYSMEM" in SysMgr.environList
        ) and not "ONLYVM" in SysMgr.environList:
            return

        vmInfo = "%s [VM > " % (" " * nrIndent)

        # swappiness #
        vmInfo += "swappiness: %s" % SysMgr.swappiness

        # vmpressure #
        vmInfo += " / cache_pressure: %s" % UtilMgr.convNum(SysMgr.vmpressure)

        # overcommit #
        vmInfo += " / overcommit: %s" % SysMgr.overcommit

        SysMgr.addPrint(vmInfo + "]\n")

    def printLMKStat(self, nrIndent):
        if not SysMgr.memEnable and not "LMKINFO" in SysMgr.environList:
            return

        # save LMK info #
        try:
            memBuf = None
            SysMgr.lmkFd.seek(0)
            memBuf = SysMgr.lmkFd.readline()
        except SystemExit:
            sys.exit(0)
        except:
            try:
                memPath = SysMgr.lmkPath
                SysMgr.lmkFd = open(memPath, "r")
                memBuf = SysMgr.lmkFd.readline()
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printOpenWarn(memPath)

        if not memBuf:
            return

        # threshold list #
        threshold = [
            "FGAPP",
            "VISAPP",
            "SECSER",
            "HIDAPP",
            "CONPRO",
            "EMPAPP",
        ]

        stats = memBuf.split(",")
        if stats:
            stats = list(map(long, stats))

        isReport = SysMgr.reportEnable or SysMgr.jsonEnable
        if isReport:
            SysMgr.jsonData.setdefault("lmk", {})

        lmkstr = "%s [LMK > " % (" " * nrIndent)

        for idx, item in enumerate(stats):
            lmkstr = "%s%s: %s, " % (
                lmkstr,
                threshold[idx],
                UtilMgr.convSize2Unit(item << 12),
            )
            if isReport:
                SysMgr.jsonData["lmk"][threshold[idx]] = item >> 8

        SysMgr.addPrint(lmkstr[:-2] + "]\n")

    def printZoneUsage(self, nrIndent):
        if not self.zoneData:
            return

        # add JSON stats #
        if SysMgr.jsonEnable:
            SysMgr.jsonData.setdefault("zone", {})

        ttyCols = SysMgr.ttyCols
        convSize = UtilMgr.convSize2Unit
        convColor = UtilMgr.convColor

        # define stat list #
        STAT_LIST = [
            "free",
            "min",
            "low",
            "high",
            "managed",
            "present",
            "spanned",
        ]

        for node, items in sorted(self.zoneData.items()):
            # check invalid zones #
            if items.get("spanned") == 0:
                continue

            zoneData = "%s [%-10s > " % (" " * nrIndent, "N%s" % node)
            lenZone = len(zoneData)

            for info in STAT_LIST:
                # check stats by sequence #
                if not info in items:
                    continue

                val = items[info]
                stat = convSize(val << 12)
                statOrig = stat

                if SysMgr.jsonEnable:
                    SysMgr.jsonData["zone"].setdefault(node, {})

                if info == "free":
                    if (
                        not node in self.prevZoneData
                        or not info in self.prevZoneData[node]
                    ):
                        diff = val
                    else:
                        diff = val - self.prevZoneData[node][info]

                    # set color #
                    if diff > 0:
                        color = "GREEN"
                    else:
                        color = "YELLOW"

                    diff = convSize(diff << 12)
                    ninfo = "diff"

                    if SysMgr.jsonEnable:
                        SysMgr.jsonData["zone"][node][ninfo] = diff

                    zoneStat = "%s: %7s / " % (ninfo, diff)
                    lenZoneStat = len(zoneStat)
                    if diff != "0":
                        zoneStat = "%s: %7s / " % (
                            ninfo,
                            convColor(diff, color, 7),
                        )

                    if ttyCols and lenZone + lenZoneStat >= ttyCols:
                        zoneData = "%s\n%s %s" % (
                            zoneData,
                            " " * 14,
                            " " * nrIndent,
                        )
                        lenZone = nrIndent

                    zoneData += zoneStat
                    lenZone += lenZoneStat

                    # apply color #
                    try:
                        if val == 0:
                            pass
                        elif val <= items["min"]:
                            stat = convColor(stat, "RED", 6)
                        elif val <= items["low"]:
                            stat = convColor(stat, "YELLOW", 6)
                        elif val <= items["high"]:
                            stat = convColor(stat, "CYAN", 6)
                        else:
                            stat = convColor(stat, "GREEN", 6)
                    except SystemExit:
                        sys.exit(0)
                    except:
                        pass

                if SysMgr.jsonEnable:
                    SysMgr.jsonData["zone"][node][info] = statOrig
                    continue

                zoneStat = "%s: %6s / " % (info, statOrig)
                lenZoneStat = len(zoneStat)
                zoneStat = "%s: %6s / " % (info, stat)

                if ttyCols and lenZone + lenZoneStat >= ttyCols:
                    zoneData = "%s\n%s %s" % (
                        zoneData,
                        " " * 14,
                        " " * nrIndent,
                    )
                    lenZone = nrIndent

                zoneData += zoneStat
                lenZone += lenZoneStat

            SysMgr.addPrint(
                "{0:<1}]\n".format(zoneData[:-2].rstrip()),
                newline=zoneData.count("\n") + 1,
            )

    def printPeakStat(self, nrIndent):
        if not SysMgr.peakEnable:
            return

        # get peak items #
        peakList = SysMgr.getPeakList()

        databuf = ""
        edata = "%s %-8s" % (" " * nrIndent, " ")
        data = "%s [%-4s > " % (" " * nrIndent, "PEAK")
        curline = str(data)

        for name, val in sorted(peakList.items()):
            if val <= 0:
                continue

            # add stats in a line #
            item = "%s: %s" % (name, UtilMgr.convNum(val))
            if len(item) + len(curline) >= SysMgr.ttyCols:
                databuf += "%s\n" % curline.rstrip(", ")
                curline = str(edata)

            curline += "%s, " % item

        # check last line #
        if curline != data:
            databuf += "%s]" % curline.rstrip(", \n")
        databuf = databuf.rstrip(", \n")
        if not databuf:
            return

        SysMgr.addPrint("%s\n" % databuf, newline=databuf.count("\n") + 1)

    def printPsiStat(self, nrIndent):
        if not SysMgr.prevPsiData:
            return

        psiData = "%s [PSI > " % (" " * nrIndent)

        # add JSON stats #
        if SysMgr.jsonEnable:
            SysMgr.jsonData.setdefault("psi", {})

        for res in ("cpu", "memory", "io"):
            if not res in SysMgr.psiData:
                continue

            data = SysMgr.psiData[res]

            resstr = ""

            if SysMgr.jsonEnable:
                SysMgr.jsonData["psi"].setdefault(res, {})

            for attr, stat in data.items():
                # get total diff #
                try:
                    diff = (
                        (
                            stat["total"]
                            - SysMgr.prevPsiData[res][attr]["total"]
                        )
                        / 10000.0
                        / SysMgr.uptimeDiff
                    )
                    SysMgr.psiData[res][attr]["diff"] = diff
                except SystemExit:
                    sys.exit(0)
                except:
                    continue

                if SysMgr.jsonEnable:
                    SysMgr.jsonData["psi"][res][attr] = diff

                if diff >= 0.1:
                    diff = UtilMgr.convColor(
                        "%s%%" % UtilMgr.convNum(diff, True), "RED"
                    )
                else:
                    diff = "0%"

                resstr += "%s(%s) / " % (attr, diff)

            if resstr:
                psiData += "[%s] %s " % (res, resstr.rstrip("/ "))

        SysMgr.addPrint("{0:<1}]\n".format(psiData.rstrip()))

    def printIrqUsage(self, nrIndent):
        if not self.irqData:
            return

        nrIrq = 0
        irqData = "%s [IRQ > " % (" " * nrIndent)
        lenIrq = len(irqData)

        # add JSON stats #
        if SysMgr.jsonEnable:
            SysMgr.jsonData.setdefault("irq", {})

        for irq, cnt in sorted(
            self.irqData.items(),
            key=lambda e: self.irqData[e[0]]
            if not e[0] in self.prevIrqData
            else e[1] - self.prevIrqData[e[0]],
            reverse=True,
        ):
            if not irq in self.prevIrqData:
                irqDiff = cnt
            else:
                irqDiff = cnt - self.prevIrqData[irq]

            if irqDiff <= 0:
                break
            elif SysMgr.jsonEnable:
                SysMgr.jsonData["irq"][irq] = irqDiff

            nrIrq += 1
            newIrq = "%s: %s / " % (irq, UtilMgr.convNum(irqDiff))
            lenNewIrq = len(newIrq)

            if lenIrq + lenNewIrq >= SysMgr.ttyCols:
                irqData = "%s\n%s %s" % (irqData, " " * 7, " " * nrIndent)
                lenIrq = nrIndent

            irqData += newIrq
            lenIrq += lenNewIrq

        if nrIrq > 0:
            SysMgr.addPrint(
                "{0:<1}]\n".format(irqData[:-2].rstrip()),
                newline=irqData.count("\n") + 1,
            )

    def printPerfUsage(self, nrIndent):
        if not SysMgr.perfEventData:
            return

        perfString = SysMgr.getPerfString(SysMgr.perfEventData)
        if not perfString:
            return

        SysMgr.addPrint("%s [PMU > %s]\n" % (" " * nrIndent, perfString))

        # add JSON stats #
        if SysMgr.jsonEnable:
            SysMgr.jsonData.setdefault("PMU", {})
            jsonData = SysMgr.jsonData["PMU"]

            plist = perfString[1:-1].split(" / ")
            for stat in plist:
                metric, value = stat.split(":")
                jsonData[metric] = value.strip()

    def printNetworkUsage(self):
        if not SysMgr.networkEnable:
            return
        elif SysMgr.uptimeDiff == 0:
            return
        elif not SysMgr.sysInstance:
            return

        # update network usage #
        SysMgr.sysInstance.updateNetworkInfo()

        if SysMgr.checkCutCond():
            return

        SysMgr.addPrint("%s\n" % twoLine)
        SysMgr.addPrint(
            "{0:^40} | {1:^53} | {2:^53} |\n{3:1}\n".format(
                "Network", "Receive", "Transfer", oneLine
            ),
            newline=2,
        )
        SysMgr.addPrint(
            (
                "{0:^16} | {1:^21} | "
                "{2:^8} | {3:^8} | {4:^8} | {5:^8} | {6:^9} | "
                "{2:^8} | {3:^8} | {4:^8} | {5:^8} | {6:^9} |\n"
            ).format(
                "Dev", "IP", "Size", "Packet", "Error", "Drop", "Multicast"
            )
        )
        SysMgr.addPrint("%s\n" % twoLine)

        convSize = UtilMgr.convSize2Unit

        if SysMgr.jsonEnable:
            SysMgr.jsonData.setdefault("net", {})

        cnt = 0
        totalStat = {"rdiff": [0] * 5, "tdiff": [0] * 5}

        for dev, val in sorted(
            SysMgr.sysInstance.networkInfo.items(), key=lambda e: e[0]
        ):
            """
            [ network stat sequence ]
            bytes, packets, errs, drop, fifo,
                frame, compressed, multicast
            """

            try:
                rdiff = val["rdiff"]
                tdiff = val["tdiff"]

                # sum total stats #
                totalStat["rdiff"][0] += rdiff[0]
                totalStat["rdiff"][1] += rdiff[1]
                totalStat["rdiff"][2] += rdiff[2]
                totalStat["rdiff"][3] += rdiff[3]
                totalStat["rdiff"][4] += rdiff[-1]
                totalStat["tdiff"][0] += tdiff[0]
                totalStat["tdiff"][1] += tdiff[1]
                totalStat["tdiff"][2] += tdiff[2]
                totalStat["tdiff"][3] += tdiff[3]
                totalStat["tdiff"][4] += tdiff[-1]
            except SystemExit:
                sys.exit(0)
            except:
                rdiff = [0 for i in xrange(len(val["recv"]))]
                tdiff = [0 for i in xrange(len(val["tran"]))]

            try:
                # convert color for network usage #
                recvSize = "%8s" % convSize(rdiff[0])
                if rdiff[0] > 0:
                    recvSize = UtilMgr.convColor(recvSize, "YELLOW")
                tranSize = "%8s" % convSize(tdiff[0])
                if tdiff[0] > 0:
                    tranSize = UtilMgr.convColor(tranSize, "YELLOW")

                # convert color for network error #
                recvErr = "%8s" % convSize(rdiff[2])
                if rdiff[2] > 0:
                    recvErr = UtilMgr.convColor(recvErr, "RED")
                tranErr = "%8s" % convSize(tdiff[2])
                if tdiff[2] > 0:
                    tranErr = UtilMgr.convColor(tranErr, "RED")

                SysMgr.addPrint(
                    (
                        "{0:>16} | {1:>21} | "
                        "{2:>8} | {3:>8} | {4:>8} | {5:>8} | {6:>9} | "
                        "{7:>8} | {8:>8} | {9:>8} | {10:>8} | {11:>9} |\n"
                    ).format(
                        dev[-16:],
                        val["ipaddr"][:21],
                        recvSize,
                        convSize(rdiff[1]),
                        recvErr,
                        convSize(rdiff[3]),
                        convSize(rdiff[-1]),
                        tranSize,
                        convSize(tdiff[1]),
                        tranErr,
                        convSize(tdiff[3]),
                        convSize(tdiff[-1]),
                    )
                )
                cnt += 1
            except SystemExit:
                sys.exit(0)
            except:
                pass

        if cnt == 0:
            SysMgr.addPrint("    None\n")
        else:
            rdiff = totalStat["rdiff"]
            tdiff = totalStat["tdiff"]

            # convert color for network usage #
            recvSize = "%8s" % convSize(rdiff[0])
            if rdiff[0] > 0:
                recvSize = UtilMgr.convColor(recvSize, "YELLOW")
            tranSize = "%8s" % convSize(tdiff[0])
            if tdiff[0] > 0:
                tranSize = UtilMgr.convColor(tranSize, "YELLOW")

            # convert color for network error #
            recvErr = "%8s" % convSize(rdiff[2])
            if rdiff[2] > 0:
                recvErr = UtilMgr.convColor(recvErr, "RED")
            tranErr = "%8s" % convSize(tdiff[2])
            if tdiff[2] > 0:
                tranErr = UtilMgr.convColor(tranErr, "RED")

            SysMgr.addPrint(
                (
                    "{0:>16} | {1:^21} | "
                    "{2:>8} | {3:>8} | {4:>8} | {5:>8} | {6:>9} | "
                    "{7:>8} | {8:>8} | {9:>8} | {10:>8} | {11:>9} |\n"
                ).format(
                    "[ TOTAL ]",
                    " ",
                    recvSize,
                    convSize(rdiff[1]),
                    recvErr,
                    convSize(rdiff[3]),
                    convSize(rdiff[-1]),
                    tranSize,
                    convSize(tdiff[1]),
                    tranErr,
                    convSize(tdiff[3]),
                    convSize(tdiff[-1]),
                )
            )

    def printDiskUsage(self):
        if not SysMgr.diskEnable:
            return
        elif SysMgr.uptimeDiff == 0:
            return
        elif not SysMgr.sysInstance:
            return

        # update storage usage #
        SysMgr.sysInstance.updateStorageInfo()

        if SysMgr.checkCutCond():
            return

        # define shortcut variables #
        convColor = UtilMgr.convColor
        convSize2Unit = UtilMgr.convSize2Unit

        SysMgr.addPrint("%s\n" % twoLine)
        SysMgr.addPrint(
            (
                "{0:^24}|{1:4}|{2:^5}|{3:^7}|{4:^7}|{5:>7}({6:>7})|"
                "{7:^5}|{8:^7}|{9:^7}|{10:^8}|{11:^53}|\n"
            ).format(
                "DEV",
                "BUSY",
                "AVQ",
                "READ",
                "WRITE",
                "FREE",
                "DIFF",
                "USAGE",
                "TOTAL",
                "NrAVF",
                "FS",
                "MountPoint <Option>",
            )
        )
        SysMgr.addPrint("%s\n" % oneLine)

        storageData = SysMgr.sysInstance.storageData
        prevStorageData = SysMgr.sysInstance.prevStorageData

        if SysMgr.jsonEnable:
            SysMgr.jsonData.setdefault("storage", {})

        onlyBusy = True if "ONLYBUSY" in SysMgr.environList else False

        printCnt = 0
        for dev, value in sorted(
            storageData.items(),
            key=lambda e: e[1]["load"] if "load" in e[1] else 0,
            reverse=True,
        ):
            # skip total usage #
            if dev == "total":
                continue
            elif SysMgr.totalEnable:
                pass
            elif value["total"] == 0:
                continue

            # handle fs #
            origDev = dev
            if "mount" in value:
                if value["mount"]["fs"] == "tmpfs":
                    dev = value["mount"]["path"]
                elif value["mount"]["fs"] == "devfs":
                    continue

            # get readtime #
            """
            try:
                if SysMgr.totalEnable:
                    readtime = value["readtime"]
                else:
                    readtime = (
                        value["readtime"]
                        - prevStorageData[origDev]["readtime"]
                    )
            except SystemExit:
                sys.exit(0)
            except:
                readtime = 0
            """

            # get writetime #
            """
            try:
                if SysMgr.totalEnable:
                    writetime = value["writetime"]
                else:
                    writetime = (
                        value["writetime"]
                        - prevStorageData[origDev]["writetime"]
                    )
            except SystemExit:
                sys.exit(0)
            except:
                writetime = 0
            """

            # get busytime #
            try:
                if SysMgr.totalEnable:
                    iotime = value["iotime"]
                    busyper = long(iotime / 10.0 / SysMgr.uptime)
                else:
                    iotime = (
                        value["iotime"] - prevStorageData[origDev]["iotime"]
                    )
                    busyper = long(iotime / 10.0 / SysMgr.uptimeDiff)

                # correct busyper #
                busyper = 100 if busyper > 100 else busyper

                # check busy option #
                if onlyBusy and not busyper:
                    continue

                # convert color for storage busy rate #
                busytime = "%3s%%" % busyper
                if busyper > 0:
                    busytime = convColor(busytime, "RED")
            except SystemExit:
                sys.exit(0)
            except:
                # check busy option #
                if onlyBusy:
                    continue

                busytime = "0%"

            # get avq #
            try:
                if SysMgr.totalEnable:
                    iowtime = value["iowtime"]
                else:
                    iowtime = (
                        value["iowtime"] - prevStorageData[origDev]["iowtime"]
                    )

                avq = "%.1f" % (iowtime / iotime)
            except SystemExit:
                sys.exit(0)
            except:
                avq = "0"

            # get read size on this interval #
            try:
                if SysMgr.totalEnable:
                    readSize = value["read"]
                else:
                    readSize = value["read"] - prevStorageData[origDev]["read"]

                if readSize:
                    readSize = convSize2Unit(readSize << 20)
                    readSize = convColor(readSize, "CYAN", 7)
            except SystemExit:
                sys.exit(0)
            except:
                readSize = 0

            # get write size on this interval #
            try:
                if SysMgr.totalEnable:
                    writeSize = value["write"]
                else:
                    writeSize = (
                        value["write"] - prevStorageData[origDev]["write"]
                    )

                if writeSize:
                    writeSize = convSize2Unit(writeSize << 20)
                    writeSize = convColor(writeSize, "CYAN", 7)
            except SystemExit:
                sys.exit(0)
            except:
                writeSize = 0

            # get other stats #
            if value["total"] > 0:
                total = convSize2Unit(value["total"] << 20)
                free = convSize2Unit(value["free"] << 20)
                fs = value["mount"]["fs"]
                path = value["mount"]["path"]

                try:
                    favail = "%7s" % convSize2Unit(value["favail"])
                    if value["favail"] == 0:
                        favail = convColor(favail, "RED")
                except SystemExit:
                    sys.exit(0)
                except:
                    favail = "%7s" % 0
            else:
                total = free = favail = fs = path = "-"

            # get free space change on this interval #
            try:
                freeDiff = value["free"] - prevStorageData[origDev]["free"]

                if freeDiff < 0:
                    op = "-"
                elif freeDiff == 0:
                    op = ""
                else:
                    op = "+"

                freeDiff = op + convSize2Unit(long(abs(freeDiff)) << 20)
            except SystemExit:
                sys.exit(0)
            except:
                freeDiff = 0

            # convert color for storage usage #
            usePer = "%4s%%" % value["usagePer"]
            if value["usagePer"] > SysMgr.diskPerHighThr:
                usePer = convColor(usePer, "RED")
            elif value["usagePer"] > 0:
                usePer = convColor(usePer, "YELLOW")

            # make disk stat string #
            option = "+".join(
                [value["mount"]["option"], value["mount"]["soption"]]
            )

            if option:
                mountInfo = "%s <%s>" % (path, option)
            else:
                mountInfo = path

            if "NOCUT" in SysMgr.environList:
                mountLen = None
            else:
                mountLen = 51

            diskInfo = (
                "{0:<24}|{1:>4}|{2:>5}|{3:>7}|{4:>7}|{5:>7}({6:>7})|"
                "{7:>5}|{8:>7}|{9:>7}|{10:>8}| {11:<52}|\n"
            ).format(
                dev[-24:],
                busytime,
                avq,
                readSize,
                writeSize,
                free,
                freeDiff,
                usePer,
                total,
                favail,
                fs,
                mountInfo[:mountLen],
            )

            if SysMgr.checkCutCond():
                return

            SysMgr.addPrint(diskInfo)

            printCnt += 1

        if printCnt == 0:
            SysMgr.addPrint("    None\n")

    def getSortedProcData(self):
        checkCond = True

        # check order type #
        if "ORDERASC" in SysMgr.environList:
            reverse = False
        elif "ORDERDESC" in SysMgr.environList:
            reverse = True
        else:
            reverse = None

        # CPU #
        if not SysMgr.sort:
            # set CPU usage as default #
            sortedProcData = sorted(
                self.procData.items(),
                key=lambda e: e[1]["ttime"],
                reverse=True if reverse is None else reverse,
            )
        # memory #
        elif SysMgr.sort == "m":
            if (
                "SORTINCSWAP" in SysMgr.environList
                or "SWAPMEMSUM" in SysMgr.environList
            ):
                for idx, value in self.procData.items():
                    self.saveProcStatusData(value["taskPath"], idx)

                sortedProcData = sorted(
                    self.procData.items(),
                    key=lambda e: long(e[1]["stat"][self.rssIdx])
                    << 2
                    + long(e[1]["status"].get("VmSwap", "0").split(None, 1)[0])
                    if e[1]["status"]
                    else 0,
                    reverse=True if reverse is None else reverse,
                )
            else:
                sortedProcData = sorted(
                    self.procData.items(),
                    key=lambda e: long(e[1]["stat"][self.rssIdx]),
                    reverse=True if reverse is None else reverse,
                )
        # block #
        elif SysMgr.sort == "b":
            sortedProcData = sorted(
                self.procData.items(),
                key=lambda e: e[1].get("rw", 0),
                reverse=True if reverse is None else reverse,
            )
        # WFC #
        elif SysMgr.sort == "w":
            sortedProcData = sorted(
                self.procData.items(),
                key=lambda e: e[1].get("cttime", 0),
                reverse=True if reverse is None else reverse,
            )
        # pid #
        elif SysMgr.sort == "p":
            sortedProcData = sorted(
                self.procData.items(),
                key=lambda e: long(e[0]),
                reverse=False if reverse is None else reverse,
            )
            checkCond = False
        # new #
        elif SysMgr.sort == "n":
            sortedProcData = sorted(
                self.procData.items(),
                key=lambda e: e[1].get("new", False),
                reverse=True if reverse is None else reverse,
            )
            checkCond = False
        # runtime #
        elif SysMgr.sort == "r":
            sortedProcData = sorted(
                self.procData.items(),
                key=lambda e: e[1].get("runtime", 0),
                reverse=True if reverse is None else reverse,
            )
        # oomscore #
        elif SysMgr.sort == "o":
            sortedProcData = sorted(
                self.procData.items(),
                key=lambda e: e[1].get("oomScore", 0),
                reverse=True if reverse is None else reverse,
            )
        # priority #
        elif SysMgr.sort == "P":
            sortedProcData = sorted(
                self.procData.items(),
                key=lambda e: long(e[1]["stat"][self.prioIdx]),
                reverse=False if reverse is None else reverse,
            )
            checkCond = False
        # exectime #
        elif SysMgr.sort in ("e", "d"):
            if SysMgr.sort == "e":
                statName = "execTime"
            elif SysMgr.sort == "d":
                statName = "waitTime"

            for idx, value in self.procData.items():
                self.saveProcSchedData(value["taskPath"], idx)

            sortedProcData = sorted(
                self.procData.items(),
                key=lambda e: e[1].get(statName, 0),
                reverse=True if reverse is None else reverse,
            )
        # swap #
        elif SysMgr.sort == "s":
            for idx, value in self.procData.items():
                self.saveProcStatusData(value["taskPath"], idx)

            sortedProcData = sorted(
                self.procData.items(),
                key=lambda e: long(
                    e[1]["status"].get("VmSwap", "0").split(None, 1)[0]
                )
                if e[1]["status"]
                else 0,
                reverse=True if reverse is None else reverse,
            )
        # contextswitch #
        elif SysMgr.sort == "C":
            for idx, value in self.procData.items():
                self.saveProcStatusData(value["taskPath"], idx)

            now = self.procData
            prev = self.prevProcData
            yld = "voluntary_ctxt_switches"
            prmpt = "nonvoluntary_ctxt_switches"
            sortedProcData = sorted(
                now.items(),
                key=lambda k: (
                    long(now[k[0]]["status"].get(yld, 0))
                    - long(prev[k[0]]["status"].get(yld, 0))
                )
                + (
                    long(now[k[0]]["status"].get(prmpt, 0))
                    - long(prev[k[0]]["status"].get(prmpt, 0))
                )
                if now[k[0]]["status"]
                and k[0] in prev
                and prev[k[0]]["status"]
                else (
                    long(now[k[0]]["status"].get(yld, 0))
                    + long(now[k[0]]["status"].get(prmpt, 0))
                    if now[k[0]]["status"]
                    else 0
                ),
                reverse=True if reverse is None else reverse,
            )
        # D-Bus #
        elif SysMgr.sort == "D":
            sortedProcData = sorted(
                self.procData.items(),
                key=lambda e: long(e[1].get("dbusCnt", 0))
                if "dbusCnt" in e[1]
                else 0,
                reverse=True if reverse is None else reverse,
            )
            checkCond = False
        # name #
        elif SysMgr.sort == "N":
            sortedProcData = sorted(
                self.procData.items(),
                key=lambda e: e[1]["stat"][self.commIdx],
                reverse=False if reverse is None else reverse,
            )
            checkCond = False
        # CPU #
        else:
            # set CPU usage as default #
            sortedProcData = sorted(
                self.procData.items(),
                key=lambda e: e[1].get("ttime", 0),
                reverse=True if reverse is None else reverse,
            )

        # convert sort condition type to number #
        if checkCond and SysMgr.sortCond:
            try:
                origCond = SysMgr.sortCond

                if SysMgr.sort in ("r", "e"):
                    SysMgr.sortCond = UtilMgr.convUnit2Time(origCond)
                else:
                    SysMgr.sortCond = UtilMgr.convUnit2Size(origCond)

                if origCond != SysMgr.sortCond:
                    SysMgr.printInfo(
                        "converted sort condition '%s' to '%s'"
                        % (origCond, SysMgr.sortCond)
                    )
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr(
                    "failed to convert '%s' to number for sort condition"
                    % SysMgr.sortCond,
                    True,
                )
                sys.exit(-1)

        return sortedProcData

    def getCgroupUsage(self):
        if not self.cgroupData:
            return {}

        interval = SysMgr.uptimeDiff
        if interval == 0:
            interval = 0.1

        stats = {}
        prevData = self.prevCgroupData
        cpuStatStr = "nr_periods 0\nnr_throttled 0\nthrottled_time 0"
        cpuStat2Str = "usage_usec 0\nuser_usec 0\nsystem_usec 0"

        # get dir map #
        dirmap = {}
        cgroupPath = SysMgr.sysInstance.getCgroupPath()
        for item, val in cgroupPath.items():
            val = val.split(",", 2)
            if len(val) > 1:
                dirmap[val[1]] = item

        def _checkItem(oname, target, system, dirmap):
            if oname != target:
                return False
            elif oname in system:
                return True
            elif oname in dirmap and dirmap[oname] in system:
                return True
            else:
                return False

        # get cgroup filter #
        cgFilter = SysMgr.environList.get("CGFILTER", [])

        # check pressure option #
        incPressure = not "NOPRESSURE" in SysMgr.environList

        # define effective list #
        validList = {}

        for system, groups in self.cgroupData.items():
            for group, values in groups.items():
                # filter group #
                if SysMgr.filterGroup:
                    if not UtilMgr.isValidStr(group, ignCap=True):
                        continue
                elif cgFilter:
                    if not UtilMgr.isValidStr(group, cgFilter, ignCap=True):
                        continue

                for name, value in values.items():
                    stats.setdefault(group, {})
                    oname = name.split(".", 1)[0]
                    isValid = True

                    # task count #
                    if name in ("tasks", "cgroup.procs"):
                        stat = value.count("\n")
                        isValid = False

                    # cpu usage (v2) / throttled time (v1) #
                    elif name == "cpu.stat":
                        try:
                            # throttled_time (v1) #
                            if value.startswith("nr"):
                                getter = lambda x: long(
                                    x.split("\n")[2].split()[1]
                                )
                                newname = name
                                isCpuUsage = False

                                # check no usage #
                                if value.startswith(cpuStatStr):
                                    raise Exception("no usage")
                            # cpu usage (v2) #
                            else:
                                # remove redundant data #
                                if (
                                    "cpuacct" in self.cgroupData
                                    or "cpu,cpuacct" in self.cgroupData
                                ):
                                    isValid = False
                                    raise Exception("redundant cpu usage")

                                getter = lambda x: long(
                                    x.split("\n", 1)[0].split()[1]
                                )
                                newname = "cpuacct.usage"
                                isCpuUsage = True

                                # check no usage #
                                if value.startswith(cpuStat2Str):
                                    raise Exception("no usage")

                            stat = getter(value)
                            if SysMgr.totalEnable:
                                prevStat = 0
                            else:
                                pvalue = prevData[system][group][name]
                                prevStat = getter(pvalue)
                            stat = (stat - prevStat) / interval

                            if isCpuUsage:
                                stat *= 1000

                            name = newname
                        except SystemExit:
                            sys.exit(0)
                        except:
                            stat = 0

                    # cpu usage (v1) #
                    elif _checkItem(oname, "cpuacct", system, dirmap):
                        try:
                            stat = long(value.rstrip())

                            if SysMgr.totalEnable:
                                prevStat = 0
                            else:
                                prevStat = prevData[system][group][name]
                                prevStat = long(prevStat.rstrip())
                            stat = (stat - prevStat) / interval
                        except SystemExit:
                            sys.exit(0)
                        except:
                            stat = 0

                    # blkio #
                    elif (
                        _checkItem(oname, "blkio", system, dirmap)
                        or name == "io.stat"
                    ):

                        def _getIOStats(value):
                            rd = wr = 0
                            for line in value.split("\n"):
                                items = line.split()
                                if len(items) != 3:
                                    continue
                                elif not items[1] in ("Read", "Write"):
                                    continue

                                # sum I/O stats for all devices #
                                # TODO: remove redundant stats for same device #
                                op = items[1]
                                if op == "Read":
                                    rd += long(items[2].rstrip())
                                else:
                                    wr += long(items[2].rstrip())

                            return rd, wr

                        def _getIOStats2(value):
                            rd = wr = 0
                            for line in value.split("\n"):
                                items = line.split()
                                for item in items:
                                    if item.startswith("rbytes="):
                                        rd += long(
                                            UtilMgr.lstrip(item, "rbytes=")
                                        )
                                    elif item.startswith("wbytes="):
                                        wr += long(
                                            UtilMgr.lstrip(item, "wbytes=")
                                        )
                            return rd, wr

                        # get read and write stats for current #
                        if name == "io.stat":
                            rd, wr = _getIOStats2(value)
                        else:
                            rd, wr = _getIOStats(value)
                        self.cgroupData[system][group][name] = [rd, wr]

                        # save stat #
                        try:
                            if SysMgr.totalEnable:
                                stats[group][name] = [rd, wr]
                            else:
                                prevItem = prevData[system][group][name]
                                if isinstance(prevItem, list):
                                    prevRd, prevWr = prevItem
                                else:
                                    prevRd, prevWr = _getIOStats(prevItem)
                                stats[group][name] = [
                                    rd - prevRd,
                                    wr - prevWr,
                                ]
                        except SystemExit:
                            sys.exit(0)
                        except:
                            SysMgr.printWarn(
                                "failed to get block I/O usage", reason=True
                            )

                    # pressure #
                    elif name.endswith("pressure"):
                        try:
                            if not incPressure:
                                raise Exception("no pressure")

                            stat = long(
                                value.split("\n")[0]
                                .split()[4]
                                .split("=")[1]
                                .strip()
                            )

                            if SysMgr.totalEnable:
                                prevStat = 0
                            else:
                                try:
                                    prevStat = prevData[system][group][name]
                                    prevStat = prevStat.split("\n")[0].split()[
                                        4
                                    ]
                                    prevStat = long(
                                        prevStat.split("=")[1].rstrip()
                                    )
                                except SystemExit:
                                    sys.exit(0)
                                except:
                                    prevStat = 0

                            stat = (stat - prevStat) / interval / 10000
                        except SystemExit:
                            sys.exit(0)
                        except:
                            stat = -1
                            isValid = False

                    # the other #
                    else:
                        try:
                            # check procsList #
                            if name.endswith("List"):
                                stat = long(value.split("\n")[0])
                            else:
                                stat = long(value.rstrip())

                            if not name in ConfigMgr.CGROUP_STAT:
                                isValid = False
                        except SystemExit:
                            sys.exit(0)
                        except:
                            stat = 0
                            isValid = False

                    if isValid:
                        validList.setdefault(system, True)

                    # save stat #
                    stats[group].setdefault(name, stat)
                    if stats[group][name] == 0:
                        stats[group][name] = stat

        return validList, stats

    def printCgroupUsage(self, alone=True):
        # check return condition #
        if SysMgr.uptimeDiff == 0 or SysMgr.checkCutCond():
            return
        elif not self.cgroupData and SysMgr.jsonEnable:
            return

        if not SysMgr.jsonEnable:
            # print menu #
            ret = SysMgr.addPrint(
                (
                    "{0:1}\n{1:<97}|{2:>4}|{3:>4}|"
                    "{4:>6}|{5:>3}|{6:>7}|{7:>7}|{8:>7}|{9:>10}|\n{10:1}\n"
                ).format(
                    twoLine,
                    "Control Group",
                    "Proc",
                    "Task",
                    "CPU(%)",
                    "Thr",
                    "Memory",
                    "Read",
                    "Write",
                    "PSI(C/M/I)",
                    oneLine,
                ),
                newline=3,
            )

            # check skip condition #
            if not self.cgroupData:
                SysMgr.addPrint("    None\n")
                if alone:
                    SysMgr.addPrint(oneLine + "\n")
                return

        # calculate resource usage of cgroup #
        validList, stats = self.getCgroupUsage()

        # update valid cgroup path list #
        invalidList = []
        for name in list(SysMgr.cgroupPathList):
            target = name.split("/")[-1]
            if not target in validList:
                SysMgr.cgroupPathList.pop(name, None)
                invalidList.append(target)
        if invalidList:
            SysMgr.printWarn(
                "disabled invalid [%s] subsystems" % ", ".join(invalidList),
                True,
            )

        # check report status #
        isReport = SysMgr.reportEnable or SysMgr.jsonEnable

        # add JSON stats #
        if isReport:
            SysMgr.jsonData.setdefault("cgroup", {})
            jsonData = SysMgr.jsonData["cgroup"]
        else:
            jsonData = {}

        # set sort value #
        if SysMgr.sort == "m":
            sortCond = "memory.usage_in_bytes"
        elif SysMgr.sort == "b":
            sortTarget = "blkio.throttle.io_service_bytes_recursive"
            sortCond = "blkio.throttle.io_service_bytes_recursive_total"
            for item in list(stats):
                if not sortTarget in stats[item]:
                    continue
                stats[item][sortCond] = sum(stats[item][sortTarget])
        elif SysMgr.sort is False:
            sortCond = None
        else:
            sortCond = "cpuacct.usage"

        # get max row value #
        maxRow = UtilMgr.getEnvironNum("MAXCGROWS", False, 0, False, True)
        commLen = UtilMgr.getEnvironNum("NAMELEN", False, 97, False, True)

        # get cgroup filter #
        cgFilter = SysMgr.environList.get("CGFILTER", [])

        convSize2Unit = UtilMgr.convSize2Unit
        convColor = UtilMgr.convColor

        # iterate stats #
        nrLine = 0
        for grp, value in sorted(
            stats.items(),
            key=lambda e: e[1][sortCond] if sortCond in e[1] else 0,
            reverse=True,
        ):
            # task #
            try:
                proc = value["cgroup.procs"]
                if "tasks" in value:
                    task = value["tasks"]
                else:
                    task = 0
            except SystemExit:
                sys.exit(0)
            except:
                proc = 0
                task = 0

            # CPU Usage #
            try:
                usage = value["cpuacct.usage"]
                if (
                    not usage
                    and not SysMgr.showAll
                    and not SysMgr.filterGroup
                    and not cgFilter
                    and sortCond == "cpuacct.usage"
                    and nrLine > 0
                ):
                    break

                reportCpu = usage = usage / 10000000
                cpu = "%6.1f" % usage

                # convert color for CPU usage #
                if not isReport and usage >= SysMgr.cpuPerLowThr:
                    cpu = UtilMgr.convCpuColor(usage, cpu)
            except SystemExit:
                sys.exit(0)
            except:
                if not SysMgr.showAll and sortCond == "cpuacct.usage":
                    break
                reportCpu = cpu = "-"

            # CPU Throttle #
            try:
                reportThrottle = throttle = long(value["cpu.stat"] / 10000000)
                if not isReport and not throttle in ("-", 0):
                    throttle = convColor(throttle, "RED", 3, "right")
            except SystemExit:
                sys.exit(0)
            except:
                reportThrottle = throttle = "-"

            # Memory #
            try:
                if "memory.usage_in_bytes" in value:
                    mem = value["memory.usage_in_bytes"]
                elif "memory.current" in value:
                    mem = value["memory.current"]
                reportMem = mem >> 20

                if SysMgr.jsonEnable:
                    mem = reportMem
                else:
                    mem = convSize2Unit(mem)
            except SystemExit:
                sys.exit(0)
            except:
                reportMem = mem = "-"

            # I/O #
            try:
                if "blkio.throttle.io_service_bytes_recursive" in value:
                    item = "blkio.throttle.io_service_bytes_recursive"
                elif "io.stat" in value:
                    item = "io.stat"
                else:
                    raise Exception("no I/O stat")

                rd, wr = value[item]
                reportRd = rd = rd >> 20
                reportWr = wr = wr >> 20

                if not SysMgr.jsonEnable:
                    rd, wr = list(map(convSize2Unit, value[item]))

                if rd == wr == 0:
                    raise Exception("no I/O")
            except SystemExit:
                sys.exit(0)
            except:
                reportRd = rd = reportWr = wr = "-"

            def _convColor(val, length):
                if val < 0:
                    return "-"
                return convColor(
                    str(long(val)),
                    "RED" if val >= 1 else "DEFAULT",
                    length,
                    "right",
                )

            # PSI #
            cpuPres = memPres = ioPres = -1
            if "cpu.pressure" in value:
                cpuPres = value["cpu.pressure"]
            psi = _convColor(cpuPres, 3)
            if "memory.pressure" in value:
                memPres = value["memory.pressure"]
            psi += "/" + _convColor(memPres, 2)
            if "io.pressure" in value:
                ioPres = value["io.pressure"]
            psi += "/" + _convColor(ioPres, 3)

            # add group name #
            pid = value.get("cgroup.procsList")
            if pid:
                if "REPCMDLINE" in SysMgr.environList:
                    _func = SysMgr.getCmdline
                else:
                    _func = SysMgr.getComm
                grp += " (%s)" % _func(pid, cache=True)

            # save report data #
            if isReport:
                jsonData[grp] = {
                    "nrProc": proc,
                    "nrTask": task,
                    "cpu": reportCpu,
                    "throttle": reportThrottle,
                    "memory": reportMem,
                    "read": reportRd,
                    "write": reportWr,
                    "cpuDelay": cpuPres,
                    "memDelay": memPres,
                    "ioDelay": ioPres,
                }

                # add intervals #
                if self.checkSysInterval("cgroup"):
                    for res, stat in jsonData[grp].items():
                        if res.startswith("nr"):
                            continue
                        self.addSysInterval(
                            "cgroup", "%s:%s" % (grp, res), stat
                        )

            # print stats of a process #
            if not SysMgr.jsonEnable:
                ret = SysMgr.addPrint(
                    (
                        "{0:<97}|{1:>4}|{2:>4}|"
                        "{3:>6}|{4:>3}|{5:>7}|{6:>7}|{7:>7}|{8:>10}|\n"
                    ).format(
                        grp[-(commLen):],
                        proc,
                        task,
                        cpu,
                        throttle,
                        mem,
                        rd,
                        wr,
                        psi,
                    )
                )
                if not ret:
                    return -1

            if maxRow and nrLine >= maxRow:
                break

            nrLine += 1

        # save report data #
        if isReport:
            self.reportData["cgroup"] = jsonData

        if SysMgr.jsonEnable:
            return

        if not stats or not nrLine:
            SysMgr.addPrint("    None\n")

        if alone:
            SysMgr.addPrint("%s\n" % oneLine)

    def getReorderedList(self, sortedProcData):
        newProcData = []
        fixedProcData = []
        for idx, val in sortedProcData:
            if idx in self.fixedProcData:
                fixedProcData.append([idx, val])
            else:
                newProcData.append([idx, val])
        sortedProcData = fixedProcData + newProcData
        return sortedProcData

    def printTaskUsageGen(self, idIndex=False):
        # check return condition #
        if SysMgr.uptimeDiff == 0 or SysMgr.checkCutCond():
            return
        elif not self.procData:
            ret = SysMgr.addPrint("%s\n" % twoLine)
            if not ret:
                return
            ret = SysMgr.addPrint("    None\n")
            if not ret:
                return
            SysMgr.addPrint(oneLine)
            return

        # increase print tick #
        self.printTick += 1

        # get pid length #
        pd = len(str(SysMgr.maxPid))
        if pd < 5:
            pd = 5

        # print menu #
        if not self.taskStreamEnable or self.printTick <= 1:
            ret = SysMgr.addPrint(
                (
                    "{24:1}\n{0:>16} ({1:>{pd}}/{2:>{pd}}/{3:>4}/{4:>4})|"
                    "{5:>4}({6:^3}/{7:^3}/{8:^3})|"
                    "{9:>4}({10:>4}/{11:^3}/{12:^3}/{13:^3})|"
                    "{14:>4}({15:>4}/{16:>4}/{17:>5})|"
                    "{18:^5}|{19:^6}|{20:^4}|{21:>9}|{22:^21}|\n{23:1}\n"
                ).format(
                    "Process",
                    "PID",
                    "PPID",
                    "Nr",
                    "Pri",
                    "CPU",
                    "Usr",
                    "Ker",
                    "Dly",
                    "VSS",
                    "RSS",
                    "Txt",
                    "Shr",
                    "Swp",
                    "Blk",
                    "RD",
                    "WR",
                    "NrFlt",
                    "Yld",
                    "User",
                    "FD",
                    "LifeTime",
                    "Parent",
                    oneLine,
                    twoLine,
                    pd=pd,
                ),
                newline=3,
            )
            if not ret:
                return

        # set sort value #
        try:
            sortVal = {
                "c": "ttimeDiff",
                "m": "rss",
                "p": "mainID",
                "N": "comm",
                "f": "num_fds",
                "r": "runtime",
                "P": "nice",
                "C": "num_ctx_switches",
            }[SysMgr.sort]
        except SystemExit:
            sys.exit(0)
        except:
            sortVal = "ttimeDiff"

        # print stats #
        printCnt = 0
        uptimeDiff = SysMgr.uptimeDiff
        for pid, value in sorted(
            self.procData.items(),
            key=lambda e: e[1][sortVal],
            reverse=True,
        ):
            # CPU #
            ttime = long(value["ttimeDiff"] * 100 / uptimeDiff)
            stime = long(value["stimeDiff"] * 100 / uptimeDiff)
            utime = long(value["utimeDiff"] * 100 / uptimeDiff)
            btime = 0

            ppid = value["ppid"]

            # check break condition #
            if SysMgr.filterGroup:
                if not UtilMgr.isValidStr(value["comm"]):
                    continue
            elif not SysMgr.showAll and (
                (sortVal == "ttimeDiff" and ttime == 0) or value[sortVal] == 0
            ):
                break

            printCnt += 1

            # I/O #
            read = 0
            write = 0
            nrpgflt = value["nrpgfltDiff"]

            # sched #
            nryield = value["yieldDiff"]
            # nrpreempted = value["preemptedDiff"]

            # memory #
            rss = value["rss"]
            if SysMgr.isDarwin:
                vss = value["vss"]  # GB #
            else:
                vss = value["vss"]  # MB #

            # user #
            if value["user"]:
                user = value["user"][:6]
            else:
                user = " "

            # etc #
            code = 0
            shr = 0
            swap = 0
            pri = value["nice"]

            if ppid in self.procData:
                etc = "%s(%s)" % (self.procData[ppid]["comm"], ppid)
            else:
                etc = ""

            # print stats of a process #
            ret = SysMgr.addPrint(
                (
                    "{0:>16} ({1:>{pd}}/{2:>{pd}}/{3:>4}/{4:>4})|"
                    "{5:>4}({6:>3}/{7:>3}/{8:>3})|"
                    "{9:>4}({10:>4}/{11:>3}/{12:>3}/{13:>3})|"
                    "{14:>4}({15:>4}/{16:>4}/{17:>5})|"
                    "{18:>5}|{19:>6}|{20:>4}|{21:>9}|{22:>21}|\n"
                ).format(
                    value["comm"][:16],
                    pid,
                    ppid,
                    value["nrThread"],
                    pri,
                    ttime,
                    utime,
                    stime,
                    0,
                    vss,
                    rss,
                    code,
                    shr,
                    swap,
                    btime,
                    read,
                    write,
                    nrpgflt,
                    nryield,
                    user,
                    value["nrFds"],
                    UtilMgr.convTime(value["runtime"])[:9],
                    etc[:21],
                    pd=pd,
                )
            )
            if not ret:
                return

            # print cmdline #
            if SysMgr.cmdlineEnable and value["cmdline"]:
                SysMgr.addPrint(
                    "{0:>39} | {1:1}\n".format("CMDLINE", value["cmdline"])
                )

        if printCnt == 0:
            ret = SysMgr.addPrint("    None\n")
            if not ret:
                return

        SysMgr.addPrint(oneLine)

    def printTaskUsage(self, idIndex=False):
        def _isBreakCond(idx, value):
            # define target #
            if not SysMgr.sort or SysMgr.sort == "c":
                target = value["ttime"]
            elif SysMgr.sort == "m":
                target = long(stat[self.rssIdx]) >> 8
                try:
                    if (
                        not "SORTINCSWAP" in SysMgr.environList
                        and not "SWAPMEMSUM" in SysMgr.environList
                    ):
                        raise Exception()

                    target += (
                        long(value["status"]["VmSwap"].split(None, 1)[0]) << 10
                    )
                except SystemExit:
                    sys.exit(0)
                except:
                    pass
            elif SysMgr.sort == "b":
                target = value["rw"]
            elif SysMgr.sort == "w":
                target = value["cttime"]
            elif SysMgr.sort == "p":
                target = long(idx)
            elif SysMgr.sort == "n":
                target = value["new"]
            elif SysMgr.sort == "o":
                target = value["oomScore"]
            elif SysMgr.sort == "r":
                target = value["runtime"]
            elif SysMgr.sort == "e":
                target = value["execTime"] / 1000000000
            elif SysMgr.sort == "d":
                target = value["waitTime"] / 1000000000
            elif SysMgr.sort == "s":
                try:
                    target = (
                        long(value["status"]["VmSwap"].split(None, 1)[0]) << 10
                    )
                except SystemExit:
                    sys.exit(0)
                except:
                    target = 0
            elif SysMgr.sort == "C":
                try:
                    prevStat = self.prevProcData[idx]["status"]
                    prevCtx = long(prevStat["voluntary_ctxt_switches"]) + long(
                        prevStat["nonvoluntary_ctxt_switches"]
                    )
                except SystemExit:
                    sys.exit(0)
                except:
                    prevCtx = 0

                try:
                    nowStat = value["status"]
                    nowCtx = long(nowStat["voluntary_ctxt_switches"]) + long(
                        nowStat["nonvoluntary_ctxt_switches"]
                    )
                except SystemExit:
                    sys.exit(0)
                except:
                    nowCtx = 0

                target = nowCtx - prevCtx
            else:
                target = 1

            # check sort condition #
            try:
                if SysMgr.sortCond:
                    if not SysMgr.sortCondOp or SysMgr.sortCondOp == ">":
                        if target <= long(SysMgr.sortCond):
                            return True
                        else:
                            return False
                    else:
                        if target >= long(SysMgr.sortCond):
                            return True
                        else:
                            return False
            except SystemExit:
                sys.exit(0)
            except:
                pass

            # check filter #
            if not SysMgr.filterGroup and not SysMgr.showAll and not target:
                return True
            else:
                return False

        def _isExceptTask(idx):
            def __check2ndFilter(exceptFlag):
                if exceptFlag:
                    return True
                elif "FILTER" in SysMgr.environList and not UtilMgr.isValidStr(
                    procData[idx]["comm"].lstrip("*"),
                    SysMgr.environList["FILTER"],
                ):
                    return True
                else:
                    return False

            exceptFlag = False

            # check fixed task list #
            if idx in self.fixedProcData:
                return False

            # check comm and ID #
            if not TaskAnalyzer.checkFilter(procData[idx]["comm"], idx):
                exceptFlag = True
            else:
                exceptFlag = __check2ndFilter(exceptFlag)

            # save cmdline info #
            if exceptFlag and SysMgr.filterGroup:
                if SysMgr.cmdlineEnable:
                    self.saveCmdlineData(procData[idx]["taskPath"], idx)
                    if TaskAnalyzer.checkFilter(procData[idx]["cmdline"], idx):
                        exceptFlag = False
                if exceptFlag and SysMgr.exeEnable:
                    self.saveExeData(procData[idx]["taskPath"], idx)
                    if TaskAnalyzer.checkFilter(procData[idx]["exe"], idx):
                        exceptFlag = False

            # single mode #
            if not SysMgr.groupProcEnable:
                return exceptFlag
            # group mode #
            elif not exceptFlag:
                return exceptFlag
            # check siblings #
            else:
                exceptFlag = False

            for item in list(SysMgr.filterGroup):
                # process mode #
                if SysMgr.processEnable:
                    ppid = procData[idx]["stat"][self.ppidIdx]

                    # check current's parent pid by comm #
                    if ppid in plist:
                        break
                    # check current's parent comm #
                    elif ppid in procData and UtilMgr.isValidStr(
                        procData[ppid]["comm"].lstrip("*"), [item]
                    ):
                        break
                    # check current's parent pid #
                    elif (
                        item in procData
                        and procData[item]["stat"][self.ppidIdx]
                        == stat[self.ppidIdx]
                    ):
                        break
                    else:
                        exceptFlag = True
                # thread mode #
                else:
                    pid = procData[idx]["mainID"]

                    # check current pid by comm #
                    if pid in plist:
                        break
                    # check current process comm #
                    elif pid in procData and UtilMgr.isValidStr(
                        procData[pid]["comm"].lstrip("*"), [item]
                    ):
                        break
                    # check current's pid #
                    elif (
                        item in procData
                        and procData[item]["mainID"] == value["mainID"]
                    ):
                        break
                    elif idx == item or value["mainID"] == item:
                        break
                    else:
                        exceptFlag = True

            exceptFlag = __check2ndFilter(exceptFlag)

            return exceptFlag

        def _getParentList():
            if not SysMgr.groupProcEnable:
                return {}

            plist = {}

            for idx, value in sortedProcData:
                if not UtilMgr.isValidStr(value["comm"].lstrip("*")):
                    continue

                if SysMgr.processEnable:
                    plist[self.procData[idx]["stat"][self.ppidIdx]] = 0
                else:
                    plist[self.procData[idx]["mainID"]] = 0

            return plist

        def _getTypes():
            if SysMgr.processEnable:
                mode = "Process"
                pidType = "PID"
                ppidType = "PPID"
                sidType = "SID"
                pgrpType = "USER"
            else:
                mode = "Thread"
                pidType = "TID"
                ppidType = "PID"
                sidType = "Yld"
                pgrpType = "Prmt"

            # check GPU option #
            if (
                (self.gpuMemData and SysMgr.gpuMemEnable)
                or "GPUMEM" in SysMgr.environList
                or "GPUMEMSUM" in SysMgr.environList
            ):
                sidType = "GPUM"

            # check delay option #
            if SysMgr.wfcEnable:
                dprop = "WFC"
            else:
                dprop = "Dly"

            # check last field #
            if SysMgr.ioschedEnable:
                etc = "IOSched"
            elif SysMgr.wchanEnable:
                etc = "WaitChannel"
            elif SysMgr.affinityEnable:
                etc = "Affinity"
            elif SysMgr.oomEnable:
                etc = "OOMScore"
            elif SysMgr.sigHandlerEnable:
                etc = "SignalHandler"
            elif SysMgr.nsEnable:
                etc = "Namespace[%s]" % SysMgr.nsType
            elif SysMgr.attrEnable:
                etc = "SecurityAttr"
            elif SysMgr.cgroupRootEnable:
                etc = "Cgroup"
            elif SysMgr.processEnable:
                etc = "Parent"
            else:
                etc = "Process"

            # set memory type #
            if SysMgr.pssEnable:
                mem = "PSS"
            elif SysMgr.ussEnable:
                mem = "USS"
            else:
                mem = "RSS"

            return mode, pidType, ppidType, sidType, pgrpType, dprop, etc, mem

        def _printStackSamples(idx):
            # set indent size including arrow #
            initIndent = 42

            for stack, cnt in sorted(
                self.stackTable[idx]["stack"].items(),
                key=lambda e: e[1],
                reverse=True,
            ):
                line = ""
                newLine = 1
                fullstack = ""
                per = long((cnt / float(self.stackTable[idx]["total"])) * 100)
                self.stackTable[idx]["stack"][stack] = 0

                if per == 0:
                    continue

                indent = initIndent + 3

                for call in stack.split("\n"):
                    try:
                        astack = call.split(None, 2)[1]

                        if astack.startswith("0xffffffff"):
                            if fullstack == line == "":
                                line = "N/A"
                            else:
                                line = line[: line.rfind("<-")]
                            break

                        lenLine = indent + len(line) + len(astack)
                        if lenLine >= SysMgr.lineLength:
                            indent = 0
                            fullstack = "%s%s\n" % (fullstack, line)
                            newLine += 1
                            line = " " * initIndent

                        line = "%s%s <- " % (line, astack)
                    except:
                        pass

                fullstack += line
                fullstack = fullstack.rstrip(" <- ")

                ret = SysMgr.addPrint(
                    "{0:>39} | {1:1}\n".format(
                        "KSTACK(%3s%%)" % per, fullstack
                    ),
                    newLine,
                )
                if not ret:
                    return -1

            return newLine

        def _printDelay(self, value):
            if not SysMgr.delayEnable:
                return

            conv = UtilMgr.convNum

            # get performance stats #
            while 1:
                val = SysMgr.getTaskstats(idx)
                if not val or str(val["ac_pid"]) == idx:
                    break

            scaledFactor = 1000000000.0

            # get total usage #
            try:
                cpuRealTotal = val["cpu_run_real_total"] / scaledFactor
                cpuVirtTotal = val["cpu_run_virtual_total"] / scaledFactor
                cpuStimeTotal = val["ac_stime"] / 1000000.0
                cpuStimePer = (cpuStimeTotal / cpuRealTotal) * 100
                cpuUtimeTotal = val["ac_utime"] / 1000000.0
                cpuUtimePer = (cpuUtimeTotal / cpuRealTotal) * 100
                majfltTotal = conv(val["ac_majflt"])
                minfltTotal = conv(val["ac_minflt"])

                cpuTotalStr = (
                    "REALTIME: %.3f / VIRTTIME: %.3f / STIME: %.3f(%d%%) / "
                    "UTIME: %.3f(%d%%) / MAJFLT: %s / MINFLT: %s"
                ) % (
                    cpuRealTotal,
                    cpuVirtTotal,
                    cpuStimeTotal,
                    cpuStimePer,
                    cpuUtimeTotal,
                    cpuUtimePer,
                    majfltTotal,
                    minfltTotal,
                )

                SysMgr.addPrint(
                    "{0:>39} | {1:1}\n".format("TOTAL_USAGE", cpuTotalStr)
                )
            except SystemExit:
                sys.exit(0)
            except:
                pass

            # get total delay #
            cpuDelay = val["cpu_delay_total"]
            blkDelay = val["blkio_delay_total"]
            swapDelay = val["swapin_delay_total"]
            rclmDelay = val["freepages_delay_total"]

            value["delay"] = {
                "CPU": cpuDelay,
                "BLK": blkDelay,
                "SWAP": swapDelay,
                "RCLM": rclmDelay,
            }

            prevData = self.prevProcData[idx]

            cpuTotalDelay = cpuDelay / scaledFactor
            blkTotalDelay = blkDelay / scaledFactor
            swapTotalDelay = swapDelay / scaledFactor
            rclmTotalDelay = rclmDelay / scaledFactor

            delayTotalStr = (
                "CPU: %.3f / BLK: %.3f / SWAP: %.3f / RCLM: %.3f"
                % (
                    cpuTotalDelay,
                    blkTotalDelay,
                    swapTotalDelay,
                    rclmTotalDelay,
                )
            )

            SysMgr.addPrint(
                "{0:>39} | {1:1}\n".format("TOTAL_DELAY", delayTotalStr)
            )

            if not "delay" in prevData:
                return

            cpuDelayDiff = cpuDelay - prevData["delay"]["CPU"]
            blkDelayDiff = blkDelay - prevData["delay"]["BLK"]
            swapDelayDiff = swapDelay - prevData["delay"]["SWAP"]
            rclmDelayDiff = rclmDelay - prevData["delay"]["RCLM"]

            cpuDelayDiff /= scaledFactor
            blkDelayDiff /= scaledFactor
            swapDelayDiff /= scaledFactor
            rclmDelayDiff /= scaledFactor

            delayStr = "CPU: %.3f / BLK: %.3f / SWAP: %.3f / RCLM: %.3f" % (
                cpuDelayDiff,
                blkDelayDiff,
                swapDelayDiff,
                rclmDelayDiff,
            )

            SysMgr.addPrint(
                "{0:>39} | {1:1}\n".format("INTER_DELAY", delayStr)
            )

        # check return condition #
        if SysMgr.uptimeDiff == 0 or SysMgr.checkCutCond():
            return
        elif not self.procData:
            SysMgr.addPrint(twoLine)
            return

        # pid length #
        pd = self.getPidLen()

        # comm length #
        if "COMMLEN" in SysMgr.environList:
            cl = UtilMgr.getEnvironNum("COMMLEN")
        else:
            cl = 26 - (pd * 2)

        # calculate resource usage of processes #
        self.setProcUsage()

        # get types #
        (
            mode,
            pidType,
            ppidType,
            sidType,
            pgrpType,
            dprop,
            etc,
            mem,
        ) = _getTypes()

        # check EXCEPTSHM #
        if (
            not (SysMgr.pssEnable or SysMgr.ussEnable)
            and "EXCEPTSHM" in SysMgr.environList
        ):
            exceptShm = True
            mem = "U" + mem
        else:
            exceptShm = False

        # check swap sum #
        if "SWAPMEMSUM" in SysMgr.environList:
            isSwapSum = True
            mem = "S" + mem
        else:
            isSwapSum = False

        # check GPU memory sum #
        if "GPUMEMSUM" in SysMgr.environList:
            isGpuMemSum = True
            mem = "G" + mem
        else:
            isGpuMemSum = False

        # environment variables #
        isGpuMem = "GPUMEM" in SysMgr.environList
        onlyVmflag = "ONLYVMFLAG" in SysMgr.environList
        onlyVmflagTask = "ONLYVMFLAGTASK" in SysMgr.environList
        smapsEnable = not "NOSMAPS" in SysMgr.environList
        useActualFd = "ACTUALFD" in SysMgr.environList
        dmabufEnable = "DMABUF" in SysMgr.environList and self.dmabufData
        sysPer = "SYSPER" in SysMgr.environList

        # get user length #
        try:
            userLen = long(SysMgr.environList.get("USERLEN", [6])[0])
        except:
            userLen = None

        # get filters #
        userFilter = SysMgr.environList.get("USERFILTER")
        etcIncFilter = SysMgr.environList.get("ETCINCFILTER", [])
        etcExcFilter = SysMgr.environList.get("ETCEXCFILTER", [])
        cgIncFilter = SysMgr.environList.get("CGINCFILTER", [])
        cgExcFilter = SysMgr.environList.get("CGEXCFILTER", [])

        # add JSON stats #
        if SysMgr.jsonEnable:
            jtype = mode.lower()
            SysMgr.jsonData.setdefault(jtype, {})
            jsonData = SysMgr.jsonData[jtype]
        else:
            jsonData = {}

        # increase print tick #
        self.printTick += 1

        # print menu #
        if not self.taskStreamEnable or self.printTick <= 1:
            ret = SysMgr.addPrint(
                (
                    "{24:1}\n{0:>{cl}} ({1:>{pd}}/{2:>{pd}}/{3:>4}/{4:>4})|"
                    "{5:>4}({6:>3}/{7:>3}/{8:>3})|"
                    "{9:>4}({10:>4}/{11:>3}/{12:>3}/{13:>3})|"
                    "{14:>4}({15:>4}/{16:>4}/{17:>5})|"
                    "{18:>5}|{19:>6}|{20:>4}|{21:>9}|{22:>21}|\n{23:1}\n"
                ).format(
                    mode,
                    pidType,
                    ppidType,
                    "Nr",
                    "Pri",
                    "CPU",
                    "Usr",
                    "Ker",
                    dprop,
                    "VSS",
                    mem,
                    "Txt",
                    "Shr",
                    "Swp",
                    "Blk",
                    "RD",
                    "WR",
                    "NrFlt",
                    sidType,
                    pgrpType,
                    "FD",
                    "LifeTime",
                    etc,
                    oneLine,
                    twoLine,
                    cl=cl,
                    pd=pd,
                ),
                newline=3,
            )
            if not ret:
                return

        # set sort value #
        try:
            sortedProcData = self.getSortedProcData()
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn("failed to sort tasks", True, True)
            sortedProcData = list(self.procData)

        # make parent list #
        plist = _getParentList()

        # define convert function #
        convNum = UtilMgr.convNum
        convSize = UtilMgr.convSize2Unit
        convTime = UtilMgr.convTime
        convColor = UtilMgr.convColor
        convCpuColor = UtilMgr.convCpuColor

        totalStats = {"read": 0, "write": 0, "yld": 0, "prtd": 0, "task": 0}
        totalStatPids = {}

        # clear ID list #
        if idIndex:
            SysMgr.idList = []

        # check WSS init #
        if SysMgr.initWssEnable:
            initWss = True
            SysMgr.initWssEnable = False
        else:
            initWss = False

        # init normal task dict #
        saveJsonStat = False
        if SysMgr.reportEnable and "SAVEJSONSTAT" in SysMgr.environList:
            self.reportData.setdefault("task", {})
            self.reportData["task"].setdefault("normal", {})
            saveJsonStat = True

        # set maximum top rank #
        if not "NRTOPRANK" in SysMgr.environList:
            nrTopRank = sys.maxsize
        else:
            nrTopRank = SysMgr.nrTopRank

        # reorder fixed tasks #
        if self.fixedProcData:
            sortedProcData = self.getReorderedList(sortedProcData)

        # set memory size shift factor #
        if "KBUNIT" in SysMgr.environList:
            memFactorKB = 0
            memFactorMB = 10

            def _memFactorPG(stat):
                return stat << 2

        else:
            memFactorKB = 10
            memFactorMB = 20

            def _memFactorPG(stat):
                return stat >> 8

        # print resource usage of tasks #
        procCnt = 0
        procData = self.procData
        prevProcData = self.prevProcData
        for idx, value in sortedProcData:
            stat = value["stat"]

            # check exception flag #
            if SysMgr.filterGroup and _isExceptTask(idx):
                continue

            # check fixed task #
            if self.fixedProcData and idx in self.fixedProcData:
                pass
            # check break condition #
            elif _isBreakCond(idx, value):
                break
            # check the number of task #
            elif procCnt >= nrTopRank:
                break
            # check terminal rows #
            elif SysMgr.checkCutCond():
                return

            # save status info #
            self.saveProcStatusData(value["taskPath"], idx)
            try:
                status = value["status"]
            except SystemExit:
                sys.exit(0)
            except:
                status = None

            # user #
            try:
                if idx in prevProcData and "user" in prevProcData[idx]:
                    value["user"] = prevProcData[idx]["user"]
                elif self.isKernelThread(idx):
                    raise Exception("kernel thread")
                else:
                    SysMgr.sysInstance.saveUserInfo()
                    userData = SysMgr.sysInstance.userData
                    uid = status["Uid"].split(None, 1)[0]
                    value["user"] = userData[uid]["name"]
            except SystemExit:
                sys.exit(0)
            except:
                value["user"] = "-"

            # check user #
            if userFilter and not UtilMgr.isValidStr(
                value["user"], userFilter
            ):
                continue

            # add task to fixed target list #
            if SysMgr.fixTargetEnable:
                SysMgr.fixedProcList.setdefault(idx, None)

            # add task into stack trace list #
            if SysMgr.stackEnable:
                self.stackTable.setdefault(idx, {})

                if not "fd" in self.stackTable[idx]:
                    spath = "%s/%s/stack" % (SysMgr.procPath, idx)
                    try:
                        self.stackTable[idx]["fd"] = open(spath, "r")
                        self.stackTable[idx]["stack"] = {}
                        self.stackTable[idx]["total"] = 0
                    except SystemExit:
                        sys.exit(0)
                    except:
                        SysMgr.printOpenWarn(spath)
                        self.stackTable.pop(idx, None)

            # add task into JSON data #
            if SysMgr.jsonEnable:
                jsonData.setdefault(
                    idx,
                    UtilMgr.convStr2Dict(
                        UtilMgr.convDict2Str(value, ignore=True)
                    ),
                )

            # init WSS #
            if initWss:
                # clear reference bits #
                SysMgr.clearPageRefs(idx, val="1")

            # comm #
            comm = value["comm"]
            if self.isKernelThread(idx):
                if comm.startswith("*"):
                    comm = "*[%s]" % comm.lstrip("*")
                else:
                    comm = "[%s]" % comm

            # parent ID #
            if SysMgr.processEnable:
                pid = stat[self.ppidIdx]
            else:
                pid = value["mainID"]

            # sched priority #
            schedPolicy, schedValue = self.getSchedInfo(stat)
            sched = schedPolicy + schedValue

            # lifetime #
            lifeTime = convTime(value["runtime"])

            # save cmdline info #
            self.saveCmdlineData(value["taskPath"], idx)

            # save exe info #
            self.saveExeData(value["taskPath"], idx)

            # save cgroup info #
            self.saveCgroupData(value["taskPath"], idx)
            if SysMgr.cgroupRootEnable:
                cgRoot = value.get("cgroupRoot", "")
                if cgIncFilter and not UtilMgr.isValidStr(cgRoot, cgIncFilter):
                    continue
                elif cgExcFilter and UtilMgr.isValidStr(cgRoot, cgExcFilter):
                    continue

            # save sched info to get delayed time #
            if not SysMgr.wfcEnable:
                self.saveProcSchedData(value["taskPath"], idx)

            # save wait channel info #
            if SysMgr.wchanEnable:
                self.saveProcWchanData(value["taskPath"], idx)

            # save attr info #
            if SysMgr.attrEnable:
                vattr = SysMgr.readProcData(idx, "attr/current", verb=False)
                value["attr"] = "%22s" % vattr[0].strip() if vattr else "None"

            # save fdinfo #
            if dmabufEnable:
                self.saveFdinfoData(idx)

            # save memory map info to get memory details #
            if SysMgr.memEnable and (
                smapsEnable or SysMgr.pssEnable or SysMgr.ussEnable
            ):
                TaskAnalyzer.saveProcSmaps(
                    value["taskPath"],
                    idx,
                    mini=not SysMgr.wssEnable,
                    vmflags=SysMgr.vmflagList,
                )

            # swap #
            try:
                swapSize = (
                    long(status["VmSwap"].split(None, 1)[0]) >> memFactorKB
                )
                value["swap"] = swapSize

                # add swap interval #
                self.addProcInterval("swap", idx, value, "swapInt", swapSize)
            except SystemExit:
                sys.exit(0)
            except:
                swapSize = "-"
                value["swap"] = 0

            # scheduling info #
            if not SysMgr.processEnable:
                try:
                    value["yield"] = status["voluntary_ctxt_switches"]
                    value["preempted"] = status["nonvoluntary_ctxt_switches"]
                except SystemExit:
                    sys.exit(0)
                except:
                    value["yield"] = "-"
                    value["preempted"] = "-"

            # file descriptor table length #
            try:
                if useActualFd:
                    if self.isKernelThread(idx):
                        fdsize = 0
                    else:
                        fdsize = SysMgr.getNrFd(idx)
                else:
                    fdsize = long(status["FDSize"])

                # update fdsize #
                value["fdSize"] = fdsize

                # apply color #
                if fdsize > SysMgr.fdHighThr:
                    fdstr = convColor(fdsize, "RED", 4)
                else:
                    fdstr = fdsize
            except SystemExit:
                sys.exit(0)
            except:
                value["fdSize"] = 0
                fdstr = "-"

            # scheduling info #
            if SysMgr.processEnable:
                # sid #
                yld = stat[self.sidIdx][-5:]
                if yld == "0":
                    yld = "-"

                # user #
                try:
                    prtd = value["user"][:userLen]
                except SystemExit:
                    sys.exit(0)
                except:
                    prtd = "-"
            else:
                # yield #
                try:
                    if SysMgr.totalEnable:
                        yld = long(value["yield"])
                    else:
                        prevStatus = prevProcData[idx]["status"]
                        yld = long(value["yield"]) - long(
                            prevStatus["voluntary_ctxt_switches"]
                        )
                except SystemExit:
                    sys.exit(0)
                except:
                    yld = "-"

                # preempted #
                try:
                    if SysMgr.totalEnable:
                        prtd = long(value["preempted"])
                    else:
                        prevStatus = prevProcData[idx]["status"]
                        prtd = long(value["preempted"]) - long(
                            prevStatus["nonvoluntary_ctxt_switches"]
                        )
                except SystemExit:
                    sys.exit(0)
                except:
                    prtd = "-"

            # GPU memory usage #
            if (
                (SysMgr.gpuMemEnable and self.gpuMemData)
                or isGpuMem
                or isGpuMemSum
            ):
                if SysMgr.processEnable:
                    tid = idx
                else:
                    tid = value["mainID"]

                if tid in self.gpuMemData:
                    yld = convColor(
                        self.gpuMemData[tid]["size"] >> memFactorMB, "CYAN", 5
                    )
                else:
                    yld = "-"

            try:
                # blocking time of parent process waits for its children #
                if SysMgr.wfcEnable:
                    dtime = long(value["cttime"])
                # calculate delayed time in runqueue for this thread #
                else:
                    if value["execTime"] == 0:
                        raise Exception()

                    if SysMgr.totalEnable:
                        prevExecTime = 0
                        prevWaitTime = 0
                    else:
                        prevExecTime = prevProcData[idx]["execTime"]
                        prevWaitTime = prevProcData[idx]["waitTime"]

                    # get diff #
                    execTime = value["execTime"] - prevExecTime
                    waitTime = value["waitTime"] - prevWaitTime
                    dtime = (
                        long(waitTime / 10000000 / SysMgr.uptimeDiff)
                        if waitTime
                        else 0
                    )

                # apply color #
                if dtime:
                    dtime = convColor("%3s" % dtime, "RED")
            except SystemExit:
                sys.exit(0)
            except:
                dtime = "-"

            # I/O size #
            try:
                readSize = value["read"] >> memFactorMB
                if readSize:
                    readSize = convColor("%4s" % readSize, "RED")

                # add read interval #
                self.addProcInterval(
                    "block", idx, value, "readInt", value["read"]
                )

                writeSize = value["write"] >> memFactorMB
                if writeSize:
                    writeSize = convColor("%4s" % writeSize, "RED")

                # add write interval #
                self.addProcInterval(
                    "block", idx, value, "writeInt", value["write"]
                )
            except SystemExit:
                sys.exit(0)
            except:
                readSize = "-"
                writeSize = "-"

            # last field info #
            try:
                # print iosched #
                if SysMgr.ioschedEnable:
                    # get I/O sched info #
                    ret = SysMgr.getIoPriority(pid=idx)
                    if not ret:
                        raise Exception("no iosched info")

                    # build I/O sched string #
                    etc = "%d[%s]" % (ret[1], ConfigMgr.IOSCHED_CLASS[ret[0]])
                elif SysMgr.wchanEnable:
                    etc = value["wchan"]
                elif SysMgr.affinityEnable:
                    etc = SysMgr.getAffinity(long(idx))
                elif SysMgr.oomEnable:
                    etc = str(value["oomScore"])
                elif SysMgr.sigHandlerEnable:
                    etc = status["SigCgt"].lstrip("0")
                elif not SysMgr.processEnable:
                    pgid = procData[idx]["mainID"]
                    etc = "%s(%s)" % (procData[pgid]["comm"], pgid)
                elif SysMgr.nsEnable:
                    ns = SysMgr.nsType
                    nsid = os.readlink("%s/ns/%s" % (value["taskPath"], ns))
                    nsid = nsid[len(ns) + 2 : -1]
                    etc = self.nsProcData[ns][nsid]
                elif SysMgr.attrEnable:
                    etc = value["attr"]
                elif SysMgr.cgroupRootEnable:
                    etc = value.get("cgroupRoot", "N/A")
                else:
                    pgid = procData[idx]["stat"][self.ppidIdx]
                    if pgid == "0":
                        etc = "swapper(0)"
                    else:
                        etc = "%s(%s)" % (procData[pgid]["comm"], pgid)
            except SystemExit:
                sys.exit(0)
            except:
                etc = "-"

            # check etc filter #
            if etcIncFilter and not UtilMgr.isValidStr(etc, etcIncFilter):
                continue
            elif etcExcFilter and UtilMgr.isValidStr(etc, etcExcFilter):
                continue

            # convert color for priority #
            try:
                nrPrio = long(stat[self.prioIdx])
                if schedPolicy == "I":
                    sched = convColor(sched, "WARNING", 4)
                elif nrPrio < 20:
                    if nrPrio >= 0:
                        sched = convColor(sched, "YELLOW", 4)
                    else:
                        sched = convColor(sched, "RED", 4)
            except SystemExit:
                sys.exit(0)
            except:
                pass

            if SysMgr.customCmd:
                # execute command #
                if self.execEnable:
                    SysMgr.executeCommand(pid=idx, comm=comm.lstrip("*"))

                # add shortcut prefix to comm #
                if idIndex:
                    SysMgr.idList.append(idx)
                    idStr = "%s>" % (len(SysMgr.idList) - 1)
                    spaces = " " * (cl - len(idStr) - len(comm))
                    comm = "%s%s%s" % (idStr, spaces, comm)

            # remove unshown field in lifetime #
            if len(lifeTime.split(":")) > 3:
                lifeTime = lifeTime[: lifeTime.rfind(":")]

            # change CPU usage to system-level #
            if sysPer:
                convFunc = float if SysMgr.floatEnable else long
                value["ttime"] /= convFunc(SysMgr.nrCore)
                value["utime"] = long(value["utime"] / SysMgr.nrCore)
                value["stime"] = long(value["stime"] / SysMgr.nrCore)
                value["btime"] /= long(SysMgr.nrCore)

            # convert time format #
            if SysMgr.floatEnable:
                ttime = "%.1f" % value["ttime"]
                btime = "%.1f" % value["btime"]
            else:
                ttime = value["ttime"]
                btime = value["btime"]

            # convert color for CPU usage #
            if value["ttime"] >= SysMgr.cpuPerLowThr:
                ttime = convCpuColor(value["ttime"], ttime, size=4)

            # vss #
            try:
                vss = long(stat[self.vssIdx]) >> memFactorMB
            except SystemExit:
                sys.exit(0)
            except:
                vss = 0

            # get physical memory usages #
            memBuf = []
            if SysMgr.memEnable:
                memBuf, nrss, pss, uss = self.getMemDetails(
                    idx, value["maps"], vss=vss << 10
                )

                if onlyVmflag or onlyVmflagTask:
                    if not [True for res in memBuf if res[0] == "FLAG"]:
                        continue

                value["pss"] = pss >> memFactorKB
                value["uss"] = uss >> memFactorKB

                if not smapsEnable:
                    memBuf = []
            elif SysMgr.pssEnable:
                try:
                    if SysMgr.processEnable:
                        raise Exception("not thread")
                    value["pss"] = self.procData[value["mainID"]]["pss"]
                except SystemExit:
                    sys.exit(0)
                except:
                    pss = TaskAnalyzer.readProcMemStats(
                        value["taskPath"], idx, retPss=True, retShr=False
                    )[0]
                    value["pss"] = pss >> memFactorKB
            elif SysMgr.ussEnable:
                try:
                    if SysMgr.processEnable:
                        raise Exception("not thread")
                    value["uss"] = self.procData[value["mainID"]]["uss"]
                except SystemExit:
                    sys.exit(0)
                except:
                    shared = TaskAnalyzer.readProcMemStats(
                        value["taskPath"], idx, retPss=False, retShr=True
                    )[1]
                    value["uss"] = value["rss"] - (shared >> memFactorKB)
                    # fix wrong stats because RSS is inaccurate #
                    if value["uss"] < 0:
                        value["uss"] = 0

            # get main memory usage #
            if SysMgr.pssEnable:
                mems = value["pss"]
            elif SysMgr.ussEnable:
                mems = value["uss"]
            else:
                mems = value["rss"]

            # add swap memory usage to physical memory usage #
            if isSwapSum and isinstance(swapSize, long):
                mems += swapSize

            # add GPU memory usage to physical memory usage #
            if isGpuMemSum and idx in self.gpuMemData:
                mems += self.gpuMemData[idx]["size"] >> memFactorMB

            # code size #
            try:
                codeSize = (
                    long(stat[self.ecodeIdx]) - long(stat[self.scodeIdx])
                ) >> memFactorMB

                # except for shared text in RSS #
                if exceptShm:
                    mems -= codeSize
                    if mems < 0:
                        mems = 0
            except SystemExit:
                sys.exit(0)
            except:
                codeSize = 0

            # shared #
            try:
                shr = _memFactorPG(long(value["statm"][self.shrIdx]))

                # except for shred memory in RSS #
                if exceptShm:
                    mems -= shr
                    if mems < 0:
                        mems = 0
            except SystemExit:
                sys.exit(0)
            except:
                shr = "-"

            # convert color for SHM #
            try:
                if long(shr) >= SysMgr.memLowThr:
                    if shr >= SysMgr.memHighThr:
                        shr = convColor(shr, "RED", 3)
                    else:
                        shr = convColor(shr, "YELLOW", 3)
            except SystemExit:
                sys.exit(0)
            except:
                pass

            # convert color for physical memory usage #
            if mems < SysMgr.memLowThr:
                memstr = mems
            else:
                if mems >= SysMgr.memHighThr:
                    memstr = convColor(mems, "RED", 4)
                else:
                    memstr = convColor(mems, "YELLOW", 4)

            # convert color for BTIME #
            if float(btime):
                btimestr = convColor(btime, "RED", 4)
            else:
                btimestr = btime

            # print stats of a process #
            ret = SysMgr.addPrint(
                (
                    "{0:>{cl}} ({1:>{pd}}/{2:>{pd}}/{3:>4}/{4:>4})|"
                    "{5:>4}({6:>3}/{7:>3}/{8:>3})|"
                    "{9:>4}({10:>4}/{11:>3}/{12:>3}/{13:>3})|"
                    "{14:>4}({15:>4}/{16:>4}/{17:>5})|"
                    "{18:>5}|{19:>6}|{20:>4}|{21:>9}|{22:>21}|\n"
                ).format(
                    comm[:cl],
                    idx,
                    pid,
                    stat[self.nrthreadIdx],
                    sched,
                    ttime,
                    value["utime"],
                    value["stime"],
                    dtime,
                    vss,
                    memstr,
                    codeSize,
                    shr,
                    swapSize,
                    btimestr,
                    readSize,
                    writeSize,
                    value["majflt"],
                    yld,
                    prtd,
                    fdstr,
                    lifeTime[:9],
                    etc[:21],
                    cl=cl,
                    pd=pd,
                )
            )
            if not ret:
                return

            # save normal task stat #
            if saveJsonStat:
                self.reportData["task"]["normal"][idx] = value

            # check stream flag #
            if self.taskStreamEnable:
                continue

            # sum stats #
            try:
                totalStats["ttime"] += value["ttime"]
                totalStats["utime"] += value["utime"]
                totalStats["stime"] += value["stime"]

                if not value["mainID"] in totalStatPids:
                    totalStats["mem"] += mems
                    if swapSize != "-":
                        totalStats["swap"] += swapSize

                totalStats["btime"] += value["btime"]
                totalStats["majflt"] += value["majflt"]
                totalStats["task"] += 1
            except SystemExit:
                sys.exit(0)
            except:
                totalStats["ttime"] = value["ttime"]
                totalStats["utime"] = value["utime"]
                totalStats["stime"] = value["stime"]
                totalStats["mem"] = mems
                totalStats["swap"] = swapSize
                totalStats["btime"] = value["btime"]
                totalStats["majflt"] = value["majflt"]
                totalStats["task"] = 1

            if not SysMgr.processEnable:
                try:
                    totalStats["yld"] += yld
                    totalStats["prtd"] += prtd
                except SystemExit:
                    sys.exit(0)
                except:
                    pass
            else:
                totalStats["yld"] = "-"
                totalStats["prtd"] = "-"

            if SysMgr.blockEnable:
                try:
                    totalStats["read"] += value["read"]
                    totalStats["write"] += value["write"]
                except SystemExit:
                    sys.exit(0)
                except:
                    totalStats["read"] = value.get("read", "-")
                    totalStats["write"] = value.get("write", "-")
            else:
                totalStats["read"] = "-"
                totalStats["write"] = "-"

            # add PID to total stat list to prevent redundant size #
            totalStatPids[value["mainID"]] = 0

            # print PMU stats #
            if SysMgr.perfGroupEnable:
                try:
                    perfData = SysMgr.collectProcPerfData(value["perfFds"])
                    perfString = SysMgr.getPerfString(perfData)
                    if perfString:
                        ret = SysMgr.addPrint(
                            "{0:>40}| {1:1}\n".format("PERF", perfString)
                        )
                        if not ret:
                            return
                except SystemExit:
                    sys.exit(0)
                except:
                    self.procData[idx]["perfFds"] = SysMgr.initProcPerfEvents(
                        long(idx)
                    )

            # insert memory stats #
            if SysMgr.wssEnable:
                for kind in ("WSS", "USS", "PSS", "RSS", "VSS"):
                    memBuf.insert(0, [kind, ""])

            # print memory details #
            for memData in memBuf:
                mprop = memData[0]
                mval = memData[1]

                if onlyVmflag and mprop != "FLAG":
                    continue

                ret = SysMgr.addPrint(mval)
                if not ret:
                    return

                if not SysMgr.wssEnable:
                    continue

                # split a long line #
                tstr = ""
                indent = 54
                indenta = 5
                lenItem = 7
                isFirstLined = True
                limit = SysMgr.lineLength - indent
                hardLimit = SysMgr.ttyCols - indent
                if not SysMgr.outPath and limit >= hardLimit:
                    limit = hardLimit
                pstr = procData[idx]["wss"][mprop]

                if SysMgr.showAll:
                    while len(pstr) > limit:
                        slimit = len(pstr[:limit])
                        des = pstr[:slimit]
                        tstr = "%s%s\n%s" % (
                            tstr,
                            des,
                            " " * (indent + indenta),
                        )

                        if isFirstLined:
                            limit -= indenta + lenItem
                            isFirstLined = False

                        pstr = pstr[slimit:]
                elif len(UtilMgr.removeColor(pstr)) > limit:
                    sizes = pstr.split("->")
                    sizes.pop(1)
                    pstr = "->".join(sizes)
                    procData[idx]["wss"][mprop] = pstr

                # highlight final size #
                sizes = pstr.rsplit("->", 1)
                if len(sizes) > 1:
                    sizes[1] = convColor(sizes[1], "CYAN", 7)
                    pstr = "->".join(sizes)
                elif SysMgr.showAll and not isFirstLined:
                    pstr = convColor(pstr, "CYAN")

                tstr += pstr

                # count newlines #
                newline = tstr.count("\n") + 1

                # set name #
                if mval:
                    name = "WSS"
                    title = " "
                else:
                    name = mprop
                    title = "MEM(%s)" % mprop

                # print info #
                ret = SysMgr.addPrint(
                    "{0:>39} | {1:3}: {2:1}\n".format(title, name, tstr),
                    newline,
                )
                if not ret:
                    return

            # print memory summary #
            if not onlyVmflag and memBuf and status:
                vmlist = [
                    "VmPeak",
                    "VmHWM",
                    "VmData",
                    "HugetlbPages",
                    "RssAnon",
                    "RssFile",
                    "RssShmem",
                ]

                memstr = ""
                memset = status

                for item in vmlist:
                    try:
                        vmsize = long(memset[item].split(None, 1)[0]) << 10
                        memstr += "%s: %s, " % (item, convSize(vmsize))
                    except SystemExit:
                        sys.exit(0)
                    except:
                        pass

                if memstr:
                    ret = SysMgr.addPrint(
                        "{0:>39} | {1:111}|\n".format("MEM(SUM)", memstr[:-2])
                    )
                    if not ret:
                        return

            # print additional stats #
            for ef, ev, ee, es, en in (
                (SysMgr.cmdlineEnable, "cmdline", "CMDLINEFILTER", None, None),
                (SysMgr.exeEnable, "exe", "EXEFILTER", None, None),
                (SysMgr.nsEnable, "ns", "", ", ", True),
                ("cgroup" in value, "cgroup", "", None, True),
            ):
                # check skip condition #
                if not ef or not value.get(ev):
                    continue

                # print items #
                eeVal = SysMgr.environList.get(ee)
                if not eeVal or UtilMgr.isValidStr(value[ev], eeVal):
                    content = value[ev].rstrip(es) if es else value[ev]
                    ret = SysMgr.addPrint(
                        "{0:>39} | {1:1}\n".format(ev.upper(), content),
                        newline=(value[ev].count("\n") + 1) if en else 1,
                    )
                    if not ret:
                        return

            # print binder stats #
            binder = self.binderData.get(idx, {})
            binderList = []
            for p, d in binder.items():
                binderList.append("%s: %s" % (p, d))
            if binderList:
                bstr = UtilMgr.convLineStr(
                    ", ".join(binderList),
                    "",
                    41,
                    SysMgr.lineLength - 43,
                    startIndent=False,
                )
                ret = SysMgr.addPrint(
                    "{0:>39} | {1:1}\n".format("BINDER", bstr.lstrip()),
                    newline=bstr.count("\n") + 1,
                )
                if not ret:
                    return

            # print D-Bus unit #
            if DbusMgr.pidUnitList:
                pidUnit = DbusMgr.pidUnitList.get(
                    idx if SysMgr.processEnable else pid
                )
                if pidUnit:
                    ret = SysMgr.addPrint(
                        "{0:>39} | {1:1}\n".format("UNIT", pidUnit)
                    )
                    if not ret:
                        return

            # print sched #
            if SysMgr.schedEnable and (
                "execTime" in value or "waitTime" in value
            ):
                execTime = float(long(value["execTime"] / 1000000000))
                if value["runtime"]:
                    execPer = (execTime / value["runtime"]) * 100
                else:
                    execPer = 0
                waitTime = float(long(value["waitTime"] / 1000000000))
                if value["runtime"]:
                    waitPer = (waitTime / value["runtime"]) * 100
                else:
                    waitPer = 0

                execStr = "Exec: %s(%.1f%%)" % (convTime(execTime), execPer)
                waitStr = "Wait: %s(%.1f%%)" % (convTime(waitTime), waitPer)
                sliceStr = "NrTimeslice: %s" % convNum(value["nrSlice"])
                schedStr = "%s / %s / %s" % (execStr, waitStr, sliceStr)
                ret = SysMgr.addPrint(
                    "{0:>39} | {1:1}\n".format("SCHED", schedStr)
                )
                if not ret:
                    return

            # print delay #
            try:
                _printDelay(self, value)
            except SystemExit:
                sys.exit(0)
            except:
                pass

            # print D-Bus #
            for line in value.get("dbusList", []):
                ret = SysMgr.addPrint(
                    "{0:>39} | {1:1}\n".format("D-BUS", line)
                )
                if not ret:
                    return

            # print stack samples #
            if SysMgr.stackEnable:
                try:
                    if _printStackSamples(idx) == -1:
                        SysMgr.addPrint("---more---")
                        return
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

                try:
                    self.stackTable[idx]["total"] = 0
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

            # print dmabuf usage #
            if self.dmabufData and idx in self.fdinfoData:
                dmabufStat = {}
                inoList = {}

                for fd, fvals in self.fdinfoData[idx].items():
                    ino = fvals.get("ino")
                    if not ino or not ino in self.dmabufData or ino in inoList:
                        continue

                    # save ino to skip duplicated ino #
                    inoList[ino] = 0

                    dname = fvals.get("exp_name")
                    if not dname:
                        continue

                    dsize = long(fvals.get("size", 0))
                    if not dsize:
                        continue

                    dmabufStat[dname] = dmabufStat.get(dname, 0) + dsize

                if dmabufStat:
                    dmabufStr = ""
                    for dname, dsize in dmabufStat.items():
                        dmabufStr += "%s: %s, " % (dname, convSize(dsize))
                    ret = SysMgr.addPrint(
                        "{0:>39} | {1:1}\n".format(
                            "DMABUF", dmabufStr.rstrip(" ,")
                        )
                    )
                    if not ret:
                        return

            procCnt += 1
            if SysMgr.memEnable and smapsEnable:
                ret = SysMgr.addPrint("%s\n" % oneLine)
                if not ret:
                    return

        # check stream flag #
        if self.taskStreamEnable:
            return
        elif procCnt:
            # total CPU #
            totalTime = "%6.1f" % totalStats["ttime"]
            totalTime = convCpuColor(totalStats["ttime"], totalTime)

            # total BLOCK #
            totalBtime = totalStats["btime"]
            if totalStats["btime"]:
                totalBtime = convColor(totalBtime, "RED", 4)

            # total READ #
            readsize = totalStats["read"]
            if readsize != "-":
                readsize = readsize >> memFactorMB
                if readsize:
                    readsize = convColor(readsize, "RED", 4)

            # total WRITE #
            writesize = totalStats["write"]
            if writesize != "-":
                writesize = writesize >> memFactorMB
                if writesize:
                    writesize = convColor(writesize, "RED", 4)

            # total MEM #
            tmem = convSize(totalStats["mem"] << 20)
            tmem = convColor(tmem, "YELLOW", 7)

            # total SWAP #
            tswap = totalStats["swap"]
            try:
                if tswap:
                    tswap = convSize(totalStats["swap"] << 20, True)
                    tswap = convColor(tswap, "YELLOW", 5)
            except SystemExit:
                sys.exit(0)
            except:
                pass

            # print total stats #
            ret = SysMgr.addPrint(
                (
                    "{0:>{td}}|"
                    "{1:>6}({2:>4}/{3:>4})|"
                    "{4:>4}:{5:>7}|{6:>3}:{7:>5}|"
                    "{8:>4}({9:>4}/{10:>4}/{11:>5})|"
                    "{12:>12}|{13:>14}|{14:>21}|\n"
                ).format(
                    "[ TOTAL ]",
                    totalTime,
                    totalStats["utime"],
                    totalStats["stime"],
                    mem,
                    tmem,
                    "Swp",
                    tswap,
                    totalBtime,
                    readsize,
                    writesize,
                    totalStats["majflt"],
                    "Yld: %s" % convNum(totalStats["yld"]),
                    "Prmt: %s" % convNum(totalStats["prtd"]),
                    "Task: %s" % convNum(totalStats["task"]),
                    td=cl + (pd * 2) + 14,
                )
            )
            if not ret:
                return

            ret = SysMgr.addPrint("%s\n" % oneLine)
            if not ret:
                return
        else:
            text = "{0:^16}".format("None")
            frame = "%s%s|" % (text, " " * (SysMgr.lineLength - len(text) - 1))
            ret = SysMgr.addPrint("{0:1}\n{1:1}\n".format(frame, oneLine))
            if not ret:
                return

        # check skip special task option #
        if "NOSPECIALTASK" in SysMgr.environList:
            return

        # print special tasks #
        if not self.printSpecialTask("abnormal", saveJsonStat):
            return
        if not self.printSpecialTask("new", saveJsonStat):
            return
        if not self.printSpecialTask("die", saveJsonStat):
            return

    def getPidLen(self):
        pidlen = len(str(self.maxPid))
        if pidlen < 4:
            return 4
        else:
            return pidlen

    def printSpecialTask(self, taskType, saveJsonStat):
        # set comm and pid size #
        pd = self.getPidLen()
        cl = 26 - (pd * 2)

        # init task dict #
        if saveJsonStat:
            self.reportData.setdefault("task", {})
            self.reportData["task"].setdefault(taskType, {})
            jsonData = self.reportData["task"][taskType]

        # get task list #
        if taskType == "abnormal":
            taskList = set(self.abnormalTasks)
            self.nrAbnormalTask = len(taskList)
            peakList = SysMgr.environList.get("CNTTASKABNORMAL", [])
        elif taskType == "new":
            taskList = set(self.procData) - set(self.prevProcData)
            self.nrNewTask = len(taskList)
            peakList = SysMgr.environList.get("CNTTASKNEW", [])
        elif taskType == "die":
            taskList = set(self.prevProcData) - set(self.procData)
            self.nrDieTask = len(taskList)
            peakList = SysMgr.environList.get("CNTTASKDIE", [])
        else:
            return

        # check EXCEPTSHM #
        if (
            not (SysMgr.pssEnable or SysMgr.ussEnable)
            and "EXCEPTSHM" in SysMgr.environList
        ):
            exceptShm = True
        else:
            exceptShm = False

        # set memory size shift factor #
        if "KBUNIT" in SysMgr.environList:
            memFactorKB = 0
            memFactorMB = 10

            def _memFactorPG(stat):
                return stat << 2

        else:
            memFactorKB = 10
            memFactorMB = 20

            def _memFactorPG(stat):
                return stat >> 8

        procCnt = 0
        for tid in sorted(list(map(long, taskList))):
            if SysMgr.checkCutCond():
                return False

            idx = str(tid)

            # remove fixed task #
            if SysMgr.fixedProcList:
                SysMgr.fixedProcList.pop(idx, None)
                if not idx in SysMgr.fixedProcList:
                    continue

            # define stat variables #
            if idx in self.procData:
                value = self.procData[idx]
                stat = value["stat"]
            elif idx in self.prevProcData:
                value = self.prevProcData[idx]
                stat = value["stat"]
            else:
                value = dict(self.init_procData)
                stat = ["?"] * 52

            try:
                jsonData[idx] = value
            except:
                pass

            # set comm #
            comm = stat[self.commIdx].strip("()")

            # add peak info #
            for f in peakList:
                if not UtilMgr.isValidStr(comm, [f]):
                    continue
                name = "nrTask%s%s" % (taskType.upper(), f)
                self.customPeakData[name] = (
                    self.customPeakData.get(name, 0) + 1
                )

            # add prefix to comm #
            if taskType == "new":
                comm = "[+]%s" % comm
            elif taskType == "die":
                comm = "[-]%s" % comm
            elif taskType == "abnormal":
                comm = "[%s]%s" % (self.abnormalTasks[idx], comm)

            if SysMgr.processEnable:
                pid = stat[self.ppidIdx]
            else:
                pid = value["mainID"]

            try:
                schedPolicy, schedValue = self.getSchedInfo(stat)
                sched = schedPolicy + schedValue
            except SystemExit:
                sys.exit(0)
            except:
                sched = "?"

            try:
                runtime = value["runtime"] + SysMgr.uptimeDiff
                lifeTime = UtilMgr.convTime(runtime)
                if len(lifeTime.split(":")) > 3:
                    lifeTime = lifeTime[: lifeTime.rfind(":")]
            except SystemExit:
                sys.exit(0)
            except:
                lifeTime = "?"

            try:
                swapSize = (
                    long(value["status"]["VmSwap"].split(None, 1)[0])
                    >> memFactorKB
                )
            except SystemExit:
                sys.exit(0)
            except:
                swapSize = "-"

            if SysMgr.blockEnable:
                try:
                    readSize = value["read"] >> memFactorMB
                    writeSize = value["write"] >> memFactorMB
                except SystemExit:
                    sys.exit(0)
                except:
                    readSize = writeSize = 0
            else:
                readSize = "-"
                writeSize = "-"

            # get common dataset for tasks #
            if idx in self.prevProcData:
                dataset = self.prevProcData
            elif idx in self.procData:
                dataset = self.procData
            else:
                dataset = None

            if not SysMgr.processEnable:
                # process name #
                try:
                    pgid = dataset[idx]["mainID"]
                    etc = "%s(%s)" % (dataset[pgid]["comm"], pgid)
                except SystemExit:
                    sys.exit(0)
                except:
                    etc = "-"
            else:
                # parent name #
                try:
                    pgid = dataset[idx]["stat"][self.ppidIdx]
                    etc = "%s(%s)" % (dataset[pgid]["comm"], pgid)
                except SystemExit:
                    sys.exit(0)
                except:
                    etc = "-"

            if SysMgr.floatEnable:
                ttime = "%.1f" % value["ttime"]
                btime = "%.1f" % value["btime"]
            else:
                ttime = value["ttime"]
                btime = value["btime"]

            try:
                vss = long(stat[self.vssIdx]) >> memFactorMB
            except SystemExit:
                sys.exit(0)
            except:
                vss = 0

            try:
                rss = _memFactorPG(long(stat[self.rssIdx]))
            except SystemExit:
                sys.exit(0)
            except:
                rss = 0

            try:
                shr = _memFactorPG(long(value["statm"][self.shrIdx]))

                # except for shred text in RSS #
                if exceptShm:
                    rss -= shr
                    if rss < 0:
                        rss = 0
            except SystemExit:
                sys.exit(0)
            except:
                shr = "-"

            try:
                codeSize = (
                    long(stat[self.ecodeIdx]) - long(stat[self.scodeIdx])
                ) >> memFactorMB

                # except for shred text in RSS #
                if exceptShm:
                    rss -= codeSize
                    if rss < 0:
                        rss = 0
            except SystemExit:
                sys.exit(0)
            except:
                codeSize = 0

            try:
                if SysMgr.gpuMemEnable and self.gpuMemData:
                    raise Exception("gpumem")

                sid = stat[self.sidIdx][-5:]
                if sid == "0":
                    sid = "-"
            except SystemExit:
                sys.exit(0)
            except:
                sid = "-"

            try:
                if tid in self.procData and "user" in self.procData[tid]:
                    user = self.procData[tid]["user"]
                elif (
                    tid in self.prevProcData
                    and "user" in self.prevProcData[tid]
                ):
                    user = self.prevProcData[tid]["user"]
                else:
                    # get uid #
                    if not value["status"]:
                        self.saveProcStatusData(
                            value["taskPath"], str(tid), force=True
                        )
                    uid = value["status"]["Uid"].split(None, 1)[0]

                    # get user list #
                    SysMgr.sysInstance.saveUserInfo()
                    user = SysMgr.sysInstance.userData[uid]["name"][:6]
            except SystemExit:
                sys.exit(0)
            except:
                user = "-"

            # print thread information #
            ret = SysMgr.addPrint(
                (
                    "{0:>{cl}} ({1:>{pd}}/{2:>{pd}}/{3:>4}/{4:>4})|"
                    "{5:>4}({6:>3}/{7:>3}/{8:>3})|"
                    "{9:>4}({10:>4}/{11:>3}/{12:>3}/{13:>3})|"
                    "{14:>4}({15:>4}/{16:>4}/{17:>5})|"
                    "{18:>5}|{19:>6}|{20:>4}|{21:>9}|{22:>21}|\n"
                ).format(
                    comm[:cl],
                    idx,
                    pid,
                    stat[self.nrthreadIdx],
                    sched,
                    ttime,
                    value["utime"],
                    value["stime"],
                    "-",
                    vss,
                    rss,
                    codeSize,
                    shr,
                    swapSize,
                    btime,
                    readSize,
                    writeSize,
                    value["majflt"],
                    sid,
                    user,
                    "-",
                    lifeTime[:9],
                    etc[:21],
                    cl=cl,
                    pd=pd,
                )
            )
            if not ret:
                return False

            procCnt += 1

        if procCnt:
            ret = SysMgr.addPrint("%s\n" % oneLine)
            if not ret:
                return False

        return True

    def printReportStat(self, reportStat, pretty=False):
        if not reportStat:
            return
        elif isinstance(reportStat, dict):
            reportStat = UtilMgr.convDict2Str(reportStat, pretty)
        elif pretty:
            reportStat = UtilMgr.convStr2Dict(reportStat)
            reportStat = UtilMgr.convDict2Str(reportStat, pretty)

        SysMgr.printPipe(reportStat, newline=False, flush=True)

        return

        printBuf = "%s\n" % twoLine

        if "event" in reportStat:
            for event, proc in reportStat["event"].items():
                printBuf += "[event] (%s)\n" % (event)

                for rank, stat in sorted(
                    proc.items(), key=lambda e: long(e[0]), reverse=False
                ):
                    printBuf += "[%s] " % (rank)

                    for item, val in stat.items():
                        printBuf += "(%s: %s) " % (item, val)

                    printBuf += "\n"

                printBuf += "%s\n" % oneLine

            del reportStat["event"]

        for idx, stat in reportStat.items():
            printBuf += "[%s] " % idx

            if isinstance(stat, dict):
                for item, val in sorted(stat.items(), reverse=False):
                    printBuf += "(%s: %s) " % (item, val)
            else:
                printBuf += "(%s) " % stat

            printBuf += "\n"

        printBuf += "%s\n" % twoLine

        SysMgr.printPipe(printBuf)

    def replyService(self, ip, port):
        if not SysMgr.remoteServObj:
            SysMgr.printErr(
                "failed to use server because it is not initialized"
            )
            return

        # send reply message to server #
        SysMgr.localServObj.sendto("ACK", ip, port)

    def handleServerResponse(self, packet):
        # return by interrupt from recv #
        if not packet:
            sys.exit(-1)

        if isinstance(packet, tuple):
            data, addr = packet[:2]
        else:
            return

        # convert data #
        if not data:
            return
        elif isinstance(data, bytes):
            try:
                data = data.decode()
            except:
                pass

        # check data from server #
        if not UtilMgr.isString(data):
            SysMgr.printErr("failed to recognize data from server")
            return

        # get address info from server #
        try:
            ip = addr[0]
            port = long(addr[1])
        except:
            SysMgr.printErr("failed to recognize address from server")

        # wrong request from client #
        if SysMgr.remoteServObj == "NONE" and data in TaskAnalyzer.requestType:
            SysMgr.printErr("failed to handle %s request from client" % data)
            return

        magicStr = SysMgr.magicStr

        # reply ACK to server #
        try:
            self.replyService(ip, port)
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to send ACK to server")

        # check hostname #
        hostname = str(SysMgr.lastHost)
        if data.startswith(SysMgr.magicStr):
            dataset = data.split(SysMgr.magicStr, 2)
            if len(dataset) > 2:
                SysMgr.lastHost = hostname = str(dataset[1])
                data = dataset[2]

        # REFUSE response #
        if data == "REFUSE":
            SysMgr.printErr(
                "failed to request service "
                "because there is no support from server"
            )
            sys.exit(-1)

        # DUPLICATED response #
        elif data == "PRINT" or data.startswith("REPORT"):
            SysMgr.printErr(
                "failed to request service "
                "because of same port used between client and sever"
            )
            sys.exit(-1)

        # ACK response #
        elif data == "ACK":
            pass

        # PRINT service #
        else:
            SysMgr.waitUserInput(0.000001, msg="DEFAULT")

            # handle RELAY data #
            if data.startswith("RELAY"):
                try:
                    hostaddr, hostname, data = data.split(magicStr, 3)[1:]
                    ip, port = hostaddr.split(":", 1)
                except SystemExit:
                    sys.exit(0)
                except:
                    pass

            # get host filter #
            hostFilter = SysMgr.environList.get("HOSTFILTER", [])

            # check host filter #
            hostaddr = "%s:%s" % (ip, port)
            if not UtilMgr.isValidStr(
                hostaddr, hostFilter
            ) and not UtilMgr.isValidStr(hostname, hostFilter):
                return

            # RELAY data to other nodes #
            SysMgr.sendOutput2Nodes(
                "RELAY%s%s:%s%s%s%s%s"
                % (magicStr, ip, port, magicStr, hostname, magicStr, data),
                force=True,
            )

            # REPORT service #
            if data[0] == "{" and data.rstrip()[-1] == "}":
                # print report data #
                self.printReportStat(data, pretty=False)
                return

            # save host data #
            if not hostaddr in SysMgr.hostList:
                SysMgr.hostList[hostaddr] = {
                    "hostname": hostname,
                    "surface": "",
                    "buffer": [],
                }

            # save screen data #
            host = SysMgr.hostList[hostaddr]
            host["hostname"] = hostname
            host["surface"] = data.rstrip().split("\n")
            if SysMgr.outPath:
                host["buffer"].insert(0, UtilMgr.removeColor(data))

            # init screen vars #
            nrLine = 0
            nrSurface = 0
            fullSurface = ""
            nrScreen = len(SysMgr.hostList)
            if SysMgr.streamEnable or SysMgr.outPath:
                windowSize = SysMgr.ttyRows = sys.maxsize
            elif SysMgr.showAll:
                windowSize = SysMgr.ttyRows
            else:
                windowSize = long(SysMgr.ttyRows / nrScreen) - 2

            # composite surfaces #
            for n, v in sorted(
                SysMgr.hostList.items(), key=lambda x: x[1]["hostname"] + x[0]
            ):
                try:
                    surface = v["surface"]
                    name = "%s(%s)" % (v["hostname"], n)

                    # last surface #
                    if SysMgr.showAll:
                        if nrLine >= windowSize:
                            break
                        cutSize = min(
                            SysMgr.ttyRows - nrLine - 2, len(surface)
                        )
                        window = surface[:cutSize]
                        nrLine += len(window) + 2
                    elif nrSurface == nrScreen - 1:
                        cutSize = SysMgr.ttyRows - nrLine - 2
                        window = surface[:cutSize]
                    else:
                        window = surface[:windowSize]
                        nrLine += len(window) + 2

                    splitLineColor = UtilMgr.convColor(
                        "%s%s" % (name, splitLine[: -len(name)]), "GREEN"
                    )

                    fullSurface += "%s\n" % splitLineColor
                    fullSurface += "\n".join(window) + "\n\n"

                    nrSurface += 1
                except SystemExit:
                    sys.exit(0)
                except KeyboardInterrupt:
                    sys.exit(0)
                except:
                    pass

            # clear screen #
            if not SysMgr.outPath and not SysMgr.streamEnable:
                SysMgr.clearScreen()

            # print screen #
            SysMgr.printPipe(fullSurface.rstrip())

    def requestService(self):
        if not SysMgr.remoteServObj or not SysMgr.localServObj:
            SysMgr.remoteServObj = None
            return

        try:
            # set block socket #
            SysMgr.localServObj.socket.setblocking(1)

            if SysMgr.remoteServObj != "NONE":
                # send request to server #
                SysMgr.localServObj.sendto(
                    SysMgr.remoteServObj.request,
                    SysMgr.remoteServObj.ip,
                    SysMgr.remoteServObj.port,
                )

                # check event #
                if SysMgr.remoteServObj.request.startswith("EVENT_"):
                    SysMgr.printStat(
                        "requested %s to server" % SysMgr.remoteServObj.request
                    )
                    sys.exit(-1)

                SysMgr.printStat(
                    "wait for response of %s registration from server"
                    % SysMgr.remoteServObj.request
                )
            else:
                SysMgr.printStat("wait for input from server")
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr(
                "failed to send request '%s'" % SysMgr.remoteServObj.request
            )

    def handleEventCmd(self, cmd, source, user):
        # RESTART #
        if cmd.startswith("RESTART"):
            # print message #
            SysMgr.printInfo(
                "restart %s(%s)..." % (SysMgr.pid, SysMgr.getComm(SysMgr.pid))
            )

            # restart process #
            SysMgr.restart()

        # check PAUSE status #
        if not SysMgr.printEnable and SysMgr.waitEnable:
            return None

        # set event name #
        name = "USER" if user else source

        # set command name #
        ocmd = cmd.split(":", 1)[0].split("#", 1)[0]

        # SAVE #
        if ocmd in ("SAVE", "SAVERAW", "SAVEMIN") or ocmd in ConfigMgr.RESSET:
            ret = self.handleSaveCmd(cmd, name, ocmd)
            if not ret:
                # disable event handling for child process #
                SysMgr.eventHandleEnable = False
            return ret
        # GETDUMP #
        elif ocmd in ("GETDUMP", "GETSUM", "GETINFO"):
            return self.handleDumpCmd(cmd)
        # EXIT #
        elif ocmd == "EXIT":
            SysMgr.printInfo("terminated by CMD_EXIT command")
            sys.exit(0)
        # OUTPATH #
        elif ocmd == "OUTPATH":
            # check command #
            if cmd == ocmd:
                SysMgr.printErr("no output path for '%s' command" % cmd)
                return None

            # get new path #
            try:
                path = os.path.realpath(UtilMgr.lstrip(cmd, "OUTPATH:"))
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr(
                    "failed to get the new output path from '%s'" % cmd, True
                )
                return None

            # print message #
            SysMgr.printInfo(
                "updated the output path from '%s' to '%s'"
                % (SysMgr.outPath, path)
            )

            SysMgr.outPath = path
        # UPDATE #
        elif ocmd == "UPDATE":
            # check command #
            if cmd == ocmd:
                SysMgr.printErr("no update path for '%s' command" % cmd)
                return None

            # get threshold data #
            value = None
            path = UtilMgr.lstrip(cmd, "UPDATE:")

            # update threshold #
            SysMgr.updateThreshold(path)

            # print message #
            SysMgr.printInfo(
                (
                    "updated the threshold data from '%s' by '%s' command "
                    "for %s event"
                )
                % (path, cmd, name)
            )
        # FILTER #
        elif ocmd == "FILTER":
            # check command #
            if cmd == ocmd:
                SysMgr.printErr("no filter keyword for '%s' command" % cmd)
                return None

            value = UtilMgr.lstrip(cmd, "FILTER:").strip()
            if value:
                value = UtilMgr.cleanItem(value.split("|"))
                SysMgr.filterGroup = value
            else:
                SysMgr.filterGroup = []
                value = "[]"

            # print message #
            SysMgr.printInfo(
                "updated the task filter to %s by '%s' command for %s event"
                % (value, cmd, name)
            )
        # NOTIFY #
        elif ocmd == "NOTIFY":
            # check command #
            if cmd == ocmd:
                SysMgr.printErr("no notify event for '%s' command" % cmd)
                return None

            value = UtilMgr.lstrip(cmd, "NOTIFY:").strip()
            if not value:
                SysMgr.printErr("failed to handle notify event '%s'" % cmd)
                return None

            # print message #
            SysMgr.printInfo("be notified of '%s' event" % value)

            # get event info #
            try:
                item, name, target = UtilMgr.cleanItem(value.split("_", 2))
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr(
                    "failed to get event info from '%s'" % value, True
                )
                return None

            # search event command #
            if not item in SysMgr.thrData:
                return None

            # init variables #
            eventList = []
            self.reportData.setdefault("event", {})

            for _name, _values in SysMgr.thrData[item].items():
                if name != _name:
                    continue

                # check type #
                if isinstance(_values, list):
                    pass
                elif isinstance(_values, dict):
                    _values = [_values]
                else:
                    continue

                for _value in _values:
                    # check skip condition #
                    if _value.get("apply") != "true":
                        continue
                    # check log condition #
                    elif _value.get("filter") == target:
                        # check oneshot flag #
                        (
                            oneshot,
                            goneshot,
                            refresh,
                            lock,
                            maxcnt,
                            skip,
                        ) = TaskAnalyzer.getThresholdAttr(_value)

                        # set threshold #
                        ename = self.setThresholdEvent(
                            _value,
                            "filter",
                            item,
                            None,
                            target,
                            name,
                            None,
                            None,
                            oneshot,
                            goneshot,
                            refresh,
                            lock,
                            maxcnt,
                            skip,
                        )
                        if ename:
                            eventList.append(ename)

                        # check keeping notifier task alive #
                        if _value.get("oneshot") != "true" or _value.get(
                            "refresh"
                        ):
                            continue

                        # terminate notifier task #
                        epname = "CMD_NOTIFY:%s" % (value)
                        tasks = SysMgr.eventCommandList.get(epname)
                        if tasks:
                            SysMgr.terminateTasks(tasks, signal.SIGKILL)
                            SysMgr.eventCommandList.pop(epname, None)

            # handle events #
            if eventList:
                self.handleThresholdEvents()
        # ENABLE / DISABLE #
        elif ocmd in ("ENABLE", "DISABLE"):
            # check command #
            if cmd == ocmd:
                SysMgr.printErr("no resource name for '%s' command" % cmd)
                return None

            if ocmd == "ENABLE":
                act = "enable"
                value = True
                res = UtilMgr.lstrip(cmd, "ENABLE:")
            else:
                act = "disable"
                value = False
                res = UtilMgr.lstrip(cmd, "DISABLE:")

            # update target resource #
            if res == "ALL":
                SysMgr.showAll = value
            elif res == "CPU":
                SysMgr.cpuEnable = value
            elif res == "GPU":
                SysMgr.gpuEnable = value
            elif res == "MEM":
                SysMgr.memEnable = value
            elif res == "IRQ":
                SysMgr.irqEnable = value
            elif res == "DISK":
                SysMgr.diskEnable = value
            elif res == "NETWORK":
                SysMgr.networkEnable = value
            elif res == "BLOCK":
                if SysMgr.checkRootPerm(False):
                    SysMgr.blockEnable = value
            elif res == "PMU":
                if SysMgr.checkRootPerm(False):
                    SysMgr.perfEnable = value

                    # initialize perf events #
                    if value:
                        SysMgr.initSystemPerfEvents()
            elif res == "LOG":
                SysMgr.logEnable = value
            else:
                SysMgr.printWarn("no support '%s' resource" % res, True)
                return None

            # print message #
            SysMgr.printInfo(
                "%s the monitoring resource '%s' by '%s' command for %s event"
                % (act, res, cmd, name)
            )
        # RESET #
        elif cmd == "RESET":
            # reset monitoring stats #
            SysMgr.clearStats()

            # print message #
            SysMgr.printInfo(
                "reseted the monitoring stats by '%s' command for %s event"
                % (cmd, name)
            )
        # CLEAR #
        elif cmd == "CLEAR":
            # clear buffer #
            SysMgr.clearProcBuffer()

            # print message #
            SysMgr.printInfo(
                "cleared the monitoring results by '%s' command for %s event"
                % (cmd, name)
            )
        # BUFFER #
        elif ocmd == "BUFFER":
            # check command #
            if cmd == ocmd:
                SysMgr.printErr("no buffer size for '%s' command" % cmd)
                return None

            # get buffer size #
            size = UtilMgr.lstrip(cmd, "BUFFER:")

            # apply new buffer size #
            sizeOrig = size
            size = UtilMgr.convUnit2Size(size)
            if size > 0:
                SysMgr.bufferSize = size
            else:
                SysMgr.printErr("failed to apply buffer size to %s" % sizeOrig)
                return None

            # print message #
            SysMgr.printInfo(
                (
                    "changed the size of the monitoring buffer to %s by "
                    "'%s' command for %s event"
                )
                % (UtilMgr.convSize2Unit(size), cmd, name)
            )
        # INTERVAL #
        elif ocmd == "INTERVAL":
            # get interval #
            interval = UtilMgr.lstrip(cmd, "INTERVAL:")
            if ocmd == cmd or not interval:
                interval = 1
            elif interval.startswith("_"):
                interval = interval[1:]

            # apply new interval #
            intervalOrig = interval
            interval = UtilMgr.convUnit2Time(interval)
            if interval > 0:
                SysMgr.intervalEnable = interval
            else:
                SysMgr.printErr(
                    "failed to apply interval to %s" % intervalOrig
                )
                return None

            # print message #
            SysMgr.printInfo(
                (
                    "changed the monitoring interval to %s sec by '%s'"
                    " command for %s event"
                )
                % (UtilMgr.convNum(interval), cmd, name)
            )
        # RELOAD #
        elif ocmd == "RELOAD":
            # print message #
            SysMgr.printInfo("start reloading threshold config")

            # get file path #
            path = UtilMgr.lstrip(cmd, "RELOAD")
            if not path:
                path = SysMgr.confFileName
            elif path.startswith(":"):
                path = path[1:]
            else:
                SysMgr.printErr("wrong command format for 'RELOAD'")
                return None

            # convert path #
            path = SysMgr.convFullPath(path)

            # terminate event handling tasks #
            if SysMgr.eventCommandList:
                for tasks in SysMgr.eventCommandList.values():
                    SysMgr.terminateTasks(tasks, signal.SIGINT)
                SysMgr.eventCommandList = {}

            # reset threshold data #
            ConfigMgr.confData = {}
            SysMgr.thrEvtHist = {}
            SysMgr.thrEvtList = {}

            # load and check config file #
            if not SysMgr.loadConfig(path):
                SysMgr.printErr("wrong path '%s' for config" % path)
                return None

            # reload threshold config #
            SysMgr.applyThreshold()
        # PAUSE #
        elif ocmd in ("PAUSE", "SLEEP"):
            if ocmd == "PAUSE":
                # clear buffer #
                SysMgr.clearPrint()
                SysMgr.clearProcBuffer()
                SysMgr.shrinkHeap()

                # print message #
                SysMgr.printInfo(
                    (
                        "cleared the monitoring results and paused all "
                        "activities by '%s' command for %s event"
                    )
                    % (cmd, name)
                )

                # disable print #
                SysMgr.printEnable = False
                SysMgr.logEnable = False

                # wait for RESTART event #
                SysMgr.waitEnable = True

            # get duration #
            duration = UtilMgr.lstrip(cmd, "PAUSE:")
            duration = UtilMgr.lstrip(duration, "SLEEP:")
            if ocmd == cmd or not duration:
                duration = -1
                SysMgr.printInfo("entered sleep mode by '%s' command" % cmd)
            else:
                try:
                    duration = long(duration)
                    SysMgr.printInfo(
                        "start waiting for %s seconds by '%s' command"
                        % (UtilMgr.convNum(duration), cmd)
                    )
                except:
                    SysMgr.printErr("wrong duration for '%s' command" % cmd)
                    return None

            while 1:
                time.sleep(1)

                # check duration #
                if duration >= 0:
                    duration -= 1
                    if duration == 0:
                        SysMgr.printInfo(
                            "wake up from sleep mode by '%s' command" % cmd
                        )
                        return True

                self.checkServer()
        # STOP #
        elif cmd == "STOP":
            # print message #
            SysMgr.printInfo("stop the threshold monitoring")

            # clear threshold data #
            SysMgr.thrData = {}
            SysMgr.thrEvtList = {}
            SysMgr.thrEvtHist = {}
        # KILL #
        elif cmd == "KILL":
            # print message #
            SysMgr.printInfo("kill all child tasks")

            # terminate event handling tasks #
            if SysMgr.eventCommandList:
                for tasks in SysMgr.eventCommandList.values():
                    SysMgr.terminateTasks(tasks, signal.SIGINT)
                SysMgr.eventCommandList = {}

            # kill all childs #
            SysMgr.killChildren()
        else:
            SysMgr.printWarn(
                "no support '%s' as an embedded command" % cmd, True
            )
            return None

        return True

    def checkServer(self):
        if not SysMgr.localServObj:
            return
        elif "IGNREQ" in SysMgr.environList:
            return

        prevMsg = None
        while 1:
            # get message from clients #
            ret = SysMgr.localServObj.recvfrom(verb=False)

            # verify request type #
            if ret is False:
                SysMgr.localServObj = None
                return
            elif not ret:
                return
            # check same packet #
            elif prevMsg == ret:
                return

            # save message #
            prevMsg = ret

            # check type #
            if not isinstance(ret, tuple):
                continue

            try:
                message = ret[0].decode()
            except:
                message = ret[0]

            # check message type #
            if not UtilMgr.isString(message) or isinstance(message, bytes):
                SysMgr.printWarn("wrong message type '%s'" % message, True)
                return

            try:
                ip = ret[1][0]
                port = ret[1][1]
            except:
                SysMgr.printWarn(
                    "failed to get address of client from message"
                )
                continue

            # create network object for send event info #
            if (
                "0.0.0.0" != SysMgr.localServObj.ip != ip
                or SysMgr.localServObj.port != port
            ):
                networkObject = NetworkMgr("client", ip, port)
                if not networkObject.ip:
                    continue

                # save current time in new object #
                networkObject.time = time.time()
            else:
                networkObject = None

            if message.startswith("EVENT_"):
                # get event name #
                event = message.split("_", 1)[1]

                # get runtime #
                pos = event.rfind("@")
                if pos >= 0:
                    rtime = event[pos + 1 :]
                    event = event[:pos]
                else:
                    rtime = SysMgr.uptime

                # append event to list #
                TaskAnalyzer.procEventData.append(
                    [SysMgr.uptime, event, rtime]
                )

                SysMgr.printInfo(
                    "added event '%s' from %s:%d" % (event, ip, port)
                )

                # handle command #
                if event.startswith("CMD_"):
                    if not SysMgr.eventHandleEnable:
                        SysMgr.printWarn("ignored '%s' event" % event)
                    else:
                        self.handleEventCmd(
                            UtilMgr.lstrip(event, "CMD_"),
                            "%s:%s" % (ip, port),
                            True,
                        )
                else:
                    # send event message #
                    if networkObject:
                        networkObject.send(message)
                        del networkObject

            elif message == "LOG":
                pass

            elif not networkObject:
                pass

            elif message == "PRINT":
                index = ip + ":" + str(port)
                if not index in SysMgr.addrListForPrint:
                    SysMgr.addrListForPrint[index] = networkObject
                    SysMgr.printInfo(
                        "registered %s:%d as remote address for PRINT"
                        % (ip, port)
                    )
                else:
                    SysMgr.printWarn(
                        "duplicated %s:%d as remote address" % (ip, port)
                    )

            elif message in ("REPORT", "THRESHOLD"):
                if not SysMgr.reportEnable:
                    SysMgr.printWarn(
                        (
                            "ignored %s request from %s:%d "
                            "because there is no service"
                        )
                        % (message, ip, port)
                    )
                    networkObject.send("REFUSE")
                    del networkObject
                    continue

                networkObject.request = message

                index = ip + ":" + str(port)
                if index in SysMgr.addrListForReport:
                    action = "updated"
                else:
                    action = "registered"

                SysMgr.addrListForReport[index] = networkObject
                SysMgr.printInfo(
                    "%s %s:%d as remote address for %s"
                    % (action, ip, port, message)
                )

            elif message == "ACK":
                index = ip + ":" + str(port)
                if index in SysMgr.addrListForPrint:
                    targetList = SysMgr.addrListForPrint
                elif index in SysMgr.addrListForReport:
                    targetList = SysMgr.addrListForReport
                else:
                    SysMgr.printWarn(
                        "failed to find %s:%d as remote address" % (ip, port)
                    )
                    continue

                targetList[index].ignore -= 1
                targetList[index].status = "READY"

            # wrong request or just data from server #
            else:
                SysMgr.printWarn(
                    "failed to recognize the request from client (%s:%s)"
                    % (ip, port)
                )

    def handleDumpCmd(self, cmd):
        # get size #
        parts = cmd.split(":", 1)
        cmd = parts[0]
        if len(parts) > 1:
            length = 0 - long(parts[1])
        else:
            length = 0

        # get connection info #
        ret = SysMgr.localServObj.recvfrom()
        if not ret or not ret[0]:
            SysMgr.printErr(
                "failed to get connection info for %s command" % cmd
            )
            return -1

        # backup threshold data #
        thrData = SysMgr.thrData

        if SysMgr.isLinux:
            # create a new process #
            pid = SysMgr.createProcess()
            if pid > 0:
                return pid

            # change priority #
            SysMgr.setLowPriority(True)

            # register SIGINT handler #
            signal.signal(signal.SIGINT, SysMgr.stopHandler)

            # disable report #
            SysMgr.reportEnable = False
        else:
            SysMgr.closePrintFd()

        # parse connection info #
        addr = ret[0].decode()

        # create a new socket for TCP #
        cliObj = NetworkMgr.setRemoteServer(addr, tcp=True)

        # connect to the agent #
        cliObj.connect()

        SysMgr.printInfo(
            "connected to the client(%s) for %s command" % (addr, cmd)
        )

        # get threshold info #
        if cmd == "GETINFO":
            data = UtilMgr.convDict2Str(thrData).encode()
        else:
            # get system info #
            if not SysMgr.sysinfoBuffer:
                if SysMgr.sysInstance:
                    SysMgr.sysInstance.printSystemInfo()

            if SysMgr.sysinfoBuffer:
                data = SysMgr.sysinfoBuffer + "\n"
            else:
                data = ""

            # make payload #
            data = (data + "\n".join(SysMgr.procBuffer[length:])).encode()

        # send data size #
        cliObj.send(str(len(data)).encode())

        # receive ACK #
        cliObj.recv(3)

        # send data #
        cliObj.send(data)

        SysMgr.printInfo(
            "sent data (%s) to the client(%s) for %s command"
            % (UtilMgr.convSize2Unit(len(data)), addr, cmd)
        )

        sys.exit(0)

    def handleSaveCmd(self, cmd, event, form=None):
        # replace output path #
        origCmd = cmd
        parts = cmd.rsplit("#", 1)
        cmd = parts[0]
        if len(parts) > 1:
            outPath = parts[1]
        else:
            outPath = None

        # replace event name #
        parts = cmd.split("@", 1)
        cmd = parts[0]
        if len(parts) > 1:
            event = "%s_%s" % (event, parts[1])

        # set target command #
        if form in ("SAVERAW", "SAVEMIN") or form in ConfigMgr.RESSET:
            target = form
        else:
            target = "SAVE"

        # verify save command #
        if cmd.split(":")[0] != target:
            SysMgr.printWarn("no support '%s' command" % origCmd, True)
            return -1

        # print message #
        SysMgr.printInfo(
            "start saving the monitoring results by '%s' command for '%s' event"
            % (origCmd, event)
        )

        # check out path #
        if not SysMgr.outPath and not outPath:
            SysMgr.printErr("no output path for '%s' command" % origCmd)
            return -1

        # increase report number #
        SysMgr.nrReport += 1

        def _handleDump(event):
            try:
                # get event info #
                attr = self.reportData["event"].get(event)
                if not attr:
                    return

                for name in ["dumpfile", "dumpfileb64"]:
                    cname = name.upper()
                    SysMgr.environList.pop(cname, None)

                    # check command #
                    item = attr.get(name)
                    if not item:
                        continue

                    if not isinstance(item, list):
                        item = [item]
                    SysMgr.environList[cname] = item

                # dump files #
                SysMgr.dumpFileData()
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printWarn("failed to dump files", True, True)

        # forkable OS #
        if SysMgr.isLinux:
            # create a new process #
            pid = SysMgr.createProcess()
            if pid > 0:
                return pid

            # change priority #
            SysMgr.setLowPriority(True)

            # register SIGINT handler #
            signal.signal(signal.SIGINT, SysMgr.stopHandler)

            # disable report #
            SysMgr.reportEnable = False

            def _setMinRep():
                SysMgr.logoEnable = False
                SysMgr.addEnvironVar("NOHEADER")
                SysMgr.addEnvironVar("NOINTSUMMARY")

            # disable interval summary #
            if form == "SAVERAW":
                SysMgr.addEnvironVar("NOINTSUMMARY")
                SysMgr.addEnvironVar("PRINTEVENTHIST")
            elif form == "SAVEMIN":
                _setMinRep()
            elif form in ConfigMgr.RESSET:
                SysMgr.clearPrint()
                SysMgr.clearProcBuffer()
                _setMinRep()
        else:
            SysMgr.closePrintFd()

        # save report reason #
        SysMgr.reportReason = event

        # get time #
        timeinfo = UtilMgr.getTime("UTCTIME" in SysMgr.environList)
        if not timeinfo:
            timeinfo = long(SysMgr.uptime)

        # get duplicated output path list #
        if outPath:
            pathList = outPath.split("+")
            outPath = pathList[0].strip()
            if len(pathList) > 1:
                for p in pathList[1:]:
                    SysMgr.addEnvironVar("DUPOUTPATH", p.strip())

        # get output dir #
        isTargetDir = True
        # new #
        if outPath:
            SysMgr.printInfo("set output path to '%s'" % outPath)
            # dir #
            if os.path.isdir(outPath):
                targetDir = outPath
            # file #
            else:
                isTargetDir = False
                SysMgr.outPath = outPath
        # /tmp #
        elif SysMgr.outPath == SysMgr.nullPath:
            targetDir = SysMgr.tmpPath
        # dir #
        elif os.path.isdir(SysMgr.outPath):
            targetDir = SysMgr.outPath
        # file to dir #
        else:
            targetDir = os.path.dirname(SysMgr.outPath)

        # set output path #
        if isTargetDir:
            SysMgr.outPath = "%s/guider_%08d_%08d_%s_%s_%s.out" % (
                targetDir,
                SysMgr.nrRun,
                SysMgr.nrReport,
                event,
                cmd,
                timeinfo,
            )

        # cleanup directories #
        try:
            # create a sync lock #
            syncLock = Debugger.getGlobalLock("FREEFILE", exit=False)
            if syncLock:
                # pylint: disable-next=undefined-variable
                lockf(syncLock, LOCK_EX)

            # cleanup directories #
            SysMgr.freeReportDir()
            SysMgr.freeDirs()
            SysMgr.freeDirsCnt(SysMgr.outPath)

            # wait for lock release #
            if syncLock:
                # pylint: disable-next=undefined-variable
                lockf(syncLock, LOCK_UN)
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn("failed to get lock to free files", True, True)

        def _printContext():
            SysMgr.printPipe(
                "[System General Info]\n.\n.\n.\n{0:20} {1:1}".format(
                    "ReportReason", SysMgr.reportReason
                )
            )
            if SysMgr.outPath:
                SysMgr.printPipe(
                    "{0:20} {1:1}".format(
                        "FileName", os.path.basename(SysMgr.outPath)
                    )
                )

        # print report reason #
        if form == "SAVEMIN":
            _printContext()
        elif form in ConfigMgr.RESSET:
            # save sort value #
            origTotal = SysMgr.totalEnable
            origSort = SysMgr.sort

            # set sort option #
            if form in ("SAVECPU", "SAVETOTALCPU"):
                if form == "SAVETOTALCPU":
                    SysMgr.totalEnable = True
                    SysMgr.showAll = True
                SysMgr.sort = "c"
            elif form in ("SAVEMEM", "SAVEMEMPROC"):
                SysMgr.memEnable = True
                SysMgr.sort = "m"
                if form == "SAVEMEM":
                    SysMgr.addEnvironVar("NOSMAPS")
            elif form in ("SAVEBLOCK", "SAVETOTALBLOCK"):
                if form == "SAVETOTALBLOCK":
                    SysMgr.totalEnable = True
                SysMgr.sort = "b"
            elif form == "SAVEPID":
                SysMgr.sort = "p"
            elif form == "SAVERUNTIME":
                SysMgr.sort = "r"
            elif form == "SAVEPRIO":
                SysMgr.sort = "P"
            elif form == "SAVESWAP":
                SysMgr.sort = "s"

            # print last status #
            _printContext()
            self.printSystemStat()

            # recover sort value #
            SysMgr.totalEnable = origTotal
            SysMgr.sort = origSort

        # dump files #
        _handleDump(event)

        # save output #
        if SysMgr.isLinux:
            # convert timer #
            try:
                # no time #
                if target == cmd:
                    timeunit = 0
                    raise Exception("no time")

                # get time #
                timeunit = UtilMgr.lstrip(cmd, "%s:" % target)
                if not timeunit:
                    raise Exception("no time")

                # convert time to seconds #
                sec = UtilMgr.convUnit2Time(timeunit)

                # trim buffer #
                if sec <= 0:
                    SysMgr.procBuffer = SysMgr.procBuffer[: abs(sec)]
                    sec = timeunit = 0
            except SystemExit:
                sys.exit(0)
            except:
                sec = timeunit

            # immediate #
            if sec == timeunit:
                os.kill(SysMgr.pid, signal.SIGINT)
            # later #
            else:
                # close all shared file descriptors #
                self.reclaimFds(sys.maxsize)

                # set timer #
                signal.signal(signal.SIGALRM, SysMgr.stopHandler)
                signal.alarm(sec)

            return 0
        else:
            # handle save commands #
            # TODO: apply sort and print stats #
            if form in ConfigMgr.RESSET:
                origProcBuffer = SysMgr.procBuffer[:]
                SysMgr.procBuffer = [SysMgr.procBuffer[0]]
            else:
                origProcBuffer = SysMgr.procBuffer

            # immediate #
            try:
                SysMgr.termFlag = False
                SysMgr.stopHandler()
            except:
                pass
            finally:
                SysMgr.termFlag = True

            # recover buffer #
            SysMgr.procBuffer = origProcBuffer

            # recover output path #
            if SysMgr.isWindows:
                SysMgr.outPath = "NUL"
            else:
                SysMgr.outPath = SysMgr.nullPath

            return 0

    def executeEventCommand(self, eventList, last=False):
        def _getChilds():
            return set(
                [
                    item
                    for sublist in SysMgr.eventCommandList.values()
                    for item in sublist
                ]
            )

        for event in eventList:
            # get command list for this time #
            if last:
                if not event in SysMgr.thrEvtList:
                    continue

                tcmd = "lcommand"
                value = SysMgr.thrEvtList[event]
                if not tcmd in value:
                    continue
            else:
                tcmd = "command"
                value = self.reportData["event"][event]
                if not tcmd in value or not isinstance(value[tcmd], list):
                    continue

            # skip events that already exist #
            if event in SysMgr.eventCommandList:
                continue
            elif not value["run"]:
                SysMgr.printWarn(
                    "skipped event handling for '%s' because of oneshot flag"
                    % event,
                    True,
                )
                continue

            # get signal timer for command task #
            siginfo = value.get("signal")
            try:
                if not siginfo:
                    raise Exception("no siginfo")

                siginfo = str(siginfo).split(":")
                timer = long(siginfo[0])
                sig = signal.SIGINT
                if len(siginfo) > 1:
                    try:
                        sig = ConfigMgr.SIG_LIST.index(siginfo[1].upper())
                    except SystemExit:
                        sys.exit(0)
                    except:
                        SysMgr.printErr(
                            "failed to get signal number for '%s'" % sig
                        )
                        raise Exception("no siginfo")
            except SystemExit:
                sys.exit(0)
            except:
                sig = timer = None

            origTasks = _getChilds() if timer else set()

            for cmd in value[tcmd]:
                # extract full command by name in current context #
                if (
                    "COMMAND" in SysMgr.thrData
                    and cmd in SysMgr.thrData["COMMAND"]
                ):
                    cmd = SysMgr.thrData["COMMAND"][cmd]
                # extract full command by name in previous context #
                elif (
                    "COMMAND" in SysMgr.prevThrData
                    and cmd in SysMgr.prevThrData["COMMAND"]
                ):
                    cmd = SysMgr.prevThrData["COMMAND"][cmd]
                # handle embedded commands #
                else:
                    ret = self.handleEventCmd(cmd, event, False)
                    # general commands #
                    if ret is True:
                        continue
                    # custom commands #
                    elif ret is None:
                        pass
                    # child task #
                    elif ret == 0:
                        return
                    # parent task #
                    else:
                        try:
                            # register the command task #
                            if ret > 0:
                                SysMgr.eventCommandList.setdefault(event, [])
                                SysMgr.eventCommandList[event].append(ret)

                            continue
                        except SystemExit:
                            sys.exit(0)
                        except:
                            pass

                # apply command variables #
                if "task" in value and value["task"]:
                    cmd = cmd.replace("EVTPID", list(value["task"])[0])
                cmd = cmd.replace("SELFPID", str(SysMgr.pid))
                cmd = cmd.replace("EVTNAME", event)
                cmd = cmd.replace("EVTTIME", str(UtilMgr.getTime()))
                cmd = cmd.replace("EVTUTCTIME", str(UtilMgr.getTime(utc=True)))
                cmd = cmd.replace("EVTUPTIME", str(long(SysMgr.uptime)))
                cmd = cmd.replace("NUMRUN", str(SysMgr.nrRun))
                cmd = cmd.replace("NUMREP", str(SysMgr.nrReport))
                if SysMgr.outPath == SysMgr.nullPath:
                    outPath = os.path.realpath(".")
                else:
                    outPath = os.path.realpath(SysMgr.outPath)
                cmd = cmd.replace(
                    "OUTDIR",
                    str(
                        outPath
                        if os.path.isdir(outPath)
                        else os.path.dirname(outPath)
                    ),
                )
                cmd = cmd.replace("OUTPATH", str(outPath))

                laststr = " finished" if last else ""
                SysMgr.printInfo(
                    'executed "%s" by %s event%s' % (cmd, event, laststr)
                )

                # create a new process to execute command #
                ret = SysMgr.createCmdProcess(cmd, initSig=True)
                if ret:
                    # register the command task #
                    SysMgr.eventCommandList.setdefault(event, [])
                    SysMgr.eventCommandList[event].append(ret)

            # register new command tasks #
            if timer:
                newTasks = _getChilds() - origTasks
                if newTasks:
                    SysMgr.thrTimerList.append([timer, event, sig, newTasks])

    def handleThresholdEvents(self):
        # check exit condition #
        if not SysMgr.thresholdEnable:
            return
        elif not SysMgr.thrEvtList and not self.reportData["event"]:
            return

        # print events #
        prevList = list(SysMgr.thrEvtList)
        nowList = list(self.reportData["event"])
        timestr = "at %s (%s)" % (
            SysMgr.uptime,
            UtilMgr.getTime("UTCTIME" in SysMgr.environList),
        )

        # print finished events #
        endList = set(prevList) - set(nowList)
        if endList:
            SysMgr.printInfo(
                "finished threshold events [ %s ] %s"
                % (", ".join(endList), timestr)
            )

            # execute commands for events finished #
            self.executeEventCommand(endList, last=True)

        # print new events #
        newList = set(nowList) - set(prevList)
        if newList:
            SysMgr.printInfo(
                "started threshold events [ %s ] %s"
                % (", ".join(newList), timestr)
            )

            # save event timestamp #
            SysMgr.broadcastEvent(list(newList), [SysMgr.pid])

            # execute commands #
            self.executeEventCommand(newList)

        # print cont events #
        contList = set(nowList) & set(prevList)
        if contList:
            SysMgr.printWarn(
                "continued threshold events [ %s ] %s"
                % (", ".join(contList), timestr)
            )

        # update event list #
        if "event" in self.reportData:
            SysMgr.thrEvtList = self.reportData["event"]
        else:
            return

        if not self.reportData["event"]:
            return

        # print event description #
        estr = UtilMgr.convDict2Str(self.reportData["event"], ignore=True)
        SysMgr.printWarn("%s" % estr)

    def checkResourceThreshold(self):
        if not SysMgr.thrData:
            return

        # check uptime condition #
        try:
            uptimecond = SysMgr.environList.get("THRUPTIMECOND", [0])[0]
            if (
                uptimecond
                and UtilMgr.convUnit2Time(uptimecond) > SysMgr.uptime
            ):
                SysMgr.printWarn(
                    "ignored checking threshold because of uptime condition",
                    True,
                )
                return
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("failed to check uptime condition", True)

        def _getIntval(item):
            return self.intervalData.get(item)

        # init event item #
        self.reportData["event"] = {}

        # init conditional event list #
        SysMgr.thrEvtCondList = {}

        # check image created #
        if SysMgr.imagePath:
            self.reportData["event"]["IMAGE_CREATED"] = SysMgr.imagePath
            SysMgr.imagePath = None

        # update refresh items #
        if SysMgr.thrRefreshList:
            newList = []
            for item in SysMgr.thrRefreshList:
                tick, ename, data = item

                tick -= 1
                # keep #
                if tick > 0:
                    newList.append([tick, ename, data])
                # expired #
                elif "apply" in data:
                    data["apply"] = "true"

                    # remove event form history #
                    SysMgr.thrEvtHist.pop(ename, None)

            SysMgr.thrRefreshList = newList

        # update timer items #
        if SysMgr.thrTimerList:
            newList = []
            for item in SysMgr.thrTimerList:
                tick, event, sig, tasks = item

                tick -= 1
                # keep #
                if tick > 0:
                    newList.append([tick, event, sig, tasks])
                # expired #
                else:
                    SysMgr.printWarn(
                        "send %s to [%s] tasks for %s event"
                        % (
                            ConfigMgr.SIG_LIST[sig],
                            SysMgr.getCommList(tasks),
                            event,
                        ),
                        True,
                    )

                    # kill command tasks #
                    SysMgr.terminateTasks(tasks, sig)

            SysMgr.thrTimerList = newList

        # check CPU #
        try:
            if not "cpu" in SysMgr.thrTarget:
                raise Exception()

            for item in (
                "total",
                "user",
                "kernel",
                "irq",
                "iowait",
                "nrCore",
                "steal",
                "temp",
            ):
                # check effective attribute #
                if not item in SysMgr.thrItemList:
                    continue

                intval = _getIntval(item)
                self.checkThreshold("cpu", item, "CPU", "big", intval=intval)
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # check CORE #
        try:
            if not "core" in SysMgr.thrTarget:
                raise Exception()

            coreData = self.reportData["cpu"]["perCore"]
            coreNumData = None

            # check online status #
            coreList = None
            for core, l in SysMgr.thrData["core"].items():
                if not isinstance(l, list):
                    l = [l]

                for v in l:
                    # check apply #
                    if v.get("apply") != "true":
                        continue

                    # get condition #
                    status = v.get("online")
                    if not status:
                        continue

                    # lazy load core lists #
                    if coreList is None:
                        coreList = set(SysMgr.getCpuList())
                    if coreNumData is None:
                        coreNumData = set(map(str, coreData))

                    # get candidate #
                    if status == "true":
                        items = coreList & coreNumData
                    else:
                        items = coreList - coreNumData

                    # set target #
                    target = [
                        c for c in items if UtilMgr.isValidStr(c, [core])
                    ]
                    if not target:
                        continue
                    target = sorted(target, key=lambda x: long(x))
                    target = "_".join(target)

                    (
                        oneshot,
                        goneshot,
                        refresh,
                        lock,
                        maxcnt,
                        skip,
                    ) = TaskAnalyzer.getThresholdAttr(v)

                    self.setThresholdEvent(
                        v,
                        "online",
                        "CORE",
                        None,
                        target=status,
                        attr=target,
                        oneshot=oneshot,
                        goneshot=goneshot,
                        refresh=refresh,
                        lock=lock,
                        maxcnt=maxcnt,
                        skip=skip,
                    )

            # check stats #
            for item in (
                "total",
                "user",
                "kernel",
                "irq",
                "iowait",
                "steal",
                "temp",
                "curFreq",
                "minFreq",
                "maxFreq",
                "governor",
            ):
                # check effective attribute #
                if not item in SysMgr.thrItemList:
                    continue

                for core, data in coreData.items():
                    target = data.get(item)
                    if target is None:
                        continue

                    if item == "governor":
                        intval = None
                    else:
                        intval = _getIntval("core%s:%s" % (core, item))

                    self.checkThreshold(
                        "core",
                        item,
                        "CORE",
                        "big",
                        target=target,
                        attr=core,
                        intval=intval,
                    )
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # check GPU #
        try:
            # check activation #
            if not "gpu" in SysMgr.thrTarget:
                raise Exception()
            elif not self.reportData["gpu"]:
                SysMgr.printErr(
                    "failed to check GPU usage because there is no support"
                )
                if not SysMgr.forceEnable:
                    sys.exit(-1)

            # each devices #
            dinfo = {}
            for dev, vals in self.reportData["gpu"].items():
                target = vals
                dinfo.update({"dev": dev})

                try:
                    self.checkThreshold(
                        "gpu",
                        "total",
                        "GPU",
                        "big",
                        target,
                        dev,
                        addval=dinfo,
                    )
                except SystemExit:
                    sys.exit(0)
                except:
                    continue
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # check memory #
        try:
            # check activation #
            if not "mem" in SysMgr.thrTarget:
                raise Exception()

            # check available memory #
            items = (
                "available",
                "free",
                "freeDiff",
                "availableDiff",
                "anonDiff",
                "fileDiff",
                "slabDiff",
                "cmaFree",
            )
            for item in items:
                # check effective attribute #
                if not item in SysMgr.thrItemList:
                    continue

                intval = _getIntval(item)
                self.checkThreshold("mem", item, "MEM", "less", intval=intval)

            # check memory stats #
            items = (
                "anon",
                "file",
                "slab",
                "cache",
                "kernel",
                "pgRclmBg",
                "pgRclmFg",
                "oomKill",
                "pgDirty",
                "nrMinFlt",
                "pgMlock",
                "CmaFree",
                "cmaDev",
            )

            convTable = {
                "kernel": "kernelmem",
            }

            for item in items:
                # check effective attribute #
                if not item in SysMgr.thrItemList:
                    continue

                intval = _getIntval(convTable.get(item, item))
                self.checkThreshold("mem", item, "MEM", "big", intval=intval)
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # check GPU memory #
        try:
            if not "gpumem" in SysMgr.thrTarget:
                raise Exception()
            elif not self.reportData["gpumem"]:
                SysMgr.printErr(
                    "failed to check GPUMEM usage because there is no support"
                )
                if not SysMgr.forceEnable:
                    sys.exit(-1)

            self.checkThreshold("gpumem", "total", "GPUMEM", "big")
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # check swap #
        try:
            if not "swap" in SysMgr.thrTarget:
                raise Exception()
            elif self.reportData["swap"]["total"] == 0:
                SysMgr.printErr(
                    "failed to check SWAP usage "
                    "because there is no swap device"
                )
                sys.exit(-1)

            for item in (
                "usage",
                "usagePer",
                "usageDiff",
                "swapin",
                "swapout",
            ):
                # check effective attribute #
                if not item in SysMgr.thrItemList:
                    continue

                intval = _getIntval(item)
                self.checkThreshold("swap", item, "SWAP", "big", intval=intval)
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # check iowait #
        try:
            if not "block" in SysMgr.thrTarget:
                raise Exception()

            for item in ("read", "write", "ioWait", "nrMajFlt", "nrTask"):
                # check effective attribute #
                if not item in SysMgr.thrItemList:
                    continue

                intval = _getIntval(item)
                self.checkThreshold("block", item, "IO", "big", intval=intval)
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # check storage #
        try:
            # check activation #
            if not "storage" in SysMgr.thrTarget:
                raise Exception()

            items = (
                ("usagePer", "big"),
                ("free", "less"),
                ("favail", "less"),
                ("readtime", "big"),
                ("writetime", "big"),
            )

            for item, op in items:
                # check effective attribute #
                if not item in SysMgr.thrItemList:
                    continue

                # get threshold #
                vals = self.reportData["storage"]["total"]
                target = self.reportData["storage"]["total"][item]

                # get total interval #
                intval = _getIntval("total:" + item)

                self.checkThreshold(
                    "storage",
                    item,
                    "STORAGE",
                    op,
                    target,
                    intval=intval,
                    addval=vals,
                )

                # each devices #
                for dev, vals in self.reportData["storage"].items():
                    if dev == "total":
                        continue

                    # get threshold #
                    target = vals[item]
                    vals = {"dev": dev}

                    # get dev interval #
                    intval = _getIntval(dev + ":" + item)

                    try:
                        # all devices and specific device #
                        self.checkThreshold(
                            "storage",
                            item,
                            "STORAGE",
                            op,
                            target,
                            dev,
                            intval=intval,
                            addval=vals,
                        )
                    except SystemExit:
                        sys.exit(0)
                    except:
                        continue
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # check network #
        try:
            # check activation #
            if not "net" in SysMgr.thrTarget:
                raise Exception()

            # total inbound #
            for direct, name in (["inbound", "NETIN"], ["outbound", "NETOUT"]):
                # check effective attribute #
                if not direct in SysMgr.thrItemList:
                    continue

                target = self.reportData["net"][direct]
                intval = _getIntval(direct)
                self.checkThreshold(
                    "net", direct, name, "big", target, intval=intval
                )

            # each devices #
            dinfo = {}
            for dev, vals in self.reportData["net"].items():
                if dev in ("inbound", "outbound"):
                    continue
                elif not isinstance(vals, dict):
                    continue

                recv = vals["recv"]["bytes"]
                trans = vals["trans"]["bytes"]
                dinfo.update({"dev": dev})

                # check devices #
                for bound in ("recv", "trans"):
                    # check effective attribute #
                    if not bound in SysMgr.thrItemList:
                        continue

                    try:
                        # get interval #
                        name = dev + ":%s" % bound
                        intval = _getIntval(name)

                        # check threshold #
                        self.checkThreshold(
                            "net",
                            bound,
                            "NETWORK",
                            "big",
                            recv if bound == "recv" else trans,
                            dev,
                            intval=intval,
                            addval=dinfo,
                        )
                    except SystemExit:
                        sys.exit(0)
                    except:
                        pass
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # check loadavg #
        try:
            if not "load" in SysMgr.thrTarget:
                raise Exception()

            intval = _getIntval("load")

            for attr in ("load1m", "load5m", "load15m"):
                # check effective attribute #
                if not item in SysMgr.thrItemList:
                    continue

                self.checkThreshold(
                    "load",
                    attr,
                    "LOAD",
                    "big",
                    self.reportData["system"][attr],
                    intval=intval,
                )
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # check sched #
        try:
            # check activation #
            if not "task" in SysMgr.thrTarget:
                raise Exception()

            def _checkTaskStatus(stat, lev, val):
                if not isinstance(val, dict):
                    return

                if val.get("apply") != "true":
                    return

                # get abnormal condition #
                abnormalCond = (
                    val["abnormalCond"]
                    if (stat == "abnormal" and "abnormalCond" in val)
                    else None
                )

                # get the number of tasks #
                nrTask = 0
                addval = {}
                for attr in taskList:
                    comm = attr["comm"].strip("*")

                    # check task condition #
                    if not UtilMgr.isValidStr(comm, [lev]):
                        continue

                    if abnormalCond:
                        if not attr["status"] in abnormalCond:
                            continue

                    nrTask += 1

                    addval[attr["pid"]] = {"comm": comm}

                # get interval data #
                ename = stat + "-" + lev
                self.addSysInterval("task", ename, nrTask, True)
                intval = _getIntval(ename)

                self.checkThreshold(
                    "task",
                    stat,
                    target,
                    "big",
                    nrTask,
                    attr=lev,
                    intval=intval,
                    addval={"task": addval},
                )

            taskData = self.reportData["task"]

            # context switch #
            if "nrCtx" in taskData and "nrCtx" in SysMgr.thrItemList:
                target = taskData["nrCtx"]
                self.checkThreshold("task", "nrCtx", "CTXSWC", "big", target)

            # lifecycle #
            for procStat, stat in (
                ("newProcs", "new"),
                ("dieProcs", "die"),
                ("abnormalProcs", "abnormal"),
            ):
                # check effective attribute #
                if not stat in SysMgr.thrItemList:
                    continue

                if not taskData[procStat]:
                    # add empty SYSTEM interval #
                    self.addSysInterval("task", stat, 0, True)

                    # add empty TASK interval #
                    for t in SysMgr.thrData["task"]:
                        if t != "SYSTEM":
                            self.addSysInterval(
                                "task", stat + "-" + t, 0, True
                            )

                    continue

                # check SYSTEM level #
                taskList = list(taskData[procStat].values())
                nrTask = len(taskList)
                target = "I".join([v["comm"] for v in taskList])

                # get interval data #
                self.addSysInterval("task", stat, nrTask, True)
                intval = _getIntval(stat)

                # check SYSTEM level #
                self.checkThreshold(
                    "task", stat, target, "big", nrTask, intval=intval
                )

                # check TASK level #
                for lev, val in SysMgr.thrData["task"].items():
                    if lev == "SYSTEM":
                        continue

                    if isinstance(val, list):
                        for v in val:
                            _checkTaskStatus(stat, lev, v)
                    else:
                        _checkTaskStatus(stat, lev, val)

            def _checkTaskLife(t, v, commList):
                # check skip condition #
                if not isinstance(v, dict):
                    return commList
                elif not "count" in v:
                    return commList

                # get task comm list #
                if not commList:
                    commList = [
                        obj["comm"].lstrip("*")
                        for obj in self.procData.values()
                    ]

                # get valid task count #
                nrTask = [
                    1 for comm in commList if UtilMgr.isValidStr(comm, [t])
                ].count(1)

                # get interval data #
                ename = "count" + "-" + t
                self.addSysInterval("task", ename, nrTask, True)
                intval = _getIntval(ename)

                self.checkThreshold(
                    "task",
                    "count",
                    t,
                    None,
                    nrTask,
                    attr=t,
                    intval=intval,
                )

                return commList

            # lifecycle #
            commList = None
            for t, v in SysMgr.thrData["task"].items():
                if t == "SYSTEM":
                    continue

                # check task life #
                if isinstance(v, list):
                    for vv in v:
                        commList = _checkTaskLife(t, vv, commList)
                else:
                    commList = _checkTaskLife(t, v, commList)
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # check fd #
        try:
            if not "fd" in SysMgr.thrTarget:
                raise Exception()

            self.checkThreshold("fd", "curFd", "FD", "big")
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # check battery #
        try:
            if not "battery" in SysMgr.thrTarget:
                raise Exception()

            # check effective attribute #
            if "per" in SysMgr.thrItemList:
                self.checkThreshold("battery", "per", "PER", "less")
            if "plugged" in SysMgr.thrItemList:
                self.checkThreshold("battery", "plugged", "PLUG", None)
            if not "left" in SysMgr.thrItemList:
                raise Exception()

            # get threshold for remaining time #
            leftThr = SysMgr.thrData["battery"]["SYSTEM"]["left"]
            if not isinstance(leftThr, long):
                # convert HH:MM:SS type #
                leftThrNew = UtilMgr.convTime2Sec(leftThr)
                if leftThrNew:
                    leftThr = leftThrNew
                else:
                    # convert unit type #
                    leftThr = UtilMgr.convUnit2Time(leftThr, False)

                # update threshold value for left #
                SysMgr.thrData["battery"]["SYSTEM"]["left"] = leftThr

            # check threshold for remaining time #
            if leftThr:
                leftStat = UtilMgr.convTime2Sec(
                    self.reportData["battery"]["left"]
                )
                self.checkThreshold(
                    "battery", "left", "LEFT", "less", leftStat
                )
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # check socket #
        try:
            if not "sock" in SysMgr.thrTarget:
                raise Exception()

            for item in ("nrUDPSock", "nrTCPSock", "nrTCPConn", "nrUDSSock"):
                # check effective attribute #
                if not item in SysMgr.thrItemList:
                    continue

                intval = _getIntval(item)
                self.checkThreshold("sock", item, "SOCK", "big", intval=intval)
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # check PSI #
        try:
            if not "psi" in SysMgr.thrTarget:
                raise Exception()
            elif not SysMgr.psiData:
                SysMgr.printErr(
                    "failed to use PSI, please check kernel config"
                )
                if not SysMgr.forceEnable:
                    sys.exit(-1)

            resList = ["cpu", "memory", "io"]
            attrList = ["some", "full"]
            statList = ["avg10", "avg60", "avg300", "diff"]
            for res in resList:
                for attr in attrList:
                    for stat in statList:
                        try:
                            name = "%s-%s" % (attr, stat)

                            # check effective attribute #
                            if not name in SysMgr.thrItemList:
                                continue

                            target = self.reportData["psi"][res][name]
                        except SystemExit:
                            sys.exit(0)
                        except:
                            continue

                        # get interval data #
                        iname = res + "_" + name
                        self.addSysInterval("psi", iname, target, True)
                        intval = _getIntval(iname)

                        self.checkThreshold(
                            "psi",
                            name,
                            "psi",
                            "big",
                            target=target,
                            attr=res,
                            intval=intval,
                        )
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # check cgroup #
        try:
            if not "cgroup" in SysMgr.thrTarget:
                raise Exception()
            elif (
                not "cgroup" in self.reportData
                or not self.reportData["cgroup"]
            ):
                SysMgr.printErr(
                    "failed to check CGROUP usage "
                    "because there is no cgroup data"
                )
                sys.exit(-1)

            def _check(attr, name, stat):
                # skip number info for tasks #
                if name.startswith("nr"):
                    return

                item = "%s:%s" % (attr, name)
                intval = _getIntval(item)
                intval = list(map(lambda x: 0 if x == "-" else x, intval))
                self.checkThreshold(
                    "cgroup",
                    name,
                    "CGROUP",
                    "big",
                    target=stat,
                    attr=attr,
                    intval=intval,
                )

            for attr, val in sorted(self.reportData["cgroup"].items()):
                if not isinstance(val, list):
                    val = [val]

                for val in val:
                    for name, stat in val.items():
                        _check(attr, name, stat)
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # check time #
        try:
            if not "time" in SysMgr.thrTarget:
                raise Exception()

            timeSystem = SysMgr.thrData["time"].get("SYSTEM", {})
            if not "uptime" in timeSystem:
                raise Exception()

            uptime = self.reportData["system"]["uptime"]
            self.checkThreshold("time", "uptime", "TIME", target=uptime)
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # check file #
        try:
            self.checkFileThreshold()
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # check task #
        try:
            self.checkTaskThreshold()
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn("failed to check task thresholds", reason=True)

        # handle events having multicond condition #
        for en, ev in SysMgr.thrEvtCondList.items():
            items, res = ev
            if not res:
                continue

            for item in list(items.values()):
                thr, conds = item

                (
                    oneshot,
                    goneshot,
                    refresh,
                    lock,
                    maxcnt,
                    skip,
                ) = TaskAnalyzer.getThresholdAttr(thr)

                evtinfo = en.split("_", 2)
                details = "%s_%s" % (evtinfo[1], "-".join(conds))

                self.setThresholdEvent(
                    thr,
                    "multicond",
                    evtinfo[0].upper(),
                    None,
                    target="true",
                    attr=details,
                    oneshot=oneshot,
                    goneshot=goneshot,
                    refresh=refresh,
                    lock=lock,
                    maxcnt=maxcnt,
                    skip=skip,
                )

        # handle events #
        self.handleThresholdEvents()

        # update event status #
        self.updateEventStatus()

    def updateEventStatus(self):
        # check event handling tasks #
        runList = SysMgr.getChildList()
        for event, pids in UtilMgr.deepcopy(SysMgr.eventCommandList).items():
            for pid in pids:
                if pid in runList:
                    continue

                # remove pid in the event task group #
                SysMgr.eventCommandList[event].remove(pid)

                # remove event in the command group #
                if not SysMgr.eventCommandList[event]:
                    SysMgr.eventCommandList.pop(event, None)

        # update event lock #
        for event in UtilMgr.deepcopy(SysMgr.eventLockList):
            if not event in SysMgr.eventCommandList:
                SysMgr.eventLockList.pop(event, None)
                SysMgr.printWarn(
                    (
                        "released the lock of the threshold handling "
                        "caused by '%s' event"
                    )
                    % event,
                    True,
                )

    def checkMultiCond(self, flag, item, comval, event, attr):
        if item == "multicond" or comval.get("multicond") != "true":
            return False

        # get condition group #
        condgrp = comval.get("condgroup")
        if condgrp:
            ename = "_" + condgrp
        else:
            ename = "%s_%s_%s" % (event, attr, comval)

        cname = str(comval)
        now = SysMgr.thrEvtCondList.get(ename)
        if now:
            # check final status #
            if now[1] is False:
                return flag

            # register group #
            if not cname in SysMgr.thrEvtCondList[ename][0]:
                SysMgr.thrEvtCondList[ename][0][cname] = [comval, [item]]
            else:
                SysMgr.thrEvtCondList[ename][0][cname][1].append(item)
            SysMgr.thrEvtCondList[ename][1] = flag
        else:
            SysMgr.thrEvtCondList[ename] = [{cname: [comval, [item]]}, flag]

        return flag

    def setThresholdEvent(
        self,
        comval,
        item,
        event,
        comp="big",
        target=None,
        attr="SYSTEM",
        intval=None,
        addval=None,
        oneshot=False,
        goneshot=False,
        refresh=0,
        lock=False,
        maxcnt=-1,
        skip=0,
    ):
        # init value #
        value = None

        # check return condition #
        if not item in comval:
            return

        # convert threshold values #
        thresholdVal = comval[item]
        try:
            if event in ("MEM", "GPUMEM"):
                if thresholdVal.endswith("%"):
                    per = long(thresholdVal.rstrip("%")) / 100.0
                    thresholdVal = long(SysMgr.memTotal * per)
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # get comp operation #
        if "comp" in comval and comval["comp"] in ("big", "less", "eq"):
            comp = comval["comp"]

        # check threshold #
        if not comp:
            # only for same value #
            if target == thresholdVal:
                value = target
        elif intval and "interval" in comval:
            if comval["interval"] > len(intval):
                self.checkMultiCond(False, item, comval, event, attr)
                return

            # check items in intervals #
            intval = intval[-comval["interval"] :]

            # get result value #
            if "type" in comval:
                dtype = comval["type"]
                if dtype == "avg":
                    result = sum(intval) / len(intval)
                elif dtype == "min":
                    result = min(intval)
                elif dtype == "max":
                    result = max(intval)
                else:
                    result = sum(intval) / len(intval)
            else:
                result = sum(intval) / len(intval)

            cond = result
        else:
            cond = target

        # compare values #
        if comp:
            threshold = UtilMgr.convUnit2Size(thresholdVal)
            if (
                (comp == "big" and threshold <= cond)
                or (comp == "less" and threshold >= cond)
                or (comp == "eq" and threshold == cond)
            ):
                value = cond

        # check result #
        if value is None:
            self.checkMultiCond(False, item, comval, event, attr)
            return

        # check time conditions #
        for key in ("after", "before", "rafter", "rbefore"):
            try:
                # check field #
                if not key in comval:
                    continue

                ok = True

                # get threshold value #
                threshold = UtilMgr.convUnit2Time(comval[key])

                # check skip conditions #
                if key == "before":
                    if SysMgr.uptime > threshold:
                        ok = False
                elif key == "after":
                    if SysMgr.uptime < threshold:
                        ok = False
                elif key == "rbefore":
                    runtime = SysMgr.getRuntime(sec=True)
                    if runtime > threshold:
                        ok = False
                elif key == "rafter":
                    runtime = SysMgr.getRuntime(sec=True)
                    if runtime < threshold:
                        ok = False

                if not ok:
                    self.checkMultiCond(False, item, comval, event, attr)
                    return
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printWarn(
                    "failed to check '%s' condition" % key, True, True
                )

        # check multicond condition #
        if self.checkMultiCond(True, item, comval, event, attr):
            return

        # set event name #
        ename = "%s_%s_%s" % (event, attr, item)
        eventList = []

        # add task info #
        if addval:
            comval.update(addval)

        # add event info #
        if "task" in comval:
            addinfo = ""
            for pid, data in comval["task"].items():
                addinfo += "%s_%s" % (data["comm"].lstrip("*"), pid)
            ename = "%s_%s" % (ename, addinfo.lstrip("_"))
        elif "dev" in comval:
            ename = "%s_%s" % (ename, comval["dev"])

        # add rest info #
        if item in comval:
            ename = "%s_%s" % (ename, thresholdVal)

        # replace '/' with 'I' for path by event name #
        ename = ename.replace("/", "I")

        # increase event count #
        SysMgr.thrEvtCntList.setdefault(ename, 0)
        SysMgr.thrEvtCntList[ename] += 1

        # check event lock #
        if SysMgr.eventLockList:
            SysMgr.printWarn(
                "ignored '%s' event because of the lock of [%s]"
                % (ename, ", ".join(SysMgr.eventLockList.keys()))
            )
            self.checkMultiCond(False, item, comval, event, attr)
            return

        # handle goneshot flag #
        if goneshot:
            if SysMgr.thrData:
                SysMgr.prevThrData = SysMgr.thrData
            SysMgr.thrData = {}
            SysMgr.printWarn(
                (
                    "disabled the threshold monitoring "
                    "because of 'goneshot' event"
                ),
                True,
            )
            eventList.append("EVENT_GONESHOT_%s" % event)
        # update apply flag for oneshot #
        elif oneshot:
            # define function for checking resources #
            def _checkResource(item):
                if (
                    isinstance(item, dict)
                    and "apply" in item
                    and item["apply"] == "true"
                ):
                    return True

                # check type #
                if isinstance(item, list):
                    for value in item:
                        if _checkResource(value):
                            return True
                elif isinstance(item, dict):
                    for key, value in item.items():
                        if _checkResource(value):
                            return True

                return False

            # update apply flag #
            if "apply" in comval:
                comval["apply"] = "false"

            eventList.append("EVENT_ONESHOT_%s" % event)

            # update threshold items #
            if refresh:
                SysMgr.thrRefreshList.append([refresh, ename, comval])
            elif not _checkResource(SysMgr.thrData):
                if SysMgr.thrData:
                    SysMgr.prevThrData = SysMgr.thrData
                SysMgr.thrData = {}
                SysMgr.printWarn(
                    (
                        "disabled the threshold monitoring "
                        "because there is no active threshold"
                    ),
                    True,
                )

        # update maxcnt #
        if maxcnt >= 0:
            SysMgr.printWarn(
                "the remaining event count for '%s' is %s" % (ename, maxcnt),
                True,
            )

            maxcnt -= 1
            comval["max"] = maxcnt

            # disable event #
            if maxcnt < 0:
                # update apply flag #
                if "apply" in comval:
                    comval["apply"] = "false"

                if not _checkResource(SysMgr.thrData):
                    if SysMgr.thrData:
                        SysMgr.prevThrData = SysMgr.thrData
                    SysMgr.thrData = {}
                    SysMgr.printWarn(
                        (
                            "disabled the threshold monitoring "
                            "because there is no active threshold"
                        ),
                        True,
                    )

        # set lock for event #
        if lock:
            SysMgr.eventLockList.setdefault(ename, None)
            SysMgr.printWarn(
                "locked the threshold handling because of '%s' event" % ename,
                True,
            )

        # set skip count #
        if skip:
            SysMgr.skipCnt = skip
            SysMgr.printWarn(
                "start skipping %s ticks because of '%s' event"
                % (UtilMgr.convNum(skip), ename),
                True,
            )
            eventList.append("EVENT_SKIP_%s_TICK" % skip)

        # send events #
        try:
            SysMgr.sendEvents(
                SysMgr.localServObj.ip,
                SysMgr.localServObj.port,
                eventList,
                pid=SysMgr.pid,
            )
        except SystemExit:
            sys.exit(0)
        except:
            pass

        # handle oneshot command #
        if goneshot:
            run = True
        elif oneshot and ename in SysMgr.thrEvtHist:
            run = False
        else:
            run = True

        # set value for event #
        self.reportData["event"][ename] = dict(comval)
        self.reportData["event"][ename]["run"] = run
        SysMgr.thrEvtHist.setdefault(ename, None)

        return ename

    @staticmethod
    def getMemStr(
        tobj, pid, verb=True, stats=[], tok="/", unit="M", color=None
    ):
        try:
            mstat = ""
            ret = TaskAnalyzer.getMemStats(tobj, pid, verb)
            if not ret or ret["vss"] == 0:
                return mstat

            if not stats:
                stats = ("vss", "rss", "pss", "uss")

            for item in stats:
                if not item in ret:
                    continue
                size = UtilMgr.convSize2Unit(ret[item], unit=unit)
                if color:
                    size = UtilMgr.convColor(size, color)
                mstat += "%s(%s)%s" % (
                    item.upper(),
                    size,
                    tok,
                )

            return mstat.rstrip(tok)
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printWarn(
                "failed to get memory stats for %s(%s)"
                % (SysMgr.getComm(pid), pid),
                reason=True,
            )
            return mstat

    @staticmethod
    def getMemStats(tobj, pid, verb=True):
        pid = str(pid)
        comm = SysMgr.getComm(pid)

        # remove proc data #
        if pid in tobj.procData:
            tobj.procData.pop(pid, None)

        # save proc data #
        ret = tobj.saveProcData(None, pid)
        if not ret:
            if not verb:
                pass
            elif not SysMgr.isAlive(pid):
                SysMgr.printErr("%s(%s) is terminated" % (comm, pid))
            else:
                SysMgr.printErr(
                    "failed to get memory usage of %s(%s)" % (comm, pid)
                )
            return None

        # get proc data #
        procData = tobj.procData[pid]

        # save memory usage #
        if SysMgr.isRoot():
            tobj.saveProcSmaps(None, pid, mini=True, rollup=True)

            try:
                maps = procData["maps"]
            except SystemExit:
                sys.exit(0)
            except:
                return None

            memBuf, rss, pss, uss = tobj.getMemDetails(pid, maps)
            return {
                "vss": long(procData["stat"][tobj.vssIdx]),
                "rss": long(rss) << 10,
                "pss": long(pss) << 10,
                "uss": long(uss) << 10,
            }
        else:
            return {
                "vss": long(procData["stat"][tobj.vssIdx]),
                "rss": long(procData["stat"][tobj.rssIdx]) << 12,
                "pss": 0,
                "uss": 0,
            }

    @staticmethod
    def getThresholdAttr(items):
        oneshot = items.get("oneshot") == "true"

        goneshot = items.get("goneshot") == "true"

        refresh = items.get("refresh", 0)

        lock = items.get("lock") == "true"

        maxcnt = items.get("max")
        maxcnt = maxcnt if isinstance(maxcnt, long) else -1

        skip = items.get("skip")
        skip = skip if isinstance(skip, long) else 0

        return oneshot, goneshot, refresh, lock, maxcnt, skip

    def checkFileThreshold(self):
        # check threshold #
        if not SysMgr.thrData:
            return False

        # define shortcut #
        td = SysMgr.thrData

        # check attribute #
        if not "file" in td:
            return False

        for path, value in td["file"].items():
            # convert items to list #
            if isinstance(value, list):
                vlist = value
            else:
                vlist = [value]

            for value in vlist:
                # check apply #
                if value.get("apply") != "true":
                    continue

                (
                    oneshot,
                    goneshot,
                    refresh,
                    lock,
                    maxcnt,
                    skip,
                ) = TaskAnalyzer.getThresholdAttr(value)

                for attr in (
                    "exist",
                    "none",
                    "size",
                    "count",
                    "data",
                    "dataInt",
                    "dataFloat",
                ):
                    if not attr in value:
                        continue

                    # set default attr name #
                    attrname = path

                    if attr in ("exist", "none"):
                        files = UtilMgr.convPath(path)
                        if not (attr == "exist" and files) and not (
                            attr == "none" and not files
                        ):
                            continue

                        # set attributes #
                        attrname = "_".join(
                            files if attr == "exist" else [path]
                        )
                        target = "true"

                    elif attr == "size":
                        files = UtilMgr.convPath(path)
                        if files:
                            # convert size #
                            try:
                                value[attr] = UtilMgr.convUnit2Size(
                                    value[attr]
                                )
                            except:
                                sys.exit(0)

                            size = 0
                            for item in files:
                                size += UtilMgr.getFileSize(item, False)

                            # TODO: handle dir #

                        # set attributes #
                        target = size

                    elif attr == "count":
                        # check target type #
                        if not os.path.isdir(path):
                            if os.path.exists(path):
                                emsg = "is not directory"
                            else:
                                emsg = "not exists"
                            SysMgr.printWarn(
                                "'%s' %s for threshold check" % (path, emsg),
                                True,
                            )
                            continue

                        # convert count #
                        try:
                            value[attr] = long(value[attr])
                        except:
                            SysMgr.printWarn(
                                "wrong file threshold value '%s' for %s"
                                % (value[attr], attr),
                                True,
                            )
                            continue

                        # set attributes #
                        target = len(os.listdir(path))

                    elif attr in ("data", "dataInt", "dataFloat"):
                        target = SysMgr.readFile(path, verb=False)

                        try:
                            if attr == "dataInt":
                                target = long(target)
                            elif attr == "dataFloat":
                                target = float(target)
                        except SystemExit:
                            sys.exit(0)
                        except:
                            SysMgr.printWarn(
                                "failed to convert data(%s) of '%s' to '%s'"
                                % (target, path, attr),
                                reason=True,
                            )
                            continue

                    # set threshold #
                    self.setThresholdEvent(
                        value,
                        attr,
                        "FILE",
                        None,
                        target=target,
                        attr=attrname,
                        oneshot=oneshot,
                        goneshot=goneshot,
                        refresh=refresh,
                        lock=lock,
                        maxcnt=maxcnt,
                        skip=skip,
                    )

    def checkThreshold(
        self,
        resource,
        item,
        event,
        comp=None,
        target=None,
        attr="SYSTEM",
        intval=None,
        addval=None,
    ):
        # check threshold #
        if (
            not SysMgr.thrData
            or not resource in SysMgr.thrData
            or not UtilMgr.isValidStr(attr, SysMgr.thrData[resource])
        ):
            return False

        # define shortcut #
        td = SysMgr.thrData

        # get threshold attributes #
        comval = []
        if attr in td[resource]:
            comval = td[resource][attr]
        else:
            for val in td[resource]:
                if not UtilMgr.isValidStr(attr, [val]):
                    continue
                if isinstance(td[resource][val], list):
                    comval += td[resource][val]
                else:
                    comval.append(td[resource][val])
        if not comval:
            return False

        # get previous usages #
        if intval:
            pass
        elif resource in self.intervalData:
            intval = self.intervalData[resource]
        else:
            intval = []

        # get current usage #
        if target is None:
            if (
                resource == "gpumem"
                and item == "total"
                and not "total" in self.reportData[resource]
            ):
                try:
                    target = self.reportData[resource]["0"]["size"] >> 20
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printWarn(
                        "failed to check '%s' of '%s'" % (item, resource),
                        True,
                        True,
                    )
                    return False
            else:
                target = self.reportData[resource][item]

        # check conditions and trigger events #
        def _checkThreshold(cond):
            if not isinstance(cond, dict):
                return False

            def _getTarget(addval, elist):
                if "task" in addval:
                    pid = next(iter(addval["task"]))
                    etarget = addval["task"][pid]["comm"].lstrip("*")
                elif "dev" in addval:
                    etarget = addval["dev"]
                else:
                    etarget = None
                elist = elist if isinstance(elist, list) else [elist]
                return etarget, elist

            cres = True

            # check apply attribute #
            if cond.get("apply") != "true":
                cres = False
            # check exceptOR attribute #
            elif "exceptOR" in cond:
                etarget, elist = _getTarget(addval, cond["exceptOR"])
                if etarget and UtilMgr.isValidStr(etarget, elist):
                    cres = False
            # check exceptAND attribute #
            elif "exceptAND" in cond:
                etarget, elist = _getTarget(addval, cond["exceptAND"])
                if etarget and UtilMgr.isValidStrAll(etarget, elist):
                    cres = False
            # check includeOR attribute #
            elif "includeOR" in cond:
                etarget, elist = _getTarget(addval, cond["includeOR"])
                if etarget and not UtilMgr.isValidStr(etarget, elist):
                    cres = False
            # check includeAND attribute #
            elif "includeAND" in cond:
                etarget, elist = _getTarget(addval, cond["includeAND"])
                if etarget and not UtilMgr.isValidStrAll(etarget, elist):
                    cres = False

            # skip #
            if cres is False:
                self.checkMultiCond(False, item, cond, event, attr)
                return cres

            # check oneshot flag #
            (
                oneshot,
                goneshot,
                refresh,
                lock,
                maxcnt,
                skip,
            ) = TaskAnalyzer.getThresholdAttr(cond)

            # set threshold #
            self.setThresholdEvent(
                cond,
                item,
                event,
                comp,
                target,
                attr,
                intval,
                addval,
                oneshot,
                goneshot,
                refresh,
                lock,
                maxcnt,
                skip,
            )

            return True

        # check thresholds #
        if isinstance(comval, list):
            for comitem in comval:
                _checkThreshold(comitem)
        else:
            _checkThreshold(comval)

    def checkTaskThreshold(self):
        if not SysMgr.thrData:
            return
        elif not SysMgr.taskThrEnable:
            return

        # init variables #
        td = SysMgr.thrData
        tt = SysMgr.thrTarget

        # mapping table between thresholds and stats #
        maps = []
        # cpu #
        if tt.get("cpu"):
            if "total" in SysMgr.thrItemList:
                maps.append(["cpu", "total", "ttime", "cpuInt", "CPU", "big"])
            if "pri" in SysMgr.thrItemList:
                maps.append(["cpu", "pri", "pri", None, "CPU", "less"])
        # mem #
        if tt.get("mem"):
            if "rss" in SysMgr.thrItemList:
                maps.append(["mem", "rss", "rss", "rssInt", "MEM", "big"])
        # gpumem #
        if tt.get("gpumem"):
            if "size" in SysMgr.thrItemList:
                maps.append(["gpumem", "size", None, None, "GPUMEM", "big"])
        # fd #
        if tt.get("fd"):
            if "fd" in SysMgr.thrItemList:
                maps.append(["fd", "fdSize", "fdSize", None, "FD", "big"])
        # swap #
        if tt.get("swap"):
            if "swap" in SysMgr.thrItemList:
                maps.append(["swap", "swap", "swap", "swapInt", "SWAP", "big"])
        # block #
        if tt.get("block"):
            if "read" in SysMgr.thrItemList:
                maps.append(
                    ["block", "read", "read", "readInt", "BLOCK", "big"]
                )
            if "write" in SysMgr.thrItemList:
                maps.append(
                    ["block", "write", "write", "writeInt", "BLOCK", "big"]
                )
        # time #
        if tt.get("time"):
            for a, t in (("runtimeBt", "big"), ("runtimeLt", "less")):
                if not a in SysMgr.thrItemList:
                    continue
                maps.append(["time", a, "runtime", None, "RUNTIME", t])

        # check exit condition #
        if not maps:
            return

        # set task type #
        if SysMgr.processEnable:
            mode = "process"
        else:
            mode = "thread"

        # traverse all tasks #
        for pid, data in self.procData.items():
            comm = data["comm"].lstrip("*")

            # skip Guider #
            if comm == __module__:
                continue

            for item in maps:
                try:
                    res, cattr, pattr, intname, event, comp = item

                    # check skip condition #
                    if not res in td:
                        continue
                    elif pattr and not pattr in data:
                        if pattr == "pri":
                            data["pri"] = long(data["stat"][self.prioIdx])
                        else:
                            continue
                    elif res in ("mem", "gpumem", "fd"):
                        if self.isKernelThread(pid):
                            continue

                    # get stat #
                    if res == "gpumem":
                        if not pid in self.gpuMemData:
                            continue
                        else:
                            value = self.gpuMemData[pid]["size"] >> 20
                    else:
                        value = data[pattr]

                    # get interval #
                    if intname in data:
                        intval = data[intname]
                    else:
                        intval = None

                    # check interval #
                    if not value:
                        if not intval:
                            continue
                        elif set(intval) == {0}:
                            continue

                    # add task info #
                    if False and pid in SysMgr.jsonData[mode]:
                        append = {"task": {pid: SysMgr.jsonData[mode][pid]}}
                    else:
                        append = {"task": {pid: data}}

                    # check a specific task #
                    if UtilMgr.isValidStr(comm, td[res]):
                        self.checkThreshold(
                            res,
                            cattr,
                            event,
                            comp,
                            value,
                            comm,
                            intval,
                            append,
                        )
                except SystemExit:
                    sys.exit(0)
                except:
                    SysMgr.printWarn(
                        "failed to check task thresholds", reason=True
                    )

    def reportSystemStat(self):
        # initialize report event list #
        """
        CPU
        MEM
        SWAP
        IO
        STORAGE
        NETIN
        NETOUT
        """

        convTime = UtilMgr.convTime

        def _setDefaultInfo(data, pid, comm, runtime=None):
            data["pid"] = long(pid)
            data["comm"] = comm
            if runtime:
                data["runtime"] = runtime

        # add per-process stats #
        if SysMgr.rankProcEnable:
            # add CPU status #
            if "cpu" in self.reportData:
                rank = 1
                self.reportData["cpu"]["procs"] = {}

                try:
                    sortedProcData = sorted(
                        self.procData.items(),
                        key=lambda e: e[1]["ttime"],
                        reverse=True,
                    )

                    # reorder fixed tasks #
                    if self.fixedProcData:
                        sortedProcData = self.getReorderedList(sortedProcData)
                except SystemExit:
                    sys.exit(0)
                except:
                    # to handle corrupted data #
                    sortedProcData = []

                for pid, data in sortedProcData:
                    comm = data["comm"]

                    # check fixed task #
                    if self.fixedProcData and pid in self.fixedProcData:
                        pass
                    # check the number of items #
                    elif SysMgr.filterGroup:
                        # check comm #
                        if not UtilMgr.isValidStr(comm):
                            continue
                    else:
                        # check rank #
                        if rank > SysMgr.nrTopRank:
                            break
                        # check usage #
                        elif not SysMgr.showAll and data["ttime"] == 0:
                            break

                    # set default #
                    evtdata = self.reportData["cpu"]["procs"]
                    evtdata[rank] = {}
                    runtime = convTime(data["runtime"])
                    _setDefaultInfo(evtdata[rank], pid, comm, runtime)

                    # total #
                    if "ttimeDiff" in data:
                        evtdata[rank]["total"] = long(data["ttimeDiff"])
                    else:
                        evtdata[rank]["total"] = data["ttime"]

                    # user #
                    if "utimeDiff" in data:
                        evtdata[rank]["user"] = long(data["utimeDiff"])
                    else:
                        evtdata[rank]["user"] = data["utime"]

                    # system #
                    if "stimeDiff" in data:
                        evtdata[rank]["kernel"] = long(data["stimeDiff"])
                    else:
                        evtdata[rank]["kernel"] = data["stime"]

                    rank += 1

            # add memory & swap status #
            if "mem" in self.reportData:
                rank = 1
                self.reportData["mem"]["procs"] = {}

                try:
                    sortedProcData = sorted(
                        self.procData.items(),
                        key=lambda e: long(e[1]["rss"]),
                        reverse=True,
                    )

                    # reorder fixed tasks #
                    if self.fixedProcData:
                        sortedProcData = self.getReorderedList(sortedProcData)
                except SystemExit:
                    sys.exit(0)
                except:
                    # to handle corrupted data #
                    sortedProcData = []

                for pid, data in sortedProcData:
                    comm = data["comm"]

                    # check fixed task #
                    if self.fixedProcData and pid in self.fixedProcData:
                        pass
                    # check the number of items #
                    elif SysMgr.filterGroup:
                        # check comm #
                        if not UtilMgr.isValidStr(comm):
                            continue
                    else:
                        # check rank #
                        if rank > SysMgr.nrTopRank:
                            break

                    try:
                        text = (
                            long(data["stat"][self.ecodeIdx])
                            - long(data["stat"][self.scodeIdx])
                        ) >> 20
                    except SystemExit:
                        sys.exit(0)
                    except:
                        text = 0

                    # set default #
                    evtdata = self.reportData["mem"]["procs"]
                    evtdata[rank] = {}
                    runtime = convTime(data["runtime"])
                    _setDefaultInfo(evtdata[rank], pid, comm, runtime)
                    evtdata[rank]["rss"] = data["rss"]
                    evtdata[rank]["text"] = text

                    # add more stats #
                    if "pss" in data:
                        evtdata[rank]["pss"] = data["pss"]
                    if "uss" in data:
                        evtdata[rank]["uss"] = data["uss"]

                    # save status info #
                    path = "%s/%s" % (SysMgr.procPath, pid)
                    self.saveProcStatusData(path, pid)

                    # swap #
                    try:
                        if "swap" in data:
                            swap = data["swap"]
                        else:
                            swap = long(
                                data["status"]["VmSwap"].split(None, 1)[0]
                            )
                            swap = swap >> 10

                        evtdata[rank]["swap"] = swap
                        self.reportData["mem"]["procs"][pid]["swap"] = swap
                    except:
                        pass

                    # shared #
                    try:
                        shm = long(data["statm"][self.shrIdx]) >> 8
                        evtdata[rank]["shared"] = shm
                        self.reportData["mem"]["procs"][pid]["shared"] = shm
                    except:
                        pass

                    rank += 1

            # add block status #
            if "block" in self.reportData:
                rank = 1
                self.reportData["block"]["procs"] = {}

                try:
                    if not SysMgr.blockEnable:
                        raise Exception("no block")

                    sortedProcData = sorted(
                        self.procData.items(),
                        key=lambda e: e[1]["rw"],
                        reverse=True,
                    )

                    # reorder fixed tasks #
                    if self.fixedProcData:
                        sortedProcData = self.getReorderedList(sortedProcData)
                except SystemExit:
                    sys.exit(0)
                except:
                    # to handle corrupted data #
                    sortedProcData = []

                for pid, data in sortedProcData:
                    comm = data["comm"]

                    # check fixed task #
                    if self.fixedProcData and pid in self.fixedProcData:
                        pass
                    # check the number of items #
                    elif SysMgr.filterGroup:
                        # check comm #
                        if not UtilMgr.isValidStr(comm):
                            continue
                    else:
                        # check total I/O size #
                        if data["rw"] == 0:
                            break

                    # set default #
                    evtdata = self.reportData["block"]["procs"]
                    evtdata[rank] = {}
                    runtime = convTime(data["runtime"])
                    _setDefaultInfo(evtdata[rank], pid, comm, runtime)
                    evtdata[rank]["iowait"] = data["btime"]
                    evtdata[rank]["read"] = data["read"] >> 20
                    evtdata[rank]["write"] = data["write"] >> 20

                    rank += 1

            # add gpumem status #
            if "gpumem" in self.reportData:
                rank = 1
                procs = {}

                try:
                    sortedProcData = sorted(
                        self.reportData["gpumem"].items(),
                        key=lambda e: e[1]["size"],
                        reverse=True,
                    )

                    # reorder fixed tasks #
                    if self.fixedProcData:
                        sortedProcData = self.getReorderedList(sortedProcData)
                except SystemExit:
                    sys.exit(0)
                except:
                    # to handle corrupted data #
                    sortedProcData = []

                total = 0
                for pid, data in sortedProcData:
                    if pid == "0":
                        total = data["size"] >> 20
                        continue

                    comm = data["comm"]

                    # check fixed task #
                    if self.fixedProcData and pid in self.fixedProcData:
                        pass
                    # check the number of items #
                    elif SysMgr.filterGroup:
                        # check comm #
                        if not UtilMgr.isValidStr(comm):
                            continue
                    else:
                        # check size #
                        if data["size"] == 0:
                            break

                    procs[rank] = {}
                    _setDefaultInfo(procs[rank], pid, comm)
                    procs[rank]["size"] = data["size"] >> 20

                    rank += 1

                # update gpumem #
                self.reportData["gpumem"] = {"total": total, "procs": procs}

        # remove not-mounted storage status #
        for item in list(self.reportData["storage"]):
            try:
                if self.reportData["storage"][item]["mount"]["path"] == "-":
                    self.reportData["storage"].pop(item, None)
            except SystemExit:
                sys.exit(0)
            except:
                pass

        # add task status #
        if "task" in self.reportData:
            items = ["abnormalProcs", "newProcs", "dieProcs"]
            for item in items:
                self.reportData["task"][item] = {}

                # check item #
                if item == "abnormalProcs":
                    tasks = set(self.abnormalTasks)
                elif item == "newProcs":
                    tasks = set(self.procData) - set(self.prevProcData)
                elif item == "dieProcs":
                    tasks = set(self.prevProcData) - set(self.procData)
                else:
                    tasks = []

                # save status #
                rank = 1
                for pid in tasks:
                    if item == "dieProcs":
                        procData = self.prevProcData
                    else:
                        procData = self.procData

                    # check pid #
                    if not pid in procData:
                        continue

                    # set default #
                    evtdata = self.reportData["task"][item]
                    evtdata[rank] = {}

                    comm = procData[pid]["comm"]
                    runtime = convTime(procData[pid]["runtime"])
                    _setDefaultInfo(evtdata[rank], pid, comm, runtime)

                    status = procData[pid]["stat"][self.statIdx]
                    evtdata[rank]["status"] = status

                    rank += 1

        # add PSI data #
        if SysMgr.psiData:
            self.reportData.setdefault("psi", {})
            for res, data in SysMgr.psiData.items():
                self.reportData["psi"].setdefault(res, {})
                for attr, vals in data.items():
                    for stat, val in vals.items():
                        pstat = "%s-%s" % (attr, stat)
                        self.reportData["psi"][res][pstat] = val

        # add file data #
        for path, data in TaskAnalyzer.fileIntData.items():
            if not data and not path in TaskAnalyzer.dirLastData:
                continue

            self.reportData.setdefault("file", {})

            # dir #
            if path in TaskAnalyzer.dirLastData:
                lastData = UtilMgr.lstrip(
                    TaskAnalyzer.dirLastData[path], SysMgr.magicStr
                ).split("|")
            # file #
            else:
                lastData = sorted(data.items(), key=lambda e: float(e[0]))[-1][
                    1
                ]

            self.reportData["file"][path] = {
                "data": lastData,
                "size": len(lastData),
            }

        # add lmk data #
        if "lmk" in SysMgr.jsonData:
            self.reportData["lmk"] = SysMgr.jsonData["lmk"]

        # check resource threshold #
        self.checkResourceThreshold()

        # legacy: print system status to file if condition is met #
        if (
            self.reportData.get("event")
            and SysMgr.reportFileEnable
            and SysMgr.outPath
        ):
            # submit summarized report and details #
            TaskAnalyzer.printIntervalUsage()

            # sync and close output file #
            SysMgr.closePrintFd()

            # make output path #
            filePath = os.path.dirname(SysMgr.inputFile) + "/guider"
            for event in self.reportData["event"]:
                filePath = "%s_%s" % (filePath, event)
            filePath = "%s_%s.out" % (filePath, str(long(SysMgr.uptime)))

            # print output info #
            try:
                # rename output file #
                os.rename(SysMgr.inputFile, filePath)

                # get output size #
                fsize = UtilMgr.getFileSizeStr(filePath)

                SysMgr.printStat(
                    (
                        "saved the results based monitoring into "
                        "'%s'%s successfully"
                    )
                    % (filePath, fsize)
                )
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printWarn(
                    "failed to rename %s to %s" % SysMgr.inputFile, filePath
                )

        # remove useless info for file report #
        if "NOSTORAGEREP" in SysMgr.environList:
            self.reportData.pop("storage")
        if "NOGPUMEMREP" in SysMgr.environList:
            self.reportData.pop("gpumem")

        # convert dict data to JSON string #
        pretty = not SysMgr.streamEnable
        jsonStr = UtilMgr.convDict2Str(
            self.reportData, pretty=pretty, ignore=True
        )
        if not jsonStr:
            SysMgr.printWarn("failed to convert report data to JSON format")
            return

        # transfer data to file or socket #
        self.tranData(jsonStr)

    def reportSystemStatElastic(self):
        """
        make data fields as the below list
        - metricset fields
        - beat fields (metricbeat, filebeat, guider, etc...)
        - system fields (cpu, process, memory, diskio, etc...)
        """

        reportElasticData = ""

        metricsetFields = {"metricset": {"module": "system", "name": ""}}

        # set beatstart flag for syncing timestamp
        if hasattr(self, "beatStart"):
            self.beatStart = False
        else:
            self.beatStart = True

        beatFields = {
            "beat": {
                "name": __module__,
                "hostname": SysMgr.localServObj.ip,
                "version": __version__,
                "beatstart": self.beatStart,
            }
        }

        # generate CPU status data #
        metricsetFields["metricset"]["name"] = "cpu"

        cpuData = self.reportData["cpu"]

        systemCpuFields = {
            "system": {
                "cpu": {
                    "total": {"pct": cpuData["total"]},
                    "idle": {"pct": cpuData["idle"]},
                    "user": {"pct": cpuData["user"]},
                    "kernel": {"pct": cpuData["kernel"]},
                    "irq": {"pct": cpuData["irq"]},
                    "iowait": {"pct": cpuData["iowait"]},
                    "cores": cpuData["nrCore"],
                }
            }
        }

        # merge CPU data dictionary #
        reportCpuData = metricsetFields.copy()
        reportCpuData.update(beatFields)
        reportCpuData.update(systemCpuFields)

        jstr = UtilMgr.convDict2Str(reportCpuData)
        if jstr:
            reportElasticData += jstr

        # generate memory status data #
        metricsetFields["metricset"]["name"] = "memory"

        memData = self.reportData["mem"]
        swapData = self.reportData["swap"]

        systemMemoryFields = {
            "system": {
                "memory": {
                    "total": memData["total"],
                    "free": memData["free"],
                    "available": memData["available"],
                    "anon": memData["anon"],
                    "file": memData["file"],
                    "slab": memData["slab"],
                    "swap": {
                        "total": swapData["total"],
                        "used": swapData["usage"],
                    },
                }
            }
        }

        # merge momory data dictionary #
        reportMemoryData = metricsetFields.copy()
        reportMemoryData.update(beatFields)
        reportMemoryData.update(systemMemoryFields)

        jstr = UtilMgr.convDict2Str(reportMemoryData)
        if jstr:
            reportElasticData += jstr

        # generate network status data #
        metricsetFields["metricset"]["name"] = "network"

        networkData = self.reportData["net"]

        systemNetworkFields = {
            "system": {
                "network": {
                    "in": {"byte": networkData["inbound"]},
                    "out": {"byte": networkData["outbound"]},
                }
            }
        }

        # merge network data dictionary #
        reportNetworkData = metricsetFields.copy()
        reportNetworkData.update(beatFields)
        reportNetworkData.update(systemNetworkFields)

        jstr = UtilMgr.convDict2Str(reportNetworkData)
        if jstr:
            reportElasticData += jstr

        # generate network status data #
        metricsetFields["metricset"]["name"] = "diskio"

        systemDiskioFields = {
            "system": {
                "diskio": {
                    "name": "",
                    "read": {"bytes": 0},
                    "write": {"bytes": 0},
                    "used": {"pct": 0},
                }
            }
        }

        diskioData = systemDiskioFields["system"]["diskio"]

        # get read/write bytes on each devices #
        for dev, value in sorted(self.reportData["storage"].items()):
            diskioData["name"] = dev
            diskioData["read"]["bytes"] = value["read"]
            diskioData["write"]["bytes"] = value["read"]
            diskioData["used"]["pct"] = value["usagePer"]

            # merge diskio data dictionary #
            reportDiskioData = metricsetFields.copy()
            reportDiskioData.update(beatFields)
            reportDiskioData.update(systemDiskioFields)

            jstr = UtilMgr.convDict2Str(reportDiskioData)
            if jstr:
                reportElasticData += jstr

        # generate process status data #
        metricsetFields["metricset"]["name"] = "process"

        systemProcessFields = {
            "system": {
                "process": {
                    "name": "",
                    "state": "",
                    "pid": 0,
                    "cpu": {
                        "user": {"pct": 0},
                        "kernel": {"pct": 0},
                        "total": {"pct": 0},
                        "runtime": "",
                    },
                    "memory": {"rss": {"bytes": 0}, "text": 0},
                }
            }
        }

        sortedProcData = sorted(
            self.procData.items(), key=lambda e: e[1]["ttime"], reverse=True
        )

        processData = systemProcessFields["system"]["process"]

        for pid, data in sortedProcData:
            if not (SysMgr.showAll or data["ttime"] > 0):
                break

            processData["pid"] = long(pid)
            processData["name"] = data["comm"]
            processData["cpu"]["user"]["pct"] = data["utime"]
            processData["cpu"]["kernel"]["pct"] = data["stime"]
            processData["cpu"]["total"]["pct"] = data["ttime"]
            processData["cpu"]["runtime"] = UtilMgr.convTime(data["runtime"])

            rss = long(data["stat"][self.rssIdx]) >> 8

            text = (
                long(data["stat"][self.ecodeIdx])
                - long(data["stat"][self.scodeIdx])
            ) >> 20

            processData["memory"]["rss"]["bytes"] = rss
            processData["memory"]["text"] = text

            # merge process data dictionary #
            reportProcessData = metricsetFields.copy()
            reportProcessData.update(beatFields)
            reportProcessData.update(systemProcessFields)

            jstr = UtilMgr.convDict2Str(reportProcessData)
            if jstr:
                reportElasticData += jstr

        # transfer data to file or socket #
        self.tranData(reportElasticData)

    def tranData(self, data):
        # report to the file #
        if SysMgr.reportObject:
            UtilMgr.writeJsonObject(
                data, fd=SysMgr.reportObject, trunc=SysMgr.truncEnable
            )

        # report to sockets #
        addrlist = dict(SysMgr.addrListForReport)
        for addr, cli in addrlist.items():
            if cli.request != "REPORT":
                continue
            elif cli.status == "SENT" and cli.ignore > 1:
                SysMgr.printInfo(
                    "unregistered %s:%d for REPORT" % (cli.ip, cli.port)
                )
                del SysMgr.addrListForReport[addr]
            else:
                ret = cli.send(data)
                if not ret:
                    del SysMgr.addrListForReport[addr]
                else:
                    cli.ignore += 1

    def printSimpleStat(self):
        pass

    def checkPrintCond(self):
        try:
            cond = SysMgr.printCond
            return (
                cond["CPUCOND"] < self.reportData["cpu"]["total"]
                and cond["MEMFREECOND"] > self.reportData["mem"]["free"]
                and cond["MEMAVLCOND"] > self.reportData["mem"]["available"]
                and cond["BLKRDCOND"] < self.reportData["block"]["read"]
                and cond["BLKWRCOND"] < self.reportData["block"]["write"]
            )
        except:
            return True

    def printSystemStatGen(self, idIndex=False, target="task"):
        title = "[Top Info]"

        if not self.taskStreamEnable:
            # print default stats #
            self.printDefaultUsage(title)

            # print system stat #
            self.printSystemUsage()

            # print disk stat #
            self.printDiskUsage()

            # print network stat #
            self.printNetworkUsage()

        # print process stat #
        self.printTaskUsageGen(idIndex=idIndex)

        # flush print buffer #
        if self.reportData:
            if self.checkPrintCond():
                SysMgr.printTopStats()
            else:
                SysMgr.clearPrint()
        else:
            SysMgr.printTopStats()

    def checkTermCond(self):
        def _searchTask(self, termCond, taskList, msg):
            for pid in taskList:
                if pid in self.prevProcData:
                    comm = self.prevProcData[pid]["comm"].lstrip("*")
                elif pid in self.procData:
                    comm = self.procData[pid]["comm"].lstrip("*")
                else:
                    comm = ""

                # check termination condition #
                if pid in termCond or (
                    comm and UtilMgr.isValidStr(comm, termCond)
                ):
                    SysMgr.printInfo("%s(%s) is %s" % (comm, pid, msg))
                    sys.exit(0)

        # terminated tasks #
        termCond = SysMgr.environList.get("EXITCONDTERM")
        if termCond:
            taskList = set(self.prevProcData) - set(self.procData)
            msg = "terminated"
            _searchTask(self, termCond, taskList, msg)

        # new tasks #
        termCond = SysMgr.environList.get("EXITCONDNEW")
        if termCond:
            taskList = set(self.procData) - set(self.prevProcData)
            msg = "executed"
            _searchTask(self, termCond, taskList, msg)

        # new files #
        termCond = SysMgr.environList.get("EXITCONDFILE", [])
        for fpath in termCond:
            fpath = SysMgr.convFullPath(fpath)
            if os.path.isfile(fpath):
                SysMgr.printInfo("'%s' is found" % fpath)
                sys.exit(0)

        # removed files #
        termCond = SysMgr.environList.get("EXITCONDNOFILE", [])
        for fpath in termCond:
            fpath = SysMgr.convFullPath(fpath)
            if not os.path.isfile(fpath):
                SysMgr.printInfo("no '%s'" % fpath)
                sys.exit(0)

        # check functions #
        termCond = SysMgr.environList.get("EXITCONDFUNC", [])
        for item in termCond:
            # get function info #
            try:
                values = item.split(":")
                path, func = values[:2]
                if len(values) > 2:
                    args = values[2:]
                else:
                    args = []
            except SystemExit:
                sys.exit(0)
            except:
                SysMgr.printErr(
                    "failed to parse function info from '%s'" % item, True
                )
                sys.exit(0)

            # call function #
            ret = UtilMgr.callPyFunc(path, func, args)
            if ret:
                SysMgr.printInfo(
                    "%s(%s) in %s returned %s" % (func, args, path, ret)
                )
                sys.exit(0)

    def printSystemStat(self, idIndex=False, targetList=["task"]):
        title = "[Top Info]"
        nrIndent = len(title)

        if not self.taskStreamEnable:
            # print default stats #
            self.printDefaultUsage(title)

            # print memory stats #
            self.printMemoryUsage(nrIndent)

            # print slab stats #
            self.printSlabUsage(nrIndent)

            # print buddy stats #
            self.printBuddyUsage(nrIndent)

            # print KSM stats #
            self.printKSMStat(nrIndent)

            # print vm stats #
            self.printVmInfo(nrIndent)

            # print LMK stats #
            self.printLMKStat(nrIndent)

            # print zone stats #
            self.printZoneUsage(nrIndent)

            # print dmabuf stats #
            self.printDmabufUsage(nrIndent)

            # print irq stats #
            self.printIrqUsage(nrIndent)

            # print PMU stat #
            self.printPerfUsage(nrIndent)

            # print PSI stat #
            self.printPsiStat(nrIndent)

            # print peak stat #
            self.printPeakStat(nrIndent)

            # print system stat #
            self.printSystemUsage()

            # print disk stat #
            self.printDiskUsage()

            # print network stat #
            self.printNetworkUsage()

        # print stats #
        for item in sorted(targetList):
            if "cgroup" == item:
                self.printCgroupUsage(alone=len(targetList) == 1)
            elif "task" == item:
                self.printTaskUsage(idIndex=idIndex)
            else:
                SysMgr.printErr("wrong monitor target for '%s'" % item)
                sys.exit(-1)

        # update session #
        SysMgr.updateSession()

        # flush print buffer #
        if self.reportData:
            if self.checkPrintCond():
                SysMgr.printTopStats()
            else:
                SysMgr.clearPrint()
        else:
            SysMgr.printTopStats()


def decoratorFunc(origFunc):
    def wrapper(*args, **kwargs):
        return origFunc(*args, **kwargs)

    return wrapper


def main(args=None):
    # update arguments #
    if UtilMgr.isString(args):
        sys.argv = [__module__] + args.split()
    elif isinstance(args, list) or isinstance(args, tuple):
        if sys.version_info < (3, 0, 0):
            args = list(map(lambda x: x.encode(), args))
        sys.argv = [__module__] + list(args)

    # init environment #
    SysMgr.initEnvironment()

    # launch commands #
    if not SysMgr.isRecordMode():
        SysMgr.checkCmdMode()

    # init system context #
    SysMgr.initSystemContext()

    # ==================== RECORD PART ====================#

    if SysMgr.isRecordMode():
        SysMgr.setRecordAttr()

        # wait for input #
        if SysMgr.waitEnable:
            SysMgr.waitUserInput(SysMgr.waitEnable, msg="DEFAULT", force=True)

        # set normal signal #
        SysMgr.setNormalSignal()

        # SYSTEM MODE #
        if SysMgr.isSystemMode():
            SysMgr.execSystemView()
            sys.exit(0)
        # FILE MODE #
        elif SysMgr.isFileMode():
            SysMgr.execFileAnalysis()
            sys.exit(0)

        # register exit handler #
        atexit.register(SysMgr.stopRecording)

        # start recording #
        SysMgr.sysInstance.startRecording()

        # THREAD & FUNCTION MODE #
        try:
            SysMgr.execRecordLoop()
        except SystemExit:
            sys.exit(0)
        except:
            SysMgr.printErr("terminated recording", reason=True)

    # ==================== ANALYSIS PART ====================#

    # register exit handler #
    atexit.register(SysMgr.doExit)

    # REPORT MODE #
    if SysMgr.checkMode("report"):
        SysMgr.setReportAttr()
    # VISUAL MODE #
    elif SysMgr.isDrawMode():
        SysMgr.setVisualAttr()

    # parse analysis option #
    SysMgr.parseAnalOption()

    # REALTIME MODE #
    if SysMgr.isTopMode():
        SysMgr.execTopCmd()
        sys.exit(0)
    # FUNCTION GRAPH MODE #
    elif SysMgr.graphEnable and SysMgr.isRecordMode() and SysMgr.isFuncMode():
        FunctionAnalyzer(SysMgr.inputFile)

    # set handler for exit #
    signal.signal(signal.SIGINT, SysMgr.exitHandler)

    # check log file is recoginizable #
    TaskAnalyzer.getInitTime(SysMgr.inputFile)

    # apply launch option from data file #
    if not SysMgr.isRecordMode():
        SysMgr.applyLaunchOption()

    # get mount info from file #
    SysMgr.getMountInfo()

    # print analysis option #
    SysMgr.printAnalOption()

    # FUNCTION MODE #
    if SysMgr.isFuncMode():
        FunctionAnalyzer(SysMgr.inputFile).printUsage()
    # DRAW MODE #
    elif SysMgr.isDrawMode():
        TaskAnalyzer.drawTimelineItems()
    # THREAD MODE #
    else:
        TaskAnalyzer(SysMgr.inputFile).printUsage()

    # print event info #
    EventAnalyzer.printEventInfo()


# define line variables #
oneLine = "-" * SysMgr.lineLength
twoLine = "=" * SysMgr.lineLength
splitLine = ">" * SysMgr.lineLength
underLine = "_" * SysMgr.lineLength
dotLine = "." * SysMgr.lineLength


# define print method for debugging #
@staticmethod
def dbgp(msg):
    SysMgr.printWarn(msg, True)


# main #
if __name__ == "__main__":
    # set main environment #
    os.environ["ISMAIN"] = "True"

    main(args=None)
