(root)/
glibc-2.38/
sysdeps/
unix/
sysv/
linux/
getsysstats.c
       1  /* Determine various system internal values, Linux version.
       2     Copyright (C) 1996-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  #include <array_length.h>
      20  #include <assert.h>
      21  #include <ctype.h>
      22  #include <errno.h>
      23  #include <ldsodefs.h>
      24  #include <limits.h>
      25  #include <not-cancel.h>
      26  #include <stdio.h>
      27  #include <stdio_ext.h>
      28  #include <sys/mman.h>
      29  #include <sys/sysinfo.h>
      30  #include <sysdep.h>
      31  
      32  int
      33  __get_nprocs_sched (void)
      34  {
      35    enum
      36      {
      37        max_num_cpus = 32768,
      38        cpu_bits_size = CPU_ALLOC_SIZE (32768)
      39      };
      40  
      41    /* This cannot use malloc because it is used on malloc initialization.  */
      42    __cpu_mask cpu_bits[cpu_bits_size / sizeof (__cpu_mask)];
      43    int r = INTERNAL_SYSCALL_CALL (sched_getaffinity, 0, cpu_bits_size,
      44  				 cpu_bits);
      45    if (r > 0)
      46      return CPU_COUNT_S (r, (cpu_set_t*) cpu_bits);
      47    else if (r == -EINVAL)
      48      /* The input buffer is still not enough to store the number of cpus.  This
      49         is an arbitrary values assuming such systems should be rare and there
      50         is no offline cpus.  */
      51      return max_num_cpus;
      52    /* Some other error.  */
      53    return 0;
      54  }
      55  
      56  static char *
      57  next_line (int fd, char *const buffer, char **cp, char **re,
      58             char *const buffer_end)
      59  {
      60    char *res = *cp;
      61    char *nl = memchr (*cp, '\n', *re - *cp);
      62    if (nl == NULL)
      63      {
      64        if (*cp != buffer)
      65          {
      66            if (*re == buffer_end)
      67              {
      68                memmove (buffer, *cp, *re - *cp);
      69                *re = buffer + (*re - *cp);
      70                *cp = buffer;
      71  
      72                ssize_t n = __read_nocancel (fd, *re, buffer_end - *re);
      73                if (n < 0)
      74                  return NULL;
      75  
      76                *re += n;
      77  
      78                nl = memchr (*cp, '\n', *re - *cp);
      79                while (nl == NULL && *re == buffer_end)
      80                  {
      81                    /* Truncate too long lines.  */
      82                    *re = buffer + 3 * (buffer_end - buffer) / 4;
      83                    n = __read_nocancel (fd, *re, buffer_end - *re);
      84                    if (n < 0)
      85                      return NULL;
      86  
      87                    nl = memchr (*re, '\n', n);
      88                    **re = '\n';
      89                    *re += n;
      90                  }
      91              }
      92            else
      93              nl = memchr (*cp, '\n', *re - *cp);
      94  
      95            res = *cp;
      96          }
      97  
      98        if (nl == NULL)
      99          nl = *re - 1;
     100      }
     101  
     102    *cp = nl + 1;
     103    assert (*cp <= *re);
     104  
     105    return res == *re ? NULL : res;
     106  }
     107  
     108  static int
     109  get_nproc_stat (void)
     110  {
     111    enum { buffer_size = 1024 };
     112    char buffer[buffer_size];
     113    char *buffer_end = buffer + buffer_size;
     114    char *cp = buffer_end;
     115    char *re = buffer_end;
     116    int result = 0;
     117  
     118    const int flags = O_RDONLY | O_CLOEXEC;
     119    int fd = __open_nocancel ("/proc/stat", flags);
     120    if (fd != -1)
     121      {
     122        char *l;
     123        while ((l = next_line (fd, buffer, &cp, &re, buffer_end)) != NULL)
     124  	/* The current format of /proc/stat has all the cpu* entries
     125  	   at the front.  We assume here that stays this way.  */
     126  	if (strncmp (l, "cpu", 3) != 0)
     127  	  break;
     128  	else if (isdigit (l[3]))
     129  	  ++result;
     130  
     131        __close_nocancel_nostatus (fd);
     132      }
     133  
     134    return result;
     135  }
     136  
     137  static int
     138  read_sysfs_file (const char *fname)
     139  {
     140    enum { buffer_size = 1024 };
     141    char buffer[buffer_size];
     142    char *buffer_end = buffer + buffer_size;
     143    char *cp = buffer_end;
     144    char *re = buffer_end;
     145  
     146    const int flags = O_RDONLY | O_CLOEXEC;
     147    /* This file contains comma-separated ranges.  */
     148    int fd = __open_nocancel (fname, flags);
     149    char *l;
     150    int result = 0;
     151    if (fd != -1)
     152      {
     153        l = next_line (fd, buffer, &cp, &re, buffer_end);
     154        if (l != NULL)
     155  	do
     156  	  {
     157  	    char *endp;
     158  	    unsigned long int n = strtoul (l, &endp, 10);
     159  	    if (l == endp)
     160  	      {
     161  		result = 0;
     162  		break;
     163  	      }
     164  
     165  	    unsigned long int m = n;
     166  	    if (*endp == '-')
     167  	      {
     168  		l = endp + 1;
     169  		m = strtoul (l, &endp, 10);
     170  		if (l == endp)
     171  		  {
     172  		    result = 0;
     173  		    break;
     174  		  }
     175  	      }
     176  
     177  	    if (m >= n)
     178  	      result += m - n + 1;
     179  
     180  	    l = endp;
     181  	    if (l < re && *l == ',')
     182  	      ++l;
     183  	  }
     184  	while (l < re && *l != '\n');
     185  
     186        __close_nocancel_nostatus (fd);
     187      }
     188  
     189    return result;
     190  }
     191  
     192  static int
     193  get_nprocs_fallback (void)
     194  {
     195    int result;
     196  
     197    /* Try /proc/stat first.  */
     198    result = get_nproc_stat ();
     199    if (result != 0)
     200      return result;
     201  
     202    /* Try sched_getaffinity.  */
     203    result = __get_nprocs_sched ();
     204    if (result != 0)
     205      return result;
     206  
     207    /* We failed to obtain an accurate number.  Be conservative: return
     208       the smallest number meaning that this is not a uniprocessor system,
     209       so atomics are needed.  */
     210    return 2;
     211  }
     212  
     213  int
     214  __get_nprocs (void)
     215  {
     216    int result = read_sysfs_file ("/sys/devices/system/cpu/online");
     217    if (result != 0)
     218      return result;
     219  
     220    /* Fall back to /proc/stat and sched_getaffinity.  */
     221    return get_nprocs_fallback ();
     222  }
     223  libc_hidden_def (__get_nprocs)
     224  weak_alias (__get_nprocs, get_nprocs)
     225  
     226  /* On some architectures it is possible to distinguish between configured
     227     and active cpus.  */
     228  int
     229  __get_nprocs_conf (void)
     230  {
     231    int result = read_sysfs_file ("/sys/devices/system/cpu/possible");
     232    if (result != 0)
     233      return result;
     234  
     235    /* Fall back to /proc/stat and sched_getaffinity.  */
     236    return get_nprocs_fallback ();
     237  }
     238  libc_hidden_def (__get_nprocs_conf)
     239  weak_alias (__get_nprocs_conf, get_nprocs_conf)
     240  
     241  
     242  /* Compute (num*mem_unit)/pagesize, but avoid overflowing long int.
     243     In practice, mem_unit is never bigger than the page size, so after
     244     the first loop it is 1.  [In the kernel, it is initialized to
     245     PAGE_SIZE in mm/page_alloc.c:si_meminfo(), and then in
     246     kernel.sys.c:do_sysinfo() it is set to 1 if unsigned long can
     247     represent all the sizes measured in bytes].  */
     248  static long int
     249  sysinfo_mempages (unsigned long int num, unsigned int mem_unit)
     250  {
     251    unsigned long int ps = __getpagesize ();
     252  
     253    while (mem_unit > 1 && ps > 1)
     254      {
     255        mem_unit >>= 1;
     256        ps >>= 1;
     257      }
     258    num *= mem_unit;
     259    while (ps > 1)
     260      {
     261        ps >>= 1;
     262        num >>= 1;
     263      }
     264    return num;
     265  }
     266  
     267  /* Return the number of pages of total/available physical memory in
     268     the system.  This used to be done by parsing /proc/meminfo, but
     269     that's unnecessarily expensive (and /proc is not always available).
     270     The sysinfo syscall provides the same information, and has been
     271     available at least since kernel 2.3.48.  */
     272  long int
     273  __get_phys_pages (void)
     274  {
     275    struct sysinfo info;
     276  
     277    __sysinfo (&info);
     278    return sysinfo_mempages (info.totalram, info.mem_unit);
     279  }
     280  libc_hidden_def (__get_phys_pages)
     281  weak_alias (__get_phys_pages, get_phys_pages)
     282  
     283  long int
     284  __get_avphys_pages (void)
     285  {
     286    struct sysinfo info;
     287  
     288    __sysinfo (&info);
     289    return sysinfo_mempages (info.freeram, info.mem_unit);
     290  }
     291  libc_hidden_def (__get_avphys_pages)
     292  weak_alias (__get_avphys_pages, get_avphys_pages)