////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2001-2026 Alexey Kuryakin daqgroup@mail.ru under MIT license //
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// This file is part of the CRW-DAQ project by DaqGroup - component CRWLIB.   //
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Purpose:                                                                   //
// This unit implement procps - functions to read/parse /proc/pid/stat.       //
// Uses some ideas derived from procps and busybox program codes.             //
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// History:                                                                   //
// 20250206 - Created by A.K.                                                 //
// 20260227 - pps_IsStateValid,pps_ValidateState                              //
////////////////////////////////////////////////////////////////////////////////

unit _crw_procps; //  procps - read & parse /proc/pid/stat.

{$I _crw_sysdef.inc}

{$I _crw_sysmode.inc}

interface

uses
 //////////////////////////////////////////////////////
 {$I _crw_uses_first.inc} // NB: MUST BE FIRST USES !!!
 //////////////////////////////////////////////////////
 sysutils, classes, math, ctypes,
 _crw_alloc;

 {
 ###############################################################################
 https://man7.org/linux/man-pages/man5/proc_pid_status.5.html
 ###############################################################################
 proc_pid_status(5)         File Formats Manual         proc_pid_status(5)

 NAME

        /proc/pid/status - memory usage and status information

 DESCRIPTION

        /proc/pid/status
               Provides much of the information in /proc/pid/stat and
               /proc/pid/statm in a format that's easier for humans to
               parse.  Here's an example:

                   $ cat /proc/$$/status
                   Name:   bash
                   Umask:  0022
                   State:  S (sleeping)
                   Tgid:   17248
                   Ngid:   0
                   Pid:    17248
                   PPid:   17200
                   TracerPid:      0
                   Uid:    1000    1000    1000    1000
                   Gid:    100     100     100     100
                   FDSize: 256
                   Groups: 16 33 100
                   NStgid: 17248
                   NSpid:  17248
                   NSpgid: 17248
                   NSsid:  17200
                   VmPeak:     131168 kB
                   VmSize:     131168 kB
                   VmLck:           0 kB
                   VmPin:           0 kB
                   VmHWM:       13484 kB
                   VmRSS:       13484 kB
                   RssAnon:     10264 kB
                   RssFile:      3220 kB
                   RssShmem:        0 kB
                   VmData:      10332 kB
                   VmStk:         136 kB
                   VmExe:         992 kB
                   VmLib:        2104 kB
                   VmPTE:          76 kB
                   VmPMD:          12 kB
                   VmSwap:          0 kB
                   HugetlbPages:          0 kB        # 4.4
                   CoreDumping:   0                       # 4.15
                   Threads:        1
                   SigQ:   0/3067
                   SigPnd: 0000000000000000
                   ShdPnd: 0000000000000000
                   SigBlk: 0000000000010000
                   SigIgn: 0000000000384004
                   SigCgt: 000000004b813efb
                   CapInh: 0000000000000000
                   CapPrm: 0000000000000000
                   CapEff: 0000000000000000
                   CapBnd: ffffffffffffffff
                   CapAmb:   0000000000000000
                   NoNewPrivs:     0
                   Seccomp:        0
                   Seccomp_filters:        0
                   Speculation_Store_Bypass:       vulnerable
                   Cpus_allowed:   00000001
                   Cpus_allowed_list:      0
                   Mems_allowed:   1
                   Mems_allowed_list:      0
                   voluntary_ctxt_switches:        150
                   nonvoluntary_ctxt_switches:     545

               The fields are as follows:

               Name   Command run by this process.  Strings longer than
                      TASK_COMM_LEN (16) characters (including the
                      terminating null byte) are silently truncated.

               Umask  Process umask, expressed in octal with a leading
                      zero; see umask(2).  (Since Linux 4.7.)

               State  Current state of the process.  One of "R (running)",
                      "S (sleeping)", "D (disk sleep)", "T (stopped)", "t
                      (tracing stop)", "Z (zombie)", or "X (dead)".

               Tgid   Thread group ID (i.e., Process ID).

               Ngid   NUMA group ID (0 if none; since Linux 3.13).

               Pid    Thread ID (see gettid(2)).

               PPid   PID of parent process.

               TracerPid
                      PID of process tracing this process (0 if not being
                      traced).

               Uid
               Gid    Real, effective, saved set, and filesystem UIDs
                      (GIDs).

               FDSize Number of file descriptor slots currently allocated.

               Groups Supplementary group list.

               NStgid Thread group ID (i.e., PID) in each of the PID
                      namespaces of which pid is a member.  The leftmost
                      entry shows the value with respect to the PID
                      namespace of the process that mounted this procfs
                      (or the root namespace if mounted by the kernel),
                      followed by the value in successively nested inner
                      namespaces.  (Since Linux 4.1.)

               NSpid  Thread ID in each of the PID namespaces of which pid
                      is a member.  The fields are ordered as for NStgid.
                      (Since Linux 4.1.)

               NSpgid Process group ID in each of the PID namespaces of
                      which pid is a member.  The fields are ordered as
                      for NStgid.  (Since Linux 4.1.)

               NSsid  descendant namespace session ID hierarchy Session ID
                      in each of the PID namespaces of which pid is a
                      member.  The fields are ordered as for NStgid.
                      (Since Linux 4.1.)

               VmPeak Peak virtual memory size.

               VmSize Virtual memory size.

               VmLck  Locked memory size (see mlock(2)).

               VmPin  Pinned memory size (since Linux 3.2).  These are
                      pages that can't be moved because something needs to
                      directly access physical memory.

               VmHWM  Peak resident set size ("high water mark").  This
                      value is inaccurate; see /proc/pid/statm above.

               VmRSS  Resident set size.  Note that the value here is the
                      sum of RssAnon, RssFile, and RssShmem.  This value
                      is inaccurate; see /proc/pid/statm above.

               RssAnon
                      Size of resident anonymous memory.  (since Linux
                      4.5).  This value is inaccurate; see /proc/pid/statm
                      above.

               RssFile
                      Size of resident file mappings.  (since Linux 4.5).
                      This value is inaccurate; see /proc/pid/statm above.

               RssShmem
                      Size of resident shared memory (includes System V
                      shared memory, mappings from tmpfs(5), and shared
                      anonymous mappings).  (since Linux 4.5).

               VmData
               VmStk
               VmExe  Size of data, stack, and text segments.  This value
                      is inaccurate; see /proc/pid/statm above.

               VmLib  Shared library code size.

               VmPTE  Page table entries size (since Linux 2.6.10).

               VmPMD  Size of second-level page tables (added in Linux
                      4.0; removed in Linux 4.15).

               VmSwap Swapped-out virtual memory size by anonymous private
                      pages; shmem swap usage is not included (since Linux
                      2.6.34).  This value is inaccurate; see
                      /proc/pid/statm above.

               HugetlbPages
                      Size of hugetlb memory portions (since Linux 4.4).

               CoreDumping
                      Contains the value 1 if the process is currently
                      dumping core, and 0 if it is not (since Linux 4.15).
                      This information can be used by a monitoring process
                      to avoid killing a process that is currently dumping
                      core, which could result in a corrupted core dump
                      file.

               Threads
                      Number of threads in process containing this thread.

               SigQ   This field contains two slash-separated numbers that
                      relate to queued signals for the real user ID of
                      this process.  The first of these is the number of
                      currently queued signals for this real user ID, and
                      the second is the resource limit on the number of
                      queued signals for this process (see the description
                      of RLIMIT_SIGPENDING in getrlimit(2)).

               SigPnd
               ShdPnd Mask (expressed in hexadecimal) of signals pending
                      for thread and for process as a whole (see
                      pthreads(7) and signal(7)).

               SigBlk
               SigIgn
               SigCgt Masks (expressed in hexadecimal) indicating signals
                      being blocked, ignored, and caught (see signal(7)).

               CapInh
               CapPrm
               CapEff Masks (expressed in hexadecimal) of capabilities
                      enabled in inheritable, permitted, and effective
                      sets (see capabilities(7)).

               CapBnd Capability bounding set, expressed in hexadecimal
                      (since Linux 2.6.26, see capabilities(7)).

               CapAmb Ambient capability set, expressed in hexadecimal
                      (since Linux 4.3, see capabilities(7)).

               NoNewPrivs
                      Value of the no_new_privs bit (since Linux 4.10, see
                      prctl(2)).

               Seccomp
                      Seccomp mode of the process (since Linux 3.8, see
                      seccomp(2)).  0 means SECCOMP_MODE_DISABLED; 1 means
                      SECCOMP_MODE_STRICT; 2 means SECCOMP_MODE_FILTER.
                      This field is provided only if the kernel was built
                      with the CONFIG_SECCOMP kernel configuration option
                      enabled.

               Seccomp_filters
                      Number of seccomp filters attached to the process
                      (since Linux 5.9, see seccomp(2)).

               Speculation_Store_Bypass
                      Speculation flaw mitigation state (since Linux 4.17,
                      see prctl(2)).

               Cpus_allowed
                      Hexadecimal mask of CPUs on which this process may
                      run (since Linux 2.6.24, see cpuset(7)).

               Cpus_allowed_list
                      Same as previous, but in "list format" (since Linux
                      2.6.26, see cpuset(7)).

               Mems_allowed
                      Mask of memory nodes allowed to this process (since
                      Linux 2.6.24, see cpuset(7)).

               Mems_allowed_list
                      Same as previous, but in "list format" (since Linux
                      2.6.26, see cpuset(7)).

               voluntary_ctxt_switches
               nonvoluntary_ctxt_switches
                      Number of voluntary and involuntary context switches
                      (since Linux 2.6.23).

 SEE ALSO

        proc(5)

 COLOPHON

        This page is part of the man-pages (Linux kernel and C library
        user-space interface documentation) project.  Information about
        the project can be found at
        ⟨https://www.kernel.org/doc/man-pages/⟩.  If you have a bug report
        for this manual page, see
        ⟨https://git.kernel.org/pub/scm/docs/man-pages/man-pages.git/tree/CONTRIBUTING⟩.
        This page was obtained from the tarball man-pages-6.10.tar.gz
        fetched from
        ⟨https://mirrors.edge.kernel.org/pub/linux/docs/man-pages/⟩ on
        2025-02-02.  If you discover any rendering problems in this HTML
        version of the page, or you believe there is a better or more up-
        to-date source for the page, or you have corrections or
        improvements to the information in this COLOPHON (which is not
        part of the original manual page), send a mail to
        man-pages@man7.org

 Linux man-pages 6.10            2024-05-02             proc_pid_status(5)
 ###############################################################################
 }
 {
 ###############################################################################
 https://man7.org/linux/man-pages/man5/proc_pid_stat.5.html
 ###############################################################################
 proc_pid_stat(5)           File Formats Manual           proc_pid_stat(5)

 NAME

        /proc/pid/stat - status information

 DESCRIPTION

        /proc/pid/stat
               Status information about the process.  This is used by
               ps(1).  It is defined in the kernel source file
               fs/proc/array.c.

               The fields, in order, with their proper scanf(3) format
               specifiers, are listed below.  Whether or not certain of
               these fields display valid information is governed by a
               ptrace access mode PTRACE_MODE_READ_FSCREDS |
               PTRACE_MODE_NOAUDIT check (refer to ptrace(2)).  If the
               check denies access, then the field value is displayed as
               0.  The affected fields are indicated with the marking
               [PT].

               (1) pid  %d
                      The process ID.

               (2) comm  %s
                      The filename of the executable, in parentheses.
                      Strings longer than TASK_COMM_LEN (16) characters
                      (including the terminating null byte) are silently
                      truncated.  This is visible whether or not the
                      executable is swapped out.

               (3) state  %c
                      One of the following characters, indicating process
                      state:

                      R      Running

                      S      Sleeping in an interruptible wait

                      D      Waiting in uninterruptible disk sleep

                      Z      Zombie

                      T      Stopped (on a signal) or (before Linux
                             2.6.33) trace stopped

                      t      Tracing stop (Linux 2.6.33 onward)

                      W      Paging (only before Linux 2.6.0)

                      X      Dead (from Linux 2.6.0 onward)

                      x      Dead (Linux 2.6.33 to 3.13 only)

                      K      Wakekill (Linux 2.6.33 to 3.13 only)

                      W      Waking (Linux 2.6.33 to 3.13 only)

                      P      Parked (Linux 3.9 to 3.13 only)

                      I      Idle (Linux 4.14 onward)

               (4) ppid  %d
                      The PID of the parent of this process.

               (5) pgrp  %d
                      The process group ID of the process.

               (6) session  %d
                      The session ID of the process.

               (7) tty_nr  %d
                      The controlling terminal of the process.  (The minor
                      device number is contained in the combination of
                      bits 31 to 20 and 7 to 0; the major device number is
                      in bits 15 to 8.)

               (8) tpgid  %d
                      The ID of the foreground process group of the
                      controlling terminal of the process.

               (9) flags  %u
                      The kernel flags word of the process.  For bit
                      meanings, see the PF_* defines in the Linux kernel
                      source file include/linux/sched.h.  Details depend
                      on the kernel version.

                      The format for this field was %lu before Linux 2.6.

               (10) minflt  %lu
                      The number of minor faults the process has made
                      which have not required loading a memory page from
                      disk.

               (11) cminflt  %lu
                      The number of minor faults that the process's
                      waited-for children have made.

               (12) majflt  %lu
                      The number of major faults the process has made
                      which have required loading a memory page from disk.

               (13) cmajflt  %lu
                      The number of major faults that the process's
                      waited-for children have made.

               (14) utime  %lu
                      Amount of time that this process has been scheduled
                      in user mode, measured in clock ticks (divide by
                      sysconf(_SC_CLK_TCK)).  This includes guest time,
                      guest_time (time spent running a virtual CPU, see
                      below), so that applications that are not aware of
                      the guest time field do not lose that time from
                      their calculations.

               (15) stime  %lu
                      Amount of time that this process has been scheduled
                      in kernel mode, measured in clock ticks (divide by
                      sysconf(_SC_CLK_TCK)).

               (16) cutime  %ld
                      Amount of time that this process's waited-for
                      children have been scheduled in user mode, measured
                      in clock ticks (divide by sysconf(_SC_CLK_TCK)).
                      (See also times(2).)  This includes guest time,
                      cguest_time (time spent running a virtual CPU, see
                      below).

               (17) cstime  %ld
                      Amount of time that this process's waited-for
                      children have been scheduled in kernel mode,
                      measured in clock ticks (divide by
                      sysconf(_SC_CLK_TCK)).

               (18) priority  %ld
                      (Explanation for Linux 2.6) For processes running a
                      real-time scheduling policy (policy below; see
                      sched_setscheduler(2)), this is the negated
                      scheduling priority, minus one; that is, a number in
                      the range -2 to -100, corresponding to real-time
                      priorities 1 to 99.  For processes running under a
                      non-real-time scheduling policy, this is the raw
                      nice value (setpriority(2)) as represented in the
                      kernel.  The kernel stores nice values as numbers in
                      the range 0 (high) to 39 (low), corresponding to the
                      user-visible nice range of -20 to 19.

                      Before Linux 2.6, this was a scaled value based on
                      the scheduler weighting given to this process.

               (19) nice  %ld
                      The nice value (see setpriority(2)), a value in the
                      range 19 (low priority) to -20 (high priority).

               (20) num_threads  %ld
                      Number of threads in this process (since Linux 2.6).
                      Before Linux 2.6, this field was hard coded to 0 as
                      a placeholder for an earlier removed field.

               (21) itrealvalue  %ld
                      The time in jiffies before the next SIGALRM is sent
                      to the process due to an interval timer.  Since
                      Linux 2.6.17, this field is no longer maintained,
                      and is hard coded as 0.

               (22) starttime  %llu
                      The time the process started after system boot.
                      Before Linux 2.6, this value was expressed in
                      jiffies.  Since Linux 2.6, the value is expressed in
                      clock ticks (divide by sysconf(_SC_CLK_TCK)).

                      The format for this field was %lu before Linux 2.6.

               (23) vsize  %lu
                      Virtual memory size in bytes.

               (24) rss  %ld
                      Resident Set Size: number of pages the process has
                      in real memory.  This is just the pages which count
                      toward text, data, or stack space.  This does not
                      include pages which have not been demand-loaded in,
                      or which are swapped out.  This value is inaccurate;
                      see /proc/pid/statm below.

               (25) rsslim  %lu
                      Current soft limit in bytes on the rss of the
                      process; see the description of RLIMIT_RSS in
                      getrlimit(2).

               (26) startcode  %lu  [PT]
                      The address above which program text can run.

               (27) endcode  %lu  [PT]
                      The address below which program text can run.

               (28) startstack  %lu  [PT]
                      The address of the start (i.e., bottom) of the
                      stack.

               (29) kstkesp  %lu  [PT]
                      The current value of ESP (stack pointer), as found
                      in the kernel stack page for the process.

               (30) kstkeip  %lu  [PT]
                      The current EIP (instruction pointer).

               (31) signal  %lu
                      The bitmap of pending signals, displayed as a
                      decimal number.  Obsolete, because it does not
                      provide information on real-time signals; use
                      /proc/pid/status instead.

               (32) blocked  %lu
                      The bitmap of blocked signals, displayed as a
                      decimal number.  Obsolete, because it does not
                      provide information on real-time signals; use
                      /proc/pid/status instead.

               (33) sigignore  %lu
                      The bitmap of ignored signals, displayed as a
                      decimal number.  Obsolete, because it does not
                      provide information on real-time signals; use
                      /proc/pid/status instead.

               (34) sigcatch  %lu
                      The bitmap of caught signals, displayed as a decimal
                      number.  Obsolete, because it does not provide
                      information on real-time signals; use
                      /proc/pid/status instead.

               (35) wchan  %lu  [PT]
                      This is the "channel" in which the process is
                      waiting.  It is the address of a location in the
                      kernel where the process is sleeping.  The
                      corresponding symbolic name can be found in
                      /proc/pid/wchan.

               (36) nswap  %lu
                      Number of pages swapped (not maintained).

               (37) cnswap  %lu
                      Cumulative nswap for child processes (not
                      maintained).

               (38) exit_signal  %d  (since Linux 2.1.22)
                      Signal to be sent to parent when we die.

               (39) processor  %d  (since Linux 2.2.8)
                      CPU number last executed on.

               (40) rt_priority  %u  (since Linux 2.5.19)
                      Real-time scheduling priority, a number in the range
                      1 to 99 for processes scheduled under a real-time
                      policy, or 0, for non-real-time processes (see
                      sched_setscheduler(2)).

               (41) policy  %u  (since Linux 2.5.19)
                      Scheduling policy (see sched_setscheduler(2)).
                      Decode using the SCHED_* constants in linux/sched.h.

                      The format for this field was %lu before Linux
                      2.6.22.

               (42) delayacct_blkio_ticks  %llu  (since Linux 2.6.18)
                      Aggregated block I/O delays, measured in clock ticks
                      (centiseconds).

               (43) guest_time  %lu  (since Linux 2.6.24)
                      Guest time of the process (time spent running a
                      virtual CPU for a guest operating system), measured
                      in clock ticks (divide by sysconf(_SC_CLK_TCK)).

               (44) cguest_time  %ld  (since Linux 2.6.24)
                      Guest time of the process's children, measured in
                      clock ticks (divide by sysconf(_SC_CLK_TCK)).

               (45) start_data  %lu  (since Linux 3.3)  [PT]
                      Address above which program initialized and
                      uninitialized (BSS) data are placed.

               (46) end_data  %lu  (since Linux 3.3)  [PT]
                      Address below which program initialized and
                      uninitialized (BSS) data are placed.

               (47) start_brk  %lu  (since Linux 3.3)  [PT]
                      Address above which program heap can be expanded
                      with brk(2).

               (48) arg_start  %lu  (since Linux 3.5)  [PT]
                      Address above which program command-line arguments
                      (argv) are placed.

               (49) arg_end  %lu  (since Linux 3.5)  [PT]
                      Address below program command-line arguments (argv)
                      are placed.

               (50) env_start  %lu  (since Linux 3.5)  [PT]
                      Address above which program environment is placed.

               (51) env_end  %lu  (since Linux 3.5)  [PT]
                      Address below which program environment is placed.

               (52) exit_code  %d  (since Linux 3.5)  [PT]
                      The thread's exit status in the form reported by
                      waitpid(2).

 SEE ALSO         top

        proc(5), proc_pid_status(5)

 COLOPHON         top

        This page is part of the man-pages (Linux kernel and C library
        user-space interface documentation) project.  Information about
        the project can be found at
        ⟨https://www.kernel.org/doc/man-pages/⟩.  If you have a bug report
        for this manual page, see
        ⟨https://git.kernel.org/pub/scm/docs/man-pages/man-pages.git/tree/CONTRIBUTING⟩.
        This page was obtained from the tarball man-pages-6.10.tar.gz
        fetched from
        ⟨https://mirrors.edge.kernel.org/pub/linux/docs/man-pages/⟩ on
        2025-02-02.  If you discover any rendering problems in this HTML
        version of the page, or you believe there is a better or more up-
        to-date source for the page, or you have corrections or
        improvements to the information in this COLOPHON (which is not
        part of the original manual page), send a mail to
        man-pages@man7.org

 Linux man-pages 6.10            2024-05-02               proc_pid_stat(5)
 ###############################################################################
 }

const TASK_COMM_LEN = 16; TASK_COMM_BUF = TASK_COMM_LEN*8;
type  TTaskCommBuff = packed array[0..TASK_COMM_BUF-1] of Char;
type
 TProcPidStat = record
  {1}  pid         : cint;          // %d   The process ID.
  {2}  comm        : TTaskCommBuff; // %s   Filename of executable, in parentheses.
  {3}  state       : Char;          // %c   The process state.
  {4}  ppid        : cint;          // %d   The PID of the parent of this process.
  {5}  pgrp        : cint;          // %d   The process group ID of the process.
  {6}  session     : cint;          // %d   The session ID of the process.
  {7}  tty_nr      : cint;          // %d   The controlling terminal of the process.
  {8}  tpgid       : cint;          // %d   The ID of foreground process group of tty.
  {9}  flags       : cuint;         // %u   The kernel flags word of the process.
  {10} minflt      : culong;        // %lu  The number of minor faults (disk pages).
  {11} cminflt     : culong;        // %lu  The number of minor faults (waited-for).
  {12} majflt      : culong;        // %lu  The number of major faults (disk pages).
  {13} cmajflt     : culong;        // %lu  The number of major faults (waited-for).
  {14} utime       : culong;        // %lu  Amount of user mode time in clock ticks.
  {15} stime       : culong;        // %lu  Amount of kern mode time in clock ticks.
  {16} cutime      : clong;         // %ld  Amount of user mode time (children).
  {17} cstime      : clong;         // %ld  Amount of kern mode time (children).
  {18} priority    : clong;         // %ld  Priority as represented in the kernel.
  {19} nice        : clong;         // %ld  The nice value [-20..19].
  {20} num_threads : clong;         // %ld  Number of threads in this process.
  {21} itrealvalue : clong;         // %ld  The time in jiffies before next SIGALRM.
  {22} starttime   : culonglong;    // %llu The time the process started after system boot.
  {23} vsize       : culong;        // %lu  Virtual memory size in bytes.
  {24} rss         : clong;         // %ld  ResidentSetSize: num.pages in real RAM.
  {25} rsslim      : culong;        // %lu  Current soft limit in bytes on the rss.
  {26} startcode   : culong;        // %lu  [PT] The address above which program text can run.
  {27} endcode     : culong;        // %lu  [PT] The address below which program text can run.
  {28} startstack  : culong;        // %lu  [PT] The address of the start of the stack.
  {29} kstkesp     : culong;        // %lu  [PT] The current value of ESP (stack pointer).
  {30} kstkeip     : culong;        // %lu  [PT] The current EIP (instruction pointer).
  {31} signal      : culong;        // %lu  The bitmap of pending signals. Obsolete.
  {32} blocked     : culong;        // %lu  The bitmap of blocked signals. Obsolete.
  {33} sigignore   : culong;        // %lu  The bitmap of ignored signals. Obsolete.
  {34} sigcatch    : culong;        // %lu  The bitmap of caught signals.  Obsolete.
  {35} wchan       : culong;        // %lu  [PT] "Channel" in which process is waiting.
  {36} nswap       : culong;        // %lu  Number of pages swapped (not maintained).
  {37} cnswap      : culong;        // %lu  Cumulative nswap for child processes.
  {38} exit_signal : cint;          // %d   Signal to be sent to parent when we die.
  {39} processor   : cint;          // %d   CPU number last executed on.
  {40} rt_priority : cuint;         // %u   Real-time scheduling priority [1..99].
  {41} policy      : cuint;         // %u   Scheduling policy, see sched_setscheduler.
  {42} delayacct_blkio_ticks : culonglong; // %llu  Aggregated block I/O delays.
  {43} guest_time  : culong;        // %lu  Guest time of the process in clock ticks.
  {44} cguest_time : clong;         // %ld  Guest time of the process's children.
  {45} start_data  : culong;        // %lu  [PT] Address of program BSS data begin.
  {46} end_data    : culong;        // %lu  [PT] Address of program BSS data end.
  {47} start_brk   : culong;        // %lu  [PT] Address of program heap.
  {48} arg_start   : culong;        // %lu  [PT] Address of program argv begin.
  {49} arg_end     : culong;        // %lu  [PT] Address of program argv end.
  {50} env_start   : culong;        // %lu  [PT] Address of program environ begin.
  {51} env_end     : culong;        // %lu  [PT] Address of program environ end.
  {52} exit_code   : cint;          // %d   [PT] The thread's exit status as waitpid(2).
 end;

const // Identifiers for TProcPidStat fields
 ppsid_pid          = 1;
 ppsid_comm         = 2;
 ppsid_state        = 3;
 ppsid_ppid         = 4;
 ppsid_pgrp         = 5;
 ppsid_session      = 6;
 ppsid_tty_nr       = 7;
 ppsid_tpgid        = 8;
 ppsid_flags        = 9;
 ppsid_minflt       = 10;
 ppsid_cminflt      = 11;
 ppsid_majflt       = 12;
 ppsid_cmajflt      = 13;
 ppsid_utime        = 14;
 ppsid_stime        = 15;
 ppsid_cutime       = 16;
 ppsid_cstime       = 17;
 ppsid_priority     = 18;
 ppsid_nice         = 19;
 ppsid_num_threads  = 20;
 ppsid_itrealvalue  = 21;
 ppsid_starttime    = 22;
 ppsid_vsize        = 23;
 ppsid_rss          = 24;
 ppsid_rsslim       = 25;
 ppsid_startcode    = 26;
 ppsid_endcode      = 27;
 ppsid_startstack   = 28;
 ppsid_kstkesp      = 29;
 ppsid_kstkeip      = 30;
 ppsid_signal       = 31;
 ppsid_blocked      = 32;
 ppsid_sigignore    = 33;
 ppsid_sigcatch     = 34;
 ppsid_wchan        = 35;
 ppsid_nswap        = 36;
 ppsid_cnswap       = 37;
 ppsid_exit_signal  = 38;
 ppsid_processor    = 39;
 ppsid_rt_priority  = 40;
 ppsid_policy       = 41;
 ppsid_delayacct_blkio_ticks = 42;
 ppsid_guest_time   = 43;
 ppsid_cguest_time  = 44;
 ppsid_start_data   = 45;
 ppsid_end_data     = 46;
 ppsid_start_brk    = 47;
 ppsid_arg_start    = 48;
 ppsid_arg_end      = 49;
 ppsid_env_start    = 50;
 ppsid_env_end      = 51;
 ppsid_exit_code    = 52;
 ppsid_all_fields   = [1..52];         // Set of all TProcPidStat fields

const                      // Note: pps is acronim for proc_pid_stat
 pps_Invalid        = '?'; // Marker for invalid/non-existing/failed
 pps_Running        = 'R'; // R  Running
 pps_Sleeping       = 'S'; // S  Sleeping in an interruptible wait
 pps_Waiting        = 'D'; // D  Waiting in uninterruptible disk sleep
 pps_Zombie         = 'Z'; // Z  Zombie
 pps_Stopped        = 'T'; // T  Stopped (on a signal) or (before Linux 2.6.33) trace stopped
 pps_Tracing        = 't'; // t  Tracing stop (Linux 2.6.33 onward)
 pps_Paging         = 'W'; // W  Paging (only before Linux 2.6.0)
 pps_Dead           = 'X'; // X  Dead (from Linux 2.6.0 onward)
 pps_DeadLo         = 'x'; // x  Dead (Linux 2.6.33 to 3.13 only)
 pps_Wakekill       = 'K'; // K  Wakekill (Linux 2.6.33 to 3.13 only)
 pps_Waking         = 'W'; // W  Waking (Linux 2.6.33 to 3.13 only)
 pps_Parked         = 'P'; // P  Parked (Linux 3.9 to 3.13 only)
 pps_Idle           = 'I'; // I  Idle (Linux 4.14 onward)
 pps_valid_states   = 'RSDZTtWXxKPI'; // Valid TProcPidStat.state chars
 pps_states_valid   = ['R','S','D','Z','T','t','W','X','x','K','P','I'];
 pps_states_death   = [ pps_Zombie, pps_Dead, pps_DeadLo ];
 pps_states_alive   = pps_states_valid - pps_states_death;

 { Check State char is valid. }
function pps_IsStateValid(State:Char):Boolean; inline;

 { Check State char corresponds to dead process. }
function pps_IsStateDeath(State:Char):Boolean; inline;

 { Check State char corresponds to alive process. }
function pps_IsStateAlive(State:Char):Boolean; inline;

 { Validate process state char. }
function pps_ValidateState(State:Char):Char;

 {
 Parse the line (stat) read from /proc/pid/stat, write result to record (pps),
 includes only fields from given set of fields (what).
 Empty what=[] means ppsid_all_fields.
 }
function parse_proc_pid_stat(const stat:LongString; out pps:TProcPidStat; what:TByteSet=[]):Boolean;

procedure Test_unit_procps;

implementation

function parse_proc_pid_stat(const stat:LongString; out pps:TProcPidStat; what:TByteSet=[]):Boolean;
var pb,pc,pe,pw:PChar; wl,ne,leng:Integer; const blanks=[#9,' '];
 function skip_item(id:Integer):Boolean; inline;
 begin
  Result:=not (id in what);
 end;
 procedure scan_word; inline;
 begin
  while (pc<pe) and (pc[0] in blanks) do inc(pc);
  pw:=pc; // skip blanks, mark pw = start of word
  while (pc<pe) and not (pc[0] in blanks) do inc(pc);
  wl:=pc-pw; // calculate number of chars in word
 end;
 procedure scan_comm;
 const rbracket=')'; var pt:PChar;
 begin
  while (pc<pe) and (pc[0] in blanks) do inc(pc);
  pw:=pc; // skip blanks, mark pw = start of word
  pt:=pe; // reverse search for last rbracket occurrence
  while (pt>pc) do begin dec(pt); if (pt[0]=rbracket) then pc:=pt; end;
  while (pc<pe) and (pc[0]=rbracket) do inc(pc); // skip tail rbrackets
  wl:=pc-pw; // calculate number of chars in word
 end;
 procedure scan_value(id:Integer; var v:LongInt); overload;
 var sign:Char;
 begin
  scan_word; v:=0; sign:=' ';
  if skip_item(id) then Exit;
  if (wl<=0) then inc(ne) else begin
   while (wl>0) and (ne=0) do begin
    case pw[0] of
     '+','-': if (sign=' ') then sign:=pw[0] else inc(ne);
     '0'..'9': v:=v*10+(Ord(pw[0])-Ord('0'));
     else inc(ne);
    end;
    inc(pw);
    dec(wl);
   end;
   if (sign='-') then v:=-v;
  end;
 end;
 procedure scan_value(id:Integer; var v:Int64); overload;
 var sign:Char;
 begin
  scan_word; v:=0; sign:=' ';
  if skip_item(id) then Exit;
  if (wl<=0) then inc(ne) else begin
   while (wl>0) and (ne=0) do begin
    case pw[0] of
     '+','-': if (sign=' ') then sign:=pw[0] else inc(ne);
     '0'..'9': v:=v*10+(Ord(pw[0])-Ord('0'));
     else inc(ne);
    end;
    inc(pw);
    dec(wl);
   end;
   if (sign='-') then v:=-v;
  end;
 end;
 procedure scan_value(id:Integer; var v:LongWord); overload;
 begin
  scan_word; v:=0;
  if skip_item(id) then Exit;
  if (wl<=0) then inc(ne) else begin
   while (wl>0) and (ne=0) do begin
    case pw[0] of
     '0'..'9': v:=v*10+(Ord(pw[0])-Ord('0'));
     else inc(ne);
    end;
    inc(pw);
    dec(wl);
   end;
  end;
 end;
 procedure scan_value(id:Integer; var v:QWord); overload;
 begin
  scan_word; v:=0;
  if skip_item(id) then Exit;
  if (wl<=0) then inc(ne) else begin
   while (wl>0) and (ne=0) do begin
    case pw[0] of
     '0'..'9': v:=v*10+(Ord(pw[0])-Ord('0'));
     else inc(ne);
    end;
    inc(pw);
    dec(wl);
   end;
  end;
 end;
 procedure scan_value(id:Integer; var v:TTaskCommBuff); overload;
 var i:Integer;
 begin
  scan_comm; i:=0;
  FillChar(v,SizeOf(v),0);
  if skip_item(id) then Exit;
  if (wl<=2) then inc(ne) else begin
   // check & skip lead/tail brackets () which must present in comm
   if (pw[0]='(')    then begin inc(pw); dec(wl); end else inc(ne);
   if (pw[wl-1]=')') then begin          dec(wl); end else inc(ne);
   while (wl>0) and (ne=0) and (i<SizeOf(v)-1) do begin
    v[i]:=pw[0]; inc(i);
    inc(pw);
    dec(wl);
   end;
  end;
 end;
 procedure scan_value(id:Integer; var v:char); overload;
 begin
  scan_word; v:='?';
  if skip_item(id) then Exit;
  if (wl<>1) then inc(ne) else v:=pw[0];
 end;
begin
 Result:=false; ne:=0;
 pps:=Default(TProcPidStat);
 if (stat='') then Exit(false);
 try
  leng:=Pos(LineEnding,stat)-1;
  if (leng<0) then leng:=Length(stat);
  if (what=[]) then what:=ppsid_all_fields;
  pb:=PChar(stat); pc:=pb; pe:=pb+leng; pw:=pc;
  while (pc=pb) do begin // The fake loop uses for error handling
   scan_value(1,  pps.pid                   ); if (ne>0) then break;
   scan_value(2,  pps.comm                  ); if (ne>0) then break;
   scan_value(3,  pps.state                 ); if (ne>0) then break;
   scan_value(4,  pps.ppid                  ); if (ne>0) then break;
   scan_value(5,  pps.pgrp                  ); if (ne>0) then break;
   scan_value(6,  pps.session               ); if (ne>0) then break;
   scan_value(7,  pps.tty_nr                ); if (ne>0) then break;
   scan_value(8,  pps.tpgid                 ); if (ne>0) then break;
   scan_value(9,  pps.flags                 ); if (ne>0) then break;
   scan_value(10, pps.minflt                ); if (ne>0) then break;
   scan_value(11, pps.cminflt               ); if (ne>0) then break;
   scan_value(12, pps.majflt                ); if (ne>0) then break;
   scan_value(13, pps.cmajflt               ); if (ne>0) then break;
   scan_value(14, pps.utime                 ); if (ne>0) then break;
   scan_value(15, pps.stime                 ); if (ne>0) then break;
   scan_value(16, pps.cutime                ); if (ne>0) then break;
   scan_value(17, pps.cstime                ); if (ne>0) then break;
   scan_value(18, pps.priority              ); if (ne>0) then break;
   scan_value(19, pps.nice                  ); if (ne>0) then break;
   scan_value(20, pps.num_threads           ); if (ne>0) then break;
   scan_value(21, pps.itrealvalue           ); if (ne>0) then break;
   scan_value(22, pps.starttime             ); if (ne>0) then break;
   scan_value(23, pps.vsize                 ); if (ne>0) then break;
   scan_value(24, pps.rss                   ); if (ne>0) then break;
   scan_value(25, pps.rsslim                ); if (ne>0) then break;
   scan_value(26, pps.startcode             ); if (ne>0) then break;
   scan_value(27, pps.endcode               ); if (ne>0) then break;
   scan_value(28, pps.startstack            ); if (ne>0) then break;
   scan_value(29, pps.kstkesp               ); if (ne>0) then break;
   scan_value(30, pps.kstkeip               ); if (ne>0) then break;
   scan_value(31, pps.signal                ); if (ne>0) then break;
   scan_value(32, pps.blocked               ); if (ne>0) then break;
   scan_value(33, pps.sigignore             ); if (ne>0) then break;
   scan_value(34, pps.sigcatch              ); if (ne>0) then break;
   scan_value(35, pps.wchan                 ); if (ne>0) then break;
   scan_value(36, pps.nswap                 ); if (ne>0) then break;
   scan_value(37, pps.cnswap                ); if (ne>0) then break;
   scan_value(38, pps.exit_signal           ); if (ne>0) then break;
   scan_value(39, pps.processor             ); if (ne>0) then break;
   scan_value(40, pps.rt_priority           ); if (ne>0) then break;
   scan_value(41, pps.policy                ); if (ne>0) then break;
   scan_value(42, pps.delayacct_blkio_ticks ); if (ne>0) then break;
   scan_value(43, pps.guest_time            ); if (ne>0) then break;
   scan_value(44, pps.cguest_time           ); if (ne>0) then break;
   scan_value(45, pps.start_data            ); if (ne>0) then break;
   scan_value(46, pps.end_data              ); if (ne>0) then break;
   scan_value(47, pps.start_brk             ); if (ne>0) then break;
   scan_value(48, pps.arg_start             ); if (ne>0) then break;
   scan_value(49, pps.arg_end               ); if (ne>0) then break;
   scan_value(50, pps.env_start             ); if (ne>0) then break;
   scan_value(51, pps.env_end               ); if (ne>0) then break;
   scan_value(52, pps.exit_code             ); if (ne>0) then break;
  end;
  Result:=(ne=0);
 except
  on E:Exception do BugReport(E,nil,'parse_proc_pid_stat');
 end;
end;

function pps_IsStateValid(State:Char):Boolean;
begin
 Result:=(State in pps_states_valid);
end;

function pps_IsStateDeath(State:Char):Boolean;
begin
 Result:=(State in pps_states_death);
end;

function pps_IsStateAlive(State:Char):Boolean;
begin
 Result:=(State in pps_states_alive);
end;

function pps_ValidateState(State:Char):Char;
begin
 if pps_IsStateValid(State)
 then Result:=State
 else Result:=pps_Invalid;
end;

procedure Test_unit_procps;
const TestStr='1597522 (cat) R 1597463 1597522 1597463 34816 1597522 4194304'+
 ' 94 0 0 0 0 0 0 0 20 0 1 0 40048329 5742592 228 18446744073709551615'+
 ' 105081329754112 105081329773993 140725544058592 0 0 0 0 0 0 0 0 0 17'+
 ' 0 0 0 0 0 0 105081329790000 105081329791616 105081335476224 140725544063950'+
 ' 140725544063970 140725544063970 140725544067051 0';
var pps:TProcPidStat;
begin
 if parse_proc_pid_stat(TestStr,pps)
 then Echo('parse_proc_pid_stat - Ok')
 else Echo('parse_proc_pid_stat - Fail');
 Echo(Format('%d %s %s %d',[pps.pid,pps.comm,pps.state,pps.ppid]));
end;

///////////////////////////////////////
// Unit initialization and finalization
///////////////////////////////////////

procedure Init_crw_procps;
begin
end;

procedure Free_crw_procps;
begin
end;

initialization

 Init_crw_procps;

finalization

 Free_crw_procps;

end.

//////////////
// END OF FILE
//////////////

