Blame sysdeps/unix/sysv/linux/getsysstats.c

Packit Service 82fcde
/* Determine various system internal values, Linux version.
Packit Service 82fcde
   Copyright (C) 1996-2018 Free Software Foundation, Inc.
Packit Service 82fcde
   This file is part of the GNU C Library.
Packit Service 82fcde
   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
Packit Service 82fcde
Packit Service 82fcde
   The GNU C Library is free software; you can redistribute it and/or
Packit Service 82fcde
   modify it under the terms of the GNU Lesser General Public
Packit Service 82fcde
   License as published by the Free Software Foundation; either
Packit Service 82fcde
   version 2.1 of the License, or (at your option) any later version.
Packit Service 82fcde
Packit Service 82fcde
   The GNU C Library is distributed in the hope that it will be useful,
Packit Service 82fcde
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 82fcde
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service 82fcde
   Lesser General Public License for more details.
Packit Service 82fcde
Packit Service 82fcde
   You should have received a copy of the GNU Lesser General Public
Packit Service 82fcde
   License along with the GNU C Library; if not, see
Packit Service 82fcde
   <http://www.gnu.org/licenses/>.  */
Packit Service 82fcde
Packit Service 82fcde
#include <alloca.h>
Packit Service 82fcde
#include <assert.h>
Packit Service 82fcde
#include <ctype.h>
Packit Service 82fcde
#include <dirent.h>
Packit Service 82fcde
#include <errno.h>
Packit Service 82fcde
#include <fcntl.h>
Packit Service 82fcde
#include <mntent.h>
Packit Service 82fcde
#include <paths.h>
Packit Service 82fcde
#include <stdio.h>
Packit Service 82fcde
#include <stdio_ext.h>
Packit Service 82fcde
#include <stdlib.h>
Packit Service 82fcde
#include <string.h>
Packit Service 82fcde
#include <unistd.h>
Packit Service 82fcde
#include <sys/sysinfo.h>
Packit Service 82fcde
Packit Service 82fcde
#include <atomic.h>
Packit Service 82fcde
#include <not-cancel.h>
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
/* How we can determine the number of available processors depends on
Packit Service 82fcde
   the configuration.  There is currently (as of version 2.0.21) no
Packit Service 82fcde
   system call to determine the number.  It is planned for the 2.1.x
Packit Service 82fcde
   series to add this, though.
Packit Service 82fcde
Packit Service 82fcde
   One possibility to implement it for systems using Linux 2.0 is to
Packit Service 82fcde
   examine the pseudo file /proc/cpuinfo.  Here we have one entry for
Packit Service 82fcde
   each processor.
Packit Service 82fcde
Packit Service 82fcde
   But not all systems have support for the /proc filesystem.  If it
Packit Service 82fcde
   is not available we simply return 1 since there is no way.  */
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
/* Other architectures use different formats for /proc/cpuinfo.  This
Packit Service 82fcde
   provides a hook for alternative parsers.  */
Packit Service 82fcde
#ifndef GET_NPROCS_PARSER
Packit Service 82fcde
# define GET_NPROCS_PARSER(FD, BUFFER, CP, RE, BUFFER_END, RESULT) \
Packit Service 82fcde
  do									\
Packit Service 82fcde
    {									\
Packit Service 82fcde
      (RESULT) = 0;							\
Packit Service 82fcde
      /* Read all lines and count the lines starting with the string	\
Packit Service 82fcde
	 "processor".  We don't have to fear extremely long lines since	\
Packit Service 82fcde
	 the kernel will not generate them.  8192 bytes are really	\
Packit Service 82fcde
	 enough.  */							\
Packit Service 82fcde
      char *l;								\
Packit Service 82fcde
      while ((l = next_line (FD, BUFFER, &CP, &RE, BUFFER_END)) != NULL) \
Packit Service 82fcde
	if (strncmp (l, "processor", 9) == 0)				\
Packit Service 82fcde
	  ++(RESULT);							\
Packit Service 82fcde
    }									\
Packit Service 82fcde
  while (0)
Packit Service 82fcde
#endif
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
static char *
Packit Service 82fcde
next_line (int fd, char *const buffer, char **cp, char **re,
Packit Service 82fcde
	   char *const buffer_end)
Packit Service 82fcde
{
Packit Service 82fcde
  char *res = *cp;
Packit Service 82fcde
  char *nl = memchr (*cp, '\n', *re - *cp);
Packit Service 82fcde
  if (nl == NULL)
Packit Service 82fcde
    {
Packit Service 82fcde
      if (*cp != buffer)
Packit Service 82fcde
	{
Packit Service 82fcde
	  if (*re == buffer_end)
Packit Service 82fcde
	    {
Packit Service 82fcde
	      memmove (buffer, *cp, *re - *cp);
Packit Service 82fcde
	      *re = buffer + (*re - *cp);
Packit Service 82fcde
	      *cp = buffer;
Packit Service 82fcde
Packit Service 82fcde
	      ssize_t n = __read_nocancel (fd, *re, buffer_end - *re);
Packit Service 82fcde
	      if (n < 0)
Packit Service 82fcde
		return NULL;
Packit Service 82fcde
Packit Service 82fcde
	      *re += n;
Packit Service 82fcde
Packit Service 82fcde
	      nl = memchr (*cp, '\n', *re - *cp);
Packit Service 82fcde
	      while (nl == NULL && *re == buffer_end)
Packit Service 82fcde
		{
Packit Service 82fcde
		  /* Truncate too long lines.  */
Packit Service 82fcde
		  *re = buffer + 3 * (buffer_end - buffer) / 4;
Packit Service 82fcde
		  n = __read_nocancel (fd, *re, buffer_end - *re);
Packit Service 82fcde
		  if (n < 0)
Packit Service 82fcde
		    return NULL;
Packit Service 82fcde
Packit Service 82fcde
		  nl = memchr (*re, '\n', n);
Packit Service 82fcde
		  **re = '\n';
Packit Service 82fcde
		  *re += n;
Packit Service 82fcde
		}
Packit Service 82fcde
	    }
Packit Service 82fcde
	  else
Packit Service 82fcde
	    nl = memchr (*cp, '\n', *re - *cp);
Packit Service 82fcde
Packit Service 82fcde
	  res = *cp;
Packit Service 82fcde
	}
Packit Service 82fcde
Packit Service 82fcde
      if (nl == NULL)
Packit Service 82fcde
	nl = *re - 1;
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  *cp = nl + 1;
Packit Service 82fcde
  assert (*cp <= *re);
Packit Service 82fcde
Packit Service 82fcde
  return res == *re ? NULL : res;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
int
Packit Service 82fcde
__get_nprocs (void)
Packit Service 82fcde
{
Packit Service 82fcde
  static int cached_result = -1;
Packit Service 82fcde
  static time_t timestamp;
Packit Service 82fcde
Packit Service 82fcde
  time_t now = time (NULL);
Packit Service 82fcde
  time_t prev = timestamp;
Packit Service 82fcde
  atomic_read_barrier ();
Packit Service 82fcde
  if (now == prev && cached_result > -1)
Packit Service 82fcde
    return cached_result;
Packit Service 82fcde
Packit Service 82fcde
  /* XXX Here will come a test for the new system call.  */
Packit Service 82fcde
Packit Service 82fcde
  const size_t buffer_size = __libc_use_alloca (8192) ? 8192 : 512;
Packit Service 82fcde
  char *buffer = alloca (buffer_size);
Packit Service 82fcde
  char *buffer_end = buffer + buffer_size;
Packit Service 82fcde
  char *cp = buffer_end;
Packit Service 82fcde
  char *re = buffer_end;
Packit Service 82fcde
Packit Service 82fcde
  const int flags = O_RDONLY | O_CLOEXEC;
Packit Service 82fcde
  int fd = __open_nocancel ("/sys/devices/system/cpu/online", flags);
Packit Service 82fcde
  char *l;
Packit Service 82fcde
  int result = 0;
Packit Service 82fcde
  if (fd != -1)
Packit Service 82fcde
    {
Packit Service 82fcde
      l = next_line (fd, buffer, &cp, &re, buffer_end);
Packit Service 82fcde
      if (l != NULL)
Packit Service 82fcde
	do
Packit Service 82fcde
	  {
Packit Service 82fcde
	    char *endp;
Packit Service 82fcde
	    unsigned long int n = strtoul (l, &endp, 10);
Packit Service 82fcde
	    if (l == endp)
Packit Service 82fcde
	      {
Packit Service 82fcde
		result = 0;
Packit Service 82fcde
		break;
Packit Service 82fcde
	      }
Packit Service 82fcde
Packit Service 82fcde
	    unsigned long int m = n;
Packit Service 82fcde
	    if (*endp == '-')
Packit Service 82fcde
	      {
Packit Service 82fcde
		l = endp + 1;
Packit Service 82fcde
		m = strtoul (l, &endp, 10);
Packit Service 82fcde
		if (l == endp)
Packit Service 82fcde
		  {
Packit Service 82fcde
		    result = 0;
Packit Service 82fcde
		    break;
Packit Service 82fcde
		  }
Packit Service 82fcde
	      }
Packit Service 82fcde
Packit Service 82fcde
	    result += m - n + 1;
Packit Service 82fcde
Packit Service 82fcde
	    l = endp;
Packit Service 82fcde
	    while (l < re && isspace (*l))
Packit Service 82fcde
	      ++l;
Packit Service 82fcde
	  }
Packit Service 82fcde
	while (l < re);
Packit Service 82fcde
Packit Service 82fcde
      __close_nocancel_nostatus (fd);
Packit Service 82fcde
Packit Service 82fcde
      if (result > 0)
Packit Service 82fcde
	goto out;
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  cp = buffer_end;
Packit Service 82fcde
  re = buffer_end;
Packit Service 82fcde
Packit Service 82fcde
  /* Default to an SMP system in case we cannot obtain an accurate
Packit Service 82fcde
     number.  */
Packit Service 82fcde
  result = 2;
Packit Service 82fcde
Packit Service 82fcde
  /* The /proc/stat format is more uniform, use it by default.  */
Packit Service 82fcde
  fd = __open_nocancel ("/proc/stat", flags);
Packit Service 82fcde
  if (fd != -1)
Packit Service 82fcde
    {
Packit Service 82fcde
      result = 0;
Packit Service 82fcde
Packit Service 82fcde
      while ((l = next_line (fd, buffer, &cp, &re, buffer_end)) != NULL)
Packit Service 82fcde
	/* The current format of /proc/stat has all the cpu* entries
Packit Service 82fcde
	   at the front.  We assume here that stays this way.  */
Packit Service 82fcde
	if (strncmp (l, "cpu", 3) != 0)
Packit Service 82fcde
	  break;
Packit Service 82fcde
	else if (isdigit (l[3]))
Packit Service 82fcde
	  ++result;
Packit Service 82fcde
Packit Service 82fcde
      __close_nocancel_nostatus (fd);
Packit Service 82fcde
    }
Packit Service 82fcde
  else
Packit Service 82fcde
    {
Packit Service 82fcde
      fd = __open_nocancel ("/proc/cpuinfo", flags);
Packit Service 82fcde
      if (fd != -1)
Packit Service 82fcde
	{
Packit Service 82fcde
	  GET_NPROCS_PARSER (fd, buffer, cp, re, buffer_end, result);
Packit Service 82fcde
	  __close_nocancel_nostatus (fd);
Packit Service 82fcde
	}
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
 out:
Packit Service 82fcde
  cached_result = result;
Packit Service 82fcde
  atomic_write_barrier ();
Packit Service 82fcde
  timestamp = now;
Packit Service 82fcde
Packit Service 82fcde
  return result;
Packit Service 82fcde
}
Packit Service 82fcde
libc_hidden_def (__get_nprocs)
Packit Service 82fcde
weak_alias (__get_nprocs, get_nprocs)
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
/* On some architectures it is possible to distinguish between configured
Packit Service 82fcde
   and active cpus.  */
Packit Service 82fcde
int
Packit Service 82fcde
__get_nprocs_conf (void)
Packit Service 82fcde
{
Packit Service 82fcde
  /* XXX Here will come a test for the new system call.  */
Packit Service 82fcde
Packit Service 82fcde
  /* Try to use the sysfs filesystem.  It has actual information about
Packit Service 82fcde
     online processors.  */
Packit Service 82fcde
  DIR *dir = __opendir ("/sys/devices/system/cpu");
Packit Service 82fcde
  if (dir != NULL)
Packit Service 82fcde
    {
Packit Service 82fcde
      int count = 0;
Packit Service 82fcde
      struct dirent64 *d;
Packit Service 82fcde
Packit Service 82fcde
      while ((d = __readdir64 (dir)) != NULL)
Packit Service 82fcde
	/* NB: the sysfs has d_type support.  */
Packit Service 82fcde
	if (d->d_type == DT_DIR && strncmp (d->d_name, "cpu", 3) == 0)
Packit Service 82fcde
	  {
Packit Service 82fcde
	    char *endp;
Packit Service 82fcde
	    unsigned long int nr = strtoul (d->d_name + 3, &endp, 10);
Packit Service 82fcde
	    if (nr != ULONG_MAX && endp != d->d_name + 3 && *endp == '\0')
Packit Service 82fcde
	      ++count;
Packit Service 82fcde
	  }
Packit Service 82fcde
Packit Service 82fcde
      __closedir (dir);
Packit Service 82fcde
Packit Service 82fcde
      return count;
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  int result = 1;
Packit Service 82fcde
Packit Service 82fcde
#ifdef GET_NPROCS_CONF_PARSER
Packit Service 82fcde
  /* If we haven't found an appropriate entry return 1.  */
Packit Service 82fcde
  FILE *fp = fopen ("/proc/cpuinfo", "rce");
Packit Service 82fcde
  if (fp != NULL)
Packit Service 82fcde
    {
Packit Service 82fcde
      char buffer[8192];
Packit Service 82fcde
Packit Service 82fcde
      /* No threads use this stream.  */
Packit Service 82fcde
      __fsetlocking (fp, FSETLOCKING_BYCALLER);
Packit Service 82fcde
      GET_NPROCS_CONF_PARSER (fp, buffer, result);
Packit Service 82fcde
      fclose (fp);
Packit Service 82fcde
    }
Packit Service 82fcde
#else
Packit Service 82fcde
  result = __get_nprocs ();
Packit Service 82fcde
#endif
Packit Service 82fcde
Packit Service 82fcde
  return result;
Packit Service 82fcde
}
Packit Service 82fcde
libc_hidden_def (__get_nprocs_conf)
Packit Service 82fcde
weak_alias (__get_nprocs_conf, get_nprocs_conf)
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
/* Compute (num*mem_unit)/pagesize, but avoid overflowing long int.
Packit Service 82fcde
   In practice, mem_unit is never bigger than the page size, so after
Packit Service 82fcde
   the first loop it is 1.  [In the kernel, it is initialized to
Packit Service 82fcde
   PAGE_SIZE in mm/page_alloc.c:si_meminfo(), and then in
Packit Service 82fcde
   kernel.sys.c:do_sysinfo() it is set to 1 if unsigned long can
Packit Service 82fcde
   represent all the sizes measured in bytes].  */
Packit Service 82fcde
static long int
Packit Service 82fcde
sysinfo_mempages (unsigned long int num, unsigned int mem_unit)
Packit Service 82fcde
{
Packit Service 82fcde
  unsigned long int ps = __getpagesize ();
Packit Service 82fcde
Packit Service 82fcde
  while (mem_unit > 1 && ps > 1)
Packit Service 82fcde
    {
Packit Service 82fcde
      mem_unit >>= 1;
Packit Service 82fcde
      ps >>= 1;
Packit Service 82fcde
    }
Packit Service 82fcde
  num *= mem_unit;
Packit Service 82fcde
  while (ps > 1)
Packit Service 82fcde
    {
Packit Service 82fcde
      ps >>= 1;
Packit Service 82fcde
      num >>= 1;
Packit Service 82fcde
    }
Packit Service 82fcde
  return num;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
/* Return the number of pages of total/available physical memory in
Packit Service 82fcde
   the system.  This used to be done by parsing /proc/meminfo, but
Packit Service 82fcde
   that's unnecessarily expensive (and /proc is not always available).
Packit Service 82fcde
   The sysinfo syscall provides the same information, and has been
Packit Service 82fcde
   available at least since kernel 2.3.48.  */
Packit Service 82fcde
long int
Packit Service 82fcde
__get_phys_pages (void)
Packit Service 82fcde
{
Packit Service 82fcde
  struct sysinfo info;
Packit Service 82fcde
Packit Service 82fcde
  __sysinfo (&info;;
Packit Service 82fcde
  return sysinfo_mempages (info.totalram, info.mem_unit);
Packit Service 82fcde
}
Packit Service 82fcde
libc_hidden_def (__get_phys_pages)
Packit Service 82fcde
weak_alias (__get_phys_pages, get_phys_pages)
Packit Service 82fcde
Packit Service 82fcde
long int
Packit Service 82fcde
__get_avphys_pages (void)
Packit Service 82fcde
{
Packit Service 82fcde
  struct sysinfo info;
Packit Service 82fcde
Packit Service 82fcde
  __sysinfo (&info;;
Packit Service 82fcde
  return sysinfo_mempages (info.freeram, info.mem_unit);
Packit Service 82fcde
}
Packit Service 82fcde
libc_hidden_def (__get_avphys_pages)
Packit Service 82fcde
weak_alias (__get_avphys_pages, get_avphys_pages)