LCOV - code coverage report
Current view: top level - shared/common/src/32_util - m_pstat.F90 (source / functions) Coverage Total Hit
Test: coverage.info Lines: 79.2 % 72 57
Test Date: 2026-09-19 15:24:51 Functions: 71.4 % 7 5

            Line data    Source code
       1              : !!****m* ABINIT/m_pstat
       2              : !! NAME
       3              : !! m_pstat
       4              : !!
       5              : !! FUNCTION
       6              : !! Interface to the /proc/{pid}/status file available on Linux.
       7              : !!
       8              : !! COPYRIGHT
       9              : !!  Copyright (C) 2017-2026 ABINIT group (MG)
      10              : !!  This file is distributed under the terms of the
      11              : !!  GNU General Public License, see ~abinit/COPYING
      12              : !!  or http://www.gnu.org/copyleft/gpl.txt .
      13              : !!
      14              : !! SOURCE
      15              : 
      16              : #if defined HAVE_CONFIG_H
      17              : #include "config.h"
      18              : #endif
      19              : 
      20              : #include "abi_common.h"
      21              : 
      22              : module m_pstat
      23              : 
      24              :  use, intrinsic :: iso_c_binding
      25              :  use defs_basis
      26              :  use m_xmpi
      27              :  use m_abicore
      28              :  use m_errors
      29              :  use m_yaml
      30              : 
      31              :  use m_fstrings, only : find_and_select, basename
      32              :  use m_clib,     only : clib_getpid
      33              : 
      34              :  implicit none
      35              : 
      36              :  private
      37              : !!***
      38              : 
      39              : !----------------------------------------------------------------------
      40              : 
      41              : !!****t* m_pstat/pstat_t
      42              : !! NAME
      43              : !! pstat_t
      44              : !!
      45              : !! FUNCTION
      46              : !! This object stores the most important quantites reported in the /proc/{pid}/status file
      47              : !! in particular the virtual memory VmRSS. See https://docs.kernel.org/filesystems/proc.html
      48              : !!
      49              : !! NB: This file is only available on Linux hence one should always check the value of pstat%ok
      50              : !! before using quantities such as vmrss_mb.
      51              : !!
      52              : !! SOURCE
      53              : 
      54              :  type, private :: pstat_t
      55              : 
      56              :   logical :: ok = .False.
      57              :   ! False if stat file is not available
      58              : 
      59              :   integer :: pid = -1
      60              :   ! Process identifier.
      61              : 
      62              :   integer :: threads = -1
      63              :   ! number of threads.
      64              : 
      65              :   integer :: fdsize = -1
      66              :    ! Number of file descriptor slots currently allocated.
      67              : 
      68              :   real(dp) :: vmrss_mb = -one
      69              :   ! Actual physical RAM used. It contains the three following parts (VmRSS = RssAnon + RssFile + RssShmem)
      70              : 
      71              :   real(dp) :: vmpeak_mb = -one
      72              :   ! Peak virtual memory size
      73              : 
      74              :   real(dp) :: vmstk_mb = -one
      75              :   ! Size of stack segments
      76              : 
      77              :   character(len=fnlen) :: filepath = ""
      78              :   ! Path of status file
      79              : 
      80              :   character(len=500) :: iomsg = ""
      81              :   ! Error message returned when parsing filepath
      82              : 
      83              :  contains
      84              :    procedure :: from_pid => pstat_from_pid     ! Init object from process identifier (main entry point).
      85              :    procedure :: from_file => pstat_from_file   ! Init object from file (useful for debugging).
      86              :    procedure :: print => pstat_print           ! Print object.
      87              :    procedure :: min_mem_mb_per_proc => pstat_min_mem_mb_per_proc
      88              :  end type pstat_t
      89              : !!***
      90              : 
      91              :  type(pstat_t),save,public :: pstat_proc
      92              : 
      93              : !----------------------------------------------------------------------
      94              : 
      95              : contains
      96              : !!***
      97              : 
      98              : !!****f* m_pstat/pstat_from_pid
      99              : !! NAME
     100              : !!  pstat_from_pid
     101              : !!
     102              : !! FUNCTION
     103              : !!   Init object from process identifier (main entry point for client code).
     104              : !!
     105              : !! SOURCE
     106              : 
     107         1442 : subroutine pstat_from_pid(pstat)
     108              : 
     109              : !Arguments ------------------------------------
     110              :  class(pstat_t),intent(out) :: pstat
     111              : 
     112              : !Local variables-------------------------------
     113              :  integer(c_int) :: pid
     114              :  character(len=500) :: spid
     115              : ! *************************************************************************
     116              : 
     117         1442 :  pid = clib_getpid()
     118         1442 :  write(spid, "(i0)") pid
     119         1442 :  spid = adjustl(spid)
     120         1442 :  call pstat%from_file("/proc/"//trim(spid)//"/status")
     121              : 
     122         1442 : end subroutine pstat_from_pid
     123              : !!***
     124              : 
     125              : !!****f* m_pstat/pstat_from_file
     126              : !! NAME
     127              : !!  pstat_from_file
     128              : !!
     129              : !! FUNCTION
     130              : !! Init object from file (useful for debugging).
     131              : !!
     132              : !! SOURCE
     133              : 
     134        38033 : subroutine pstat_from_file(pstat, filepath)
     135              : 
     136              : !Arguments ------------------------------------
     137              :  class(pstat_t),intent(inout) :: pstat
     138              :  character(len=*),intent(in) :: filepath
     139              : 
     140              : !Local variables-------------------------------
     141              :  integer :: unit, ierr
     142              :  character(len=500) :: line
     143              :  integer :: istart, istop, iostat
     144              : ! *************************************************************************
     145              : 
     146        38033 :  pstat%ok = .False.
     147        38033 :  pstat%filepath = filepath
     148              : 
     149        38033 :  open(newunit=unit, file=trim(pstat%filepath), action="read", status="old", iostat=ierr, iomsg=pstat%iomsg)
     150        38033 :  if (ierr /= 0) then
     151            0 :    close(unit); return
     152              :  end if
     153              : 
     154              :  do
     155      2243947 :    read(unit, "(a)", iostat=ierr, end=10, iomsg=pstat%iomsg) line
     156      2205914 :    if (ierr > 0) then ! EOF
     157            0 :      close(unit); return
     158              :    end if
     159              : 
     160              :    ! Parse useful integers
     161      2205914 :    if (index(line, "Pid:") == 1) call get_int(line, pstat%pid)
     162      2205914 :    if (index(line, "Threads:") == 1) call get_int(line, pstat%threads)
     163      2205914 :    if (index(line, "FDSize:") == 1) call get_int(line, pstat%fdsize)
     164              : 
     165              :    ! Parse memory entries
     166      2205914 :    if (index(line, "VmRSS:") == 1) call get_mem_mb(line, pstat%vmrss_mb)
     167      2205914 :    if (index(line, "VmPeak:") == 1) call get_mem_mb(line, pstat%vmpeak_mb)
     168      2205914 :    if (index(line, "VmStk:") == 1) call get_mem_mb(line, pstat%vmstk_mb)
     169              :  end do
     170              : 
     171        38033 : 10 close(unit)
     172        38033 :   pstat%ok = .True.
     173        76066 :   pstat%iomsg = ""
     174              : 
     175              : contains
     176              : 
     177       114099 : subroutine get_mem_mb(str, mem_mb)
     178              : 
     179              :  character(len=*),intent(in) :: str
     180              :  real(dp),intent(out) :: mem_mb
     181              : 
     182              :  ! Generic mem entry has format `VmRSS: 2492 kB`
     183              :  real(dp) :: mem_fact
     184       114099 :  istart = index(str, ":") + 1
     185              :  istop = find_and_select(str, &
     186              :                         ["kB", "mB"], &
     187       342297 :                         [one/1024._dp, one], mem_fact, pstat%iomsg) !default=one,
     188       114099 :  ABI_CHECK(istop /= -1, pstat%iomsg)
     189       114099 :  read(str(istart+1:istop-1), fmt=*, iostat=iostat, iomsg=pstat%iomsg) mem_mb
     190       114099 :  ABI_CHECK(iostat == 0, pstat%iomsg)
     191       114099 :  mem_mb = mem_mb * mem_fact
     192              : 
     193       114099 : end subroutine get_mem_mb
     194              : 
     195       114099 : subroutine get_int(str, out_ival)
     196              : 
     197              :  character(len=*),intent(in) :: str
     198              :  integer,intent(out) :: out_ival
     199       114099 :  istart = index(str, ":") + 1
     200       114099 :  read(str(istart+1:), fmt=*, iostat=iostat, iomsg=pstat%iomsg) out_ival
     201              :  !ABI_CHECK(iostat == 0, pstat%iomsg)
     202              : 
     203       114099 : end subroutine get_int
     204              : 
     205              : end subroutine pstat_from_file
     206              : !!***
     207              : 
     208              : !!****f* m_pstat/pstat_print
     209              : !! NAME
     210              : !!  pstat_print
     211              : !!
     212              : !! FUNCTION
     213              : !!  Print object in Yaml format to std_out
     214              : !!
     215              : !! SOURCE
     216              : 
     217        36591 : subroutine pstat_print(pstat, comm, file, line)
     218              : 
     219              :  class(pstat_t),intent(inout) :: pstat
     220              :  character(len=*),optional,intent(in) :: file
     221              :  integer,optional,intent(in) :: line, comm
     222              : 
     223              : !Local variables-------------------------------
     224              :  integer :: units(1), ierr
     225              :  integer :: f90line = 0
     226              :  real(dp) :: min_mpicomm_vmrss_mb, max_mpicomm_vmrss_mb
     227              :  character(len=500) :: f90name='Subroutine Unknown'
     228              :  type(yamldoc_t) :: ydoc
     229              : ! *************************************************************************
     230              : 
     231            0 :  if (pstat%pid == -1) return
     232        36591 :  units(1) = std_out
     233        36591 :  if (std_out < 1) return
     234              : 
     235        36591 :  call pstat%from_file(pstat%filepath)
     236              : 
     237        36591 :  if (present(line)) f90line = line
     238        36591 :  if (present(file)) f90name = basename(file)
     239              : 
     240        36591 :  min_mpicomm_vmrss_mb = pstat%vmrss_mb
     241        36591 :  max_mpicomm_vmrss_mb = pstat%vmrss_mb
     242        36591 :  if (present(comm)) then
     243         1426 :    call xmpi_min(pstat%vmrss_mb, min_mpicomm_vmrss_mb, comm, ierr)
     244         1426 :    call xmpi_max(pstat%vmrss_mb, max_mpicomm_vmrss_mb, comm, ierr)
     245              :  end if
     246              : 
     247              : #ifndef FC_NVHPC
     248        36591 :  ydoc = yamldoc_open("PstatData")
     249        36591 :  call ydoc%add_int("pid", pstat%pid)
     250        36591 :  call ydoc%add_string("file", f90name)
     251        36591 :  call ydoc%add_int("line", f90line)
     252        36591 :  call ydoc%add_real("vmrss_mb", pstat%vmrss_mb)
     253        36591 :  call ydoc%add_real("min_mpicomm_vmrss_mb", min_mpicomm_vmrss_mb)
     254        36591 :  call ydoc%add_real("max_mpicomm_vmrss_mb", max_mpicomm_vmrss_mb)
     255        36591 :  call ydoc%add_real("vmpeak_mb", pstat%vmpeak_mb)
     256        36591 :  call ydoc%add_real("vmstk_mb", pstat%vmstk_mb)
     257        36591 :  if (len_trim(pstat%iomsg) > 0) call ydoc%add_string("iomsg", trim(pstat%iomsg))
     258        36591 :  call ydoc%write_units_and_free(units)
     259              : #else
     260              :  ! Yet another wild NVHPC bug (only on eos_nvhpc_23.9_elpa)
     261              :  write(std_out, "(a)")"--- !PstatData"
     262              :  write(std_out, *)"vmrss_mb: ", pstat%vmrss_mb
     263              :  write(std_out, *)"min_mpicomm_vmrss: ", min_mpicomm_vmrss_mb
     264              :  write(std_out, *)"max_mpicomm_vmrss: ", max_mpicomm_vmrss_mb
     265              :  write(std_out, "(a)")"..."
     266              : #endif
     267              : 
     268        36591 : end subroutine pstat_print
     269              : !!***
     270              : 
     271              : !!****f* m_pstat/pstat_min_mem_mb_per_proc
     272              : !! NAME
     273              : !!  pstat_min_mem_mb_per_proc
     274              : !!
     275              : !! FUNCTION
     276              : !!  This function estimates the available memory (in MB) per process, within the MPI communicator comm
     277              : !!  based on process statistics (pstat).
     278              : !!
     279              : !! SOURCE
     280              : 
     281            0 : real(dp) function pstat_min_mem_mb_per_proc(pstat, comm) result(min_mem_mb)
     282              : 
     283              : !Arguments ------------------------------------
     284              :  class(pstat_t),intent(inout) :: pstat
     285              :  integer,intent(in) :: comm
     286              : 
     287              : !Local variables-------------------------------
     288              :  integer :: ierr
     289              :  logical :: all_ok
     290              : ! *************************************************************************
     291              : 
     292            0 :  call pstat%from_file(pstat%filepath)
     293              : 
     294            0 :  all_ok = pstat%ok
     295            0 :  call xmpi_land(all_ok, comm)
     296              : 
     297            0 :  if (.not. all_ok) then
     298              :    ! Handle case in which pstat is not available or something went wrong when reading.
     299            0 :    min_mem_mb = mem_per_cpu_mb * half
     300            0 :    return
     301              :  end if
     302              : 
     303              :  ! Compute min inside comm
     304            0 :  call xmpi_min(pstat%vmrss_mb, min_mem_mb, comm, ierr)
     305              : 
     306            0 :  min_mem_mb = (mem_per_cpu_mb - min_mem_mb)
     307              : 
     308              :  ! Fallback for too small values
     309            0 :  if (min_mem_mb <= tol1) min_mem_mb = mem_per_cpu_mb * half
     310              : 
     311            0 : end function pstat_min_mem_mb_per_proc
     312              : !!***
     313              : 
     314            0 : end module m_pstat
     315              : !!***
        

Generated by: LCOV version 2.3-1