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