854008cac8de5ad71c38e1e7fb594a3afd69c877
[dockapps.git] / wmSMPmon / wmSMPmon / sysinfo-linux.c
blob854008cac8de5ad71c38e1e7fb594a3afd69c877
1 /*######################################################################
2 # #
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. #
6 # #
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 #
12 # #
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. #
16 # #
17 ######################################################################*/
19 #include "sysinfo-linux.h" /* include self to verify prototypes */
20 #include "general.h"
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{ \
44 static int local_n; \
45 if (fd == -1 && (fd = open(filename, O_RDONLY)) == -1) { \
46 fprintf(stderr, BAD_OPEN_MESSAGE); \
47 fflush(NULL); \
48 _exit(102); \
49 } \
50 lseek(fd, 0L, SEEK_SET); \
51 if ((local_n = read(fd, buf, sizeof buf - 1)) < 0) { \
52 perror(filename); \
53 fflush(NULL); \
54 _exit(103); \
55 } \
56 buf[local_n] = '\0'; \
57 }while(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)
69 long smp_num_cpus;
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 */
76 exit(1);
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)) // grrr... only
87 // some 2.6.0-testX :-(
88 States_fmts = STATES_line2x6;
90 smp_num_cpus = sysconf(_SC_NPROCESSORS_CONF); // or _SC_NPROCESSORS_ONLN
91 if (smp_num_cpus < 1)
93 smp_num_cpus = 1; /* SPARC glibc is buggy */
96 if (smp_num_cpus > 255)
98 /* we don't support more than 255 CPUs (well, in fact no more
99 than two ate the moment... */
100 smp_num_cpus = 255;
103 return (int)smp_num_cpus;
106 /***********************************************************************/
108 * Copyright 1999 by Albert Cahalan; all rights reserved.
109 * This file may be used subject to the terms and conditions of the
110 * GNU Library General Public License Version 2, or any later version
111 * at your option, as published by the Free Software Foundation.
112 * This program is distributed in the hope that it will be useful,
113 * but WITHOUT ANY WARRANTY; without even the implied warranty of
114 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
115 * GNU Library General Public License for more details.
117 typedef struct mem_table_struct {
118 const char *name; /* memory type name */
119 unsigned long *slot; /* slot in return struct */
120 } mem_table_struct;
122 static int compare_mem_table_structs(const void *a, const void *b){
123 return strcmp(((const mem_table_struct*)a)->name,((const mem_table_struct*)b)->name);
126 /* example data, following junk, with comments added:
128 * MemTotal: 61768 kB old
129 * MemFree: 1436 kB old
130 * MemShared: 0 kB old (now always zero; not calculated)
131 * Buffers: 1312 kB old
132 * Cached: 20932 kB old
133 * Active: 12464 kB new
134 * Inact_dirty: 7772 kB new
135 * Inact_clean: 2008 kB new
136 * Inact_target: 0 kB new
137 * Inact_laundry: 0 kB new, and might be missing too
138 * HighTotal: 0 kB
139 * HighFree: 0 kB
140 * LowTotal: 61768 kB
141 * LowFree: 1436 kB
142 * SwapTotal: 122580 kB old
143 * SwapFree: 60352 kB old
144 * Inactive: 20420 kB 2.5.41+
145 * Dirty: 0 kB 2.5.41+
146 * Writeback: 0 kB 2.5.41+
147 * Mapped: 9792 kB 2.5.41+
148 * Slab: 4564 kB 2.5.41+
149 * Committed_AS: 8440 kB 2.5.41+
150 * PageTables: 304 kB 2.5.41+
151 * ReverseMaps: 5738 2.5.41+
152 * SwapCached: 0 kB 2.5.??+
153 * HugePages_Total: 220 2.5.??+
154 * HugePages_Free: 138 2.5.??+
155 * Hugepagesize: 4096 kB 2.5.??+
158 /* obsolete */
159 unsigned long kb_main_shared;
160 /* old but still kicking -- the important stuff */
161 unsigned long kb_main_buffers;
162 unsigned long kb_main_cached;
163 unsigned long kb_main_free;
164 unsigned long kb_main_total;
165 unsigned long kb_swap_free;
166 unsigned long kb_swap_total;
167 /* recently introduced */
168 unsigned long kb_high_free;
169 unsigned long kb_high_total;
170 unsigned long kb_low_free;
171 unsigned long kb_low_total;
172 /* 2.4.xx era */
173 unsigned long kb_active;
174 unsigned long kb_inact_laundry;
175 unsigned long kb_inact_dirty;
176 unsigned long kb_inact_clean;
177 unsigned long kb_inact_target;
178 unsigned long kb_swap_cached; /* late 2.4 only */
179 /* derived values */
180 unsigned long kb_swap_used;
181 unsigned long kb_main_used;
182 /* 2.5.41+ */
183 unsigned long kb_writeback;
184 unsigned long kb_slab;
185 unsigned long nr_reversemaps;
186 unsigned long kb_committed_as;
187 unsigned long kb_dirty;
188 unsigned long kb_inactive;
189 unsigned long kb_mapped;
190 unsigned long kb_pagetables;
192 static void meminfo(void){
193 char namebuf[16]; /* big enough to hold any row name */
194 mem_table_struct findme = { namebuf, NULL};
195 mem_table_struct *found;
196 char *head;
197 char *tail;
198 static const mem_table_struct mem_table[] = {
199 {"Active", &kb_active}, // important
200 {"Buffers", &kb_main_buffers}, // important
201 {"Cached", &kb_main_cached}, // important
202 {"Committed_AS", &kb_committed_as},
203 {"Dirty", &kb_dirty}, // kB version of vmstat nr_dirty
204 {"HighFree", &kb_high_free},
205 {"HighTotal", &kb_high_total},
206 {"Inact_clean", &kb_inact_clean},
207 {"Inact_dirty", &kb_inact_dirty},
208 {"Inact_laundry",&kb_inact_laundry},
209 {"Inact_target", &kb_inact_target},
210 {"Inactive", &kb_inactive}, // important
211 {"LowFree", &kb_low_free},
212 {"LowTotal", &kb_low_total},
213 {"Mapped", &kb_mapped}, // kB version of vmstat nr_mapped
214 {"MemFree", &kb_main_free}, // important
215 {"MemShared", &kb_main_shared}, // important
216 {"MemTotal", &kb_main_total}, // important
217 {"PageTables", &kb_pagetables}, // kB version of vmstat
218 // nr_page_table_pages
219 {"ReverseMaps", &nr_reversemaps}, // same as vmstat
220 // nr_page_table_pages
221 {"Slab", &kb_slab}, // kB version of vmstat nr_slab
222 {"SwapCached", &kb_swap_cached},
223 {"SwapFree", &kb_swap_free}, // important
224 {"SwapTotal", &kb_swap_total}, // important
225 {"Writeback", &kb_writeback}, // kB version of vmstat
226 // nr_writeback
228 const int mem_table_count = sizeof(mem_table)/sizeof(mem_table_struct);
230 FILE_TO_BUF(MEMINFO_FILE,meminfo_fd);
232 kb_inactive = ~0UL;
234 head = buf;
235 for(;;){
236 tail = strchr(head, ':');
237 if(!tail) break;
238 *tail = '\0';
239 if(strlen(head) >= sizeof(namebuf)){
240 head = tail+1;
241 goto nextline;
243 strcpy(namebuf,head);
244 found = bsearch(&findme, mem_table, mem_table_count,
245 sizeof(mem_table_struct), compare_mem_table_structs
247 head = tail+1;
248 if(!found) goto nextline;
249 *(found->slot) = strtoul(head,&tail,10);
250 nextline:
251 tail = strchr(head, '\n');
252 if(!tail) break;
253 head = tail+1;
255 if(!kb_low_total){ /* low==main except with large-memory support */
256 kb_low_total = kb_main_total;
257 kb_low_free = kb_main_free;
259 if(kb_inactive==~0UL){
260 kb_inactive = kb_inact_dirty + kb_inact_clean + kb_inact_laundry;
262 kb_swap_used = kb_swap_total - kb_swap_free;
263 kb_main_used = kb_main_total - kb_main_free;
267 /*************************************************************************/
269 * This guy's modeled on libproc's 'five_cpu_numbers' function except
270 * we preserve all cpu data in our CPU_t array which is organized
271 * as follows:
272 * cpus[0] thru cpus[n] == tics for each separate cpu
273 * cpus[Cpu_tot] == tics from the 1st /proc/stat line */
274 static CPU_t *cpus_refresh (CPU_t *cpus, unsigned int Cpu_tot)
276 static FILE *fp = NULL;
277 int i;
278 // enough for a /proc/stat CPU line (not the intr line)
279 char buf[SMLBUFSIZ];
281 /* by opening this file once, we'll avoid the hit on minor page faults
282 (sorry Linux, but you'll have to close it for us) */
283 if (!fp) {
284 if (!(fp = fopen("/proc/stat", "r")))
285 std_err(fmtmk("Failed /proc/stat open: %s", strerror(errno)));
286 /* note: we allocate one more CPU_t than Cpu_tot so that the
287 last slot can hold tics representing the /proc/stat cpu
288 summary (the first line read) -- that slot supports our
289 View_CPUSUM toggle */
290 cpus = alloc_c((1 + Cpu_tot) * sizeof(CPU_t));
292 rewind(fp);
293 fflush(fp);
295 // first value the last slot with the cpu summary line
296 if (!fgets(buf, sizeof(buf), fp)) std_err("failed /proc/stat read");
298 cpus[Cpu_tot].x = 0; // FIXME: can't tell by kernel version number
299 cpus[Cpu_tot].y = 0; // FIXME: can't tell by kernel version number
300 if (4 > sscanf(buf, CPU_FMTS_JUST1, &cpus[Cpu_tot].u, &cpus[Cpu_tot].n, &cpus[Cpu_tot].s, &cpus[Cpu_tot].i, &cpus[Cpu_tot].w, &cpus[Cpu_tot].x, &cpus[Cpu_tot].y))
301 std_err("failed /proc/stat read");
302 // and just in case we're 2.2.xx compiled without SMP support...
303 if (1 == Cpu_tot)
305 /* do it "manually", otherwise we overwrite charge and total */
306 cpus[0].u = cpus[1].u;
307 cpus[0].n = cpus[1].n;
308 cpus[0].s = cpus[1].s;
309 cpus[0].i = cpus[1].i;
310 cpus[0].w = cpus[1].w;
311 cpus[0].x = cpus[1].x;
312 cpus[0].y = cpus[1].y;
316 // now value each separate cpu's tics
317 for (i = 0; 1 < Cpu_tot && i < Cpu_tot; i++) {
319 if (!fgets(buf, sizeof(buf), fp)) std_err("failed /proc/stat read");
320 cpus[i].x = 0; // FIXME: can't tell by kernel version number
321 cpus[i].y = 0; // FIXME: can't tell by kernel version number
322 if (4 > sscanf(buf, CPU_FMTS_MULTI, &cpus[i].u, &cpus[i].n, &cpus[i].s, &cpus[i].i, &cpus[i].w, &cpus[i].x, &cpus[i].y))
323 std_err("failed /proc/stat read");
325 return cpus;
328 unsigned int *Get_CPU_Load(unsigned int *load, unsigned int Cpu_tot)
330 static CPU_t *smpcpu = NULL;
331 unsigned int j;
332 register unsigned long charge, total = 0 ;
334 smpcpu = cpus_refresh(smpcpu, Cpu_tot);
336 for(j = 0 ; j < Cpu_tot ; j ++)
338 charge = smpcpu[j].u+smpcpu[j].s+smpcpu[j].n;
339 total = charge + smpcpu[j].i;
341 /* scale cpu to a maximum of HAUTEUR */
342 load[j] = ((HAUTEUR * (charge - smpcpu[j].charge)) / (total - smpcpu[j].total + 0.001)) + 1 ;
343 smpcpu[j].total = total ;
344 smpcpu[j].charge = charge ;
347 return load;
350 unsigned int Get_Memory(void)
352 meminfo();
354 return (kb_main_used - kb_main_cached) / (kb_main_total / 100) ;
355 /* should be between 0 and 100 now */
358 unsigned int Get_Swap(void)
360 /* returns swap usage as value between 0 and 100 OR 999 if no swap
361 * present */
362 meminfo();
364 return ( kb_swap_total == 0 ? 999 : kb_swap_used / (kb_swap_total / 100) );