1 /*######################################################################
3 # This file contains all functions necessary to provide system #
4 # information on a Linux 2.2-2.6 system. #
5 # All routines were taken from/based on code from top and/or procps. #
7 # With thanks to the original authors: #
8 # - James C. Warner <warnerjc@worldnet.att.net> #
9 # - Michael K. Johnson <johnsonm@redhat.com> #
10 # - Albert D. Cahalan, <albert@users.sf.net> #
11 # - Fabian Frederick #
13 # This file is placed under the conditions of the GNU Library #
14 # General Public License, version 2, or any later version. #
15 # See file COPYING for information on distribution conditions. #
17 ######################################################################*/
19 #include "sysinfo-linux.h" /* include self to verify prototypes */
21 #include "standards.h"
23 #define BAD_OPEN_MESSAGE \
24 "Error: /proc must be mounted\n" \
25 " To mount /proc at boot you need an /etc/fstab line like:\n" \
26 " /proc /proc proc defaults\n" \
27 " In the meantime, mount /proc /proc -t proc\n"
29 #define MEMINFO_FILE "/proc/meminfo"
30 static int meminfo_fd
= -1;
32 static char buf
[1024];
34 /* assume no IO-wait stats (default kernel 2.4.x),
35 overridden if linux 2.5.x or 2.6.x */
36 static const char *States_fmts
= STATES_line2x4
;
39 /* This macro opens filename only if necessary and seeks to 0 so
40 * that successive calls to the functions are more efficient.
41 * It also reads the current contents of the file into the global buf.
43 #define FILE_TO_BUF(filename, fd) do { \
45 if (fd == -1 && (fd = open(filename, O_RDONLY)) == -1) {\
46 fprintf(stderr, BAD_OPEN_MESSAGE); \
50 lseek(fd, 0L, SEEK_SET); \
51 if ((local_n = read(fd, buf, sizeof buf - 1)) < 0) {\
56 buf[local_n] = '\0'; \
59 #define LINUX_VERSION(x,y,z) (0x10000*(x) + 0x100*(y) + z)
62 /***********************************************/
63 /* get number of CPUs - max. 255 are supported */
64 /* also, perform some initialisation */
65 /* code taken from top and procps */
66 /***********************************************/
67 unsigned int NumCpus_DoInit(void)
70 int linux_version_code
;
72 static struct utsname uts
;
73 int x
= 0, y
= 0, z
= 0; /* cleared in case sscanf() < 3 */
75 if (uname(&uts
) == -1) /* failure implies impending death */
77 if (sscanf(uts
.release
, "%d.%d.%d", &x
, &y
, &z
) < 3)
78 fprintf(stderr
, /* *very* unlikely to happen by accident */
79 "Non-standard uts for running kernel:\n"
80 "release %s=%d.%d.%d gives version code %d\n",
81 uts
.release
, x
, y
, z
, LINUX_VERSION(x
,y
,z
));
82 linux_version_code
= LINUX_VERSION(x
, y
, z
);
84 if (linux_version_code
> LINUX_VERSION(2, 5, 41))
85 States_fmts
= STATES_line2x5
;
86 if (linux_version_code
>= LINUX_VERSION(2, 6, 0)) {
87 // grrr... only some 2.6.0-testX :-(
88 States_fmts
= STATES_line2x6
;
91 smp_num_cpus
= sysconf(_SC_NPROCESSORS_CONF
); // or _SC_NPROCESSORS_ONLN
92 if (smp_num_cpus
< 1) {
93 smp_num_cpus
= 1; /* SPARC glibc is buggy */
96 if (smp_num_cpus
> 255) {
97 /* we don't support more than 255 CPUs (well, in fact no more
98 than two ate the moment... */
102 return (int)smp_num_cpus
;
105 /***********************************************************************/
107 * Copyright 1999 by Albert Cahalan; all rights reserved.
108 * This file may be used subject to the terms and conditions of the
109 * GNU Library General Public License Version 2, or any later version
110 * at your option, as published by the Free Software Foundation.
111 * This program is distributed in the hope that it will be useful,
112 * but WITHOUT ANY WARRANTY; without even the implied warranty of
113 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
114 * GNU Library General Public License for more details.
116 typedef struct mem_table_struct
{
117 const char *name
; /* memory type name */
118 unsigned long *slot
; /* slot in return struct */
121 static int compare_mem_table_structs(const void *a
, const void *b
)
123 return strcmp(((const mem_table_struct
*) a
)->name
,
124 ((const mem_table_struct
*) b
)->name
);
127 /* example data, following junk, with comments added:
129 * MemTotal: 61768 kB old
130 * MemFree: 1436 kB old
131 * MemShared: 0 kB old (now always zero; not calculated)
132 * Buffers: 1312 kB old
133 * Cached: 20932 kB old
134 * Active: 12464 kB new
135 * Inact_dirty: 7772 kB new
136 * Inact_clean: 2008 kB new
137 * Inact_target: 0 kB new
138 * Inact_laundry: 0 kB new, and might be missing too
143 * SwapTotal: 122580 kB old
144 * SwapFree: 60352 kB old
145 * Inactive: 20420 kB 2.5.41+
146 * Dirty: 0 kB 2.5.41+
147 * Writeback: 0 kB 2.5.41+
148 * Mapped: 9792 kB 2.5.41+
149 * Slab: 4564 kB 2.5.41+
150 * Committed_AS: 8440 kB 2.5.41+
151 * PageTables: 304 kB 2.5.41+
152 * ReverseMaps: 5738 2.5.41+
153 * SwapCached: 0 kB 2.5.??+
154 * HugePages_Total: 220 2.5.??+
155 * HugePages_Free: 138 2.5.??+
156 * Hugepagesize: 4096 kB 2.5.??+
160 unsigned long kb_main_shared
;
161 /* old but still kicking -- the important stuff */
162 unsigned long kb_main_buffers
;
163 unsigned long kb_main_cached
;
164 unsigned long kb_main_free
;
165 unsigned long kb_main_total
;
166 unsigned long kb_swap_free
;
167 unsigned long kb_swap_total
;
168 /* recently introduced */
169 unsigned long kb_high_free
;
170 unsigned long kb_high_total
;
171 unsigned long kb_low_free
;
172 unsigned long kb_low_total
;
174 unsigned long kb_active
;
175 unsigned long kb_inact_laundry
;
176 unsigned long kb_inact_dirty
;
177 unsigned long kb_inact_clean
;
178 unsigned long kb_inact_target
;
179 unsigned long kb_swap_cached
; /* late 2.4 only */
181 unsigned long kb_swap_used
;
182 unsigned long kb_main_used
;
184 unsigned long kb_writeback
;
185 unsigned long kb_slab
;
186 unsigned long nr_reversemaps
;
187 unsigned long kb_committed_as
;
188 unsigned long kb_dirty
;
189 unsigned long kb_inactive
;
190 unsigned long kb_mapped
;
191 unsigned long kb_pagetables
;
193 static void meminfo(void)
195 char namebuf
[16]; /* big enough to hold any row name */
196 mem_table_struct findme
= { namebuf
, NULL
};
197 mem_table_struct
*found
;
200 static const mem_table_struct mem_table
[] = {
201 {"Active", &kb_active
}, // important
202 {"Buffers", &kb_main_buffers
}, // important
203 {"Cached", &kb_main_cached
}, // important
204 {"Committed_AS", &kb_committed_as
},
205 {"Dirty", &kb_dirty
}, // kB version of vmstat nr_dirty
206 {"HighFree", &kb_high_free
},
207 {"HighTotal", &kb_high_total
},
208 {"Inact_clean", &kb_inact_clean
},
209 {"Inact_dirty", &kb_inact_dirty
},
210 {"Inact_laundry",&kb_inact_laundry
},
211 {"Inact_target", &kb_inact_target
},
212 {"Inactive", &kb_inactive
}, // important
213 {"LowFree", &kb_low_free
},
214 {"LowTotal", &kb_low_total
},
215 {"Mapped", &kb_mapped
}, // kB version of vmstat nr_mapped
216 {"MemFree", &kb_main_free
}, // important
217 {"MemShared", &kb_main_shared
}, // important
218 {"MemTotal", &kb_main_total
}, // important
219 {"PageTables", &kb_pagetables
}, // kB version of vmstat
220 // nr_page_table_pages
221 {"ReverseMaps", &nr_reversemaps
}, // same as vmstat
222 // nr_page_table_pages
223 {"Slab", &kb_slab
}, // kB version of vmstat nr_slab
224 {"SwapCached", &kb_swap_cached
},
225 {"SwapFree", &kb_swap_free
}, // important
226 {"SwapTotal", &kb_swap_total
}, // important
227 {"Writeback", &kb_writeback
}, // kB version of vmstat
230 const int mem_table_count
=
231 sizeof(mem_table
) / sizeof(mem_table_struct
);
233 FILE_TO_BUF(MEMINFO_FILE
,meminfo_fd
);
239 tail
= strchr(head
, ':');
243 if (strlen(head
) >= sizeof(namebuf
)) {
247 strcpy(namebuf
, head
);
248 found
= bsearch(&findme
, mem_table
, mem_table_count
,
249 sizeof(mem_table_struct
), compare_mem_table_structs
);
253 *(found
->slot
) = strtoul(head
, &tail
, 10);
255 tail
= strchr(head
, '\n');
260 if (!kb_low_total
) { /* low==main except with large-memory support */
261 kb_low_total
= kb_main_total
;
262 kb_low_free
= kb_main_free
;
264 if (kb_inactive
== ~0UL) {
265 kb_inactive
= kb_inact_dirty
+ kb_inact_clean
+ kb_inact_laundry
;
267 kb_swap_used
= kb_swap_total
- kb_swap_free
;
268 kb_main_used
= kb_main_total
- kb_main_free
;
271 /*************************************************************************/
273 * This guy's modeled on libproc's 'five_cpu_numbers' function except
274 * we preserve all cpu data in our CPU_t array which is organized
276 * cpus[0] thru cpus[n] == tics for each separate cpu
277 * cpus[Cpu_tot] == tics from the 1st /proc/stat line */
278 static CPU_t
*cpus_refresh (CPU_t
*cpus
, unsigned int Cpu_tot
)
280 static FILE *fp
= NULL
;
283 // enough for a /proc/stat CPU line (not the intr line)
286 /* by opening this file once, we'll avoid the hit on minor page faults
287 (sorry Linux, but you'll have to close it for us) */
289 if (!(fp
= fopen("/proc/stat", "r")))
290 std_err(fmtmk("Failed /proc/stat open: %s",
292 /* note: we allocate one more CPU_t than Cpu_tot so that the
293 last slot can hold tics representing the /proc/stat cpu
294 summary (the first line read) -- that slot supports our
295 View_CPUSUM toggle */
296 cpus
= alloc_c((1 + Cpu_tot
) * sizeof(CPU_t
));
301 // first value the last slot with the cpu summary line
302 if (!fgets(buf
, sizeof(buf
), fp
))
303 std_err("failed /proc/stat read");
305 cpus
[Cpu_tot
].x
= 0; // FIXME: can't tell by kernel version number
306 cpus
[Cpu_tot
].y
= 0; // FIXME: can't tell by kernel version number
307 if (4 > sscanf(buf
, CPU_FMTS_JUST1
, &cpus
[Cpu_tot
].u
, &cpus
[Cpu_tot
].n
,
308 &cpus
[Cpu_tot
].s
, &cpus
[Cpu_tot
].i
, &cpus
[Cpu_tot
].w
,
309 &cpus
[Cpu_tot
].x
, &cpus
[Cpu_tot
].y
))
310 std_err("failed /proc/stat read");
311 // and just in case we're 2.2.xx compiled without SMP support...
313 /* do it "manually", otherwise we overwrite charge and total */
314 cpus
[0].u
= cpus
[1].u
;
315 cpus
[0].n
= cpus
[1].n
;
316 cpus
[0].s
= cpus
[1].s
;
317 cpus
[0].i
= cpus
[1].i
;
318 cpus
[0].w
= cpus
[1].w
;
319 cpus
[0].x
= cpus
[1].x
;
320 cpus
[0].y
= cpus
[1].y
;
323 // now value each separate cpu's tics
324 for (i
= 0; 1 < Cpu_tot
&& i
< Cpu_tot
; i
++) {
325 if (!fgets(buf
, sizeof(buf
), fp
))
326 std_err("failed /proc/stat read");
327 cpus
[i
].x
= 0; // FIXME: can't tell by kernel version number
328 cpus
[i
].y
= 0; // FIXME: can't tell by kernel version number
329 if (4 > sscanf(buf
, CPU_FMTS_MULTI
, &cpus
[i
].u
,
330 &cpus
[i
].n
, &cpus
[i
].s
, &cpus
[i
].i
, &cpus
[i
].w
,
331 &cpus
[i
].x
, &cpus
[i
].y
))
332 std_err("failed /proc/stat read");
337 unsigned int *Get_CPU_Load(unsigned int *load
, unsigned int Cpu_tot
)
339 static CPU_t
*smpcpu
= NULL
;
341 register unsigned long charge
, total
= 0;
343 smpcpu
= cpus_refresh(smpcpu
, Cpu_tot
);
345 for (j
= 0; j
< Cpu_tot
; j
++) {
346 charge
= smpcpu
[j
].u
+ smpcpu
[j
].s
+ smpcpu
[j
].n
;
347 total
= charge
+ smpcpu
[j
].i
;
349 /* scale cpu to a maximum of HAUTEUR */
350 load
[j
] = ((HAUTEUR
* (charge
- smpcpu
[j
].charge
)) /
351 (total
- smpcpu
[j
].total
+ 0.001)) + 1 ;
352 smpcpu
[j
].total
= total
;
353 smpcpu
[j
].charge
= charge
;
359 unsigned int Get_Memory(void)
363 return ((kb_main_used
- kb_main_cached
) / (kb_main_total
/ 100));
364 /* should be between 0 and 100 now */
367 unsigned int Get_Swap(void)
369 /* returns swap usage as value between 0 and 100
370 * OR 999 if no swap present */
373 return (kb_swap_total
== 0 ? 999 : kb_swap_used
/ (kb_swap_total
/ 100));