casacore
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
HostInfoLinux.h
Go to the documentation of this file.
1 //# HostInfo_linux.h: Linux specific memory, swap, and CPU code.
2 //# $Id$
3 
4  /*
5  ** This is a greatly MODIFIED version of a "top" machine dependent file.
6  ** The only resemblance it bears to the original is with respect to the
7  ** mechanics of finding various system details. The copyright details
8  ** follow.
9  **
10  ** --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
11  **
12  ** Top users/processes display for Unix
13  ** Version 3
14  **
15  ** This program may be freely redistributed,
16  ** but this entire comment MUST remain intact.
17  **
18  ** Copyright (c) 1984, 1989, William LeFebvre, Rice University
19  ** Copyright (c) 1989 - 1994, William LeFebvre, Northwestern University
20  ** Copyright (c) 1994, 1995, William LeFebvre, Argonne National Laboratory
21  ** Copyright (c) 1996, William LeFebvre, Group sys Consulting
22  ** Copyright (c) 2002, Associated Universities Inc.
23  */
24 
25 #ifndef CASA_HOSTINFOLINUX_H
26 #define CASA_HOSTINFOLINUX_H
27 
28 # if defined(HOSTINFO_DO_IMPLEMENT)
29 
30 /*
31  * AUTHOR: Darrell Schiebel <drs@nrao.edu>
32  *
33  * ORIGINAL AUTHORS: Richard Henderson <rth@tamu.edu>
34  * Alexey Klimkin <kad@klon.tme.mcst.ru>
35  *
36  *
37  */
38 
39 #ifndef _GNU_SOURCE
40 #define _GNU_SOURCE
41 #endif
42 #include <sched.h>
43 #include <string.h>
44 #include <ctype.h>
45 #include <sys/types.h>
46 #include <sys/stat.h>
47 #include <sys/vfs.h>
48 #include <unistd.h>
49 #include <fcntl.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <fstream>
53 #include <iostream>
54 #include <string>
55 #include <sstream>
56 #include <vector>
57 #include <limits>
58 #include <sys/time.h>
59 #include <sys/resource.h>
60 
61 
62 // <summary>
63 // HostInfo for Linux machines.
64 // </summary>
65 
66 // <use visibility=local>
67 
68 // <reviewed reviewer="UNKNOWN" date="before2004/08/25" tests="" demos="">
69 // </reviewed>
70 
71 // <prerequisite>
72 // <li> <linkto class=HostInfo>HostInfo</linkto>
73 // </prerequisite>
74 
75 // <synopsis>
76 // This file provides the Linux specific functions for HostInfo.
77 // It is selectively included by HostInfo.cc.
78 // </synopsis>
79 //
80 // <group name="HostInfo">
81 
82 #if 0
83 #include <linux/proc_fs.h> /* for PROC_SUPER_MAGIC */
84 #else
85 #define PROC_SUPER_MAGIC 0x9fa0
86 #endif
87 
88 #define PROCFS "/proc"
89 #define CPUINFO "/proc/cpuinfo"
90 #define MEMINFO "/proc/meminfo"
91 
92 #define bytetok(x) (((x) + 512) >> 10)
93 
94 namespace casacore { //# NAMESPACE CASACORE - BEGIN
95 
96 class HostMachineInfo {
97 friend class HostInfo;
98 
99  HostMachineInfo( );
100  void update_info( );
101 
102  int valid;
103  int cpus;
104 
105  ptrdiff_t memory_total;
106  ptrdiff_t memory_used;
107  ptrdiff_t memory_free;
108 
109  ptrdiff_t swap_total;
110  ptrdiff_t swap_used;
111  ptrdiff_t swap_free;
112 };
113 
114 // </group>
115 
116 
117 static inline char *
118 skip_ws(const char *p)
119 {
120  while (isspace(*p)) p++;
121  return (char *)p;
122 }
123 
124 static inline char *
125 skip_token(const char *p)
126 {
127  while (isspace(*p)) p++;
128  while (*p && !isspace(*p)) p++;
129  return (char *)p;
130 }
131 
132 // get integer value from v1 cgroup hierarchy of current processes, if
133 // sub_value is set it returns the entry of a collection identified by value,
134 // e.g. total_rss from memory.stat
135 // returns std::numeric_limits<uInt64>::max() on error
136 // note unset cgroup limits usually have intptr_t.max()
137 // does not support v2 cgroups
138 static inline uInt64
139 get_cgroup_limit(std::string group, std::string value, std::string sub_value="")
140 {
142  // assume common location, technically one needs to search for mounts
143  const std::string cgroup = std::string("/sys/fs/cgroup/") + group + "/";
144 
145  // get hierarchy of current process, v1 format: id:controller:hierarchy
146  std::string line;
147  std::ifstream ifs("/proc/self/cgroup", std::ifstream::in);
148  std::string hierarchy;
149  while (getline(ifs, line)) {
150  std::stringstream ss(line);
151  std::string token;
152  std::vector<std::string> fields;
153  while (getline(ss, token, ':')) {
154  fields.push_back(token);
155  }
156  if (fields.size() % 3 != 0) {
157  return result;
158  }
159  for (std::vector<std::string>::size_type i=1; i < fields.size(); i+=3) {
160  if (fields[i].find(group) != std::string::npos) {
161  hierarchy = fields[i + 1] + "/";
162  }
163  }
164  }
165  if (hierarchy.size() == 0) {
166  return result;
167  }
168 
169  std::ifstream flimit((cgroup + hierarchy + value).c_str(), std::ifstream::in);
170  // if hierarchy does not exist we might be in a namespace, use the root group
171  if (!flimit.is_open()) {
172  flimit.open((cgroup + value).c_str(), std::ifstream::in);
173  }
174  if (flimit.is_open()) {
175  if (!sub_value.empty()) {
176  /* scan 'key value' entry like memory.stat for key == sub_value */
177  while (getline(flimit, line)) {
178  std::stringstream ss(line);
179  std::string token;
180  ss >> token;
181  if (token == sub_value) {
182  ss >> result;
183  break;
184  }
185  }
186  }
187  else {
188  flimit >> result;
189  }
190  }
191  return result;
192 }
193 
194 HostMachineInfo::HostMachineInfo( ) : valid(1)
195 {
196  char buffer[4096+1];
197  char *p;
198 
199  /* get number of usable CPUs */
200  cpu_set_t cpuset;
201  if (sched_getaffinity(0, sizeof(cpuset), &cpuset) == 0) {
202 # ifdef CPU_COUNT /* glibc < 2.6 */
203  cpus = CPU_COUNT(&cpuset);
204 # else
205  for (int i = 0; i < CPU_SETSIZE; i++) {
206  if (CPU_ISSET(i, &cpuset)) {
207  cpus++;
208  }
209  }
210 # endif
211  }
212  else {
213 #ifndef AIPS_CRAY_PGI
214  /* make sure the proc filesystem is mounted */
215  {
216  struct statfs sb;
217  if (statfs(PROCFS, &sb) < 0 || sb.f_type != PROC_SUPER_MAGIC)
218  {
219  fprintf( stderr, "proc filesystem not mounted on " PROCFS "\n" );
220  valid = 0;
221  return;
222  }
223  }
224 #endif
225 
226  /* get number of CPUs */
227  {
228  cpus = 0;
229  FILE *fptr = fopen(CPUINFO, "r");
230  while ( (p = fgets( buffer, sizeof(buffer), fptr )) ) {
231  if ( ! strncmp( p, "processor", 9 ) ) ++cpus;
232  }
233  fclose(fptr);
234  }
235  }
236 
237  update_info();
238 }
239 
240 void HostMachineInfo::update_info( )
241 {
242  char buffer[4096+1];
243  int fd, len;
244 
245  /* get system wide memory usage */
246  {
247  char *p;
248  unsigned long sys_mem_total, sys_mem_free, sys_mem_cached,
249  sys_mem_avail, sys_swap_total, sys_swap_free;
250 
251  fd = open(MEMINFO, O_RDONLY);
252  len = ::read(fd, buffer, sizeof(buffer)-1);
253  close(fd);
254  buffer[len] = '\0';
255 
256  p = strstr(buffer, "MemTotal:");
257 
258  if (sscanf (p,"MemTotal: %lu kB\nMemFree: %lu kB\n",
259  &sys_mem_total, &sys_mem_free) != 2)
260  cerr << "Error parsing MemTotal and MemFree in /proc/meminfo\n";
261 
262  p = strstr (buffer, "Cached:");
263  if (sscanf (p,"Cached: %lu kB\n", &sys_mem_cached) != 1)
264  cerr << "Error parsing Cached in /proc/meminfo\n";
265 
266  /* available since 3.14, real available memory accounting for
267  * unreclaimable page cache and slab cache */
268  p = strstr (buffer, "MemAvailable:");
269  if (!p || sscanf (p,"MemAvailable: %lu kB\n", &sys_mem_avail) != 1) {
270  /* too old kernel, estimate via cache and free */
271  sys_mem_avail = sys_mem_cached + sys_mem_free;
272  }
273 
274  /* check resource limits, note that these are not enforced by linux */
275  struct rlimit rlim;
276  if (getrlimit(RLIMIT_RSS, &rlim) == 0 && rlim.rlim_cur > 0) {
277  sys_mem_total = std::min(rlim.rlim_cur / 1024, (rlim_t)sys_mem_total);
278  /* without cgroups we cannot determine available memory within the limit */
279  sys_mem_avail = std::min(sys_mem_total, sys_mem_avail);
280  }
281 
282  /* can't use more memory than allowed by cgroups, enforced */
283  uInt64 proc_mem_max = get_cgroup_limit("memory", "memory.limit_in_bytes") / 1024;
284  /* usage_in_bytes also includes cache so use memory.stat */
285  uInt64 proc_mem_used = get_cgroup_limit("memory", "memory.stat", "total_rss") / 1024;
286 
287  /* set HostInfo memoryTotal() */
288  memory_total = std::min((uInt64)sys_mem_total, proc_mem_max);
289 
290  /* if we have a valid cgroup limit we can determine memoryFree() exactly */
291  if (proc_mem_max <= sys_mem_total && proc_mem_used <= proc_mem_max) {
292  memory_free = proc_mem_max - proc_mem_used;
293  }
294  else {
295  /* no cgroups so we have to assume all memory of host is available */
296  memory_free = std::min((uInt64)sys_mem_avail, (uInt64)memory_total);
297  }
298  memory_used = memory_total - memory_free;
299 
300  p = strstr (buffer, "SwapTotal:");
301  if (sscanf (p, "SwapTotal: %lu kB\nSwapFree: %lu kB\n",
302  &sys_swap_total, &sys_swap_free) != 2)
303  cerr << "Error parsing SwapTotal and SwapFree in /proc/meminfo\n";
304 
305  /* can't use more swap than allowed by cgroups */
306  uInt64 proc_swap_max = get_cgroup_limit("memory", "memory.memsw.limit_in_bytes") / 1024;
307  uInt64 proc_swap_used = get_cgroup_limit("memory", "memory.stat", "total_swap") / 1024;
308  /* limit is mem + swap */
309  if (proc_mem_max <= sys_mem_total && proc_mem_max <= proc_swap_max) {
310  proc_swap_max = proc_swap_max - proc_mem_max;
311  }
312 
313  /* set swapTotal() */
314  swap_total = std::min((uInt64)sys_swap_total, proc_swap_max);
315 
316  if (proc_swap_max <= (uInt64)swap_total && proc_swap_used <= proc_swap_max) {
317  swap_free = proc_swap_max - proc_swap_used;
318  }
319  else {
320  swap_free = sys_swap_free;
321  }
322  swap_used = swap_total - swap_free;
323  }
324 }
325 
326 } //# NAMESPACE CASACORE - END
327 
328 # endif
329 
330 #endif
LatticeExprNode max(const LatticeExprNode &left, const LatticeExprNode &right)
unsigned long long uInt64
Definition: aipsxtype.h:39
LatticeExprNode min(const LatticeExprNode &left, const LatticeExprNode &right)
LatticeExprNode value(const LatticeExprNode &expr)
This function returns the value of the expression without a mask.