"Fossies" - the Fresh Open Source Software Archive 
Member "atop-2.8.1/photosyst.c" (7 Jan 2023, 68592 Bytes) of package /linux/misc/atop-2.8.1.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "photosyst.c" see the
Fossies "Dox" file reference documentation and the last
Fossies "Diffs" side-by-side code changes report:
2.7.1_vs_2.8.0.
1 /*
2 ** ATOP - System & Process Monitor
3 **
4 ** The program 'atop' offers the possibility to view the activity of
5 ** the system on system-level as well as process-level.
6 **
7 ** This source-file contains functions to read all relevant system-level
8 ** figures.
9 ** ==========================================================================
10 ** Author: Gerlof Langeveld
11 ** E-mail: gerlof.langeveld@atoptool.nl
12 ** Date: November 1996
13 ** LINUX-port: June 2000
14 ** --------------------------------------------------------------------------
15 ** Copyright (C) 2000-2012 Gerlof Langeveld
16 **
17 ** This program is free software; you can redistribute it and/or modify it
18 ** under the terms of the GNU General Public License as published by the
19 ** Free Software Foundation; either version 2, or (at your option) any
20 ** later version.
21 **
22 ** This program is distributed in the hope that it will be useful, but
23 ** WITHOUT ANY WARRANTY; without even the implied warranty of
24 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
25 ** See the GNU General Public License for more details.
26 **
27 ** You should have received a copy of the GNU General Public License
28 ** along with this program; if not, write to the Free Software
29 ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
30 ** --------------------------------------------------------------------------
31 */
32 #include <sys/types.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <unistd.h>
36 #include <stdlib.h>
37 #include <errno.h>
38 #include <regex.h>
39 #include <sys/stat.h>
40 #include <sys/times.h>
41 #include <sys/wait.h>
42 #include <time.h>
43 #include <signal.h>
44 #include <string.h>
45 #include <dirent.h>
46 #include <sys/ioctl.h>
47 #include <sys/sysmacros.h>
48 #include <limits.h>
49
50 #define SCALINGMAXCPU 8 // threshold for scaling info per CPU
51
52
53 #ifndef NOPERFEVENT
54 #include <linux/perf_event.h>
55 #include <asm/unistd.h>
56 #endif
57
58 #include <sys/socket.h>
59 #include <netinet/in.h>
60 #include <netdb.h>
61
62 // #define _GNU_SOURCE
63 #include <sys/ipc.h>
64 #include <sys/shm.h>
65
66 #include "atop.h"
67 #include "ifprop.h"
68 #include "photosyst.h"
69
70 #define MAXCNT 64
71
72 /* return value of isdisk() */
73 #define NONTYPE 0
74 #define DSKTYPE 1
75 #define MDDTYPE 2
76 #define LVMTYPE 3
77
78 /* recognize numa node */
79 #define NUMADIR "/sys/devices/system/node"
80
81 /* recognize LLC monitor data */
82 #define LLCDIR "/sys/fs/resctrl/mon_data"
83 #define L3SIZE "/sys/devices/system/cpu/cpu0/cache/index3/size"
84
85 /* Refer to mmzone.h, the default is 11 */
86 #define MAX_ORDER 11
87
88 #ifndef NOPERFEVENT
89 enum {
90 PERF_EVENTS_AUTO = 0,
91 PERF_EVENTS_ENABLE,
92 PERF_EVENTS_DISABLE,
93 };
94
95 static int perfevents = PERF_EVENTS_AUTO;
96 static void getperfevents(struct cpustat *);
97 #endif
98
99 static int get_infiniband(struct ifbstat *);
100 static int get_ksm(struct sstat *);
101 static int get_zswap(struct sstat *);
102
103 static int isdisk(unsigned int, unsigned int,
104 char *, struct perdsk *, int);
105
106 static struct ipv6_stats ipv6_tmp;
107 static struct icmpv6_stats icmpv6_tmp;
108 static struct udpv6_stats udpv6_tmp;
109
110 struct v6tab {
111 char *nam;
112 count_t *val;
113 };
114
115 static struct v6tab v6tab[] = {
116 {"Ip6InReceives", &ipv6_tmp.Ip6InReceives, },
117 {"Ip6InHdrErrors", &ipv6_tmp.Ip6InHdrErrors, },
118 {"Ip6InTooBigErrors", &ipv6_tmp.Ip6InTooBigErrors, },
119 {"Ip6InNoRoutes", &ipv6_tmp.Ip6InNoRoutes, },
120 {"Ip6InAddrErrors", &ipv6_tmp.Ip6InAddrErrors, },
121 {"Ip6InUnknownProtos", &ipv6_tmp.Ip6InUnknownProtos, },
122 {"Ip6InTruncatedPkts", &ipv6_tmp.Ip6InTruncatedPkts, },
123 {"Ip6InDiscards", &ipv6_tmp.Ip6InDiscards, },
124 {"Ip6InDelivers", &ipv6_tmp.Ip6InDelivers, },
125 {"Ip6OutForwDatagrams", &ipv6_tmp.Ip6OutForwDatagrams, },
126 {"Ip6OutRequests", &ipv6_tmp.Ip6OutRequests, },
127 {"Ip6OutDiscards", &ipv6_tmp.Ip6OutDiscards, },
128 {"Ip6OutNoRoutes", &ipv6_tmp.Ip6OutNoRoutes, },
129 {"Ip6ReasmTimeout", &ipv6_tmp.Ip6ReasmTimeout, },
130 {"Ip6ReasmReqds", &ipv6_tmp.Ip6ReasmReqds, },
131 {"Ip6ReasmOKs", &ipv6_tmp.Ip6ReasmOKs, },
132 {"Ip6ReasmFails", &ipv6_tmp.Ip6ReasmFails, },
133 {"Ip6FragOKs", &ipv6_tmp.Ip6FragOKs, },
134 {"Ip6FragFails", &ipv6_tmp.Ip6FragFails, },
135 {"Ip6FragCreates", &ipv6_tmp.Ip6FragCreates, },
136 {"Ip6InMcastPkts", &ipv6_tmp.Ip6InMcastPkts, },
137 {"Ip6OutMcastPkts", &ipv6_tmp.Ip6OutMcastPkts, },
138
139 {"Icmp6InMsgs", &icmpv6_tmp.Icmp6InMsgs, },
140 {"Icmp6InErrors", &icmpv6_tmp.Icmp6InErrors, },
141 {"Icmp6InDestUnreachs", &icmpv6_tmp.Icmp6InDestUnreachs, },
142 {"Icmp6InPktTooBigs", &icmpv6_tmp.Icmp6InPktTooBigs, },
143 {"Icmp6InTimeExcds", &icmpv6_tmp.Icmp6InTimeExcds, },
144 {"Icmp6InParmProblems", &icmpv6_tmp.Icmp6InParmProblems, },
145 {"Icmp6InEchos", &icmpv6_tmp.Icmp6InEchos, },
146 {"Icmp6InEchoReplies", &icmpv6_tmp.Icmp6InEchoReplies, },
147 {"Icmp6InGroupMembQueries", &icmpv6_tmp.Icmp6InGroupMembQueries, },
148 {"Icmp6InGroupMembResponses",
149 &icmpv6_tmp.Icmp6InGroupMembResponses, },
150 {"Icmp6InGroupMembReductions",
151 &icmpv6_tmp.Icmp6InGroupMembReductions, },
152 {"Icmp6InRouterSolicits", &icmpv6_tmp.Icmp6InRouterSolicits, },
153 {"Icmp6InRouterAdvertisements",
154 &icmpv6_tmp.Icmp6InRouterAdvertisements, },
155 {"Icmp6InNeighborSolicits", &icmpv6_tmp.Icmp6InNeighborSolicits, },
156 {"Icmp6InNeighborAdvertisements",
157 &icmpv6_tmp.Icmp6InNeighborAdvertisements, },
158 {"Icmp6InRedirects", &icmpv6_tmp.Icmp6InRedirects, },
159 {"Icmp6OutMsgs", &icmpv6_tmp.Icmp6OutMsgs, },
160 {"Icmp6OutDestUnreachs", &icmpv6_tmp.Icmp6OutDestUnreachs, },
161 {"Icmp6OutPktTooBigs", &icmpv6_tmp.Icmp6OutPktTooBigs, },
162 {"Icmp6OutTimeExcds", &icmpv6_tmp.Icmp6OutTimeExcds, },
163 {"Icmp6OutParmProblems", &icmpv6_tmp.Icmp6OutParmProblems, },
164 {"Icmp6OutEchoReplies", &icmpv6_tmp.Icmp6OutEchoReplies, },
165 {"Icmp6OutRouterSolicits", &icmpv6_tmp.Icmp6OutRouterSolicits, },
166 {"Icmp6OutNeighborSolicits",&icmpv6_tmp.Icmp6OutNeighborSolicits, },
167 {"Icmp6OutNeighborAdvertisements",
168 &icmpv6_tmp.Icmp6OutNeighborAdvertisements, },
169 {"Icmp6OutRedirects", &icmpv6_tmp.Icmp6OutRedirects, },
170 {"Icmp6OutGroupMembResponses",
171 &icmpv6_tmp.Icmp6OutGroupMembResponses, },
172 {"Icmp6OutGroupMembReductions",
173 &icmpv6_tmp.Icmp6OutGroupMembReductions, },
174
175 {"Udp6InDatagrams", &udpv6_tmp.Udp6InDatagrams, },
176 {"Udp6NoPorts", &udpv6_tmp.Udp6NoPorts, },
177 {"Udp6InErrors", &udpv6_tmp.Udp6InErrors, },
178 {"Udp6OutDatagrams", &udpv6_tmp.Udp6OutDatagrams, },
179 };
180
181 static int v6tab_entries = sizeof(v6tab)/sizeof(struct v6tab);
182
183 // The following values are used to accumulate cpu statistics per numa.
184 // The bitmask realization is from numactl
185 #define CPUMASK_SZ (64 * 8)
186
187 #define bitsperlong (8 * sizeof(unsigned long))
188 #define howmany(x,y) (((x)+((y)-1))/(y))
189 #define longsperbits(n) howmany(n, bitsperlong)
190
191 #define round_up(x,y) (((x) + (y) - 1) & ~((y)-1))
192 #define BITS_PER_LONG (sizeof(unsigned long) * 8)
193 #define CPU_BYTES(x) (round_up(x, BITS_PER_LONG)/8)
194 #define CPU_LONGS(x) (CPU_BYTES(x) / sizeof(long))
195
196 struct bitmask {
197 unsigned long size; /* number of bits in the map */
198 unsigned long long *maskp;
199 };
200
201 /*
202 * Allocate a bitmask for cpus, of a size large enough to
203 * match the kernel's cpumask_t.
204 */
205 struct bitmask *
206 numa_allocate_cpumask()
207 {
208 int ncpus = CPUMASK_SZ;
209 struct bitmask *bmp;
210
211 bmp = malloc(sizeof(*bmp));
212 if (!bmp)
213 ptrverify(bmp, "Malloc failed for numa bitmask");
214
215 bmp->size = ncpus;
216 bmp->maskp = calloc(longsperbits(ncpus), sizeof(unsigned long));
217 ptrverify((bmp->maskp), "Malloc failed for numa maskp");
218
219 return bmp;
220 }
221
222 void
223 numa_bitmask_free(struct bitmask *bmp)
224 {
225 if (bmp == 0)
226 return;
227 free(bmp->maskp);
228 bmp->maskp = (unsigned long long *)0xdeadcdef; /* double free tripwire */
229 free(bmp);
230 return;
231 }
232
233 int
234 numa_parse_bitmap_v2(char *line, struct bitmask *mask)
235 {
236 int i;
237 char *p = strchr(line, '\n');
238 if (!p)
239 return -1;
240 int ncpus = mask->size;
241
242 for (i = 0; p > line;i++) {
243 char *oldp, *endp;
244 oldp = p;
245 if (*p == ',')
246 --p;
247 while (p > line && *p != ',')
248 --p;
249 /* Eat two 32bit fields at a time to get longs */
250 if (p > line && sizeof(unsigned long) == 8) {
251 oldp--;
252 memmove(p, p+1, oldp-p+1);
253 while (p > line && *p != ',')
254 --p;
255 }
256 if (*p == ',')
257 p++;
258 if (i >= CPU_LONGS(ncpus))
259 return -1;
260 mask->maskp[i] = strtoul(p, &endp, 16);
261 if (endp != oldp)
262 return -1;
263 p--;
264 }
265 return 0;
266 }
267
268 void
269 photosyst(struct sstat *si)
270 {
271 static char part_stats = 1; /* per-partition statistics ? */
272 static char ib_stats = 1; /* InfiniBand statistics ? */
273 static char ksm_stats = 1;
274 static char zswap_stats = 1;
275
276 register int i, nr, j;
277 count_t cnts[MAXCNT];
278 float lavg1, lavg5, lavg15;
279 FILE *fp;
280 DIR *dirp;
281 struct dirent *dentry;
282 char linebuf[1024], nam[64], origdir[1024];
283 unsigned int major, minor;
284 struct shm_info shminfo;
285 #if HTTPSTATS
286 static int wwwvalid = 1;
287 #endif
288
289 memset(si, 0, sizeof(struct sstat));
290
291 if ( getcwd(origdir, sizeof origdir) == NULL)
292 mcleanstop(54, "failed to save current dir\n");
293
294 if ( chdir("/proc") == -1)
295 mcleanstop(54, "failed to change to /proc\n");
296
297 /*
298 ** gather various general statistics from the file /proc/stat and
299 ** store them in binary form
300 */
301 if ( (fp = fopen("stat", "r")) != NULL)
302 {
303 while ( fgets(linebuf, sizeof(linebuf), fp) != NULL)
304 {
305 nr = sscanf(linebuf,
306 "%s %lld %lld %lld %lld %lld %lld %lld "
307 "%lld %lld %lld %lld %lld %lld %lld %lld ",
308 nam,
309 &cnts[0], &cnts[1], &cnts[2], &cnts[3],
310 &cnts[4], &cnts[5], &cnts[6], &cnts[7],
311 &cnts[8], &cnts[9], &cnts[10], &cnts[11],
312 &cnts[12], &cnts[13], &cnts[14]);
313
314 if (nr < 2) /* headerline ? --> skip */
315 continue;
316
317 if ( strcmp("cpu", nam) == EQ)
318 {
319 si->cpu.all.utime = cnts[0];
320 si->cpu.all.ntime = cnts[1];
321 si->cpu.all.stime = cnts[2];
322 si->cpu.all.itime = cnts[3];
323
324 if (nr > 5) /* 2.6 kernel? */
325 {
326 si->cpu.all.wtime = cnts[4];
327 si->cpu.all.Itime = cnts[5];
328 si->cpu.all.Stime = cnts[6];
329
330 if (nr > 8) /* steal support */
331 si->cpu.all.steal = cnts[7];
332
333 if (nr > 9) /* guest support */
334 si->cpu.all.guest = cnts[8];
335 }
336 continue;
337 }
338
339 if ( strncmp("cpu", nam, 3) == EQ)
340 {
341 i = atoi(&nam[3]);
342
343 if (i >= MAXCPU)
344 {
345 fprintf(stderr,
346 "cpu %s exceeds maximum of %d\n",
347 nam, MAXCPU);
348 continue;
349 }
350
351 si->cpu.cpu[i].cpunr = i;
352 si->cpu.cpu[i].utime = cnts[0];
353 si->cpu.cpu[i].ntime = cnts[1];
354 si->cpu.cpu[i].stime = cnts[2];
355 si->cpu.cpu[i].itime = cnts[3];
356
357 if (nr > 5) /* 2.6 kernel? */
358 {
359 si->cpu.cpu[i].wtime = cnts[4];
360 si->cpu.cpu[i].Itime = cnts[5];
361 si->cpu.cpu[i].Stime = cnts[6];
362
363 if (nr > 8) /* steal support */
364 si->cpu.cpu[i].steal = cnts[7];
365
366 if (nr > 9) /* guest support */
367 si->cpu.cpu[i].guest = cnts[8];
368 }
369
370 si->cpu.nrcpu++;
371 continue;
372 }
373
374 if ( strcmp("ctxt", nam) == EQ)
375 {
376 si->cpu.csw = cnts[0];
377 continue;
378 }
379
380 if ( strcmp("intr", nam) == EQ)
381 {
382 si->cpu.devint = cnts[0];
383 continue;
384 }
385
386 if ( strcmp("processes", nam) == EQ)
387 {
388 si->cpu.nprocs = cnts[0];
389 continue;
390 }
391
392 if ( strcmp("swap", nam) == EQ) /* < 2.6 */
393 {
394 si->mem.swins = cnts[0];
395 si->mem.swouts = cnts[1];
396 continue;
397 }
398 }
399
400 fclose(fp);
401
402 if (si->cpu.nrcpu == 0)
403 si->cpu.nrcpu = 1;
404 }
405
406 /*
407 ** gather loadaverage values from the file /proc/loadavg and
408 ** store them in binary form
409 */
410 if ( (fp = fopen("loadavg", "r")) != NULL)
411 {
412 if ( fgets(linebuf, sizeof(linebuf), fp) != NULL)
413 {
414 if ( sscanf(linebuf, "%f %f %f",
415 &lavg1, &lavg5, &lavg15) == 3)
416 {
417 si->cpu.lavg1 = lavg1;
418 si->cpu.lavg5 = lavg5;
419 si->cpu.lavg15 = lavg15;
420 }
421 }
422
423 fclose(fp);
424 }
425
426 /*
427 ** gather frequency scaling info.
428 ** sources (in order of preference):
429 ** /sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state
430 ** or
431 ** /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq
432 ** /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq
433 **
434 ** store them in binary form
435 */
436 static char fn[512];
437 int didone=0;
438
439 if (si->cpu.nrcpu <= SCALINGMAXCPU)
440 {
441 for (i = 0; i < si->cpu.nrcpu; ++i)
442 {
443 long long f=0;
444
445 snprintf(fn, sizeof fn,
446 "/sys/devices/system/cpu/cpu%d/cpufreq/stats/time_in_state",
447 i);
448
449 if ((fp=fopen(fn, "r")) != 0)
450 {
451 long long hits=0;
452 long long maxfreq=0;
453 long long cnt=0;
454 long long sum=0;
455
456 while (fscanf(fp, "%lld %lld", &f, &cnt) == 2)
457 {
458 f /= 1000;
459 sum += (f*cnt);
460 hits += cnt;
461
462 if (f > maxfreq)
463 maxfreq=f;
464 didone=1;
465 }
466
467 si->cpu.cpu[i].freqcnt.maxfreq = maxfreq;
468 si->cpu.cpu[i].freqcnt.cnt = sum;
469 si->cpu.cpu[i].freqcnt.ticks = hits;
470
471 fclose(fp);
472 }
473 else
474 { // governor statistics not available
475 snprintf(fn, sizeof fn,
476 "/sys/devices/system/cpu/cpu%d/cpufreq/cpuinfo_max_freq",
477 i);
478
479 if ((fp=fopen(fn, "r")) != 0)
480 {
481 if (fscanf(fp, "%lld", &f) == 1)
482 {
483 // convert KHz to MHz
484 si->cpu.cpu[i].freqcnt.maxfreq =f/1000;
485 }
486
487 fclose(fp);
488 }
489 else
490 {
491 si->cpu.cpu[i].freqcnt.maxfreq=0;
492 }
493
494 snprintf(fn, sizeof fn,
495 "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_cur_freq",
496 i);
497
498 if ((fp=fopen(fn, "r")) != 0)
499 {
500 if (fscanf(fp, "%lld", &f) == 1)
501 {
502 // convert KHz to MHz
503 si->cpu.cpu[i].freqcnt.cnt = f/1000;
504 si->cpu.cpu[i].freqcnt.ticks = 0;
505 didone=1;
506 }
507
508 fclose(fp);
509 }
510 else
511 {
512 si->cpu.cpu[i].freqcnt.cnt = 0;
513 si->cpu.cpu[i].freqcnt.ticks = 0;
514 }
515 }
516 } // for all CPUs
517 }
518
519 if (!didone) // did not get processor freq statistics.
520 // use /proc/cpuinfo
521 {
522 if ( (fp = fopen("cpuinfo", "r")) != NULL)
523 {
524 // get information from the lines
525 // processor\t: 0
526 // cpu MHz\t\t: 800.000
527
528 int cpuno=-1;
529
530 while ( fgets(linebuf, sizeof(linebuf), fp) != NULL)
531 {
532 if (memcmp(linebuf, "processor", 9)== EQ)
533 sscanf(linebuf, "%*s %*s %d", &cpuno);
534
535 if (memcmp(linebuf, "cpu MHz", 7) == EQ)
536 {
537 if (cpuno >= 0 && cpuno < si->cpu.nrcpu)
538 {
539 sscanf(linebuf,
540 "%*s %*s %*s %lld",
541 &(si->cpu.cpu[cpuno].freqcnt.cnt));
542 }
543 }
544 }
545
546 fclose(fp);
547 }
548
549 }
550
551 /*
552 ** gather virtual memory statistics from the file /proc/vmstat and
553 ** store them in binary form (>= kernel 2.6)
554 */
555 si->mem.oomkills = -1;
556
557 si->mem.allocstall = 0;
558 si->mem.numamigrate = 0;
559 si->mem.pgmigrate = 0;
560
561 if ( (fp = fopen("vmstat", "r")) != NULL)
562 {
563 while ( fgets(linebuf, sizeof(linebuf), fp) != NULL)
564 {
565 nr = sscanf(linebuf, "%s %lld", nam, &cnts[0]);
566
567 if (nr < 2) /* headerline ? --> skip */
568 continue;
569
570 /* pgpgin & pgpgout fields in KB from vmstat */
571 if ( strcmp("pgpgin", nam) == EQ)
572 {
573 si->mem.pgins = cnts[0] * 1024 / pagesize;
574 continue;
575 }
576
577 if ( strcmp("pgpgout", nam) == EQ)
578 {
579 si->mem.pgouts = cnts[0] * 1024 / pagesize;
580 continue;
581 }
582
583 if ( strcmp("pswpin", nam) == EQ)
584 {
585 si->mem.swins = cnts[0];
586 continue;
587 }
588
589 if ( strcmp("pswpout", nam) == EQ)
590 {
591 si->mem.swouts = cnts[0];
592 continue;
593 }
594
595 if ( strncmp("pgscan_", nam, 7) == EQ)
596 {
597 si->mem.pgscans += cnts[0];
598 continue;
599 }
600
601 if ( strncmp("pgsteal_", nam, 8) == EQ)
602 {
603 si->mem.pgsteal += cnts[0];
604 continue;
605 }
606
607 // more counters might start with "allocstall"
608 if ( memcmp("allocstall", nam, 10) == EQ)
609 {
610 si->mem.allocstall += cnts[0];
611 continue;
612 }
613
614 if ( strcmp("oom_kill", nam) == EQ)
615 {
616 si->mem.oomkills = cnts[0];
617 continue;
618 }
619
620 if ( strcmp("compact_stall", nam) == EQ) {
621 si->mem.compactstall = cnts[0];
622 continue;
623 }
624
625 if ( strcmp("numa_pages_migrated", nam) == EQ) {
626 si->mem.numamigrate = cnts[0];
627 continue;
628 }
629
630 if ( strcmp("pgmigrate_success", nam) == EQ) {
631 si->mem.pgmigrate = cnts[0];
632 continue;
633 }
634 }
635
636 fclose(fp);
637 }
638
639 /*
640 ** gather memory-related statistics from the file /proc/meminfo and
641 ** store them in binary form
642 **
643 ** in the file /proc/meminfo a 2.4 kernel starts with two lines
644 ** headed by the strings "Mem:" and "Swap:" containing all required
645 ** fields, except proper value for page cache
646 ** if these lines are present we try to skip parsing the rest
647 ** of the lines; if these lines are not present we should get the
648 ** required field from other lines
649 */
650 si->mem.physmem = (count_t)-1;
651 si->mem.freemem = (count_t)-1;
652 si->mem.buffermem = (count_t)-1;
653 si->mem.cachemem = (count_t)-1;
654 si->mem.slabmem = (count_t) 0;
655 si->mem.slabreclaim = (count_t) 0;
656 si->mem.shmem = (count_t) 0;
657 si->mem.totswap = (count_t)-1;
658 si->mem.freeswap = (count_t)-1;
659 si->mem.swapcached = (count_t) 0;
660 si->mem.committed = (count_t) 0;
661 si->mem.pagetables = (count_t) 0;
662
663 if ( (fp = fopen("meminfo", "r")) != NULL)
664 {
665 while ( fgets(linebuf, sizeof(linebuf), fp) != NULL)
666 {
667 nr = sscanf(linebuf,
668 "%s %lld %lld %lld %lld %lld %lld %lld "
669 "%lld %lld %lld\n",
670 nam,
671 &cnts[0], &cnts[1], &cnts[2], &cnts[3],
672 &cnts[4], &cnts[5], &cnts[6], &cnts[7],
673 &cnts[8], &cnts[9]);
674
675 if (nr < 2) /* headerline ? --> skip */
676 continue;
677
678 if ( strcmp("Mem:", nam) == EQ)
679 {
680 si->mem.physmem = cnts[0] / pagesize;
681 si->mem.freemem = cnts[2] / pagesize;
682 si->mem.buffermem = cnts[4] / pagesize;
683 }
684 else if ( strcmp("Swap:", nam) == EQ)
685 {
686 si->mem.totswap = cnts[0] / pagesize;
687 si->mem.freeswap = cnts[2] / pagesize;
688 }
689 else if (strcmp("Cached:", nam) == EQ)
690 {
691 if (si->mem.cachemem == (count_t)-1)
692 {
693 si->mem.cachemem =
694 cnts[0]*1024/pagesize;
695 }
696 }
697 else if (strcmp("Dirty:", nam) == EQ)
698 {
699 si->mem.cachedrt =
700 cnts[0]*1024/pagesize;
701 }
702 else if (strcmp("MemTotal:", nam) == EQ)
703 {
704 if (si->mem.physmem == (count_t)-1)
705 {
706 si->mem.physmem =
707 cnts[0]*1024/pagesize;
708 }
709 }
710 else if (strcmp("MemFree:", nam) == EQ)
711 {
712 if (si->mem.freemem == (count_t)-1)
713 {
714 si->mem.freemem =
715 cnts[0]*1024/pagesize;
716 }
717 }
718 else if (strcmp("Buffers:", nam) == EQ)
719 {
720 if (si->mem.buffermem == (count_t)-1)
721 {
722 si->mem.buffermem =
723 cnts[0]*1024/pagesize;
724 }
725 }
726 else if (strcmp("Shmem:", nam) == EQ)
727 {
728 si->mem.shmem = cnts[0]*1024/pagesize;
729 }
730 else if (strcmp("SwapTotal:", nam) == EQ)
731 {
732 if (si->mem.totswap == (count_t)-1)
733 {
734 si->mem.totswap =
735 cnts[0]*1024/pagesize;
736 }
737 }
738 else if (strcmp("SwapFree:", nam) == EQ)
739 {
740 if (si->mem.freeswap == (count_t)-1)
741 {
742 si->mem.freeswap =
743 cnts[0]*1024/pagesize;
744 }
745 }
746 else if (strcmp("SwapCached:", nam) == EQ)
747 {
748 si->mem.swapcached =
749 cnts[0]*1024/pagesize;
750 }
751 else if (strcmp("Slab:", nam) == EQ)
752 {
753 si->mem.slabmem = cnts[0]*1024/pagesize;
754 }
755 else if (strcmp("SReclaimable:", nam) == EQ)
756 {
757 si->mem.slabreclaim = cnts[0]*1024/
758 pagesize;
759 }
760 else if (strcmp("Committed_AS:", nam) == EQ)
761 {
762 si->mem.committed = cnts[0]*1024/
763 pagesize;
764 }
765 else if (strcmp("CommitLimit:", nam) == EQ)
766 {
767 si->mem.commitlim = cnts[0]*1024/
768 pagesize;
769 }
770 else if (strcmp("HugePages_Total:", nam) == EQ)
771 {
772 si->mem.tothugepage = cnts[0];
773 }
774 else if (strcmp("HugePages_Free:", nam) == EQ)
775 {
776 si->mem.freehugepage = cnts[0];
777 }
778 else if (strcmp("Hugepagesize:", nam) == EQ)
779 {
780 si->mem.hugepagesz = cnts[0]*1024;
781 }
782 else if (strcmp("PageTables:", nam) == EQ)
783 {
784 si->mem.pagetables = cnts[0]*1024/
785 pagesize;
786 }
787 }
788
789 fclose(fp);
790 }
791
792 /*
793 ** gather vmware-related statistics from /sys/kernel/debug/vmmemctl
794 ** (only present if balloon driver enabled) or from /proc/vmmemctl
795 ** for older balloon driver implementations
796 */
797 si->mem.vmwballoon = (count_t) -1;
798
799 if ( (fp = fopen("/sys/kernel/debug/vmmemctl", "r")) != NULL ||
800 (fp = fopen("/proc/vmmemctl", "r")) != NULL )
801 {
802 while ( fgets(linebuf, sizeof(linebuf), fp) != NULL)
803 {
804 nr = sscanf(linebuf, "%s %lld ", nam, &cnts[0]);
805
806 if ( strcmp("current:", nam) == EQ)
807 {
808 si->mem.vmwballoon = cnts[0];
809 break;
810 }
811 }
812
813 fclose(fp);
814 }
815
816 /*
817 ** ZFSonlinux: gather size of ARC cache in memory
818 ** searching for:
819 ** size 4 519101312
820 */
821 si->mem.zfsarcsize = (count_t) -1;
822
823 if ( (fp = fopen("spl/kstat/zfs/arcstats", "r")) != NULL)
824 {
825 while ( fgets(linebuf, sizeof(linebuf), fp) != NULL)
826 {
827 nr = sscanf(linebuf,
828 "%s %lld %lld", nam, &cnts[0], &cnts[1]);
829
830 if (nr < 3)
831 continue;
832
833 if ( strcmp("size", nam) == EQ)
834 {
835 si->mem.zfsarcsize = cnts[1] / pagesize;
836 break;
837 }
838 }
839
840 fclose(fp);
841 }
842
843 /*
844 ** gather per numa memory-related statistics from the file
845 ** /sys/devices/system/node/node0/meminfo, and store them in binary form.
846 */
847 dirp = opendir(NUMADIR);
848
849 if (dirp)
850 {
851 /*
852 ** read every directory-entry and search for all numa nodes
853 */
854 while ( (dentry = readdir(dirp)) )
855 {
856 if (strncmp(dentry->d_name, "node", 4))
857 continue;
858
859 j = strtoul(dentry->d_name + 4, NULL, 0);
860
861 if (j >= MAXNUMA) // too many NUMA nodes?
862 continue; // skip (no break, because order unknown)
863
864 si->memnuma.nrnuma++;
865
866 snprintf(fn, sizeof fn, NUMADIR "/%s/meminfo", dentry->d_name);
867
868 if ( (fp = fopen(fn, "r")) != NULL)
869 {
870 while ( fgets(linebuf, sizeof(linebuf), fp) != NULL)
871 {
872 nr = sscanf(&linebuf[5],
873 "%lld %s %lld\n",
874 &cnts[0], nam, &cnts[1]);
875
876 if (cnts[0] != j)
877 continue;
878
879 si->memnuma.numa[j].numanr = j;
880 if ( strcmp("MemTotal:", nam) == EQ)
881 si->memnuma.numa[j].totmem = cnts[1]*1024/pagesize;
882 else if ( strcmp("MemFree:", nam) == EQ)
883 si->memnuma.numa[j].freemem = cnts[1]*1024/pagesize;
884 else if ( strcmp("FilePages:", nam) == EQ)
885 si->memnuma.numa[j].filepage = cnts[1]*1024/pagesize;
886 else if ( strcmp("Active:", nam) == EQ)
887 si->memnuma.numa[j].active = cnts[1]*1024/pagesize;
888 else if ( strcmp("Inactive:", nam) == EQ)
889 si->memnuma.numa[j].inactive = cnts[1]*1024/pagesize;
890 else if ( strcmp("Dirty:", nam) == EQ)
891 si->memnuma.numa[j].dirtymem = cnts[1]*1024/pagesize;
892 else if ( strcmp("Shmem:", nam) == EQ)
893 si->memnuma.numa[j].shmem = cnts[1]*1024/pagesize;
894 else if ( strcmp("Slab:", nam) == EQ)
895 si->memnuma.numa[j].slabmem = cnts[1]*1024/pagesize;
896 else if ( strcmp("SReclaimable:", nam) == EQ)
897 si->memnuma.numa[j].slabreclaim = cnts[1]*1024/pagesize;
898 else if ( strcmp("HugePages_Total:", nam) == EQ)
899 si->memnuma.numa[j].tothp = cnts[1];
900 }
901 fclose(fp);
902 }
903 }
904 closedir(dirp);
905 }
906
907 /* gather fragmentation level for per numa, only for 'Normal' */
908 if (si->memnuma.nrnuma > 0)
909 {
910 char tmp[64];
911 float frag[MAX_ORDER];
912
913 /* If kernel CONFIG_COMPACTION is enabled, get the percentage directly */
914 if ( (fp = fopen("/sys/kernel/debug/extfrag/unusable_index", "r")) != NULL )
915 {
916 while ( fgets(linebuf, sizeof(linebuf), fp) != NULL )
917 {
918 nr = sscanf(&linebuf[5],
919 "%lld, %s %s %f %f %f %f %f %f %f %f %f %f %f\n",
920 &cnts[0], tmp, nam, &frag[0], &frag[1], &frag[2],
921 &frag[3], &frag[4], &frag[5], &frag[6], &frag[7],
922 &frag[8], &frag[9], &frag[10]);
923
924 if ( nr < 3 || strcmp("Normal", nam) != 0 )
925 continue;
926
927 for (i = 0; i < MAX_ORDER; i++)
928 si->memnuma.numa[cnts[0]].frag += frag[i];
929
930 si->memnuma.numa[cnts[0]].frag /= MAX_ORDER;
931 }
932 fclose(fp);
933 }
934 /* If CONFIG_COMPACTION is not enabled, calculate from buddyinfo file */
935 else if ( (fp = fopen("/proc/buddyinfo", "r")) != NULL )
936 {
937 count_t free_page[MAX_ORDER];
938 count_t total_free, prev_free;
939 float total_frag;
940
941 while ( fgets(linebuf, sizeof(linebuf), fp) != NULL )
942 {
943 nr = sscanf(&linebuf[5],
944 "%lld, %s %s %lld %lld %lld %lld %lld "
945 "%lld %lld %lld %lld %lld %lld\n",
946 &cnts[0], tmp, nam, &cnts[1], &cnts[2], &cnts[3],
947 &cnts[4], &cnts[5], &cnts[6], &cnts[7], &cnts[8],
948 &cnts[9], &cnts[10], &cnts[11]);
949
950 if (nr < 3 || strcmp("Normal", nam) != 0 )
951 continue;
952
953 /* get free pages numbers for each order, and total free pages */
954 total_free = 0;
955 total_frag = 0.00;
956 for (i = 0; i < MAX_ORDER; i++)
957 {
958 free_page[i] = cnts[i+1] << i;
959 total_free += free_page[i];
960 }
961 /* get fragmentation level for each order, then summarize */
962 for (i = 0; i < MAX_ORDER; i++)
963 {
964 prev_free = 0;
965 for (j = 0; j < i; j++)
966 prev_free += free_page[j];
967 total_frag += (float)prev_free/total_free;
968 }
969
970 if (cnts[0] < MAXNUMA)
971 si->memnuma.numa[cnts[0]].frag = total_frag/MAX_ORDER;
972 }
973 fclose(fp);
974 }
975 }
976
977 /*
978 ** accumulate each cpu statistic for per NUMA, and identify numa/cpu
979 ** relationship from /sys/devices/system/node/node0/cpumap.
980 */
981 if (si->memnuma.nrnuma > 1)
982 {
983 char *line = NULL;
984 size_t len = 0;
985 struct bitmask *mask;
986
987 mask = numa_allocate_cpumask();
988
989 si->cpunuma.nrnuma = si->memnuma.nrnuma;
990
991 for (j=0; j < si->cpunuma.nrnuma; j++)
992 {
993 snprintf(fn, sizeof fn, NUMADIR "/node%d/cpumap", j);
994
995 if ( (fp = fopen(fn, "r")) != 0)
996 {
997 if ( getdelim(&line, &len, '\n', fp) > 0 )
998 {
999 if (numa_parse_bitmap_v2(line, mask) < 0)
1000 {
1001 mcleanstop(54, "failed to parse numa bitmap\n");
1002 }
1003 }
1004 fclose(fp);
1005 }
1006
1007 for (i=0; i < mask->size; i++)
1008 {
1009 if ( (mask->maskp[i/bitsperlong] >> (i % bitsperlong)) & 1 )
1010 {
1011 si->cpunuma.numa[j].nrcpu++;
1012 si->cpunuma.numa[j].utime += si->cpu.cpu[i].utime;
1013 si->cpunuma.numa[j].ntime += si->cpu.cpu[i].ntime;
1014 si->cpunuma.numa[j].stime += si->cpu.cpu[i].stime;
1015 si->cpunuma.numa[j].itime += si->cpu.cpu[i].itime;
1016 si->cpunuma.numa[j].wtime += si->cpu.cpu[i].wtime;
1017 si->cpunuma.numa[j].Itime += si->cpu.cpu[i].Itime;
1018 si->cpunuma.numa[j].Stime += si->cpu.cpu[i].Stime;
1019 si->cpunuma.numa[j].steal += si->cpu.cpu[i].steal;
1020 si->cpunuma.numa[j].guest += si->cpu.cpu[i].guest;
1021 }
1022 }
1023 si->cpunuma.numa[j].numanr = j;
1024 }
1025
1026 free(line);
1027 numa_bitmask_free(mask);
1028 }
1029 else
1030 {
1031 si->cpunuma.nrnuma = 0;
1032 }
1033
1034 /*
1035 ** gather network-related statistics
1036 ** - interface stats from the file /proc/net/dev
1037 ** - IPv4 stats from the file /proc/net/snmp
1038 ** - IPv6 stats from the file /proc/net/snmp6
1039 ** - sock mem stats from the file /proc/net/sockstat
1040 */
1041
1042 /*
1043 ** interface statistics
1044 */
1045 initifprop(); // periodically refresh interface properties
1046
1047 if ( (fp = fopen("net/dev", "r")) != NULL)
1048 {
1049 struct ifprop ifprop;
1050 char *cp;
1051
1052 i = 0;
1053
1054 while ( fgets(linebuf, sizeof(linebuf), fp) != NULL)
1055 {
1056 if ( (cp = strchr(linebuf, ':')) != NULL)
1057 *cp = ' '; /* substitute ':' by space */
1058
1059 nr = sscanf(linebuf,
1060 "%15s %lld %lld %lld %lld %lld %lld %lld "
1061 "%lld %lld %lld %lld %lld %lld %lld %lld "
1062 "%lld\n",
1063 si->intf.intf[i].name,
1064 &(si->intf.intf[i].rbyte),
1065 &(si->intf.intf[i].rpack),
1066 &(si->intf.intf[i].rerrs),
1067 &(si->intf.intf[i].rdrop),
1068 &(si->intf.intf[i].rfifo),
1069 &(si->intf.intf[i].rframe),
1070 &(si->intf.intf[i].rcompr),
1071 &(si->intf.intf[i].rmultic),
1072 &(si->intf.intf[i].sbyte),
1073 &(si->intf.intf[i].spack),
1074 &(si->intf.intf[i].serrs),
1075 &(si->intf.intf[i].sdrop),
1076 &(si->intf.intf[i].sfifo),
1077 &(si->intf.intf[i].scollis),
1078 &(si->intf.intf[i].scarrier),
1079 &(si->intf.intf[i].scompr));
1080
1081 /*
1082 ** skip header line and lines without stats
1083 */
1084 if (nr != 17)
1085 continue;
1086
1087 /*
1088 ** skip interfaces that are invalidated
1089 ** (mainly virtual interfaces)
1090 ** because the total number of interfaces
1091 ** exceeds the maximum supported by atop (MAXINTF)
1092 */
1093 strcpy(ifprop.name, si->intf.intf[i].name);
1094
1095 if (!getifprop(&ifprop))
1096 continue;
1097
1098 /*
1099 ** accept this interface but skip the remaining
1100 ** interfaces because we reached the total number
1101 ** of interfaces supported by atop (MAXINTF)
1102 */
1103 if (++i >= MAXINTF-1)
1104 break;
1105 }
1106
1107 si->intf.intf[i].name[0] = '\0'; /* set terminator for table */
1108 si->intf.nrintf = i;
1109
1110 fclose(fp);
1111 }
1112
1113 /*
1114 ** IP version 4 statistics
1115 */
1116 if ( (fp = fopen("net/snmp", "r")) != NULL)
1117 {
1118 while ( fgets(linebuf, sizeof(linebuf), fp) != NULL)
1119 {
1120 nr = sscanf(linebuf,
1121 "%s %lld %lld %lld %lld %lld %lld %lld %lld %lld "
1122 "%lld %lld %lld %lld %lld %lld %lld %lld %lld %lld "
1123 "%lld %lld %lld %lld %lld %lld %lld %lld %lld %lld "
1124 "%lld %lld %lld %lld %lld %lld %lld %lld %lld %lld "
1125 "%lld\n",
1126 nam,
1127 &cnts[0], &cnts[1], &cnts[2], &cnts[3],
1128 &cnts[4], &cnts[5], &cnts[6], &cnts[7],
1129 &cnts[8], &cnts[9], &cnts[10], &cnts[11],
1130 &cnts[12], &cnts[13], &cnts[14], &cnts[15],
1131 &cnts[16], &cnts[17], &cnts[18], &cnts[19],
1132 &cnts[20], &cnts[21], &cnts[22], &cnts[23],
1133 &cnts[24], &cnts[25], &cnts[26], &cnts[27],
1134 &cnts[28], &cnts[29], &cnts[30], &cnts[31],
1135 &cnts[32], &cnts[33], &cnts[34], &cnts[35],
1136 &cnts[36], &cnts[37], &cnts[38], &cnts[39]);
1137
1138 if (nr < 2) /* headerline ? --> skip */
1139 continue;
1140
1141 if ( strcmp("Ip:", nam) == 0)
1142 {
1143 memcpy(&si->net.ipv4, cnts,
1144 sizeof si->net.ipv4);
1145 continue;
1146 }
1147
1148 if ( strcmp("Icmp:", nam) == 0)
1149 {
1150 memcpy(&si->net.icmpv4, cnts,
1151 sizeof si->net.icmpv4);
1152 continue;
1153 }
1154
1155 if ( strcmp("Tcp:", nam) == 0)
1156 {
1157 memcpy(&si->net.tcp, cnts,
1158 sizeof si->net.tcp);
1159 continue;
1160 }
1161
1162 if ( strcmp("Udp:", nam) == 0)
1163 {
1164 memcpy(&si->net.udpv4, cnts,
1165 sizeof si->net.udpv4);
1166 continue;
1167 }
1168 }
1169
1170 fclose(fp);
1171 }
1172
1173 /*
1174 ** IP version 6 statistics
1175 */
1176 memset(&ipv6_tmp, 0, sizeof ipv6_tmp);
1177 memset(&icmpv6_tmp, 0, sizeof icmpv6_tmp);
1178 memset(&udpv6_tmp, 0, sizeof udpv6_tmp);
1179
1180 if ( (fp = fopen("net/snmp6", "r")) != NULL)
1181 {
1182 count_t countval;
1183 int cur = 0;
1184
1185 /*
1186 ** one name-value pair per line
1187 */
1188 while ( fgets(linebuf, sizeof(linebuf), fp) != NULL)
1189 {
1190 nr = sscanf(linebuf, "%s %lld", nam, &countval);
1191
1192 if (nr < 2) /* unexpected line ? --> skip */
1193 continue;
1194
1195 if (strcmp(v6tab[cur].nam, nam) == 0)
1196 {
1197 *(v6tab[cur].val) = countval;
1198 }
1199 else
1200 {
1201 for (cur=0; cur < v6tab_entries; cur++)
1202 if (strcmp(v6tab[cur].nam, nam) == 0)
1203 break;
1204
1205 if (cur < v6tab_entries) /* found ? */
1206 *(v6tab[cur].val) = countval;
1207 }
1208
1209 if (++cur >= v6tab_entries)
1210 cur = 0;
1211 }
1212
1213 memcpy(&si->net.ipv6, &ipv6_tmp, sizeof ipv6_tmp);
1214 memcpy(&si->net.icmpv6, &icmpv6_tmp, sizeof icmpv6_tmp);
1215 memcpy(&si->net.udpv6, &udpv6_tmp, sizeof udpv6_tmp);
1216
1217 fclose(fp);
1218 }
1219
1220 /*
1221 ** IP version 4: TCP & UDP memory allocations.
1222 */
1223 if ( (fp = fopen("net/sockstat", "r")) != NULL)
1224 {
1225 char tcpmem[16], udpmem[16];
1226
1227 while ( fgets(linebuf, sizeof(linebuf), fp) != NULL)
1228 {
1229 nr = sscanf(linebuf,
1230 "%15s %*s %*d %s %lld %*s %*d %*s %*d %s %lld\n",
1231 nam, udpmem, &cnts[0], tcpmem, &cnts[1]);
1232
1233 if ( strcmp("TCP:", nam) == 0)
1234 {
1235 if ( strcmp("mem", tcpmem) == 0) {
1236 si->mem.tcpsock = cnts[1];
1237 }
1238 continue;
1239 }
1240
1241 if ( strcmp("UDP:", nam) == 0)
1242 {
1243 if ( strcmp("mem", udpmem) == 0) {
1244 si->mem.udpsock = cnts[0];
1245 }
1246 continue;
1247 }
1248 }
1249 fclose(fp);
1250 }
1251
1252 /*
1253 ** check if extended partition-statistics are provided < kernel 2.6
1254 */
1255 if ( part_stats && (fp = fopen("partitions", "r")) != NULL)
1256 {
1257 char diskname[256];
1258
1259 i = 0;
1260
1261 while ( fgets(linebuf, sizeof(linebuf), fp) )
1262 {
1263 nr = sscanf(linebuf,
1264 "%*d %*d %*d %255s %lld %*d %lld %*d "
1265 "%lld %*d %lld %*d %lld %lld %lld",
1266 diskname,
1267 &(si->dsk.dsk[i].nread),
1268 &(si->dsk.dsk[i].nrsect),
1269 &(si->dsk.dsk[i].nwrite),
1270 &(si->dsk.dsk[i].nwsect),
1271 &(si->dsk.dsk[i].inflight),
1272 &(si->dsk.dsk[i].io_ms),
1273 &(si->dsk.dsk[i].avque) );
1274
1275 /*
1276 ** check if this line concerns the entire disk
1277 ** or just one of the partitions of a disk (to be
1278 ** skipped)
1279 */
1280 if (nr == 8) /* full stats-line ? */
1281 {
1282 if ( isdisk(0, 0, diskname,
1283 &(si->dsk.dsk[i]),
1284 MAXDKNAM) != DSKTYPE)
1285 continue;
1286
1287 if (++i >= MAXDSK-1)
1288 break;
1289 }
1290 }
1291
1292 si->dsk.dsk[i].name[0] = '\0'; /* set terminator for table */
1293 si->dsk.ndsk = i;
1294
1295 fclose(fp);
1296
1297 if (i == 0)
1298 part_stats = 0; /* do not try again for next cycles */
1299 }
1300
1301
1302 /*
1303 ** check if disk-statistics are provided (kernel 2.6 onwards)
1304 */
1305 if ( (fp = fopen("diskstats", "r")) != NULL)
1306 {
1307 char diskname[256];
1308 struct perdsk tmpdsk;
1309
1310 si->dsk.ndsk = 0;
1311 si->dsk.nmdd = 0;
1312 si->dsk.nlvm = 0;
1313
1314 while ( fgets(linebuf, sizeof(linebuf), fp) )
1315 {
1316 /* discards are not supported in older kernels */
1317 tmpdsk.ndisc = -1;
1318
1319 nr = sscanf(linebuf,
1320 "%d %d %255s " // ident
1321 "%lld %*d %lld %*d " // reads
1322 "%lld %*d %lld %*d " // writes
1323 "%lld %lld %lld " // misc
1324 "%lld %*d %lld %*d", // discards
1325 &major, &minor, diskname,
1326 &tmpdsk.nread, &tmpdsk.nrsect,
1327 &tmpdsk.nwrite, &tmpdsk.nwsect,
1328 &tmpdsk.inflight, &tmpdsk.io_ms, &tmpdsk.avque,
1329 &tmpdsk.ndisc, &tmpdsk.ndsect);
1330
1331 if (nr >= 10) /* full stats-line ? */
1332 {
1333 /*
1334 ** when no transfers issued, skip disk (partition)
1335 */
1336 if (tmpdsk.nread + tmpdsk.nwrite +
1337 (tmpdsk.ndisc == -1 ? 0 : tmpdsk.ndisc) == 0)
1338 continue;
1339
1340 /*
1341 ** check if this line concerns the entire disk
1342 ** or just one of the partitions of a disk (to be
1343 ** skipped)
1344 */
1345 switch ( isdisk(major, minor, diskname,
1346 &tmpdsk, MAXDKNAM) )
1347 {
1348 case NONTYPE:
1349 continue;
1350
1351 case DSKTYPE:
1352 if (si->dsk.ndsk < MAXDSK-1)
1353 si->dsk.dsk[si->dsk.ndsk++] = tmpdsk;
1354 break;
1355
1356 case MDDTYPE:
1357 if (si->dsk.nmdd < MAXMDD-1)
1358 si->dsk.mdd[si->dsk.nmdd++] = tmpdsk;
1359 break;
1360
1361 case LVMTYPE:
1362 if (si->dsk.nlvm < MAXLVM-1)
1363 si->dsk.lvm[si->dsk.nlvm++] = tmpdsk;
1364 break;
1365 }
1366 }
1367 }
1368
1369 /*
1370 ** set terminator for table
1371 */
1372 si->dsk.dsk[si->dsk.ndsk].name[0] = '\0';
1373 si->dsk.mdd[si->dsk.nmdd].name[0] = '\0';
1374 si->dsk.lvm[si->dsk.nlvm].name[0] = '\0';
1375
1376 fclose(fp);
1377 }
1378
1379 /*
1380 ** get information about the shared memory statistics
1381 */
1382 if ( shmctl(0, SHM_INFO, (struct shmid_ds *)&shminfo) != -1)
1383 {
1384 si->mem.shmrss = shminfo.shm_rss;
1385 si->mem.shmswp = shminfo.shm_swp;
1386 }
1387
1388 /*
1389 ** NFS server statistics
1390 */
1391 if ( (fp = fopen("net/rpc/nfsd", "r")) != NULL)
1392 {
1393 char label[32];
1394 count_t cnt[40];
1395
1396 /*
1397 ** every line starts with a small label,
1398 ** followed by upto 60 counters
1399 */
1400 while ( fgets(linebuf, sizeof(linebuf), fp) != NULL)
1401 {
1402 memset(cnt, 0, sizeof cnt);
1403
1404 nr = sscanf(linebuf, "%31s %lld %lld %lld %lld %lld"
1405 "%lld %lld %lld %lld %lld"
1406 "%lld %lld %lld %lld %lld"
1407 "%lld %lld %lld %lld %lld"
1408 "%lld %lld %lld %lld %lld"
1409 "%lld %lld %lld %lld %lld"
1410 "%lld %lld %lld %lld %lld"
1411 "%lld %lld %lld %lld %lld",
1412 label,
1413 &cnt[0], &cnt[1], &cnt[2], &cnt[3],
1414 &cnt[4], &cnt[5], &cnt[6], &cnt[7],
1415 &cnt[8], &cnt[9], &cnt[10], &cnt[11],
1416 &cnt[12], &cnt[13], &cnt[14], &cnt[15],
1417 &cnt[16], &cnt[17], &cnt[18], &cnt[19],
1418 &cnt[20], &cnt[21], &cnt[22], &cnt[23],
1419 &cnt[24], &cnt[25], &cnt[26], &cnt[27],
1420 &cnt[28], &cnt[29], &cnt[30], &cnt[31],
1421 &cnt[32], &cnt[33], &cnt[34], &cnt[35],
1422 &cnt[36], &cnt[37], &cnt[38], &cnt[39]);
1423
1424 if (nr < 2) // unexpected empty line ?
1425 continue;
1426
1427 if (strcmp(label, "rc") == 0)
1428 {
1429 si->nfs.server.rchits = cnt[0];
1430 si->nfs.server.rcmiss = cnt[1];
1431 si->nfs.server.rcnoca = cnt[2];
1432
1433 continue;
1434 }
1435
1436 if (strcmp(label, "io") == 0)
1437 {
1438 si->nfs.server.nrbytes = cnt[0];
1439 si->nfs.server.nwbytes = cnt[1];
1440
1441 continue;
1442 }
1443
1444 if (strcmp(label, "net") == 0)
1445 {
1446 si->nfs.server.netcnt = cnt[0];
1447 si->nfs.server.netudpcnt = cnt[1];
1448 si->nfs.server.nettcpcnt = cnt[2];
1449 si->nfs.server.nettcpcon = cnt[3];
1450
1451 continue;
1452 }
1453
1454 if (strcmp(label, "rpc") == 0)
1455 {
1456 si->nfs.server.rpccnt = cnt[0];
1457 si->nfs.server.rpcbadfmt = cnt[1];
1458 si->nfs.server.rpcbadaut = cnt[2];
1459 si->nfs.server.rpcbadcln = cnt[3];
1460
1461 continue;
1462 }
1463 //
1464 // first counter behind 'proc..' is number of
1465 // counters that follow
1466 if (strcmp(label, "proc2") == 0)
1467 {
1468 si->nfs.server.rpcread += cnt[7]; // offset+1
1469 si->nfs.server.rpcwrite += cnt[9]; // offset+1
1470 continue;
1471 }
1472 if (strcmp(label, "proc3") == 0)
1473 {
1474 si->nfs.server.rpcread += cnt[7]; // offset+1
1475 si->nfs.server.rpcwrite += cnt[8]; // offset+1
1476 continue;
1477 }
1478 if (strcmp(label, "proc4ops") == 0)
1479 {
1480 si->nfs.server.rpcread += cnt[26]; // offset+1
1481 si->nfs.server.rpcwrite += cnt[39]; // offset+1
1482 continue;
1483 }
1484 }
1485
1486 fclose(fp);
1487 }
1488
1489 /*
1490 ** NFS client statistics
1491 */
1492 if ( (fp = fopen("net/rpc/nfs", "r")) != NULL)
1493 {
1494 char label[32];
1495 count_t cnt[10];
1496
1497 /*
1498 ** every line starts with a small label,
1499 ** followed by counters
1500 */
1501 while ( fgets(linebuf, sizeof(linebuf), fp) != NULL)
1502 {
1503 memset(cnt, 0, sizeof cnt);
1504
1505 nr = sscanf(linebuf, "%31s %lld %lld %lld %lld %lld"
1506 "%lld %lld %lld %lld %lld",
1507 label,
1508 &cnt[0], &cnt[1], &cnt[2], &cnt[3],
1509 &cnt[4], &cnt[5], &cnt[6], &cnt[7],
1510 &cnt[8], &cnt[9]);
1511
1512 if (nr < 2) // unexpected empty line ?
1513 continue;
1514
1515 if (strcmp(label, "rpc") == 0)
1516 {
1517 si->nfs.client.rpccnt = cnt[0];
1518 si->nfs.client.rpcretrans = cnt[1];
1519 si->nfs.client.rpcautrefresh = cnt[2];
1520 continue;
1521 }
1522
1523 // first counter behind 'proc..' is number of
1524 // counters that follow
1525 if (strcmp(label, "proc2") == 0)
1526 {
1527 si->nfs.client.rpcread += cnt[7]; // offset+1
1528 si->nfs.client.rpcwrite += cnt[9]; // offset+1
1529 continue;
1530 }
1531 if (strcmp(label, "proc3") == 0)
1532 {
1533 si->nfs.client.rpcread += cnt[7]; // offset+1
1534 si->nfs.client.rpcwrite += cnt[8]; // offset+1
1535 continue;
1536 }
1537 if (strcmp(label, "proc4") == 0)
1538 {
1539 si->nfs.client.rpcread += cnt[2]; // offset+1
1540 si->nfs.client.rpcwrite += cnt[3]; // offset+1
1541 continue;
1542 }
1543 }
1544
1545 fclose(fp);
1546 }
1547
1548 /*
1549 ** NFS client: per-mount statistics
1550 */
1551 regainrootprivs();
1552
1553 if ( (fp = fopen("self/mountstats", "r")) != NULL)
1554 {
1555 char mountdev[128], fstype[32], label[32];
1556 count_t cnt[8];
1557
1558 i = 0;
1559
1560 while ( fgets(linebuf, sizeof(linebuf), fp) != NULL)
1561 {
1562 // if 'device' line, just remember the mounted device
1563 if (sscanf(linebuf,
1564 "device %127s mounted on %*s with fstype %31s",
1565 mountdev, fstype) == 2)
1566 {
1567 continue;
1568 }
1569
1570 if (memcmp(fstype, "nfs", 3) != 0)
1571 continue;
1572
1573 // this is line with NFS client stats
1574 nr = sscanf(linebuf,
1575 "%31s %lld %lld %lld %lld %lld %lld %lld %lld",
1576 label, &cnt[0], &cnt[1], &cnt[2], &cnt[3],
1577 &cnt[4], &cnt[5], &cnt[6], &cnt[7]);
1578
1579 if (nr >= 2 )
1580 {
1581 if (strcmp(label, "age:") == 0)
1582 {
1583 strcpy(si->nfs.nfsmounts.nfsmnt[i].mountdev,
1584 mountdev);
1585
1586 si->nfs.nfsmounts.nfsmnt[i].age = cnt[0];
1587 }
1588
1589 if (strcmp(label, "bytes:") == 0)
1590 {
1591 si->nfs.nfsmounts.nfsmnt[i].bytesread =
1592 cnt[0];
1593 si->nfs.nfsmounts.nfsmnt[i].byteswrite =
1594 cnt[1];
1595 si->nfs.nfsmounts.nfsmnt[i].bytesdread =
1596 cnt[2];
1597 si->nfs.nfsmounts.nfsmnt[i].bytesdwrite =
1598 cnt[3];
1599 si->nfs.nfsmounts.nfsmnt[i].bytestotread =
1600 cnt[4];
1601 si->nfs.nfsmounts.nfsmnt[i].bytestotwrite =
1602 cnt[5];
1603 si->nfs.nfsmounts.nfsmnt[i].pagesmread =
1604 cnt[6];
1605 si->nfs.nfsmounts.nfsmnt[i].pagesmwrite =
1606 cnt[7];
1607
1608 if (++i >= MAXNFSMOUNT-1)
1609 break;
1610 }
1611 }
1612 }
1613
1614 si->nfs.nfsmounts.nrmounts = i;
1615
1616 fclose(fp);
1617 }
1618
1619 if (! droprootprivs())
1620 mcleanstop(42, "failed to drop root privs\n");
1621
1622 /*
1623 ** pressure statistics in /proc/pressure (>= 4.20)
1624 **
1625 ** cpu: some avg10=0.00 avg60=1.37 avg300=3.73 total=30995960
1626 ** io: some avg10=0.00 avg60=8.83 avg300=22.86 total=141658568
1627 ** io: full avg10=0.00 avg60=8.33 avg300=21.56 total=133129045
1628 ** memory: some avg10=0.00 avg60=0.74 avg300=1.67 total=10663184
1629 ** memory: full avg10=0.00 avg60=0.45 avg300=0.94 total=6461782
1630 **
1631 ** verify if pressure stats supported by this system
1632 */
1633 if ( chdir("pressure") == 0)
1634 {
1635 struct psi psitemp;
1636 char psitype;
1637 char psiformat[] =
1638 "%c%*s avg10=%f avg60=%f avg300=%f total=%llu";
1639
1640 si->psi.present = 1;
1641
1642 if ( (fp = fopen("cpu", "r")) != NULL)
1643 {
1644 if ( fgets(linebuf, sizeof(linebuf), fp) != NULL)
1645 {
1646 nr = sscanf(linebuf, psiformat,
1647 &psitype,
1648 &psitemp.avg10, &psitemp.avg60,
1649 &psitemp.avg300, &psitemp.total);
1650
1651 if (nr == 5) // complete line ?
1652 memmove(&(si->psi.cpusome), &psitemp,
1653 sizeof psitemp);
1654 }
1655 fclose(fp);
1656 }
1657
1658 if ( (fp = fopen("memory", "r")) != NULL)
1659 {
1660 while ( fgets(linebuf, sizeof(linebuf), fp) != NULL)
1661 {
1662 nr = sscanf(linebuf, psiformat,
1663 &psitype,
1664 &psitemp.avg10, &psitemp.avg60,
1665 &psitemp.avg300, &psitemp.total);
1666
1667 if (nr == 5)
1668 {
1669 if (psitype == 's')
1670 memmove(&(si->psi.memsome),
1671 &psitemp,
1672 sizeof psitemp);
1673 else
1674 memmove(&(si->psi.memfull),
1675 &psitemp,
1676 sizeof psitemp);
1677 }
1678 }
1679 fclose(fp);
1680 }
1681
1682 if ( (fp = fopen("io", "r")) != NULL)
1683 {
1684 while ( fgets(linebuf, sizeof(linebuf), fp) != NULL)
1685 {
1686 nr = sscanf(linebuf, psiformat,
1687 &psitype,
1688 &psitemp.avg10, &psitemp.avg60,
1689 &psitemp.avg300, &psitemp.total);
1690
1691 if (nr == 5)
1692 {
1693 if (psitype == 's')
1694 memmove(&(si->psi.iosome),
1695 &psitemp,
1696 sizeof psitemp);
1697 else
1698 memmove(&(si->psi.iofull),
1699 &psitemp,
1700 sizeof psitemp);
1701 }
1702 }
1703 fclose(fp);
1704 }
1705
1706 if ( chdir("..") == -1)
1707 mcleanstop(54, "failed to return to /proc\n");
1708 }
1709 else
1710 {
1711 si->psi.present = 0;
1712 }
1713
1714 /*
1715 ** Container statistics (if any)
1716 */
1717 if ( (fp = fopen("user_beancounters", "r")) != NULL)
1718 {
1719 unsigned long ctid;
1720 char label[32];
1721 count_t cnt;
1722
1723 i = -1;
1724
1725 /*
1726 ** lines introducing a new container have an extra
1727 ** field with the container id at the beginning.
1728 */
1729 while ( fgets(linebuf, sizeof(linebuf), fp) != NULL)
1730 {
1731 nr = sscanf(linebuf, "%lu: %31s %lld",
1732 &ctid, label, &cnt);
1733
1734 if (nr == 3) // new container ?
1735 {
1736 if (++i >= MAXCONTAINER)
1737 break;
1738
1739 si->cfs.cont[i].ctid = ctid;
1740 }
1741 else
1742 {
1743 nr = sscanf(linebuf, "%31s %lld", label, &cnt);
1744
1745 if (nr != 2)
1746 continue;
1747 }
1748
1749 if (i == -1) // no container defined yet
1750 continue;
1751
1752 if (strcmp(label, "numproc") == 0)
1753 {
1754 si->cfs.cont[i].numproc = cnt;
1755 continue;
1756 }
1757
1758 if (strcmp(label, "physpages") == 0)
1759 {
1760 si->cfs.cont[i].physpages = cnt;
1761 continue;
1762 }
1763 }
1764
1765 fclose(fp);
1766
1767 si->cfs.nrcontainer = i+1;
1768
1769 if ( (fp = fopen("vz/vestat", "r")) != NULL)
1770 {
1771 unsigned long ctid;
1772 count_t cnt[8];
1773
1774 /*
1775 ** relevant lines start with container id
1776 */
1777 while ( fgets(linebuf, sizeof(linebuf), fp) != NULL)
1778 {
1779 nr = sscanf(linebuf, "%lu %lld %lld %lld %lld"
1780 "%lld %lld %lld %lld %lld",
1781 &ctid,
1782 &cnt[0], &cnt[1], &cnt[2], &cnt[3],
1783 &cnt[4], &cnt[5], &cnt[6], &cnt[7],
1784 &cnt[8]);
1785
1786 if (nr < 9) // irrelevant contents
1787 continue;
1788
1789 // relevant stats: search for containerid
1790 for (i=0; i < si->cfs.nrcontainer; i++)
1791 {
1792 if (si->cfs.cont[i].ctid == ctid)
1793 break;
1794 }
1795
1796 if (i >= si->cfs.nrcontainer)
1797 continue; // container not found
1798
1799 si->cfs.cont[i].user = cnt[0];
1800 si->cfs.cont[i].nice = cnt[1];
1801 si->cfs.cont[i].system = cnt[2];
1802 si->cfs.cont[i].uptime = cnt[3];
1803 }
1804
1805 fclose(fp);
1806 }
1807 }
1808
1809 /*
1810 ** gather per LLC related statistics from the file
1811 ** /sys/fs/resctrl/mon_data/mon_L3_XX
1812 */
1813 dirp = opendir(LLCDIR);
1814
1815 if (dirp)
1816 {
1817 static int l3_cache_size;
1818
1819 if (!l3_cache_size)
1820 {
1821 if ( (fp = fopen(L3SIZE, "r")) != NULL)
1822 {
1823 if ( fgets(linebuf, sizeof(linebuf), fp) != NULL)
1824 {
1825 sscanf(linebuf, "%uK\n", &l3_cache_size);
1826 l3_cache_size *= 1024;
1827 }
1828
1829 fclose(fp);
1830 }
1831 }
1832
1833 /*
1834 ** walk the LLC directory, gather each LLC
1835 */
1836 while ( (dentry = readdir(dirp)) )
1837 {
1838 struct perllc *llc = &si->llc.perllc[si->llc.nrllcs];
1839 unsigned long llc_occupancy;
1840
1841 if (strncmp(dentry->d_name, "mon_L3_", 7))
1842 continue;
1843
1844 /* get cache id from directory name like mon_L3_00 */
1845 sscanf(dentry->d_name + 7, "%hhd\n", &llc->id);
1846
1847 snprintf(fn, sizeof fn, LLCDIR "/%s/llc_occupancy", dentry->d_name);
1848 if ( (fp = fopen(fn, "r")) != NULL)
1849 {
1850 if ( fgets(linebuf, sizeof(linebuf), fp) != NULL)
1851 {
1852 sscanf(linebuf, "%lu\n", &llc_occupancy);
1853 llc->occupancy = (float)llc_occupancy / l3_cache_size;
1854 }
1855
1856 fclose(fp);
1857 }
1858
1859 snprintf(fn, sizeof fn, LLCDIR "/%s/mbm_local_bytes", dentry->d_name);
1860 if ( (fp = fopen(fn, "r")) != NULL)
1861 {
1862 if ( fgets(linebuf, sizeof(linebuf), fp) != NULL)
1863 {
1864 sscanf(linebuf, "%llu\n", &llc->mbm_local);
1865 }
1866
1867 fclose(fp);
1868 }
1869
1870 snprintf(fn, sizeof fn, LLCDIR "/%s/mbm_total_bytes", dentry->d_name);
1871 if ( (fp = fopen(fn, "r")) != NULL)
1872 {
1873 if ( fgets(linebuf, sizeof(linebuf), fp) != NULL)
1874 {
1875 sscanf(linebuf, "%llu\n", &llc->mbm_total);
1876 }
1877 fclose(fp);
1878 }
1879
1880 if (++si->llc.nrllcs >= MAXLLC) /* too many LLC ? */
1881 break;
1882 }
1883
1884 closedir(dirp);
1885 }
1886
1887 /*
1888 ** verify presence of InfiniBand controllers
1889 ** warning: possibly switches to other directory
1890 */
1891 if (ib_stats)
1892 ib_stats = get_infiniband(&(si->ifb));
1893
1894 /*
1895 ** get counters related to ksm
1896 */
1897 if (ksm_stats)
1898 ksm_stats = get_ksm(si);
1899
1900 /*
1901 ** get counters related to zswap
1902 */
1903 if (zswap_stats)
1904 zswap_stats = get_zswap(si);
1905
1906 /*
1907 ** return to original directory
1908 */
1909 if ( chdir(origdir) == -1)
1910 mcleanstop(55, "failed to change to %s\n", origdir);
1911
1912 #ifndef NOPERFEVENT
1913 /*
1914 ** get low-level CPU event counters
1915 */
1916 getperfevents(&(si->cpu));
1917 #endif
1918
1919 /*
1920 ** fetch application-specific counters
1921 */
1922 #if HTTPSTATS
1923 if ( wwwvalid)
1924 wwwvalid = getwwwstat(80, &(si->www));
1925 #endif
1926 }
1927
1928 /*
1929 ** set of subroutines to determine which disks should be monitored
1930 ** and to translate name strings into (shorter) name strings
1931 */
1932 static void
1933 nullmodname(unsigned int major, unsigned int minor,
1934 char *curname, struct perdsk *px, int maxlen)
1935 {
1936 strncpy(px->name, curname, maxlen-1);
1937 *(px->name+maxlen-1) = 0;
1938 }
1939
1940 static void
1941 abbrevname1(unsigned int major, unsigned int minor,
1942 char *curname, struct perdsk *px, int maxlen)
1943 {
1944 char cutype[128];
1945 int hostnum, busnum, targetnum, lunnum;
1946
1947 sscanf(curname, "%[^/]/host%d/bus%d/target%d/lun%d",
1948 cutype, &hostnum, &busnum, &targetnum, &lunnum);
1949
1950 snprintf(px->name, maxlen, "%c-h%db%dt%d",
1951 cutype[0], hostnum, busnum, targetnum);
1952 }
1953
1954 /*
1955 ** recognize LVM logical volumes
1956 */
1957 #define NUMDMHASH 64
1958 #define DMHASH(x,y) (((x)+(y))%NUMDMHASH)
1959 #define MAPDIR "/dev/mapper"
1960
1961 struct devmap {
1962 unsigned int major;
1963 unsigned int minor;
1964 char name[MAXDKNAM];
1965 struct devmap *next;
1966 };
1967
1968 static void
1969 lvmmapname(unsigned int major, unsigned int minor,
1970 char *curname, struct perdsk *px, int maxlen)
1971 {
1972 static int firstcall = 1;
1973 static struct devmap *devmaps[NUMDMHASH], *dmp;
1974 int hashix;
1975
1976 /*
1977 ** setup a list of major-minor numbers of dm-devices with their
1978 ** corresponding name
1979 */
1980 if (firstcall)
1981 {
1982 DIR *dirp;
1983 struct dirent *dentry;
1984 struct stat statbuf;
1985 char path[PATH_MAX];
1986
1987 if ( (dirp = opendir(MAPDIR)) )
1988 {
1989 /*
1990 ** read every directory-entry and search for
1991 ** block devices
1992 */
1993 while ( (dentry = readdir(dirp)) )
1994 {
1995 snprintf(path, sizeof path, "%s/%s",
1996 MAPDIR, dentry->d_name);
1997
1998 if ( stat(path, &statbuf) == -1 )
1999 continue;
2000
2001 if ( ! S_ISBLK(statbuf.st_mode) )
2002 continue;
2003 /*
2004 ** allocate struct to store name
2005 */
2006 if ( !(dmp = malloc(sizeof (struct devmap))))
2007 continue;
2008
2009 /*
2010 ** store info in hash list
2011 */
2012 strncpy(dmp->name, dentry->d_name, MAXDKNAM);
2013 dmp->name[MAXDKNAM-1] = 0;
2014 dmp->major = major(statbuf.st_rdev);
2015 dmp->minor = minor(statbuf.st_rdev);
2016
2017 hashix = DMHASH(dmp->major, dmp->minor);
2018
2019 dmp->next = devmaps[hashix];
2020
2021 devmaps[hashix] = dmp;
2022 }
2023
2024 closedir(dirp);
2025 }
2026
2027 firstcall = 0;
2028 }
2029
2030 /*
2031 ** find info in hash list
2032 */
2033 hashix = DMHASH(major, minor);
2034 dmp = devmaps[hashix];
2035
2036 while (dmp)
2037 {
2038 if (dmp->major == major && dmp->minor == minor)
2039 {
2040 /*
2041 ** info found in hash list; fill proper name
2042 */
2043 strncpy(px->name, dmp->name, maxlen-1);
2044 *(px->name+maxlen-1) = 0;
2045 return;
2046 }
2047
2048 dmp = dmp->next;
2049 }
2050
2051 /*
2052 ** info not found in hash list; fill original name
2053 */
2054 strncpy(px->name, curname, maxlen-1);
2055 *(px->name+maxlen-1) = 0;
2056 }
2057
2058 /*
2059 ** this table is used in the function isdisk()
2060 **
2061 ** table contains the names (in regexp format) of disks
2062 ** to be recognized, together with a function to modify
2063 ** the name-strings (i.e. to abbreviate long strings);
2064 ** some frequently found names (like 'loop' and 'ram')
2065 ** are also recognized to skip them as fast as possible
2066 */
2067 static struct {
2068 char *regexp;
2069 regex_t compreg;
2070 void (*modname)(unsigned int, unsigned int,
2071 char *, struct perdsk *, int);
2072 int retval;
2073 } validdisk[] = {
2074 { "^ram[0-9][0-9]*$", {0}, (void *)0, NONTYPE, },
2075 { "^loop[0-9][0-9]*$", {0}, (void *)0, NONTYPE, },
2076 { "^sd[a-z][a-z]*$", {0}, nullmodname, DSKTYPE, },
2077 { "^dm-[0-9][0-9]*$", {0}, lvmmapname, LVMTYPE, },
2078 { "^md[0-9][0-9]*$", {0}, nullmodname, MDDTYPE, },
2079 { "^vd[a-z][a-z]*$", {0}, nullmodname, DSKTYPE, },
2080 { "^nvme[0-9][0-9]*n[0-9][0-9]*$", {0}, nullmodname, DSKTYPE, },
2081 { "^nvme[0-9][0-9]*c[0-9][0-9]*n[0-9][0-9]*$", {0}, nullmodname, DSKTYPE, },
2082 { "^nbd[0-9][0-9]*$", {0}, nullmodname, DSKTYPE, },
2083 { "^hd[a-z]$", {0}, nullmodname, DSKTYPE, },
2084 { "^rd/c[0-9][0-9]*d[0-9][0-9]*$", {0}, nullmodname, DSKTYPE, },
2085 { "^cciss/c[0-9][0-9]*d[0-9][0-9]*$", {0}, nullmodname, DSKTYPE, },
2086 { "^fio[a-z][a-z]*$", {0}, nullmodname, DSKTYPE, },
2087 { "/host.*/bus.*/target.*/lun.*/disc", {0}, abbrevname1, DSKTYPE, },
2088 { "^xvd[a-z][a-z]*[0-9]*$", {0}, nullmodname, DSKTYPE, },
2089 { "^dasd[a-z][a-z]*$", {0}, nullmodname, DSKTYPE, },
2090 { "^mmcblk[0-9][0-9]*$", {0}, nullmodname, DSKTYPE, },
2091 { "^emcpower[a-z][a-z]*$", {0}, nullmodname, DSKTYPE, },
2092 { "^rbd[0-9][0-9]*$", {0}, nullmodname, DSKTYPE, },
2093 { "^rbd[0-9][0-9]*p[0-9][0-9]*$", {0}, nullmodname, DSKTYPE, },
2094 };
2095
2096 static int
2097 isdisk(unsigned int major, unsigned int minor,
2098 char *curname, struct perdsk *px, int maxlen)
2099 {
2100 static int firstcall = 1;
2101 register int i;
2102
2103 if (firstcall) /* compile the regular expressions */
2104 {
2105 for (i=0; i < sizeof validdisk/sizeof validdisk[0]; i++)
2106 regcomp(&validdisk[i].compreg, validdisk[i].regexp,
2107 REG_NOSUB);
2108 firstcall = 0;
2109 }
2110
2111 /*
2112 ** try to recognize one of the compiled regular expressions
2113 */
2114 for (i=0; i < sizeof validdisk/sizeof validdisk[0]; i++)
2115 {
2116 if (regexec(&validdisk[i].compreg, curname, 0, NULL, 0) == 0)
2117 {
2118 /*
2119 ** name-string recognized; modify name-string
2120 */
2121 if (validdisk[i].retval != NONTYPE)
2122 (*validdisk[i].modname)(major, minor,
2123 curname, px, maxlen);
2124
2125 return validdisk[i].retval;
2126 }
2127 }
2128
2129 return NONTYPE;
2130 }
2131
2132 /*
2133 ** LINUX SPECIFIC:
2134 ** Determine boot-time of this system (as number of jiffies since 1-1-1970).
2135 */
2136 unsigned long long
2137 getbootlinux(long hertz)
2138 {
2139 int cpid;
2140 char tmpbuf[1280];
2141 FILE *fp;
2142 unsigned long startticks;
2143 unsigned long long bootjiffies = 0;
2144 struct timespec ts;
2145
2146 /*
2147 ** dirty hack to get the boottime, since the
2148 ** Linux 2.6 kernel (2.6.5) does not return a proper
2149 ** boottime-value with the times() system call :-(
2150 */
2151 if ( (cpid = fork()) == 0 )
2152 {
2153 /*
2154 ** child just waiting to be killed by parent
2155 */
2156 pause();
2157 }
2158 else
2159 {
2160 /*
2161 ** parent determines start-time (in jiffies since boot)
2162 ** of the child and calculates the boottime in jiffies
2163 ** since 1-1-1970
2164 */
2165 (void) clock_gettime(CLOCK_REALTIME, &ts); // get current
2166 bootjiffies = 1LL * ts.tv_sec * hertz +
2167 1LL * ts.tv_nsec * hertz / 1000000000LL;
2168
2169 snprintf(tmpbuf, sizeof tmpbuf, "/proc/%d/stat", cpid);
2170
2171 if ( (fp = fopen(tmpbuf, "r")) != NULL)
2172 {
2173 if ( fscanf(fp, "%*d (%*[^)]) %*c %*d %*d %*d %*d "
2174 "%*d %*d %*d %*d %*d %*d %*d %*d "
2175 "%*d %*d %*d %*d %*d %*d %lu",
2176 &startticks) == 1)
2177 {
2178 bootjiffies -= startticks;
2179 }
2180
2181 fclose(fp);
2182 }
2183
2184 /*
2185 ** kill the child and get rid of the zombie
2186 */
2187 kill(cpid, SIGKILL);
2188 (void) wait((int *)0);
2189 }
2190
2191 return bootjiffies;
2192 }
2193
2194
2195 /*
2196 ** get stats of all InfiniBand ports below
2197 ** /sys/class/infiniband/<controller>/ports/<port#>/....
2198 */
2199 static struct ibcachent {
2200 char *ibha; // InfiniBand Host Adaptor
2201 unsigned short port; // port number
2202 unsigned short lanes; // number of lanes
2203 count_t rate; // transfer rate in bytes/sec
2204 char *pathrcvb; // path name for received bytes
2205 char *pathsndb; // path name for transmitted bytes
2206 char *pathrcvp; // path name for received packets
2207 char *pathsndp; // path name for transmitted packets
2208 } ibcache[MAXIBPORT];
2209
2210 static int nib; // number of IB ports in cache
2211
2212 static void ibprep(struct ibcachent *);
2213 static int ibstat(struct ibcachent *, struct perifb *);
2214
2215 static int
2216 get_infiniband(struct ifbstat *si)
2217 {
2218 static int firstcall = 1;
2219 int i;
2220
2221 // verify if InfiniBand used in this system
2222 if ( chdir("/sys/class/infiniband") == -1)
2223 return 0; // no path, no IB, so don't try again
2224
2225 if (firstcall)
2226 {
2227 char path[PATH_MAX], *p;
2228 struct stat statbuf;
2229 struct dirent *contdent, *portdent;
2230 DIR *contp, *portp;
2231
2232 firstcall = 0;
2233
2234 /*
2235 ** once setup a cache with all info that is needed
2236 ** to gather the necessary stats with every subsequent
2237 ** call, including path names, etcetera.
2238 */
2239 if ( (contp = opendir(".")) )
2240 {
2241 /*
2242 ** read every directory-entry and search for
2243 ** subdirectories (i.e. controllers)
2244 */
2245 while ( (contdent = readdir(contp)) )
2246 {
2247 // skip . and ..
2248 if (contdent->d_name[0] == '.')
2249 continue;
2250
2251 if ( stat(contdent->d_name, &statbuf) == -1 )
2252 continue;
2253
2254 if ( ! S_ISDIR(statbuf.st_mode) )
2255 continue;
2256
2257 // controller found
2258 // store controller name for cache
2259 //
2260 p = malloc( strlen(contdent->d_name)+1 );
2261 strcpy(p, contdent->d_name);
2262
2263 if (strlen(contdent->d_name) > MAXIBNAME-1)
2264 p[MAXIBNAME-1] = '\0';
2265
2266 // discover all ports
2267 //
2268 snprintf(path, sizeof path, "%s/ports",
2269 contdent->d_name);
2270
2271 if ( (portp = opendir(path)) )
2272 {
2273 /*
2274 ** read every directory-entry and
2275 ** search for subdirectories (i.e.
2276 ** port numbers)
2277 */
2278 while ( (portdent = readdir(portp)) )
2279 {
2280 int port;
2281
2282 // skip . and ..
2283 if (portdent->d_name[0] == '.')
2284 continue;
2285
2286 port = atoi(portdent->d_name);
2287
2288 if (!port)
2289 continue;
2290
2291 // valid port
2292 // fill cache info
2293 //
2294 ibcache[nib].port = port;
2295 ibcache[nib].ibha = p;
2296
2297 ibprep(&ibcache[nib]);
2298
2299 if (++nib >= MAXIBPORT)
2300 break;
2301 }
2302
2303 closedir(portp);
2304
2305 if (nib >= MAXIBPORT)
2306 break;
2307 }
2308 }
2309
2310 closedir(contp);
2311 }
2312 }
2313
2314 /*
2315 ** get static and variable metrics
2316 */
2317 for (i=0; i < nib; i++)
2318 {
2319 // static metrics from cache
2320 strcpy(si->ifb[i].ibname, ibcache[i].ibha);
2321
2322 si->ifb[i].portnr = ibcache[i].port;
2323 si->ifb[i].lanes = ibcache[i].lanes;
2324 si->ifb[i].rate = ibcache[i].rate;
2325
2326 // variable metrics from sysfs
2327 ibstat(&(ibcache[i]), &(si->ifb[i]));
2328 }
2329
2330 si->nrports = nib;
2331 return 1;
2332 }
2333
2334 /*
2335 ** determine rate and number of lanes
2336 ** from <contr>/ports/<port>/rate --> e.g. "100 Gb/sec (4X EDR)"
2337 ** and assemble path names to be used for counters later on
2338 */
2339 static void
2340 ibprep(struct ibcachent *ibc)
2341 {
2342 FILE *fp;
2343 char path[PATH_MAX], linebuf[64], speedunit;
2344
2345 // determine port rate and number of lanes
2346 snprintf(path, sizeof path, "%s/ports/%d/rate", ibc->ibha, ibc->port);
2347
2348 if ( (fp = fopen(path, "r")) )
2349 {
2350 if ( fgets(linebuf, sizeof(linebuf), fp) != NULL)
2351 {
2352 (void) sscanf(linebuf, "%lld %c%*s (%hdX",
2353 &(ibc->rate), &speedunit, &(ibc->lanes));
2354
2355 // calculate megabits/second
2356 switch (speedunit)
2357 {
2358 case 'M':
2359 case 'm':
2360 break;
2361 case 'G':
2362 case 'g':
2363 ibc->rate *= 1000;
2364 break;
2365 case 'T':
2366 case 't':
2367 ibc->rate *= 1000000;
2368 break;
2369 }
2370
2371 }
2372 else
2373 {
2374 ibc->lanes = 0;
2375 ibc->rate = 0;
2376 }
2377
2378 fclose(fp);
2379 }
2380
2381 // build all pathnames to obtain the counters
2382 // of this port later on
2383 snprintf(path, sizeof path, "%s/ports/%d/counters/port_rcv_data",
2384 ibc->ibha, ibc->port);
2385 ibc->pathrcvb = malloc( strlen(path)+1 );
2386 strcpy(ibc->pathrcvb, path);
2387
2388 snprintf(path, sizeof path, "%s/ports/%d/counters/port_xmit_data",
2389 ibc->ibha, ibc->port);
2390 ibc->pathsndb = malloc( strlen(path)+1 );
2391 strcpy(ibc->pathsndb, path);
2392
2393 snprintf(path, sizeof path, "%s/ports/%d/counters/port_rcv_packets",
2394 ibc->ibha, ibc->port);
2395 ibc->pathrcvp = malloc( strlen(path)+1 );
2396 strcpy(ibc->pathrcvp, path);
2397
2398 snprintf(path, sizeof path, "%s/ports/%d/counters/port_xmit_packets",
2399 ibc->ibha, ibc->port);
2400 ibc->pathsndp = malloc( strlen(path)+1 );
2401 strcpy(ibc->pathsndp, path);
2402 }
2403
2404 /*
2405 ** read necessary variable counters for this IB port
2406 */
2407 static int
2408 ibstat(struct ibcachent *ibc, struct perifb *ifb)
2409 {
2410 FILE *fp;
2411 char linebuf[64];
2412
2413 if ( (fp = fopen(ibc->pathrcvb, "r")) )
2414 {
2415 if ( fgets(linebuf, sizeof(linebuf), fp) != NULL)
2416 {
2417 if (sscanf(linebuf, "%lld", &(ifb->rcvb)) == 0)
2418 ifb->rcvb = 0;
2419 }
2420 else
2421 {
2422 ifb->rcvb = 0;
2423 }
2424 fclose(fp);
2425 }
2426
2427 if ( (fp = fopen(ibc->pathsndb, "r")) )
2428 {
2429 if ( fgets(linebuf, sizeof(linebuf), fp) != NULL)
2430 {
2431 if (sscanf(linebuf, "%lld", &(ifb->sndb)) == 0)
2432 ifb->sndb = 0;
2433 }
2434 else
2435 {
2436 ifb->sndb = 0;
2437 }
2438 fclose(fp);
2439 }
2440
2441 if ( (fp = fopen(ibc->pathrcvp, "r")) )
2442 {
2443 if ( fgets(linebuf, sizeof(linebuf), fp) != NULL)
2444 {
2445 if (sscanf(linebuf, "%lld", &(ifb->rcvp)) == 0)
2446 ifb->rcvp = 0;
2447 }
2448 else
2449 {
2450 ifb->rcvp = 0;
2451 }
2452 fclose(fp);
2453 }
2454
2455 if ( (fp = fopen(ibc->pathsndp, "r")) )
2456 {
2457 if ( fgets(linebuf, sizeof(linebuf), fp) != NULL)
2458 {
2459 if (sscanf(linebuf, "%lld", &(ifb->sndp)) == 0)
2460 ifb->sndp = 0;
2461 }
2462 else
2463 {
2464 ifb->sndp = 0;
2465 }
2466 fclose(fp);
2467 }
2468
2469 return 1;
2470 }
2471
2472 /*
2473 ** retrieve ksm values (if switched on)
2474 */
2475 static int
2476 get_ksm(struct sstat *si)
2477 {
2478 FILE *fp;
2479 int state;
2480
2481 si->mem.ksmsharing = -1;
2482 si->mem.ksmshared = -1;
2483
2484 if ((fp=fopen("/sys/kernel/mm/ksm/run", "r")) != 0)
2485 {
2486 if (fscanf(fp, "%d", &state) == 1)
2487 {
2488 if (state == 0)
2489 {
2490 fclose(fp);
2491 return 0; // no more calling
2492 }
2493 }
2494
2495 fclose(fp);
2496 }
2497
2498 if ((fp=fopen("/sys/kernel/mm/ksm/pages_sharing", "r")) != 0)
2499 {
2500 if (fscanf(fp, "%llu", &(si->mem.ksmsharing)) != 1)
2501 si->mem.ksmsharing = 0;
2502
2503 fclose(fp);
2504 }
2505
2506 if ((fp=fopen("/sys/kernel/mm/ksm/pages_shared", "r")) != 0)
2507 {
2508 if (fscanf(fp, "%llu", &(si->mem.ksmshared)) != 1)
2509 si->mem.ksmshared = 0;
2510
2511 fclose(fp);
2512 }
2513
2514 return 1;
2515 }
2516
2517 /*
2518 ** retrieve zswap values (if switched on)
2519 */
2520 static int
2521 get_zswap(struct sstat *si)
2522 {
2523 FILE *fp;
2524 char state;
2525
2526 si->mem.zswtotpool = -1;
2527 si->mem.zswstored = -1;
2528
2529 if ((fp=fopen("/sys/module/zswap/parameters/enabled", "r")) != 0)
2530 {
2531 if (fscanf(fp, "%c", &state) == 1)
2532 {
2533 if (state != 'Y')
2534 {
2535 fclose(fp);
2536 return 0; // no more calling
2537 }
2538 }
2539
2540 fclose(fp);
2541 }
2542
2543 regainrootprivs();
2544
2545 if ((fp=fopen("/sys/kernel/debug/zswap/pool_total_size", "r")) != 0)
2546 {
2547 if (fscanf(fp, "%llu", &(si->mem.zswtotpool)) != 1)
2548 si->mem.zswtotpool = 0;
2549 else
2550 si->mem.zswtotpool /= pagesize;
2551
2552 fclose(fp);
2553 }
2554
2555 if ((fp=fopen("/sys/kernel/debug/zswap/stored_pages", "r")) != 0)
2556 {
2557 if (fscanf(fp, "%llu", &(si->mem.zswstored)) != 1)
2558 si->mem.zswstored = 0;
2559
2560 fclose(fp);
2561 }
2562
2563 if (! droprootprivs())
2564 mcleanstop(42, "failed to drop root privs\n");
2565
2566 return 1;
2567 }
2568
2569
2570 #if HTTPSTATS
2571 /*
2572 ** retrieve statistics from local HTTP daemons
2573 ** via http://localhost/server-status?auto
2574 */
2575 int
2576 getwwwstat(unsigned short port, struct wwwstat *wp)
2577 {
2578 int sockfd, tobefound;
2579 FILE *sockfp;
2580 struct sockaddr_in sockname;
2581 char linebuf[4096];
2582 char label[512];
2583 long long value;
2584
2585 memset(wp, 0, sizeof *wp);
2586
2587 /*
2588 ** allocate a socket and connect to the local HTTP daemon
2589 */
2590 if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
2591 return 0;
2592
2593 sockname.sin_family = AF_INET;
2594 sockname.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
2595 sockname.sin_port = htons(port);
2596
2597 if ( connect(sockfd, (struct sockaddr *) &sockname,
2598 sizeof sockname) == -1)
2599 {
2600 close(sockfd);
2601 return 0;
2602 }
2603
2604 /*
2605 ** write a GET-request for /server-status
2606 */
2607 if ( write(sockfd, HTTPREQ, sizeof HTTPREQ) < sizeof HTTPREQ)
2608 {
2609 close(sockfd);
2610 return 0;
2611 }
2612
2613 /*
2614 ** remap socket descriptor to a stream to allow stdio calls
2615 */
2616 sockfp = fdopen(sockfd, "r+");
2617
2618 /*
2619 ** read response line by line
2620 */
2621 tobefound = 5; /* number of values to be searched */
2622
2623 while ( fgets(linebuf, sizeof linebuf, sockfp) && tobefound)
2624 {
2625 /*
2626 ** handle line containing status code
2627 */
2628 if ( strncmp(linebuf, "HTTP/", 5) == 0)
2629 {
2630 sscanf(linebuf, "%511s %lld %*s\n", label, &value);
2631
2632 if (value != 200) /* HTTP-request okay? */
2633 {
2634 fclose(sockfp);
2635 close(sockfd);
2636 return 0;
2637 }
2638
2639 continue;
2640 }
2641
2642 /*
2643 ** decode line and search for the required counters
2644 */
2645 if (sscanf(linebuf, "%511[^:]: %lld\n", label, &value) == 2)
2646 {
2647 if ( strcmp(label, "Total Accesses") == 0)
2648 {
2649 wp->accesses = value;
2650 tobefound--;
2651 }
2652
2653 if ( strcmp(label, "Total kBytes") == 0)
2654 {
2655 wp->totkbytes = value;
2656 tobefound--;
2657 }
2658
2659 if ( strcmp(label, "Uptime") == 0)
2660 {
2661 wp->uptime = value;
2662 tobefound--;
2663 }
2664
2665 if ( strcmp(label, "BusyWorkers") == 0)
2666 {
2667 wp->bworkers = value;
2668 tobefound--;
2669 }
2670
2671 if ( strcmp(label, "IdleWorkers") == 0)
2672 {
2673 wp->iworkers = value;
2674 tobefound--;
2675 }
2676 }
2677 }
2678
2679 fclose(sockfp);
2680 close(sockfd);
2681
2682 return 1;
2683 }
2684 #endif
2685
2686
2687
2688 /*
2689 ** retrieve low-level CPU events:
2690 ** instructions and cycles per CPU
2691 */
2692 #ifndef NOPERFEVENT
2693
2694 void
2695 do_perfevents(char *tagname, char *tagvalue)
2696 {
2697 if (!strcmp("enable", tagvalue))
2698 perfevents = PERF_EVENTS_ENABLE;
2699 else if (!strcmp("disable", tagvalue))
2700 perfevents = PERF_EVENTS_DISABLE;
2701 else
2702 {
2703 if (run_in_guest())
2704 perfevents = PERF_EVENTS_DISABLE;
2705 else
2706 perfevents = PERF_EVENTS_ENABLE;
2707 }
2708 }
2709
2710 static int
2711 enable_perfevents()
2712 {
2713 if (perfevents == PERF_EVENTS_AUTO)
2714 do_perfevents("perfevents", "auto");
2715
2716 return perfevents == PERF_EVENTS_ENABLE;
2717 }
2718
2719 long
2720 perf_event_open(struct perf_event_attr *hwevent, pid_t pid,
2721 int cpu, int groupfd, unsigned long flags)
2722 {
2723 return syscall(__NR_perf_event_open, hwevent, pid, cpu, groupfd, flags);
2724 }
2725
2726 static void
2727 getperfevents(struct cpustat *cs)
2728 {
2729 static int firstcall = 1, cpualloced, *fdi, *fdc;
2730 int i;
2731 int liResult;
2732
2733 if (!enable_perfevents())
2734 return;
2735
2736 /*
2737 ** once initialize perf event counter retrieval
2738 */
2739 if (firstcall)
2740 {
2741 struct perf_event_attr pea;
2742 int success = 0;
2743
2744 firstcall = 0;
2745
2746 /*
2747 ** allocate space for per-cpu file descriptors
2748 */
2749 cpualloced = cs->nrcpu;
2750 fdi = malloc(sizeof(int) * cpualloced);
2751 fdc = malloc(sizeof(int) * cpualloced);
2752
2753 /*
2754 ** fill perf_event_attr struct with appropriate values
2755 */
2756 memset(&pea, 0, sizeof(struct perf_event_attr));
2757
2758 pea.type = PERF_TYPE_HARDWARE;
2759 pea.size = sizeof(struct perf_event_attr);
2760 pea.inherit = 1;
2761 pea.pinned = 1;
2762
2763 regainrootprivs();
2764
2765 for (i=0; i < cpualloced; i++)
2766 {
2767 pea.config = PERF_COUNT_HW_INSTRUCTIONS;
2768
2769 if ( (*(fdi+i) = perf_event_open(&pea, -1, i, -1,
2770 PERF_FLAG_FD_CLOEXEC)) >= 0)
2771 success++;
2772
2773 pea.config = PERF_COUNT_HW_CPU_CYCLES;
2774
2775 if ( (*(fdc+i) = perf_event_open(&pea, -1, i, -1,
2776 PERF_FLAG_FD_CLOEXEC)) >= 0)
2777 success++;
2778 }
2779
2780 if (! droprootprivs())
2781 mcleanstop(42, "failed to drop root privs\n");
2782
2783
2784 /*
2785 ** all failed (probably no kernel support)?
2786 */
2787 if (success == 0)
2788 {
2789 free(fdi);
2790 free(fdc);
2791 cpualloced = 0;
2792 }
2793 else
2794 {
2795 cs->all.instr = 1;
2796 cs->all.cycle = 1;
2797 }
2798
2799 return; // initialization finished for first sample
2800 }
2801
2802 /*
2803 ** every sample: check if counters available anyhow
2804 */
2805 if (!cpualloced)
2806 return;
2807
2808 /*
2809 ** retrieve counters per CPU and in total
2810 */
2811 cs->all.instr = 0;
2812 cs->all.cycle = 0;
2813
2814 for (i=0; i < cpualloced; i++)
2815 {
2816 if (*(fdi+i) != -1)
2817 {
2818 liResult = read(*(fdi+i), &(cs->cpu[i].instr), sizeof(count_t));
2819 cs->all.instr += cs->cpu[i].instr;
2820 if(liResult < 0)
2821 {
2822 char lcMessage[64];
2823
2824 snprintf(lcMessage, sizeof(lcMessage) - 1,
2825 "%s:%d - Error %d reading instr counters\n",
2826 __FILE__, __LINE__, errno);
2827 fprintf(stderr, "%s", lcMessage);
2828 }
2829
2830 liResult = read(*(fdc+i), &(cs->cpu[i].cycle), sizeof(count_t));
2831 cs->all.cycle += cs->cpu[i].cycle;
2832 if(liResult < 0)
2833 {
2834 char lcMessage[64];
2835
2836 snprintf(lcMessage, sizeof(lcMessage) - 1,
2837 "%s:%d - Error %d reading cycle counters\n",
2838 __FILE__, __LINE__, errno );
2839 fprintf(stderr, "%s", lcMessage);
2840 }
2841 }
2842 }
2843 }
2844
2845 #else /* ! NOPERFEVENT */
2846 void
2847 do_perfevents(char *tagname, char *tagvalue)
2848 {
2849 if (strcmp("disable", tagvalue))
2850 mcleanstop(1, "atop built with NOPERFEVENT, cannot use perfevents\n");
2851 }
2852 #endif