atop-2.12.1/0000755000203100020310000000000015064552761012053 5ustar gerlofgerlofatop-2.12.1/acctproc.c0000644000203100020310000006556015064552761014031 0ustar gerlofgerlof/* ** ATOP - System & Process Monitor ** ** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as process-level. ** ** This source-file contains functions to manipulate with process-accounting ** features (switch it on/off and read the process-accounting records). ** ================================================================ ** Author: Gerlof Langeveld ** E-mail: gerlof.langeveld@atoptool.nl ** Date: November 1996 ** LINUX-port: June 2000 ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. */ #define _FILE_OFFSET_BITS 64 #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include "atop.h" #include "photoproc.h" #include "acctproc.h" #include "atopacctd.h" #define ACCTDIR "/var/cache/atop.d" #define ACCTFILE "atop.acct" #define ACCTENV "ATOPACCT" static char acctatop; /* boolean: atop's own accounting busy ? */ static off_t acctsize; /* previous size of account file */ static int acctrecsz; /* size of account record */ static int acctversion; /* version of account record layout */ static int acctfd = -1; /* fd of account file (-1 = not open) */ static long curshadowseq; /* current shadow file sequence number */ static long maxshadowrec; /* number of records per shadow file */ static char *pacctdir = PACCTDIR; static count_t acctexp (comp_t ct); static int acctvers(int); static void acctrestarttrial(void); static void switchshadow(void); static int atopacctd(int); /* ** possible process accounting files used by (ps)acct package */ struct pacctadm { char *name; struct stat stat; } pacctadm[] = { { "/var/log/pacct", {0, }, }, { "/var/account/pacct", {0, }, }, { "/var/log/account/pacct", {0, }, } }; struct pacctadm *pacctcur; // pointer to entry in use /* ** Semaphore-handling ** ** A semaphore-group with two semaphores is created ** ** 0 - Binary semaphore (mutex) to get access to active atop-counter ** ** 1 - Active atop-counter (inverted). ** This semaphore is initialized at some high value and is ** decremented by every atop-incarnation which uses the private ** accounting-file and incremented as soon as such atop stops again. ** If an atop-incarnation stops and it appears to be the last one ** using the private accounting-file, accounting is stopped ** and the file removed ** (Yes, I know: it's not proper usage of the semaphore-principle). */ #define ATOPACCTKEY 3121959 #define ATOPACCTTOT 100 struct sembuf semclaim = {0, -1, SEM_UNDO}, semrelse = {0, +1, SEM_UNDO}, semdecre = {1, -1, SEM_UNDO}, semincre = {1, +1, SEM_UNDO}; struct sembuf semreglock[] = {{0, -1, SEM_UNDO|IPC_NOWAIT}, {1, -1, SEM_UNDO|IPC_NOWAIT}}, semunlock = {1, +1, SEM_UNDO}; /* ** switch on the process-accounting mechanism ** ** return value: ** 0 - activated (success) ** 1 - not activated: unreadable accounting file ** 2 - not activated: empty environment variable ATOPACCT ** 3 - not activated: no access to semaphore group ** 4 - not activated: impossible to create own accounting file ** 5 - not activated: no root privileges */ int acctswon(void) { int i, j, sematopid; static unsigned short vals[] = {1, ATOPACCTTOT}; union {unsigned short *array;} arg = {vals}; struct stat statbuf; char *ep; int ret; /* ** when a particular environment variable is present, atop should ** use a specific accounting-file (as defined by the environment ** variable) or should use no process accounting at all (when ** contents of environment variable is empty) */ if ( (ep = getenv(ACCTENV)) ) { /* ** environment variable exists */ if (*ep) { /* ** open active account file with the specified name */ if (! droprootprivs() ) mcleanstop(42, "failed to drop root privs\n"); if ( (acctfd = open(ep, O_RDONLY) ) == -1) return 1; if ( !acctvers(acctfd) ) { (void) close(acctfd); acctfd = -1; return 1; } supportflags |= ACCTACTIVE; return 0; } else { /* ** no contents */ return 2; } } /* ** when the atopacctd daemon is active on this system, ** it should be the preferred way to read the accounting records */ if ( (ret = atopacctd(1)) >= 0 ) return ret; /* ** check if process accounting is already switched on ** for one of the standard accounting-files; in that case we ** open this file and get our info from here .... */ for (i=0, j=0; i < sizeof pacctadm/sizeof pacctadm[0]; i++) { if ( stat(pacctadm[i].name, &pacctadm[i].stat) == 0) j++; } if (j) { /* ** at least one file is present; check if it is really in use ** at this moment for accounting by forking a child-process ** which immediately finishes to force a new accounting-record */ if ( fork() == 0 ) exit(0); /* let the child finish */ (void) wait((int *) 0); /* let the parent wait */ for (i=0; i < sizeof pacctadm/sizeof pacctadm[0]; i++) { if ( stat(pacctadm[i].name, &statbuf) == 0) { /* ** accounting-file has grown? */ if (statbuf.st_size > pacctadm[i].stat.st_size) { /* ** open active account file */ if ( (acctfd = open(pacctadm[i].name, O_RDONLY) ) == -1) return 1; if ( !acctvers(acctfd) ) { (void) close(acctfd); acctfd = -1; return 1; } supportflags |= ACCTACTIVE; pacctcur = &pacctadm[i]; // register current return 0; } } } } /* ** process-accounting is not yet switched on in a standard way; ** check if another atop has switched on accounting already ** ** first try to create a semaphore group exclusively; if this succeeds, ** this is the first atop-incarnation since boot and the semaphore group ** should be initialized` */ if ( (sematopid = semget(ATOPACCTKEY, 2, 0600|IPC_CREAT|IPC_EXCL)) >= 0) (void) semctl(sematopid, 0, SETALL, arg); else sematopid = semget(ATOPACCTKEY, 0, 0); /* ** check if we got access to the semaphore group */ if (sematopid == -1) return 3; /* ** the semaphore group is opened now; claim exclusive rights */ (void) semop(sematopid, &semclaim, 1); /* ** are we the first to use the accounting-mechanism ? */ if (semctl(sematopid, 1, GETVAL, 0) == ATOPACCTTOT) { /* ** create a new separate directory below /tmp ** for the accounting file; ** if this directory exists (e.g. previous atop-run killed) ** it will be cleaned and newly created */ if ( mkdir(ACCTDIR, 0700) == -1) { if (errno == EEXIST) { (void) acct(0); (void) unlink(ACCTDIR "/" ACCTFILE); (void) rmdir(ACCTDIR); } if ( mkdir(ACCTDIR, 0700) == -1) { /* ** persistent failure */ (void) semop(sematopid, &semrelse, 1); return 4; } } /* ** create accounting file in new directory */ (void) close( creat(ACCTDIR "/" ACCTFILE, 0600) ); /* ** switch on accounting */ if ( acct(ACCTDIR "/" ACCTFILE) < 0) { (void) unlink(ACCTDIR "/" ACCTFILE); (void) rmdir(ACCTDIR); (void) semop(sematopid, &semrelse, 1); return 5; } } /* ** accounting is switched on now; ** open accounting-file */ if ( (acctfd = open(ACCTDIR "/" ACCTFILE, O_RDONLY) ) < 0) { (void) acct(0); (void) unlink(ACCTDIR "/" ACCTFILE); (void) rmdir(ACCTDIR); (void) semop(sematopid, &semrelse, 1); return 1; } /* ** accounting successfully switched on */ (void) semop(sematopid, &semdecre, 1); (void) semop(sematopid, &semrelse, 1); acctatop = 1; /* ** determine version of accounting-record */ if (fstat(acctfd, &statbuf) == -1) { (void) acct(0); (void) close(acctfd); (void) unlink(ACCTDIR "/" ACCTFILE); (void) rmdir(ACCTDIR); acctfd = -1; return 1; } if (statbuf.st_size == 0) /* no acct record written yet */ { /* ** let's write one record */ if ( fork() == 0 ) exit(0); /* let the child finish */ (void) wait((int *) 0); /* let the parent wait */ } if ( !acctvers(acctfd) ) { (void) acct(0); (void) close(acctfd); (void) unlink(ACCTDIR "/" ACCTFILE); (void) rmdir(ACCTDIR); acctfd = -1; return 1; } supportflags |= ACCTACTIVE; return 0; } /* ** try to use process accounting via the atopacctd daemon ** swon: 1 - initial switch on ** 0 - switch on again after the atopacct service has been down */ static int atopacctd(int swon) { int sempacctpubid; acctfd = -1; // reset to not being open /* ** open semaphore group that has been initialized by atopacctd ** semaphore 0: 100 counting down to reflect number of users of atopacctd ** semaphore 1: value 0 is locked and value 1 is unlocked (binary semaphore) */ if ( (sempacctpubid = semget(PACCTPUBKEY, 2, 0)) != -1) { FILE *cfp; char shadowpath[128]; struct flock flock; struct timespec maxsemwait = {3, 0}; if (! droprootprivs() ) mcleanstop(42, "failed to drop root privs\n"); /* ** lock binary semaphore and decrement semaphore 0 (extra user) */ if (semtimedop(sempacctpubid, semreglock, 2, &maxsemwait) == -1) { regainrootprivs(); return 3; } /* ** open the 'current' file, containing the current ** shadow sequence number and maximum number of records ** per shadow file */ snprintf(shadowpath, sizeof shadowpath, "%s/%s/%s", pacctdir, PACCTSHADOWD, PACCTSHADOWC); if ( (cfp = fopen(shadowpath, "r")) ) { if (fscanf(cfp, "%ld/%ld", &curshadowseq, &maxshadowrec) == 2) { fclose(cfp); /* ** open the current shadow file */ snprintf(shadowpath, sizeof shadowpath, PACCTSHADOWF, pacctdir, PACCTSHADOWD, curshadowseq); if ( (acctfd = open(shadowpath, O_RDONLY))!=-1) { if ( swon && !acctvers(acctfd) ) { int maxcnt = 40; if ( fork() == 0 ) exit(0); (void) wait((int *) 0); while ( !acctvers(acctfd) && --maxcnt) usleep(50000); if (!acctversion) { (void) close(acctfd); acctfd = -1; semop(sempacctpubid, &semrelse, 1); semop(sempacctpubid, &semunlock, 1); regainrootprivs(); maxshadowrec = 0; return -1; // try other } } /* ** set read lock on current shadow file */ flock.l_type = F_RDLCK; flock.l_whence = SEEK_SET; flock.l_start = 0; flock.l_len = 1; if ( fcntl(acctfd, F_SETLK, &flock) != -1) { supportflags |= ACCTACTIVE; regainrootprivs(); semop(sempacctpubid, &semunlock, 1); return 0; } (void) close(acctfd); acctfd = -1; } else { perror("open shadowpath"); abort(); } } else { fprintf(stderr, "fscanf failed on shadow currency\n"); fclose(cfp); maxshadowrec = 0; } } (void) semop(sempacctpubid, &semrelse, 1); (void) semop(sempacctpubid, &semunlock, 1); } return -1; // try another accounting mechanism } /* ** determine the version of the accounting-record layout/length ** and reposition the seek-pointer to the end of the accounting file */ static int acctvers(int fd) { struct acct tmprec; /* ** read first record from accounting file to verify ** the second byte (always contains version number) */ if ( read(fd, &tmprec, sizeof tmprec) < sizeof tmprec) return 0; switch (tmprec.ac_version & 0x0f) { case 2: acctrecsz = sizeof(struct acct); acctversion = 2; break; case 3: acctrecsz = sizeof(struct acct_v3); acctversion = 3; break; default: mcleanstop(8, "Unknown format of process accounting file\n"); } /* ** accounting successfully switched on */ acctsize = acctprocnt() * acctrecsz; /* ** reposition to actual file-size */ (void) lseek(fd, acctsize, SEEK_SET); return 1; } /* ** switch off the process-accounting mechanism */ void acctswoff(void) { int sematopid; struct stat before, after; /* ** if accounting not supported, skip call */ if (acctfd == -1) return; /* ** our private accounting-file in use? */ if (acctatop) { acctatop = 0; /* ** claim the semaphore to get exclusive rights for ** the accounting-manipulation */ sematopid = semget(ATOPACCTKEY, 0, 0); (void) semop(sematopid, &semclaim, 1); (void) semop(sematopid, &semincre, 1); /* ** check if we were the last user of accounting */ if (semctl(sematopid, 1, GETVAL, 0) == ATOPACCTTOT) { /* ** switch off private accounting ** ** verify if private accounting is still in use to ** avoid that we switch off process accounting that ** has been activated manually in the meantime */ (void) fstat(acctfd, &before); if ( fork() == 0 ) /* create a child and */ exit(0); /* let the child finish */ (void) wait((int *) 0); /* let the parent wait */ (void) fstat(acctfd, &after); if (after.st_size > before.st_size) { /* ** remove the directory and file */ regainrootprivs(); /* get root privs again */ (void) acct(0); (void) unlink(ACCTDIR "/" ACCTFILE); (void) rmdir(ACCTDIR); if (! droprootprivs() ) mcleanstop(42, "failed to drop root privs\n"); } } (void) semop(sematopid, &semrelse, 1); } /* ** anyhow close the accounting-file again */ (void) close(acctfd); /* close account file again */ acctfd = -1; supportflags &= ~ACCTACTIVE; } /* ** get the number of exited processes written ** in the process-account file since the previous sample */ unsigned long acctprocnt(void) { struct stat statacc; /* ** handle atopacctd-based process accounting on bases of ** fixed-chunk shadow files */ if (maxshadowrec) // atopacctd has been detected before? { unsigned long numrecs = 0; long newseq; char shadowpath[128]; FILE *cfp; /* ** determine the current size of the current shadow file ** (fails if acctfd == -1) and determine if the file ** has not been deleted by stopping the atopacct service */ if (fstat(acctfd, &statacc) == -1 || statacc.st_nlink == 0) { /* close the previous obsolete shadow file */ (void) close(acctfd); acctsize = 0; /* reacquire the current real acctfd */ if (atopacctd(0)) return 0; // reaqcuire failed if (fstat(acctfd, &statacc) == -1) return 0; } /* ** verify how many new processes are added to the current ** shadow file */ numrecs = (statacc.st_size - acctsize) / acctrecsz; /* ** verify if subsequent shadow files are involved ** (i.e. if the current file is full) */ if (statacc.st_size / acctrecsz < maxshadowrec) return numrecs; /* ** more shadow files available ** get current shadow file */ snprintf(shadowpath, sizeof shadowpath, "%s/%s/%s", pacctdir, PACCTSHADOWD, PACCTSHADOWC); if ( (cfp = fopen(shadowpath, "r")) ) { if (fscanf(cfp, "%ld", &newseq) == 1) { fclose(cfp); } else { fclose(cfp); return numrecs; } } else { return numrecs; } if (newseq == curshadowseq) return numrecs; snprintf(shadowpath, sizeof shadowpath, PACCTSHADOWF, pacctdir, PACCTSHADOWD, newseq); /* ** determine the size of newest shadow file */ if (stat(shadowpath, &statacc) == -1) { fprintf(stderr, "failed to stat the size of newest shadow file\n"); return numrecs; } /* ** Check cases like statacc.st_size / acctrecsz > maxshadowrec, ** and atopacctd restarts at the same time, now newseq is zero. ** Omit this interval's statistics by returning zero. */ if (newseq > curshadowseq) { numrecs += ((newseq - curshadowseq - 1) * maxshadowrec) + (statacc.st_size / acctrecsz); } else { numrecs = 0; } return numrecs; } else /* ** classic process accounting on bases of directly opened ** process accounting file */ { /* ** determine size of current process accounting file */ if (acctfd == -1 || fstat(acctfd, &statacc) == -1) return 0; /* ** accounting reset? */ if (acctsize > statacc.st_size) { /* ** reposition to start of file */ (void) lseek(acctfd, 0, SEEK_SET); acctsize = 0; } /* ** using account file managed by (ps)acct package? */ if (pacctcur) { /* ** check inode of the current file and compare this ** with the inode of the opened file; if not equal, ** a file rotation has taken place and the size of ** the new file has to be added */ if ( stat(pacctcur->name, &(pacctcur->stat)) == 0) { if (statacc.st_ino != pacctcur->stat.st_ino) { return (statacc.st_size - acctsize + pacctcur->stat.st_size) / acctrecsz; } } } return (statacc.st_size - acctsize) / acctrecsz; } } /* ** reposition the seek offset in the process accounting file to skip ** processes that have not been read */ void acctrepos(unsigned int noverflow) { /* ** if accounting not supported, skip call */ if (acctfd == -1) return; if (maxshadowrec) { int i; off_t virtoffset = acctsize + noverflow * acctrecsz; off_t maxshadowsz = maxshadowrec * acctrecsz; long switches = virtoffset / maxshadowsz; acctsize = virtoffset % maxshadowsz; for (i=0; i < switches; i++) switchshadow(); (void) lseek(acctfd, acctsize, SEEK_SET); } else { /* ** just reposition to skip superfluous records */ (void) lseek(acctfd, noverflow * acctrecsz, SEEK_CUR); acctsize += noverflow * acctrecsz; /* ** when the new seek pointer is beyond the current file size ** and reading from a process accounting file written by ** the (ps)acct package, a logrotation might have taken place */ if (pacctcur) { struct stat statacc; /* ** determine the size of the current accounting file */ if (fstat(acctfd, &statacc) == -1) return; /* ** seek pointer beyond file size and rotated to ** new account file? */ if (acctsize > statacc.st_size && statacc.st_ino != pacctcur->stat.st_ino) { /* ** - close old file ** - open new file ** - adapt acctsize to actual offset in new file ** and seek to that offset */ (void) close(acctfd); if ( (acctfd = open(pacctcur->name, O_RDONLY) ) == -1) return; // open failed acctsize = acctsize - statacc.st_size; (void) lseek(acctfd, acctsize, SEEK_SET); if (fstat(acctfd, &statacc) == -1) return; // no new inode info } } } } /* ** read the process records from the process accounting file, ** that are written since the previous cycle */ unsigned long acctphotoproc(struct tstat *accproc, int nrprocs) { register int nrexit; register struct tstat *api; struct acct acctrec; struct acct_v3 acctrec_v3; struct stat statacc; int filled; /* ** if accounting not supported, skip call */ if (acctfd == -1) return 0; /* ** determine the size of the (current) account file */ if (fstat(acctfd, &statacc) == -1) return 0; /* ** check all exited processes in accounting file */ for (nrexit=0, api=accproc; nrexit < nrprocs; ) { /* ** in case of shadow accounting files, we might have to ** switch from the current accounting file to the next */ if (maxshadowrec && acctsize >= statacc.st_size) { switchshadow(); /* ** determine the size of the new (current) account file ** and initialize the current offset within that file */ if (fstat(acctfd, &statacc) == -1) return 0; acctsize = 0; } /* ** in case of account file managed by (ps)acct package, ** be aware that a switch to a newer logfile might have ** to be done */ if (pacctcur && acctsize >= statacc.st_size) { /* ** check inode of the current file and compare this ** with the inode of the opened file; if not equal, ** a file rotation has taken place and the file ** has to be reopened */ if ( stat(pacctcur->name, &(pacctcur->stat)) == 0) { if (statacc.st_ino != pacctcur->stat.st_ino) { (void) close(acctfd); if ( (acctfd = open(pacctcur->name, O_RDONLY) ) == -1) return 0; // open failed if (fstat(acctfd, &statacc) == -1) return 0; // no new inode info acctsize = 0; // reset size new file } } } /* ** read the next record */ switch (acctversion) { case 2: if ( read(acctfd, &acctrec, acctrecsz) < acctrecsz ) break; /* unexpected end of account file */ /* ** fill process info from accounting-record */ api->gen.state = 'E'; api->gen.nthr = 1; api->gen.isproc = 1; api->gen.pid = 0; api->gen.tgid = 0; api->gen.ppid = 0; api->gen.excode = acctrec.ac_exitcode; api->gen.ruid = acctrec.ac_uid16; api->gen.rgid = acctrec.ac_gid16; api->gen.btime = acctrec.ac_btime; api->gen.elaps = acctrec.ac_etime; api->cpu.stime = acctexp(acctrec.ac_stime); api->cpu.utime = acctexp(acctrec.ac_utime); api->mem.minflt = acctexp(acctrec.ac_minflt); api->mem.majflt = acctexp(acctrec.ac_majflt); api->dsk.rio = acctexp(acctrec.ac_rw); safe_strcpy(api->gen.name, acctrec.ac_comm, sizeof api->gen.name); filled = 1; break; case 3: if ( read(acctfd, &acctrec_v3, acctrecsz) < acctrecsz ) break; /* unexpected end of account file */ /* ** fill process info from accounting-record */ api->gen.state = 'E'; api->gen.pid = acctrec_v3.ac_pid; api->gen.tgid = acctrec_v3.ac_pid; api->gen.ppid = acctrec_v3.ac_ppid; api->gen.nthr = 1; api->gen.isproc = 1; api->gen.excode = acctrec_v3.ac_exitcode; api->gen.ruid = acctrec_v3.ac_uid; api->gen.rgid = acctrec_v3.ac_gid; api->gen.btime = acctrec_v3.ac_btime; api->gen.elaps = acctrec_v3.ac_etime; api->cpu.stime = acctexp(acctrec_v3.ac_stime); api->cpu.utime = acctexp(acctrec_v3.ac_utime); api->mem.minflt = acctexp(acctrec_v3.ac_minflt); api->mem.majflt = acctexp(acctrec_v3.ac_majflt); api->dsk.rio = acctexp(acctrec_v3.ac_rw); safe_strcpy(api->gen.name, acctrec_v3.ac_comm, sizeof api->gen.name); filled = 1; break; } if (filled == 1) { nrexit++; api++; acctsize += acctrecsz; filled = 0; } } if (acctsize > ACCTMAXFILESZ && !maxshadowrec) acctrestarttrial(); return nrexit; } /* ** when the size of the private accounting file exceeds a certain limit, ** it might be useful to stop process accounting, truncate the ** process accounting file to zero and start process accounting again ** ** this will only be done if this atop process is the only one ** that is currently using the accounting file */ static void acctrestarttrial(void) { struct stat statacc; int sematopid; /* ** not private accounting-file in use? */ if (!acctatop) return; // do not restart /* ** still remaining records in accounting file that are ** written between the moment that the number of exited ** processes was counted and the moment that all processes ** were read */ (void) fstat(acctfd, &statacc); if (acctsize != statacc.st_size) return; // do not restart /* ** claim the semaphore to get exclusive rights for ** the accounting-manipulation */ sematopid = semget(ATOPACCTKEY, 0, 0); (void) semop(sematopid, &semclaim, 1); /* ** check if there are more users of accounting file */ if (semctl(sematopid, 1, GETVAL, 0) < ATOPACCTTOT-1) { (void) semop(sematopid, &semrelse, 1); return; // do not restart } /* ** restart is possible ** ** - switch off accounting ** - truncate the file ** - switch on accounting */ regainrootprivs(); // get root privs again (void) acct(0); // switch off accounting if ( truncate(ACCTDIR "/" ACCTFILE, 0) == 0) (void) lseek(acctfd, 0, SEEK_SET); (void) acct(ACCTDIR "/" ACCTFILE); if (! droprootprivs() ) mcleanstop(42, "failed to drop root privs\n"); acctsize = 0; (void) semop(sematopid, &semrelse, 1); } /* ** expand the special compression-methods */ static count_t acctexp(comp_t ct) { count_t exp; count_t val; exp = (ct >> 13) & 0x7; /* obtain 3 bits base-8 exponent */ val = ct & 0x1fff; /* obtain 13 bits mantissa */ while (exp-- > 0) val <<= 3; return val; } /* ** switch to the next accounting shadow file */ static void switchshadow(void) { int tmpfd; char shadowpath[128]; struct flock flock; /* ** determine path name of new shadow file */ curshadowseq++; snprintf(shadowpath, sizeof shadowpath, PACCTSHADOWF, pacctdir, PACCTSHADOWD, curshadowseq); /* ** open new shadow file, while the previous is also kept open ** (to keep the read lock until a read lock is set for the new ** shadow file) */ if ( (tmpfd = open(shadowpath, O_RDONLY)) != -1) { /* ** set read lock on new shadow file */ flock.l_type = F_RDLCK; flock.l_whence = SEEK_SET; flock.l_start = 0; flock.l_len = 1; if ( fcntl(tmpfd, F_SETLK, &flock) != -1) { (void) close(acctfd); // implicit release read lock acctfd = tmpfd; return; } else { (void) close(tmpfd); } } } /* ** handle the option 'pacctdir' in the /etc/atoprc file */ void do_pacctdir(char *tagname, char *tagvalue) { char shadowpath[128]; struct stat dirstat; /* ** copy the directory pathname to an own buffer */ if ( (pacctdir = malloc(strlen(tagvalue)+1)) == NULL ) { perror("malloc pacctdir"); exit(11); } strcpy(pacctdir, tagvalue); /* ** verify if the atopacctd daemon is active */ if ( semget(PACCTPUBKEY, 0, 0) == -1) { fprintf(stderr, "Warning: option '%s' specified " "while atopacctd not running!\n", tagname); sleep(2); return; } /* ** verify that the topdirectory exists */ if ( stat(pacctdir, &dirstat) == -1 ) { fprintf(stderr, "Warning: option '%s' specified - ", tagname); perror(pacctdir); sleep(2); return; } if (! S_ISDIR(dirstat.st_mode) ) { fprintf(stderr, "Warning: option '%s' specified - ", tagname); fprintf(stderr, "%s not a directory\n", pacctdir); sleep(2); return; } /* ** verify that the topdirectory contains the required subdirectory */ snprintf(shadowpath, sizeof shadowpath, "%s/%s", pacctdir, PACCTSHADOWD); if ( stat(shadowpath, &dirstat) == -1 ) { fprintf(stderr, "Warning: option '%s' specified - ", tagname); perror(shadowpath); sleep(2); return; } if (! S_ISDIR(dirstat.st_mode) ) { fprintf(stderr, "Warning: option '%s' specified - ", tagname); fprintf(stderr, "%s not a directory\n", shadowpath); sleep(2); return; } } atop-2.12.1/acctproc.h0000644000203100020310000001262315064552761014026 0ustar gerlofgerlof/* ** ATOP - System & Process Monitor ** ** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as process-level. ** ** Include-file for process-accounting functions. ** ================================================================ ** Author: Gerlof Langeveld ** E-mail: gerlof.langeveld@atoptool.nl ** Date: November 1996 ** LINUX-port: June 2000 ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. */ #ifndef __ACCTPROC__ #define __ACCTPROC__ int acctswon(void); void acctswoff(void); unsigned long acctprocnt(void); unsigned long acctphotoproc(struct tstat *, int); void acctrepos(unsigned int); /* ** maximum number of records to be read from process accounting file ** for one sample, to avoid that atop explodes and introduces OOM killing .... ** ** the maximum is based on a limit of 50 MiB extra memory (approx. 70000 procs) */ #define MAXACCTPROCS (50*1024*1024/sizeof(struct tstat)) /* ** preferred maximum size of process accounting file (200 MiB) */ #define ACCTMAXFILESZ (200*1024*1024) /* ** alternative layout of accounting record if kernel-patch ** has been installed */ #include typedef __u16 comp_t; typedef __u32 comp2_t; #define ACCT_COMM 16 struct acct_atop { char ac_flag; /* Flags */ char ac_version; /* Always set to ACCT_VERSION */ __u32 ac_pid; /* Process ID */ __u32 ac_ppid; /* Parent Process ID */ __u16 ac_uid16; /* LSB of Real User ID */ __u16 ac_gid16; /* LSB of Real Group ID */ __u16 ac_tty; /* Control Terminal */ __u32 ac_btime; /* Process Creation Time */ comp_t ac_utime; /* User Time */ comp_t ac_stime; /* System Time */ comp_t ac_etime; /* Elapsed Time */ comp_t ac_mem; /* Virtual Memory */ comp_t ac_rss; /* Resident Memory */ comp_t ac_io; /* Chars Transferred */ comp_t ac_rw; /* Blocks Read or Written */ comp_t ac_bread; /* Blocks Read */ comp_t ac_bwrite; /* Blocks Written */ comp2_t ac_dskrsz; /* Cum. blocks read */ comp2_t ac_dskwsz; /* Cum. blocks written */ comp_t ac_tcpsnd; /* TCP send requests */ comp_t ac_tcprcv; /* TCP recv requests */ comp2_t ac_tcpssz; /* TCP cum. length */ comp2_t ac_tcprsz; /* TCP cum. length */ comp_t ac_udpsnd; /* UDP send requests */ comp_t ac_udprcv; /* UDP recv requests */ comp2_t ac_udpssz; /* UDP cum. length */ comp2_t ac_udprsz; /* UDP cum. length */ comp_t ac_rawsnd; /* RAW send requests */ comp_t ac_rawrcv; /* RAW recv requests */ comp_t ac_minflt; /* Minor Pagefaults */ comp_t ac_majflt; /* Major Pagefaults */ comp_t ac_swaps; /* Number of Swaps */ /* m68k had no padding here. */ #if !defined(CONFIG_M68K) || !defined(__KERNEL__) __u16 ac_ahz; /* AHZ */ #endif __u32 ac_exitcode; /* Exitcode */ char ac_comm[ACCT_COMM + 1]; /* Command Name */ __u8 ac_etime_hi; /* Elapsed Time MSB */ __u16 ac_etime_lo; /* Elapsed Time LSB */ __u32 ac_uid; /* Real User ID */ __u32 ac_gid; /* Real Group ID */ }; /* ** default layout of accounting record ** (copied from /usr/src/linux/include/linux/acct.h) */ struct acct { char ac_flag; /* Flags */ char ac_version; /* Always set to ACCT_VERSION */ /* for binary compatibility back until 2.0 */ __u16 ac_uid16; /* LSB of Real User ID */ __u16 ac_gid16; /* LSB of Real Group ID */ __u16 ac_tty; /* Control Terminal */ __u32 ac_btime; /* Process Creation Time */ comp_t ac_utime; /* User Time */ comp_t ac_stime; /* System Time */ comp_t ac_etime; /* Elapsed Time */ comp_t ac_mem; /* Average Memory Usage */ comp_t ac_io; /* Chars Transferred */ comp_t ac_rw; /* Blocks Read or Written */ comp_t ac_minflt; /* Minor Pagefaults */ comp_t ac_majflt; /* Major Pagefaults */ comp_t ac_swaps; /* Number of Swaps */ /* m68k had no padding here. */ #if !defined(CONFIG_M68K) || !defined(__KERNEL__) __u16 ac_ahz; /* AHZ */ #endif __u32 ac_exitcode; /* Exitcode */ char ac_comm[ACCT_COMM + 1]; /* Command Name */ __u8 ac_etime_hi; /* Elapsed Time MSB */ __u16 ac_etime_lo; /* Elapsed Time LSB */ __u32 ac_uid; /* Real User ID */ __u32 ac_gid; /* Real Group ID */ }; struct acct_v3 { char ac_flag; /* Flags */ char ac_version; /* Always set to ACCT_VERSION */ __u16 ac_tty; /* Control Terminal */ __u32 ac_exitcode; /* Exitcode */ __u32 ac_uid; /* Real User ID */ __u32 ac_gid; /* Real Group ID */ __u32 ac_pid; /* Process ID */ __u32 ac_ppid; /* Parent Process ID */ __u32 ac_btime; /* Process Creation Time */ #ifdef __KERNEL__ __u32 ac_etime; /* Elapsed Time */ #else float ac_etime; /* Elapsed Time */ #endif comp_t ac_utime; /* User Time */ comp_t ac_stime; /* System Time */ comp_t ac_mem; /* Average Memory Usage */ comp_t ac_io; /* Chars Transferred */ comp_t ac_rw; /* Blocks Read or Written */ comp_t ac_minflt; /* Minor Pagefaults */ comp_t ac_majflt; /* Major Pagefaults */ comp_t ac_swaps; /* Number of Swaps */ char ac_comm[ACCT_COMM]; /* Command Name */ }; #endif atop-2.12.1/atopacctd.c0000644000203100020310000006232515064552761014171 0ustar gerlofgerlof/* ** The atopacctd daemon switches on the process accounting feature ** in the kernel and let the process accounting records be written to ** a file, called the source file. After process accounting is active, ** the atopacctd daemon transfers every process accounting record that ** is available in the source file to a shadow file. ** Client processes (like atop) can read the shadow file instead of ** the original process accounting source file. ** ** This approach has the following advantages: ** ** - The atopacctd daemon keeps the source file to a limited size ** by truncating it regularly. ** ** - The atopacct daemon takes care that a shadow file has a limited size. ** As soon as the current shadow file reaches its maximum size, the ** atopacctd daemon creates a subsequent shadow file. For this reason, ** the name of a shadow file contains a sequence number. ** Shadow files that are not used by client processes any more, are ** automatically removed by the atopacctd daemon. ** ** - When no client processes are active (any more), all shadow files ** will be deleted and no records will be transferred to shadow files ** any more. As soon as at least one client is activated again, the ** atopacctd daemon start writing shadow files again. ** ** The directory /run is used as the default top-directory. An ** alternative top-directory can be specified as command line argument ** (in that case, also modify /etc/atoprc to inform atop as a client). ** Below this top-directory the source file pacct_source is created and ** the subdirectory pacct_shadow.d as a 'container' for the shadow files. ** ---------------------------------------------------------------------- ** Copyright (C) 2014 Gerlof Langeveld (gerlof.langeveld@atoptool.nl) ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License version 2 as ** published by the Free Software Foundation. */ #define _POSIX_C_SOURCE #define _XOPEN_SOURCE #define _GNU_SOURCE #define _DEFAULT_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "atop.h" #include "photoproc.h" #include "acctproc.h" #include "version.h" #include "versdate.h" #include "atopacctd.h" #define RETRYCNT 10 // # retries to read account record #define RETRYMS 25 // timeout (millisec) to read account record #define NORECINTERVAL 3600 // no-record-available interval (seconds) #define PACCTSEC 3 // timeout (sec) to retry switch on pacct #define POLLSEC 1 // timeout (sec) when NETLINK fails #define GCINTERVAL 60 // garbage collection interval (seconds) /* ** Semaphore-handling ** ** Two semaphore groups are created: ** ** The private semaphore (group) specifies the number of atopacctd processes ** running (to be sure that only one daemon is active at the time). ** ** The public semaphore group contains two semaphores: ** ** 0: the number of processes using the process accounting shadow files, ** i.e. the number of (atop) clients. This semaphore starts at a high ** value and should be decremented by every (atop) client that starts ** using the shadow files and incremented again whenever that (atop) ** client terminates. ** ** 1: binary semphore that has to be claimed before using/modifying ** semaphore 0 in this group. */ static int semprv; static int sempub; #define SEMTOTAL 100 #define NUMCLIENTS (SEMTOTAL - semctl(sempub, 0, GETVAL, 0)) struct sembuf semlocknowait = {1, -1, SEM_UNDO|IPC_NOWAIT}, semunlock = {1, +1, SEM_UNDO}; static char atopacctdversion[] = ATOPVERS; static char atopacctddate[] = ATOPDATE; static unsigned long maxshadowrec = MAXSHADOWREC; static char *pacctdir = PACCTDIR; static int cleanup_and_go = 0; /* ** function prototypes */ static int awaitprocterm(int, int, int, char *, int *, unsigned long *, unsigned long *); static int swonpacct(int, char *); static int createshadow(long); static int pass2shadow(int, char *, int); static void gcshadows(unsigned long *, unsigned long); static void setcurrent(long); static int acctsize(struct acct *); static void parent_cleanup(int); static void child_cleanup(int); int main(int argc, char *argv[]) { int i, nfd, afd, sfd; int parentpid; struct stat dirstat; struct rlimit rlim; FILE *pidf; struct sembuf semincr = {0, +1, SEM_UNDO}; char shadowdir[128], shadowpath[128]; char accountpath[128]; unsigned long oldshadow = 0, curshadow = 0; int shadowbusy = 0; time_t gclast = time(0); struct sigaction sigcleanup; int liResult; /* ** argument passed? */ if (argc == 2) { /* ** version number required (flag -v or -V)? */ if (*argv[1] == '-') // flag? { if ( *(argv[1]+1) == 'v' || *(argv[1]+1) == 'V') { printf("%s \n", getstrvers()); return 0; } else { fprintf(stderr, "Usage: atopacctd [-v|topdirectory]\n" "Default topdirectory: %s\n", PACCTDIR); exit(1); } } /* ** if first argument is not a flag, it should be the name ** of an alternative top directory (to be validated later on) */ pacctdir = argv[1]; } else { if (argc != 1) { fprintf(stderr, "Usage: atopacctd [-v|topdirectory]\n" "Default topdirectory: %s\n", PACCTDIR); exit(1); } } /* ** verify if we are running with the right privileges */ if (geteuid() != 0) { fprintf(stderr, "Root privileges are needed!\n"); exit(1); } /* ** verify that the top directory is not world-writable ** and owned by root */ if ( stat(pacctdir, &dirstat) == -1 ) { perror(pacctdir); fprintf(stderr, "Usage: atopacctd [-v|topdirectory]\n" "Default topdirectory: %s\n", PACCTDIR); exit(2); } if (! S_ISDIR(dirstat.st_mode) ) { fprintf(stderr, "atopacctd: %s is not a directory\n", pacctdir); exit(2); } if (dirstat.st_uid != 0) { fprintf(stderr, "atopacctd: directory %s must be owned by root\n", pacctdir); exit(2); } if (dirstat.st_mode & (S_IWGRP|S_IWOTH)) { fprintf(stderr, "atopacctd: directory %s may not be writable " "for group/others\n", pacctdir); exit(2); } /* ** create the semaphore groups and initialize the semaphores; ** if the private semaphore already exists, verify if another ** atopacctd daemon is already running */ if ( (semprv = semget(PACCTPRVKEY, 0, 0)) >= 0) // exists? { if ( semctl(semprv, 0, GETVAL, 0) > 0) { fprintf(stderr, "atopacctd is already running!\n"); exit(3); } } else { if ( (semprv = semget(PACCTPRVKEY, 1, 0600|IPC_CREAT|IPC_EXCL)) >= 0) { (void) semctl(semprv, 0, SETVAL, 0); } else { perror("cannot create private semaphore"); exit(3); } } // create new semaphore group // if ( (sempub = semget(PACCTPUBKEY, 0, 0)) != -1) // existing? (void) semctl(sempub, 0, IPC_RMID, 0); if ( (sempub = semget(PACCTPUBKEY, 2, 0666|IPC_CREAT|IPC_EXCL)) >= 0) { (void) semctl(sempub, 0, SETVAL, SEMTOTAL); (void) semctl(sempub, 1, SETVAL, 1); } else { perror("cannot create public semaphore"); exit(3); } /* ** daemonize this process ** i.e. be sure that the daemon is no session leader (any more) ** and get rid of a possible bad context that might have been ** inherited from ancestors */ parentpid = getpid(); // to be killed when initialized /* ** prepare cleanup signal handler */ memset(&sigcleanup, 0, sizeof sigcleanup); sigemptyset(&sigcleanup.sa_mask); sigcleanup.sa_handler = parent_cleanup; (void) sigaction(SIGTERM, &sigcleanup, (struct sigaction *)0); if ( fork() ) // implicitly switch to background { /* ** parent after forking first child: ** wait for signal 15 from child before terminating ** because systemd expects parent to terminate whenever ** service is up and running */ pause(); // wait for signal from child exit(0); // finish parent } setsid(); // become session leader to lose ctty if ( fork() ) // finish parent; continue in child exit(0); // --> no session leader, no ctty sigcleanup.sa_handler = child_cleanup; (void) sigaction(SIGTERM, &sigcleanup, (struct sigaction *)0); getrlimit(RLIMIT_NOFILE, &rlim); for (i=0; i < rlim.rlim_cur; i++) // close all files, but { if (i != 2) // do not close stderr close(i); } umask(022); liResult = chdir("/tmp"); // go to a safe place if(liResult != 0) { char lcMessage[64]; snprintf(lcMessage, sizeof(lcMessage) - 1, "%s:%d - Error %d changing to tmp dir\n", __FILE__, __LINE__, errno ); fprintf(stderr, "%s", lcMessage); } /* ** increase semaphore to define that atopacctd is running */ if ( semop(semprv, &semincr, 1) == -1) { perror("cannot increment private semaphore"); kill(parentpid, SIGTERM); exit(4); } /* ** create source accounting file to which the kernel can write ** its records */ snprintf(accountpath, sizeof accountpath, "%s/%s", pacctdir, PACCTORIG); (void) unlink(accountpath); // in case atopacctd previously killed if ( (afd = creat(accountpath, 0600)) == -1) { perror(accountpath); kill(parentpid, SIGTERM); exit(5); } (void) close(afd); /* ** open the accounting file for read */ if ( (afd = open(accountpath, O_RDONLY)) == -1) { perror(accountpath); kill(parentpid, SIGTERM); exit(5); } /* ** create directory to store the shadow files ** when atopacctd was previously killed, rename the ** old directory to a unique name */ snprintf(shadowdir, sizeof shadowdir, "%s/%s", pacctdir, PACCTSHADOWD); if ( stat(shadowdir, &dirstat) == 0 ) // already exists? { if (S_ISDIR(dirstat.st_mode)) // and is directory? { // define new name to save directory char newshadow[256]; snprintf(newshadow, sizeof newshadow, "%s-old-%d", shadowdir, getpid()); if ( rename(shadowdir, newshadow) == -1) { perror("could not rename old shadow directory"); exit(5); } } } if ( mkdir(shadowdir, 0755) == -1) { perror(shadowdir); kill(parentpid, SIGTERM); exit(5); } sfd = createshadow(curshadow); setcurrent(curshadow); /* ** open syslog interface */ openlog("atopacctd", LOG_PID, LOG_DAEMON); syslog(LOG_INFO, "%s ", getstrvers()); /* ** raise priority (be sure the nice value becomes -20, ** independent of the current nice value) ** this may fail without notice for non-privileged processes */ liResult = nice(-39); /* ** connect to NETLINK socket of kernel to be triggered ** when processes have finished */ if ( (nfd = netlink_open()) == -1) { (void) unlink(accountpath); kill(parentpid, SIGTERM); exit(5); } /* ** switch on accounting - inital */ if ( swonpacct(afd, accountpath) == -1) { (void) unlink(accountpath); kill(parentpid, SIGTERM); exit(6); } syslog(LOG_INFO, "accounting to %s", accountpath); /* ** signal handling */ (void) signal(SIGHUP, SIG_IGN); (void) sigaction(SIGINT, &sigcleanup, (struct sigaction *)0); (void) sigaction(SIGQUIT, &sigcleanup, (struct sigaction *)0); (void) sigaction(SIGTERM, &sigcleanup, (struct sigaction *)0); /* ** create PID file */ if ( (pidf = fopen(PIDFILE, "w")) ) { fprintf(pidf, "%d\n", getpid()); fclose(pidf); } /* ** terminate parent: service initialized */ kill(parentpid, SIGTERM); /* ** main loop */ while (! cleanup_and_go) { int state; time_t curtime; /* ** await termination of (at least) one process and ** copy the process accounting record(s) */ state = awaitprocterm(nfd, afd, sfd, accountpath, &shadowbusy, &oldshadow, &curshadow); if (state == -1) // irrecoverable error? break; /* ** garbage collection (i.e. removal of shadow files that ** are not in use any more) is needed in case: ** ** - shadow files are currently maintained because ** at least one atop is running, and ** - shadow files have not been removed for GCINTERVAL ** seconds, or ** - the system clock has been modified (lowered) */ if ( shadowbusy && (time(&curtime) > gclast + GCINTERVAL || curtime < gclast ) ) { gcshadows(&oldshadow, curshadow); gclast = time(0); } } /* ** cleanup and terminate */ (void) acct((char *) 0); // disable process accounting (void) unlink(accountpath); // remove source file for (; oldshadow <= curshadow; oldshadow++) // remove shadow files { /* ** assemble path name of shadow file (starting by oldest) */ snprintf(shadowpath, sizeof shadowpath, PACCTSHADOWF, pacctdir, PACCTSHADOWD, oldshadow); (void) unlink(shadowpath); } snprintf(shadowpath, sizeof shadowpath, "%s/%s/%s", pacctdir, PACCTSHADOWD, PACCTSHADOWC); (void) unlink(shadowpath); // remove file 'current' (void) rmdir(shadowdir); // remove shadow.d directory if (cleanup_and_go) { syslog(LOG_NOTICE, "Terminated by signal %d\n", cleanup_and_go); if (cleanup_and_go == SIGTERM) return 0; else return cleanup_and_go + 128; } else { syslog(LOG_NOTICE, "Terminated!\n"); return 13; } } /* ** wait for at least one process termination and copy process accounting ** record(s) from the source process accounting file to the current ** shadow accounting file ** ** return code: 0 - no process accounting record read ** 1 - at least one process accounting record read ** -1 - irrecoverable failure */ static int awaitprocterm(int nfd, int afd, int sfd, char *accountpath, int *shadowbusyp, unsigned long *oldshadowp, unsigned long *curshadowp) { static int arecsize, netlinkactive = 1; static unsigned long long atotsize, stotsize, maxshadowsz; static time_t reclast; struct timespec retrytimer = {0, RETRYMS/2*1000000}; int retrycount = RETRYCNT; int asz, rv, ssz; char abuf[16000]; int partsz, remsz; /* ** neutral state: ** ** wait for info from NETLINK indicating that at least ** one process has finished; the real contents of the ** NETLINK message is ignored, it is only used as trigger ** that something can be read from the process accounting file ** ** unfortunately it is not possible to use inotify() on the ** source file as a trigger that a new accounting record ** has been written (does not work if the kernel itself ** writes to the file) ** ** when the NETLINK interface fails due to kernel bug 190711, ** we switch to polling mode: ** wait for timer and verify if process accounting ** records are available (repeatedly); ugly but the only ** thing we can do if we can't use NETLINK */ if (netlinkactive) { rv = netlink_recv(nfd, 0); if (rv == 0) // EOF? { syslog(LOG_ERR, "unexpected EOF on NETLINK\n"); perror("unexpected EOF on NETLINK\n"); return -1; } if (rv < 0) // failure? { switch (-rv) { // acceptable errors that might indicate that // processes have terminated case EINTR: case ENOMEM: case ENOBUFS: break; default: syslog(LOG_ERR, "unexpected error on NETLINK: %s\n", strerror(-rv)); fprintf(stderr, "unexpected error on NETLINK: %s\n", strerror(-rv)); if (-rv == EINVAL) { syslog(LOG_ERR, "(see ATOP README about kernel bug 190711)\n"); fprintf(stderr, "(see ATOP README about kernel bug 190711)\n"); } syslog(LOG_ERR, "switching to polling mode\n"); fprintf(stderr, "switching to polling mode\n"); netlinkactive = 0; // polling mode wanted return 0; } } /* ** get rid of all other waiting finished processes via netlink ** before handling the process accounting record(s) */ while ( netlink_recv(nfd, MSG_DONTWAIT) > 0 ); } else // POLLING MODE { sleep(POLLSEC); retrycount = 1; } /* ** read new process accounting record(s) ** such record(s) may not immediately be available (timing matter), ** so some retries might be necessary */ while ((asz = read(afd, abuf, sizeof abuf)) == 0 && --retrycount) { nanosleep(&retrytimer, (struct timespec *)0); retrytimer.tv_nsec = RETRYMS*1000000; } switch (asz) { case 0: // EOF (no records available)? if (time(0) > reclast + NORECINTERVAL && reclast) { syslog(LOG_WARNING, "reactivate process accounting\n"); if (truncate(accountpath, 0) != -1) { lseek(afd, 0, SEEK_SET); atotsize = swonpacct(afd, accountpath); } reclast = time(0); } return 0; // wait for NETLINK again case -1: // failure? syslog(LOG_ERR, "%s - unexpected read error: %s\n", accountpath, strerror(errno)); return -1; } reclast = time(0); /* ** only once: determine the size of an accounting ** record and calculate the maximum size for each ** shadow file */ if (!arecsize) { arecsize = acctsize((struct acct *)abuf); if (arecsize) { maxshadowsz = maxshadowrec * arecsize; } else { syslog(LOG_ERR, "cannot determine size of account record\n"); return -1; } } /* ** truncate process accounting file regularly */ atotsize += asz; // maintain current size if (atotsize >= MAXORIGSZ) { if (truncate(accountpath, 0) != -1) { lseek(afd, 0, SEEK_SET); atotsize = 0; } } /* ** determine if any client is using the shadow ** accounting files; if not, verify if clients ** have been using the shadow files till now and ** cleanup has to be performed */ if (semop(sempub, &semlocknowait, 1) == 0) // lock succeeded? { if (NUMCLIENTS == 0) { /* ** did last client just disappear? */ if (*shadowbusyp) { /* ** remove all shadow files */ gcshadows(oldshadowp, (*curshadowp)+1); *oldshadowp = 0; *curshadowp = 0; stotsize = 0; /* ** create new file with sequence 0 */ (void) close(sfd); sfd = createshadow(*curshadowp); setcurrent(*curshadowp); *shadowbusyp = 0; } (void) semop(sempub, &semunlock, 1); return 1; } (void) semop(sempub, &semunlock, 1); } *shadowbusyp = 1; /* ** transfer process accounting data to shadow file ** but take care to fill a shadow file exactly ** to its maximum and not more... */ if (stotsize + asz <= maxshadowsz) // would fit? { /* ** pass all data */ if ( (ssz = pass2shadow(sfd, abuf, asz)) > 0) stotsize += ssz; remsz = 0; partsz = 0; } else { /* ** calculate the remainder that has to ** be written to the next shadow file */ partsz = maxshadowsz - stotsize; remsz = asz - partsz; /* ** transfer first part of the data to current ** shadow file */ if ( (ssz = pass2shadow(sfd, abuf, partsz)) > 0) stotsize += ssz; } /* ** verify if current shadow file has reached its ** maximum size; if so, switch to next sequence number ** and write remaining data (if any) */ if (stotsize >= maxshadowsz) { close(sfd); sfd = createshadow(++(*curshadowp)); setcurrent(*curshadowp); stotsize = 0; /* ** transfer remaining part of the data */ if (remsz) { if ( (ssz = pass2shadow(sfd, abuf+partsz, remsz)) > 0) stotsize += ssz; } } return 1; } /* ** create first shadow file with requested sequence number */ static int createshadow(long seq) { int sfd; char shadowpath[128]; snprintf(shadowpath, sizeof shadowpath, PACCTSHADOWF, pacctdir, PACCTSHADOWD, seq); /* ** open the shadow file for write */ if ( (sfd = creat(shadowpath, 0644)) == -1) { perror(shadowpath); exit(5); } return sfd; } /* ** transfer process accounting data to shadow file */ static int pass2shadow(int sfd, char *sbuf, int ssz) { static unsigned long long nrskipped; struct statvfs statvfs; /* ** check if the filesystem is not filled for more than 95% */ if ( fstatvfs(sfd, &statvfs) != -1) { if (statvfs.f_blocks == 0 || statvfs.f_bfree * 100 / statvfs.f_blocks < 5 ) { if (nrskipped == 0) // first skip? { syslog(LOG_WARNING, "Filesystem > 95%% full; " "pacct writing skipped\n"); } nrskipped++; return 0; } } /* ** there is enough space in the filesystem (again) ** verify if writing has been suspended due to lack of space (if so, ** write a log message) */ if (nrskipped) { syslog(LOG_WARNING, "Pacct writing continued (%llu skipped)\n", nrskipped); nrskipped = 0; } /* ** transfer process accounting record(s) to shadow file */ if ( write(sfd, sbuf, ssz) == -1 ) { syslog(LOG_ERR, "Unexpected write error to shadow file: %s\n", strerror(errno)); cleanup_and_go = 129; } return ssz; } /* ** switch on the process accounting mechanism ** first parameter: file descriptor of open accounting file ** second parameter: name of accounting file ** return value: -1 in case of permanent failure, ** otherwise number of bytes read from accounting file */ static int swonpacct(int afd, char *accountpath) { int n, acctokay = 0; char abuf[4096]; /* ** due to kernel bug 190271 (process accounting sometimes ** does not work), we verify if process accounting really ** works after switching it on. If not, we keep retrying ** for a while. */ while (! acctokay) { int maxcnt = 40; /* ** switch on process accounting */ if ( acct(accountpath) == -1) { perror("cannot switch on process accounting"); return -1; } /* ** try if process accounting works by spawning a ** child process that immediately finishes (should ** result in a process accounting record) */ if ( fork() == 0 ) exit(0); (void) wait((int *)0); // wait for child to finish while ( (n = read(afd, abuf, sizeof abuf)) <= 0 && --maxcnt) usleep(50000); if (n > 0) // process accounting works { acctokay = 1; } else { syslog(LOG_ERR, "Retrying to switch on process accounting\n"); syslog(LOG_ERR, "(see ATOP README about kernel bug 190271)\n"); acct((char *)0); // switch off process accounting sleep(PACCTSEC); // wait a while before retrying } } return n; } /* ** remove old shadow files not being in use any more ** ** When a reading process (atop) opens a shadow file, ** it places a read lock on the first byte of that file. ** More then one read lock is allowed on that first byte ** (in case of more atop incarnations). ** If at least one read lock exists, a write lock (to be ** tried here) will fail which means that the file is still ** in use by at least one reader. */ static void gcshadows(unsigned long *oldshadowp, unsigned long curshadow) { struct flock flock; int tmpsfd; char shadowpath[128]; /* ** fill flock structure: write lock on first byte */ flock.l_type = F_WRLCK; flock.l_whence = SEEK_SET; flock.l_start = 0; flock.l_len = 1; for (; *oldshadowp < curshadow; (*oldshadowp)++) { /* ** assemble path name of shadow file (starting by oldest) */ snprintf(shadowpath, sizeof shadowpath, PACCTSHADOWF, pacctdir, PACCTSHADOWD, *oldshadowp); /* ** try to open oldest existing file for write ** and verify if it is in use */ if ( (tmpsfd = open(shadowpath, O_WRONLY)) == -1) break; if ( fcntl(tmpsfd, F_SETLK, &flock) == -1) // no-wait trial { close(tmpsfd); break; // setting lock failed, so still in use } /* ** lock successfully set, so file is unused: remove file; ** closing the file implicitly removes the succeeded lock */ (void) close(tmpsfd); (void) unlink(shadowpath); } } /* ** write sequence number of current (newest) file */ static void setcurrent(long curshadow) { static int cfd = -1; char currentpath[128], currentdata[128]; int len; int liResult; /* ** assemble file name of currency file and open (only once) */ if (cfd == -1) { snprintf(currentpath, sizeof currentpath, "%s/%s/%s", pacctdir, PACCTSHADOWD, PACCTSHADOWC); if ( (cfd = creat(currentpath, 0644)) == -1) { syslog(LOG_ERR, "Could not create currency file: %s\n", strerror(errno)); return; } } /* ** assemble ASCII string to be written: seqnumber/maxrecords */ len = snprintf(currentdata, sizeof currentdata, "%ld/%lu", curshadow, maxshadowrec); /* ** wipe currency file and write new assembled string */ liResult = ftruncate(cfd, 0); if(liResult != 0) { char lcMessage[64]; snprintf(lcMessage, sizeof(lcMessage) - 1, "%s:%d - Error %d ftruncate\n\n", __FILE__, __LINE__, errno ); fprintf(stderr, "%s", lcMessage); } (void) lseek(cfd, 0, SEEK_SET); liResult = write(cfd, currentdata, len); if(liResult == -1) { char lcMessage[64]; snprintf(lcMessage, sizeof(lcMessage) - 1, "%s:%d - Error %d writing\n\n", __FILE__, __LINE__, errno ); fprintf(stderr, "%s", lcMessage); } } /* ** determine the size of an accounting record */ static int acctsize(struct acct *parec) { switch (parec->ac_version & 0x0f) { case 2: return sizeof(struct acct); case 3: return sizeof(struct acct_v3); default: return 0; } } /* ** generate version number and date */ char * getstrvers(void) { static char vers[256]; snprintf(vers, sizeof vers, "Version: %s - %s", atopacctdversion, atopacctddate); return vers; } /* ** signal catchers: ** set flag to be verified in main loop to cleanup and terminate */ void child_cleanup(int sig) { cleanup_and_go = sig; } void parent_cleanup(int sig) { exit(0); } atop-2.12.1/atopacctd.h0000644000203100020310000000451715064552761014175 0ustar gerlofgerlof/* ** ATOP - System & Process Monitor ** ** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as process-level. ** ========================================================================== ** Author: Gerlof Langeveld ** E-mail: gerlof.langeveld@atoptool.nl ** Date: September 2002 ** -------------------------------------------------------------------------- ** Copyright (C) 2000-2010 Gerlof Langeveld ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ** -------------------------------------------------------------------------- */ #ifndef __ATOPACCTD__ #define __ATOPACCTD__ /* ** keys to access the semaphores */ #define PACCTPUBKEY 1071980 // # clients using shadow files #define PACCTPRVKEY (PACCTPUBKEY-1) // # atopacctd daemons busy (max. 1) /* ** name of the PID file */ #define PIDFILE "/run/atopacctd.pid" /* ** directory containing the source accounting file and ** the subdirectory (PACCTSHADOWD) containing the shadow file(s) ** this directory can be overruled by a command line parameter (atopacctd) ** or by a keyword in the /etc/atoprc file (atop) */ #define PACCTDIR "/run" /* ** accounting file (source file to which kernel writes records) */ #define PACCTORIG "pacct_source" // file name in PACCTDIR #define MAXORIGSZ (1024*1024) // maximum size of source file /* ** file and directory names for shadow files */ #define PACCTSHADOWD "pacct_shadow.d" // directory name in PACCTDIR #define PACCTSHADOWF "%s/%s/%010ld.paf" // file name of shadow file #define PACCTSHADOWC "current" // file containining current // sequence and MAXSHADOWREC #define MAXSHADOWREC 10000 // number of accounting records per shadow file #endif atop-2.12.1/atop.c0000644000203100020310000010702615064552761013170 0ustar gerlofgerlof/* ** ATOP - System & Process Monitor ** ** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as process-level. ** ** This source-file contains the main-function, which verifies the ** calling-parameters and takes care of initialization. ** The engine-function drives the main sample-loop in which after the ** indicated interval-time a snapshot is taken of the system-level and ** process-level counters and the deviations are calculated and ** visualized for the user. ** ========================================================================== ** Author: Gerlof Langeveld ** E-mail: gerlof.langeveld@atoptool.nl ** Date: November 1996 ** Linux-port: June 2000 ** Modified: May 2001 - Ported to kernel 2.4 ** -------------------------------------------------------------------------- ** Copyright (C) 2000-2024 Gerlof Langeveld ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ** -------------------------------------------------------------------------- ** ** After initialization, the main-function calls the ENGINE. ** For every cycle (so after another interval) the ENGINE calls various ** functions as shown below: ** ** +------------------------------------------------------------------------+ ** | E N G I N E | ** | | ** | | ** | _____________________await interval-timer________________________ | ** | | ^ | ** | | _______ _______ _______ ________ ________ | | ** | | ^ | ^ | ^ | ^ | ^ | | | ** +---|---|-------|--|-------|--|-------|-----|--------|----|--------|--|--+ ** | | | | | | | | | | | | ** +--V-----+ +--V----+ +--V----+ +--V--------+ +--V-------+ +--V-----+ ** | | | | | | | | | deviate | | | ** | photo | | photo | | photo | | acct | | ..cgroup | | print | ** | cgroup | | syst | | proc | | photoproc | | ..syst | | | ** | | | | | | | | | ..proc | | | ** +--------+ +-------+ +-------+ +-----------+ +----------+ +--------+ ** ^ ^ ^ ^ | | ** | | | | | | ** | | | V V V ** _______ _____ _____ __________ ________ _________ ** / \ / \ / \ / \ / \ / \ ** /sys/fs /proc /proc accounting task screen or ** /cgroup file database file ** \_______/ \_____/ \_____/ \__________/ \________/ \_________/ ** ** - photocgroup() ** Takes a snapshot of the counters related to resource usage on ** cgroup-level v2 (cpu, disk, memory). ** ** - photosyst() ** Takes a snapshot of the counters related to resource-usage on ** system-level (cpu, disk, memory, network). ** ** - photoproc() ** Takes a snapshot of the counters related to resource-usage of ** tasks which are currently active. For this purpose the whole ** task-list is read. ** ** - acctphotoproc() ** Takes a snapshot of the counters related to resource-usage of ** tasks which have been finished during the last interval. ** For this purpose all new records in the accounting-file are read. ** ** When all counters have been gathered, functions are called to calculate ** the difference between the current counter values and the counter values ** of the previous cycle. These functions operate on cgroup level, system level ** as well as on task level. ** These differences are stored in a new structure (table). ** ** - deviatcgroup() ** Calculates the differences between the current cgroup-level ** counters and the corresponding counters of the previous cycle. ** ** - deviatsyst() ** Calculates the differences between the current system-level ** counters and the corresponding counters of the previous cycle. ** ** - deviattask() ** Calculates the differences between the current task-level ** counters and the corresponding counters of the previous cycle. ** The per-task counters of the previous cycle are stored in the ** task-database; this "database" is implemented as a linked list ** of taskinfo structures in memory (so no disk-accesses needed). ** Within this linked list hash-buckets are maintained for fast searches. ** The entire task-database is handled via a set of well-defined ** functions from which the name starts with "pdb_..." (see the ** source-file procdbase.c). ** The processes which have been finished during the last cycle ** are also treated by deviattask() in order to calculate what their ** resource-usage was before they finished. ** ** All information is ready to be visualized now. ** There is a structure which holds the start-address of the ** visualization-function to be called. Initially this structure contains ** the address of the generic visualization-function ("generic_samp"), but ** these addresses can be modified in the main-function depending on particular ** flags. In this way various representation-layers (ASCII, graphical, ...) ** can be linked with 'atop'; the one to use can eventually be chosen ** at runtime. */ #define _POSIX_C_SOURCE #define _XOPEN_SOURCE #define _GNU_SOURCE #define _DEFAULT_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "atop.h" #include "acctproc.h" #include "ifprop.h" #include "photoproc.h" #include "photosyst.h" #include "cgroups.h" #include "showgeneric.h" #include "showlinux.h" #include "parseable.h" #include "json.h" #include "gpucom.h" #include "netatop.h" #define allflags "ab:cde:fghijklmnopqrstuvwxyz:123456789ABCDEFGHIJ:KL:MNOP:QRSTUVWXYZ" #define MAXFL 84 /* maximum number of command-line flags */ /* ** declaration of global variables */ struct utsname utsname; int utsnodenamelen; time_t pretime; /* timing info */ time_t curtime; /* timing info */ unsigned long interval = 10; unsigned long sampcnt; char screen; int fdinotify = -1; /* inotify fd for twin mode */ pid_t twinpid; /* PID of lower half for twin mode */ char twindir[RAWNAMESZ] = "/tmp"; int linelen = 80; char acctreason; /* accounting not active (return val) */ char irawname[RAWNAMESZ]; char orawname[RAWNAMESZ]; char rawreadflag; char idnamesuppress; /* suppress UID/GID to name translation */ time_t begintime, endtime, cursortime; // epoch or time in day char flaglist[MAXFL]; char deviatonly = 1; char usecolors = 1; /* boolean: colors for high occupation */ char threadview = 0; /* boolean: show individual threads */ char calcpss = 0; /* boolean: read/calculate process PSS */ char getwchan = 0; /* boolean: obtain wchan string */ char rmspaces = 0; /* boolean: remove spaces from command */ /* name in case of parsable output */ char displaymode = 'T'; /* 'T' = text, 'D' = draw */ char barmono = 0; /* boolean: bar without categories? */ /* name in case of parseable output */ char prependenv = 0; /* boolean: prepend selected */ /* environment variables to cmdline */ char connectgpud = 0; /* boolean: connect to atopgpud */ char connectnetatop = 0; /* boolean: connect to netatop(bpf) */ regex_t envregex; unsigned short hertz; unsigned int pidwidth; unsigned int pagesize; unsigned int nrgpus; int osrel; int osvers; int ossub; extern GHashTable *ghash_net; int supportflags; /* supported features */ char **argvp; struct handler handlers[MAXHANDLERS]; int numhandlers; /* ** argument values */ static char awaittrigger; /* boolean: awaiting trigger */ static unsigned int nsamples = 0xffffffff; static char midnightflag; static char rawwriteflag; static char parseoutflag; static char jsonoutflag; static char screenoutflag; char twinmodeflag; /* ** interpretation of defaults-file /etc/atoprc and $HOME/.atop */ static void readrc(char *, int); static void do_interval(char *, char *); static void do_linelength(char *, char *); static struct { char *tag; void (*func)(char *, char *); int sysonly; } manrc[] = { { "flags", do_flags, 0, }, { "twindir", do_twindir, 0, }, { "interval", do_interval, 0, }, { "linelen", do_linelength, 0, }, { "username", do_username, 0, }, { "procname", do_procname, 0, }, { "maxlinecpu", do_maxcpu, 0, }, { "maxlinegpu", do_maxgpu, 0, }, { "maxlinedisk", do_maxdisk, 0, }, { "maxlinemdd", do_maxmdd, 0, }, { "maxlinelvm", do_maxlvm, 0, }, { "maxlineintf", do_maxintf, 0, }, { "maxlineifb", do_maxifb, 0, }, { "maxlinenfsm", do_maxnfsm, 0, }, { "maxlinecont", do_maxcont, 0, }, { "maxlinenuma", do_maxnuma, 0, }, { "maxlinellc", do_maxllc, 0, }, { "colorinfo", do_colinfo, 0, }, { "coloralmost", do_colalmost, 0, }, { "colorcritical", do_colcrit, 0, }, { "colorthread", do_colthread, 0, }, { "ownallcpuline", do_ownallcpuline, 0, }, { "ownonecpuline", do_ownindivcpuline, 0, }, { "owncplline", do_owncplline, 0, }, { "ownmemline", do_ownmemline, 0, }, { "ownswpline", do_ownswpline, 0, }, { "ownpagline", do_ownpagline, 0, }, { "ownmemnumaline", do_ownmemnumaline, 0, }, { "ownnumacpuline", do_owncpunumaline, 0, }, { "ownllcline", do_ownllcline, 0, }, { "owndskline", do_owndskline, 0, }, { "ownnettrline", do_ownnettransportline, 0, }, { "ownnetnetline", do_ownnetnetline, 0, }, { "ownnetifline", do_ownnetinterfaceline, 0, }, { "ownifbline", do_owninfinibandline, 0, }, { "ownprocline", do_ownprocline, 0, }, { "ownsysprcline", do_ownsysprcline, 0, }, { "owndskline", do_owndskline, 0, }, { "cpucritperc", do_cpucritperc, 0, }, { "gpucritperc", do_gpucritperc, 0, }, { "memcritperc", do_memcritperc, 0, }, { "swpcritperc", do_swpcritperc, 0, }, { "dskcritperc", do_dskcritperc, 0, }, { "netcritperc", do_netcritperc, 0, }, { "swoutcritsec", do_swoutcritsec, 0, }, { "almostcrit", do_almostcrit, 0, }, { "atopsarflags", do_atopsarflags, 0, }, { "perfevents", do_perfevents, 0, }, { "pacctdir", do_pacctdir, 1, }, }; /* ** internal prototypes */ static void engine(void); static void twinprepare(void); static void twinclean(void); int main(int argc, char *argv[]) { register int i; int c; char *p; struct rlimit rlim; /* ** since privileged actions will be done later on, at this stage ** the root-privileges are dropped by switching effective user-id ** to real user-id (security reasons) */ if (! droprootprivs() ) { fprintf(stderr, "not possible to drop root privs\n"); exit(42); } /* ** preserve command arguments to allow restart of other version */ argvp = argv; /* ** read defaults-files /etc/atoprc en $HOME/.atoprc (if any) */ readrc("/etc/atoprc", 1); if ( (p = getenv("HOME")) ) { char path[1024]; snprintf(path, sizeof path, "%s/.atoprc", p); readrc(path, 0); } /* ** check if we are supposed to behave as 'atopsar' ** i.e. system statistics only */ if ( (p = strrchr(argv[0], '/'))) p++; else p = argv[0]; if ( strcmp(p, "atopsar") == 0) return atopsar(argc, argv); /* ** interpret command-line arguments & flags */ if (argc > 1) { /* ** gather all flags for visualization-functions ** ** generic flags will be handled here; ** unrecognized flags are passed to the print-routines */ i = 0; while (i < MAXFL-1 && (c=getopt(argc, argv, allflags)) != EOF) { switch (c) { case '?': /* usage wanted ? */ prusage(argv[0]); break; case 'V': /* version wanted ? */ printf("%s\n", getstrvers()); exit(0); case 'w': /* writing of raw data ? */ if (optind >= argc) prusage(argv[0]); safe_strcpy(orawname, argv[optind++], sizeof orawname); if (!rawwriteflag) { rawwriteflag++; handlers[numhandlers++].handle_sample = rawwrite; } break; case 'r': /* reading of raw data ? */ if (optind < argc) { if (*(argv[optind]) == '-') { if (strlen(argv[optind]) == 1) { strcpy(irawname, "/dev/stdin"); optind++; } } else { safe_strcpy(irawname, argv[optind], sizeof irawname); optind++; } } rawreadflag++; break; case 't': /* twin mode ? */ // optional absolute path name of directory? if (optind < argc) { if (*(argv[optind]) == '/') { safe_strcpy(twindir, argv[optind], sizeof twindir); optind++; } } twinmodeflag++; break; case 'B': /* bar graphs ? */ displaymode = 'D'; break; case 'H': /* bar graphs ? */ barmono = 1; break; case 'S': /* midnight limit ? */ midnightflag++; break; case 'I': /* suppress ID translation ? */ idnamesuppress++; break; case 'b': /* begin time ? */ if ( !getbranchtime(optarg, &begintime) ) prusage(argv[0]); break; case 'e': /* end time ? */ if ( !getbranchtime(optarg, &endtime) ) prusage(argv[0]); break; case 'P': /* parsable output? */ if ( !parsedef(optarg) ) prusage(argv[0]); if (!parseoutflag) { parseoutflag++; handlers[numhandlers++].handle_sample = parseout; } break; case 'J': /* json output? */ if ( !jsondef(optarg) ) prusage(argv[0]); if (!jsonoutflag) { jsonoutflag++; handlers[numhandlers++].handle_sample = jsonout; } break; case 'L': /* line length */ if ( !numeric(optarg) ) prusage(argv[0]); linelen = atoi(optarg); break; case MALLACTIVE: /* all processes/cgroups ? */ deviatonly = 0; break; case MCALCPSS: /* calculate PSS per sample ? */ if (rawreadflag) { fprintf(stderr, "PSIZE gathering depends on rawfile\n"); sleep(3); break; } calcpss = 1; if (!rootprivs()) { fprintf(stderr, "PSIZE gathering only for own " "processes\n"); sleep(3); } break; case MGETWCHAN: /* obtain wchan string? */ getwchan = 1; break; case MRMSPACES: /* remove spaces from command */ rmspaces = 1; break; case 'z': /* prepend regex matching environment variables */ if (regcomp(&envregex, optarg, REG_NOSUB|REG_EXTENDED)) { fprintf(stderr, "Invalid environment regular expression!"); prusage(argv[0]); } prependenv = 1; break; case 'k': /* try to open TCP connection to atopgpud */ connectgpud = 1; break; case 'K': /* try to open connection to netatop/netatop-bpf */ connectnetatop = 1; break; default: /* gather other flags */ flaglist[i++] = c; } /* ** check if this flag explicitly refers to ** generic (screen) output */ if (strchr("gmdnsevcoBGaCMDNEAupjSf", c)) screenoutflag++; } /* ** get optional interval-value and optional number of samples */ if (optind < argc && optind < MAXFL) { if (!numeric(argv[optind])) prusage(argv[0]); interval = atoi(argv[optind]); optind++; if (optind < argc) { if (!numeric(argv[optind]) ) prusage(argv[0]); if ( (nsamples = atoi(argv[optind])) < 1) prusage(argv[0]); } } } /* ** verify if the generic handler has to be installed as default ** (no other handler choosen) or if the generic screen handler ** has to be added due to an explicit flag */ if (numhandlers == 0 || screenoutflag) handlers[numhandlers++].handle_sample = generic_samp; /* ** determine the name of this node (without domain-name) ** and the kernel-version */ (void) uname(&utsname); if ( (p = strchr(utsname.nodename, '.')) ) *p = '\0'; utsnodenamelen = strlen(utsname.nodename); sscanf(utsname.release, "%d.%d.%d", &osrel, &osvers, &ossub); /* ** determine the clock rate and memory page size for this machine */ hertz = sysconf(_SC_CLK_TCK); pagesize = sysconf(_SC_PAGESIZE); pidwidth = getpidwidth(); /* ** check if twin mode wanted with two atop processes: ** ** - lower half: gather statistics and write to raw file ** - upper half: read statistics and present to user ** ** consistency checks */ if (twinmodeflag) twinprepare(); /* ** check if raw data from a file must be viewed */ if (rawreadflag) { rawread(); cleanstop(0); } /* ** be sure to be leader of an own process group when ** running as a daemon (or at least: when not interactive); ** needed for systemd */ if (rawwriteflag) (void) setpgid(0, 0); /* ** determine start-time for gathering current statistics */ curtime = getboot() / hertz; /* ** catch signals for proper close-down */ signal(SIGHUP, cleanstop); signal(SIGTERM, cleanstop); /* ** regain the root-privileges that we dropped at the beginning ** to do some privileged work */ regainrootprivs(); /* ** lock ATOP in memory to get reliable samples (also when ** memory is low and swapping is going on); ** ignored if not running under superuser privileges! */ rlim.rlim_cur = RLIM_INFINITY; rlim.rlim_max = RLIM_INFINITY; if (setrlimit(RLIMIT_MEMLOCK, &rlim) == 0) (void) mlockall(MCL_CURRENT|MCL_FUTURE); /* ** increment CPU scheduling-priority to get reliable samples (also ** during heavy CPU load); ** ignored if not running under superuser privileges! */ if ( nice(-20) == -1) ; set_oom_score_adj(); /* ** switch-on the process-accounting mechanism to register the ** (remaining) resource-usage by processes which have finished */ acctreason = acctswon(); /* ** determine properties (like speed) of all interfaces */ initifprop(); /* ** open socket to the IP layer to issue getsockopt() calls later on */ if (connectnetatop) netatop_ipopen(); /* ** since privileged activities are finished now, there is no ** need to keep running under root-privileges, so switch ** effective user-id to real user-id */ if (! droprootprivs() ) mcleanstop(42, "failed to drop root privs\n"); /* ** determine if cgroups v2 is supported */ cgroupv2support(); /* ** determine if real NUMA is used */ realnuma_support(); /* ** determine if zswap is used */ zswap_support(); /* ** start the engine now ..... */ engine(); cleanstop(0); return 0; /* never reached */ } /* ** The engine() drives the main-loop of the program */ static void engine(void) { struct sigaction sigact; static time_t timelimit; /* ** reserve space for cgroup-level statistics */ static struct cgchainer *devcstat; int ncgroups = 0; int npids = 0; int i; /* ** reserve space for system-level statistics */ static struct sstat *cursstat; /* current */ static struct sstat *presstat; /* previous */ static struct sstat *devsstat; /* deviation */ static struct sstat *hlpsstat; /* ** reserve space for task-level statistics */ static struct tstat *curtpres; /* current present list */ static unsigned long curtlen; /* size of present list */ struct tstat *curpexit; /* exited process list */ static struct devtstat devtstat; /* deviation info */ unsigned long ntaskpres; /* number of tasks present */ unsigned long nprocexit; /* number of exited procs */ unsigned long nprocexitnet; /* number of exited procs */ /* via netatopd daemon */ unsigned long noverflow; int nrgpuproc=0, /* number of GPU processes */ gpupending=0; /* boolean: request sent */ struct gpupidstat *gp = NULL; /* ** initialization: allocate required memory dynamically */ cursstat = calloc(1, sizeof(struct sstat)); presstat = calloc(1, sizeof(struct sstat)); devsstat = calloc(1, sizeof(struct sstat)); ptrverify(cursstat, "Malloc failed for current sysstats\n"); ptrverify(presstat, "Malloc failed for prev sysstats\n"); ptrverify(devsstat, "Malloc failed for deviate sysstats\n"); /* ** install the signal-handler for ALARM, USR1 and USR2 (triggers * for the next sample) */ memset(&sigact, 0, sizeof sigact); sigact.sa_handler = getusr1; sigaction(SIGUSR1, &sigact, (struct sigaction *)0); memset(&sigact, 0, sizeof sigact); sigact.sa_handler = getusr2; sigaction(SIGUSR2, &sigact, (struct sigaction *)0); memset(&sigact, 0, sizeof sigact); sigact.sa_handler = getalarm; sigaction(SIGALRM, &sigact, (struct sigaction *)0); if (interval > 0) alarm(interval); if (midnightflag) { time_t timenow = time(0); struct tm *tp = localtime(&timenow); tp->tm_hour = 23; tp->tm_min = 59; tp->tm_sec = 59; timelimit = mktime(tp); } /* ** open socket to the atopgpud daemon for GPU statistics ** if explicitly required */ if (connectgpud) { nrgpus = gpud_init(); if (nrgpus) supportflags |= GPUSTAT; } /* ** MAIN-LOOP: ** - Wait for the requested number of seconds or for other trigger ** ** - System-level counters ** get current counters ** calculate the differences with the previous sample ** ** - Process-level counters ** get current counters from running & exited processes ** calculate the differences with the previous sample ** ** - Call the print-function to visualize the differences */ for (sampcnt=0; sampcnt < nsamples; sampcnt++) { char lastcmd = ' '; /* ** if the limit-flag is specified: ** check if the next sample is expected before midnight; ** if not, stop atop now */ if (midnightflag && (curtime+interval) > timelimit) break; /* ** wait for alarm-signal to arrive (except first sample) ** or wait for SIGUSR1/SIGUSR2 */ if (sampcnt > 0 && awaittrigger) pause(); awaittrigger = 1; /* ** gather time info for this sample */ pretime = curtime; curtime = time(0); /* seconds since 1-1-1970 */ /* ** send request for statistics to atopgpud */ if (nrgpus) { if ((gpupending = gpud_statrequest()) == 0) nrgpus = 0; } /* ** take a snapshot of the current system-level metrics ** and calculate the deviations (i.e. calculate the activity ** during the last sample) */ hlpsstat = cursstat; /* swap current/prev. stats */ cursstat = presstat; presstat = hlpsstat; photosyst(cursstat); /* obtain new system-level counters */ /* ** take a snapshot of the current cgroup-level metrics ** when cgroups v2 supported */ if ( (supportflags&CGROUPV2) ) photocgroup(); /* ** receive and parse response from atopgpud */ if (nrgpus && gpupending) { nrgpuproc = gpud_statresponse(nrgpus, cursstat->gpu.gpu, &gp); gpupending = 0; // connection lost or timeout on receive? if (nrgpuproc == -1) { nrgpus = 0; supportflags &= ~GPUSTAT; } cursstat->gpu.nrgpus = nrgpus; } deviatsyst(cursstat, presstat, devsstat, curtime-pretime > 0 ? curtime-pretime : 1); /* ** take a snapshot of the current task-level statistics ** and calculate the deviations (i.e. calculate the activity ** during the last sample) ** ** first register active tasks */ curtpres = NULL; do { curtlen = counttasks(); // worst-case value curtpres = realloc(curtpres, curtlen * sizeof(struct tstat)); ptrverify(curtpres, "Malloc failed for %lu tstats\n", curtlen); memset(curtpres, 0, curtlen * sizeof(struct tstat)); } while ( (ntaskpres = photoproc(curtpres, curtlen)) == curtlen); /* ** register processes that exited during last sample; ** first determine how many processes exited ** ** the number of exited processes is limited to avoid ** that atop explodes in memory and introduces OOM killing */ nprocexit = acctprocnt(); /* number of exited processes */ if (nprocexit > MAXACCTPROCS) { noverflow = nprocexit - MAXACCTPROCS; nprocexit = MAXACCTPROCS; } else noverflow = 0; /* ** determine how many processes have been exited ** for the netatop module (only processes that have ** used the network) */ if (nprocexit > 0 && (supportflags & NETATOPD)) nprocexitnet = netatop_exitstore(); else nprocexitnet = 0; /* ** reserve space for the exited processes and read them */ if (nprocexit > 0) { curpexit = malloc(nprocexit * sizeof(struct tstat)); ptrverify(curpexit, "Malloc failed for %lu exited processes\n", nprocexit); memset(curpexit, 0, nprocexit * sizeof(struct tstat)); nprocexit = acctphotoproc(curpexit, nprocexit); /* ** reposition offset in accounting file when not ** all exited processes have been read (i.e. skip ** those processes) */ if (noverflow) acctrepos(noverflow); } else { curpexit = NULL; } /* ** merge GPU per-process stats with other per-process stats */ if (nrgpus && nrgpuproc > 0) gpumergeproc(curtpres, ntaskpres, curpexit, nprocexit, gp, nrgpuproc); /* ** calculate process-level deviations */ deviattask(curtpres, ntaskpres, curpexit, nprocexit, &devtstat, devsstat); if (supportflags & NETATOPBPF) { g_hash_table_destroy(ghash_net); ghash_net = NULL; } /* ** calculate cgroup-level v2 deviations ** ** allocation and deallocation of structs ** is arranged at a lower level */ if ( (supportflags&CGROUPV2) ) ncgroups = deviatcgroup(&devcstat, &npids); /* ** activate the installed print function to visualize ** the deviations */ for (i=0; handlers[i].handle_sample; i++) { lastcmd = (handlers[i].handle_sample)(curtime, curtime-pretime > 0 ? curtime-pretime : 1, &devtstat, devsstat, devcstat, ncgroups, npids, nprocexit, noverflow, sampcnt==0); } /* ** release dynamically allocated memory */ if (nprocexit > 0) free(curpexit); free(curtpres); if ((supportflags & NETATOPD) && (nprocexitnet > 0)) netatop_exiterase(); free(gp); gp = NULL; // avoid double free if (lastcmd == MRESET) /* reset requested ? */ { sampcnt = -1; curtime = getboot() / hertz; // reset current time /* set current (will be 'previous') counters to 0 */ memset(cursstat, 0, sizeof(struct sstat)); /* remove all tasks in database */ pdb_makeresidue(); pdb_cleanresidue(); /* remove current cgroup info */ cgwipecur(); } } /* end of main-loop */ } /* ** print usage of this command */ void prusage(char *myname) { printf("Usage: %s [-t [absdir]] [-flags] [interval [samples]]\n", myname); printf("\t\tor\n"); printf("Usage: %s -w file [-S] [-%c] [interval [samples]]\n", myname, MALLACTIVE); printf(" %s -r [file] [-b [YYYYMMDD]hhmm[ss]] [-e [YYYYMMDD]hhmm[ss]] [-flags]\n", myname); printf("\n"); printf("\tgeneric flags:\n"); printf("\t -t twin mode: live measurement with possibility to review earlier samples\n"); printf("\t (raw file created in /tmp or in specific directory path)\n"); printf("\t -%c show bar graphs for system statistics\n", MBARGRAPH); printf("\t -%c show bar graphs without categories\n", MBARMONO); printf("\t -%c show cgroup v2 metrics\n", MCGROUPS); printf("\t -7 define cgroup v2 depth level -2 till -9 (default: -7)\n"); printf("\t -%c show version information\n", MVERSION); printf("\t -%c show all processes and cgroups (i.s.o. active only)\n", MALLACTIVE); printf("\t -%c calculate proportional set size (PSS) per process\n", MCALCPSS); printf("\t -%c determine WCHAN (string) per thread\n", MGETWCHAN); printf("\t -P generate parsable output for specified label(s)\n"); printf("\t -J generate JSON output for specified label(s)\n"); printf("\t -%c no spaces in parsable output for command (line)\n", MRMSPACES); printf("\t -L alternate line length (default 80) in case of " "non-screen output\n"); printf("\t -z prepend regex matching environment variables to " "command line\n"); printf("\t WARNING: don't use this flag when writing (publicly readable) raw files!\n"); printf("\t -I suppress UID/GID to name translation (show numbers instead)\n"); printf("\t -k try to connect to external atopgpud daemon (default: do not connect)\n"); printf("\t -K try to connect to netatop/netatop-bpf interface (default: do not connect)\n"); generic_usage(); printf("\n"); printf("\tspecific flags for raw logfiles:\n"); printf("\t -w write raw data to file (compressed)\n"); printf("\t -r read raw data from file (compressed)\n"); printf("\t symbolic file: y[y...] for yesterday (repeated)\n"); printf("\t file name '-': read raw data from stdin\n"); printf("\t -S finish atop automatically before midnight " "(i.s.o. #samples)\n"); printf("\t -b begin showing data from specified date/time\n"); printf("\t -e finish showing data after specified date/time\n"); printf("\n"); printf("\tinterval: number of seconds (minimum 0)\n"); printf("\tsamples: number of intervals (minimum 1)\n"); printf("\n"); printf("If the interval-value is zero, a new sample can be\n"); printf("forced manually by sending signal USR1" " (kill -USR1 pid_atop)\n"); printf("or with the keystroke '%c' in interactive mode.\n", MSAMPNEXT); printf("\n"); printf("Please refer to the man-page of 'atop' for more details.\n"); cleanstop(1); } /* ** handler for ALRM-signal */ void getalarm(int sig) { awaittrigger=0; if (interval > 0) alarm(interval); /* restart the timer */ } /* ** handler for USR1-signal */ void getusr1(int sig) { awaittrigger=0; } /* ** handler for USR2-signal */ void getusr2(int sig) { awaittrigger=0; nsamples = sampcnt; // force stop after next sample } /* ** functions to handle a particular tag in the .atoprc file */ static void do_interval(char *name, char *val) { interval = get_posval(name, val); } static void do_linelength(char *name, char *val) { linelen = get_posval(name, val); } /* ** read RC-file and modify defaults accordingly */ static void readrc(char *path, int syslevel) { int i, nr, line=0, errorcnt = 0; /* ** check if this file is readable with the user's ** *real uid/gid* with syscall access() */ if ( access(path, R_OK) == 0) { FILE *fp; char linebuf[256], tagname[20], tagvalue[256]; fp = fopen(path, "r"); while ( fgets(linebuf, sizeof linebuf, fp) ) { line++; i = strlen(linebuf); if (i <= 1) // empty line? continue; if (linebuf[i-1] == '\n') linebuf[i-1] = 0; nr = sscanf(linebuf, "%19s %255[^#]", tagname, tagvalue); switch (nr) { case 0: continue; case 1: if (tagname[0] == '#') continue; mcleanstop(1, "%s: syntax error line " "%d (no value specified)\n", path, line); break; /* not reached */ default: if (tagname[0] == '#') continue; if (tagvalue[0] != '#') break; mcleanstop(1, "%s: syntax error line " "%d (no value specified)\n", path, line); } /* ** tag name and tag value found ** try to recognize tag name */ for (i=0; i < sizeof manrc/sizeof manrc[0]; i++) { if ( strcmp(tagname, manrc[i].tag) == 0) { if (manrc[i].sysonly && !syslevel) { fprintf(stderr, "%s: warning at line %2d " "- tag name %s not allowed " "in private atoprc\n", path, line, tagname); errorcnt++; break; } manrc[i].func(tagname, tagvalue); break; } } /* ** tag name not recognized */ if (i == sizeof manrc/sizeof manrc[0]) { fprintf(stderr, "%s: warning at line %2d " "- tag name %s not recognized\n", path, line, tagname); errorcnt++; } } if (errorcnt) sleep(2); fclose(fp); } } /* ** prepare twin mode */ #define TWINNAME "atoptwinXXXXXX" static char *tempname; static void twinprepare(void) { char eventbuf[1024]; int tempfd; /* ** consistency checks for used options */ if (rawreadflag) { fprintf(stderr, "twin mode can not be combined with -r\n"); exit(42); } if (rawwriteflag) { fprintf(stderr, "twin mode can not be combined with -w\n"); exit(42); } if (parseoutflag) { fprintf(stderr, "twin mode can not be combined with -P\n"); exit(42); } if (jsonoutflag) { fprintf(stderr, "twin mode can not be combined with -J\n"); exit(42); } if (!isatty(fileno(stdout)) ) // output to pipe or file? { fprintf(stderr, "twin mode only for interactive use\n"); exit(42); } /* ** create unique temporary file */ if (strlen(twindir) + sizeof TWINNAME + 1 >= RAWNAMESZ) { fprintf(stderr, "twin mode directory path too long\n"); exit(42); } tempname = malloc(strlen(twindir) + sizeof TWINNAME + 1); ptrverify(tempname, "Malloc failed for temporary twin name\n"); snprintf(tempname, strlen(twindir) + sizeof TWINNAME + 1, "%s/%s", twindir, TWINNAME); if ( (tempfd = mkstemp(tempname)) == -1) { fprintf(stderr, "%s: ", tempname); perror("twin mode file creation"); exit(42); } /* ** create lower half as child process */ switch (twinpid = fork()) { case -1: perror("fork twin process"); exit(42); case 0: // lower half: gather data and write to rawfile rawwriteflag++; handlers[0].handle_sample = rawwrite; break; default: // upper half: read from raw file and visualize rawreadflag++; /* ** created inotify instance to be awoken when the lower half ** has written a new sample to the temporary file */ if ( (fdinotify = inotify_init()) == -1) { perror("twin mode inotify init"); exit(42); } (void) inotify_add_watch(fdinotify, tempname, IN_MODIFY); /* ** arrange an automic kill of the lower half ** at the moment that the upper half terminates */ atexit(twinclean); /* ** wait for first sample to be written by lower half */ (void) read(fdinotify, eventbuf, sizeof eventbuf); } /* ** define current raw file name for both parent and child */ safe_strcpy(irawname, tempname, sizeof irawname); safe_strcpy(orawname, tempname, sizeof orawname); } /* ** kill twin process that gathers data and ** remove the temporary raw file */ static void twinclean(void) { if (twinpid) // kill lower half process kill(twinpid, SIGTERM); (void) unlink(tempname); } atop-2.12.1/atopcat.c0000644000203100020310000002064615064552761013662 0ustar gerlofgerlof/* ** ATOP - System & Process Monitor ** ** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as process-level. ** ** This program concatenates several raw logfiles into one output stream, ** to be stored as new file or to be passed via a pipe to atop/atopsar directly. ** ========================================================================== ** Author: Gerlof Langeveld ** E-mail: gerlof.langeveld@atoptool.nl ** Initial: March 2020 ** -------------------------------------------------------------------------- ** Copyright (C) 2020 Gerlof Langeveld ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ** -------------------------------------------------------------------------- */ #define _POSIX_C_SOURCE #define _XOPEN_SOURCE #define _GNU_SOURCE #define _DEFAULT_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include "atop.h" #include "rawlog.h" char *convepoch(time_t); void prusage(char *); int main(int argc, char *argv[]) { int i, fd, n, c; int firstfile, beverbose=0, dryrun=0; struct rawheader rh; struct rawrecord rr; char *infile, *sstat, *pstat, *cstat, *istat; unsigned int aversion, cgroupv2 = 0; // verify the command line arguments: input filename(s) // if (argc < 2) prusage(argv[0]); while ((c = getopt(argc, argv, "?hvd")) != EOF) { switch (c) { case '?': // usage wanted? case 'h': // usage wanted? prusage(argv[0]); break; case 'v': // verbosity wanted? beverbose = 1; break; case 'd': // dry run wanted? dryrun = 1; break; default: prusage(argv[0]); } } if ( isatty(fileno(stdout)) && !dryrun) { fprintf(stderr, "This program produces binary output on stdout " "that should be redirected\nto a file or pipe!\n"); exit(1); } // open all input files one-by-one // for (i=optind, firstfile=1; i < argc; i++, firstfile=0) { infile = argv[i]; // open raw file for reading // if ( (fd = open(infile, O_RDONLY)) == -1) { fprintf(stderr, "%s - ", infile); perror("open for reading"); exit(2); } // read the raw header // if ( read(fd, &rh, sizeof rh) < sizeof rh) { fprintf(stderr, "%s: cannot read raw header\n", infile); close(fd); exit(3); } // verify if this is a correct rawlog file // if (rh.magic != MYMAGIC) { fprintf(stderr, "%s: not a valid rawlog file " "(wrong magic number)\n", infile); close(fd); exit(4); } // only for the first file, store the version number and write // the raw header for the entire stream // // for the next files, be sure that the version is the same // as the first file // if (firstfile) { aversion = rh.aversion; cgroupv2 = rh.supportflags & CGROUPV2; if (!dryrun) { if ( write(1, &rh, sizeof rh) < sizeof rh) { fprintf(stderr, "can not write raw header\n"); exit(10); } } if (beverbose) { fprintf(stderr, "Logs created by atop version %d.%d\n\n", (rh.aversion >> 8) & 0x7f, rh.aversion & 0xff); fprintf(stderr, "%-10s %-8s %12s %8s %9s %8s %8s\n", "date", "time", "interval", "comprsys", "comprproc", "comprcgr", "comppids"); } } else // subsequent file { if (aversion != rh.aversion) { fprintf(stderr, "Version of file %s is unequal to " "version of first file\n", infile); close(fd); exit(5); } if (cgroupv2 != (rh.supportflags & CGROUPV2)) { fprintf(stderr, "Cgroups support of file %s is unequal to " "first file\n", infile); close(fd); exit(5); } } // read every raw record followed by the compressed // system-level stats, process-level stats, // cgroup-level stats and pidlist. // while ( read(fd, &rr, sizeof rr) == sizeof rr ) { if (beverbose) { fprintf(stderr, "%19s %12u %8u %9u %8u %8u %s\n", convepoch(rr.curtime), rr.interval, rr.scomplen, rr.pcomplen, rr.ccomplen, rr.icomplen, rr.flags&RRBOOT ? "boot" : ""); } // dynamically allocate space to read stats // if ( (sstat = malloc(rr.scomplen)) == NULL) { fprintf(stderr, "malloc failed for sstat\n"); exit(7); } if ( (pstat = malloc(rr.pcomplen)) == NULL) { fprintf(stderr, "malloc failed for pstat\n"); exit(7); } if ( (cstat = malloc(rr.ccomplen)) == NULL) { fprintf(stderr, "malloc failed for cstat\n"); exit(7); } if ( (istat = malloc(rr.icomplen)) == NULL) { fprintf(stderr, "malloc failed for istat\n"); exit(7); } // read system-level stats // if ((n = read(fd, sstat, rr.scomplen)) != rr.scomplen) { if (n == -1) { fprintf(stderr, "read file %s", infile); perror(""); exit(8); } else { fprintf(stderr, "file %s incomplete!\n", infile); free(sstat); free(pstat); free(cstat); free(istat); break; } } // read process-level stats // if ((n = read(fd, pstat, rr.pcomplen)) != rr.pcomplen) { if (n == -1) { fprintf(stderr, "read file %s", infile); perror(""); exit(8); } else { fprintf(stderr, "file %s incomplete!\n", infile); free(sstat); free(pstat); free(cstat); free(istat); break; } } // read cgroup-level stats // if ((n = read(fd, cstat, rr.ccomplen)) != rr.ccomplen) { if (n == -1) { fprintf(stderr, "read file %s", infile); perror(""); exit(8); } else { fprintf(stderr, "file %s incomplete!\n", infile); free(sstat); free(pstat); free(cstat); free(istat); break; } } // read compressed pidlist // if ((n = read(fd, istat, rr.icomplen)) != rr.icomplen) { if (n == -1) { fprintf(stderr, "read file %s", infile); perror(""); exit(8); } else { fprintf(stderr, "file %s incomplete!\n", infile); free(sstat); free(pstat); free(cstat); free(istat); break; } } if (!dryrun) { // write raw record followed by the compressed // system-level stats, process-level stats, // cgroup-level stats and pidlist // if ( write(1, &rr, sizeof rr) < sizeof rr) { fprintf(stderr, "can not write raw record\n"); exit(11); } if ( write(1, sstat, rr.scomplen) < rr.scomplen) { fprintf(stderr, "can not write sstat\n"); exit(11); } if ( write(1, pstat, rr.pcomplen) < rr.pcomplen) { fprintf(stderr, "can not write pstat\n"); exit(11); } if ( write(1, cstat, rr.ccomplen) < rr.ccomplen) { fprintf(stderr, "can not write cstat\n"); exit(11); } if ( write(1, istat, rr.icomplen) < rr.icomplen) { fprintf(stderr, "can not write istat\n"); exit(11); } } // free dynamically allocated buffers // free(sstat); free(pstat); free(cstat); free(istat); } close(fd); } return 0; } // Function to convert an epoch time to date-time format // char * convepoch(time_t utime) { struct tm *tt; static char datetime[64]; tt = localtime(&utime); snprintf(datetime, sizeof datetime, "%04d/%02d/%02d %02d:%02d:%02d", tt->tm_year+1900, tt->tm_mon+1, tt->tm_mday, tt->tm_hour, tt->tm_min, tt->tm_sec); return datetime; } // Function that shows the usage message // void prusage(char *name) { fprintf(stderr, "Usage: %s [-dv] rawfile [rawfile]...\n", name); fprintf(stderr, "\t-d\tdry run (no raw output generated)\n"); fprintf(stderr, "\t-v\tbe verbose\n"); exit(1); } atop-2.12.1/atopconvert.c0000644000203100020310000020043315064552761014565 0ustar gerlofgerlof/* ** ATOP - System & Process Monitor ** ** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as process-level. ** ** This program converts a raw logfile created by a particular version ** of atop to (by default) the current version of atop. ** ========================================================================== ** Author: Gerlof Langeveld ** E-mail: gerlof.langeveld@atoptool.nl ** Initial: July/August 2018 ** -------------------------------------------------------------------------- ** Copyright (C) 2018-2025 Gerlof Langeveld ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ** -------------------------------------------------------------------------- */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "atop.h" #include "photosyst.h" #include "photoproc.h" #include "cgroups.h" #include "rawlog.h" #include "prev/netstats_wrong.h" #include "prev/photosyst_200.h" #include "prev/photoproc_200.h" #include "prev/photosyst_201.h" #include "prev/photoproc_201.h" #include "prev/photosyst_202.h" #include "prev/photoproc_202.h" #include "prev/photosyst_203.h" #include "prev/photoproc_203.h" #include "prev/photosyst_204.h" #include "prev/photoproc_204.h" #include "prev/photosyst_205.h" #include "prev/photoproc_205.h" #include "prev/photosyst_206.h" #include "prev/photoproc_206.h" #include "prev/photosyst_207.h" #include "prev/photoproc_207.h" #include "prev/photosyst_208.h" #include "prev/photoproc_208.h" #include "prev/photosyst_209.h" #include "prev/photoproc_209.h" #include "prev/photosyst_210.h" #include "prev/photoproc_210.h" #include "prev/photosyst_211.h" #include "prev/photoproc_211.h" #include "prev/cgroups_211.h" #include "prev/photosyst_212.h" #include "prev/photoproc_212.h" #include "prev/cgroups_212.h" void justcopy(void *, void *, count_t, count_t); void scpu_to_21(void *, void *, count_t, count_t); void sdsk_to_21(void *, void *, count_t, count_t); void sint_to_22(void *, void *, count_t, count_t); void scpu_to_27(void *, void *, count_t, count_t); void smem_to_27(void *, void *, count_t, count_t); void sdsk_to_27(void *, void *, count_t, count_t); void smem_to_28(void *, void *, count_t, count_t); void snet_to_28(void *, void *, count_t, count_t); void sdsk_to_28(void *, void *, count_t, count_t); void smnu_to_28(void *, void *, count_t, count_t); void scnu_to_28(void *, void *, count_t, count_t); void sllc_to_210(void *, void *, count_t, count_t); void tgen_to_21(void *, void *, count_t, count_t); void tmem_to_21(void *, void *, count_t, count_t); void tgen_to_22(void *, void *, count_t, count_t); void tcpu_to_26(void *, void *, count_t, count_t); void tmem_to_26(void *, void *, count_t, count_t); void tcpu_to_28(void *, void *, count_t, count_t); void tmem_to_28(void *, void *, count_t, count_t); void tgen_to_210(void *, void *, count_t, count_t); void tgen_to_211(void *, void *, count_t, count_t); void tcpu_to_211(void *, void *, count_t, count_t); void tmem_to_211(void *, void *, count_t, count_t); void smnu_to_211(void *, void *, count_t, count_t); void scnu_to_211(void *, void *, count_t, count_t); /////////////////////////////////////////////////////////////// // Conversion functions // -------------------- // The structures with system level, cgroups level and // process level info consists of sub-structures, // generally for cpu, memory, disk and network values. // These sub-structures might have changed from a // specific version of atop to the next version. // For modified sub-structures, conversion functions have to be // written. These conversion functions will be called in a chained // way for one version increment at the time. Suppose that a // raw log is offered that has been created by atop 2.0 to be // converted to atop 2.3, every sub-structure will be converted // from 2.0 to 2.1, from 2.1 to 2.2 and finally from 2.2 to 2.3. // When a sub-structure has NOT been changed from one version to // another, it will just be copied by the generic conversion // function 'justcopy' to the proper location in the structure // of the next version. // All conversion steps are controlled by the convs[] table. /////////////////////////////////////////////////////////////// // Generic function that just copies an old structure byte-wise to // a new structure (new structure implicitly padded with binary zeroes) // void justcopy(void *old, void *new, count_t oldsize, count_t newsize) { if (oldsize) memcpy(new, old, newsize > oldsize ? oldsize : newsize); } // ///////////////////////////////////////////////////////////////// // Specific functions that convert an old sstat sub-structure to // a new sub-structure (system level) // ///////////////////////////////////////////////////////////////// void scpu_to_21(void *old, void *new, count_t oldsize, count_t newsize) { // cfuture[1] --> cfuture[4] in struct percpu // struct cpustat_20 *c20 = old; struct cpustat_21 *c21 = new; int i; memcpy(c21, c20, (char *)&c20->all - (char *)c20); // base values memcpy(&c21->all, &c20->all, sizeof(struct percpu_20)); for (i=0; i < MAXCPU_20; i++) memcpy( &(c21->cpu[i]), &(c20->cpu[i]), sizeof(struct percpu_20)); } void sdsk_to_21(void *old, void *new, count_t oldsize, count_t newsize) { // MAXDSK and MAXLVM have been enlarged // struct dskstat_20 *d20 = old; struct dskstat_21 *d21 = new; d21->ndsk = d20->ndsk; d21->nmdd = d20->nmdd; d21->nlvm = d20->nlvm; memcpy(d21->dsk, d20->dsk, sizeof d20->dsk); memcpy(d21->mdd, d20->mdd, sizeof d20->mdd); memcpy(d21->lvm, d20->lvm, sizeof d20->lvm); } void sint_to_22(void *old, void *new, count_t oldsize, count_t newsize) { // members 'type' and 'speedp' added to struct perintf // struct intfstat_21 *i21 = old; struct intfstat_22 *i22 = new; int i; i22->nrintf = i21->nrintf; for (i=0; i < MAXINTF_21; i++) { memcpy(&(i22->intf[i]), &(i21->intf[i]), sizeof(struct perintf_21)); // correct last members by refilling // i22->intf[i].type = '?'; i22->intf[i].speed = i21->intf[i].speed; i22->intf[i].speedp = i21->intf[i].speed; i22->intf[i].duplex = i21->intf[i].duplex; memset(i22->intf[i].cfuture, 0, sizeof i22->intf[i].cfuture); } } void scpu_to_27(void *old, void *new, count_t oldsize, count_t newsize) { // cfuture[2] --> cfuture[4] in struct percpu // struct cpustat_26 *c26 = old; struct cpustat_27 *c27 = new; int i; memcpy(c27, c26, (char *)&c26->all - (char *)c26); // base values memcpy(&c27->all, &c26->all, sizeof(struct percpu_26)); for (i=0; i < MAXCPU_26; i++) memcpy( &(c27->cpu[i]), &(c26->cpu[i]), sizeof(struct percpu_26)); } void smem_to_27(void *old, void *new, count_t oldsize, count_t newsize) { struct memstat_26 *m26 = old; struct memstat_27 *m27 = new; memcpy(m27, m26, sizeof *m26); m27->oomkills = -1; // explicitly define 'unused' } void sdsk_to_27(void *old, void *new, count_t oldsize, count_t newsize) { struct dskstat_26 *d26 = old; struct dskstat_27 *d27 = new; int i; memcpy(d27, d26, oldsize); for (i=0; i < d27->ndsk; i++) d27->dsk[i].ndisc = -1; // explicitly define 'unused' for (i=0; i < d27->nmdd; i++) d27->mdd[i].ndisc = -1; // explicitly define 'unused' for (i=0; i < d27->nlvm; i++) d27->lvm[i].ndisc = -1; // explicitly define 'unused' } void smem_to_28(void *old, void *new, count_t oldsize, count_t newsize) { struct memstat_27 *m27 = old; struct memstat_28 *m28 = new; memcpy(m28, m27, sizeof *m27); m28->tcpsock = 0; // new counter m28->udpsock = 0; // new counter m28->commitlim = m27->commitlim; m28->committed = m27->committed; m28->shmem = m27->shmem; m28->shmrss = m27->shmrss; m28->shmswp = m27->shmswp; m28->slabreclaim = m27->slabreclaim; m28->tothugepage = m27->tothugepage; m28->freehugepage = m27->freehugepage; m28->hugepagesz = m27->hugepagesz; m28->vmwballoon = m27->vmwballoon; m28->zfsarcsize = m27->zfsarcsize; m28->swapcached = m27->swapcached; m28->ksmsharing = m27->ksmsharing; m28->ksmshared = m27->ksmshared; m28->zswstored = m27->zswstored; m28->zswtotpool = m27->zswtotpool; m28->oomkills = m27->oomkills; m28->compactstall = m27->compactstall; m28->pgmigrate = m27->pgmigrate; m28->numamigrate = m27->numamigrate; m28->pgouts = 0; // new counter m28->pgins = 0; // new counter m28->pagetables = 0; // new counter memset(m28->cfuture, 0, sizeof m28->cfuture); } void snet_to_28(void *old, void *new, count_t oldsize, count_t newsize) { struct netstat_wrong *n27 = old; struct netstat *n28 = new; // copy unmodified structs // memcpy(&(n28->ipv4), &(n27->ipv4), sizeof n28->ipv4); memcpy(&(n28->ipv6), &(n27->ipv6), sizeof n28->ipv6); memcpy(&(n28->udpv4), &(n27->udpv4), sizeof n28->udpv4); memcpy(&(n28->udpv6), &(n27->udpv6), sizeof n28->udpv6); memcpy(&(n28->icmpv6), &(n27->icmpv6), sizeof n28->icmpv6); // convert tcp_stats (last counter added) // memcpy(&(n28->tcp), &(n27->tcp), sizeof n27->tcp); n28->tcp.InCsumErrors = 0; // convert icmpv4_stats (counter added in the middle) // n28->icmpv4.InMsgs = n27->icmpv4.InMsgs; n28->icmpv4.InErrors = n27->icmpv4.InErrors; n28->icmpv4.InCsumErrors = 0; // new counter n28->icmpv4.InDestUnreachs = n27->icmpv4.InDestUnreachs; n28->icmpv4.InTimeExcds = n27->icmpv4.InTimeExcds; n28->icmpv4.InParmProbs = n27->icmpv4.InParmProbs; n28->icmpv4.InSrcQuenchs = n27->icmpv4.InSrcQuenchs; n28->icmpv4.InRedirects = n27->icmpv4.InRedirects; n28->icmpv4.InEchos = n27->icmpv4.InEchos; n28->icmpv4.InEchoReps = n27->icmpv4.InEchoReps; n28->icmpv4.InTimestamps = n27->icmpv4.InTimestamps; n28->icmpv4.InTimestampReps = n27->icmpv4.InTimestampReps; n28->icmpv4.InAddrMasks = n27->icmpv4.InAddrMasks; n28->icmpv4.InAddrMaskReps = n27->icmpv4.InAddrMaskReps; n28->icmpv4.OutMsgs = n27->icmpv4.OutMsgs; n28->icmpv4.OutErrors = n27->icmpv4.OutErrors; n28->icmpv4.OutDestUnreachs = n27->icmpv4.OutDestUnreachs; n28->icmpv4.OutTimeExcds = n27->icmpv4.OutTimeExcds; n28->icmpv4.OutParmProbs = n27->icmpv4.OutParmProbs; n28->icmpv4.OutSrcQuenchs = n27->icmpv4.OutSrcQuenchs; n28->icmpv4.OutRedirects = n27->icmpv4.OutRedirects; n28->icmpv4.OutEchos = n27->icmpv4.OutEchos; n28->icmpv4.OutEchoReps = n27->icmpv4.OutEchoReps; n28->icmpv4.OutTimestamps = n27->icmpv4.OutTimestamps; n28->icmpv4.OutTimestampReps = n27->icmpv4.OutTimestampReps; n28->icmpv4.OutAddrMasks = n27->icmpv4.OutAddrMasks; n28->icmpv4.OutAddrMaskReps = n27->icmpv4.OutAddrMaskReps; } void sdsk_to_28(void *old, void *new, count_t oldsize, count_t newsize) { struct dskstat_27 *d27 = old; struct dskstat_28 *d28 = new; int i; d28->ndsk = d27->ndsk; d28->nmdd = d27->nmdd; d28->nlvm = d27->nlvm; for (i=0; i < d28->ndsk; i++) memcpy(&(d28->dsk[i]), &(d27->dsk[i]), sizeof d27->dsk[i]); for (i=0; i < d28->nmdd; i++) memcpy(&(d28->mdd[i]), &(d27->mdd[i]), sizeof d27->mdd[i]); for (i=0; i < d28->nlvm; i++) memcpy(&(d28->lvm[i]), &(d27->lvm[i]), sizeof d27->lvm[i]); } void smnu_to_28(void *old, void *new, count_t oldsize, count_t newsize) { struct memnuma_27 *n27 = old; struct memnuma_28 *n28 = new; int i; n28->nrnuma = n27->nrnuma; for (i=0; i < n28->nrnuma; i++) { n28->numa[i].numanr = i; n28->numa[i].frag = n27->numa[i].frag; n28->numa[i].totmem = n27->numa[i].totmem; n28->numa[i].freemem = n27->numa[i].freemem; n28->numa[i].filepage = n27->numa[i].filepage; n28->numa[i].dirtymem = n27->numa[i].dirtymem; n28->numa[i].slabmem = n27->numa[i].slabmem; n28->numa[i].slabreclaim = n27->numa[i].slabreclaim; n28->numa[i].active = n27->numa[i].active; n28->numa[i].inactive = n27->numa[i].inactive; n28->numa[i].shmem = n27->numa[i].shmem; n28->numa[i].tothp = n27->numa[i].tothp; } } void scnu_to_28(void *old, void *new, count_t oldsize, count_t newsize) { struct cpunuma_27 *n27 = old; struct cpunuma_28 *n28 = new; int i; n28->nrnuma = n27->nrnuma; for (i=0; i < n28->nrnuma; i++) { n28->numa[i].numanr = i; n28->numa[i].nrcpu = n27->numa[i].nrcpu; n28->numa[i].stime = n27->numa[i].stime; n28->numa[i].utime = n27->numa[i].utime; n28->numa[i].ntime = n27->numa[i].ntime; n28->numa[i].itime = n27->numa[i].itime; n28->numa[i].wtime = n27->numa[i].wtime; n28->numa[i].Itime = n27->numa[i].Itime; n28->numa[i].Stime = n27->numa[i].Stime; n28->numa[i].steal = n27->numa[i].steal; n28->numa[i].guest = n27->numa[i].guest; } } void sllc_to_210(void *old, void *new, count_t oldsize, count_t newsize) { struct llcstat_29 *l29 = old; struct llcstat_210 *l210 = new; int i; l210->nrllcs = l29->nrllcs; for (i=0; i < l210->nrllcs; i++) { l210->perllc[i].id = l29->perllc[i].id; l210->perllc[i].occupancy = l29->perllc[i].occupancy; l210->perllc[i].mbm_local = l29->perllc[i].mbm_local; l210->perllc[i].mbm_total = l29->perllc[i].mbm_total; } } // ///////////////////////////////////////////////////////////////// // Specific functions that convert an old tstat sub-structure to // a new sub-structure (process level) // ///////////////////////////////////////////////////////////////// void tgen_to_21(void *old, void *new, count_t oldsize, count_t newsize) { // member 'envid' inserted in struct gen // struct gen_20 *g20 = old; struct gen_21 *g21 = new; memcpy(g21, g20, (char *)g20->ifuture - (char *)g20); // base values g21->envid = 0; } void tmem_to_21(void *old, void *new, count_t oldsize, count_t newsize) { // members 'pmem' and 'cfuture[4]' inserted in struct mem // struct mem_20 *m20 = old; struct mem_21 *m21 = new; m21->minflt = m20->minflt; m21->majflt = m20->majflt; m21->vexec = m20->vexec; m21->vmem = m20->vmem; m21->rmem = m20->rmem; m21->pmem = 0; m21->vgrow = m20->vgrow; m21->rgrow = m20->rgrow; m21->vdata = m20->vdata; m21->vstack = m20->vstack; m21->vlibs = m20->vlibs; m21->vswap = m20->vswap; } void tgen_to_22(void *old, void *new, count_t oldsize, count_t newsize) { // member 'envid' removed, members 'ctid' and 'vpid' inserted in struct gen // struct gen_21 *g21 = old; struct gen_22 *g22 = new; memcpy(g22, g21, (char *)&g21->envid - (char *)g21); // copy base values g22->ctid = g21->envid; g22->vpid = 0; } void tcpu_to_26(void *old, void *new, count_t oldsize, count_t newsize) { // unused values appear not to be zeroed in version 2.5 // struct cpu_25 *c25 = old; struct cpu_26 *c26 = new; memcpy(c26, c25, sizeof *c26); // copy entire struct memset(&(c26->wchan), 0, sizeof c26->wchan); c26->rundelay = 0; } void tmem_to_26(void *old, void *new, count_t oldsize, count_t newsize) { // unused values appear not to be zeroed in version 2.5 // struct mem_25 *m25 = old; struct mem_26 *m26 = new; memcpy(m26, m25, sizeof *m26); // copy entire struct m26->vlock = 0; } void tcpu_to_28(void *old, void *new, count_t oldsize, count_t newsize) { // cgroup counters inserted // struct cpu_27 *c27 = old; struct cpu_28 *c28 = new; c28->utime = c27->utime; c28->stime = c27->stime; c28->nice = c27->nice; c28->prio = c27->prio; c28->rtprio = c27->rtprio; c28->policy = c27->policy; c28->curcpu = c27->curcpu; c28->sleepavg = c27->sleepavg; c28->rundelay = c27->rundelay; memcpy(c28->wchan, c27->wchan, sizeof c28->wchan); c28->blkdelay = 0; c28->cgcpuweight= 0; c28->cgcpumax = 0; c28->cgcpumaxr = 0; memset(c28->ifuture, 0, sizeof c28->ifuture); memset(c28->cfuture, 0, sizeof c28->cfuture); } void tmem_to_28(void *old, void *new, count_t oldsize, count_t newsize) { struct mem_27 *m27 = old; struct mem_28 *m28 = new; memcpy(m28, m27, sizeof *m27); // copy old struct m28->cgmemmax = 0; m28->cgmemmaxr = 0; m28->cgswpmax = 0; m28->cgswpmaxr = 0; memset(m28->cfuture, 0, sizeof m28->cfuture); } void tgen_to_210(void *old, void *new, count_t oldsize, count_t newsize) { // member 'nthridle' inserted, everything from member 'ctid' must shift // struct gen_29 *g29 = old; struct gen_210 *g210 = new; memcpy(g210, g29, (char *)&g29->ctid - (char *)g29); // copy base values g210->nthridle = 0; g210->ctid = g29->ctid; g210->vpid = g29->vpid; g210->wasinactive = g29->wasinactive; memcpy(g210->utsname, g29->container, sizeof(g210->utsname)); memcpy(g210->cgpath, g29->cgpath, sizeof(g210->cgpath)); } void tgen_to_211(void *old, void *new, count_t oldsize, count_t newsize) { // member 'cgpath' removed, member cgroupix and ifuture instead // struct gen_210 *g210 = old; struct gen_211 *g211 = new; memcpy(g211, g210, sizeof *g211); // copy base values memset(g211->ifuture, 0, sizeof g211->ifuture); g211->cgroupix = -1; } void tcpu_to_211(void *old, void *new, count_t oldsize, count_t newsize) { // set future values (released cgroup values) to zero // struct cpu_210 *c210 = old; struct cpu_211 *c211 = new; memcpy(c211, c210, sizeof *c211); // copy base values memset(c211->ifuture, 0, sizeof c211->ifuture); } void tmem_to_211(void *old, void *new, count_t oldsize, count_t newsize) { // set future values (released cgroup values) to zero // struct mem_210 *m210 = old; struct mem_211 *m211 = new; memcpy(m211, m210, sizeof *m211); // copy base values memset(m211->cfuture, 0, sizeof m211->cfuture); } void smnu_to_211(void *old, void *new, count_t oldsize, count_t newsize) { struct memnuma_210 *n210 = old; struct memnuma_211 *n211 = new; int i; n211->nrnuma = n210->nrnuma; for (i=0; i < n211->nrnuma; i++) { n211->numa[i].numanr = i; n211->numa[i].frag = n210->numa[i].frag; n211->numa[i].totmem = n210->numa[i].totmem; n211->numa[i].freemem = n210->numa[i].freemem; n211->numa[i].filepage = n210->numa[i].filepage; n211->numa[i].dirtymem = n210->numa[i].dirtymem; n211->numa[i].slabmem = n210->numa[i].slabmem; n211->numa[i].slabreclaim = n210->numa[i].slabreclaim; n211->numa[i].active = n210->numa[i].active; n211->numa[i].inactive = n210->numa[i].inactive; n211->numa[i].shmem = n210->numa[i].shmem; n211->numa[i].tothp = n210->numa[i].tothp; n211->numa[i].freehp = n210->numa[i].freehp; memset(n211->numa[i].cfuture, 0, sizeof n211->numa[i].cfuture); } } void scnu_to_211(void *old, void *new, count_t oldsize, count_t newsize) { struct cpunuma_210 *n210 = old; struct cpunuma_211 *n211 = new; int i; n211->nrnuma = n210->nrnuma; for (i=0; i < n211->nrnuma; i++) { n211->numa[i].numanr = i; n211->numa[i].nrcpu = n210->numa[i].nrcpu; n211->numa[i].stime = n210->numa[i].stime; n211->numa[i].utime = n210->numa[i].utime; n211->numa[i].ntime = n210->numa[i].ntime; n211->numa[i].itime = n210->numa[i].itime; n211->numa[i].wtime = n210->numa[i].wtime; n211->numa[i].Itime = n210->numa[i].Itime; n211->numa[i].Stime = n210->numa[i].Stime; n211->numa[i].steal = n210->numa[i].steal; n211->numa[i].guest = n210->numa[i].guest; memset(n211->numa[i].cfuture, 0, sizeof n211->numa[i].cfuture); } } /////////////////////////////////////////////////////////////// // conversion definition for various structs in sstat and tstat // #define SETVERSION(major, minor) ((major << 8) | minor) #define STROFFSET(str, begin) ((long)((char *)str - (char *)begin)) struct sconvstruct { count_t structsize; void *structptr; void (*structconv)(void *, void *, count_t, count_t); }; struct tconvstruct { count_t structsize; long structoffset; void (*structconv)(void *, void *, count_t, count_t); }; struct cconvstruct { count_t structsize; long structoffset; void (*structconv)(void *, void *, count_t, count_t); }; struct sstat_20 sstat_20; struct sstat_21 sstat_21; struct sstat_22 sstat_22; struct sstat_23 sstat_23; struct sstat_24 sstat_24; struct sstat_25 sstat_25; struct sstat_26 sstat_26; struct sstat_27 sstat_27; struct sstat_28 sstat_28; struct sstat_29 sstat_29; struct sstat_210 sstat_210; struct sstat_211 sstat_211; struct sstat_212 sstat_212; struct sstat sstat; struct cstat_211 cstat_211; struct cstat_212 cstat_212; struct cstat cstat; struct tstat_20 tstat_20; struct tstat_21 tstat_21; struct tstat_22 tstat_22; struct tstat_23 tstat_23; struct tstat_24 tstat_24; struct tstat_25 tstat_25; struct tstat_26 tstat_26; struct tstat_27 tstat_27; struct tstat_28 tstat_28; struct tstat_29 tstat_29; struct tstat_210 tstat_210; struct tstat_211 tstat_211; struct tstat_212 tstat_212; struct tstat tstat; struct convertall { int version; // version of raw log unsigned int sstatlen; // length of struct sstat void *sstat; // pointer to sstat struct unsigned int tstatlen; // length of struct tstat void *tstat; // pointer to tstat structs unsigned int cstatlen; // length of struct cstat (>= 2.11) void *cstat; // pointer to all cstat structs struct cstat **cslist; // pointer to list of cstat pointers // conversion definition for subparts within sstat // struct sconvstruct scpu; struct sconvstruct smem; struct sconvstruct snet; struct sconvstruct sintf; struct sconvstruct sdsk; struct sconvstruct snfs; struct sconvstruct scfs; struct sconvstruct swww; struct sconvstruct spsi; struct sconvstruct sgpu; struct sconvstruct sifb; struct sconvstruct smnum; struct sconvstruct scnum; struct sconvstruct sllc; // conversion definition for subparts within tstat // struct tconvstruct tgen; struct tconvstruct tcpu; struct tconvstruct tdsk; struct tconvstruct tmem; struct tconvstruct tnet; struct tconvstruct tgpu; // conversion definition for subparts within cstat // relevant from version 2.11 onwards // struct cconvstruct cggen; struct cconvstruct cgconf; struct cconvstruct cgcpu; struct cconvstruct cgmem; struct cconvstruct cgdsk; } convs[] = { {SETVERSION(2,0), sizeof(struct sstat_20), &sstat_20, sizeof(struct tstat_20), NULL, 0, NULL, NULL, {sizeof(struct cpustat_20), &sstat_20.cpu, NULL}, {sizeof(struct memstat_20), &sstat_20.mem, NULL}, {sizeof(struct netstat_20), &sstat_20.net, NULL}, {sizeof(struct intfstat_20), &sstat_20.intf, NULL}, {sizeof(struct dskstat_20), &sstat_20.dsk, NULL}, {0, NULL, NULL}, {0, NULL, NULL}, {sizeof(struct wwwstat_20), &sstat_20.www, NULL}, {0, NULL, NULL}, {0, NULL, NULL}, {0, NULL, NULL}, {0, NULL, NULL}, {0, NULL, NULL}, {0, NULL, NULL}, {sizeof(struct gen_20), STROFFSET(&tstat_20.gen, &tstat_20), NULL}, {sizeof(struct cpu_20), STROFFSET(&tstat_20.cpu, &tstat_20), NULL}, {sizeof(struct dsk_20), STROFFSET(&tstat_20.dsk, &tstat_20), NULL}, {sizeof(struct mem_20), STROFFSET(&tstat_20.mem, &tstat_20), NULL}, {sizeof(struct net_20), STROFFSET(&tstat_20.net, &tstat_20), NULL}, {0, 0, NULL}, {0, 0, NULL}, {0, 0, NULL}, {0, 0, NULL}, {0, 0, NULL}, {0, 0, NULL}, }, {SETVERSION(2,1), // 2.0 --> 2.1 sizeof(struct sstat_21), &sstat_21, sizeof(struct tstat_21), NULL, 0, NULL, NULL, {sizeof(struct cpustat_21), &sstat_21.cpu, scpu_to_21}, {sizeof(struct memstat_21), &sstat_21.mem, justcopy}, {sizeof(struct netstat_21), &sstat_21.net, justcopy}, {sizeof(struct intfstat_21), &sstat_21.intf, justcopy}, {sizeof(struct dskstat_21), &sstat_21.dsk, sdsk_to_21}, {0, NULL, NULL}, {0, NULL, NULL}, {sizeof(struct wwwstat_21), &sstat_21.www, justcopy}, {0, NULL, NULL}, {0, NULL, NULL}, {0, NULL, NULL}, {0, NULL, NULL}, {0, NULL, NULL}, {0, NULL, NULL}, {sizeof(struct gen_21), STROFFSET(&tstat_21.gen, &tstat_21), tgen_to_21}, {sizeof(struct cpu_21), STROFFSET(&tstat_21.cpu, &tstat_21), justcopy}, {sizeof(struct dsk_21), STROFFSET(&tstat_21.dsk, &tstat_21), justcopy}, {sizeof(struct mem_21), STROFFSET(&tstat_21.mem, &tstat_21), tmem_to_21}, {sizeof(struct net_21), STROFFSET(&tstat_21.net, &tstat_21), justcopy}, {0, 0, NULL}, {0, 0, NULL}, {0, 0, NULL}, {0, 0, NULL}, {0, 0, NULL}, {0, 0, NULL}, }, {SETVERSION(2,2), // 2.1 --> 2.2 sizeof(struct sstat_22), &sstat_22, sizeof(struct tstat_22), NULL, 0, NULL, NULL, {sizeof(struct cpustat_22), &sstat_22.cpu, justcopy}, {sizeof(struct memstat_22), &sstat_22.mem, justcopy}, {sizeof(struct netstat_22), &sstat_22.net, justcopy}, {sizeof(struct intfstat_22), &sstat_22.intf, sint_to_22}, {sizeof(struct dskstat_22), &sstat_22.dsk, justcopy}, {sizeof(struct nfsstat_22), &sstat_22.nfs, NULL}, {sizeof(struct contstat_22), &sstat_22.cfs, NULL}, {sizeof(struct wwwstat_22), &sstat_22.www, justcopy}, {0, NULL, NULL}, {0, NULL, NULL}, {0, NULL, NULL}, {0, NULL, NULL}, {0, NULL, NULL}, {0, NULL, NULL}, {sizeof(struct gen_22), STROFFSET(&tstat_22.gen, &tstat_22), tgen_to_22}, {sizeof(struct cpu_22), STROFFSET(&tstat_22.cpu, &tstat_22), justcopy}, {sizeof(struct dsk_22), STROFFSET(&tstat_22.dsk, &tstat_22), justcopy}, {sizeof(struct mem_22), STROFFSET(&tstat_22.mem, &tstat_22), justcopy}, {sizeof(struct net_22), STROFFSET(&tstat_22.net, &tstat_22), justcopy}, {0, 0, NULL}, {0, 0, NULL}, {0, 0, NULL}, {0, 0, NULL}, {0, 0, NULL}, {0, 0, NULL}, }, {SETVERSION(2,3), // 2.2 --> 2.3 sizeof(struct sstat_23), &sstat_23, sizeof(struct tstat_23), NULL, 0, NULL, NULL, {sizeof(struct cpustat_23), &sstat_23.cpu, justcopy}, {sizeof(struct memstat_23), &sstat_23.mem, justcopy}, {sizeof(struct netstat_23), &sstat_23.net, justcopy}, {sizeof(struct intfstat_23), &sstat_23.intf, justcopy}, {sizeof(struct dskstat_23), &sstat_23.dsk, justcopy}, {sizeof(struct nfsstat_23), &sstat_23.nfs, justcopy}, {sizeof(struct contstat_23), &sstat_23.cfs, justcopy}, {sizeof(struct wwwstat_23), &sstat_23.www, justcopy}, {0, NULL, NULL}, {0, NULL, NULL}, {0, NULL, NULL}, {0, NULL, NULL}, {0, NULL, NULL}, {0, NULL, NULL}, {sizeof(struct gen_23), STROFFSET(&tstat_23.gen, &tstat_23), justcopy}, {sizeof(struct cpu_23), STROFFSET(&tstat_23.cpu, &tstat_23), justcopy}, {sizeof(struct dsk_23), STROFFSET(&tstat_23.dsk, &tstat_23), justcopy}, {sizeof(struct mem_23), STROFFSET(&tstat_23.mem, &tstat_23), justcopy}, {sizeof(struct net_23), STROFFSET(&tstat_23.net, &tstat_23), justcopy}, {0, 0, NULL}, {0, 0, NULL}, {0, 0, NULL}, {0, 0, NULL}, {0, 0, NULL}, {0, 0, NULL}, }, {SETVERSION(2,4), // 2.3 --> 2.4 sizeof(struct sstat_24), &sstat_24, sizeof(struct tstat_24), NULL, 0, NULL, NULL, {sizeof(struct cpustat_24), &sstat_24.cpu, justcopy}, {sizeof(struct memstat_24), &sstat_24.mem, justcopy}, {sizeof(struct netstat_24), &sstat_24.net, justcopy}, {sizeof(struct intfstat_24), &sstat_24.intf, justcopy}, {sizeof(struct dskstat_24), &sstat_24.dsk, justcopy}, {sizeof(struct nfsstat_24), &sstat_24.nfs, justcopy}, {sizeof(struct contstat_24), &sstat_24.cfs, justcopy}, {sizeof(struct wwwstat_24), &sstat_24.www, justcopy}, {0, &sstat_24.psi, NULL}, {0, &sstat_24.gpu, NULL}, {0, &sstat_24.ifb, NULL}, {0, NULL, NULL}, {0, NULL, NULL}, {0, NULL, NULL}, {sizeof(struct gen_24), STROFFSET(&tstat_24.gen, &tstat_24), justcopy}, {sizeof(struct cpu_24), STROFFSET(&tstat_24.cpu, &tstat_24), justcopy}, {sizeof(struct dsk_24), STROFFSET(&tstat_24.dsk, &tstat_24), justcopy}, {sizeof(struct mem_24), STROFFSET(&tstat_24.mem, &tstat_24), justcopy}, {sizeof(struct net_24), STROFFSET(&tstat_24.net, &tstat_24), justcopy}, {sizeof(struct gpu_24), STROFFSET(&tstat_24.gpu, &tstat_24), justcopy}, {0, 0, NULL}, {0, 0, NULL}, {0, 0, NULL}, {0, 0, NULL}, {0, 0, NULL}, }, {SETVERSION(2,5), // 2.4 --> 2.5 sizeof(struct sstat_25), &sstat_25, sizeof(struct tstat_25), NULL, 0, NULL, NULL, {sizeof(struct cpustat_25), &sstat_25.cpu, justcopy}, {sizeof(struct memstat_25), &sstat_25.mem, justcopy}, {sizeof(struct netstat_25), &sstat_25.net, justcopy}, {sizeof(struct intfstat_25), &sstat_25.intf, justcopy}, {sizeof(struct dskstat_25), &sstat_25.dsk, justcopy}, {sizeof(struct nfsstat_25), &sstat_25.nfs, justcopy}, {sizeof(struct contstat_25), &sstat_25.cfs, justcopy}, {sizeof(struct wwwstat_25), &sstat_25.www, justcopy}, {sizeof(struct pressure_25), &sstat_25.psi, justcopy}, {sizeof(struct gpustat_25), &sstat_25.gpu, justcopy}, {sizeof(struct ifbstat_25), &sstat_25.ifb, justcopy}, {0, NULL, NULL}, {0, NULL, NULL}, {0, NULL, NULL}, {sizeof(struct gen_25), STROFFSET(&tstat_25.gen, &tstat_25), justcopy}, {sizeof(struct cpu_25), STROFFSET(&tstat_25.cpu, &tstat_25), justcopy}, {sizeof(struct dsk_25), STROFFSET(&tstat_25.dsk, &tstat_25), justcopy}, {sizeof(struct mem_25), STROFFSET(&tstat_25.mem, &tstat_25), justcopy}, {sizeof(struct net_25), STROFFSET(&tstat_25.net, &tstat_25), justcopy}, {sizeof(struct gpu_25), STROFFSET(&tstat_25.gpu, &tstat_25), justcopy}, {0, 0, NULL}, {0, 0, NULL}, {0, 0, NULL}, {0, 0, NULL}, {0, 0, NULL}, }, {SETVERSION(2,6), // 2.5 --> 2.6 sizeof(struct sstat_26), &sstat_26, sizeof(struct tstat_26), NULL, 0, NULL, NULL, {sizeof(struct cpustat_26), &sstat_26.cpu, justcopy}, {sizeof(struct memstat_26), &sstat_26.mem, justcopy}, {sizeof(struct netstat_26), &sstat_26.net, justcopy}, {sizeof(struct intfstat_26), &sstat_26.intf, justcopy}, {sizeof(struct dskstat_26), &sstat_26.dsk, justcopy}, {sizeof(struct nfsstat_26), &sstat_26.nfs, justcopy}, {sizeof(struct contstat_26), &sstat_26.cfs, justcopy}, {sizeof(struct wwwstat_26), &sstat_26.www, justcopy}, {sizeof(struct pressure_26), &sstat_26.psi, justcopy}, {sizeof(struct gpustat_26), &sstat_26.gpu, justcopy}, {sizeof(struct ifbstat_26), &sstat_26.ifb, justcopy}, {0, NULL, NULL}, {0, NULL, NULL}, {0, NULL, NULL}, {sizeof(struct gen_26), STROFFSET(&tstat_26.gen, &tstat_26), justcopy}, {sizeof(struct cpu_26), STROFFSET(&tstat_26.cpu, &tstat_26), tcpu_to_26}, {sizeof(struct dsk_26), STROFFSET(&tstat_26.dsk, &tstat_26), justcopy}, {sizeof(struct mem_26), STROFFSET(&tstat_26.mem, &tstat_26), tmem_to_26}, {sizeof(struct net_26), STROFFSET(&tstat_26.net, &tstat_26), justcopy}, {sizeof(struct gpu_26), STROFFSET(&tstat_26.gpu, &tstat_26), justcopy}, {0, 0, NULL}, {0, 0, NULL}, {0, 0, NULL}, {0, 0, NULL}, {0, 0, NULL}, }, {SETVERSION(2,7), // 2.6 --> 2.7 sizeof(struct sstat_27), &sstat_27, sizeof(struct tstat_27), NULL, 0, NULL, NULL, {sizeof(struct cpustat_27), &sstat_27.cpu, scpu_to_27}, {sizeof(struct memstat_27), &sstat_27.mem, smem_to_27}, {sizeof(struct netstat_27), &sstat_27.net, justcopy}, {sizeof(struct intfstat_27), &sstat_27.intf, justcopy}, {sizeof(struct dskstat_27), &sstat_27.dsk, sdsk_to_27}, {sizeof(struct nfsstat_27), &sstat_27.nfs, justcopy}, {sizeof(struct contstat_27), &sstat_27.cfs, justcopy}, {sizeof(struct wwwstat_27), &sstat_27.www, justcopy}, {sizeof(struct pressure_27), &sstat_27.psi, justcopy}, {sizeof(struct gpustat_27), &sstat_27.gpu, justcopy}, {sizeof(struct ifbstat_27), &sstat_27.ifb, justcopy}, {0, &sstat_27.memnuma, NULL}, {0, &sstat_27.cpunuma, NULL}, {0, NULL, NULL}, {sizeof(struct gen_27), STROFFSET(&tstat_27.gen, &tstat_27), justcopy}, {sizeof(struct cpu_27), STROFFSET(&tstat_27.cpu, &tstat_27), justcopy}, {sizeof(struct dsk_27), STROFFSET(&tstat_27.dsk, &tstat_27), justcopy}, {sizeof(struct mem_27), STROFFSET(&tstat_27.mem, &tstat_27), justcopy}, {sizeof(struct net_27), STROFFSET(&tstat_27.net, &tstat_27), justcopy}, {sizeof(struct gpu_27), STROFFSET(&tstat_27.gpu, &tstat_27), justcopy}, {0, 0, NULL}, {0, 0, NULL}, {0, 0, NULL}, {0, 0, NULL}, {0, 0, NULL}, }, {SETVERSION(2,8), // 2.7 --> 2.8 sizeof(struct sstat_28), &sstat_28, sizeof(struct tstat_28), NULL, 0, NULL, NULL, {sizeof(struct cpustat_28), &sstat_28.cpu, justcopy}, {sizeof(struct memstat_28), &sstat_28.mem, smem_to_28}, {sizeof(struct netstat_28), &sstat_28.net, snet_to_28}, {sizeof(struct intfstat_28), &sstat_28.intf, justcopy}, {sizeof(struct dskstat_28), &sstat_28.dsk, sdsk_to_28}, {sizeof(struct nfsstat_28), &sstat_28.nfs, justcopy}, {sizeof(struct contstat_28), &sstat_28.cfs, justcopy}, {sizeof(struct wwwstat_28), &sstat_28.www, justcopy}, {sizeof(struct pressure_28), &sstat_28.psi, justcopy}, {sizeof(struct gpustat_28), &sstat_28.gpu, justcopy}, {sizeof(struct ifbstat_28), &sstat_28.ifb, justcopy}, {sizeof(struct memnuma_28), &sstat_28.memnuma, smnu_to_28}, {sizeof(struct cpunuma_28), &sstat_28.cpunuma, scnu_to_28}, {0, &sstat_28.llc, NULL}, {sizeof(struct gen_28), STROFFSET(&tstat_28.gen, &tstat_28), justcopy}, {sizeof(struct cpu_28), STROFFSET(&tstat_28.cpu, &tstat_28), tcpu_to_28}, {sizeof(struct dsk_28), STROFFSET(&tstat_28.dsk, &tstat_28), justcopy}, {sizeof(struct mem_28), STROFFSET(&tstat_28.mem, &tstat_28), tmem_to_28}, {sizeof(struct net_28), STROFFSET(&tstat_28.net, &tstat_28), justcopy}, {sizeof(struct gpu_28), STROFFSET(&tstat_28.gpu, &tstat_28), justcopy}, {0, 0, NULL}, {0, 0, NULL}, {0, 0, NULL}, {0, 0, NULL}, {0, 0, NULL}, }, {SETVERSION(2,9), // 2.8 --> 2.9 sizeof(struct sstat_29), &sstat_29, sizeof(struct tstat_29), NULL, 0, NULL, NULL, {sizeof(struct cpustat_29), &sstat_29.cpu, justcopy}, {sizeof(struct memstat_29), &sstat_29.mem, justcopy}, {sizeof(struct netstat_29), &sstat_29.net, justcopy}, {sizeof(struct intfstat_29), &sstat_29.intf, justcopy}, {sizeof(struct dskstat_29), &sstat_29.dsk, justcopy}, {sizeof(struct nfsstat_29), &sstat_29.nfs, justcopy}, {sizeof(struct contstat_29), &sstat_29.cfs, justcopy}, {sizeof(struct wwwstat_29), &sstat_29.www, justcopy}, {sizeof(struct pressure_29), &sstat_29.psi, justcopy}, {sizeof(struct gpustat_29), &sstat_29.gpu, justcopy}, {sizeof(struct ifbstat_29), &sstat_29.ifb, justcopy}, {sizeof(struct memnuma_29), &sstat_29.memnuma, justcopy}, {sizeof(struct cpunuma_29), &sstat_29.cpunuma, justcopy}, {sizeof(struct llcstat_29), &sstat_29.llc, justcopy}, {sizeof(struct gen_29), STROFFSET(&tstat_29.gen, &tstat_29), justcopy}, {sizeof(struct cpu_29), STROFFSET(&tstat_29.cpu, &tstat_29), justcopy}, {sizeof(struct dsk_29), STROFFSET(&tstat_29.dsk, &tstat_29), justcopy}, {sizeof(struct mem_29), STROFFSET(&tstat_29.mem, &tstat_29), justcopy}, {sizeof(struct net_29), STROFFSET(&tstat_29.net, &tstat_29), justcopy}, {sizeof(struct gpu_29), STROFFSET(&tstat_29.gpu, &tstat_29), justcopy}, {0, 0, NULL}, {0, 0, NULL}, {0, 0, NULL}, {0, 0, NULL}, {0, 0, NULL}, }, {SETVERSION(2,10), // 2.9 --> 2.10 sizeof(struct sstat_210), &sstat_210, sizeof(struct tstat_210), NULL, 0, NULL, NULL, {sizeof(struct cpustat_210), &sstat_210.cpu, justcopy}, {sizeof(struct memstat_210), &sstat_210.mem, justcopy}, {sizeof(struct netstat_210), &sstat_210.net, justcopy}, {sizeof(struct intfstat_210), &sstat_210.intf, justcopy}, {sizeof(struct dskstat_210), &sstat_210.dsk, justcopy}, {sizeof(struct nfsstat_210), &sstat_210.nfs, justcopy}, {sizeof(struct contstat_210), &sstat_210.cfs, justcopy}, {sizeof(struct wwwstat_210), &sstat_210.www, justcopy}, {sizeof(struct pressure_210), &sstat_210.psi, justcopy}, {sizeof(struct gpustat_210), &sstat_210.gpu, justcopy}, {sizeof(struct ifbstat_210), &sstat_210.ifb, justcopy}, {sizeof(struct memnuma_210), &sstat_210.memnuma, justcopy}, {sizeof(struct cpunuma_210), &sstat_210.cpunuma, justcopy}, {sizeof(struct llcstat_210), &sstat_210.llc, sllc_to_210}, {sizeof(struct gen_210), STROFFSET(&tstat_210.gen, &tstat_210), tgen_to_210}, {sizeof(struct cpu_210), STROFFSET(&tstat_210.cpu, &tstat_210), justcopy}, {sizeof(struct dsk_210), STROFFSET(&tstat_210.dsk, &tstat_210), justcopy}, {sizeof(struct mem_210), STROFFSET(&tstat_210.mem, &tstat_210), justcopy}, {sizeof(struct net_210), STROFFSET(&tstat_210.net, &tstat_210), justcopy}, {sizeof(struct gpu_210), STROFFSET(&tstat_210.gpu, &tstat_210), justcopy}, {0, 0, NULL}, {0, 0, NULL}, {0, 0, NULL}, {0, 0, NULL}, {0, 0, NULL}, }, {SETVERSION(2,11), // 2.10 --> 2.11 sizeof(struct sstat_211), &sstat_211, sizeof(struct tstat_211), NULL, sizeof(struct cstat_211), &cstat_211, NULL, {sizeof(struct cpustat_211), &sstat_211.cpu, justcopy}, {sizeof(struct memstat_211), &sstat_211.mem, justcopy}, {sizeof(struct netstat_211), &sstat_211.net, justcopy}, {sizeof(struct intfstat_211), &sstat_211.intf, justcopy}, {sizeof(struct dskstat_211), &sstat_211.dsk, justcopy}, {sizeof(struct nfsstat_211), &sstat_211.nfs, justcopy}, {sizeof(struct contstat_211), &sstat_211.cfs, justcopy}, {sizeof(struct wwwstat_211), &sstat_211.www, justcopy}, {sizeof(struct pressure_211), &sstat_211.psi, justcopy}, {sizeof(struct gpustat_211), &sstat_211.gpu, justcopy}, {sizeof(struct ifbstat_211), &sstat_211.ifb, justcopy}, {sizeof(struct memnuma_211), &sstat_211.memnuma, smnu_to_211}, {sizeof(struct cpunuma_211), &sstat_211.cpunuma, scnu_to_211}, {sizeof(struct llcstat_211), &sstat_211.llc, justcopy}, {sizeof(struct gen_211), STROFFSET(&tstat_211.gen, &tstat_211), tgen_to_211}, {sizeof(struct cpu_211), STROFFSET(&tstat_211.cpu, &tstat_211), tcpu_to_211}, {sizeof(struct dsk_211), STROFFSET(&tstat_211.dsk, &tstat_211), justcopy}, {sizeof(struct mem_211), STROFFSET(&tstat_211.mem, &tstat_211), tmem_to_211}, {sizeof(struct net_211), STROFFSET(&tstat_211.net, &tstat_211), justcopy}, {sizeof(struct gpu_211), STROFFSET(&tstat_211.gpu, &tstat_211), justcopy}, {sizeof(struct cggen_211), STROFFSET(&cstat_211.gen, &cstat_211), justcopy}, {sizeof(struct cgconf_211), STROFFSET(&cstat_211.conf, &cstat_211), justcopy}, {sizeof(struct cgcpu_211), STROFFSET(&cstat_211.cpu, &cstat_211), justcopy}, {sizeof(struct cgmem_211), STROFFSET(&cstat_211.mem, &cstat_211), justcopy}, {sizeof(struct cgdsk_211), STROFFSET(&cstat_211.dsk, &cstat_211), justcopy}, }, {SETVERSION(2,12), // 2.11 --> 2.12 sizeof(struct sstat_212), &sstat_212, sizeof(struct tstat_212), NULL, sizeof(struct cstat_212), &cstat_212, NULL, {sizeof(struct cpustat_212), &sstat_212.cpu, justcopy}, {sizeof(struct memstat_212), &sstat_212.mem, justcopy}, {sizeof(struct netstat_212), &sstat_212.net, justcopy}, {sizeof(struct intfstat_212), &sstat_212.intf, justcopy}, {sizeof(struct dskstat_212), &sstat_212.dsk, justcopy}, {sizeof(struct nfsstat_212), &sstat_212.nfs, justcopy}, {sizeof(struct contstat_212), &sstat_212.cfs, justcopy}, {sizeof(struct wwwstat_212), &sstat_212.www, justcopy}, {sizeof(struct pressure_212), &sstat_212.psi, justcopy}, {sizeof(struct gpustat_212), &sstat_212.gpu, justcopy}, {sizeof(struct ifbstat_212), &sstat_212.ifb, justcopy}, {sizeof(struct memnuma_212), &sstat_212.memnuma, justcopy}, {sizeof(struct cpunuma_212), &sstat_212.cpunuma, justcopy}, {sizeof(struct llcstat_212), &sstat_212.llc, justcopy}, {sizeof(struct gen_212), STROFFSET(&tstat_212.gen, &tstat_212), justcopy}, {sizeof(struct cpu_212), STROFFSET(&tstat_212.cpu, &tstat_212), justcopy}, {sizeof(struct dsk_212), STROFFSET(&tstat_212.dsk, &tstat_212), justcopy}, {sizeof(struct mem_212), STROFFSET(&tstat_212.mem, &tstat_212), justcopy}, {sizeof(struct net_212), STROFFSET(&tstat_212.net, &tstat_212), justcopy}, {sizeof(struct gpu_212), STROFFSET(&tstat_212.gpu, &tstat_212), justcopy}, {sizeof(struct cggen_212), STROFFSET(&cstat_212.gen, &cstat_212), justcopy}, {sizeof(struct cgconf_212), STROFFSET(&cstat_212.conf, &cstat_212), justcopy}, {sizeof(struct cgcpu_212), STROFFSET(&cstat_212.cpu, &cstat_212), justcopy}, {sizeof(struct cgmem_212), STROFFSET(&cstat_212.mem, &cstat_212), justcopy}, {sizeof(struct cgdsk_212), STROFFSET(&cstat_212.dsk, &cstat_212), justcopy}, }, }; int numconvs = sizeof convs / sizeof(struct convertall); /////////////////////////////////////////////////////////////// // End of conversion functions /////////////////////////////////////////////////////////////// // function prototypes // static int openin(char *); static void readin(int, void *, int); static int openout(char *); static void writeout(int, void *, int); static void writesamp(int, struct rawrecord *, void *, int, void *, int, void *, int, pid_t *, int); static void copy_file(int, int); static void convert_samples(int, int, struct rawheader *, int, int, int); static void do_sconvert(struct sconvstruct *, struct sconvstruct *); static void do_tconvert(void *, void *, struct tconvstruct *, struct tconvstruct *); static void do_cconvert(void *, void *, struct cconvstruct *, struct cconvstruct *); static int getrawsstat(int, struct sstat *, unsigned long, int); static int getrawtstat(int, struct tstat *, unsigned long, int, int); static struct cstat ** getrawcstat(int, struct cstat *, unsigned long, int, int); static void testcompval(int, char *, char *); int main(int argc, char *argv[]) { int ifd, ofd; struct rawheader irh, orh; int i, versionix, targetix = -1, cgroupsv2 = 0; int c, major, minor, targetvers; char *infile, *outfile; // verify the command line arguments: // optional flags and mandatory input and output filename // if (argc < 2) prusage(argv[0]); while ((c = getopt(argc, argv, "?t:")) != EOF) { switch (c) { case '?': // usage wanted ? prusage(argv[0]); break; case 't': // target version if ( sscanf(optarg, "%d.%d", &major, &minor) != 2) { fprintf(stderr, "target version format: major.minor\n"); prusage(argv[0]); } targetvers = SETVERSION(major, minor); // search for target version in conversion table // for (i=0, targetix=-1; i < numconvs; i++) { if (targetvers == convs[i].version) { targetix = i; break; } } if (targetix == -1) // incorrect target version? { fprintf(stderr, "target version incorrect!"); prusage(argv[0]); } break; default: prusage(argv[0]); break; } } if (optind >= argc) prusage(argv[0]); infile = argv[optind++]; // determine target version (default: latest version) // if (targetix == -1) // no specific target version requested? targetix = numconvs - 1; // open the input file and verify magic number // if ( (ifd = openin(infile)) == -1) { prusage(argv[0]); exit(2); } readin(ifd, &irh, sizeof irh); if (irh.magic != MYMAGIC) { fprintf(stderr, "File %s does not contain atop/atopsar data " "(wrong magic number)\n", infile); exit(3); } printf("Version of %s: %d.%d\n", infile, (irh.aversion >> 8) & 0x7f, irh.aversion & 0xff); if (irh.rawheadlen != sizeof(struct rawheader) || irh.rawreclen != sizeof(struct rawrecord) ) { fprintf(stderr, "File %s created with atop compiled " "for other CPU architecture\n", infile); exit(3); } // search for version of input file in conversion table // for (i=0, versionix=-1; i < numconvs; i++) { if (convs[i].version == (irh.aversion & 0x7fff)) { versionix = i; break; } } if (versionix == -1) { fprintf(stderr, "This version is not supported for conversion!\n"); exit(11); } if (versionix > targetix) { fprintf(stderr, "Downgrading of version is not supported!\n"); exit(11); } // various consistency checks for system stats, task stats and // (in case of a version > 2.11) cgroup stats // if ( irh.sstatlen != convs[versionix].sstatlen || irh.tstatlen != convs[versionix].tstatlen ) { fprintf(stderr, "File %s contains unexpected internal structures\n", infile); fprintf(stderr, "sstat: %d/%d, tstat: %d/%d\n", irh.sstatlen, convs[versionix].sstatlen, irh.tstatlen, convs[versionix].tstatlen); exit(11); } if (convs[versionix].version >= SETVERSION(2,11) && irh.supportflags & CGROUPV2 ) cgroupsv2 = 1; if (cgroupsv2 && irh.cstatlen != convs[versionix].cstatlen) { fprintf(stderr, "File %s contains unexpected internal structures\n", infile); fprintf(stderr, "cstat: %d/%d\n", irh.cstatlen, convs[versionix].cstatlen); exit(11); } // handle the output file // if (optind >= argc) exit(0); outfile = argv[optind++]; if (strcmp(infile, outfile) == 0) { fprintf(stderr, "input file and output file should not be identical!\n"); exit(12); } // open the output file // if ( (ofd = openout(outfile)) == -1) { prusage(argv[0]); exit(4); } // write raw header to output file // orh = irh; orh.aversion = convs[targetix].version | 0x8000; orh.sstatlen = convs[targetix].sstatlen; orh.cstatlen = convs[targetix].cstatlen; orh.tstatlen = convs[targetix].tstatlen; if (orh.pidwidth == 0) // no pid width known in old raw log? orh.pidwidth = getpidwidth(); writeout(ofd, &orh, sizeof orh); printf("Version of %s: %d.%d\n", outfile, (orh.aversion >> 8) & 0x7f, orh.aversion & 0xff); // copy and convert every sample, unless the version of the // input file is identical to the target version (then just copy) // if (versionix < targetix) convert_samples(ifd, ofd, &irh, versionix, targetix, cgroupsv2); else copy_file(ifd, ofd); close(ifd); close(ofd); return 0; } // // Function that reads the input file sample-by-sample, // converts it to an output sample and writes that to the output file // static void convert_samples(int ifd, int ofd, struct rawheader *irh, int ivix, int ovix, int cgroupsv2) { struct rawrecord irr, orr; int i, t, c, ctotlen; count_t count = 0; pid_t *cgpidlist = NULL; while ( read(ifd, &irr, irh->rawreclen) == irh->rawreclen) { count++; // read compressed system-level statistics and decompress // if ( !getrawsstat(ifd, convs[ivix].sstat, convs[ivix].sstatlen, irr.scomplen) ) exit(7); // read compressed process-level statistics and decompress // convs[ivix].tstat = malloc(convs[ivix].tstatlen * irr.ndeviat); ptrverify(convs[ivix].tstat, "Malloc failed for %d stored tasks\n", irr.ndeviat); if ( !getrawtstat(ifd, convs[ivix].tstat, convs[ivix].tstatlen * irr.ndeviat, irr.pcomplen, irr.ndeviat) ) exit(7); // read cgroups information // if (cgroupsv2) { // read compressed cgroups-level statistics and decompress // convs[ivix].cstat = malloc(irr.coriglen); ptrverify(convs[ivix].cstat, "Malloc failed for cgroups stats\n"); if ( !(convs[ivix].cslist = getrawcstat(ifd, convs[ivix].cstat, irr.coriglen, irr.ccomplen, irr.ncgroups)) ) exit(7); // read compressed pid list for which no conversion is needed // (will not be decompressed and transparantly // written to the output file) // cgpidlist = malloc(irr.icomplen); ptrverify(cgpidlist, "Malloc failed for compressed pidlist\n"); if ( read(ifd, cgpidlist, irr.icomplen) < irr.icomplen) { fprintf(stderr, "Failed to read %d bytes for pidlist\n", irr.icomplen); exit(7); } } // STEP-BY-STEP CONVERSION FROM OLD VERSION TO NEW VERSION // for (i=ivix; i < ovix; i++) { // convert system-level statistics to newer version // memset(convs[i+1].sstat, 0, convs[i+1].sstatlen); do_sconvert(&(convs[i].scpu), &(convs[i+1].scpu)); do_sconvert(&(convs[i].smem), &(convs[i+1].smem)); do_sconvert(&(convs[i].snet), &(convs[i+1].snet)); do_sconvert(&(convs[i].sintf), &(convs[i+1].sintf)); do_sconvert(&(convs[i].sdsk), &(convs[i+1].sdsk)); do_sconvert(&(convs[i].snfs), &(convs[i+1].snfs)); do_sconvert(&(convs[i].scfs), &(convs[i+1].scfs)); do_sconvert(&(convs[i].spsi), &(convs[i+1].spsi)); do_sconvert(&(convs[i].sgpu), &(convs[i+1].sgpu)); do_sconvert(&(convs[i].sifb), &(convs[i+1].sifb)); do_sconvert(&(convs[i].smnum), &(convs[i+1].smnum)); do_sconvert(&(convs[i].scnum), &(convs[i+1].scnum)); do_sconvert(&(convs[i].swww), &(convs[i+1].swww)); // convert process-level statistics to newer version // convs[i+1].tstat = malloc(convs[i+1].tstatlen * irr.ndeviat); ptrverify(convs[i+1].tstat, "Malloc failed for %d stored tasks\n", irr.ndeviat); memset(convs[i+1].tstat, 0, convs[i+1].tstatlen * irr.ndeviat); for (t=0; t < irr.ndeviat; t++) // for every task { do_tconvert( convs[i].tstat +(t*convs[i].tstatlen), convs[i+1].tstat+(t*convs[i+1].tstatlen), &(convs[i].tgen), &(convs[i+1].tgen)); do_tconvert( convs[i].tstat +(t*convs[i].tstatlen), convs[i+1].tstat+(t*convs[i+1].tstatlen), &(convs[i].tcpu), &(convs[i+1].tcpu)); do_tconvert( convs[i].tstat +(t*convs[i].tstatlen), convs[i+1].tstat+(t*convs[i+1].tstatlen), &(convs[i].tdsk), &(convs[i+1].tdsk)); do_tconvert( convs[i].tstat +(t*convs[i].tstatlen), convs[i+1].tstat+(t*convs[i+1].tstatlen), &(convs[i].tmem), &(convs[i+1].tmem)); do_tconvert( convs[i].tstat +(t*convs[i].tstatlen), convs[i+1].tstat+(t*convs[i+1].tstatlen), &(convs[i].tnet), &(convs[i+1].tnet)); do_tconvert( convs[i].tstat +(t*convs[i].tstatlen), convs[i+1].tstat+(t*convs[i+1].tstatlen), &(convs[i].tgpu), &(convs[i+1].tgpu)); } free(convs[i].tstat); // convert cgroups-level statistics to newer version // if (cgroupsv2) { // dynamically allocate pointer list for cstats structs // convs[i+1].cstat = NULL; // no total space for cstats convs[i+1].cslist = malloc(irr.ncgroups * sizeof(struct cstat *)); ptrverify(convs[i+1].cslist, "Malloc failed for cstat pointer list\n"); for (c=0; c < irr.ncgroups; c++) // for every cgroup { // calculate rounded length for target struct // int rcstatlen = convs[i+1].cstatlen + convs[i].cslist[c]->gen.namelen + 1; if (rcstatlen & 0x7) // length is not 64-bit multiple? rcstatlen = ((rcstatlen >> 3) + 1) << 3; // round up // dynamically allocate target cstat struct // convs[i+1].cslist[c] = calloc(1, rcstatlen); ptrverify(convs[i+1].cslist[c], "Malloc single cstat failed\n"); // convert all structs inside cstat // do_cconvert(convs[i].cslist[c], convs[i+1].cslist[c], &(convs[i].cggen), &(convs[i+1].cggen)); do_cconvert(convs[i].cslist[c], convs[i+1].cslist[c], &(convs[i].cgconf), &(convs[i+1].cgconf)); do_cconvert(convs[i].cslist[c], convs[i+1].cslist[c], &(convs[i].cgcpu), &(convs[i+1].cgcpu)); do_cconvert(convs[i].cslist[c], convs[i+1].cslist[c], &(convs[i].cgmem), &(convs[i+1].cgmem)); do_cconvert(convs[i].cslist[c], convs[i+1].cslist[c], &(convs[i].cgdsk), &(convs[i+1].cgdsk)); // copy cgroup name string and correct total struct length // strcpy(convs[i+1].cslist[c]->cgname, convs[i].cslist[c]->cgname); convs[i+1].cslist[c]->gen.structlen = rcstatlen; } if (convs[i].cstat) { // free entire area at once // (only for area read from raw log) // free(convs[i].cstat); } else { // free every single cstat from pointer list // for (c=0; c < irr.ncgroups; c++) free(convs[i].cslist[c]); } free(convs[i].cslist); } // in version 2.11 incompatible cgroups v2 metrics // are implemented and earlier metrics will be lost // if (convs[i].version == SETVERSION(2,10)) irr.flags &= ~RRCGRSTAT; } // prepare target cstat structs before writing // ctotlen = 0; if (cgroupsv2) { int structlen; char *cp; // glue all single cstat struct together into one area // - calculate area length // for (c=0; c < irr.ncgroups; c++) ctotlen += convs[ovix].cslist[c]->gen.structlen; // - dynamically allocate area and copy all cstats // convs[ovix].cstat = cp = malloc(ctotlen); ptrverify(convs[ovix].cstat, "Malloc failed for final cstat\n"); for (c=0; c < irr.ncgroups; c++, cp += structlen) { structlen = convs[ovix].cslist[c]->gen.structlen; memcpy(cp, convs[ovix].cslist[c], structlen); free(convs[ovix].cslist[c]); } } // write new sample to output file // orr = irr; writesamp(ofd, &orr, convs[ovix].sstat, convs[ovix].sstatlen, convs[ovix].tstat, convs[ovix].tstatlen, convs[ovix].cstat, ctotlen, cgpidlist, cgroupsv2); free(convs[ovix].tstat); // cleanup target tstats if (cgroupsv2) { free(convs[ovix].cstat); // cleanup target cstats free(convs[ovix].cslist); // cleanup target pointer list free(cgpidlist); // cleanup compressed pid list } } printf("Samples converted: %llu\n", count); } // // Function that calls the appropriate function to convert a struct // from the sstat structure // static void do_sconvert(struct sconvstruct *cur, struct sconvstruct *next) { if (next->structconv) { (*(next->structconv))(cur->structptr, next->structptr, cur->structsize, next->structsize); } } // // Function that calls the appropriate function to convert a struct // from one of the tstat structures // static void do_tconvert(void *curtstat, void *nexttstat, struct tconvstruct *cur, struct tconvstruct *next) { if (next->structconv) { (*(next->structconv))(curtstat + cur->structoffset, nexttstat + next->structoffset, cur->structsize, next->structsize); } } // // Function that calls the appropriate function to convert a struct // from the cstat structure // static void do_cconvert(void *curcstat, void *nextcstat, struct cconvstruct *cur, struct cconvstruct *next) { if (next->structconv) { (*(next->structconv))(curcstat + cur->structoffset, nextcstat + next->structoffset, cur->structsize, next->structsize); } } // // Function to copy input file transparently to output file // static void copy_file(int ifd, int ofd) { unsigned char buf[64*1024]; int nr, nw; (void) lseek(ifd, 0, SEEK_SET); (void) lseek(ofd, 0, SEEK_SET); while ( (nr = read(ifd, buf, sizeof buf)) > 0) { if ( (nw = write(ofd, buf, nr)) < nr) { if (nw == -1) { perror("write output file"); exit(42); } else { fprintf(stderr, "Output file saturated\n"); exit(42); } } } if (nr == -1) { perror("read input file"); exit(42); } else { printf("Raw file copied (version already up-to-date)\n"); } } // // Function that opens an existing raw file and // verifies the magic number // static int openin(char *infile) { int rawfd; /* ** open raw file for reading */ if ( (rawfd = open(infile, O_RDONLY)) == -1) { fprintf(stderr, "%s - ", infile); perror("open for reading"); return -1; } return rawfd; } // // Function that reads a chunk of bytes from // the input raw file // static void readin(int rawfd, void *buf, int size) { /* ** read the requested chunk and verify */ if ( read(rawfd, buf, size) < size) { fprintf(stderr, "can not read raw file\n"); close(rawfd); exit(9); } } // // Function that creates a raw log for output // static int openout(char *outfile) { int rawfd; /* ** create new output file */ if ( (rawfd = creat(outfile, 0666)) == -1) { fprintf(stderr, "%s - ", outfile); perror("create raw output file"); return -1; } return rawfd; } // // Function that reads a chunk of bytes from // the input raw file // static void writeout(int rawfd, void *buf, int size) { /* ** write the provided chunk and verify */ if ( write(rawfd, buf, size) < size) { fprintf(stderr, "can not write raw file\n"); close(rawfd); exit(10); } } // // Function that shows the usage message // void prusage(char *name) { fprintf(stderr, "Usage: %s [-t version] rawinput [rawoutput]\n", name); fprintf(stderr, "\t-t version target version (default: %d.%d) for output\n", (convs[numconvs-1].version >> 8) & 0x7f, convs[numconvs-1].version & 0x7f); exit(1); } // // Function to read the system-level statistics from the current offset // static int getrawsstat(int rawfd, struct sstat *sp, unsigned long uncomplen, int complen) { Byte *compbuf; unsigned long expected_uncomplen = uncomplen; int rv; compbuf = malloc(complen); ptrverify(compbuf, "Malloc failed for reading compressed sysstats\n"); if ( read(rawfd, compbuf, complen) < complen) { free(compbuf); fprintf(stderr, "Failed to read %d bytes for system\n", complen); return 0; } rv = uncompress((Byte *)sp, &uncomplen, compbuf, complen); testcompval(rv, "sstat", "uncompress"); free(compbuf); if (uncomplen != expected_uncomplen) { fprintf(stderr, "Unexpected length of uncompressed sstat\n"); exit(3); } return 1; } // // Function to read the process-level statistics from the current offset // static int getrawtstat(int rawfd, struct tstat *pp, unsigned long uncomplen, int complen, int ndeviat) { Byte *compbuf; int rv; unsigned long expected_uncomplen = uncomplen; compbuf = malloc(complen); ptrverify(compbuf, "Malloc failed for reading compressed procstats\n"); if ( read(rawfd, compbuf, complen) < complen) { free(compbuf); fprintf(stderr, "Failed to read %d bytes for tasks\n", complen); return 0; } rv = uncompress((Byte *)pp, &uncomplen, compbuf, complen); testcompval(rv, "tstat", "uncompress"); free(compbuf); if (uncomplen != expected_uncomplen) { fprintf(stderr, "Unexpected length of uncompressed tstat\n"); exit(3); } return 1; } // // Function to read the cgroups-level statistics from the current offset // as one chunk, decompress and build a list of pointers to the single // cstat structs (to be returned) // static struct cstat ** getrawcstat(int rawfd, struct cstat *cp, unsigned long uncomplen, int complen, int ncgroups) { Byte *compbuf; int rv, i; unsigned long expected_uncomplen = uncomplen; struct cstat **cslist; // read compressed chunk // compbuf = malloc(complen); ptrverify(compbuf, "Malloc failed for reading compressed cgroups stats\n"); if ( read(rawfd, compbuf, complen) < complen) { free(compbuf); fprintf(stderr, "Failed to read %d bytes for system\n", complen); return 0; } // decompress // rv = uncompress((Byte *)cp, &uncomplen, compbuf, complen); testcompval(rv, "cstat", "uncompress"); free(compbuf); if (uncomplen != expected_uncomplen) { fprintf(stderr, "Unexpected length of uncompressed cstat\n"); exit(3); } // build pointer list // cslist = malloc(ncgroups * sizeof(struct cstat *)); ptrverify(cslist, "Malloc failed for cstat pointer list\n"); for (i=0; i < ncgroups; i++, cp = (struct cstat *)((char *)cp + cp->gen.structlen)) *(cslist+i) = cp; return cslist; } // // Function that writes a new sample to the output file // static void writesamp(int ofd, struct rawrecord *rr, void *sstat, int sstatlen, void *tstat, int tstatlen, void *cstat, int cstattotlen, pid_t *cgpidlist, int cgroupsv2) { int rv; Byte scompbuf[sstatlen], *pcompbuf, *ccompbuf; unsigned long scomplen = sizeof scompbuf; unsigned long pcomplen = tstatlen * rr->ndeviat; unsigned long ccomplen = cstattotlen; struct stat filestat; /* ** compress system- and process-level statistics */ rv = compress(scompbuf, &scomplen, (Byte *)sstat, (unsigned long)sstatlen); testcompval(rv, "sstat", "compress"); pcompbuf = malloc(pcomplen); ptrverify(pcompbuf, "Malloc failed for compression buffer\n"); rv = compress(pcompbuf, &pcomplen, (Byte *)tstat, (unsigned long)pcomplen); testcompval(rv, "tstat", "compress"); rr->scomplen = scomplen; rr->pcomplen = pcomplen; /* ** compress cgroups statistics (conditional) */ if (cgroupsv2) { ccompbuf = malloc(ccomplen); ptrverify(ccompbuf, "Malloc failed for compression buffer\n"); rv = compress(ccompbuf, &ccomplen, (Byte *)cstat, (unsigned long)ccomplen); testcompval(rv, "cstat", "compress"); rr->ccomplen = ccomplen; rr->coriglen = cstattotlen; } else { rr->ccomplen = 0; rr->coriglen = 0; rr->icomplen = 0; } /* ** register current size of file in order to "roll back" ** writes that have been done while not *all* writes could ** succeed, e.g. when file system full */ (void) fstat(ofd, &filestat); /* ** write raw record to file */ if ( write(ofd, rr, sizeof *rr) != sizeof *rr) goto rollback_and_stop; /* ** write compressed system status structure to file */ if ( write(ofd, scompbuf, scomplen) != scomplen) goto rollback_and_stop; /* ** write compressed list of process status structures to file */ if ( write(ofd, pcompbuf, pcomplen) != pcomplen) goto rollback_and_stop; free(pcompbuf); /* ** write compressed list of cgroups status structures ** and compressed pid list to file */ if (cgroupsv2) { if ( write(ofd, ccompbuf, ccomplen) != ccomplen) goto rollback_and_stop; free(ccompbuf); if ( write(ofd, cgpidlist, rr->icomplen) != rr->icomplen) goto rollback_and_stop; } return; rollback_and_stop: (void) ftruncate(ofd, filestat.st_size); fprintf(stderr, "Write to output raw log failed!\n"); exit(9); } // // check success of (de)compression // static void testcompval(int rv, char *name, char *func) { switch (rv) { case Z_OK: case Z_STREAM_END: case Z_NEED_DICT: break; case Z_MEM_ERROR: fprintf(stderr, "%s %s: failed due to lack of memory\n", name, func); exit(7); case Z_BUF_ERROR: fprintf(stderr, "%s %s: failed due to lack of room in buffer\n", name, func); exit(7); case Z_DATA_ERROR: fprintf(stderr, "%s %s: failed due to corrupted/incomplete data\n", name, func); exit(7); default: fprintf(stderr, "%s %s: unexpected error %d\n", name, func, rv); exit(7); } } // // generic pointer verification after malloc // void ptrverify(const void *ptr, const char *errormsg, ...) { if (!ptr) { va_list args; va_start(args, errormsg); vfprintf(stderr, errormsg, args); va_end (args); exit(13); } } /* ** return maximum number of digits for PID/TID ** from the current system */ int getpidwidth(void) { FILE *fp; char linebuf[64]; int numdigits = 5; if ( (fp = fopen("/proc/sys/kernel/pid_max", "r")) != NULL) { if ( fgets(linebuf, sizeof(linebuf), fp) != NULL) { numdigits = strlen(linebuf) - 1; } fclose(fp); } return numdigits; } atop-2.12.1/atop.h0000644000203100020310000001514515064552761013175 0ustar gerlofgerlof/* ** ATOP - System & Process Monitor ** ** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as process-level. ** ** Include-file describing miscellaneous constants and function-prototypes. ** ================================================================ ** Author: Gerlof Langeveld ** E-mail: gerlof.langeveld@atoptool.nl ** Date: November 1996 ** LINUX-port: June 2000 ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. */ #ifndef __ATOP__ #define __ATOP__ #include #define EQ 0 #define SECONDSINDAY 86400 #define RAWNAMESZ 256 /* ** memory-size formatting possibilities */ #define BFORMAT 0 #define KBFORMAT 1 #define KBFORMAT_INT 2 #define MBFORMAT 3 #define MBFORMAT_INT 4 #define GBFORMAT 5 #define GBFORMAT_INT 6 #define TBFORMAT 7 #define TBFORMAT_INT 8 #define PBFORMAT 9 #define PBFORMAT_INT 10 #define EBFORMAT 11 #define EBFORMAT_INT 12 #define OVFORMAT 13 typedef long long count_t; struct tstat; struct devtstat; struct sstat; struct cgchainer; struct netpertask; /* ** miscellaneous flags */ #define RRBOOT 0x0001 #define RRLAST 0x0002 #define RRNETATOP 0x0004 #define RRNETATOPD 0x0008 #define RRACCTACTIVE 0x0010 #define RRIOSTAT 0x0020 #define RRCONTAINERSTAT 0x0040 #define RRGPUSTAT 0x0080 #define RRCGRSTAT 0x0100 #define MAXHANDLERS 10 struct handler { char (*handle_sample) (time_t, int, struct devtstat *, struct sstat *, struct cgchainer *, int, int, int, unsigned int, char); }; /* ** external values */ extern struct utsname utsname; extern int utsnodenamelen; extern time_t pretime; extern time_t curtime; extern unsigned long interval; extern unsigned long sampcnt; extern char screen; extern int fdinotify; extern pid_t twinpid; extern int linelen; extern char acctreason; extern char deviatonly; extern char usecolors; extern char threadview; extern char calcpss; extern char getwchan; extern char irawname[]; extern char orawname[]; extern char twindir[]; extern char rawreadflag; extern char connectnetatop; extern char idnamesuppress; extern char rmspaces; extern time_t begintime, endtime, cursortime; // epoch or time in day extern char flaglist[]; extern struct handler handlers[]; extern char displaymode; extern char barmono; extern int osrel; extern int osvers; extern int ossub; extern unsigned short hertz; extern unsigned int pagesize; extern unsigned int pidwidth; extern unsigned int nrgpus; extern int supportflags; extern int cpubadness; extern int membadness; extern int swpbadness; extern int dskbadness; extern int netbadness; extern int pagbadness; extern int almostcrit; /* ** bit-values for supportflags */ #define ACCTACTIVE 0x00000001 #define IOSTAT 0x00000004 #define NETATOP 0x00000010 #define NETATOPD 0x00000020 #define CONTAINERSTAT 0x00000040 #define GPUSTAT 0x00000080 #define CGROUPV2 0x00000100 #define NETATOPBPF 0x00001000 #define REALNUMA 0x00002000 #define ZSWAP 0x00004000 /* ** in rawlog file, the four least significant bits ** are moved to the per-sample flags and therefor dummy ** in the support flags of the general header */ #define RAWLOGNG (ACCTACTIVE|IOSTAT|NETATOP|NETATOPD) /* ** structure containing the start addresses of functions for visualization */ char generic_samp (time_t, int, struct devtstat *, struct sstat *, struct cgchainer *, int, int, int, unsigned int, char); void generic_error(const char *, ...); void generic_end (void); void generic_usage(void); /* ** miscellaneous prototypes */ int atopsar(int, char *[]); char *convtime(time_t, char *); char *convdate(time_t, char *); int getbranchtime(char *, time_t *); time_t normalize_epoch(time_t, long); char *val2valstr(count_t, char *, int, int, int); char *val2memstr(count_t, char *, int, int, int); char *val2cpustr(count_t, char *); char *val2Hzstr(count_t, char *); int val2elapstr(int, char *); char *uid2name(uid_t); char *gid2name(gid_t); void safe_strcpy(char *, const char *, size_t); int compcpu(const void *, const void *); int compdsk(const void *, const void *); int compmem(const void *, const void *); int compnet(const void *, const void *); int compgpu(const void *, const void *); int compusr(const void *, const void *); int compnam(const void *, const void *); int compcon(const void *, const void *); int cpucompar (const void *, const void *); int gpucompar (const void *, const void *); int diskcompar(const void *, const void *); int intfcompar(const void *, const void *); int ifbcompar(const void *, const void *); int nfsmcompar(const void *, const void *); int contcompar(const void *, const void *); int memnumacompar(const void *, const void *); int cpunumacompar(const void *, const void *); int llccompar(const void *, const void *); int rawread(void); char rawwrite (time_t, int, struct devtstat *, struct sstat *, struct cgchainer *, int, int, int, unsigned int, char); int numeric(char *); void getalarm(int); unsigned long long getboot(void); char *getstrvers(void); unsigned short getnumvers(void); void ptrverify(const void *, const char *, ...); void mcleanstop(int, const char *, ...); void cleanstop(int); int getpidwidth(void); void prusage(char *); int rootprivs(void); int droprootprivs(void); void regainrootprivs(void); FILE *fopen_tryroot(const char *, const char *); void netatop_ipopen(void); void netatop_probe(void); void netatop_signoff(void); void netatop_gettask(pid_t, char, struct tstat *); unsigned int netatop_exitstore(void); void netatop_exiterase(void); void netatop_exithash(char); void netatop_exitfind(unsigned long, struct tstat *, struct tstat *); void netatop_bpf_ipopen(void); void netatop_bpf_probe(void); void netatop_bpf_gettask(void); void netatop_bpf_exitfind(unsigned long, struct tstat *, struct tstat *); void set_oom_score_adj(void); int run_in_guest(void); void getusr1(int), getusr2(int); void do_pacctdir(char *, char *); void do_atopsarflags(char *, char *); int netlink_open(void); int netlink_recv(int, int); int getutsname(struct tstat *); void resetutsname(void); #endif atop-2.12.1/atophide.c0000644000203100020310000004666615064552761014036 0ustar gerlofgerlof/* ** ATOP - System & Process Monitor ** ** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as process-level. ** ** This program copies an input raw logfile to an output raw logfile, ** while offering the possibility to select a subset of the samples ** (begin time and/or end time) and to anonymize the samples ** (subsitute command names/arguments, host name, logical volume names, ** etcetera by place holders). ** ========================================================================== ** Author: Gerlof Langeveld ** E-mail: gerlof.langeveld@atoptool.nl ** Initial: July 2023 ** -------------------------------------------------------------------------- ** Copyright (C) 2023 Gerlof Langeveld ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ** -------------------------------------------------------------------------- */ #define _POSIX_C_SOURCE #define _XOPEN_SOURCE #define _GNU_SOURCE #define _DEFAULT_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "atop.h" #include "photosyst.h" #include "photoproc.h" #include "rawlog.h" // struct to register fakenames that are assigned // to the original names // struct standin { char *origname; char *fakename; struct standin *next; }; // function prototypes // static int openin(char *); static void readin(int, void *, int); static int openout(char *); static void writeout(int, void *, int); static void writesamp(int, struct rawrecord *, void *, int, void *, int, int, void *, int, void *, int); static int getrawsstat(int, struct sstat *, int); static int getrawtstat(int, struct tstat *, int, int); static void testcompval(int, char *); static void anonymize(struct sstat *, struct tstat *, int); static char *findstandin(struct standin **, unsigned long *, char *, char *); // command names that will not be anonymized (in alphabetical order) // static char *allowedcoms[] = { "^0anacron$", "^agetty$", "^anacron$", "^atd$", "^atop", "^auditd$", "^avahi-", "^awk$", "^basename$", "^bash$", "^bc$", "^bunzip2$", "^bzip2$", "^cat$", "^chmod$", "^chmod$", "^chown$", "^chromium", "^chronyc$", "^chronyd$", "^cp$", "^cpio$", "^crond$", "^csh$", "^cut$", "^date$", "^dbus", "^dd$", "^df$", "^diff$", "^dig$", "^dircolors$", "^dirname$", "^dnf$", "^echo$", "^expr$", "^file$", "^find$", "^firefox$", "^firewalld$", "^gawk$", "^git$", "^grep$", "^grepconf.sh$", "^gunzip$", "^gzip$", "^head$", "^head$", "^host$", "^hostname$", "^hostnamectl$", "^id$", "^ip$", "^iptables$", "^irqbalance$", "^kill$", "^ksh$", "^ldconfig$", "^less$", "^ln$", "^locale$", "^locate$", "^logger$", "^logrotate$", "^ls$", "^lsmd$", "^man$", "^make$", "^mcelog$", "^mkdate$", "^mkdir$", "^mktemp$", "^modprobe$", "^more$", "^mount$", "^mv$", "^netatop", "^NetworkManager$", "^nice$", "^nl$", "^oom_", "^pr$", "^ps$", "^pwd$", "^python$", "^python3$", "^qemu-kvm$", "^readlink$", "^rm$", "^rmdir$", "^rpcbind$", "^rpc.imapd$", "^rpm$", "^rsyslogd$", "^scp$", "^sed$", "^sh$", "^sleep$", "^smartd$", "^sort$", "^ss$", "^ssh$", "^sshd$", "^stat$", "^su$", "^sudo$", "^systemctl$", "^systemd", "^tail$", "^tar$", "^tclsh$", "^tee$", "^thunderbird$", "^top$", "^touch$", "^tr$", "^tuned$", "^udevd", "^uname$", "^uniq$", "^unxz$", "^updatedb$", "^usecpu$", "^usemem$", "^vi$", "^vim$", "^vmtoolsd$", "^wc$", "^which$", "^xargs$", "^xz$", "^xzcat$", "^yum$", "^zcat$", "^zgrep$", }; static regex_t *compreg; // compiled REs of allowed command names int main(int argc, char *argv[]) { struct rawheader rh; struct rawrecord rr; struct sstat sstat; struct tstat *tstatp; struct cstat *cstatp; char *istatp; int ifd, ofd=0, writecnt = 0, anonflag = 0; int i, c, numallowedcoms = sizeof allowedcoms/sizeof(char *); char *infile, *outfile; time_t begintime = 0, endtime = 0; // verify the command line arguments: // mandatory input and output filename // if (argc < 3) prusage(argv[0]); while ((c = getopt(argc, argv, "ab:e:")) != EOF) { switch (c) { case 'a': // anonymize anonflag = 1; break; case 'b': // begin time if ( !getbranchtime(optarg, &begintime) ) prusage(argv[0]); break; case 'e': // end time if ( !getbranchtime(optarg, &endtime) ) prusage(argv[0]); break; default: prusage(argv[0]); break; } } if (optind >= argc) prusage(argv[0]); infile = argv[optind++]; if (optind >= argc) prusage(argv[0]); outfile = argv[optind++]; // open the input file and verify magic number // if ( (ifd = openin(infile)) == -1) { prusage(argv[0]); exit(2); } readin(ifd, &rh, sizeof rh); if (rh.magic != MYMAGIC) { fprintf(stderr, "File %s does not contain atop/atopsar data " "(wrong magic number)\n", infile); exit(3); } if (rh.sstatlen != sizeof(struct sstat) || rh.tstatlen != sizeof(struct tstat) || rh.rawheadlen != sizeof(struct rawheader) || rh.rawreclen != sizeof(struct rawrecord) ) { fprintf(stderr, "File %s created with incompatible version of atop " "or created on other CPU architecture\n", infile); exit(3); } // handle the output file // if (strcmp(infile, outfile) == 0) { fprintf(stderr, "Input file and output file should not be identical!\n"); exit(12); } if (anonflag) // anonymize wanted? { // allocate space for compiled command REs and compile all REs // compreg = malloc(sizeof(regex_t) * numallowedcoms); ptrverify(compreg, "Malloc failed for regex\n"); for (i=0; i < numallowedcoms; i++) regcomp(&compreg[i], allowedcoms[i], REG_NOSUB); // anonymize host name // memset(rh.utsname.nodename, '\0', sizeof rh.utsname.nodename); strcpy(rh.utsname.nodename, "anonymized"); } // read recorded samples and copy to output file // while ( read(ifd, &rr, rh.rawreclen) == rh.rawreclen) { // skip records that are recorded before specified begin time // if (begintime && begintime > rr.curtime) { (void) lseek(ifd, rr.scomplen, SEEK_CUR); (void) lseek(ifd, rr.pcomplen, SEEK_CUR); (void) lseek(ifd, rr.ccomplen, SEEK_CUR); (void) lseek(ifd, rr.icomplen, SEEK_CUR); continue; } // skip records that are recorded after specified end time // if (endtime && endtime < rr.curtime) break; // open the output file and write the rawheader once // if (writecnt++ == 0) { if ( (ofd = openout(outfile)) == -1) { prusage(argv[0]); exit(4); } writeout(ofd, &rh, sizeof rh); } // read compressed system-level statistics and decompress // if ( !getrawsstat(ifd, &sstat, rr.scomplen) ) exit(7); // read compressed process-level statistics and decompress // tstatp = malloc(sizeof(struct tstat) * rr.ndeviat); ptrverify(tstatp, "Malloc failed for %d stored tasks\n", rr.ndeviat); if ( !getrawtstat(ifd, tstatp, rr.pcomplen, rr.ndeviat) ) exit(7); // read compressed cgroup-level statistics (no need to decompress) // cstatp = malloc(rr.ccomplen); ptrverify(cstatp, "Malloc failed for compressed cgroup stats\n"); readin(ifd, cstatp, rr.ccomplen); // read compressed pidlist (no need to decompress) // istatp = malloc(rr.icomplen); ptrverify(istatp, "Malloc failed for compressed pidlist\n"); readin(ifd, istatp, rr.icomplen); // anonymize command lines and hostname // if (anonflag) anonymize(&sstat, tstatp, rr.ndeviat); // write record header, system-level stats, process-level stats, // cgroup-level stats and pidlist // writesamp(ofd, &rr, &sstat, sizeof sstat, tstatp, sizeof *tstatp, rr.ndeviat, cstatp, rr.ccomplen, istatp, rr.icomplen); // cleanup // free(tstatp); free(cstatp); free(istatp); } // close files // close(ifd); printf("Samples written: %d", writecnt); if (writecnt == 0) printf(" -- no output file created!\n"); else printf("\n"); return 0; } // Funtion to anonymize the command lines and host name // static struct standin *lvmhead; static unsigned long lvmsequence; static struct standin *nfshead; static unsigned long nfssequence; static struct standin *cmdhead; static unsigned long cmdsequence; static void anonymize(struct sstat *ssp, struct tstat *tsp, int ntask) { int i, r, numallowedcoms = sizeof allowedcoms/sizeof(char *); char *standin, *p; // anonimize system-level stats // // be sure for that old value is fully wiped, especially // when the original string is langer that the substitute // // - logical volume names // for (i=0; i < ssp->dsk.nlvm; i++) { standin = findstandin(&lvmhead, &lvmsequence, "logvol", ssp->dsk.lvm[i].name); memset(ssp->dsk.lvm[i].name, '\0', sizeof ssp->dsk.lvm[i].name); safe_strcpy(ssp->dsk.lvm[i].name, standin, sizeof ssp->dsk.lvm[i].name); } // anonimize system-level stats // - NFS mounted shares // for (i=0; i < ssp->nfs.nfsmounts.nrmounts; i++) { standin = findstandin(&nfshead, &nfssequence, "nfsmnt", ssp->nfs.nfsmounts.nfsmnt[i].mountdev); memset(ssp->nfs.nfsmounts.nfsmnt[i].mountdev, '\0', sizeof ssp->nfs.nfsmounts.nfsmnt[i].mountdev); safe_strcpy(ssp->nfs.nfsmounts.nfsmnt[i].mountdev, standin, sizeof ssp->nfs.nfsmounts.nfsmnt[i].mountdev); } // anonymize process-level stats // - preserve all kernel processes (i.e. processes without memory) // - all command line arguments will be removed // - selected command names will be kept // (mainly standard commands and daemons) // for (i=0; i < ntask; i++, tsp++) { // preserve kernel processes // if (tsp->mem.vmem == 0 && tsp->gen.state != 'E') continue; // remove command line arguments // if ( (p = strchr(tsp->gen.cmdline, ' ')) ) memset(p, '\0', CMDLEN-(p - tsp->gen.cmdline)); // check all allowed names // for (r=0; r < numallowedcoms; r++) { // allowed name recognized: leave loop // if (regexec(&compreg[r], tsp->gen.name, 0, NULL, 0) == 0) break; } // when command name does not appear to be allowed, // replace command name by fake name // if (r == numallowedcoms) { standin = findstandin(&cmdhead, &cmdsequence, "prog", tsp->gen.name); memset(tsp->gen.name, '\0', sizeof tsp->gen.name); safe_strcpy(tsp->gen.name, standin, sizeof tsp->gen.name); memset(tsp->gen.cmdline, '\0', sizeof tsp->gen.cmdline); safe_strcpy(tsp->gen.cmdline, standin, sizeof tsp->gen.cmdline); } } } // Function that searches for the original name and returns // an anonymized replacement string. // When the original string does not exist yet, a replacement // string will be generated by using the prefix followed by a // 5-digit sequence number. That replacement string is stored // together with the original string for a subsequent search. // static char * findstandin(struct standin **head, unsigned long *sequence, char *prefix, char *origp) { struct standin *sp; // first check if the original string can be found in the linked list // for (sp = *head; sp; sp = sp->next) { if ( strcmp(sp->origname, origp) == 0) return sp->fakename; // found! } // original name not known yet // create a new entry in the linked list // sp = malloc(sizeof *sp); ptrverify(sp, "Malloc failed for standin struct\n"); sp->origname = malloc(strlen(origp)+1); sp->fakename = malloc(strlen(prefix)+6); ptrverify(sp->origname, "Malloc failed for standin orig\n"); ptrverify(sp->fakename, "Malloc failed for standin fake\n"); strcpy(sp->origname, origp); snprintf(sp->fakename, strlen(prefix)+6, "%s%05lu", prefix, (*sequence)++); sp->next = *head; *head = sp; return sp->fakename; } // Function that opens an existing raw file and // verifies the magic number // static int openin(char *infile) { int rawfd; /* ** open raw file for reading */ if ( (rawfd = open(infile, O_RDONLY)) == -1) { fprintf(stderr, "%s - ", infile); perror("open for reading"); return -1; } return rawfd; } // Function that reads a chunk of bytes from // the input raw file // static void readin(int rawfd, void *buf, int size) { /* ** read the requested chunk and verify */ if ( read(rawfd, buf, size) < size) { fprintf(stderr, "can not read raw file\n"); close(rawfd); exit(9); } } // Function that creates a raw log for output // static int openout(char *outfile) { int rawfd; /* ** create new output file */ if ( (rawfd = creat(outfile, 0666)) == -1) { fprintf(stderr, "%s - ", outfile); perror("create raw output file"); return -1; } return rawfd; } // Function that reads a chunk of bytes from // the input raw file // static void writeout(int rawfd, void *buf, int size) { /* ** write the provided chunk and verify */ if ( write(rawfd, buf, size) < size) { fprintf(stderr, "can not write raw file\n"); close(rawfd); exit(10); } } // Function that shows the usage message // void prusage(char *name) { fprintf(stderr, "Usage: %s [-a] [-b YYYYMMDDhhmm] [-e YYYYMMDDhhmm] " "rawin rawout\n\n", name); fprintf(stderr, "\t-a\tanonymize command names, host name, " "logical volume names, etc\n"); fprintf(stderr, "\t-b\twrite output from specified begin time\n"); fprintf(stderr, "\t-e\twrite output until specified end time\n"); exit(1); } // Function to read the system-level statistics from the current offset // static int getrawsstat(int rawfd, struct sstat *sp, int complen) { Byte *compbuf; unsigned long uncomplen = sizeof(struct sstat); int rv; compbuf = malloc(complen); ptrverify(compbuf, "Malloc failed for reading compressed sysstats\n"); if ( read(rawfd, compbuf, complen) < complen) { free(compbuf); fprintf(stderr, "Failed to read %d bytes for system\n", complen); return 0; } rv = uncompress((Byte *)sp, &uncomplen, compbuf, complen); testcompval(rv, "uncompress"); free(compbuf); return 1; } // Function to read the process-level statistics from the current offset // static int getrawtstat(int rawfd, struct tstat *pp, int complen, int ndeviat) { Byte *compbuf; unsigned long uncomplen = sizeof(struct tstat) * ndeviat; int rv; compbuf = malloc(complen); ptrverify(compbuf, "Malloc failed for reading compressed procstats\n"); if ( read(rawfd, compbuf, complen) < complen) { free(compbuf); fprintf(stderr, "Failed to read %d bytes for tasks\n", complen); return 0; } rv = uncompress((Byte *)pp, &uncomplen, compbuf, complen); testcompval(rv, "uncompress"); free(compbuf); return 1; } // Function to write a new output sample from the current offset // static void writesamp(int ofd, struct rawrecord *rr, void *sstat, int sstatlen, void *tstat, int tstatlen, int ntask, void *cstat, int cstatlen, void *istat, int istatlen) { int rv; Byte scompbuf[sstatlen], *pcompbuf; unsigned long scomplen = sizeof scompbuf; unsigned long pcomplen = tstatlen * ntask; /* ** compress system- and process-level statistics */ rv = compress(scompbuf, &scomplen, (Byte *)sstat, (unsigned long)sstatlen); testcompval(rv, "compress"); pcompbuf = malloc(pcomplen); ptrverify(pcompbuf, "Malloc failed for compression buffer\n"); rv = compress(pcompbuf, &pcomplen, (Byte *)tstat, (unsigned long)pcomplen); testcompval(rv, "compress"); rr->scomplen = scomplen; rr->pcomplen = pcomplen; if ( write(ofd, rr, sizeof *rr) == -1) { perror("write raw record"); exit(7); } /* ** write compressed system status structure to file */ if ( write(ofd, scompbuf, scomplen) == -1) { perror("write raw status record"); exit(7); } /* ** write compressed list of process status structures to file */ if ( write(ofd, pcompbuf, pcomplen) == -1) { perror("write raw process records"); exit(7); } free(pcompbuf); /* ** write compressed cgroup status structures to file */ if ( write(ofd, cstat, cstatlen) == -1) { perror("write raw cgroup records"); exit(7); } /* ** write compressed PID list */ if ( write(ofd, istat, istatlen) == -1) { perror("write raw pidlist"); exit(7); } } // check success of (de)compression // static void testcompval(int rv, char *func) { switch (rv) { case Z_OK: case Z_STREAM_END: case Z_NEED_DICT: break; case Z_MEM_ERROR: fprintf(stderr, "%s: failed due to lack of memory\n", func); exit(7); case Z_BUF_ERROR: fprintf(stderr, "%s: failed due to lack of room in buffer\n", func); exit(7); case Z_DATA_ERROR: fprintf(stderr, "%s: failed due to corrupted/incomplete data\n", func); exit(7); default: fprintf(stderr, "%s: unexpected error %d\n", func, rv); exit(7); } } // generic pointer verification after malloc // void ptrverify(const void *ptr, const char *errormsg, ...) { if (!ptr) { va_list args; va_start(args, errormsg); vfprintf(stderr, errormsg, args); va_end (args); exit(13); } } // Convert a string in format YYYYMMDDhhmm into an epoch time value. // // Arguments: String with date-time in format YYYYMMDDhhmm // Pointer to time_t containing 0 or current epoch time. // // Return-value: 0 - Wrong input-format // 1 - Success // int getbranchtime(char *itim, time_t *newtime) { register int ilen = strlen(itim); time_t epoch; struct tm tm; memset(&tm, 0, sizeof tm); /* ** verify length of input string */ if (ilen != 12) return 0; // wrong date-time format /* ** check string syntax for absolute time specified as ** YYYYMMDDhhmm */ if ( sscanf(itim, "%4d%2d%2d%2d%2d", &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min) != 5) return 0; tm.tm_year -= 1900; tm.tm_mon -= 1; if (tm.tm_year < 100 || tm.tm_mon < 0 || tm.tm_mon > 11 || tm.tm_mday < 1 || tm.tm_mday > 31 || tm.tm_hour < 0 || tm.tm_hour > 23 || tm.tm_min < 0 || tm.tm_min > 59 ) { return 0; // wrong date-time format } tm.tm_isdst = -1; if ((epoch = mktime(&tm)) == -1) return 0; // wrong date-time format // correct date-time format *newtime = epoch; return 1; } // copy a string to a destination buffer that will always // be null-terminated // void safe_strcpy(char *dst, const char *src, size_t dstsize) { if (dstsize == 0) return; dst[0] = '\0'; strncat(dst, src, dstsize - 1); } atop-2.12.1/atopsar.c0000644000203100020310000022227615064552761013703 0ustar gerlofgerlof/* ** ATOP - System & Process Monitor ** ** The program 'atop'/'atopsar' offers the possibility to view the activity of ** the system on system-level as well as process-level. ** ** This source-file contains the 'atopsar'-functionality, that makes use ** of the 'atop'-framework. ** ========================================================================== ** Author: Gerlof Langeveld ** E-mail: gerlof.langeveld@atoptool.nl ** Initial: July 2007 ** -------------------------------------------------------------------------- ** Copyright (C) 2007-2010 Gerlof Langeveld ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ** -------------------------------------------------------------------------- */ #define _POSIX_C_SOURCE #define _XOPEN_SOURCE #define _GNU_SOURCE #define _DEFAULT_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "atop.h" #include "ifprop.h" #include "photosyst.h" #include "photoproc.h" #include "cgroups.h" #include "gpucom.h" #define MAXFL 64 /* maximum number of command-line flags */ /* ** color definitions */ #define COLSETHEAD "\033[30;43m" /* black on yellow */ #define COLSETMED "\033[36m" /* cyan */ #define COLSETHIGH "\033[31m" /* red */ #define COLRESET "\033[00m" /* reset any color */ /* ** miscellaneous values */ static unsigned int nsamples = 9999999; static char stampalways; static char usemarkers; static char allresources; static int numreports; static time_t saved_begintime; static unsigned int repeathead = 9999999; static unsigned int summarycnt = 1; static char *datemsg = "-------------------------- analysis " "date: %s --------------------------\n"; /* ** structure definition for print-functions */ struct pridef { char wanted; /* selected option (boolean) */ char *cntcat; /* used categories of counters */ char flag; /* flag on command line */ void (*prihead)(int, int, int); /* print header of list */ int (*priline)(struct sstat *, struct tstat *, struct tstat **, int, time_t, time_t, time_t, int, int, int, char *, int, int, int, int, int, int, int); /* print counters per line (excl. time) */ char *about; /* statistics about what */ }; extern struct pridef pridef[]; /* table of print-functions */ extern int pricnt; /* total number of print-functions */ static time_t daylim; /* last second of day in epoch */ static int prinow; /* current selection */ static char coloron; /* boolean: colors active now */ /* ** local prototypes */ static void engine(void); static void pratopsaruse(char *); static void reportlive(time_t, int, struct sstat *); static char reportraw (time_t, int, struct devtstat *, struct sstat *, struct cgchainer *, int, int, int, unsigned int, char); static void reportheader(struct utsname *, time_t); static time_t daylimit(time_t); int atopsar(int argc, char *argv[]) { register int i, c; struct rlimit rlim; char *p, *flaglist; usecolors = 't'; /* ** interpret command-line arguments & flags */ if (argc > 1) { /* ** gather all flags for the print-functions */ flaglist = malloc(pricnt+32); if (!flaglist) ptrverify(flaglist, "Malloc failed for %d flags\n", pricnt+32); for (i=0; i < pricnt; i++) flaglist[i] = pridef[i].flag; flaglist[i] = 0; /* ** add generic flags */ strcat(flaglist, "b:e:SxCMHr:R:aA"); while ((c=getopt(argc, argv, flaglist)) != EOF) { switch (c) { case '?': /* usage wanted ? */ pratopsaruse(argv[0]); break; case 'b': /* begin time ? */ if ( !getbranchtime(optarg, &begintime) ) pratopsaruse(argv[0]); saved_begintime = begintime; break; case 'e': /* end time ? */ if ( !getbranchtime(optarg, &endtime) ) pratopsaruse(argv[0]); break; case 'r': /* reading of file data ? */ safe_strcpy(irawname, optarg, RAWNAMESZ); if (strcmp(irawname, "-") == 0) safe_strcpy(irawname, "/dev/stdin", RAWNAMESZ); rawreadflag++; break; case 'R': /* summarize samples */ if (!numeric(optarg)) pratopsaruse(argv[0]); summarycnt = atoi(optarg); if (summarycnt < 1) pratopsaruse(argv[0]); break; case 'S': /* timestamp on every line */ stampalways = 1; break; case 'x': /* never use colors */ usecolors = 0; break; case 'C': /* always use colors */ usecolors = 'a'; break; case 'M': /* markers for overload */ usemarkers = 1; break; case 'H': /* repeat headers */ repeathead = 23; /* define default */ if (isatty(fileno(stdout))) { struct winsize wsz; if ( ioctl(1, TIOCGWINSZ, &wsz) != -1) repeathead = wsz.ws_row - 1; } break; case 'a': /* every interval all units */ allresources = 1; break; case 'A': /* all reports wanted ? */ for (i=0; i < pricnt; i++) pridef[i].wanted = 1; numreports = pricnt; break; default: /* gather report-flags */ for (i=0; i < pricnt; i++) { if (pridef[i].flag == c && pridef[i].wanted == 0 ) { pridef[i].wanted = 1; numreports++; break; } } if (i == pricnt) pratopsaruse(argv[0]); } } free(flaglist); /* ** get optional interval-value and ** optional number of samples */ if (optind < argc && optind < MAXFL) { if (rawreadflag) pratopsaruse(argv[0]); if (!numeric(argv[optind])) pratopsaruse(argv[0]); interval = atoi(argv[optind]); optind++; if (optind < argc) { if (!numeric(argv[optind]) ) pratopsaruse(argv[0]); if ( (nsamples = atoi(argv[optind])) < 1) pratopsaruse(argv[0]); } } else /* if no interval specified, read from logfile */ { rawreadflag++; } } else /* if no flags specified at all, read from logfile */ { rawreadflag++; } /* ** if no report-flags have been specified, take the first ** option in the print-list as default */ if (numreports == 0) { pridef[0].wanted = 1; numreports = 1; } /* ** set stdout output on line-basis */ setvbuf(stdout, (char *)0, _IOLBF, BUFSIZ); /* ** if only use colors when the output is directed to a tty, ** be sure that output is directed to a tty */ if (usecolors == 't') { if (! isatty(fileno(stdout)) ) usecolors = 0; } /* ** check if raw data from a file must be viewed */ if (rawreadflag) { /* ** select own reportraw-function to be called ** by the rawread function */ handlers[0].handle_sample = reportraw; for (i=0; i < pricnt; i++) { if ( pridef[i].wanted ) { prinow = i; daylim = 0; begintime = saved_begintime; if (!rawread()) // reading from named pipe break; // can only be done once printf("\n"); } } cleanstop(0); } /* ** live data must be gathered ** ** determine the name of this node (without domain-name) ** and the kernel-version */ (void) uname(&utsname); if ( (p = strchr(utsname.nodename, '.')) ) *p = '\0'; sscanf(utsname.release, "%d.%d.%d", &osrel, &osvers, &ossub); /* ** determine the clock rate and memory page size for this machine */ hertz = sysconf(_SC_CLK_TCK); pagesize = sysconf(_SC_PAGESIZE); /* ** determine start-time for gathering current statistics */ curtime = time(0); /* ** regain the root-privileges that we dropped at the beginning ** to do some privileged work now */ regainrootprivs(); /* ** lock in memory to get reliable samples (also when ** memory is low); ** ignored if not running under superuser privileges! */ rlim.rlim_cur = RLIM_INFINITY; rlim.rlim_max = RLIM_INFINITY; if (setrlimit(RLIMIT_MEMLOCK, &rlim) == 0) (void) mlockall(MCL_CURRENT|MCL_FUTURE); /* ** increment CPU scheduling-priority to get reliable samples (also ** during heavy CPU load); ** ignored if not running under superuser privileges! */ if ( nice(-20) == -1) ; /* ** determine properties (like speed) of all interfaces */ initifprop(); /* ** since privileged activities are finished now, there is no ** need to keep running under root-privileges, so switch ** effective user-id to real user-id */ if (! droprootprivs() ) mcleanstop(42, "failed to drop root privs\n"); /* ** start live reporting */ engine(); return 0; } /* ** engine() that drives the main-loop for atopsar */ static void engine(void) { struct sigaction sigact; int nrgpus; /* number of GPUs */ int nrgpuproc, /* number of GPU procs */ gpustats=0; /* boolean: request sent */ /* ** reserve space for system-level statistics */ static struct sstat *cursstat; /* current */ static struct sstat *presstat; /* previous */ static struct sstat *devsstat; /* deviation */ static struct sstat *hlpsstat; /* ** initialization: allocate required memory dynamically */ cursstat = calloc(1, sizeof(struct sstat)); presstat = calloc(1, sizeof(struct sstat)); devsstat = calloc(1, sizeof(struct sstat)); ptrverify(cursstat, "Malloc failed for current sysstats\n"); ptrverify(presstat, "Malloc failed for prev sysstats\n"); ptrverify(devsstat, "Malloc failed for deviate sysstats\n"); /* ** install the signal-handler for ALARM and SIGUSR1 (both triggers ** for the next sample) */ memset(&sigact, 0, sizeof sigact); sigact.sa_handler = getusr1; sigaction(SIGUSR1, &sigact, (struct sigaction *)0); memset(&sigact, 0, sizeof sigact); sigact.sa_handler = getalarm; sigaction(SIGALRM, &sigact, (struct sigaction *)0); if (interval > 0) alarm(interval); /* ** print overall report header */ reportheader(&utsname, time(0)); /* ** open socket to the atopgpud daemon for GPU statistics */ nrgpus = gpud_init(); /* ** MAIN-LOOP: ** - Wait for the requested number of seconds or for other trigger ** ** - System-level counters ** get current counters ** calculate the differences with the previous sample ** ** - Call the print-function to visualize the differences */ for (sampcnt=0; sampcnt < nsamples+1; sampcnt++) { /* ** wait for alarm-signal to arrive or ** wait for SIGUSR1 in case of an interval of 0. */ if (sampcnt > 0) pause(); /* ** gather time info for this sample */ pretime = curtime; curtime = time(0); /* seconds since 1-1-1970 */ /* ** send request for statistics to atopgpud */ if (nrgpus) gpustats = gpud_statrequest(); /* ** take a snapshot of the current system-level statistics ** and calculate the deviations (i.e. calculate the activity ** during the last sample) */ hlpsstat = cursstat; /* swap current/prev. stats */ cursstat = presstat; presstat = hlpsstat; photosyst(cursstat); /* obtain new counters */ /* ** receive and parse response from atopgpud */ if (nrgpus && gpustats) { nrgpuproc = gpud_statresponse(nrgpus, cursstat->gpu.gpu, NULL); // connection lost or timeout on receive? if (nrgpuproc == -1) { int ng; // try to reconnect ng = gpud_init(); if (ng != nrgpus) // no success nrgpus = 0; if (nrgpus) { // request for stats again if (gpud_statrequest()) { // receive stats response nrgpuproc = gpud_statresponse(nrgpus, cursstat->gpu.gpu, NULL); // persistent failure? if (nrgpuproc == -1) nrgpus = 0; } } } cursstat->gpu.nrgpus = nrgpus; } /* ** calculate deviations, i.e. activity during interval */ deviatsyst(cursstat, presstat, devsstat, curtime-pretime > 0 ? curtime-pretime : 1); /* ** activate the report-function to visualize the deviations */ reportlive(curtime, curtime-pretime > 0 ? curtime-pretime : 1, devsstat); } /* end of main-loop */ } /* ** report function to print a new sample in case of live measurements */ static void reportlive(time_t curtime, int numsecs, struct sstat *ss) { char timebuf[16], datebuf[16]; int i, nr = numreports, rv; static unsigned int curline, headline; /* ** printing more reports needs another way of handling compared ** to printing one report */ if (numreports > 1) { /* ** skip first sample */ if (sampcnt == 0) return; printf(datemsg, convdate(curtime, datebuf)); for (i=0; i < pricnt && nr > 0; i++) { if ( !pridef[i].wanted ) continue; nr--; /* ** print header-line */ printf("\n"); if (usecolors) printf(COLSETHEAD); printf("%s ", convtime(curtime-numsecs, timebuf)); (pridef[i].prihead)(osvers, osrel, ossub); if (usecolors) printf(COLRESET); printf("\n"); /* ** print line with statistical counters */ printf("%s ", convtime(curtime, timebuf)); if ( !(pridef[i].priline)(ss, (struct tstat *)0, 0, 0, numsecs, numsecs*hertz, hertz, osvers, osrel, ossub, stampalways ? timebuf : " ", 0, 0, 0, 0, 0, 0, 0) ) { /* ** print line has failed; ** do not call function again */ pridef[i].wanted = 0; if (--numreports == 0) cleanstop(1); } } printf("\n"); } else /* just one report to be printed */ { /* ** search required report */ for (i=0; i < pricnt; i++) if ( pridef[i].wanted ) break; /* ** verify if we have passed midnight of some day */ if (curtime > daylim) { printf(datemsg, convdate(curtime, datebuf)); daylim = daylimit(curtime); curline++; } /* ** print first header */ if (sampcnt == 0) { /* ** print header-line */ printf("\n"); if (usecolors) printf(COLSETHEAD); printf("%s ", convtime(curtime, timebuf)); (pridef[i].prihead)(osvers, osrel, ossub); if (usecolors) printf(COLRESET); printf("\n"); curline+=2; headline = repeathead; return; } /* ** print line with statistical counters */ printf("%s ", convtime(curtime, timebuf)); if ( !(rv = (pridef[i].priline)(ss, (struct tstat *)0, 0, 0, numsecs, numsecs*hertz, hertz, osvers, osrel, ossub, stampalways ? timebuf : " ", 0, 0, 0, 0, 0, 0, 0) ) ) { /* ** print line has failed; ** do not call function again */ cleanstop(1); } curline+=rv; if (curline >= headline) { headline = curline + repeathead; /* ** print header-line */ printf("\n"); if (usecolors) printf(COLSETHEAD); printf("%s ", convtime(curtime, timebuf)); (pridef[i].prihead)(osvers, osrel, ossub); if (usecolors) printf(COLRESET); printf("\n"); curline+=2; } } } /* ** report function to print a new sample in case of logged measurements */ static char reportraw(time_t curtime, int numsecs, struct devtstat *devtstat, struct sstat *sstat, struct cgchainer *devchain, int ncgroups, int npids, int nexit, unsigned int noverflow, char flags) { static char firstcall = 1; char timebuf[16], datebuf[16]; unsigned int rv; static unsigned int curline, headline, sampsum, totalsec, totalexit, lastnpres, lastntrun, lastntslpi, lastntslpu, lastntidle, lastnzomb; static time_t lasttime; static struct sstat totsyst; /* ** is this function still wanted? */ if ( ! pridef[prinow].wanted ) return '\0'; /* ** when this is first call to this function, ** print overall header with system information */ if (firstcall) { reportheader(&utsname, time(0)); firstcall = 0; } /* ** verify if we have passed midnight */ if (curtime > daylim) { printf(datemsg, convdate(curtime, datebuf)); daylim = daylimit(curtime); curline++; } /* ** when this is the first record for a new report, ** initialize various variables */ if (sampcnt == 1) { /* ** initialize variables for new report */ pretime = curtime; curline = 1; headline = 0; sampsum = summarycnt + 1; totalsec = 0; totalexit = 0; memset(&totsyst, 0, sizeof totsyst); return '\0'; } /* ** check if a (new) report header needs to be printed */ if (curline >= headline) { headline = curline + repeathead; /* ** print header-line */ printf("\n"); if (usecolors) printf(COLSETHEAD); printf("%s ", convtime(pretime, timebuf)); (pridef[prinow].prihead)(osvers, osrel, ossub); if (usecolors) printf(COLRESET); printf("\n"); curline+=2; } /* ** when current record contains log-restart indicator, ** print message and reinitialize variables */ if (flags & RRBOOT) { /* ** when accumulating counters, print results upto ** the *previous* record */ if (summarycnt > 1 && sampcnt <= sampsum && totalsec) { printf("%s ", convtime(lasttime, timebuf)); rv = (pridef[prinow].priline)(&totsyst, (struct tstat *)0, 0, 0, totalsec, totalsec*hertz, hertz, osvers, osrel, ossub, stampalways ? timebuf : " ", lastnpres, lastntrun, lastntslpi, lastntslpu, lastntidle, totalexit, lastnzomb); if (rv == 0) { curline++; pridef[prinow].wanted = 0; /* not call again */ if (--numreports == 0) cleanstop(1); } else { curline += rv; } } /* ** print restart-line in case of logging restarted */ printf("%s ", convtime(curtime, timebuf)); printf("......................... logging restarted " ".........................\n"); pretime = curtime; curline++; /* ** reinitialize variables */ sampsum = summarycnt + sampcnt; totalsec = 0; totalexit = 0; memset(&totsyst, 0, sizeof totsyst); return '\0'; } /* ** when no accumulation is required, ** just print current sample */ if (summarycnt == 1) { printf("%s ", convtime(curtime, timebuf)); rv = (pridef[prinow].priline) (sstat, devtstat->taskall, devtstat->procall, devtstat->nprocall, numsecs, numsecs*hertz, hertz, osvers, osrel, ossub, stampalways ? timebuf : " ", devtstat->ntaskall, devtstat->totrun, devtstat->totslpi, devtstat->totslpu, devtstat->totidle, nexit, devtstat->totzombie); if (rv == 0) { curline++; pridef[prinow].wanted = 0; /* not call again */ if (--numreports == 0) cleanstop(1); } else { curline += rv; } } else /* accumulation is required */ { char *cp = pridef[prinow].cntcat; /* ** maintain totals per category */ while (*cp) { totalsyst(*cp, sstat, &totsyst); cp++; } totalsec += numsecs; totalexit += nexit; /* ** remember some values in case the next record ** contains the log-restart indicator */ lasttime = curtime; lastnpres = devtstat->nprocall; lastntrun = devtstat->totrun; lastntslpi = devtstat->totslpi; lastntslpu = devtstat->totslpu; lastntidle = devtstat->totidle; lastnzomb = devtstat->totzombie; /* ** print line only if needed */ if (sampcnt >= sampsum || ( (flags&RRLAST) && totalsec) ) { /* ** print output line for required report */ printf("%s ", convtime(curtime, timebuf)); rv = (pridef[prinow].priline) (&totsyst, (struct tstat *)0, 0, 0, totalsec, totalsec*hertz, hertz, osvers, osrel, ossub, stampalways ? timebuf : " ", devtstat->ntaskall, devtstat->totrun, devtstat->totslpi, devtstat->totslpu, devtstat->totidle, totalexit, devtstat->totzombie); if (rv == 0) { curline++; pridef[prinow].wanted = 0; /* not call again */ if (--numreports == 0) cleanstop(1); } else { curline += rv; } sampsum = summarycnt + sampcnt; totalsec = 0; totalexit = 0; memset(&totsyst, 0, sizeof totsyst); } else { rv = 1; } } if (!rv) { /* ** print for line has failed; ** never call this function again */ pridef[prinow].wanted = 0; if (--numreports == 0) cleanstop(1); } pretime = curtime; return '\0'; } /* ** print overall header */ static void reportheader(struct utsname *uname, time_t mtime) { char cdate[16]; printf("\n%s %s %s %s %s\n\n", uname->nodename, uname->release, uname->version, uname->machine, convdate(mtime, cdate)); } /* ** print usage of atopsar command */ void pratopsaruse(char *myname) { int i; fprintf(stderr, "Usage: %s [-flags] [-r file|-|date|y...] [-R cnt] [-b time] [-e time]\n", myname); fprintf(stderr, "\t\tor\n"); fprintf(stderr, "Usage: %s [-flags] interval [samples]\n", myname); fprintf(stderr, "\n"); fprintf(stderr, "\tToday's atop logfile is used by default!\n"); fprintf(stderr, "\n"); fprintf(stderr, "\tGeneric flags:\n"); fprintf(stderr, "\t -r read statistical data from specific atop logfile\n"); fprintf(stderr, "\t (pathname, - for stdin, date in format YYYYMMDD, or y[y..])\n"); fprintf(stderr, "\t -R summarize samples into one sample\n"); fprintf(stderr, "\t -b begin showing data from specified time as [YYYYMMDD]hhmm[ss]\n"); fprintf(stderr, "\t -e finish showing data after specified time as [YYYYMMDD]hhmm[ss]\n"); fprintf(stderr, "\t -S print timestamp on every line in case of more " "resources\n"); fprintf(stderr, "\t -x never use colors to indicate overload" " (default: only if tty)\n"); fprintf(stderr, "\t -C always use colors to indicate overload" " (default: only if tty)\n"); fprintf(stderr, "\t -M use markers to indicate overload " "(* = critical, + = almost)\n"); fprintf(stderr, "\t -H repeat report headers " "(in case of tty: depending on screen lines)\n"); fprintf(stderr, "\t -a print all resources, even when inactive\n"); fprintf(stderr, "\n"); fprintf(stderr, "\tSpecific flags to select reports:\n"); fprintf(stderr, "\t -A print all available reports\n"); for (i=0; i < pricnt; i++) fprintf(stderr, "\t -%c %s\n", pridef[i].flag, pridef[i].about); fprintf(stderr, "\n"); fprintf(stderr, "Please refer to the man-page of 'atopsar' " "for more details.\n"); cleanstop(1); } /* ** calculate the epoch-value for the last second ** of the day given a certain epoch */ static time_t daylimit(time_t timval) { struct tm *tp = localtime(&timval); tp->tm_hour = 23; tp->tm_min = 59; tp->tm_sec = 59; return mktime(tp); } /* ** function to be called before printing a statistics line ** to switch on colors when necessary */ static void preprint(unsigned int badness) { if (usecolors) { if (badness >= 100) { coloron = 1; printf(COLSETHIGH); } else { if (almostcrit && badness >= almostcrit) { coloron = 1; printf(COLSETMED); } } } } /* ** function to be called after printing a statistics line ** to switch off colors when necessary and print a line feed */ static void postprint(unsigned int badness) { if (coloron) { coloron = 0; printf(COLRESET); } if (usemarkers) { if (badness >= 100) { printf(" *"); } else { if (almostcrit && badness >= almostcrit) printf(" +"); } } printf("\n"); } /* ** function to handle the default flags for atopsar as ** read from the files ~/.atoprc and /etc/atoprc */ void do_atopsarflags(char *name, char *val) { int i, j; for (i=0; val[i]; i++) { switch (val[i]) { case '-': break; case 'S': /* timestamp on every line */ stampalways = 1; break; case 'x': /* always colors for overload */ usecolors = 0; break; case 'C': /* always colors for overload */ usecolors = 'a'; break; case 'M': /* markers for overload */ usemarkers = 1; break; case 'H': /* repeat headers */ repeathead = 23; /* define default */ if (isatty(fileno(stdout))) { struct winsize wsz; if ( ioctl(1, TIOCGWINSZ, &wsz) != -1) repeathead = wsz.ws_row - 1; } break; case 'a': /* every interval all units */ allresources = 1; break; case 'A': /* all reports wanted ? */ for (j=0; j < pricnt; j++) pridef[j].wanted = 1; numreports = pricnt; break; default: /* gather report-flags */ for (j=0; j < pricnt; j++) { if (pridef[j].flag == val[i] && pridef[j].wanted == 0 ) { pridef[j].wanted = 1; numreports++; break; } } } } } /**************************************************************************/ /* Functions to print statistics */ /**************************************************************************/ /* ** CPU statistics */ static void cpuhead(int osvers, int osrel, int ossub) { printf("cpu %%usr %%nice %%sys %%irq %%softirq %%steal %%guest " " %%wait %%idle _cpu_"); } static int cpuline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { register int i, nlines = 1; count_t cputot; unsigned int badness; /* ** print overall statistics */ cputot = ss->cpu.all.stime + ss->cpu.all.utime + ss->cpu.all.ntime + ss->cpu.all.itime + ss->cpu.all.wtime + ss->cpu.all.Itime + ss->cpu.all.Stime + ss->cpu.all.steal; if (cputot == 0) cputot = 1; /* avoid divide-by-zero */ if (cpubadness) badness = ((cputot - ss->cpu.all.itime - ss->cpu.all.wtime) * 100.0 / cputot) * 100 / cpubadness; else badness = 0; preprint(badness); printf("all %5.0lf %5.0lf %4.0lf %4.0lf %8.0lf %7.0f %6.0f %6.0lf %5.0lf", (double) (ss->cpu.all.utime * 100.0) / cputot * ss->cpu.nrcpu, (double) (ss->cpu.all.ntime * 100.0) / cputot * ss->cpu.nrcpu, (double) (ss->cpu.all.stime * 100.0) / cputot * ss->cpu.nrcpu, (double) (ss->cpu.all.Itime * 100.0) / cputot * ss->cpu.nrcpu, (double) (ss->cpu.all.Stime * 100.0) / cputot * ss->cpu.nrcpu, (double) (ss->cpu.all.steal * 100.0) / cputot * ss->cpu.nrcpu, (double) (ss->cpu.all.guest * 100.0) / cputot * ss->cpu.nrcpu, (double) (ss->cpu.all.wtime * 100.0) / cputot * ss->cpu.nrcpu, (double) (ss->cpu.all.itime * 100.0) / cputot * ss->cpu.nrcpu); postprint(badness); /* ** print per-cpu statistics */ if (ss->cpu.nrcpu > 1) { for (i=0; i < ss->cpu.nrcpu; i++) { cputot = ss->cpu.cpu[i].stime + ss->cpu.cpu[i].utime + ss->cpu.cpu[i].ntime + ss->cpu.cpu[i].itime + ss->cpu.cpu[i].wtime + ss->cpu.cpu[i].Itime + ss->cpu.cpu[i].Stime + ss->cpu.cpu[i].steal; if (cputot == 0) cputot = 1; /* avoid divide-by-zero */ if (cpubadness) badness = ((cputot - ss->cpu.cpu[i].itime - ss->cpu.cpu[i].wtime) * 100.0 / cputot) * 100 / cpubadness; else badness = 0; printf("%s ", tstamp); preprint(badness); printf("%4d %5.0lf %5.0lf %4.0lf %4.0lf %8.0lf " "%7.0f %6.0lf %6.0lf %5.0lf", i, (double)(ss->cpu.cpu[i].utime * 100.0) / cputot, (double)(ss->cpu.cpu[i].ntime * 100.0) / cputot, (double)(ss->cpu.cpu[i].stime * 100.0) / cputot, (double)(ss->cpu.cpu[i].Itime * 100.0) / cputot, (double)(ss->cpu.cpu[i].Stime * 100.0) / cputot, (double)(ss->cpu.cpu[i].steal * 100.0) / cputot, (double)(ss->cpu.cpu[i].guest * 100.0) / cputot, (double)(ss->cpu.cpu[i].wtime * 100.0) / cputot, (double)(ss->cpu.cpu[i].itime * 100.0) / cputot); postprint(badness); nlines++; } } return nlines; } /* ** GPU statistics */ static void gpuhead(int osvers, int osrel, int ossub) { printf(" busaddr gpubusy membusy memocc memtot memuse gputype" " _gpu_"); } static int gpuline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { static char firstcall = 1; register long i, nlines = 0; char fmt1[16], fmt2[16]; count_t avgmemuse; for (i=0; i < ss->gpu.nrgpus; i++) /* per GPU */ { /* ** determine whether or not the GPU has been active ** during interval */ int wasactive; wasactive = ss->gpu.gpu[i].gpuperccum + ss->gpu.gpu[i].memperccum; if (wasactive == -2) // metrics not available? wasactive = 0; if (ss->gpu.gpu[i].samples == 0) avgmemuse = ss->gpu.gpu[i].memusenow; else avgmemuse = ss->gpu.gpu[i].memusecum / ss->gpu.gpu[i].samples; // memusage > 512 MiB (rather arbitrary)? // if (avgmemuse > 512*1024) wasactive = 1; /* ** print for the first sample all GPUs that are found; ** afterwards print only info about the GPUs ** that were really active during the interval */ if (!firstcall && !allresources && !wasactive) continue; if (nlines++) printf("%s ", tstamp); if (ss->gpu.gpu[i].samples == 0) ss->gpu.gpu[i].samples = 1; if (ss->gpu.gpu[i].gpuperccum == -1) strcpy(fmt1, "N/A"); else snprintf(fmt1, sizeof fmt1, "%lld%%", ss->gpu.gpu[i].gpuperccum / ss->gpu.gpu[i].samples); if (ss->gpu.gpu[i].memperccum == -1) strcpy(fmt2, "N/A"); else snprintf(fmt2, sizeof fmt2, "%lld%%", ss->gpu.gpu[i].memperccum / ss->gpu.gpu[i].samples); if (ss->gpu.gpu[i].memtotnow == 0) ss->gpu.gpu[i].memtotnow = 1; printf("%2ld/%9.9s %7s %7s %5lld%% %5lldM %5lldM %s\n", i, ss->gpu.gpu[i].busid, fmt1, fmt2, ss->gpu.gpu[i].memusenow*100/ss->gpu.gpu[i].memtotnow, ss->gpu.gpu[i].memtotnow / 1024, ss->gpu.gpu[i].memusenow / 1024, ss->gpu.gpu[i].type); } if (nlines == 0) { printf("\n"); nlines++; } firstcall = 0; return nlines; } /* ** other processor statistics */ static void prochead(int osvers, int osrel, int ossub) { printf("pswch/s devintr/s clones/s loadavg1 loadavg5 loadavg15 " " _load_"); } static int procline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { printf("%7.0lf %9.0lf %9.2lf %8.2lf %8.2lf %8.2lf\n", (double)ss->cpu.csw / deltasec, (double)ss->cpu.devint / deltasec, (double)ss->cpu.nprocs / deltasec, ss->cpu.lavg1, ss->cpu.lavg5, ss->cpu.lavg15); return 1; } /* ** process statistics */ static void taskhead(int osvers, int osrel, int ossub) { printf("clones/s pexit/s curproc curzomb trun tslpi tslpu tidle " "_procthr_"); } static int taskline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { if (ppres == 0) { printf("report not available for live measurements.....\n"); return 0; } if (ts) /* process statistics available */ { printf("%8.2lf %7.2lf %7d %7d %6d %6d %5d %5d\n", (double)ss->cpu.nprocs / deltasec, (double)pexit / deltasec, nactproc-pexit, pzombie, ntrun, ntslpi, ntslpu, ntidle); } else { printf("%8.2lf %7.2lf %7d %7d\n", (double)ss->cpu.nprocs / deltasec, (double)pexit / deltasec, nactproc-pexit, pzombie); } return 1; } /* ** memory- & swap-usage */ static void memhead(int osvers, int osrel, int ossub) { printf("memtotal memfree buffers cached dirty slabmem" " swptotal swpfree _mem_" ); } static int memline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { unsigned int mbadness, sbadness; if (membadness) mbadness = ((ss->mem.physmem - ss->mem.freemem - ss->mem.cachemem - ss->mem.buffermem + ss->mem.shmem) * 100.0 / ss->mem.physmem) * 100 / membadness; else mbadness = 0; if (swpbadness) sbadness = ((ss->mem.totswap - ss->mem.freeswap) * 100.0 / ss->mem.totswap) * 100 / swpbadness; else sbadness = 0; preprint(mbadness >= sbadness ? mbadness : sbadness); printf("%7lldM %6lldM %6lldM %5lldM %4lldM %6lldM %7lldM %6lldM", ss->mem.physmem * (pagesize / 1024) /1024, ss->mem.freemem * (pagesize / 1024) /1024, ss->mem.buffermem * (pagesize / 1024) /1024, ss->mem.cachemem * (pagesize / 1024) /1024, ss->mem.cachedrt * (pagesize / 1024) /1024, ss->mem.slabmem * (pagesize / 1024) /1024, ss->mem.totswap * (pagesize / 1024) /1024, ss->mem.freeswap * (pagesize / 1024) /1024); postprint(mbadness >= sbadness ? mbadness : sbadness); return 1; } /* ** swapping statistics */ static void swaphead(int osvers, int osrel, int ossub) { printf("pagescan/s swapin/s swapout/s oomkill" " commitspc commitlim _swap_"); } static int swapline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { unsigned int badness; if (membadness) badness = (ss->mem.swouts / deltasec * pagbadness) * 100 / membadness; else badness = 0; /* ** take care that this line is anyhow colored for ** 'almost critical' in case of swapouts > 1 per second */ if (ss->mem.swouts / deltasec > 0 && pagbadness && almostcrit && badness < almostcrit) badness = almostcrit; if (ss->mem.commitlim && ss->mem.committed > ss->mem.commitlim) badness = 100; /* force colored output */ preprint(badness); printf("%10.2lf %9.2lf %9.2lf %7lld %9lluM %9lluM", (double)ss->mem.pgscans / deltasec, (double)ss->mem.swins / deltasec, (double)ss->mem.swouts / deltasec, ss->mem.oomkills, ss->mem.committed * (pagesize / 1024) / 1024, ss->mem.commitlim * (pagesize / 1024) / 1024); postprint(badness); return 1; } /* ** PSI statistics */ static void psihead(int osvers, int osrel, int ossub) { printf("cpusome memsome memfull iosome iofull"); } static int psiline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { // calculate pressure percentages for entire interval unsigned int csperc = ss->psi.cpusome.total/(deltatic*10000/hz); unsigned int msperc = ss->psi.memsome.total/(deltatic*10000/hz); unsigned int mfperc = ss->psi.memfull.total/(deltatic*10000/hz); unsigned int isperc = ss->psi.iosome.total /(deltatic*10000/hz); unsigned int ifperc = ss->psi.iofull.total /(deltatic*10000/hz); unsigned int badness = 0; if (!ss->psi.present) { printf("no PSI stats available for this interval...\n"); return 1; } // correct percentages if needed if (csperc > 100) csperc = 100; if (msperc > 100) msperc = 100; if (mfperc > 100) mfperc = 100; if (isperc > 100) isperc = 100; if (ifperc > 100) ifperc = 100; // consider a 'some' percentage > 0 as almost critical // (I/O full tends to increase rapidly as well) if (csperc || msperc || isperc || ifperc) badness = 80; // consider a memory 'full' percentage > 0 as critical if (mfperc) badness = 100; // show results preprint(badness); printf(" %3u%% %3u%% %3u%% %3u%% %3u%%", csperc, msperc, mfperc, isperc, ifperc); postprint(badness); return 1; } /* ** disk statistics */ static void lvmhead(int osvers, int osrel, int ossub) { printf("disk busy read/s KB/read " "writ/s KB/writ avque avserv _lvm_"); } static void mddhead(int osvers, int osrel, int ossub) { printf("disk busy read/s KB/read " "writ/s KB/writ avque avserv _mdd_"); } static void dskhead(int osvers, int osrel, int ossub) { printf("disk busy read/s KB/read " "writ/s KB/writ avque avserv _dsk_"); } static int gendskline(struct sstat *ss, char *tstamp, char selector) { static char firstcall = 1; register int i, nlines = 0, nunit = 0; count_t mstot, iotot; struct perdsk *dp; unsigned int badness; switch (selector) { case 'l': dp = ss->dsk.lvm; nunit = ss->dsk.nlvm; break; case 'm': dp = ss->dsk.mdd; nunit = ss->dsk.nmdd; break; case 'd': dp = ss->dsk.dsk; nunit = ss->dsk.ndsk; break; default: return 0; } mstot = (ss->cpu.all.stime + ss->cpu.all.utime + ss->cpu.all.ntime + ss->cpu.all.itime + ss->cpu.all.wtime + ss->cpu.all.Itime + ss->cpu.all.Stime + ss->cpu.all.steal ) * (count_t)1000 / hertz / ss->cpu.nrcpu; for (i=0; i < nunit; i++, dp++) { char *pn; int len; iotot = dp->nread + dp->nwrite + (dp->ndisc != -1 ? dp->ndisc : 0); if (iotot == 0 && !firstcall && !allresources) continue; /* no activity on this disk */ /* ** disk was active during last interval; print info */ if (nlines++) printf("%s ", tstamp); if (dskbadness) badness = (dp->io_ms * 100.0 / mstot) * 100/dskbadness; else badness = 0; preprint(badness); if ( (len = strlen(dp->name)) > 14) pn = dp->name + len - 14; else pn = dp->name; printf("%-14s %3.0lf%% %6.1lf %7.1lf %7.1lf %7.1lf " "%5.1lf %9.5lf ms", pn, mstot ? (double)dp->io_ms * 100.0 / mstot : 0.0, mstot ? (double)dp->nread * 1000.0 / mstot : 0.0, dp->nread ? (double)dp->nrsect / dp->nread / 2.0 : 0.0, mstot ? (double)dp->nwrite * 1000.0 / mstot : 0.0, dp->nwrite ? (double)dp->nwsect / dp->nwrite / 2.0 : 0.0, dp->io_ms ? (double)dp->avque / dp->io_ms : 0.0, iotot ? (double)dp->io_ms / iotot : 0.0); postprint(badness); } if (nlines == 0) { printf("\n"); nlines++; } firstcall = 0; return nlines; } static int lvmline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { return gendskline(ss, tstamp, 'l'); } static int mddline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { return gendskline(ss, tstamp, 'm'); } static int dskline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { return gendskline(ss, tstamp, 'd'); } /* ** NFS client statistics */ static void nfmhead(int osvers, int osrel, int ossub) { printf("mounted_device physread/s physwrit/s" " _nfm_"); } static int nfmline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { static char firstcall = 1; register long i, nlines = 0; char *pn, state; int len; for (i=0; i < ss->nfs.nfsmounts.nrmounts; i++) /* per NFS mount */ { /* ** print for the first sample all mounts that ** are found; afterwards print only the mounts ** that were really active during the interval */ if (firstcall || allresources || ss->nfs.nfsmounts.nfsmnt[i].age < deltasec || ss->nfs.nfsmounts.nfsmnt[i].bytestotread || ss->nfs.nfsmounts.nfsmnt[i].bytestotwrite ) { if (nlines++) printf("%s ", tstamp); if ( (len = strlen(ss->nfs.nfsmounts.nfsmnt[i].mountdev)) > 38) pn = ss->nfs.nfsmounts.nfsmnt[i].mountdev + len - 38; else pn = ss->nfs.nfsmounts.nfsmnt[i].mountdev; if (ss->nfs.nfsmounts.nfsmnt[i].age < deltasec) state = 'M'; else state = ' '; printf("%-38s %10.3lfK %10.3lfK %c\n", pn, (double)ss->nfs.nfsmounts.nfsmnt[i].bytestotread / 1024 / deltasec, (double)ss->nfs.nfsmounts.nfsmnt[i].bytestotwrite / 1024 / deltasec, state); } } if (nlines == 0) { printf("\n"); nlines++; } firstcall= 0; return nlines; } static void nfchead(int osvers, int osrel, int ossub) { printf(" rpc/s rpcread/s rpcwrite/s retrans/s autrefresh/s " " _nfc_"); } static int nfcline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { printf("%10.2lf %10.2lf %10.2lf %10.2lf %12.2lf\n", (double)ss->nfs.client.rpccnt / deltasec, (double)ss->nfs.client.rpcread / deltasec, (double)ss->nfs.client.rpcwrite / deltasec, (double)ss->nfs.client.rpcretrans / deltasec, (double)ss->nfs.client.rpcautrefresh / deltasec); return 1; } static void nfshead(int osvers, int osrel, int ossub) { printf(" rpc/s rpcread/s rpcwrite/s MBcr/s MBcw/s " "nettcp/s netudp/s _nfs_"); } static int nfsline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { printf("%7.2lf %10.2lf %10.2lf %6.2lf %7.2lf %9.2lf %8.2lf\n", (double)ss->nfs.server.rpccnt / deltasec, (double)ss->nfs.server.rpcread / deltasec, (double)ss->nfs.server.rpcwrite / deltasec, (double)ss->nfs.server.nrbytes / 1024.0 / 1024.0 / deltasec, (double)ss->nfs.server.nwbytes / 1024.0 / 1024.0 / deltasec, (double)ss->nfs.server.nettcpcnt / deltasec, (double)ss->nfs.server.netudpcnt / deltasec); return 1; } /* ** network-interface statistics */ static void ibhead(int osvers, int osrel, int ossub) { printf("controller port busy ipack/s opack/s " "igbps ogbps maxgbps lanes _ib_"); } static int ibline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { static char firstcall = 1; register long i, nlines = 0; double busy; unsigned int badness; for (i=0; i < ss->ifb.nrports; i++) /* per interface */ { count_t ival, oval; /* ** print for the first sample all ports that ** are found; afterwards print only the ports ** that were really active during the interval */ if (!firstcall && !allresources && !ss->ifb.ifb[i].rcvb && !ss->ifb.ifb[i].sndb) continue; /* ** convert byte-transfers to bit-transfers (* 8) ** convert bit-transfers to gigabit-transfers (/ 1000000000) ** per second */ ival = ss->ifb.ifb[i].rcvb*ss->ifb.ifb[i].lanes/125000000/deltasec; oval = ss->ifb.ifb[i].sndb*ss->ifb.ifb[i].lanes/125000000/deltasec; /* ** calculate busy-percentage for port */ busy = (ival > oval ? ival*100 : oval*100)/ss->ifb.ifb[i].rate; if (nlines++) printf("%s ", tstamp); if (netbadness) badness = busy * 100 / netbadness; else badness = 0; preprint(badness); printf("%-10s %4hd %4.0f%% %7.1lf %7.1lf %5lld %5lld %7lld %5d", ss->ifb.ifb[i].ibname, ss->ifb.ifb[i].portnr, busy, (double)ss->ifb.ifb[i].rcvp / deltasec, (double)ss->ifb.ifb[i].sndp / deltasec, ival, oval, ss->ifb.ifb[i].rate / 1000, ss->ifb.ifb[i].lanes); postprint(badness); } if (nlines == 0) { printf("\n"); nlines++; } firstcall = 0; return nlines; } /* ** network-interface statistics */ static void ifhead(int osvers, int osrel, int ossub) { printf("interf busy ipack/s opack/s iKbyte/s oKbyte/s " "imbps ombps maxmbps_if_"); } static int ifline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { static char firstcall = 1; register long i, nlines = 0; double busy; char busyval[16], dupval; unsigned int badness; char *pn; int len; for (i=0; i < ss->intf.nrintf; i++) /* per interface */ { count_t ival, oval; /* ** print for the first sample all interfaces which ** are found; afterwards print only the interfaces ** which were really active during the interval */ if (!firstcall && !allresources && !ss->intf.intf[i].rpack && !ss->intf.intf[i].spack) continue; /* ** convert byte-transfers to bit-transfers (* 8) ** convert bit-transfers to megabit-transfers (/ 1000000) ** per second */ ival = ss->intf.intf[i].rbyte/125000/deltasec; oval = ss->intf.intf[i].sbyte/125000/deltasec; /* ** calculate busy-percentage for interface */ if (ss->intf.intf[i].speed) /* speed known? */ { if (ss->intf.intf[i].duplex) busy = (ival > oval ? ival*100 : oval*100) / ss->intf.intf[i].speed; else busy = (ival + oval) * 100 / ss->intf.intf[i].speed; // especially with wireless, the speed might have // dropped temporarily to a very low value (snapshot) // it might be better to take the speed of the // previous sample if (busy > 100 && ss->intf.intf[i].speed < ss->intf.intf[i].speedp ) { ss->intf.intf[i].speed = ss->intf.intf[i].speedp; if (ss->intf.intf[i].duplex) busy = (ival > oval ? ival*100 : oval*100) / ss->intf.intf[i].speed; else busy = (ival + oval) * 100 / ss->intf.intf[i].speed; } snprintf(busyval, sizeof busyval, "%3.0lf%%", busy); } else { strcpy(busyval, "?"); /* speed unknown */ busy = 0; } if (nlines++) printf("%s ", tstamp); if (ss->intf.intf[i].speed) { if (ss->intf.intf[i].duplex) dupval = 'f'; else dupval = 'h'; } else { dupval = ' '; } if (netbadness) badness = busy * 100 / netbadness; else badness = 0; if ( (len = strlen(ss->intf.intf[i].name)) > 6) pn = ss->intf.intf[i].name + len - 6; else pn = ss->intf.intf[i].name; preprint(badness); printf("%-6s %4s %7.1lf %7.1lf %8.0lf %8.0lf " "%5lld %5lld %7ld %c", pn, busyval, (double)ss->intf.intf[i].rpack / deltasec, (double)ss->intf.intf[i].spack / deltasec, (double)ss->intf.intf[i].rbyte / 1024 / deltasec, (double)ss->intf.intf[i].sbyte / 1024 / deltasec, ival, oval, ss->intf.intf[i].speed, dupval); postprint(badness); } if (nlines == 0) { printf("\n"); nlines++; } firstcall = 0; return nlines; } static void IFhead(int osvers, int osrel, int ossub) { printf("interf ierr/s oerr/s coll/s idrop/s odrop/s " "iframe/s ocarrier/s _if_"); } static int IFline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { static char firstcall = 1; register long i, nlines = 0; char *pn; int len; for (i=0; i < ss->intf.nrintf; i++) /* per interface */ { /* ** print for the first sample all interfaces which ** are found; afterwards print only the interfaces ** which were really active during the interval */ if (!firstcall && !allresources && !ss->intf.intf[i].rpack && !ss->intf.intf[i].spack) continue; if (nlines++) printf("%s ", tstamp); if ( (len = strlen(ss->intf.intf[i].name)) > 6) pn = ss->intf.intf[i].name + len - 6; else pn = ss->intf.intf[i].name; printf("%-6s %6.2lf %6.2lf %6.2lf %7.2lf %7.2lf " "%8.2lf %10.2lf\n", pn, (double)ss->intf.intf[i].rerrs / deltasec, (double)ss->intf.intf[i].serrs / deltasec, (double)ss->intf.intf[i].scollis / deltasec, (double)ss->intf.intf[i].rdrop / deltasec, (double)ss->intf.intf[i].sdrop / deltasec, (double)ss->intf.intf[i].rframe / deltasec, (double)ss->intf.intf[i].scarrier / deltasec); } if (nlines == 0) { printf("\n"); nlines++; } firstcall= 0; return nlines; } /* ** IP version 4 statistics */ static void ipv4head(int osvers, int osrel, int ossub) { printf("inrecv/s outreq/s indeliver/s forward/s " "reasmok/s fragcreat/s _ipv4_"); } static int ipv4line(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { printf("%8.1lf %8.1lf %11.1lf %9.1lf %9.1lf %11.1lf\n", (double)ss->net.ipv4.InReceives / deltasec, (double)ss->net.ipv4.OutRequests / deltasec, (double)ss->net.ipv4.InDelivers / deltasec, (double)ss->net.ipv4.Forwarding / deltasec, (double)ss->net.ipv4.ReasmOKs / deltasec, (double)ss->net.ipv4.FragCreates / deltasec); return 1; } static void IPv4head(int osvers, int osrel, int ossub) { printf("in: dsc/s hder/s ader/s unkp/s ratim/s rfail/s " "out: dsc/s nrt/s_ipv4_"); } static int IPv4line(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { printf(" %5.1lf %6.1lf %6.1lf %6.1lf %7.1lf %7.1lf " " %5.1lf %5.1lf\n", (double)ss->net.ipv4.InDiscards / deltasec, (double)ss->net.ipv4.InHdrErrors / deltasec, (double)ss->net.ipv4.InAddrErrors / deltasec, (double)ss->net.ipv4.InUnknownProtos / deltasec, (double)ss->net.ipv4.ReasmTimeout / deltasec, (double)ss->net.ipv4.ReasmFails / deltasec, (double)ss->net.ipv4.OutDiscards / deltasec, (double)ss->net.ipv4.OutNoRoutes / deltasec); return 1; } /* ** ICMP version 4 statistics */ static void icmpv4head(int osvers, int osrel, int ossub) { printf("intot/s outtot/s inecho/s inerep/s " "otecho/s oterep/s _icmpv4_" ); } static int icmpv4line(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { printf("%7.1lf %8.1lf %8.2lf %8.2lf %8.2lf %8.2lf\n", (double)ss->net.icmpv4.InMsgs / deltasec, (double)ss->net.icmpv4.OutMsgs / deltasec, (double)ss->net.icmpv4.InEchos / deltasec, (double)ss->net.icmpv4.OutEchos / deltasec, (double)ss->net.icmpv4.InEchoReps / deltasec, (double)ss->net.icmpv4.OutEchoReps / deltasec); return 1; } static void ICMPv4head(int osvers, int osrel, int ossub) { printf("ierr/s isq/s ird/s idu/s ite/s " "oerr/s osq/s ord/s odu/s ote/s_icmpv4_"); } static int ICMPv4line(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { printf("%6.2lf %5.2lf %5.2lf %5.2lf %5.2lf " "%6.2lf %5.2lf %5.2lf %5.2lf %5.2lf\n", (double)ss->net.icmpv4.InErrors / deltasec, (double)ss->net.icmpv4.InSrcQuenchs / deltasec, (double)ss->net.icmpv4.InRedirects / deltasec, (double)ss->net.icmpv4.InDestUnreachs / deltasec, (double)ss->net.icmpv4.InTimeExcds / deltasec, (double)ss->net.icmpv4.OutErrors / deltasec, (double)ss->net.icmpv4.OutSrcQuenchs / deltasec, (double)ss->net.icmpv4.OutRedirects / deltasec, (double)ss->net.icmpv4.OutDestUnreachs / deltasec, (double)ss->net.icmpv4.OutTimeExcds / deltasec); return 1; } /* ** UDP version 4 statistics */ static void udpv4head(int osvers, int osrel, int ossub) { printf("indgram/s outdgram/s inerr/s noport/s " " _udpv4_"); } static int udpv4line(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { printf("%9.1lf %10.1lf %7.2lf %9.2lf\n", (double)ss->net.udpv4.InDatagrams / deltasec, (double)ss->net.udpv4.OutDatagrams / deltasec, (double)ss->net.udpv4.InErrors / deltasec, (double)ss->net.udpv4.NoPorts / deltasec); return 1; } /* ** IP version 6 statistics */ static void ipv6head(int osvers, int osrel, int ossub) { printf("inrecv/s outreq/s inmc/s outmc/s indeliv/s " "reasmok/s fragcre/s _ipv6_"); } static int ipv6line(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { printf("%8.1lf %8.1lf %6.1lf %7.1lf %9.1lf %9.1lf %9.1lf\n", (double)ss->net.ipv6.Ip6InReceives / deltasec, (double)ss->net.ipv6.Ip6OutRequests / deltasec, (double)ss->net.ipv6.Ip6InMcastPkts / deltasec, (double)ss->net.ipv6.Ip6OutMcastPkts / deltasec, (double)ss->net.ipv6.Ip6InDelivers / deltasec, (double)ss->net.ipv6.Ip6ReasmOKs / deltasec, (double)ss->net.ipv6.Ip6FragCreates / deltasec); return 1; } static void IPv6head(int osvers, int osrel, int ossub) { printf("in: dsc/s hder/s ader/s unkp/s ratim/s rfail/s " "out: dsc/s nrt/s_ipv6_"); } static int IPv6line(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { printf(" %5.1lf %6.1lf %6.1lf %6.1lf %7.1lf %7.1lf " " %5.1lf %5.1lf\n", (double)ss->net.ipv6.Ip6InDiscards / deltasec, (double)ss->net.ipv6.Ip6InHdrErrors / deltasec, (double)ss->net.ipv6.Ip6InAddrErrors / deltasec, (double)ss->net.ipv6.Ip6InUnknownProtos / deltasec, (double)ss->net.ipv6.Ip6ReasmTimeout / deltasec, (double)ss->net.ipv6.Ip6ReasmFails / deltasec, (double)ss->net.ipv6.Ip6OutDiscards / deltasec, (double)ss->net.ipv6.Ip6OutNoRoutes / deltasec); return 1; } /* ** ICMP version 6 statistics */ static void icmpv6head(int osvers, int osrel, int ossub) { printf("intot/s outtot/s inerr/s innsol/s innadv/s " "otnsol/s otnadv/s _icmp6_" ); } static int icmpv6line(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { printf("%7.1lf %8.1lf %7.2lf %8.2lf %8.2lf %8.2lf %8.2lf\n", (double)ss->net.icmpv6.Icmp6InMsgs / deltasec, (double)ss->net.icmpv6.Icmp6OutMsgs / deltasec, (double)ss->net.icmpv6.Icmp6InErrors / deltasec, (double)ss->net.icmpv6.Icmp6InNeighborSolicits / deltasec, (double)ss->net.icmpv6.Icmp6InNeighborAdvertisements/ deltasec, (double)ss->net.icmpv6.Icmp6OutNeighborSolicits / deltasec, (double)ss->net.icmpv6.Icmp6OutNeighborAdvertisements /deltasec); return 1; } static void ICMPv6head(int osvers, int osrel, int ossub) { printf("iecho/s ierep/s oerep/s idu/s odu/s ird/s ord/s ite/s " "ote/s _icmpv6_"); } static int ICMPv6line(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { printf("%7.2lf %7.2lf %7.2lf %5.2lf %5.2lf " "%5.2lf %5.2lf %5.2lf %5.2lf\n", (double)ss->net.icmpv6.Icmp6InEchos / deltasec, (double)ss->net.icmpv6.Icmp6InEchoReplies / deltasec, (double)ss->net.icmpv6.Icmp6OutEchoReplies / deltasec, (double)ss->net.icmpv6.Icmp6InDestUnreachs / deltasec, (double)ss->net.icmpv6.Icmp6OutDestUnreachs / deltasec, (double)ss->net.icmpv6.Icmp6InRedirects / deltasec, (double)ss->net.icmpv6.Icmp6OutRedirects / deltasec, (double)ss->net.icmpv6.Icmp6InTimeExcds / deltasec, (double)ss->net.icmpv6.Icmp6OutTimeExcds / deltasec); return 1; } /* ** UDP version 6 statistics */ static void udpv6head(int osvers, int osrel, int ossub) { printf("indgram/s outdgram/s inerr/s noport/s " " _udpv6_"); } static int udpv6line(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { printf("%9.1lf %10.1lf %7.2lf %9.2lf\n", (double)ss->net.udpv6.Udp6InDatagrams / deltasec, (double)ss->net.udpv6.Udp6OutDatagrams / deltasec, (double)ss->net.udpv6.Udp6InErrors / deltasec, (double)ss->net.udpv6.Udp6NoPorts / deltasec); return 1; } /* ** TCP statistics */ static void tcphead(int osvers, int osrel, int ossub) { printf("insegs/s outsegs/s actopen/s pasopen/s " "nowopen _tcp_"); } static int tcpline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { printf("%8.1lf %9.1lf %9.1lf %9.1lf %7lld\n", (double)ss->net.tcp.InSegs / deltasec, (double)ss->net.tcp.OutSegs / deltasec, (double)ss->net.tcp.ActiveOpens / deltasec, (double)ss->net.tcp.PassiveOpens / deltasec, ss->net.tcp.CurrEstab); return 1; } static void TCPhead(int osvers, int osrel, int ossub) { printf("inerr/s retrans/s attfail/s " "estabreset/s outreset/s _tcp_"); } static int TCPline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { printf("%7.1lf %9.1lf %9.1lf %12.1lf %10.1lf\n", (double)ss->net.tcp.InErrs / deltasec, (double)ss->net.tcp.RetransSegs / deltasec, (double)ss->net.tcp.AttemptFails / deltasec, (double)ss->net.tcp.EstabResets / deltasec, (double)ss->net.tcp.OutRsts / deltasec); return 1; } #if HTTPSTATS static void httphead(int osvers, int osrel, int ossub) { printf("requests/s Kbytes/s bytes/req " "idleworkers busyworkers _http_"); } static int httpline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { printf("%10.2lf %8.2lf %9.2lf %11d %11d\n", (double)ss->www.accesses / deltasec, (double)ss->www.totkbytes / deltasec, ss->www.accesses ? (double)ss->www.totkbytes*1024/ss->www.accesses : 0, ss->www.iworkers, ss->www.bworkers); return 1; } #endif /* ** per-process statistics: top-3 processor consumers */ static void topchead(int osvers, int osrel, int ossub) { printf(" pid command cpu%% | pid command cpu%% | " " pid command cpu%%_top3_"); } static int topcline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { count_t availcpu; if (!ts) { printf("report not available.....\n"); return 0; } /* ** sort process list in cpu order */ qsort(ps, nactproc, sizeof(struct tstat *), compcpu); availcpu = ss->cpu.all.stime + ss->cpu.all.utime + ss->cpu.all.ntime + ss->cpu.all.itime + ss->cpu.all.wtime + ss->cpu.all.Itime + ss->cpu.all.Stime + ss->cpu.all.steal; availcpu /= ss->cpu.nrcpu; if (availcpu == 0) availcpu = 1; /* avoid divide-by-zero */ if (nactproc >= 1 && (ps[0])->cpu.stime + (ps[0])->cpu.utime > 0) printf("%5d %-8.8s %3.0lf%% | ", (ps[0])->gen.pid, (ps[0])->gen.name, (double)((ps[0])->cpu.stime + (ps[0])->cpu.utime)*100.0/availcpu); else printf("%19s | ", " "); if (nactproc >= 2 && (ps[1])->cpu.stime + (ps[1])->cpu.utime > 0) printf("%5d %-8.8s %3.0lf%% | ", (ps[1])->gen.pid, (ps[1])->gen.name, (double)((ps[1])->cpu.stime + (ps[1])->cpu.utime)*100.0/availcpu); else printf("%19s | ", " "); if (nactproc >= 3 && (ps[2])->cpu.stime + (ps[2])->cpu.utime > 0) printf("%5d %-8.8s %3.0lf%%\n", (ps[2])->gen.pid, (ps[2])->gen.name, (double)((ps[2])->cpu.stime + (ps[2])->cpu.utime)*100.0/availcpu); else printf("%19s\n", " "); return 1; } /* ** per-process statistics: top-3 memory consumers */ static void topmhead(int osvers, int osrel, int ossub) { printf(" pid command mem%% | pid command mem%% | " " pid command mem%%_top3_"); } static int topmline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { count_t availmem; if (!ts) { printf("report not available.....\n"); return 0; } /* ** sort process list in memory order */ qsort(ps, nactproc, sizeof(struct tstat *), compmem); availmem = ss->mem.physmem * pagesize/1024; if (nactproc >= 1) printf("%5d %-8.8s %3.0lf%% | ", (ps[0])->gen.pid, (ps[0])->gen.name, (double)(ps[0])->mem.rmem * 100.0 / availmem); else printf("%19s | ", " "); if (nactproc >= 2) printf("%5d %-8.8s %3.0lf%% | ", (ps[1])->gen.pid, (ps[1])->gen.name, (double)(ps[1])->mem.rmem * 100.0 / availmem); else printf("%19s | ", " "); if (nactproc >= 3) printf("%5d %-8.8s %3.0lf%%\n", (ps[2])->gen.pid, (ps[2])->gen.name, (double)(ps[2])->mem.rmem * 100.0 / availmem); else printf("%19s\n", " "); return 1; } /* ** per-process statistics: top-3 disk consumers */ static void topdhead(int osvers, int osrel, int ossub) { printf(" pid command dsk%% | pid command dsk%% | " " pid command dsk%%_top3_"); } static int topdline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { int i; count_t availdsk; if (!ts) { printf("report not available.....\n"); return 0; } if ( !(supportflags & IOSTAT) ) { printf("no per-process disk counters available.....\n"); return 0; } /* ** determine total disk accesses for all processes */ for (i=0, availdsk=0; i < nactproc; i++) { availdsk += (ps[i])->dsk.rio + (ps[i])->dsk.wio; } if (availdsk == 0) availdsk = 1; /* ** sort process list in disk order */ qsort(ps, nactproc, sizeof(struct tstat *), compdsk); if (nactproc >= 1 && (ps[0])->dsk.rio + (ps[0])->dsk.wio > 0) printf("%5d %-8.8s %3.0lf%% | ", (ps[0])->gen.pid, (ps[0])->gen.name, (double)((ps[0])->dsk.rio+(ps[0])->dsk.wio) *100.0/availdsk); else printf("%19s | ", " "); if (nactproc >= 2 && (ps[1])->dsk.rio + (ps[1])->dsk.wio > 0) printf("%5d %-8.8s %3.0lf%% | ", (ps[1])->gen.pid, (ps[1])->gen.name, (double)((ps[1])->dsk.rio+(ps[1])->dsk.wio) *100.0/availdsk); else printf("%19s | ", " "); if (nactproc >= 3 && (ps[2])->dsk.rio + (ps[2])->dsk.wio > 0) printf("%5d %-8.8s %3.0lf%%\n", (ps[2])->gen.pid, (ps[2])->gen.name, (double)((ps[2])->dsk.rio+(ps[2])->dsk.wio) *100.0/availdsk); else printf("%19s\n", " "); return 1; } /* ** per-process statistics: top-3 network consumers */ static void topnhead(int osvers, int osrel, int ossub) { printf(" pid command net%% | pid command net%% | " " pid command net%%_top3_"); } static int topnline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { int i; count_t availnet; count_t totbytes; if (!ts) { printf("report not available.....\n"); return 0; } if ( !(supportflags & NETATOP) ) { printf("no per-process network counters available.....\n"); return 0; } /* ** determine total network accesses for all processes */ for (i=0, availnet=0; i < nactproc; i++) { availnet += (*(ps+i))->net.tcpssz + (*(ps+i))->net.tcprsz + (*(ps+i))->net.udpssz + (*(ps+i))->net.udprsz; } if (availnet == 0) availnet = 1; /* ** sort process list in network order */ qsort(ps, nactproc, sizeof(struct tstat *), compnet); if (nactproc >= 1) { totbytes = (ps[0])->net.tcpssz + (ps[0])->net.tcprsz + (ps[0])->net.udpssz + (ps[0])->net.udprsz; if (totbytes > 0) printf("%5d %-8.8s %3.0lf%% | ", (ps[0])->gen.pid, (ps[0])->gen.name, (double)totbytes * 100.0 / availnet); else printf("%19s | ", " "); } else printf("%19s | ", " "); if (nactproc >= 2) { totbytes = (ps[1])->net.tcpssz + (ps[1])->net.tcprsz + (ps[1])->net.udpssz + (ps[1])->net.udprsz; if (totbytes > 0) printf("%5d %-8.8s %3.0lf%% | ", (ps[1])->gen.pid, (ps[1])->gen.name, (double)totbytes * 100.0 / availnet); else printf("%19s | ", " "); } else printf("%19s | ", " "); if (nactproc >= 3) { totbytes = (ps[2])->net.tcpssz + (ps[2])->net.tcprsz + (ps[2])->net.udpssz + (ps[2])->net.udprsz; if (totbytes > 0) printf("%5d %-8.8s %3.0lf%%\n", (ps[2])->gen.pid, (ps[2])->gen.name, (double)totbytes * 100.0 / availnet); else printf("%19s\n", " "); } else printf("%19s\n", " "); return 1; } /*********************************************************************/ /* Function definition table. */ /* */ /* The layout of this table is as follows: */ /* Column 1: */ /* Boolean which indicates if the specified function is */ /* active during a run of 'atopsar'. When started, */ /* this boolean will be defined 'true' for all entries for */ /* which the command-line flag has been specified. Initially */ /* this column should contain 0 (false), unless this function */ /* is always required. */ /* If no flags are specified for 'atopsar', the first entry */ /* in this table is defined active (default flag). */ /* */ /* Column 2: */ /* Categories of counters used by this function. */ /* c = cpu counters, m = memory counters, */ /* d = disk counters, n = network counters */ /* */ /* Column 3: */ /* Flag which can be used as command-line argument to */ /* select the function defined in this table-entry. Be sure */ /* that a unique character is choosen. */ /* Notice that certain flags are reserved! */ /* */ /* Column 4: */ /* Entry-point of the 'printhead' function. */ /* */ /* Column 5: */ /* Entry-point of the 'printline' function. */ /* */ /* Column 6: */ /* Information about the statistics shown by the function */ /* specified by the table-entry. This text is printed as */ /* command-usage. */ /*********************************************************************/ struct pridef pridef[] = { {0, "c", 'c', cpuhead, cpuline, "cpu utilization", }, {0, "c", 'p', prochead, procline, "process(or) load", }, {0, "c", 'P', taskhead, taskline, "processes & threads", }, {0, "c", 'g', gpuhead, gpuline, "gpu utilization", }, {0, "m", 'm', memhead, memline, "memory & swapspace", }, {0, "m", 's', swaphead, swapline, "swap rate", }, {0, "cmd",'B', psihead, psiline, "pressure stall info (PSI)",}, {0, "cd", 'l', lvmhead, lvmline, "logical volume activity", }, {0, "cd", 'f', mddhead, mddline, "multiple device activity",}, {0, "cd", 'd', dskhead, dskline, "disk activity", }, {0, "n", 'h', ibhead, ibline, "infiniband utilization", }, {0, "n", 'n', nfmhead, nfmline, "NFS client mounts", }, {0, "n", 'j', nfchead, nfcline, "NFS client activity", }, {0, "n", 'J', nfshead, nfsline, "NFS server activity", }, {0, "n", 'i', ifhead, ifline, "net-interf (general)", }, {0, "n", 'I', IFhead, IFline, "net-interf (errors)", }, {0, "n", 'w', ipv4head, ipv4line, "ip v4 (general)", }, {0, "n", 'W', IPv4head, IPv4line, "ip v4 (errors)", }, {0, "n", 'y', icmpv4head, icmpv4line, "icmp v4 (general)", }, {0, "n", 'Y', ICMPv4head, ICMPv4line, "icmp v4 (per type)", }, {0, "n", 'u', udpv4head, udpv4line, "udp v4", }, {0, "n", 'z', ipv6head, ipv6line, "ip v6 (general)", }, {0, "n", 'Z', IPv6head, IPv6line, "ip v6 (errors)", }, {0, "n", 'k', icmpv6head, icmpv6line, "icmp v6 (general)", }, {0, "n", 'K', ICMPv6head, ICMPv6line, "icmp v6 (per type)", }, {0, "n", 'U', udpv6head, udpv6line, "udp v6", }, {0, "n", 't', tcphead, tcpline, "tcp (general)", }, {0, "n", 'T', TCPhead, TCPline, "tcp (errors)", }, #if HTTPSTATS {0, "n", 'o', httphead, httpline, "HTTP activity", }, #endif {0, "", 'O', topchead, topcline, "top-3 processes cpu", }, {0, "", 'G', topmhead, topmline, "top-3 processes memory", }, {0, "", 'D', topdhead, topdline, "top-3 processes disk", }, {0, "", 'N', topnhead, topnline, "top-3 processes network",}, }; int pricnt = sizeof(pridef)/sizeof(struct pridef); atop-2.12.1/cgroups.c0000644000203100020310000012232715064552761013710 0ustar gerlofgerlof/* ** ATOP - System & Process Monitor ** ** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as process-/thread-level. ** ** This source-file contains functions to read and handle cgroup metrics. ** ========================================================================== ** Author: Gerlof Langeveld ** E-mail: gerlof.langeveld@atoptool.nl ** Initial date: January 2024 ** -------------------------------------------------------------------------- ** Copyright (C) 2024 Gerlof Langeveld ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ** -------------------------------------------------------------------------- */ #define _POSIX_C_SOURCE #define _XOPEN_SOURCE #define _GNU_SOURCE #define _DEFAULT_SOURCE #include #include #include #include #include #include #include #include #include #include #include "atop.h" #include "cgroups.h" #include "photosyst.h" #include "photoproc.h" #include "showgeneric.h" static void cgcalcdeviate(void); static void cgrewind(struct cgchainer **); static struct cgchainer *cgnext(struct cgchainer **, struct cgchainer **); static void cgwipe(struct cgchainer **, struct cgchainer **, struct cgchainer **, struct cgchainer **); static unsigned long walkcgroup(char *, struct cgchainer *, int, long, int, int); static void getconfig(struct cstat *, struct cstat *); static int readconfigval(char *, count_t []); static void getmetrics(struct cstat *); static void getpressure(char *, count_t *, count_t *); static long hashcalc(char *, long, int); static void hashadd(struct cgchainer *[], struct cgchainer *); static struct cgchainer *hashfind(struct cgchainer *[], long); static int cgroupfilter(struct cstat *, int, char); #define CGROUPROOT "/sys/fs/cgroup" #define CGROUPNHASH 128 // power of 2 #define CGROUPMASK (CGROUPNHASH-1) // cgroup administration // ===================== // three types of cgroup collections are maintained: // // - current linked list // ------------------- // this administration consists of a chain of cgchainer structs // that each refer to a cstat struct containing the cgroup metrics // // this chain is built while gathering the current information by the // walkcgroup() function // this function mallocs one cgchainer struct and one cstat struct // // - previous linked list // -------------------- // this administration consists of a chain of cgchainer structs // that each refer to a cstat struct containing the previous cgroup metrics // // before building a new current chain, the current chain will be // saved as the previous chain // // - deviation array // --------------- // this administration consists of an array(!) of cgchainer structs in which // the cgchainer struct are still chained to the next element in the array // // current cgroup admi // static struct cgchainer *cgcurfirst, // first in linked list *cgcurlast, // last in linked list *cgcurcursor; static unsigned int cgcursize; // total size of all cstat structs static unsigned int cgcurnum; // number of cstat structs static unsigned int cgcurprocs; // number of processes in current list static int cgcursequence; // maintain sequence number to be // assigned to every cgchainer struct // // later on this value can be used // as index in the deviation array // previous cgroup admi // static struct cgchainer *cgprefirst, // first in linked list *cgprelast, // last in linked list *cgprecursor, *cgprehash[CGROUPNHASH]; // deviation cgroup admi // static struct cgchainer *cgdevfirst, // pointer to array (!) *cgdevcursor; // Check if cgroup v2 is supported on this machine // // Return value: 1 - yes // 0 - no // int cgroupv2support(void) { FILE *fp; char line[128]; // check if this kernel offers cgroups version 2 // if ( (fp = fopen("/proc/1/cgroup", "r")) ) { while (fgets(line, sizeof line, fp)) { if (memcmp(line, "0::", 3) == 0) // equal? { supportflags |= CGROUPV2; break; } } fclose(fp); } return (supportflags&CGROUPV2) > 0; } // Gather all metrics from the entire cgroup tree, // creating a chain of cgchainer structs // void photocgroup(void) { char origdir[4096]; // wipe previous cgroup chain (not needed any more) // cgwipe(&cgprefirst, &cgprelast, &cgprecursor, cgprehash); // move current cgroup chain to previous cgroup chain // cgprefirst = cgcurfirst; cgprelast = cgcurlast; cgcurfirst = cgcurlast = cgcurcursor = NULL; // wipe deviation cgroup memory // - wipe all contiguous cstat structs (start address in first cgchainer) // - wipe pidlist (start address in first cgchainer) // - wipe all contiguous cgchainer structs // if (cgdevfirst) { free(cgdevfirst->cstat); free(cgdevfirst->proclist); free(cgdevfirst); } // save current directory and move to top directory // of cgroup fs // if ( getcwd(origdir, sizeof origdir) == NULL) mcleanstop(53, "failed to save current dir\n"); if ( chdir(CGROUPROOT) == -1) mcleanstop(54, "failed to change to " CGROUPROOT "\n"); // read all subdirectory names below the cgroup top directory // cgcursequence = 0; cgcursize = 0; cgcurnum = 0; cgcurprocs = 0; walkcgroup(".", NULL, -1, 0, 0, 0); // return to original directory // if ( chdir(origdir) == -1) mcleanstop(55, "cannot change to %s\n", origdir); } // Gather statistics from one cgroup directory level // and walk the directory tree further downwards by nested calls. // Every call to this function will add one struct to the current // cgroup chain. // // Return value: number of processes in this dir and the dirs underneath // -1 = fail // static unsigned long walkcgroup(char *dirname, struct cgchainer *cparent, int parentseq, long upperhash, int upperlen, int depth) { FILE *fp; DIR *dirp; struct dirent *entp; int namelen = strlen(dirname); int cstatlen, i; unsigned long procsbelow=0, proccnt=0, hash; struct cgchainer *ccp; // change to new directory // if ( chdir(dirname) == -1) return -1; // -------------------------------------------- // gather statistics for this cgroup directory // -------------------------------------------- // - create new cgchainer struct // ccp = calloc(1, sizeof(struct cgchainer)); ptrverify(ccp, "Malloc failed for current cgchainer\n"); // - add cgchainer to current chain // if (cgcurfirst) { cgcurlast->next = ccp; cgcurlast = ccp; } else { cgcurfirst = ccp; cgcurlast = ccp; } // - create corresponding cstat struct // with a rounded length to avoid unaligned structs later on // cstatlen = sizeof(struct cstat) + namelen + 1; if (cstatlen & 0x7) // length is not 64-bit multiple? cstatlen = ((cstatlen >> 3) + 1) << 3; // round up to 64-bit ccp->cstat = calloc(1, cstatlen); ptrverify(ccp->cstat, "Malloc failed for cstat of %d bytes\n", cstatlen); cgcursize += cstatlen; cgcurnum++; // - determine number of processes in this cgroup directory // if ( (fp = fopen("cgroup.procs", "r")) ) { char line[64]; while (fgets(line, sizeof line, fp)) proccnt++; fclose(fp); } // - create corresponding pid list to cgchainer // and fill it with the PIDs // if (proccnt) { ccp->proclist = malloc(sizeof(pid_t) * proccnt); ptrverify(ccp->proclist, "Malloc failed for proclist (%d pids)\n", proccnt); } else { ccp->proclist = NULL; } i = 0; if ( (fp = fopen("cgroup.procs", "r")) ) { char line[64]; while (fgets(line, sizeof line, fp)) { *(ccp->proclist+i) = strtol(line, NULL, 10); if (++i >= proccnt) break; } fclose(fp); } proccnt = i; // number of processes might have shrunk cgcurprocs += proccnt; // - fill basic info in current cgchainer // strcpy(ccp->cstat->cgname, dirname); // copy directory name if (*dirname == '.') // top directory? { ccp->cstat->cgname[0] = '\0'; // wipe name namelen = 0; } hash = hashcalc(ccp->cstat->cgname, upperhash, upperlen); ccp->cstat->gen.structlen = cstatlen; ccp->cstat->gen.sequence = cgcursequence++; // assign unique sequence number ccp->cstat->gen.parentseq = parentseq; ccp->cstat->gen.depth = depth; ccp->cstat->gen.nprocs = proccnt; ccp->cstat->gen.namelen = namelen; ccp->cstat->gen.fullnamelen = upperlen + namelen; // excluding slashes ccp->cstat->gen.namehash = hash; // - gather and store current cgroup configuration // getconfig(ccp->cstat, cparent ? cparent->cstat : NULL); // - gather and store current cgroup metrics // getmetrics(ccp->cstat); // -------------------------------------------- // walk subdirectories by nested calls // -------------------------------------------- // dirp = opendir("."); while ( (entp = readdir(dirp)) ) { // skip dot files/directories // if (entp->d_name[0] == '.') continue; // check if this entry represents a subdirectory // to be walked as well // if (entp->d_type == DT_DIR) procsbelow += walkcgroup(entp->d_name, ccp, ccp->cstat->gen.sequence, hash, upperlen+namelen, depth+1); } closedir(dirp); chdir(".."); ccp->cstat->gen.procsbelow = procsbelow; return procsbelow + proccnt; } // Gather configuration values for one specific cgroup // value -2: undefined // value -1: maximum // static void getconfig(struct cstat *csp, struct cstat *csparent) { count_t retvals[2]; // get cpu.weight // csp->conf.cpuweight = -2; // initial value (undefined) switch (readconfigval("cpu.weight", retvals)) { case 1: csp->conf.cpuweight = retvals[0]; break; } // get cpu.max limitation // csp->conf.cpumax = -2; // initial value (undefined) switch (readconfigval("cpu.max", retvals)) { case 2: if (retvals[0] == -1) csp->conf.cpumax = -1; // max else csp->conf.cpumax = retvals[0] * 100 / retvals[1]; break; } // get io.bpf.weight // csp->conf.dskweight = -2; // initial value (undefined) switch (readconfigval("io.bfq.weight", retvals)) { case 2: csp->conf.dskweight = retvals[1]; break; } // get memory.max limitation // csp->conf.memmax = -2; // initial value (undefined) switch (readconfigval("memory.max", retvals)) { case 1: if (retvals[0] == -1) csp->conf.memmax = -1; // max else csp->conf.memmax = retvals[0] / pagesize; break; } // get memory.swap.max limitation // csp->conf.swpmax = -2; // initial value (undefined) switch (readconfigval("memory.swap.max", retvals)) { case 1: if (retvals[0] == -1) csp->conf.swpmax = -1; // max else csp->conf.swpmax = retvals[0] / pagesize; break; } } // read line with one or two values and // fill one or (maximum) two values // in retvals (value 'max' converted into -1) // // return value: number of entries in retvals filled // static int readconfigval(char *fname, count_t retvals[]) { char line[64]; int n; FILE *fp; if ( (fp = fopen(fname, "r")) ) { char firststr[16]; if (!fgets(line, sizeof line, fp)) { fclose(fp); return 0; } fclose(fp); switch (n = sscanf(line, "%15s %llu", firststr, &retvals[1])) { case 0: return 0; case 1: case 2: if ( strcmp(firststr, "max") == 0) retvals[0] = -1; else retvals[0] = atol(firststr); return n; default: return 0; } } return 0; } // Gather metrics for one specific cgroup // static void getmetrics(struct cstat *csp) { FILE *fp; char line[256]; int cnt; // gather CPU metrics // csp->cpu.utime = -1; // undefined csp->cpu.stime = -1; // undefined cnt = 0; if ( (fp = fopen("cpu.stat", "r")) ) { while (fgets(line, sizeof line, fp) && cnt < 2) { if (memcmp(line, "user_usec ", 10) == 0) { sscanf(line, "user_usec %lld", &(csp->cpu.utime)); cnt++; continue; } if (memcmp(line, "system_usec ", 12) == 0) { sscanf(line, "system_usec %lld", &(csp->cpu.stime)); cnt++; continue; } } fclose(fp); } getpressure("cpu.pressure", &(csp->cpu.somepres), &(csp->cpu.fullpres)); // gather memory metrics // csp->mem.current = -1; // undefined csp->mem.anon = -1; // undefined csp->mem.file = -1; // undefined csp->mem.kernel = -1; // undefined csp->mem.shmem = -1; // undefined cnt = 0; if ( (fp = fopen("memory.current", "r")) ) { if (fgets(line, sizeof line, fp) != NULL) csp->mem.current = strtoll(line, NULL, 10) / pagesize; fclose(fp); } if ( (fp = fopen("memory.stat", "r")) ) { while (fgets(line, sizeof line, fp) && cnt < 4) { if (memcmp(line, "anon ", 5) == 0) { sscanf(line, "anon %lld", &(csp->mem.anon)); csp->mem.anon /= pagesize; cnt++; continue; } if (memcmp(line, "file ", 5) == 0) { sscanf(line, "file %lld", &(csp->mem.file)); csp->mem.file /= pagesize; cnt++; continue; } if (memcmp(line, "kernel ", 7) == 0) { sscanf(line, "kernel %lld", &(csp->mem.kernel)); csp->mem.kernel /= pagesize; cnt++; continue; } if (memcmp(line, "shmem ", 6) == 0) { sscanf(line, "shmem %lld", &(csp->mem.shmem)); csp->mem.shmem /= pagesize; cnt++; continue; } } fclose(fp); } getpressure("memory.pressure", &(csp->mem.somepres), &(csp->mem.fullpres)); // gather disk I/O metrics // // 253:2 rbytes=2544128 wbytes=2192896 rios=313 wios=22 dbytes=0 dios=0 // 253:1 rbytes=143278080 wbytes=3843604480 rios=34222 wios=853554 ... // 253:0 rbytes=19492288000 wbytes=105266814976 rios=386493 wios=1691322 // 11:0 // 8:0 rbytes=328956266496 wbytes=109243312640 rios=764129 wios=1415575 // csp->dsk.rbytes = 0; csp->dsk.wbytes = 0; csp->dsk.rios = 0; csp->dsk.wios = 0; if ( (fp = fopen("io.stat", "r")) ) { int major, minor; count_t rbytes, wbytes, rios, wios; while (fgets(line, sizeof line, fp)) { if (sscanf(line, "%d:%d rbytes=%lld wbytes=%lld rios=%lld wios=%lld", &major, &minor, &rbytes, &wbytes, &rios, &wios) == 6) { if (isdisk_major(major) == DSKTYPE) { csp->dsk.rbytes += rbytes; csp->dsk.wbytes += wbytes; csp->dsk.rios += rios; csp->dsk.wios += wios; } } } fclose(fp); } else { csp->dsk.rbytes = -1; // undefined csp->dsk.wbytes = -1; // undefined csp->dsk.rios = -1; // undefined csp->dsk.wios = -1; // undefined } getpressure("io.pressure", &(csp->dsk.somepres), &(csp->dsk.fullpres)); } // Get total pressure values from file with a format similar to: // // some avg10=0.00 avg60=0.00 avg300=0.00 total=9660682563 // full avg10=0.00 avg60=0.00 avg300=0.00 total=9376892229 // static void getpressure(char *fname, count_t *some, count_t *full) { char psiformat[] = "%c%*s avg10=%f avg60=%f avg300=%f total=%llu", linebuf[256], psitype; float a10, a60, a300; FILE *fp; *some = -1; // initially undefined *full = -1; // initially undefined if ( (fp = fopen(fname, "r")) != NULL) { // handle first line: 'some' pressure // if ( fgets(linebuf, sizeof(linebuf), fp) != NULL) { sscanf(linebuf, psiformat, &psitype, &a10, &a60, &a300, some); } // handle second line: 'full' pressure // if ( fgets(linebuf, sizeof(linebuf), fp) != NULL) { sscanf(linebuf, psiformat, &psitype, &a10, &a60, &a300, full); } fclose(fp); } } // Calculate deviations between current cgroup chain and // previous cgroup chain. // Notice that the deviation chain structure is identical to // the current chain structure, so they can be walked alongside. // Also the deviation cstat struct is already a copy of the current // cstat structure. // static void cgcalcdeviate(void) { struct cgchainer *dp, *pp; int insync = 1, prevfound; // walk thru the deviation chain and calculate the differences // with the previous chain // // when no cgroups have been added or removed since the // previous sample, we might assume that the previous chain // has the same order of elements as the deviation chain (insync) // // when cgroups have been added or removed since the // previous sample (!insync), we build a hash list for the // previous chain to find the correct entry based on the hashed name // cgrewind(&cgdevcursor); cgrewind(&cgprecursor); while ( (dp = cgnext(&cgdevfirst, &cgdevcursor)) ) { // find previous cgroup struct // prevfound = 0; if (insync) { // assume unchanged cgroup chain: take next from previous chain // pp = cgnext(&cgprefirst, &cgprecursor); if (pp && dp->cstat->gen.namehash == pp->cstat->gen.namehash) { prevfound = 1; } else { insync = 0; // build hash list for previous chain to find names // based on hashed name for the rest of this loop // cgrewind(&cgprecursor); while ( (pp = cgnext(&cgprefirst, &cgprecursor)) ) hashadd(cgprehash, pp); } } if (!insync) { // modified cgroup chain: search previous by name hash // if ( (pp = hashfind(cgprehash, dp->cstat->gen.namehash)) ) prevfound = 1; } // calculate deviations related to previous sample // if (prevfound) { if (dp->cstat->cpu.utime != -1) // defined? dp->cstat->cpu.utime -= pp->cstat->cpu.utime; if (dp->cstat->cpu.stime != -1) // defined? dp->cstat->cpu.stime -= pp->cstat->cpu.stime; if (dp->cstat->cpu.somepres != -1) // defined? dp->cstat->cpu.somepres -= pp->cstat->cpu.somepres; if (dp->cstat->cpu.fullpres != -1) // defined? dp->cstat->cpu.fullpres -= pp->cstat->cpu.fullpres; if (dp->cstat->mem.somepres != -1) // defined? dp->cstat->mem.somepres -= pp->cstat->mem.somepres; if (dp->cstat->mem.fullpres != -1) // defined? dp->cstat->mem.fullpres -= pp->cstat->mem.fullpres; if (dp->cstat->dsk.rbytes != -1) // defined? { dp->cstat->dsk.rbytes -= pp->cstat->dsk.rbytes; dp->cstat->dsk.wbytes -= pp->cstat->dsk.wbytes; dp->cstat->dsk.rios -= pp->cstat->dsk.rios; dp->cstat->dsk.wios -= pp->cstat->dsk.wios; } if (dp->cstat->dsk.somepres != -1) // defined? dp->cstat->dsk.somepres -= pp->cstat->dsk.somepres; if (dp->cstat->dsk.fullpres != -1) // defined? dp->cstat->dsk.fullpres -= pp->cstat->dsk.fullpres; } } } // Rewind cglist // static void cgrewind(struct cgchainer **cursor) { *cursor = NULL; } // Get next cgchainer from current cglist // static struct cgchainer * cgnext(struct cgchainer **first, struct cgchainer **cursor) { if (! *cursor) *cursor = *first; else *cursor = (*cursor)->next; if (*cursor) return *cursor; else return NULL; } // Wipe current cgroup chain // void cgwipecur(void) { cgwipe(&cgcurfirst, &cgcurlast, &cgcurcursor, NULL); } // Wipe all struct's from cgroup admi // static void cgwipe(struct cgchainer **first, struct cgchainer **last, struct cgchainer **cursor, struct cgchainer **hashlist) { struct cgchainer *cp, *cpn; if (! *first) // empty already? return; // free all chain members // for (cp=*first; cp; cp=cpn) { cpn = cp->next; free(cp->proclist); free(cp->cstat); free(cp); } *first = *last = *cursor = NULL; if (hashlist) memset(hashlist, 0, sizeof(struct cgchainer *) * CGROUPNHASH); } // Create enough memory to copy all cstat structures from // the current list to one contiguous area. // Also create enough memory to copy all pid lists from // the current list to one contiguous area. // Next, create and fill an array of cgchainer structs. // // Returns: number of cgchainer structs // int deviatcgroup(struct cgchainer **cdpp, int *npids) { struct cgchainer *ccp = cgcurfirst; char *allc, *allp, *cp, *pp; // allocate contiguous memory areas for all cstat structs // and all pid lists // allc = calloc(1, cgcursize); allp = malloc(sizeof(pid_t) * cgcurprocs); ptrverify(allc, "Malloc failed for contiguous cstats (%d bytes)\n", cgcursize); ptrverify(allp, "Malloc failed for contiguous pids (%d pids)\n", cgcurprocs); // concatenate all current cstat structs and all pid lists // into the new memory areas // ccp = cgcurfirst; cp = allc; pp = allp; while (ccp) { int cstatlen = ccp->cstat->gen.structlen; int plistlen = ccp->cstat->gen.nprocs * sizeof(pid_t); memcpy(cp, ccp->cstat, cstatlen); cp += cstatlen; if (ccp->proclist) { memcpy(pp, ccp->proclist, plistlen); pp += plistlen; } ccp = ccp->next; } // build array of cgchainer structs and a hash list for the // concatenated cstat structs for the deviation values // cgbuildarray(&cgdevfirst, allc, allp, cgcurnum); // calculate deviation values // cgcalcdeviate(); *cdpp = cgdevfirst; *npids = cgcurprocs; return cgcurnum; } // Create concatenated array of cgchainer structs referring to // proper location in the concatenated cstat structs and // the concatenated pid lists. // void cgbuildarray(struct cgchainer **firstp, char *cstats, char *pids, int ncstats) { struct cgchainer *cdp; int i; *firstp = malloc(sizeof(struct cgchainer) * ncstats); ptrverify(firstp, "Malloc failed for contiguous cgchainers (%d)\n", ncstats); for (cdp=*firstp, i=0; i < ncstats; cdp++, i++) { cdp->next = cdp+1; cdp->hashnext = NULL; cdp->proclist = (pid_t *)pids; pids += sizeof(pid_t) * ((struct cstat *)cstats)->gen.nprocs; cdp->cstat = (struct cstat *)cstats; cstats += ((struct cstat *)cstats)->gen.structlen; } cdp = *firstp + ncstats - 1; cdp->next = NULL; // terminate chain } // Assemble full pathname of cgroup directory (from cgroup top directory) // from the deviation array. // For JSON output, double escape characters (backslashes) can be requested // vi boolean 'escdouble'. // // Returns: malloc'ed string with full path name to be freed later on // #define MAXESCAPES 16 char * cggetpath(struct cgchainer *cdp, struct cgchainer *cdbase, int escdouble) { // calculate path length including slashes (depth) and terminating 0-byte // // in case of double escapes, add a (hopefully) worst-case number of // bytes to the malloc'ed buffer to double the backslashes // // in case of the top directory (without a parent), reserve one extra // byte for the '/' character // int pathlen = cdp->cstat->gen.fullnamelen + cdp->cstat->gen.depth + 1 + (cdp->cstat->gen.sequence ? 0 : 1); char *path = calloc(1, pathlen); char *ps; ptrverify(path, "Malloc failed for concatenated cgpath (%d)\n", pathlen); // not the top directory? // if (cdp->cstat->gen.sequence) { // any other path: place all path components // including preceding slashes // while (cdp->cstat->gen.parentseq != -1) { // determine location of preceding slash // of this specific path component // ps = path + cdp->cstat->gen.fullnamelen - cdp->cstat->gen.namelen + cdp->cstat->gen.depth - 1; // store slash and path component // *ps = '/'; memcpy(ps+1, cdp->cstat->cgname, cdp->cstat->gen.namelen); // climb upwards // cdp = cdbase + cdp->cstat->gen.parentseq; } *(path+pathlen-1) = '\0'; // terminate string } else // exceptional case only for top directory { *path = '/'; *(path+1) = '\0'; // terminate string } // when double escapes are required and at least one backslash // is present, reallocate and convert the assembled path // // room for a worst-case number of backslashes is allocated // to avoid counting backslashes before transferring all bytes // if (escdouble && strchr(path, '\\')) { char *escpath, *pi, *po; pathlen += MAXESCAPES; escpath = malloc(pathlen); ptrverify(escpath, "Malloc failed for escape cgpath (%d)\n", pathlen); pi = path; po = escpath; while (*pi) { *po++ = *pi; if (*pi++ == '\\') // insert additional backslash *po++ = '\\'; if (po - escpath >= pathlen-1) break; // truncate to avoid overflow } *po = 0; free(path); // free original string path = escpath; } return path; } // =========================================================== // name hashing to find cgroup with specific path name // =========================================================== // Calculate hash for directory name. // The 'basehash' contains the hashed value of the // previous path components and the 'offset' is // the offset of this (sub)string in a larger path. // Notice that '/' characters will be ignored during // calculation of the hash value (and ignored in the offset). // static long hashcalc(char *p, long basehash, int offset) { offset += 1; // avoid multiplication by 0 while (*p) { if (*p == '/') { p++; continue; } basehash += *p++ * offset++; } return basehash; } // Add new hash cgroup directory name to hash // static void hashadd(struct cgchainer *hashlist[], struct cgchainer *cp) { long hash = cp->cstat->gen.namehash; cp->hashnext = hashlist[hash&CGROUPMASK]; hashlist[hash&CGROUPMASK] = cp; } // Find directory name in hash based on hashed name value // static struct cgchainer * hashfind(struct cgchainer *hashlist[], long hash) { struct cgchainer *cp; // search hash list // for (cp = hashlist[hash&CGROUPMASK]; cp; cp = cp->hashnext) { if (hash == cp->cstat->gen.namehash) return cp; } return NULL; } // =========================================================== // PID hashing to find the processes that are related to // a particular cgroup // =========================================================== struct pid2tstat { struct pid2tstat *hashnext; struct tstat *tstat; }; #define PIDNHASH 512 // power of 2 #define PIDMASK (PIDNHASH-1) static int compselcpu(const void *, const void *); static int compselmem(const void *, const void *); static int compseldsk(const void *, const void *); // Create a mixed list with cgroups and // processes belonging to those cgroups // // Return: number of entries (= screen lines) in the list // int mergecgrouplist(struct cglinesel **cgroupselp, int newdepth, struct cgchainer **cgchainerp, int ncgroups, struct tstat **tpp, int nprocs, char showorder) { int ic, ip, im, is; struct cglinesel *cgroupsel; struct pid2tstat *pidhash[PIDNHASH], *pidlist, *pidcur; // create a hashlist to find the PIDs in a fast way // if (newdepth == 8 || newdepth == 9) // depth requires processes? { // initialize hash list // memset(pidhash, 0, sizeof pidhash); // create one pid2tstat struct per tstat struct // pidlist = calloc(nprocs, sizeof(struct pid2tstat)); ptrverify(pidlist, "Malloc for pid2tstat structs failed (%d)\n", nprocs); pidcur = pidlist; // build hash list // for (ip=0; ip < nprocs; ip++, tpp++, pidcur++) { int hash = (*tpp)->gen.pid&PIDMASK; pidcur->hashnext = pidhash[hash]; pidcur->tstat = (*tpp); pidhash[hash] = pidcur; } } else { nprocs = 0; // ignore processes } // allocate space for merged list // cgroupsel = malloc(sizeof(struct cglinesel) * (ncgroups+nprocs)); if (cgroupsel == NULL) ptrverify(cgroupsel, "Malloc for cglinesel structs failed (%d)\n", ncgroups + nprocs); *cgroupselp = cgroupsel; // fill merged list // for (ic=im=0; ic < ncgroups; ic++) { int cgroupnprocs; // take next cgroup and add it to the merged list // if ( cgroupfilter((*(cgchainerp+ic))->cstat, newdepth, showorder) ) { (cgroupsel+im)->cgp = *(cgchainerp+ic); (cgroupsel+im)->tsp = NULL; im++; } else { // suppress this cgroup // when it is a stub cgroup for this level, // pass the stub to the previous valid entry // of this level // if ( (*(cgchainerp+ic))->stub) { int j; for (j=im-1; j > 0; j--) { int depth = (*(cgchainerp+ic))->cstat->gen.depth; if (depth > (cgroupsel+j)->cgp->cstat->gen.depth) { break; } if (depth == (cgroupsel+j)->cgp->cstat->gen.depth) { (cgroupsel+j)->cgp->stub = 7; break; } else { if (depth < CGRMAXDEPTH) (cgroupsel+j)->cgp->vlinemask &= ~(1ULL << (depth-1)); } } } continue; } // if relevant, search for processes that belong to this cgroup // and add them to the merged list // if (!nprocs) // ignore processes anyhow? continue; cgroupnprocs = (*(cgchainerp+ic))->cstat->gen.nprocs; if (!cgroupnprocs) // no processes in this cgroup? continue; for (ip=0, is=im; ip < cgroupnprocs; ip++) { int pid = (*(cgchainerp+ic))->proclist[ip]; int hash = pid&PIDMASK; // search for tstat struct relate to this PID via hashlist // for (pidcur=pidhash[hash]; pidcur; pidcur=pidcur->hashnext) { // process tstat found?? // if (pidcur->tstat->gen.pid == pid) { // skip kernel processes in case of level 8 // if (newdepth == 9 || pidcur->tstat->mem.vmem > 0 ) { (cgroupsel+im)->cgp = *(cgchainerp+ic); (cgroupsel+im)->tsp = pidcur->tstat; im++; } break; } } } // sort the list of processes added for this cgroup // if (im-is > 1) { switch (showorder) { case MSORTCPU: qsort(cgroupsel+is, im-is, sizeof(struct cglinesel), compselcpu); break; case MSORTMEM: qsort(cgroupsel+is, im-is, sizeof(struct cglinesel), compselmem); break; case MSORTDSK: qsort(cgroupsel+is, im-is, sizeof(struct cglinesel), compseldsk); break; } } } if (nprocs) free(pidlist); return im; } // Judge if a cgroup should be selected, based on the // current requested directory level (depth) and // based on the fact if unused branches are requested // // returns 0 (false) or 1 (true) // static int cgroupfilter(struct cstat *csp, int newdepth, char showorder) { // skip lower level of cgroups when required // by entering key 2 till 8 (9=max) // if (newdepth < 9 && newdepth <= csp->gen.depth) return 0; // skip this level and lower levels if // no processes are assigned at all // if (deviatonly && showorder != MSORTMEM && csp->gen.nprocs == 0 && csp->gen.procsbelow == 0 ) return 0; return 1; } // Funtion to be called by qsort() to compare the // CPU time consumption of two processes in a cgroup. // static int compselcpu(const void *a, const void *b) { count_t acpu = ((struct cglinesel *)a)->tsp->cpu.utime + ((struct cglinesel *)a)->tsp->cpu.stime; count_t bcpu = ((struct cglinesel *)b)->tsp->cpu.utime + ((struct cglinesel *)b)->tsp->cpu.stime; if (acpu < bcpu) return 1; if (acpu > bcpu) return -1; return 0; } // Funtion to be called by qsort() to compare the // memory consumption of two processes in a cgroup. // static int compselmem(const void *a, const void *b) { count_t amem = ((struct cglinesel *)a)->tsp->mem.rmem; count_t bmem = ((struct cglinesel *)b)->tsp->mem.rmem; if (amem < bmem) return 1; if (amem > bmem) return -1; return 0; } // Funtion to be called by qsort() to compare the // disk activity of two processes in a cgroup. // static int compseldsk(const void *a, const void *b) { count_t adisk = ((struct cglinesel *)a)->tsp->dsk.rsz + ((struct cglinesel *)a)->tsp->dsk.wsz; count_t bdisk = ((struct cglinesel *)b)->tsp->dsk.rsz + ((struct cglinesel *)b)->tsp->dsk.wsz; if (adisk < bdisk) return 1; if (adisk > bdisk) return -1; return 0; } // Generate a list of pointers to cgchainer struct, sorted on // specific resource consumption in the cgroup // struct cgsorter { struct cgchainer *cgthis; struct cgsorter *cgsame; struct cgsorter *cgchild; struct cgsorter **sortlist; count_t sortval; int nrchild; }; static struct cgsorter cgroot; // struct for root with depth zero static struct cgchainer *sortlevel(int, struct cgsorter *, struct cgchainer *, int, char); static struct cgchainer **mergelevels(struct cgsorter *, int); static int mergelevel(struct cgsorter *, struct cgchainer **, unsigned long); static void createsortlist(struct cgsorter *); static int compsortval(const void *, const void *); // Main function to create a list of pointers to cgchainer structs, // in the order of resource consumption while maintaining // the proper cgroup directory structure. // // Return value: list with sorted pointers to cgchainer structs // struct cgchainer ** cgsort(struct cgchainer *cgphys, int cgsize, char showorder) { struct cgchainer **cgsorted; // reinitialize the root cgsorter struct and fill // the pointer to the root cgchainer // memset(&cgroot, 0, sizeof cgroot); cgroot.cgthis = cgphys; // build a tree structure reflecting the directories of the cgroups // (skip the first entry representing the root cgroup) // sortlevel(1, &cgroot, &cgphys[1], cgsize-1, showorder); // merge all sorted cgchainer struct pointers into one // contiguous list // cgsorted = mergelevels(&cgroot, cgsize); return cgsorted; } // Create a tree structure of cgsorter structs (by nested calls). // A cgsorter struct will be created per cgchainer struct, // with the following members: // // cgthis pointer to corresponding cgchainer struct // cgsame pointer to next cgsorter struct on same directory level // cgchild pointer to first cgsorter struct on lower directory level // sortlist pointer to list of pointers to child cgsorter structs // (same pointers as chained via cgchild) to be sorted // sortval sort value (e.g. consumed CPU time or used memory) // nrchild number of direct child members // static struct cgchainer * sortlevel(int curlevel, struct cgsorter *cgparent, struct cgchainer *cgp, int cgsize, char showorder) { int newlevel, cgleft = cgsize; struct cgchainer *cgc = cgp; struct cgsorter *cgs = NULL; // as long as cgchainer structs are left... // while (cgleft > 0) { newlevel = cgc->cstat->gen.depth; // higher level in directory tree? // - create and sort the cgchainer pointers in order of resource utilization // - return to caller to continue with higher level // if (newlevel < curlevel) { createsortlist(cgparent); return cgc; // leave this level } // same directory level: chain to same-level list // which is the child list of the higher level that called us // if (newlevel == curlevel) { // malloc and fill new cgsorter struct // cgs = malloc(sizeof(struct cgsorter)); ptrverify(cgs, "Malloc failed for cgsorter struct\n"); cgs->cgthis = cgc; cgs->cgsame = cgparent->cgchild; cgs->cgchild = 0; cgs->nrchild = 0; switch (showorder) { case MSORTCPU: cgs->sortval = cgc->cstat->cpu.utime + cgc->cstat->cpu.stime; break; case MSORTMEM: if (cgc->cstat->mem.current > 0) { cgs->sortval = cgc->cstat->mem.current; } else { cgs->sortval = cgc->cstat->mem.anon + cgc->cstat->mem.file + cgc->cstat->mem.kernel + cgc->cstat->mem.shmem; } break; case MSORTDSK: cgs->sortval = cgc->cstat->dsk.rbytes + cgc->cstat->dsk.wbytes; break; default: cgs->sortval = 0; // no sorting } // chain cgsorter struct to this level // cgparent->cgchild = cgs; cgparent->nrchild++; cgc++; cgleft--; continue; } // newlevel > curlevel? // recursively call sortlevel() for each new // directory level underneath // cgc = sortlevel(newlevel, cgs, cgc, cgleft, showorder); cgleft = cgsize - (cgc - cgp); } createsortlist(cgparent); return cgc; } // Create a list of pointers to the cgchainer structs // on the current level and sort it on resource consumption // static void createsortlist(struct cgsorter *cgparent) { int i; struct cgsorter *cgs; if (cgparent->nrchild == 1) // no sorting needed with one child { cgparent->sortlist = NULL; } else // sorting needed by creating sortlist { cgparent->sortlist = calloc(cgparent->nrchild, sizeof(struct cgsorter *)); ptrverify(cgparent->sortlist, "Malloc failed for cgsorter list\n"); // fill elements of sortlist with pointers to child cgsorters // for (i=0, cgs=cgparent->cgchild; i < cgparent->nrchild; i++, cgs = cgs->cgsame) { *((cgparent->sortlist)+i) = cgs; } // sort list on sort value // qsort(cgparent->sortlist, cgparent->nrchild, sizeof(struct cgsorter *), compsortval); } } // Function to be called by qsort() to compare // the sortvalue in two cgsorter structs // static int compsortval(const void *a, const void *b) { struct cgsorter *cga = *(struct cgsorter **)a; struct cgsorter *cgb = *(struct cgsorter **)b; if (cga->sortval < cgb->sortval) return 1; if (cga->sortval > cgb->sortval) return -1; return 0; } // Gather all sorted cgsorter structs from the entire tree // and create one array of cgchainer pointers in sorted order. // static struct cgchainer ** mergelevels(struct cgsorter *cgrootp, int cgsize) { struct cgchainer **cgpp; unsigned long vlinemask = 0; // allocate the list of pointers to be returned // cgpp = malloc(sizeof(struct cgchainer *) * cgsize); ptrverify(cgpp, "Malloc failed for cgchainer ptr list (%d)\n", cgsize); // fill the first entry with the root cgchainer struct pointer // *cgpp = cgrootp->cgthis; (*cgpp)->stub = 1; // no more entries on this level (*cgpp)->vlinemask = vlinemask; mergelevel(cgrootp, cgpp+1, vlinemask); return cgpp; } // Gather all cgsorter structs from one directory level. // To be called in a nested way for each level underneath. // static int mergelevel(struct cgsorter *cgparent, struct cgchainer **cgpp, unsigned long vlinemask) { struct cgsorter *cgs, *cgsave; int i, j, depth = cgparent->cgthis->cstat->gen.depth; switch (cgparent->nrchild) { case 0: // should never occur.... j = 0; break; case 1: // in case of one child no sortlist has been created *cgpp = cgparent->cgchild->cgthis; (*cgpp)->stub = 1; // no more entries on this level if (depth < CGRMAXDEPTH) vlinemask &= ~(1ULL << depth); (*cgpp)->vlinemask = vlinemask; j = 1; if (cgparent->cgchild->nrchild) // merge descendants on lower level? j += mergelevel(cgparent->cgchild, cgpp+1, vlinemask); free(cgparent->cgchild); break; default: for (i=0, j=0; i < cgparent->nrchild; i++, j++) { cgs = *((cgparent->sortlist)+i); *(cgpp+j) = cgs->cgthis; if (i == cgparent->nrchild -1) // last on this level? { (*(cgpp+j))->stub = 1; // no more entries on this level if (depth < CGRMAXDEPTH) vlinemask &= ~(1ULL << depth); (*(cgpp+j))->vlinemask = vlinemask; } else { (*(cgpp+j))->stub = 0; // more entries on this level if (depth < CGRMAXDEPTH) vlinemask |= 1ULL << depth; (*(cgpp+j))->vlinemask = vlinemask; } if (cgs->nrchild) // merge descendants on lower level? j += mergelevel(cgs, cgpp+j+1, vlinemask); } // for all cgroups of this level merged: free malloced areas // free(cgparent->sortlist); for (cgs=cgparent->cgchild; cgs; cgs=cgsave) { cgsave = cgs->cgsame; free(cgs); } } return j; } // =========================================================== // PID hashing to find the cgroup that is related to // a particular process // =========================================================== struct pid2cgchainer { struct pid2cgchainer *hashnext; pid_t pid; int cgindex; // cgchainer index }; // For every tstat struct, fill the reference (index) to // the related cgroup, represented by the cgchainer struct // void cgfillref(struct devtstat *devtstat, struct cgchainer *devchain, int ncgroups, int npids) { int ic, ip, it, hash; pid_t pid; struct pid2cgchainer *pidhash[PIDNHASH], *pidlist, *pidcur; struct cgchainer *cp; struct tstat *tp = devtstat->taskall; // create a hashlist to find the PIDs in a fast way // initialize hash list // memset(pidhash, 0, sizeof pidhash); // create one pid2cgchainer struct per cgroup pid // pidlist = calloc(npids, sizeof(struct pid2cgchainer)); ptrverify(pidlist, "Malloc for pid2cgchainer structs failed (%d)\n", npids); // build hash list to find right cgchainer (cgroup) for a PID // for (ic=0, cp=devchain, pidcur=pidlist; ic < ncgroups; ic++, cp++) { for (ip=0; ip < cp->cstat->gen.nprocs; ip++, pidcur++) { pid = cp->proclist[ip]; hash = pid & PIDMASK; pidcur->hashnext = pidhash[hash]; pidcur->pid = pid; pidcur->cgindex = ic; pidhash[hash] = pidcur; } } // connect every tstat struct to the concerning cgchainer // by filling the index // for (it=0; it < devtstat->ntaskall; it++, tp++) { tp->gen.cgroupix = -1; if (! tp->gen.isproc) // skip threads continue; pid = tp->gen.pid; hash = pid & PIDMASK; // search for cgchainer index related to this PID via hashlist // for (pidcur=pidhash[hash]; pidcur; pidcur=pidcur->hashnext) { if (pidcur->pid == pid) { tp->gen.cgroupix = pidcur->cgindex; break; } } } free(pidlist); } atop-2.12.1/cgroups.h0000644000203100020310000001057515064552761013716 0ustar gerlofgerlof/* ** ATOP - System & Process Monitor ** ** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as process-level. ** ** Include-file describing cgroup-level counters to be maintained. ** ================================================================ ** Author: Gerlof Langeveld ** E-mail: gerlof.langeveld@atoptool.nl ** Date: January/February 2024 ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. */ #ifndef __CGROUPS__ #define __CGROUPS__ // structure containing general info and metrics per cgroup (directory) // struct cstat { // GENERAL INFO struct cggen { int structlen; // struct length including rounded name int sequence; // sequence number in chain/array int parentseq; // parent sequence number in chain/array int depth; // cgroup tree depth starting from 0 int nprocs; // number of processes in cgroup int procsbelow; // number of processes in cgroups below int namelen; // cgroup name length (at end of struct) int fullnamelen; // cgroup path length int ifuture[4]; long namehash; // cgroup name hash of // full path name excluding slashes long lfuture[4]; } gen; // CONFIGURATION INFO struct cgconf { int cpuweight; // -1=max, -2=undefined int cpumax; // -1=max, -2=undefined (perc) count_t memmax; // -1=max, -2=undefined (pages) count_t swpmax; // -1=max, -2=undefined (pages) int dskweight; // -1=max, -2=undefined int ifuture[5]; count_t cfuture[5]; } conf; // CPU STATISTICS struct cgcpu { count_t utime; // time user text (usec) -1=undefined count_t stime; // time system text (usec) -1=undefined count_t somepres; // some pressure (microsec) count_t fullpres; // full pressure (microsec) count_t cfuture[5]; } cpu; // MEMORY STATISTICS struct cgmem { count_t current; // current memory (pages) -1=undefined count_t anon; // anonymous memory (pages) -1=undefined count_t file; // file memory (pages) -1=undefined count_t kernel; // kernel memory (pages) -1=undefined count_t shmem; // shared memory (pages) -1=undefined count_t somepres; // some pressure (microsec) count_t fullpres; // full pressure (microsec) count_t cfuture[5]; } mem; // DISK I/O STATISTICS struct cgdsk { count_t rbytes; // total bytes read on all physical disks count_t wbytes; // total bytes written on all physical disks count_t rios; // total read I/Os on all physical disks count_t wios; // total write I/Os on all physical disks count_t somepres; // some pressure (microsec) count_t fullpres; // full pressure (microsec) count_t cfuture[5]; } dsk; // cgroup name with variable length char cgname[]; }; // structure to be used for chaining the cstat struct and pid list // that are maintained per cgroup // struct cgchainer { struct cgchainer *next; struct cgchainer *hashnext; struct cstat *cstat; // cgroup info and stats pid_t *proclist; // PID list of cgroup unsigned long vlinemask; // bit list for tree drawing: // bit '1' for continuous line char stub; // boolean for tree drawing: // true means corner }; #define CGRMAXDEPTH (sizeof(unsigned long)*8) // #bits in vlinemask // structure to be used for printing a merged list // of cgroups and processes // struct tstat; struct cglinesel { struct cgchainer *cgp; // always filled with reference to cgroup struct tstat *tsp; // filled for process info, otherwise NULL }; int mergecgrouplist(struct cglinesel **, int, struct cgchainer **, int, struct tstat **, int, char); int cgroupv2support(void); void photocgroup(void); int deviatcgroup(struct cgchainer **, int *); struct cgchainer **cgsort(struct cgchainer *, int, char); char *cggetpath(struct cgchainer *, struct cgchainer *, int); void cgwipecur(void); void cgbuildarray(struct cgchainer **, char *, char *, int); void cgfillref(struct devtstat *, struct cgchainer *, int, int); #endif atop-2.12.1/deviate.c0000644000203100020310000016161715064552761013654 0ustar gerlofgerlof/* ** ATOP - System & Process Monitor ** ** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as process-level. ** ** This source-file contains functions to calculate the differences for ** the system-level and process-level counters since the previous sample. ** ========================================================================== ** Author: Gerlof Langeveld ** E-mail: gerlof.langeveld@atoptool.nl ** Date: November 1996 ** LINUX-port: June 2000 ** -------------------------------------------------------------------------- ** Copyright (C) 2000-2021 Gerlof Langeveld ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ** -------------------------------------------------------------------------- */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "atop.h" #include "ifprop.h" #include "photoproc.h" #include "photosyst.h" static void calcdiff(struct tstat *, const struct tstat *, const struct tstat *, char, count_t); static inline count_t subcount(count_t, count_t); /* ** calculate the process-activity during the last sample */ void deviattask(struct tstat *curtpres, unsigned long ntaskpres, struct tstat *curpexit, unsigned long nprocexit, struct devtstat *devtstat, struct sstat *devsstat) { register int c, d, pall=0, pact=0; register struct tstat *curstat, *devstat, *thisproc; struct tstat prestat, *pprestat; struct pinfo *pinfo; count_t totusedcpu; char hashtype = 'p'; /* ** needed for sanity check later on... */ totusedcpu = devsstat->cpu.all.stime + devsstat->cpu.all.utime + devsstat->cpu.all.ntime + devsstat->cpu.all.itime + devsstat->cpu.all.wtime + devsstat->cpu.all.Itime + devsstat->cpu.all.Stime + devsstat->cpu.all.steal; /* ** make new list of all tasks in the task-database; ** after handling all task, the left-overs are tasks ** that have disappeared since the previous sample */ pdb_makeresidue(); /* ** remove allocated lists of previous sample and initialize counters */ free(devtstat->taskall); free(devtstat->procall); free(devtstat->procactive); memset(devtstat, 0, sizeof *devtstat); /* ** create list for the sample deviations of all tasks */ devtstat->ntaskall = ntaskpres + nprocexit; devtstat->taskall = malloc(devtstat->ntaskall * sizeof(struct tstat)); ptrverify(devtstat->taskall, "Malloc failed for %lu deviated tasks\n", devtstat->ntaskall); /* ** calculate deviations per present task */ for (c=0, thisproc = devtstat->taskall; c < ntaskpres; c++) { char newtask = 0; curstat = curtpres+c; devstat = devtstat->taskall+c; if (curstat->gen.isproc) { thisproc = devstat; // remember last process seen devtstat->nprocall++; if (curstat->gen.state == 'Z') { devtstat->totzombie++; } else { devtstat->totrun += curstat->gen.nthrrun; devtstat->totslpi += curstat->gen.nthrslpi; devtstat->totslpu += curstat->gen.nthrslpu; devtstat->totidle += curstat->gen.nthridle; } } /* ** get previous figures from task-database */ if ( pdb_gettask(curstat->gen.pid, curstat->gen.isproc, curstat->gen.btime, &pinfo)) { /* ** task already present in the previous sample ** ** save stats from previous sample (to use for ** further calculations) and store new statistics ** in task-database */ if (memcmp(curstat, &pinfo->tstat, sizeof(struct tstat)) == EQ) { /* ** no activity for task */ curstat->gen.wasinactive = 1; pprestat = curstat; } else { /* ** save the values of the previous sample ** and overwrite the previous sample in ** the database with the current sample */ prestat = pinfo->tstat; pprestat = &prestat; pinfo->tstat = *curstat; curstat->gen.wasinactive = 0; devtstat->ntaskactive++; if (curstat->gen.isproc) { devtstat->nprocactive++; } else { if (thisproc->gen.wasinactive) { thisproc->gen.wasinactive = 0; devtstat->ntaskactive++; devtstat->nprocactive++; } } } } else { /* ** new task which must have been started during ** last interval */ memset(&prestat, 0, sizeof(prestat)); pprestat = &prestat; curstat->gen.wasinactive = 0; devtstat->ntaskactive++; if (curstat->gen.isproc) { devtstat->nprocactive++; } else { if (thisproc->gen.wasinactive) { thisproc->gen.wasinactive = 0; devtstat->ntaskactive++; devtstat->nprocactive++; } } /* ** create new task struct */ pinfo = calloc(1, sizeof(struct pinfo)); ptrverify(pinfo, "Malloc failed for new pinfo\n"); pinfo->tstat = *curstat; /* ** add new task to task-database */ pdb_addtask(curstat->gen.pid, pinfo); newtask = 1; } /* ** do the difference calculations */ calcdiff(devstat, curstat, pprestat, newtask, totusedcpu); } /* ** calculate deviations per exited process */ if (nprocexit > 0 && supportflags&NETATOPD) { if (curpexit->gen.pid) hashtype = 'p'; else hashtype = 'b'; netatop_exithash(hashtype); } for (d=c, c=0; c < nprocexit; c++) { /* ** check if this process has been started AND ** finished since previous sample; ** if so, it has no use to check if there is still ** existing info present in the process-database */ curstat = curpexit+c; curstat->gen.wasinactive = 0; devtstat->nprocall++; devtstat->nprocactive++; devtstat->ntaskactive++; if (curstat->gen.pid) /* acctrecord contains pid? */ { if ( pdb_gettask(curstat->gen.pid, 1, curstat->gen.btime, &pinfo)) prestat = pinfo->tstat; else memset(&prestat, 0, sizeof(prestat)); } else { if ( curstat->gen.btime > pretime ) { /* ** process-start and -finish in same interval */ memset(&prestat, 0, sizeof(prestat)); } else { /* ** process must be known in process-database; ** try to match one of the remaining processes ** against this exited one */ if ( pdb_srchresidue(curstat, &pinfo) ) prestat = pinfo->tstat; else memset(&prestat, 0, sizeof(prestat)); } } /* ** now do the calculations */ devstat = devtstat->taskall+d; memset(devstat, 0, sizeof *devstat); devstat->gen = curstat->gen; if ( curstat->gen.pid == 0 ) devstat->gen.pid = prestat.gen.pid; if (!prestat.gen.pid) devstat->gen.excode |= ~(INT_MAX); strcpy(devstat->gen.cmdline, prestat.gen.cmdline); strcpy(devstat->gen.utsname, prestat.gen.utsname); devstat->cpu.curcpu = -1; /* ** due to the strange exponent-type storage of values ** in the process accounting record, the resource-value ** in the exit-record might have been smaller than the ** stored value of the last registered sample; in that ** case the deviation should be set to zero */ if (curstat->cpu.stime > prestat.cpu.stime) devstat->cpu.stime = curstat->cpu.stime - prestat.cpu.stime; if (curstat->cpu.utime > prestat.cpu.utime) devstat->cpu.utime = curstat->cpu.utime - prestat.cpu.utime; if (curstat->mem.minflt > prestat.mem.minflt) devstat->mem.minflt = curstat->mem.minflt - prestat.mem.minflt; if (curstat->mem.majflt > prestat.mem.majflt) devstat->mem.majflt = curstat->mem.majflt - prestat.mem.majflt; if (curstat->dsk.rio > (prestat.dsk.rio + prestat.dsk.wio)) devstat->dsk.rio = curstat->dsk.rio - prestat.dsk.rio - prestat.dsk.wio; /* ** try to match the network counters of netatop */ if (supportflags & NETATOPBPF) { unsigned long val = (hashtype == 'p' ? curstat->gen.pid : curstat->gen.btime); netatop_bpf_exitfind(val, devstat, &prestat); } else if (supportflags & NETATOPD) { unsigned long val = (hashtype == 'p' ? curstat->gen.pid : curstat->gen.btime); netatop_exitfind(val, devstat, &prestat); } /* ** handle the gpu counters */ if (curstat->gpu.state || prestat.gpu.state) // GPU use? { if (curstat->gpu.state) devstat->gpu.state = curstat->gpu.state; else devstat->gpu.state = prestat.gpu.state; devstat->gpu.nrgpus = curstat->gpu.nrgpus; devstat->gpu.gpulist = curstat->gpu.gpulist; devstat->gpu.gpubusy = curstat->gpu.gpubusy; devstat->gpu.membusy = curstat->gpu.membusy; devstat->gpu.timems = curstat->gpu.timems; devstat->gpu.memnow = curstat->gpu.memnow; devstat->gpu.memcum = curstat->gpu.memcum - prestat.gpu.memcum; devstat->gpu.sample = curstat->gpu.sample - prestat.gpu.sample; } else { devstat->gpu.state = '\0'; } if (prestat.gen.pid > 0) pdb_deltask(prestat.gen.pid, prestat.gen.isproc); d++; } /* ** remove unused entries from RESIDUE chain */ pdb_cleanresidue(); /* ** create and fill other pointer lists */ devtstat->procall = malloc(devtstat->nprocall * sizeof(struct tstat *)); devtstat->procactive = malloc(devtstat->nprocactive * sizeof(struct tstat *)); ptrverify(devtstat->procall, "Malloc failed for %d processes\n", devtstat->nprocall); ptrverify(devtstat->procactive, "Malloc failed for %d active procs\n", devtstat->nprocactive); for (c=0, thisproc=devstat=devtstat->taskall; c < devtstat->ntaskall; c++, devstat++) { if (devstat->gen.isproc) { devtstat->procall[pall++] = devstat; if (! devstat->gen.wasinactive) devtstat->procactive[pact++] = devstat; } } } /* ** calculate the differences between the current sample and ** the previous sample for a task */ static void calcdiff(struct tstat *devstat, const struct tstat *curstat, const struct tstat *prestat, char newtask, count_t totusedcpu) { /* ** for inactive tasks, set all counters to zero to avoid calculating ** the deviations (after all, there are no deviations) */ if (curstat->gen.wasinactive) { memset(devstat, 0, sizeof *devstat); } /* ** copy all STATIC values from the current task settings */ devstat->gen = curstat->gen; if (newtask) devstat->gen.excode |= ~(INT_MAX); devstat->cpu.nice = curstat->cpu.nice; devstat->cpu.prio = curstat->cpu.prio; devstat->cpu.rtprio = curstat->cpu.rtprio; devstat->cpu.policy = curstat->cpu.policy; devstat->cpu.curcpu = curstat->cpu.curcpu; devstat->cpu.sleepavg = curstat->cpu.sleepavg; if (curstat->cpu.wchan[0]) strcpy(devstat->cpu.wchan, curstat->cpu.wchan); else devstat->cpu.wchan[0] = 0; devstat->mem.vexec = curstat->mem.vexec; devstat->mem.vmem = curstat->mem.vmem; devstat->mem.rmem = curstat->mem.rmem; devstat->mem.pmem = curstat->mem.pmem; devstat->mem.vdata = curstat->mem.vdata; devstat->mem.vstack = curstat->mem.vstack; devstat->mem.vlibs = curstat->mem.vlibs; devstat->mem.vswap = curstat->mem.vswap; devstat->mem.vlock = curstat->mem.vlock; if (curstat->gpu.state || prestat->gpu.state) // GPU use? { if (curstat->gpu.state) devstat->gpu.state = curstat->gpu.state; else devstat->gpu.state = prestat->gpu.state; devstat->gpu.nrgpus = curstat->gpu.nrgpus; devstat->gpu.gpulist = curstat->gpu.gpulist; devstat->gpu.gpubusy = curstat->gpu.gpubusy; devstat->gpu.membusy = curstat->gpu.membusy; devstat->gpu.memnow = curstat->gpu.memnow; devstat->gpu.timems = curstat->gpu.timems; } else { devstat->gpu.state = '\0'; } /* ** for inactive tasks, only the static values had to be copied, while ** all use counters have already been set to zero */ if (curstat->gen.wasinactive) return; /* ** calculate deviations for tasks that were really active ** (i.e. modified) during the sample */ devstat->cpu.stime = subcount(curstat->cpu.stime, prestat->cpu.stime); devstat->cpu.utime = subcount(curstat->cpu.utime, prestat->cpu.utime); /* ** particular kernel versions sometimes supply a smaller ** amount for consumed CPU-ticks than a previous sample; ** with unsigned calculations this results in 497 days of ** CPU-consumption so a sanity-check is needed here... */ if (devstat->cpu.stime > totusedcpu) devstat->cpu.stime = 1; if (devstat->cpu.utime > totusedcpu) devstat->cpu.utime = 1; devstat->cpu.rundelay = subcount(curstat->cpu.rundelay, prestat->cpu.rundelay); devstat->cpu.blkdelay = subcount(curstat->cpu.blkdelay, prestat->cpu.blkdelay); if (curstat->cpu.nvcsw >= prestat->cpu.nvcsw) devstat->cpu.nvcsw = subcount(curstat->cpu.nvcsw, prestat->cpu.nvcsw); else devstat->cpu.nvcsw = curstat->cpu.nvcsw; if (curstat->cpu.nivcsw >= prestat->cpu.nivcsw) devstat->cpu.nivcsw = subcount(curstat->cpu.nivcsw, prestat->cpu.nivcsw); else devstat->cpu.nivcsw = curstat->cpu.nivcsw; devstat->dsk.rio = subcount(curstat->dsk.rio, prestat->dsk.rio); devstat->dsk.rsz = subcount(curstat->dsk.rsz, prestat->dsk.rsz); devstat->dsk.wio = subcount(curstat->dsk.wio, prestat->dsk.wio); devstat->dsk.wsz = subcount(curstat->dsk.wsz, prestat->dsk.wsz); devstat->dsk.cwsz = subcount(curstat->dsk.cwsz, prestat->dsk.cwsz); devstat->mem.vgrow = curstat->mem.vmem - prestat->mem.vmem; devstat->mem.rgrow = curstat->mem.rmem - prestat->mem.rmem; devstat->mem.minflt = subcount(curstat->mem.minflt, prestat->mem.minflt); devstat->mem.majflt = subcount(curstat->mem.majflt, prestat->mem.majflt); /* ** network counters: due to an unload/load of the netatop module, ** previous counters might be larger than the current */ if (curstat->net.tcpsnd >= prestat->net.tcpsnd) devstat->net.tcpsnd = subcount(curstat->net.tcpsnd, prestat->net.tcpsnd); else devstat->net.tcpsnd = curstat->net.tcpsnd; if (curstat->net.tcpssz >= prestat->net.tcpssz) devstat->net.tcpssz = subcount(curstat->net.tcpssz, prestat->net.tcpssz); else devstat->net.tcpssz = curstat->net.tcpssz; if (curstat->net.tcprcv >= prestat->net.tcprcv) devstat->net.tcprcv = subcount(curstat->net.tcprcv, prestat->net.tcprcv); else devstat->net.tcprcv = curstat->net.tcprcv; if (curstat->net.tcprsz >= prestat->net.tcprsz) devstat->net.tcprsz = subcount(curstat->net.tcprsz, prestat->net.tcprsz); else devstat->net.tcprsz = curstat->net.tcprsz; if (curstat->net.udpsnd >= prestat->net.udpsnd) devstat->net.udpsnd = subcount(curstat->net.udpsnd, prestat->net.udpsnd); else devstat->net.udpsnd = curstat->net.udpsnd; if (curstat->net.udpssz >= prestat->net.udpssz) devstat->net.udpssz = subcount(curstat->net.udpssz, prestat->net.udpssz); else devstat->net.udpssz = curstat->net.udpssz; if (curstat->net.udprcv >= prestat->net.udprcv) devstat->net.udprcv = subcount(curstat->net.udprcv, prestat->net.udprcv); else devstat->net.udprcv = curstat->net.udprcv; if (curstat->net.udprsz >= prestat->net.udprsz) devstat->net.udprsz = subcount(curstat->net.udprsz, prestat->net.udprsz); else devstat->net.udprsz = curstat->net.udprsz; if (curstat->gpu.state) { devstat->gpu.memcum = curstat->gpu.memcum - prestat->gpu.memcum; devstat->gpu.sample = curstat->gpu.sample - prestat->gpu.sample; } } /* ** calculate the system-activity during the last sample */ void deviatsyst(struct sstat *cur, struct sstat *pre, struct sstat *dev, long interval) { register int i, j; count_t *cdev, *ccur, *cpre; struct ifprop ifprop; dev->cpu.nrcpu = cur->cpu.nrcpu; dev->cpu.devint = subcount(cur->cpu.devint, pre->cpu.devint); dev->cpu.csw = subcount(cur->cpu.csw, pre->cpu.csw); dev->cpu.nprocs = subcount(cur->cpu.nprocs, pre->cpu.nprocs); dev->cpu.all.stime = subcount(cur->cpu.all.stime, pre->cpu.all.stime); dev->cpu.all.utime = subcount(cur->cpu.all.utime, pre->cpu.all.utime); dev->cpu.all.ntime = subcount(cur->cpu.all.ntime, pre->cpu.all.ntime); dev->cpu.all.itime = subcount(cur->cpu.all.itime, pre->cpu.all.itime); dev->cpu.all.wtime = subcount(cur->cpu.all.wtime, pre->cpu.all.wtime); dev->cpu.all.Itime = subcount(cur->cpu.all.Itime, pre->cpu.all.Itime); dev->cpu.all.Stime = subcount(cur->cpu.all.Stime, pre->cpu.all.Stime); dev->cpu.all.steal = subcount(cur->cpu.all.steal, pre->cpu.all.steal); dev->cpu.all.guest = subcount(cur->cpu.all.guest, pre->cpu.all.guest); dev->cpu.all.instr = subcount(cur->cpu.all.instr, pre->cpu.all.instr); dev->cpu.all.cycle = subcount(cur->cpu.all.cycle, pre->cpu.all.cycle); for (i=0; i < dev->cpu.nrcpu; i++) { count_t ticks; dev->cpu.cpu[i].cpunr = cur->cpu.cpu[i].cpunr; dev->cpu.cpu[i].stime = subcount(cur->cpu.cpu[i].stime, pre->cpu.cpu[i].stime); dev->cpu.cpu[i].utime = subcount(cur->cpu.cpu[i].utime, pre->cpu.cpu[i].utime); dev->cpu.cpu[i].ntime = subcount(cur->cpu.cpu[i].ntime, pre->cpu.cpu[i].ntime); dev->cpu.cpu[i].itime = subcount(cur->cpu.cpu[i].itime, pre->cpu.cpu[i].itime); dev->cpu.cpu[i].wtime = subcount(cur->cpu.cpu[i].wtime, pre->cpu.cpu[i].wtime); dev->cpu.cpu[i].Itime = subcount(cur->cpu.cpu[i].Itime, pre->cpu.cpu[i].Itime); dev->cpu.cpu[i].Stime = subcount(cur->cpu.cpu[i].Stime, pre->cpu.cpu[i].Stime); dev->cpu.cpu[i].steal = subcount(cur->cpu.cpu[i].steal, pre->cpu.cpu[i].steal); dev->cpu.cpu[i].guest = subcount(cur->cpu.cpu[i].guest, pre->cpu.cpu[i].guest); dev->cpu.cpu[i].instr = subcount(cur->cpu.cpu[i].instr, pre->cpu.cpu[i].instr); dev->cpu.cpu[i].cycle = subcount(cur->cpu.cpu[i].cycle, pre->cpu.cpu[i].cycle); ticks = cur->cpu.cpu[i].freqcnt.ticks; dev->cpu.cpu[i].freqcnt.maxfreq = cur->cpu.cpu[i].freqcnt.maxfreq; dev->cpu.cpu[i].freqcnt.cnt = ticks ? subcount(cur->cpu.cpu[i].freqcnt.cnt, pre->cpu.cpu[i].freqcnt.cnt) : cur->cpu.cpu[i].freqcnt.cnt; dev->cpu.cpu[i].freqcnt.ticks = ticks ? subcount(cur->cpu.cpu[i].freqcnt.ticks, pre->cpu.cpu[i].freqcnt.ticks) : cur->cpu.cpu[i].freqcnt.ticks; } dev->cpu.lavg1 = cur->cpu.lavg1; dev->cpu.lavg5 = cur->cpu.lavg5; dev->cpu.lavg15 = cur->cpu.lavg15; dev->mem.physmem = cur->mem.physmem; dev->mem.freemem = cur->mem.freemem; dev->mem.availablemem = cur->mem.availablemem; dev->mem.buffermem = cur->mem.buffermem; dev->mem.slabmem = cur->mem.slabmem; dev->mem.slabreclaim = cur->mem.slabreclaim; dev->mem.committed = cur->mem.committed; dev->mem.commitlim = cur->mem.commitlim; dev->mem.cachemem = cur->mem.cachemem; dev->mem.cachedrt = cur->mem.cachedrt; dev->mem.totswap = cur->mem.totswap; dev->mem.freeswap = cur->mem.freeswap; dev->mem.swapcached = cur->mem.swapcached; dev->mem.pagetables = cur->mem.pagetables; dev->mem.zswap = cur->mem.zswap; dev->mem.zswapped = cur->mem.zswapped; dev->mem.shmem = cur->mem.shmem; dev->mem.shmrss = cur->mem.shmrss; dev->mem.shmswp = cur->mem.shmswp; dev->mem.stothugepage = cur->mem.stothugepage; dev->mem.sfreehugepage = cur->mem.sfreehugepage; dev->mem.shugepagesz = cur->mem.shugepagesz; dev->mem.ltothugepage = cur->mem.ltothugepage; dev->mem.lfreehugepage = cur->mem.lfreehugepage; dev->mem.lhugepagesz = cur->mem.lhugepagesz; dev->mem.anonhugepage = cur->mem.anonhugepage; dev->mem.vmwballoon = cur->mem.vmwballoon; dev->mem.zfsarcsize = cur->mem.zfsarcsize; dev->mem.ksmsharing = cur->mem.ksmsharing; dev->mem.ksmshared = cur->mem.ksmshared; dev->mem.tcpsock = cur->mem.tcpsock; dev->mem.udpsock = cur->mem.udpsock; dev->mem.pgouts = subcount(cur->mem.pgouts, pre->mem.pgouts); dev->mem.pgins = subcount(cur->mem.pgins, pre->mem.pgins); dev->mem.swouts = subcount(cur->mem.swouts, pre->mem.swouts); dev->mem.swins = subcount(cur->mem.swins, pre->mem.swins); dev->mem.zswouts = subcount(cur->mem.zswouts, pre->mem.zswouts); dev->mem.zswins = subcount(cur->mem.zswins, pre->mem.zswins); dev->mem.pgscans = subcount(cur->mem.pgscans, pre->mem.pgscans); dev->mem.pgsteal = subcount(cur->mem.pgsteal, pre->mem.pgsteal); dev->mem.allocstall = subcount(cur->mem.allocstall, pre->mem.allocstall); if (cur->mem.oomkills != -1) dev->mem.oomkills = subcount(cur->mem.oomkills, pre->mem.oomkills); else dev->mem.oomkills = -1; dev->mem.compactstall = subcount(cur->mem.compactstall, pre->mem.compactstall); dev->mem.numamigrate = subcount(cur->mem.numamigrate, pre->mem.numamigrate); dev->mem.pgmigrate = subcount(cur->mem.pgmigrate, pre->mem.pgmigrate); dev->memnuma.nrnuma = cur->memnuma.nrnuma; for (i=0; i < dev->memnuma.nrnuma; i++) { dev->memnuma.numa[i].numanr = cur->memnuma.numa[i].numanr; dev->memnuma.numa[i].totmem = cur->memnuma.numa[i].totmem; dev->memnuma.numa[i].freemem = cur->memnuma.numa[i].freemem; dev->memnuma.numa[i].filepage = cur->memnuma.numa[i].filepage; dev->memnuma.numa[i].active = cur->memnuma.numa[i].active; dev->memnuma.numa[i].inactive = cur->memnuma.numa[i].inactive; dev->memnuma.numa[i].dirtymem = cur->memnuma.numa[i].dirtymem; dev->memnuma.numa[i].shmem = cur->memnuma.numa[i].shmem; dev->memnuma.numa[i].slabmem = cur->memnuma.numa[i].slabmem; dev->memnuma.numa[i].slabreclaim = cur->memnuma.numa[i].slabreclaim; dev->memnuma.numa[i].tothp = cur->memnuma.numa[i].tothp; dev->memnuma.numa[i].freehp = cur->memnuma.numa[i].freehp; dev->memnuma.numa[i].frag = cur->memnuma.numa[i].frag; } dev->cpunuma.nrnuma = cur->cpunuma.nrnuma; if (dev->cpunuma.nrnuma > 1) { for (i=0; i < dev->cpunuma.nrnuma; i++) { dev->cpunuma.numa[i].nrcpu = cur->cpunuma.numa[i].nrcpu; dev->cpunuma.numa[i].numanr = cur->cpunuma.numa[i].numanr; dev->cpunuma.numa[i].utime = subcount(cur->cpunuma.numa[i].utime, pre->cpunuma.numa[i].utime); dev->cpunuma.numa[i].ntime = subcount(cur->cpunuma.numa[i].ntime, pre->cpunuma.numa[i].ntime); dev->cpunuma.numa[i].stime = subcount(cur->cpunuma.numa[i].stime, pre->cpunuma.numa[i].stime); dev->cpunuma.numa[i].itime = subcount(cur->cpunuma.numa[i].itime, pre->cpunuma.numa[i].itime); dev->cpunuma.numa[i].wtime = subcount(cur->cpunuma.numa[i].wtime, pre->cpunuma.numa[i].wtime); dev->cpunuma.numa[i].Itime = subcount(cur->cpunuma.numa[i].Itime, pre->cpunuma.numa[i].Itime); dev->cpunuma.numa[i].Stime = subcount(cur->cpunuma.numa[i].Stime, pre->cpunuma.numa[i].Stime); dev->cpunuma.numa[i].steal = subcount(cur->cpunuma.numa[i].steal, pre->cpunuma.numa[i].steal); dev->cpunuma.numa[i].guest = subcount(cur->cpunuma.numa[i].guest, pre->cpunuma.numa[i].guest); } } dev->psi = cur->psi; if (cur->psi.present) { dev->psi.cpusome.total = cur->psi.cpusome.total - pre->psi.cpusome.total; dev->psi.memsome.total = cur->psi.memsome.total - pre->psi.memsome.total; dev->psi.memfull.total = cur->psi.memfull.total - pre->psi.memfull.total; dev->psi.iosome.total = cur->psi.iosome.total - pre->psi.iosome.total; dev->psi.iofull.total = cur->psi.iofull.total - pre->psi.iofull.total; } /* ** structures with network-related counters are considered ** as tables of frequency-counters that have to be subtracted; ** values that do not represent a frequency are corrected afterwards */ for (cdev = (count_t *)&dev->net.ipv4, ccur = (count_t *)&cur->net.ipv4, cpre = (count_t *)&pre->net.ipv4, i = 0; i < (sizeof dev->net.ipv4 / sizeof(count_t)); cdev++, ccur++, cpre++, i++) *cdev = *ccur - *cpre; dev->net.ipv4.Forwarding = cur->net.ipv4.Forwarding; dev->net.ipv4.DefaultTTL = cur->net.ipv4.DefaultTTL; /* ------------- */ for (cdev = (count_t *)&dev->net.icmpv4, ccur = (count_t *)&cur->net.icmpv4, cpre = (count_t *)&pre->net.icmpv4, i = 0; i < (sizeof dev->net.icmpv4 / sizeof(count_t)); cdev++, ccur++, cpre++, i++) *cdev = *ccur - *cpre; /* ------------- */ for (cdev = (count_t *)&dev->net.udpv4, ccur = (count_t *)&cur->net.udpv4, cpre = (count_t *)&pre->net.udpv4, i = 0; i < (sizeof dev->net.udpv4 / sizeof(count_t)); cdev++, ccur++, cpre++, i++) *cdev = *ccur - *cpre; /* ------------- */ for (cdev = (count_t *)&dev->net.ipv6, ccur = (count_t *)&cur->net.ipv6, cpre = (count_t *)&pre->net.ipv6, i = 0; i < (sizeof dev->net.ipv6 / sizeof(count_t)); cdev++, ccur++, cpre++, i++) *cdev = *ccur - *cpre; /* ------------- */ for (cdev = (count_t *)&dev->net.icmpv6, ccur = (count_t *)&cur->net.icmpv6, cpre = (count_t *)&pre->net.icmpv6, i = 0; i < (sizeof dev->net.icmpv6 / sizeof(count_t)); cdev++, ccur++, cpre++, i++) *cdev = *ccur - *cpre; /* ------------- */ for (cdev = (count_t *)&dev->net.udpv6, ccur = (count_t *)&cur->net.udpv6, cpre = (count_t *)&pre->net.udpv6, i = 0; i < (sizeof dev->net.udpv6 / sizeof(count_t)); cdev++, ccur++, cpre++, i++) *cdev = *ccur - *cpre; /* ------------- */ for (cdev = (count_t *)&dev->net.tcp, ccur = (count_t *)&cur->net.tcp, cpre = (count_t *)&pre->net.tcp, i = 0; i < (sizeof dev->net.tcp / sizeof(count_t)); cdev++, ccur++, cpre++, i++) *cdev = *ccur - *cpre; dev->net.tcp.RtoAlgorithm = cur->net.tcp.RtoAlgorithm; dev->net.tcp.RtoMin = cur->net.tcp.RtoMin; dev->net.tcp.RtoMax = cur->net.tcp.RtoMax; dev->net.tcp.MaxConn = cur->net.tcp.MaxConn; dev->net.tcp.CurrEstab = cur->net.tcp.CurrEstab; /* ** calculate deviations for interfaces */ for (i=0; cur->intf.intf[i].name[0]; i++) { // fill current properties for each valid interface strcpy(ifprop.name, cur->intf.intf[i].name); getifprop(&ifprop); cur->intf.intf[i].type = ifprop.type; cur->intf.intf[i].speed = ifprop.speed; cur->intf.intf[i].speedp = ifprop.speed; cur->intf.intf[i].duplex = ifprop.fullduplex; } if (pre->intf.intf[0].name[0] == '\0') /* first sample? */ { for (i=0; cur->intf.intf[i].name[0]; i++) { strcpy(pre->intf.intf[i].name, cur->intf.intf[i].name); pre->intf.intf[i].type = cur->intf.intf[i].type; pre->intf.intf[i].speed = cur->intf.intf[i].speed; pre->intf.intf[i].speedp = cur->intf.intf[i].speedp; pre->intf.intf[i].duplex = cur->intf.intf[i].duplex; } } for (i=0, j=0; cur->intf.intf[i].name[0]; i++, j++) { /* ** be sure that we have the same interface ** (interfaces could have been added or removed since ** previous sample) */ if (strcmp(cur->intf.intf[i].name, pre->intf.intf[j].name) != 0) { // try to resync for (j=0; pre->intf.intf[j].name[0]; j++) { if (strcmp(cur->intf.intf[i].name, pre->intf.intf[j].name) == 0) break; } // resync not succeeded? if (! pre->intf.intf[j].name[0]) { memcpy(&dev->intf.intf[i], &cur->intf.intf[i], sizeof cur->intf.intf[i]); j = 0; continue; } } /* ** calculate interface deviations for this sample */ strcpy(dev->intf.intf[i].name, cur->intf.intf[i].name); dev->intf.intf[i].rbyte = subcount(cur->intf.intf[i].rbyte, pre->intf.intf[j].rbyte); dev->intf.intf[i].rpack = subcount(cur->intf.intf[i].rpack, pre->intf.intf[j].rpack); dev->intf.intf[i].rerrs = subcount(cur->intf.intf[i].rerrs, pre->intf.intf[j].rerrs); dev->intf.intf[i].rdrop = subcount(cur->intf.intf[i].rdrop, pre->intf.intf[j].rdrop); dev->intf.intf[i].rfifo = subcount(cur->intf.intf[i].rfifo, pre->intf.intf[j].rfifo); dev->intf.intf[i].rframe= subcount(cur->intf.intf[i].rframe, pre->intf.intf[j].rframe); dev->intf.intf[i].rcompr= subcount(cur->intf.intf[i].rcompr, pre->intf.intf[j].rcompr); dev->intf.intf[i].rmultic=subcount(cur->intf.intf[i].rmultic, pre->intf.intf[j].rmultic); dev->intf.intf[i].sbyte = subcount(cur->intf.intf[i].sbyte, pre->intf.intf[j].sbyte); dev->intf.intf[i].spack = subcount(cur->intf.intf[i].spack, pre->intf.intf[j].spack); dev->intf.intf[i].serrs = subcount(cur->intf.intf[i].serrs, pre->intf.intf[j].serrs); dev->intf.intf[i].sdrop = subcount(cur->intf.intf[i].sdrop, pre->intf.intf[j].sdrop); dev->intf.intf[i].sfifo = subcount(cur->intf.intf[i].sfifo, pre->intf.intf[j].sfifo); dev->intf.intf[i].scollis= subcount(cur->intf.intf[i].scollis, pre->intf.intf[j].scollis); dev->intf.intf[i].scarrier= subcount(cur->intf.intf[i].scarrier, pre->intf.intf[j].scarrier); dev->intf.intf[i].scompr= subcount(cur->intf.intf[i].scompr, pre->intf.intf[j].scompr); dev->intf.intf[i].type = cur->intf.intf[i].type; dev->intf.intf[i].duplex = cur->intf.intf[i].duplex; dev->intf.intf[i].speed = cur->intf.intf[i].speed; dev->intf.intf[i].speedp = pre->intf.intf[j].speed; cur->intf.intf[i].speedp = pre->intf.intf[j].speed; } dev->intf.intf[i].name[0] = '\0'; dev->intf.nrintf = i; /* ** calculate deviations for disks */ for (i=j=0; cur->dsk.dsk[i].name[0]; i++) { int realj = j; /* ** check if disk has been added or removed since ** previous interval */ if ( strcmp(cur->dsk.dsk[i].name, pre->dsk.dsk[j].name) != 0) { for (j++; pre->dsk.dsk[j].name[0]; j++) { if ( strcmp(cur->dsk.dsk[i].name, pre->dsk.dsk[j].name) == 0) break; } /* ** either the corresponding entry has been found ** in the case that a disk has been removed, or ** an empty entry has been found (all counters ** on zero) in the case that a disk has been added ** during the last sample */ } strcpy(dev->dsk.dsk[i].name, cur->dsk.dsk[i].name); dev->dsk.dsk[i].nread = subcount(cur->dsk.dsk[i].nread, pre->dsk.dsk[j].nread); dev->dsk.dsk[i].nrsect = subcount(cur->dsk.dsk[i].nrsect, pre->dsk.dsk[j].nrsect); dev->dsk.dsk[i].nwrite = subcount(cur->dsk.dsk[i].nwrite, pre->dsk.dsk[j].nwrite); dev->dsk.dsk[i].nwsect = subcount(cur->dsk.dsk[i].nwsect, pre->dsk.dsk[j].nwsect); dev->dsk.dsk[i].inflight = cur->dsk.dsk[i].inflight; dev->dsk.dsk[i].io_ms = subcount(cur->dsk.dsk[i].io_ms, pre->dsk.dsk[j].io_ms); dev->dsk.dsk[i].avque = subcount(cur->dsk.dsk[i].avque, pre->dsk.dsk[j].avque); if (cur->dsk.dsk[i].ndisc != -1) // discards supported? { dev->dsk.dsk[i].ndisc = subcount(cur->dsk.dsk[i].ndisc, pre->dsk.dsk[j].ndisc); dev->dsk.dsk[i].ndsect = subcount(cur->dsk.dsk[i].ndsect, pre->dsk.dsk[j].ndsect); } else { dev->dsk.dsk[i].ndisc = -1; dev->dsk.dsk[i].ndsect = 0; } /* ** determine new j */ if (pre->dsk.dsk[j].name[0]) // existing matching entry j++; else j = realj; // empty entry: stick to old j } dev->dsk.dsk[i].name[0] = '\0'; dev->dsk.ndsk = i; /* ** calculate deviations for multiple devices */ for (i=j=0; cur->dsk.mdd[i].name[0]; i++) { int realj = j; /* ** check if md has been added or removed since ** previous interval */ if ( strcmp(cur->dsk.mdd[i].name, pre->dsk.mdd[j].name) != 0) { for (j++; pre->dsk.mdd[j].name[0]; j++) { if ( strcmp(cur->dsk.mdd[i].name, pre->dsk.mdd[j].name) == 0) break; } /* ** either the corresponding entry has been found ** in the case that a md has been removed, or ** an empty entry has been found (all counters ** on zero) in the case that a md has been added ** during the last sample */ } strcpy(dev->dsk.mdd[i].name, cur->dsk.mdd[i].name); dev->dsk.mdd[i].nread = subcount(cur->dsk.mdd[i].nread, pre->dsk.mdd[j].nread); dev->dsk.mdd[i].nrsect = subcount(cur->dsk.mdd[i].nrsect, pre->dsk.mdd[j].nrsect); dev->dsk.mdd[i].nwrite = subcount(cur->dsk.mdd[i].nwrite, pre->dsk.mdd[j].nwrite); dev->dsk.mdd[i].nwsect = subcount(cur->dsk.mdd[i].nwsect, pre->dsk.mdd[j].nwsect); dev->dsk.mdd[i].io_ms = subcount(cur->dsk.mdd[i].io_ms, pre->dsk.mdd[j].io_ms); dev->dsk.mdd[i].avque = subcount(cur->dsk.mdd[i].avque, pre->dsk.mdd[j].avque); if (cur->dsk.mdd[i].ndisc != -1) // discards supported? { dev->dsk.mdd[i].ndisc = subcount(cur->dsk.mdd[i].ndisc, pre->dsk.mdd[j].ndisc); dev->dsk.mdd[i].ndsect = subcount(cur->dsk.mdd[i].ndsect, pre->dsk.mdd[j].ndsect); } else { dev->dsk.mdd[i].ndisc = -1; dev->dsk.mdd[i].ndsect = 0; } /* ** determine new j */ if (pre->dsk.mdd[j].name[0]) // existing matching entry j++; else j = realj; // empty entry: stick to old j } dev->dsk.mdd[i].name[0] = '\0'; dev->dsk.nmdd = i; /* ** calculate deviations for LVM logical volumes */ for (i=j=0; cur->dsk.lvm[i].name[0]; i++) { int realj = j; /* ** check if logical volume has been added or removed since ** previous interval */ if ( strcmp(cur->dsk.lvm[i].name, pre->dsk.lvm[j].name) != 0) { for (j++; pre->dsk.lvm[j].name[0]; j++) { if ( strcmp(cur->dsk.lvm[i].name, pre->dsk.lvm[j].name) == 0) break; } /* ** either the corresponding entry has been found ** in the case that a logical volume has been removed, ** or an empty entry has been found (all counters ** on zero) in the case that a logical volume has ** been added during the last sample */ } strcpy(dev->dsk.lvm[i].name, cur->dsk.lvm[i].name); dev->dsk.lvm[i].nread = subcount(cur->dsk.lvm[i].nread, pre->dsk.lvm[j].nread); dev->dsk.lvm[i].nrsect = subcount(cur->dsk.lvm[i].nrsect, pre->dsk.lvm[j].nrsect); dev->dsk.lvm[i].nwrite = subcount(cur->dsk.lvm[i].nwrite, pre->dsk.lvm[j].nwrite); dev->dsk.lvm[i].nwsect = subcount(cur->dsk.lvm[i].nwsect, pre->dsk.lvm[j].nwsect); dev->dsk.lvm[i].io_ms = subcount(cur->dsk.lvm[i].io_ms, pre->dsk.lvm[j].io_ms); dev->dsk.lvm[i].avque = subcount(cur->dsk.lvm[i].avque, pre->dsk.lvm[j].avque); if (cur->dsk.lvm[i].ndisc != -1) // discards supported? { dev->dsk.lvm[i].ndisc = subcount(cur->dsk.lvm[i].ndisc, pre->dsk.lvm[j].ndisc); dev->dsk.lvm[i].ndsect = subcount(cur->dsk.lvm[i].ndsect, pre->dsk.lvm[j].ndsect); } else { dev->dsk.lvm[i].ndisc = -1; dev->dsk.lvm[i].ndsect = 0; } /* ** determine new j */ if (pre->dsk.lvm[j].name[0]) // existing matching entry j++; else j = realj; // empty entry: stick to old j } dev->dsk.lvm[i].name[0] = '\0'; dev->dsk.nlvm = i; /* ** calculate deviations for NFS */ dev->nfs.server.netcnt = subcount(cur->nfs.server.netcnt, pre->nfs.server.netcnt); dev->nfs.server.netudpcnt = subcount(cur->nfs.server.netudpcnt, pre->nfs.server.netudpcnt); dev->nfs.server.nettcpcnt = subcount(cur->nfs.server.nettcpcnt, pre->nfs.server.nettcpcnt); dev->nfs.server.nettcpcon = subcount(cur->nfs.server.nettcpcon, pre->nfs.server.nettcpcon); dev->nfs.server.rpccnt = subcount(cur->nfs.server.rpccnt, pre->nfs.server.rpccnt); dev->nfs.server.rpcread = subcount(cur->nfs.server.rpcread, pre->nfs.server.rpcread); dev->nfs.server.rpcwrite = subcount(cur->nfs.server.rpcwrite, pre->nfs.server.rpcwrite); dev->nfs.server.rpcbadfmt = subcount(cur->nfs.server.rpcbadfmt, pre->nfs.server.rpcbadfmt); dev->nfs.server.rpcbadaut = subcount(cur->nfs.server.rpcbadaut, pre->nfs.server.rpcbadaut); dev->nfs.server.rpcbadcln = subcount(cur->nfs.server.rpcbadcln, pre->nfs.server.rpcbadcln); dev->nfs.server.rchits = subcount(cur->nfs.server.rchits, pre->nfs.server.rchits); dev->nfs.server.rcmiss = subcount(cur->nfs.server.rcmiss, pre->nfs.server.rcmiss); dev->nfs.server.rcnoca = subcount(cur->nfs.server.rcnoca, pre->nfs.server.rcnoca); dev->nfs.server.nrbytes = subcount(cur->nfs.server.nrbytes, pre->nfs.server.nrbytes); dev->nfs.server.nwbytes = subcount(cur->nfs.server.nwbytes, pre->nfs.server.nwbytes); dev->nfs.client.rpccnt = subcount(cur->nfs.client.rpccnt, pre->nfs.client.rpccnt); dev->nfs.client.rpcread = subcount(cur->nfs.client.rpcread, pre->nfs.client.rpcread); dev->nfs.client.rpcwrite = subcount(cur->nfs.client.rpcwrite, pre->nfs.client.rpcwrite); dev->nfs.client.rpcretrans = subcount(cur->nfs.client.rpcretrans, pre->nfs.client.rpcretrans); dev->nfs.client.rpcautrefresh = subcount(cur->nfs.client.rpcautrefresh, pre->nfs.client.rpcautrefresh); for (i=j=0; i < cur->nfs.nfsmounts.nrmounts; i++, j++) { /* ** check if nfsmounts have been added or removed since ** previous interval */ if ( strcmp(cur->nfs.nfsmounts.nfsmnt[i].mountdev, pre->nfs.nfsmounts.nfsmnt[j].mountdev) != 0) { for (j=0; j < pre->nfs.nfsmounts.nrmounts; j++) { if ( strcmp(cur->nfs.nfsmounts.nfsmnt[i].mountdev, pre->nfs.nfsmounts.nfsmnt[j].mountdev) == 0) break; } /* ** either the corresponding entry has been found ** in the case that a container has been removed, ** or an empty entry has been found (all counters ** on zero) in the case that a container has ** been added during the last sample */ } strcpy(dev->nfs.nfsmounts.nfsmnt[i].mountdev, cur->nfs.nfsmounts.nfsmnt[i].mountdev); dev->nfs.nfsmounts.nfsmnt[i].age = cur->nfs.nfsmounts.nfsmnt[i].age; if (dev->nfs.nfsmounts.nfsmnt[i].age <= interval) memset(&(pre->nfs.nfsmounts.nfsmnt[j]), 0, sizeof(struct pernfsmount)); dev->nfs.nfsmounts.nfsmnt[i].bytesread = subcount(cur->nfs.nfsmounts.nfsmnt[i].bytesread, pre->nfs.nfsmounts.nfsmnt[j].bytesread); dev->nfs.nfsmounts.nfsmnt[i].byteswrite = subcount(cur->nfs.nfsmounts.nfsmnt[i].byteswrite, pre->nfs.nfsmounts.nfsmnt[j].byteswrite); dev->nfs.nfsmounts.nfsmnt[i].bytesdread = subcount(cur->nfs.nfsmounts.nfsmnt[i].bytesdread, pre->nfs.nfsmounts.nfsmnt[j].bytesdread); dev->nfs.nfsmounts.nfsmnt[i].bytesdwrite = subcount(cur->nfs.nfsmounts.nfsmnt[i].bytesdwrite, pre->nfs.nfsmounts.nfsmnt[j].bytesdwrite); dev->nfs.nfsmounts.nfsmnt[i].bytestotread = subcount(cur->nfs.nfsmounts.nfsmnt[i].bytestotread, pre->nfs.nfsmounts.nfsmnt[j].bytestotread); dev->nfs.nfsmounts.nfsmnt[i].bytestotwrite = subcount(cur->nfs.nfsmounts.nfsmnt[i].bytestotwrite, pre->nfs.nfsmounts.nfsmnt[j].bytestotwrite); dev->nfs.nfsmounts.nfsmnt[i].pagesmread = subcount(cur->nfs.nfsmounts.nfsmnt[i].pagesmread, pre->nfs.nfsmounts.nfsmnt[j].pagesmread); dev->nfs.nfsmounts.nfsmnt[i].pagesmwrite = subcount(cur->nfs.nfsmounts.nfsmnt[i].pagesmwrite, pre->nfs.nfsmounts.nfsmnt[j].pagesmwrite); } dev->nfs.nfsmounts.nrmounts = cur->nfs.nfsmounts.nrmounts; /* ** calculate deviations for containers */ for (i=j=0; i < cur->cfs.nrcontainer; i++, j++) { /* ** check if containers have been added or removed since ** previous interval */ if (cur->cfs.cont[i].ctid != pre->cfs.cont[j].ctid) { for (j=0; j < pre->cfs.nrcontainer; j++) { if (cur->cfs.cont[i].ctid == pre->cfs.cont[j].ctid) break; } /* ** either the corresponding entry has been found ** in the case that a container has been removed, ** or an empty entry has been found (all counters ** on zero) in the case that a container has ** been added during the last sample */ } dev->cfs.cont[i].ctid = cur->cfs.cont[i].ctid; dev->cfs.cont[i].numproc = cur->cfs.cont[i].numproc; dev->cfs.cont[i].physpages = cur->cfs.cont[i].physpages; dev->cfs.cont[i].system = subcount(cur->cfs.cont[i].system, pre->cfs.cont[j].system); dev->cfs.cont[i].user = subcount(cur->cfs.cont[i].user, pre->cfs.cont[j].user); dev->cfs.cont[i].nice = subcount(cur->cfs.cont[i].nice, pre->cfs.cont[j].nice); dev->cfs.cont[i].uptime = subcount(cur->cfs.cont[i].uptime, pre->cfs.cont[j].uptime); } dev->cfs.nrcontainer = cur->cfs.nrcontainer; /* ** calculate deviations for GPUs */ for (i=0; i < cur->gpu.nrgpus; i++) { dev->gpu.gpu[i].gpunr = i; strcpy(dev->gpu.gpu[i].type, cur->gpu.gpu[i].type); strcpy(dev->gpu.gpu[i].busid, cur->gpu.gpu[i].busid); dev->gpu.gpu[i].taskstats = cur->gpu.gpu[i].taskstats; dev->gpu.gpu[i].nrprocs = cur->gpu.gpu[i].nrprocs; dev->gpu.gpu[i].gpupercnow = cur->gpu.gpu[i].gpupercnow; dev->gpu.gpu[i].mempercnow = cur->gpu.gpu[i].mempercnow; dev->gpu.gpu[i].memtotnow = cur->gpu.gpu[i].memtotnow; dev->gpu.gpu[i].memusenow = cur->gpu.gpu[i].memusenow; dev->gpu.gpu[i].samples = subcount(cur->gpu.gpu[i].samples, pre->gpu.gpu[i].samples); if (cur->gpu.gpu[i].gpuperccum >= 0) dev->gpu.gpu[i].gpuperccum = subcount(cur->gpu.gpu[i].gpuperccum, pre->gpu.gpu[i].gpuperccum); else dev->gpu.gpu[i].gpuperccum = -1; if (cur->gpu.gpu[i].memusecum >= 0) dev->gpu.gpu[i].memusecum = subcount(cur->gpu.gpu[i].memusecum, pre->gpu.gpu[i].memusecum); else dev->gpu.gpu[i].memusecum = -1; if (cur->gpu.gpu[i].memperccum >= 0) dev->gpu.gpu[i].memperccum = subcount(cur->gpu.gpu[i].memperccum, pre->gpu.gpu[i].memperccum); else dev->gpu.gpu[i].memperccum = -1; } dev->gpu.nrgpus = cur->gpu.nrgpus; /* ** calculate deviations for InfiniBand */ for (i=0; i < cur->ifb.nrports; i++) { strcpy(dev->ifb.ifb[i].ibname, cur->ifb.ifb[i].ibname); dev->ifb.ifb[i].portnr = cur->ifb.ifb[i].portnr; dev->ifb.ifb[i].lanes = cur->ifb.ifb[i].lanes; dev->ifb.ifb[i].rate = cur->ifb.ifb[i].rate; dev->ifb.ifb[i].rcvb = cur->ifb.ifb[i].rcvb - pre->ifb.ifb[i].rcvb; dev->ifb.ifb[i].sndb = cur->ifb.ifb[i].sndb - pre->ifb.ifb[i].sndb; dev->ifb.ifb[i].rcvp = cur->ifb.ifb[i].rcvp - pre->ifb.ifb[i].rcvp; dev->ifb.ifb[i].sndp = cur->ifb.ifb[i].sndp - pre->ifb.ifb[i].sndp; } dev->ifb.nrports = cur->ifb.nrports; /* ** calculate deviations for Last Level Cache */ for (i = 0; i < cur->llc.nrllcs; i++) { dev->llc.perllc[i].id = cur->llc.perllc[i].id; dev->llc.perllc[i].occupancy = cur->llc.perllc[i].occupancy; dev->llc.perllc[i].mbm_local = cur->llc.perllc[i].mbm_local - pre->llc.perllc[i].mbm_local; dev->llc.perllc[i].mbm_total = cur->llc.perllc[i].mbm_total - pre->llc.perllc[i].mbm_total; } dev->llc.nrllcs = cur->llc.nrllcs; #if HTTPSTATS /* ** application-specific counters */ if (cur->www.uptime >= pre->www.uptime) { dev->www.accesses = subcount(cur->www.accesses, pre->www.accesses); dev->www.totkbytes = subcount(cur->www.totkbytes, pre->www.totkbytes); } else { dev->www.accesses = cur->www.accesses; dev->www.totkbytes = cur->www.totkbytes; } dev->www.bworkers = cur->www.bworkers; dev->www.iworkers = cur->www.iworkers; #endif } /* ** add the values of a new sample to a structure holding the totals ** for the indicated category (c=cpu, m=memory, d=disk, n=network). */ void totalsyst(char category, struct sstat *new, struct sstat *tot) { register int i; count_t *ctot, *cnew; switch (category) { case 'c': /* accumulate cpu-related counters */ tot->cpu.nrcpu = new->cpu.nrcpu; tot->cpu.devint += new->cpu.devint; tot->cpu.csw += new->cpu.csw; tot->cpu.nprocs += new->cpu.nprocs; tot->cpu.all.stime += new->cpu.all.stime; tot->cpu.all.utime += new->cpu.all.utime; tot->cpu.all.ntime += new->cpu.all.ntime; tot->cpu.all.itime += new->cpu.all.itime; tot->cpu.all.wtime += new->cpu.all.wtime; tot->cpu.all.Itime += new->cpu.all.Itime; tot->cpu.all.Stime += new->cpu.all.Stime; tot->cpu.all.steal += new->cpu.all.steal; tot->cpu.all.guest += new->cpu.all.guest; if (new->cpu.nrcpu == 1) { tot->cpu.cpu[0] = tot->cpu.all; } else { for (i=0; i < new->cpu.nrcpu; i++) { tot->cpu.cpu[i].cpunr = new->cpu.cpu[i].cpunr; tot->cpu.cpu[i].stime += new->cpu.cpu[i].stime; tot->cpu.cpu[i].utime += new->cpu.cpu[i].utime; tot->cpu.cpu[i].ntime += new->cpu.cpu[i].ntime; tot->cpu.cpu[i].itime += new->cpu.cpu[i].itime; tot->cpu.cpu[i].wtime += new->cpu.cpu[i].wtime; tot->cpu.cpu[i].Itime += new->cpu.cpu[i].Itime; tot->cpu.cpu[i].Stime += new->cpu.cpu[i].Stime; tot->cpu.cpu[i].steal += new->cpu.cpu[i].steal; tot->cpu.cpu[i].guest += new->cpu.cpu[i].guest; } } tot->cpu.lavg1 = new->cpu.lavg1; tot->cpu.lavg5 = new->cpu.lavg5; tot->cpu.lavg15 = new->cpu.lavg15; break; case 'm': /* accumulate memory-related counters */ tot->mem.physmem = new->mem.physmem; tot->mem.freemem = new->mem.freemem; tot->mem.buffermem = new->mem.buffermem; tot->mem.slabmem = new->mem.slabmem; tot->mem.slabreclaim = new->mem.slabreclaim; tot->mem.committed = new->mem.committed; tot->mem.commitlim = new->mem.commitlim; tot->mem.cachemem = new->mem.cachemem; tot->mem.cachedrt = new->mem.cachedrt; tot->mem.totswap = new->mem.totswap; tot->mem.freeswap = new->mem.freeswap; tot->mem.swapcached = new->mem.swapcached; tot->mem.pagetables = new->mem.pagetables; tot->mem.zswap = new->mem.zswap; tot->mem.zswapped = new->mem.zswapped; tot->mem.shmem = new->mem.shmem; tot->mem.shmrss = new->mem.shmrss; tot->mem.shmswp = new->mem.shmswp; tot->mem.tcpsock = new->mem.tcpsock; tot->mem.udpsock = new->mem.udpsock; tot->mem.pgouts += new->mem.pgouts; tot->mem.pgins += new->mem.pgins; tot->mem.swouts += new->mem.swouts; tot->mem.swins += new->mem.swins; tot->mem.zswouts += new->mem.zswouts; tot->mem.zswins += new->mem.zswins; tot->mem.pgscans += new->mem.pgscans; tot->mem.allocstall += new->mem.allocstall; tot->mem.compactstall += new->mem.compactstall; break; case 'n': /* accumulate network-related counters */ tot->nfs.server.rpccnt += new->nfs.server.rpccnt; tot->nfs.server.rpcread += new->nfs.server.rpcread; tot->nfs.server.rpcwrite += new->nfs.server.rpcwrite; tot->nfs.server.rpcbadfmt += new->nfs.server.rpcbadfmt; tot->nfs.server.rpcbadaut += new->nfs.server.rpcbadaut; tot->nfs.server.rpcbadcln += new->nfs.server.rpcbadcln; tot->nfs.server.netcnt += new->nfs.server.netcnt; tot->nfs.server.nettcpcnt += new->nfs.server.nettcpcnt; tot->nfs.server.netudpcnt += new->nfs.server.netudpcnt; tot->nfs.server.nettcpcon += new->nfs.server.nettcpcon; tot->nfs.server.rchits += new->nfs.server.rchits; tot->nfs.server.rcmiss += new->nfs.server.rcmiss; tot->nfs.server.rcnoca += new->nfs.server.rcnoca; tot->nfs.server.nrbytes += new->nfs.server.nrbytes; tot->nfs.server.nwbytes += new->nfs.server.nwbytes; tot->nfs.client.rpccnt += new->nfs.client.rpccnt; tot->nfs.client.rpcread += new->nfs.client.rpcread; tot->nfs.client.rpcwrite += new->nfs.client.rpcwrite; tot->nfs.client.rpcretrans += new->nfs.client.rpcretrans; tot->nfs.client.rpcautrefresh += new->nfs.client.rpcautrefresh; /* ** other structures with network counters are considered ** as tables of frequency-counters that will be accumulated; ** values that do not represent a frequency are corrected ** afterwards */ for (ctot = (count_t *)&tot->net.ipv4, cnew = (count_t *)&new->net.ipv4, i = 0; i < (sizeof tot->net.ipv4 / sizeof(count_t)); ctot++, cnew++, i++) *ctot += *cnew; tot->net.ipv4.Forwarding = new->net.ipv4.Forwarding; tot->net.ipv4.DefaultTTL = new->net.ipv4.DefaultTTL; /* ------------- */ for (ctot = (count_t *)&tot->net.icmpv4, cnew = (count_t *)&new->net.icmpv4, i = 0; i < (sizeof tot->net.icmpv4 / sizeof(count_t)); ctot++, cnew++, i++) *ctot += *cnew; /* ------------- */ for (ctot = (count_t *)&tot->net.udpv4, cnew = (count_t *)&new->net.udpv4, i = 0; i < (sizeof tot->net.udpv4 / sizeof(count_t)); ctot++, cnew++, i++) *ctot += *cnew; /* ------------- */ for (ctot = (count_t *)&tot->net.ipv6, cnew = (count_t *)&new->net.ipv6, i = 0; i < (sizeof tot->net.ipv6 / sizeof(count_t)); ctot++, cnew++, i++) *ctot += *cnew; /* ------------- */ for (ctot = (count_t *)&tot->net.icmpv6, cnew = (count_t *)&new->net.icmpv6, i = 0; i < (sizeof tot->net.icmpv6 / sizeof(count_t)); ctot++, cnew++, i++) *ctot += *cnew; /* ------------- */ for (ctot = (count_t *)&tot->net.udpv6, cnew = (count_t *)&new->net.udpv6, i = 0; i < (sizeof tot->net.udpv6 / sizeof(count_t)); ctot++, cnew++, i++) *ctot += *cnew; /* ------------- */ for (ctot = (count_t *)&tot->net.tcp, cnew = (count_t *)&new->net.tcp, i = 0; i < (sizeof tot->net.tcp / sizeof(count_t)); ctot++, cnew++, i++) *ctot += *cnew; tot->net.tcp.RtoAlgorithm = new->net.tcp.RtoAlgorithm; tot->net.tcp.RtoMin = new->net.tcp.RtoMin; tot->net.tcp.RtoMax = new->net.tcp.RtoMax; tot->net.tcp.MaxConn = new->net.tcp.MaxConn; tot->net.tcp.CurrEstab = new->net.tcp.CurrEstab; for (i=0; new->intf.intf[i].name[0]; i++) { /* ** check if an interface has been added or removed; ** in that case, zero all counters */ if (strcmp(new->intf.intf[i].name, tot->intf.intf[i].name) != 0) { tot->intf.intf[i].rbyte = 0; tot->intf.intf[i].rpack = 0; tot->intf.intf[i].rerrs = 0; tot->intf.intf[i].rdrop = 0; tot->intf.intf[i].rfifo = 0; tot->intf.intf[i].rframe = 0; tot->intf.intf[i].rcompr = 0; tot->intf.intf[i].rmultic = 0; tot->intf.intf[i].sbyte = 0; tot->intf.intf[i].spack = 0; tot->intf.intf[i].serrs = 0; tot->intf.intf[i].sdrop = 0; tot->intf.intf[i].sfifo = 0; tot->intf.intf[i].scollis = 0; tot->intf.intf[i].scarrier = 0; tot->intf.intf[i].scompr = 0; } /* ** accumulate counters for this sample */ strcpy(tot->intf.intf[i].name, new->intf.intf[i].name); tot->intf.intf[i].rbyte += new->intf.intf[i].rbyte; tot->intf.intf[i].rpack += new->intf.intf[i].rpack; tot->intf.intf[i].rerrs += new->intf.intf[i].rerrs; tot->intf.intf[i].rdrop += new->intf.intf[i].rdrop; tot->intf.intf[i].rfifo += new->intf.intf[i].rfifo; tot->intf.intf[i].rframe += new->intf.intf[i].rframe; tot->intf.intf[i].rcompr += new->intf.intf[i].rcompr; tot->intf.intf[i].rmultic += new->intf.intf[i].rmultic; tot->intf.intf[i].sbyte += new->intf.intf[i].sbyte; tot->intf.intf[i].spack += new->intf.intf[i].spack; tot->intf.intf[i].serrs += new->intf.intf[i].serrs; tot->intf.intf[i].sdrop += new->intf.intf[i].sdrop; tot->intf.intf[i].sfifo += new->intf.intf[i].sfifo; tot->intf.intf[i].scollis += new->intf.intf[i].scollis; tot->intf.intf[i].scarrier+= new->intf.intf[i].scarrier; tot->intf.intf[i].scompr += new->intf.intf[i].scompr; tot->intf.intf[i].type = new->intf.intf[i].type; tot->intf.intf[i].speed = new->intf.intf[i].speed; tot->intf.intf[i].duplex = new->intf.intf[i].duplex; } tot->intf.intf[i].name[0] = '\0'; tot->intf.nrintf = i; #if HTTPSTATS tot->www.accesses += new->www.accesses; tot->www.totkbytes += new->www.totkbytes; tot->www.bworkers = new->www.bworkers; tot->www.iworkers = new->www.iworkers; #endif break; case 'd': /* accumulate disk-related counters */ for (i=0; new->dsk.dsk[i].name[0]; i++) { strcpy(tot->dsk.dsk[i].name, new->dsk.dsk[i].name); tot->dsk.dsk[i].nread += new->dsk.dsk[i].nread; tot->dsk.dsk[i].nrsect += new->dsk.dsk[i].nrsect; tot->dsk.dsk[i].nwrite += new->dsk.dsk[i].nwrite; tot->dsk.dsk[i].nwsect += new->dsk.dsk[i].nwsect; tot->dsk.dsk[i].io_ms += new->dsk.dsk[i].io_ms; tot->dsk.dsk[i].avque += new->dsk.dsk[i].avque; if (new->dsk.dsk[i].ndisc != -1) // discards? { tot->dsk.dsk[i].ndisc += new->dsk.dsk[i].ndisc; tot->dsk.dsk[i].ndsect += new->dsk.dsk[i].ndsect; } } tot->dsk.dsk[i].name[0] = '\0'; tot->dsk.ndsk = i; for (i=0; new->dsk.lvm[i].name[0]; i++) { strcpy(tot->dsk.lvm[i].name, new->dsk.lvm[i].name); tot->dsk.lvm[i].nread += new->dsk.lvm[i].nread; tot->dsk.lvm[i].nrsect += new->dsk.lvm[i].nrsect; tot->dsk.lvm[i].nwrite += new->dsk.lvm[i].nwrite; tot->dsk.lvm[i].nwsect += new->dsk.lvm[i].nwsect; tot->dsk.lvm[i].io_ms += new->dsk.lvm[i].io_ms; tot->dsk.lvm[i].avque += new->dsk.lvm[i].avque; if (new->dsk.lvm[i].ndisc != -1) // discards? { tot->dsk.lvm[i].ndisc += new->dsk.lvm[i].ndisc; tot->dsk.lvm[i].ndsect += new->dsk.lvm[i].ndsect; } } tot->dsk.lvm[i].name[0] = '\0'; tot->dsk.nlvm = i; for (i=0; new->dsk.mdd[i].name[0]; i++) { strcpy(tot->dsk.mdd[i].name, new->dsk.mdd[i].name); tot->dsk.mdd[i].nread += new->dsk.mdd[i].nread; tot->dsk.mdd[i].nrsect += new->dsk.mdd[i].nrsect; tot->dsk.mdd[i].nwrite += new->dsk.mdd[i].nwrite; tot->dsk.mdd[i].nwsect += new->dsk.mdd[i].nwsect; tot->dsk.mdd[i].io_ms += new->dsk.mdd[i].io_ms; tot->dsk.mdd[i].avque += new->dsk.mdd[i].avque; if (new->dsk.mdd[i].ndisc != -1) // discards? { tot->dsk.mdd[i].ndisc += new->dsk.lvm[i].ndisc; tot->dsk.mdd[i].ndsect += new->dsk.lvm[i].ndsect; } } tot->dsk.mdd[i].name[0] = '\0'; tot->dsk.nmdd = i; break; } } /* ** Generic function to subtract two counters taking into ** account the possibility that the counter is invalid ** (i.e. non-existing). */ static inline count_t subcount(count_t newval, count_t oldval) { if (newval == -1) // invalid counter? return -1; if (newval >= oldval) // normal situation return newval - oldval; else // counter seems to be reset return newval; } atop-2.12.1/drawbar.c0000644000203100020310000022413215064552761013645 0ustar gerlofgerlof/* ** ATOP - System & Process Monitor ** ** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as process-level. ** ** This source-file contains functions for bar graph representation of ** system-level statistics about processors, memory, disks and network ** interfaces. ** ========================================================================== ** Author: Gerlof Langeveld ** E-mail: gerlof.langeveld@atoptool.nl ** Date: March/April 2023 Initial ** November/December 2024 Add PSI bar graphs ** -------------------------------------------------------------------------- ** Copyright (C) 2023-2024 Gerlof Langeveld ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ** -------------------------------------------------------------------------- */ ///////////////////////////////////////////////////////////////////////////// // Screen layout in bar graph mode will be based on one of these two models: // // ============ // Memory model // ============ // This model is preferred when the number of disks and network interfaces // is limited, because it shows more memory details. // // +---------------------------------------------------+ // | ATOP - host
... elapsed | // | | // | | | // | Processor bar graph | | // | | | // | | | // | | | // |---------------------------------| Memory graph | // | | | | // | | | | // | Disk graph | Interface graph | | // | | | | // | | | | // +---------------------------------------------------+ // // OR // // ============ // I/O model // ============ // When many disks and/or network interfaces are present, more space // is needed for these windows in the lower half of the screen. So the // memory window only uses the upper half of the screen. // // +---------------------------------------------------+ // | ATOP - host
... elapsed | // | | // | | | // | | | // | Processor bar graph | Memory graph | // | | | // | | | // |---------------------------------------------------| // | | | // | | | // | Disk bar graph | Interface bar graph | // | | | // | | | // +---------------------------------------------------+ // // For every bar graph (processor, memory, disk and interface) a // separate window is created. Apart from these four windows, // other windows are created: // // 1. A window for the header line (always). // // 2. A window for the horizontal ruler line in between the upper // and lower half of the screen (always). // // 3. A window for the vertical ruler between the disk and interface // window in the lower half (always). // // 4. A window for the vertical ruler between the processor and memory // window in the upper half (in case of I/O model) or a screen-size // vertical ruler (in case of memory model). // // The choice between the memory model and I/O model is dynamically made // based on the number of columns in the screen (terminal window) and the // number of disks/interfaces to be presented. When the terminal window is // horizontally scaled by the user, atop might switch from one model to // the other. ///////////////////////////////////////////////////////////////////////////// #define _POSIX_C_SOURCE #define _XOPEN_SOURCE #define _GNU_SOURCE #define _DEFAULT_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include "atop.h" #include "photoproc.h" #include "showgeneric.h" #include "photosyst.h" extern char usecolors; static char winchange; static char keywaiting; // key pushed back in stream? // maximum X axis label length // #define MAXLABLEN 7 // number of columns in memory management bars // #define MEMORYBARSZ 11 #define SWAPBARSZ 8 #define EVENTBARSZ 10 // four windows are created to show graphs // // - processor stats (left upper) // - memory stats (right upper or right full) // - disk stats (left lower) // - interface stats (right lower) // // metadata for each of these windows: struct perwindow // struct perwindow { WINDOW *win; int nlines, ncols; }; static struct perwindow wincpu, winmem, windsk, winnet; // additional windows for the header line and for line drawing // static WINDOW *headwin, *midline, *colupper, *collower; // struct for calling drawvertbars() // #define MAXCAT 5 // maximum number of categories within one bar #define MAXHEIGHT 25 // maximum bar height in lines struct vertval { int barval; // total value of bar char *barlab; // bar label char basecolor; // bar color or fill color char barmap[MAXHEIGHT]; // color map char barchr[MAXHEIGHT]; // character map int numcat; // number of categories in bar struct { int cval; // per-category value int ccol; // per-category color char clab; // per-category label } category[MAXCAT]; }; // struct for calling drawnetbars() // struct netval { count_t pvals; // send bytes-per-second count_t pvalr; // recv bytes-per-second int speed; // speed in Mbits/sec int maxmbits; // bar scale char *barlab; // bar label }; // header message // static char *headmsg = "Press '?' for help"; // function prototypes // static int headergetch(time_t, int, char *, int); static int wininit(struct sstat *); static void winexit(void); static void showhelp(void); static int getwininput(char *, char *, int, char); static void colorswon(WINDOW *, int); static void colorswoff(WINDOW *, int); static int severitycolor(char); static char setseverity(long, long, long); static float getwinratio(struct sstat *, char *); static void do_cpubars(struct sstat *, int, char, char); static void do_dskbars(struct sstat *, int, char, char); static void do_netbars(struct sstat *, int, char, char); static void sortvertbars(int, int, struct vertval **); static int compvertval(const void *, const void *); static void sortnetbars(int, struct netval **); static int compnetval(const void *, const void *); static void fillbarmaps(int, struct vertval *, float, int); static int drawvertbars(struct perwindow *, float, float, int, int, struct vertval *, int, char *, char *, int, int, int); static void drawpsibars(WINDOW *, int, int, int, int, int, int, int, int); static int drawnetbars(struct perwindow *, int, struct netval *, int, char *,char *); static int drawmemory(struct perwindow *w, struct sstat *, int, time_t, char); static int drawmemlines(struct perwindow *, int, int, int, int, int, char *, char *); static int drawevent(struct perwindow *, int, int, int, char *, char *, long); static void getsigwinch(int); ///////////////////////////////////////////////////// // entry point to display the deviation counters // on system level in bar graph mode ///////////////////////////////////////////////////// char draw_samp(time_t curtime, int nsecs, struct sstat *sstat, char flag, char sorted) { static char winitialized, wassorted, initlabels, swapinuse, ttyrescaled; static time_t lasttime; static int nrdisk, nrallintf, nrintf; int lastchar, i, newinterval, statuscol; char *statusmsg = NULL, buf[32], lower=0, answer[16]; // when needed (re)initialize the windows for bar graphs // if (!winitialized || nrdisk != sstat->dsk.ndsk || nrallintf != sstat->intf.nrintf ) { // determine the number of disks and network interfaces // only the physical disks and network interfaces apply // nrdisk = sstat->dsk.ndsk; nrallintf = sstat->intf.nrintf; for (i=0, nrintf=0; i < sstat->intf.nrintf; i++) { if (sstat->intf.intf[i].type != 'v') nrintf++; } // initialize graph windows // swapinuse = sstat->mem.totswap ? 1 : 0; if (winitialized) // already initialized -> first remove winexit(); wininit(sstat); winitialized = 1; } // verify if situation related to swap usage has been // changed (in that case the memory window has // to be redefined) // if (( sstat->mem.totswap && !swapinuse) || (!sstat->mem.totswap && swapinuse) ) { swapinuse = sstat->mem.totswap ? 1 : 0; winexit(); wininit(sstat); } // main loop: // - draw bar graphs // - wait for keystroke or alarm expiration // while (1) { initlabels = sorted != wassorted; if (!initlabels && lasttime != curtime && sorted) initlabels = 1; // show processor graph // do_cpubars(sstat, nsecs, initlabels, barmono); // show disk graph // do_dskbars(sstat, nsecs, initlabels, barmono); // show network (interfaces) graph // do_netbars(sstat, nsecs, initlabels, lower); // show memory graph // drawmemory(&winmem, sstat, nsecs, curtime, flag); // reset label initialization // initlabels = 0; wassorted = sorted; // verify if status message is required // statusmsg = NULL; statuscol = FGCOLORBORDER; if (flag&RRBOOT) statusmsg = "SINCE BOOT"; if (paused) // might overrule other message statusmsg = "PAUSED"; if (ttyrescaled) { snprintf(buf, sizeof buf, " %dx%d ", LINES, COLS); statusmsg = buf; ttyrescaled = 0; // show critical color when getting close to minimum // screen width // if (COLS < MINCOLUMNS+5 || LINES < MINLINES+5) statuscol = FGCOLORCRIT; } // wait for keystroke, alarm expiration or inotify watcher // switch (lastchar = headergetch(curtime, nsecs, statusmsg, statuscol)) { case ERR: // alarm expired? case 0: return lastchar; case MCGROUPS: case MPROCGEN: case MPROCMEM: case MPROCDSK: case MPROCNET: case MPROCGPU: case MPROCSCH: case MPROCVAR: case MPROCARG: case MBARGRAPH: winexit(); // close windows winitialized = 0; return lastchar; // switch to text mode case KEY_RESIZE: // terminal window resize? winexit(); // when window gets too small for bar graph mode, // switch to text mode // if (COLS < MINCOLUMNS || LINES < MINLINES) { winitialized = 0; return MBARGRAPH; } // terminal window size still fine: // reinitialize all ncurses windows // wininit(sstat); ttyrescaled = 1; break; // redraw with current values case MSAMPNEXT: // manual trigger for next sample? if (paused && !twinpid) break; getalarm(0); return lastchar; case MSAMPPREV: // manual trigger for previous sample? if (!rawreadflag) { beep(); break; } if (!paused && twinpid) { paused=1; // implicit pause in twin mode clrtoeol(); refresh(); } getalarm(0); return lastchar; case MRESET: // reset to begin? getalarm(0); paused = 0; if (twinpid) { paused=1; // implicit pause in twin mode clrtoeol(); refresh(); } return lastchar; case MEND: // branch to end? // only possible in twin mode or when viewing raw file // if (!rawreadflag) { beep(); break; } if (!paused && twinpid) { paused=1; // implicit pause in twin mode clrtoeol(); refresh(); } begintime = 0x7fffffff; return MSAMPBRANCH; case MSAMPBRANCH: // branch to other time? // only possible in twin mode or when viewing raw file // if (!rawreadflag) { beep(); break; } if (!paused && twinpid) { paused=1; // implicit pause in twin mode clrtoeol(); refresh(); } if (getwininput("Enter new time (format [YYYYMMDD]hhmm): ", answer, sizeof answer, 1) >= 0) { begintime = cursortime; if ( !getbranchtime(answer, &begintime) ) { beep(); begintime = 0; break; // branch failed } return lastchar; } break; case MBARLOWER: // reset network scale? lower = 1; break; case MBARMONO: // categorized busy bars? if (barmono) barmono = 0; else barmono = 1; break; case MPAUSE: // pause key (toggle)? if (rawreadflag && !twinpid) { beep(); break; } if (paused) { paused=0; if (!rawreadflag) { alarm(1); } else { begintime = 0x7fffffff; return MSAMPBRANCH; } } else { paused=1; alarm(0); // stop the clock } break; case MINTERVAL: // modify interval? // not possible in twin mode or when viewing raw file // if (rawreadflag) { beep(); break; } alarm(0); // stop the clock if ( (newinterval = getwininput("Interval: ", answer, sizeof answer, 1)) >= 0) interval = newinterval; if (!paused) alarm(1); // set short timer break; case MQUIT: // quit entirely? winexit(); move(LINES-1, 0); clrtoeol(); refresh(); cleanstop(0); case MHELP1: // help wanted? case MHELP2: alarm(0); // stop the clock // show help lines // showhelp(); // reinitialize original windows // winexit(); wininit(sstat); if (interval && !paused && !rawreadflag) alarm(1); // force new sample if (twinpid) // twin mode? { // jump to last sample after awaiting input begintime = 0x7fffffff; return MSAMPBRANCH; } break; default: // any other key pressed? break; // ignore } } } ///////////////////////////////////////////////////// // prepare the CPU specific bar graph ///////////////////////////////////////////////////// static void do_cpubars(struct sstat *sstat, int nsecs, char initlabels, char mono) { static int labellen, numcpus, numlabs; static char *labarea, *p; static struct vertval *vertvals; count_t alltics; int i, psisomeperc, psifullperc; char buf[16]; // check if the number of CPUs has been changed since // previous sample and create X axis labels for all // CPUs // if (numcpus != sstat->cpu.nrcpu || initlabels) { // remove old label space // if (vertvals) { free(vertvals); free(labarea); } // create new label space // - for one CPU, one label is enough (only 'Avg') // - for more than one CPU, one label is added (for 'Avg') // numcpus = sstat->cpu.nrcpu; numlabs = numcpus > 1 ? numcpus + 1 : 1; labellen = snprintf(buf, sizeof buf, "%d", numcpus); vertvals = malloc(numlabs * sizeof(struct vertval)); ptrverify(vertvals, "Malloc failed for %d vertval structs\n", numlabs); labarea = malloc(numlabs * (labellen+1)); ptrverify(labarea, "Malloc failed for %d CPU labels\n", numlabs); // create new X axis labels // if (numcpus == 1) vertvals->barlab = "0"; else { vertvals->barlab = "Avg "; for (i=0, p=labarea; i < numcpus; i++) { (vertvals+i+1)->barlab = p; snprintf(p, labellen+1, "%-*d", labellen, sstat->cpu.cpu[i].cpunr); p += labellen+1; } } } // calculate overall busy percentage and // fill first busy value (average) // alltics = sstat->cpu.all.stime + sstat->cpu.all.utime + sstat->cpu.all.ntime + sstat->cpu.all.itime + sstat->cpu.all.wtime + sstat->cpu.all.Itime + sstat->cpu.all.Stime + sstat->cpu.all.steal; vertvals->barval = 100 - (sstat->cpu.all.itime + sstat->cpu.all.wtime) * 100 / alltics; vertvals->basecolor = WHITE_BLUE0; if (!mono) { vertvals->category[0].ccol = COLORCPUINTR; vertvals->category[0].clab = 'I'; vertvals->category[0].cval = (sstat->cpu.all.Stime + sstat->cpu.all.Itime) * 100 / alltics; vertvals->category[1].ccol = COLORCPUSYS; vertvals->category[1].clab = 'S'; vertvals->category[1].cval = sstat->cpu.all.stime * 100 / alltics; vertvals->category[2].ccol = COLORCPUUSR; vertvals->category[2].clab = 'U'; vertvals->category[2].cval = (sstat->cpu.all.utime + sstat->cpu.all.ntime - sstat->cpu.all.guest) * 100 / alltics; vertvals->category[3].ccol = COLORCPUSTEAL; vertvals->category[3].clab = 's'; vertvals->category[3].cval = sstat->cpu.all.steal * 100 / alltics; vertvals->category[4].ccol = COLORCPUGUEST; vertvals->category[4].clab = 'G'; vertvals->category[4].cval = sstat->cpu.all.guest * 100 / alltics; vertvals->numcat = 5; } else { vertvals->numcat = 0; } // if more than one CPU: calculate per CPU // if (numcpus > 1) { // total ticks during last interval for CPU 0 // alltics = sstat->cpu.cpu[0].stime + sstat->cpu.cpu[0].utime + sstat->cpu.cpu[0].ntime + sstat->cpu.cpu[0].itime + sstat->cpu.cpu[0].wtime + sstat->cpu.cpu[0].Itime + sstat->cpu.cpu[0].Stime + sstat->cpu.cpu[0].steal; // busy percentage per CPU // for (i=0; i < numcpus; i++) { (vertvals+i+1)->barval = 100 - (sstat->cpu.cpu[i].itime + sstat->cpu.cpu[i].wtime ) *100/alltics; if ((vertvals+i+1)->barval < 0) (vertvals+i+1)->barval = 0; (vertvals+i+1)->basecolor = WHITE_BLUE0; if (!mono) { (vertvals+i+1)->category[0].ccol = COLORCPUINTR; (vertvals+i+1)->category[0].clab = 'I'; (vertvals+i+1)->category[0].cval = (sstat->cpu.cpu[i].Stime + sstat->cpu.cpu[i].Itime) *100/alltics; (vertvals+i+1)->category[1].ccol = COLORCPUSYS; (vertvals+i+1)->category[1].clab = 'S'; (vertvals+i+1)->category[1].cval = sstat->cpu.cpu[i].stime * 100 / alltics; (vertvals+i+1)->category[2].ccol = COLORCPUUSR; (vertvals+i+1)->category[2].clab = 'U'; (vertvals+i+1)->category[2].cval = (sstat->cpu.cpu[i].utime + sstat->cpu.cpu[i].ntime - sstat->cpu.cpu[i].guest) *100/alltics; (vertvals+i+1)->category[3].ccol = COLORCPUSTEAL; (vertvals+i+1)->category[3].clab = 's'; (vertvals+i+1)->category[3].cval = sstat->cpu.cpu[i].steal *100/alltics; (vertvals+i+1)->category[4].ccol = COLORCPUGUEST; (vertvals+i+1)->category[4].clab = 'G'; (vertvals+i+1)->category[4].cval = sstat->cpu.cpu[i].guest *100/alltics; (vertvals+i+1)->numcat = 5; } else { (vertvals+i+1)->numcat = 0; } } } // calculate PSI percentages, if supported // if (sstat->psi.present) { psisomeperc = sstat->psi.cpusome.total / ((count_t)nsecs*10000); if (psisomeperc > 100) psisomeperc = 100; psifullperc = -1; } else { psisomeperc = -1; psifullperc = -1; } // draw bar graph showing busy percentages of CPUs // drawvertbars(&wincpu, 100.0, cpubadness, numlabs, numcpus == 1 ? 0 : 1, vertvals, labellen, "Busy%", "Processors", 0, psisomeperc, psifullperc); } ///////////////////////////////////////////////////// // prepare the disk specific bar graph ///////////////////////////////////////////////////// static void do_dskbars(struct sstat *sstat, int nsecs, char initlabels, char mono) { static int labellen, numdisks; static char *labarea, *p; static struct vertval *vertvals; count_t mstot; int i, namlen, psisomeperc, psifullperc; // check if the number of disks has been changed since // previous sample and create X axis labels for all disks // if (numdisks != sstat->dsk.ndsk || initlabels) { // remove old label space // if (vertvals) { free(vertvals); free(labarea); } // create new label space // numdisks = sstat->dsk.ndsk; vertvals = malloc(numdisks * sizeof(struct vertval)); ptrverify(vertvals, "Malloc failed for %d vertval structs\n", numdisks); labarea = malloc(numdisks * (MAXLABLEN+1)); ptrverify(labarea, "Malloc failed for %d disk labels\n", numdisks); // create new X axis labels // for (i=0, labellen=0, p=labarea; i < numdisks; i++) { (vertvals+i)->barlab = p; namlen = strlen(sstat->dsk.dsk[i].name); if (labellen < namlen) labellen = namlen; if (namlen > MAXLABLEN) snprintf(p, MAXLABLEN+1, "%-*s", MAXLABLEN, sstat->dsk.dsk[i].name+namlen-MAXLABLEN); else snprintf(p, MAXLABLEN+1, "%-*s", MAXLABLEN, sstat->dsk.dsk[i].name); p += MAXLABLEN+1; } if (labellen > MAXLABLEN) labellen = MAXLABLEN; } // calculate total number of milliseconds in the interval // mstot = (sstat->cpu.all.stime + sstat->cpu.all.utime + sstat->cpu.all.ntime + sstat->cpu.all.itime + sstat->cpu.all.wtime + sstat->cpu.all.Itime + sstat->cpu.all.Stime + sstat->cpu.all.steal ) * (count_t)1000 / hertz / sstat->cpu.nrcpu; if (!mstot) // avoid division by zero mstot = 1; // per disk: fill total busy percentage and fill two // sidebars for the ratio between reads and writes // for (i=0; i < numdisks; i++) { count_t totsect = sstat->dsk.dsk[i].nrsect + sstat->dsk.dsk[i].nwsect; int perc = sstat->dsk.dsk[i].io_ms *100/mstot; (vertvals+i)->barval = perc; // total disk busy% (vertvals+i)->basecolor = WHITE_GREEN0; if (!mono) { if (!totsect) totsect = 1; // avoid division by zero (vertvals+i)->category[0].cval = (sstat->dsk.dsk[i].nrsect * perc + totsect/2) / totsect; (vertvals+i)->category[0].ccol = COLORDSKREAD; (vertvals+i)->category[0].clab = 'R'; (vertvals+i)->category[1].cval = perc - (vertvals+i)->category[0].cval; (vertvals+i)->category[1].ccol = COLORDSKWRITE; (vertvals+i)->category[1].clab = 'W'; (vertvals+i)->numcat = 2; } else { (vertvals+i)->numcat = 0; } } // calculate PSI percentages, if supported // if (sstat->psi.present) { psisomeperc = sstat->psi.iosome.total / ((count_t)nsecs*10000); if (psisomeperc > 100) psisomeperc = 100; psifullperc = sstat->psi.iofull.total / ((count_t)nsecs*10000); if (psifullperc > 100) psifullperc = 100; } else { psisomeperc = -1; psifullperc = -1; } // draw bar graph showing busy percentages of disks // drawvertbars(&windsk, 100.0, dskbadness, numdisks, 0, vertvals, labellen, "Busy%", "Disks", 3, psisomeperc, psifullperc); } ///////////////////////////////////////////////////// // return the ratio between network interfaces // and disks, and determine the window model ///////////////////////////////////////////////////// static float getwinratio(struct sstat *sstat, char *winmodel) { int disklabellen, intflabellen, i, namlen, nrintf, nrdisk; int dskcols, intcols, memcols; float dsk2netratio; // determine disk label length // for (i=0, disklabellen=0, nrdisk=sstat->dsk.ndsk; i < nrdisk; i++) { namlen = strlen(sstat->dsk.dsk[i].name); if (disklabellen < namlen) disklabellen = namlen; } if (disklabellen > MAXLABLEN) disklabellen = MAXLABLEN; // determine interface label length // for (i=0, intflabellen=0, nrintf=0; i < sstat->intf.nrintf; i++) { if (sstat->intf.intf[i].type != 'v') { nrintf++; namlen = strlen(sstat->intf.intf[i].name); if (intflabellen < namlen) intflabellen = namlen; } } if (intflabellen > MAXLABLEN) intflabellen = MAXLABLEN; // determine the number of columns needed // for all disks and for all interfaces // dskcols = 7 + nrdisk * (disklabellen+1); intcols = 1 + nrintf * (intflabellen+5); // when pressure values available, reserve space for disk PSI // if (sstat->psi.present) dskcols += 4; // determine the ratio between the size of the // disk window and interface window // dsk2netratio = 1.0 * dskcols / intcols; // determine window model: // 'm' - memory model (preferred) // 'i' - I/O model when lots of disks and/or interfaces are present // memcols = 1 + MEMORYBARSZ + 2 + EVENTBARSZ + 1 + 1 + (sstat->mem.totswap ? SWAPBARSZ+1 : 0); if (dskcols + intcols + 1 + memcols > COLS) *winmodel = 'i'; else *winmodel = 'm'; return dsk2netratio; } ///////////////////////////////////////////////////// // maintain hash list to register the current scale // per network interface ///////////////////////////////////////////////////// #define IFRESERVED 5 #define ISNUM 16 // factor of 2! struct ifscale { struct ifscale *next; char *interface; int curscale; }; static struct ifscale *ishash[ISNUM]; // search the ifscale struct for a specific interface // and return pointer // static struct ifscale * getifscale(char *interface) { int hash = 0; struct ifscale *isp; char *p = interface; for (; *p; p++) hash += *p; for (isp=ishash[hash&(ISNUM-1)]; isp; isp=isp->next) { if ( strcmp(interface, isp->interface) == 0 ) return isp; } return NULL; // not found } // create new ifscale struct for an interface, add it to // a hash list and return pointer // static struct ifscale * addifscale(char *interface, int scale) { int len, hash = 0; struct ifscale *isp; char *p = interface; for (len=0; *p; p++, len++) hash += *p; isp = malloc(sizeof(struct ifscale)); ptrverify(isp, "Malloc failed for ifscale struct\n"); isp->interface = malloc(len+1); ptrverify(isp->interface, "Malloc failed for ifscale name\n"); strcpy(isp->interface, interface); isp->next = ishash[hash&(ISNUM-1)]; isp->curscale = scale > winnet.nlines - IFRESERVED ? scale : winnet.nlines - IFRESERVED; ishash[hash&(ISNUM-1)] = isp; return isp; } // lower scale of all interfaces to the new scale (e.g. in case that // the number of lines in the window has changed), but only if the // old scale of an interface was zero or the given old scale (which // is the initial scale related to the number of lines in the current // window) // static void lowerifscales(int oldlines, int newlines) { int i; struct ifscale *isp; for (i=0; i < ISNUM; i++) // all hash buckets { for (isp=ishash[i]; isp; isp=isp->next) { if (oldlines == 0 || isp->curscale == oldlines-IFRESERVED) isp->curscale = newlines - IFRESERVED; } } } ///////////////////////////////////////////////////// // prepare the interface specific bar graph ///////////////////////////////////////////////////// static void do_netbars(struct sstat *sstat, int nsecs, char initlabels, char lower) { static long totints, numints; static char *labarea, *p; static int labellen; static struct netval *netvals; struct ifscale *isp; count_t ival, oval; int i, j, namlen; // check if the number of interfaces has been changed since the // previous sample and create X axis labels for all interfaces // if (totints != sstat->intf.nrintf || initlabels) { totints = sstat->intf.nrintf; // calculate how many physical interfaces (ethernet and wlan) // for (i=0, numints=0; i < totints; i++) { if (sstat->intf.intf[i].type != 'v') numints++; } // remove old label space // if (netvals) { free(netvals); free(labarea); } // create new label space // netvals = malloc(numints * sizeof(struct netval)); ptrverify(netvals, "Malloc failed for %ld netvals structs\n", numints); labarea = malloc(numints * (MAXLABLEN+1)); ptrverify(labarea, "Malloc failed for %ld interface labels\n", numints); // create new X axis labels // for (i=j=0, labellen=0, p=labarea; i < totints; i++) { if (sstat->intf.intf[i].type != 'v') { (netvals+j)->barlab = p; namlen = strlen(sstat->intf.intf[i].name); if (labellen < namlen) labellen = namlen; if (namlen > MAXLABLEN) snprintf(p, MAXLABLEN+1, "%-.*s", MAXLABLEN, sstat->intf.intf[i].name); else snprintf(p, MAXLABLEN+1, "%s", sstat->intf.intf[i].name); p += MAXLABLEN+1; j++; } } if (labellen > MAXLABLEN) labellen = MAXLABLEN; } // lower of all scales required by the user? // if (lower) lowerifscales(0, winnet.nlines); // fill traffic values per physical interface // for (i=j=0; i < totints; i++) { if (sstat->intf.intf[i].type != 'v') { int maxmbits = 0; ival = sstat->intf.intf[i].rbyte/125/1000/nsecs; oval = sstat->intf.intf[i].sbyte/125/1000/nsecs; (netvals+j)->pvalr = ival; (netvals+j)->pvals = oval; (netvals+j)->speed = sstat->intf.intf[i].speed; // determine minimum scale for bar graph // maxmbits = ival > oval ? ival : oval; // search for worst-case vertical scale so far and // check if the new scale is larger --> exchange // // first verify if scale is known // if ( (isp = getifscale( (netvals+j)->barlab)) ) { if (isp->curscale < maxmbits) isp->curscale = maxmbits; } else // first time: scale not known { isp = addifscale( (netvals+j)->barlab, maxmbits); } (netvals+j)->maxmbits = isp->curscale; j++; } } drawnetbars(&winnet, numints, netvals, labellen, "Mbits/s", "Interfaces"); } ///////////////////////////////////////////////////// // draw vertical bars in a graph window // mainly for CPUs and disks ///////////////////////////////////////////////////// #define BARCHAR ' ' // arguments: // - w pointer to struct perwindow // - barscale maximum value for Y axis // - hthreshold high threshold, i.e. critical value // - numbars number of bars to draw // - avgbar boolean: first value is for average bar? // - vvp list of vertval structs with the bar values (numbars elements) // - barlabsize length of label for each bar // - ytitle title for Y axis // - xtitle title for X axis // - barwidth number of columns for one bar // 0 = automatic (as many columns as possible, max. 3) // 1 = single column bars with empty bar in between // 2 = double column bars with empty bar in between // 3 = triple column bars with empty bar in between // - psisomeperc percentage of PSI some (-1 if not supported) // - psifullperc percentage of PSI full (-1 if not supported) // // returns: number of bars drawn // static int drawvertbars( struct perwindow *w, float barscale, float hthreshold, int numbars, int avgbar, struct vertval *vvp, int barlabsize, char *ytitle, char *xtitle, int barwidth, int psisomeperc, int psifullperc) { char buf[16], *ychar, horizontalxlab=0, barch; int i, j, curline, curcol, barlines, realbars, realcols, availcols, needcols; int autoscale, color, labwidth = 0, spacing = 1; float valperunit, level; int ytitlelen = strlen(ytitle), xtitlelen = strlen(xtitle); int ytitleline, xtitlespace; int scalelen = snprintf(buf, sizeof buf, "%d", (int)barscale), xindent, psicols = 0; void *vvporig = vvp; struct vertval *vp; // calculate indentation in front of first bar column // considering line layout: // - position 1: character of Y title // - position 2: space // - position 3: space // - position 4..n: number of positions taken for Y label (scalelen) // - position n+1: Y axis vertical line // xindent = 3 + scalelen + 1; if (psisomeperc+psifullperc > -2) psicols = 5; availcols = w->ncols - xindent - psicols; // number of columns for bars // verify if there is enough horizontal space in the // window to show all per-bar X labels horizontally in one line // instead of vertically in several lines // if (numbars*barlabsize+numbars-1 < availcols) horizontalxlab = 1; // calculate effective number of lines for bar graph // reserving 3 lines: // - one line for x axis line // - one line empty below per-bar labels // - one line for x axis title // barlines = w->nlines - 3 - (horizontalxlab ? 1 : barlabsize); if (barlines <= 0) return 0; if (barlines > MAXHEIGHT) barlines = MAXHEIGHT; // calculate value represented by each bar graph line // valperunit = barscale / barlines; // determine the width of each bar // // variables involved: // - realbars: how many bars can be drawn (realbars <= numbars) // - realcols: how many screen columns will be used // autoscale = barwidth ? 0 : 1; if (autoscale) // define preferred bar width barwidth = 3; while (1) { switch (barwidth) { // single bars and space between each bar // case 1: needcols = numbars * 2 - 1; realbars = needcols < availcols ? numbars : (availcols+1)/2; realcols = realbars * 2 - 1; break; // double bars and space between each bar // case 2: needcols = numbars * 3 - 1; realbars = needcols <= availcols ? numbars : (availcols+1)/3; realcols = realbars * 3 - 1; break; // triple bars and space between each bar // default: needcols = numbars * 4 - 1; realbars = needcols <= availcols ? numbars : (availcols+1)/4; realcols = realbars * 4 - 1; } if (!autoscale || realbars == numbars || --barwidth < 1) break; } if (barwidth < 1) barwidth = 1; // when all bars do not fit in the window width, // sort the values in decreasing order to show // the most relevant ones // if (availcols < needcols) sortvertbars(numbars, avgbar, &vvp); // calculate horizontal position of X title // (centered below bar columns) // if (horizontalxlab) { if (barlabsize < barwidth) { labwidth = barwidth; } else { labwidth = barlabsize; spacing = barlabsize + 1 - barwidth; realcols = realbars * barlabsize + realbars - 1; } } // calculate vertical position of Y title // (centered left from bar lines) // if (ytitlelen > barlines) ytitleline = 0; else ytitleline = (barlines - ytitlelen + 1) / 2; // create colormap and character map for each bar // fillbarmaps(numbars, vvp, valperunit, barlines); // wipe window contents // werase(w->win); // draw bar graph line-by-line // for (curline=0; curline < barlines; curline++) { int filler; // calculate value represented by this level and // draw the high-threshold line if needed // level = (barlines - curline) * valperunit; if (level + valperunit/2 > hthreshold && level - valperunit/2 <= hthreshold ) filler = ACS_HLINE; else filler = ' '; // select the character that has to be printed from // the vertical title // if (curline >= ytitleline && (curline-ytitleline) < ytitlelen) ychar = ytitle+curline-ytitleline; else ychar = " "; // print vertical Y title character and Y axis value // mvwprintw(w->win, curline, 0, "%c %*d", *ychar, scalelen, (int)level); waddch(w->win, ACS_VLINE); // for each bar // for (curcol=0, vp=vvp; curcol < realbars; curcol++, vp++) { color = vp->barmap[barlines-curline-1]; barch = vp->barchr[barlines-curline-1]; // print colored character(s) // if (color) { colorswon(w->win, color); switch (barwidth) { case 1: waddch(w->win, barch); break; case 2: waddch(w->win, barch); waddch(w->win, BARCHAR); break; case 3: waddch(w->win, BARCHAR); waddch(w->win, barch); waddch(w->win, BARCHAR); } colorswoff(w->win, color); } else { // print fillers // if (filler != ' ') colorswon(w->win, FGCOLORCRIT); for (i=0; i < barwidth; i++) waddch(w->win, filler); if (filler != ' ') colorswoff(w->win, FGCOLORCRIT); } // add fillers between the bars // if (filler != ' ') colorswon(w->win, FGCOLORCRIT); for (i=0; i < (curcol < realbars-1 ? spacing : spacing-1); i++) waddch(w->win, filler); if (filler != ' ') colorswoff(w->win, FGCOLORCRIT); } } // print line for X axis // if (curline >= ytitleline && (curline-ytitleline) < ytitlelen) ychar = ytitle+curline-ytitleline; else ychar = " "; mvwprintw(w->win, curline++, 0, "%c %*d", *ychar, scalelen, 0); waddch(w->win, ACS_LLCORNER); for (i=0; i < realcols; i++) waddch(w->win, ACS_HLINE); if (horizontalxlab) { // print X axis values horizontally (one line) // wmove(w->win, curline++, xindent); if (barlabsize == 1 && barwidth == 3) waddch(w->win, ' '); // alignment for (i=0, vp=vvp; i < realbars; i++) wprintw(w->win, "%-*.*s ", labwidth, barlabsize, (vp+i)->barlab); } else { // print X axis values vertically // for (i=0; i < barlabsize; i++) { wmove(w->win, curline++, xindent); for (curcol=0, vp=vvp; curcol < realbars; curcol++,vp++) { waddch(w->win, *((vp->barlab)+i)); for (j=0; j < barwidth-1; j++) waddch(w->win, BARCHAR); if (spacing) waddch(w->win, ' '); } } } // print X title centered under bar graph // curline++; // empty line if (xtitlelen > realcols) xtitlespace = xindent + xtitlelen; else xtitlespace = xindent + (xtitlelen + realcols)/2; mvwprintw(w->win, curline, 0, "%*s", xtitlespace, xtitle); if (vvporig != vvp) // reallocated by sortvertbars()? free(vvp); // draw PSI bars if supported // if (psisomeperc+psifullperc > -2) { int startcol = w->ncols - psicols + 1; drawpsibars(w->win, barlines, 0, startcol, valperunit, 1, 0, psisomeperc, psifullperc); } // flush window content // wrefresh(w->win); return realbars; } ///////////////////////////////////////////////////// // fill the color map and character map of each bar // // the color map describes a color code per bar line // and the character map describes a character per // bar line ///////////////////////////////////////////////////// static void fillbarmaps(int numbars, struct vertval *vvp, float valperunit, int barlines) { int i, c, n, m; struct vertval *vp; char *mp, *cp; // for each bar, fill the color map and character map // for (i=0, vp=vvp; i < numbars; i++, vp++) { // initialize both maps // memset(vp->barmap, '\0', MAXHEIGHT); memset(vp->barchr, BARCHAR, MAXHEIGHT); // when no separate categories defined, // fill the entire bar with the base color // if (vp->numcat == 0) { m = (vp->barval + valperunit/2) / valperunit; memset(vp->barmap, vp->basecolor, m); continue; } // for each bar category, fill the corresponding color // in the color map and on the lowest position in the // character map fill the category character // for (c=0, mp=vp->barmap, cp=vp->barchr; c < vp->numcat; c++) { // determine the number of color/character positions // for this category // n = (vp->category[c].cval + valperunit/2) / valperunit; if (n > 0 && mp - vp->barmap + n <= MAXHEIGHT) { // fill all color positions (lines) // memset(mp, vp->category[c].ccol, n); mp += n; // fill one character position // *cp = vp->category[c].clab; cp += n; } } // verify that all positions of the total bar value are // filled; if not (due to rounding issues), add a filler // // rounding issues: // suppose that the total percentage is 49, subdivided // into percentage 22 for category 'read' and 27 for // category 'write' // when every bar line represents 5% the total number // of bar lines should be 10 (49 rounded to 50) // for the category 'read' 4 bar lines will be used // (22 rounded to 20) and for the category 'write' // 5 bar lines (27 rounded to 25) // then 1 line should be filled with a neutral color // n = mp - vp->barmap; // number of units filled m = (vp->barval + valperunit/2) / valperunit; // total units if (n < m && m < MAXHEIGHT) memset(mp, vp->basecolor, m-n); } } ///////////////////////////////////////////////////// // draw two bars for the 'some' and 'full' PSI // percentages ///////////////////////////////////////////////////// static void drawpsibars(WINDOW *win, int barlines, int startline, int startcol, int valperunit, int barwidth, int boxed, int psisomeperc, int psifullperc) { int i, curline, visiblepressure; int somefromline = barlines - (psisomeperc + (valperunit/2)) / valperunit; int fullfromline = barlines - (psifullperc + (valperunit/2)) / valperunit; // determine if there is any visible pressure // if (psisomeperc == -1) somefromline = barlines; if (psifullperc == -1) fullfromline = barlines; if (somefromline + fullfromline == 2 * barlines) visiblepressure = 0; else visiblepressure = 1; /////////////////////// // draw graph framework /////////////////////// // // draw Y axe // (gray if no pressure now) // if (!visiblepressure) colorswon(win, FGCOLORGREY); for (curline=0; curline < barlines; curline++) { mvwaddch(win, curline+startline, startcol, ACS_VLINE); if (boxed) mvwaddch(win, curline+startline, startcol+2*barwidth+1, ACS_VLINE); } // draw X axe // mvwaddch(win, curline+startline, startcol, ACS_LLCORNER); for (i=0; i < barwidth*2; i++) waddch(win, ACS_HLINE); if (boxed) mvwaddch(win, curline+startline, startcol+2*barwidth+1, ACS_LRCORNER); curline++; // print X label // mvwprintw(win, curline+startline, startcol+barwidth/2+boxed, "PSI"); if (!visiblepressure) colorswoff(win, FGCOLORGREY); /////////////////////// // draw the PSI values /////////////////////// for (curline=0; curline < barlines; curline++) { // address the cursor on the start position // wmove(win, curline+startline, startcol+1); // draw the 'some' PSI value // if (curline >= somefromline) { colorswon(win, COLORWARN); for (i=0; i < barwidth; i++) waddch(win, curline == barlines-1 ? "Some"[i] : ' '); colorswoff(win, COLORWARN); } else { for (i=0; i < barwidth; i++) waddch(win, ' '); } // draw the 'full' PSI value // if (curline >= fullfromline) { colorswon(win, COLORBAD); for (i=0; i < barwidth; i++) waddch(win, curline == barlines-1 ? "Full"[i] : ' '); colorswoff(win, COLORBAD); } else { for (i=0; i < barwidth; i++) waddch(win, ' '); } } } ///////////////////////////////////////////////////// // draw vertical bars for network interfaces ///////////////////////////////////////////////////// // arguments: // - w pointer to struct perwindow // - numbars number of bars to draw, i.e. number of interfaces // - nvp pointer to struct containing traffic info per interface // - barlabsize length of label per bar // - ytitle title for Y axis // - xtitle title for X axis // // returns: number of bars drawn // static int drawnetbars(struct perwindow *w, int numbars, struct netval *nvp, int barlabsize, char *ytitle, char *xtitle) { char *ychar; int i, ifbar, curline, barlines, realbars, color, perifcols; float *valperline, level; int ytitlelen = strlen(ytitle), xtitlelen=strlen(xtitle), ytitleline; void *nvporig = nvp; // define number of columns needed per interface // considering layout: // - position 1: space // - position 2..7: six positions for Y value // - position 8: Y axis vertical line // - position 9..12: double column for transmit + receive // perifcols = 12; if ((w->ncols-1)/perifcols >= numbars) realbars = numbars; // real number of interfaces else realbars = (w->ncols-1) / perifcols; // calculate effective number of lines for bar graph // reserving 3 lines: // - one line for x axis line // - one line for speed // - one line for x axis label // - one line blank under labels // - one line containing x title // barlines = w->nlines - IFRESERVED; if (barlines <= 0) return 0; // when all network interfaces do not fit in the window width, // sort the values in decreasing order to show // the most relevant ones // if (numbars > realbars) sortnetbars(numbars, &nvp); // calculate value represented by each bar graph line // (different for each interface) // valperline = malloc(numbars * sizeof(float)); ptrverify(valperline, "Malloc failed for %d values per line\n", numbars); for (i=0; i < numbars; i++) *(valperline+i) = (float)(nvp+i)->maxmbits / barlines; // calculate vertical position of y title // (centered left from bar lines) // if (ytitlelen > barlines) ytitleline = 0; else ytitleline = (barlines - ytitlelen + 1) / 2; // wipe window contents // werase(w->win); // draw bar graphs line-by-line // for (curline=0; curline < barlines; curline++) { // select the character that has to be printed from // the vertical Y title // if (curline >= ytitleline && (curline-ytitleline) < ytitlelen) ychar = ytitle+curline-ytitleline; else ychar = " "; // print vertical y title character // mvwaddch(w->win, curline, 0, *ychar); // print four columns per interface // for (ifbar=0; ifbar < realbars; ifbar++) { // calculate value represented by this level // level = (barlines - curline) * *(valperline+ifbar); wprintw(w->win, " %6d", (int)level); waddch(w->win, ACS_VLINE); // rounded receive bar reaches this level? // if ((nvp+ifbar)->pvalr + *(valperline+ifbar) / 2 >= level) { // print colored bar // color = COLORNETRECV; colorswon(w->win, color); if (curline == barlines-1) { wprintw(w->win, "RX"); } else { waddch(w->win, BARCHAR); waddch(w->win, BARCHAR); } colorswoff(w->win, color); } else { // print spaces // wprintw(w->win, " "); } // rounded send second bar reaches this level? // if ((nvp+ifbar)->pvals + *(valperline+ifbar) / 2 >= level) { // print colored bar // color = COLORNETSEND; colorswon(w->win, color); if (curline == barlines-1) { wprintw(w->win, "TX"); } else { waddch(w->win, BARCHAR); waddch(w->win, BARCHAR); } colorswoff(w->win, color); } else { // print space // wprintw(w->win, " "); } } } // print lines for X axis // if (curline >= ytitleline && (curline-ytitleline) < ytitlelen) ychar = ytitle+curline-ytitleline; else ychar = " "; mvwaddch(w->win, curline++, 0, *ychar); for (ifbar=0; ifbar < realbars; ifbar++) { wprintw(w->win, " %5d", 0); waddch(w->win, ACS_LLCORNER); for (i=0; i < perifcols-8; i++) waddch(w->win, ACS_HLINE); } // print speed per interface // wmove(w->win, curline++, 1); for (ifbar=0; ifbar < realbars; ifbar++) { if ((nvp+ifbar)->speed) wprintw(w->win, "%*dM", perifcols-1, (nvp+ifbar)->speed); else wprintw(w->win, "%*sM", perifcols-1, "?"); } // print horizontal label per interface bar // wmove(w->win, curline++, 1); for (ifbar=0; ifbar < realbars; ifbar++) wprintw(w->win, "%*.*s", perifcols, barlabsize, (nvp+ifbar)->barlab); // print X title centered under bar graph // curline++; // empty line mvwprintw(w->win, curline++, 0, "%*s", 3+(realbars*perifcols)/2+(xtitlelen/2), xtitle); wrefresh(w->win); // free allocated memory // free(valperline); if (nvp != nvporig) free(nvp); // allocated by sortnetbars() return realbars; } ///////////////////////////////////////////////////// // draw specific window with memory management info ///////////////////////////////////////////////////// static int drawmemory(struct perwindow *w, struct sstat *sstat, int nsecs, time_t curtime, char flag) { static time_t lastoomkills; long long totalmem, cachemem, shmemrss, tmpfsmem, slabmem, freemem, hugefree, hugeused, shmrssreal; long long totalswp, shmemswp, freeswp; char scanseverity, swapseverity, killseverity; int curline=0, barlines, eventlines, psilines, color; int usedlines, freelines, cachelines, tmpfslines, slablines, shmemlines, hugelines; int memorycol = 1, psisomeperc, psifullperc, swapcol = memorycol + MEMORYBARSZ + 1, eventcol = swapcol + SWAPBARSZ + 2; float valperunit; char formatbuf[16]; // calculate all memory values, keeping in mind: // // - shmem resident System V shared memory // including resident tmpfs (POSIX shamem) // excluding static huge pages // // - shmrss resident System V shared memory // including static huge pages // // - cachemem page cache including shmem // totalmem = sstat->mem.physmem * pagesize; cachemem = (sstat->mem.cachemem + sstat->mem.buffermem - sstat->mem.shmem) * pagesize; shmemrss = sstat->mem.shmrss * pagesize; slabmem = sstat->mem.slabmem * pagesize; freemem = sstat->mem.freemem * pagesize; hugefree = sstat->mem.sfreehugepage * sstat->mem.shugepagesz + sstat->mem.lfreehugepage * sstat->mem.lhugepagesz; totalswp = sstat->mem.totswap * pagesize; shmemswp = sstat->mem.shmswp * pagesize; freeswp = sstat->mem.freeswap* pagesize; // assumption: most of static huge pages use for SYSV shared memory, // although static hige pages can also be used for mmap() // hugeused = (sstat->mem.stothugepage - sstat->mem.sfreehugepage) * sstat->mem.shugepagesz + (sstat->mem.ltothugepage - sstat->mem.lfreehugepage) * sstat->mem.lhugepagesz; shmrssreal = (sstat->mem.shmrss * pagesize) - hugeused; // in bytes! if (shmrssreal < 0) // (partly) wrong assumption about static huge pages shmrssreal = 0; tmpfsmem = (sstat->mem.shmem - sstat->mem.shmswp) * pagesize - shmrssreal / pagesize; // determine severity for pagescans, swapouts and oomkills // 'n' - normal, // 'w' - warning, // 'c' - critical // // for oomkills specifically: // show warning level for 15 minutes after the last oomkill // occurred (current time can be smaller than last time in // case of 'T' key when viewing raw logs) // scanseverity = setseverity(sstat->mem.pgscans/nsecs, 100000, 1000); swapseverity = setseverity(sstat->mem.swouts/nsecs, 500, 100); if (sstat->mem.oomkills <= 0) // no new oomkills? { if (curtime > lastoomkills && curtime - lastoomkills < 900) killseverity = 'w'; else killseverity = 'n'; } else // new oomkills during last interval { killseverity = 'c'; if (flag&RRBOOT) lastoomkills = curtime - nsecs; else lastoomkills = curtime; } // calculate effective number of lines for bar graph // barlines = w->nlines - 2; if (barlines <= 5) return 0; // calculate value represented by each bar graph line // valperunit = totalmem / barlines; // calculate number of lines for free, shared memory, // tmpfs, slab and page cache // freelines = (freemem + valperunit/2) / valperunit; shmemlines = (shmemrss + valperunit/2) / valperunit; tmpfslines = (tmpfsmem + valperunit/2) / valperunit; slablines = (slabmem + valperunit/2) / valperunit; cachelines = (cachemem + valperunit/2) / valperunit; hugelines = (hugefree + valperunit/2) / valperunit; usedlines = barlines - freelines - shmemlines - hugelines - tmpfslines - slablines - cachelines; // wipe window contents // werase(w->win); // draw lines for free memory // curline += drawmemlines(w, curline, memorycol, freelines, MEMORYBARSZ, COLORMEMFREE, "free", NULL); // draw lines for cache memory // curline += drawmemlines(w, curline, memorycol, cachelines, MEMORYBARSZ, COLORMEMCACH, "pagecache", NULL); // draw lines for free static huge pages memory // (occupied static huge pages are already part of processes // or shared memory) // curline += drawmemlines(w, curline, memorycol, hugelines, MEMORYBARSZ, COLORMEMHUGE, "free huge", "pages"); // draw lines for tmpfs memory // curline += drawmemlines(w, curline, memorycol, tmpfslines, MEMORYBARSZ, COLORMEMTMP, "tmpfs", NULL); // draw lines for shared memory // curline += drawmemlines(w, curline, memorycol, shmemlines, MEMORYBARSZ, COLORMEMSHM, "sharedmem", NULL); // draw lines for slab memory // curline += drawmemlines(w, curline, memorycol, slablines, MEMORYBARSZ, COLORMEMSLAB, "slab", "caches"); // draw lines for other used memory // if ((totalmem-cachemem-freemem)*100 / totalmem >= membadness) color = COLORBAD; else color = COLORMEMUSED; curline += drawmemlines(w, curline, memorycol, usedlines, MEMORYBARSZ, color, "processes", "&kernel"); // show memory size // mvwprintw(w->win, curline++, memorycol, "%*s", MEMORYBARSZ-(MEMORYBARSZ-7+1)/2, val2memstr(totalmem, formatbuf, MBFORMAT, 0, 0)); mvwprintw(w->win, curline, memorycol, "%*s", MEMORYBARSZ-(MEMORYBARSZ-6)/2, "Memory"); // show swap space (if used) // if (totalswp) { // calculate value represented by each bar graph line // valperunit = totalswp / barlines; // calculate number of lines for free swap // freelines = (freeswp + (valperunit/2)) / valperunit; shmemlines = (shmemswp + (valperunit/2)) / valperunit; usedlines = barlines - shmemlines - freelines; // draw lines for free swap // curline = 0; curline += drawmemlines(w, curline, swapcol, freelines, SWAPBARSZ, COLORMEMFREE, "free", NULL); // draw lines for swapped shared memory swap // curline += drawmemlines(w, curline, swapcol, shmemlines, SWAPBARSZ, COLORMEMSHM, "shamem", NULL); // draw lines for occupied swap // highly occupied swap is only an issue when also memory // is highly occupied // if ((totalswp-freeswp) * 100 / totalswp >= swpbadness && (totalmem-cachemem-freemem) * 100 / totalmem >= membadness) color = COLORBAD; else color = COLORMEMUSED; curline += drawmemlines(w, curline, swapcol, usedlines, SWAPBARSZ, color, "procs", "&tmpfs"); // show swap size // mvwprintw(w->win, curline++, swapcol, "%*s", SWAPBARSZ-(SWAPBARSZ-7+1)/2, val2memstr(totalswp, formatbuf, MBFORMAT, 0, 0)); mvwprintw(w->win, curline, swapcol, "%*s", SWAPBARSZ-(SWAPBARSZ-4-1)/2, "Swap"); } else { eventcol = swapcol+1; } // calculate PSI percentages, if supported // if (sstat->psi.present) { psisomeperc = sstat->psi.memsome.total / ((count_t)nsecs*10000); if (psisomeperc > 100) psisomeperc = 100; psifullperc = sstat->psi.memfull.total / ((count_t)nsecs*10000); if (psifullperc > 100) psifullperc = 100; // calculate how many free lines are available on top of // the event counters to be used for drawing PSI bar graph, // supposing that all event counters (19 lines) can be shown // psilines = barlines - 19 - 1; if (psilines < 7) // not enough lines? psilines = 7; } else { psisomeperc = -1; psifullperc = -1; psilines = 0; } eventlines = barlines - psilines - 3; // show as many event counters as possible, starting with // the most relevant counters first (bottom-up) // also consider if PSI // mvwprintw(w->win, curline, eventcol, " Events "); if (eventlines > 1) // show oomkilling? curline = drawevent(w, curline, eventcol, severitycolor(killseverity), " oomkills ", " %8ld ", sstat->mem.oomkills); if (eventlines > 4) // show swapouts? curline = drawevent(w, curline, eventcol, severitycolor(swapseverity), " swapouts ", "%7ld/s ", sstat->mem.swouts/nsecs); if (eventlines > 7) // show pagescans? curline = drawevent(w, curline, eventcol, severitycolor(scanseverity), " pagscans ", "%7ld/s ", sstat->mem.pgscans / nsecs); if (eventlines > 10) // show swapins? curline = drawevent(w, curline, eventcol, COLORMEMBAR, " swapins ", "%7ld/s ", sstat->mem.swins / nsecs); if (eventlines > 13) // show pageouts? curline = drawevent(w, curline, eventcol, COLORMEMBAR, " pagouts ", "%7ld/s ", sstat->mem.pgouts / nsecs); if (eventlines > 16) // show pageins? curline = drawevent(w, curline, eventcol, COLORMEMBAR, " pageins ", "%7ld/s ", sstat->mem.pgins / nsecs); // draw PSI bar graph // if (psilines) drawpsibars(w->win, psilines, 0, eventcol, 100/psilines, 4, 1, psisomeperc, psifullperc); wrefresh(w->win); return 1; } ///////////////////////////////////////////////////// // draw lines for specific memory category ///////////////////////////////////////////////////// static int drawmemlines(struct perwindow *w, int startline, int startcolumn, int numlines, int width, int color, char *cat1, char *cat2) { int line=startline, catline, targetline=startline+numlines, len; if (numlines == 0) return 0; if (usecolors) wattron(w->win, COLOR_PAIR(color)); wattron(w->win, A_BOLD); for (catline=startline+(numlines-1)/2; line < targetline; line++) { wmove(w->win, line, startcolumn); if (line == catline) { len = strlen(cat1); if (len > width) { // truncate // wprintw(w->win, "%.*s", width, cat1); } else { int cw = width - (width-len+1)/2; wprintw(w->win, "%*s%*s", cw, cat1, width-cw, " "); } } else { if (line == catline+1 && cat2) { len = strlen(cat2); if (len > width) { wprintw(w->win, "%.*s", width, cat2); } else { int cw = width - (width-len+1)/2; wprintw(w->win, "%*s%*s", cw, cat2, width-cw, " "); } } else { wprintw(w->win, "%*s", width, " "); } } } wattroff(w->win, A_BOLD); if (usecolors) wattroff(w->win, COLOR_PAIR(color)); return numlines; } ///////////////////////////////////////////////////// // draw lines for specific memory event ///////////////////////////////////////////////////// static int drawevent(struct perwindow *w, int line, int column, int color, char *text, char *format, long value) { colorswon(w->win, color); line -= 2; if (value >= 0) mvwprintw(w->win, line, column, format, value); else mvwprintw(w->win, line, column, " ? "); line -= 1; wattron(w->win, A_BOLD); mvwprintw(w->win, line, column, "%s", text); wattroff(w->win, A_BOLD); colorswoff(w->win, color); return line; } ///////////////////////////////////////////////////// // fill the header line (separate window) // and wait for keyboard input event ///////////////////////////////////////////////////// static int headergetch(time_t curtime, int nsecs, char *statusmsg, int statuscol) { int colsunused, fill1, fill2, fill3, statcol; int headlen = strlen(headmsg); char buf[64], timestr[16], datestr[16]; int seclen = val2elapstr(nsecs, buf); int lastchar; fd_set readfds; char eventbuf[1024]; int nrfds; convdate(curtime, datestr); /* date to ascii string */ convtime(curtime, timestr); /* time to ascii string */ // calculate subdivision of areas in header line // colsunused = COLS - 35 - seclen - utsnodenamelen - headlen; fill1 = colsunused / 6; fill2 = (colsunused - fill1) / 2; fill3 = colsunused - fill1 - fill2; // fill header line // werase(headwin); wattron(headwin, A_REVERSE); wprintw(headwin, "ATOP - %s %*s%s %s%*s%s%*s%s elapsed", utsname.nodename, fill1, " ", datestr, timestr, fill2, " ", headmsg, fill3, " ", buf); wattroff(headwin, A_REVERSE); // display specific status if needed // statcol = 27 + utsnodenamelen + fill1 + 2; if (statusmsg) { colorswon(headwin, statuscol); wattron(headwin, A_REVERSE); mvwprintw(headwin, 0, statcol, "%s", statusmsg); wattroff(headwin, A_REVERSE); colorswoff(headwin, statuscol); } // wait for keystroke // wmove(headwin, 1, 0); wrefresh(headwin); if (twinpid && !keywaiting) // twin mode? { struct sigaction sigact, sigold; /* ** catch window size changes while in select */ memset(&sigact, 0, sizeof sigact); sigact.sa_handler = getsigwinch; sigaction(SIGWINCH, &sigact, &sigold); winchange = 0; /* ** await input character from keyboard, or ** inotify trigger in case of twin mode, or ** interval timer expiration */ FD_ZERO(&readfds); FD_SET(0, &readfds); if (!paused && fdinotify != -1) // twin mode? { FD_SET(fdinotify, &readfds); nrfds = fdinotify + 1; } else { nrfds = 1; } switch (select(nrfds, &readfds, (fd_set *)0, (fd_set *)0, (struct timeval *)0)) { case -1: /* ** window change or timer expiration? */ if (winchange) { // window change: set new dimensions struct winsize w; ioctl(0, TIOCGWINSZ, &w); resizeterm(w.ws_row, w.ws_col); lastchar = KEY_RESIZE; } else { // time interrupt lastchar = 0; } break; default: /* ** inotify trigger that new sample has been written? ** pretend as if the 't' key has been pressed ** to read that sample ** ** otherwise: read keystroke from keyboard */ if (FD_ISSET(fdinotify, &readfds)) { read(fdinotify, eventbuf, sizeof eventbuf); lastchar = MSAMPNEXT; } else { lastchar = wgetch(headwin); keywaiting = 0; } } sigaction(SIGWINCH, &sigold, (struct sigaction *)0); } else // no twin mode: neutral state is getch() { lastchar = wgetch(headwin); } return lastchar; } ///////////////////////////////////////////////////// // create all windows ///////////////////////////////////////////////////// static int wininit(struct sstat *sstat) { int lpw, c4c, c4m, c4d, c4n, i, avail; float col, dsk2netratio; char winmodel; // determine the ratio between the size of the // disk window and interface window // // determine window model: // 'm' - memory model (preferred) // 'i' - I/O model when lots of disks and/or interfaces are present // dsk2netratio = getwinratio(sstat, &winmodel); // cleanup underlying standard screen // werase(stdscr); refresh(); // calculate number of lines for a half-screen bar graph window // lpw = (LINES-1) / 2 - 1; // lines per window // calculate number of columns for the windows in the upper half // // - memory window gets fixed horizontal size (columns), // either with or without swap bar // // - cpu window gets rest of the colums in upper half // c4m = MEMORYBARSZ + EVENTBARSZ + 4; c4m += sstat->mem.totswap ? SWAPBARSZ+1 : 0; c4c = COLS - c4m - 1; // cpu: rest of columns // calculate number of columns for windows in lower half // // - width of the disk and the interface windows will be // calculated according to the requested ratio // depending of the model, the full screen width can be used // (I/O model) or the remaining width without the memory columns // (memory model) // avail = COLS - (winmodel == 'm' ? c4m + 1: 0); // available columns col = avail / (dsk2netratio+1.0); c4d = col * dsk2netratio; // columns for disk c4n = avail - c4d - 1; // columns for interfaces // create window of two lines for the header line // (second line only meant to 'park' the cursor) // headwin = newwin(2, COLS, 0, 0); // create window of one line as horizonal ruler between the // upper and lower half, and draw horizontal ruler // avail = winmodel=='m' ? COLS - c4m : COLS; midline = newwin(1, avail, lpw+2, 0); colorswon(midline, FGCOLORBORDER); for (i=0; i < avail; i++) { if (i == c4c && c4c != c4d) { waddch(midline, ACS_BTEE); continue; } if (i == c4d) { if (c4c != c4d) waddch(midline, ACS_TTEE); else waddch(midline, ACS_PLUS); continue; } waddch(midline, ACS_HLINE); } colorswoff(midline, FGCOLORBORDER); wrefresh(midline); // create window of one column for vertical ruler // (half or full) and draw vertical ruler // avail = winmodel=='m' ? LINES : lpw+1; // available lines colupper = newwin(avail, 1, 1, c4c); colorswon(colupper, FGCOLORBORDER); for (i=0; i < avail; i++) if (i == lpw+1) waddch(colupper, ACS_RTEE); else waddch(colupper, ACS_VLINE); colorswoff(colupper, FGCOLORBORDER); wrefresh(colupper); // create window of one column for vertical ruler // in the lower half and draw vertical ruler // collower = newwin(LINES-lpw-3, 1, lpw+3, c4d); colorswon(collower, FGCOLORBORDER); for (i=0; i < lpw+1; i++) waddch(collower, ACS_VLINE); colorswoff(collower, FGCOLORBORDER); wrefresh(collower); // create four windows for the resource graphs // and fill dimensions // wincpu.nlines = lpw; wincpu.ncols = c4c; wincpu.win = newwin(wincpu.nlines, wincpu.ncols, 2, 0); winmem.nlines = winmodel == 'i' ? lpw : LINES-2; winmem.ncols = c4m; winmem.win = newwin(winmem.nlines, winmem.ncols, 2, c4c+1); lpw = LINES - 3 - lpw; // recalc for extra line in case of odd lines windsk.nlines = lpw; windsk.ncols = c4d; windsk.win = newwin(windsk.nlines, windsk.ncols, wincpu.nlines+3, 0); lowerifscales(winnet.nlines, lpw); // lower initial scales winnet.nlines = lpw; winnet.ncols = c4n; winnet.win = newwin(winnet.nlines, winnet.ncols, wincpu.nlines+3, c4d+1); wrefresh(wincpu.win); wrefresh(winmem.win); wrefresh(windsk.win); wrefresh(winnet.win); return 1; } ///////////////////////////////////////////////////// // delete all windows ///////////////////////////////////////////////////// static void winexit(void) { delwin(wincpu.win); delwin(winmem.win); delwin(windsk.win); delwin(winnet.win); delwin(colupper); delwin(collower); delwin(midline); delwin(headwin); } ///////////////////////////////////////////////////// // create a separate window to request the user // to enter a value // // arguments // - prompt pointer to prompt string // - answer pointer to buffer in which // the answer will be returned // - maxanswer maximum size of answer buffer // - numerical boolean: convert value to integer? // // return value: // if 'numerical' is true, integer value or // -1 when input was not numeric // // if 'numerical' is false, value 0 or // -1 when no input was given ///////////////////////////////////////////////////// static int getwininput(char *prompt, char *answer, int maxanswer, char numerical) { WINDOW *mywin; int inumval = -1, numcols = strlen(prompt) + maxanswer + 1; // create a boxed window of three lines // for the conversation // mywin = newwin(3, numcols, (LINES-3)/3, (COLS-numcols)/3); box(mywin, ACS_VLINE, ACS_HLINE); // show the prompt // mvwprintw(mywin, 1, 1, "%s", prompt); // prepare reading input // echo(); // switch echoing on answer[0] = 0; if (wgetnstr(mywin, answer, maxanswer-1) != ERR) { // conversion to integer required? // if (numerical) { if (answer[0]) // data entered? { if ( numeric(answer) ) { inumval = atoi(answer); } else { beep(); wmove(mywin, 1, 1); wclrtoeol(mywin); box(mywin, ACS_VLINE, ACS_HLINE); mvwprintw(mywin, 1, 1, "Not numeric!"); wrefresh(mywin); sleep(2); } } } else { inumval = 0; } } else { beep(); } noecho(); delwin(mywin); return inumval; } ///////////////////////////////////////////////////// // create a separate window with help text and // wait for any keyboard input ///////////////////////////////////////////////////// #define HELPLINES 27 #define HELPCOLS 70 static void showhelp(void) { WINDOW *helpwin; int line=1, inputkey; // create centered window for help text // // notice that this window is bigger than the required // minimum size of the terminal // helpwin = newwin(HELPLINES, HELPCOLS, (LINES-HELPLINES)/2, (COLS-HELPCOLS)/2); if (!helpwin) // window allocation failed? return; box(helpwin, ACS_VLINE, ACS_HLINE); // show help text // mvwprintw(helpwin, line++, 2, "Display mode:"); mvwprintw(helpwin, line++, 2, " '%c' - text mode: keep same process info", MBARGRAPH); mvwprintw(helpwin, line++, 2, " '%c' - text mode: cgroups and related processes", MCGROUPS); mvwprintw(helpwin, line++, 2, " '%c' - text mode: generic info", MPROCGEN); mvwprintw(helpwin, line++, 2, " '%c' - text mode: memory details", MPROCMEM); if (supportflags & IOSTAT) mvwprintw(helpwin, line++, 2, " '%c' - text mode: disk details", MPROCDSK); if (supportflags & NETATOP) mvwprintw(helpwin, line++, 2, " '%c' - text mode: network details", MPROCNET); if (supportflags & GPUSTAT) mvwprintw(helpwin, line++, 2, " '%c' - text mode: GPU details", MPROCGPU); mvwprintw(helpwin, line++, 2, " '%c' - text mode: scheduling and thread-group info", MPROCSCH); mvwprintw(helpwin, line++, 2, " '%c' - text mode: various info", MPROCVAR); mvwprintw(helpwin, line++, 2, " '%c' - text mode: full command line per process", MPROCARG); line++; // show context dependent help text for raw file viewing/twin mode // or live measurement // if (rawreadflag) { mvwprintw(helpwin, line++, 2, "Raw file viewing or twin mode:"); mvwprintw(helpwin, line++, 2, " '%c' - show next sample", MSAMPNEXT); mvwprintw(helpwin, line++, 2, " '%c' - show previous sample", MSAMPPREV); mvwprintw(helpwin, line++, 2, " '%c' - rewind to begin", MRESET); mvwprintw(helpwin, line++, 2, " '%c' - fast-forward to end", MEND); mvwprintw(helpwin, line++, 2, " '%c' - branch to certain time", MSAMPBRANCH); mvwprintw(helpwin, line++, 2, " '%c' - pause button to freeze or continue (twin mode)", MPAUSE); } else { mvwprintw(helpwin, line++, 2, "Control:"); mvwprintw(helpwin, line++, 2, " '%c' - change interval timer (0 = only manual trigger)", MINTERVAL); mvwprintw(helpwin, line++, 2, " '%c' - manual trigger to force next sample", MSAMPNEXT); mvwprintw(helpwin, line++, 2, " '%c' - reset counters to boot time values", MRESET); mvwprintw(helpwin, line++, 2, " '%c' - pause button to freeze current sample (toggle)", MPAUSE); } line++; mvwprintw(helpwin, line++, 2, "General:"); mvwprintw(helpwin, line++, 2, " '%c' - reset network scale (otherwise keeps highest level)", MBARLOWER); mvwprintw(helpwin, line++, 2, " '%c' - busy bars with/without categories (toggle)", MBARMONO); mvwprintw(helpwin, line++, 2, " '%c' - quit this program", MQUIT); line++; mvwprintw(helpwin, line++, 2, "Select one of these keys (except '%c') or ", MQUIT); mvwprintw(helpwin, line++, 2, "any other key to leave help..."); wrefresh(helpwin); // wait for any keystroke // inputkey = wgetch(helpwin); // push this keystroke back to be received by the main loop // if (inputkey != MQUIT) { ungetch(inputkey); keywaiting = 1; } // remove help window delwin(helpwin); } ///////////////////////////////////////////////////// // switch certain color on ///////////////////////////////////////////////////// static void colorswon(WINDOW *win, int color) { if (usecolors) wattron(win, COLOR_PAIR(color)); else wattron(win, A_REVERSE); } ///////////////////////////////////////////////////// // switch certain color off ///////////////////////////////////////////////////// static void colorswoff(WINDOW *win, int color) { if (usecolors) wattroff(win, COLOR_PAIR(color)); else wattroff(win, A_REVERSE); } ///////////////////////////////////////////////////// // return background color depending on severity ///////////////////////////////////////////////////// static int severitycolor(char severity) { int color; switch (severity) { case 'n': // normal color = COLOROKAY; break; case 'w': // warning color = COLORWARN; break; case 'c': // critical color = COLORBAD; break; default: color = 0; } return color; } ///////////////////////////////////////////////////// // return character to represent the severity ///////////////////////////////////////////////////// // return value // - n normal // - w warning // - c critical // static char setseverity(long val, long cthreshold, long wthreshold) { if (val < wthreshold) return 'n'; if (val < cthreshold) return 'w'; return 'c'; } ///////////////////////////////////////////////////// // sort bar values in descending order // for CPU and disk stats ///////////////////////////////////////////////////// static void sortvertbars(int nbars, int avgbar, struct vertval **valpp) { // copy original array to be sorted // struct vertval *sortlist = malloc(sizeof(struct vertval) * nbars); ptrverify(sortlist, "Malloc failed for %d sortitems\n", nbars); memcpy(sortlist, *valpp, sizeof(struct vertval) * nbars); // sort the copied list // qsort(sortlist+avgbar, nbars-avgbar, sizeof(struct vertval), compvertval); *valpp = sortlist; } ///////////////////////////////////////////////////// // function to be called by qsort in sortvertbars() ///////////////////////////////////////////////////// static int compvertval(const void *a, const void *b) { const struct vertval *sia = a; const struct vertval *sib = b; if (sia->barval > sib->barval) return -1; if (sia->barval < sib->barval) return 1; return 0; } ///////////////////////////////////////////////////// // sort network bar values in descending order // for network stats ///////////////////////////////////////////////////// static void sortnetbars(int nbars, struct netval **valpp) { // copy original array to be sorted // struct netval *sortlist = malloc(sizeof(struct netval) * nbars); ptrverify(sortlist, "Malloc failed for %d sortitems\n", nbars); memcpy(sortlist, *valpp, sizeof(struct netval) * nbars); // sort the copied list // qsort(sortlist, nbars, sizeof(struct netval), compnetval); *valpp = sortlist; } ///////////////////////////////////////////////////// // function to be called by qsort in sortnetbars() ///////////////////////////////////////////////////// static int compnetval(const void *a, const void *b) { const struct netval *nva = a; const struct netval *nvb = b; long long v1, v2; v1 = nva->pvals + nva->pvalr; v2 = nvb->pvals + nvb->pvalr; if (v1 > v2) return -1; if (v1 < v2) return 1; return 0; } // signal catcher // static void getsigwinch(int signr) { winchange = 1; } atop-2.12.1/gpucom.c0000644000203100020310000003552215064552761013520 0ustar gerlofgerlof/* ** ATOP - System & Process Monitor ** ** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as process-level. ** ** This source-file contains functions to interface with the atopgpud ** daemon that maintains statistics about the processor and memory ** utilization of the GPUs. ** ================================================================ ** Author: Gerlof Langeveld ** E-mail: gerlof.langeveld@atoptool.nl ** Initial: April/August 2018 ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. */ #define _POSIX_C_SOURCE #define _XOPEN_SOURCE #define _GNU_SOURCE #define _DEFAULT_SOURCE #include #include #include #include #include #include #include #include #include "atop.h" #include "photosyst.h" #include "photoproc.h" #include "gpucom.h" #define DUMMY ' ' #define GPUDELIM '@' #define PIDDELIM '#' #define GPUDPORT 59123 static int gputype_parse(char *); static int gpustat_parse(int, char *, int, struct pergpu *, struct gpupidstat *); static int gpuparse(int, char *, struct pergpu *); static int pidparse(int, char *, struct gpupidstat *); static int rcvuntil(int, char *, int); static int actsock = -1; static int numgpus; static char **gpubusid; // array with char* to busid strings static char **gputypes; // array with char* to type strings static char *gputasks; // array with chars with tasksupport booleans /* ** Open TCP connection to port of atopgpud and ** obtain type information of every GPU. ** ** Return value: ** number of GPUs */ int gpud_init(void) { struct sockaddr_in name; socklen_t namelen = sizeof name; char typereq[] = {'T', APIVERSION}; uint32_t prelude; char *buf; int version, length; struct timeval rcvtimeout = {2, 0}; // 2 seconds /* ** get local socket */ if ( (actsock = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror("socket creation"); return 0; } /* ** connect to server port */ memset(&name, 0, sizeof name); name.sin_family = AF_INET; name.sin_addr.s_addr = htonl(INADDR_LOOPBACK); name.sin_port = htons(GPUDPORT); if (connect(actsock, (struct sockaddr *)&name, namelen) == -1) goto close_and_return; /* ** set receive timeout, not to block atop forever ** in case something fails in the commmunication */ (void) setsockopt(actsock, SOL_SOCKET, SO_RCVTIMEO, &rcvtimeout, sizeof rcvtimeout); /* ** send request: GPU types */ if ( write(actsock, typereq, sizeof typereq) < sizeof typereq) { perror("send type request to atopgpud"); goto close_and_return; } /* ** receive response: GPU types */ if (rcvuntil(actsock, (char *)&prelude, sizeof prelude) == -1) { perror("receive prelude from atopgpud"); goto close_and_return; } prelude = ntohl(prelude); // big endian to native endianess version = (prelude >> 24) & 0xff; length = prelude & 0xffffff; if (version != APIVERSION) { fprintf(stderr, "wrong API version from atopgpud: %d %d\n", version, APIVERSION); goto close_and_return; } if (length > 8192) // sanity check { fprintf(stderr, "unexpected response length atopgpud: %d\n", length); goto close_and_return; } buf = malloc(length+1); ptrverify(buf, "Malloc failed for gpu rcvbuf\n"); if ( rcvuntil(actsock, buf, length) == -1) { perror("receive type request from atopgpud"); free(buf); goto close_and_return; } buf[length] = '\0'; if (! gputype_parse(buf)) { free(buf); goto close_and_return; } return numgpus; close_and_return: close(actsock); actsock = -1; numgpus = 0; return 0; } /* ** Transmit status request for all GPUs. ** ** Calling parameters: ** void ** ** Return value: ** 0 in case of failure ** 1 in case of success (request pending) */ int gpud_statrequest(void) { char statreq[] = {'S', APIVERSION}; if (actsock == -1) return 0; if ( write(actsock, statreq, sizeof statreq) < sizeof statreq) { close(actsock); actsock = -1; numgpus = 0; return 0; } return 1; } /* ** Receive status response for all GPUs. ** ** Calling parameters: ** *ggs pointer to allocated array of pergpu structs ** **gps pointer to pointer in which addresses to gpupidstat structs ** are returned ** can be NULL pointer is caller is not interested in proc stats ** ** Return value: ** number of gpupidstat addresses (i.e. per-process info) ** -1 in case of failure */ int gpud_statresponse(int maxgpu, struct pergpu *ggs, struct gpupidstat **gps) { uint32_t prelude; char *buf = NULL, *p; int version, length; int maxprocs = 0, nrprocs; if (actsock == -1) return -1; /* ** receive 4-bytes introducer: ** first byte: API version ** next three bytes: length of string that follows */ if ( rcvuntil(actsock, (char *)&prelude, sizeof prelude) == -1) { perror("receive 4-byte prelude from atopgpud"); goto close_and_return; } prelude = ntohl(prelude); // big endian to native endianess version = (prelude >> 24) & 0xff; length = prelude & 0xffffff; if (version != APIVERSION) { fprintf(stderr, "wrong API version from atopgpud: %d %d\n", version, APIVERSION); goto close_and_return; } if (length > 8192) // sanity check { fprintf(stderr, "unexpected response length atopgpud: %d\n", length); goto close_and_return; } buf = malloc(length+1); ptrverify(buf, "Malloc failed for gpu rcvbuf\n"); /* ** receive statistics string */ if ( rcvuntil(actsock, buf, length) == -1) { perror("receive stats string from atopgpud"); goto close_and_return; } *(buf+length) = '\0'; /* ** determine number of per-process stats in string ** and malloc space to store these stats */ for (p=buf; *p; p++) { if (*p == PIDDELIM) maxprocs++; } if (gps) { if (maxprocs) { *gps = malloc(maxprocs * sizeof(struct gpupidstat)); ptrverify(*gps, "Malloc failed for gpu pidstats\n"); memset(*gps, 0, maxprocs * sizeof(struct gpupidstat)); } else { *gps = NULL; } } /* ** parse stats string for per-gpu stats */ if ( (nrprocs = gpustat_parse(version, buf, maxgpu, ggs, gps ? *gps : NULL)) == -1) { if (gps) { free(*gps); *gps = NULL; // avoid double free later on } goto close_and_return; // inconsistent data received from atopgpud } free(buf); return nrprocs; close_and_return: free(buf); close(actsock); actsock = -1; numgpus = 0; return -1; } /* ** Receive given number of bytes from given socket ** into given buffer address ** ** Return value: number of bytes received ** -1 - failed (including end-of-connection) */ static int rcvuntil(int sock, char *buf, int size) { int remain = size, n; while (remain) { n = read(sock, buf, remain); if (n <= 0) return -1; buf += n; remain -= n; } return size; } /* ** Parse response string from server on 'T' request ** ** Store the type, busid and tasksupport of every GPU in ** static pointer tables ** ** Return value: 1 - success ** 0 - failed */ static int gputype_parse(char *buf) { char *p, *start, **bp, **tp, *cp, fails=0; /* ** determine number of GPUs */ if ( sscanf(buf, "%d@", &numgpus) != 1) return 0; numgpus = numgpus <= MAXGPU ? numgpus : MAXGPU; /* ** search for first GPU delimiter (@) */ for (p=buf; *p; p++) { if (*p == GPUDELIM) { p++; break; } } if (*p == 0) // no delimiter or no data behind delimeter? return 0; /* ** parse GPU info and build arrays of pointers to the ** busid strings, type strings and tasksupport strings. */ if (numgpus) // GPUs present anyhow? { int field; gpubusid = bp = malloc((numgpus+1) * sizeof(char *)); gputypes = tp = malloc((numgpus+1) * sizeof(char *)); gputasks = cp = malloc((numgpus) * sizeof(char )); ptrverify(gpubusid, "Malloc failed for gpu busids\n"); ptrverify(gputypes, "Malloc failed for gpu types\n"); ptrverify(gputasks, "Malloc failed for gpu tasksup\n"); for (field=0, start=p; fails == 0; p++) { if (*p == ' ' || *p == '\0' || *p == GPUDELIM) { switch(field) { case 0: if (bp - gpubusid >= numgpus) { fails++; break; // inconsistent with number of GPUs } if (p-start <= MAXGPUBUS) *bp++ = start; else *bp++ = p - MAXGPUBUS; break; case 1: if (tp - gputypes >= numgpus) { fails++; break; // inconsistent with number of GPUs } if (p-start <= MAXGPUTYPE) *tp++ = start; else *tp++ = p - MAXGPUTYPE; break; case 2: if (cp - gputasks >= numgpus) { fails++; break; // inconsistent with number of GPUs } *cp++ = *start; break; default: fails++; } field++; start = p+1; if (*p == '\0') break; if (*p == GPUDELIM) field = 0; *p = '\0'; } } *bp = NULL; *tp = NULL; /* ** verify if number of GPUs and supplied per-GPU information ** appears to be inconsistent */ if (fails || bp - gpubusid != numgpus || tp - gputypes != numgpus || cp - gputasks != numgpus) { free(gpubusid); free(gputypes); free(gputasks); return 0; } } else { return 0; } return 1; } /* ** Parse entire response string from server. ** ** Every series with counters on GPU level is introduced ** with a '@' delimiter. ** Every series with counters on process level is introduced ** with a '#' delimiter (last part of the GPU level data). ** ** Return value: valid number of processes ** -1 - failed */ static int gpustat_parse(int version, char *buf, int maxgpu, struct pergpu *gg, struct gpupidstat *gp) { char *p, *pp, *start; int gpunum, nrprocs = 0; /* ** parse stats string */ for (p=buf; *p && *p != GPUDELIM; p++) // find first GPU deimiter ; if (*p == 0) // string without GPU delimiter return -1; for (p++, start=p, gpunum=0; gpunum < maxgpu; p++) { char delimnext; // search next GPU delimiter // if (*p && *p != GPUDELIM) continue; /* ** next GPU delimiter or end-of-string found */ delimnext = *p; *p = 0; /* ** parse GPU itself */ if (! gpuparse(version, start, gg)) return -1; safe_strcpy(gg->type, gputypes[gpunum], sizeof gg->type); safe_strcpy(gg->busid, gpubusid[gpunum], sizeof gg->busid); /* ** continue searching for per-process stats for this GPU */ if (gp) { for (pp = start; pp < p; pp++) { if (*pp != PIDDELIM) continue; // new PID delimiter (#) found // if (! pidparse(version, pp+1, gp)) return -1; gp->gpu.nrgpus++; gp->gpu.gpulist = 1<nrprocs++; // per GPU nrprocs++; // total } } gpunum++; gg++; if (delimnext == 0 || *(p+1) == 0) break; start = p+1; } return nrprocs; } /* ** Parse GPU statistics string ** ** Return value: 1 - success ** 0 - failed */ static int gpuparse(int version, char *p, struct pergpu *gg) { int nr; switch (version) { case 1: nr = sscanf(p, "%d %d %lld %lld %lld %lld %lld %lld", &(gg->gpupercnow), &(gg->mempercnow), &(gg->memtotnow), &(gg->memusenow), &(gg->samples), &(gg->gpuperccum), &(gg->memperccum), &(gg->memusecum)); if (nr < 8) // parse error: unexpected data return 0; gg->nrprocs = 0; break; } return 1; } /* ** Parse PID statistics string ** ** Return value: 1 - success ** 0 - failed */ static int pidparse(int version, char *p, struct gpupidstat *gp) { int nr; switch (version) { case 1: nr = sscanf(p, "%c %ld %d %d %lld %lld %lld %lld", &(gp->gpu.state), &(gp->pid), &(gp->gpu.gpubusy), &(gp->gpu.membusy), &(gp->gpu.timems), &(gp->gpu.memnow), &(gp->gpu.memcum), &(gp->gpu.sample)); if (nr < 8) // parse error: unexpected data return 0; break; } return 1; } /* ** Merge the GPU per-process counters with the other ** per-process counters */ static int compgpupid(const void *, const void *); void gpumergeproc(struct tstat *curtpres, int ntaskpres, struct tstat *curpexit, int nprocexit, struct gpupidstat *gpuproc, int nrgpuproc) { struct gpupidstat **gpp; int t, g, gpuleft = nrgpuproc; /* ** make pointer list for elements in gpuproc */ gpp = malloc(nrgpuproc * sizeof(struct gpupidstat *)); if (!gpp) ptrverify(gpp, "Malloc failed for process list\n"); for (g=0; g < nrgpuproc; g++) gpp[g] = gpuproc + g; /* ** sort the list with pointers in order of pid */ if (nrgpuproc > 1) qsort(gpp, nrgpuproc, sizeof(struct gpupidstat *), compgpupid); /* ** accumulate entries that contain stats from same PID ** on different GPUs */ for (g=1; g < nrgpuproc; g++) { if (gpp[g-1]->pid == gpp[g]->pid) { struct gpupidstat *p = gpp[g-1], *q = gpp[g]; p->gpu.nrgpus += q->gpu.nrgpus; p->gpu.gpulist |= q->gpu.gpulist; if (p->gpu.gpubusy != -1) p->gpu.gpubusy += q->gpu.gpubusy; if (p->gpu.membusy != -1) p->gpu.membusy += q->gpu.membusy; if (p->gpu.timems != -1) p->gpu.timems += q->gpu.timems; p->gpu.memnow += q->gpu.memnow; p->gpu.memcum += q->gpu.memcum; p->gpu.sample += q->gpu.sample; if (nrgpuproc-g-1 > 0) memmove(&(gpp[g]), &(gpp[g+1]), (nrgpuproc-g-1) * sizeof p); nrgpuproc--; g--; } } /* ** merge gpu stats with sorted task list of active processes */ for (t=g=0; t < ntaskpres && g < nrgpuproc; t++) { if (curtpres[t].gen.isproc) { if (curtpres[t].gen.pid == gpp[g]->pid) { curtpres[t].gpu = gpp[g]->gpu; gpp[g++] = NULL; if (--gpuleft == 0 || g >= nrgpuproc) break; } // anyhow resync while ( curtpres[t].gen.pid > gpp[g]->pid) { if (++g >= nrgpuproc) break; } } } if (gpuleft == 0) { free(gpp); return; } /* ** compact list with pointers to remaining pids */ for (g=t=0; g < nrgpuproc; g++) { if (gpp[g] == NULL) { for (; t < nrgpuproc; t++) { if (gpp[t]) { gpp[g] = gpp[t]; gpp[t] = NULL; break; } } } } /* ** merge remaining gpu stats with task list of exited processes */ for (t=0; t < nprocexit && gpuleft; t++) { if (curpexit[t].gen.isproc) { for (g=0; g < gpuleft; g++) { if (gpp[g] == NULL) continue; if (curpexit[t].gen.pid == gpp[g]->pid) { curpexit[t].gpu = gpp[g]->gpu; gpp[g] = NULL; gpuleft--; } } } } free(gpp); } static int compgpupid(const void *a, const void *b) { return (*((struct gpupidstat **)a))->pid - (*((struct gpupidstat **)b))->pid; } atop-2.12.1/gpucom.h0000644000203100020310000000314315064552761013517 0ustar gerlofgerlof/* ** ATOP - System & Process Monitor ** ** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as process-level. ** ========================================================================== ** Author: Gerlof Langeveld ** E-mail: gerlof.langeveld@atoptool.nl ** Date: September 2002 ** -------------------------------------------------------------------------- ** Copyright (C) 2000-2010 Gerlof Langeveld ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ** -------------------------------------------------------------------------- */ #ifndef __GPUCOM__ #define __GPUCOM__ #define APIVERSION 1 struct gpupidstat { long pid; struct gpu gpu; }; int gpud_init(void); int gpud_statrequest(void); int gpud_statresponse(int, struct pergpu *, struct gpupidstat **); void gpumergeproc(struct tstat *, int, struct tstat *, int, struct gpupidstat *, int); #endif atop-2.12.1/ifprop.c0000644000203100020310000002334015064552761013520 0ustar gerlofgerlof/* ** ATOP - System & Process Monitor ** ** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as process-level. ** ** ========================================================================== ** Author: Gerlof Langeveld ** E-mail: gerlof.langeveld@atoptool.nl ** Date: January 2007 ** -------------------------------------------------------------------------- ** Copyright (C) 2007-2010 Gerlof Langeveld ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ** -------------------------------------------------------------------------- */ #include #include #include #include #include #include #include #include #include #include #include #include typedef __u64 u64; typedef __u32 u32; typedef __u16 u16; typedef __u8 u8; #include #include #ifndef SPEED_UNKNOWN #define SPEED_UNKNOWN -1 #endif #include "atop.h" #include "ifprop.h" #include "photosyst.h" static int calcbucket(char *); static int getphysprop(struct ifprop *); /* ** hash table for linked lists with *all* interfaces ** of this system (including virtual interfaces), even ** if the number of interfaces exceeds the maximum that ** is supported by atop (MAXINTF) ** when the number of interfaces in the system exceeds MAXINTF, ** preferably virtual interfaces are marked 'invalid' to ensure ** that all physical interfaces are reported ** ** the hash table is meant to be searched on interface name */ #define NUMIFHASH 256 // must be power of 2!! static struct ifprop *ifhash[NUMIFHASH]; /* ** refresh interval of ifhash table ** ** periodic refreshing is needed because interfaces might have been ** created or removed, or the speed might have changed (e.g. with wireless) */ #define REFRESHTIME 60 // seconds static time_t lastrefreshed; // epoch /* ** function that searches for the properties of a particular interface; ** the interface name should be filled in the struct ifprop before ** calling this function ** ** return value reflects true (valid interface) or false (invalid interface) */ int getifprop(struct ifprop *p) { register int bucket; struct ifprop *ifp; /* ** search properties related to given interface name */ bucket = calcbucket(p->name); for (ifp=ifhash[bucket]; ifp; ifp=ifp->next) { if ( strcmp(ifp->name, p->name) == EQ ) { if (ifp->type == 'i') // invalidated interface? break; // valid interface; copy properties *p = *ifp; return 1; } } p->type = '?'; p->speed = 0; p->fullduplex = 0; return 0; } /* ** function (re)stores properties of all interfaces in a hash table */ void initifprop(void) { FILE *fp; char *cp, linebuf[2048]; struct ifprop *ifp, *ifpsave = NULL; int bucket, nrinterfaces=0, nrphysical=0; DIR *dirp; struct dirent *dentry; /* ** verify if the interface properties have to be refreshed ** at this moment already */ if (time(0) < lastrefreshed + REFRESHTIME) return; /* ** when this function has been called before, first remove ** old entries */ if (lastrefreshed) { for (bucket=0; bucket < NUMIFHASH; bucket++) { for (ifp = ifhash[bucket]; ifp; ifp = ifpsave) { ifpsave = ifp->next; free(ifp); } ifhash[bucket] = NULL; } } /* ** open /proc/net/dev and read all interface names to be able to ** setup new entries in the hash table */ if ( (fp = fopen("/proc/net/dev", "r")) == NULL) return; while ( fgets(linebuf, sizeof(linebuf), fp) != NULL) { /* ** skip lines containing a '|' symbol (headers) */ if ( strchr(linebuf, '|') != NULL) continue; if ( (cp = strchr(linebuf, ':')) != NULL) *cp = ' '; /* subst ':' by space */ /* ** allocate new ifprop struct for this interface */ ifp = malloc(sizeof *ifp); ptrverify(ifp, "Malloc failed for ifprop struct\n"); memset(ifp, 0, sizeof *ifp); sscanf(linebuf, "%30s", ifp->name); // fill name ifp->type = 'i'; // initially 'invalid' /* ** add ifprop struct to proper hash bucket */ bucket = calcbucket(ifp->name); ifp->next = ifhash[bucket]; ifhash[bucket] = ifp; nrinterfaces++; } fclose(fp); /* ** read /sys/devices/virtual/net/xxx to determine which ** interfaces are virtual (xxx is subdirectory name) */ if ( (dirp = opendir("/sys/devices/virtual/net")) ) { while ( (dentry = readdir(dirp)) ) { if (dentry->d_name[0] == '.') continue; // valid name // search ifprop and mark it as 'virtual' bucket = calcbucket(dentry->d_name); for (ifp = ifhash[bucket]; ifp; ifp = ifp->next) { if ( strcmp(ifp->name, dentry->d_name) == EQ ) { ifp->type = 'v'; // virtual interface break; } } } closedir(dirp); } /* ** for physical interfaces, determine the speed and duplex mode */ for (bucket=0; bucket < NUMIFHASH; bucket++) { for (ifp=ifhash[bucket]; ifp; ifp=ifp->next) { // possible physical interface? if (ifp->type == 'i') { if (getphysprop(ifp)) nrphysical++; } } } lastrefreshed = time(0); if (nrinterfaces < MAXINTF) return; /* ** when the number of interfaces exceeds the maximum, ** invalidate the appropriate number of interfaces (preferably ** virtual interfaces) */ for (bucket=0; bucket < NUMIFHASH && nrinterfaces >= MAXINTF; bucket++) { for (ifp=ifhash[bucket]; ifp && nrinterfaces >= MAXINTF; ifp=ifp->next) { // interface invalid already? if (ifp->type == 'i') { nrinterfaces--; continue; } // physical interface (ethernet or wireless)? if (ifp->type == 'e' || ifp->type == 'w') { // only invalidate when the number of physical // interfaces exceeds MAXINTF if (nrphysical >= MAXINTF) { ifp->type = 'i'; nrphysical--; nrinterfaces--; } continue; } // virtual or unknown interface, invalidate anyhow ifp->type = 'i'; nrinterfaces--; } } } static int calcbucket(char *p) { int bucket = 0; while (*p) bucket += *p++; return bucket & (NUMIFHASH-1); } /* ** function gathers the properties of a particular physical interface; ** the name of the interface should have been filled before calling ** ** return value reflects true (success) or false (unknown interface type) */ static int getphysprop(struct ifprop *p) { int sockfd; #ifdef ETHTOOL_GLINKSETTINGS struct ethtool_link_settings *ethlink = NULL; // preferred! #endif struct ethtool_cmd ethcmd; // deprecated struct ifreq ifreq; struct iwreq iwreq; unsigned long speed = 0; unsigned char duplex = 0, ethernet = 0; if ( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) return 0; /* ** determine properties of ethernet interface ** preferably with actual struct ethtool_link_settings, ** otherwise with deprecated struct ethtool_cmd */ memset(&ifreq, 0, sizeof ifreq); safe_strcpy((void *)&ifreq.ifr_ifrn.ifrn_name, p->name, sizeof ifreq.ifr_ifrn.ifrn_name); #ifdef ETHTOOL_GLINKSETTINGS ethlink = calloc(1, sizeof *ethlink); ptrverify(ethlink, "Calloc failed for ethtool_link_settings\n"); ethlink->cmd = ETHTOOL_GLINKSETTINGS; ifreq.ifr_ifru.ifru_data = (void *)ethlink; if ( ioctl(sockfd, SIOCETHTOOL, &ifreq) == 0) { if (ethlink->link_mode_masks_nwords <= 0) { /* ** hand shaked ethlink required with added maps ** ** layout of link_mode_masks fields: ** __u32 map_supported[link_mode_masks_nwords]; ** __u32 map_advertising[link_mode_masks_nwords]; ** __u32 map_lp_advertising[link_mode_masks_nwords]; */ ethlink->link_mode_masks_nwords = -ethlink->link_mode_masks_nwords; ethlink = realloc(ethlink, sizeof *ethlink + 3 * sizeof(__u32) * ethlink->link_mode_masks_nwords); ptrverify(ethlink, "Realloc failed for ethtool_link_settings\n"); ifreq.ifr_ifru.ifru_data = (void *)ethlink; // might have changed if ( ioctl(sockfd, SIOCETHTOOL, &ifreq) != 0 ) { close(sockfd); free(ethlink); return 0; } } ethernet = 1; speed = ethlink->speed; duplex = ethlink->duplex; } else #endif { memset(ðcmd, 0, sizeof ethcmd); ethcmd.cmd = ETHTOOL_GSET; ifreq.ifr_ifru.ifru_data = (void *)ðcmd; if ( ioctl(sockfd, SIOCETHTOOL, &ifreq) == 0) { ethernet = 1; speed = ethcmd.speed; duplex = ethcmd.duplex; } } if (ethernet) { p->type = 'e'; // type ethernet if (speed == (u32)SPEED_UNKNOWN || speed == 65535) p->speed = 0; else p->speed = speed; switch (duplex) { case DUPLEX_FULL: p->fullduplex = 1; break; default: p->fullduplex = 0; } } else { /* ** determine properties of wireless interface */ memset(&iwreq, 0, sizeof iwreq); safe_strcpy(iwreq.ifr_ifrn.ifrn_name, p->name, sizeof iwreq.ifr_ifrn.ifrn_name); if ( ioctl(sockfd, SIOCGIWRATE, &iwreq) == 0) { p->type = 'w'; // type wireless p->fullduplex = 0; p->speed = (iwreq.u.bitrate.value + 500000) / 1000000; } else { p->type = '?'; // type unknown p->fullduplex = 0; p->speed = 0; } } #ifdef ETHTOOL_GLINKSETTINGS free(ethlink); #endif close(sockfd); return 1; } atop-2.12.1/ifprop.h0000644000203100020310000000320215064552761013520 0ustar gerlofgerlof/* ** ATOP - System & Process Monitor ** ** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as process-level. ** ========================================================================== ** Author: Gerlof Langeveld ** E-mail: gerlof.langeveld@atoptool.nl ** Date: September 2002 ** -------------------------------------------------------------------------- ** Copyright (C) 2000-2010 Gerlof Langeveld ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ** -------------------------------------------------------------------------- */ #ifndef __IFPROP__ #define __IFPROP__ struct ifprop { char type; /* type: 'e' - ethernet */ /* 'w' - wireless */ /* 'v' - virtual */ char name[31]; /* name of interface */ long int speed; /* in megabits per second */ char fullduplex; /* boolean */ struct ifprop *next; /* next in hash list */ }; int getifprop(struct ifprop *); void initifprop(void); #endif atop-2.12.1/json.c0000644000203100020310000011334515064552761013177 0ustar gerlofgerlof/* ** ATOP - System & Process Monitor ** ** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as process-level. ** ** ========================================================================== ** Author: Fei Li & zhenwei pi ** E-mail: lifei.shirley@bytedance.com, pizhenwei@bytedance.com ** Date: August 2019 ** -------------------------------------------------------------------------- ** Copyright (C) 2019-2022 bytedance.com ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ** -------------------------------------------------------------------------- ** ** Revision 1.1 2019/08/08 14:02:19 ** Initial revision ** Add support for json style output, basing on the parseable.c file. ** */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "atop.h" #include "photosyst.h" #include "photoproc.h" #include "cgroups.h" #include "json.h" #define LEN_HP_SIZE 64 #define LINE_BUF_SIZE 1024 static void json_print_CPU(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); static void json_print_cpu(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); static void json_print_CPL(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); static void json_print_GPU(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); static void json_print_MEM(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); static void json_print_SWP(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); static void json_print_PAG(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); static void json_print_PSI(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); static void json_print_LVM(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); static void json_print_MDD(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); static void json_print_DSK(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); static void json_print_NFM(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); static void json_print_NFC(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); static void json_print_NFS(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); static void json_print_NET(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); static void json_print_IFB(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); static void json_print_NUM(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); static void json_print_NUC(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); static void json_print_LLC(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); static void json_print_CGR(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); static void json_print_PRG(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); static void json_print_PRC(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); static void json_print_PRM(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); static void json_print_PRD(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); static void json_print_PRN(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); static void json_print_PRE(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); /* ** table with possible labels and the corresponding ** print-function for json style output */ struct labeldef { char *label; short valid; short cgroupref; void (*prifunc)(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); }; static struct labeldef labeldef[] = { { "CPU", 0, 0, json_print_CPU }, { "cpu", 0, 0, json_print_cpu }, { "CPL", 0, 0, json_print_CPL }, { "GPU", 0, 0, json_print_GPU }, { "MEM", 0, 0, json_print_MEM }, { "SWP", 0, 0, json_print_SWP }, { "PAG", 0, 0, json_print_PAG }, { "PSI", 0, 0, json_print_PSI }, { "LVM", 0, 0, json_print_LVM }, { "MDD", 0, 0, json_print_MDD }, { "DSK", 0, 0, json_print_DSK }, { "NFM", 0, 0, json_print_NFM }, { "NFC", 0, 0, json_print_NFC }, { "NFS", 0, 0, json_print_NFS }, { "NET", 0, 0, json_print_NET }, { "IFB", 0, 0, json_print_IFB }, { "NUM", 0, 0, json_print_NUM }, { "NUC", 0, 0, json_print_NUC }, { "LLC", 0, 0, json_print_LLC }, { "CGR", 0, 0, json_print_CGR }, { "PRG", 0, 1, json_print_PRG }, { "PRC", 0, 1, json_print_PRC }, { "PRM", 0, 1, json_print_PRM }, { "PRD", 0, 0, json_print_PRD }, { "PRN", 0, 0, json_print_PRN }, { "PRE", 0, 0, json_print_PRE }, }; static int numlabels = sizeof labeldef / sizeof(struct labeldef); /* ** analyse the json-definition string that has been ** passed as argument with the flag -J */ int jsondef(char *pd) { register int i; char *p, *ep = pd + strlen(pd); /* ** check if string passed behind -J is not another flag */ if (*pd == '-') { printf("json labels should be followed by label list\n"); return 0; } /* ** check list of comma-separated labels */ while (pd < ep) { /* ** exchange comma by null-byte */ if ( (p = strchr(pd, ',')) ) *p = 0; else p = ep-1; /* ** check if the next label exists */ for (i=0; i < numlabels; i++) { if ( strcmp(labeldef[i].label, pd) == 0) { labeldef[i].valid = 1; break; } } /* ** non-existing label has been used */ if (i == numlabels) { /* ** check if special label 'ALL' has been used */ if ( strcmp("ALL", pd) == 0) { for (i=0; i < numlabels; i++) labeldef[i].valid = 1; break; } else { printf("json labels not supported: %s\n", pd); return 0; } } pd = p+1; } setbuf(stdout, (char *)0); return 1; } /* ** produce json output for an interval */ char jsonout(time_t curtime, int numsecs, struct devtstat *devtstat, struct sstat *sstat, struct cgchainer *devchain, int ncgroups, int npids, int nexit, unsigned int noverflow, char flag) { register int i, j, k, cgroupref_created = 0; char header[256]; struct tstat *tmp = devtstat->taskall; printf("{\"host\": \"%s\", " "\"timestamp\": %ld, " "\"elapsed\": %d", utsname.nodename, curtime, numsecs ); /* Replace " with # in case json can not parse this out */ for (k = 0; k < devtstat->ntaskall; k++, tmp++) { for (j = 0; (j < sizeof(tmp->gen.name)) && tmp->gen.name[j]; j++) if ((tmp->gen.name[j] == '\"') || (tmp->gen.name[j] == '\\')) tmp->gen.name[j] = '#'; for (j = 0; (j < sizeof(tmp->gen.cmdline) && tmp->gen.cmdline[j]); j++) if ((tmp->gen.cmdline[j] == '\"') || (tmp->gen.cmdline[j] == '\\')) tmp->gen.cmdline[j] = '#'; } /* ** iterate all labels defined in labeldef[] */ for (i=0; i < numlabels; i++) { if (!labeldef[i].valid) continue; /* ** when cgroup index is needed to map the tstat to a cgroup, ** once fill the tstat.gen.cgroupix variables */ if (supportflags & CGROUPV2 && labeldef[i].cgroupref && !cgroupref_created) { cgfillref(devtstat, devchain, ncgroups, npids); cgroupref_created = 1; } /* prepare generic columns */ snprintf(header, sizeof header, "\"%s\"", labeldef[i].label); /* call all print-functions */ (labeldef[i].prifunc)(header, sstat, devtstat->taskall, devtstat->ntaskall, devchain, ncgroups); } printf("}\n"); return '\0'; } /* ** print functions for system-level statistics */ static void json_calc_freqscale(count_t maxfreq, count_t cnt, count_t ticks, count_t *freq, int *freqperc) { // if ticks != 0,do full calcs if (maxfreq && ticks) { *freq = cnt/ticks; *freqperc = 100* *freq / maxfreq; } else if (maxfreq) { // max frequency is known so % can be calculated *freq = cnt; *freqperc = 100 * cnt / maxfreq; } else if (cnt) { // no max known, set % to 100 *freq = cnt; *freqperc = 100; } else { // nothing is known: set freq to 0, % to 100 *freq = 0; *freqperc = 100; } } static void json_print_CPU(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *cs, int ncgroups) { count_t maxfreq=0; count_t cnt=0; count_t ticks=0; count_t freq; int freqperc; int i; // calculate average clock frequency for (i = 0; i < ss->cpu.nrcpu; i++) { cnt += ss->cpu.cpu[i].freqcnt.cnt; ticks += ss->cpu.cpu[i].freqcnt.ticks; } maxfreq = ss->cpu.cpu[0].freqcnt.maxfreq; json_calc_freqscale(maxfreq, cnt, ticks, &freq, &freqperc); if (ss->cpu.all.instr == 1) { ss->cpu.all.instr = 0; ss->cpu.all.cycle = 0; } printf(", %s: {" "\"hertz\": %u, " "\"nrcpu\": %lld, " "\"stime\": %lld, " "\"utime\": %lld, " "\"ntime\": %lld, " "\"itime\": %lld, " "\"wtime\": %lld, " "\"Itime\": %lld, " "\"Stime\": %lld, " "\"steal\": %lld, " "\"guest\": %lld, " "\"freq\": %lld, " "\"freqperc\": %d, " "\"instr\": %lld, " "\"cycle\": %lld}", hp, hertz, ss->cpu.nrcpu, ss->cpu.all.stime, ss->cpu.all.utime, ss->cpu.all.ntime, ss->cpu.all.itime, ss->cpu.all.wtime, ss->cpu.all.Itime, ss->cpu.all.Stime, ss->cpu.all.steal, ss->cpu.all.guest, freq, freqperc, ss->cpu.all.instr, ss->cpu.all.cycle ); } static void json_print_cpu(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *cs, int ncgroups) { register int i; count_t maxfreq=0; count_t cnt=0; count_t ticks=0; count_t freq; int freqperc; printf(", %s: [", hp); for (i = 0; i < ss->cpu.nrcpu; i++) { if (i > 0) { printf(", "); } cnt = ss->cpu.cpu[i].freqcnt.cnt; ticks = ss->cpu.cpu[i].freqcnt.ticks; maxfreq= ss->cpu.cpu[0].freqcnt.maxfreq; json_calc_freqscale(maxfreq, cnt, ticks, &freq, &freqperc); printf("{\"cpuid\": %d, " "\"stime\": %lld, " "\"utime\": %lld, " "\"ntime\": %lld, " "\"itime\": %lld, " "\"wtime\": %lld, " "\"Itime\": %lld, " "\"Stime\": %lld, " "\"steal\": %lld, " "\"guest\": %lld, " "\"freq\": %lld, " "\"freqperc\": %d, " "\"instr\": %lld, " "\"cycle\": %lld}", i, ss->cpu.cpu[i].stime, ss->cpu.cpu[i].utime, ss->cpu.cpu[i].ntime, ss->cpu.cpu[i].itime, ss->cpu.cpu[i].wtime, ss->cpu.cpu[i].Itime, ss->cpu.cpu[i].Stime, ss->cpu.cpu[i].steal, ss->cpu.cpu[i].guest, freq, freqperc, ss->cpu.cpu[i].instr, ss->cpu.cpu[i].cycle); } printf("]"); } static void json_print_CPL(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *cs, int ncgroups) { printf(", %s: {" "\"lavg1\": %.2f, " "\"lavg5\": %.2f, " "\"lavg15\": %.2f, " "\"csw\": %lld, " "\"devint\": %lld}", hp, ss->cpu.lavg1, ss->cpu.lavg5, ss->cpu.lavg15, ss->cpu.csw, ss->cpu.devint); } static void json_print_GPU(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *cs, int ncgroups) { int i; printf(", %s: [", hp); for (i = 0; i < ss->gpu.nrgpus; i++) { if (i > 0) { printf(", "); } printf("{\"gpuid\": %d, " "\"busid\": \"%.19s\", " "\"type\": \"%.19s\", " "\"gpupercnow\": %d, " "\"mempercnow\": %d, " "\"memtotnow\": %lld, " "\"memusenow\": %lld, " "\"samples\": %lld, " "\"gpuperccum\": %lld, " "\"memperccum\": %lld, " "\"memusecum\": %lld}", i, ss->gpu.gpu[i].busid, ss->gpu.gpu[i].type, ss->gpu.gpu[i].gpupercnow, ss->gpu.gpu[i].mempercnow, ss->gpu.gpu[i].memtotnow, ss->gpu.gpu[i].memusenow, ss->gpu.gpu[i].samples, ss->gpu.gpu[i].gpuperccum, ss->gpu.gpu[i].memperccum, ss->gpu.gpu[i].memusecum); } printf("]"); } static void json_print_MEM(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *cs, int ncgroups) { printf(", %s: {" "\"physmem\": %lld, " "\"freemem\": %lld, " "\"cachemem\": %lld, " "\"buffermem\": %lld, " "\"slabmem\": %lld, " "\"cachedrt\": %lld, " "\"slabreclaim\": %lld, " "\"vmwballoon\": %lld, " "\"shmem\": %lld, " "\"shmrss\": %lld, " "\"shmswp\": %lld, " "\"hugepagesz\": %lld, " "\"tothugepage\": %lld, " "\"freehugepage\": %lld, " "\"lhugepagesz\": %lld, " "\"ltothugepage\": %lld, " "\"lfreehugepage\": %lld, " "\"availablemem\": %lld, " "\"anonhugepage\": %lld}", hp, ss->mem.physmem * pagesize, ss->mem.freemem * pagesize, ss->mem.cachemem * pagesize, ss->mem.buffermem * pagesize, ss->mem.slabmem * pagesize, ss->mem.cachedrt * pagesize, ss->mem.slabreclaim * pagesize, ss->mem.vmwballoon * pagesize, ss->mem.shmem * pagesize, ss->mem.shmrss * pagesize, ss->mem.shmswp * pagesize, ss->mem.shugepagesz, ss->mem.stothugepage, ss->mem.sfreehugepage, ss->mem.lhugepagesz, ss->mem.ltothugepage, ss->mem.lfreehugepage, ss->mem.availablemem * pagesize, ss->mem.anonhugepage * pagesize); } static void json_print_SWP(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *cs, int ncgroups) { printf(", %s: {" "\"totswap\": %lld, " "\"freeswap\": %lld, " "\"swcac\": %lld, " "\"committed\": %lld, " "\"commitlim\": %lld}", hp, ss->mem.totswap * pagesize, ss->mem.freeswap * pagesize, ss->mem.swapcached * pagesize, ss->mem.committed * pagesize, ss->mem.commitlim * pagesize); } static void json_print_PAG(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *cs, int ncgroups) { printf(", %s: {" "\"compacts\": %lld, " "\"numamigs\": %lld, " "\"migrates\": %lld, " "\"pgscans\": %lld, " "\"allocstall\": %lld, " "\"pgins\": %lld, " "\"pgouts\": %lld, " "\"swins\": %lld, " "\"swouts\": %lld, " "\"oomkills\": %lld}", hp, ss->mem.compactstall, ss->mem.numamigrate, ss->mem.pgmigrate, ss->mem.pgscans, ss->mem.allocstall, ss->mem.pgins, ss->mem.pgouts, ss->mem.swins, ss->mem.swouts, ss->mem.oomkills); } static void json_print_PSI(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *cs, int ncgroups) { if ( !(ss->psi.present) ) return; printf(", %s: {" "\"psi\": \"%c\", " "\"cs10\": %.1f, " "\"cs60\": %.1f, " "\"cs300\": %.1f, " "\"cstot\": %llu, " "\"ms10\": %.1f, " "\"ms60\": %.1f, " "\"ms300\": %.1f, " "\"mstot\": %llu, " "\"mf10\": %.1f, " "\"mf60\": %.1f, " "\"mf300\": %.1f, " "\"mftot\": %llu, " "\"ios10\": %.1f, " "\"ios60\": %.1f, " "\"ios300\": %.1f, " "\"iostot\": %llu, " "\"iof10\": %.1f, " "\"iof60\": %.1f, " "\"iof300\": %.1f, " "\"ioftot\": %llu}", hp, ss->psi.present ? 'y' : 'n', ss->psi.cpusome.avg10, ss->psi.cpusome.avg60, ss->psi.cpusome.avg300, ss->psi.cpusome.total, ss->psi.memsome.avg10, ss->psi.memsome.avg60, ss->psi.memsome.avg300, ss->psi.memsome.total, ss->psi.memfull.avg10, ss->psi.memfull.avg60, ss->psi.memfull.avg300, ss->psi.memfull.total, ss->psi.iosome.avg10, ss->psi.iosome.avg60, ss->psi.iosome.avg300, ss->psi.iosome.total, ss->psi.iofull.avg10, ss->psi.iofull.avg60, ss->psi.iofull.avg300, ss->psi.iofull.total); } static void json_print_LVM(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *cs, int ncgroups) { register int i; printf(", %s: [", hp); for (i = 0; ss->dsk.lvm[i].name[0]; i++) { if (i > 0) { printf(", "); } printf("{\"lvmname\": \"%.19s\", " "\"io_ms\": %lld, " "\"nread\": %lld, " "\"nrsect\": %lld, " "\"nwrite\": %lld, " "\"nwsect\": %lld, " "\"avque\": %lld, " "\"inflight\": %lld}", ss->dsk.lvm[i].name, ss->dsk.lvm[i].io_ms, ss->dsk.lvm[i].nread, ss->dsk.lvm[i].nrsect, ss->dsk.lvm[i].nwrite, ss->dsk.lvm[i].nwsect, ss->dsk.lvm[i].avque, ss->dsk.lvm[i].inflight); } printf("]"); } static void json_print_MDD(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *cs, int ncgroups) { register int i; printf(", %s: [", hp); for (i = 0; ss->dsk.mdd[i].name[0]; i++) { if (i > 0) { printf(", "); } printf("{\"mddname\": \"%.19s\", " "\"io_ms\": %lld, " "\"nread\": %lld, " "\"nrsect\": %lld, " "\"nwrite\": %lld, " "\"nwsect\": %lld, " "\"avque\": %lld, " "\"inflight\": %lld}", ss->dsk.mdd[i].name, ss->dsk.mdd[i].io_ms, ss->dsk.mdd[i].nread, ss->dsk.mdd[i].nrsect, ss->dsk.mdd[i].nwrite, ss->dsk.mdd[i].nwsect, ss->dsk.mdd[i].avque, ss->dsk.mdd[i].inflight); } printf("]"); } static void json_print_DSK(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *cs, int ncgroups) { register int i; printf(", %s: [", hp); for (i = 0; ss->dsk.dsk[i].name[0]; i++) { if (i > 0) { printf(", "); } printf("{\"dskname\": \"%.19s\", " "\"io_ms\": %lld, " "\"nread\": %lld, " "\"nrsect\": %lld, " "\"ndiscrd\": %lld, " "\"nwrite\": %lld, " "\"nwsect\": %lld, " "\"avque\": %lld, " "\"inflight\": %lld}", ss->dsk.dsk[i].name, ss->dsk.dsk[i].io_ms, ss->dsk.dsk[i].nread, ss->dsk.dsk[i].nrsect, ss->dsk.dsk[i].ndisc, ss->dsk.dsk[i].nwrite, ss->dsk.dsk[i].nwsect, ss->dsk.dsk[i].avque, ss->dsk.dsk[i].inflight); } printf("]"); } static void json_print_NFM(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *cs, int ncgroups) { register int i; printf(", %s: [", hp); for (i = 0; i < ss->nfs.nfsmounts.nrmounts; i++) { if (i > 0) { printf(", "); } printf("{\"mountdev\": \"%.19s\", " "\"bytestotread\": %lld, " "\"bytestotwrite\": %lld, " "\"bytesread\": %lld, " "\"byteswrite\": %lld, " "\"bytesdread\": %lld, " "\"bytesdwrite\": %lld, " "\"pagesmread\": %lld, " "\"pagesmwrite\": %lld}", ss->nfs.nfsmounts.nfsmnt[i].mountdev, ss->nfs.nfsmounts.nfsmnt[i].bytestotread, ss->nfs.nfsmounts.nfsmnt[i].bytestotwrite, ss->nfs.nfsmounts.nfsmnt[i].bytesread, ss->nfs.nfsmounts.nfsmnt[i].byteswrite, ss->nfs.nfsmounts.nfsmnt[i].bytesdread, ss->nfs.nfsmounts.nfsmnt[i].bytesdwrite, ss->nfs.nfsmounts.nfsmnt[i].pagesmread * pagesize, ss->nfs.nfsmounts.nfsmnt[i].pagesmwrite * pagesize); } printf("]"); } static void json_print_NFC(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *cs, int ncgroups) { printf(", %s: {" "\"rpccnt\": %lld, " "\"rpcread\": %lld, " "\"rpcwrite\": %lld, " "\"rpcretrans\": %lld, " "\"rpcautrefresh\": %lld}", hp, ss->nfs.client.rpccnt, ss->nfs.client.rpcread, ss->nfs.client.rpcwrite, ss->nfs.client.rpcretrans, ss->nfs.client.rpcautrefresh); } static void json_print_NFS(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *cs, int ncgroups) { printf(", %s: {" "\"rpccnt\": %lld, " "\"rpcread\": %lld, " "\"rpcwrite\": %lld, " "\"nrbytes\": %lld, " "\"nwbytes\": %lld, " "\"rpcbadfmt\": %lld, " "\"rpcbadaut\": %lld, " "\"rpcbadcln\": %lld, " "\"netcnt\": %lld, " "\"nettcpcnt\": %lld, " "\"netudpcnt\": %lld, " "\"nettcpcon\": %lld, " "\"rchits\": %lld, " "\"rcmiss\": %lld, " "\"rcnocache\": %lld}", hp, ss->nfs.server.rpccnt, ss->nfs.server.rpcread, ss->nfs.server.rpcwrite, ss->nfs.server.nrbytes, ss->nfs.server.nwbytes, ss->nfs.server.rpcbadfmt, ss->nfs.server.rpcbadaut, ss->nfs.server.rpcbadcln, ss->nfs.server.netcnt, ss->nfs.server.nettcpcnt, ss->nfs.server.netudpcnt, ss->nfs.server.nettcpcon, ss->nfs.server.rchits, ss->nfs.server.rcmiss, ss->nfs.server.rcnoca); } static void json_print_NET(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *cs, int ncgroups) { register int i; printf(", \"NET_GENERAL\": {" "\"rpacketsTCP\": %lld, " "\"spacketsTCP\": %lld, " "\"activeOpensTCP\": %lld, " "\"passiveOpensTCP\": %lld, " "\"retransSegsTCP\": %lld, " "\"rpacketsUDP\": %lld, " "\"spacketsUDP\": %lld, " "\"rpacketsIP\": %lld, " "\"spacketsIP\": %lld, " "\"dpacketsIP\": %lld, " "\"fpacketsIP\": %lld, " "\"icmpi\" : %lld, " "\"icmpo\" : %lld}", ss->net.tcp.InSegs, ss->net.tcp.OutSegs, ss->net.tcp.ActiveOpens, ss->net.tcp.PassiveOpens, ss->net.tcp.RetransSegs, ss->net.udpv4.InDatagrams + ss->net.udpv6.Udp6InDatagrams, ss->net.udpv4.OutDatagrams + ss->net.udpv6.Udp6OutDatagrams, ss->net.ipv4.InReceives + ss->net.ipv6.Ip6InReceives, ss->net.ipv4.OutRequests + ss->net.ipv6.Ip6OutRequests, ss->net.ipv4.InDelivers + ss->net.ipv6.Ip6InDelivers, ss->net.ipv4.ForwDatagrams + ss->net.ipv6.Ip6OutForwDatagrams, ss->net.icmpv4.InMsgs + ss->net.icmpv6.Icmp6InMsgs, ss->net.icmpv4.OutMsgs + ss->net.icmpv6.Icmp6OutMsgs); printf(", %s: [", hp); for (i = 0; ss->intf.intf[i].name[0]; i++) { if (i > 0) { printf(", "); } printf("{\"name\": \"%.19s\", " "\"rpack\": %lld, " "\"rbyte\": %lld, " "\"rerrs\": %lld, " "\"rdrop\": %lld, " "\"rfifo\": %lld, " "\"rcompr\": %lld, " "\"rframe\": %lld, " "\"rmultic\": %lld, " "\"spack\": %lld, " "\"sbyte\": %lld, " "\"serrs\": %lld, " "\"sdrop\": %lld, " "\"sfifo\": %lld, " "\"scompr\": %lld, " "\"scollis\": %lld, " "\"scarrier\": %lld, " "\"speed\": \"%ld\", " "\"duplex\": %d}", ss->intf.intf[i].name, ss->intf.intf[i].rpack, ss->intf.intf[i].rbyte, ss->intf.intf[i].rerrs, ss->intf.intf[i].rdrop, ss->intf.intf[i].rfifo, ss->intf.intf[i].rcompr, ss->intf.intf[i].rframe, ss->intf.intf[i].rmultic, ss->intf.intf[i].spack, ss->intf.intf[i].sbyte, ss->intf.intf[i].serrs, ss->intf.intf[i].sdrop, ss->intf.intf[i].sfifo, ss->intf.intf[i].scompr, ss->intf.intf[i].scollis, ss->intf.intf[i].scarrier, ss->intf.intf[i].speed, ss->intf.intf[i].duplex); } printf("]"); } static void json_print_IFB(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *cs, int ncgroups) { register int i; printf(", %s: [", hp); for (i = 0; i < ss->ifb.nrports; i++) { if (i > 0) { printf(", "); } printf("{\"ibname\": \"%.19s\", " "\"portnr\": \"%hd\", " "\"lanes\": \"%hd\", " "\"maxrate\": %lld, " "\"rcvb\": %lld, " "\"sndb\": %lld, " "\"rcvp\": %lld, " "\"sndp\": %lld}", ss->ifb.ifb[i].ibname, ss->ifb.ifb[i].portnr, ss->ifb.ifb[i].lanes, ss->ifb.ifb[i].rate, ss->ifb.ifb[i].rcvb, ss->ifb.ifb[i].sndb, ss->ifb.ifb[i].rcvp, ss->ifb.ifb[i].sndp); } printf("]"); } static void json_print_NUM(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *cs, int ncgroups) { register int i; printf(", %s: [", hp); for (i = 0; i < ss->memnuma.nrnuma; i++) { if (i > 0) { printf(", "); } printf("{\"numanr\": \"%d\", " "\"frag\": \"%f\", " "\"totmem\": %lld, " "\"freemem\": %lld, " "\"active\": %lld, " "\"inactive\": %lld, " "\"filepage\": %lld, " "\"dirtymem\": %lld, " "\"slabmem\": %lld, " "\"slabreclaim\": %lld, " "\"shmem\": %lld, " "\"tothp\": %lld, " "\"freehp\": %lld}", ss->memnuma.numa[i].numanr, ss->memnuma.numa[i].frag * 100.0, ss->memnuma.numa[i].totmem * pagesize, ss->memnuma.numa[i].freemem * pagesize, ss->memnuma.numa[i].active * pagesize, ss->memnuma.numa[i].inactive * pagesize, ss->memnuma.numa[i].filepage * pagesize, ss->memnuma.numa[i].dirtymem * pagesize, ss->memnuma.numa[i].slabmem * pagesize, ss->memnuma.numa[i].slabreclaim * pagesize, ss->memnuma.numa[i].shmem * pagesize, ss->memnuma.numa[i].tothp * ss->mem.shugepagesz, ss->memnuma.numa[i].freehp * ss->mem.shugepagesz); } printf("]"); } static void json_print_NUC(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *cs, int ncgroups) { register int i; printf(", %s: [", hp); for (i = 0; i < ss->cpunuma.nrnuma; i++) { if (i > 0) { printf(", "); } printf("{\"numanr\": \"%d\", " "\"stime\": %lld, " "\"utime\": %lld, " "\"ntime\": %lld, " "\"itime\": %lld, " "\"wtime\": %lld, " "\"Itime\": %lld, " "\"Stime\": %lld, " "\"steal\": %lld, " "\"guest\": %lld}", ss->cpunuma.numa[i].numanr, ss->cpunuma.numa[i].stime, ss->cpunuma.numa[i].utime, ss->cpunuma.numa[i].ntime, ss->cpunuma.numa[i].itime, ss->cpunuma.numa[i].wtime, ss->cpunuma.numa[i].Itime, ss->cpunuma.numa[i].Stime, ss->cpunuma.numa[i].steal, ss->cpunuma.numa[i].guest); } printf("]"); } static void json_print_LLC(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *cs, int ncgroups) { register int i; printf(", %s: [", hp); for (i = 0; i < ss->llc.nrllcs; i++) { if (i > 0) { printf(", "); } printf("{\"LLC\": \"%3d\", " "\"occupancy\": \"%3.1f%%\", " "\"mbm_total\": \"%lld\", " "\"mbm_local\": %lld}", ss->llc.perllc[i].id, ss->llc.perllc[i].occupancy * 100, ss->llc.perllc[i].mbm_total, ss->llc.perllc[i].mbm_local); } printf("]"); } /* ** print functions for cgroups-level statistics */ static void json_print_CGR(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *cs, int ncgroups) { if ( !(supportflags & CGROUPV2) ) return; register int i, p; char *cgrpath; printf(", %s: [", hp); for (i=0; i < ncgroups; i++) { if (i > 0) { printf(", "); } cgrpath = cggetpath(cs+i, cs, 1); // print cgroup level metrics // printf( "{\"path\": \"%s\", " "\"nprocs\": %d, " "\"procsbelow\": %d, " "\"utime\": %lld, " "\"stime\": %lld, " "\"cpuweight\": %d, " "\"cpumax\": %d, " "\"cpupsisome\": %lld, " "\"cpupsitotal\": %lld, " "\"memcurrent\": %lld, " "\"memanon\": %lld, " "\"memfile\": %lld, " "\"memkernel\": %lld, " "\"memshmem\": %lld, " "\"memmax\": %lld, " "\"swpmax\": %lld, " "\"mempsisome\": %lld, " "\"mempsitotal\": %lld, " "\"diskrbytes\": %lld, " "\"diskwbytes\": %lld, " "\"diskrios\": %lld, " "\"diskwios\": %lld, " "\"diskweight\": %d, " "\"diskpsisome\": %lld, " "\"diskpsitotal\": %lld, " "\"pidlist\": [", cgrpath, (cs+i)->cstat->gen.nprocs, (cs+i)->cstat->gen.procsbelow, (cs+i)->cstat->cpu.utime, (cs+i)->cstat->cpu.stime, (cs+i)->cstat->conf.cpuweight, (cs+i)->cstat->conf.cpumax, (cs+i)->cstat->cpu.somepres, (cs+i)->cstat->cpu.fullpres, (cs+i)->cstat->mem.current > 0 ? (cs+i)->cstat->mem.current * pagesize : (cs+i)->cstat->mem.current, (cs+i)->cstat->mem.anon > 0 ? (cs+i)->cstat->mem.anon * pagesize : (cs+i)->cstat->mem.anon, (cs+i)->cstat->mem.file > 0 ? (cs+i)->cstat->mem.file * pagesize : (cs+i)->cstat->mem.file, (cs+i)->cstat->mem.kernel > 0 ? (cs+i)->cstat->mem.kernel * pagesize : (cs+i)->cstat->mem.kernel, (cs+i)->cstat->mem.shmem > 0 ? (cs+i)->cstat->mem.shmem * pagesize : (cs+i)->cstat->mem.shmem, (cs+i)->cstat->conf.memmax > 0 ? (cs+i)->cstat->conf.memmax * pagesize : (cs+i)->cstat->conf.memmax, (cs+i)->cstat->conf.swpmax > 0 ? (cs+i)->cstat->conf.swpmax * pagesize : (cs+i)->cstat->conf.swpmax, (cs+i)->cstat->mem.somepres, (cs+i)->cstat->mem.fullpres, (cs+i)->cstat->dsk.rbytes, (cs+i)->cstat->dsk.wbytes, (cs+i)->cstat->dsk.rios, (cs+i)->cstat->dsk.wios, (cs+i)->cstat->conf.dskweight, (cs+i)->cstat->dsk.somepres, (cs+i)->cstat->dsk.fullpres); free(cgrpath); // generate related pidlist // if ((cs+i)->cstat->gen.nprocs) { for (p=0; p < (cs+i)->cstat->gen.nprocs; p++) { if (p > 0) printf(", "); printf("%d", (cs+i)->proclist[p]); } } printf("]}"); } printf("]"); } /* ** print functions for process-level statistics */ static void json_print_PRG(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *cs, int ncgroups) { register int i, exitcode; static char st[3]; char *cgrpath; printf(", %s: [", hp); for (i = 0; i < nact; i++, ps++) { /* For one thread whose pid==tgid and isproc=n, it has the same value with pid==tgid and isproc=y, thus filter it out. */ if (ps->gen.tgid == ps->gen.pid && !ps->gen.isproc) continue; if (ps->gen.excode & ~(INT_MAX)) st[0]='N'; else st[0]='-'; if (ps->gen.excode & 0xff) // killed by signal? { exitcode = (ps->gen.excode & 0x7f) + 256; if (ps->gen.excode & 0x80) st[1] = 'C'; else st[1] = 'S'; } else { exitcode = (ps->gen.excode >> 8) & 0xff; st[1] = 'E'; } if (i > 0) { printf(", "); } if (supportflags & CGROUPV2 && ps->gen.cgroupix != -1) cgrpath = cggetpath((cs + ps->gen.cgroupix), cs, 1); else cgrpath = "-"; /* ** using getpwuid() & getpwuid to convert ruid & euid to string ** seems better, but the two functions take a long time */ printf("{\"pid\": %d, " "\"cmd\": \"%.19s\", " "\"state\": \"%c\", " "\"ruid\": %d, " "\"rgid\": %d, " "\"tgid\": %d, " "\"nthr\": %d, " "\"st\": \"%s\", " "\"exitcode\": %d, " "\"btime\": \"%ld\", " "\"cmdline\": \"(%.130s)\", " "\"ppid\": %d, " "\"nthrrun\": %d, " "\"nthrslpi\": %d, " "\"nthrslpu\": %d, " "\"nthridle\": %d, " "\"euid\": %d, " "\"egid\": %d, " "\"elaps\": \"%ld\", " "\"isproc\": %d, " "\"cid\": \"%.19s\", " "\"cgroup\": \"%s\"}", ps->gen.pid, ps->gen.name, ps->gen.state, ps->gen.ruid, ps->gen.rgid, ps->gen.tgid, ps->gen.nthr, st, exitcode, ps->gen.btime, ps->gen.cmdline, ps->gen.ppid, ps->gen.nthrrun, ps->gen.nthrslpi, ps->gen.nthrslpu, ps->gen.nthridle, ps->gen.euid, ps->gen.egid, ps->gen.elaps, !!ps->gen.isproc, /* convert to boolean */ ps->gen.utsname[0] ? ps->gen.utsname:"-", cgrpath); if (supportflags & CGROUPV2 && ps->gen.cgroupix != -1) free(cgrpath); } printf("]"); } static void json_print_PRC(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *cs, int ncgroups) { register int i; char *cgrpath; printf(", %s: [", hp); for (i = 0; i < nact; i++, ps++) { if (ps->gen.tgid == ps->gen.pid && !ps->gen.isproc) continue; if (i > 0) { printf(", "); } if (supportflags & CGROUPV2 && ps->gen.cgroupix != -1) cgrpath = cggetpath((cs + ps->gen.cgroupix), cs, 1); else cgrpath = "-"; printf("{\"pid\": %d, " "\"cmd\": \"%.19s\", " "\"utime\": %lld, " "\"stime\": %lld, " "\"nice\": %d, " "\"prio\": %d, " "\"curcpu\": %d, " "\"tgid\": %d, " "\"isproc\": %d, " "\"rundelay\": %lld, " "\"blkdelay\": %lld, " "\"nvcsw\": %llu, " "\"nivcsw\": %llu, " "\"sleepavg\": %d, " "\"cgroup\": \"%s\"}", ps->gen.pid, ps->gen.name, ps->cpu.utime, ps->cpu.stime, ps->cpu.nice, ps->cpu.prio, ps->cpu.curcpu, ps->gen.tgid, !!ps->gen.isproc, ps->cpu.rundelay/1000000, ps->cpu.blkdelay*1000/hertz, ps->cpu.nvcsw, ps->cpu.nivcsw, ps->cpu.sleepavg, cgrpath); if (supportflags & CGROUPV2 && ps->gen.cgroupix != -1) free(cgrpath); } printf("]"); } static void json_print_PRM(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *cs, int ncgroups) { register int i; char *cgrpath; printf(", %s: [", hp); for (i = 0; i < nact; i++, ps++) { if (ps->gen.tgid == ps->gen.pid && !ps->gen.isproc) continue; if (i > 0) { printf(", "); } if (supportflags & CGROUPV2 && ps->gen.cgroupix != -1) cgrpath = cggetpath((cs + ps->gen.cgroupix), cs, 1); else cgrpath = "-"; printf("{\"pid\": %d, " "\"cmd\": \"%.19s\", " "\"vmem\": %lld, " "\"rmem\": %lld, " "\"vexec\": %lld, " "\"vgrow\": %lld, " "\"rgrow\": %lld, " "\"minflt\": %lld, " "\"majflt\": %lld, " "\"vlibs\": %lld, " "\"vdata\": %lld, " "\"vstack\": %lld, " "\"vlock\": %lld, " "\"vswap\": %lld, " "\"pmem\": %lld, " "\"cgroup\": \"%s\"}", ps->gen.pid, ps->gen.name, ps->mem.vmem, ps->mem.rmem, ps->mem.vexec, ps->mem.vgrow, ps->mem.rgrow, ps->mem.minflt, ps->mem.majflt, ps->mem.vlibs, ps->mem.vdata, ps->mem.vstack, ps->mem.vlock, ps->mem.vswap, ps->mem.pmem == (unsigned long long)-1LL ? 0:ps->mem.pmem, cgrpath); if (supportflags & CGROUPV2 && ps->gen.cgroupix != -1) free(cgrpath); } printf("]"); } static void json_print_PRD(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *cs, int ncgroups) { register int i; printf(", %s: [", hp); for (i = 0; i < nact; i++, ps++) { if (ps->gen.tgid == ps->gen.pid && !ps->gen.isproc) continue; if (i > 0) { printf(", "); } printf("{\"pid\": %d, " "\"cmd\": \"%.19s\", " "\"rio\": %lld, " "\"rsz\": %lld, " "\"wio\": %lld, " "\"wsz\": %lld, " "\"cwsz\": %lld}", ps->gen.pid, ps->gen.name, ps->dsk.rio, ps->dsk.rsz, ps->dsk.wio, ps->dsk.wsz, ps->dsk.cwsz); } printf("]"); } static void json_print_PRN(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *cs, int ncgroups) { if (!(supportflags & NETATOP)) return; register int i; printf(", %s: [", hp); for (i = 0; i < nact; i++, ps++) { if (ps->gen.tgid == ps->gen.pid && !ps->gen.isproc) continue; if (i > 0) { printf(", "); } printf("{\"pid\": %d, " "\"cmd\": \"%.19s\", " "\"tcpsnd\": \"%lld\", " "\"tcpssz\": \"%lld\", " "\"tcprcv\": \"%lld\", " "\"tcprsz\": \"%lld\", " "\"udpsnd\": \"%lld\", " "\"udpssz\": \"%lld\", " "\"udprcv\": \"%lld\", " "\"udprsz\": \"%lld\"}", ps->gen.pid, ps->gen.name, ps->net.tcpsnd, ps->net.tcpssz, ps->net.tcprcv, ps->net.tcprsz, ps->net.udpsnd, ps->net.udpssz, ps->net.udprcv, ps->net.udprsz); } printf("]"); } static void json_print_PRE(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *cs, int ncgroups) { if ( !(supportflags & GPUSTAT) ) return; register int i; printf(", %s: [", hp); for (i = 0; i < nact; i++, ps++) { if (ps->gen.tgid == ps->gen.pid && !ps->gen.isproc) continue; if (i > 0) { printf(", "); } printf("{\"pid\": %d, " "\"cmd\": \"%.19s\", " "\"gpustate\": \"%c\", " "\"nrgpus\": %d, " "\"gpulist\": \"%x\", " "\"gpubusy\": %d, " "\"membusy\": %d, " "\"memnow\": %lld, " "\"memcum\": %lld, " "\"sample\": %lld}", ps->gen.pid, ps->gen.name, ps->gpu.state == '\0' ? 'N':ps->gpu.state, ps->gpu.nrgpus, ps->gpu.gpulist, ps->gpu.gpubusy, ps->gpu.membusy, ps->gpu.memnow, ps->gpu.memcum, ps->gpu.sample); } printf("]"); } atop-2.12.1/json.h0000644000203100020310000000267315064552761013205 0ustar gerlofgerlof/* ** ATOP - System & Process Monitor ** ** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as process-level. ** ========================================================================== ** Author: Fei Li & zhenwei pi ** E-mail: lifei.shirley@bytedance.com, pizhenwei@bytedance.com ** Date: August 2019 ** -------------------------------------------------------------------------- ** Copyright (C) Copyright (C) 2019-2022 bytedance.com ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ** -------------------------------------------------------------------------- */ int jsondef(char *); char jsonout(time_t, int, struct devtstat *, struct sstat *, struct cgchainer *, int, int, int, unsigned int, char); atop-2.12.1/netatopbpfif.c0000644000203100020310000001110015064552761014671 0ustar gerlofgerlof/* ** ATOP - System & Process Monitor ** ** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as process-level. ** ** This source-file contains functions to interface with the netatop-bpf ** in the kernel. This keeps track of network activity per process ** and thread, and exited process. ** ================================================================ ** Author: Ting Liu ** E-mail: liuting.0xffff@bytedance.com ** Date: 2023 ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "atop.h" #include "photoproc.h" #include "netatop.h" static int netsock = -1; static void fill_networkcnt(struct tstat *, struct tstat *, struct taskcount *); void my_handler(int); int len; struct sockaddr_un un; /* ** create a UNIX domain stream socket and ** connect the netatop-bpf userspace program */ void netatop_bpf_ipopen(void) { char *name = NETATOPBPF_SOCKET; /* create a UNIX domain stream socket */ if((netsock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) return; /* fill socket address structure with server's address */ memset(&un, 0, sizeof(un)); un.sun_family = AF_UNIX; strcpy(un.sun_path, name); len = offsetof(struct sockaddr_un, sun_path) + strlen(name); if(connect(netsock, (struct sockaddr *)&un, len) < 0) { close(netsock); return; } supportflags |= NETATOPBPF; return; } /* ** check if at this moment the netatop-bpf is loaded */ void netatop_bpf_probe(void) { /* ** check if netsock is not connect. ** if not, try to connect again. */ if (!(supportflags & NETATOPBPF)) { char *name = NETATOPBPF_SOCKET; if (!access(name,F_OK)) { netatop_bpf_ipopen(); } } } void my_handler (int param) { supportflags &= ~NETATOPBPF; close(netsock); netsock = -1; } /* the NET hash function */ GHashTable *ghash_net; void netatop_bpf_gettask() { int ret; struct netpertask npt; ghash_net = g_hash_table_new_full(g_int_hash, g_int_equal, g_free, g_free); /* ** get statistics of this process/thread */ signal(SIGPIPE, my_handler); if (send(netsock, &npt, sizeof(npt), 0) < 0) { supportflags &= ~NETATOPBPF; close(netsock); netsock = -1; return; } while ((ret = recv(netsock, &npt, sizeof(npt), 0)) > 0) { if (npt.id == 0) { break; } gint *key = g_new(gint, 1); *key = npt.id; struct taskcount *value = g_new(struct taskcount, 1); *value = npt.tc; g_hash_table_insert(ghash_net, key, value); } if (ret <= 0) { supportflags &= ~NETATOPBPF; close(netsock); netsock = -1; return ; } } /* ** search for relevant exited network task and ** update counters in tstat struct */ void netatop_bpf_exitfind(unsigned long key, struct tstat *dev, struct tstat *pre) { struct taskcount *tc = g_hash_table_lookup(ghash_net, &key); /* ** correct PID found */ if (tc) { if (tc->tcpsndpacks < pre->net.tcpsnd || tc->tcpsndbytes < pre->net.tcpssz || tc->tcprcvpacks < pre->net.tcprcv || tc->tcprcvbytes < pre->net.tcprsz || tc->udpsndpacks < pre->net.udpsnd || tc->udpsndbytes < pre->net.udpssz || tc->udprcvpacks < pre->net.udprcv || tc->udprcvbytes < pre->net.udprsz ) return; fill_networkcnt(dev, pre, tc); } } static void fill_networkcnt(struct tstat *dev, struct tstat *pre, struct taskcount *tc) { dev->net.tcpsnd = tc->tcpsndpacks - pre->net.tcpsnd; dev->net.tcpssz = tc->tcpsndbytes - pre->net.tcpssz; dev->net.tcprcv = tc->tcprcvpacks - pre->net.tcprcv; dev->net.tcprsz = tc->tcprcvbytes - pre->net.tcprsz; dev->net.udpsnd = tc->udpsndpacks - pre->net.udpsnd; dev->net.udpssz = tc->udpsndbytes - pre->net.udpssz; dev->net.udprcv = tc->udprcvpacks - pre->net.udprcv; dev->net.udprsz = tc->udprcvbytes - pre->net.udprsz; } atop-2.12.1/netatopd.h0000644000203100020310000000330415064552761014042 0ustar gerlofgerlof/* ** ATOP - System & Process Monitor ** ** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as process-level. ** ========================================================================== ** Author: Gerlof Langeveld ** E-mail: gerlof.langeveld@atoptool.nl ** Date: September 2002 ** -------------------------------------------------------------------------- ** Copyright (C) 2000-2010 Gerlof Langeveld ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ** -------------------------------------------------------------------------- */ #ifndef __NETATOPD__ #define __NETATOPD__ #define SEMAKEY 1541961 #define NETEXITFILE "/run/netatop.log" #define MYMAGIC (unsigned int) 0xfeedb0b0 struct naheader { u_int32_t magic; // magic number MYMAGIC u_int32_t curseq; // sequence number of last netpertask u_int16_t hdrlen; // length of this header u_int16_t ntplen; // length of netpertask structure pid_t mypid; // PID of netatopd itself }; #endif atop-2.12.1/netatop.h0000644000203100020310000000477415064552761013712 0ustar gerlofgerlof/* ** ATOP - System & Process Monitor ** ** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as process-level. ** ========================================================================== ** Author: Gerlof Langeveld ** E-mail: gerlof.langeveld@atoptool.nl ** Date: September 2002 ** -------------------------------------------------------------------------- ** Copyright (C) 2000-2010 Gerlof Langeveld ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ** -------------------------------------------------------------------------- */ #ifndef __NETATOP__ #define __NETATOP__ #define COMLEN 16 struct taskcount { unsigned long long tcpsndpacks; unsigned long long tcpsndbytes; unsigned long long tcprcvpacks; unsigned long long tcprcvbytes; unsigned long long udpsndpacks; unsigned long long udpsndbytes; unsigned long long udprcvpacks; unsigned long long udprcvbytes; /* space for future extensions */ }; struct netpertask { pid_t id; // tgid or tid (depending on command) unsigned long btime; char command[COMLEN]; struct taskcount tc; }; /* ** getsocktop commands */ #define NETATOP_BASE_CTL 15661 // just probe if the netatop module is active #define NETATOP_PROBE (NETATOP_BASE_CTL) // force garbage collection to make finished processes available #define NETATOP_FORCE_GC (NETATOP_BASE_CTL+1) // wait until all finished processes are read (blocks until done) #define NETATOP_EMPTY_EXIT (NETATOP_BASE_CTL+2) // get info for finished process (blocks until available) #define NETATOP_GETCNT_EXIT (NETATOP_BASE_CTL+3) // get counters for thread group (i.e. process): input is 'id' (pid) #define NETATOP_GETCNT_TGID (NETATOP_BASE_CTL+4) // get counters for thread: input is 'id' (tid) #define NETATOP_GETCNT_PID (NETATOP_BASE_CTL+5) #define NETATOPBPF_SOCKET "/run/netatop-bpf-socket" #endif atop-2.12.1/netatopif.c0000644000203100020310000002676315064552761014226 0ustar gerlofgerlof/* ** ATOP - System & Process Monitor ** ** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as process-level. ** ** This source-file contains functions to interface with the netatop ** module in the kernel. That module keeps track of network activity ** per process and thread. ** ================================================================ ** Author: Gerlof Langeveld ** E-mail: gerlof.langeveld@atoptool.nl ** Date: August/September 2012 ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. */ #define _POSIX_C_SOURCE #define _XOPEN_SOURCE #define _GNU_SOURCE #define _DEFAULT_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "atop.h" #include "photoproc.h" #include "netatop.h" #include "netatopd.h" static int netsock = -1; static int netexitfd = -1; static struct naheader *nahp; static int semid = -1; static unsigned long lastseq; /* ** storage of last exited tasks read from exitfile ** every exitstore struct is registered in hash buckets, ** by its pid or by its begin time */ struct exitstore { struct exitstore *next; unsigned char isused; struct netpertask npt; }; #define NHASH 1024 // must be power of two! #define HASHCALC(x) ((x)&(NHASH-1)) static struct exitstore *esbucket[NHASH]; static struct exitstore *exitall; static int exitnum; static char exithash; static void fill_networkcnt(struct tstat *, struct tstat *, struct exitstore *); /* ** open a raw socket to the IP layer (root privs required) */ void netatop_ipopen(void) { netsock = socket(PF_INET, SOCK_RAW, IPPROTO_RAW); } /* ** check if at this moment the netatop kernel module is loaded and ** the netatopd daemon is active */ void netatop_probe(void) { struct sembuf semdecr = {1, -1, SEM_UNDO}; socklen_t sl = 0; struct stat exstat; /* ** check if IP socket is open */ if (netsock == -1) return; /* ** probe if the netatop module is active */ if ( getsockopt(netsock, SOL_IP, NETATOP_PROBE, NULL, &sl) != 0) { supportflags &= ~NETATOP; supportflags &= ~NETATOPD; return; } // set appropriate support flag supportflags |= NETATOP; /* ** check if the netatopd daemon is active to register exited tasks ** and decrement semaphore to indicate that we want to subscribe */ if (semid == -1) { if ( (semid = semget(SEMAKEY, 0, 0)) == -1 || semop(semid, &semdecr, 1) == -1 ) { supportflags &= ~NETATOPD; return; } } if (semctl(semid, 0, GETVAL, 0) != 1) { supportflags &= ~NETATOPD; return; } /* ** check if exitfile still open and not removed by netatopd */ if (netexitfd != -1) { if ( fstat(netexitfd, &exstat) == 0 && exstat.st_nlink > 0 ) // not removed { supportflags |= NETATOPD; return; } else { (void) close(netexitfd); if (nahp) munmap(nahp, sizeof *nahp); netexitfd = -1; nahp = NULL; } } /* ** open file with compressed stats of exited tasks ** and (re)mmap the start record, mainly to obtain current sequence */ if (netexitfd == -1) { if ( (netexitfd = open(NETEXITFILE, O_RDONLY, 0)) == -1) { supportflags &= ~NETATOPD; return; } } if ( (nahp = mmap((void *)0, sizeof *nahp, PROT_READ, MAP_SHARED, netexitfd, 0)) == (void *) -1) { (void) close(netexitfd); netexitfd = -1; nahp = NULL; supportflags &= ~NETATOPD; return; } /* ** if this is a new incarnation of the netatopd daemon, ** position seek pointer on first task that is relevant to us ** and remember last sequence number to know where to start */ (void) lseek(netexitfd, 0, SEEK_END); lastseq = nahp->curseq; // set appropriate support flag supportflags |= NETATOPD; } void netatop_signoff(void) { struct sembuf semincr = {1, +1, SEM_UNDO}; if (netsock == -1 || nahp == NULL) return; if (supportflags & NETATOPD) { regainrootprivs(); (void) semop(semid, &semincr, 1); kill(nahp->mypid, SIGHUP); if (! droprootprivs()) mcleanstop(42, "failed to drop root privs\n"); (void) munmap(nahp, sizeof *nahp); (void) close(netexitfd); } } /* ** read network counters for one existing task ** (type 'g' for thread group or type 't' for thread) */ void netatop_gettask(pid_t id, char type, struct tstat *tp) { struct netpertask npt; socklen_t socklen = sizeof npt; int cmd = (type == 'g' ? NETATOP_GETCNT_TGID : NETATOP_GETCNT_PID); /* ** if kernel module netatop not active on this system, skip call */ if (!(supportflags & NETATOP) ) { memset(&tp->net, 0, sizeof tp->net); return; } /* ** get statistics of this process/thread */ npt.id = id; regainrootprivs(); if (getsockopt(netsock, SOL_IP, cmd, &npt, &socklen) != 0) { memset(&tp->net, 0, sizeof tp->net); if (! droprootprivs()) mcleanstop(42, "failed to drop root privs\n"); if (errno == ENOPROTOOPT || errno == EPERM) { supportflags &= ~NETATOP; supportflags &= ~NETATOPD; close(netsock); netsock = -1; } return; } if (! droprootprivs()) mcleanstop(42, "failed to drop root privs\n"); /* ** statistics available: fill counters */ tp->net.tcpsnd = npt.tc.tcpsndpacks; tp->net.tcprcv = npt.tc.tcprcvpacks; tp->net.tcpssz = npt.tc.tcpsndbytes; tp->net.tcprsz = npt.tc.tcprcvbytes; tp->net.udpsnd = npt.tc.udpsndpacks; tp->net.udprcv = npt.tc.udprcvpacks; tp->net.udpssz = npt.tc.udpsndbytes; tp->net.udprsz = npt.tc.udprcvbytes; } /* ** read all exited processes that have been added to the exitfile ** and store them into memory */ unsigned int netatop_exitstore(void) { socklen_t socklen = 0, nexitnet, sz, nr=0; unsigned long uncomplen; unsigned char nextsize; unsigned char readbuf[nahp->ntplen+100]; unsigned char databuf[nahp->ntplen]; struct netpertask *tmp = (struct netpertask *)databuf; struct exitstore *esp; regainrootprivs(); /* ** force garbage collection: ** netatop module builds new list of exited processes that ** can be read by netatopd and written to exitfile */ if (getsockopt(netsock, SOL_IP, NETATOP_FORCE_GC, NULL, &socklen)!=0) { if (! droprootprivs()) mcleanstop(42, "failed to drop root privs\n"); if (errno == ENOPROTOOPT || errno == EPERM) { supportflags &= ~NETATOP; supportflags &= ~NETATOPD; close(netsock); netsock = -1; } return 0; } /* ** wait until list of exited processes is read by netatopd ** and available to be read by atop */ if (getsockopt(netsock, SOL_IP, NETATOP_EMPTY_EXIT, 0, &socklen) !=0) { if (! droprootprivs()) mcleanstop(42, "failed to drop root privs\n"); if (errno == ENOPROTOOPT || errno == EPERM) { supportflags &= ~NETATOP; supportflags &= ~NETATOPD; close(netsock); netsock = -1; } return 0; } if (! droprootprivs()) mcleanstop(42, "failed to drop root privs\n"); /* ** verify how many exited processes are available to be read ** from the exitfile */ nexitnet = nahp->curseq - lastseq; lastseq = nahp->curseq; if (nexitnet == 0) return 0; /* ** allocate storage for all exited processes */ exitall = malloc(nexitnet * sizeof(struct exitstore)); ptrverify(exitall, "Malloc failed for %d exited netprocs\n", nexitnet); memset(exitall, 0, nexitnet * sizeof(struct exitstore)); esp = exitall; /* ** read next byte from exitfile that specifies the length ** of the next record */ if ( read(netexitfd, &nextsize, 1) != 1) return 0; /* ** read the next record and (if possible) the byte specifying ** the size of the next record */ while ( (sz = read(netexitfd, readbuf, nextsize+1)) >= nextsize) { /* ** decompress record and store it */ uncomplen = nahp->ntplen; if (nahp->ntplen <= sizeof(struct netpertask)) { (void) uncompress((Byte *)&(esp->npt), &uncomplen, readbuf, nextsize); } else { (void) uncompress((Byte *)databuf, &uncomplen, readbuf, nextsize); esp->npt = *tmp; } esp++; nr++; /* ** check if we have read all records */ if (nr == nexitnet) { /* ** if we have read one byte too many: ** reposition seek pointer */ if (sz > nextsize) (void) lseek(netexitfd, -1, SEEK_CUR); break; } /* ** prepare reading next record */ if (sz > nextsize) nextsize = readbuf[nextsize]; else break; // unexpected: more requested than available } exitnum = nr; return nr; } /* ** remove all stored exited processes from the hash bucket list */ void netatop_exiterase(void) { free(exitall); memset(esbucket, 0, sizeof esbucket); exitnum = 0; } /* ** add all stored tasks to a hash bucket, either ** by pid (argument 'p') or by begin time (argument 'b') */ void netatop_exithash(char hashtype) { int i, h; struct exitstore *esp; for (i=0, esp=exitall; i < exitnum; i++, esp++) { if (hashtype == 'p') h = HASHCALC(esp->npt.id); else h = HASHCALC(esp->npt.btime); esp->next = esbucket[h]; esbucket[h] = esp; } exithash = hashtype; } /* ** search for relevant exited network task and ** update counters in tstat struct */ void netatop_exitfind(unsigned long key, struct tstat *dev, struct tstat *pre) { int h = HASHCALC(key); struct exitstore *esp; /* ** if bucket empty, forget about it */ if ( (esp = esbucket[h]) == NULL) return; /* ** search thru hash bucket list */ for (; esp; esp=esp->next) { switch (exithash) { case 'p': // search by PID if (key != esp->npt.id) continue; /* ** correct PID found */ fill_networkcnt(dev, pre, esp); break; case 'b': // search by begin time if (esp->isused) continue; if (key != esp->npt.btime) continue; /* ** btime is okay; additional checks required */ if ( strcmp(esp->npt.command, pre->gen.name) != 0) continue; if (esp->npt.tc.tcpsndpacks < pre->net.tcpsnd || esp->npt.tc.tcpsndbytes < pre->net.tcpssz || esp->npt.tc.tcprcvpacks < pre->net.tcprcv || esp->npt.tc.tcprcvbytes < pre->net.tcprsz || esp->npt.tc.udpsndpacks < pre->net.udpsnd || esp->npt.tc.udpsndbytes < pre->net.udpssz || esp->npt.tc.udprcvpacks < pre->net.udprcv || esp->npt.tc.udprcvbytes < pre->net.udprsz ) continue; esp->isused = 1; fill_networkcnt(dev, pre, esp); break; } } } static void fill_networkcnt(struct tstat *dev, struct tstat *pre, struct exitstore *esp) { dev->net.tcpsnd = esp->npt.tc.tcpsndpacks - pre->net.tcpsnd; dev->net.tcpssz = esp->npt.tc.tcpsndbytes - pre->net.tcpssz; dev->net.tcprcv = esp->npt.tc.tcprcvpacks - pre->net.tcprcv; dev->net.tcprsz = esp->npt.tc.tcprcvbytes - pre->net.tcprsz; dev->net.udpsnd = esp->npt.tc.udpsndpacks - pre->net.udpsnd; dev->net.udpssz = esp->npt.tc.udpsndbytes - pre->net.udpssz; dev->net.udprcv = esp->npt.tc.udprcvpacks - pre->net.udprcv; dev->net.udprsz = esp->npt.tc.udprcvbytes - pre->net.udprsz; } atop-2.12.1/netlink.c0000644000203100020310000001345415064552761013672 0ustar gerlofgerlof/* ** Copyright (C) 2014 Gerlof Langeveld ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ** -------------------------------------------------------------------------- */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "atop.h" /* ** generic macro's */ #define GENLMSG_DATA(glh) ((void *)(NLMSG_DATA(glh) + GENL_HDRLEN)) #define GENLMSG_PAYLOAD(glh) (NLMSG_PAYLOAD(glh, 0) - GENL_HDRLEN) #define NLA_DATA(na) ((void *)((char*)(na) + NLA_HDRLEN)) #define NLA_PAYLOAD(len) (len - NLA_HDRLEN) /* ** function prototypes */ static int nlsock_open(void); static int nlsock_sendcmd(int, __u16, __u32, __u8, __u16, void *, int); static int nlsock_getfam(int); static int getnumcpu(void); /* ** message format to communicate with NETLINK */ struct msgtemplate { struct nlmsghdr n; struct genlmsghdr g; char buf[2048]; }; int netlink_open(void) { int nlsock, famid; char cpudef[64]; /* ** open the netlink socket */ nlsock = nlsock_open(); /* ** get the family id for the TASKSTATS family */ famid = nlsock_getfam(nlsock); /* ** determine maximum number of CPU's for this system ** and specify mask to register all cpu's */ snprintf(cpudef, sizeof cpudef, "0-%d", getnumcpu() -1); /* ** indicate to listen for processes from all CPU's */ if (nlsock_sendcmd(nlsock, famid, getpid(), TASKSTATS_CMD_GET, TASKSTATS_CMD_ATTR_REGISTER_CPUMASK, &cpudef, strlen(cpudef)+1) == -1) { fprintf(stderr, "register cpumask failed\n"); return -1; } return nlsock; } int netlink_recv(int nlsock, int flags) { int len; struct msgtemplate msg; if ( (len = recv(nlsock, &msg, sizeof msg, flags)) == -1) return -errno; // negative: errno if (msg.n.nlmsg_type == NLMSG_ERROR || !NLMSG_OK(&msg.n, len)) { struct nlmsgerr *err = NLMSG_DATA(&msg); return err->error; // negative: errno } return len; // 0 or positive value } static int nlsock_getfam(int nlsock) { int len; struct nlattr *nlattr; struct msgtemplate msg; nlsock_sendcmd(nlsock, GENL_ID_CTRL, getpid(), CTRL_CMD_GETFAMILY, CTRL_ATTR_FAMILY_NAME, TASKSTATS_GENL_NAME, sizeof TASKSTATS_GENL_NAME); if ( (len = recv(nlsock, &msg, sizeof msg, 0)) == -1) { perror("receive NETLINK family"); exit(1); } if (msg.n.nlmsg_type == NLMSG_ERROR || !NLMSG_OK(&msg.n, len)) { struct nlmsgerr *err = NLMSG_DATA(&msg); fprintf(stderr, "receive NETLINK family, errno %d\n", err->error); exit(1); } nlattr = (struct nlattr *) GENLMSG_DATA(&msg); nlattr = (struct nlattr *) ((char *) nlattr + NLA_ALIGN(nlattr->nla_len)); if (nlattr->nla_type != CTRL_ATTR_FAMILY_ID) { fprintf(stderr, "unexpected family id\n"); exit(1); } return *(__u16 *) NLA_DATA(nlattr); } static int nlsock_open(void) { int nlsock, rcvsz = 256*1024; struct sockaddr_nl nlsockaddr; if ( (nlsock = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC) ) == -1) { perror("open NETLINK socket"); exit(1); } if (setsockopt(nlsock, SOL_SOCKET, SO_RCVBUF, &rcvsz, sizeof rcvsz) == -1) { perror("set length receive buffer"); exit(1); } memset(&nlsockaddr, 0, sizeof nlsockaddr); nlsockaddr.nl_family = AF_NETLINK; if (bind(nlsock, (struct sockaddr *) &nlsockaddr, sizeof nlsockaddr) == -1) { perror("bind NETLINK socket"); close(nlsock); exit(1); } return nlsock; } static int nlsock_sendcmd(int nlsock, __u16 nlmsg_type, __u32 nlmsg_pid, __u8 genl_cmd, __u16 nla_type, void *nla_data, int nla_len) { struct nlattr *na; struct sockaddr_nl nlsockaddr; int rv, buflen; char *buf; struct msgtemplate msg; msg.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); msg.n.nlmsg_type = nlmsg_type; msg.n.nlmsg_flags = NLM_F_REQUEST; msg.n.nlmsg_seq = 0; msg.n.nlmsg_pid = nlmsg_pid; msg.g.cmd = genl_cmd; msg.g.version = 0x1; na = (struct nlattr *) GENLMSG_DATA(&msg); na->nla_type = nla_type; na->nla_len = nla_len + 1 + NLA_HDRLEN; memcpy(NLA_DATA(na), nla_data, nla_len); msg.n.nlmsg_len += NLMSG_ALIGN(na->nla_len); buf = (char *) &msg; buflen = msg.n.nlmsg_len ; memset(&nlsockaddr, 0, sizeof(nlsockaddr)); nlsockaddr.nl_family = AF_NETLINK; while ((rv = sendto(nlsock, buf, buflen, 0, (struct sockaddr *) &nlsockaddr, sizeof(nlsockaddr))) < buflen) { if (rv == -1) { if (errno != EAGAIN) { perror("sendto NETLINK"); return -1; } } else { buf += rv; buflen -= rv; } } return 0; } static int getnumcpu(void) { FILE *fp; char linebuf[4096], label[256]; int cpunum, maxcpu = 0; if ( (fp = fopen("/proc/stat", "r")) != NULL) { while ( fgets(linebuf, sizeof(linebuf), fp) != NULL) { sscanf(linebuf, "%255s", label); if ( strncmp("cpu", label, 3) == 0 && strlen(label) >3) { cpunum = atoi(&label[3]); if (maxcpu < cpunum) maxcpu = cpunum; } if ( strncmp("int", label, 3) == 0) break; } fclose(fp); } return maxcpu+1; } atop-2.12.1/netstats.h0000644000203100020310000001061015064552761014067 0ustar gerlofgerlof/* ** ATOP - System & Process Monitor ** ** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as process-level. ** ========================================================================== ** Author: Gerlof Langeveld ** E-mail: gerlof.langeveld@atoptool.nl ** Date: September 2002 ** -------------------------------------------------------------------------- ** Copyright (C) 2000-2010 Gerlof Langeveld ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ** -------------------------------------------------------------------------- */ #ifndef __NETSTATS__ #define __NETSTATS__ /* ** structures defined from the output of /proc/net/snmp and /proc/net/snmp6 */ struct ipv4_stats { count_t Forwarding; count_t DefaultTTL; count_t InReceives; count_t InHdrErrors; count_t InAddrErrors; count_t ForwDatagrams; count_t InUnknownProtos; count_t InDiscards; count_t InDelivers; count_t OutRequests; count_t OutDiscards; count_t OutNoRoutes; count_t ReasmTimeout; count_t ReasmReqds; count_t ReasmOKs; count_t ReasmFails; count_t FragOKs; count_t FragFails; count_t FragCreates; }; struct icmpv4_stats { count_t InMsgs; count_t InErrors; count_t InCsumErrors; count_t InDestUnreachs; count_t InTimeExcds; count_t InParmProbs; count_t InSrcQuenchs; count_t InRedirects; count_t InEchos; count_t InEchoReps; count_t InTimestamps; count_t InTimestampReps; count_t InAddrMasks; count_t InAddrMaskReps; count_t OutMsgs; count_t OutErrors; count_t OutDestUnreachs; count_t OutTimeExcds; count_t OutParmProbs; count_t OutSrcQuenchs; count_t OutRedirects; count_t OutEchos; count_t OutEchoReps; count_t OutTimestamps; count_t OutTimestampReps; count_t OutAddrMasks; count_t OutAddrMaskReps; }; struct udpv4_stats { count_t InDatagrams; count_t NoPorts; count_t InErrors; count_t OutDatagrams; }; struct tcp_stats { count_t RtoAlgorithm; count_t RtoMin; count_t RtoMax; count_t MaxConn; count_t ActiveOpens; count_t PassiveOpens; count_t AttemptFails; count_t EstabResets; count_t CurrEstab; count_t InSegs; count_t OutSegs; count_t RetransSegs; count_t InErrs; count_t OutRsts; count_t InCsumErrors; }; struct ipv6_stats { count_t Ip6InReceives; count_t Ip6InHdrErrors; count_t Ip6InTooBigErrors; count_t Ip6InNoRoutes; count_t Ip6InAddrErrors; count_t Ip6InUnknownProtos; count_t Ip6InTruncatedPkts; count_t Ip6InDiscards; count_t Ip6InDelivers; count_t Ip6OutForwDatagrams; count_t Ip6OutRequests; count_t Ip6OutDiscards; count_t Ip6OutNoRoutes; count_t Ip6ReasmTimeout; count_t Ip6ReasmReqds; count_t Ip6ReasmOKs; count_t Ip6ReasmFails; count_t Ip6FragOKs; count_t Ip6FragFails; count_t Ip6FragCreates; count_t Ip6InMcastPkts; count_t Ip6OutMcastPkts; }; struct icmpv6_stats { count_t Icmp6InMsgs; count_t Icmp6InErrors; count_t Icmp6InDestUnreachs; count_t Icmp6InPktTooBigs; count_t Icmp6InTimeExcds; count_t Icmp6InParmProblems; count_t Icmp6InEchos; count_t Icmp6InEchoReplies; count_t Icmp6InGroupMembQueries; count_t Icmp6InGroupMembResponses; count_t Icmp6InGroupMembReductions; count_t Icmp6InRouterSolicits; count_t Icmp6InRouterAdvertisements; count_t Icmp6InNeighborSolicits; count_t Icmp6InNeighborAdvertisements; count_t Icmp6InRedirects; count_t Icmp6OutMsgs; count_t Icmp6OutDestUnreachs; count_t Icmp6OutPktTooBigs; count_t Icmp6OutTimeExcds; count_t Icmp6OutParmProblems; count_t Icmp6OutEchoReplies; count_t Icmp6OutRouterSolicits; count_t Icmp6OutNeighborSolicits; count_t Icmp6OutNeighborAdvertisements; count_t Icmp6OutRedirects; count_t Icmp6OutGroupMembResponses; count_t Icmp6OutGroupMembReductions; }; struct udpv6_stats { count_t Udp6InDatagrams; count_t Udp6NoPorts; count_t Udp6InErrors; count_t Udp6OutDatagrams; }; #endif atop-2.12.1/parseable.c0000644000203100020310000007762315064552761014174 0ustar gerlofgerlof/* ** ATOP - System & Process Monitor ** ** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as process-level. ** ** ========================================================================== ** Author: Gerlof Langeveld ** E-mail: gerlof.langeveld@atoptool.nl ** Date: February 2007 ** -------------------------------------------------------------------------- ** Copyright (C) 2007-2024 Gerlof Langeveld ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ** -------------------------------------------------------------------------- */ #include #include #include #include #include #include #include #include "atop.h" #include "photosyst.h" #include "photoproc.h" #include "cgroups.h" #include "parseable.h" void print_CPU(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); void print_cpu(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); void print_CPL(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); void print_GPU(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); void print_MEM(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); void print_SWP(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); void print_PAG(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); void print_PSI(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); void print_LVM(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); void print_MDD(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); void print_DSK(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); void print_NFM(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); void print_NFC(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); void print_NFS(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); void print_NET(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); void print_IFB(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); void print_NUM(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); void print_NUC(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); void print_LLC(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); void print_CGR(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); void print_PRG(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); void print_PRC(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); void print_PRM(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); void print_PRD(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); void print_PRN(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); void print_PRE(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); static void calc_freqscale(count_t, count_t, count_t, count_t *, int *); static char *spaceformat(char *, char *); /* ** table with possible labels and the corresponding ** print-function for parsable output */ struct labeldef { char *label; short valid; short cgroupref; void (*prifunc)(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); }; static struct labeldef labeldef[] = { { "CPU", 0, 0, print_CPU }, { "cpu", 0, 0, print_cpu }, { "CPL", 0, 0, print_CPL }, { "GPU", 0, 0, print_GPU }, { "MEM", 0, 0, print_MEM }, { "SWP", 0, 0, print_SWP }, { "PAG", 0, 0, print_PAG }, { "PSI", 0, 0, print_PSI }, { "LVM", 0, 0, print_LVM }, { "MDD", 0, 0, print_MDD }, { "DSK", 0, 0, print_DSK }, { "NFM", 0, 0, print_NFM }, { "NFC", 0, 0, print_NFC }, { "NFS", 0, 0, print_NFS }, { "NET", 0, 0, print_NET }, { "IFB", 0, 0, print_IFB }, { "NUM", 0, 0, print_NUM }, { "NUC", 0, 0, print_NUC }, { "LLC", 0, 0, print_LLC }, { "CGR", 0, 0, print_CGR }, { "PRG", 0, 1, print_PRG }, { "PRC", 0, 1, print_PRC }, { "PRM", 0, 1, print_PRM }, { "PRD", 0, 0, print_PRD }, { "PRN", 0, 0, print_PRN }, { "PRE", 0, 0, print_PRE }, }; static int numlabels = sizeof labeldef/sizeof(struct labeldef); /* ** analyse the parse-definition string that has been ** passed as argument with the flag -P */ int parsedef(char *pd) { register int i; char *p, *ep = pd + strlen(pd); /* ** check if string passed bahind -P is not another flag */ if (*pd == '-') { fprintf(stderr, "flag -P should be followed by label list\n"); return 0; } /* ** check list of comma-separated labels */ while (pd < ep) { /* ** exchange comma by null-byte */ if ( (p = strchr(pd, ',')) ) *p = 0; else p = ep-1; /* ** check if the next label exists */ for (i=0; i < numlabels; i++) { if ( strcmp(labeldef[i].label, pd) == 0) { labeldef[i].valid = 1; break; } } /* ** non-existing label has been used */ if (i == numlabels) { /* ** check if special label 'ALL' has been used */ if ( strcmp("ALL", pd) == 0) { for (i=0; i < numlabels; i++) labeldef[i].valid = 1; break; } else { fprintf(stderr, "label %s not found\n", pd); return 0; } } pd = p+1; } setbuf(stdout, (char *)0); return 1; } /* ** produce parsable output for an interval */ char parseout(time_t curtime, int numsecs, struct devtstat *devtstat, struct sstat *sstat, struct cgchainer *devchain, int ncgroups, int npids, int nexit, unsigned int noverflow, char flag) { register int i, cgroupref_created = 0; char datestr[32], timestr[32], header[256]; /* ** print reset-label for sample-values since boot */ if (flag&RRBOOT) printf("RESET\n"); /* ** search all labels which are selected before */ for (i=0; i < numlabels; i++) { if (labeldef[i].valid) { /* ** when cgroup index is needed to map the tstat to a cgroup, ** once fill the tstat.gen.cgroupix variables */ if (supportflags & CGROUPV2 && labeldef[i].cgroupref && !cgroupref_created) { cgfillref(devtstat, devchain, ncgroups, npids); cgroupref_created = 1; } /* ** prepare generic columns */ convdate(curtime, datestr); convtime(curtime, timestr); snprintf(header, sizeof header, "%s %s %lld %s %s %d", labeldef[i].label, utsname.nodename, (long long)curtime, datestr, timestr, numsecs); /* ** call a selected print function */ (labeldef[i].prifunc)(header, sstat, devtstat->taskall, devtstat->ntaskall, devchain, ncgroups); } } /* ** print separator */ printf("SEP\n"); return '\0'; } /* ** print functions for system-level statistics */ static void calc_freqscale(count_t maxfreq, count_t cnt, count_t ticks, count_t *freq, int *freqperc) { // if ticks != 0, do full calcs if (maxfreq && ticks) { *freq=cnt/ticks; *freqperc=100* *freq / maxfreq; } else if (maxfreq) // max frequency is known so % can be calculated { *freq=cnt; *freqperc=100*cnt/maxfreq; } else if (cnt) // no max known, set % to 100 { *freq=cnt; *freqperc=100; } else // nothing is known: set freq to 0, % to 100 { *freq=0; *freqperc=100; } } void print_CPU(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *devchain, int ncgroups) { count_t maxfreq=0; count_t cnt=0; count_t ticks=0; count_t freq; int freqperc; int i; // calculate average clock frequency for (i=0; i < ss->cpu.nrcpu; i++) { cnt += ss->cpu.cpu[i].freqcnt.cnt; ticks += ss->cpu.cpu[i].freqcnt.ticks; } maxfreq = ss->cpu.cpu[0].freqcnt.maxfreq; calc_freqscale(maxfreq, cnt, ticks, &freq, &freqperc); if (ss->cpu.all.instr == 1) { ss->cpu.all.instr = 0; ss->cpu.all.cycle = 0; } printf("%s %u %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %d %lld %lld\n", hp, hertz, ss->cpu.nrcpu, ss->cpu.all.stime, ss->cpu.all.utime, ss->cpu.all.ntime, ss->cpu.all.itime, ss->cpu.all.wtime, ss->cpu.all.Itime, ss->cpu.all.Stime, ss->cpu.all.steal, ss->cpu.all.guest, freq, freqperc, ss->cpu.all.instr, ss->cpu.all.cycle ); } void print_cpu(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *devchain, int ncgroups) { register int i; count_t maxfreq=0; count_t cnt=0; count_t ticks=0; count_t freq; int freqperc; for (i=0; i < ss->cpu.nrcpu; i++) { cnt = ss->cpu.cpu[i].freqcnt.cnt; ticks = ss->cpu.cpu[i].freqcnt.ticks; maxfreq= ss->cpu.cpu[0].freqcnt.maxfreq; calc_freqscale(maxfreq, cnt, ticks, &freq, &freqperc); printf("%s %u %d %lld %lld %lld " "%lld %lld %lld %lld %lld %lld %lld %d %lld %lld\n", hp, hertz, i, ss->cpu.cpu[i].stime, ss->cpu.cpu[i].utime, ss->cpu.cpu[i].ntime, ss->cpu.cpu[i].itime, ss->cpu.cpu[i].wtime, ss->cpu.cpu[i].Itime, ss->cpu.cpu[i].Stime, ss->cpu.cpu[i].steal, ss->cpu.cpu[i].guest, freq, freqperc, ss->cpu.cpu[i].instr, ss->cpu.cpu[i].cycle ); } } void print_CPL(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *devchain, int ncgroups) { printf("%s %lld %.2f %.2f %.2f %lld %lld\n", hp, ss->cpu.nrcpu, ss->cpu.lavg1, ss->cpu.lavg5, ss->cpu.lavg15, ss->cpu.csw, ss->cpu.devint); } void print_GPU(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *devchain, int ncgroups) { int i; for (i=0; i < ss->gpu.nrgpus; i++) { printf("%s %d %s %s %d %d %lld %lld %lld %lld %lld %lld\n", hp, i, ss->gpu.gpu[i].busid, ss->gpu.gpu[i].type, ss->gpu.gpu[i].gpupercnow, ss->gpu.gpu[i].mempercnow, ss->gpu.gpu[i].memtotnow, ss->gpu.gpu[i].memusenow, ss->gpu.gpu[i].samples, ss->gpu.gpu[i].gpuperccum, ss->gpu.gpu[i].memperccum, ss->gpu.gpu[i].memusecum); } } void print_MEM(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *devchain, int ncgroups) { printf( "%s %u %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld " "%lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld " "%lld %lld %lld\n", hp, pagesize, ss->mem.physmem, ss->mem.freemem, ss->mem.cachemem, ss->mem.buffermem, ss->mem.slabmem, ss->mem.cachedrt, ss->mem.slabreclaim, ss->mem.vmwballoon != -1 ? ss->mem.vmwballoon : 0, ss->mem.shmem, ss->mem.shmrss, ss->mem.shmswp, ss->mem.shugepagesz, ss->mem.stothugepage, ss->mem.sfreehugepage, ss->mem.zfsarcsize != -1 ? ss->mem.zfsarcsize : 0, ss->mem.ksmsharing != -1 ? ss->mem.ksmsharing : 0, ss->mem.ksmshared != -1 ? ss->mem.ksmshared : 0, ss->mem.tcpsock, ss->mem.udpsock, ss->mem.pagetables, ss->mem.lhugepagesz, ss->mem.ltothugepage, ss->mem.lfreehugepage, ss->mem.availablemem, ss->mem.anonhugepage); } void print_SWP(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *devchain, int ncgroups) { printf( "%s %u %lld %lld %lld %lld %lld %lld %lld %lld\n", hp, pagesize, ss->mem.totswap, ss->mem.freeswap, ss->mem.swapcached, ss->mem.committed, ss->mem.commitlim, ss->mem.swapcached, ss->mem.zswapped, ss->mem.zswap); } void print_PAG(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *devchain, int ncgroups) { printf("%s %u %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld " "%lld %lld\n", hp, pagesize, ss->mem.pgscans, ss->mem.allocstall, (long long)0, ss->mem.swins, ss->mem.swouts, ss->mem.oomkills, ss->mem.compactstall, ss->mem.pgmigrate, ss->mem.numamigrate, ss->mem.pgins, ss->mem.pgouts, ss->mem.zswins, ss->mem.zswouts); } void print_PSI(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *devchain, int ncgroups) { printf("%s %c %.1f %.1f %.1f %llu %.1f %.1f %.1f %llu " "%.1f %.1f %.1f %llu %.1f %.1f %.1f %llu %.1f %.1f %.1f %llu\n", hp, ss->psi.present ? 'y' : 'n', ss->psi.cpusome.avg10, ss->psi.cpusome.avg60, ss->psi.cpusome.avg300, ss->psi.cpusome.total, ss->psi.memsome.avg10, ss->psi.memsome.avg60, ss->psi.memsome.avg300, ss->psi.memsome.total, ss->psi.memfull.avg10, ss->psi.memfull.avg60, ss->psi.memfull.avg300, ss->psi.memfull.total, ss->psi.iosome.avg10, ss->psi.iosome.avg60, ss->psi.iosome.avg300, ss->psi.iosome.total, ss->psi.iofull.avg10, ss->psi.iofull.avg60, ss->psi.iofull.avg300, ss->psi.iofull.total); } void print_LVM(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *devchain, int ncgroups) { register int i; for (i=0; ss->dsk.lvm[i].name[0]; i++) { printf( "%s %s %lld %lld %lld %lld %lld %lld %lld %lld %.2f\n", hp, ss->dsk.lvm[i].name, ss->dsk.lvm[i].io_ms, ss->dsk.lvm[i].nread, ss->dsk.lvm[i].nrsect, ss->dsk.lvm[i].nwrite, ss->dsk.lvm[i].nwsect, ss->dsk.lvm[i].ndisc, ss->dsk.lvm[i].ndsect, ss->dsk.lvm[i].inflight, ss->dsk.lvm[i].io_ms > 0 ? (double)ss->dsk.lvm[i].avque/ss->dsk.lvm[i].io_ms : 0.0); } } void print_MDD(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *devchain, int ncgroups) { register int i; for (i=0; ss->dsk.mdd[i].name[0]; i++) { printf( "%s %s %lld %lld %lld %lld %lld %lld %lld %lld %.2f\n", hp, ss->dsk.mdd[i].name, ss->dsk.mdd[i].io_ms, ss->dsk.mdd[i].nread, ss->dsk.mdd[i].nrsect, ss->dsk.mdd[i].nwrite, ss->dsk.mdd[i].nwsect, ss->dsk.mdd[i].ndisc, ss->dsk.mdd[i].ndsect, ss->dsk.mdd[i].inflight, ss->dsk.mdd[i].io_ms > 0 ? (double)ss->dsk.mdd[i].avque/ss->dsk.mdd[i].io_ms : 0.0); } } void print_DSK(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *devchain, int ncgroups) { register int i; for (i=0; ss->dsk.dsk[i].name[0]; i++) { printf( "%s %s %lld %lld %lld %lld %lld %lld %lld %lld %.2f\n", hp, ss->dsk.dsk[i].name, ss->dsk.dsk[i].io_ms, ss->dsk.dsk[i].nread, ss->dsk.dsk[i].nrsect, ss->dsk.dsk[i].nwrite, ss->dsk.dsk[i].nwsect, ss->dsk.dsk[i].ndisc, ss->dsk.dsk[i].ndsect, ss->dsk.dsk[i].inflight, ss->dsk.dsk[i].io_ms > 0 ? (double)ss->dsk.dsk[i].avque/ss->dsk.dsk[i].io_ms : 0.0); } } void print_NFM(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *devchain, int ncgroups) { register int i; for (i=0; i < ss->nfs.nfsmounts.nrmounts; i++) { printf("%s %s %lld %lld %lld %lld %lld %lld %lld %lld\n", hp, ss->nfs.nfsmounts.nfsmnt[i].mountdev, ss->nfs.nfsmounts.nfsmnt[i].bytestotread, ss->nfs.nfsmounts.nfsmnt[i].bytestotwrite, ss->nfs.nfsmounts.nfsmnt[i].bytesread, ss->nfs.nfsmounts.nfsmnt[i].byteswrite, ss->nfs.nfsmounts.nfsmnt[i].bytesdread, ss->nfs.nfsmounts.nfsmnt[i].bytesdwrite, ss->nfs.nfsmounts.nfsmnt[i].pagesmread, ss->nfs.nfsmounts.nfsmnt[i].pagesmwrite); } } void print_NFC(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *devchain, int ncgroups) { printf( "%s %lld %lld %lld %lld %lld\n", hp, ss->nfs.client.rpccnt, ss->nfs.client.rpcread, ss->nfs.client.rpcwrite, ss->nfs.client.rpcretrans, ss->nfs.client.rpcautrefresh); } void print_NFS(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *devchain, int ncgroups) { printf( "%s %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld " "%lld %lld %lld %lld %lld\n", hp, ss->nfs.server.rpccnt, ss->nfs.server.rpcread, ss->nfs.server.rpcwrite, ss->nfs.server.nrbytes, ss->nfs.server.nwbytes, ss->nfs.server.rpcbadfmt, ss->nfs.server.rpcbadaut, ss->nfs.server.rpcbadcln, ss->nfs.server.netcnt, ss->nfs.server.nettcpcnt, ss->nfs.server.netudpcnt, ss->nfs.server.nettcpcon, ss->nfs.server.rchits, ss->nfs.server.rcmiss, ss->nfs.server.rcnoca); } void print_NET(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *devchain, int ncgroups) { register int i; printf( "%s %s %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld\n", hp, "upper", ss->net.tcp.InSegs, ss->net.tcp.OutSegs, ss->net.udpv4.InDatagrams + ss->net.udpv6.Udp6InDatagrams, ss->net.udpv4.OutDatagrams + ss->net.udpv6.Udp6OutDatagrams, ss->net.ipv4.InReceives + ss->net.ipv6.Ip6InReceives, ss->net.ipv4.OutRequests + ss->net.ipv6.Ip6OutRequests, ss->net.ipv4.InDelivers + ss->net.ipv6.Ip6InDelivers, ss->net.ipv4.ForwDatagrams + ss->net.ipv6.Ip6OutForwDatagrams, ss->net.udpv4.InErrors + ss->net.udpv6.Udp6InErrors, ss->net.udpv4.NoPorts + ss->net.udpv6.Udp6NoPorts, ss->net.tcp.ActiveOpens, ss->net.tcp.PassiveOpens, ss->net.tcp.CurrEstab, ss->net.tcp.RetransSegs, ss->net.tcp.InErrs, ss->net.tcp.OutRsts, ss->net.tcp.InCsumErrors); for (i=0; ss->intf.intf[i].name[0]; i++) { printf( "%s %s %lld %lld %lld %lld %ld %d " "%lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld\n", hp, ss->intf.intf[i].name, ss->intf.intf[i].rpack, ss->intf.intf[i].rbyte, ss->intf.intf[i].spack, ss->intf.intf[i].sbyte, ss->intf.intf[i].speed, ss->intf.intf[i].duplex, ss->intf.intf[i].rerrs, ss->intf.intf[i].serrs, ss->intf.intf[i].rdrop, ss->intf.intf[i].sdrop, ss->intf.intf[i].rfifo, ss->intf.intf[i].sfifo, ss->intf.intf[i].rcompr, ss->intf.intf[i].scompr, ss->intf.intf[i].rframe, ss->intf.intf[i].rmultic, ss->intf.intf[i].scollis, ss->intf.intf[i].scarrier); } } void print_IFB(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *devchain, int ncgroups) { register int i; for (i=0; i < ss->ifb.nrports; i++) { printf( "%s %s %hd %hd %lld %lld %lld %lld %lld\n", hp, ss->ifb.ifb[i].ibname, ss->ifb.ifb[i].portnr, ss->ifb.ifb[i].lanes, ss->ifb.ifb[i].rate, ss->ifb.ifb[i].rcvb, ss->ifb.ifb[i].sndb, ss->ifb.ifb[i].rcvp, ss->ifb.ifb[i].sndp); } } void print_NUM(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *devchain, int ncgroups) { register int i; for (i=0; i < ss->memnuma.nrnuma; i++) { printf( "%s %d %u %.0f %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld\n", hp, ss->memnuma.numa[i].numanr, pagesize, ss->memnuma.numa[i].frag * 100.0, ss->memnuma.numa[i].totmem, ss->memnuma.numa[i].freemem, ss->memnuma.numa[i].active, ss->memnuma.numa[i].inactive, ss->memnuma.numa[i].filepage, ss->memnuma.numa[i].dirtymem, ss->memnuma.numa[i].slabmem, ss->memnuma.numa[i].slabreclaim, ss->memnuma.numa[i].shmem, ss->memnuma.numa[i].tothp, ss->memnuma.numa[i].freehp); } } void print_NUC(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *devchain, int ncgroups) { register int i; for (i=0; i < ss->cpunuma.nrnuma; i++) { printf( "%s %d %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld\n", hp, ss->cpunuma.numa[i].numanr, ss->cpunuma.numa[i].nrcpu, ss->cpunuma.numa[i].stime, ss->cpunuma.numa[i].utime, ss->cpunuma.numa[i].ntime, ss->cpunuma.numa[i].itime, ss->cpunuma.numa[i].wtime, ss->cpunuma.numa[i].Itime, ss->cpunuma.numa[i].Stime, ss->cpunuma.numa[i].steal, ss->cpunuma.numa[i].guest); } } void print_LLC(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *devchain, int ncgroups) { register int i; for (i=0; i < ss->llc.nrllcs; i++) { printf( "%s LLC%03d %3.1f%% %lld %lld\n", hp, ss->llc.perllc[i].id, ss->llc.perllc[i].occupancy * 100, ss->llc.perllc[i].mbm_total, ss->llc.perllc[i].mbm_local); } } /* ** print functions for cgroups-level statistics */ void print_CGR(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *devchain, int ncgroups) { register int i, p; char *cgrpath; if ( !(supportflags & CGROUPV2) ) return; for (i=0; i < ncgroups; i++) { // print cgroup level metrics // cgrpath = cggetpath(devchain+i, devchain, 0); printf( "%s C %s %d %d %lld %lld %d %d " "%lld %lld %lld %lld %lld %lld %lld " "%lld %lld %lld %lld %d %lld %lld " "%lld %lld %lld %lld\n", hp, cgrpath, (devchain+i)->cstat->gen.nprocs, (devchain+i)->cstat->gen.procsbelow, (devchain+i)->cstat->cpu.utime, (devchain+i)->cstat->cpu.stime, (devchain+i)->cstat->conf.cpuweight, (devchain+i)->cstat->conf.cpumax, (devchain+i)->cstat->mem.current, (devchain+i)->cstat->mem.anon, (devchain+i)->cstat->mem.file, (devchain+i)->cstat->mem.kernel, (devchain+i)->cstat->mem.shmem, (devchain+i)->cstat->conf.memmax, (devchain+i)->cstat->conf.swpmax, (devchain+i)->cstat->dsk.rbytes, (devchain+i)->cstat->dsk.wbytes, (devchain+i)->cstat->dsk.rios, (devchain+i)->cstat->dsk.wios, (devchain+i)->cstat->conf.dskweight, (devchain+i)->cstat->cpu.somepres, (devchain+i)->cstat->cpu.fullpres, (devchain+i)->cstat->mem.somepres, (devchain+i)->cstat->mem.fullpres, (devchain+i)->cstat->dsk.somepres, (devchain+i)->cstat->dsk.fullpres); // print related pidlist in one line // if ((devchain+i)->cstat->gen.nprocs) { printf( "%s P %s", hp, cgrpath); for (p=0; p < (devchain+i)->cstat->gen.nprocs; p++) printf(" %d", (devchain+i)->proclist[p]); printf("\n"); } free(cgrpath); } } /* ** print functions for process-level statistics */ void print_PRG(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *devchain, int ncgroups) { register int i, exitcode, cgrlen, cgrpathsize = 256; char namout[PNAMLEN+1+2], cmdout[CMDLEN+1+2], defaultbuf[8], *cgrout=defaultbuf, *cgrpath; // create assumed worst-case cgroup path size // if it appears to be too small, it will later on be realloc'ed // if (supportflags & CGROUPV2) { cgrout = calloc(1, cgrpathsize); ptrverify(cgrout, "Malloc failed for output cgroup path\n"); } for (i=0; i < nact; i++, ps++) { if (supportflags & CGROUPV2 && ps->gen.cgroupix != -1) // valid cgroup index? { cgrpath = cggetpath((devchain + ps->gen.cgroupix), devchain, 0); if (cgrpathsize < (cgrlen = strlen(cgrpath) + 3)) { cgrpathsize = cgrlen; cgrout = realloc(cgrout, cgrpathsize); ptrverify(cgrout, "Realloc failed for output cgroup path\n"); } } else { cgrpath = "-"; } if (ps->gen.excode & 0xff) // killed by signal? exitcode = (ps->gen.excode & 0x7f) + 256; else exitcode = (ps->gen.excode >> 8) & 0xff; printf("%s %d %s %c %d %d %d %d %d %ld %s %d %d %d %d " "%d %d %d %d %d %d %ld %c %d %d %s %c %s %ld %d\n", hp, ps->gen.pid, spaceformat(ps->gen.name, namout), ps->gen.state, ps->gen.ruid, ps->gen.rgid, ps->gen.tgid, ps->gen.nthr, exitcode, ps->gen.btime, spaceformat(ps->gen.cmdline, cmdout), ps->gen.ppid, ps->gen.nthrrun, ps->gen.nthrslpi, ps->gen.nthrslpu, ps->gen.euid, ps->gen.egid, ps->gen.suid, ps->gen.sgid, ps->gen.fsuid, ps->gen.fsgid, ps->gen.elaps, ps->gen.isproc ? 'y':'n', ps->gen.vpid, ps->gen.ctid, ps->gen.utsname[0] ? ps->gen.utsname:"-", ps->gen.excode & ~(INT_MAX) ? 'N' : '-', spaceformat(cgrpath, cgrout), ps->gen.state == 'E' ? ps->gen.btime + ps->gen.elaps/hertz : 0, ps->gen.nthridle); if (supportflags & CGROUPV2 && ps->gen.cgroupix != -1) free(cgrpath); } if (supportflags & CGROUPV2) free(cgrout); } void print_PRC(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *devchain, int ncgroups) { register int i, cpumax; char namout[PNAMLEN+1+2], wchanout[20]; for (i=0; i < nact; i++, ps++) { if (supportflags & CGROUPV2) { if (ps->gen.cgroupix != -1) cpumax = (devchain+ps->gen.cgroupix)->cstat->conf.cpumax; else cpumax = -2; } else { cpumax = -3; } printf("%s %d %s %c %u %lld %lld %d %d %d %d %d %d %d %c " "%llu %s %llu %d %d %llu %llu\n", hp, ps->gen.pid, spaceformat(ps->gen.name, namout), ps->gen.state, hertz, ps->cpu.utime, ps->cpu.stime, ps->cpu.nice, ps->cpu.prio, ps->cpu.rtprio, ps->cpu.policy, ps->cpu.curcpu, ps->cpu.sleepavg, ps->gen.tgid, ps->gen.isproc ? 'y':'n', ps->cpu.rundelay, spaceformat(ps->cpu.wchan, wchanout), ps->cpu.blkdelay, cpumax, -2, // most restrictive cpumax no longer supported ps->cpu.nvcsw, ps->cpu.nivcsw); } } void print_PRM(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *devchain, int ncgroups) { register int i, memmax, swpmax; char namout[PNAMLEN+1+2]; for (i=0; i < nact; i++, ps++) { if (supportflags & CGROUPV2) { if (ps->gen.cgroupix != -1) { memmax = (devchain+ps->gen.cgroupix)->cstat->conf.memmax; if (memmax > 0) memmax *= pagesize / 1024; swpmax = (devchain+ps->gen.cgroupix)->cstat->conf.swpmax; if (swpmax > 0) swpmax *= pagesize / 1024; } else { memmax = -2; swpmax = -2; } } else { memmax = -3; swpmax = -3; } printf("%s %d %s %c %u %lld %lld %lld %lld %lld %lld " "%lld %lld %lld %lld %lld %d %c %lld %lld %d %d %d %d\n", hp, ps->gen.pid, spaceformat(ps->gen.name, namout), ps->gen.state, pagesize, ps->mem.vmem, ps->mem.rmem, ps->mem.vexec, ps->mem.vgrow, ps->mem.rgrow, ps->mem.minflt, ps->mem.majflt, ps->mem.vlibs, ps->mem.vdata, ps->mem.vstack, ps->mem.vswap, ps->gen.tgid, ps->gen.isproc ? 'y':'n', ps->mem.pmem == (unsigned long long)-1LL ? 0:ps->mem.pmem, ps->mem.vlock, memmax, -2, swpmax, -2); } } void print_PRD(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *devchain, int ncgroups) { register int i; char namout[PNAMLEN+1+2]; for (i=0; i < nact; i++, ps++) { printf("%s %d %s %c %c %c %lld %lld %lld %lld %lld %d n %c\n", hp, ps->gen.pid, spaceformat(ps->gen.name, namout), ps->gen.state, 'n', supportflags & IOSTAT ? 'y' : 'n', ps->dsk.rio, ps->dsk.rsz, ps->dsk.wio, ps->dsk.wsz, ps->dsk.cwsz, ps->gen.tgid, ps->gen.isproc ? 'y':'n'); } } void print_PRN(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *devchain, int ncgroups) { register int i; char namout[PNAMLEN+1+2]; for (i=0; i < nact; i++, ps++) { printf("%s %d %s %c %c %lld %lld %lld %lld %lld %lld " "%lld %lld %d %d %d %c\n", hp, ps->gen.pid, spaceformat(ps->gen.name, namout), ps->gen.state, supportflags & NETATOP ? 'y' : 'n', ps->net.tcpsnd, ps->net.tcpssz, ps->net.tcprcv, ps->net.tcprsz, ps->net.udpsnd, ps->net.udpssz, ps->net.udprcv, ps->net.udprsz, 0, 0, ps->gen.tgid, ps->gen.isproc ? 'y':'n'); } } void print_PRE(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *devchain, int ncgroups) { register int i; char namout[PNAMLEN+1+2]; for (i=0; i < nact; i++, ps++) { printf("%s %d %s %c %c %d %x %d %d %lld %lld %lld\n", hp, ps->gen.pid, spaceformat(ps->gen.name, namout), ps->gen.state, ps->gpu.state == '\0' ? 'N':ps->gpu.state, ps->gpu.nrgpus, ps->gpu.gpulist, ps->gpu.gpubusy, ps->gpu.membusy, ps->gpu.memnow, ps->gpu.memcum, ps->gpu.sample); } } /* ** Strings, like command name, might contain spaces ** and will be represented for that reason surrounded ** by parenthesis. However, this is difficult to parse ** by other tools, so the option -Z might have been ** specified to exchange all spaces by underscores while ** omitting the parenthesis in that case. ** ** This function formats the input string (istr) in the ** required format to the output string (ostr). ** Take care that the buffer pointed to by ostr is at least ** two bytes larger than the input string (for the parenthesis). ** The pointer ostr is also returned. */ static char * spaceformat(char *istr, char *ostr) { // formatting with spaces and parenthesis required? if (!rmspaces) { *ostr = '('; strcpy(ostr+1, istr); strcat(ostr, ")"); } // formatting with underscores without parenthesis required else { register char *pi = istr, *po = ostr; while (*pi) { if (*pi == ' ') { *po++ = '_'; pi++; } else { *po++ = *pi++; } } if (po == ostr) // empty string: still return underscore *po++ = '_'; *po = '\0'; // terminate output string } return ostr; } atop-2.12.1/parseable.h0000644000203100020310000000266115064552761014167 0ustar gerlofgerlof/* ** ATOP - System & Process Monitor ** ** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as process-level. ** ========================================================================== ** Author: Gerlof Langeveld ** E-mail: gerlof.langeveld@atoptool.nl ** Date: September 2002 ** -------------------------------------------------------------------------- ** Copyright (C) 2000-2010 Gerlof Langeveld ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ** -------------------------------------------------------------------------- */ #ifndef __PARSEABLE__ #define __PARSEABLE__ int parsedef(char *); char parseout(time_t, int, struct devtstat *, struct sstat *, struct cgchainer *, int, int, int, unsigned int, char); #endif atop-2.12.1/photoproc.c0000644000203100020310000005325415064552761014245 0ustar gerlofgerlof/* ** ATOP - System & Process Monitor ** ** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as process-/thread-level. ** ** This source-file contains functions to read the process-administration ** of every running process from kernel-space and extract the required ** activity-counters. ** ========================================================================== ** Author: Gerlof Langeveld ** E-mail: gerlof.langeveld@atoptool.nl ** Date: November 1996 ** LINUX-port: June 2000 ** -------------------------------------------------------------------------- ** Copyright (C) 2000-2022 Gerlof Langeveld ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ** -------------------------------------------------------------------------- */ #define _POSIX_C_SOURCE #define _XOPEN_SOURCE #define _GNU_SOURCE #define _DEFAULT_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include "atop.h" #include "photoproc.h" #include "netatop.h" #define SCANSTAT "%c %d %*d %*d %*d %*d " \ "%*d %lld %*d %lld %*d %lld " \ "%lld %*d %*d %d %d %*d " \ "%*d %ld %lld %lld %*d %*d " \ "%*d %*d %*d %*d %*d %*d " \ "%*d %*d %*d %*d %*d %*d " \ "%d %d %d %lld" static int procstat(struct tstat *, unsigned long long, char); static int procstatus(struct tstat *); static int procio(struct tstat *); static void proccmd(struct tstat *); static void procsmaps(struct tstat *); static void procwchan(struct tstat *); static count_t procschedstat(struct tstat *); extern GHashTable *ghash_net; extern char prependenv; extern regex_t envregex; unsigned long photoproc(struct tstat *tasklist, int maxtask) { static int firstcall = 1; static unsigned long long bootepoch; register struct tstat *curtask; FILE *fp; DIR *dirp; struct dirent *entp; char origdir[4096], dockstat=0; unsigned long tval=0; /* ** one-time initialization stuff */ if (firstcall) { /* ** check if this kernel offers io-statistics per task */ regainrootprivs(); if ( (fp = fopen("/proc/1/io", "r")) ) { supportflags |= IOSTAT; fclose(fp); } if (! droprootprivs()) mcleanstop(42, "failed to drop root privs\n"); /* ** find epoch time of boot moment */ bootepoch = getboot(); firstcall = 0; } /* ** probe if the netatop module and (optionally) the ** netatopd daemon are active */ regainrootprivs(); /* ** if netatop kernel module is not active on this system, ** try to connect to netatop-bpf */ if (connectnetatop && !(supportflags & NETATOP)) { netatop_bpf_probe(); } /* ** if netatop-bpf is not active on this system, ** try to connect to kernel module */ if (connectnetatop && !(supportflags & NETATOPBPF)) { netatop_probe(); } /* ** if netatop-bpf is active on this system, fetch data */ if (supportflags & NETATOPBPF) { netatop_bpf_gettask(); } if (! droprootprivs()) mcleanstop(42, "failed to drop root privs\n"); /* ** read all subdirectory-names below the /proc directory */ if ( getcwd(origdir, sizeof origdir) == NULL) mcleanstop(53, "failed to save current dir\n"); if ( chdir("/proc") == -1) mcleanstop(54, "failed to change to /proc\n"); dirp = opendir("."); while ( (entp = readdir(dirp)) && tval < maxtask ) { /* ** skip non-numerical names */ if (!isdigit(entp->d_name[0])) continue; /* ** change to the process' subdirectory */ if ( chdir(entp->d_name) != 0 ) continue; /* ** gather process-level information */ curtask = tasklist+tval; if ( !procstat(curtask, bootepoch, 1)) /* from /proc/pid/stat */ { if ( chdir("..") == -1) ; continue; } if ( !procstatus(curtask) ) /* from /proc/pid/status */ { if ( chdir("..") == -1) ; continue; } if ( !procio(curtask) ) /* from /proc/pid/io */ { if ( chdir("..") == -1) ; continue; } procschedstat(curtask); /* from /proc/pid/schedstat */ proccmd(curtask); /* from /proc/pid/cmdline */ dockstat += getutsname(curtask); /* retrieve container/pod name */ /* ** reading the smaps file for every process with every sample ** is a really 'expensive' from a CPU consumption point-of-view, ** so gathering this info is optional */ if (calcpss) procsmaps(curtask); /* from /proc/pid/smaps */ /* ** determine thread's wchan, if wanted ('expensive' from ** a CPU consumption point-of-view) */ if (getwchan) procwchan(curtask); if (supportflags & NETATOPBPF) { struct taskcount *tc = g_hash_table_lookup(ghash_net, &(curtask->gen.tgid)); if (tc) { // printf("%d %d %d %d %d\n",curtask->gen.tgid, tc->tcpsndpacks, tc->tcprcvpacks, tc->udpsndpacks, tc->udprcvpacks); curtask->net.tcpsnd = tc->tcpsndpacks; curtask->net.tcprcv = tc->tcprcvpacks; curtask->net.tcpssz = tc->tcpsndbytes; curtask->net.tcprsz = tc->tcprcvbytes; curtask->net.udpsnd = tc->udpsndpacks; curtask->net.udprcv = tc->udprcvpacks; curtask->net.udpssz = tc->udpsndbytes; curtask->net.udprsz = tc->udprcvbytes; } } else { // read network stats from netatop (if active) netatop_gettask(curtask->gen.tgid, 'g', curtask); } tval++; /* increment for process-level info */ /* ** if needed (when number of threads is larger than 1): ** read and fill new entries with thread-level info */ if (curtask->gen.nthr > 1) { DIR *dirtask; struct dirent *tent; curtask->gen.nthrrun = 0; curtask->gen.nthrslpi = 0; curtask->gen.nthrslpu = 0; curtask->gen.nthridle = 0; /* ** rundelay and blkdelay on process level only ** concerns the delays of the main thread; ** totalize the delays of all threads */ curtask->cpu.rundelay = 0; curtask->cpu.blkdelay = 0; /* ** nvcsw and nivcsw on process level only ** concerns the delays of the main thread; ** totalize the delays of all threads */ curtask->cpu.nvcsw = 0; curtask->cpu.nivcsw = 0; /* ** open underlying task directory */ if ( chdir("task") == 0 ) { unsigned long cur_nth = 0; dirtask = opendir("."); /* ** due to race condition, opendir() might ** have failed (leave task and process-level ** directories) */ if( dirtask == NULL ) { if(chdir("../..") == -1) ; continue; } while ((tent=readdir(dirtask)) && tvald_name[0] == '.' || chdir(tent->d_name) != 0 ) continue; if ( !procstat(curthr, bootepoch, 0)) { if ( chdir("..") == -1) ; continue; } if ( !procstatus(curthr) ) { if ( chdir("..") == -1) ; continue; } if ( !procio(curthr) ) { if ( chdir("..") == -1) ; continue; } /* ** determine thread's wchan, if wanted ** ('expensive' from a CPU consumption ** point-of-view) */ if (getwchan) procwchan(curthr); // totalize values of all threads curtask->cpu.rundelay += procschedstat(curthr); curtask->cpu.blkdelay += curthr->cpu.blkdelay; curtask->cpu.nvcsw += curthr->cpu.nvcsw; curtask->cpu.nivcsw += curthr->cpu.nivcsw; // continue gathering strcpy(curthr->gen.utsname, curtask->gen.utsname); switch (curthr->gen.state) { case 'R': curtask->gen.nthrrun += 1; break; case 'S': curtask->gen.nthrslpi += 1; break; case 'D': curtask->gen.nthrslpu += 1; break; case 'I': curtask->gen.nthridle += 1; break; } curthr->gen.nthr = 1; // try to read network stats from netatop's module if (!(supportflags & NETATOPBPF)) { netatop_gettask(curthr->gen.pid, 't', curthr); } // all stats read now tval++; /* increment thread-level */ cur_nth++; /* increment # threads */ if ( chdir("..") == -1) ; /* thread */ } closedir(dirtask); if ( chdir("..") == -1) ; /* leave task */ // calibrate number of threads curtask->gen.nthr = cur_nth; } } if ( chdir("..") == -1) ; /* leave process-level directry */ } closedir(dirp); if ( chdir(origdir) == -1) mcleanstop(55, "cannot change to %s\n", origdir); if (dockstat) supportflags |= CONTAINERSTAT; else supportflags &= ~CONTAINERSTAT; resetutsname(); // reassociate atop with own UTS namespace return tval; } /* ** count number of tasks in the system, i.e. ** the number of processes plus the total number of threads */ #define TPTOLERANCE 2 unsigned long counttasks(void) { unsigned long nrproc=0, nrthread=0; char linebuf[256]; FILE *fp; DIR *dirp; struct dirent *entp; char origdir[1024]; /* ** determine total number of threads */ if ( (fp = fopen("/proc/loadavg", "r")) != NULL) { if ( fgets(linebuf, sizeof(linebuf), fp) != NULL) { if ( sscanf(linebuf, "%*f %*f %*f %*d/%lu", &nrthread) < 1) mcleanstop(53, "wrong /proc/loadavg\n"); } else mcleanstop(53, "unreadable /proc/loadavg\n"); fclose(fp); } else mcleanstop(53, "can not open /proc/loadavg\n"); /* ** add total number of processes */ if ( getcwd(origdir, sizeof origdir) == NULL) mcleanstop(53, "cannot determine cwd\n"); if ( chdir("/proc") == -1) mcleanstop(53, "cannot change to /proc\n"); dirp = opendir("."); while ( (entp = readdir(dirp)) ) { /* ** count subdirectory names under /proc starting with a digit */ if (isdigit(entp->d_name[0])) nrproc++; } closedir(dirp); if ( chdir(origdir) == -1) mcleanstop(53, "cannot change to %s\n", origdir); /* ** In a normal situation the number of threads will be far more ** than the number of processes since every process consists of ** at least one thread (even zombie processes) while many processes ** consist of multiple threads. Some malicious kernel versions in ** the past did not provide correct information about the number of ** threads for which a sanity check was added at this point. ** ** However, in the early boot phase (when the atop daemon is started) ** only single-threaded processes might run. Since the gathering of ** the number of threads and the number of processes is not one atomic ** operation, the number of threads might be 1 or 2 less than the number ** of processes. This should not lead to a preliminary termination. */ if (nrthread < nrproc) { if (nrproc - nrthread > TPTOLERANCE) mcleanstop(53, "#threads (%ld) < #procs (%ld)\n", nrthread, nrproc); nrthread = nrproc; // correct number of threads } return nrproc + nrthread; } /* ** open file "stat" and obtain required info */ static int procstat(struct tstat *curtask, unsigned long long bootepoch, char isproc) { FILE *fp; int nr; char line[4096], *p, *cmdhead, *cmdtail; if ( (fp = fopen("stat", "r")) == NULL) return 0; if ( (nr = fread(line, 1, sizeof line-1, fp)) == 0) { fclose(fp); return 0; } line[nr] = '\0'; // terminate string /* ** fetch command name */ cmdhead = strchr (line, '('); cmdtail = strrchr(line, ')'); if (!cmdhead || !cmdtail || cmdtail < cmdhead) // parsing failed? { fclose(fp); return 0; } if ( (nr = cmdtail-cmdhead-1) > PNAMLEN) nr = PNAMLEN; p = curtask->gen.name; memcpy(p, cmdhead+1, nr); *(p+nr) = 0; while ( (p = strchr(p, '\n')) != NULL) { *p = '?'; p++; } /* ** fetch other values */ curtask->gen.isproc = isproc; curtask->cpu.rtprio = 0; curtask->cpu.policy = 0; curtask->gen.excode = 0; sscanf(line, "%d", &(curtask->gen.pid)); /* fetch pid */ nr = sscanf(cmdtail+2, SCANSTAT, &(curtask->gen.state), &(curtask->gen.ppid), &(curtask->mem.minflt), &(curtask->mem.majflt), &(curtask->cpu.utime), &(curtask->cpu.stime), &(curtask->cpu.prio), &(curtask->cpu.nice), &(curtask->gen.btime), &(curtask->mem.vmem), &(curtask->mem.rmem), &(curtask->cpu.curcpu), &(curtask->cpu.rtprio), &(curtask->cpu.policy), &(curtask->cpu.blkdelay)); if (nr < 12) /* parsing failed? */ { fclose(fp); return 0; } /* ** normalization */ curtask->gen.btime = (curtask->gen.btime+bootepoch)/hertz; curtask->cpu.prio += 100; /* was subtracted by kernel */ curtask->mem.vmem /= 1024; curtask->mem.rmem *= pagesize/1024; fclose(fp); switch (curtask->gen.state) { case 'R': curtask->gen.nthrrun = 1; break; case 'S': curtask->gen.nthrslpi = 1; break; case 'D': curtask->gen.nthrslpu = 1; break; case 'I': curtask->gen.nthridle = 1; break; } return 1; } /* ** open file "status" and obtain required info */ static int procstatus(struct tstat *curtask) { FILE *fp; char line[4096]; if ( (fp = fopen("status", "r")) == NULL) return 0; curtask->gen.nthr = 1; /* for compat with 2.4 */ curtask->cpu.sleepavg = 0; /* for compat with 2.4 */ curtask->mem.vgrow = 0; /* calculated later */ curtask->mem.rgrow = 0; /* calculated later */ while (fgets(line, sizeof line, fp)) { if (memcmp(line, "Tgid:", 5) ==0) { sscanf(line, "Tgid: %d", &(curtask->gen.tgid)); continue; } if (memcmp(line, "Pid:", 4) ==0) { sscanf(line, "Pid: %d", &(curtask->gen.pid)); continue; } if (memcmp(line, "SleepAVG:", 9)==0) { sscanf(line, "SleepAVG: %d%%", &(curtask->cpu.sleepavg)); continue; } if (memcmp(line, "Uid:", 4)==0) { sscanf(line, "Uid: %d %d %d %d", &(curtask->gen.ruid), &(curtask->gen.euid), &(curtask->gen.suid), &(curtask->gen.fsuid)); continue; } if (memcmp(line, "Gid:", 4)==0) { sscanf(line, "Gid: %d %d %d %d", &(curtask->gen.rgid), &(curtask->gen.egid), &(curtask->gen.sgid), &(curtask->gen.fsgid)); continue; } if (memcmp(line, "envID:", 6) ==0) { sscanf(line, "envID: %d", &(curtask->gen.ctid)); continue; } if (memcmp(line, "VPid:", 5) ==0) { sscanf(line, "VPid: %d", &(curtask->gen.vpid)); continue; } if (memcmp(line, "Threads:", 8)==0) { sscanf(line, "Threads: %d", &(curtask->gen.nthr)); continue; } if (memcmp(line, "VmData:", 7)==0) { sscanf(line, "VmData: %lld", &(curtask->mem.vdata)); continue; } if (memcmp(line, "VmStk:", 6)==0) { sscanf(line, "VmStk: %lld", &(curtask->mem.vstack)); continue; } if (memcmp(line, "VmExe:", 6)==0) { sscanf(line, "VmExe: %lld", &(curtask->mem.vexec)); continue; } if (memcmp(line, "VmLib:", 6)==0) { sscanf(line, "VmLib: %lld", &(curtask->mem.vlibs)); continue; } if (memcmp(line, "VmSwap:", 7)==0) { sscanf(line, "VmSwap: %lld", &(curtask->mem.vswap)); continue; } if (memcmp(line, "VmLck:", 6)==0) { sscanf(line, "VmLck: %lld", &(curtask->mem.vlock)); continue; } if (memcmp(line, "voluntary_ctxt_switches:", 24)==0) { sscanf(line, "voluntary_ctxt_switches: %lld", &(curtask->cpu.nvcsw)); continue; } if (memcmp(line, "nonvoluntary_ctxt_switches:", 27)==0) { sscanf(line, "nonvoluntary_ctxt_switches: %lld", &(curtask->cpu.nivcsw)); continue; } } fclose(fp); return 1; } /* ** open file "io" (>= 2.6.20) and obtain required info */ #define IO_READ "read_bytes:" #define IO_WRITE "write_bytes:" #define IO_CWRITE "cancelled_write_bytes:" static int procio(struct tstat *curtask) { FILE *fp; char line[4096]; count_t dskrsz=0, dskwsz=0, dskcwsz=0; if (supportflags & IOSTAT) { regainrootprivs(); if ( (fp = fopen("io", "r")) ) { while (fgets(line, sizeof line, fp)) { if (memcmp(line, IO_READ, sizeof IO_READ -1) == 0) { sscanf(line, "%*s %llu", &dskrsz); dskrsz /= 512; // in sectors continue; } if (memcmp(line, IO_WRITE, sizeof IO_WRITE -1) == 0) { sscanf(line, "%*s %llu", &dskwsz); dskwsz /= 512; // in sectors continue; } if (memcmp(line, IO_CWRITE, sizeof IO_CWRITE -1) == 0) { sscanf(line, "%*s %llu", &dskcwsz); dskcwsz /= 512; // in sectors continue; } } fclose(fp); curtask->dsk.rsz = dskrsz; curtask->dsk.rio = dskrsz; // to enable sort curtask->dsk.wsz = dskwsz; curtask->dsk.wio = dskwsz; // to enable sort curtask->dsk.cwsz = dskcwsz; } if (! droprootprivs()) mcleanstop(42, "failed to drop root privs\n"); } return 1; } /* ** store the full command line ** ** the command-line may contain: ** - null-bytes as a separator between the arguments ** - newlines (e.g. arguments for awk or sed) ** - tabs (e.g. arguments for awk or sed) ** these special bytes will be converted to spaces ** ** the command line may be prepended by environment variables */ #define ABBENVLEN 16 static void proccmd(struct tstat *curtask) { FILE *fp, *fpe; register int i, nr; ssize_t env_len = 0; register char *pc = curtask->gen.cmdline; memset(curtask->gen.cmdline, 0, CMDLEN+1); // initialize command line // prepend by environment variables (if required) // if ( prependenv && (fpe = fopen("environ", "r")) != NULL) { char *line = NULL; ssize_t nread; size_t len = 0; while ((nread = getdelim(&line, &len, '\0', fpe)) != -1) { if (nread > 0 && !regexec(&envregex, line, 0, NULL, 0)) { if (env_len + nread >= CMDLEN) { // try to add abbreviated env string // if (env_len + ABBENVLEN + 1 >= CMDLEN) { break; } else { line[ABBENVLEN-4] = '.'; line[ABBENVLEN-3] = '.'; line[ABBENVLEN-2] = '.'; line[ABBENVLEN-1] = '\0'; line[ABBENVLEN] = '\0'; nread = ABBENVLEN; } } env_len += nread; *(line+nread-1) = ' '; // modify NULL byte to space strcpy(pc, line); pc += nread; } } /* line has been (re)allocated within the first call to getdelim */ /* even if the call fails, see getline(3) */ free(line); fclose(fpe); } // add command line and parameters // if ( (fp = fopen("cmdline", "r")) != NULL) { nr = fread(pc, 1, CMDLEN-env_len, fp); fclose(fp); if (nr > 0) /* anything read? */ { for (i=0; i < nr-1; i++, pc++) { switch (*pc) { case '\0': case '\n': case '\r': case '\t': *pc = ' '; } } } else { // nothing read (usually for kernel processes) // wipe prepended environment vars not to disturb // checks on an empty command line in other places // curtask->gen.cmdline[0] = '\0'; } } } /* ** determine the wait channel of a sleeping thread ** i.e. the name of the kernel function in which the thread ** has been put in sleep state) */ static void procwchan(struct tstat *curtask) { FILE *fp; register int nr = 0; if ( (fp = fopen("wchan", "r")) != NULL) { nr = fread(curtask->cpu.wchan, 1, sizeof(curtask->cpu.wchan)-1, fp); if (nr < 0) nr = 0; fclose(fp); } curtask->cpu.wchan[nr] = 0; } /* ** open file "smaps" and obtain required info ** since Linux-4.14, kernel supports "smaps_rollup" which has better ** performence. check "smaps_rollup" in first call ** if kernel supports "smaps_rollup", use "smaps_rollup" instead */ static void procsmaps(struct tstat *curtask) { FILE *fp; char line[4096]; count_t pssval; static int procsmaps_firstcall = 1; static char *smapsfile = "smaps"; if (procsmaps_firstcall) { regainrootprivs(); if ( (fp = fopen("/proc/1/smaps_rollup", "r")) ) { smapsfile = "smaps_rollup"; fclose(fp); } procsmaps_firstcall = 0; } /* ** open the file (always succeeds, even if no root privs) */ regainrootprivs(); if ( (fp = fopen(smapsfile, "r")) ) { curtask->mem.pmem = 0; while (fgets(line, sizeof line, fp)) { if (memcmp(line, "Pss:", 4) != 0) continue; // PSS line found to be accumulated sscanf(line, "Pss: %llu", &pssval); curtask->mem.pmem += pssval; } /* ** verify if fgets returned NULL due to error i.s.o. EOF */ if (ferror(fp)) curtask->mem.pmem = (unsigned long long)-1LL; fclose(fp); } else { curtask->mem.pmem = (unsigned long long)-1LL; } if (! droprootprivs()) mcleanstop(42, "failed to drop root privs\n"); } /* ** get run_delay from /proc//schedstat ** ref: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/scheduler/sched-stats.rst?h=v5.7-rc6 */ static count_t procschedstat(struct tstat *curtask) { FILE *fp; char line[4096]; count_t runtime, rundelay = 0; unsigned long pcount; static char *schedstatfile = "schedstat"; /* ** open the schedstat file */ if ( (fp = fopen(schedstatfile, "r")) ) { curtask->cpu.rundelay = 0; if (fgets(line, sizeof line, fp)) { sscanf(line, "%llu %llu %lu\n", &runtime, &rundelay, &pcount); curtask->cpu.rundelay = rundelay; } /* ** verify if fgets returned NULL due to error i.s.o. EOF */ if (ferror(fp)) curtask->cpu.rundelay = 0; fclose(fp); } else { curtask->cpu.rundelay = 0; } return curtask->cpu.rundelay; } atop-2.12.1/photoproc.h0000644000203100020310000001613215064552761014244 0ustar gerlofgerlof/* ** ATOP - System & Process Monitor ** ** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as process-level. ** ** Include-file describing process-level counters maintained and functions ** to access the process-database. ** ================================================================ ** Author: Gerlof Langeveld ** E-mail: gerlof.langeveld@atoptool.nl ** Date: November 1996 ** LINUX-port: June 2000 ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. */ #ifndef __PHOTOPROC__ #define __PHOTOPROC__ #define PNAMLEN 15 #define CMDLEN 255 #define CGRLEN 64 #define UTSLEN 15 /* ** structure containing only relevant process-info extracted ** from kernel's process-administration */ struct tstat { /* GENERAL TASK INFO */ struct gen { int tgid; /* threadgroup identification */ int pid; /* process identification */ int ppid; /* parent process identification*/ int ruid; /* real user identification */ int euid; /* eff. user identification */ int suid; /* saved user identification */ int fsuid; /* fs user identification */ int rgid; /* real group identification */ int egid; /* eff. group identification */ int sgid; /* saved group identification */ int fsgid; /* fs group identification */ int nthr; /* number of threads in tgroup */ char name[PNAMLEN+1];/* process name string */ char isproc; /* boolean: process level? */ char state; /* process state ('E' = exited) */ int excode; /* process exit status */ time_t btime; /* process start time (epoch) */ time_t elaps; /* process elaps time (hertz) */ char cmdline[CMDLEN+1];/* command-line string */ int nthrslpi; /* # threads in state 'S' */ int nthrslpu; /* # threads in state 'D' */ int nthrrun; /* # threads in state 'R' */ int nthridle; /* # threads in state 'I' */ int ctid; /* OpenVZ container ID */ int vpid; /* OpenVZ virtual PID */ int wasinactive; /* boolean: task inactive */ char utsname[UTSLEN+1];/* UTS name container or pod */ int cgroupix; /* index in devchain -1=invalid */ /* lazy filling (parsable/json) */ int ifuture[4]; /* reserved for future use */ } gen; /* CPU STATISTICS */ struct cpu { count_t utime; /* time user text (ticks) */ count_t stime; /* time system text (ticks) */ int nice; /* nice value */ int prio; /* priority */ int rtprio; /* realtime priority */ int policy; /* scheduling policy */ int curcpu; /* current processor */ int sleepavg; /* sleep average percentage */ int ifuture[6]; /* reserved for future use */ char wchan[16]; /* wait channel string */ count_t rundelay; /* schedstat rundelay (nanosec) */ count_t blkdelay; /* blkio delay (ticks) */ count_t nvcsw; /* voluntary cxt switch counts */ count_t nivcsw; /* involuntary csw counts */ count_t cfuture[3]; /* reserved for future use */ } cpu; /* DISK STATISTICS */ struct dsk { count_t rio; /* number of read requests */ count_t rsz; /* cumulative # sectors read */ count_t wio; /* number of write requests */ count_t wsz; /* cumulative # sectors written */ count_t cwsz; /* cumulative # written sectors */ /* being cancelled */ count_t cfuture[4]; /* reserved for future use */ } dsk; /* MEMORY STATISTICS */ struct mem { count_t minflt; /* number of page-reclaims */ count_t majflt; /* number of page-faults */ count_t vexec; /* virtmem execfile (Kb) */ count_t vmem; /* virtual memory (Kb) */ count_t rmem; /* resident memory (Kb) */ count_t pmem; /* resident memory (Kb) */ count_t vgrow; /* virtual growth (Kb) */ count_t rgrow; /* resident growth (Kb) */ count_t vdata; /* virtmem data (Kb) */ count_t vstack; /* virtmem stack (Kb) */ count_t vlibs; /* virtmem libexec (Kb) */ count_t vswap; /* swap space used (Kb) */ count_t vlock; /* virtual locked (Kb) */ count_t cfuture[7]; /* reserved for future use */ } mem; /* NETWORK STATISTICS */ struct net { count_t tcpsnd; /* number of TCP-packets sent */ count_t tcpssz; /* cumulative size packets sent */ count_t tcprcv; /* number of TCP-packets recved */ count_t tcprsz; /* cumulative size packets rcvd */ count_t udpsnd; /* number of UDP-packets sent */ count_t udpssz; /* cumulative size packets sent */ count_t udprcv; /* number of UDP-packets recved */ count_t udprsz; /* cumulative size packets sent */ count_t avail1; /* */ count_t avail2; /* */ count_t cfuture[4]; /* reserved for future use */ } net; struct gpu { char state; // A - active, E - Exit, '\0' - no use char bfuture[3]; // short nrgpus; // number of GPUs for this process int32_t gpulist; // bitlist with GPU numbers int gpubusy; // gpu busy perc process lifetime -1 = n/a int membusy; // memory busy perc process lifetime -1 = n/a count_t timems; // milliseconds accounting -1 = n/a // value 0 for active process, // value > 0 after termination count_t memnow; // current memory consumption in KiB count_t memcum; // cumulative memory consumption in KiB count_t sample; // number of samples count_t cfuture[3]; // } gpu; }; struct pinfo { struct pinfo *phnext; /* next process in hash chain */ struct pinfo *prnext; /* next process in residue chain */ struct pinfo *prprev; /* prev process in residue chain */ struct tstat tstat; /* per-process statistics */ }; /* ** structure to maintains all deviation info related to one sample */ struct devtstat { struct tstat *taskall; // all processes (also exited) and threads struct tstat **procall; // all processes (also exited) struct tstat **procactive; // all processes (also exited) being active unsigned long ntaskall; unsigned long ntaskactive; unsigned long nprocall; unsigned long nprocactive; unsigned long totrun, totslpi, totslpu, totidle, totzombie; }; /* ** prototypes of process-database functions */ int pdb_gettask(int, char, time_t, struct pinfo **); void pdb_addtask(int, struct pinfo *); int pdb_deltask(int, char); int pdb_makeresidue(void); int pdb_cleanresidue(void); int pdb_srchresidue(struct tstat *, struct pinfo **); /* ** prototypes for raw process-statistics functions */ struct netpertask; void deviattask(struct tstat *, unsigned long, struct tstat *, unsigned long, struct devtstat *, struct sstat *); unsigned long photoproc(struct tstat *, int); unsigned long counttasks(void); #endif atop-2.12.1/photosyst.c0000644000203100020310000021561715064552761014307 0ustar gerlofgerlof/* ** ATOP - System & Process Monitor ** ** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as process-level. ** ** This source-file contains functions to read all relevant system-level ** figures. ** ========================================================================== ** Author: Gerlof Langeveld ** E-mail: gerlof.langeveld@atoptool.nl ** Date: November 1996 ** LINUX-port: June 2000 ** -------------------------------------------------------------------------- ** Copyright (C) 2000-2024 Gerlof Langeveld ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ** -------------------------------------------------------------------------- */ #define _POSIX_C_SOURCE #define _XOPEN_SOURCE #define _GNU_SOURCE #define _DEFAULT_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define SCALINGMAXCPU 8 // threshold for scaling info per CPU #ifndef NOPERFEVENT #include #include #endif #include #include #include // #define _GNU_SOURCE #include #include #include "atop.h" #include "ifprop.h" #include "photosyst.h" #define MAXCNT 64 /* recognize numa node */ #define NUMADIR "/sys/devices/system/node" /* recognize large huge pages */ #define HUGEPAGEDIR "/sys/kernel/mm/hugepages" /* recognize LLC monitor data */ #define LLCDIR "/sys/fs/resctrl/mon_data" #define L3SIZE "/sys/devices/system/cpu/cpu0/cache/index3/size" /* Refer to mmzone.h, the default is 11 */ #define MAX_ORDER 11 #ifndef NOPERFEVENT enum { PERF_EVENTS_AUTO = 0, PERF_EVENTS_ENABLE, PERF_EVENTS_DISABLE, }; static int perfevents = PERF_EVENTS_AUTO; static long perf_event_open(struct perf_event_attr *, pid_t, int, int, unsigned long); static void getperfevents(struct cpustat *); #endif static int get_infiniband(struct ifbstat *); static int get_ksm(struct sstat *); static int isdisk_name(unsigned int, unsigned int, char *, struct perdsk *, int); static struct bitmask *numa_allocate_cpumask(void); static void numa_bitmask_free(struct bitmask *); static int numa_parse_bitmap_v2(char *, struct bitmask *); static struct ipv6_stats ipv6_tmp; static struct icmpv6_stats icmpv6_tmp; static struct udpv6_stats udpv6_tmp; struct v6tab { char *nam; count_t *val; }; static struct v6tab v6tab[] = { {"Ip6InReceives", &ipv6_tmp.Ip6InReceives, }, {"Ip6InHdrErrors", &ipv6_tmp.Ip6InHdrErrors, }, {"Ip6InTooBigErrors", &ipv6_tmp.Ip6InTooBigErrors, }, {"Ip6InNoRoutes", &ipv6_tmp.Ip6InNoRoutes, }, {"Ip6InAddrErrors", &ipv6_tmp.Ip6InAddrErrors, }, {"Ip6InUnknownProtos", &ipv6_tmp.Ip6InUnknownProtos, }, {"Ip6InTruncatedPkts", &ipv6_tmp.Ip6InTruncatedPkts, }, {"Ip6InDiscards", &ipv6_tmp.Ip6InDiscards, }, {"Ip6InDelivers", &ipv6_tmp.Ip6InDelivers, }, {"Ip6OutForwDatagrams", &ipv6_tmp.Ip6OutForwDatagrams, }, {"Ip6OutRequests", &ipv6_tmp.Ip6OutRequests, }, {"Ip6OutDiscards", &ipv6_tmp.Ip6OutDiscards, }, {"Ip6OutNoRoutes", &ipv6_tmp.Ip6OutNoRoutes, }, {"Ip6ReasmTimeout", &ipv6_tmp.Ip6ReasmTimeout, }, {"Ip6ReasmReqds", &ipv6_tmp.Ip6ReasmReqds, }, {"Ip6ReasmOKs", &ipv6_tmp.Ip6ReasmOKs, }, {"Ip6ReasmFails", &ipv6_tmp.Ip6ReasmFails, }, {"Ip6FragOKs", &ipv6_tmp.Ip6FragOKs, }, {"Ip6FragFails", &ipv6_tmp.Ip6FragFails, }, {"Ip6FragCreates", &ipv6_tmp.Ip6FragCreates, }, {"Ip6InMcastPkts", &ipv6_tmp.Ip6InMcastPkts, }, {"Ip6OutMcastPkts", &ipv6_tmp.Ip6OutMcastPkts, }, {"Icmp6InMsgs", &icmpv6_tmp.Icmp6InMsgs, }, {"Icmp6InErrors", &icmpv6_tmp.Icmp6InErrors, }, {"Icmp6InDestUnreachs", &icmpv6_tmp.Icmp6InDestUnreachs, }, {"Icmp6InPktTooBigs", &icmpv6_tmp.Icmp6InPktTooBigs, }, {"Icmp6InTimeExcds", &icmpv6_tmp.Icmp6InTimeExcds, }, {"Icmp6InParmProblems", &icmpv6_tmp.Icmp6InParmProblems, }, {"Icmp6InEchos", &icmpv6_tmp.Icmp6InEchos, }, {"Icmp6InEchoReplies", &icmpv6_tmp.Icmp6InEchoReplies, }, {"Icmp6InGroupMembQueries", &icmpv6_tmp.Icmp6InGroupMembQueries, }, {"Icmp6InGroupMembResponses", &icmpv6_tmp.Icmp6InGroupMembResponses, }, {"Icmp6InGroupMembReductions", &icmpv6_tmp.Icmp6InGroupMembReductions, }, {"Icmp6InRouterSolicits", &icmpv6_tmp.Icmp6InRouterSolicits, }, {"Icmp6InRouterAdvertisements", &icmpv6_tmp.Icmp6InRouterAdvertisements, }, {"Icmp6InNeighborSolicits", &icmpv6_tmp.Icmp6InNeighborSolicits, }, {"Icmp6InNeighborAdvertisements", &icmpv6_tmp.Icmp6InNeighborAdvertisements, }, {"Icmp6InRedirects", &icmpv6_tmp.Icmp6InRedirects, }, {"Icmp6OutMsgs", &icmpv6_tmp.Icmp6OutMsgs, }, {"Icmp6OutDestUnreachs", &icmpv6_tmp.Icmp6OutDestUnreachs, }, {"Icmp6OutPktTooBigs", &icmpv6_tmp.Icmp6OutPktTooBigs, }, {"Icmp6OutTimeExcds", &icmpv6_tmp.Icmp6OutTimeExcds, }, {"Icmp6OutParmProblems", &icmpv6_tmp.Icmp6OutParmProblems, }, {"Icmp6OutEchoReplies", &icmpv6_tmp.Icmp6OutEchoReplies, }, {"Icmp6OutRouterSolicits", &icmpv6_tmp.Icmp6OutRouterSolicits, }, {"Icmp6OutNeighborSolicits",&icmpv6_tmp.Icmp6OutNeighborSolicits, }, {"Icmp6OutNeighborAdvertisements", &icmpv6_tmp.Icmp6OutNeighborAdvertisements, }, {"Icmp6OutRedirects", &icmpv6_tmp.Icmp6OutRedirects, }, {"Icmp6OutGroupMembResponses", &icmpv6_tmp.Icmp6OutGroupMembResponses, }, {"Icmp6OutGroupMembReductions", &icmpv6_tmp.Icmp6OutGroupMembReductions, }, {"Udp6InDatagrams", &udpv6_tmp.Udp6InDatagrams, }, {"Udp6NoPorts", &udpv6_tmp.Udp6NoPorts, }, {"Udp6InErrors", &udpv6_tmp.Udp6InErrors, }, {"Udp6OutDatagrams", &udpv6_tmp.Udp6OutDatagrams, }, }; static int v6tab_entries = sizeof(v6tab)/sizeof(struct v6tab); // The following values are used to accumulate cpu statistics per numa. // The bitmask realization is from numactl #define CPUMASK_SZ (64 * 8) #define bitsperlong (8 * sizeof(unsigned long)) #define howmany(x,y) (((x)+((y)-1))/(y)) #define longsperbits(n) howmany(n, bitsperlong) #define round_up(x,y) (((x) + (y) - 1) & ~((y)-1)) #define BITS_PER_LONG (sizeof(unsigned long) * 8) #define CPU_BYTES(x) (round_up(x, BITS_PER_LONG)/8) #define CPU_LONGS(x) (CPU_BYTES(x) / sizeof(long)) struct bitmask { unsigned long size; /* number of bits in the map */ unsigned long long *maskp; }; /* * Allocate a bitmask for cpus, of a size large enough to * match the kernel's cpumask_t. */ static struct bitmask * numa_allocate_cpumask(void) { int ncpus = CPUMASK_SZ; struct bitmask *bmp; bmp = malloc(sizeof(*bmp)); if (!bmp) ptrverify(bmp, "Malloc failed for numa bitmask"); bmp->size = ncpus; bmp->maskp = calloc(longsperbits(ncpus), sizeof(unsigned long)); ptrverify((bmp->maskp), "Malloc failed for numa maskp"); return bmp; } static void numa_bitmask_free(struct bitmask *bmp) { if (bmp == 0) return; free(bmp->maskp); bmp->maskp = (unsigned long long *)0xdeadcdef; /* double free tripwire */ free(bmp); return; } static int numa_parse_bitmap_v2(char *line, struct bitmask *mask) { int i; char *p = strchr(line, '\n'); if (!p) return -1; int ncpus = mask->size; for (i = 0; p > line;i++) { char *oldp, *endp; oldp = p; if (*p == ',') --p; while (p > line && *p != ',') --p; /* Eat two 32bit fields at a time to get longs */ if (p > line && sizeof(unsigned long) == 8) { oldp--; memmove(p, p+1, oldp-p+1); while (p > line && *p != ',') --p; } if (*p == ',') p++; if (i >= CPU_LONGS(ncpus)) return -1; mask->maskp[i] = strtoul(p, &endp, 16); if (endp != oldp) return -1; p--; } return 0; } void photosyst(struct sstat *si) { static char part_stats = 1; /* per-partition statistics ? */ static char ib_stats = 1; /* InfiniBand statistics ? */ static char ksm_stats = 1; static char *lhugepagetot; /* name of large hugepage dir total */ /* might be -1 if not applicable */ static char *lhugepagefree; /* name of large hugepage dir free */ /* might be -1 if not applicable */ static unsigned int hpsize; register int i, nr, j; count_t cnts[MAXCNT]; float lavg1, lavg5, lavg15; FILE *fp; DIR *dirp; struct dirent *dentry; char linebuf[1024], nam[64], origdir[4096]; unsigned int major, minor; struct shm_info shminfo; #if HTTPSTATS static int wwwvalid = 1; #endif memset(si, 0, sizeof(struct sstat)); if ( getcwd(origdir, sizeof origdir) == NULL) mcleanstop(54, "failed to save current dir\n"); if ( chdir("/proc") == -1) mcleanstop(54, "failed to change to /proc\n"); /* ** gather various general statistics from the file /proc/stat and ** store them in binary form */ if ( (fp = fopen("stat", "r")) != NULL) { while ( fgets(linebuf, sizeof(linebuf), fp) != NULL) { nr = sscanf(linebuf, "%s %lld %lld %lld %lld %lld %lld %lld " "%lld %lld %lld %lld %lld %lld %lld %lld ", nam, &cnts[0], &cnts[1], &cnts[2], &cnts[3], &cnts[4], &cnts[5], &cnts[6], &cnts[7], &cnts[8], &cnts[9], &cnts[10], &cnts[11], &cnts[12], &cnts[13], &cnts[14]); if (nr < 2) /* headerline ? --> skip */ continue; if ( strcmp("cpu", nam) == EQ) { si->cpu.all.utime = cnts[0]; si->cpu.all.ntime = cnts[1]; si->cpu.all.stime = cnts[2]; si->cpu.all.itime = cnts[3]; if (nr > 5) /* 2.6 kernel? */ { si->cpu.all.wtime = cnts[4]; si->cpu.all.Itime = cnts[5]; si->cpu.all.Stime = cnts[6]; if (nr > 8) /* steal support */ si->cpu.all.steal = cnts[7]; if (nr > 9) /* guest support */ si->cpu.all.guest = cnts[8]; } continue; } if ( strncmp("cpu", nam, 3) == EQ) { i = atoi(&nam[3]); if (i >= MAXCPU) { fprintf(stderr, "cpu %s exceeds maximum of %d\n", nam, MAXCPU); continue; } si->cpu.cpu[i].cpunr = i; si->cpu.cpu[i].utime = cnts[0]; si->cpu.cpu[i].ntime = cnts[1]; si->cpu.cpu[i].stime = cnts[2]; si->cpu.cpu[i].itime = cnts[3]; if (nr > 5) /* 2.6 kernel? */ { si->cpu.cpu[i].wtime = cnts[4]; si->cpu.cpu[i].Itime = cnts[5]; si->cpu.cpu[i].Stime = cnts[6]; if (nr > 8) /* steal support */ si->cpu.cpu[i].steal = cnts[7]; if (nr > 9) /* guest support */ si->cpu.cpu[i].guest = cnts[8]; } si->cpu.nrcpu++; continue; } if ( strcmp("ctxt", nam) == EQ) { si->cpu.csw = cnts[0]; continue; } if ( strcmp("intr", nam) == EQ) { si->cpu.devint = cnts[0]; continue; } if ( strcmp("processes", nam) == EQ) { si->cpu.nprocs = cnts[0]; continue; } if ( strcmp("swap", nam) == EQ) /* < 2.6 */ { si->mem.swins = cnts[0]; si->mem.swouts = cnts[1]; continue; } } fclose(fp); if (si->cpu.nrcpu == 0) si->cpu.nrcpu = 1; } /* ** gather loadaverage values from the file /proc/loadavg and ** store them in binary form */ if ( (fp = fopen("loadavg", "r")) != NULL) { if ( fgets(linebuf, sizeof(linebuf), fp) != NULL) { if ( sscanf(linebuf, "%f %f %f", &lavg1, &lavg5, &lavg15) == 3) { si->cpu.lavg1 = lavg1; si->cpu.lavg5 = lavg5; si->cpu.lavg15 = lavg15; } } fclose(fp); } /* ** gather frequency scaling info. ** sources (in order of preference): ** /sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state ** or ** /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq ** /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq ** ** store them in binary form */ static char fn[512]; int didone=0; // check governor statistics for (i = 0; i < si->cpu.nrcpu; ++i) { long long f=0; snprintf(fn, sizeof fn, "/sys/devices/system/cpu/cpu%d/cpufreq/stats/time_in_state", i); if ((fp=fopen(fn, "r")) != 0) { long long hits=0; long long maxfreq=0; long long cnt=0; long long sum=0; while (fscanf(fp, "%lld %lld", &f, &cnt) == 2) { f /= 1000; sum += (f*cnt); hits += cnt; if (f > maxfreq) maxfreq=f; } si->cpu.cpu[i].freqcnt.maxfreq = maxfreq; si->cpu.cpu[i].freqcnt.cnt = sum; si->cpu.cpu[i].freqcnt.ticks = hits; fclose(fp); didone=1; } else { break; } } if (!didone) // did not get processor freq statistics yet { long long f=0; for (i = 0; i < si->cpu.nrcpu; ++i) { snprintf(fn, sizeof fn, "/sys/devices/system/cpu/cpu%d/cpufreq/cpuinfo_max_freq", i); if ((fp=fopen(fn, "r")) != 0) { if (fscanf(fp, "%lld", &f) == 1) { // convert KHz to MHz si->cpu.cpu[i].freqcnt.maxfreq =f/1000; } fclose(fp); } else { si->cpu.cpu[i].freqcnt.maxfreq=0; } snprintf(fn, sizeof fn, "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_cur_freq", i); if ((fp=fopen(fn, "r")) != 0) { if (fscanf(fp, "%lld", &f) == 1) { // convert KHz to MHz si->cpu.cpu[i].freqcnt.cnt = f/1000; si->cpu.cpu[i].freqcnt.ticks = 0; } fclose(fp); didone=1; } else { si->cpu.cpu[i].freqcnt.cnt = 0; si->cpu.cpu[i].freqcnt.ticks = 0; } } } // for all CPUs if (!didone) // did not get processor freq statistics. // use /proc/cpuinfo { if ( (fp = fopen("cpuinfo", "r")) != NULL) { // get information from the lines // processor\t: 0 // cpu MHz\t\t: 800.000 int cpuno=-1; while ( fgets(linebuf, sizeof(linebuf), fp) != NULL) { if (memcmp(linebuf, "processor", 9)== EQ) sscanf(linebuf, "%*s %*s %d", &cpuno); if (memcmp(linebuf, "cpu MHz", 7) == EQ) { if (cpuno >= 0 && cpuno < si->cpu.nrcpu) { sscanf(linebuf, "%*s %*s %*s %lld", &(si->cpu.cpu[cpuno].freqcnt.cnt)); } } } fclose(fp); } } /* ** gather virtual memory statistics from the file /proc/vmstat and ** store them in binary form (>= kernel 2.6) */ si->mem.oomkills = -1; si->mem.allocstall = 0; si->mem.numamigrate = 0; si->mem.pgmigrate = 0; if ( (fp = fopen("vmstat", "r")) != NULL) { while ( fgets(linebuf, sizeof(linebuf), fp) != NULL) { nr = sscanf(linebuf, "%s %lld", nam, &cnts[0]); if (nr < 2) /* headerline ? --> skip */ continue; /* pgpgin & pgpgout fields in KB from vmstat */ if ( strcmp("pgpgin", nam) == EQ) { si->mem.pgins = cnts[0] * 1024 / pagesize; continue; } if ( strcmp("pgpgout", nam) == EQ) { si->mem.pgouts = cnts[0] * 1024 / pagesize; continue; } if ( strcmp("pswpin", nam) == EQ) { si->mem.swins = cnts[0]; continue; } if ( strcmp("pswpout", nam) == EQ) { si->mem.swouts = cnts[0]; continue; } if ( strncmp("pgscan_kswapd", nam, 13) == EQ) { si->mem.pgscans += cnts[0]; continue; } if ( strncmp("pgscan_direct", nam, 13) == EQ) { si->mem.pgscans += cnts[0]; continue; } if ( strncmp("pgscan_khugepaged", nam, 17) == EQ) { si->mem.pgscans += cnts[0]; continue; } if ( strncmp("pgsteal_kswapd", nam, 14) == EQ) { si->mem.pgsteal += cnts[0]; continue; } if ( strncmp("pgsteal_direct", nam, 14) == EQ) { si->mem.pgsteal += cnts[0]; continue; } if ( strncmp("pgsteal_khugepaged", nam, 18) == EQ) { si->mem.pgsteal += cnts[0]; continue; } // more counters might start with "allocstall" if ( memcmp("allocstall", nam, 10) == EQ) { si->mem.allocstall += cnts[0]; continue; } if ( strcmp("oom_kill", nam) == EQ) { si->mem.oomkills = cnts[0]; continue; } if ( strcmp("compact_stall", nam) == EQ) { si->mem.compactstall = cnts[0]; continue; } if ( strcmp("numa_pages_migrated", nam) == EQ) { si->mem.numamigrate = cnts[0]; continue; } if ( strcmp("pgmigrate_success", nam) == EQ) { si->mem.pgmigrate = cnts[0]; continue; } if ( strcmp("zswpout", nam) == EQ) { si->mem.zswouts = cnts[0]; continue; } if ( strcmp("zswpin", nam) == EQ) { si->mem.zswins = cnts[0]; continue; } } fclose(fp); } /* ** gather memory-related statistics from the file /proc/meminfo and ** store them in binary form ** ** in the file /proc/meminfo a 2.4 kernel starts with two lines ** headed by the strings "Mem:" and "Swap:" containing all required ** fields, except proper value for page cache ** if these lines are present we try to skip parsing the rest ** of the lines; if these lines are not present we should get the ** required field from other lines */ si->mem.physmem = (count_t)-1; si->mem.freemem = (count_t)-1; si->mem.buffermem = (count_t)-1; si->mem.cachemem = (count_t)-1; si->mem.slabmem = (count_t) 0; si->mem.slabreclaim = (count_t) 0; si->mem.shmem = (count_t) 0; si->mem.totswap = (count_t)-1; si->mem.freeswap = (count_t)-1; si->mem.swapcached = (count_t) 0; si->mem.committed = (count_t) 0; si->mem.pagetables = (count_t) 0; if ( (fp = fopen("meminfo", "r")) != NULL) { while ( fgets(linebuf, sizeof(linebuf), fp) != NULL) { nr = sscanf(linebuf, "%s %lld %lld %lld %lld %lld %lld %lld " "%lld %lld %lld\n", nam, &cnts[0], &cnts[1], &cnts[2], &cnts[3], &cnts[4], &cnts[5], &cnts[6], &cnts[7], &cnts[8], &cnts[9]); if (nr < 2) /* headerline ? --> skip */ continue; if ( strcmp("Mem:", nam) == EQ) { si->mem.physmem = cnts[0] / pagesize; si->mem.freemem = cnts[2] / pagesize; si->mem.buffermem = cnts[4] / pagesize; } else if ( strcmp("Swap:", nam) == EQ) { si->mem.totswap = cnts[0] / pagesize; si->mem.freeswap = cnts[2] / pagesize; } else if (strcmp("Cached:", nam) == EQ) { if (si->mem.cachemem == (count_t)-1) { si->mem.cachemem = cnts[0]*1024/pagesize; } } else if (strcmp("Dirty:", nam) == EQ) { si->mem.cachedrt = cnts[0]*1024/pagesize; } else if (strcmp("MemTotal:", nam) == EQ) { if (si->mem.physmem == (count_t)-1) { si->mem.physmem = cnts[0]*1024/pagesize; } } else if (strcmp("MemFree:", nam) == EQ) { if (si->mem.freemem == (count_t)-1) { si->mem.freemem = cnts[0]*1024/pagesize; } } else if (strcmp("MemAvailable:", nam) == EQ) { si->mem.availablemem = cnts[0]*1024/pagesize; } else if (strcmp("Buffers:", nam) == EQ) { if (si->mem.buffermem == (count_t)-1) { si->mem.buffermem = cnts[0]*1024/pagesize; } } else if (strcmp("Shmem:", nam) == EQ) { si->mem.shmem = cnts[0]*1024/pagesize; } else if (strcmp("SwapTotal:", nam) == EQ) { if (si->mem.totswap == (count_t)-1) { si->mem.totswap = cnts[0]*1024/pagesize; } } else if (strcmp("SwapFree:", nam) == EQ) { if (si->mem.freeswap == (count_t)-1) { si->mem.freeswap = cnts[0]*1024/pagesize; } } else if (strcmp("SwapCached:", nam) == EQ) { si->mem.swapcached = cnts[0]*1024/pagesize; } else if (strcmp("Slab:", nam) == EQ) { si->mem.slabmem = cnts[0]*1024/pagesize; } else if (strcmp("SReclaimable:", nam) == EQ) { si->mem.slabreclaim = cnts[0]*1024/ pagesize; } else if (strcmp("Committed_AS:", nam) == EQ) { si->mem.committed = cnts[0]*1024/ pagesize; } else if (strcmp("CommitLimit:", nam) == EQ) { si->mem.commitlim = cnts[0]*1024/ pagesize; } else if (strcmp("AnonHugePages:", nam) == EQ) { si->mem.anonhugepage = cnts[0]*1024/ pagesize; } else if (strcmp("HugePages_Total:", nam) == EQ) { si->mem.stothugepage = cnts[0]; } else if (strcmp("HugePages_Free:", nam) == EQ) { si->mem.sfreehugepage = cnts[0]; } else if (strcmp("Hugepagesize:", nam) == EQ) { si->mem.shugepagesz = cnts[0]*1024; } else if (strcmp("PageTables:", nam) == EQ) { si->mem.pagetables = cnts[0]*1024/ pagesize; } else if (strcmp("Zswap:", nam) == EQ) { si->mem.zswap = cnts[0]*1024/ pagesize; } else if (strcmp("Zswapped:", nam) == EQ) { si->mem.zswapped = cnts[0]*1024/ pagesize; } } fclose(fp); } /* ** gather values of larger huge pages that are not provided ** by /proc/meminfo ** ** all required hugepage info can be found in: ** ** /sys/kernel/mm/hugepages/hugepages-1048576kB ** /free_hugepages ** /nr_hugepages ** ** /sys/kernel/mm/hugepages/hugepages-2048kB ** /free_hugepages ** /nr_hugepages ** ** the info about the smaller hugepages (2M) is already ** read from /proc/memory ** ** first time initialization: ** find out if there is a large hugepage directory */ if (!lhugepagetot) { lhugepagetot = (char *) -1; // should be overwritten lhugepagefree = (char *) -1; // should be overwritten dirp = opendir(HUGEPAGEDIR); if (dirp) { /* ** read directory entries that start with "hugepages-" */ while ( (dentry = readdir(dirp)) ) { if (strncmp(dentry->d_name, "hugepages-", 10)) continue; hpsize = strtoul(dentry->d_name+10, NULL, 0) * 1024; if (hpsize == si->mem.shugepagesz) continue; // values already known /* ** directory name of larger hugepages found: ** store for use in subsequent intervals */ lhugepagetot = malloc(sizeof HUGEPAGEDIR + 1 + strlen(dentry->d_name) + 1 + sizeof "nr_hugepages" + 1); ptrverify(lhugepagetot, "Malloc failed for huge page total"); snprintf(lhugepagetot, sizeof HUGEPAGEDIR + 1 + strlen(dentry->d_name) + 1 + sizeof "nr_hugepages" + 1, "%s/%s/nr_hugepages", HUGEPAGEDIR, dentry->d_name); lhugepagefree = malloc(sizeof HUGEPAGEDIR + 1 + strlen(dentry->d_name) + 1 + sizeof "free_hugepages" + 1); ptrverify(lhugepagefree, "Malloc failed for huge page free"); snprintf(lhugepagefree, sizeof HUGEPAGEDIR + 1 + strlen(dentry->d_name) + 1 + sizeof "free_hugepages" + 1, "%s/%s/free_hugepages", HUGEPAGEDIR, dentry->d_name); break; } closedir(dirp); } } // large hugepages for each interval: // if ( lhugepagetot != (char *)-1 && (fp = fopen(lhugepagetot, "r")) != NULL) { if ( fgets(linebuf, sizeof(linebuf), fp) != NULL) nr = sscanf(linebuf, "%lld", &si->mem.ltothugepage); fclose(fp); } if ( lhugepagefree != (char *)-1 && (fp = fopen(lhugepagefree, "r")) != NULL) { if ( fgets(linebuf, sizeof(linebuf), fp) != NULL) nr = sscanf(linebuf, "%lld", &si->mem.lfreehugepage); fclose(fp); } si->mem.lhugepagesz = hpsize; /* ** gather vmware-related statistics from /sys/kernel/debug/vmmemctl ** (only present if balloon driver enabled) or from /proc/vmmemctl ** for older balloon driver implementations */ si->mem.vmwballoon = (count_t) -1; if ( (fp = fopen("/sys/kernel/debug/vmmemctl", "r")) != NULL || (fp = fopen("/proc/vmmemctl", "r")) != NULL ) { while ( fgets(linebuf, sizeof(linebuf), fp) != NULL) { nr = sscanf(linebuf, "%s %lld ", nam, &cnts[0]); if ( strcmp("current:", nam) == EQ) { si->mem.vmwballoon = cnts[0]; break; } } fclose(fp); } /* ** ZFSonlinux: gather size of ARC cache in memory ** searching for: ** size 4 519101312 */ si->mem.zfsarcsize = (count_t) -1; if ( (fp = fopen("spl/kstat/zfs/arcstats", "r")) != NULL) { while ( fgets(linebuf, sizeof(linebuf), fp) != NULL) { nr = sscanf(linebuf, "%s %lld %lld", nam, &cnts[0], &cnts[1]); if (nr < 3) continue; if ( strcmp("size", nam) == EQ) { si->mem.zfsarcsize = cnts[1] / pagesize; break; } } fclose(fp); } /* ** gather per numa memory-related statistics from the file ** /sys/devices/system/node/node0/meminfo, and store them in binary form. */ dirp = opendir(NUMADIR); if (dirp) { /* ** read every directory-entry and search for all numa nodes */ while ( (dentry = readdir(dirp)) ) { if (strncmp(dentry->d_name, "node", 4)) continue; j = strtoul(dentry->d_name + 4, NULL, 0); if (j >= MAXNUMA) // too many NUMA nodes? continue; // skip (no break, because order unknown) si->memnuma.nrnuma++; snprintf(fn, sizeof fn, NUMADIR "/%s/meminfo", dentry->d_name); if ( (fp = fopen(fn, "r")) != NULL) { while ( fgets(linebuf, sizeof(linebuf), fp) != NULL) { nr = sscanf(&linebuf[5], "%lld %s %lld\n", &cnts[0], nam, &cnts[1]); if (cnts[0] != j) continue; si->memnuma.numa[j].numanr = j; if ( strcmp("MemTotal:", nam) == EQ) si->memnuma.numa[j].totmem = cnts[1]*1024/pagesize; else if ( strcmp("MemFree:", nam) == EQ) si->memnuma.numa[j].freemem = cnts[1]*1024/pagesize; else if ( strcmp("FilePages:", nam) == EQ) si->memnuma.numa[j].filepage = cnts[1]*1024/pagesize; else if ( strcmp("Active:", nam) == EQ) si->memnuma.numa[j].active = cnts[1]*1024/pagesize; else if ( strcmp("Inactive:", nam) == EQ) si->memnuma.numa[j].inactive = cnts[1]*1024/pagesize; else if ( strcmp("Dirty:", nam) == EQ) si->memnuma.numa[j].dirtymem = cnts[1]*1024/pagesize; else if ( strcmp("Shmem:", nam) == EQ) si->memnuma.numa[j].shmem = cnts[1]*1024/pagesize; else if ( strcmp("Slab:", nam) == EQ) si->memnuma.numa[j].slabmem = cnts[1]*1024/pagesize; else if ( strcmp("SReclaimable:", nam) == EQ) si->memnuma.numa[j].slabreclaim = cnts[1]*1024/pagesize; else if ( strcmp("HugePages_Total:", nam) == EQ) si->memnuma.numa[j].tothp = cnts[1]; else if ( strcmp("HugePages_Free:", nam) == EQ) si->memnuma.numa[j].freehp = cnts[1]; } fclose(fp); } } closedir(dirp); } /* gather fragmentation level for per numa, only for 'Normal' */ if (si->memnuma.nrnuma > 0) { char tmp[64]; float frag[MAX_ORDER]; /* If kernel CONFIG_COMPACTION is enabled, get the percentage directly */ if ( (fp = fopen("/sys/kernel/debug/extfrag/unusable_index", "r")) != NULL ) { while ( fgets(linebuf, sizeof(linebuf), fp) != NULL ) { nr = sscanf(&linebuf[5], "%lld, %s %s %f %f %f %f %f %f %f %f %f %f %f\n", &cnts[0], tmp, nam, &frag[0], &frag[1], &frag[2], &frag[3], &frag[4], &frag[5], &frag[6], &frag[7], &frag[8], &frag[9], &frag[10]); if ( nr < 3 || strcmp("Normal", nam) != 0 ) continue; for (i = 0; i < MAX_ORDER; i++) si->memnuma.numa[cnts[0]].frag += frag[i]; si->memnuma.numa[cnts[0]].frag /= MAX_ORDER; } fclose(fp); } /* If CONFIG_COMPACTION is not enabled, calculate from buddyinfo file */ else if ( (fp = fopen("/proc/buddyinfo", "r")) != NULL ) { count_t free_page[MAX_ORDER]; count_t total_free, prev_free; float total_frag; while ( fgets(linebuf, sizeof(linebuf), fp) != NULL ) { nr = sscanf(&linebuf[5], "%lld, %s %s %lld %lld %lld %lld %lld " "%lld %lld %lld %lld %lld %lld\n", &cnts[0], tmp, nam, &cnts[1], &cnts[2], &cnts[3], &cnts[4], &cnts[5], &cnts[6], &cnts[7], &cnts[8], &cnts[9], &cnts[10], &cnts[11]); if (nr < 3 || strcmp("Normal", nam) != 0 ) continue; /* get free pages numbers for each order, and total free pages */ total_free = 0; total_frag = 0.00; for (i = 0; i < MAX_ORDER; i++) { free_page[i] = cnts[i+1] << i; total_free += free_page[i]; } /* get fragmentation level for each order, then summarize */ for (i = 0; i < MAX_ORDER; i++) { prev_free = 0; for (j = 0; j < i; j++) prev_free += free_page[j]; total_frag += (float)prev_free/total_free; } if (cnts[0] < MAXNUMA) si->memnuma.numa[cnts[0]].frag = total_frag/MAX_ORDER; } fclose(fp); } } /* ** accumulate each cpu statistic for per NUMA, and identify numa/cpu ** relationship from /sys/devices/system/node/node0/cpumap. */ if (si->memnuma.nrnuma > 1) { char *line = NULL; size_t len = 0; struct bitmask *mask; mask = numa_allocate_cpumask(); si->cpunuma.nrnuma = si->memnuma.nrnuma; for (j=0; j < si->cpunuma.nrnuma; j++) { snprintf(fn, sizeof fn, NUMADIR "/node%d/cpumap", j); if ( (fp = fopen(fn, "r")) != 0) { if ( getdelim(&line, &len, '\n', fp) > 0 ) { if (numa_parse_bitmap_v2(line, mask) < 0) { mcleanstop(54, "failed to parse numa bitmap\n"); } } fclose(fp); } for (i=0; i < mask->size; i++) { if ( (mask->maskp[i/bitsperlong] >> (i % bitsperlong)) & 1 ) { si->cpunuma.numa[j].nrcpu++; si->cpunuma.numa[j].utime += si->cpu.cpu[i].utime; si->cpunuma.numa[j].ntime += si->cpu.cpu[i].ntime; si->cpunuma.numa[j].stime += si->cpu.cpu[i].stime; si->cpunuma.numa[j].itime += si->cpu.cpu[i].itime; si->cpunuma.numa[j].wtime += si->cpu.cpu[i].wtime; si->cpunuma.numa[j].Itime += si->cpu.cpu[i].Itime; si->cpunuma.numa[j].Stime += si->cpu.cpu[i].Stime; si->cpunuma.numa[j].steal += si->cpu.cpu[i].steal; si->cpunuma.numa[j].guest += si->cpu.cpu[i].guest; } } si->cpunuma.numa[j].numanr = j; } free(line); numa_bitmask_free(mask); } else { si->cpunuma.nrnuma = 0; } /* ** gather network-related statistics ** - interface stats from the file /proc/net/dev ** - IPv4 stats from the file /proc/net/snmp ** - IPv6 stats from the file /proc/net/snmp6 ** - sock mem stats from the file /proc/net/sockstat */ /* ** interface statistics */ initifprop(); // periodically refresh interface properties if ( (fp = fopen("net/dev", "r")) != NULL) { struct ifprop ifprop; char *cp; i = 0; while ( fgets(linebuf, sizeof(linebuf), fp) != NULL) { if ( (cp = strchr(linebuf, ':')) != NULL) *cp = ' '; /* substitute ':' by space */ nr = sscanf(linebuf, "%15s %lld %lld %lld %lld %lld %lld %lld " "%lld %lld %lld %lld %lld %lld %lld %lld " "%lld\n", si->intf.intf[i].name, &(si->intf.intf[i].rbyte), &(si->intf.intf[i].rpack), &(si->intf.intf[i].rerrs), &(si->intf.intf[i].rdrop), &(si->intf.intf[i].rfifo), &(si->intf.intf[i].rframe), &(si->intf.intf[i].rcompr), &(si->intf.intf[i].rmultic), &(si->intf.intf[i].sbyte), &(si->intf.intf[i].spack), &(si->intf.intf[i].serrs), &(si->intf.intf[i].sdrop), &(si->intf.intf[i].sfifo), &(si->intf.intf[i].scollis), &(si->intf.intf[i].scarrier), &(si->intf.intf[i].scompr)); /* ** skip header line and lines without stats */ if (nr != 17) continue; /* ** skip interfaces that are invalidated ** (mainly virtual interfaces) ** because the total number of interfaces ** exceeds the maximum supported by atop (MAXINTF) */ strcpy(ifprop.name, si->intf.intf[i].name); if (!getifprop(&ifprop)) continue; /* ** accept this interface but skip the remaining ** interfaces because we reached the total number ** of interfaces supported by atop (MAXINTF) */ if (++i >= MAXINTF-1) break; } si->intf.intf[i].name[0] = '\0'; /* set terminator for table */ si->intf.nrintf = i; fclose(fp); } /* ** IP version 4 statistics */ if ( (fp = fopen("net/snmp", "r")) != NULL) { while ( fgets(linebuf, sizeof(linebuf), fp) != NULL) { nr = sscanf(linebuf, "%s %lld %lld %lld %lld %lld %lld %lld %lld %lld " "%lld %lld %lld %lld %lld %lld %lld %lld %lld %lld " "%lld %lld %lld %lld %lld %lld %lld %lld %lld %lld " "%lld %lld %lld %lld %lld %lld %lld %lld %lld %lld " "%lld\n", nam, &cnts[0], &cnts[1], &cnts[2], &cnts[3], &cnts[4], &cnts[5], &cnts[6], &cnts[7], &cnts[8], &cnts[9], &cnts[10], &cnts[11], &cnts[12], &cnts[13], &cnts[14], &cnts[15], &cnts[16], &cnts[17], &cnts[18], &cnts[19], &cnts[20], &cnts[21], &cnts[22], &cnts[23], &cnts[24], &cnts[25], &cnts[26], &cnts[27], &cnts[28], &cnts[29], &cnts[30], &cnts[31], &cnts[32], &cnts[33], &cnts[34], &cnts[35], &cnts[36], &cnts[37], &cnts[38], &cnts[39]); if (nr < 2) /* headerline ? --> skip */ continue; if ( strcmp("Ip:", nam) == 0) { memcpy(&si->net.ipv4, cnts, sizeof si->net.ipv4); continue; } if ( strcmp("Icmp:", nam) == 0) { memcpy(&si->net.icmpv4, cnts, sizeof si->net.icmpv4); continue; } if ( strcmp("Tcp:", nam) == 0) { memcpy(&si->net.tcp, cnts, sizeof si->net.tcp); continue; } if ( strcmp("Udp:", nam) == 0) { memcpy(&si->net.udpv4, cnts, sizeof si->net.udpv4); continue; } } fclose(fp); } /* ** IP version 6 statistics */ memset(&ipv6_tmp, 0, sizeof ipv6_tmp); memset(&icmpv6_tmp, 0, sizeof icmpv6_tmp); memset(&udpv6_tmp, 0, sizeof udpv6_tmp); if ( (fp = fopen("net/snmp6", "r")) != NULL) { count_t countval; int cur = 0; /* ** one name-value pair per line */ while ( fgets(linebuf, sizeof(linebuf), fp) != NULL) { nr = sscanf(linebuf, "%s %lld", nam, &countval); if (nr < 2) /* unexpected line ? --> skip */ continue; if (strcmp(v6tab[cur].nam, nam) == 0) { *(v6tab[cur].val) = countval; } else { for (cur=0; cur < v6tab_entries; cur++) if (strcmp(v6tab[cur].nam, nam) == 0) break; if (cur < v6tab_entries) /* found ? */ *(v6tab[cur].val) = countval; } if (++cur >= v6tab_entries) cur = 0; } memcpy(&si->net.ipv6, &ipv6_tmp, sizeof ipv6_tmp); memcpy(&si->net.icmpv6, &icmpv6_tmp, sizeof icmpv6_tmp); memcpy(&si->net.udpv6, &udpv6_tmp, sizeof udpv6_tmp); fclose(fp); } /* ** IP version 4: TCP & UDP memory allocations. */ if ( (fp = fopen("net/sockstat", "r")) != NULL) { char tcpmem[16], udpmem[16]; while ( fgets(linebuf, sizeof(linebuf), fp) != NULL) { nr = sscanf(linebuf, "%15s %*s %*d %s %lld %*s %*d %*s %*d %s %lld\n", nam, udpmem, &cnts[0], tcpmem, &cnts[1]); if ( strcmp("TCP:", nam) == 0) { if ( strcmp("mem", tcpmem) == 0) { si->mem.tcpsock = cnts[1]; } continue; } if ( strcmp("UDP:", nam) == 0) { if ( strcmp("mem", udpmem) == 0) { si->mem.udpsock = cnts[0]; } continue; } } fclose(fp); } /* ** check if extended partition-statistics are provided < kernel 2.6 */ if ( part_stats && (fp = fopen("partitions", "r")) != NULL) { char diskname[256]; i = 0; while ( fgets(linebuf, sizeof(linebuf), fp) ) { nr = sscanf(linebuf, "%*d %*d %*d %255s %lld %*d %lld %*d " "%lld %*d %lld %*d %lld %lld %lld", diskname, &(si->dsk.dsk[i].nread), &(si->dsk.dsk[i].nrsect), &(si->dsk.dsk[i].nwrite), &(si->dsk.dsk[i].nwsect), &(si->dsk.dsk[i].inflight), &(si->dsk.dsk[i].io_ms), &(si->dsk.dsk[i].avque) ); /* ** check if this line concerns the entire disk ** or just one of the partitions of a disk (to be ** skipped) */ if (nr == 8) /* full stats-line ? */ { if ( isdisk_name(0, 0, diskname, &(si->dsk.dsk[i]), MAXDKNAM) != DSKTYPE) continue; if (++i >= MAXDSK-1) break; } } si->dsk.dsk[i].name[0] = '\0'; /* set terminator for table */ si->dsk.ndsk = i; fclose(fp); if (i == 0) part_stats = 0; /* do not try again for next cycles */ } /* ** check if disk-statistics are provided (kernel 2.6 onwards) */ if ( (fp = fopen("diskstats", "r")) != NULL) { char diskname[256]; struct perdsk tmpdsk; si->dsk.ndsk = 0; si->dsk.nmdd = 0; si->dsk.nlvm = 0; while ( fgets(linebuf, sizeof(linebuf), fp) ) { /* discards are not supported in older kernels */ tmpdsk.ndisc = -1; nr = sscanf(linebuf, "%d %d %255s " // ident "%lld %*d %lld %*d " // reads "%lld %*d %lld %*d " // writes "%lld %lld %lld " // misc "%lld %*d %lld %*d", // discards &major, &minor, diskname, &tmpdsk.nread, &tmpdsk.nrsect, &tmpdsk.nwrite, &tmpdsk.nwsect, &tmpdsk.inflight, &tmpdsk.io_ms, &tmpdsk.avque, &tmpdsk.ndisc, &tmpdsk.ndsect); if (nr >= 10) /* full stats-line ? */ { /* ** when no transfers issued, skip disk (partition) */ if (tmpdsk.nread + tmpdsk.nwrite + (tmpdsk.ndisc == -1 ? 0 : tmpdsk.ndisc) == 0) continue; /* ** check if this line concerns the entire disk ** or just one of the partitions of a disk (to be ** skipped) */ switch ( isdisk_name(major, minor, diskname, &tmpdsk, MAXDKNAM) ) { case NONTYPE: continue; case DSKTYPE: if (si->dsk.ndsk < MAXDSK-1) si->dsk.dsk[si->dsk.ndsk++] = tmpdsk; break; case MDDTYPE: if (si->dsk.nmdd < MAXMDD-1) si->dsk.mdd[si->dsk.nmdd++] = tmpdsk; break; case LVMTYPE: if (si->dsk.nlvm < MAXLVM-1) si->dsk.lvm[si->dsk.nlvm++] = tmpdsk; break; } } } /* ** set terminator for table */ si->dsk.dsk[si->dsk.ndsk].name[0] = '\0'; si->dsk.mdd[si->dsk.nmdd].name[0] = '\0'; si->dsk.lvm[si->dsk.nlvm].name[0] = '\0'; fclose(fp); } /* ** get information about the shared memory statistics */ if ( shmctl(0, SHM_INFO, (struct shmid_ds *)&shminfo) != -1) { si->mem.shmrss = shminfo.shm_rss; si->mem.shmswp = shminfo.shm_swp; } /* ** NFS server statistics */ if ( (fp = fopen("net/rpc/nfsd", "r")) != NULL) { char label[32]; count_t cnt[40]; /* ** every line starts with a small label, ** followed by upto 60 counters */ while ( fgets(linebuf, sizeof(linebuf), fp) != NULL) { memset(cnt, 0, sizeof cnt); nr = sscanf(linebuf, "%31s %lld %lld %lld %lld %lld" "%lld %lld %lld %lld %lld" "%lld %lld %lld %lld %lld" "%lld %lld %lld %lld %lld" "%lld %lld %lld %lld %lld" "%lld %lld %lld %lld %lld" "%lld %lld %lld %lld %lld" "%lld %lld %lld %lld %lld", label, &cnt[0], &cnt[1], &cnt[2], &cnt[3], &cnt[4], &cnt[5], &cnt[6], &cnt[7], &cnt[8], &cnt[9], &cnt[10], &cnt[11], &cnt[12], &cnt[13], &cnt[14], &cnt[15], &cnt[16], &cnt[17], &cnt[18], &cnt[19], &cnt[20], &cnt[21], &cnt[22], &cnt[23], &cnt[24], &cnt[25], &cnt[26], &cnt[27], &cnt[28], &cnt[29], &cnt[30], &cnt[31], &cnt[32], &cnt[33], &cnt[34], &cnt[35], &cnt[36], &cnt[37], &cnt[38], &cnt[39]); if (nr < 2) // unexpected empty line ? continue; if (strcmp(label, "rc") == 0) { si->nfs.server.rchits = cnt[0]; si->nfs.server.rcmiss = cnt[1]; si->nfs.server.rcnoca = cnt[2]; continue; } if (strcmp(label, "io") == 0) { si->nfs.server.nrbytes = cnt[0]; si->nfs.server.nwbytes = cnt[1]; continue; } if (strcmp(label, "net") == 0) { si->nfs.server.netcnt = cnt[0]; si->nfs.server.netudpcnt = cnt[1]; si->nfs.server.nettcpcnt = cnt[2]; si->nfs.server.nettcpcon = cnt[3]; continue; } if (strcmp(label, "rpc") == 0) { si->nfs.server.rpccnt = cnt[0]; si->nfs.server.rpcbadfmt = cnt[1]; si->nfs.server.rpcbadaut = cnt[2]; si->nfs.server.rpcbadcln = cnt[3]; continue; } // // first counter behind 'proc..' is number of // counters that follow if (strcmp(label, "proc2") == 0) { si->nfs.server.rpcread += cnt[7]; // offset+1 si->nfs.server.rpcwrite += cnt[9]; // offset+1 continue; } if (strcmp(label, "proc3") == 0) { si->nfs.server.rpcread += cnt[7]; // offset+1 si->nfs.server.rpcwrite += cnt[8]; // offset+1 continue; } if (strcmp(label, "proc4ops") == 0) { si->nfs.server.rpcread += cnt[26]; // offset+1 si->nfs.server.rpcwrite += cnt[39]; // offset+1 continue; } } fclose(fp); } /* ** NFS client statistics */ if ( (fp = fopen("net/rpc/nfs", "r")) != NULL) { char label[32]; count_t cnt[10]; /* ** every line starts with a small label, ** followed by counters */ while ( fgets(linebuf, sizeof(linebuf), fp) != NULL) { memset(cnt, 0, sizeof cnt); nr = sscanf(linebuf, "%31s %lld %lld %lld %lld %lld" "%lld %lld %lld %lld %lld", label, &cnt[0], &cnt[1], &cnt[2], &cnt[3], &cnt[4], &cnt[5], &cnt[6], &cnt[7], &cnt[8], &cnt[9]); if (nr < 2) // unexpected empty line ? continue; if (strcmp(label, "rpc") == 0) { si->nfs.client.rpccnt = cnt[0]; si->nfs.client.rpcretrans = cnt[1]; si->nfs.client.rpcautrefresh = cnt[2]; continue; } // first counter behind 'proc..' is number of // counters that follow if (strcmp(label, "proc2") == 0) { si->nfs.client.rpcread += cnt[7]; // offset+1 si->nfs.client.rpcwrite += cnt[9]; // offset+1 continue; } if (strcmp(label, "proc3") == 0) { si->nfs.client.rpcread += cnt[7]; // offset+1 si->nfs.client.rpcwrite += cnt[8]; // offset+1 continue; } if (strcmp(label, "proc4") == 0) { si->nfs.client.rpcread += cnt[2]; // offset+1 si->nfs.client.rpcwrite += cnt[3]; // offset+1 continue; } } fclose(fp); } /* ** NFS client: per-mount statistics */ regainrootprivs(); if ( (fp = fopen("self/mountstats", "r")) != NULL) { char mountdev[128], fstype[32], label[32]; count_t cnt[8]; i = 0; while ( fgets(linebuf, sizeof(linebuf), fp) != NULL) { // if 'device' line, just remember the mounted device if (sscanf(linebuf, "device %127s mounted on %*s with fstype %31s", mountdev, fstype) == 2) { continue; } if (memcmp(fstype, "nfs", 3) != 0) continue; // this is line with NFS client stats nr = sscanf(linebuf, "%31s %lld %lld %lld %lld %lld %lld %lld %lld", label, &cnt[0], &cnt[1], &cnt[2], &cnt[3], &cnt[4], &cnt[5], &cnt[6], &cnt[7]); if (nr >= 2 ) { if (strcmp(label, "age:") == 0) { strcpy(si->nfs.nfsmounts.nfsmnt[i].mountdev, mountdev); si->nfs.nfsmounts.nfsmnt[i].age = cnt[0]; } if (strcmp(label, "bytes:") == 0) { si->nfs.nfsmounts.nfsmnt[i].bytesread = cnt[0]; si->nfs.nfsmounts.nfsmnt[i].byteswrite = cnt[1]; si->nfs.nfsmounts.nfsmnt[i].bytesdread = cnt[2]; si->nfs.nfsmounts.nfsmnt[i].bytesdwrite = cnt[3]; si->nfs.nfsmounts.nfsmnt[i].bytestotread = cnt[4]; si->nfs.nfsmounts.nfsmnt[i].bytestotwrite = cnt[5]; si->nfs.nfsmounts.nfsmnt[i].pagesmread = cnt[6]; si->nfs.nfsmounts.nfsmnt[i].pagesmwrite = cnt[7]; if (++i >= MAXNFSMOUNT-1) break; } } } si->nfs.nfsmounts.nrmounts = i; fclose(fp); } if (! droprootprivs()) mcleanstop(42, "failed to drop root privs\n"); /* ** pressure statistics in /proc/pressure (>= 4.20) ** ** cpu: some avg10=0.00 avg60=1.37 avg300=3.73 total=30995960 ** cpu: full avg10=0.00 avg60=0.00 avg300=0.00 total=0 ** io: some avg10=0.00 avg60=8.83 avg300=22.86 total=141658568 ** io: full avg10=0.00 avg60=8.33 avg300=21.56 total=133129045 ** memory: some avg10=0.00 avg60=0.74 avg300=1.67 total=10663184 ** memory: full avg10=0.00 avg60=0.45 avg300=0.94 total=6461782 ** ** verify if pressure stats supported by this system */ if ( chdir("pressure") == 0) { struct psi psitemp; char psitype; char psiformat[] = "%c%*s avg10=%f avg60=%f avg300=%f total=%llu"; si->psi.present = 1; if ( (fp = fopen("cpu", "r")) != NULL) { while ( fgets(linebuf, sizeof(linebuf), fp) != NULL) { nr = sscanf(linebuf, psiformat, &psitype, &psitemp.avg10, &psitemp.avg60, &psitemp.avg300, &psitemp.total); if (nr == 5) // complete line ? { if (psitype == 's') memmove(&(si->psi.cpusome), &psitemp, sizeof psitemp); // cpu full always seems to be zero } } fclose(fp); } if ( (fp = fopen("memory", "r")) != NULL) { while ( fgets(linebuf, sizeof(linebuf), fp) != NULL) { nr = sscanf(linebuf, psiformat, &psitype, &psitemp.avg10, &psitemp.avg60, &psitemp.avg300, &psitemp.total); if (nr == 5) { if (psitype == 's') memmove(&(si->psi.memsome), &psitemp, sizeof psitemp); else memmove(&(si->psi.memfull), &psitemp, sizeof psitemp); } } fclose(fp); } if ( (fp = fopen("io", "r")) != NULL) { while ( fgets(linebuf, sizeof(linebuf), fp) != NULL) { nr = sscanf(linebuf, psiformat, &psitype, &psitemp.avg10, &psitemp.avg60, &psitemp.avg300, &psitemp.total); if (nr == 5) { if (psitype == 's') memmove(&(si->psi.iosome), &psitemp, sizeof psitemp); else memmove(&(si->psi.iofull), &psitemp, sizeof psitemp); } } fclose(fp); } if ( chdir("..") == -1) mcleanstop(54, "failed to return to /proc\n"); } else { si->psi.present = 0; } /* ** Container statistics (if any) */ if ( (fp = fopen("user_beancounters", "r")) != NULL) { unsigned long ctid; char label[32]; count_t cnt; i = -1; /* ** lines introducing a new container have an extra ** field with the container id at the beginning. */ while ( fgets(linebuf, sizeof(linebuf), fp) != NULL) { nr = sscanf(linebuf, "%lu: %31s %lld", &ctid, label, &cnt); if (nr == 3) // new container ? { if (++i >= MAXCONTAINER) break; si->cfs.cont[i].ctid = ctid; } else { nr = sscanf(linebuf, "%31s %lld", label, &cnt); if (nr != 2) continue; } if (i == -1) // no container defined yet continue; if (strcmp(label, "numproc") == 0) { si->cfs.cont[i].numproc = cnt; continue; } if (strcmp(label, "physpages") == 0) { si->cfs.cont[i].physpages = cnt; continue; } } fclose(fp); si->cfs.nrcontainer = i+1; if ( (fp = fopen("vz/vestat", "r")) != NULL) { unsigned long ctid; count_t cnt[8]; /* ** relevant lines start with container id */ while ( fgets(linebuf, sizeof(linebuf), fp) != NULL) { nr = sscanf(linebuf, "%lu %lld %lld %lld %lld" "%lld %lld %lld %lld %lld", &ctid, &cnt[0], &cnt[1], &cnt[2], &cnt[3], &cnt[4], &cnt[5], &cnt[6], &cnt[7], &cnt[8]); if (nr < 9) // irrelevant contents continue; // relevant stats: search for containerid for (i=0; i < si->cfs.nrcontainer; i++) { if (si->cfs.cont[i].ctid == ctid) break; } if (i >= si->cfs.nrcontainer) continue; // container not found si->cfs.cont[i].user = cnt[0]; si->cfs.cont[i].nice = cnt[1]; si->cfs.cont[i].system = cnt[2]; si->cfs.cont[i].uptime = cnt[3]; } fclose(fp); } } /* ** gather per LLC related statistics from the file ** /sys/fs/resctrl/mon_data/mon_L3_XX */ dirp = opendir(LLCDIR); if (dirp) { static int l3_cache_size; if (!l3_cache_size) { if ( (fp = fopen(L3SIZE, "r")) != NULL) { if ( fgets(linebuf, sizeof(linebuf), fp) != NULL) { sscanf(linebuf, "%uK\n", &l3_cache_size); l3_cache_size *= 1024; } fclose(fp); } } /* ** walk the LLC directory, gather each LLC */ while ( (dentry = readdir(dirp)) ) { struct perllc *llc = &si->llc.perllc[si->llc.nrllcs]; unsigned long llc_occupancy; if (strncmp(dentry->d_name, "mon_L3_", 7)) continue; /* get cache id from directory name like mon_L3_00 */ sscanf(dentry->d_name + 7, "%hhd\n", &llc->id); snprintf(fn, sizeof fn, LLCDIR "/%s/llc_occupancy", dentry->d_name); if ( (fp = fopen(fn, "r")) != NULL) { if ( fgets(linebuf, sizeof(linebuf), fp) != NULL) { sscanf(linebuf, "%lu\n", &llc_occupancy); llc->occupancy = (float)llc_occupancy / l3_cache_size; } fclose(fp); } snprintf(fn, sizeof fn, LLCDIR "/%s/mbm_local_bytes", dentry->d_name); if ( (fp = fopen(fn, "r")) != NULL) { if ( fgets(linebuf, sizeof(linebuf), fp) != NULL) { sscanf(linebuf, "%llu\n", &llc->mbm_local); } fclose(fp); } snprintf(fn, sizeof fn, LLCDIR "/%s/mbm_total_bytes", dentry->d_name); if ( (fp = fopen(fn, "r")) != NULL) { if ( fgets(linebuf, sizeof(linebuf), fp) != NULL) { sscanf(linebuf, "%llu\n", &llc->mbm_total); } fclose(fp); } if (++si->llc.nrllcs >= MAXLLC) /* too many LLC ? */ break; } closedir(dirp); } /* ** verify presence of InfiniBand controllers ** warning: possibly switches to other directory */ if (ib_stats) ib_stats = get_infiniband(&(si->ifb)); /* ** get counters related to ksm */ if (ksm_stats) ksm_stats = get_ksm(si); /* ** return to original directory */ if ( chdir(origdir) == -1) mcleanstop(55, "failed to change to %s\n", origdir); #ifndef NOPERFEVENT /* ** get low-level CPU event counters */ getperfevents(&(si->cpu)); #endif /* ** fetch application-specific counters */ #if HTTPSTATS if ( wwwvalid) wwwvalid = getwwwstat(80, &(si->www)); #endif } /* ** set of subroutines to determine which disks should be monitored ** and to translate name strings into (shorter) name strings */ static void nullmodname(unsigned int major, unsigned int minor, char *curname, struct perdsk *px, int maxlen) { safe_strcpy(px->name, curname, maxlen); } static void abbrevname1(unsigned int major, unsigned int minor, char *curname, struct perdsk *px, int maxlen) { char cutype[128]; int hostnum, busnum, targetnum, lunnum; sscanf(curname, "%[^/]/host%d/bus%d/target%d/lun%d", cutype, &hostnum, &busnum, &targetnum, &lunnum); snprintf(px->name, maxlen, "%c-h%db%dt%d", cutype[0], hostnum, busnum, targetnum); } /* ** recognize LVM logical volumes */ #define NUMDMHASH 64 #define DMHASH(x,y) (((x)+(y))%NUMDMHASH) #define MAPDIR "/dev/mapper" struct devmap { unsigned int major; unsigned int minor; char name[MAXDKNAM]; struct devmap *next; }; static void lvmmapname(unsigned int major, unsigned int minor, char *curname, struct perdsk *px, int maxlen) { static int firstcall = 1; static struct devmap *devmaps[NUMDMHASH], *dmp; int hashix; /* ** setup a list of major-minor numbers of dm-devices with their ** corresponding name */ if (firstcall) { DIR *dirp; struct dirent *dentry; struct stat statbuf; char path[PATH_MAX]; if ( (dirp = opendir(MAPDIR)) ) { /* ** read every directory-entry and search for ** block devices */ while ( (dentry = readdir(dirp)) ) { snprintf(path, sizeof path, "%s/%s", MAPDIR, dentry->d_name); if ( stat(path, &statbuf) == -1 ) continue; if ( ! S_ISBLK(statbuf.st_mode) ) continue; /* ** allocate struct to store name */ if ( !(dmp = malloc(sizeof (struct devmap)))) continue; /* ** store info in hash list */ safe_strcpy(dmp->name, dentry->d_name, sizeof dmp->name); dmp->major = major(statbuf.st_rdev); dmp->minor = minor(statbuf.st_rdev); hashix = DMHASH(dmp->major, dmp->minor); dmp->next = devmaps[hashix]; devmaps[hashix] = dmp; } closedir(dirp); } firstcall = 0; } /* ** find info in hash list */ hashix = DMHASH(major, minor); dmp = devmaps[hashix]; while (dmp) { if (dmp->major == major && dmp->minor == minor) { /* ** info found in hash list; fill proper name */ safe_strcpy(px->name, dmp->name, maxlen); return; } dmp = dmp->next; } /* ** info not found in hash list; fill original name */ safe_strcpy(px->name, curname, maxlen); } /* ** this table is used in the functions isdisk_name() and isdick_major() ** ** table contains the names (in regexp format) of disks ** to be recognized, together with a function to modify ** the name-strings (i.e. to abbreviate long strings); ** some frequently found names (like 'loop' and 'ram') ** are also recognized to skip them as fast as possible */ static struct { char *regexp; regex_t compreg; void (*modname)(unsigned int, unsigned int, char *, struct perdsk *, int); int major; // to be filled via isdisk_name() int retval; } validdisk[] = { { "^ram[0-9][0-9]*$", {0}, (void *)0, 0, NONTYPE, }, { "^loop[0-9][0-9]*$", {0}, (void *)0, 0, NONTYPE, }, { "^sd[a-z][a-z]*$", {0}, nullmodname, 0, DSKTYPE, }, { "^dm-[0-9][0-9]*$", {0}, lvmmapname, 0, LVMTYPE, }, { "^md[0-9][0-9]*$", {0}, nullmodname, 0, MDDTYPE, }, { "^vd[a-z][a-z]*$", {0}, nullmodname, 0, DSKTYPE, }, { "^nvme[0-9][0-9]*n[0-9][0-9]*$", {0}, nullmodname, 0, DSKTYPE, }, { "^nvme[0-9][0-9]*c[0-9][0-9]*n[0-9][0-9]*$", {0}, nullmodname, 0, DSKTYPE, }, { "^nbd[0-9][0-9]*$", {0}, nullmodname, 0, DSKTYPE, }, { "^hd[a-z]$", {0}, nullmodname, 0, DSKTYPE, }, { "^rd/c[0-9][0-9]*d[0-9][0-9]*$", {0}, nullmodname, 0, DSKTYPE, }, { "^cciss/c[0-9][0-9]*d[0-9][0-9]*$", {0}, nullmodname, 0, DSKTYPE, }, { "^fio[a-z][a-z]*$", {0}, nullmodname, 0, DSKTYPE, }, { "/host.*/bus.*/target.*/lun.*/disc", {0}, abbrevname1, 0, DSKTYPE, }, { "^xvd[a-z][a-z]*[0-9]*$", {0}, nullmodname, 0, DSKTYPE, }, { "^dasd[a-z][a-z]*$", {0}, nullmodname, 0, DSKTYPE, }, { "^mmcblk[0-9][0-9]*$", {0}, nullmodname, 0, DSKTYPE, }, { "^emcpower[a-z][a-z]*$", {0}, nullmodname, 0, DSKTYPE, }, { "^rbd[0-9][0-9]*$", {0}, nullmodname, 0, DSKTYPE, }, { "^rbd[0-9][0-9]*p[0-9][0-9]*$", {0}, nullmodname, 0, DSKTYPE, }, }; static int isdisk_name(unsigned int major, unsigned int minor, char *curname, struct perdsk *px, int maxlen) { static int firstcall = 1; register int i; if (firstcall) /* compile the regular expressions */ { for (i=0; i < sizeof validdisk/sizeof validdisk[0]; i++) regcomp(&validdisk[i].compreg, validdisk[i].regexp, REG_NOSUB); firstcall = 0; } /* ** try to recognize one of the compiled regular expressions */ for (i=0; i < sizeof validdisk/sizeof validdisk[0]; i++) { if (regexec(&validdisk[i].compreg, curname, 0, NULL, 0) == 0) { /* ** store major number of driver */ if (major) validdisk[i].major = major; /* ** name-string recognized; modify name-string */ if (validdisk[i].retval != NONTYPE) (*validdisk[i].modname)(major, minor, curname, px, maxlen); return validdisk[i].retval; } } return NONTYPE; } int isdisk_major(unsigned int major) { register int i; /* ** search for the major number of the disk */ for (i=0; i < sizeof validdisk/sizeof validdisk[0]; i++) { if (major == validdisk[i].major) return validdisk[i].retval; } return NONTYPE; } /* ** get stats of all InfiniBand ports below ** /sys/class/infiniband//ports//.... */ static struct ibcachent { char *ibha; // InfiniBand Host Adaptor unsigned short port; // port number unsigned short lanes; // number of lanes count_t rate; // transfer rate in bytes/sec char *pathrcvb; // path name for received bytes char *pathsndb; // path name for transmitted bytes char *pathrcvp; // path name for received packets char *pathsndp; // path name for transmitted packets } ibcache[MAXIBPORT]; static int nib; // number of IB ports in cache static void ibprep(struct ibcachent *); static int ibstat(struct ibcachent *, struct perifb *); static int get_infiniband(struct ifbstat *si) { static int firstcall = 1; int i; // verify if InfiniBand used in this system if ( chdir("/sys/class/infiniband") == -1) return 0; // no path, no IB, so don't try again if (firstcall) { char path[PATH_MAX], *p; struct stat statbuf; struct dirent *contdent, *portdent; DIR *contp, *portp; firstcall = 0; /* ** once setup a cache with all info that is needed ** to gather the necessary stats with every subsequent ** call, including path names, etcetera. */ if ( (contp = opendir(".")) ) { /* ** read every directory-entry and search for ** subdirectories (i.e. controllers) */ while ( (contdent = readdir(contp)) ) { // skip . and .. if (contdent->d_name[0] == '.') continue; if ( stat(contdent->d_name, &statbuf) == -1 ) continue; if ( ! S_ISDIR(statbuf.st_mode) ) continue; // controller found // store controller name for cache // p = malloc( strlen(contdent->d_name)+1 ); strcpy(p, contdent->d_name); if (strlen(contdent->d_name) > MAXIBNAME-1) p[MAXIBNAME-1] = '\0'; // discover all ports // snprintf(path, sizeof path, "%s/ports", contdent->d_name); if ( (portp = opendir(path)) ) { /* ** read every directory-entry and ** search for subdirectories (i.e. ** port numbers) */ while ( (portdent = readdir(portp)) ) { int port; // skip . and .. if (portdent->d_name[0] == '.') continue; port = atoi(portdent->d_name); if (!port) continue; // valid port // fill cache info // ibcache[nib].port = port; ibcache[nib].ibha = p; ibprep(&ibcache[nib]); if (++nib >= MAXIBPORT) break; } closedir(portp); if (nib >= MAXIBPORT) break; } } closedir(contp); } } /* ** get static and variable metrics */ for (i=0; i < nib; i++) { // static metrics from cache strcpy(si->ifb[i].ibname, ibcache[i].ibha); si->ifb[i].portnr = ibcache[i].port; si->ifb[i].lanes = ibcache[i].lanes; si->ifb[i].rate = ibcache[i].rate; // variable metrics from sysfs ibstat(&(ibcache[i]), &(si->ifb[i])); } si->nrports = nib; return 1; } /* ** determine rate and number of lanes ** from /ports//rate --> e.g. "100 Gb/sec (4X EDR)" ** and assemble path names to be used for counters later on */ static void ibprep(struct ibcachent *ibc) { FILE *fp; char path[PATH_MAX], linebuf[64], speedunit; // determine port rate and number of lanes snprintf(path, sizeof path, "%s/ports/%d/rate", ibc->ibha, ibc->port); if ( (fp = fopen(path, "r")) ) { if ( fgets(linebuf, sizeof(linebuf), fp) != NULL) { (void) sscanf(linebuf, "%lld %c%*s (%hdX", &(ibc->rate), &speedunit, &(ibc->lanes)); // calculate megabits/second switch (speedunit) { case 'M': case 'm': break; case 'G': case 'g': ibc->rate *= 1000; break; case 'T': case 't': ibc->rate *= 1000000; break; } } else { ibc->lanes = 0; ibc->rate = 0; } fclose(fp); } // build all pathnames to obtain the counters // of this port later on snprintf(path, sizeof path, "%s/ports/%d/counters/port_rcv_data", ibc->ibha, ibc->port); ibc->pathrcvb = malloc( strlen(path)+1 ); strcpy(ibc->pathrcvb, path); snprintf(path, sizeof path, "%s/ports/%d/counters/port_xmit_data", ibc->ibha, ibc->port); ibc->pathsndb = malloc( strlen(path)+1 ); strcpy(ibc->pathsndb, path); snprintf(path, sizeof path, "%s/ports/%d/counters/port_rcv_packets", ibc->ibha, ibc->port); ibc->pathrcvp = malloc( strlen(path)+1 ); strcpy(ibc->pathrcvp, path); snprintf(path, sizeof path, "%s/ports/%d/counters/port_xmit_packets", ibc->ibha, ibc->port); ibc->pathsndp = malloc( strlen(path)+1 ); strcpy(ibc->pathsndp, path); } /* ** read necessary variable counters for this IB port */ static int ibstat(struct ibcachent *ibc, struct perifb *ifb) { FILE *fp; char linebuf[64]; if ( (fp = fopen(ibc->pathrcvb, "r")) ) { if ( fgets(linebuf, sizeof(linebuf), fp) != NULL) { if (sscanf(linebuf, "%lld", &(ifb->rcvb)) == 0) ifb->rcvb = 0; } else { ifb->rcvb = 0; } fclose(fp); } if ( (fp = fopen(ibc->pathsndb, "r")) ) { if ( fgets(linebuf, sizeof(linebuf), fp) != NULL) { if (sscanf(linebuf, "%lld", &(ifb->sndb)) == 0) ifb->sndb = 0; } else { ifb->sndb = 0; } fclose(fp); } if ( (fp = fopen(ibc->pathrcvp, "r")) ) { if ( fgets(linebuf, sizeof(linebuf), fp) != NULL) { if (sscanf(linebuf, "%lld", &(ifb->rcvp)) == 0) ifb->rcvp = 0; } else { ifb->rcvp = 0; } fclose(fp); } if ( (fp = fopen(ibc->pathsndp, "r")) ) { if ( fgets(linebuf, sizeof(linebuf), fp) != NULL) { if (sscanf(linebuf, "%lld", &(ifb->sndp)) == 0) ifb->sndp = 0; } else { ifb->sndp = 0; } fclose(fp); } return 1; } /* ** retrieve ksm values (if switched on) */ static int get_ksm(struct sstat *si) { FILE *fp; int state; si->mem.ksmsharing = -1; si->mem.ksmshared = -1; if ((fp=fopen("/sys/kernel/mm/ksm/run", "r")) != 0) { if (fscanf(fp, "%d", &state) == 1) { if (state == 0) { fclose(fp); return 0; // no more calling } } fclose(fp); } if ((fp=fopen("/sys/kernel/mm/ksm/pages_sharing", "r")) != 0) { if (fscanf(fp, "%llu", &(si->mem.ksmsharing)) != 1) si->mem.ksmsharing = 0; fclose(fp); } if ((fp=fopen("/sys/kernel/mm/ksm/pages_shared", "r")) != 0) { if (fscanf(fp, "%llu", &(si->mem.ksmshared)) != 1) si->mem.ksmshared = 0; fclose(fp); } return 1; } /* ** determine if this system uses *real* NUMA rather than *fake* NUMA ** which is the case when not all node distances have the same value */ #define NUMADISTANCE0 "/sys/devices/system/node/node0/distance" void realnuma_support(void) { FILE *fp; int i, total, nr=0, dist[10]; char linebuf[1024]; if ( (fp = fopen(NUMADISTANCE0, "r")) == NULL) return; // open failed if ( fgets(linebuf, sizeof(linebuf), fp) != NULL) { nr = sscanf(linebuf, "%d %d %d %d %d %d %d %d %d %d", &dist[0], &dist[1], &dist[2], &dist[3], &dist[4], &dist[5], &dist[6], &dist[7], &dist[8], &dist[9]); } fclose(fp); if (nr <= 0) // any recognized numerical distances? return; // totalize all distances for (i=0, total=0; i < nr; i++) total += dist[i]; // average distance not equal to the first distance? if (total / i != dist[0]) supportflags |= REALNUMA; } /* ** determine if this system uses zswap */ void zswap_support(void) { FILE *fp; char state; if ((fp=fopen("/sys/module/zswap/parameters/enabled", "r")) == NULL) return; // open failed if (fscanf(fp, "%c", &state) == 1 && state == 'Y') supportflags |= ZSWAP; fclose(fp); } #if HTTPSTATS /* ** retrieve statistics from local HTTP daemons ** via http://localhost/server-status?auto */ int getwwwstat(unsigned short port, struct wwwstat *wp) { int sockfd, tobefound; FILE *sockfp; struct sockaddr_in sockname; char linebuf[4096]; char label[512]; long long value; memset(wp, 0, sizeof *wp); /* ** allocate a socket and connect to the local HTTP daemon */ if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) return 0; sockname.sin_family = AF_INET; sockname.sin_addr.s_addr = htonl(INADDR_LOOPBACK); sockname.sin_port = htons(port); if ( connect(sockfd, (struct sockaddr *) &sockname, sizeof sockname) == -1) { close(sockfd); return 0; } /* ** write a GET-request for /server-status */ if ( write(sockfd, HTTPREQ, sizeof HTTPREQ) < sizeof HTTPREQ) { close(sockfd); return 0; } /* ** remap socket descriptor to a stream to allow stdio calls */ sockfp = fdopen(sockfd, "r+"); /* ** read response line by line */ tobefound = 5; /* number of values to be searched */ while ( fgets(linebuf, sizeof linebuf, sockfp) && tobefound) { /* ** handle line containing status code */ if ( strncmp(linebuf, "HTTP/", 5) == 0) { sscanf(linebuf, "%511s %lld %*s\n", label, &value); if (value != 200) /* HTTP-request okay? */ { fclose(sockfp); close(sockfd); return 0; } continue; } /* ** decode line and search for the required counters */ if (sscanf(linebuf, "%511[^:]: %lld\n", label, &value) == 2) { if ( strcmp(label, "Total Accesses") == 0) { wp->accesses = value; tobefound--; } if ( strcmp(label, "Total kBytes") == 0) { wp->totkbytes = value; tobefound--; } if ( strcmp(label, "Uptime") == 0) { wp->uptime = value; tobefound--; } if ( strcmp(label, "BusyWorkers") == 0) { wp->bworkers = value; tobefound--; } if ( strcmp(label, "IdleWorkers") == 0) { wp->iworkers = value; tobefound--; } } } fclose(sockfp); close(sockfd); return 1; } #endif /* ** retrieve low-level CPU events: ** instructions and cycles per CPU */ #ifndef NOPERFEVENT void do_perfevents(char *tagname, char *tagvalue) { if (!strcmp("enable", tagvalue)) perfevents = PERF_EVENTS_ENABLE; else if (!strcmp("disable", tagvalue)) perfevents = PERF_EVENTS_DISABLE; else { if (run_in_guest()) perfevents = PERF_EVENTS_DISABLE; else perfevents = PERF_EVENTS_ENABLE; } } static int enable_perfevents() { if (perfevents == PERF_EVENTS_AUTO) do_perfevents("perfevents", "auto"); return perfevents == PERF_EVENTS_ENABLE; } static long perf_event_open(struct perf_event_attr *hwevent, pid_t pid, int cpu, int groupfd, unsigned long flags) { return syscall(__NR_perf_event_open, hwevent, pid, cpu, groupfd, flags); } static void getperfevents(struct cpustat *cs) { static int firstcall = 1, cpualloced, *fdi, *fdc; int i; int liResult; if (!enable_perfevents()) return; /* ** once initialize perf event counter retrieval */ if (firstcall) { struct perf_event_attr pea; int success=0, minfds = cs->nrcpu*2 + 32; struct rlimit rlim; firstcall = 0; /* ** for perf events two file descriptors will ** be opened permanently per CPU, so take care ** that enough open files are allowed for this process */ getrlimit(RLIMIT_NOFILE, &rlim); if (rlim.rlim_cur < minfds) // default not enough? { rlim.rlim_cur = minfds; (void) setrlimit(RLIMIT_NOFILE, &rlim); } /* ** allocate space for per-cpu file descriptors */ cpualloced = cs->nrcpu; fdi = malloc(sizeof(int) * cpualloced); fdc = malloc(sizeof(int) * cpualloced); /* ** fill perf_event_attr struct with appropriate values */ memset(&pea, 0, sizeof(struct perf_event_attr)); pea.type = PERF_TYPE_HARDWARE; pea.size = sizeof(struct perf_event_attr); pea.inherit = 1; pea.pinned = 1; regainrootprivs(); for (i=0; i < cpualloced; i++) { pea.config = PERF_COUNT_HW_INSTRUCTIONS; if ( (*(fdi+i) = perf_event_open(&pea, -1, i, -1, PERF_FLAG_FD_CLOEXEC)) >= 0) success++; pea.config = PERF_COUNT_HW_CPU_CYCLES; if ( (*(fdc+i) = perf_event_open(&pea, -1, i, -1, PERF_FLAG_FD_CLOEXEC)) >= 0) success++; } if (! droprootprivs()) mcleanstop(42, "failed to drop root privs\n"); /* ** all failed (probably no kernel support)? */ if (success == 0) { free(fdi); free(fdc); cpualloced = 0; } else { cs->all.instr = 1; cs->all.cycle = 1; } return; // initialization finished for first sample } /* ** every sample: check if counters available anyhow */ if (!cpualloced) return; /* ** retrieve counters per CPU and in total */ cs->all.instr = 0; cs->all.cycle = 0; for (i=0; i < cpualloced; i++) { if (*(fdi+i) != -1) { liResult = read(*(fdi+i), &(cs->cpu[i].instr), sizeof(count_t)); if(liResult < 0) { char lcMessage[64]; snprintf(lcMessage, sizeof(lcMessage) - 1, "%s:%d - Error %d reading instr counters\n", __FILE__, __LINE__, errno); fprintf(stderr, "%s", lcMessage); } else { cs->all.instr += cs->cpu[i].instr; } liResult = read(*(fdc+i), &(cs->cpu[i].cycle), sizeof(count_t)); if(liResult < 0) { char lcMessage[64]; snprintf(lcMessage, sizeof(lcMessage) - 1, "%s:%d - Error %d reading cycle counters\n", __FILE__, __LINE__, errno ); fprintf(stderr, "%s", lcMessage); } else { cs->all.cycle += cs->cpu[i].cycle; } } } } #else /* ! NOPERFEVENT */ void do_perfevents(char *tagname, char *tagvalue) { if (strcmp("disable", tagvalue)) mcleanstop(1, "atop built with NOPERFEVENT, cannot use perfevents\n"); } #endif atop-2.12.1/photosyst.h0000644000203100020310000003712015064552761014303 0ustar gerlofgerlof/* ** ATOP - System & Process Monitor ** ** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as process-level. ** ** Include-file describing system-level counters maintained. ** ================================================================ ** Author: Gerlof Langeveld ** E-mail: gerlof.langeveld@atoptool.nl ** Date: November 1996 ** LINUX-port: June 2000 ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. */ #ifndef __PHOTOSYST__ #define __PHOTOSYST__ #include "netstats.h" #define MAXCPU 2048 #define MAXDSK 1024 #define MAXNUMA 1024 #define MAXLVM 2048 #define MAXMDD 256 #define MAXINTF 128 #define MAXCONTAINER 128 #define MAXNFSMOUNT 64 #define MAXIBPORT 32 #define MAXGPU 32 #define MAXGPUBUS 12 #define MAXGPUTYPE 12 #define MAXLLC 256 #define MAXDKNAM 32 #define MAXIBNAME 12 /************************************************************************/ struct memstat { count_t physmem; // number of physical pages count_t freemem; // number of free pages count_t buffermem; // number of buffer pages count_t slabmem; // number of slab pages count_t cachemem; // number of cache pages count_t cachedrt; // number of cache pages (dirty) count_t totswap; // number of pages in swap count_t freeswap; // number of free swap pages count_t pgscans; // number of page scans count_t pgsteal; // number of page steals count_t allocstall; // try to free pages forced count_t swouts; // number of pages swapped out count_t swins; // number of pages swapped in count_t tcpsock; // number of pages allocated by TCP sockets count_t udpsock; // number of pages allocated by UDP sockets count_t commitlim; // commit limit in pages count_t committed; // number of reserved pages count_t shmem; // tot shmem incl. tmpfs (pages) count_t shmrss; // resident shared memory (pages) count_t shmswp; // swapped shared memory (pages) count_t slabreclaim; // reclaimable slab (pages) count_t stothugepage; // total huge pages (huge pages) - small count_t sfreehugepage; // free huge pages (huge pages) - small count_t shugepagesz; // huge page size (bytes) - small count_t vmwballoon; // vmware claimed balloon pages count_t zfsarcsize; // zfsonlinux ARC size (pages) count_t swapcached; // swap cache (pages) count_t ksmsharing; // saved i.e. deduped memory (pages) count_t ksmshared; // current size shared pages (pages) count_t zswapped; // zswap stored pages decompressed (pages) count_t zswap; // zswap current pool size compressed (pages) count_t oomkills; // number of oom killings count_t compactstall; // counter for process stalls count_t pgmigrate; // counter for migrated successfully (pages) count_t numamigrate; // counter for numa migrated (pages) count_t pgouts; // total number of pages written to block device count_t pgins; // total number of pages read from block device count_t pagetables; // page tables of processes (pages) count_t zswouts; // number of pages swapped out to zswap count_t zswins; // number of pages swapped in from zswap count_t ltothugepage; // total huge pages (huge pages) - large count_t lfreehugepage; // free huge pages (huge pages) - large count_t lhugepagesz; // huge page size (bytes) - large count_t availablemem; // available memory (pages) count_t anonhugepage; // anonymous transparent huge pages // (in units of 'normal' pages) count_t cfuture[5]; // reserved for future use }; /************************************************************************/ struct mempernuma { int numanr; float frag; // fragmentation level for this numa count_t totmem; // number of physical pages for this numa count_t freemem; // number of free pages for this numa count_t filepage; // number of file pages for this numa count_t dirtymem; // number of cache pages (dirty) for this numa count_t slabmem; // number of slab pages for this numa count_t slabreclaim; // reclaimable slab (pages) for this numa count_t active; // number of pages used more recently for this numa count_t inactive; // number of pages less recently used for this numa count_t shmem; // tot shmem incl. tmpfs (pages) for this numa count_t tothp; // total huge pages (huge pages) for this numa count_t freehp; // total free pages (huge pages) for this numa count_t cfuture[2]; // reserved for future use }; struct memnuma { count_t nrnuma; /* the counts of numa */ struct mempernuma numa[MAXNUMA]; }; struct cpupernuma { int numanr; count_t nrcpu; // number of cpu's count_t stime; // accumulate system time in clock ticks for per numa count_t utime; // accumulate user time in clock ticks for per numa count_t ntime; // accumulate nice time in clock ticks for per numa count_t itime; // accumulate idle time in clock ticks for per numa count_t wtime; // accumulate iowait time in clock ticks for per numa count_t Itime; // accumulate irq time in clock ticks for per numa count_t Stime; // accumulate softirq time in clock ticks for per numa count_t steal; // accumulate steal time in clock ticks for per numa count_t guest; // accumulate guest time in clock ticks for per numa count_t cfuture[2]; // reserved for future use }; struct cpunuma { count_t nrnuma; /* the counts of numa */ struct cpupernuma numa[MAXNUMA]; }; /************************************************************************/ struct netstat { struct ipv4_stats ipv4; struct icmpv4_stats icmpv4; struct udpv4_stats udpv4; struct ipv6_stats ipv6; struct icmpv6_stats icmpv6; struct udpv6_stats udpv6; struct tcp_stats tcp; }; /************************************************************************/ struct freqcnt { count_t maxfreq;/* frequency in MHz */ count_t cnt; /* number of clock ticks times state */ count_t ticks; /* number of total clock ticks */ /* if zero, cnt is actual freq */ }; struct percpu { int cpunr; count_t stime; /* system time in clock ticks */ count_t utime; /* user time in clock ticks */ count_t ntime; /* nice time in clock ticks */ count_t itime; /* idle time in clock ticks */ count_t wtime; /* iowait time in clock ticks */ count_t Itime; /* irq time in clock ticks */ count_t Stime; /* softirq time in clock ticks */ count_t steal; /* steal time in clock ticks */ count_t guest; /* guest time in clock ticks */ struct freqcnt freqcnt;/* frequency scaling info */ count_t instr; /* CPU instructions */ count_t cycle; /* CPU cycles */ count_t cfuture[6]; /* reserved for future use */ }; struct cpustat { count_t nrcpu; /* number of cpu's */ count_t devint; /* number of device interrupts */ count_t csw; /* number of context switches */ count_t nprocs; /* number of processes started */ float lavg1; /* load average last minute */ float lavg5; /* load average last 5 minutes */ float lavg15; /* load average last 15 minutes */ count_t cfuture[4]; /* reserved for future use */ struct percpu all; struct percpu cpu[MAXCPU]; }; /************************************************************************/ struct perdsk { char name[MAXDKNAM]; /* empty string for last */ count_t nread; /* number of read transfers */ count_t nrsect; /* number of sectors read */ count_t nwrite; /* number of write transfers */ count_t nwsect; /* number of sectors written */ count_t io_ms; /* number of millisecs spent for I/O */ count_t avque; /* average queue length */ count_t ndisc; /* number of discards (-1 = unavailable)*/ count_t ndsect; /* number of sectors discarded */ count_t inflight; /* number of inflight I/O */ count_t cfuture[3]; /* reserved for future use */ }; struct dskstat { int ndsk; /* number of physical disks */ int nmdd; /* number of md volumes */ int nlvm; /* number of logical volumes */ struct perdsk dsk[MAXDSK]; struct perdsk mdd[MAXMDD]; struct perdsk lvm[MAXLVM]; }; /************************************************************************/ struct perintf { char name[16]; /* empty string for last */ count_t rbyte; /* number of read bytes */ count_t rpack; /* number of read packets */ count_t rerrs; /* receive errors */ count_t rdrop; /* receive drops */ count_t rfifo; /* receive fifo */ count_t rframe; /* receive framing errors */ count_t rcompr; /* receive compressed */ count_t rmultic;/* receive multicast */ count_t rfuture[4]; /* reserved for future use */ count_t sbyte; /* number of written bytes */ count_t spack; /* number of written packets */ count_t serrs; /* transmit errors */ count_t sdrop; /* transmit drops */ count_t sfifo; /* transmit fifo */ count_t scollis;/* collisions */ count_t scarrier;/* transmit carrier */ count_t scompr; /* transmit compressed */ count_t sfuture[4]; /* reserved for future use */ char type; /* interface type ('e'/'w'/'v'/'?') */ long speed; /* interface speed in megabits/second */ long speedp; /* previous interface speed */ char duplex; /* full duplex (boolean) */ count_t cfuture[4]; /* reserved for future use */ }; struct intfstat { int nrintf; struct perintf intf[MAXINTF]; }; /************************************************************************/ struct pernfsmount { char mountdev[128]; /* mountdevice */ count_t age; /* number of seconds mounted */ count_t bytesread; /* via normal reads */ count_t byteswrite; /* via normal writes */ count_t bytesdread; /* via direct reads */ count_t bytesdwrite; /* via direct writes */ count_t bytestotread; /* via reads */ count_t bytestotwrite; /* via writes */ count_t pagesmread; /* via mmap reads */ count_t pagesmwrite; /* via mmap writes */ count_t future[8]; }; struct nfsstat { struct { count_t netcnt; count_t netudpcnt; count_t nettcpcnt; count_t nettcpcon; count_t rpccnt; count_t rpcbadfmt; count_t rpcbadaut; count_t rpcbadcln; count_t rpcread; count_t rpcwrite; count_t rchits; /* repcache hits */ count_t rcmiss; /* repcache misses */ count_t rcnoca; /* uncached requests */ count_t nrbytes; /* read bytes */ count_t nwbytes; /* written bytes */ count_t future[8]; } server; struct { count_t rpccnt; count_t rpcretrans; count_t rpcautrefresh; count_t rpcread; count_t rpcwrite; count_t future[8]; } client; struct { int nrmounts; struct pernfsmount nfsmnt[MAXNFSMOUNT]; } nfsmounts; }; /************************************************************************/ struct psi { float avg10; // average pressure last 10 seconds float avg60; // average pressure last 60 seconds float avg300; // average pressure last 300 seconds count_t total; // total number of milliseconds }; struct pressure { char present; /* pressure stats supported? */ char future[3]; struct psi cpusome; /* pressure stall info 'some' */ struct psi memsome; /* pressure stall info 'some' */ struct psi memfull; /* pressure stall info 'full' */ struct psi iosome; /* pressure stall info 'some' */ struct psi iofull; /* pressure stall info 'full' */ }; /************************************************************************/ struct percontainer { unsigned long ctid; /* container id */ unsigned long numproc; /* number of processes */ count_t system; /* */ count_t user; /* */ count_t nice; /* */ count_t uptime; /* */ count_t physpages; /* */ }; struct contstat { int nrcontainer; struct percontainer cont[MAXCONTAINER]; }; /************************************************************************/ /* ** experimental stuff for access to local HTTP daemons */ #define HTTPREQ "GET /server-status?auto HTTP/1.1\nHost: localhost\n\n" struct wwwstat { count_t accesses; /* total number of HTTP-requests */ count_t totkbytes; /* total kbytes transfer for HTTP-req */ count_t uptime; /* number of seconds since startup */ int bworkers; /* number of busy httpd-daemons */ int iworkers; /* number of idle httpd-daemons */ }; #if HTTPSTATS int getwwwstat(unsigned short, struct wwwstat *); #endif /************************************************************************/ struct pergpu { char taskstats; // GPU task statistics supported? unsigned char nrprocs; // number of processes using GPU char type[MAXGPUTYPE+1]; // GPU type char busid[MAXGPUBUS+1]; // GPU bus identification int gpunr; // GPU number int gpupercnow; // processor percentage last second // -1 if not supported int mempercnow; // memory percentage last second // -1 if not supported count_t memtotnow; // total memory in KiB count_t memusenow; // used memory in KiB count_t samples; // number of samples count_t gpuperccum; // cumulative processor busy percentage // -1 if not supported count_t memperccum; // cumulative memory percentage // -1 if not supported count_t memusecum; // cumulative used memory in KiB }; struct gpustat { int nrgpus; // total number of GPUs struct pergpu gpu[MAXGPU]; }; /************************************************************************/ struct perifb { char ibname[MAXIBNAME]; // InfiniBand controller short portnr; // InfiniBand controller port short lanes; // number of lanes (traffic factor) count_t rate; // transfer rate in megabits/sec count_t rcvb; // bytes received count_t sndb; // bytes transmitted count_t rcvp; // packets received count_t sndp; // packets transmitted count_t cfuture[4]; // reserved for future use }; struct ifbstat { int nrports; // total number of IB ports struct perifb ifb[MAXIBPORT]; }; /************************************************************************/ struct perllc { unsigned char id; float occupancy; count_t mbm_local; count_t mbm_total; }; struct llcstat { int nrllcs; // total number of LLC struct perllc perllc[MAXLLC]; }; /************************************************************************/ struct sstat { struct cpustat cpu; struct memstat mem; struct netstat net; struct intfstat intf; struct memnuma memnuma; struct cpunuma cpunuma; struct dskstat dsk; struct nfsstat nfs; struct contstat cfs; struct pressure psi; struct gpustat gpu; struct ifbstat ifb; struct llcstat llc; struct wwwstat www; }; /* ** prototypes */ void photosyst (struct sstat *); void deviatsyst(struct sstat *, struct sstat *, struct sstat *, long); void totalsyst (char, struct sstat *, struct sstat *); void do_perfevents(char *, char *); int isdisk_major(unsigned int); void realnuma_support(void); void zswap_support(void); /* ** return value of isdisk_...() */ #define NONTYPE 0 #define DSKTYPE 1 #define MDDTYPE 2 #define LVMTYPE 3 #endif atop-2.12.1/procdbase.c0000644000203100020310000001652515064552761014172 0ustar gerlofgerlof/* ** ATOP - System & Process Monitor ** ** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as process-level. ** ** This source-file contains all functions required to manipulate the ** process-database. This database is implemented as a linked list of ** all running processes, needed to remember the process-counters from ** the previous sample. ** ========================================================================== ** Author: Gerlof Langeveld ** E-mail: gerlof.langeveld@atoptool.nl ** Date: November 1996 ** LINUX-port: June 2000 ** -------------------------------------------------------------------------- ** Copyright (C) 2000-2012 Gerlof Langeveld ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ** -------------------------------------------------------------------------- */ #include #include #include #include #include #include #include #include #include "atop.h" #include "photoproc.h" /*****************************************************************************/ #define NPHASH 256 /* number of hash queues for process dbase */ /* MUST be a power of 2 !!! */ /* hash buckets for getting process-info */ /* for a given PID */ static struct pinfo *phash[NPHASH]; /* cyclic list of all processes, to detect */ /* which processes were not referred */ static struct pinfo presidue; /*****************************************************************************/ /* ** search process database for the given PID */ int pdb_gettask(int pid, char isproc, time_t btime, struct pinfo **pinfopp) { register struct pinfo *pp; pp = phash[pid&(NPHASH-1)]; /* get proper hash bucket */ /* ** scan all entries in hash Q */ while (pp) { /* ** if this is required PID, unchain it from the RESIDUE-list ** and return info */ if (pp->tstat.gen.pid == pid && pp->tstat.gen.isproc == isproc ) { int diff = pp->tstat.gen.btime - btime; /* ** with longer intervals, the same PID might be ** found more than once, so also check the start ** time of the task */ if (diff > 1 || diff < -1) { pp = pp->phnext; continue; } if (pp->prnext) /* if part of RESIDUE-list */ { (pp->prnext)->prprev = pp->prprev; /* unchain */ (pp->prprev)->prnext = pp->prnext; } pp->prnext = NULL; pp->prprev = NULL; *pinfopp = pp; return 1; } pp = pp->phnext; } /* ** end of list; PID not found */ return 0; } /* ** add new process-info structure to the process database */ void pdb_addtask(int pid, struct pinfo *pinfop) { register int i = pid&(NPHASH-1); pinfop->phnext = phash[i]; phash[i] = pinfop; } /* ** delete a process from the process database */ int pdb_deltask(int pid, char isproc) { register struct pinfo *pp, *ppp; pp = phash[pid&(NPHASH-1)]; /* get proper hash bucket */ /* ** check first entry in hash Q */ if (pp->tstat.gen.pid == pid && pp->tstat.gen.isproc == isproc) { phash[pid&(NPHASH-1)] = pp->phnext; if ( pp->prnext ) /* still part of RESIDUE-list ? */ { (pp->prprev)->prnext = pp->prnext; (pp->prnext)->prprev = pp->prprev; /* unchain */ } /* ** remove process-info from process-database */ free(pp); return 1; } /* ** scan other entries of hash-list */ ppp = pp; pp = pp->phnext; while (pp) { /* ** if this is wanted PID, unchain it from the RESIDUE-list ** and return info */ if (pp->tstat.gen.pid == pid && pp->tstat.gen.isproc == isproc) { ppp->phnext = pp->phnext; if ( pp->prnext ) /* part of RESIDUE-list ? */ { (pp->prnext)->prprev = pp->prprev; (pp->prprev)->prnext = pp->prnext; } /* ** remove process-info from process-database */ free(pp); return 1; } ppp = pp; pp = pp->phnext; } return 0; /* PID not found */ } /* ** Chain all process-info structures into the RESIDUE-list; ** every process-info struct which is referenced later on by pdb_gettask(), ** will be removed from this list again. After that, the remaining ** (unreferred) process-info structs can be easily discovered and ** eventually removed. */ int pdb_makeresidue(void) { register struct pinfo *pp, *pr; register int i; /* ** prepare RESIDUE-list anchor */ pr = &presidue; pr->prnext = pr; pr->prprev = pr; /* ** check all entries in hash list */ for (i=0; i < NPHASH; i++) { if (!phash[i]) continue; /* empty hash bucket */ pp = phash[i]; /* get start of list */ while (pp) /* all entries in hash list */ { pp->prnext = pr->prnext; pr->prnext = pp; pp->prprev = (pp->prnext)->prprev; (pp->prnext)->prprev = pp; pp = pp->phnext; /* get next of hash list */ } } /* ** all entries chained in doubly-linked RESIDUE-list */ return 1; } /* ** remove all remaining entries in RESIDUE-list */ int pdb_cleanresidue(void) { register struct pinfo *pr; register int pid; char isproc; /* ** start at RESIDUE-list anchor and delete all entries */ pr = presidue.prnext; while (pr != &presidue) { pid = pr->tstat.gen.pid; isproc = pr->tstat.gen.isproc; pr = pr->prnext; /* MUST be done before deletion */ pdb_deltask(pid, isproc); } return 1; } /* ** search in the RESIDUE-list for process-info which may fit to the ** given process-info, for which the PID is not known */ int pdb_srchresidue(struct tstat *tstatp, struct pinfo **pinfopp) { register struct pinfo *pr, *prmin=NULL; register long btimediff; /* ** start at RESIDUE-list anchor and search through ** all remaining entries */ pr = presidue.prnext; while (pr != &presidue) /* still entries left ? */ { /* ** check if this entry matches searched info */ if ( pr->tstat.gen.ruid == tstatp->gen.ruid && pr->tstat.gen.rgid == tstatp->gen.rgid && strcmp(pr->tstat.gen.name, tstatp->gen.name) == EQ ) { /* ** check if the start-time of the process is exactly ** the same ----> then we have a match; ** however sometimes the start-time may deviate a ** second although it IS the process we are looking ** for (depending on the rounding of the boot-time), ** so if we don't find the exact match, we will check ** later on if we found an almost-exact match */ btimediff = pr->tstat.gen.btime - tstatp->gen.btime; if (btimediff == 0) /* gotcha !! */ { *pinfopp = pr; return 1; } if ((btimediff== -1 || btimediff== 1) && prmin== NULL) prmin = pr; /* remember this process */ } pr = pr->prnext; } /* ** nothing found that matched exactly; ** do we remember a process that matched almost exactly? */ if (prmin) { *pinfopp = prmin; return 1; } return 0; /* even not almost */ } atop-2.12.1/rawlog.c0000644000203100020310000006637015064552761013526 0ustar gerlofgerlof/* ** ATOP - System & Process Monitor ** ** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as process-level. ** ========================================================================== ** Author: Gerlof Langeveld ** E-mail: gerlof.langeveld@atoptool.nl ** Date: September 2002 ** Date: February 2024 (added cgroup support) ** -------------------------------------------------------------------------- ** Copyright (C) 2000-2024 Gerlof Langeveld ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ** -------------------------------------------------------------------------- */ #define _POSIX_C_SOURCE #define _XOPEN_SOURCE #define _GNU_SOURCE #define _DEFAULT_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "atop.h" #include "photoproc.h" #include "photosyst.h" #include "cgroups.h" #include "showgeneric.h" #include "rawlog.h" #define BASEPATH "/var/log/atop" static int getrawrec (int, struct rawrecord *, int, int); static int getrawsstat(int, struct sstat *, int); static int getrawtstat(int, struct tstat *, int, int); static int getrawcstat(int, struct cgchainer **, unsigned long, unsigned long, unsigned long, int, int); static int rawwopen(void); static int readchunk(int, void *, int); static int lookslikedatetome(char *); static void testcompval(int, char *); /* ** write a raw record to file ** (file is opened/created during the first call) */ char rawwrite(time_t curtime, int numsecs, struct devtstat *devtstat, struct sstat *sstat, struct cgchainer *devchain, int ncgroups, int npids, int nexit, unsigned int noverflow, char flag) { static int rawfd = -1; struct rawrecord rr; int rv; struct stat filestat; Byte scompbuf[sizeof(struct sstat)], *pcompbuf, *ccompbuf = NULL, *icompbuf = NULL; unsigned long soriglen = sizeof scompbuf, scomplen = compressBound(soriglen), poriglen, pcomplen, coriglen, ccomplen, ioriglen, icomplen; struct iovec iov[5]; int nrvectors; /* ** first call: ** take care that the log file is opened */ if (rawfd == -1) rawfd = rawwopen(); /* ** register current size of file in order to "roll back" ** writes that have been done while not *all* writes could ** succeed, e.g. when file system full */ (void) fstat(rawfd, &filestat); /* ** compress system level metrics */ rv = compress(scompbuf, &scomplen, (Byte *)sstat, soriglen); testcompval(rv, "compress system stats"); /* ** compress process level metrics */ poriglen = sizeof(struct tstat) * devtstat->ntaskall; pcomplen = compressBound(poriglen); pcompbuf = malloc(pcomplen); ptrverify(pcompbuf, "Malloc failed for process compression buffer\n"); rv = compress(pcompbuf, &pcomplen, (Byte *)devtstat->taskall, poriglen); testcompval(rv, "compress processes"); /* ** compress cgroup level metrics */ if (supportflags & CGROUPV2) { /* ** calculate the size of all contiguous cstat structs ** and compress */ coriglen = (char *)(devchain+ncgroups-1)->cstat - (char *) devchain->cstat + (devchain+ncgroups-1)->cstat->gen.structlen; ccomplen = compressBound(coriglen); ccompbuf = malloc(ccomplen); ptrverify(ccompbuf, "Malloc failed for cgroup compression buffer\n"); rv = compress(ccompbuf, &ccomplen, (Byte *)devchain->cstat, coriglen); testcompval(rv, "compress cgroups"); /* ** calculate the size of the cgroups pidlist ** and compress */ ioriglen = npids * sizeof(pid_t); icomplen = compressBound(ioriglen); icompbuf = malloc(icomplen); ptrverify(icompbuf, "Malloc failed for cgroup compression pidlist\n"); rv = compress(icompbuf, &icomplen, (Byte *)devchain->proclist, ioriglen); nrvectors = 5; } else { coriglen = ccomplen = 0; ioriglen = icomplen = 0; nrvectors = 3; } /* ** fill record header and write to file */ memset(&rr, 0, sizeof rr); rr.curtime = curtime; rr.interval = numsecs; rr.flags = 0; rr.ndeviat = devtstat->ntaskall; rr.nactproc = devtstat->nprocactive; rr.ntask = devtstat->ntaskall; rr.nexit = nexit; rr.noverflow = noverflow; rr.totproc = devtstat->nprocall; rr.totrun = devtstat->totrun; rr.totslpi = devtstat->totslpi; rr.totslpu = devtstat->totslpu; rr.totidle = devtstat->totidle; rr.totzomb = devtstat->totzombie; rr.ncgroups = ncgroups; rr.ncgpids = npids; rr.scomplen = scomplen; rr.pcomplen = pcomplen; rr.ccomplen = ccomplen; rr.coriglen = coriglen; rr.icomplen = icomplen; if (flag&RRBOOT) rr.flags |= RRBOOT; if (supportflags & ACCTACTIVE) rr.flags |= RRACCTACTIVE; if (supportflags & IOSTAT) rr.flags |= RRIOSTAT; if (supportflags & NETATOP) rr.flags |= RRNETATOP; if (supportflags & NETATOPD) rr.flags |= RRNETATOPD; if (supportflags & CGROUPV2) rr.flags |= RRCGRSTAT; if (supportflags & CONTAINERSTAT) rr.flags |= RRCONTAINERSTAT; if (supportflags & GPUSTAT) rr.flags |= RRGPUSTAT; /* ** writev can be used to write different chunks of data to ** a regular (raw) file in one operation atomically (i.e. without ** intermingling with data written by other processes). ** however, this call does not avoid that only part of the ** data is written to the (raw) file! */ iov[0].iov_base = &rr; iov[0].iov_len = sizeof(rr); iov[1].iov_base = scompbuf; iov[1].iov_len = scomplen; iov[2].iov_base = pcompbuf; iov[2].iov_len = pcomplen; iov[3].iov_base = ccompbuf; iov[3].iov_len = ccomplen; iov[4].iov_base = icompbuf; iov[4].iov_len = icomplen; if ( writev(rawfd, iov, nrvectors) < sizeof(rr) + scomplen + pcomplen + ccomplen + icomplen) { /* ** restore original file size from before partly write ** to keep file consistency */ if ( ftruncate(rawfd, filestat.st_size) == -1) mcleanstop(8, "failed to write raw/status/process record to %s\n", orawname); mcleanstop(7, "failed to write raw/status/process record to %s\n", orawname); } free(pcompbuf); if (supportflags & CGROUPV2) { free(ccompbuf); free(icompbuf); } return '\0'; } /* ** open a raw file for writing ** ** if the raw file exists already: ** - read and validate the header record (be sure it is an atop-file) ** - seek to the end of the file ** ** if the raw file does not yet exist: ** - create the raw file ** - write a header record ** ** return the filedescriptor of the raw file */ static int rawwopen() { struct rawheader rh; struct rawrecord rr; int fd, rv; struct stat filestats; time_t prevtime = 0; /* ** check if the file exists already */ if ( (fd = open(orawname, O_RDWR)) >= 0 ) { /* ** check if the file already contains a file header (and records) */ if (fstat(fd, &filestats) == 0 && filestats.st_size > 0) { /* ** read and verify raw file header */ if ( read(fd, &rh, sizeof rh) < sizeof rh) mcleanstop(7, "%s - cannot read header\n", orawname); if (rh.magic != MYMAGIC) mcleanstop(7, "file %s exists but does not contain raw " "atop output (wrong magic number)\n", orawname); if ( rh.sstatlen != sizeof(struct sstat) || rh.tstatlen != sizeof(struct tstat) || rh.cstatlen != sizeof(struct cstat) || rh.rawheadlen != sizeof(struct rawheader) || rh.rawreclen != sizeof(struct rawrecord) ) { fprintf(stderr, "existing file %s has incompatible header\n", orawname); if (rh.aversion & 0x8000 && (rh.aversion & 0x7fff) != getnumvers()) { fprintf(stderr, "(created by version %d.%d - " "current version %d.%d)\n", (rh.aversion >> 8) & 0x7f, rh.aversion & 0xff, getnumvers() >> 8, getnumvers() & 0x7f); } cleanstop(7); } if ((rh.supportflags&CGROUPV2) != (supportflags&CGROUPV2)) mcleanstop(7, "%s - different cgroup version in existing raw log\n", orawname); if (rh.hertz != hertz) mcleanstop(7, "%s - different hertz in existing raw log\n", orawname); if (rh.pagesize != pagesize) mcleanstop(7, "%s - different page size in existing raw log\n", orawname); /* ** loop through the existing sample records in the file ** to do some sanity checking and to find out if the end ** of the file is consistent (the latter is already ** verified by the getrawrec() function) */ while ( (rv = getrawrec(fd, &rr, rh.rawreclen, 1)) == rh.rawreclen) { if ( rr.curtime < prevtime || rr.ccomplen > rr.coriglen || rr.scomplen > sizeof(struct sstat) || rr.pcomplen > sizeof(struct tstat) * rr.ndeviat) { mcleanstop(7, "Inconsistencies found in existing raw file\n"); } prevtime = rr.curtime; lseek(fd, rr.scomplen+rr.pcomplen+rr.ccomplen+rr.icomplen, SEEK_CUR); } if (rv != 0) { mcleanstop(7, "Incomplete record header in existing raw file\n"); } return fd; } } else { /* ** file does not exist (or can not be opened) */ if ( (fd = creat(orawname, 0666)) == -1) { fprintf(stderr, "%s - ", orawname); perror("create raw file"); cleanstop(7); } } /* ** empty file is opened now ** write a raw file header */ memset(&rh, 0, sizeof rh); rh.magic = MYMAGIC; rh.aversion = getnumvers() | 0x8000; rh.sstatlen = sizeof(struct sstat); rh.tstatlen = sizeof(struct tstat); rh.cstatlen = sizeof(struct cstat); rh.rawheadlen = sizeof(struct rawheader); rh.rawreclen = sizeof(struct rawrecord); rh.supportflags = supportflags | RAWLOGNG; rh.osrel = osrel; rh.osvers = osvers; rh.ossub = ossub; rh.hertz = hertz; rh.pagesize = pagesize; rh.pidwidth = getpidwidth(); memcpy(&rh.utsname, &utsname, sizeof rh.utsname); if ( write(fd, &rh, sizeof rh) == -1) { fprintf(stderr, "%s - ", orawname); perror("write raw header"); cleanstop(7); } return fd; } /* ** read the contents of a raw file */ #define OFFCHUNK 256 int rawread(void) { static struct devtstat devtstat; int i, j, v, rv, rawfd, len, isregular = 1; char *py; struct rawheader rh; struct rawrecord rr; struct sstat sstat; struct cgchainer *devchain = NULL; struct stat filestat; /* ** variables to maintain the offsets of the raw records ** to be able to see previous samples again */ off_t *offlist = NULL; unsigned int offsize = 0; unsigned int offcur = 0; char lastcmd = 'X', flags; time_t timenow; struct tm *tp; switch ( len = strlen(irawname) ) { /* ** if no filename is specified, assemble the name of the raw file */ case 0: timenow = time(0); tp = localtime(&timenow); snprintf(irawname, RAWNAMESZ, "%s/atop_%04d%02d%02d", BASEPATH, tp->tm_year+1900, tp->tm_mon+1, tp->tm_mday); break; /* ** if date specified as filename in format YYYYMMDD, assemble ** the full pathname of the raw file */ case 8: if ( access(irawname, F_OK) == 0) break; /* existing file */ if (lookslikedatetome(irawname)) { char savedname[16]; strcpy(savedname, irawname); // no overflow (len=8) snprintf(irawname, RAWNAMESZ, "%s/atop_%s", BASEPATH, savedname); break; } /* ** if one or more 'y' (yesterday) characters are used and that ** string is not known as an existing file, the standard logfile ** is shown from N days ago (N is determined by the number ** of y's). */ default: if ( access(irawname, F_OK) == 0) break; /* existing file */ /* ** make a string existing of y's to compare with */ py = malloc(len+1); ptrverify(py, "Malloc failed for 'yes' sequence\n"); memset(py, 'y', len); *(py+len) = '\0'; if ( strcmp(irawname, py) == 0 ) { timenow = time(0); timenow -= len*3600*24; tp = localtime(&timenow); snprintf(irawname, RAWNAMESZ, "%s/atop_%04d%02d%02d", BASEPATH, tp->tm_year+1900, tp->tm_mon+1, tp->tm_mday); } free(py); } /* ** make sure the file is a regular file (seekable) or ** a pipe (not seekable) */ if (stat(irawname, &filestat) == -1) { fprintf(stderr, "%s - ", irawname); perror("stat raw file"); cleanstop(7); } if (!S_ISREG(filestat.st_mode) && !S_ISFIFO(filestat.st_mode)) { fprintf(stderr, "raw file must be a regular file or pipe\n"); cleanstop(7); } isregular = S_ISREG(filestat.st_mode); /* ** open raw file for reading */ if ( (rawfd = open(irawname, O_RDONLY)) == -1) { fprintf(stderr, "%s - ", irawname); perror("open raw file"); cleanstop(7); } /* ** make the kernel readahead more effective */ if (isregular) posix_fadvise(rawfd, 0, 0, POSIX_FADV_SEQUENTIAL); /* ** read the raw header and verify the magic */ if ( readchunk(rawfd, &rh, sizeof rh) < sizeof rh) { fprintf(stderr, "can not read raw file header\n"); cleanstop(7); } if (rh.magic != MYMAGIC) { fprintf(stderr, "file %s does not contain raw atop/atopsar " "output (wrong magic number)\n", irawname); cleanstop(7); } /* ** magic okay, but file-layout might have been modified */ if (rh.sstatlen != sizeof(struct sstat) || rh.tstatlen != sizeof(struct tstat) || rh.cstatlen != sizeof(struct cstat) || rh.rawheadlen != sizeof(struct rawheader) || rh.rawreclen != sizeof(struct rawrecord) ) { fprintf(stderr, "sstatlen: %d/%lu\n", rh.sstatlen, sizeof(struct sstat)); fprintf(stderr, "cstatlen: %d/%lu\n", rh.cstatlen, sizeof(struct cstat)); fprintf(stderr, "tstatlen: %d/%lu\n", rh.tstatlen, sizeof(struct tstat)); fprintf(stderr, "headlen: %d/%lu\n", rh.rawheadlen, sizeof(struct rawheader)); fprintf(stderr, "reclen: %d/%lu\n", rh.rawreclen, sizeof(struct rawrecord)); fprintf(stderr, "\nraw file %s has incompatible format\n", irawname); if (rh.aversion & 0x8000 && (rh.aversion & 0x7fff) != getnumvers()) { fprintf(stderr, "(created by version %d.%d - " "current version %d.%d)\n", (rh.aversion >> 8) & 0x7f, rh.aversion & 0xff, getnumvers() >> 8, getnumvers() & 0x7f); } else { fprintf(stderr, "(files from other system architectures might" " be binary incompatible)\n"); } close(rawfd); cleanstop(7); } memcpy(&utsname, &rh.utsname, sizeof utsname); utsnodenamelen = strlen(utsname.nodename); supportflags = rh.supportflags; osrel = rh.osrel; osvers = rh.osvers; ossub = rh.ossub; interval = 0; if (rh.hertz) hertz = rh.hertz; if (rh.pagesize) pagesize = rh.pagesize; if (rh.pidwidth) pidwidth = rh.pidwidth; else pidwidth = 5; /* ** allocate a list for backtracking of rawrecord-offsets */ if (isregular) { offlist = malloc(sizeof(off_t) * OFFCHUNK); ptrverify(offlist, "Malloc failed for backtrack list\n"); offsize = OFFCHUNK; *offlist = lseek(rawfd, 0, SEEK_CUR); offcur = 1; } /* ** read a raw record header until end-of-file */ sampcnt = 0; while (lastcmd && lastcmd != 'q') { while ( (rv = getrawrec(rawfd, &rr, rh.rawreclen, isregular)) == rh.rawreclen) { unsigned int k, l; cursortime = rr.curtime; // maintain current /* ** normalize the begintime and endtime if the ** format hh:mm has been used instead of an ** absolute date-time string ** (only happens for the first record) */ if (begintime <= SECONDSINDAY) begintime = normalize_epoch(cursortime, begintime); if (endtime && endtime <= SECONDSINDAY) endtime = normalize_epoch(cursortime, endtime); /* ** store the offset of the raw record in the offset list ** in case of offset list overflow, extend the list */ if (isregular) { *(offlist+offcur) = lseek(rawfd, 0, SEEK_CUR) - rh.rawreclen; if ( ++offcur >= offsize ) { offlist = realloc(offlist, (offsize+OFFCHUNK)*sizeof(off_t)); ptrverify(offlist, "Realloc failed for backtrack list\n"); offsize+= OFFCHUNK; } } /* ** check if this sample is within the time-range ** specified with the -b and -e flags (if any) */ if ( (begintime > cursortime) ) { lastcmd = 1; if (isregular) { static off_t curr_pos = -1; off_t next_pos; lastcmd = 1; next_pos = lseek(rawfd, rr.scomplen+rr.pcomplen+rr.ccomplen+rr.icomplen, SEEK_CUR); if ((curr_pos >> READAHEADOFF) != (next_pos >> READAHEADOFF)) { int liResult; /* just read READAHEADSIZE bytes into page cache */ char *buf = malloc(READAHEADSIZE); ptrverify(buf, "Malloc failed for readahead"); liResult = pread(rawfd, buf, READAHEADSIZE, next_pos & ~(READAHEADSIZE - 1)); if(liResult == -1) { char lcMessage[64]; snprintf(lcMessage, sizeof(lcMessage) - 1, "%s:%d - Error %d in readahead\n", __FILE__, __LINE__, errno); fprintf(stderr, "%s", lcMessage); } free(buf); } curr_pos = next_pos; continue; } else // named pipe not seekable { char *dummybuf = malloc(rr.scomplen+rr.pcomplen+rr.ccomplen+rr.icomplen); ptrverify(dummybuf, "Malloc rawlog pipe buffer failed\n"); readchunk(rawfd, dummybuf, rr.scomplen+rr.pcomplen+rr.ccomplen+rr.icomplen); free(dummybuf); } continue; } begintime = 0; // allow earlier times from now on if ( (endtime && endtime < cursortime) ) { if (isregular) free(offlist); close(rawfd); return isregular; } /* ** allocate space, read compressed system-level ** metrics and decompress */ if ( !getrawsstat(rawfd, &sstat, rr.scomplen) ) cleanstop(7); /* ** allocate space, read compressed process-level ** metrics and decompress */ devtstat.taskall = malloc(sizeof(struct tstat) * rr.ndeviat); if (rr.totproc < rr.nactproc) // compat old raw files devtstat.procall = malloc(sizeof(struct tstat *) * rr.nactproc); else devtstat.procall = malloc(sizeof(struct tstat *) * rr.totproc); devtstat.procactive = malloc(sizeof(struct tstat *) * rr.nactproc); ptrverify(devtstat.taskall, "Malloc failed for %d stored tasks\n", rr.ndeviat); ptrverify(devtstat.procall, "Malloc failed for total %d processes\n", rr.totproc); ptrverify(devtstat.procactive, "Malloc failed for %d active processes\n", rr.nactproc); if ( !getrawtstat(rawfd, devtstat.taskall, rr.pcomplen, rr.ndeviat) ) cleanstop(7); for (i=j=k=l=0; i < rr.ndeviat; i++) { if ( (devtstat.taskall+i)->gen.isproc) { devtstat.procall[j++] = devtstat.taskall+i; if (! (devtstat.taskall+i)->gen.wasinactive) devtstat.procactive[k++] = devtstat.taskall+i; } if (! (devtstat.taskall+i)->gen.wasinactive) l++; } devtstat.ntaskall = i; devtstat.nprocall = j; devtstat.nprocactive = k; devtstat.ntaskactive = l; devtstat.totrun = rr.totrun; devtstat.totslpi = rr.totslpi; devtstat.totslpu = rr.totslpu; devtstat.totidle = rr.totidle; devtstat.totzombie = rr.totzomb; /* ** allocate space, read compressed cgroup-level ** metrics, the pidlist and decompress */ if (rr.flags & RRCGRSTAT) { if ( !getrawcstat(rawfd, &devchain, rr.ccomplen, rr.coriglen, rr.icomplen, rr.ncgroups, rr.ncgpids) ) cleanstop(7); } /* ** activate the installed print function to visualize ** the system metrics, process metrics and cgroup metrics */ sampcnt++; if ( (rh.supportflags & RAWLOGNG) == RAWLOGNG) { if (rr.flags & RRACCTACTIVE) supportflags |= ACCTACTIVE; else supportflags &= ~ACCTACTIVE; if (rr.flags & RRIOSTAT) supportflags |= IOSTAT; else supportflags &= ~IOSTAT; } if (rr.flags & RRNETATOP) supportflags |= NETATOP; else supportflags &= ~NETATOP; if (rr.flags & RRNETATOPD) supportflags |= NETATOPD; else supportflags &= ~NETATOPD; if (rr.flags & RRCGRSTAT) supportflags |= CGROUPV2; else supportflags &= ~CGROUPV2; if (rr.flags & RRCONTAINERSTAT) supportflags |= CONTAINERSTAT; else supportflags &= ~CONTAINERSTAT; if (rr.flags & RRGPUSTAT) supportflags |= GPUSTAT; else supportflags &= ~GPUSTAT; flags = rr.flags & RRBOOT; nrgpus = sstat.gpu.nrgpus; if (isregular) { (void) fstat(rawfd, &filestat); if ( filestat.st_size - lseek(rawfd, (off_t)0, SEEK_CUR) <= rh.rawreclen) flags |= RRLAST; } do { for (v=0; handlers[v].handle_sample; v++) { lastcmd = (handlers[v].handle_sample)(rr.curtime, rr.interval, &devtstat, &sstat, devchain, rr.ncgroups, rr.ncgpids, rr.nexit, rr.noverflow, flags); } } while (!isregular && ( lastcmd == MSAMPPREV || lastcmd == MRESET || lastcmd == MEND || (lastcmd == MSAMPBRANCH && begintime < cursortime) )); free(devtstat.taskall); free(devtstat.procall); free(devtstat.procactive); if (rr.flags & RRCGRSTAT) { free(devchain->cstat); free(devchain->proclist); free(devchain); } switch (lastcmd) { case MSAMPPREV: if (offcur >= 2) offcur -= 2; else offcur = 0; lseek(rawfd, *(offlist+offcur), SEEK_SET); break; case MRESET: lseek(rawfd, *offlist, SEEK_SET); offcur = 1; break; case MEND: begintime = 0x7fffffff; lastcmd = MSAMPBRANCH; break; case MSAMPBRANCH: if (begintime < cursortime && isregular) { lseek(rawfd, *offlist, SEEK_SET); offcur = 1; } } } begintime = 0; // allow earlier times from now on if (isregular) { if (rv != 0) // inconsistent/incomplete raw file? mcleanstop(7, "inconsistent raw file!\n"); if (offcur >= 1) offcur--; lseek(rawfd, *(offlist+offcur), SEEK_SET); } else { lastcmd = 'q'; } } if (isregular) free(offlist); close(rawfd); return isregular; } /* ** read the next raw record from the raw logfile */ static int getrawrec(int rawfd, struct rawrecord *prr, int rrlen, int isregular) { // read rawrecord itself // struct stat filestats; int n = readchunk(rawfd, prr, rrlen); // verify file consistency: // are all expected compressed buffers written // behind the raw record header? // if (n == rrlen && isregular) { if ( fstat(rawfd, &filestats) == 0) { if (filestats.st_size - lseek(rawfd, 0, SEEK_CUR) < prr->scomplen + prr->pcomplen + prr->ccomplen + prr->icomplen) { mcleanstop(9, "raw file is incomplete!\n"); } } } return n; } /* ** read the system-level statistics from the current offset */ static int getrawsstat(int rawfd, struct sstat *sp, int complen) { Byte *compbuf; unsigned long uncomplen = sizeof(struct sstat); int rv; compbuf = malloc(complen); ptrverify(compbuf, "Malloc failed for reading compressed sysstats\n"); if ( readchunk(rawfd, compbuf, complen) < complen) { free(compbuf); return 0; } rv = uncompress((Byte *)sp, &uncomplen, compbuf, complen); testcompval(rv, "uncompress"); free(compbuf); return 1; } /* ** read the process-level statistics from the current offset */ static int getrawtstat(int rawfd, struct tstat *pp, int complen, int ndeviat) { Byte *compbuf; unsigned long uncomplen = sizeof(struct tstat) * ndeviat; int rv; compbuf = malloc(complen); ptrverify(compbuf, "Malloc failed for reading compressed procstats\n"); if ( readchunk(rawfd, compbuf, complen) < complen) { free(compbuf); return 0; } rv = uncompress((Byte *)pp, &uncomplen, compbuf, complen); testcompval(rv, "uncompress"); free(compbuf); return 1; } /* ** read the cgroup-level statistics and pidlist from the current offset */ static int getrawcstat(int rawfd, struct cgchainer **cpp, unsigned long ccomplen, unsigned long coriglen, unsigned long icomplen, int ncgroups, int npids) { Byte *ccompbuf, *corigbuf, *icompbuf, *iorigbuf; int rv; unsigned long ioriglen = npids * sizeof(pid_t); /* ** read all cstat structs */ ccompbuf = malloc(ccomplen); corigbuf = malloc(coriglen); ptrverify(ccompbuf, "Malloc failed for reading compressed cgroups\n"); ptrverify(corigbuf, "Malloc failed for decompressing cgroups\n"); if ( readchunk(rawfd, ccompbuf, ccomplen) < ccomplen) { free(ccompbuf); free(corigbuf); return 0; } rv = uncompress((Byte *)corigbuf, &coriglen, ccompbuf, ccomplen); testcompval(rv, "uncompress cgroups"); free(ccompbuf); /* ** read pidlist */ icompbuf = malloc(icomplen); iorigbuf = malloc(ioriglen); ptrverify(icompbuf, "Malloc failed for reading pidlist\n"); ptrverify(iorigbuf, "Malloc failed for decompresssed pidlist\n"); if ( readchunk(rawfd, icompbuf, icomplen) < icomplen) { free(corigbuf); free(icompbuf); free(iorigbuf); return 0; } rv = uncompress((Byte *)iorigbuf, &ioriglen, icompbuf, icomplen); testcompval(rv, "uncompress cgroups pidlist"); free(icompbuf); /* ** reconstruct an array of cgchainer structs from which ** each entry refers to one cstat struct and its own start ** entry in the pidlist */ cgbuildarray(cpp, (char *)corigbuf, (char *)iorigbuf, ncgroups); return 1; } /* ** verify if a particular ascii-string is in the format yyyymmdd */ static int lookslikedatetome(char *p) { register int i; for (i=0; i < 8; i++) if ( !isdigit(*(p+i)) ) return 0; if (*p != '2') return 0; /* adapt this in the year 3000 */ if ( *(p+4) > '1') return 0; if ( *(p+6) > '3') return 0; return 1; /* yes, looks like a date to me */ } /* ** test return code of (de)compression and ** terminate when wrong */ static void testcompval(int rv, char *func) { switch (rv) { case Z_OK: case Z_STREAM_END: case Z_NEED_DICT: break; case Z_MEM_ERROR: mcleanstop(7, "atop/atopsar - " "%s: failed due to lack of memory\n", func); case Z_BUF_ERROR: mcleanstop(7, "atop/atopsar - " "%s: failed due to lack of room in buffer\n", func); case Z_DATA_ERROR: mcleanstop(7, "atop/atopsar - " "%s: failed due to corrupted/incomplete data\n", func); default: mcleanstop(7, "atop/atopsar - " "%s: unexpected error %d\n", func, rv); } } /* ** read chunk of data with specified length ** (specifically important when reading from pipe) ** ** returns: number of bytes read */ static int readchunk(int fd, void *buf, int len) { char *p = buf; int n; while (len > 0) { switch (n = read(fd, p, len)) { case 0: // EOF? return (char *)p - (char *)buf; case -1: perror("read raw file"); cleanstop(9); default: len -= n; p += n; } } return (char *)p - (char *)buf; } atop-2.12.1/rawlog.h0000644000203100020310000001125215064552761013520 0ustar gerlofgerlof/* ** ATOP - System & Process Monitor ** ** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as process-level. ** ========================================================================== ** Author: Gerlof Langeveld ** E-mail: gerlof.langeveld@atoptool.nl ** Date: September 2002 ** -------------------------------------------------------------------------- ** Copyright (C) 2000-2010 Gerlof Langeveld ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ** -------------------------------------------------------------------------- */ #ifndef __RAWLOG__ #define __RAWLOG__ /* ** structure describing the raw file contents ** ** layout raw file: rawheader ** ** rawrecord \ ** compressed system-level statistics | ** compressed process-level statistics | sample 1 ** compressed cgroupv2-level statistics (optional) | ** compressed cgroupv2 pidlist (optional) / ** ** rawrecord \ ** compressed system-level statistics | ** compressed process-level statistics | sample 2 ** compressed cgroupv2-level statistics (optional) | ** compressed cgroupv2 pidlist (optional) / ** ** etcetera ..... */ #define MYMAGIC (unsigned int) 0xfeedbeef #define READAHEADOFF 22 #define READAHEADSIZE (1 << READAHEADOFF) struct rawheader { unsigned int magic; unsigned short aversion; /* creator atop version with MSB */ unsigned short future1; /* can be reused */ unsigned short future2; /* can be reused */ unsigned short rawheadlen; /* length of struct rawheader */ unsigned short rawreclen; /* length of struct rawrecord */ unsigned short hertz; /* clock interrupts per second */ unsigned short pidwidth; /* number of digits for PID/TID */ unsigned short sfuture[5]; /* future use */ unsigned int sstatlen; /* length of struct sstat */ unsigned int tstatlen; /* length of struct tstat */ struct utsname utsname; /* info about this system */ char cfuture[8]; /* future use */ unsigned int pagesize; /* size of memory page (bytes) */ int supportflags; /* used features */ int osrel; /* OS release number */ int osvers; /* OS version number */ int ossub; /* OS version subnumber */ int cstatlen; /* length of struct cstat */ int ifuture[5]; /* future use */ }; struct rawrecord { time_t curtime; /* current time (epoch) */ unsigned short flags; /* various flags */ unsigned short ncgroups; /* number of cgroups */ unsigned short sfuture[2]; /* future use */ unsigned int scomplen; /* length of compressed sstat */ unsigned int pcomplen; /* length of compressed tstat's */ unsigned int interval; /* interval (number of seconds) */ unsigned int ndeviat; /* number of tasks in list */ unsigned int nactproc; /* number of processes in list */ unsigned int ntask; /* total number of tasks */ unsigned int totproc; /* total number of processes */ unsigned int totrun; /* number of running threads */ unsigned int totslpi; /* number of sleeping threads(S)*/ unsigned int totslpu; /* number of sleeping threads(D)*/ unsigned int totzomb; /* number of zombie processes */ unsigned int nexit; /* number of exited processes */ unsigned int noverflow; /* number of overflow processes */ unsigned int totidle; /* number of idle threads(I)*/ unsigned int ccomplen; /* length of compressed cstats */ unsigned int coriglen; /* length of original cstats */ unsigned int ncgpids; /* number of cgroups pidlist */ unsigned int icomplen; /* length of compressed pidlist */ unsigned int ifuture; /* future use */ }; #endif atop-2.12.1/showgeneric.c0000644000203100020310000026127515064552761014551 0ustar gerlofgerlof/* ** ATOP - System & Process Monitor ** ** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as process-level. ** ** This source-file contains the print-functions to visualize the calculated ** figures. ** ========================================================================== ** Author: Gerlof Langeveld ** E-mail: gerlof.langeveld@atoptool.nl ** Date: November 1996 ** LINUX-port: June 2000 ** -------------------------------------------------------------------------- ** Copyright (C) 2000-2010 Gerlof Langeveld ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ** -------------------------------------------------------------------------- */ #define _POSIX_C_SOURCE #define _XOPEN_SOURCE #define _GNU_SOURCE #define _DEFAULT_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "atop.h" #include "photoproc.h" #include "photosyst.h" #include "cgroups.h" #include "showgeneric.h" #include "showlinux.h" static struct pselection procsel = {"", {USERSTUB, }, {0,}, "", 0, { 0, }, "", 0, { 0, }, "", "" }; static struct sselection syssel; static void showhelp(int); static int fixedhead; /* boolean: fixate header-lines */ static int sysnosort; /* boolean: suppress sort of resources */ static int threadsort; /* boolean: sort threads per process */ static int avgval; /* boolean: average values i.s.o. total */ static int suppressexit; /* boolean: suppress terminated processes */ static char showtype = MPROCGEN; static char showorder = MSORTCPU; static int maxcpulines = 999; /* maximum cpu lines */ static int maxgpulines = 999; /* maximum gpu lines */ static int maxdsklines = 999; /* maximum disk lines */ static int maxmddlines = 999; /* maximum MDD lines */ static int maxlvmlines = 999; /* maximum LVM lines */ static int maxintlines = 999; /* maximum interface lines */ static int maxifblines = 999; /* maximum infinibnd lines */ static int maxnfslines = 999; /* maximum nfs mount lines */ static int maxcontlines = 999; /* maximum container lines */ static int maxnumalines = 999; /* maximum numa lines */ static int maxllclines = 999; /* maximum llc lines */ static short colorinfo = COLOR_GREEN; static short coloralmost = COLOR_CYAN; static short colorcrit = COLOR_RED; static short colorthread = COLOR_YELLOW; static char winchange; static char keywaiting; // set after key has been pushed back int paused; // boolean: currently in pause-mode int cgroupdepth = 7; // default: cgroups without processes static int cumusers(struct tstat **, struct tstat *, int); static int cumprogs(struct tstat **, struct tstat *, int); static int cumconts(struct tstat **, struct tstat *, int); static void accumulate(struct tstat *, struct tstat *); static int procsuppress(struct tstat *, struct pselection *); static void limitedlines(void); static long getnumval(char *, long, int); static void getsigwinch(int); static void generic_init(void); static char text_samp(time_t, int, struct devtstat *, struct sstat *, struct cgchainer *, int, int, unsigned int, char); static int (*procsort[])(const void *, const void *) = { [MSORTCPU&0x1f]=compcpu, [MSORTMEM&0x1f]=compmem, [MSORTDSK&0x1f]=compdsk, [MSORTNET&0x1f]=compnet, [MSORTGPU&0x1f]=compgpu, }; extern detail_printpair ownprocs[]; /* ** global: incremented by -> key and decremented by <- key */ int startoffset; /* ** main function to handle sample generically ** to show flat text, full screen text or bar graphs */ char generic_samp(time_t curtime, int nsecs, struct devtstat *devtstat, struct sstat *sstat, struct cgchainer *cstats, int ncgroups, int npids, int nexit, unsigned int noverflow, char flag) { static char firstcall = 1; char retval, sorted = 0; if (firstcall) { generic_init(); firstcall = 0; } while (1) { if (displaymode == 'T') // text mode? { // show sample and wait for input or timer expiration // switch (retval = text_samp(curtime, nsecs, devtstat, sstat, cstats, ncgroups, nexit, noverflow, flag)) { case MBARGRAPH: displaymode = 'D'; break; default: return retval; } sorted = 1; // resources have been sorted by text mode } if (displaymode == 'D') // draw mode? { // show sample and wait for input or timer expiration // switch (retval = draw_samp(curtime, nsecs, sstat, flag, sorted)) { case MBARGRAPH: // just switch to text mode displaymode = 'T'; break; case MCGROUPS: // switch to text mode: cgroups if (supportflags & CGROUPV2) showtype = MCGROUPS; else showtype = MPROCGEN; displaymode = 'T'; break; case MPROCGEN: // switch to text mode: generic showtype = MPROCGEN; if (showorder != MSORTAUTO) showorder = MSORTCPU; displaymode = 'T'; break; case MPROCMEM: // switch to text mode: memory showtype = MPROCMEM; if (showorder != MSORTAUTO) showorder = MSORTMEM; displaymode = 'T'; break; case MPROCDSK: // switch to text mode: disk showtype = MPROCDSK; if (showorder != MSORTAUTO) showorder = MSORTDSK; displaymode = 'T'; break; case MPROCNET: // switch to text mode: network if (supportflags & NETATOP || supportflags & NETATOPBPF) { showtype = MPROCNET; if (showorder != MSORTAUTO) showorder = MSORTNET; } else { showtype = MPROCGEN; showorder = MSORTCPU; } displaymode = 'T'; break; case MPROCGPU: // switch to text mode: GPU if (supportflags & GPUSTAT) { showtype = MPROCGPU; if (showorder != MSORTAUTO) showorder = MSORTGPU; } else { showtype = MPROCGEN; showorder = MSORTCPU; } displaymode = 'T'; break; case MPROCSCH: // switch to text mode: scheduling showtype = MPROCSCH; if (showorder != MSORTAUTO) showorder = MSORTCPU; displaymode = 'T'; break; case MPROCVAR: // switch to text mode: various showtype = MPROCVAR; displaymode = 'T'; break; case MPROCARG: // switch to text mode: arguments showtype = MPROCARG; displaymode = 'T'; break; default: return retval; } } } } /* ** print the deviation-counters on process level, cgroups level and ** system level in text mode */ static char text_samp(time_t curtime, int nsecs, struct devtstat *devtstat, struct sstat *sstat, struct cgchainer *cgchainers, int ncgroups, int nexit, unsigned int noverflow, char flag) { register int i, curline, statline, nproc; int firstitem=0, slistsz, alistsz, killpid, killsig; int lastchar; char format1[16], format2[16], branchtime[32]; char *statmsg = NULL, statbuf[80], genline[80]; char *lastsortp, curorder, autoorder; char buf[33]; struct passwd *pwd; struct syscap syscap; fd_set readfds; char eventbuf[1024]; int nrfds; /* ** number of entries in the active list of cgroups/tasks ** to be displayed */ int ncurlist = 0; /* ** cgroupsel points to the merged list of cgchainer pointers (to cgroups) ** and tstat pointers (to processes), representing the cgroups information ** to be displayed (for cgroups visualization); ** ncurlist indicates the number of entries in this list ** ** this list will only be allocated 'lazy' only when ** cgroups visualization is requested ** ** cgroupsort refers to a list with cgchainer pointers in ** sorted order according to the current showorder */ struct cglinesel *cgroupsel = 0; struct cgchainer **cgroupsort = 0; char cstatdeviate = ' ', cstatdepth = ' ', cstatorder = ' '; /* ** curlist points to the active list of tstat pointers that ** should be displayed; ncurlist indicates the number of entries in ** this list */ struct tstat **curlist; /* ** tXcumlist is a list of tstat structs holding one entry ** per accumulated (per user or per program) group of processes ** ** Xcumlist contains the pointers to all structs in tXcumlist ** ** these lists will only be allocated 'lazy' ** only when accumulation is requested */ struct tstat *tpcumlist = 0; // per program accumulation struct tstat **pcumlist = 0; int npcum = 0; char plastorder = 0; struct tstat *tucumlist = 0; // per user accumulation struct tstat **ucumlist = 0; int nucum = 0; char ulastorder = 0; struct tstat *tccumlist = 0; // per container/pod accumulation struct tstat **ccumlist = 0; int nccum = 0; char clastorder = 0; /* ** tsklist contains the pointers to all structs in tstat ** sorted on process with the related threads immediately ** following the process ** ** this list will be allocated 'lazy' */ struct tstat **tsklist = 0; int ntsk = 0; char tlastorder = 0; char zipagain = 0; char tdeviate = 0; /* ** sellist contains the pointers to the structs in tstat ** that are currently selected on basis of a particular ** username (regexp), program name (regexp), container/pod name ** or suppressed terminated procs ** ** this list will be allocated 'lazy' */ struct tstat **sellist = 0; int nsel = 0; char slastorder = 0; char threadallowed = 0; startoffset = 0; /* ** compute the total capacity of this system for the ** four main resources */ totalcap(&syscap, sstat, devtstat->procactive, devtstat->nprocactive); /* ** sort per-cpu statistics on busy percentage ** sort per-logical-volume statistics on busy percentage ** sort per-multiple-device statistics on busy percentage ** sort per-disk statistics on busy percentage ** sort per-interface statistics on busy percentage (if known) */ if (!sysnosort) { if (sstat->cpu.nrcpu > 1 && maxcpulines > 0) qsort(sstat->cpu.cpu, sstat->cpu.nrcpu, sizeof sstat->cpu.cpu[0], cpucompar); if (sstat->gpu.nrgpus > 1 && maxgpulines > 0) qsort(sstat->gpu.gpu, sstat->gpu.nrgpus, sizeof sstat->gpu.gpu[0], gpucompar); if (sstat->dsk.nlvm > 1 && maxlvmlines > 0) qsort(sstat->dsk.lvm, sstat->dsk.nlvm, sizeof sstat->dsk.lvm[0], diskcompar); if (sstat->dsk.nmdd > 1 && maxmddlines > 0) qsort(sstat->dsk.mdd, sstat->dsk.nmdd, sizeof sstat->dsk.mdd[0], diskcompar); if (sstat->dsk.ndsk > 1 && maxdsklines > 0) qsort(sstat->dsk.dsk, sstat->dsk.ndsk, sizeof sstat->dsk.dsk[0], diskcompar); if (sstat->intf.nrintf > 1 && maxintlines > 0) qsort(sstat->intf.intf, sstat->intf.nrintf, sizeof sstat->intf.intf[0], intfcompar); if (sstat->ifb.nrports > 1 && maxifblines > 0) qsort(sstat->ifb.ifb, sstat->ifb.nrports, sizeof sstat->ifb.ifb[0], ifbcompar); if (sstat->nfs.nfsmounts.nrmounts > 1 && maxnfslines > 0) qsort(sstat->nfs.nfsmounts.nfsmnt, sstat->nfs.nfsmounts.nrmounts, sizeof sstat->nfs.nfsmounts.nfsmnt[0], nfsmcompar); if (sstat->cfs.nrcontainer > 1 && maxcontlines > 0) qsort(sstat->cfs.cont, sstat->cfs.nrcontainer, sizeof sstat->cfs.cont[0], contcompar); if (sstat->memnuma.nrnuma > 1 && maxnumalines > 0) qsort(sstat->memnuma.numa, sstat->memnuma.nrnuma, sizeof sstat->memnuma.numa[0], memnumacompar); if (sstat->cpunuma.nrnuma > 1 && maxnumalines > 0) qsort(sstat->cpunuma.numa, sstat->cpunuma.nrnuma, sizeof sstat->cpunuma.numa[0], cpunumacompar); if (sstat->llc.nrllcs > 1 && maxllclines > 0) qsort(sstat->llc.perllc, sstat->llc.nrllcs, sizeof sstat->llc.perllc[0], llccompar); } /* ** loop in which the system resources and the list of active ** processes are shown; the loop will be preempted by receiving ** a timer-signal or when the trigger-button is pressed. */ while (1) { curline = 1; genline[0] = '\0'; /* ** prepare screen or file output for new sample */ if (screen) werase(stdscr); else printf("\n\n"); /* ** print general headerlines */ convdate(curtime, format1); /* date to ascii string */ convtime(curtime, format2); /* time to ascii string */ if (screen) attron(A_REVERSE); int seclen = val2elapstr(nsecs, buf); int lenavail = (screen ? COLS : linelen) - 60 - seclen - utsnodenamelen; int len1 = lenavail / 3; if (len1 <= 0) len1 = 1; int len2 = lenavail - len1 - len1; printg("ATOP - %s%*s%s %s %s %*s" "%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%*s%s elapsed", utsname.nodename, len1, "", format1, format2, " ", len1, "", threadview ? MTHREAD : '-', threadsort ? MTHRSORT : '-', fixedhead ? MSYSFIXED : '-', sysnosort ? MSYSNOSORT : '-', deviatonly ? '-' : MALLACTIVE, usecolors ? '-' : MCOLORS, avgval ? MAVGVAL : '-', calcpss ? MCALCPSS : '-', getwchan ? MGETWCHAN : '-', suppressexit ? MSUPEXITS : '-', procsel.userid[0] != USERSTUB ? MSELUSER : '-', procsel.prognamesz ? MSELPROC : '-', procsel.utsname[0] ? MSELCONT : '-', procsel.pid[0] != 0 ? MSELPID : '-', procsel.argnamesz ? MSELARG : '-', procsel.states[0] ? MSELSTATE : '-', syssel.lvmnamesz + syssel.dsknamesz + syssel.itfnamesz ? MSELSYS : '-', cgroupdepth+0x30, len2, "", buf); if (screen) attroff(A_REVERSE); else printg("\n"); if (screen && paused) { if (usecolors) attron(COLOR_PAIR(FGCOLORBORDER)); attron(A_REVERSE); mvprintw(0, 27 + utsnodenamelen + len1, "PAUSED"); attroff(A_REVERSE); if (usecolors) attroff(COLOR_PAIR(FGCOLORBORDER)); } /* ** print cumulative system- and user-time for all processes */ pricumproc(sstat, devtstat, nexit, noverflow, avgval, nsecs); if (noverflow) { snprintf(statbuf, sizeof statbuf, "Only %d terminated processes handled " "-- %u skipped!", nexit, noverflow); statmsg = statbuf; } curline=2; /* ** print other lines of system-wide statistics */ if (showorder == MSORTAUTO) autoorder = MSORTCPU; else autoorder = showorder; curline = prisyst(sstat, curline, nsecs, avgval, fixedhead, &syssel, &autoorder, maxcpulines, maxgpulines, maxdsklines, maxmddlines, maxlvmlines, maxintlines, maxifblines, maxnfslines, maxcontlines, maxnumalines, maxllclines); /* ** if system-wide statistics do not fit, ** limit the number of variable resource lines ** and try again */ if (screen && curline+2 > LINES) { curline = 2; move(curline, 0); clrtobot(); move(curline, 0); limitedlines(); curline = prisyst(sstat, curline, nsecs, avgval, fixedhead, &syssel, &autoorder, maxcpulines, maxgpulines, maxdsklines, maxmddlines, maxlvmlines, maxintlines, maxifblines, maxnfslines, maxcontlines, maxnumalines, maxllclines); /* ** if system-wide statistics still do not fit, ** the window is really to small */ if (curline+2 > LINES) { endwin(); // finish curses interface fprintf(stderr, "Not enough screen-lines available " "(need at least %d lines)\n", curline+2); fprintf(stderr, "Please resize window....\n"); cleanstop(1); } else { statmsg = "Number of variable resources" " limited to fit in this window"; } } statline = curline; if (screen) move(curline, 0); if (statmsg) { if (screen) { clrtoeol(); if (usecolors) attron(COLOR_PAIR(FGCOLORINFO)); } printg(statmsg); if (screen) { if (usecolors) attroff(COLOR_PAIR(FGCOLORINFO)); } statmsg = NULL; } else { if (flag&RRBOOT) { char *initmsg = "*** System and Process Activity since Boot ***"; char *viewmsg; if (rawreadflag) { if (twinpid) viewmsg = "Twin mode"; else viewmsg = "Rawfile view"; } else { if (rootprivs()) viewmsg = "Unrestricted view (privileged)"; else viewmsg = "Restricted view (unprivileged)"; } if (screen) { if (usecolors) attron(COLOR_PAIR(FGCOLORINFO)); } printg(initmsg); if (screen) { if (usecolors) attroff(COLOR_PAIR(FGCOLORINFO)); printg("%*s", COLS - strlen(initmsg) - strlen(viewmsg), " "); } else { printg("%*s", 80 - strlen(initmsg) - strlen(viewmsg), " "); } if (screen) { if (usecolors) attron(COLOR_PAIR(FGCOLORALMOST)); } printg(viewmsg); if (screen) { if (usecolors) attroff(COLOR_PAIR(FGCOLORALMOST)); } } } if (showtype != MCGROUPS) { /* ** select the required list with tasks to be shown ** ** if cumulative figures required, accumulate resource ** consumption of all processes in the current list */ switch (showtype) { case MCUMUSER: threadallowed = 0; if (ucumlist) /* previous list still available? */ { free(ucumlist); free(tucumlist); ulastorder = 0; } if (deviatonly) nproc = devtstat->nprocactive; else nproc = devtstat->nprocall; /* ** allocate space for new (temporary) list with ** one entry per user (list has worst-case size) */ tucumlist = calloc(sizeof(struct tstat), nproc); ucumlist = malloc(sizeof(struct tstat *) * nproc); ptrverify(tucumlist, "Malloc failed for %d ucum procs\n", nproc); ptrverify(ucumlist, "Malloc failed for %d ucum ptrs\n", nproc); for (i=0; i < nproc; i++) { /* fill pointers */ ucumlist[i] = tucumlist+i; } nucum = cumusers(deviatonly ? devtstat->procactive : devtstat->procall, tucumlist, nproc); curlist = ucumlist; ncurlist = nucum; lastsortp = &ulastorder; break; case MCUMPROC: threadallowed = 0; if (pcumlist) /* previous list still available? */ { free(pcumlist); free(tpcumlist); plastorder = 0; } if (deviatonly) nproc = devtstat->nprocactive; else nproc = devtstat->nprocall; /* ** allocate space for new (temporary) list with ** one entry per program (list has worst-case size) */ tpcumlist = calloc(sizeof(struct tstat), nproc); pcumlist = malloc(sizeof(struct tstat *) * nproc); ptrverify(tpcumlist, "Malloc failed for %d pcum procs\n", nproc); ptrverify(pcumlist, "Malloc failed for %d pcum ptrs\n", nproc); for (i=0; i < nproc; i++) { /* fill pointers */ pcumlist[i] = tpcumlist+i; } npcum = cumprogs(deviatonly ? devtstat->procactive : devtstat->procall, tpcumlist, nproc); curlist = pcumlist; ncurlist = npcum; lastsortp = &plastorder; break; case MCUMCONT: threadallowed = 0; if (ccumlist) /* previous list still available? */ { free(ccumlist); free(tccumlist); clastorder = 0; } if (deviatonly) nproc = devtstat->nprocactive; else nproc = devtstat->nprocall; /* ** allocate space for new (temporary) list with ** one entry per user (list has worst-case size) */ tccumlist = calloc(sizeof(struct tstat), nproc); ccumlist = malloc(sizeof(struct tstat *) * nproc); ptrverify(tccumlist, "Malloc failed for %d ccum procs\n", nproc); ptrverify(ccumlist, "Malloc failed for %d ccum ptrs\n", nproc); for (i=0; i < nproc; i++) { /* fill pointers */ ccumlist[i] = tccumlist+i; } nccum = cumconts(deviatonly ? devtstat->procactive : devtstat->procall, tccumlist, nproc); curlist = ccumlist; ncurlist = nccum; lastsortp = &clastorder; break; default: threadallowed = 1; if (deviatonly && showtype != MPROCMEM && showorder != MSORTMEM ) { curlist = devtstat->procactive; ncurlist = devtstat->nprocactive; } else { curlist = devtstat->procall; ncurlist = devtstat->nprocall; } lastsortp = &tlastorder; if ( procsel.userid[0] == USERSTUB && !procsel.prognamesz && !procsel.utsname[0] && !procsel.states[0] && !procsel.argnamesz && !procsel.pid[0] && !suppressexit ) /* no selection wanted */ break; /* ** selection specified for tasks: ** create new (worst case) pointer list if needed */ free(sellist); // remove previous list if needed sellist = malloc(sizeof(struct tstat *) * ncurlist); ptrverify(sellist, "Malloc failed for %d select ptrs\n", ncurlist); for (i=nsel=0; i < ncurlist; i++) { if (procsuppress(*(curlist+i), &procsel)) continue; if (curlist[i]->gen.state == 'E' && suppressexit ) continue; sellist[nsel++] = curlist[i]; } curlist = sellist; ncurlist = nsel; tlastorder = 0; /* new sort and zip normal view */ slastorder = 0; /* new sort and zip now */ lastsortp = &slastorder; } /* ** sort the list in required order ** (default CPU-consumption) and print the list */ if (showorder == MSORTAUTO) curorder = autoorder; else curorder = showorder; /* ** determine size of list to be displayed */ if (screen) slistsz = LINES-curline-2; else if (threadview && threadallowed) slistsz = devtstat->ntaskactive; else slistsz = ncurlist; if (ncurlist > 0 && slistsz > 0) { /* ** if sorting order is changed, sort again */ if (*lastsortp != curorder) { qsort(curlist, ncurlist, sizeof(struct tstat *), procsort[(int)curorder&0x1f]); *lastsortp = curorder; zipagain = 1; } if (threadview && threadallowed) { int ntotal, j, t; if (deviatonly && showtype != MPROCMEM && showorder != MSORTMEM ) ntotal = devtstat->ntaskactive; else ntotal = devtstat->ntaskall; /* ** check if existing pointer list still usable ** if not, allocate new pointer list to be able ** to zip process list with references to threads */ if (!tsklist || ntsk != ntotal || tdeviate != deviatonly) { free(tsklist); // remove current tsklist = malloc(sizeof(struct tstat *) * ntotal); ptrverify(tsklist, "Malloc failed for %d taskptrs\n", ntotal); ntsk = ntotal; tdeviate = deviatonly; zipagain = 1; } else j = ntotal; /* ** zip process list with thread list */ if (zipagain) { struct tstat *tall = devtstat->taskall; struct tstat *pcur; long int n; for (i=j=0; i < ncurlist; i++) { pcur = curlist[i]; tsklist[j++] = pcur; // take process n = j; // start index of threads for (t = pcur - tall + 1; t < devtstat->ntaskall && pcur->gen.tgid && pcur->gen.tgid == (tall+t)->gen.tgid; t++) { if (procsuppress(tall+t, &procsel)) continue; if (deviatonly && showtype != MPROCMEM && showorder != MSORTMEM ) { if (!(tall+t)->gen.wasinactive) tsklist[j++] = tall+t; } else { tsklist[j++] = tall+t; } } if (threadsort && j-n > 0 && curorder != MSORTMEM) { qsort(&tsklist[n], j-n, sizeof(struct tstat *), procsort[(int)curorder&0x1f]); } } zipagain = 0; } curlist = tsklist; ncurlist = j; } /* ** print the header ** first determine the column-header for the current ** sorting order of processes */ if (screen) { attron(A_REVERSE); move(curline+1, 0); } prihead(firstitem/slistsz+1, (ncurlist-1)/slistsz+1, &showtype, &curorder, showorder == MSORTAUTO ? 1 : 0, sstat->cpu.nrcpu); if (screen) { attroff(A_REVERSE); clrtobot(); } /* ** print the list */ priproc(curlist, firstitem, ncurlist, curline+2, firstitem/slistsz+1, (ncurlist-1)/slistsz+1, showtype, curorder, &syscap, nsecs, avgval); } } else // MCGROUPS: print cgroups { /* ** sort the list in required order ** (default CPU-consumption) and print the list */ if (showorder == MSORTAUTO) curorder = autoorder; else curorder = showorder; /* ** make new list with a selection (if needed) of cgroups ** merged with processes related to those cgroups */ if (cgroupsel == NULL || // not created yet cstatdeviate != deviatonly || // or not the right contents? cstatdepth != cgroupdepth || cstatorder != curorder ) { struct tstat **tp; /* ** get sorted list of pointers to the cgchainer structs ** ** when a list has been created already that is ** not suitable, first remove it */ free(cgroupsort); cgroupsort = cgsort(cgchainers, ncgroups, curorder); /* ** determine required list of processes (all or active) ** for memory usage even non-active processes are taken */ if (deviatonly && curorder != MSORTMEM) { nproc = devtstat->nprocactive; tp = devtstat->procactive; } else { nproc = devtstat->nprocall; tp = devtstat->procall; } /* ** when a selection list has been created ** already that is not suitable, first remove it */ free(cgroupsel); /* ** create new merged list of cgroups and processes */ ncurlist = mergecgrouplist(&cgroupsel, cgroupdepth, cgroupsort, ncgroups, tp, nproc, curorder); /* ** preserve list characteristics */ cstatdeviate = deviatonly; cstatdepth = cgroupdepth; cstatorder = curorder; } /* ** determine size of list to be displayed */ if (screen) slistsz = LINES-curline-2; else slistsz = ncurlist; if (ncurlist > 0 && slistsz > 0) { /* ** print the header */ if (screen) { attron(A_REVERSE); move(curline+1, 0); } prihead(firstitem/slistsz+1, (ncurlist-1)/slistsz+1, &showtype, &curorder, curorder == MSORTAUTO ? 1 : 0, sstat->cpu.nrcpu); if (screen) { attroff(A_REVERSE); clrtobot(); } /* ** print the list of cgroups */ pricgroup(cgroupsel, firstitem, ncurlist, curline+2, firstitem/slistsz+1, (ncurlist-1)/slistsz+1, &syscap, nsecs, avgval); } } alistsz = ncurlist; /* preserve size of active list */ /* ** in case of writing to a terminal, the user can also enter ** a character to switch options, etc */ if (screen) { /* ** refresh screen output generated sofar */ move(statline, 0); refresh(); if (twinpid && !keywaiting) // twin mode? { struct sigaction sigact, sigold; /* ** catch window size changes while in select */ memset(&sigact, 0, sizeof sigact); sigact.sa_handler = getsigwinch; sigaction(SIGWINCH, &sigact, &sigold); winchange = 0; /* ** await input character from keyboard, or ** inotify trigger in case of twin mode, or ** interval timer expiration */ FD_ZERO(&readfds); FD_SET(0, &readfds); if (!paused && fdinotify != -1) // twin mode? { FD_SET(fdinotify, &readfds); nrfds = fdinotify + 1; } else { nrfds = 1; } switch (select(nrfds, &readfds, (fd_set *)0, (fd_set *)0, (struct timeval *)0)) { case -1: /* ** window change or timer expiration? */ if (winchange) { // window change: set new dimensions struct winsize w; ioctl(0, TIOCGWINSZ, &w); resizeterm(w.ws_row, w.ws_col); lastchar = KEY_RESIZE; } else { // time interrupt lastchar = 0; } break; default: /* ** inotify trigger that new sample has been written? ** pretend as if the 't' key has been pressed ** to read that sample ** ** otherwise: read keystroke from keyboard */ if (FD_ISSET(fdinotify, &readfds)) { read(fdinotify, eventbuf, sizeof eventbuf); lastchar = MSAMPNEXT; } else { lastchar = getch(); } } sigaction(SIGWINCH, &sigold, (struct sigaction *)0); } else // no twin mode: neutral state is getch() { lastchar = getch(); keywaiting = 0; } switch (lastchar) { /* ** timer expired */ case ERR: case 0: timeout(0); (void) getch(); timeout(-1); goto free_and_return; /* ** stop it */ case MQUIT: move(LINES-1, 0); clrtoeol(); refresh(); cleanstop(0); /* ** switch to bar graph mode */ case MBARGRAPH: erase(); refresh(); goto free_and_return; /* ** manual trigger for next sample */ case MSAMPNEXT: if (paused && !twinpid) { beep(); break; } getalarm(0); goto free_and_return; /* ** manual trigger for previous sample */ case MSAMPPREV: if (!rawreadflag) { statmsg = "Only allowed in twin mode or " "when viewing raw file!"; beep(); break; } if (!paused && twinpid) { paused=1; // implicit pause in twin mode clrtoeol(); refresh(); } goto free_and_return; /* ** branch to certain time stamp */ case MSAMPBRANCH: if (!rawreadflag) { statmsg = "Only allowed in twin mode or " "when viewing raw file!"; beep(); break; } if (!paused && twinpid) { paused=1; // implicit pause in twin mode clrtoeol(); refresh(); } echo(); move(statline, 0); clrtoeol(); printw("Enter new time " "(format [YYYYMMDD]hhmm[ss]): "); branchtime[0] = '\0'; scanw("%31s\n", branchtime); noecho(); begintime = cursortime; if ( !getbranchtime(branchtime, &begintime) ) { move(statline, 0); clrtoeol(); statmsg = "Wrong time format!"; beep(); begintime = 0; break; } goto free_and_return; /* ** sort order automatically depending on ** most busy resource */ case MSORTAUTO: showorder = MSORTAUTO; firstitem = 0; break; /* ** sort in cpu-activity order */ case MSORTCPU: showorder = MSORTCPU; firstitem = 0; break; /* ** sort in memory-consumption order */ case MSORTMEM: showorder = MSORTMEM; firstitem = 0; break; /* ** sort in disk-activity order */ case MSORTDSK: showorder = MSORTDSK; firstitem = 0; break; /* ** sort in network-activity order */ case MSORTNET: if ( !(supportflags & NETATOP || supportflags & NETATOPBPF)) { statmsg = "Ignored: 'netatop' or 'netatop-bpf' not " "active, no -K specified or no root privs"; break; } showorder = MSORTNET; firstitem = 0; break; /* ** sort in gpu-activity order */ case MSORTGPU: if ( !(supportflags & GPUSTAT) ) { statmsg = "Ignored: no GPU daemon running or " "no -k specified"; break; } showorder = MSORTGPU; firstitem = 0; break; /* ** general figures per process */ case MPROCGEN: showtype = MPROCGEN; if (showorder != MSORTAUTO) showorder = MSORTCPU; firstitem = 0; break; /* ** memory-specific figures per process */ case MPROCMEM: showtype = MPROCMEM; if (showorder != MSORTAUTO) showorder = MSORTMEM; firstitem = 0; break; /* ** disk-specific figures per process */ case MPROCDSK: showtype = MPROCDSK; if (showorder != MSORTAUTO) showorder = MSORTDSK; firstitem = 0; break; /* ** network-specific figures per process */ case MPROCNET: if ( !(supportflags & NETATOP || supportflags & NETATOPBPF) ) { statmsg = "Ignored: 'netatop' or 'netatop-bpf' not " "active, no -K specified or no root privs"; break; } showtype = MPROCNET; if (showorder != MSORTAUTO) showorder = MSORTNET; firstitem = 0; break; /* ** GPU-specific figures per process */ case MPROCGPU: if ( !(supportflags & GPUSTAT) ) { statmsg = "Ignored: no GPU daemon running or " "no -k specified"; break; } showtype = MPROCGPU; if (showorder != MSORTAUTO) showorder = MSORTGPU; firstitem = 0; break; /* ** various info per process */ case MPROCVAR: showtype = MPROCVAR; firstitem = 0; break; /* ** command line per process */ case MPROCARG: showtype = MPROCARG; firstitem = 0; break; /* ** cgroup v2 info per process */ case MCGROUPS: if ( !(supportflags & CGROUPV2) ) { statmsg = "No cgroup v2 metrics " "available; request ignored!"; break; } showtype = MCGROUPS; firstitem = 0; break; case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': cgroupdepth = lastchar - 0x30; firstitem = 0; break; /* ** own defined output per process */ case MPROCOWN: if (! ownprocs[0].pf) { statmsg = "Own process line is not " "configured in rc-file; " "request ignored"; break; } showtype = MPROCOWN; firstitem = 0; break; /* ** scheduling-values per process */ case MPROCSCH: showtype = MPROCSCH; if (showorder != MSORTAUTO) showorder = MSORTCPU; firstitem = 0; break; /* ** accumulated resource consumption per user */ case MCUMUSER: statmsg = "Consumption per user; use 'a' to " "toggle between all/active processes"; showtype = MCUMUSER; firstitem = 0; break; /* ** accumulated resource consumption per program */ case MCUMPROC: statmsg = "Consumption per program; use 'a' to " "toggle between all/active processes"; showtype = MCUMPROC; firstitem = 0; break; /* ** accumulated resource consumption per container/pod */ case MCUMCONT: if (!rawreadflag && !rootprivs()) { statmsg = "No privileges to get " "container/pod identity!"; break; } statmsg = "Consumption per container/pod; use 'a' to " "toggle between all/active processes"; showtype = MCUMCONT; firstitem = 0; break; /* ** help wanted? */ case MHELP1: case MHELP2: alarm(0); /* stop the clock */ move(1, 0); clrtobot(); /* blank the screen */ refresh(); showhelp(2); move(statline, 0); if (interval && !paused && !rawreadflag) alarm(1); /* force new sample */ firstitem = 0; if (twinpid) // twin mode? { // jump to last sample after awaiting input begintime = 0x7fffffff; lastchar = MSAMPBRANCH; goto free_and_return; } break; /* ** send signal to process */ case MKILLPROC: if (rawreadflag && !twinpid) { statmsg = "Not possible when viewing " "raw file!"; beep(); break; } alarm(0); /* stop the clock */ killpid = getnumval("Pid of process: ", 0, statline); switch (killpid) { case 0: case -1: break; case 1: statmsg = "Sending signal to pid 1 not " "allowed!"; beep(); break; default: clrtoeol(); killsig = getnumval("Signal [%d]: ", 15, statline); if ( kill(killpid, killsig) == -1) { statmsg = "Not possible to " "send signal to this pid!"; beep(); } } if (!paused && !twinpid) alarm(3); /* set short timer */ firstitem = 0; if (twinpid) // twin mode? { begintime = 0x7fffffff; lastchar = MSAMPBRANCH; goto free_and_return; } break; /* ** change interval timeout */ case MINTERVAL: if (rawreadflag) { statmsg = "Not possible in twin mode or " "when viewing raw file!"; beep(); break; } alarm(0); /* stop the clock */ interval = getnumval("New interval in seconds " "(now %d): ", interval, statline); if (interval) { if (!paused) alarm(1); /* set short timer */ } else { statmsg = "No timer set; waiting for " "manual trigger ('t')....."; } firstitem = 0; if (twinpid) // twin mode? { begintime = 0x7fffffff; lastchar = MSAMPBRANCH; goto free_and_return; } break; /* ** focus on specific user */ case MSELUSER: alarm(0); /* stop the clock */ echo(); move(statline, 0); clrtoeol(); printw("Users as regular expression or " "one numerical UID (enter=all users): "); procsel.username[0] = '\0'; scanw("%255s\n", procsel.username); noecho(); if (procsel.username[0]) /* data entered ? */ { regex_t userregex; int u = 0; if ( regcomp(&userregex, procsel.username, REG_NOSUB)) { statmsg = "Invalid regular " "expression!"; beep(); procsel.username[0] = '\0'; } else { while ( (pwd = getpwent())) { if (regexec(&userregex, pwd->pw_name, 0, NULL, 0)) continue; if (u < MAXUSERSEL-1) { procsel.userid[u] = pwd->pw_uid; u++; } } endpwent(); procsel.userid[u] = USERSTUB; if (u == 0) { /* ** possibly a numerical ** value specified? */ if (numeric( procsel.username)) { procsel.userid[0] = atoi(procsel.username); procsel.userid[1] = USERSTUB; } else { statmsg = "No user-names " "match this " "pattern!"; beep(); } } } } else { procsel.userid[0] = USERSTUB; } if (interval && !paused && !rawreadflag) alarm(3); /* set short timer */ firstitem = 0; if (twinpid) // twin mode? { begintime = 0x7fffffff; lastchar = MSAMPBRANCH; goto free_and_return; } break; /* ** focus on specific process-name */ case MSELPROC: alarm(0); /* stop the clock */ echo(); move(statline, 0); clrtoeol(); printw("Process-name as regular " "expression (enter=no regex): "); procsel.prognamesz = 0; procsel.progname[0] = '\0'; scanw("%63s\n", procsel.progname); procsel.prognamesz = strlen(procsel.progname); if (procsel.prognamesz) { if (regcomp(&procsel.progregex, procsel.progname, REG_NOSUB)) { statmsg = "Invalid regular " "expression!"; beep(); procsel.prognamesz = 0; procsel.progname[0] = '\0'; } } noecho(); move(statline, 0); if (interval && !paused && !rawreadflag) alarm(3); /* set short timer */ firstitem = 0; if (twinpid) // twin mode? { begintime = 0x7fffffff; lastchar = MSAMPBRANCH; goto free_and_return; } break; /* ** focus on specific container/pod id */ case MSELCONT: if (!rawreadflag && !rootprivs()) { statmsg = "No privileges to get " "container/pod identity!"; beep(); break; } alarm(0); /* stop the clock */ echo(); move(statline, 0); clrtoeol(); printw("Container or pod name (enter=all, " "'host'=host processes): "); procsel.utsname[0] = '\0'; scanw("%15s", procsel.utsname); procsel.utsname[UTSLEN] = '\0'; switch (strlen(procsel.utsname)) { case 0: break; // enter key pressed case 4: // host? if (strcmp(procsel.utsname, "host") == 0) { procsel.utsname[0] = 'H'; procsel.utsname[1] = '\0'; } break; } noecho(); move(statline, 0); if (interval && !paused && !rawreadflag) alarm(3); /* set short timer */ firstitem = 0; if (twinpid) // twin mode? { begintime = 0x7fffffff; lastchar = MSAMPBRANCH; goto free_and_return; } break; /* ** focus on specific PIDs */ case MSELPID: alarm(0); /* stop the clock */ echo(); move(statline, 0); clrtoeol(); printw("Comma-separated PIDs of processes " "(enter=no selection): "); scanw("%79s\n", genline); int id = 0; char *pidp = strtok(genline, ","); while (pidp) { char *ep; if (id >= MAXPID-1) { procsel.pid[id] = 0; // stub statmsg = "Maximum number of" "PIDs reached!"; beep(); break; } procsel.pid[id] = strtol(pidp, &ep, 10); if (*ep) { statmsg = "Non-numerical PID!"; beep(); procsel.pid[0] = 0; // stub break; } id++; pidp = strtok(NULL, ","); } procsel.pid[id] = 0; // stub noecho(); move(statline, 0); if (interval && !paused && !rawreadflag) alarm(3); /* set short timer */ firstitem = 0; if (twinpid) // twin mode? { begintime = 0x7fffffff; lastchar = MSAMPBRANCH; goto free_and_return; } break; /* ** focus on specific process/thread state */ case MSELSTATE: alarm(0); /* stop the clock */ echo(); move(statline, 0); clrtoeol(); /* Linux fs/proc/array.c - task_state_array */ printw("Comma-separated thread states within process " "(R|S|D|I|T|t|X|Z|P): "); memset(procsel.states, 0, sizeof procsel.states); scanw("%15s\n", genline); char *sp = strtok(genline, ","); while (sp && *sp) { if (isspace(*sp)) { sp++; continue; } if (strlen(sp) > 1) { statmsg = "Invalid state!"; memset(procsel.states, 0, sizeof procsel.states); break; } int needed = 0; switch (*sp) { case 'R': /* running */ case 'S': /* sleeping */ case 'D': /* disk sleep */ case 'I': /* idle */ case 'T': /* stopped */ case 't': /* tracing stop */ case 'X': /* dead */ case 'Z': /* zombie */ case 'P': /* parked */ if (!strchr(procsel.states, *sp)) needed = 1; break; default: statmsg = "Invalid state!"; memset(procsel.states, 0, sizeof procsel.states); beep(); break; } if (needed) procsel.states[strlen(procsel.states)] = *sp; sp = strtok(NULL, ","); } noecho(); move(statline, 0); if (interval && !paused && !rawreadflag) alarm(3); /* set short timer */ firstitem = 0; if (twinpid) // twin mode? { begintime = 0x7fffffff; lastchar = MSAMPBRANCH; goto free_and_return; } break; /* ** focus on specific command line arguments */ case MSELARG: alarm(0); /* stop the clock */ echo(); move(statline, 0); clrtoeol(); printw("Command line string as regular " "expression (enter=no regex): "); procsel.argnamesz = 0; procsel.argname[0] = '\0'; scanw("%63s\n", procsel.argname); procsel.argnamesz = strlen(procsel.argname); if (procsel.argnamesz) { if (regcomp(&procsel.argregex, procsel.argname, REG_NOSUB)) { statmsg = "Invalid regular " "expression!"; beep(); procsel.argnamesz = 0; procsel.argname[0] = '\0'; } } noecho(); move(statline, 0); if (interval && !paused && !rawreadflag) alarm(3); /* set short timer */ firstitem = 0; if (twinpid) // twin mode? { begintime = 0x7fffffff; lastchar = MSAMPBRANCH; goto free_and_return; } break; /* ** focus on specific system resource */ case MSELSYS: alarm(0); /* stop the clock */ echo(); move(statline, 0); clrtoeol(); printw("Logical volume name as regular " "expression (enter=no specific name): "); syssel.lvmnamesz = 0; syssel.lvmname[0] = '\0'; scanw("%63s\n", syssel.lvmname); syssel.lvmnamesz = strlen(syssel.lvmname); if (syssel.lvmnamesz) { if (regcomp(&syssel.lvmregex, syssel.lvmname, REG_NOSUB)) { statmsg = "Invalid regular " "expression!"; beep(); syssel.lvmnamesz = 0; syssel.lvmname[0] = '\0'; } } move(statline, 0); clrtoeol(); printw("Disk name as regular " "expression (enter=no specific name): "); syssel.dsknamesz = 0; syssel.dskname[0] = '\0'; scanw("%63s\n", syssel.dskname); syssel.dsknamesz = strlen(syssel.dskname); if (syssel.dsknamesz) { if (regcomp(&syssel.dskregex, syssel.dskname, REG_NOSUB)) { statmsg = "Invalid regular " "expression!"; beep(); syssel.dsknamesz = 0; syssel.dskname[0] = '\0'; } } move(statline, 0); clrtoeol(); printw("Interface name as regular " "expression (enter=no specific name): "); syssel.itfnamesz = 0; syssel.itfname[0] = '\0'; scanw("%63s\n", syssel.itfname); syssel.itfnamesz = strlen(syssel.itfname); if (syssel.itfnamesz) { if (regcomp(&syssel.itfregex, syssel.itfname, REG_NOSUB)) { statmsg = "Invalid regular " "expression!"; beep(); syssel.itfnamesz = 0; syssel.itfname[0] = '\0'; } } noecho(); move(statline, 0); if (interval && !paused && !rawreadflag) alarm(3); /* set short timer */ firstitem = 0; if (twinpid) // twin mode? { begintime = 0x7fffffff; lastchar = MSAMPBRANCH; goto free_and_return; } break; /* ** toggle pause-state */ case MPAUSE: if (rawreadflag && !twinpid) { statmsg = "Just use 'T' and 't' to browse..."; break; } if (paused) { paused=0; clrtoeol(); refresh(); if (!rawreadflag) { alarm(1); /* start the clock */ } else { begintime = 0x7fffffff; lastchar = MSAMPBRANCH; goto free_and_return; } } else { paused=1; clrtoeol(); refresh(); alarm(0); /* stop the clock */ } break; /* ** toggle between modified processes and ** all processes, or between used cgroups and ** all cgroups */ case MALLACTIVE: if (deviatonly) { deviatonly=0; statmsg = "All processes/threads/cgroups will be " "shown/accumulated..."; } else { deviatonly=1; statmsg = "Only active processes/threads/cgroups " "will be shown/accumulated..."; } tlastorder = 0; firstitem = 0; break; /* ** toggle average or total values */ case MAVGVAL: if (avgval) avgval=0; else avgval=1; break; /* ** system-statistics lines: ** toggle fixed or variable */ case MSYSFIXED: if (fixedhead) { fixedhead=0; statmsg = "Only active system-resources" " will be shown ......"; } else { fixedhead=1; statmsg = "Also inactive " "system-resources will be shown....."; } firstitem = 0; break; /* ** system-statistics lines: ** toggle fixed or variable */ case MSYSNOSORT: if (sysnosort) { sysnosort=0; statmsg = "System resources will be " "sorted on utilization..."; } else { sysnosort=1; statmsg = "System resources will not " "be sorted on utilization..."; } firstitem = 0; break; /* ** per-thread view wanted with sorting on ** process level */ case MTHREAD: if (threadview) { threadview = 0; statmsg = "Thread view disabled"; firstitem = 0; } else { threadview = 1; statmsg = "Thread view enabled"; firstitem = 0; } break; /* ** sorting on thread level as well (threadview) */ case MTHRSORT: if (threadsort) { threadsort = 0; statmsg = "Thread sorting disabled for thread view"; firstitem = 0; } else { threadsort = 1; statmsg = "Thread sorting enabled for thread view"; firstitem = 0; } break; /* ** per-process PSS calculation wanted */ case MCALCPSS: if (rawreadflag) { statmsg = "PSIZE gathering depends " "on rawfile"; break; } if (calcpss) { calcpss = 0; statmsg = "PSIZE gathering disabled"; } else { calcpss = 1; if (rootprivs()) statmsg = "PSIZE gathering enabled"; else statmsg = "PSIZE gathering only " "for own processes"; } break; /* ** per-thread WCHAN definition */ case MGETWCHAN: if (getwchan) { getwchan = 0; statmsg = "WCHAN gathering disabled"; } else { getwchan = 1; statmsg = "WCHAN gathering enabled"; } break; /* ** suppression of terminated processes in output */ case MSUPEXITS: if (suppressexit) { suppressexit = 0; statmsg = "Exited processes will " "be shown/accumulated"; firstitem = 0; } else { suppressexit = 1; statmsg = "Exited processes will " "not be shown/accumulated"; firstitem = 0; } break; /* ** screen lines: ** toggle for colors */ case MCOLORS: if (usecolors) { usecolors=0; statmsg = "No colors will be used..."; } else { if (screen && has_colors()) { usecolors=1; statmsg = "Colors will be used..."; } else { statmsg="No colors supported!"; } } firstitem = 0; break; /* ** system-statistics lines: ** toggle no or all active disk */ case MSYSLIMIT: alarm(0); /* stop the clock */ maxcpulines = getnumval("Maximum lines for per-cpu " "statistics (now %d): ", maxcpulines, statline); maxgpulines = getnumval("Maximum lines for per-gpu " "statistics (now %d): ", maxgpulines, statline); if (sstat->dsk.nlvm > 0) { maxlvmlines = getnumval("Maximum lines for LVM " "statistics (now %d): ", maxlvmlines, statline); } if (sstat->dsk.nmdd > 0) { maxmddlines = getnumval("Maximum lines for MD " "device statistics (now %d): ", maxmddlines, statline); } maxdsklines = getnumval("Maximum lines for disk " "statistics (now %d): ", maxdsklines, statline); maxintlines = getnumval("Maximum lines for interface " "statistics (now %d): ", maxintlines, statline); maxifblines = getnumval("Maximum lines for infiniband " "port statistics (now %d): ", maxifblines, statline); maxnfslines = getnumval("Maximum lines for NFS mount " "statistics (now %d): ", maxnfslines, statline); maxcontlines = getnumval("Maximum lines for container " "statistics (now %d): ", maxcontlines, statline); maxnumalines = getnumval("Maximum lines for numa " "statistics (now %d): ", maxnumalines, statline); maxllclines = getnumval("Maximum lines for LLC " "statistics (now %d): ", maxllclines, statline); if (interval && !paused && !rawreadflag) alarm(1); /* set short timer */ firstitem = 0; if (twinpid) // twin mode? { begintime = 0x7fffffff; lastchar = MSAMPBRANCH; goto free_and_return; } break; /* ** reset counters */ case MRESET: getalarm(0); /* restart the clock */ paused = 0; if (twinpid) { paused=1; // implicit pause in twin mode clrtoeol(); refresh(); } goto free_and_return; /* ** branch to end of log */ case MEND: if (!rawreadflag) { statmsg = "Only allowed in twin mode or " "when viewing raw file!"; beep(); break; } if (!paused && twinpid) { paused=1; // implicit pause in twin mode clrtoeol(); refresh(); } goto free_and_return; /* ** show version info */ case MVERSION: statmsg = getstrvers(); break; /* ** handle redraw request */ case MREDRAW: wclear(stdscr); break; /* ** handle arrow right for command line */ case KEY_RIGHT: startoffset++; break; /* ** handle arrow left for command line */ case KEY_LEFT: if (startoffset > 0) startoffset--; break; /* ** handle arrow down to go one line down */ case KEY_DOWN: if (firstitem < alistsz-1) firstitem += 1; break; /* ** handle arrow up to go one line up */ case KEY_UP: if (firstitem > 0) firstitem -= 1; break; /* ** handle forward */ case KEY_NPAGE: case MLISTFW: if (alistsz-firstitem > slistsz) firstitem += slistsz; break; /* ** handle backward */ case KEY_PPAGE: case MLISTBW: if (firstitem >= slistsz) firstitem -= slistsz; else firstitem = 0; break; /* ** handle screen resize */ case KEY_RESIZE: snprintf(statbuf, sizeof statbuf, "Window resized to %dx%d...", COLS, LINES); statmsg = statbuf; timeout(0); (void) getch(); timeout(-1); break; /* ** unknown key-stroke */ default: beep(); } } else /* no screen */ { lastchar = '\0'; goto free_and_return; } } free_and_return: free(tpcumlist); free(pcumlist); free(tucumlist); free(ucumlist); free(tccumlist); free(ccumlist); free(tsklist); free(sellist); free(cgroupsort); free(cgroupsel); return lastchar; } /* ** accumulate all processes per user in new list */ static int cumusers(struct tstat **curprocs, struct tstat *curusers, int numprocs) { register int i, numusers; /* ** sort list of active processes in order of uid (increasing) */ qsort(curprocs, numprocs, sizeof(struct tstat *), compusr); /* ** accumulate all processes per user in the new list */ for (numusers=i=0; i < numprocs; i++, curprocs++) { if (procsuppress(*curprocs, &procsel)) continue; if ((*curprocs)->gen.state == 'E' && suppressexit) continue; if ( curusers->gen.ruid != (*curprocs)->gen.ruid ) { if (curusers->gen.pid) { numusers++; curusers++; } curusers->gen.ruid = (*curprocs)->gen.ruid; } accumulate(*curprocs, curusers); } if (curusers->gen.pid) numusers++; return numusers; } /* ** accumulate all processes with the same name (i.e. same program) ** into a new list */ static int cumprogs(struct tstat **curprocs, struct tstat *curprogs, int numprocs) { register int i, numprogs; /* ** sort list of active processes in order of process-name */ qsort(curprocs, numprocs, sizeof(struct tstat *), compnam); /* ** accumulate all processes with same name in the new list */ for (numprogs=i=0; i < numprocs; i++, curprocs++) { if (procsuppress(*curprocs, &procsel)) continue; if ((*curprocs)->gen.state == 'E' && suppressexit) continue; if ( strcmp(curprogs->gen.name, (*curprocs)->gen.name) != 0) { if (curprogs->gen.pid) { numprogs++; curprogs++; } strcpy(curprogs->gen.name, (*curprocs)->gen.name); } accumulate(*curprocs, curprogs); } if (curprogs->gen.pid) numprogs++; return numprogs; } /* ** accumulate all processes per container/pod in new list */ static int cumconts(struct tstat **curprocs, struct tstat *curconts, int numprocs) { register int i, numconts; /* ** sort list of active processes in order of container/pod (increasing) */ qsort(curprocs, numprocs, sizeof(struct tstat *), compcon); /* ** accumulate all processes per container/pod in the new list */ for (numconts=i=0; i < numprocs; i++, curprocs++) { if (procsuppress(*curprocs, &procsel)) continue; if ((*curprocs)->gen.state == 'E' && suppressexit) continue; if ( strcmp(curconts->gen.utsname, (*curprocs)->gen.utsname) != 0) { if (curconts->gen.pid) { numconts++; curconts++; } strcpy(curconts->gen.utsname, (*curprocs)->gen.utsname); } accumulate(*curprocs, curconts); } if (curconts->gen.pid) numconts++; return numconts; } /* ** accumulate relevant counters from individual task to ** combined task */ static void accumulate(struct tstat *curproc, struct tstat *curstat) { count_t nett_wsz; curstat->gen.pid++; /* misuse as counter */ curstat->gen.isproc = 1; curstat->gen.nthr += curproc->gen.nthr; curstat->cpu.utime += curproc->cpu.utime; curstat->cpu.stime += curproc->cpu.stime; curstat->cpu.nvcsw += curproc->cpu.nvcsw; curstat->cpu.nivcsw += curproc->cpu.nivcsw; curstat->cpu.rundelay += curproc->cpu.rundelay; curstat->cpu.blkdelay += curproc->cpu.blkdelay; if (curproc->dsk.wsz > curproc->dsk.cwsz) nett_wsz = curproc->dsk.wsz -curproc->dsk.cwsz; else nett_wsz = 0; curstat->dsk.rio += curproc->dsk.rsz; curstat->dsk.wio += nett_wsz; curstat->dsk.rsz = curstat->dsk.rio; curstat->dsk.wsz = curstat->dsk.wio; curstat->net.tcpsnd += curproc->net.tcpsnd; curstat->net.tcprcv += curproc->net.tcprcv; curstat->net.udpsnd += curproc->net.udpsnd; curstat->net.udprcv += curproc->net.udprcv; curstat->net.tcpssz += curproc->net.tcpssz; curstat->net.tcprsz += curproc->net.tcprsz; curstat->net.udpssz += curproc->net.udpssz; curstat->net.udprsz += curproc->net.udprsz; if (curproc->gen.state != 'E') { if (curproc->mem.pmem != -1) // no errors? curstat->mem.pmem += curproc->mem.pmem; curstat->mem.vmem += curproc->mem.vmem; curstat->mem.rmem += curproc->mem.rmem; curstat->mem.vlibs += curproc->mem.vlibs; curstat->mem.vdata += curproc->mem.vdata; curstat->mem.vstack += curproc->mem.vstack; curstat->mem.vswap += curproc->mem.vswap; curstat->mem.vlock += curproc->mem.vlock; curstat->mem.rgrow += curproc->mem.rgrow; curstat->mem.vgrow += curproc->mem.vgrow; if (curproc->gpu.state) // GPU is use? { int i; curstat->gpu.state = 'A'; if (curproc->gpu.gpubusy == -1) curstat->gpu.gpubusy = -1; else curstat->gpu.gpubusy += curproc->gpu.gpubusy; if (curproc->gpu.membusy == -1) curstat->gpu.membusy = -1; else curstat->gpu.membusy += curproc->gpu.membusy; curstat->gpu.memnow += curproc->gpu.memnow; curstat->gpu.gpulist |= curproc->gpu.gpulist; curstat->gpu.nrgpus = 0; for (i=0; i < MAXGPU; i++) { if (curstat->gpu.gpulist & 1<gpu.nrgpus++; } } } } /* ** function that checks if the current process or thread must be ** selected or suppressed ** ** returns 1 (suppress) or 0 (do not suppress) */ static int procsuppress(struct tstat *curstat, struct pselection *sel) { /* ** check if only processes of a particular user ** should be shown */ if (sel->userid[0] != USERSTUB) { int u = 0; while (sel->userid[u] != USERSTUB) { if (sel->userid[u] == curstat->gen.ruid) break; u++; } if (sel->userid[u] != curstat->gen.ruid) return 1; } /* ** check if only processes with particular PIDs ** should be shown */ if (sel->pid[0]) { int i = 0; while (sel->pid[i]) { if (sel->pid[i] == curstat->gen.tgid) break; i++; } if (sel->pid[i] != curstat->gen.tgid) return 1; } /* ** check if only processes with a particular name ** should be shown */ if (sel->prognamesz && regexec(&(sel->progregex), curstat->gen.name, 0, NULL, 0)) return 1; /* ** check if only processes with a particular command line string ** should be shown */ if (sel->argnamesz) { if (curstat->gen.cmdline[0]) { if (regexec(&(sel->argregex), curstat->gen.cmdline, 0, NULL, 0)) return 1; } else { if (regexec(&(sel->argregex), curstat->gen.name, 0, NULL, 0)) return 1; } } /* ** check if only processes related to a particular container/pod ** should be shown (container/pod 'H' stands for native host processes) */ if (sel->utsname[0]) { if (sel->utsname[0] == 'H') // only host processes { if (curstat->gen.utsname[0]) return 1; } else { if (memcmp(sel->utsname, curstat->gen.utsname, 12)) return 1; } } /* ** check if only processes in specific states should be shown ** ** notice that the state of a process (i.e. the main thread) ** may be 'S' while a thread in the process might have state 'R' ** --> still show the process in that case! */ if (sel->states[0]) { // check the states of the threads of this process // if (strchr(sel->states, 'R') && curstat->gen.nthrrun) return 0; if (strchr(sel->states, 'S') && curstat->gen.nthrslpi) return 0; if (strchr(sel->states, 'D') && curstat->gen.nthrslpu) return 0; if (strchr(sel->states, 'I') && curstat->gen.nthridle) return 0; // check the state of the process itself // if (strchr(sel->states, curstat->gen.state) == NULL) return 1; } return 0; } static void limitedlines(void) { if (maxcpulines == 999) // default? maxcpulines = 0; if (maxgpulines == 999) // default? maxgpulines = 2; if (maxdsklines == 999) // default? maxdsklines = 3; if (maxmddlines == 999) // default? maxmddlines = 3; if (maxlvmlines == 999) // default? maxlvmlines = 4; if (maxintlines == 999) // default? maxintlines = 2; if (maxifblines == 999) // default? maxifblines = 2; if (maxnfslines == 999) // default? maxnfslines = 2; if (maxcontlines == 999) // default? maxcontlines = 1; if (maxnumalines == 999) // default? maxnumalines = 0; if (maxllclines == 999) // default? maxllclines = 0; } /* ** get a numerical value from the user and verify */ static long getnumval(char *ask, long valuenow, int statline) { char numval[16]; long retval; echo(); move(statline, 0); clrtoeol(); printw(ask, valuenow); numval[0] = 0; scanw("%15s", numval); move(statline, 0); noecho(); if (numval[0]) /* data entered ? */ { if ( numeric(numval) ) { retval = atol(numval); } else { beep(); clrtoeol(); printw("Value not numeric (current value kept)!"); refresh(); sleep(2); retval = valuenow; } } else { retval = valuenow; } return retval; } /* ** generic print-function which checks if printf should be used ** (to file or pipe) or curses (to screen) */ void printg(const char *format, ...) { va_list args; va_start(args, format); if (screen) vw_printw(stdscr, (char *) format, args); else vprintf(format, args); va_end(args); } /* ** initialize generic sample output functions */ static void generic_init(void) { int i; /* ** check if default sort order and/or showtype are overruled ** by command-line flags */ for (i=0; flaglist[i]; i++) { switch (flaglist[i]) { case MSORTAUTO: showorder = MSORTAUTO; break; case MSORTCPU: showorder = MSORTCPU; break; case MSORTGPU: showorder = MSORTGPU; break; case MSORTMEM: showorder = MSORTMEM; break; case MSORTDSK: showorder = MSORTDSK; break; case MSORTNET: showorder = MSORTNET; break; case MPROCGEN: showtype = MPROCGEN; showorder = MSORTCPU; break; case MPROCGPU: showtype = MPROCGPU; showorder = MSORTGPU; break; case MPROCMEM: showtype = MPROCMEM; showorder = MSORTMEM; break; case MPROCSCH: showtype = MPROCSCH; showorder = MSORTCPU; break; case MPROCDSK: showtype = MPROCDSK; showorder = MSORTDSK; break; case MPROCNET: if ( !(supportflags & NETATOP || supportflags & NETATOPBPF) ) { fprintf(stderr, "Ignored: 'netatop' or 'netatop-bpf' not " "active, no -K specified or no root privs"); sleep(3); break; } showtype = MPROCNET; showorder = MSORTNET; break; case MPROCVAR: showtype = MPROCVAR; break; case MPROCARG: showtype = MPROCARG; break; case MCGROUPS: if ( !(supportflags & CGROUPV2) ) { fprintf(stderr, "No cgroup v2 metrics " "available; request ignored!\n"); sleep(3); break; } showtype = MCGROUPS; break; case MPROCOWN: showtype = MPROCOWN; break; case MAVGVAL: if (avgval) avgval=0; else avgval=1; break; case MCUMUSER: showtype = MCUMUSER; break; case MCUMPROC: showtype = MCUMPROC; break; case MCUMCONT: if (!rawreadflag && !rootprivs()) { fprintf(stderr, "No privileges to get " "container/pod identity!\n"); sleep(3); break; } showtype = MCUMCONT; break; case MSYSFIXED: if (fixedhead) fixedhead=0; else fixedhead=1; break; case MSYSNOSORT: if (sysnosort) sysnosort=0; else sysnosort=1; break; case MTHREAD: if (threadview) threadview = 0; else threadview = 1; break; case MTHRSORT: if (threadsort) threadsort = 0; else threadsort = 1; break; case MCALCPSS: if (rawreadflag) { fprintf(stderr, "PSIZE gathering depends on rawfile\n"); sleep(3); break; } if (calcpss) { calcpss = 0; } else { calcpss = 1; if (!rootprivs()) { fprintf(stderr, "PSIZE gathering only for own " "processes\n"); sleep(3); } } break; case MGETWCHAN: if (getwchan) getwchan = 0; else getwchan = 1; break; case MSUPEXITS: if (suppressexit) suppressexit = 0; else suppressexit = 1; break; case MCOLORS: if (usecolors) usecolors=0; else usecolors=1; break; case MSYSLIMIT: limitedlines(); break; case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': cgroupdepth = flaglist[i] - 0x30; break; default: prusage("atop"); } } /* ** set stdout output on line-basis */ setvbuf(stdout, (char *)0, _IOLBF, BUFSIZ); /* ** check if STDOUT is related to a tty or ** something else (file, pipe) */ if ( isatty(fileno(stdout)) ) screen = 1; else screen = 0; /* ** install catch-routine to finish in a controlled way ** and activate cbreak mode */ if (screen) { /* ** if stdin is not connected to a tty (might be redirected ** to pipe or file), close it and duplicate stdout (tty) ** to stdin */ if ( !isatty(fileno(stdin)) ) { (void) dup2(fileno(stdout), fileno(stdin)); } /* ** initialize screen-handling via curses */ setlocale(LC_ALL, ""); setlocale(LC_NUMERIC, "C"); initscr(); cbreak(); noecho(); keypad(stdscr, TRUE); /* ** verify minimal dimensions */ if (COLS < MINCOLUMNS || LINES < MINLINES) { endwin(); // finish ncurses interface fprintf(stderr, "Terminal size should be at least " "%d columns by %d lines\n", MINCOLUMNS, MINLINES); cleanstop(1); } if (has_colors()) { use_default_colors(); start_color(); // color definitions // init_color(COLOR_MYORANGE, 675, 500, 50); init_color(COLOR_MYGREEN, 0, 600, 100); init_color(COLOR_MYGREY, 240, 240, 240); init_color(COLOR_MYLGREY, 800, 800, 800); init_color(COLOR_MYBROWN1, 420, 160, 160); init_color(COLOR_MYBROWN2, 735, 280, 280); init_color(COLOR_MYBLUE0, 50, 50, 300); init_color(COLOR_MYBLUE1, 50, 300, 500); init_color(COLOR_MYBLUE2, 50, 500, 800); init_color(COLOR_MYBLUE3, 100, 350, 600); init_color(COLOR_MYBLUE4, 150, 500, 700); init_color(COLOR_MYBLUE5, 200, 650, 800); init_color(COLOR_MYGREEN0, 90, 300, 0); init_color(COLOR_MYGREEN1, 90, 400, 100); init_color(COLOR_MYGREEN2, 90, 600, 100); // color pair definitions (foreground/background) // init_pair(FGCOLORINFO, colorinfo, -1); init_pair(FGCOLORALMOST, coloralmost, -1); init_pair(FGCOLORCRIT, colorcrit, -1); init_pair(FGCOLORTHR, colorthread, -1); init_pair(FGCOLORBORDER, COLOR_CYAN, -1); init_pair(FGCOLORGREY, COLOR_MYLGREY, -1); init_pair(WHITE_GREEN, COLOR_WHITE, COLOR_MYGREEN); init_pair(WHITE_ORANGE, COLOR_WHITE, COLOR_MYORANGE); init_pair(WHITE_RED, COLOR_WHITE, COLOR_RED); init_pair(WHITE_GREY, COLOR_WHITE, COLOR_MYGREY); init_pair(WHITE_BLUE, COLOR_WHITE, COLOR_BLUE); init_pair(WHITE_MAGENTA, COLOR_WHITE, COLOR_MAGENTA); init_pair(WHITE_BROWN1, COLOR_WHITE, COLOR_MYBROWN1); init_pair(WHITE_BROWN2, COLOR_WHITE, COLOR_MYBROWN2); init_pair(WHITE_BLUE0, COLOR_WHITE, COLOR_MYBLUE0); init_pair(WHITE_BLUE1, COLOR_WHITE, COLOR_MYBLUE1); init_pair(WHITE_BLUE2, COLOR_WHITE, COLOR_MYBLUE2); init_pair(WHITE_BLUE3, COLOR_WHITE, COLOR_MYBLUE3); init_pair(WHITE_BLUE4, COLOR_WHITE, COLOR_MYBLUE4); init_pair(WHITE_BLUE5, COLOR_WHITE, COLOR_MYBLUE5); init_pair(WHITE_GREEN0, COLOR_WHITE, COLOR_MYGREEN0); init_pair(WHITE_GREEN1, COLOR_WHITE, COLOR_MYGREEN1); init_pair(WHITE_GREEN2, COLOR_WHITE, COLOR_MYGREEN2); } else { usecolors = 0; } } signal(SIGINT, cleanstop); signal(SIGTERM, cleanstop); } /* ** show help information in interactive mode */ static struct helptext { char *helpline; char helparg; char mode; // 'l' - live, 'r' - rawlog, 'a' - all } helptext[] = { {"Display mode:\n", ' ', 'a'}, {"\t'%c' - show bar graphs for system utilization (toggle)\n", MBARGRAPH, 'a'}, {"\n", ' ', 'a'}, {"Information in text mode about cgroups v2:\n", ' ', 'a'}, {"\t'%c' - cgroups v2 metrics\n", MCGROUPS, 'a'}, {"\t 2-7 - cgroups tree level selection (default 7)\n", ' ', 'a'}, {"\t 8 - cgroups with related processes, except kernel processes\n", ' ', 'a'}, {"\t 9 - cgroups with related processes\n", ' ', 'a'}, {"\t'%c' - show all cgroups/processes i.s.o. only active ones (toggle)\n", MALLACTIVE, 'a'}, {"\t'%c' - sort on cpu activity\n", MSORTCPU, 'a'}, {"\t'%c' - sort on memory utilization\n", MSORTMEM, 'a'}, {"\t'%c' - sort on disk transfer rate\n", MSORTDSK, 'a'}, {"\n", ' ', 'a'}, {"Information in text mode about processes:\n", ' ', 'a'}, {"\t'%c' - generic info (default)\n", MPROCGEN, 'a'}, {"\t'%c' - memory details\n", MPROCMEM, 'a'}, {"\t'%c' - disk details\n", MPROCDSK, 'a'}, {"\t'%c' - network details\n", MPROCNET, 'a'}, {"\t'%c' - scheduling and thread-group info\n", MPROCSCH, 'a'}, {"\t'%c' - GPU details\n", MPROCGPU, 'a'}, {"\t'%c' - various info (ppid, user/group, date/time, status, " "exitcode)\n", MPROCVAR, 'a'}, {"\t'%c' - full command line per process\n", MPROCARG, 'a'}, {"\t'%c' - use own output line definition\n", MPROCOWN, 'a'}, {"\n", ' ', 'a'}, {"Sort list of processes in order of:\n", ' ', 'a'}, {"\t'%c' - cpu activity\n", MSORTCPU, 'a'}, {"\t'%c' - memory consumption\n", MSORTMEM, 'a'}, {"\t'%c' - disk activity\n", MSORTDSK, 'a'}, {"\t'%c' - network activity\n", MSORTNET, 'a'}, {"\t'%c' - GPU activity\n", MSORTGPU, 'a'}, {"\t'%c' - most active system resource (auto mode)\n", MSORTAUTO, 'a'}, {"\n", ' ', 'a'}, {"Raw file viewing and twin mode:\n", ' ', 'r'}, {"\t'%c' - show next sample\n", MSAMPNEXT, 'r'}, {"\t'%c' - show previous sample\n", MSAMPPREV, 'r'}, {"\t'%c' - branch to certain time\n", MSAMPBRANCH, 'r'}, {"\t'%c' - rewind to begin\n", MRESET, 'r'}, {"\t'%c' - fast-forward to end\n", MEND, 'r'}, {"\t'%c' - pause button to freeze or continue (twin mode)\n", MPAUSE, 'r'}, {"\n", ' ', 'r'}, {"Accumulated process figures:\n", ' ', 'a'}, {"\t'%c' - total resource consumption per user\n", MCUMUSER, 'a'}, {"\t'%c' - total resource consumption per program (i.e. same " "process name)\n", MCUMPROC, 'a'}, {"\t'%c' - total resource consumption per container/pod\n",MCUMCONT, 'a'}, {"\n", ' ', 'a'}, {"Process selections (keys shown in header line):\n", ' ', 'a'}, {"\t'%c' - focus on specific user name " "(regular expression)\n", MSELUSER, 'a'}, {"\t'%c' - focus on specific program name " "(regular expression)\n", MSELPROC, 'a'}, {"\t'%c' - focus on specific container/pod name\n", MSELCONT, 'a'}, {"\t'%c' - focus on specific command line string " "(regular expression)\n", MSELARG, 'a'}, {"\t'%c' - focus on specific process id (PID)\n", MSELPID, 'a'}, {"\t'%c' - focus on specific process/thread state(s)\n", MSELSTATE, 'a'}, {"\n", ' ', 'a'}, {"System resource selections (keys shown in header line):\n",' ', 'a'}, {"\t'%c' - focus on specific system resources " "(regular expression)\n", MSELSYS, 'a'}, {"\n", ' ', 'a'}, {"Screen handling:\n", ' ', 'a'}, {"\t^L - redraw the screen \n", ' ', 'a'}, {"\tPgDn - show next page in the process list (or ^F)\n", ' ', 'a'}, {"\tPgUp - show previous page in the process list (or ^B)\n", ' ', 'a'}, {"\tArDn - arrow-down for next line in process list\n", ' ', 'a'}, {"\tArUp arrow-up for previous line in process list\n", ' ', 'a'}, {"\tArRt - arrow-right for next character in full command line\n", ' ', 'a'}, {"\tArLt - arrow-left for previous character in full command line\n", ' ', 'a'}, {"\n", ' ', 'a'}, {"Presentation (keys shown in header line):\n", ' ', 'a'}, {"\t'%c' - show all processes/threads (i.s.o. active) (toggle)\n", MALLACTIVE, 'a'}, {"\t'%c' - show threads within process (thread view) (toggle)\n", MTHREAD, 'a'}, {"\t'%c' - sort threads (when combined with thread view) (toggle)\n", MTHRSORT, 'a'}, {"\t'%c' - show fixed number of header lines (toggle)\n", MSYSFIXED, 'a'}, {"\t'%c' - suppress sorting system resources (toggle)\n", MSYSNOSORT, 'a'}, {"\t'%c' - suppress terminated processes in output (toggle)\n", MSUPEXITS, 'a'}, {"\t'%c' - no colors to indicate high occupation (toggle)\n", MCOLORS, 'a'}, {"\t'%c' - show average-per-second i.s.o. total values (toggle)\n", MAVGVAL, 'a'}, {"\t'%c' - calculate proportional set size (PSIZE) (toggle)\n", MCALCPSS, 'a'}, {"\t'%c' - determine WCHAN per thread (toggle)\n", MGETWCHAN, 'a'}, {"\n", ' '}, {"Miscellaneous commands:\n", ' '}, {"\t'%c' - change interval timer (0 = only manual trigger)\n", MINTERVAL, 'l'}, {"\t'%c' - manual trigger to force next sample\n", MSAMPNEXT, 'l'}, {"\t'%c' - reset counters to boot time values\n", MRESET, 'l'}, {"\t'%c' - pause button to freeze current sample (toggle)\n", MPAUSE, 'l'}, {"\n", ' ', 'l'}, {"\t'%c' - limit lines for per-cpu, disk and interface resources\n", MSYSLIMIT, 'a'}, {"\t'%c' - kill a process (i.e. send a signal)\n", MKILLPROC, 'a'}, {"\n", ' '}, {"\t'%c' - version information\n", MVERSION, 'a'}, {"\t'%c' - help information\n", MHELP1, 'a'}, {"\t'%c' - help information\n", MHELP2, 'a'}, {"\t'%c' - quit this program\n", MQUIT, 'a'}, }; static int helplines = sizeof(helptext)/sizeof(struct helptext); static void showhelp(int helpline) { int winlines = LINES-helpline, shown, tobeshown=1, i, lines; WINDOW *helpwin; /* ** create a new window for the help-info in which scrolling is ** allowed */ helpwin = newwin(winlines, COLS, helpline, 0); scrollok(helpwin, 1); /* ** show help-lines */ for (i=0, lines=0, shown=0; i < helplines; i++) { if (rawreadflag) // implies twin mode { if (helptext[i].mode == 'l') continue; } else { if (helptext[i].mode == 'r') continue; } wprintw(helpwin, helptext[i].helpline, helptext[i].helparg); /* ** when the window is full, start paging interactively */ if (lines >= winlines-2 && shown >= tobeshown) { int inputkey; wmove(helpwin, winlines-1, 0); wclrtoeol(helpwin); wprintw(helpwin, "Press q (leave help), " "space (next page), " "Enter (next line) or select key..."); keypad(helpwin, 1); // recognize keypad keys switch (inputkey = wgetch(helpwin)) { case KEY_NPAGE: case KEY_PPAGE: case KEY_UP: case KEY_DOWN: case KEY_LEFT: case KEY_RIGHT: break; // ignore keypad keys case 'q': delwin(helpwin); return; case ' ': shown = 0; tobeshown = winlines-1; break; case '\n': shown = 0; tobeshown = 1; break; default: ungetch(inputkey); keywaiting = 1; delwin(helpwin); return; } wmove(helpwin, winlines-1, 0); } lines++; shown++; } wmove(helpwin, winlines-1, 0); wclrtoeol(helpwin); wprintw(helpwin, "End of help - press 'q' to leave help..."); while (wgetch(helpwin) != 'q'); delwin(helpwin); } /* ** function to be called to print error-messages */ void generic_error(const char *format, ...) { va_list args; va_start(args, format); vfprintf(stderr, format, args); va_end (args); } /* ** function to be called when the program stops */ void generic_end(void) { endwin(); } /* ** function to be called when usage-info is required */ void generic_usage(void) { printf("\t -%c show fixed number of lines with system statistics\n", MSYSFIXED); printf("\t -%c suppress sorting of system resources\n", MSYSNOSORT); printf("\t -%c suppress terminated processes in output\n", MSUPEXITS); printf("\t -%c show limited number of lines for certain resources\n", MSYSLIMIT); printf("\t -%c show threads within process\n", MTHREAD); printf("\t -%c sort threads (when combined with '%c')\n", MTHRSORT, MTHREAD); printf("\t -%c show average-per-second i.s.o. total values\n\n", MAVGVAL); printf("\t -%c no colors in case of high occupation\n", MCOLORS); printf("\t -%c show general process-info (default)\n", MPROCGEN); printf("\t -%c show memory-related process-info\n", MPROCMEM); printf("\t -%c show disk-related process-info\n", MPROCDSK); printf("\t -%c show network-related process-info\n", MPROCNET); printf("\t -%c show scheduling-related process-info\n", MPROCSCH); printf("\t -%c show various process-info (ppid, user/group, " "date/time)\n", MPROCVAR); printf("\t -%c show command line per process\n", MPROCARG); printf("\t -%c show own defined process-info\n", MPROCOWN); printf("\t -%c show cumulated process-info per user\n", MCUMUSER); printf("\t -%c show cumulated process-info per program " "(i.e. same name)\n", MCUMPROC); printf("\t -%c show cumulated process-info per container/pod\n\n", MCUMCONT); printf("\t -%c sort processes in order of cpu consumption " "(default)\n", MSORTCPU); printf("\t -%c sort processes in order of memory consumption\n", MSORTMEM); printf("\t -%c sort processes in order of disk activity\n", MSORTDSK); printf("\t -%c sort processes in order of network activity\n", MSORTNET); printf("\t -%c sort processes in order of GPU activity\n", MSORTGPU); printf("\t -%c sort processes in order of most active resource " "(auto mode)\n", MSORTAUTO); } /* ** functions to handle a particular tag in the /etc/atoprc and .atoprc file */ void do_username(char *name, char *val) { struct passwd *pwd; safe_strcpy(procsel.username, val, sizeof procsel.username); if (procsel.username[0]) { regex_t userregex; int u = 0; if (regcomp(&userregex, procsel.username, REG_NOSUB)) { fprintf(stderr, "atoprc - %s: invalid regular expression %s\n", name, val); exit(1); } while ( (pwd = getpwent())) { if (regexec(&userregex, pwd->pw_name, 0, NULL, 0)) continue; if (u < MAXUSERSEL-1) { procsel.userid[u] = pwd->pw_uid; u++; } } endpwent(); procsel.userid[u] = USERSTUB; if (u == 0) { /* ** possibly a numerical value has been specified */ if (numeric(procsel.username)) { procsel.userid[0] = atoi(procsel.username); procsel.userid[1] = USERSTUB; } else { fprintf(stderr, "atoprc - %s: user-names matching %s " "do not exist\n", name, val); exit(1); } } } else { procsel.userid[0] = USERSTUB; } } void do_procname(char *name, char *val) { safe_strcpy(procsel.progname, val, sizeof procsel.progname); procsel.prognamesz = strlen(procsel.progname); if (procsel.prognamesz) { if (regcomp(&procsel.progregex, procsel.progname, REG_NOSUB)) { fprintf(stderr, "atoprc - %s: invalid regular expression %s\n", name, val); exit(1); } } } void do_maxcpu(char *name, char *val) { maxcpulines = get_posval(name, val); } void do_maxgpu(char *name, char *val) { maxgpulines = get_posval(name, val); } void do_maxdisk(char *name, char *val) { maxdsklines = get_posval(name, val); } void do_maxmdd(char *name, char *val) { maxmddlines = get_posval(name, val); } void do_maxlvm(char *name, char *val) { maxlvmlines = get_posval(name, val); } void do_maxintf(char *name, char *val) { maxintlines = get_posval(name, val); } void do_maxifb(char *name, char *val) { maxifblines = get_posval(name, val); } void do_maxnfsm(char *name, char *val) { maxnfslines = get_posval(name, val); } void do_maxcont(char *name, char *val) { maxcontlines = get_posval(name, val); } void do_maxnuma(char *name, char *val) { maxnumalines = get_posval(name, val); } void do_maxllc(char *name, char *val) { maxllclines = get_posval(name, val); } struct colmap { char *colname; short colval; } colormap[] = { { "red", COLOR_RED, }, { "green", COLOR_GREEN, }, { "yellow", COLOR_YELLOW, }, { "blue", COLOR_BLUE, }, { "magenta", COLOR_MAGENTA, }, { "cyan", COLOR_CYAN, }, { "black", COLOR_BLACK, }, { "white", COLOR_WHITE, }, }; static short modify_color(char *colorname) { int i; for (i=0; i < sizeof colormap/sizeof colormap[0]; i++) { if ( strcmp(colorname, colormap[i].colname) == 0) return colormap[i].colval; } // required color not found fprintf(stderr, "atoprc - invalid color used: %s\n", colorname); fprintf(stderr, "supported colors:"); for (i=0; i < sizeof colormap/sizeof colormap[0]; i++) fprintf(stderr, " %s", colormap[i].colname); fprintf(stderr, "\n"); exit(1); } void do_colinfo(char *name, char *val) { colorinfo = modify_color(val); } void do_colalmost(char *name, char *val) { coloralmost = modify_color(val); } void do_colcrit(char *name, char *val) { colorcrit = modify_color(val); } void do_colthread(char *name, char *val) { colorthread = modify_color(val); } void do_twindir(char *name, char *val) { safe_strcpy(twindir, val, RAWNAMESZ); } void do_flags(char *name, char *val) { int i; extern char twinmodeflag; for (i=0; val[i]; i++) { switch (val[i]) { case '-': break; case MBARGRAPH: displaymode = 'D'; break; case MBARMONO: barmono = 1; break; case MSORTCPU: showorder = MSORTCPU; break; case MSORTGPU: showorder = MSORTGPU; break; case MSORTMEM: showorder = MSORTMEM; break; case MSORTDSK: showorder = MSORTDSK; break; case MSORTNET: showorder = MSORTNET; break; case MSORTAUTO: showorder = MSORTAUTO; break; case MPROCGEN: showtype = MPROCGEN; showorder = MSORTCPU; break; case MPROCGPU: showtype = MPROCGPU; showorder = MSORTGPU; break; case MPROCMEM: showtype = MPROCMEM; showorder = MSORTMEM; break; case MPROCDSK: showtype = MPROCDSK; showorder = MSORTDSK; break; case MPROCNET: showtype = MPROCNET; showorder = MSORTNET; break; case MPROCVAR: showtype = MPROCVAR; break; case MPROCSCH: showtype = MPROCSCH; showorder = MSORTCPU; break; case MPROCARG: showtype = MPROCARG; break; case MPROCOWN: showtype = MPROCOWN; break; case MCUMUSER: showtype = MCUMUSER; break; case MCUMPROC: showtype = MCUMPROC; break; case MCUMCONT: showtype = MCUMCONT; break; case MCGROUPS: showtype = MCGROUPS; break; case MALLACTIVE: deviatonly = 0; break; case MAVGVAL: avgval=1; break; case MSYSFIXED: fixedhead = 1; break; case MSYSNOSORT: sysnosort = 1; break; case MTHREAD: threadview = 1; break; case MTHRSORT: threadsort = 1; break; case MCOLORS: usecolors = 0; break; case MCALCPSS: calcpss = 1; break; case MGETWCHAN: getwchan = 1; break; case MSUPEXITS: suppressexit = 1; break; case '2': // cgroup level case '3': case '4': case '5': case '6': case '7': case '8': case '9': cgroupdepth = val[i] - 0x30; break; case 't': twinmodeflag++; break; case 'I': idnamesuppress++; break; } } } static void getsigwinch(int signr) { winchange = 1; } atop-2.12.1/showgeneric.h0000644000203100020310000001511715064552761014546 0ustar gerlofgerlof/* ** ATOP - System & Process Monitor ** ** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as process-level. ** ** Include-file describing prototypes and structures for visualization ** of counters. ** ================================================================ ** Author: Gerlof Langeveld ** E-mail: gerlof.langeveld@atoptool.nl ** Date: July 2002 ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. */ #ifndef __SHOWGENERIC__ #define __SHOWGENERIC__ #define USERSTUB 9999999 #define MAXUSERSEL 64 #define MAXPID 32 // minimum screen dimensions // #define MINLINES 24 #define MINCOLUMNS 60 struct syscap { int nrcpu; count_t availcpu; count_t availmem; count_t availdsk; count_t availnet; int nrgpu; count_t availgpumem; // GPU memory in Kb! int nrmemnuma; int nrcpunuma; }; struct pselection { char username[256]; uid_t userid[MAXUSERSEL]; pid_t pid[MAXPID]; char progname[64]; int prognamesz; regex_t progregex; char argname[64]; int argnamesz; regex_t argregex; char utsname[UTSLEN+1]; char states[16]; }; struct sselection { char lvmname[64]; // logical volume selection int lvmnamesz; regex_t lvmregex; char dskname[64]; // disk selection int dsknamesz; regex_t dskregex; char itfname[64]; // network interface selection int itfnamesz; regex_t itfregex; }; /* ** color definitions */ #define COLOR_MYORANGE 20 #define COLOR_MYGREEN 21 #define COLOR_MYGREY 22 #define COLOR_MYLGREY 23 #define COLOR_MYBROWN1 24 #define COLOR_MYBROWN2 25 #define COLOR_MYBLUE0 26 #define COLOR_MYBLUE1 27 #define COLOR_MYBLUE2 28 #define COLOR_MYBLUE3 29 #define COLOR_MYBLUE4 30 #define COLOR_MYBLUE5 31 #define COLOR_MYGREEN0 35 #define COLOR_MYGREEN1 36 #define COLOR_MYGREEN2 37 /* ** color pair definitions */ #define FGCOLORBORDER 1 #define FGCOLORINFO 2 #define FGCOLORALMOST 3 #define FGCOLORCRIT 4 #define FGCOLORTHR 5 #define FGCOLORGREY 6 #define WHITE_GREEN 10 #define WHITE_ORANGE 11 #define WHITE_RED 12 #define WHITE_GREY 13 #define WHITE_BLUE 14 #define WHITE_MAGENTA 15 #define WHITE_BROWN1 18 #define WHITE_BROWN2 19 #define WHITE_BLUE0 20 #define WHITE_BLUE1 21 #define WHITE_BLUE2 22 #define WHITE_BLUE3 23 #define WHITE_BLUE4 24 #define WHITE_BLUE5 25 #define WHITE_GREEN0 30 #define WHITE_GREEN1 31 #define WHITE_GREEN2 32 /* ** text and bar color selections */ #define COLOROKAY WHITE_GREEN #define COLORWARN WHITE_ORANGE #define COLORBAD WHITE_RED #define COLORCPUSYS WHITE_BLUE1 #define COLORCPUUSR WHITE_BLUE2 #define COLORCPUINTR WHITE_BLUE3 #define COLORCPUSTEAL WHITE_BLUE4 #define COLORCPUGUEST WHITE_BLUE5 #define COLORMEMFREE WHITE_GREEN #define COLORMEMCACH WHITE_ORANGE #define COLORMEMHUGE WHITE_BLUE5 #define COLORMEMUSED WHITE_GREY #define COLORMEMSHM WHITE_BROWN1 #define COLORMEMTMP WHITE_BLUE #define COLORMEMSLAB WHITE_MAGENTA #define COLORMEMBAR WHITE_BLUE3 #define COLORDSKREAD WHITE_GREEN1 #define COLORDSKWRITE WHITE_GREEN2 #define COLORNETRECV WHITE_BROWN1 #define COLORNETSEND WHITE_BROWN2 /* ** list with keystrokes/flags */ #define MPROCGEN 'g' #define MPROCMEM 'm' #define MPROCDSK 'd' #define MPROCNET 'n' #define MPROCGPU 'e' #define MPROCSCH 's' #define MPROCVAR 'v' #define MPROCARG 'c' #define MCGROUPS 'G' #define MPROCOWN 'o' #define MCUMUSER 'u' #define MCUMPROC 'p' #define MCUMCONT 'j' #define MSORTCPU 'C' #define MSORTDSK 'D' #define MSORTMEM 'M' #define MSORTNET 'N' #define MSORTGPU 'E' #define MSORTAUTO 'A' #define MTHREAD 'y' #define MTHRSORT 'Y' #define MCALCPSS 'R' #define MGETWCHAN 'W' #define MSUPEXITS 'X' #define MCOLORS 'x' #define MSYSFIXED 'f' #define MSYSNOSORT 'F' #define MSYSLIMIT 'l' #define MRMSPACES 'Z' #define MSELUSER 'U' #define MSELPROC 'P' #define MSELCONT 'J' #define MSELPID 'I' #define MSELARG '/' #define MSELSTATE 'Q' #define MSELSYS 'S' #define MALLACTIVE 'a' #define MKILLPROC 'k' #define MLISTFW 0x06 #define MLISTBW 0x02 #define MREDRAW 0x0c #define MINTERVAL 'i' #define MPAUSE 'z' #define MQUIT 'q' #define MRESET 'r' #define MEND 'Z' #define MSAMPNEXT 't' #define MSAMPPREV 'T' #define MSAMPBRANCH 'b' #define MVERSION 'V' #define MAVGVAL '1' #define MHELP1 '?' #define MHELP2 'h' #define MBARGRAPH 'B' #define MBARLOWER 'L' #define MBARMONO 'H' /* ** extern values for globals */ extern int paused; extern int cgroupdepth; /* ** general function prototypes */ void totalcap (struct syscap *, struct sstat *, struct tstat **, int); void pricumproc (struct sstat *, struct devtstat *, int, unsigned int, int, int); void showgenproc(struct tstat *, double, int, int); void showmemproc(struct tstat *, double, int, int); void showdskproc(struct tstat *, double, int, int); void shownetproc(struct tstat *, double, int, int); void showvarproc(struct tstat *, double, int, int); void showschproc(struct tstat *, double, int, int); void showtotproc(struct tstat *, double, int, int); void showcmdproc(struct tstat *, double, int, int); void printg(const char *, ...); int prisyst(struct sstat *, int, int, int, int, struct sselection *, char *, int, int, int, int, int, int, int, int, int, int, int); void prihead(int, int, char *, char *, char, count_t); struct cglinesel; int pricgroup(struct cglinesel *, int, int, int, int, int, struct syscap *, int, int); int priproc(struct tstat **, int, int, int, int, int, char, char, struct syscap *, int, int); char draw_samp(time_t, int, struct sstat *, char, char); void do_username(char *, char *); void do_procname(char *, char *); void do_maxcpu(char *, char *); void do_maxgpu(char *, char *); void do_maxdisk(char *, char *); void do_maxmdd(char *, char *); void do_maxlvm(char *, char *); void do_maxintf(char *, char *); void do_maxifb(char *, char *); void do_maxnfsm(char *, char *); void do_maxcont(char *, char *); void do_maxnuma(char *, char *); void do_maxllc(char *, char *); void do_colinfo(char *, char *); void do_colalmost(char *, char *); void do_colcrit(char *, char *); void do_colthread(char *, char *); void do_twindir(char *, char *); void do_flags(char *, char *); #endif atop-2.12.1/showlinux.c0000644000203100020310000024447415064552761014276 0ustar gerlofgerlof/* ** ATOP - System & Process Monitor ** ** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as process-level. ** ** This source-file contains the Linux-specific functions to calculate ** figures to be visualized. ** ========================================================================== ** Author: Gerlof Langeveld ** Original version. ** E-mail: gerlof.langeveld@atoptool.nl ** Date: July 2002 ** ** Author: JC van Winkel - AT Computing, Nijmegen, Holland ** Complete redesign. ** Date: November 2009 ** -------------------------------------------------------------------------- ** Copyright (C) 2009-2010 JC van Winkel ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ** -------------------------------------------------------------------------- */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "atop.h" #include "cgroups.h" #include "photoproc.h" #include "photosyst.h" #include "showgeneric.h" #include "showlinux.h" static void make_proc_dynamicgen(void); static int get_perc(char *, char *); static void make_detail_prints(detail_printpair *, int, const char *, const char *); /* ** critical percentages for occupation-percentage; ** these defaults can be overruled via the config-file */ int cpubadness = 90; /* percentage */ int gpubadness = 100; /* percentage */ int membadness = 90; /* percentage */ int swpbadness = 80; /* percentage */ int dskbadness = 90; /* percentage */ int netbadness = 90; /* percentage */ int pagbadness = 10; /* number per second */ int almostcrit = 80; /* percentage */ /* * tables with all sys_printdefs */ sys_printdef *prcsyspdefs[] = { &syspdef_PRCSYS, &syspdef_PRCUSER, &syspdef_PRCNPROC, &syspdef_PRCNRUNNING, &syspdef_PRCNSLEEPING, &syspdef_PRCNDSLEEPING, &syspdef_PRCNIDLE, &syspdef_PRCNZOMBIE, &syspdef_PRCCLONES, &syspdef_PRCNNEXIT, &syspdef_BLANKBOX, 0 }; sys_printdef *cpusyspdefs[] = { &syspdef_CPUSYS, &syspdef_CPUUSER, &syspdef_CPUIRQ, &syspdef_CPUIDLE, &syspdef_CPUWAIT, &syspdef_BLANKBOX, &syspdef_CPUIPC, &syspdef_CPUCYCLE, &syspdef_CPUFREQ, &syspdef_CPUSCALE, &syspdef_CPUSTEAL, &syspdef_CPUGUEST, &syspdef_BLANKBOX, 0 }; sys_printdef *cpisyspdefs[] = { &syspdef_CPUISYS, &syspdef_CPUIUSER, &syspdef_CPUIIRQ, &syspdef_CPUIIDLE, &syspdef_CPUIWAIT, &syspdef_BLANKBOX, &syspdef_CPUIIPC, &syspdef_CPUICYCLE, &syspdef_CPUIFREQ, &syspdef_CPUISCALE, &syspdef_CPUISTEAL, &syspdef_CPUIGUEST, &syspdef_BLANKBOX, 0 }; sys_printdef *cplsyspdefs[] = { &syspdef_CPLNUMCPU, &syspdef_CPLAVG1, &syspdef_CPLAVG5, &syspdef_CPLAVG15, &syspdef_CPLCSW, &syspdef_CPLINTR, &syspdef_BLANKBOX, 0 }; sys_printdef *gpusyspdefs[] = { &syspdef_GPUBUS, &syspdef_GPUGPUPERC, &syspdef_GPUMEMPERC, &syspdef_GPUMEMOCC, &syspdef_GPUMEMTOT, &syspdef_GPUMEMUSE, &syspdef_GPUMEMAVG, &syspdef_GPUTYPE, &syspdef_GPUNRPROC, &syspdef_BLANKBOX, 0 }; sys_printdef *memsyspdefs1[] = { &syspdef_MEMTOT, &syspdef_MEMFREE, &syspdef_MEMAVAIL, &syspdef_BLANKBOX, &syspdef_MEMCACHE, &syspdef_MEMDIRTY, &syspdef_MEMBUFFER, &syspdef_BLANKBOX, &syspdef_MEMSLAB, &syspdef_RECSLAB, &syspdef_BLANKBOX, &syspdef_HUPTOT, &syspdef_HUPUSE, 0 }; sys_printdef *memsyspdefs2[] = { &syspdef_NUMNUMA, &syspdef_BLANKBOX, &syspdef_TCPSOCK, &syspdef_UDPSOCK, &syspdef_BLANKBOX, &syspdef_SHMEM, &syspdef_SHMRSS, &syspdef_SHMSWP, &syspdef_BLANKBOX, &syspdef_PAGETABS, &syspdef_BLANKBOX, &syspdef_ANONTHP, &syspdef_VMWBAL, &syspdef_BLANKBOX, &syspdef_ZFSARC, 0 }; sys_printdef *swpsyspdefs[] = { &syspdef_SWPTOT, &syspdef_SWPFREE, &syspdef_SWPCACHE, &syspdef_BLANKBOX, &syspdef_ZSWPOOL, &syspdef_ZSWSTORED, &syspdef_KSMSHARING, &syspdef_KSMSHARED, &syspdef_BLANKBOX, &syspdef_SWPCOMMITTED, &syspdef_SWPCOMMITLIM, &syspdef_BLANKBOX, 0 }; sys_printdef *pagsyspdefs[] = { &syspdef_PAGSCAN, &syspdef_PAGSTEAL, &syspdef_PAGSTALL, &syspdef_PAGCOMPACT, &syspdef_PGMIGRATE, &syspdef_NUMAMIGRATE, &syspdef_PAGSWIN, &syspdef_PAGSWOUT, &syspdef_PAGZSWIN, &syspdef_PAGZSWOUT, &syspdef_OOMKILLS, &syspdef_PAGPGIN, &syspdef_PAGPGOUT, &syspdef_BLANKBOX, 0 }; sys_printdef *memnumasyspdefs[] = { &syspdef_NUMATOT, &syspdef_NUMAFREE, &syspdef_NUMAFILEPAGE, &syspdef_NUMANR, &syspdef_NUMADIRTY, &syspdef_NUMAACTIVE, &syspdef_NUMAINACTIVE, &syspdef_NUMASLAB, &syspdef_NUMASLABRECLAIM, &syspdef_NUMASHMEM, &syspdef_NUMAFRAG, &syspdef_NUMAHUPTOT, &syspdef_NUMAHUPUSE, 0 }; sys_printdef *cpunumasyspdefs[] = { &syspdef_NUMACPUSYS, &syspdef_NUMACPUUSER, &syspdef_NUMACPUNICE, &syspdef_NUMACPUIRQ, &syspdef_NUMACPUSOFTIRQ, &syspdef_NUMACPUIDLE, &syspdef_NUMACPUWAIT, &syspdef_NUMACPUSTEAL, &syspdef_NUMACPUGUEST, &syspdef_NUMANUMCPU, 0 }; sys_printdef *llcsyspdefs[] = { &syspdef_LLCMBMTOTAL, &syspdef_LLCMBMLOCAL, &syspdef_NUMLLC, &syspdef_BLANKBOX, 0 }; sys_printdef *psisyspdefs[] = { &syspdef_PSICPUSTOT, &syspdef_PSIMEMSTOT, &syspdef_PSIMEMFTOT, &syspdef_PSIIOSTOT, &syspdef_PSIIOFTOT, &syspdef_PSICPUS, &syspdef_PSIMEMS, &syspdef_PSIMEMF, &syspdef_PSIIOS, &syspdef_PSIIOF, &syspdef_BLANKBOX, 0 }; sys_printdef *contsyspdefs[] = { &syspdef_CONTNAME, &syspdef_CONTNPROC, &syspdef_CONTCPU, &syspdef_CONTMEM, &syspdef_BLANKBOX, 0 }; sys_printdef *dsksyspdefs[] = { &syspdef_DSKNAME, &syspdef_DSKBUSY, &syspdef_DSKNREAD, &syspdef_DSKNWRITE, &syspdef_DSKNDISC, &syspdef_DSKMBPERSECWR, &syspdef_DSKMBPERSECRD, &syspdef_DSKKBPERRD, &syspdef_DSKKBPERWR, &syspdef_DSKKBPERDS, &syspdef_DSKINFLIGHT, &syspdef_DSKAVQUEUE, &syspdef_DSKAVIO, &syspdef_BLANKBOX, 0 }; sys_printdef *nfsmntsyspdefs[] = { &syspdef_NFMPATH, &syspdef_NFMSERVER, &syspdef_NFMTOTREAD, &syspdef_NFMTOTWRITE, &syspdef_NFMNREAD, &syspdef_NFMNWRITE, &syspdef_NFMDREAD, &syspdef_NFMDWRITE, &syspdef_NFMMREAD, &syspdef_NFMMWRITE, &syspdef_BLANKBOX, 0 }; sys_printdef *nfcsyspdefs[] = { &syspdef_NFCRPCCNT, &syspdef_NFCRPCREAD, &syspdef_NFCRPCWRITE, &syspdef_NFCRPCRET, &syspdef_NFCRPCARF, &syspdef_BLANKBOX, 0 }; sys_printdef *nfssyspdefs[] = { &syspdef_NFSRPCCNT, &syspdef_NFSRPCREAD, &syspdef_NFSRPCWRITE, &syspdef_NFSNRBYTES, &syspdef_NFSNWBYTES, &syspdef_NFSNETTCP, &syspdef_NFSNETUDP, &syspdef_NFSBADFMT, &syspdef_NFSBADAUT, &syspdef_NFSBADCLN, &syspdef_NFSRCHITS, &syspdef_NFSRCMISS, &syspdef_NFSRCNOCA, &syspdef_BLANKBOX, 0 }; sys_printdef *nettranssyspdefs[] = { &syspdef_NETTRANSPORT, &syspdef_NETTCPI, &syspdef_NETTCPO, &syspdef_NETUDPI, &syspdef_NETUDPO, &syspdef_NETTCPACTOPEN, &syspdef_NETTCPPASVOPEN, &syspdef_NETTCPRETRANS, &syspdef_NETTCPINERR, &syspdef_NETTCPORESET, &syspdef_NETTCPCSUMERR, &syspdef_NETUDPNOPORT, &syspdef_NETUDPINERR, &syspdef_BLANKBOX, 0 }; sys_printdef *netnetsyspdefs[] = { &syspdef_NETNETWORK, &syspdef_NETIPI, &syspdef_NETIPO, &syspdef_NETIPFRW, &syspdef_NETIPDELIV, &syspdef_NETICMPIN, &syspdef_NETICMPOUT, &syspdef_BLANKBOX, 0 }; sys_printdef *netintfsyspdefs[] = { &syspdef_NETNAME, &syspdef_NETPCKI, &syspdef_NETPCKO, &syspdef_NETSPEEDMAX, &syspdef_NETSPEEDIN, &syspdef_NETSPEEDOUT, &syspdef_NETCOLLIS, &syspdef_NETMULTICASTIN, &syspdef_NETRCVERR, &syspdef_NETSNDERR, &syspdef_NETRCVDROP, &syspdef_NETSNDDROP, &syspdef_BLANKBOX, 0 }; sys_printdef *infinisyspdefs[] = { &syspdef_IFBNAME, &syspdef_IFBPCKI, &syspdef_IFBPCKO, &syspdef_IFBSPEEDMAX, &syspdef_IFBSPEEDIN, &syspdef_IFBSPEEDOUT, &syspdef_IFBLANES, &syspdef_BLANKBOX, 0 }; /* * table with all detail_printdefs */ detail_printdef *alldetaildefs[]= { &procprt_PID, &procprt_TID, &procprt_PPID, &procprt_SYSCPU, &procprt_USRCPU, &procprt_RUNDELAY, &procprt_BLKDELAY, &procprt_WCHAN, &procprt_NVCSW, &procprt_NIVCSW, &procprt_VGROW, &procprt_RGROW, &procprt_MINFLT, &procprt_MAJFLT, &procprt_VSTEXT, &procprt_VSIZE, &procprt_RSIZE, &procprt_PSIZE, &procprt_VSLIBS, &procprt_VDATA, &procprt_VSTACK, &procprt_SWAPSZ, &procprt_LOCKSZ, &procprt_CMD, &procprt_RUID, &procprt_EUID, &procprt_SUID, &procprt_FSUID, &procprt_RGID, &procprt_EGID, &procprt_SGID, &procprt_FSGID, &procprt_CTID, &procprt_VPID, &procprt_CID, &procprt_STDATE, &procprt_STTIME, &procprt_ENDATE, &procprt_ENTIME, &procprt_THR, &procprt_TRUN, &procprt_TSLPI, &procprt_TSLPU, &procprt_TIDLE, &procprt_POLI, &procprt_NICE, &procprt_PRI, &procprt_RTPR, &procprt_CURCPU, &procprt_ST, &procprt_EXC, &procprt_S, &procprt_COMMAND_LINE, &procprt_NPROCS, &procprt_RDDSK, &procprt_WRDSK, &procprt_CWRDSK, &procprt_WCANCEL, &procprt_TCPRCV, &procprt_TCPRASZ, &procprt_TCPSND, &procprt_TCPSASZ, &procprt_UDPRCV, &procprt_UDPRASZ, &procprt_UDPSND, &procprt_UDPSASZ, &procprt_RNET, &procprt_SNET, &procprt_BANDWI, &procprt_BANDWO, &procprt_GPULIST, &procprt_GPUMEMNOW, &procprt_GPUMEMAVG, &procprt_GPUGPUBUSY, &procprt_GPUMEMBUSY, &procprt_SORTITEM, &cgroupprt_CGROUP_PATH, &cgroupprt_CGRNPROCS, &cgroupprt_CGRNPROCSB, &cgroupprt_CGRCPUBUSY, &cgroupprt_CGRCPUPSI, &cgroupprt_CGRCPUMAX, &cgroupprt_CGRCPUWGT, &cgroupprt_CGRMEMORY, &cgroupprt_CGRMEMPSI, &cgroupprt_CGRMEMMAX, &cgroupprt_CGRSWPMAX, &cgroupprt_CGRDISKIO, &cgroupprt_CGRDSKPSI, &cgroupprt_CGRDSKWGT, &cgroupprt_CGRPID, &cgroupprt_CGRCMD, 0 }; /* * table with all detail_printdefs with PID/TID width to be initialized */ detail_printdef *idprocpdefs[]= { &procprt_PID, &procprt_TID, &procprt_PPID, &procprt_VPID, &cgroupprt_CGRPID, 0 }; /***************************************************************/ /* * output definitions for process and cgroups data * these should be user configurable */ detail_printpair userprocs[MAXITEMS]; detail_printpair memprocs[MAXITEMS]; detail_printpair schedprocs[MAXITEMS]; detail_printpair genprocs[MAXITEMS]; detail_printpair dskprocs[MAXITEMS]; detail_printpair netprocs[MAXITEMS]; detail_printpair gpuprocs[MAXITEMS]; detail_printpair varprocs[MAXITEMS]; detail_printpair cmdprocs[MAXITEMS]; detail_printpair ownprocs[MAXITEMS]; detail_printpair totusers[MAXITEMS]; detail_printpair totprocs[MAXITEMS]; detail_printpair totconts[MAXITEMS]; detail_printpair gencgroups[MAXITEMS]; // cgroups data detail_printpair gencgrprocs[MAXITEMS]; // cgroups process data /*****************************************************************/ /* * output definitions for system data * these should be user configurable */ sys_printpair sysprcline[MAXITEMS]; sys_printpair allcpuline[MAXITEMS]; sys_printpair indivcpuline[MAXITEMS]; sys_printpair cplline[MAXITEMS]; sys_printpair gpuline[MAXITEMS]; sys_printpair memline1[MAXITEMS]; sys_printpair memline2[MAXITEMS]; sys_printpair swpline[MAXITEMS]; sys_printpair memnumaline[MAXITEMS]; sys_printpair cpunumaline[MAXITEMS]; sys_printpair llcline[MAXITEMS]; sys_printpair pagline[MAXITEMS]; sys_printpair psiline[MAXITEMS]; sys_printpair contline[MAXITEMS]; sys_printpair dskline[MAXITEMS]; sys_printpair nettransportline[MAXITEMS]; sys_printpair netnetline[MAXITEMS]; sys_printpair netinterfaceline[MAXITEMS]; sys_printpair infinibandline[MAXITEMS]; sys_printpair nfsmountline[MAXITEMS]; sys_printpair nfcline[MAXITEMS]; sys_printpair nfsline[MAXITEMS]; typedef struct { const char *name; int prio; } name_prio; /* ** make an string,int pair array from a string. chop based on spaces/tabs ** example: input: "ABCD:3 EFG:1 QWE:16" ** output: { { "ABCD", 3 }, {"EFG", 1}, { "QWE", 16}, { 0, 0 } } */ static void makeargv(char *line, const char *linename, name_prio *vec) { int i=0; char *p=line; char *name=0; char *prio=0; // find pair and scan it while (*p && i= INT_MAX || lprio <0) { fprintf(stderr, "atoprc - %s: item `%s` has " "invalid priority `", linename, name); while (*prio && *prio !=' ') { fputc(*prio, stderr); prio++; } fprintf(stderr, "'\n"); cleanstop(1); } vec[i].name=name; vec[i].prio=lprio; ++i; } vec[i].name=0; } /* * make_sys_prints: make array of sys_printpairs * input: string, sys_printpair array, maxentries */ static void make_sys_prints(sys_printpair *ar, int maxn, const char *pairs, sys_printdef *permissables[], const char *linename, struct sstat *sstat, extraparam *extra) { name_prio items[MAXITEMS]; int i, a, n=strlen(pairs); char str[n+1]; strcpy(str, pairs); makeargv(str, linename, items); for(i=a=0; iconfigname, name) == 0) { // call validate function to see if this // counter is relevant // if (sstat != NULL && permissables[j]->dovalidate != NULL && permissables[j]->dovalidate(sstat) == 0) break; ar[a].f = permissables[j]; ar[a].prio = items[i].prio; a++; break; } } if (permissables[j]==0) { mcleanstop(1, "atoprc - own system line: item %s invalid in %s line\n", name, linename); } } ar[a].f=0; ar[a].prio=0; } /* * init_proc_prints: determine width of columns that are * dependent of dynamic values */ static void init_proc_prints(count_t numcpu) { char linebuf[64]; int i; /* ** fill number of digits for various PID/TID columns ** and reformat header to new width */ for (i=0; idprocpdefs[i] != 0; i++) { idprocpdefs[i]->width = pidwidth; if ( strlen(idprocpdefs[i]->head) < pidwidth) { char *p = malloc(pidwidth+1); ptrverify(p, "Malloc failed for formatted header\n"); snprintf(p, pidwidth+1, "%*s", pidwidth, idprocpdefs[i]->head); idprocpdefs[i]->head = p; } } /* ** fill number of positions for the SORTITEM (percentage), ** depending on the number of CPUs (e.g. with 10+ CPUs a process ** can reach a CPU percentage of 1000% and with 100+ CPUs a ** CPU percentage of 10000%). */ procprt_SORTITEM.width = snprintf(linebuf, sizeof linebuf, "%lld", numcpu*100) + 1; } /* ** make_detail_prints: make array of detail_printpairs ** input: string, detail_printpair array, maxentries */ static void make_detail_prints(detail_printpair *ar, int maxn, const char *pairs, const char *linename) { name_prio items[MAXITEMS]; int n=strlen(pairs); char str[n+1]; strcpy(str, pairs); makeargv(str, linename, items); int i; for(i=0; iconfigname, name)==0) { ar[i].pf = alldetaildefs[j]; ar[i].prio = items[i].prio; break; } } if (alldetaildefs[j]==0) { mcleanstop(1, "atoprc - ownprocline: item %s invalid!\n", name); } } ar[i].pf=0; ar[i].prio=0; } /* ** calculate the total consumption on system-level for the ** four main resources */ void totalcap(struct syscap *psc, struct sstat *sstat, struct tstat **proclist, int nactproc) { register int i; psc->nrcpu = sstat->cpu.nrcpu; psc->availcpu = sstat->cpu.all.stime + sstat->cpu.all.utime + sstat->cpu.all.ntime + sstat->cpu.all.itime + sstat->cpu.all.wtime + sstat->cpu.all.Itime + sstat->cpu.all.Stime + sstat->cpu.all.steal; psc->availmem = sstat->mem.physmem * pagesize/1024; /* ** calculate total transfer issued by the active processes ** for disk and for network */ for (psc->availnet=psc->availdsk=0, i=0; i < nactproc; i++) { struct tstat *curstat = *(proclist+i); count_t nett_wsz; psc->availnet += curstat->net.tcpssz; psc->availnet += curstat->net.tcprsz; psc->availnet += curstat->net.udpssz; psc->availnet += curstat->net.udprsz; if (curstat->dsk.wsz > curstat->dsk.cwsz) nett_wsz = curstat->dsk.wsz - curstat->dsk.cwsz; else nett_wsz = 0; psc->availdsk += curstat->dsk.rsz; psc->availdsk += nett_wsz; } for (psc->availgpumem=i=0; i < sstat->gpu.nrgpus; i++) psc->availgpumem += sstat->gpu.gpu[i].memtotnow; psc->nrgpu = sstat->gpu.nrgpus; psc->nrmemnuma = sstat->memnuma.nrnuma; psc->nrcpunuma = sstat->cpunuma.nrnuma; } /* ** calculate cumulative system- and user-time for all active processes ** besides, initialize all counter lines on system level */ void pricumproc(struct sstat *sstat, struct devtstat *devtstat, int nexit, unsigned int noverflow, int avgval, int nsecs) { static int firsttime=1; int i; extraparam extra; for (i=0, extra.totut=extra.totst=0; i < devtstat->nprocactive; i++) { struct tstat *curstat = *(devtstat->procactive+i); extra.totut += curstat->cpu.utime; extra.totst += curstat->cpu.stime; } extra.nproc = devtstat->nprocall; extra.ntrun = devtstat->totrun; extra.ntslpi = devtstat->totslpi; extra.ntslpu = devtstat->totslpu; extra.ntidle = devtstat->totidle; extra.nzomb = devtstat->totzombie; extra.nexit = nexit; extra.noverflow = noverflow; extra.avgval = avgval; extra.nsecs = nsecs; extra.index = 0; if (firsttime) { firsttime=0; if (sysprcline[0].f == 0) { make_sys_prints(sysprcline, MAXITEMS, "PRCSYS:8 " "PRCUSER:8 " "BLANKBOX:0 " "PRCNPROC:7 " "PRCNRUNNING:5 " "PRCNSLEEPING:5 " "PRCNDSLEEPING:5 " "PRCNIDLE:5 " "PRCNZOMBIE:4 " "PRCCLONES:3 " "BLANKBOX:0 " "PRCNNEXIT:6", prcsyspdefs, "builtin sysprcline", sstat, &extra); } if (allcpuline[0].f == 0) { make_sys_prints(allcpuline, MAXITEMS, "CPUSYS:9 " "CPUUSER:8 " "CPUIRQ:6 " "BLANKBOX:0 " "CPUIDLE:7 " "CPUWAIT:7 " "CPUSTEAL:2 " "CPUGUEST:3 " "BLANKBOX:0 " "CPUIPC:5 " "CPUCYCLE:4 " "CPUFREQ:4 " "CPUSCALE:4 ", cpusyspdefs, "builtin allcpuline", sstat, &extra); } if (indivcpuline[0].f == 0) { make_sys_prints(indivcpuline, MAXITEMS, "CPUISYS:9 " "CPUIUSER:8 " "CPUIIRQ:6 " "BLANKBOX:0 " "CPUIIDLE:7 " "CPUIWAIT:7 " "CPUISTEAL:2 " "CPUIGUEST:3 " "BLANKBOX:0 " "CPUIIPC:5 " "CPUICYCLE:4 " "CPUIFREQ:4 " "CPUISCALE:4 ", cpisyspdefs, "builtin indivcpuline", sstat, &extra); } if (cplline[0].f == 0) { make_sys_prints(cplline, MAXITEMS, "CPLNUMCPU:7" "BLANKBOX:0 " "CPLAVG1:4 " "CPLAVG5:3 " "CPLAVG15:2 " "BLANKBOX:0 " "CPLCSW:6 " "CPLINTR:5 ", cplsyspdefs, "builtin cplline", sstat, &extra); } if (gpuline[0].f == 0) { make_sys_prints(gpuline, MAXITEMS, "GPUBUS:8 " "GPUGPUPERC:7 " "GPUMEMPERC:6 " "GPUMEMOCC:5 " "GPUMEMTOT:3 " "GPUMEMUSE:4 " "GPUMEMAVG:2 " "GPUNRPROC:2 " "BLANKBOX:0 " "GPUTYPE:1 ", gpusyspdefs, "builtin gpuline", NULL, NULL); } if (memline1[0].f == 0) { make_sys_prints(memline1, MAXITEMS, "MEMTOT:8 " "MEMFREE:9 " "MEMAVAIL:7 " "BLANKBOX:0 " "MEMCACHE:7 " "MEMDIRTY:5 " "MEMBUFFER:6 " "BLANKBOX:0 " "MEMSLAB:6 " "RECSLAB:2 " "BLANKBOX:0 " "HUPTOT:4 " "HUPUSE:1 ", memsyspdefs1, "builtin memline1", sstat, &extra); } if (memline2[0].f == 0) { make_sys_prints(memline2, MAXITEMS, "NUMNUMA:8 " "BLANKBOX:1 " "SHMEM:6 " "SHMRSS:4 " "SHMSWP:4 " "BLANKBOX:0 " "TCPSOCK:3 " "UDPSOCK:2 " "BLANKBOX:0 " "PAGETABS:5 " "BLANKBOX:0 " "ANONTHP:4 " "VMWBAL:4 " "BLANKBOX:0 " "ZFSARC:5 ", memsyspdefs2, "builtin memline2", sstat, &extra); } if (swpline[0].f == 0) { make_sys_prints(swpline, MAXITEMS, "SWPTOT:5 " "SWPFREE:6 " "SWPCACHE:4 " "BLANKBOX:0 " "ZSWPOOL:3 " "ZSWSTORED:3 " "BLANKBOX:0 " "KSMSHARED:2 " "KSMSHARING:2 " "BLANKBOX:0 " "SWPCOMMITTED:7 " "SWPCOMMITLIM:8", swpsyspdefs, "builtin swpline", sstat, &extra); } if (memnumaline[0].f == 0) { make_sys_prints(memnumaline, MAXITEMS, "NUMATOT:8 " "NUMAFREE:9 " "NUMAFILEPAGE:9 " "NUMANR:7 " "NUMADIRTY:5 " "NUMAACTIVE:5 " "NUMAINACTIVE:5 " "NUMASLAB:7 " "NUMASLABRECLAIM:4 " "NUMASHMEM:4 " "NUMAFRAG:6 " "NUMAHUPTOT:4 " "NUMAHUPUSE:3 ", memnumasyspdefs, "builtin memnumaline", sstat, &extra); } if (cpunumaline[0].f == 0) { make_sys_prints(cpunumaline, MAXITEMS, "NUMACPUSYS:9 " "NUMACPUUSER:8 " "NUMACPUNICE:8 " "NUMACPUIRQ:6 " "NUMACPUSOFTIRQ:6 " "NUMACPUIDLE:7 " "NUMACPUWAIT:7 " "NUMACPUSTEAL:2 " "NUMACPUGUEST:3 " "NUMANUMCPU:5", cpunumasyspdefs, "builtin cpunumaline", NULL, NULL); } if (llcline[0].f == 0) { make_sys_prints(llcline, MAXITEMS, "LLCMBMTOTAL:9 " "LLCMBMLOCAL:8 " "NUMLLC:7 " "BLANKBOX:0 ", llcsyspdefs, "builtin llcline", sstat, &extra); } if (pagline[0].f == 0) { make_sys_prints(pagline, MAXITEMS, "PAGSCAN:3 " "PAGSTEAL:2 " "PAGSTALL:1 " "PAGCOMPACT:5 " "NUMAMIGRATE:5" "PGMIGRATE:6" "PAGPGIN:6 " "PAGPGOUT:6 " "PAGZSWIN:4 " "PAGZSWOUT:7 " "PAGSWIN:5 " "PAGSWOUT:8 " "OOMKILLS:9 ", pagsyspdefs, "builtin pagline", sstat, &extra); } if (psiline[0].f == 0) { make_sys_prints(psiline, MAXITEMS, "PSICPUSTOT:7 " "PSIMEMSTOT:7 " "PSIMEMFTOT:8 " "PSIIOSTOT:7 " "PSIIOFTOT:8 " "PSICPUS:6 " "PSIMEMS:5 " "PSIMEMF:3 " "PSIIOS:4 " "PSIIOF:2 " "BLANKBOX:0 ", psisyspdefs, "builtin psiline", sstat, &extra); } if (contline[0].f == 0) { make_sys_prints(contline, MAXITEMS, "CONTNAME:8 " "CONTNPROC:7 " "CONTCPU:6 " "CONTMEM:6 " "BLANKBOX:0 " "BLANKBOX:0 ", contsyspdefs, "builtin contline", NULL, NULL); } if (dskline[0].f == 0) { make_sys_prints(dskline, MAXITEMS, "DSKNAME:9 " "DSKBUSY:8 " "DSKNREAD:7 " "DSKNWRITE:7 " "DSKNDISC:6 " "DSKKBPERRD:5 " "DSKKBPERWR:5 " "DSKKBPERDS:4 " "DSKMBPERSECRD:6 " "DSKMBPERSECWR:6 " "DSKINFLIGHT:2 " "DSKAVQUEUE:1 " "DSKAVIO:6", dsksyspdefs, "builtin dskline", sstat, &extra); } if (nfsmountline[0].f == 0) { make_sys_prints(nfsmountline, MAXITEMS, "NFMPATH:8 " "NFMSERVER:8 " "NFMTOTREAD:8 " "NFMTOTWRITE:8 " "BLANKBOX:0 " "NFMNREAD:7 " "NFMNWRITE:6 " "BLANKBOX:0 " "NFMDREAD:5 " "NFMDWRITE:4 " "BLANKBOX:0 " "NFMMREAD:3 " "NFMMWRITE:2 " "BLANKBOX:0 " "BLANKBOX:0", nfsmntsyspdefs, "builtin nfsmountline", NULL, NULL); } if (nfcline[0].f == 0) { make_sys_prints(nfcline, MAXITEMS, "NFCRPCCNT:8 " "NFCRPCREAD:7 " "NFCRPCWRITE:7 " "NFCRPCRET:5 " "NFCRPCARF:5 " "BLANKBOX:0 " "BLANKBOX:0 " "BLANKBOX:0 " "BLANKBOX:0 " "BLANKBOX:0 ", nfcsyspdefs, "builtin nfcline", sstat, &extra); } if (nfsline[0].f == 0) { make_sys_prints(nfsline, MAXITEMS, "NFSRPCCNT:8 " "NFSRPCREAD:6 " "NFSRPCWRITE:6 " "BLANKBOX:0 " "NFSNRBYTES:7 " "NFSNWBYTES:7 " "BLANKBOX:0 " "NFSNETTCP:5 " "NFSNETUDP:5 " "BLANKBOX:0 " "NFSRCHITS:3 " "NFSRCMISS:2 " "NFSRCNOCA:1 " "BLANKBOX:0 " "NFSBADFMT:4 " "NFSBADAUT:4 " "NFSBADCLN:4 ", nfssyspdefs, "builtin nfsline", sstat, &extra); } if (nettransportline[0].f == 0) { make_sys_prints(nettransportline, MAXITEMS, "NETTRANSPORT:9 " "NETTCPI:8 " "NETTCPO:8 " "NETUDPI:8 " "NETUDPO:8 " "NETTCPACTOPEN:6 " "NETTCPPASVOPEN:5 " "NETTCPRETRANS:4 " "NETTCPINERR:3 " "NETTCPORESET:2 " "NETTCPCSUMERR:2 " "NETUDPNOPORT:1 " "NETUDPINERR:3", nettranssyspdefs, "builtin nettransportline", sstat, &extra); } if (netnetline[0].f == 0) { make_sys_prints(netnetline, MAXITEMS, "NETNETWORK:5 " "NETIPI:4 " "NETIPO:4 " "NETIPFRW:4 " "NETIPDELIV:4 " "BLANKBOX:0 " "BLANKBOX:0 " "BLANKBOX:0 " "NETICMPIN:1 " "NETICMPOUT:1 ", netnetsyspdefs, "builtin netnetline", sstat, &extra); } if (netinterfaceline[0].f == 0) { make_sys_prints(netinterfaceline, MAXITEMS, "NETNAME:8 " "BLANKBOX:0 " "NETPCKI:7 " "NETPCKO:7 " "BLANKBOX:0 " "NETSPEEDMAX:5 " "NETSPEEDIN:6 " "NETSPEEDOUT:6 " "BLANKBOX:0 " "NETCOLLIS:2 " "NETMULTICASTIN:2 " "NETRCVERR:4 " "NETSNDERR:4 " "NETRCVDROP:3 " "NETSNDDROP:3", netintfsyspdefs, "builtin netinterfaceline", NULL, NULL); } if (infinibandline[0].f == 0) { make_sys_prints(infinibandline, MAXITEMS, "IFBNAME:8 " "BLANKBOX:0 " "IFBPCKI:7 " "IFBPCKO:7 " "BLANKBOX:0 " "IFBSPEEDMAX:5 " "IFBSPEEDIN:6 " "IFBSPEEDOUT:6 " "IFBLANES:4 " "BLANKBOX:0 " "BLANKBOX:0 " "BLANKBOX:0 " "BLANKBOX:0 " "BLANKBOX:0 " "BLANKBOX:0 ", infinisyspdefs, "builtin infinibandline", NULL, NULL); } } // firsttime move(1, 0); showsysline(sysprcline, sstat, &extra, "PRC", 0); } /* ** print the header for the process/cgroups list */ void prihead(int curlist, int totlist, char *showtype, char *showorder, char autosort, count_t numcpu) { static int firsttime=1; static int prev_supportflags = -1, prev_threadview = -1; /* ** determine once the layout of all per-process reports ** except for the generic report (might change dynamically) */ if (firsttime) { init_proc_prints(numcpu); make_detail_prints(memprocs, MAXITEMS, "PID:10 TID:3 MINFLT:2 MAJFLT:2 VSTEXT:4 VSLIBS:4 " "VDATA:4 VSTACK:4 LOCKSZ:3 VSIZE:6 RSIZE:7 PSIZE:5 " "VGROW:7 RGROW:8 SWAPSZ:5 RUID:1 EUID:0 " "SORTITEM:9 CMD:10", "built-in memprocs"); make_detail_prints(schedprocs, MAXITEMS, "PID:10 TID:6 CID:4 VPID:3 CTID:3 TRUN:7 TSLPI:7 " "TSLPU:7 TIDLE:7 POLI:8 NICE:9 PRI:5 RTPR:9 CPUNR:8 " "ST:8 EXC:8 S:8 RDELAY:8 BDELAY:7 WCHAN:5 " "NVCSW:7 NIVCSW:7 SORTITEM:10 CMD:10", "built-in schedprocs"); make_detail_prints(dskprocs, MAXITEMS, "PID:10 TID:4 RDDSK:9 " "WRDSK:9 WCANCL:8 " "SORTITEM:10 CMD:10", "built-in dskprocs"); make_detail_prints(netprocs, MAXITEMS, "PID:10 TID:6 " "TCPRCV:9 TCPRASZ:4 TCPSND:9 TCPSASZ:4 " "UDPRCV:8 UDPRASZ:3 UDPSND:8 UDPSASZ:3 " "BANDWI:10 BANDWO:10 " "SORTITEM:10 CMD:10", "built-in netprocs"); make_detail_prints(gpuprocs, MAXITEMS, "PID:10 TID:5 CID:4 GPULIST:8 GPUGPUBUSY:8 GPUMEMBUSY:8 " "GPUMEM:7 GPUMEMAVG:6 S:8 SORTITEM:10 CMD:10", "built-in gpuprocs"); make_detail_prints(varprocs, MAXITEMS, "PID:10 TID:4 PPID:9 CID:2 VPID:1 CTID:1 " "RUID:8 RGID:8 EUID:5 EGID:4 " "SUID:3 SGID:2 FSUID:3 FSGID:2 " "STDATE:7 STTIME:7 ENDATE:5 ENTIME:5 " "ST:6 EXC:6 S:6 SORTITEM:10 CMD:10", "built-in varprocs"); make_detail_prints(cmdprocs, MAXITEMS, "PID:10 TID:4 S:8 SORTITEM:10 COMMAND-LINE:10", "built-in cmdprocs"); make_detail_prints(totusers, MAXITEMS, "NPROCS:10 SYSCPU:9 USRCPU:9 VSIZE:6 " "RSIZE:8 PSIZE:8 LOCKSZ:3 SWAPSZ:5 RDDSK:7 CWRDSK:7 " "RNET:6 SNET:6 SORTITEM:10 RUID:10", "built-in totusers"); make_detail_prints(totprocs, MAXITEMS, "NPROCS:10 SYSCPU:9 USRCPU:9 VSIZE:6 " "RSIZE:8 PSIZE:8 LOCKSZ:3 SWAPSZ:5 RDDSK:7 CWRDSK:7 " "RNET:6 SNET:6 SORTITEM:10 CMD:10", "built-in totprocs"); make_detail_prints(totconts, MAXITEMS, "NPROCS:10 SYSCPU:9 USRCPU:9 RDELAY:8 BDELAY:7 VSIZE:6 " "RSIZE:8 PSIZE:8 LOCKSZ:3 SWAPSZ:5 RDDSK:7 CWRDSK:7 " "RNET:6 SNET:6 SORTITEM:10 CID:10", "built-in totconts"); // meant to show cgroups // make_detail_prints(gencgroups, MAXITEMS, "CGRPATH:10 CGRNPROCS:9 CGRNPROCSB:8 " "CGRCPUBUSY:7 CGRCPUPSI:4 CGRCPUMAX:3 CGRCPUWGT:2 " "CGRMEMORY:7 CGRMEMPSI:4 CGRMEMMAX:3 CGRSWPMAX:1 " "CGRDISKIO:6 CGRDSKPSI:4 CGRDSKWGT:2 CGRPID:6 CGRCMD:5", "built-in gencgroups"); } /* ** update the generic report if needed */ if (prev_supportflags != supportflags || prev_threadview != threadview) { make_proc_dynamicgen(); prev_supportflags = supportflags; prev_threadview = threadview; if (*showtype == MPROCNET && !(supportflags&NETATOP||supportflags&NETATOPBPF) ) { *showtype = MPROCGEN; *showorder = MSORTCPU; } } /* ** print the header line */ switch (*showtype) { case MPROCGEN: showprochead(genprocs, curlist, totlist, *showorder, autosort); break; case MPROCMEM: showprochead(memprocs, curlist, totlist, *showorder, autosort); break; case MPROCDSK: showprochead(dskprocs, curlist, totlist, *showorder, autosort); break; case MPROCNET: showprochead(netprocs, curlist, totlist, *showorder, autosort); break; case MPROCGPU: showprochead(gpuprocs, curlist, totlist, *showorder, autosort); break; case MPROCVAR: showprochead(varprocs, curlist, totlist, *showorder, autosort); break; case MPROCARG: showprochead(cmdprocs, curlist, totlist, *showorder, autosort); break; case MPROCOWN: showprochead(ownprocs, curlist, totlist, *showorder, autosort); break; case MPROCSCH: showprochead(schedprocs, curlist, totlist, *showorder, autosort); break; case MCUMUSER: showprochead(totusers, curlist, totlist, *showorder, autosort); break; case MCUMPROC: showprochead(totprocs, curlist, totlist, *showorder, autosort); break; case MCUMCONT: showprochead(totconts, curlist, totlist, *showorder, autosort); break; case MCGROUPS: showcgrouphead(gencgroups, curlist, totlist, *showorder); break; } } /* ** assemble the layout of the generic line, ** depending on the supported features (like ** I/O stats, network stats) and current view */ #define FORMPID "PID:10 " #define FORMTID "TID:6 " #define FORMCID "CID:5 " #define FORMCPU "SYSCPU:9 USRCPU:9 " #define FORMDEL "RDELAY:4 " #define FORMBDL "BDELAY:4 " #define FORMMEM "VGROW:8 RGROW:8 " #define FORMDSK "RDDSK:7 CWRDSK:7 " #define FORMNET "RNET:6 SNET:6 " #define FORMMSC "RUID:2 EUID:1 ST:3 EXC:3 THR:3 S:3 CPUNR:3 " #define FORMEND "SORTITEM:10 CMD:10" static void make_proc_dynamicgen() { char format[300], *p = format; memcpy(p, FORMPID, sizeof FORMPID -1); p += sizeof FORMPID -1; if (threadview) { memcpy(p, FORMTID, sizeof FORMTID -1); p += sizeof FORMTID -1; } if (supportflags & CONTAINERSTAT) { memcpy(p, FORMCID, sizeof FORMCID -1); p += sizeof FORMCID -1; } memcpy(p, FORMCPU, sizeof FORMCPU -1); p += sizeof FORMCPU -1; memcpy(p, FORMDEL, sizeof FORMDEL -1); p += sizeof FORMDEL -1; memcpy(p, FORMBDL, sizeof FORMBDL -1); p += sizeof FORMBDL -1; memcpy(p, FORMMEM, sizeof FORMMEM -1); p += sizeof FORMMEM -1; if (supportflags & IOSTAT) { memcpy(p, FORMDSK, sizeof FORMDSK -1); p += sizeof FORMDSK -1; } if (supportflags & NETATOP || supportflags & NETATOPBPF) { memcpy(p, FORMNET, sizeof FORMNET -1); p += sizeof FORMNET -1; } memcpy(p, FORMMSC, sizeof FORMMSC -1); p += sizeof FORMMSC -1; memcpy(p, FORMEND, sizeof FORMEND); p += sizeof FORMEND; make_detail_prints(genprocs, MAXITEMS, format, "built-in genprocs"); } /* ** print the list of cgroups (and processes) from the deviation list */ int pricgroup(struct cglinesel *itemlist, int firstitem, int lastitem, int curline, int curlist, int totlist, struct syscap *sb, int nsecs, int avgval) { int i; /* ** print info per cgroup */ for (i=firstitem; i < lastitem; i++) { /* ** screen filled entirely ? */ if (screen && curline >= LINES) break; if (screen) move(curline,0); /* ** print information for next line */ showcgroupline(gencgroups, (itemlist+i)->cgp, (itemlist+i)->tsp, nsecs, avgval, sb->availcpu, sb->nrcpu); curline++; } return curline; } /* ** print the list of processes from the deviation list */ int priproc(struct tstat **proclist, int firstproc, int lastproc, int curline, int curlist, int totlist, char showtype, char showorder, struct syscap *sb, int nsecs, int avgval) { register int i; register struct tstat *curstat; double perc; /* ** print info per actual process and maintain totals */ for (i=firstproc; i < lastproc; i++) { curstat = *(proclist+i); if (screen && curline >= LINES) /* screen filled entirely ? */ break; /* ** calculate occupation-percentage of this process ** depending on selected resource */ switch (showorder) { case MSORTCPU: perc = 0.0; if (sb->availcpu) { perc = (double)(curstat->cpu.stime + curstat->cpu.utime ) * 100 / (sb->availcpu / sb->nrcpu); if (perc > 100.0 * sb->nrcpu) perc = 100.0 * sb->nrcpu; if (perc > 100.0 * curstat->gen.nthr) perc = 100.0 * curstat->gen.nthr; } break; case MSORTMEM: perc = 0.0; if (sb->availmem) { perc = (double)curstat->mem.rmem * 100.0 / sb->availmem; if (perc > 100.0) perc = 100.0; } break; case MSORTDSK: perc = 0.0; if (sb->availdsk) { count_t nett_wsz; if (curstat->dsk.wsz > curstat->dsk.cwsz) nett_wsz = curstat->dsk.wsz - curstat->dsk.cwsz; else nett_wsz = 0; perc = (double)(curstat->dsk.rsz + nett_wsz) * 100.0 / sb->availdsk; if (perc > 100.0) perc = 100.0; } break; case MSORTNET: perc = 0.0; if (sb->availnet) { perc = (double)(curstat->net.tcpssz + curstat->net.tcprsz + curstat->net.udpssz + curstat->net.udprsz ) * 100.0 / sb->availnet; if (perc > 100.0) perc = 100.0; } break; case MSORTGPU: perc = 0.0; if (!curstat->gpu.state) break; if (curstat->gpu.gpubusy != -1) { perc = curstat->gpu.gpubusy; } else { perc = curstat->gpu.memnow*100 * sb->nrgpu / sb->availgpumem; } break; default: perc = 0.0; } /* ** now do the formatting of output */ if (screen) { move(curline,0); } switch (showtype) { case MPROCGEN: showprocline(genprocs, curstat, perc, nsecs, avgval); break; case MPROCMEM: showprocline(memprocs, curstat, perc, nsecs, avgval); break; case MPROCDSK: showprocline(dskprocs, curstat, perc, nsecs, avgval); break; case MPROCNET: showprocline(netprocs, curstat, perc, nsecs, avgval); break; case MPROCGPU: showprocline(gpuprocs, curstat, perc, nsecs, avgval); break; case MPROCVAR: showprocline(varprocs, curstat, perc, nsecs, avgval); break; case MPROCARG: showprocline(cmdprocs, curstat, perc, nsecs, avgval); break; case MPROCOWN: showprocline(ownprocs, curstat, perc, nsecs, avgval); break; case MPROCSCH: showprocline(schedprocs, curstat, perc, nsecs, avgval); break; case MCUMUSER: showprocline(totusers, curstat, perc, nsecs, avgval); break; case MCUMPROC: showprocline(totprocs, curstat, perc, nsecs, avgval); break; case MCUMCONT: showprocline(totconts, curstat, perc, nsecs, avgval); break; } curline++; } return curline; } /* ** print the system-wide statistics */ static void pridisklike(extraparam *, struct perdsk *, char *, char *, int, unsigned int *, int *, int, regex_t *); int prisyst(struct sstat *sstat, int curline, int nsecs, int avgval, int fixedhead, struct sselection *selp, char *highorderp, int maxcpulines, int maxgpulines, int maxdsklines, int maxmddlines, int maxlvmlines, int maxintlines, int maxifblines, int maxnfslines, int maxcontlines, int maxnumalines, int maxllclines) { extraparam extra; int lin; count_t busy; unsigned int badness, highbadness=0; extra.nsecs = nsecs; extra.avgval = avgval; /* ** CPU statistics */ extra.cputot = sstat->cpu.all.stime + sstat->cpu.all.utime + sstat->cpu.all.ntime + sstat->cpu.all.itime + sstat->cpu.all.wtime + sstat->cpu.all.Itime + sstat->cpu.all.Stime + sstat->cpu.all.steal; busy = (extra.cputot - sstat->cpu.all.itime - sstat->cpu.all.wtime) * 100.0 / extra.cputot; if (cpubadness) badness = busy * 100 / cpubadness; else badness = 0; if (highbadness < badness) { highbadness = badness; *highorderp = MSORTCPU; } if (extra.cputot == 0) extra.cputot = 1; /* avoid divide-by-zero */ extra.percputot = extra.cputot / sstat->cpu.nrcpu; if (extra.percputot == 0) extra.percputot = 1; /* avoid divide-by-zero */ if (screen) move(curline, 0); showsysline(allcpuline, sstat, &extra, "CPU", badness); curline++; if (sstat->cpu.nrcpu > 1) { for (extra.index=lin=0; extra.index < sstat->cpu.nrcpu && lin < maxcpulines; extra.index++) { extra.percputot = sstat->cpu.cpu[extra.index].stime + sstat->cpu.cpu[extra.index].utime + sstat->cpu.cpu[extra.index].ntime + sstat->cpu.cpu[extra.index].itime + sstat->cpu.cpu[extra.index].wtime + sstat->cpu.cpu[extra.index].Itime + sstat->cpu.cpu[extra.index].Stime + sstat->cpu.cpu[extra.index].steal; if (extra.percputot == (sstat->cpu.cpu[extra.index].itime + sstat->cpu.cpu[extra.index].wtime ) && !fixedhead ) continue; /* inactive cpu */ busy = (extra.percputot - sstat->cpu.cpu[extra.index].itime - sstat->cpu.cpu[extra.index].wtime) * 100.0 / extra.percputot; if (cpubadness) badness = busy * 100 / cpubadness; else badness = 0; if (highbadness < badness) { highbadness = badness; *highorderp = MSORTCPU; } if (extra.percputot == 0) extra.percputot = 1; /* avoid divide-by-zero */ if (screen) move(curline, 0); showsysline(indivcpuline, sstat, &extra, "cpu", badness); curline++; lin++; } } /* ** other CPU-related statistics */ if (screen) move(curline, 0); showsysline(cplline, sstat, &extra, "CPL", 0); curline++; /* ** GPU statistics */ if (sstat->gpu.nrgpus) { for (extra.index=0, lin=0; extra.index < sstat->gpu.nrgpus && lin < maxgpulines; extra.index++) { int totbusy; count_t avgmemuse; // notice that GPU percentage and memory percentage // are not always available; in that case both // values have the value -1 // totbusy = sstat->gpu.gpu[extra.index].gpuperccum + sstat->gpu.gpu[extra.index].memperccum; if (totbusy == -2) // metrics available? totbusy= 0; if (sstat->gpu.gpu[extra.index].samples == 0) { totbusy = sstat->gpu.gpu[extra.index].gpupercnow + sstat->gpu.gpu[extra.index].mempercnow; avgmemuse = sstat->gpu.gpu[extra.index].memusenow; } else { totbusy = totbusy / sstat->gpu.gpu[extra.index].samples; avgmemuse = sstat->gpu.gpu[extra.index].memusecum/ sstat->gpu.gpu[extra.index].samples; } if (gpubadness) badness = totbusy * 100 / gpubadness; else badness = 0; if ( totbusy > 0 || // memusage > 512 MiB (rather arbitrary)? avgmemuse > 512*1024 || fixedhead ) { showsysline(gpuline, sstat, &extra, "GPU", badness); curline++; lin++; } } } /* ** MEMORY statistics */ busy = (sstat->mem.physmem - sstat->mem.freemem - sstat->mem.cachemem - sstat->mem.buffermem - sstat->mem.slabreclaim + sstat->mem.shmem) * 100.0 / sstat->mem.physmem; if (membadness) badness = busy * 100 / membadness; else badness = 0; if (highbadness < badness) { highbadness = badness; *highorderp = MSORTMEM; } if (screen) move(curline, 0); showsysline(memline1, sstat, &extra, "MEM", badness); curline++; showsysline(memline2, sstat, &extra, "MEM", badness); curline++; /* ** SWAP statistics */ busy = (sstat->mem.totswap - sstat->mem.freeswap) * 100.0 / sstat->mem.totswap; if (swpbadness) { badness = busy * 100 / swpbadness; } else { badness = 0; } if (highbadness < badness) { highbadness = badness; *highorderp = MSORTMEM; } if (screen) move(curline, 0); showsysline(swpline, sstat, &extra, "SWP", badness); curline++; /* ** memory info related for per NUMA */ if (sstat->memnuma.nrnuma > 1 && supportflags&REALNUMA) { for (extra.index=lin=0; extra.index < sstat->memnuma.nrnuma && lin < maxnumalines; extra.index++) { busy = (sstat->memnuma.numa[extra.index].totmem - sstat->memnuma.numa[extra.index].freemem - sstat->memnuma.numa[extra.index].filepage - sstat->memnuma.numa[extra.index].slabreclaim + sstat->memnuma.numa[extra.index].shmem) * 100.0 / sstat->memnuma.numa[extra.index].totmem; if (membadness) badness = busy * 100 / membadness; else badness = 0; if (highbadness < badness) { highbadness = badness; *highorderp = MSORTMEM; } if (screen) move(curline, 0); showsysline(memnumaline, sstat, &extra, "NUM", badness); curline++; lin++; } } /* ** Accumulate each cpu statistic for per NUMA */ if (sstat->cpunuma.nrnuma > 1 && supportflags&REALNUMA) { for (extra.index=lin=0; extra.index < sstat->cpunuma.nrnuma && lin < maxnumalines; extra.index++) { extra.pernumacputot = sstat->cpunuma.numa[extra.index].stime + sstat->cpunuma.numa[extra.index].utime + sstat->cpunuma.numa[extra.index].ntime + sstat->cpunuma.numa[extra.index].itime + sstat->cpunuma.numa[extra.index].wtime + sstat->cpunuma.numa[extra.index].Itime + sstat->cpunuma.numa[extra.index].Stime + sstat->cpunuma.numa[extra.index].steal; if (extra.pernumacputot == (sstat->cpunuma.numa[extra.index].itime + sstat->cpunuma.numa[extra.index].wtime ) && !fixedhead ) continue; /* inactive cpu */ if (extra.pernumacputot == 0) extra.pernumacputot = 1; /* avoid divide-by-zero */ busy = (extra.pernumacputot - sstat->cpunuma.numa[extra.index].itime - sstat->cpunuma.numa[extra.index].wtime) * 100.0 / extra.pernumacputot; if (cpubadness) badness = busy * 100 / cpubadness; else badness = 0; if (highbadness < badness) { highbadness = badness; *highorderp = MSORTCPU; } if (sstat->cpunuma.numa[extra.index].nrcpu) extra.percputot = extra.pernumacputot / sstat->cpunuma.numa[extra.index].nrcpu; else extra.percputot = 1; if (extra.percputot == 0) extra.percputot = 1; /* avoid divide-by-zero */ if (screen) move(curline, 0); showsysline(cpunumaline, sstat, &extra, "NUC", badness); curline++; lin++; } } /* ** LLC statistics (if supported by kernel and ** pseudo filesystem mounted) */ for (extra.index=0, lin=0; extra.index < sstat->llc.nrllcs && lin < maxllclines; extra.index++) { if (fixedhead || sstat->llc.perllc[extra.index].mbm_local || sstat->llc.perllc[extra.index].mbm_total ) { if (screen) move(curline, 0); showsysline(llcline, sstat, &extra, "LLC", 0); curline++; lin++; } } /* ** PAGING statistics */ if (fixedhead || sstat->mem.pgscans || sstat->mem.pgsteal || sstat->mem.allocstall || sstat->mem.compactstall || sstat->mem.pgins || sstat->mem.pgouts || sstat->mem.tcpsock || sstat->mem.udpsock || sstat->mem.swins || sstat->mem.swouts || sstat->mem.oomkills > 0 || sstat->mem.pgmigrate || sstat->mem.numamigrate ) { busy = sstat->mem.swouts / nsecs * pagbadness; if (busy > 100) busy = 100; if (membadness) badness = busy * 100 / membadness; else badness = 0; if (highbadness < badness) { highbadness = badness; *highorderp = MSORTMEM; } /* ** take care that this line is anyhow colored for ** 'almost critical' in case of swapouts > 1 per second */ if (sstat->mem.swouts / nsecs > 0 && pagbadness && almostcrit && badness < almostcrit) badness = almostcrit; if (screen) move(curline, 0); showsysline(pagline, sstat, &extra, "PAG", badness); curline++; } /* ** Pressure statistics */ if (sstat->psi.present) { if (fixedhead || sstat->psi.cpusome.avg10 || sstat->psi.memsome.avg10 || sstat->psi.iosome.avg10 || sstat->psi.cpusome.avg60 || sstat->psi.memsome.avg60 || sstat->psi.iosome.avg60 || sstat->psi.cpusome.avg300 || sstat->psi.memsome.avg300 || sstat->psi.iosome.avg300 ) { badness = sstat->psi.cpusome.avg10 > sstat->psi.cpusome.avg60 ? sstat->psi.cpusome.avg10 : sstat->psi.cpusome.avg60; if (badness < sstat->psi.cpusome.avg300) badness = sstat->psi.cpusome.avg300; if (screen) move(curline, 0); showsysline(psiline, sstat, &extra,"PSI", badness); curline++; } } /* ** Container statistics (if any) */ for (extra.index=0, lin=0; extra.index < sstat->cfs.nrcontainer && lin < maxcontlines; extra.index++) { if (fixedhead || sstat->cfs.cont[extra.index].system || sstat->cfs.cont[extra.index].user || sstat->cfs.cont[extra.index].nice ) { if (screen) move(curline, 0); showsysline(contline, sstat, &extra, "CON", 0); curline++; lin++; } } /* ** DISK statistics */ extra.mstot = extra.cputot * 1000 / hertz / sstat->cpu.nrcpu; pridisklike(&extra, sstat->dsk.lvm, "LVM", highorderp, maxlvmlines, &highbadness, &curline, fixedhead, selp->lvmnamesz ? &(selp->lvmregex) : (void *) 0); pridisklike(&extra, sstat->dsk.mdd, "MDD", highorderp, maxmddlines, &highbadness, &curline, fixedhead, (void *) 0); pridisklike(&extra, sstat->dsk.dsk, "DSK", highorderp, maxdsklines, &highbadness, &curline, fixedhead, selp->dsknamesz ? &(selp->dskregex) : (void *) 0); /* ** NFS server and client statistics */ for (extra.index=0, lin=0; extra.index < sstat->nfs.nfsmounts.nrmounts && lin < maxnfslines; extra.index++) { int i = extra.index; if ( (sstat->nfs.nfsmounts.nfsmnt[i].bytesread + sstat->nfs.nfsmounts.nfsmnt[i].byteswrite + sstat->nfs.nfsmounts.nfsmnt[i].bytesdread + sstat->nfs.nfsmounts.nfsmnt[i].bytesdwrite + sstat->nfs.nfsmounts.nfsmnt[i].bytestotread + sstat->nfs.nfsmounts.nfsmnt[i].bytestotwrite + sstat->nfs.nfsmounts.nfsmnt[i].pagesmread + sstat->nfs.nfsmounts.nfsmnt[i].pagesmwrite ) || sstat->nfs.nfsmounts.nfsmnt[i].age < nsecs || fixedhead ) { if (screen) move(curline, 0); showsysline(nfsmountline, sstat, &extra, "NFM", 0); curline++; lin++; } } if (sstat->nfs.client.rpccnt || fixedhead ) { if (screen) move(curline, 0); showsysline(nfcline, sstat, &extra, "NFC", 0); curline++; } if (sstat->nfs.server.rpccnt || fixedhead ) { if (screen) move(curline, 0); showsysline(nfsline, sstat, &extra, "NFS", 0); curline++; } /* ** NET statistics: transport */ if (sstat->net.tcp.InSegs || sstat->net.tcp.OutSegs || sstat->net.udpv4.InDatagrams || sstat->net.udpv6.Udp6InDatagrams || sstat->net.udpv4.OutDatagrams || sstat->net.udpv6.Udp6OutDatagrams || fixedhead ) { if (screen) move(curline, 0); showsysline(nettransportline, sstat, &extra, "NET", 0); curline++; } /* ** NET statistics: network */ if (sstat->net.ipv4.InReceives || sstat->net.ipv6.Ip6InReceives || sstat->net.ipv4.OutRequests || sstat->net.ipv6.Ip6OutRequests || fixedhead ) { if (screen) move(curline, 0); showsysline(netnetline, sstat, &extra, "NET", 0); curline++; } /* ** NET statistics: interfaces */ for (extra.index=0, lin=0; sstat->intf.intf[extra.index].name[0] && lin < maxintlines; extra.index++) { if (sstat->intf.intf[extra.index].rpack || sstat->intf.intf[extra.index].spack || fixedhead) { if (selp->itfnamesz && regexec(&(selp->itfregex), sstat->intf.intf[extra.index].name, 0, NULL, 0)) continue; // suppress (not selected) /* ** calculate busy-percentage for interface */ count_t ival, oval; /* ** convert byte-transfers to bit-transfers (* 8) ** convert bit-transfers to kilobit-transfers (/ 1000) ** per second */ ival = sstat->intf.intf[extra.index].rbyte/125/nsecs; oval = sstat->intf.intf[extra.index].sbyte/125/nsecs; /* speed known? */ if (sstat->intf.intf[extra.index].speed) { if (sstat->intf.intf[extra.index].duplex) busy = (ival > oval ? ival : oval) / (sstat->intf.intf[extra.index].speed *10); else busy = (ival + oval) / (sstat->intf.intf[extra.index].speed *10); } else { busy = 0; } if (netbadness) badness = busy * 100 / netbadness; else badness = 0; if (highbadness < badness && (supportflags & NETATOP || supportflags & NETATOPBPF) ) { highbadness = badness; *highorderp = MSORTNET; } if (screen) move(curline, 0); showsysline(netinterfaceline, sstat, &extra, "NET", badness); curline++; lin++; } } /* ** NET statistics: InfiniBand */ for (extra.index=0, lin=0; extra.index < sstat->ifb.nrports && lin < maxifblines; extra.index++) { if (sstat->ifb.ifb[extra.index].rcvb || sstat->ifb.ifb[extra.index].sndb || fixedhead) { /* ** calculate busy-percentage for IB port */ count_t ival, oval; /* ** convert byte-transfers to bit-transfers (* 8) ** convert bit-transfers to kilobit-transfers (/ 1000) ** per second */ ival = sstat->ifb.ifb[extra.index].rcvb/125/nsecs; oval = sstat->ifb.ifb[extra.index].sndb/125/nsecs; if (sstat->ifb.ifb[extra.index].rate) busy = (ival > oval ? ival : oval) * sstat->ifb.ifb[extra.index].lanes / (sstat->ifb.ifb[extra.index].rate * 10); else busy = 0; if (netbadness) badness = busy * 100 / netbadness; else badness = 0; if (highbadness < badness) { highbadness = badness; *highorderp = MSORTNET; } if (screen) move(curline, 0); showsysline(infinibandline, sstat, &extra, "IFB", badness); curline++; lin++; } } /* ** application statistics ** ** WWW: notice that we cause one access ourselves by fetching ** the statistical counters */ #if HTTPSTATS if (sstat->www.accesses > 1 || fixedhead ) { char format1[8], format2[8], format3[8], format4[8], format5[8]; if (screen) move(curline, 0); printg("WWW | reqs %s | totKB %s | byt/rq %s | iwork %s |" " bwork %s |", val2valstr(sstat->www.accesses, format1, 6, avgval, nsecs), val2valstr(sstat->www.totkbytes, format2, 6, avgval, nsecs), val2valstr(sstat->www.accesses ? sstat->www.totkbytes*1024/sstat->www.accesses : 0, format3, 5, 0, 0), val2valstr(sstat->www.iworkers, format4, 6, 0, 0), val2valstr(sstat->www.bworkers, format5, 6, 0, 0) ); if (!screen) { printg("\n"); } curline++; } #endif /* ** if the system is hardly loaded, still CPU-ordering of ** processes is most interesting (instead of memory) */ if (highbadness < 70 && *highorderp == MSORTMEM) *highorderp = MSORTCPU; return curline; } /* ** handle all instances of a specific disk-like device */ static void pridisklike(extraparam *ep, struct perdsk *dp, char *lp, char *highorderp, int maxlines, unsigned int *highbadp, int *curlinp, int fixedhead, regex_t *rep) { int lin; count_t busy; unsigned int badness; for (ep->perdsk = dp, ep->index=0, lin=0; ep->perdsk[ep->index].name[0] && lin < maxlines; ep->index++) { if (rep && regexec(rep, ep->perdsk[ep->index].name, 0, NULL, 0)) continue; // suppress (not selected) ep->iotot = ep->perdsk[ep->index].nread + ep->perdsk[ep->index].nwrite; busy = (double)(ep->perdsk[ep->index].io_ms * 100.0 / ep->mstot); if (dskbadness) badness = busy * 100 / dskbadness; else badness = 0; if (*highbadp < badness && (supportflags & IOSTAT) ) { *highbadp = badness; *highorderp = MSORTDSK; } if (ep->iotot || fixedhead) { move(*curlinp, 0); showsysline(dskline, 0, ep, lp, badness); (*curlinp)++; lin++; } } } /* ** process-level sort functions */ int compcpu(const void *a, const void *b) { register count_t acpu = (*(struct tstat **)a)->cpu.stime + (*(struct tstat **)a)->cpu.utime; register count_t bcpu = (*(struct tstat **)b)->cpu.stime + (*(struct tstat **)b)->cpu.utime; if (acpu < bcpu) return 1; if (acpu > bcpu) return -1; return compmem(a, b); } int compdsk(const void *a, const void *b) { struct tstat *ta = *(struct tstat **)a; struct tstat *tb = *(struct tstat **)b; count_t adsk; count_t bdsk; if (ta->dsk.wsz > ta->dsk.cwsz) adsk = ta->dsk.rio + ta->dsk.wsz - ta->dsk.cwsz; else adsk = ta->dsk.rio; if (tb->dsk.wsz > tb->dsk.cwsz) bdsk = tb->dsk.rio + tb->dsk.wsz - tb->dsk.cwsz; else bdsk = tb->dsk.rio; if (adsk < bdsk) return 1; if (adsk > bdsk) return -1; return compcpu(a, b); } int compmem(const void *a, const void *b) { register count_t amem = (*(struct tstat **)a)->mem.rmem; register count_t bmem = (*(struct tstat **)b)->mem.rmem; if (amem < bmem) return 1; if (amem > bmem) return -1; return 0; } int compgpu(const void *a, const void *b) { register char astate = (*(struct tstat **)a)->gpu.state; register char bstate = (*(struct tstat **)b)->gpu.state; register count_t abusy = (*(struct tstat **)a)->gpu.gpubusy; register count_t bbusy = (*(struct tstat **)b)->gpu.gpubusy; register count_t amem = (*(struct tstat **)a)->gpu.memnow; register count_t bmem = (*(struct tstat **)b)->gpu.memnow; if (!astate) // no GPU usage? abusy = amem = -2; if (!bstate) // no GPU usage? bbusy = bmem = -2; if (abusy == -1 || bbusy == -1) { if (amem < bmem) return 1; if (amem > bmem) return -1; return 0; } else { if (abusy < bbusy) return 1; if (abusy > bbusy) return -1; return 0; } } int compnet(const void *a, const void *b) { register count_t anet = (*(struct tstat **)a)->net.tcpssz + (*(struct tstat **)a)->net.tcprsz + (*(struct tstat **)a)->net.udpssz + (*(struct tstat **)a)->net.udprsz ; register count_t bnet = (*(struct tstat **)b)->net.tcpssz + (*(struct tstat **)b)->net.tcprsz + (*(struct tstat **)b)->net.udpssz + (*(struct tstat **)b)->net.udprsz ; if (anet < bnet) return 1; if (anet > bnet) return -1; return compcpu(a, b); } int compusr(const void *a, const void *b) { register int uida = (*(struct tstat **)a)->gen.ruid; register int uidb = (*(struct tstat **)b)->gen.ruid; if (uida > uidb) return 1; if (uida < uidb) return -1; return 0; } int compnam(const void *a, const void *b) { register char *nama = (*(struct tstat **)a)->gen.name; register char *namb = (*(struct tstat **)b)->gen.name; return strcmp(nama, namb); } int compcon(const void *a, const void *b) { register char *utsa = (*(struct tstat **)a)->gen.utsname; register char *utsb = (*(struct tstat **)b)->gen.utsname; return strcmp(utsa, utsb); } /* ** system-level sort functions */ int cpucompar(const void *a, const void *b) { register count_t aidle = ((struct percpu *)a)->itime + ((struct percpu *)a)->wtime; register count_t bidle = ((struct percpu *)b)->itime + ((struct percpu *)b)->wtime; if (aidle < bidle) return -1; if (aidle > bidle) return 1; return 0; } int gpucompar(const void *a, const void *b) { register count_t agpuperc = ((struct pergpu *)a)->gpuperccum; register count_t bgpuperc = ((struct pergpu *)b)->gpuperccum; register count_t amemuse = ((struct pergpu *)a)->memusenow; register count_t bmemuse = ((struct pergpu *)b)->memusenow; if (agpuperc == -1 || bgpuperc == -1) { if (amemuse < bmemuse) return 1; if (amemuse > bmemuse) return -1; return 0; } else { if (agpuperc < bgpuperc) return 1; if (agpuperc > bgpuperc) return -1; return 0; } } int diskcompar(const void *a, const void *b) { register count_t amsio = ((struct perdsk *)a)->io_ms; register count_t bmsio = ((struct perdsk *)b)->io_ms; if (amsio < bmsio) return 1; if (amsio > bmsio) return -1; return 0; } int intfcompar(const void *a, const void *b) { register count_t afactor=0, bfactor=0; count_t aspeed = ((struct perintf *)a)->speed; count_t bspeed = ((struct perintf *)b)->speed; char aduplex = ((struct perintf *)a)->duplex; char bduplex = ((struct perintf *)b)->duplex; count_t arbyte = ((struct perintf *)a)->rbyte; count_t brbyte = ((struct perintf *)b)->rbyte; count_t asbyte = ((struct perintf *)a)->sbyte; count_t bsbyte = ((struct perintf *)b)->sbyte; /* ** if speed of first interface known, calculate busy factor */ if (aspeed) { if (aduplex) afactor = (arbyte > asbyte ? arbyte : asbyte) * 10 / aspeed; else afactor = (arbyte + asbyte) * 10 / aspeed; } /* ** if speed of second interface known, calculate busy factor */ if (bspeed) { if (bduplex) bfactor = (brbyte > bsbyte ? brbyte : bsbyte) * 10 / bspeed; else bfactor = (brbyte + bsbyte) * 10 / bspeed; } /* ** compare interfaces */ if (aspeed && bspeed) { if (afactor < bfactor) return 1; if (afactor > bfactor) return -1; return 0; } if (!aspeed && !bspeed) { if ((arbyte + asbyte) < (brbyte + bsbyte)) return 1; if ((arbyte + asbyte) > (brbyte + bsbyte)) return -1; return 0; } if (aspeed) return -1; else return 1; } int ifbcompar(const void *a, const void *b) { count_t atransfer = ((struct perifb *)a)->rcvb + ((struct perifb *)a)->sndb; count_t btransfer = ((struct perifb *)b)->rcvb + ((struct perifb *)b)->sndb; if (atransfer < btransfer) return 1; if (atransfer > btransfer) return -1; return 0; } int nfsmcompar(const void *a, const void *b) { const struct pernfsmount *na = a; const struct pernfsmount *nb = b; register count_t aused = na->bytesread + na->byteswrite + na->bytesdread + na->bytesdwrite + na->bytestotread + na->bytestotwrite + na->pagesmread + na->pagesmwrite; register count_t bused = nb->bytesread + nb->byteswrite + nb->bytesdread + nb->bytesdwrite + nb->bytestotread + nb->bytestotwrite + nb->pagesmread + nb->pagesmwrite; if (aused < bused) return 1; if (aused > bused) return -1; return 0; } int contcompar(const void *a, const void *b) { const struct percontainer *ca = a; const struct percontainer *cb = b; register count_t aused = ca->system + ca->user + ca->nice; register count_t bused = cb->system + cb->user + cb->nice; if (aused < bused) return 1; if (aused > bused) return -1; return 0; } int memnumacompar(const void *a, const void *b) { register count_t aused = ((struct mempernuma *)a)->totmem - ((struct mempernuma *)a)->freemem; register count_t bused = ((struct mempernuma *)b)->totmem - ((struct mempernuma *)b)->freemem; if (aused < bused) return 1; if (aused > bused) return -1; return 0; } int cpunumacompar(const void *a, const void *b) { register count_t aidle = ((struct cpupernuma *)a)->itime + ((struct cpupernuma *)a)->wtime; register count_t bidle = ((struct cpupernuma *)b)->itime + ((struct cpupernuma *)b)->wtime; if (aidle < bidle) return -1; if (aidle > bidle) return 1; return 0; } int llccompar(const void *a, const void *b) { const struct perllc *ca = a; const struct perllc *cb = b; register count_t aused = ca->mbm_local + ca->mbm_total; register count_t bused = cb->mbm_local + cb->mbm_total; if (aused < bused) return 1; if (aused > bused) return -1; return 0; } /* ** handle modifications from the /etc/atoprc and ~/.atoprc file */ int get_posval(char *name, char *val) { int value = atoi(val); if ( !numeric(val)) { fprintf(stderr, "atoprc: %s value %s not a (positive) numeric\n", name, val); exit(1); } if (value < 0) { fprintf(stderr, "atoprc: %s value %d not positive\n", name, value); exit(1); } return value; } static int get_perc(char *name, char *val) { int value = get_posval(name, val); if (value < 0 || value > 100) { fprintf(stderr, "atoprc: %s value %d not in range 0-100\n", name, value); exit(1); } return value; } void do_cpucritperc(char *name, char *val) { cpubadness = get_perc(name, val); } void do_gpucritperc(char *name, char *val) { gpubadness = get_perc(name, val); } void do_memcritperc(char *name, char *val) { membadness = get_perc(name, val); } void do_swpcritperc(char *name, char *val) { swpbadness = get_perc(name, val); } void do_dskcritperc(char *name, char *val) { dskbadness = get_perc(name, val); } void do_netcritperc(char *name, char *val) { netbadness = get_perc(name, val); } void do_swoutcritsec(char *name, char *val) { pagbadness = get_posval(name, val); } void do_almostcrit(char *name, char *val) { almostcrit = get_perc(name, val); } void do_ownsysprcline(char *name, char *val) { make_sys_prints(sysprcline, MAXITEMS, val, prcsyspdefs, name, NULL, NULL); } void do_ownallcpuline(char *name, char *val) { make_sys_prints(allcpuline, MAXITEMS, val, cpusyspdefs, name, NULL, NULL); } void do_ownindivcpuline(char *name, char *val) { make_sys_prints(indivcpuline, MAXITEMS, val, cpisyspdefs, name, NULL, NULL); } void do_owncplline(char *name, char *val) { make_sys_prints(cplline, MAXITEMS, val, cplsyspdefs, name, NULL, NULL); } void do_owngpuline(char *name, char *val) { make_sys_prints(gpuline, MAXITEMS, val, gpusyspdefs, name, NULL, NULL); } void do_ownmemline(char *name, char *val) { make_sys_prints(memline1, MAXITEMS, val, memsyspdefs1, name, NULL, NULL); } void do_ownswpline(char *name, char *val) { make_sys_prints(swpline, MAXITEMS, val, swpsyspdefs, name, NULL, NULL); } void do_ownpagline(char *name, char *val) { make_sys_prints(pagline, MAXITEMS, val, pagsyspdefs, name, NULL, NULL); } void do_ownmemnumaline(char *name, char *val) { make_sys_prints(memnumaline, MAXITEMS, val, memnumasyspdefs, name, NULL, NULL); } void do_owncpunumaline(char *name, char *val) { make_sys_prints(cpunumaline, MAXITEMS, val, cpunumasyspdefs, name, NULL, NULL); } void do_ownllcline(char *name, char *val) { make_sys_prints(llcline, MAXITEMS, val, llcsyspdefs, name, NULL, NULL); } void do_owndskline(char *name, char *val) { make_sys_prints(dskline, MAXITEMS, val, dsksyspdefs, name, NULL, NULL); } void do_ownnettransportline(char *name, char *val) { make_sys_prints(nettransportline, MAXITEMS, val, nettranssyspdefs, name, NULL, NULL); } void do_ownnetnetline(char *name, char *val) { make_sys_prints(netnetline, MAXITEMS, val, netnetsyspdefs, name, NULL, NULL); } void do_ownnetinterfaceline(char *name, char *val) { make_sys_prints(netinterfaceline, MAXITEMS, val, netintfsyspdefs, name, NULL, NULL); } void do_owninfinibandline(char *name, char *val) { make_sys_prints(infinibandline, MAXITEMS, val, infinisyspdefs, name, NULL, NULL); } void do_ownprocline(char *name, char *val) { make_detail_prints(ownprocs, MAXITEMS, val, name); } atop-2.12.1/showlinux.h0000644000203100020310000004561415064552761014276 0ustar gerlofgerlof/* ** ATOP - System & Process Monitor ** ** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as process-level. ** ** This source-file contains the Linux-specific functions to calculate ** figures to be visualized. ** ========================================================================== ** Author: JC van Winkel - AT Computing, Nijmegen, Holland ** E-mail: jc@ATComputing.nl ** Date: November 2009 ** -------------------------------------------------------------------------- ** Copyright (C) 2009 JC van Winkel ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ** -------------------------------------------------------------------------- */ #ifndef __SHOWLINUX__ #define __SHOWLINUX__ #define MAXITEMS 80 /* The maximum number of items per line */ /* * structure for extra parameters for system related data */ typedef struct { count_t totut; count_t totst; int nact; int nproc; int ntrun; int ntslpi; int ntslpu; int ntidle; int nzomb; int nexit; int noverflow; int avgval; int nsecs; count_t mstot; count_t iotot; struct perdsk *perdsk; int index; count_t cputot; count_t pernumacputot; count_t percputot; } extraparam; /*************************************************************** * * structure for system print list * * configname name as used to identify this field when configuring * the print line * doformat pointer to function that formats this field into a * string of 12 positions, to be returned as char pointer * dovalidate pointer to function that determines if this is a * valid (i.e. relevant) field on this system, returning * 0 (false) or non-zero (true) * when this function pointer is NULL, true is considered */ typedef struct { char *configname; char* (*doformat)(struct sstat *, extraparam *, int, int *); int (*dovalidate)(struct sstat *); } sys_printdef; /* * structure for system print-list with priority * in case of lack of screen space, lowest priority items will be * removed first */ typedef struct { sys_printdef *f; int prio; } sys_printpair; void showsysline(sys_printpair* elemptr, struct sstat* sstat, extraparam *extra, char *labeltext, unsigned int badness); /* ** structure for process and cgroup print list */ typedef struct { char *head; // column header char *configname; // name as used to config print line union { char *(*doactiveconverts)(struct tstat *, int, int); char *(*doactiveconvertc)(struct cgchainer *, struct tstat *, int, int, count_t, int, int *); } ac; // pointer to conv function // for active process char *(*doexitconvert)(struct tstat *, int, int); // pointer to conv function // for exited process char sortcrit; // sort criterion similar to showorder int width; // required width int varwidth; // width may grow (eg cmd params) } detail_printdef; typedef struct { detail_printdef *pf; int prio; } detail_printpair; void showprochead(detail_printpair *, int, int, char, char); void showprocline(detail_printpair *, struct tstat *, double, int, int); void showcgrouphead(detail_printpair *, int, int, char); void showcgroupline(detail_printpair *, struct cgchainer *, struct tstat *, int, int, count_t, int); void do_cpucritperc(char *, char *); void do_gpucritperc(char *, char *); void do_memcritperc(char *, char *); void do_swpcritperc(char *, char *); void do_dskcritperc(char *, char *); void do_netcritperc(char *, char *); void do_swoutcritsec(char *, char *); void do_almostcrit(char *, char *); void do_ownsysprcline(char *, char *); void do_ownallcpuline(char *, char *); void do_ownindivcpuline(char *, char *); void do_owncplline(char *, char *); void do_owngpuline(char *, char *); void do_ownmemline(char *, char *); void do_ownswpline(char *, char *); void do_ownpagline(char *, char *); void do_ownmemnumaline(char *, char *); void do_owncpunumaline(char *, char *); void do_ownllcline(char *, char *); void do_owndskline(char *, char *); void do_ownnettransportline(char *, char *); void do_ownnetnetline(char *, char *); void do_ownnetinterfaceline(char *, char *); void do_owninfinibandline(char *, char *); void do_ownprocline(char *, char *); int get_posval(char *, char *); extern sys_printdef *prcsyspdefs[]; extern sys_printdef *cpusyspdefs[]; extern sys_printdef *cpisyspdefs[]; extern sys_printdef *cplsyspdefs[]; extern sys_printdef *memsyspdefs1[]; extern sys_printdef *memsyspdefs2[]; extern sys_printdef *swpsyspdefs[]; extern sys_printdef *pagsyspdefs[]; extern sys_printdef *numasyspdefs[]; extern sys_printdef *numacpusyspdefs[]; extern sys_printdef *dsksyspdefs[]; extern sys_printdef *nettranssyspdefs[]; extern sys_printdef *netnetsyspdefs[]; extern sys_printdef *netintfsyspdefs[]; extern sys_printdef *infinisyspdefs[]; extern sys_printdef syspdef_PRCSYS; extern sys_printdef syspdef_PRCUSER; extern sys_printdef syspdef_PRCNPROC; extern sys_printdef syspdef_PRCNRUNNING; extern sys_printdef syspdef_PRCNSLEEPING; extern sys_printdef syspdef_PRCNDSLEEPING; extern sys_printdef syspdef_PRCNIDLE; extern sys_printdef syspdef_PRCNZOMBIE; extern sys_printdef syspdef_PRCCLONES; extern sys_printdef syspdef_PRCNNEXIT; extern sys_printdef syspdef_CPUSYS; extern sys_printdef syspdef_CPUUSER; extern sys_printdef syspdef_CPUIRQ; extern sys_printdef syspdef_CPUIDLE; extern sys_printdef syspdef_CPUWAIT; extern sys_printdef syspdef_CPUISYS; extern sys_printdef syspdef_CPUIUSER; extern sys_printdef syspdef_CPUIIRQ; extern sys_printdef syspdef_CPUIIDLE; extern sys_printdef syspdef_CPUIWAIT; extern sys_printdef syspdef_CPUISTEAL; extern sys_printdef syspdef_CPUIFREQ; extern sys_printdef syspdef_CPUFREQ; extern sys_printdef syspdef_CPUSCALE; extern sys_printdef syspdef_CPUISCALE; extern sys_printdef syspdef_CPUSTEAL; extern sys_printdef syspdef_CPUISTEAL; extern sys_printdef syspdef_CPUGUEST; extern sys_printdef syspdef_CPUIGUEST; extern sys_printdef syspdef_CPUIPC; extern sys_printdef syspdef_CPUIIPC; extern sys_printdef syspdef_CPUCYCLE; extern sys_printdef syspdef_CPUICYCLE; extern sys_printdef syspdef_CPLAVG1; extern sys_printdef syspdef_CPLAVG5; extern sys_printdef syspdef_CPLAVG15; extern sys_printdef syspdef_CPLCSW; extern sys_printdef syspdef_CPLNUMCPU; extern sys_printdef syspdef_CPLINTR; extern sys_printdef syspdef_GPUBUS; extern sys_printdef syspdef_GPUTYPE; extern sys_printdef syspdef_GPUNRPROC; extern sys_printdef syspdef_GPUMEMPERC; extern sys_printdef syspdef_GPUMEMOCC; extern sys_printdef syspdef_GPUGPUPERC; extern sys_printdef syspdef_GPUMEMTOT; extern sys_printdef syspdef_GPUMEMUSE; extern sys_printdef syspdef_GPUMEMAVG; extern sys_printdef syspdef_MEMTOT; extern sys_printdef syspdef_MEMFREE; extern sys_printdef syspdef_MEMAVAIL; extern sys_printdef syspdef_MEMCACHE; extern sys_printdef syspdef_MEMDIRTY; extern sys_printdef syspdef_MEMBUFFER; extern sys_printdef syspdef_MEMSLAB; extern sys_printdef syspdef_RECSLAB; extern sys_printdef syspdef_SHMEM; extern sys_printdef syspdef_SHMRSS; extern sys_printdef syspdef_SHMSWP; extern sys_printdef syspdef_VMWBAL; extern sys_printdef syspdef_ZFSARC; extern sys_printdef syspdef_PAGETABS; extern sys_printdef syspdef_ANONTHP; extern sys_printdef syspdef_HUPTOT; extern sys_printdef syspdef_HUPUSE; extern sys_printdef syspdef_SWPTOT; extern sys_printdef syspdef_SWPFREE; extern sys_printdef syspdef_SWPCACHE; extern sys_printdef syspdef_ZSWPOOL; extern sys_printdef syspdef_ZSWSTORED; extern sys_printdef syspdef_KSMSHARING; extern sys_printdef syspdef_KSMSHARED; extern sys_printdef syspdef_SWPCOMMITTED; extern sys_printdef syspdef_SWPCOMMITLIM; extern sys_printdef syspdef_NUMNUMA; extern sys_printdef syspdef_NUMANR; extern sys_printdef syspdef_NUMATOT; extern sys_printdef syspdef_NUMAFREE; extern sys_printdef syspdef_NUMAFILEPAGE; extern sys_printdef syspdef_NUMASLAB; extern sys_printdef syspdef_NUMADIRTY; extern sys_printdef syspdef_NUMAACTIVE; extern sys_printdef syspdef_NUMAINACTIVE; extern sys_printdef syspdef_NUMASHMEM; extern sys_printdef syspdef_NUMASLABRECLAIM; extern sys_printdef syspdef_NUMAFRAG; extern sys_printdef syspdef_NUMAHUPTOT; extern sys_printdef syspdef_NUMAHUPUSE; extern sys_printdef syspdef_NUMANUMCPU; extern sys_printdef syspdef_NUMACPUSYS; extern sys_printdef syspdef_NUMACPUUSER; extern sys_printdef syspdef_NUMACPUNICE; extern sys_printdef syspdef_NUMACPUIRQ; extern sys_printdef syspdef_NUMACPUSOFTIRQ; extern sys_printdef syspdef_NUMACPUIDLE; extern sys_printdef syspdef_NUMACPUWAIT; extern sys_printdef syspdef_NUMACPUSTEAL; extern sys_printdef syspdef_NUMACPUGUEST; extern sys_printdef syspdef_LLCMBMTOTAL; extern sys_printdef syspdef_LLCMBMLOCAL; extern sys_printdef syspdef_NUMLLC; extern sys_printdef syspdef_PAGSCAN; extern sys_printdef syspdef_PAGSTEAL; extern sys_printdef syspdef_PAGSTALL; extern sys_printdef syspdef_PAGCOMPACT; extern sys_printdef syspdef_NUMAMIGRATE; extern sys_printdef syspdef_PGMIGRATE; extern sys_printdef syspdef_PAGPGIN; extern sys_printdef syspdef_PAGPGOUT; extern sys_printdef syspdef_TCPSOCK; extern sys_printdef syspdef_UDPSOCK; extern sys_printdef syspdef_PAGSWIN; extern sys_printdef syspdef_PAGSWOUT; extern sys_printdef syspdef_PAGZSWIN; extern sys_printdef syspdef_PAGZSWOUT; extern sys_printdef syspdef_OOMKILLS; extern sys_printdef syspdef_PSICPUSTOT; extern sys_printdef syspdef_PSIMEMSTOT; extern sys_printdef syspdef_PSIMEMFTOT; extern sys_printdef syspdef_PSIIOSTOT; extern sys_printdef syspdef_PSIIOFTOT; extern sys_printdef syspdef_PSICPUS; extern sys_printdef syspdef_PSIMEMS; extern sys_printdef syspdef_PSIMEMF; extern sys_printdef syspdef_PSIIOS; extern sys_printdef syspdef_PSIIOF; extern sys_printdef syspdef_CONTNAME; extern sys_printdef syspdef_CONTNPROC; extern sys_printdef syspdef_CONTCPU; extern sys_printdef syspdef_CONTMEM; extern sys_printdef syspdef_DSKNAME; extern sys_printdef syspdef_DSKBUSY; extern sys_printdef syspdef_DSKNREAD; extern sys_printdef syspdef_DSKNWRITE; extern sys_printdef syspdef_DSKNDISC; extern sys_printdef syspdef_DSKMBPERSECRD; extern sys_printdef syspdef_DSKMBPERSECWR; extern sys_printdef syspdef_DSKKBPERRD; extern sys_printdef syspdef_DSKKBPERWR; extern sys_printdef syspdef_DSKKBPERDS; extern sys_printdef syspdef_DSKINFLIGHT; extern sys_printdef syspdef_DSKAVQUEUE; extern sys_printdef syspdef_DSKAVIO; extern sys_printdef syspdef_NETTRANSPORT; extern sys_printdef syspdef_NETTCPI; extern sys_printdef syspdef_NETTCPO; extern sys_printdef syspdef_NETTCPACTOPEN; extern sys_printdef syspdef_NETTCPPASVOPEN; extern sys_printdef syspdef_NETTCPRETRANS; extern sys_printdef syspdef_NETTCPINERR; extern sys_printdef syspdef_NETTCPORESET; extern sys_printdef syspdef_NETTCPCSUMERR; extern sys_printdef syspdef_NETUDPNOPORT; extern sys_printdef syspdef_NETUDPINERR; extern sys_printdef syspdef_NETUDPI; extern sys_printdef syspdef_NETUDPO; extern sys_printdef syspdef_NETNETWORK; extern sys_printdef syspdef_NETIPI; extern sys_printdef syspdef_NETIPO; extern sys_printdef syspdef_NETIPFRW; extern sys_printdef syspdef_NETIPDELIV; extern sys_printdef syspdef_NETICMPIN; extern sys_printdef syspdef_NETICMPOUT; extern sys_printdef syspdef_NETNAME; extern sys_printdef syspdef_NETPCKI; extern sys_printdef syspdef_NETPCKO; extern sys_printdef syspdef_NETSPEEDMAX; extern sys_printdef syspdef_NETSPEEDIN; extern sys_printdef syspdef_NETSPEEDOUT; extern sys_printdef syspdef_NETCOLLIS; extern sys_printdef syspdef_NETMULTICASTIN; extern sys_printdef syspdef_NETRCVERR; extern sys_printdef syspdef_NETSNDERR; extern sys_printdef syspdef_NETRCVDROP; extern sys_printdef syspdef_NETSNDDROP; extern sys_printdef syspdef_NFMSERVER; extern sys_printdef syspdef_NFMPATH; extern sys_printdef syspdef_NFMTOTREAD; extern sys_printdef syspdef_NFMTOTWRITE; extern sys_printdef syspdef_NFMNREAD; extern sys_printdef syspdef_NFMNWRITE; extern sys_printdef syspdef_NFMDREAD; extern sys_printdef syspdef_NFMDWRITE; extern sys_printdef syspdef_NFMMREAD; extern sys_printdef syspdef_NFMMWRITE; extern sys_printdef syspdef_NFCRPCCNT; extern sys_printdef syspdef_NFCRPCREAD; extern sys_printdef syspdef_NFCRPCWRITE; extern sys_printdef syspdef_NFCRPCRET; extern sys_printdef syspdef_NFCRPCARF; extern sys_printdef syspdef_NFSRPCCNT; extern sys_printdef syspdef_NFSRPCREAD; extern sys_printdef syspdef_NFSRPCWRITE; extern sys_printdef syspdef_NFSBADFMT; extern sys_printdef syspdef_NFSBADAUT; extern sys_printdef syspdef_NFSBADCLN; extern sys_printdef syspdef_NFSNETTCP; extern sys_printdef syspdef_NFSNETUDP; extern sys_printdef syspdef_NFSNRBYTES; extern sys_printdef syspdef_NFSNWBYTES; extern sys_printdef syspdef_NFSRCHITS; extern sys_printdef syspdef_NFSRCMISS; extern sys_printdef syspdef_NFSRCNOCA; extern sys_printdef syspdef_IFBNAME; extern sys_printdef syspdef_IFBPCKI; extern sys_printdef syspdef_IFBPCKO; extern sys_printdef syspdef_IFBSPEEDMAX; extern sys_printdef syspdef_IFBLANES; extern sys_printdef syspdef_IFBSPEEDIN; extern sys_printdef syspdef_IFBSPEEDOUT; extern sys_printdef syspdef_BLANKBOX; /* ** functions that print ???? for unavailable data */ char *procprt_NOTAVAIL_4(struct tstat *curstat, int avgval, int nsecs); char *procprt_NOTAVAIL_5(struct tstat *curstat, int avgval, int nsecs); char *procprt_NOTAVAIL_6(struct tstat *curstat, int avgval, int nsecs); char *procprt_NOTAVAIL_7(struct tstat *curstat, int avgval, int nsecs); extern detail_printdef *alldetaildefs[]; extern detail_printdef procprt_PID; extern detail_printdef procprt_TID; extern detail_printdef procprt_PPID; extern detail_printdef procprt_SYSCPU; extern detail_printdef procprt_USRCPU; extern detail_printdef procprt_VGROW; extern detail_printdef procprt_RGROW; extern detail_printdef procprt_MINFLT; extern detail_printdef procprt_MAJFLT; extern detail_printdef procprt_VSTEXT; extern detail_printdef procprt_VSIZE; extern detail_printdef procprt_RSIZE; extern detail_printdef procprt_PSIZE; extern detail_printdef procprt_VSLIBS; extern detail_printdef procprt_VDATA; extern detail_printdef procprt_VSTACK; extern detail_printdef procprt_SWAPSZ; extern detail_printdef procprt_LOCKSZ; extern detail_printdef procprt_CMD; extern detail_printdef procprt_RUID; extern detail_printdef procprt_EUID; extern detail_printdef procprt_SUID; extern detail_printdef procprt_FSUID; extern detail_printdef procprt_RGID; extern detail_printdef procprt_EGID; extern detail_printdef procprt_SGID; extern detail_printdef procprt_FSGID; extern detail_printdef procprt_CTID; extern detail_printdef procprt_VPID; extern detail_printdef procprt_CID; extern detail_printdef procprt_STDATE; extern detail_printdef procprt_STTIME; extern detail_printdef procprt_ENDATE; extern detail_printdef procprt_ENTIME; extern detail_printdef procprt_THR; extern detail_printdef procprt_TRUN; extern detail_printdef procprt_TSLPI; extern detail_printdef procprt_TSLPU; extern detail_printdef procprt_TIDLE; extern detail_printdef procprt_POLI; extern detail_printdef procprt_NICE; extern detail_printdef procprt_PRI; extern detail_printdef procprt_RTPR; extern detail_printdef procprt_CURCPU; extern detail_printdef procprt_ST; extern detail_printdef procprt_EXC; extern detail_printdef procprt_S; extern detail_printdef procprt_COMMAND_LINE; extern detail_printdef procprt_NPROCS; extern detail_printdef procprt_RDDSK; extern detail_printdef procprt_WRDSK; extern detail_printdef procprt_CWRDSK; extern detail_printdef procprt_WCANCEL; extern detail_printdef procprt_TCPRCV; extern detail_printdef procprt_TCPRASZ; extern detail_printdef procprt_TCPSND; extern detail_printdef procprt_TCPSASZ; extern detail_printdef procprt_UDPRCV; extern detail_printdef procprt_UDPRASZ; extern detail_printdef procprt_UDPSND; extern detail_printdef procprt_UDPSASZ; extern detail_printdef procprt_RNET; extern detail_printdef procprt_SNET; extern detail_printdef procprt_BANDWI; extern detail_printdef procprt_BANDWO; extern detail_printdef procprt_GPULIST; extern detail_printdef procprt_GPUMEMNOW; extern detail_printdef procprt_GPUMEMAVG; extern detail_printdef procprt_GPUGPUBUSY; extern detail_printdef procprt_GPUMEMBUSY; extern detail_printdef procprt_SORTITEM; extern detail_printdef procprt_RUNDELAY; extern detail_printdef procprt_BLKDELAY; extern detail_printdef procprt_WCHAN; extern detail_printdef procprt_NVCSW; extern detail_printdef procprt_NIVCSW; extern detail_printdef cgroupprt_CGROUP_PATH; extern detail_printdef cgroupprt_CGRNPROCS; extern detail_printdef cgroupprt_CGRNPROCSB; extern detail_printdef cgroupprt_CGRCPUBUSY; extern detail_printdef cgroupprt_CGRCPUPSI; extern detail_printdef cgroupprt_CGRCPUWGT; extern detail_printdef cgroupprt_CGRCPUMAX; extern detail_printdef cgroupprt_CGRMEMORY; extern detail_printdef cgroupprt_CGRMEMPSI; extern detail_printdef cgroupprt_CGRMEMMAX; extern detail_printdef cgroupprt_CGRSWPMAX; extern detail_printdef cgroupprt_CGRDISKIO; extern detail_printdef cgroupprt_CGRDSKPSI; extern detail_printdef cgroupprt_CGRDSKWGT; extern detail_printdef cgroupprt_CGRPID; extern detail_printdef cgroupprt_CGRCMD; extern char *procprt_SNET_a(struct tstat *, int, int); extern char *procprt_SNET_e(struct tstat *, int, int); extern char *procprt_RNET_a(struct tstat *, int, int); extern char *procprt_RNET_e(struct tstat *, int, int); extern char *procprt_TCPSND_a(struct tstat *, int, int); extern char *procprt_TCPRCV_a(struct tstat *, int, int); extern char *procprt_UDPSND_a(struct tstat *, int, int); extern char *procprt_UDPRCV_a(struct tstat *, int, int); extern char *procprt_TCPSASZ_a(struct tstat *, int, int); extern char *procprt_TCPRASZ_a(struct tstat *, int, int); extern char *procprt_UDPSASZ_a(struct tstat *, int, int); extern char *procprt_UDPRASZ_a(struct tstat *, int, int); extern char *procprt_TCPSND_e(struct tstat *, int, int); extern char *procprt_TCPRCV_e(struct tstat *, int, int); extern char *procprt_UDPSND_e(struct tstat *, int, int); extern char *procprt_UDPRCV_e(struct tstat *, int, int); extern char *procprt_TCPSASZ_e(struct tstat *, int, int); extern char *procprt_TCPRASZ_e(struct tstat *, int, int); extern char *procprt_UDPSASZ_e(struct tstat *, int, int); extern char *procprt_UDPRASZ_e(struct tstat *, int, int); #endif atop-2.12.1/showprocs.c0000644000203100020310000024120715064552761014254 0ustar gerlofgerlof/* ** ATOP - System & Process Monitor ** ** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as cgroupess-level. ** ** This source-file contains the Linux-specific functions to calculate ** figures to be visualized on process/thread level and cgroup level. ** ========================================================================== ** Author: JC van Winkel - AT Computing, Nijmegen, Holland ** E-mail: jc@ATComputing.nl ** Date: November 2009 ** ** Process level ** -------------------------------------------------------------------------- ** Author: Gerlof Langeveld ** E-mail: gerlof.langeveld@atoptool.nl ** Date: January 2024 ** ** Additions for cgroups ** -------------------------------------------------------------------------- ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ** -------------------------------------------------------------------------- */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "atop.h" #include "photoproc.h" #include "photosyst.h" #include "cgroups.h" #include "showgeneric.h" #include "showlinux.h" static void format_bandw(char *, int, count_t); static void gettotwidth(detail_printpair *, int *, int *, int *); static int *getspacings(detail_printpair *); char *procprt_TID_ae(struct tstat *, int, int); char *procprt_PID_a(struct tstat *, int, int); char *procprt_PID_e(struct tstat *, int, int); char *procprt_PPID_a(struct tstat *, int, int); char *procprt_PPID_e(struct tstat *, int, int); char *procprt_VPID_a(struct tstat *, int, int); char *procprt_VPID_e(struct tstat *, int, int); char *procprt_CTID_a(struct tstat *, int, int); char *procprt_CTID_e(struct tstat *, int, int); char *procprt_CID_a(struct tstat *, int, int); char *procprt_CID_e(struct tstat *, int, int); char *procprt_SYSCPU_ae(struct tstat *, int, int); char *procprt_USRCPU_ae(struct tstat *, int, int); char *procprt_VGROW_a(struct tstat *, int, int); char *procprt_VGROW_e(struct tstat *, int, int); char *procprt_RGROW_a(struct tstat *, int, int); char *procprt_RGROW_e(struct tstat *, int, int); char *procprt_MINFLT_ae(struct tstat *, int, int); char *procprt_MAJFLT_ae(struct tstat *, int, int); char *procprt_VSTEXT_a(struct tstat *, int, int); char *procprt_VSTEXT_e(struct tstat *, int, int); char *procprt_VSIZE_a(struct tstat *, int, int); char *procprt_VSIZE_e(struct tstat *, int, int); char *procprt_RSIZE_a(struct tstat *, int, int); char *procprt_RSIZE_e(struct tstat *, int, int); char *procprt_PSIZE_a(struct tstat *, int, int); char *procprt_PSIZE_e(struct tstat *, int, int); char *procprt_VSLIBS_a(struct tstat *, int, int); char *procprt_VSLIBS_e(struct tstat *, int, int); char *procprt_VDATA_a(struct tstat *, int, int); char *procprt_VDATA_e(struct tstat *, int, int); char *procprt_VSTACK_a(struct tstat *, int, int); char *procprt_VSTACK_e(struct tstat *, int, int); char *procprt_SWAPSZ_a(struct tstat *, int, int); char *procprt_SWAPSZ_e(struct tstat *, int, int); char *procprt_LOCKSZ_a(struct tstat *, int, int); char *procprt_LOCKSZ_e(struct tstat *, int, int); char *procprt_CMD_a(struct tstat *, int, int); char *procprt_CMD_e(struct tstat *, int, int); char *procprt_RUID_ae(struct tstat *, int, int); char *procprt_EUID_a(struct tstat *, int, int); char *procprt_EUID_e(struct tstat *, int, int); char *procprt_SUID_a(struct tstat *, int, int); char *procprt_SUID_e(struct tstat *, int, int); char *procprt_FSUID_a(struct tstat *, int, int); char *procprt_FSUID_e(struct tstat *, int, int); char *procprt_RGID_ae(struct tstat *, int, int); char *procprt_EGID_a(struct tstat *, int, int); char *procprt_EGID_e(struct tstat *, int, int); char *procprt_SGID_a(struct tstat *, int, int); char *procprt_SGID_e(struct tstat *, int, int); char *procprt_FSGID_a(struct tstat *, int, int); char *procprt_FSGID_e(struct tstat *, int, int); char *procprt_STDATE_ae(struct tstat *, int, int); char *procprt_STTIME_ae(struct tstat *, int, int); char *procprt_ENDATE_a(struct tstat *, int, int); char *procprt_ENDATE_e(struct tstat *, int, int); char *procprt_ENTIME_a(struct tstat *, int, int); char *procprt_ENTIME_e(struct tstat *, int, int); char *procprt_THR_a(struct tstat *, int, int); char *procprt_THR_e(struct tstat *, int, int); char *procprt_TRUN_a(struct tstat *, int, int); char *procprt_TRUN_e(struct tstat *, int, int); char *procprt_TSLPI_a(struct tstat *, int, int); char *procprt_TSLPI_e(struct tstat *, int, int); char *procprt_TSLPU_a(struct tstat *, int, int); char *procprt_TSLPU_e(struct tstat *, int, int); char *procprt_TIDLE_a(struct tstat *, int, int); char *procprt_TIDLE_e(struct tstat *, int, int); char *procprt_POLI_a(struct tstat *, int, int); char *procprt_POLI_e(struct tstat *, int, int); char *procprt_NICE_a(struct tstat *, int, int); char *procprt_NICE_e(struct tstat *, int, int); char *procprt_PRI_a(struct tstat *, int, int); char *procprt_PRI_e(struct tstat *, int, int); char *procprt_RTPR_a(struct tstat *, int, int); char *procprt_RTPR_e(struct tstat *, int, int); char *procprt_CURCPU_a(struct tstat *, int, int); char *procprt_CURCPU_e(struct tstat *, int, int); char *procprt_ST_a(struct tstat *, int, int); char *procprt_ST_e(struct tstat *, int, int); char *procprt_EXC_a(struct tstat *, int, int); char *procprt_EXC_e(struct tstat *, int, int); char *procprt_S_a(struct tstat *, int, int); char *procprt_S_e(struct tstat *, int, int); char *procprt_COMMAND_LINE_ae(struct tstat *, int, int); char *procprt_NPROCS_ae(struct tstat *, int, int); char *procprt_RDDSK_a(struct tstat *, int, int); char *procprt_RDDSK_e(struct tstat *, int, int); char *procprt_WRDSK_a(struct tstat *, int, int); char *procprt_WRDSK_e(struct tstat *, int, int); char *procprt_CWRDSK_a(struct tstat *, int, int); char *procprt_WCANCEL_a(struct tstat *, int, int); char *procprt_WCANCEL_e(struct tstat *, int, int); char *procprt_BANDWI_a(struct tstat *, int, int); char *procprt_BANDWI_e(struct tstat *, int, int); char *procprt_BANDWO_a(struct tstat *, int, int); char *procprt_BANDWO_e(struct tstat *, int, int); char *procprt_GPULIST_ae(struct tstat *, int, int); char *procprt_GPUMEMNOW_ae(struct tstat *, int, int); char *procprt_GPUMEMAVG_ae(struct tstat *, int, int); char *procprt_GPUGPUBUSY_ae(struct tstat *, int, int); char *procprt_GPUMEMBUSY_ae(struct tstat *, int, int); char *procprt_WCHAN_a(struct tstat *, int, int); char *procprt_WCHAN_e(struct tstat *, int, int); char *procprt_RUNDELAY_a(struct tstat *, int, int); char *procprt_RUNDELAY_e(struct tstat *, int, int); char *procprt_BLKDELAY_a(struct tstat *, int, int); char *procprt_BLKDELAY_e(struct tstat *, int, int); char *procprt_NVCSW_a(struct tstat *, int, int); char *procprt_NVCSW_e(struct tstat *, int, int); char *procprt_NIVCSW_a(struct tstat *, int, int); char *procprt_NIVCSW_e(struct tstat *, int, int); char *procprt_SORTITEM_ae(struct tstat *, int, int); char *cgroup_CGROUP_PATH(struct cgchainer *, struct tstat *, int, int, count_t, int, int *); char *cgroup_CGRNPROCS(struct cgchainer *, struct tstat *, int, int, count_t, int, int *); char *cgroup_CGRNPROCSB(struct cgchainer *, struct tstat *, int, int, count_t, int, int *); char *cgroup_CGRCPUBUSY(struct cgchainer *, struct tstat *, int, int, count_t, int, int *); char *cgroup_CGRCPUPSI(struct cgchainer *, struct tstat *, int, int, count_t, int, int *); char *cgroup_CGRCPUWGT(struct cgchainer *, struct tstat *, int, int, count_t, int, int *); char *cgroup_CGRCPUMAX(struct cgchainer *, struct tstat *, int, int, count_t, int, int *); char *cgroup_CGRMEMORY(struct cgchainer *, struct tstat *, int, int, count_t, int, int *); char *cgroup_CGRMEMPSI(struct cgchainer *, struct tstat *, int, int, count_t, int, int *); char *cgroup_CGRMEMMAX(struct cgchainer *, struct tstat *, int, int, count_t, int, int *); char *cgroup_CGRSWPMAX(struct cgchainer *, struct tstat *, int, int, count_t, int, int *); char *cgroup_CGRDISKIO(struct cgchainer *, struct tstat *, int, int, count_t, int, int *); char *cgroup_CGRDSKPSI(struct cgchainer *, struct tstat *, int, int, count_t, int, int *); char *cgroup_CGRDSKWGT(struct cgchainer *, struct tstat *, int, int, count_t, int, int *); char *cgroup_CGRPID(struct cgchainer *, struct tstat *, int, int, count_t, int, int *); char *cgroup_CGRCMD(struct cgchainer *, struct tstat *, int, int, count_t, int, int *); static char *columnhead[] = { [MSORTCPU]= "ACPU", [MSORTMEM]= "AMEM", [MSORTDSK]= "ADSK", [MSORTNET]= "ANET", [MSORTGPU]= "AGPU", }; /***************************************************************/ static int *colspacings; // ugly static var, // but saves a lot of recomputations // points to table with intercolumn // spacings static detail_printpair newelems[MAXITEMS]; // ugly static var, // but saves a lot of recomputations // contains the actual list of items to // be printed /***************************************************************/ /* * gettotwidth: calculate the sum of widths and number of columns * Also copys the detail_printpair elements to the static array newelems * for later removal of lower priority elements. * Params: * elemptr: the array of what to print * nitems: (ref) returns the number of printitems in the array * sumwidth: (ref) returns the total width of the printitems in the array * varwidth: (ref) returns the number of variable width items in the array */ static void gettotwidth(detail_printpair *elemptr, int *nitems, int *sumwidth, int* varwidth) { int i; int col; int varw=0; for (i=0, col=0; elemptr[i].pf!=0; ++i) { col += (elemptr[i].pf->varwidth ? 0 : elemptr[i].pf->width); varw += elemptr[i].pf->varwidth; newelems[i]=elemptr[i]; // copy element } newelems[i].pf=0; *nitems=i; *sumwidth=col; *varwidth=varw; } /***************************************************************/ /* * getspacings: determine how much extra space there is for * inter-column space. * returns an int array this number of spaces to add after each column * also removes items from the newelems array if the available width * is lower than what is needed. The lowest priority columns are * removed first. * * Note: this function is only to be called when screen is true. */ static int * getspacings(detail_printpair *elemptr) { static int spacings[MAXITEMS]; int col=0; int nitems; int varwidth=0; int j; int maxw=screen ? COLS : linelen; // for non screen: 80 columns max // get width etc; copy elemptr array to static newelms gettotwidth(elemptr, &nitems, &col, &varwidth); /* cases: * 1) nitems==1: just one column, no spacing needed. Done * * 2) total width is more than COLS: remove low prio columns * 2a) a varwidth column: no spacing needed * 2b) total width is less than COLS: compute inter spacing */ if (nitems==1) // no inter column spacing if 1 column { spacings[0]=0; return spacings; } // Check if available width is less than required. // If so, delete columns to make things fit // space required: // width + (nitems-1) * 1 space + 12 for a varwidth column. while (col + nitems-1+ 12*varwidth > maxw) { int lowestprio=999999; int lowestprio_index=-1; int i; for (i=0; iwidth; varwidth -= newelems[lowestprio_index].pf->varwidth; memmove(newelems+lowestprio_index, newelems+lowestprio_index+1, (nitems-lowestprio_index)* sizeof(detail_printpair)); // also copies final 0 entry nitems--; } /* if there is a var width column, handle that separately */ if (varwidth) { for (j=0; jvarwidth) { elemptr[j].pf->width=maxw-col-(nitems-1); // only nitems-1 in-between spaces // needed } } return spacings; } // avoid division by 0 if (nitems==1) { spacings[0]=0; return spacings; } /* fixed columns, spread whitespace over columns */ double over=(0.0+maxw-col)/(nitems-1); double todo=over; for (j=0; jhead == 0) // empty header==special: SORTITEM { snprintf(buf, sizeof buf, "%*s", procprt_SORTITEM.width, autosort ? columnhead[order] : columnhead[order]+1); chead = buf; } else { chead = curelem.pf->head; } if (screen) { // print sort criterium column? then switch on color // if (curelem.pf->head == 0) { if (usecolors) attron(COLOR_PAIR(FGCOLORINFO)); else attron(A_BOLD); } // print column header // printg("%-*s", curelem.pf->width, chead); // print sort criterium column? then switch off color // if (curelem.pf->head == 0) { if (usecolors) attroff(COLOR_PAIR(FGCOLORINFO)); else attroff(A_BOLD); } printg("%*s", colspacings[n], ""); } else { printg("%s ", chead); } elemptr++; n++; } if (screen) // add page number, eat from last header if needed... { pagindiclen = snprintf(pagindic, sizeof pagindic, "%d/%d", curlist, totlist); move(curline, COLS-pagindiclen); printg("%s", pagindic); } else { printg("\n"); } } /***************************************************************/ /* * showprocline: show line for processes. * if in interactive mode, columns are aligned to fill out rows * params: * elemptr: pointer to array of print definition structs ptrs * curstat: the process to print * perc: the sort order used * nsecs: number of seconds elapsed between previous and this sample * avgval: is averaging out per second needed? */ void showprocline(detail_printpair* elemptr, struct tstat *curstat, double perc, int nsecs, int avgval) { detail_printpair curelem; elemptr=newelems; // point to static array int n=0; if (screen && threadview) { if (usecolors && !curstat->gen.isproc) { attron(COLOR_PAIR(FGCOLORTHR)); } else { if (!usecolors && curstat->gen.isproc) attron(A_BOLD); } } while ((curelem=*elemptr).pf!=0) { // what to print? SORTITEM, or active process or // exited process? if (curelem.pf->head==0) // empty string=sortitem { printg("%*.0lf%%", procprt_SORTITEM.width-1, perc); } else if (curstat->gen.state != 'E') // active process { printg("%s", curelem.pf->ac.doactiveconverts(curstat, avgval, nsecs)); } else // exited process { printg("%s", curelem.pf->doexitconvert(curstat, avgval, nsecs)); } if (screen) { printg("%*s",colspacings[n], ""); } else { printg(" "); } elemptr++; n++; } if (screen && threadview) { if (usecolors && !curstat->gen.isproc) { attroff(COLOR_PAIR(FGCOLORTHR)); } else { if (!usecolors && curstat->gen.isproc) attroff(A_BOLD); } } if (!screen) { printg("\n"); } } /***************************************************************/ /* Generic functions to translate UID or GID number into a */ /* string of 8 characters, to be stored in strbuf with a */ /* of maximum 9 bytes. */ /* When no translation is wanted (-I flag) or when the */ /* name is longer than 8, a string representation of the */ /* UID or GID number is returned. */ /***************************************************************/ static void uid2str(uid_t uid, char *strbuf) { char *username; if (!idnamesuppress && (username = uid2name(uid)) && strlen(username) <= 8) snprintf(strbuf, 9, "%-8.8s", username); else snprintf(strbuf, 9, "%-8d", uid); } static void gid2str(gid_t gid, char *strbuf) { char *grpname; if (!idnamesuppress && (grpname = gid2name(gid)) && strlen(grpname) <= 8) snprintf(strbuf, 9, "%-8.8s", grpname); else snprintf(strbuf, 9, "%-8d", gid); } /*******************************************************************/ /* PROCESS PRINT FUNCTIONS */ /***************************************************************/ char * procprt_NOTAVAIL_4(struct tstat *curstat, int avgval, int nsecs) { return " ?"; } char * procprt_NOTAVAIL_5(struct tstat *curstat, int avgval, int nsecs) { return " ?"; } char * procprt_NOTAVAIL_6(struct tstat *curstat, int avgval, int nsecs) { return " ?"; } char * procprt_NOTAVAIL_7(struct tstat *curstat, int avgval, int nsecs) { return " ?"; } /***************************************************************/ char * procprt_TID_ae(struct tstat *curstat, int avgval, int nsecs) { static char buf[64]; if (curstat->gen.isproc) snprintf(buf, sizeof buf, "%*s", procprt_TID.width, "-"); else snprintf(buf, sizeof buf, "%*d", procprt_TID.width, curstat->gen.pid); return buf; } detail_printdef procprt_TID = { "TID", "TID", .ac.doactiveconverts = procprt_TID_ae, procprt_TID_ae, ' ', 5}; //DYNAMIC WIDTH! /***************************************************************/ char * procprt_PID_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[64]; snprintf(buf, sizeof buf, "%*d", procprt_PID.width, curstat->gen.tgid); return buf; } char * procprt_PID_e(struct tstat *curstat, int avgval, int nsecs) { static char buf[64]; if (curstat->gen.pid == 0) snprintf(buf, sizeof buf, "%*s", procprt_PID.width, "?"); else snprintf(buf, sizeof buf, "%*d", procprt_PID.width, curstat->gen.tgid); return buf; } detail_printdef procprt_PID = { "PID", "PID", .ac.doactiveconverts = procprt_PID_a, procprt_PID_e, ' ', 5}; //DYNAMIC WIDTH! /***************************************************************/ char * procprt_PPID_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[64]; snprintf(buf, sizeof buf, "%*d", procprt_PPID.width, curstat->gen.ppid); return buf; } char * procprt_PPID_e(struct tstat *curstat, int avgval, int nsecs) { static char buf[64]; if (curstat->gen.ppid) snprintf(buf, sizeof buf, "%*d", procprt_PPID.width, curstat->gen.ppid); else snprintf(buf, sizeof buf, "%*s", procprt_PPID.width, "-"); return buf; } detail_printdef procprt_PPID = { "PPID", "PPID", .ac.doactiveconverts = procprt_PPID_a, procprt_PPID_e, ' ', 5}; //DYNAMIC WIDTH! /***************************************************************/ char * procprt_VPID_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[64]; snprintf(buf, sizeof buf, "%*d", procprt_VPID.width, curstat->gen.vpid); return buf; } char * procprt_VPID_e(struct tstat *curstat, int avgval, int nsecs) { static char buf[64]; snprintf(buf, sizeof buf, "%*s", procprt_VPID.width, "-"); return buf; } detail_printdef procprt_VPID = { "VPID", "VPID", .ac.doactiveconverts = procprt_VPID_a, procprt_VPID_e, ' ', 5}; //DYNAMIC WIDTH! /***************************************************************/ char * procprt_CTID_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[32]; snprintf(buf, sizeof buf, "%5d", curstat->gen.ctid); return buf; } char * procprt_CTID_e(struct tstat *curstat, int avgval, int nsecs) { return " -"; } detail_printdef procprt_CTID = { " CTID", "CTID", .ac.doactiveconverts = procprt_CTID_a, procprt_CTID_e, ' ', 5}; /***************************************************************/ char * procprt_CID_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[64]; if (curstat->gen.utsname[0]) snprintf(buf, sizeof buf, "%-15s", curstat->gen.utsname); else snprintf(buf, sizeof buf, "%-15s", "host-----------"); return buf; } char * procprt_CID_e(struct tstat *curstat, int avgval, int nsecs) { static char buf[64]; if (curstat->gen.utsname[0]) snprintf(buf, sizeof buf, "%-15s", curstat->gen.utsname); else snprintf(buf, sizeof buf, "%-15s", "?"); return buf; } detail_printdef procprt_CID = { "CID/POD ", "CID", .ac.doactiveconverts = procprt_CID_a, procprt_CID_e, ' ', 15}; /***************************************************************/ char * procprt_SYSCPU_ae(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; val2cpustr(curstat->cpu.stime*1000/hertz, buf); return buf; } detail_printdef procprt_SYSCPU = { "SYSCPU", "SYSCPU", .ac.doactiveconverts = procprt_SYSCPU_ae, procprt_SYSCPU_ae, ' ', 6}; /***************************************************************/ char * procprt_USRCPU_ae(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; val2cpustr(curstat->cpu.utime*1000/hertz, buf); return buf; } detail_printdef procprt_USRCPU = { "USRCPU", "USRCPU", .ac.doactiveconverts = procprt_USRCPU_ae, procprt_USRCPU_ae, ' ', 6}; /***************************************************************/ char * procprt_VGROW_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; val2memstr(curstat->mem.vgrow*1024, buf, BFORMAT, 0, 0); return buf; } char * procprt_VGROW_e(struct tstat *curstat, int avgval, int nsecs) { return " 0K"; } detail_printdef procprt_VGROW = { " VGROW", "VGROW", .ac.doactiveconverts = procprt_VGROW_a, procprt_VGROW_e, ' ', 6}; /***************************************************************/ char * procprt_RGROW_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; val2memstr(curstat->mem.rgrow*1024, buf, BFORMAT, 0, 0); return buf; } char * procprt_RGROW_e(struct tstat *curstat, int avgval, int nsecs) { return " 0K"; } detail_printdef procprt_RGROW = { " RGROW", "RGROW", .ac.doactiveconverts = procprt_RGROW_a, procprt_RGROW_e, ' ', 6}; /***************************************************************/ char * procprt_MINFLT_ae(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; val2valstr(curstat->mem.minflt, buf, 6, avgval, nsecs); return buf; } detail_printdef procprt_MINFLT = { "MINFLT", "MINFLT", .ac.doactiveconverts = procprt_MINFLT_ae, procprt_MINFLT_ae, ' ', 6}; /***************************************************************/ char * procprt_MAJFLT_ae(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; val2valstr(curstat->mem.majflt, buf, 6, avgval, nsecs); return buf; } detail_printdef procprt_MAJFLT = { "MAJFLT", "MAJFLT", .ac.doactiveconverts = procprt_MAJFLT_ae, procprt_MAJFLT_ae, ' ', 6}; /***************************************************************/ char * procprt_VSTEXT_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; val2memstr(curstat->mem.vexec*1024, buf, BFORMAT, 0, 0); return buf; } char * procprt_VSTEXT_e(struct tstat *curstat, int avgval, int nsecs) { return " 0K"; } detail_printdef procprt_VSTEXT = { "VSTEXT", "VSTEXT", .ac.doactiveconverts = procprt_VSTEXT_a, procprt_VSTEXT_e, ' ', 6}; /***************************************************************/ char * procprt_VSIZE_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; val2memstr(curstat->mem.vmem*1024, buf, BFORMAT, 0, 0); return buf; } char * procprt_VSIZE_e(struct tstat *curstat, int avgval, int nsecs) { return " 0K"; } detail_printdef procprt_VSIZE = { " VSIZE", "VSIZE", .ac.doactiveconverts = procprt_VSIZE_a, procprt_VSIZE_e, ' ', 6}; /***************************************************************/ char * procprt_RSIZE_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; val2memstr(curstat->mem.rmem*1024, buf, BFORMAT, 0, 0); return buf; } char * procprt_RSIZE_e(struct tstat *curstat, int avgval, int nsecs) { return " 0K"; } detail_printdef procprt_RSIZE = { " RSIZE", "RSIZE", .ac.doactiveconverts = procprt_RSIZE_a, procprt_RSIZE_e, ' ', 6}; /***************************************************************/ char * procprt_PSIZE_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; if (curstat->mem.pmem == (unsigned long long)-1LL) return " ?K"; val2memstr(curstat->mem.pmem*1024, buf, BFORMAT, 0, 0); return buf; } char * procprt_PSIZE_e(struct tstat *curstat, int avgval, int nsecs) { return " 0K"; } detail_printdef procprt_PSIZE = { " PSIZE", "PSIZE", .ac.doactiveconverts = procprt_PSIZE_a, procprt_PSIZE_e, ' ', 6}; /***************************************************************/ char * procprt_VSLIBS_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; val2memstr(curstat->mem.vlibs*1024, buf, BFORMAT, 0, 0); return buf; } char * procprt_VSLIBS_e(struct tstat *curstat, int avgval, int nsecs) { return " 0K"; } detail_printdef procprt_VSLIBS = { "VSLIBS", "VSLIBS", .ac.doactiveconverts = procprt_VSLIBS_a, procprt_VSLIBS_e, ' ', 6}; /***************************************************************/ char * procprt_VDATA_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; val2memstr(curstat->mem.vdata*1024, buf, BFORMAT, 0, 0); return buf; } char * procprt_VDATA_e(struct tstat *curstat, int avgval, int nsecs) { return " 0K"; } detail_printdef procprt_VDATA = { " VDATA", "VDATA", .ac.doactiveconverts = procprt_VDATA_a, procprt_VDATA_e, ' ', 6}; /***************************************************************/ char * procprt_VSTACK_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; val2memstr(curstat->mem.vstack*1024, buf, BFORMAT, 0, 0); return buf; } char * procprt_VSTACK_e(struct tstat *curstat, int avgval, int nsecs) { return " 0K"; } detail_printdef procprt_VSTACK = { "VSTACK", "VSTACK", .ac.doactiveconverts = procprt_VSTACK_a, procprt_VSTACK_e, ' ', 6}; /***************************************************************/ char * procprt_SWAPSZ_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; val2memstr(curstat->mem.vswap*1024, buf, BFORMAT, 0, 0); return buf; } char * procprt_SWAPSZ_e(struct tstat *curstat, int avgval, int nsecs) { return " 0K"; } detail_printdef procprt_SWAPSZ = { "SWAPSZ", "SWAPSZ", .ac.doactiveconverts = procprt_SWAPSZ_a, procprt_SWAPSZ_e, ' ', 6}; /***************************************************************/ char * procprt_LOCKSZ_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; val2memstr(curstat->mem.vlock*1024, buf, KBFORMAT, 0, 0); return buf; } char * procprt_LOCKSZ_e(struct tstat *curstat, int avgval, int nsecs) { return " 0K"; } detail_printdef procprt_LOCKSZ = { "LOCKSZ", "LOCKSZ", .ac.doactiveconverts = procprt_LOCKSZ_a, procprt_LOCKSZ_e, ' ', 6}; /***************************************************************/ char * procprt_CMD_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[15]; snprintf(buf, sizeof buf, "%-14.14s", curstat->gen.name); return buf; } char * procprt_CMD_e(struct tstat *curstat, int avgval, int nsecs) { static char buf[15]="<"; char helpbuf[15]; snprintf(helpbuf, sizeof helpbuf, "<%.12s>", curstat->gen.name); snprintf(buf, sizeof buf, "%-14.14s", helpbuf); return buf; } detail_printdef procprt_CMD = { "CMD ", "CMD", .ac.doactiveconverts = procprt_CMD_a, procprt_CMD_e, ' ', 14}; /***************************************************************/ char * procprt_RUID_ae(struct tstat *curstat, int avgval, int nsecs) { static char buf[9]; uid2str(curstat->gen.ruid, buf); return buf; } detail_printdef procprt_RUID = { "RUID ", "RUID", .ac.doactiveconverts = procprt_RUID_ae, procprt_RUID_ae, ' ', 8}; /***************************************************************/ char * procprt_EUID_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[9]; uid2str(curstat->gen.euid, buf); return buf; } char * procprt_EUID_e(struct tstat *curstat, int avgval, int nsecs) { return "- "; } detail_printdef procprt_EUID = { "EUID ", "EUID", .ac.doactiveconverts = procprt_EUID_a, procprt_EUID_e, ' ', 8}; /***************************************************************/ char * procprt_SUID_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[9]; uid2str(curstat->gen.suid, buf); return buf; } char * procprt_SUID_e(struct tstat *curstat, int avgval, int nsecs) { return "- "; } detail_printdef procprt_SUID = { "SUID ", "SUID", .ac.doactiveconverts = procprt_SUID_a, procprt_SUID_e, ' ', 8}; /***************************************************************/ char * procprt_FSUID_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[9]; uid2str(curstat->gen.fsuid, buf); return buf; } char * procprt_FSUID_e(struct tstat *curstat, int avgval, int nsecs) { return "- "; } detail_printdef procprt_FSUID = { "FSUID ", "FSUID", .ac.doactiveconverts = procprt_FSUID_a, procprt_FSUID_e, ' ', 8}; /***************************************************************/ char * procprt_RGID_ae(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; gid2str(curstat->gen.rgid, buf); return buf; } detail_printdef procprt_RGID = { "RGID ", "RGID", .ac.doactiveconverts = procprt_RGID_ae, procprt_RGID_ae, ' ', 8}; /***************************************************************/ char * procprt_EGID_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; gid2str(curstat->gen.egid, buf); return buf; } char * procprt_EGID_e(struct tstat *curstat, int avgval, int nsecs) { return "- "; } detail_printdef procprt_EGID = { "EGID ", "EGID", .ac.doactiveconverts = procprt_EGID_a, procprt_EGID_e, ' ', 8}; /***************************************************************/ char * procprt_SGID_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; gid2str(curstat->gen.sgid, buf); return buf; } char * procprt_SGID_e(struct tstat *curstat, int avgval, int nsecs) { return "- "; } detail_printdef procprt_SGID = { "SGID ", "SGID", .ac.doactiveconverts = procprt_SGID_a, procprt_SGID_e, ' ', 8}; /***************************************************************/ char * procprt_FSGID_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; gid2str(curstat->gen.fsgid, buf); return buf; } char * procprt_FSGID_e(struct tstat *curstat, int avgval, int nsecs) { return "- "; } detail_printdef procprt_FSGID = { "FSGID ", "FSGID", .ac.doactiveconverts = procprt_FSGID_a, procprt_FSGID_e, ' ', 8}; /***************************************************************/ char * procprt_STDATE_ae(struct tstat *curstat, int avgval, int nsecs) { static char buf[11]; convdate(curstat->gen.btime, buf); return buf; } detail_printdef procprt_STDATE = { " STDATE ", "STDATE", .ac.doactiveconverts = procprt_STDATE_ae, procprt_STDATE_ae, ' ', 10}; /***************************************************************/ char * procprt_STTIME_ae(struct tstat *curstat, int avgval, int nsecs) { static char buf[9]; convtime(curstat->gen.btime, buf); return buf; } detail_printdef procprt_STTIME = { " STTIME ", "STTIME", .ac.doactiveconverts = procprt_STTIME_ae, procprt_STTIME_ae, ' ', 8}; /***************************************************************/ char * procprt_ENDATE_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[11]; strcpy(buf, " active "); return buf; } char * procprt_ENDATE_e(struct tstat *curstat, int avgval, int nsecs) { static char buf[11]; convdate(curstat->gen.btime + curstat->gen.elaps/hertz, buf); return buf; } detail_printdef procprt_ENDATE = { " ENDATE ", "ENDATE", .ac.doactiveconverts = procprt_ENDATE_a, procprt_ENDATE_e, ' ', 10}; /***************************************************************/ char * procprt_ENTIME_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[9]; strcpy(buf, " active "); return buf; } char * procprt_ENTIME_e(struct tstat *curstat, int avgval, int nsecs) { static char buf[9]; convtime(curstat->gen.btime + curstat->gen.elaps/hertz, buf); return buf; } detail_printdef procprt_ENTIME = { " ENTIME ", "ENTIME", .ac.doactiveconverts = procprt_ENTIME_a, procprt_ENTIME_e, ' ', 8}; /***************************************************************/ char * procprt_THR_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[15]; snprintf(buf, sizeof buf, "%4d", curstat->gen.nthr); return buf; } char * procprt_THR_e(struct tstat *curstat, int avgval, int nsecs) { return " 0"; } detail_printdef procprt_THR = { " THR", "THR", .ac.doactiveconverts = procprt_THR_a, procprt_THR_e, ' ', 4}; /***************************************************************/ char * procprt_TRUN_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[15]; snprintf(buf, sizeof buf, "%4d", curstat->gen.nthrrun); return buf; } char * procprt_TRUN_e(struct tstat *curstat, int avgval, int nsecs) { return " 0"; } detail_printdef procprt_TRUN = { "TRUN", "TRUN", .ac.doactiveconverts = procprt_TRUN_a, procprt_TRUN_e, ' ', 4}; /***************************************************************/ char * procprt_TSLPI_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[15]; snprintf(buf, sizeof buf, "%5d", curstat->gen.nthrslpi); return buf; } char * procprt_TSLPI_e(struct tstat *curstat, int avgval, int nsecs) { return " 0"; } detail_printdef procprt_TSLPI = { "TSLPI", "TSLPI", .ac.doactiveconverts = procprt_TSLPI_a, procprt_TSLPI_e, ' ', 5}; /***************************************************************/ char * procprt_TSLPU_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[15]; snprintf(buf, sizeof buf, "%5d", curstat->gen.nthrslpu); return buf; } char * procprt_TSLPU_e(struct tstat *curstat, int avgval, int nsecs) { return " 0"; } detail_printdef procprt_TSLPU = { "TSLPU", "TSLPU", .ac.doactiveconverts = procprt_TSLPU_a, procprt_TSLPU_e, ' ', 5}; /***************************************************************/ char * procprt_TIDLE_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[15]; snprintf(buf, sizeof buf, "%5d", curstat->gen.nthridle); return buf; } char * procprt_TIDLE_e(struct tstat *curstat, int avgval, int nsecs) { return " 0"; } detail_printdef procprt_TIDLE = { "TIDLE", "TIDLE", .ac.doactiveconverts = procprt_TIDLE_a, procprt_TIDLE_e, ' ', 5}; /***************************************************************/ #define SCHED_NORMAL 0 #define SCHED_FIFO 1 #define SCHED_RR 2 #define SCHED_BATCH 3 #define SCHED_ISO 4 #define SCHED_IDLE 5 #define SCHED_DEADLINE 6 char * procprt_POLI_a(struct tstat *curstat, int avgval, int nsecs) { switch (curstat->cpu.policy) { case SCHED_NORMAL: return "norm"; break; case SCHED_FIFO: return "fifo"; break; case SCHED_RR: return "rr "; break; case SCHED_BATCH: return "btch"; break; case SCHED_ISO: return "iso "; break; case SCHED_IDLE: return "idle"; break; case SCHED_DEADLINE: return "dead"; break; } return "? "; } char * procprt_POLI_e(struct tstat *curstat, int avgval, int nsecs) { return "- "; } detail_printdef procprt_POLI = { "POLI", "POLI", .ac.doactiveconverts = procprt_POLI_a, procprt_POLI_e, ' ', 4}; /***************************************************************/ char * procprt_NICE_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[15]; snprintf(buf, sizeof buf, "%4d", curstat->cpu.nice); return buf; } char * procprt_NICE_e(struct tstat *curstat, int avgval, int nsecs) { return " -"; } detail_printdef procprt_NICE = { "NICE", "NICE", .ac.doactiveconverts = procprt_NICE_a, procprt_NICE_e, ' ', 4}; /***************************************************************/ char * procprt_PRI_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[15]; snprintf(buf, sizeof buf, "%3d", curstat->cpu.prio); return buf; } char * procprt_PRI_e(struct tstat *curstat, int avgval, int nsecs) { return " -"; } detail_printdef procprt_PRI = { "PRI", "PRI", .ac.doactiveconverts = procprt_PRI_a, procprt_PRI_e, ' ', 3}; /***************************************************************/ char * procprt_RTPR_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[15]; snprintf(buf, sizeof buf, "%4d", curstat->cpu.rtprio); return buf; } char * procprt_RTPR_e(struct tstat *curstat, int avgval, int nsecs) { return " -"; } detail_printdef procprt_RTPR = { "RTPR", "RTPR", .ac.doactiveconverts = procprt_RTPR_a, procprt_RTPR_e, ' ', 4}; /***************************************************************/ char * procprt_CURCPU_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[15]; snprintf(buf, sizeof buf, "%5d", curstat->cpu.curcpu); return buf; } char * procprt_CURCPU_e(struct tstat *curstat, int avgval, int nsecs) { return " -"; } detail_printdef procprt_CURCPU = { "CPUNR", "CPUNR", .ac.doactiveconverts = procprt_CURCPU_a, procprt_CURCPU_e, ' ', 5}; /***************************************************************/ char * procprt_ST_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[3]="--"; if (curstat->gen.excode & ~(INT_MAX)) { buf[0]='N'; } else { buf[0]='-'; } return buf; } char * procprt_ST_e(struct tstat *curstat, int avgval, int nsecs) { static char buf[3]; if (curstat->gen.excode & ~(INT_MAX)) { buf[0]='N'; } else { buf[0]='-'; } if (curstat->gen.excode & 0xff) { if (curstat->gen.excode & 0x80) buf[1] = 'C'; else buf[1] = 'S'; } else { buf[1] = 'E'; } return buf; } detail_printdef procprt_ST = { "ST", "ST", .ac.doactiveconverts = procprt_ST_a, procprt_ST_e, ' ', 2}; /***************************************************************/ char * procprt_EXC_a(struct tstat *curstat, int avgval, int nsecs) { return " -"; } char * procprt_EXC_e(struct tstat *curstat, int avgval, int nsecs) { static char buf[4]; snprintf(buf, sizeof buf, "%3d", curstat->gen.excode & 0xff ? curstat->gen.excode & 0x7f : (curstat->gen.excode>>8) & 0xff); return buf; } detail_printdef procprt_EXC = { "EXC", "EXC", .ac.doactiveconverts = procprt_EXC_a, procprt_EXC_e, ' ', 3}; /***************************************************************/ char * procprt_S_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[2]="E"; buf[0]=curstat->gen.state; return buf; } char * procprt_S_e(struct tstat *curstat, int avgval, int nsecs) { return "E"; } detail_printdef procprt_S = { "S", "S", .ac.doactiveconverts = procprt_S_a, procprt_S_e, ' ', 1}; /***************************************************************/ char * procprt_COMMAND_LINE_ae(struct tstat *curstat, int avgval, int nsecs) { extern detail_printdef procprt_COMMAND_LINE; extern int startoffset; // influenced by -> and <- keys static char buf[CMDLEN+1]; char *pline = curstat->gen.cmdline[0] ? curstat->gen.cmdline : curstat->gen.name; int curwidth = procprt_COMMAND_LINE.width <= CMDLEN ? procprt_COMMAND_LINE.width : CMDLEN; int cmdlen = strlen(pline); int curoffset = startoffset <= cmdlen ? startoffset : cmdlen; if (screen) snprintf(buf, sizeof buf, "%-*.*s", curwidth, curwidth, pline+curoffset); else snprintf(buf, sizeof buf, "%.*s", CMDLEN, pline+curoffset); return buf; } detail_printdef procprt_COMMAND_LINE = { "COMMAND-LINE (horizontal scroll with <- and -> keys)", "COMMAND-LINE", .ac.doactiveconverts = procprt_COMMAND_LINE_ae, procprt_COMMAND_LINE_ae, ' ', 0, 1}; /***************************************************************/ char * procprt_NPROCS_ae(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; val2valstr(curstat->gen.pid, buf, 6, 0, 0); // pid abused as proc counter return buf; } detail_printdef procprt_NPROCS = { "NPROCS", "NPROCS", .ac.doactiveconverts = procprt_NPROCS_ae, procprt_NPROCS_ae, ' ', 6}; /***************************************************************/ char * procprt_RDDSK_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; if (supportflags & IOSTAT) val2memstr(curstat->dsk.rsz*512, buf, BFORMAT, avgval, nsecs); else strcpy(buf, "nopriv"); return buf; } char * procprt_RDDSK_e(struct tstat *curstat, int avgval, int nsecs) { return " -"; } detail_printdef procprt_RDDSK = { " RDDSK", "RDDSK", .ac.doactiveconverts = procprt_RDDSK_a, procprt_RDDSK_e, ' ', 6}; /***************************************************************/ char * procprt_WRDSK_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; if (supportflags & IOSTAT) val2memstr(curstat->dsk.wsz*512, buf, BFORMAT, avgval, nsecs); else strcpy(buf, "nopriv"); return buf; } char * procprt_WRDSK_e(struct tstat *curstat, int avgval, int nsecs) { return " -"; } detail_printdef procprt_WRDSK = { " WRDSK", "WRDSK", .ac.doactiveconverts = procprt_WRDSK_a, procprt_WRDSK_e, ' ', 6}; /***************************************************************/ char * procprt_CWRDSK_a(struct tstat *curstat, int avgval, int nsecs) { count_t nett_wsz; static char buf[10]; if (curstat->dsk.wsz > curstat->dsk.cwsz) nett_wsz = curstat->dsk.wsz - curstat->dsk.cwsz; else nett_wsz = 0; val2memstr(nett_wsz*512, buf, BFORMAT, avgval, nsecs); return buf; } detail_printdef procprt_CWRDSK = {" WRDSK", "CWRDSK", .ac.doactiveconverts = procprt_CWRDSK_a, procprt_WRDSK_e, ' ', 6}; /***************************************************************/ char * procprt_WCANCEL_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; if (supportflags & IOSTAT) val2memstr(curstat->dsk.cwsz*512, buf, BFORMAT, avgval, nsecs); else strcpy(buf, "nopriv"); return buf; } char * procprt_WCANCEL_e(struct tstat *curstat, int avgval, int nsecs) { return " -"; } detail_printdef procprt_WCANCEL = {"WCANCL", "WCANCL", .ac.doactiveconverts = procprt_WCANCEL_a, procprt_WCANCEL_e, ' ', 6}; /***************************************************************/ char * procprt_TCPRCV_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; val2valstr(curstat->net.tcprcv, buf, 6, avgval, nsecs); return buf; } char * procprt_TCPRCV_e(struct tstat *curstat, int avgval, int nsecs) { if (supportflags & NETATOPD || supportflags & NETATOPBPF ) { static char buf[10]; val2valstr(curstat->net.tcprcv, buf, 6, avgval, nsecs); return buf; } else return " -"; } detail_printdef procprt_TCPRCV = { "TCPRCV", "TCPRCV", .ac.doactiveconverts = procprt_TCPRCV_a, procprt_TCPRCV_e, ' ', 6}; /***************************************************************/ char * procprt_TCPRASZ_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; int avgtcpr = curstat->net.tcprcv ? curstat->net.tcprsz / curstat->net.tcprcv : 0; val2valstr(avgtcpr, buf, 7, 0, 0); return buf; } char * procprt_TCPRASZ_e(struct tstat *curstat, int avgval, int nsecs) { if (supportflags & NETATOPD || supportflags & NETATOPBPF ) { static char buf[10]; int avgtcpr = curstat->net.tcprcv ? curstat->net.tcprsz / curstat->net.tcprcv : 0; val2valstr(avgtcpr, buf, 7, 0, 0); return buf; } else return " -"; } detail_printdef procprt_TCPRASZ = { "TCPRASZ", "TCPRASZ", .ac.doactiveconverts = procprt_TCPRASZ_a, procprt_TCPRASZ_e, ' ', 7}; /***************************************************************/ char * procprt_TCPSND_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; val2valstr(curstat->net.tcpsnd, buf, 6, avgval, nsecs); return buf; } char * procprt_TCPSND_e(struct tstat *curstat, int avgval, int nsecs) { if (supportflags & NETATOPD || supportflags & NETATOPBPF ) { static char buf[10]; val2valstr(curstat->net.tcpsnd, buf, 6, avgval, nsecs); return buf; } else return " -"; } detail_printdef procprt_TCPSND = { "TCPSND", "TCPSND", .ac.doactiveconverts = procprt_TCPSND_a, procprt_TCPSND_e, ' ', 6}; /***************************************************************/ char * procprt_TCPSASZ_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; int avgtcps = curstat->net.tcpsnd ? curstat->net.tcpssz / curstat->net.tcpsnd : 0; val2valstr(avgtcps, buf, 7, 0, 0); return buf; } char * procprt_TCPSASZ_e(struct tstat *curstat, int avgval, int nsecs) { if (supportflags & NETATOPD || supportflags & NETATOPBPF ) { static char buf[10]; int avgtcps = curstat->net.tcpsnd ? curstat->net.tcpssz / curstat->net.tcpsnd : 0; val2valstr(avgtcps, buf, 7, 0, 0); return buf; } else return " -"; } detail_printdef procprt_TCPSASZ = { "TCPSASZ", "TCPSASZ", .ac.doactiveconverts = procprt_TCPSASZ_a, procprt_TCPSASZ_e, ' ', 7}; /***************************************************************/ char * procprt_UDPRCV_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; val2valstr(curstat->net.udprcv, buf, 6, avgval, nsecs); return buf; } char * procprt_UDPRCV_e(struct tstat *curstat, int avgval, int nsecs) { if (supportflags & NETATOPD || supportflags & NETATOPBPF ) { static char buf[10]; val2valstr(curstat->net.udprcv, buf, 6, avgval, nsecs); return buf; } else return " -"; } detail_printdef procprt_UDPRCV = { "UDPRCV", "UDPRCV", .ac.doactiveconverts = procprt_UDPRCV_a, procprt_UDPRCV_e, ' ', 6}; /***************************************************************/ char * procprt_UDPRASZ_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; int avgudpr = curstat->net.udprcv ? curstat->net.udprsz / curstat->net.udprcv : 0; val2valstr(avgudpr, buf, 7, 0, 0); return buf; } char * procprt_UDPRASZ_e(struct tstat *curstat, int avgval, int nsecs) { if (supportflags & NETATOPD || supportflags & NETATOPBPF ) { static char buf[10]; int avgudpr = curstat->net.udprcv ? curstat->net.udprsz / curstat->net.udprcv : 0; val2valstr(avgudpr, buf, 7, 0, 0); return buf; } else return " -"; } detail_printdef procprt_UDPRASZ = { "UDPRASZ", "UDPRASZ", .ac.doactiveconverts = procprt_UDPRASZ_a, procprt_UDPRASZ_e, ' ', 7}; /***************************************************************/ char * procprt_UDPSND_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; val2valstr(curstat->net.udpsnd, buf, 6, avgval, nsecs); return buf; } char * procprt_UDPSND_e(struct tstat *curstat, int avgval, int nsecs) { if (supportflags & NETATOPD || supportflags & NETATOPBPF ) { static char buf[10]; val2valstr(curstat->net.udpsnd, buf, 6, avgval, nsecs); return buf; } else return " -"; } detail_printdef procprt_UDPSND = { "UDPSND", "UDPSND", .ac.doactiveconverts = procprt_UDPSND_a, procprt_UDPSND_e, ' ', 6}; /***************************************************************/ char * procprt_UDPSASZ_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; int avgudps = curstat->net.udpsnd ? curstat->net.udpssz / curstat->net.udpsnd : 0; val2valstr(avgudps, buf, 7, 0, 0); return buf; } char * procprt_UDPSASZ_e(struct tstat *curstat, int avgval, int nsecs) { if (supportflags & NETATOPD || supportflags & NETATOPBPF ) { static char buf[10]; int avgudps = curstat->net.udpsnd ? curstat->net.udpssz / curstat->net.udpsnd : 0; val2valstr(avgudps, buf, 7, 0, 0); return buf; } else return " -"; } detail_printdef procprt_UDPSASZ = { "UDPSASZ", "UDPSASZ", .ac.doactiveconverts = procprt_UDPSASZ_a, procprt_UDPSASZ_e, ' ', 7}; /***************************************************************/ char * procprt_RNET_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; val2valstr(curstat->net.tcprcv + curstat->net.udprcv , buf, 5, avgval, nsecs); return buf; } char * procprt_RNET_e(struct tstat *curstat, int avgval, int nsecs) { if (supportflags & NETATOPD || supportflags & NETATOPBPF ) { static char buf[10]; val2valstr(curstat->net.tcprcv + curstat->net.udprcv , buf, 5, avgval, nsecs); return buf; } else return " -"; } detail_printdef procprt_RNET = { " RNET", "RNET", .ac.doactiveconverts = procprt_RNET_a, procprt_RNET_e, ' ', 5}; /***************************************************************/ char * procprt_SNET_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; val2valstr(curstat->net.tcpsnd + curstat->net.udpsnd, buf, 5, avgval, nsecs); return buf; } char * procprt_SNET_e(struct tstat *curstat, int avgval, int nsecs) { if (supportflags & NETATOPD || supportflags & NETATOPBPF ) { static char buf[10]; val2valstr(curstat->net.tcpsnd + curstat->net.udpsnd, buf, 5, avgval, nsecs); return buf; } else return " -"; } detail_printdef procprt_SNET = { " SNET", "SNET", .ac.doactiveconverts = procprt_SNET_a, procprt_SNET_e, ' ', 5}; /***************************************************************/ char * procprt_BANDWI_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[16]; count_t rkbps = (curstat->net.tcprsz+curstat->net.udprsz)/125/nsecs; format_bandw(buf, sizeof buf, rkbps); return buf; } char * procprt_BANDWI_e(struct tstat *curstat, int avgval, int nsecs) { if (supportflags & NETATOPD || supportflags & NETATOPBPF ) { static char buf[16]; count_t rkbps = (curstat->net.tcprsz + curstat->net.udprsz) /125/nsecs; format_bandw(buf, sizeof buf, rkbps); return buf; } else return " -"; } detail_printdef procprt_BANDWI = { " BANDWI", "BANDWI", .ac.doactiveconverts = procprt_BANDWI_a, procprt_BANDWI_e, ' ', 9}; /***************************************************************/ char * procprt_BANDWO_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[16]; count_t skbps = (curstat->net.tcpssz+curstat->net.udpssz)/125/nsecs; format_bandw(buf, sizeof buf, skbps); return buf; } char * procprt_BANDWO_e(struct tstat *curstat, int avgval, int nsecs) { if (supportflags & NETATOPD || supportflags & NETATOPBPF ) { static char buf[16]; count_t skbps = (curstat->net.tcpssz + curstat->net.udpssz) /125/nsecs; format_bandw(buf, sizeof buf, skbps); return buf; } else return " -"; } detail_printdef procprt_BANDWO = { " BANDWO", "BANDWO", .ac.doactiveconverts = procprt_BANDWO_a, procprt_BANDWO_e, ' ', 9}; /***************************************************************/ static void format_bandw(char *buf, int bufsize, count_t kbps) { char c; if (kbps < 10000) { c='K'; } else if (kbps < (count_t)10000 * 1000) { kbps/=1000; c = 'M'; } else if (kbps < (count_t)10000 * 1000 * 1000) { kbps/=1000 * 1000; c = 'G'; } else { kbps = kbps / 1000 / 1000 / 1000; c = 'T'; } snprintf(buf, bufsize, "%4lld %cbps", kbps%100000, c); } /***************************************************************/ char * procprt_GPULIST_ae(struct tstat *curstat, int avgval, int nsecs) { static char buf[64]; char tmp[64], *p=tmp; int i; if (!curstat->gpu.state) return " -"; if (!curstat->gpu.gpulist) return " -"; for (i=0; i < nrgpus; i++) { if (curstat->gpu.gpulist & 1< 8) { snprintf(tmp, sizeof tmp, "0x%06x", curstat->gpu.gpulist); break; } } } snprintf(buf, sizeof buf, "%8.8s", tmp); return buf; } detail_printdef procprt_GPULIST = { " GPUNUMS", "GPULIST", .ac.doactiveconverts = procprt_GPULIST_ae, procprt_GPULIST_ae, ' ', 8}; /***************************************************************/ char * procprt_GPUMEMNOW_ae(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; if (!curstat->gpu.state) return " -"; val2memstr(curstat->gpu.memnow*1024, buf, BFORMAT, 0, 0); return buf; } detail_printdef procprt_GPUMEMNOW = { "MEMNOW", "GPUMEM", .ac.doactiveconverts = procprt_GPUMEMNOW_ae, procprt_GPUMEMNOW_ae, ' ', 6}; /***************************************************************/ char * procprt_GPUMEMAVG_ae(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; if (!curstat->gpu.state) return " -"; if (curstat->gpu.sample == 0) return(" 0K"); val2memstr(curstat->gpu.nrgpus * curstat->gpu.memcum / curstat->gpu.sample*1024, buf, BFORMAT, 0, 0); return buf; } detail_printdef procprt_GPUMEMAVG = { "MEMAVG", "GPUMEMAVG", .ac.doactiveconverts = procprt_GPUMEMAVG_ae, procprt_GPUMEMAVG_ae, ' ', 6}; /***************************************************************/ char * procprt_GPUGPUBUSY_ae(struct tstat *curstat, int avgval, int nsecs) { static char buf[16]; if (!curstat->gpu.state) return " -"; if (curstat->gpu.gpubusy == -1) return " N/A"; snprintf(buf, sizeof buf, "%6d%%", curstat->gpu.gpubusy); return buf; } detail_printdef procprt_GPUGPUBUSY = { "GPUBUSY", "GPUGPUBUSY", .ac.doactiveconverts = procprt_GPUGPUBUSY_ae, procprt_GPUGPUBUSY_ae, ' ', 7}; /***************************************************************/ char * procprt_GPUMEMBUSY_ae(struct tstat *curstat, int avgval, int nsecs) { static char buf[16]; if (!curstat->gpu.state) return " -"; if (curstat->gpu.membusy == -1) return " N/A"; snprintf(buf, sizeof buf, "%6d%%", curstat->gpu.membusy); return buf; } detail_printdef procprt_GPUMEMBUSY = { "MEMBUSY", "GPUMEMBUSY", .ac.doactiveconverts = procprt_GPUMEMBUSY_ae, procprt_GPUMEMBUSY_ae, ' ', 7}; /***************************************************************/ char * procprt_WCHAN_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[32]; if (curstat->gen.state != 'R') snprintf(buf, sizeof buf, "%-15.15s", curstat->cpu.wchan); else snprintf(buf, sizeof buf, "%-15.15s", " "); return buf; } char * procprt_WCHAN_e(struct tstat *curstat, int avgval, int nsecs) { static char buf[32]; snprintf(buf, sizeof buf, "%-15.15s", " "); return buf; } detail_printdef procprt_WCHAN = { "WCHAN ", "WCHAN", .ac.doactiveconverts = procprt_WCHAN_a, procprt_WCHAN_e, ' ', 15}; /***************************************************************/ char * procprt_RUNDELAY_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; val2cpustr(curstat->cpu.rundelay/1000000, buf); return buf; } char * procprt_RUNDELAY_e(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; snprintf(buf, sizeof buf, " -"); return buf; } detail_printdef procprt_RUNDELAY = { "RDELAY", "RDELAY", .ac.doactiveconverts = procprt_RUNDELAY_a, procprt_RUNDELAY_e, ' ', 6}; /***************************************************************/ char * procprt_BLKDELAY_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; val2cpustr(curstat->cpu.blkdelay*1000/hertz, buf); return buf; } char * procprt_BLKDELAY_e(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; snprintf(buf, sizeof buf, " -"); return buf; } detail_printdef procprt_BLKDELAY = { "BDELAY", "BDELAY", .ac.doactiveconverts = procprt_BLKDELAY_a, procprt_BLKDELAY_e, ' ', 6}; /***************************************************************/ char * procprt_NVCSW_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[64]; val2valstr(curstat->cpu.nvcsw, buf, 6, avgval, nsecs); return buf; } char * procprt_NVCSW_e(struct tstat *curstat, int avgval, int nsecs) { return " -"; } detail_printdef procprt_NVCSW = { " NVCSW", "NVCSW", .ac.doactiveconverts = procprt_NVCSW_a, procprt_NVCSW_e, ' ', 6}; /***************************************************************/ char * procprt_NIVCSW_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[64]; val2valstr(curstat->cpu.nivcsw, buf, 6, avgval, nsecs); return buf; } char * procprt_NIVCSW_e(struct tstat *curstat, int avgval, int nsecs) { return " -"; } detail_printdef procprt_NIVCSW = { "NIVCSW", "NIVCSW", .ac.doactiveconverts = procprt_NIVCSW_a, procprt_NIVCSW_e, ' ', 6}; /***************************************************************/ char * procprt_SORTITEM_ae(struct tstat *curstat, int avgval, int nsecs) { return ""; // dummy function } detail_printdef procprt_SORTITEM = // width is dynamically defined! { 0, "SORTITEM", .ac.doactiveconverts = procprt_SORTITEM_ae, procprt_SORTITEM_ae, ' ', 4}; /***************************************************************/ /* CGROUP LEVEL FORMATTING */ /***************************************************************/ /* * showcgrouphead: show header line for cgroups. * if in interactive mode, also add a page number * if in interactive mode, columns are aligned to fill out rows */ void showcgrouphead(detail_printpair *elemptr, int curlist, int totlist, char showorder) { detail_printpair curelem; char *chead=""; int col=0, curline; char pagindic[16]; int pagindiclen; int n=0; int bufsz; int maxw=screen ? COLS : linelen; colspacings = getspacings(elemptr); bufsz = maxw+1; elemptr = newelems; // point to adjusted array char buf[bufsz+2]; // long live dynamically sized auto arrays... if (screen) getyx(stdscr, curline, col); // get current line else printg("\n"); // show column by column // while ((curelem=*elemptr).pf!=0) { chead = curelem.pf->head; if (screen) { // print header, optionally colored when it is // the current sort criterion // if (showorder == curelem.pf->sortcrit) { if (usecolors) attron(COLOR_PAIR(FGCOLORINFO)); else attron(A_BOLD); } printg("%s", chead); if (showorder == curelem.pf->sortcrit) { if (usecolors) attroff(COLOR_PAIR(FGCOLORINFO)); else attroff(A_BOLD); } // print filler spaces // printg("%*s", colspacings[n], ""); } else { col += snprintf(buf+col, sizeof buf-col,"%s%s ", "", chead); } elemptr++; n++; } if (screen) // add page number, eat from last header if needed... { pagindiclen = snprintf(pagindic, sizeof pagindic, "%d/%d", curlist, totlist); move(curline, COLS-pagindiclen); printg("%s", pagindic); } else // no screen: print entire buffer at once { printg("%s\n", buf); } } /***************************************************************/ /* * showcgroupline: show line for cgroups. * if in interactive mode, columns are aligned to fill out rows * params: * elemptr: pointer to array of print definition structs ptrs * cgchain: the cgroup to print * tstat: the process to print (is NULL for a cgroup line) * nsecs: number of seconds elapsed between previous and this sample * avgval: is averaging out per second needed? * cputicks: total ticks elapsed * nrcpu: number of CPUs */ void showcgroupline(detail_printpair* elemptr, struct cgchainer *cgchain, struct tstat *tstat, int nsecs, int avgval, count_t cputicks, int nrcpu) { detail_printpair curelem; int n=0, color=0, linecolor=0; elemptr=newelems; // point to static array if (screen) { if (cgchain->cstat->gen.depth <= 1) // zero or first cgroup level? linecolor = FGCOLORINFO; if (tstat) // process info line? linecolor = FGCOLORBORDER; } while ((curelem=*elemptr).pf!=0) { char *buf; color = 0; buf = curelem.pf->ac.doactiveconvertc(cgchain, tstat, avgval, nsecs, cputicks, nrcpu, &color); if (screen) { if (cgchain->cstat->gen.depth == 0 && !tstat) // root cgroup? attron(A_BOLD); if (color == 0) // no explicit color from conversion fc color = linecolor; if (color) { if (usecolors) attron(COLOR_PAIR(color)); else attron(A_BOLD); } printg("%s", buf); printg("%*s",colspacings[n], ""); if (color) { if (usecolors) attroff(COLOR_PAIR(color)); else attroff(A_BOLD); } if (cgchain->cstat->gen.depth == 0 && !tstat) // root cgroup? attroff(A_BOLD); } else { printg("%s", buf); printg(" "); } elemptr++; n++; } if (!screen) printg("\n"); } /***************************************************************/ char * cgroup_CGROUP_PATH(struct cgchainer *cgchain, struct tstat *tstat, int avgval, int nsecs, count_t cputicks, int nrcpu, int *color) { static char buf[4098]; extern detail_printdef cgroupprt_CGROUP_PATH; extern int startoffset; // influenced by -> and <- keys int i; char *cgrname = cgchain->cstat->cgname; int namelen = cgchain->cstat->gen.namelen; int cgrdepth = cgchain->cstat->gen.depth; unsigned long vlinemask = cgchain->vlinemask; if (*cgrname == '\0') // root cgroup? { cgrname = "/"; namelen = 1; } int maxnamelen = cgroupprt_CGROUP_PATH.width - (cgrdepth*3); int curoffset = startoffset <= namelen ? startoffset : namelen; if (screen) { switch (cgrdepth) { case 0: snprintf(buf, sizeof buf, "%-*s", cgroupprt_CGROUP_PATH.width, "/"); break; default: // draw continuous vertical bars for // previous levels if not stub // for (i=0; i < cgrdepth-1; i++) { if (i >= CGRMAXDEPTH || vlinemask & (1ULL<= CGRMAXDEPTH || cgchain->stub) addch(ACS_LLCORNER); else addch(ACS_LTEE); addch(ACS_HLINE); } else // process line { if (cgrdepth >= CGRMAXDEPTH || cgchain->stub) addch(' '); else addch(ACS_VLINE); addch(' '); } snprintf(buf, sizeof buf, " %-*.*s", maxnamelen, maxnamelen, cgrname+curoffset); } } else { snprintf(buf, sizeof buf, "%*s%-*.*s", cgrdepth*2, "", cgroupprt_CGROUP_PATH.width - cgrdepth*2, cgroupprt_CGROUP_PATH.width - cgrdepth*2, cgrname); } return buf; } detail_printdef cgroupprt_CGROUP_PATH = {"CGROUP (scroll: <- ->) ", "CGRPATH", .ac.doactiveconvertc = cgroup_CGROUP_PATH, NULL, ' ', 26, 0}; /***************************************************************/ char * cgroup_CGRNPROCS(struct cgchainer *cgchain, struct tstat *tstat, int avgval, int nsecs, count_t cputicks, int nrcpu, int *color) { static char buf[10]; if (tstat) // process info? return " "; // cgroup info val2valstr(cgchain->cstat->gen.nprocs, buf, 6, 0, 0); return buf; } detail_printdef cgroupprt_CGRNPROCS = { "NPROCS", "CGRNPROCS", .ac.doactiveconvertc = cgroup_CGRNPROCS, NULL, ' ', 6}; /***************************************************************/ char * cgroup_CGRNPROCSB(struct cgchainer *cgchain, struct tstat *tstat, int avgval, int nsecs, count_t cputicks, int nrcpu, int *color) { static char buf[10]; if (tstat) // process info? return " "; // cgroup info val2valstr(cgchain->cstat->gen.procsbelow, buf, 6, 0, 0); return buf; } detail_printdef cgroupprt_CGRNPROCSB = { "PBELOW", "CGRNPROCSB", .ac.doactiveconvertc = cgroup_CGRNPROCSB, NULL, ' ', 6}; /***************************************************************/ char * cgroup_CGRCPUBUSY(struct cgchainer *cgchain, struct tstat *tstat, int avgval, int nsecs, count_t cputicks, int nrcpu, int *color) { static char buf[16]; float perc; int maxperc, badness = 0; if (!tstat) // cgroup info? { if (cgchain->cstat->cpu.utime == -1) // undefined? return " -"; perc = (cgchain->cstat->cpu.utime + cgchain->cstat->cpu.stime) / (cputicks/nrcpu*100.0); maxperc = cgchain->cstat->conf.cpumax; // determine if CPU load is limited on system level // if (cpubadness) badness = perc / nrcpu * 100.0 / cpubadness; if (badness >= 100) *color = FGCOLORCRIT; // determine if CPU load is limited by cpu.max within cgroup // if (maxperc >= 0 && perc + 2 >= maxperc) *color = FGCOLORCRIT; } else // process info { perc = (tstat->cpu.utime + tstat->cpu.stime) * 100.0 / (cputicks/nrcpu); } if (perc < 1000.0) snprintf(buf, sizeof buf, "%6.2f%%", perc); else if (perc < 10000.0) snprintf(buf, sizeof buf, "%6.1f%%", perc); else snprintf(buf, sizeof buf, "%6.0f%%", perc); return buf; } detail_printdef cgroupprt_CGRCPUBUSY = { "CPUBUSY", "CGRCPUBUSY", .ac.doactiveconvertc = cgroup_CGRCPUBUSY, NULL, 'C', 7}; /***************************************************************/ char * cgroup_CGRCPUPSI(struct cgchainer *cgchain, struct tstat *tstat, int avgval, int nsecs, count_t cputicks, int nrcpu, int *color) { static char buf[16]; float perc; if (tstat) // process info? return " "; // cgroup info switch (cgchain->cstat->cpu.somepres) { case -1: return " -"; default: perc = cgchain->cstat->cpu.somepres / (cputicks/nrcpu*100.0); if (perc >= 25.0) *color = FGCOLORCRIT; snprintf(buf, sizeof buf, "%4.0f%%", perc); return buf; } } detail_printdef cgroupprt_CGRCPUPSI = { "CPUPS", "CGRCPUPSI", .ac.doactiveconvertc = cgroup_CGRCPUPSI, NULL, ' ', 5}; /***************************************************************/ char * cgroup_CGRCPUMAX(struct cgchainer *cgchain, struct tstat *tstat, int avgval, int nsecs, count_t cputicks, int nrcpu, int *color) { static char buf[16]; float perc; int maxperc; if (tstat) // process info? return " "; maxperc = cgchain->cstat->conf.cpumax; // when current cpu percentage is colored due to limitation // by cpu.max, also color cpu.max itself // if (cgchain->cstat->cpu.utime != -1) // cpu usage available? { perc = (cgchain->cstat->cpu.utime + cgchain->cstat->cpu.stime) / (cputicks/nrcpu*100.0); if (maxperc >= 0 && perc + 2 >= maxperc) *color = FGCOLORCRIT; } // cgroup info // switch (cgchain->cstat->conf.cpumax) { case -1: return " max"; case -2: return " -"; default: snprintf(buf, sizeof buf, "%5d%%", maxperc); return buf; } } detail_printdef cgroupprt_CGRCPUMAX = { "CPUMAX", "CGRCPUMAX", .ac.doactiveconvertc = cgroup_CGRCPUMAX, NULL, ' ', 6}; /***************************************************************/ char * cgroup_CGRCPUWGT(struct cgchainer *cgchain, struct tstat *tstat, int avgval, int nsecs, count_t cputicks, int nrcpu, int *color) { static char buf[16]; if (tstat) // process info? return " "; // cgroup info switch (cgchain->cstat->conf.cpuweight) { case -2: return " -"; default: snprintf(buf, sizeof buf, "%6d", cgchain->cstat->conf.cpuweight); return buf; } } detail_printdef cgroupprt_CGRCPUWGT = { "CPUWGT", "CGRCPUWGT", .ac.doactiveconvertc = cgroup_CGRCPUWGT, NULL, ' ', 6}; /***************************************************************/ char * cgroup_CGRMEMORY(struct cgchainer *cgchain, struct tstat *tstat, int avgval, int nsecs, count_t cputicks, int nrcpu, int *color) { static char buf[16]; count_t memusage, maxusage; if (!tstat) // show cgroup info? { if (cgchain->cstat->mem.current > 0) // defined? { memusage = cgchain->cstat->mem.current; } else { if (cgchain->cstat->mem.anon == -1) // undefined? return " -"; memusage = (cgchain->cstat->mem.anon + cgchain->cstat->mem.file + cgchain->cstat->mem.kernel + cgchain->cstat->mem.shmem); } maxusage = cgchain->cstat->conf.memmax; // set color if occupation percentage > 95% // if (maxusage > 0 && memusage * 100 / maxusage > 95) *color = FGCOLORCRIT; val2memstr(memusage * pagesize, buf, BFORMAT, 0, 0); } else // show process info { val2memstr(tstat->mem.rmem*1024, buf, BFORMAT, 0, 0); } return buf; } detail_printdef cgroupprt_CGRMEMORY = { "MEMORY", "CGRMEMORY", .ac.doactiveconvertc = cgroup_CGRMEMORY, NULL, 'M', 6}; /***************************************************************/ char * cgroup_CGRMEMPSI(struct cgchainer *cgchain, struct tstat *tstat, int avgval, int nsecs, count_t cputicks, int nrcpu, int *color) { static char buf[16]; float perc; if (tstat) // process info? return " "; // cgroup info switch (cgchain->cstat->mem.somepres) { case -1: return " -"; default: perc = cgchain->cstat->mem.fullpres / (cputicks/nrcpu*100.0); if (perc >= 20.0) *color = FGCOLORCRIT; snprintf(buf, sizeof buf, "%4.0f%%", perc); return buf; } } detail_printdef cgroupprt_CGRMEMPSI = { "MEMPS", "CGRMEMPSI", .ac.doactiveconvertc = cgroup_CGRMEMPSI, NULL, ' ', 5}; /***************************************************************/ char * cgroup_CGRMEMMAX(struct cgchainer *cgchain, struct tstat *tstat, int avgval, int nsecs, count_t cputicks, int nrcpu, int *color) { static char buf[16]; count_t memusage, maxusage; if (tstat) // process info? return " "; maxusage = cgchain->cstat->conf.memmax; if (cgchain->cstat->mem.anon != -1) // current usage defined? { memusage = (cgchain->cstat->mem.anon + cgchain->cstat->mem.file + cgchain->cstat->mem.kernel); // set color if occupation percentage > 95% // if (maxusage > 0 && memusage * 100 / maxusage > 95) *color = FGCOLORCRIT; } // cgroup info switch (cgchain->cstat->conf.memmax) { case -1: return " max"; case -2: return " -"; default: val2memstr(maxusage*pagesize, buf, BFORMAT, 0, 0); return buf; } } detail_printdef cgroupprt_CGRMEMMAX = { "MEMMAX", "CGRMEMMAX", .ac.doactiveconvertc = cgroup_CGRMEMMAX, NULL, ' ', 6}; /***************************************************************/ char * cgroup_CGRSWPMAX(struct cgchainer *cgchain, struct tstat *tstat, int avgval, int nsecs, count_t cputicks, int nrcpu, int *color) { static char buf[16]; if (tstat) // process info? return " "; // cgroup info switch (cgchain->cstat->conf.swpmax) { case -1: return " max"; case -2: return " -"; default: val2memstr(cgchain->cstat->conf.swpmax*pagesize, buf, BFORMAT, 0, 0); return buf; } } detail_printdef cgroupprt_CGRSWPMAX = { "SWPMAX", "CGRSWPMAX", .ac.doactiveconvertc = cgroup_CGRSWPMAX, NULL, ' ', 6}; /***************************************************************/ char * cgroup_CGRDISKIO(struct cgchainer *cgchain, struct tstat *tstat, int avgval, int nsecs, count_t cputicks, int nrcpu, int *color) { static char buf[16]; if (!tstat) // show cgroup info? { if (cgchain->cstat->dsk.rbytes == -1) // not defined? return " -"; val2memstr(cgchain->cstat->dsk.rbytes + cgchain->cstat->dsk.wbytes, buf, BFORMAT, avgval, nsecs); } else // show process info { if (supportflags & IOSTAT) val2memstr((tstat->dsk.rsz+tstat->dsk.wsz)*512, buf, BFORMAT, avgval, nsecs); else strcpy(buf, "nopriv"); } return buf; } detail_printdef cgroupprt_CGRDISKIO = { "DISKIO", "CGRDISKIO", .ac.doactiveconvertc = cgroup_CGRDISKIO, NULL, 'D', 6}; /***************************************************************/ char * cgroup_CGRDSKPSI(struct cgchainer *cgchain, struct tstat *tstat, int avgval, int nsecs, count_t cputicks, int nrcpu, int *color) { static char buf[16]; float perc; if (tstat) // process info? return " "; // cgroup info switch (cgchain->cstat->dsk.somepres) { case -1: return " -"; default: perc = cgchain->cstat->dsk.fullpres / (cputicks/nrcpu*100.0); if (perc >= 25.0) *color = FGCOLORCRIT; snprintf(buf, sizeof buf, "%4.0f%%", perc); return buf; } } detail_printdef cgroupprt_CGRDSKPSI = { "DSKPS", "CGRDSKPSI", .ac.doactiveconvertc = cgroup_CGRDSKPSI, NULL, ' ', 5}; /***************************************************************/ char * cgroup_CGRDSKWGT(struct cgchainer *cgchain, struct tstat *tstat, int avgval, int nsecs, count_t cputicks, int nrcpu, int *color) { static char buf[16]; if (tstat) // process info? return " "; // cgroup info switch (cgchain->cstat->conf.dskweight) { case -2: return " -"; default: snprintf(buf, sizeof buf, "%5d", cgchain->cstat->conf.dskweight); return buf; } } detail_printdef cgroupprt_CGRDSKWGT = { "IOWGT", "CGRDSKWGT", .ac.doactiveconvertc = cgroup_CGRDSKWGT, NULL, ' ', 5}; /***************************************************************/ char * cgroup_CGRPID(struct cgchainer *cgchain, struct tstat *tstat, int avgval, int nsecs, count_t cputicks, int nrcpu, int *color) { static char buf[64]; if (tstat) // process info? snprintf(buf, sizeof buf, "%*d", cgroupprt_CGRPID.width, tstat->gen.pid); else // only cgroup info snprintf(buf, sizeof buf, "%*s", cgroupprt_CGRPID.width, " "); return buf; } detail_printdef cgroupprt_CGRPID = { "PID", "CGRPID", .ac.doactiveconvertc = cgroup_CGRPID, NULL, ' ', 5}; //DYNAMIC WIDTH! /***************************************************************/ char * cgroup_CGRCMD(struct cgchainer *cgchain, struct tstat *tstat, int avgval, int nsecs, count_t cputicks, int nrcpu, int *color) { static char buf[16]; if (tstat) // process info? { snprintf(buf, sizeof buf, "%-14.14s", tstat->gen.name); } else // cgroup info { if (cgroupdepth == 8 && cgchain->cstat->gen.depth == 0) { snprintf(buf, sizeof buf, "[suppressed]"); *color = FGCOLORBORDER; } else { snprintf(buf, sizeof buf, "%-14.14s", " "); } } return buf; } detail_printdef cgroupprt_CGRCMD = { "CMD ", "CGRCMD", .ac.doactiveconvertc = cgroup_CGRCMD, NULL, ' ', 14}; /***************************************************************/ atop-2.12.1/showsys.c0000644000203100020310000032305415064552761013745 0ustar gerlofgerlof/* ** ATOP - System & Process Monitor ** ** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as process-level. ** ** This source-file contains the Linux-specific functions to calculate ** figures to be visualized. ** ========================================================================== ** Author: JC van Winkel - AT Computing, Nijmegen, Holland ** E-mail: jc@ATComputing.nl ** Date: November 2009 ** -------------------------------------------------------------------------- ** Copyright (C) 2009 JC van Winkel ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ** -------------------------------------------------------------------------- */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "atop.h" #include "photoproc.h" #include "photosyst.h" #include "showgeneric.h" #include "showlinux.h" static void addblanks(double *, double *); static void sumscaling(struct sstat *, count_t *, count_t *, count_t *); static void psiformatavg(struct psi *, char *, char *, int); static void psiformattot(struct psi *, char *, extraparam *, int *, char *, int); /*******************************************************************/ /* ** print the label of a system-statistics line and switch on ** colors if needed */ static int syscolorlabel(char *labeltext, unsigned int badness) { if (screen) { if (badness >= 100) { attron (A_BLINK); if (usecolors) { attron(COLOR_PAIR(FGCOLORCRIT)); printg(labeltext); attroff(COLOR_PAIR(FGCOLORCRIT)); } else { attron(A_BOLD); printg(labeltext); attroff(A_BOLD); } attroff(A_BLINK); return FGCOLORCRIT; } if (almostcrit && badness >= almostcrit) { if (usecolors) { attron(COLOR_PAIR(FGCOLORALMOST)); printg(labeltext); attroff(COLOR_PAIR(FGCOLORALMOST)); } else { attron(A_BOLD); printg(labeltext); attroff(A_BOLD); } return FGCOLORALMOST; } } /* ** no colors required or no reason to show colors */ printg(labeltext); return 0; } static char *sysprt_BLANKBOX(struct sstat *sstat, extraparam *notused, int, int *); static void addblanks(double *charslackused, double *charslackover) { *charslackused+=*charslackover; while (*charslackused>0.5) { printg(" "); *charslackused-=1; } } /* * showsysline * print an array of sys_printpair things. If the screen contains too * few character columns, lower priority items are removed * */ #define MAXELEMS 40 void showsysline(sys_printpair* elemptr, struct sstat* sstat, extraparam *extra, char *labeltext, unsigned int badness) { sys_printdef *curelem; int maxw = screen ? COLS : linelen; // every 15-char item is printed as: // >>>> | datadatadata<<<<< // 012345678901234 /* how many items will fit on one line? */ int avail = (maxw-5)/15; syscolorlabel(labeltext, badness); /* count number of items */ sys_printpair newelems[MAXELEMS]; int nitems; for (nitems=0; nitems < MAXELEMS-1 && elemptr[nitems].f != 0; ++nitems) newelems[nitems]=elemptr[nitems]; newelems[nitems].f=0; /* remove lowest priority box to make room as needed */ while (nitems > avail) { int lowestprio=999999; int lowestprio_index=-1; int i; for (i=0; i1) { slackitemsover=(double)(avail-nitems)/(nitems); } else { slackitemsover=(avail-nitems)/2; } // charslack: the slack in characters after using as many // items as possible double charslackover = screen ? ((COLS - 5) % 15) : ((linelen - 5) %15); // two places per items where blanks can be added charslackover /= (avail * 2); double charslackused=0.0; double itemslackused=0.0; elemptr=newelems; while ((curelem=elemptr->f)!=0) { char *itemp; int color; /* ** by default no color is shown for this field (color = 0) ** ** the format-function can set a color-number (color > 0) ** when a specific color is wanted or the format-function ** can leave the decision to display with a color to the piece ** of code below (color == -1) */ color = 0; itemp = curelem->doformat(sstat, extra, badness, &color); if (!itemp) { itemp = " ?"; } printg(" | "); addblanks(&charslackused, &charslackover); if (screen) { if (color == -1) // default color wanted { color = 0; if (badness >= 100) color = FGCOLORCRIT; else if (almostcrit && badness >= almostcrit) color = FGCOLORALMOST; } if (color) // after all: has a color been set? { if (usecolors) attron(COLOR_PAIR(color)); else attron(A_BOLD); } } printg("%s", itemp); if (color && screen) // color set for this value? { if (usecolors) attroff(COLOR_PAIR(color)); else attroff(A_BOLD); } itemslackused+=slackitemsover; while (itemslackused>0.5) { addblanks(&charslackused, &charslackover); printg(" | "); printg("%s", sysprt_BLANKBOX(0, 0, 0, 0)); addblanks(&charslackused, &charslackover); itemslackused-=1; } elemptr++; addblanks(&charslackused, &charslackover); } printg(" |"); if (!screen) { printg("\n"); } } /*******************************************************************/ /* SYSTEM PRINT FUNCTIONS */ /*******************************************************************/ static char * sysprt_PRCSYS(struct sstat *notused, extraparam *as, int badness, int *color) { static char buf[15]="sys "; val2cpustr(as->totst * 1000/hertz, buf+6); return buf; } sys_printdef syspdef_PRCSYS = {"PRCSYS", sysprt_PRCSYS, NULL}; /*******************************************************************/ static char * sysprt_PRCUSER(struct sstat *notused, extraparam *as, int badness, int *color) { static char buf[15]="user "; val2cpustr(as->totut * 1000/hertz, buf+6); return buf; } sys_printdef syspdef_PRCUSER = {"PRCUSER", sysprt_PRCUSER, NULL}; /*******************************************************************/ static char * sysprt_PRCNPROC(struct sstat *notused, extraparam *as, int badness, int *color) { static char buf[15]="#proc "; val2valstr(as->nproc - as->nexit, buf+6, 6, 0, 0); return buf; } sys_printdef syspdef_PRCNPROC = {"PRCNPROC", sysprt_PRCNPROC, NULL}; /*******************************************************************/ static char * sysprt_PRCNRUNNING(struct sstat *notused, extraparam *as, int badness, int *color) { static char buf[15]="#trun "; val2valstr(as->ntrun, buf+6, 6, 0, 0); return buf; } sys_printdef syspdef_PRCNRUNNING = {"PRCNRUNNING", sysprt_PRCNRUNNING, NULL}; /*******************************************************************/ static char * sysprt_PRCNSLEEPING(struct sstat *notused, extraparam *as, int badness, int *color) { static char buf[15]="#tslpi "; val2valstr(as->ntslpi, buf+8, 4, 0, 0); return buf; } sys_printdef syspdef_PRCNSLEEPING = {"PRCNSLEEPING", sysprt_PRCNSLEEPING, NULL}; /*******************************************************************/ static char * sysprt_PRCNDSLEEPING(struct sstat *notused, extraparam *as, int badness, int *color) { static char buf[15]="#tslpu "; val2valstr(as->ntslpu, buf+8, 4, 0, 0); return buf; } sys_printdef syspdef_PRCNDSLEEPING = {"PRCNDSLEEPING", sysprt_PRCNDSLEEPING, NULL}; /*******************************************************************/ static char * sysprt_PRCNIDLE(struct sstat *notused, extraparam *as, int badness, int *color) { static char buf[15]="#tidle "; val2valstr(as->ntidle, buf+8, 4, 0, 0); return buf; } sys_printdef syspdef_PRCNIDLE = {"PRCNIDLE", sysprt_PRCNIDLE, NULL}; /*******************************************************************/ static char * sysprt_PRCNZOMBIE(struct sstat *notused, extraparam *as, int badness, int *color) { static char buf[15]="#zombie "; if (as->nzomb > 30) *color = FGCOLORALMOST; if (as->nzomb > 50) *color = FGCOLORCRIT; val2valstr(as->nzomb, buf+8, 4, 0, 0); return buf; } sys_printdef syspdef_PRCNZOMBIE = {"PRCNZOMBIE", sysprt_PRCNZOMBIE, NULL}; /*******************************************************************/ static char * sysprt_PRCNNEXIT(struct sstat *notused, extraparam *as, int badness, int *color) { static char firstcall = 1; static char buf[15]="#exit "; if (supportflags & ACCTACTIVE) { if (as->noverflow) { *color = FGCOLORCRIT; buf[6] = '>'; val2valstr(as->nexit, buf+7, 5, as->avgval, as->nsecs); } else { val2valstr(as->nexit, buf+6, 6, as->avgval, as->nsecs); } return buf; } else { if (firstcall) { *color = FGCOLORCRIT; firstcall = 0; } else { *color = FGCOLORINFO; } switch (acctreason) { case 1: return "no procacct"; // "no acctread"; case 2: return "no procacct"; // "no acctwant"; case 3: return "no procacct"; // "no acctsema"; case 4: return "no procacct"; // "no acctmkdir"; case 5: return "no procacct"; // "no rootprivs"; default: return "no procacct"; } } } sys_printdef syspdef_PRCNNEXIT = {"PRCNNEXIT", sysprt_PRCNNEXIT, NULL}; /*******************************************************************/ static char * sysprt_CPUSYS(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[15]; float perc = (sstat->cpu.all.stime * 100.0) / as->percputot; if (perc > 1.0) *color = -1; snprintf(buf, sizeof buf, "sys %6.0f%%", perc); return buf; } sys_printdef syspdef_CPUSYS = {"CPUSYS", sysprt_CPUSYS, NULL}; /*******************************************************************/ static char * sysprt_CPUUSER(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[15]; float perc = (sstat->cpu.all.utime + sstat->cpu.all.ntime) * 100.0 / as->percputot; if (perc > 1.0) *color = -1; snprintf(buf, sizeof buf, "user %6.0f%%", perc); return buf; } sys_printdef syspdef_CPUUSER = {"CPUUSER", sysprt_CPUUSER, NULL}; /*******************************************************************/ static char * sysprt_CPUIRQ(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[15]; float perc = (sstat->cpu.all.Itime + sstat->cpu.all.Stime) * 100.0 / as->percputot; if (perc > 1.0) *color = -1; snprintf(buf, sizeof buf, "irq %6.0f%%", perc); return buf; } sys_printdef syspdef_CPUIRQ = {"CPUIRQ", sysprt_CPUIRQ, NULL}; /*******************************************************************/ static char * sysprt_CPUIDLE(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[15]; snprintf(buf, sizeof buf, "idle %6.0f%%", (sstat->cpu.all.itime * 100.0) / as->percputot); return buf; } sys_printdef syspdef_CPUIDLE = {"CPUIDLE", sysprt_CPUIDLE, NULL}; /*******************************************************************/ static char * sysprt_CPUWAIT(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[15]; snprintf(buf, sizeof buf, "wait %6.0f%%", (sstat->cpu.all.wtime * 100.0) / as->percputot); return buf; } sys_printdef syspdef_CPUWAIT = {"CPUWAIT", sysprt_CPUWAIT, NULL}; /*******************************************************************/ static char * sysprt_CPUISYS(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[15]; float perc = sstat->cpu.cpu[as->index].stime * 100.0 / as->percputot; if (perc > 1.0) *color = -1; snprintf(buf, sizeof buf, "sys %6.0f%%", perc); return buf; } sys_printdef syspdef_CPUISYS = {"CPUISYS", sysprt_CPUISYS, NULL}; /*******************************************************************/ static char * sysprt_CPUIUSER(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[15]; float perc = (sstat->cpu.cpu[as->index].utime + sstat->cpu.cpu[as->index].ntime) * 100.0 / as->percputot; if (perc > 1.0) *color = -1; snprintf(buf, sizeof buf, "user %6.0f%%", perc); return buf; } sys_printdef syspdef_CPUIUSER = {"CPUIUSER", sysprt_CPUIUSER, NULL}; /*******************************************************************/ static char * sysprt_CPUIIRQ(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[15]; float perc = (sstat->cpu.cpu[as->index].Itime + sstat->cpu.cpu[as->index].Stime) * 100.0 / as->percputot; if (perc > 1.0) *color = -1; snprintf(buf, sizeof buf, "irq %6.0f%%", perc); return buf; } sys_printdef syspdef_CPUIIRQ = {"CPUIIRQ", sysprt_CPUIIRQ, NULL}; /*******************************************************************/ static char * sysprt_CPUIIDLE(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[15]; snprintf(buf, sizeof buf, "idle %6.0f%%", (sstat->cpu.cpu[as->index].itime * 100.0) / as->percputot); return buf; } sys_printdef syspdef_CPUIIDLE = {"CPUIIDLE", sysprt_CPUIIDLE, NULL}; /*******************************************************************/ static char * sysprt_CPUIWAIT(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[15]; snprintf(buf, sizeof buf, "cpu%03d w%3.0f%%", sstat->cpu.cpu[as->index].cpunr, (sstat->cpu.cpu[as->index].wtime * 100.0) / as->percputot); return buf; } sys_printdef syspdef_CPUIWAIT = {"CPUIWAIT", sysprt_CPUIWAIT, NULL}; /*******************************************************************/ static char * dofmt_cpufreq(char *buf, count_t maxfreq, count_t cnt, count_t ticks) { // if ticks != 0, do full output if (ticks) { count_t curfreq = cnt/ticks; strcpy(buf, "avgf "); val2Hzstr(curfreq, buf+5); } else if (cnt) // no max, no %. if freq is known: print it { strcpy(buf, "curf "); val2Hzstr(cnt, buf+5); } else // nothing is known: suppress { buf = " -"; } return buf; } /* ** sumscaling: sum scaling info for all processors */ static void sumscaling(struct sstat *sstat, count_t *maxfreq, count_t *cnt, count_t *ticks) { count_t mymaxfreq = 0; count_t mycnt = 0; count_t myticks = 0; int n=sstat->cpu.nrcpu; int i; for (i=0; i < n; ++i) { mymaxfreq+= sstat->cpu.cpu[i].freqcnt.maxfreq; mycnt += sstat->cpu.cpu[i].freqcnt.cnt; myticks += sstat->cpu.cpu[i].freqcnt.ticks; } *maxfreq= mymaxfreq; *cnt = mycnt; *ticks = myticks; } static char * dofmt_cpuscale(char *buf, count_t maxfreq, count_t cnt, count_t ticks) { if (ticks) { count_t curfreq = cnt/ticks; int perc = maxfreq ? 100 * curfreq / maxfreq : 0; strcpy(buf, "avgscal "); snprintf(buf+7, 6, "%4d%%", perc); } else if (maxfreq) // max frequency is known so % can be calculated { strcpy(buf, "curscal "); snprintf(buf+7, 6, "%4lld%%", 100 * cnt / maxfreq); } else // nothing is known: suppress { buf = NULL; } return buf; } /*******************************************************************/ static char * sysprt_CPUFREQ(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[15]; count_t maxfreq; count_t cnt; count_t ticks; int n = sstat->cpu.nrcpu; sumscaling(sstat, &maxfreq, &cnt, &ticks); return dofmt_cpufreq(buf, maxfreq/n, cnt/n, ticks/n); } static int sysval_CPUFREQ(struct sstat *sstat) { char buf[15]; count_t maxfreq; count_t cnt; count_t ticks; int n = sstat->cpu.nrcpu; sumscaling(sstat, &maxfreq, &cnt, &ticks); if (dofmt_cpufreq(buf, maxfreq/n, cnt/n, ticks/n)) return 1; else return 0; } sys_printdef syspdef_CPUFREQ = {"CPUFREQ", sysprt_CPUFREQ, sysval_CPUFREQ}; /*******************************************************************/ static char * sysprt_CPUIFREQ(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[15]; count_t maxfreq = sstat->cpu.cpu[as->index].freqcnt.maxfreq; count_t cnt = sstat->cpu.cpu[as->index].freqcnt.cnt; count_t ticks = sstat->cpu.cpu[as->index].freqcnt.ticks; return dofmt_cpufreq(buf, maxfreq, cnt, ticks); } sys_printdef syspdef_CPUIFREQ = {"CPUIFREQ", sysprt_CPUIFREQ, sysval_CPUFREQ}; /*******************************************************************/ static char * sysprt_CPUSCALE(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[32] = "scaling ?"; count_t maxfreq; count_t cnt; count_t ticks; int n = sstat->cpu.nrcpu; sumscaling(sstat, &maxfreq, &cnt, &ticks); dofmt_cpuscale(buf, maxfreq/n, cnt/n, ticks/n); return buf; } static int sysval_CPUSCALE(struct sstat *sstat) { char buf[32]; count_t maxfreq; count_t cnt; count_t ticks; int n = sstat->cpu.nrcpu; sumscaling(sstat, &maxfreq, &cnt, &ticks); if (dofmt_cpuscale(buf, maxfreq/n, cnt/n, ticks/n)) return 1; else return 0; } sys_printdef syspdef_CPUSCALE = {"CPUSCALE", sysprt_CPUSCALE, sysval_CPUSCALE}; /*******************************************************************/ static char * sysprt_CPUISCALE(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[32] = "scaling ?"; count_t maxfreq = sstat->cpu.cpu[as->index].freqcnt.maxfreq; count_t cnt = sstat->cpu.cpu[as->index].freqcnt.cnt; count_t ticks = sstat->cpu.cpu[as->index].freqcnt.ticks; dofmt_cpuscale(buf, maxfreq, cnt, ticks); return buf; } sys_printdef syspdef_CPUISCALE = {"CPUISCALE", sysprt_CPUISCALE, sysval_CPUSCALE}; /*******************************************************************/ static char * sysprt_CPUSTEAL(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[15]; float perc = sstat->cpu.all.steal * 100.0 / as->percputot; if (perc > 1.0) *color = -1; snprintf(buf, sizeof buf, "steal %5.0f%%", perc); return buf; } sys_printdef syspdef_CPUSTEAL = {"CPUSTEAL", sysprt_CPUSTEAL, NULL}; /*******************************************************************/ static char * sysprt_CPUISTEAL(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[15]; float perc = sstat->cpu.cpu[as->index].steal * 100.0 / as->percputot; if (perc > 1.0) *color = -1; snprintf(buf, sizeof buf, "steal %5.0f%%", perc); return buf; } sys_printdef syspdef_CPUISTEAL = {"CPUISTEAL", sysprt_CPUISTEAL, NULL}; /*******************************************************************/ static char * sysprt_CPUGUEST(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[15]; float perc = sstat->cpu.all.guest * 100.0 / as->percputot; if (perc > 1.0) *color = -1; snprintf(buf, sizeof buf, "guest %5.0f%%", perc); return buf; } sys_printdef syspdef_CPUGUEST = {"CPUGUEST", sysprt_CPUGUEST, NULL}; /*******************************************************************/ static char * sysprt_CPUIGUEST(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[15]; float perc = sstat->cpu.cpu[as->index].guest * 100.0 / as->percputot; if (perc > 1.0) *color = -1; snprintf(buf, sizeof buf, "guest %5.0f%%", perc); return buf; } sys_printdef syspdef_CPUIGUEST = {"CPUIGUEST", sysprt_CPUIGUEST, NULL}; /*******************************************************************/ static char * sysprt_CPUIPC(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[15]; float ipc = 0.0; switch (sstat->cpu.all.cycle) { case 0: snprintf(buf, sizeof buf, "ipc notavail"); break; case 1: *color = FGCOLORINFO; snprintf(buf, sizeof buf, "ipc initial"); break; default: ipc = sstat->cpu.all.instr * 100 / sstat->cpu.all.cycle / 100.0; snprintf(buf, sizeof buf, "ipc %8.2f", ipc); } return buf; } static int sysval_IPCVALIDATE(struct sstat *sstat) { return sstat->cpu.all.cycle; } sys_printdef syspdef_CPUIPC = {"CPUIPC", sysprt_CPUIPC, sysval_IPCVALIDATE}; /*******************************************************************/ static char * sysprt_CPUIIPC(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[15]; float ipc = 0.0; switch (sstat->cpu.all.cycle) { case 0: snprintf(buf, sizeof buf, "ipc notavail"); break; case 1: *color = FGCOLORINFO; snprintf(buf, sizeof buf, "ipc initial"); break; default: if (sstat->cpu.cpu[as->index].cycle) ipc = sstat->cpu.cpu[as->index].instr * 100 / sstat->cpu.cpu[as->index].cycle / 100.0; snprintf(buf, sizeof buf, "ipc %8.2f", ipc); } return buf; } sys_printdef syspdef_CPUIIPC = {"CPUIIPC", sysprt_CPUIIPC, sysval_IPCVALIDATE}; /*******************************************************************/ static char * sysprt_CPUCYCLE(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[15] = "cycl "; switch (sstat->cpu.all.cycle) { case 0: snprintf(buf+5, sizeof buf-5, "missing"); break; case 1: *color = FGCOLORINFO; snprintf(buf+5, sizeof buf-5, "initial"); break; default: val2Hzstr(sstat->cpu.all.cycle/1000000/as->nsecs/ sstat->cpu.nrcpu, buf+5); } return buf; } sys_printdef syspdef_CPUCYCLE = {"CPUCYCLE", sysprt_CPUCYCLE, sysval_IPCVALIDATE}; /*******************************************************************/ static char * sysprt_CPUICYCLE(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[15] = "cycl "; switch (sstat->cpu.all.cycle) { case 0: snprintf(buf+5, sizeof buf-5, "missing"); break; case 1: *color = FGCOLORINFO; snprintf(buf+5, sizeof buf-5, "initial"); break; default: val2Hzstr(sstat->cpu.cpu[as->index].cycle/1000000/ as->nsecs, buf+5); } return buf; } sys_printdef syspdef_CPUICYCLE = {"CPUICYCLE", sysprt_CPUICYCLE, sysval_IPCVALIDATE}; /*******************************************************************/ static char * sysprt_CPLAVG1(struct sstat *sstat, extraparam *notused, int badness, int *color) { static char buf[17]="avg1 "; if (sstat->cpu.lavg1 > 999999.0) { snprintf(buf+5, sizeof buf-5, ">999999"); } else if (sstat->cpu.lavg1 > 999.0) { snprintf(buf+5, sizeof buf-5, "%7.0f", sstat->cpu.lavg1); } else { snprintf(buf+5, sizeof buf-5, "%7.2f", sstat->cpu.lavg1); } return buf; } sys_printdef syspdef_CPLAVG1 = {"CPLAVG1", sysprt_CPLAVG1, NULL}; /*******************************************************************/ static char * sysprt_CPLAVG5(struct sstat *sstat, extraparam *notused, int badness, int *color) { static char buf[15]="avg5 "; if (sstat->cpu.lavg5 > 999999.0) { snprintf(buf+5, sizeof buf-5, ">999999"); } else if (sstat->cpu.lavg5 > 999.0) { snprintf(buf+5, sizeof buf-5, "%7.0f", sstat->cpu.lavg5); } else { snprintf(buf+5, sizeof buf-5, "%7.2f", sstat->cpu.lavg5); } return buf; } sys_printdef syspdef_CPLAVG5 = {"CPLAVG5", sysprt_CPLAVG5, NULL}; /*******************************************************************/ static char * sysprt_CPLAVG15(struct sstat *sstat, extraparam *notused, int badness, int *color) { static char buf[15]="avg15 "; if (sstat->cpu.lavg15 > (2 * sstat->cpu.nrcpu) ) *color = FGCOLORALMOST; if (sstat->cpu.lavg15 > 99999.0) { snprintf(buf+6, sizeof buf-6, ">99999"); } else if (sstat->cpu.lavg15 > 999.0) { snprintf(buf+6, sizeof buf-6, "%6.0f", sstat->cpu.lavg15); } else { snprintf(buf+6, sizeof buf-6, "%6.2f", sstat->cpu.lavg15); } return buf; } sys_printdef syspdef_CPLAVG15 = {"CPLAVG15", sysprt_CPLAVG15, NULL}; /*******************************************************************/ static char * sysprt_CPLCSW(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="csw "; val2valstr(sstat->cpu.csw, buf+4 , 8,as->avgval,as->nsecs); return buf; } sys_printdef syspdef_CPLCSW = {"CPLCSW", sysprt_CPLCSW, NULL}; /*******************************************************************/ static char * sysprt_PRCCLONES(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="clones "; val2valstr(sstat->cpu.nprocs, buf+7 , 5,as->avgval,as->nsecs); return buf; } sys_printdef syspdef_PRCCLONES = {"PRCCLONES", sysprt_PRCCLONES, NULL}; /*******************************************************************/ static char * sysprt_CPLNUMCPU(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="numcpu "; val2valstr(sstat->cpu.nrcpu, buf+7 , 5,0,as->nsecs); return buf; } sys_printdef syspdef_CPLNUMCPU = {"CPLNUMCPU", sysprt_CPLNUMCPU, NULL}; /*******************************************************************/ static char * sysprt_CPLINTR(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="intr "; val2valstr(sstat->cpu.devint, buf+5 , 7,as->avgval,as->nsecs); return buf; } sys_printdef syspdef_CPLINTR = {"CPLINTR", sysprt_CPLINTR, NULL}; /*******************************************************************/ static char * sysprt_GPUBUS(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]; char *pn; int len; if ( (len = strlen(sstat->gpu.gpu[as->index].busid)) > 9) pn = sstat->gpu.gpu[as->index].busid + len - 9; else pn = sstat->gpu.gpu[as->index].busid; snprintf(buf, sizeof buf, "%9.9s %2d", pn, sstat->gpu.gpu[as->index].gpunr); return buf; } sys_printdef syspdef_GPUBUS = {"GPUBUS", sysprt_GPUBUS, NULL}; /*******************************************************************/ static char * sysprt_GPUTYPE(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]; char *pn; int len; if ( (len = strlen(sstat->gpu.gpu[as->index].type)) > 12) pn = sstat->gpu.gpu[as->index].type + len - 12; else pn = sstat->gpu.gpu[as->index].type; snprintf(buf, sizeof buf, "%12.12s", pn); return buf; } sys_printdef syspdef_GPUTYPE = {"GPUTYPE", sysprt_GPUTYPE, NULL}; /*******************************************************************/ static char * sysprt_GPUNRPROC(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16] = "#proc "; val2valstr(sstat->gpu.gpu[as->index].nrprocs, buf+6, 6, 0, 0); return buf; } sys_printdef syspdef_GPUNRPROC = {"GPUNRPROC", sysprt_GPUNRPROC, NULL}; /*******************************************************************/ static char * sysprt_GPUMEMPERC(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="membusy "; int perc = sstat->gpu.gpu[as->index].mempercnow; if (perc == -1) { snprintf(buf+8, sizeof buf-8, " N/A"); } else { // preferably take the average percentage over sample if (sstat->gpu.gpu[as->index].samples) perc = sstat->gpu.gpu[as->index].memperccum / sstat->gpu.gpu[as->index].samples; if (perc >= 40) *color = FGCOLORALMOST; snprintf(buf+8, sizeof buf-8, "%3d%%", perc); } return buf; } sys_printdef syspdef_GPUMEMPERC = {"GPUMEMPERC", sysprt_GPUMEMPERC, NULL}; /*******************************************************************/ static char * sysprt_GPUGPUPERC(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="gpubusy "; int perc = sstat->gpu.gpu[as->index].gpupercnow; if (perc == -1) // metric not available? { snprintf(buf+8, sizeof buf-8, " N/A"); } else { // preferably take the average percentage over sample if (sstat->gpu.gpu[as->index].samples) perc = sstat->gpu.gpu[as->index].gpuperccum / sstat->gpu.gpu[as->index].samples; if (perc >= 90) *color = FGCOLORALMOST; snprintf(buf+8, sizeof buf-8, "%3d%%", perc); } return buf; } sys_printdef syspdef_GPUGPUPERC = {"GPUGPUPERC", sysprt_GPUGPUPERC, NULL}; /*******************************************************************/ static char * sysprt_GPUMEMOCC(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="memocc "; int perc; perc = sstat->gpu.gpu[as->index].memusenow * 100 / (sstat->gpu.gpu[as->index].memtotnow ? sstat->gpu.gpu[as->index].memtotnow : 1); snprintf(buf+7, sizeof buf-7, "%4d%%", perc); return buf; } sys_printdef syspdef_GPUMEMOCC = {"GPUMEMOCC", sysprt_GPUMEMOCC, NULL}; /*******************************************************************/ static char * sysprt_GPUMEMTOT(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16] = "total "; val2memstr(sstat->gpu.gpu[as->index].memtotnow * 1024, buf+6, MBFORMAT, 0, 0); return buf; } sys_printdef syspdef_GPUMEMTOT = {"GPUMEMTOT", sysprt_GPUMEMTOT, NULL}; /*******************************************************************/ static char * sysprt_GPUMEMUSE(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16] = "used "; val2memstr(sstat->gpu.gpu[as->index].memusenow * 1024, buf+6, MBFORMAT, 0, 0); return buf; } sys_printdef syspdef_GPUMEMUSE = {"GPUMEMUSE", sysprt_GPUMEMUSE, NULL}; /*******************************************************************/ static char * sysprt_GPUMEMAVG(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16] = "usavg "; if (sstat->gpu.gpu[as->index].samples) val2memstr(sstat->gpu.gpu[as->index].memusecum * 1024 / sstat->gpu.gpu[as->index].samples, buf+6, MBFORMAT, 0, 0); else return "usavg ?"; return buf; } sys_printdef syspdef_GPUMEMAVG = {"GPUMEMAVG", sysprt_GPUMEMAVG, NULL}; /*******************************************************************/ static char * sysprt_MEMTOT(struct sstat *sstat, extraparam *notused, int badness, int *color) { static char buf[16]="tot "; *color = -1; val2memstr(sstat->mem.physmem * pagesize, buf+6, MBFORMAT, 0, 0); return buf; } sys_printdef syspdef_MEMTOT = {"MEMTOT", sysprt_MEMTOT, NULL}; /*******************************************************************/ static char * sysprt_MEMFREE(struct sstat *sstat, extraparam *notused, int badness, int *color) { static char buf[16]="free "; *color = -1; val2memstr(sstat->mem.freemem * pagesize, buf+6, MBFORMAT, 0, 0); return buf; } sys_printdef syspdef_MEMFREE = {"MEMFREE", sysprt_MEMFREE, NULL}; /*******************************************************************/ static char * sysprt_MEMAVAIL(struct sstat *sstat, extraparam *notused, int badness, int *color) { static char buf[16]="avail "; *color = -1; val2memstr(sstat->mem.availablemem * pagesize, buf+6, MBFORMAT, 0, 0); return buf; } sys_printdef syspdef_MEMAVAIL = {"MEMAVAIL", sysprt_MEMAVAIL, NULL}; /*******************************************************************/ static char * sysprt_MEMCACHE(struct sstat *sstat, extraparam *notused, int badness, int *color) { static char buf[16]="cache "; *color = -1; val2memstr(sstat->mem.cachemem * pagesize, buf+6, MBFORMAT, 0, 0); return buf; } sys_printdef syspdef_MEMCACHE = {"MEMCACHE", sysprt_MEMCACHE, NULL}; /*******************************************************************/ static char * sysprt_MEMDIRTY(struct sstat *sstat, extraparam *notused, int badness, int *color) { static char buf[16] = "dirty "; val2memstr(sstat->mem.cachedrt * pagesize, buf+6, MBFORMAT, 0, 0); return buf; } sys_printdef syspdef_MEMDIRTY = {"MEMDIRTY", sysprt_MEMDIRTY, NULL}; /*******************************************************************/ static char * sysprt_MEMBUFFER(struct sstat *sstat, extraparam *notused, int badness, int *color) { static char buf[16]="buff "; *color = -1; val2memstr(sstat->mem.buffermem * pagesize, buf+6, MBFORMAT, 0, 0); return buf; } sys_printdef syspdef_MEMBUFFER = {"MEMBUFFER", sysprt_MEMBUFFER, NULL}; /*******************************************************************/ static char * sysprt_MEMSLAB(struct sstat *sstat, extraparam *notused, int badness, int *color) { static char buf[16]="slab "; *color = -1; val2memstr(sstat->mem.slabmem * pagesize, buf+6, MBFORMAT, 0, 0); return buf; } sys_printdef syspdef_MEMSLAB = {"MEMSLAB", sysprt_MEMSLAB, NULL}; /*******************************************************************/ static char * sysprt_RECSLAB(struct sstat *sstat, extraparam *notused, int badness, int *color) { static char buf[16]="slrec "; *color = -1; val2memstr(sstat->mem.slabreclaim * pagesize, buf+6, MBFORMAT, 0, 0); return buf; } sys_printdef syspdef_RECSLAB = {"RECSLAB", sysprt_RECSLAB, NULL}; /*******************************************************************/ static char * sysprt_PAGETABS(struct sstat *sstat, extraparam *notused, int badness, int *color) { static char buf[16]="pgtab "; *color = -1; val2memstr(sstat->mem.pagetables * pagesize, buf+6, MBFORMAT, 0, 0); return buf; } sys_printdef syspdef_PAGETABS = {"PAGETABS", sysprt_PAGETABS, NULL}; /*******************************************************************/ static char * sysprt_SHMEM(struct sstat *sstat, extraparam *notused, int badness, int *color) { static char buf[16]="shmem "; *color = -1; val2memstr(sstat->mem.shmem * pagesize, buf+6, MBFORMAT, 0, 0); return buf; } sys_printdef syspdef_SHMEM = {"SHMEM", sysprt_SHMEM, NULL}; /*******************************************************************/ static char * sysprt_SHMRSS(struct sstat *sstat, extraparam *notused, int badness, int *color) { static char buf[16]="shrss "; *color = -1; val2memstr(sstat->mem.shmrss * pagesize, buf+6, MBFORMAT, 0, 0); return buf; } sys_printdef syspdef_SHMRSS = {"SHMRSS", sysprt_SHMRSS, NULL}; /*******************************************************************/ static char * sysprt_SHMSWP(struct sstat *sstat, extraparam *notused, int badness, int *color) { static char buf[16]="shswp "; *color = -1; val2memstr(sstat->mem.shmswp * pagesize, buf+6, MBFORMAT, 0, 0); return buf; } sys_printdef syspdef_SHMSWP = {"SHMSWP", sysprt_SHMSWP, NULL}; /*******************************************************************/ static char * sysprt_ANONTHP(struct sstat *sstat, extraparam *notused, int badness, int *color) { static char buf[16]="anthp "; *color = -1; val2memstr(sstat->mem.anonhugepage * pagesize, buf+6, MBFORMAT, 0, 0); return buf; } static int sysval_ANONTHP(struct sstat *sstat) { return sstat->mem.anonhugepage; } sys_printdef syspdef_ANONTHP = {"ANONTHP", sysprt_ANONTHP, sysval_ANONTHP}; /*******************************************************************/ static char * sysprt_HUPTOT(struct sstat *sstat, extraparam *notused, int badness, int *color) { static char buf[16]="hptot "; *color = -1; val2memstr(sstat->mem.stothugepage * sstat->mem.shugepagesz + sstat->mem.ltothugepage * sstat->mem.lhugepagesz, buf+6, MBFORMAT, 0, 0); return buf; } static int sysval_HUPTOT(struct sstat *sstat) { return sstat->mem.stothugepage + sstat->mem.ltothugepage; } sys_printdef syspdef_HUPTOT = {"HUPTOT", sysprt_HUPTOT, sysval_HUPTOT}; /*******************************************************************/ static char * sysprt_HUPUSE(struct sstat *sstat, extraparam *notused, int badness, int *color) { static char buf[16]="hpuse "; *color = -1; val2memstr( (sstat->mem.stothugepage - sstat->mem.sfreehugepage) * sstat->mem.shugepagesz + (sstat->mem.ltothugepage - sstat->mem.lfreehugepage) * sstat->mem.lhugepagesz, buf+6, MBFORMAT, 0, 0); return buf; } static int sysval_HUPUSE(struct sstat *sstat) { return sstat->mem.stothugepage + sstat->mem.ltothugepage; } sys_printdef syspdef_HUPUSE = {"HUPUSE", sysprt_HUPUSE, sysval_HUPUSE}; /*******************************************************************/ static char * sysprt_VMWBAL(struct sstat *sstat, extraparam *notused, int badness, int *color) { static char buf[16]="vmbal "; *color = -1; val2memstr(sstat->mem.vmwballoon * pagesize, buf+6, MBFORMAT, 0, 0); return buf; } static int sysval_VMWBAL(struct sstat *sstat) { if (sstat->mem.vmwballoon == -1) return 0; else return 1; } sys_printdef syspdef_VMWBAL = {"VMWBAL", sysprt_VMWBAL, sysval_VMWBAL}; /*******************************************************************/ static char * sysprt_ZFSARC(struct sstat *sstat, extraparam *notused, int badness, int *color) { static char buf[16]="zfarc "; if (sstat->mem.zfsarcsize == -1) { val2memstr(0, buf+6, MBFORMAT, 0, 0); } else { *color = -1; val2memstr(sstat->mem.zfsarcsize * pagesize, buf+6, MBFORMAT, 0, 0); } return buf; } static int sysval_ZFSARC(struct sstat *sstat) { if (sstat->mem.zfsarcsize == -1) return 0; else return 1; } sys_printdef syspdef_ZFSARC = {"ZFSARC", sysprt_ZFSARC, sysval_ZFSARC}; /*******************************************************************/ static char * sysprt_SWPTOT(struct sstat *sstat, extraparam *notused, int badness, int *color) { static char buf[16]="tot "; *color = -1; val2memstr(sstat->mem.totswap * pagesize, buf+6, MBFORMAT, 0, 0); return buf; } sys_printdef syspdef_SWPTOT = {"SWPTOT", sysprt_SWPTOT, NULL}; /*******************************************************************/ static char * sysprt_SWPFREE(struct sstat *sstat, extraparam *notused, int badness, int *color) { static char buf[16]="free "; *color = -1; val2memstr(sstat->mem.freeswap * pagesize, buf+6, MBFORMAT, 0, 0); return buf; } sys_printdef syspdef_SWPFREE = {"SWPFREE", sysprt_SWPFREE, NULL}; /*******************************************************************/ static char * sysprt_SWPCACHE(struct sstat *sstat, extraparam *notused, int badness, int *color) { static char buf[16]="swcac "; *color = -1; val2memstr(sstat->mem.swapcached * pagesize, buf+6, MBFORMAT, 0, 0); return buf; } sys_printdef syspdef_SWPCACHE = {"SWPCACHE", sysprt_SWPCACHE, NULL}; /*******************************************************************/ static char * sysprt_ZSWPOOL(struct sstat *sstat, extraparam *notused, int badness, int *color) { static char buf[16]="zswap "; *color = -1; val2memstr(sstat->mem.zswap * pagesize, buf+6, MBFORMAT, 0, 0); return buf; } sys_printdef syspdef_ZSWPOOL = {"ZSWPOOL", sysprt_ZSWPOOL, NULL}; /*******************************************************************/ static char * sysprt_ZSWSTORED(struct sstat *sstat, extraparam *notused, int badness, int *color) { static char buf[16]="zstor "; *color = -1; val2memstr(sstat->mem.zswapped * pagesize, buf+6, MBFORMAT, 0, 0); return buf; } sys_printdef syspdef_ZSWSTORED = {"ZSWSTORED", sysprt_ZSWSTORED, NULL}; /*******************************************************************/ static char * sysprt_TCPSOCK(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="tcpsk "; val2memstr(sstat->mem.tcpsock * pagesize, buf+6, MBFORMAT, 0, 0); return buf; } sys_printdef syspdef_TCPSOCK = {"TCPSOCK", sysprt_TCPSOCK, NULL}; /*******************************************************************/ static char * sysprt_UDPSOCK(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="udpsk "; val2memstr(sstat->mem.udpsock * pagesize, buf+6, MBFORMAT, 0, 0); return buf; } sys_printdef syspdef_UDPSOCK = {"UDPSOCK", sysprt_UDPSOCK, NULL}; /*******************************************************************/ static char * sysprt_KSMSHARING(struct sstat *sstat, extraparam *notused, int badness, int *color) { static char buf[16]="kssav "; if (sstat->mem.ksmsharing == -1) { val2memstr(0, buf+6, MBFORMAT, 0, 0); } else { *color = -1; val2memstr(sstat->mem.ksmsharing * pagesize, buf+6, MBFORMAT, 0, 0); } return buf; } static int sysval_KSMSHARING(struct sstat *sstat) { if (sstat->mem.ksmsharing == -1) return 0; else return 1; } sys_printdef syspdef_KSMSHARING = {"KSMSHARING", sysprt_KSMSHARING, sysval_KSMSHARING}; /*******************************************************************/ static char * sysprt_KSMSHARED(struct sstat *sstat, extraparam *notused, int badness, int *color) { static char buf[16]="ksuse "; if (sstat->mem.ksmshared == -1) { val2memstr(0, buf+6, MBFORMAT, 0, 0); } else { *color = -1; val2memstr(sstat->mem.ksmshared * pagesize, buf+6, MBFORMAT, 0, 0); } return buf; } static int sysval_KSMSHARED(struct sstat *sstat) { if (sstat->mem.ksmshared == -1) return 0; else return 1; } sys_printdef syspdef_KSMSHARED = {"KSMSHARED", sysprt_KSMSHARED, sysval_KSMSHARED}; /*******************************************************************/ static char * sysprt_NUMNUMA(struct sstat *sstat, extraparam *notused, int badness, int *color) { static char buf[16]="numnode "; val2valstr(sstat->memnuma.nrnuma, buf+8, 4, 0, 0); return buf; } sys_printdef syspdef_NUMNUMA = {"NUMNUMA", sysprt_NUMNUMA, NULL}; /*******************************************************************/ static char * sysprt_SWPCOMMITTED(struct sstat *sstat, extraparam *notused, int badness, int *color) { static char buf[16]="vmcom "; val2memstr(sstat->mem.committed * pagesize, buf+6, MBFORMAT, 0, 0); if (sstat->mem.commitlim && sstat->mem.committed > sstat->mem.commitlim) *color = FGCOLORALMOST; return buf; } sys_printdef syspdef_SWPCOMMITTED = {"SWPCOMMITTED", sysprt_SWPCOMMITTED, NULL}; /*******************************************************************/ static char * sysprt_SWPCOMMITLIM(struct sstat *sstat, extraparam *notused, int badness, int *color) { static char buf[16]="vmlim "; val2memstr(sstat->mem.commitlim * pagesize, buf+6, MBFORMAT, 0, 0); if (sstat->mem.commitlim && sstat->mem.committed > sstat->mem.commitlim) *color = FGCOLORINFO; return buf; } sys_printdef syspdef_SWPCOMMITLIM = {"SWPCOMMITLIM", sysprt_SWPCOMMITLIM, NULL}; /*******************************************************************/ static char * sysprt_PAGSCAN(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="scan "; val2valstr(sstat->mem.pgscans, buf+5, 7, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_PAGSCAN = {"PAGSCAN", sysprt_PAGSCAN, NULL}; /*******************************************************************/ static char * sysprt_PAGSTEAL(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="steal "; val2valstr(sstat->mem.pgsteal, buf+ 6, 6, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_PAGSTEAL = {"PAGSTEAL", sysprt_PAGSTEAL, NULL}; /*******************************************************************/ static char * sysprt_PAGSTALL(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="stall "; val2valstr(sstat->mem.allocstall, buf+6, 6, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_PAGSTALL = {"PAGSTALL", sysprt_PAGSTALL, NULL}; /*******************************************************************/ static char * sysprt_PAGCOMPACT(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="compact "; val2valstr(sstat->mem.compactstall, buf+8, 4, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_PAGCOMPACT = {"PAGCOMPACT", sysprt_PAGCOMPACT, NULL}; /*******************************************************************/ static char * sysprt_NUMAMIGRATE(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="numamig "; val2valstr(sstat->mem.numamigrate, buf+8, 4, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_NUMAMIGRATE = {"NUMAMIGRATE", sysprt_NUMAMIGRATE, NULL}; /*******************************************************************/ static char * sysprt_PGMIGRATE(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="migrate "; val2valstr(sstat->mem.pgmigrate, buf+8, 4, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_PGMIGRATE = {"PGMIGRATE", sysprt_PGMIGRATE, NULL}; /*******************************************************************/ static char * sysprt_PAGPGIN(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="pgin "; val2valstr(sstat->mem.pgins, buf+5, 7, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_PAGPGIN = {"PAGPGIN", sysprt_PAGPGIN, NULL}; /*******************************************************************/ static char * sysprt_PAGPGOUT(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="pgout "; val2valstr(sstat->mem.pgouts, buf+6, 6, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_PAGPGOUT = {"PAGPGOUT", sysprt_PAGPGOUT, NULL}; /*******************************************************************/ static char * sysprt_PAGSWIN(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="swin "; val2valstr(sstat->mem.swins, buf+5, 7, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_PAGSWIN = {"PAGSWIN", sysprt_PAGSWIN, NULL}; /*******************************************************************/ static char * sysprt_PAGSWOUT(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="swout "; *color = -1; val2valstr(sstat->mem.swouts, buf+6, 6, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_PAGSWOUT = {"PAGSWOUT", sysprt_PAGSWOUT, NULL}; /*******************************************************************/ static char * sysprt_PAGZSWIN(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="zswin "; val2valstr(sstat->mem.zswins, buf+6, 6, as->avgval, as->nsecs); return buf; } static int sysval_ZSWAP(struct sstat *sstat) { return supportflags & ZSWAP; } sys_printdef syspdef_PAGZSWIN = {"PAGZSWIN", sysprt_PAGZSWIN, sysval_ZSWAP}; /*******************************************************************/ static char * sysprt_PAGZSWOUT(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="zswout "; *color = -1; val2valstr(sstat->mem.zswouts, buf+7, 5, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_PAGZSWOUT = {"PAGZSWOUT", sysprt_PAGZSWOUT, sysval_ZSWAP}; /*******************************************************************/ static char * sysprt_OOMKILLS(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="oomkill "; if (sstat->mem.oomkills) *color = FGCOLORCRIT; val2valstr(sstat->mem.oomkills, buf+8, 4, as->avgval, as->nsecs); return buf; } static int sysval_OOMKILLS(struct sstat *sstat) { if (sstat->mem.oomkills == -1) // non-existing? return 0; else return 1; } sys_printdef syspdef_OOMKILLS = {"OOMKILLS", sysprt_OOMKILLS, sysval_OOMKILLS}; /*******************************************************************/ static char * sysprt_NUMATOT(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="tot "; *color = -1; val2memstr(sstat->memnuma.numa[as->index].totmem * pagesize, buf+6, MBFORMAT, 0, 0); return buf; } sys_printdef syspdef_NUMATOT = {"NUMATOT", sysprt_NUMATOT, NULL}; /*******************************************************************/ static char * sysprt_NUMAFREE(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="free "; *color = -1; val2memstr(sstat->memnuma.numa[as->index].freemem * pagesize, buf+6, MBFORMAT, 0, 0); return buf; } sys_printdef syspdef_NUMAFREE = {"NUMAFREE", sysprt_NUMAFREE, NULL}; /*******************************************************************/ static char * sysprt_NUMAFILE(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="file "; *color = -1; val2memstr(sstat->memnuma.numa[as->index].filepage * pagesize, buf+6, MBFORMAT, 0, 0); return buf; } sys_printdef syspdef_NUMAFILEPAGE = {"NUMAFILEPAGE", sysprt_NUMAFILE, NULL}; /*******************************************************************/ static char * sysprt_NUMANR(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]; *color = -1; snprintf(buf, sizeof buf, "numanode%04d", sstat->memnuma.numa[as->index].numanr); return buf; } sys_printdef syspdef_NUMANR = {"NUMANR", sysprt_NUMANR, NULL}; /*******************************************************************/ static char * sysprt_NUMADIRTY(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="dirty "; *color = -1; val2memstr(sstat->memnuma.numa[as->index].dirtymem * pagesize, buf+6, MBFORMAT, 0, 0); return buf; } sys_printdef syspdef_NUMADIRTY = {"NUMADIRTY", sysprt_NUMADIRTY, NULL}; /*******************************************************************/ static char * sysprt_NUMAACTIVE(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="activ "; *color = -1; val2memstr(sstat->memnuma.numa[as->index].active * pagesize, buf+6, MBFORMAT, 0, 0); return buf; } sys_printdef syspdef_NUMAACTIVE = {"NUMAACTIVE", sysprt_NUMAACTIVE, NULL}; /*******************************************************************/ static char * sysprt_NUMAINACTIVE(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="inact "; *color = -1; val2memstr(sstat->memnuma.numa[as->index].inactive * pagesize, buf+6, MBFORMAT, 0, 0); return buf; } sys_printdef syspdef_NUMAINACTIVE = {"NUMAINACTIVE", sysprt_NUMAINACTIVE, NULL}; /*******************************************************************/ static char * sysprt_NUMASLAB(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="slab "; *color = -1; val2memstr(sstat->memnuma.numa[as->index].slabmem * pagesize, buf+6, MBFORMAT, 0, 0); return buf; } sys_printdef syspdef_NUMASLAB = {"NUMASLAB", sysprt_NUMASLAB, NULL}; /*******************************************************************/ static char * sysprt_NUMASLABRECLAIM(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="slrec "; *color = -1; val2memstr(sstat->memnuma.numa[as->index].slabreclaim * pagesize, buf+6, MBFORMAT, 0, 0); return buf; } sys_printdef syspdef_NUMASLABRECLAIM = {"NUMASLABRECLAIM", sysprt_NUMASLABRECLAIM, NULL}; /*******************************************************************/ static char * sysprt_NUMASHMEM(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="shmem "; *color = -1; val2memstr(sstat->memnuma.numa[as->index].shmem * pagesize, buf+6, MBFORMAT, 0, 0); return buf; } sys_printdef syspdef_NUMASHMEM = {"NUMASHMEM", sysprt_NUMASHMEM, NULL}; /*******************************************************************/ static char * sysprt_NUMAFRAG(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[15]; float perc = sstat->memnuma.numa[as->index].frag * 100.0; if (perc > 1.0) *color = -1; snprintf(buf, sizeof buf, "frag %6.0f%%", perc); return buf; } sys_printdef syspdef_NUMAFRAG = {"NUMAFRAG", sysprt_NUMAFRAG, NULL}; /*******************************************************************/ static char * sysprt_NUMAHUPTOT(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="hptot "; if (sstat->mem.stothugepage == 0) return NULL; *color = -1; val2memstr(sstat->memnuma.numa[as->index].tothp * sstat->mem.shugepagesz, buf+6, MBFORMAT, 0, 0); return buf; } sys_printdef syspdef_NUMAHUPTOT = {"NUMAHUPTOT", sysprt_NUMAHUPTOT, sysval_HUPTOT}; /*******************************************************************/ static char * sysprt_NUMAHUPUSE(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="hpuse "; if (sstat->mem.stothugepage == 0) return NULL; *color = -1; val2memstr( (sstat->memnuma.numa[as->index].tothp - sstat->memnuma.numa[as->index].freehp) * sstat->mem.shugepagesz, buf+6, MBFORMAT, 0, 0); return buf; } sys_printdef syspdef_NUMAHUPUSE = {"NUMAHUPUSE", sysprt_NUMAHUPUSE, sysval_HUPUSE}; /*******************************************************************/ static char * sysprt_NUMANUMCPU(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="numcpu "; val2valstr(sstat->cpunuma.numa[as->index].nrcpu, buf+7, 5,0,as->nsecs); return buf; } sys_printdef syspdef_NUMANUMCPU = {"NUMANUMCPU", sysprt_NUMANUMCPU, NULL}; /*******************************************************************/ static char * sysprt_NUMACPUSYS(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[15]; float perc = sstat->cpunuma.numa[as->index].stime * 100.0 / as->percputot; if (perc > 1.0) *color = -1; snprintf(buf, sizeof buf, "sys %6.0f%%", perc); return buf; } sys_printdef syspdef_NUMACPUSYS = {"NUMACPUSYS", sysprt_NUMACPUSYS, NULL}; /*******************************************************************/ static char * sysprt_NUMACPUUSER(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[15]; float perc = sstat->cpunuma.numa[as->index].utime * 100.0 / as->percputot; if (perc > 1.0) *color = -1; snprintf(buf, sizeof buf, "user %6.0f%%", perc); return buf; } sys_printdef syspdef_NUMACPUUSER = {"NUMACPUUSER", sysprt_NUMACPUUSER, NULL}; /*******************************************************************/ static char * sysprt_NUMACPUNICE(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[15]; float perc = sstat->cpunuma.numa[as->index].ntime * 100.0 / as->percputot; if (perc > 1.0) *color = -1; snprintf(buf, sizeof buf, "nice %6.0f%%", perc); return buf; } sys_printdef syspdef_NUMACPUNICE = {"NUMACPUNICE", sysprt_NUMACPUNICE, NULL}; /*******************************************************************/ static char * sysprt_NUMACPUIRQ(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[15]; float perc = sstat->cpunuma.numa[as->index].Itime * 100.0 / as->percputot; if (perc > 1.0) *color = -1; snprintf(buf, sizeof buf, "irq %6.0f%%", perc); return buf; } sys_printdef syspdef_NUMACPUIRQ = {"NUMACPUIRQ", sysprt_NUMACPUIRQ, NULL}; /*******************************************************************/ static char * sysprt_NUMACPUSOFTIRQ(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[15]; float perc = sstat->cpunuma.numa[as->index].Stime * 100.0 / as->percputot; if (perc > 1.0) *color = -1; snprintf(buf, sizeof buf, "sirq %6.0f%%", perc); return buf; } sys_printdef syspdef_NUMACPUSOFTIRQ = {"NUMACPUSOFTIRQ", sysprt_NUMACPUSOFTIRQ, NULL}; /*******************************************************************/ static char * sysprt_NUMACPUIDLE(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[15]; snprintf(buf, sizeof buf, "idle %6.0f%%", (sstat->cpunuma.numa[as->index].itime * 100.0) / as->percputot); return buf; } sys_printdef syspdef_NUMACPUIDLE = {"NUMACPUIDLE", sysprt_NUMACPUIDLE, NULL}; /*******************************************************************/ static char * sysprt_NUMACPUWAIT(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[15]; snprintf(buf, sizeof buf, "nod%03d w%3.0f%%", sstat->cpunuma.numa[as->index].numanr, (sstat->cpunuma.numa[as->index].wtime * 100.0) / as->percputot); return buf; } sys_printdef syspdef_NUMACPUWAIT = {"NUMACPUWAIT", sysprt_NUMACPUWAIT, NULL}; /*******************************************************************/ static char * sysprt_NUMACPUSTEAL(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[15]; float perc = (sstat->cpunuma.numa[as->index].steal * 100.0) / as->percputot; if (perc > 1.0) *color = -1; snprintf(buf, sizeof buf, "steal %5.0f%%", perc); return buf; } sys_printdef syspdef_NUMACPUSTEAL = {"NUMACPUSTEAL", sysprt_NUMACPUSTEAL, NULL}; /*******************************************************************/ static char * sysprt_NUMACPUGUEST(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[15]; float perc = (sstat->cpunuma.numa[as->index].guest * 100.0) / as->percputot; if (perc > 1.0) *color = -1; snprintf(buf, sizeof buf, "guest %5.0f%%", perc); return buf; } sys_printdef syspdef_NUMACPUGUEST = {"NUMACPUGUEST", sysprt_NUMACPUGUEST, NULL}; /*******************************************************************/ static char * sysprt_LLCMBMTOTAL(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="tot "; *color = -1; val2memstr(sstat->llc.perllc[as->index].mbm_total, buf+6, MBFORMAT, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_LLCMBMTOTAL = {"LLCMBMTOTAL", sysprt_LLCMBMTOTAL, NULL}; /*******************************************************************/ static char * sysprt_LLCMBMLOCAL(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="loc "; *color = -1; val2memstr(sstat->llc.perllc[as->index].mbm_local, buf+6, MBFORMAT, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_LLCMBMLOCAL = {"LLCMBMLOCAL", sysprt_LLCMBMLOCAL, NULL}; /*******************************************************************/ static char * sysprt_NUMLLC(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]; *color = -1; snprintf(buf, sizeof buf, "LLC%02d %5.0f%%", sstat->llc.perllc[as->index].id, sstat->llc.perllc[as->index].occupancy * 100); return buf; } sys_printdef syspdef_NUMLLC = {"NUMLLC", sysprt_NUMLLC, NULL}; /*******************************************************************/ // general formatting of PSI field in avg10/avg60/avg300 static void psiformatavg(struct psi *p, char *head, char *buf, int bufsize) { static char formats[] = "%.0f/%.0f/%.0f"; char tmpbuf[32]; snprintf(tmpbuf, sizeof tmpbuf, formats, p->avg10, p->avg60, p->avg300); if (strlen(tmpbuf) > 9) // reformat needed? { float avg10 = p->avg10; float avg60 = p->avg60; float avg300 = p->avg300; if (avg10 > 99.0) avg10 = 99.0; if (avg60 > 99.0) avg60 = 99.0; if (avg300 > 99.0) avg300 = 99.0; snprintf(tmpbuf, sizeof tmpbuf, formats, avg10, avg60, avg300); } snprintf(buf, bufsize, "%s %9.9s", head, tmpbuf); } static char * sysprt_PSICPUS(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]; psiformatavg(&(sstat->psi.cpusome), "cs", buf, sizeof buf); return buf; } sys_printdef syspdef_PSICPUS = {"PSICPUS", sysprt_PSICPUS, NULL}; static char * sysprt_PSIMEMS(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]; psiformatavg(&(sstat->psi.memsome), "ms", buf, sizeof buf); return buf; } sys_printdef syspdef_PSIMEMS = {"PSIMEMS", sysprt_PSIMEMS, NULL}; static char * sysprt_PSIMEMF(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]; psiformatavg(&(sstat->psi.memfull), "mf", buf, sizeof buf); return buf; } sys_printdef syspdef_PSIMEMF = {"PSIMEMF", sysprt_PSIMEMF, NULL}; static char * sysprt_PSIIOS(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]; psiformatavg(&(sstat->psi.iosome), "is", buf, sizeof buf); return buf; } sys_printdef syspdef_PSIIOS = {"PSIIOS", sysprt_PSIIOS, NULL}; static char * sysprt_PSIIOF(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]; psiformatavg(&(sstat->psi.iofull), "if", buf, sizeof buf); return buf; } sys_printdef syspdef_PSIIOF = {"PSIIOF", sysprt_PSIIOF, NULL}; /*******************************************************************/ // general formatting of PSI field in total percentage static void psiformattot(struct psi *p, char *head, extraparam *as, int *color, char *buf, int bufsize) { static char formats[] = "%-7.7s %3lu%%"; unsigned long perc = p->total/((count_t)as->nsecs*10000); if (perc > 100) perc = 100; if (perc >= 1) *color = FGCOLORALMOST; snprintf(buf, bufsize, formats, head, perc); } static char * sysprt_PSICPUSTOT(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[32]; psiformattot(&(sstat->psi.cpusome), "cpusome", as, color, buf, sizeof buf); return buf; } sys_printdef syspdef_PSICPUSTOT = {"PSICPUSTOT", sysprt_PSICPUSTOT, NULL}; static char * sysprt_PSIMEMSTOT(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[32]; psiformattot(&(sstat->psi.memsome), "memsome", as, color, buf, sizeof buf); return buf; } sys_printdef syspdef_PSIMEMSTOT = {"PSIMEMSTOT", sysprt_PSIMEMSTOT, NULL}; static char * sysprt_PSIMEMFTOT(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[32]; psiformattot(&(sstat->psi.memfull), "memfull", as, color, buf, sizeof buf); return buf; } sys_printdef syspdef_PSIMEMFTOT = {"PSIMEMFTOT", sysprt_PSIMEMFTOT, NULL}; static char * sysprt_PSIIOSTOT(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[32]; psiformattot(&(sstat->psi.iosome), "iosome", as, color, buf, sizeof buf); return buf; } sys_printdef syspdef_PSIIOSTOT = {"PSIIOSTOT", sysprt_PSIIOSTOT, NULL}; static char * sysprt_PSIIOFTOT(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[32]; psiformattot(&(sstat->psi.iofull), "iofull", as, color, buf, sizeof buf); return buf; } sys_printdef syspdef_PSIIOFTOT = {"PSIIOFTOT", sysprt_PSIIOFTOT, NULL}; /*******************************************************************/ static char * sysprt_CONTNAME(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[32] = "ctid "; *color = -1; snprintf(buf+5, sizeof buf-5, "%7lu", sstat->cfs.cont[as->index].ctid); return buf; } sys_printdef syspdef_CONTNAME = {"CONTNAME", sysprt_CONTNAME, NULL}; /*******************************************************************/ static char * sysprt_CONTNPROC(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="nproc "; *color = -1; val2valstr(sstat->cfs.cont[as->index].numproc, buf+6, 6, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_CONTNPROC = {"CONTNPROC", sysprt_CONTNPROC, NULL}; /*******************************************************************/ static char * sysprt_CONTCPU(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]; float perc; count_t used = sstat->cfs.cont[as->index].system + sstat->cfs.cont[as->index].user + sstat->cfs.cont[as->index].nice; *color = -1; if (sstat->cfs.cont[as->index].uptime) { perc = used * 100.0 / sstat->cfs.cont[as->index].uptime; snprintf(buf, sizeof buf, "cpubusy %3.0f%%", perc); } else snprintf(buf, sizeof buf, "cpubusy ?%%"); return buf; } sys_printdef syspdef_CONTCPU = {"CONTCPU", sysprt_CONTCPU, NULL}; /*******************************************************************/ static char * sysprt_CONTMEM(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="mem "; *color = -1; val2memstr(sstat->cfs.cont[as->index].physpages * pagesize, buf+6, MBFORMAT, 0, 0); return buf; } sys_printdef syspdef_CONTMEM = {"CONTMEM", sysprt_CONTMEM, NULL}; /*******************************************************************/ static char * sysprt_DSKNAME(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]; char *pn; int len; *color = -1; if ( (len = strlen(as->perdsk[as->index].name)) > 12) pn = as->perdsk[as->index].name + len - 12; else pn = as->perdsk[as->index].name; snprintf(buf, sizeof buf, "%12.12s", pn); return buf; } sys_printdef syspdef_DSKNAME = {"DSKNAME", sysprt_DSKNAME, NULL}; /*******************************************************************/ static char * sysprt_DSKBUSY(struct sstat *sstat, extraparam *as, int badness, int *color) { double perc; static char buf[16]="busy "; *color = -1; perc = as->perdsk[as->index].io_ms * 100.0 / as->mstot; if (perc >= 0.0 && perc < 1000000.0) snprintf(buf+5, sizeof buf-5, "%6.0lf%%", perc); else snprintf(buf+5, sizeof buf-5, "%6.0lf%%", 999999.0); return buf; } sys_printdef syspdef_DSKBUSY = {"DSKBUSY", sysprt_DSKBUSY, NULL}; /*******************************************************************/ static char * sysprt_DSKNREAD(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="read "; *color = -1; val2valstr(as->perdsk[as->index].nread >= 0 ? as->perdsk[as->index].nread : 0, buf+5, 7, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_DSKNREAD = {"DSKNREAD", sysprt_DSKNREAD, NULL}; /*******************************************************************/ static char * sysprt_DSKNWRITE(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="write "; *color = -1; val2valstr(as->perdsk[as->index].nwrite >= 0 ? as->perdsk[as->index].nwrite : 0, buf+6, 6, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_DSKNWRITE = {"DSKNWRITE", sysprt_DSKNWRITE, NULL}; /*******************************************************************/ static char * sysprt_DSKNDISC(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="discrd "; *color = -1; // value might be -1 in case not supported --> "?" val2valstr(as->perdsk[as->index].ndisc, buf+7, 5, as->avgval, as->nsecs); return buf; } static int sysval_DSKNDISK(struct sstat *sstat) { if (sstat->dsk.ndsk > 0 && sstat->dsk.dsk[0].ndisc != -1) return 1; else return 0; } sys_printdef syspdef_DSKNDISC = {"DSKNDISC", sysprt_DSKNDISC, sysval_DSKNDISK}; /*******************************************************************/ static char * sysprt_DSKKBPERRD(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="KiB/r "; struct perdsk *dp = &(as->perdsk[as->index]); val2valstr(dp->nread > 0 ? dp->nrsect / dp->nread / 2 : 0, buf+6, 6, 0, as->nsecs); return buf; } sys_printdef syspdef_DSKKBPERRD = {"DSKKBPERRD", sysprt_DSKKBPERRD, NULL}; /*******************************************************************/ static char * sysprt_DSKKBPERWR(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="KiB/w "; struct perdsk *dp = &(as->perdsk[as->index]); val2valstr(dp->nwrite > 0 ? dp->nwsect / dp->nwrite / 2 : 0, buf+6, 6, 0, as->nsecs); return buf; } sys_printdef syspdef_DSKKBPERWR = {"DSKKBPERWR", sysprt_DSKKBPERWR, NULL}; /*******************************************************************/ static char * sysprt_DSKKBPERDS(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="KiB/d ?"; struct perdsk *dp = &(as->perdsk[as->index]); if (dp->ndisc != -1) val2valstr(dp->ndisc > 0 ? dp->ndsect/dp->ndisc/2 : 0, buf+6, 6, 0, as->nsecs); return buf; } sys_printdef syspdef_DSKKBPERDS = {"DSKKBPERDS", sysprt_DSKKBPERDS, sysval_DSKNDISK}; /*******************************************************************/ static char * sysprt_DSKMBPERSECWR(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="MBw/s "; struct perdsk *dp = &(as->perdsk[as->index]); snprintf(buf+6, sizeof buf-6, "%6.1lf", dp->nwsect / 2.0 / 1024 / as->nsecs); return buf; } sys_printdef syspdef_DSKMBPERSECWR = {"DSKMBPERSECWR", sysprt_DSKMBPERSECWR, NULL}; /*******************************************************************/ static char * sysprt_DSKMBPERSECRD(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="MBr/s "; struct perdsk *dp = &(as->perdsk[as->index]); snprintf(buf+6, sizeof buf-6, "%6.1lf", dp->nrsect / 2.0 / 1024 / as->nsecs); return buf; } sys_printdef syspdef_DSKMBPERSECRD = {"DSKMBPERSECRD", sysprt_DSKMBPERSECRD, NULL}; /*******************************************************************/ static char * sysprt_DSKINFLIGHT(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="inflt "; struct perdsk *dp = &(as->perdsk[as->index]); val2valstr(dp->inflight, buf+6, 6, 0, 0); return buf; } sys_printdef syspdef_DSKINFLIGHT = {"DSKINFLIGHT", sysprt_DSKINFLIGHT, NULL}; /*******************************************************************/ static char * sysprt_DSKAVQUEUE(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="avq "; struct perdsk *dp = &(as->perdsk[as->index]); snprintf(buf+4, sizeof buf-4, "%8.2f", dp->io_ms > 0 ? (double)dp->avque / dp->io_ms : 0.0); return buf; } sys_printdef syspdef_DSKAVQUEUE = {"DSKAVQUEUE", sysprt_DSKAVQUEUE, NULL}; /*******************************************************************/ static char * sysprt_DSKAVIO(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[32]="avio "; double avioms = as->iotot > 0 ? (double)(as->perdsk[as->index].io_ms)/as->iotot:0.0; *color = -1; if (avioms >= 9995.0) { val2valstr((unsigned long long)avioms / 1000, buf+5, 5, 0, 0); snprintf(buf+10, sizeof buf-10, " s"); } else if (avioms >= 99.95) { snprintf(buf+5, sizeof buf-5, "%4.0lf ms", avioms); } else if (avioms >= 9.995) { snprintf(buf+5, sizeof buf-5, "%4.1lf ms", avioms); } else if (avioms >= 0.09995) { snprintf(buf+5, sizeof buf-5, "%4.2lf ms", avioms); } else if (avioms >= 0.01) { snprintf(buf+5, sizeof buf-5, "%4.1lf µs", avioms * 1000.0); } else if (avioms >= 0.0001) { snprintf(buf+5, sizeof buf-5, "%4.2lf µs", avioms * 1000.0); } else { snprintf(buf+5, sizeof buf-5, "%4.1lf ns", avioms * 1000000.0); } return buf; } sys_printdef syspdef_DSKAVIO = {"DSKAVIO", sysprt_DSKAVIO, NULL}; /*******************************************************************/ static char * sysprt_NETTRANSPORT(struct sstat *sstat, extraparam *notused, int badness, int *color) { return "transport "; } sys_printdef syspdef_NETTRANSPORT = {"NETTRANSPORT", sysprt_NETTRANSPORT, NULL}; /*******************************************************************/ static char * sysprt_NETTCPI(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="tcpi "; val2valstr(sstat->net.tcp.InSegs, buf+5, 7, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_NETTCPI = {"NETTCPI", sysprt_NETTCPI, NULL}; /*******************************************************************/ static char * sysprt_NETTCPO(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="tcpo "; val2valstr(sstat->net.tcp.OutSegs, buf+5, 7, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_NETTCPO = {"NETTCPO", sysprt_NETTCPO, NULL}; /*******************************************************************/ static char * sysprt_NETTCPACTOPEN(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="tcpao "; val2valstr(sstat->net.tcp.ActiveOpens, buf+6, 6, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_NETTCPACTOPEN = {"NETTCPACTOPEN", sysprt_NETTCPACTOPEN, NULL}; /*******************************************************************/ static char * sysprt_NETTCPPASVOPEN(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="tcppo "; val2valstr(sstat->net.tcp.PassiveOpens, buf+6, 6, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_NETTCPPASVOPEN = {"NETTCPPASVOPEN", sysprt_NETTCPPASVOPEN, NULL}; /*******************************************************************/ static char * sysprt_NETTCPRETRANS(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="tcprs "; val2valstr(sstat->net.tcp.RetransSegs, buf+6, 6, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_NETTCPRETRANS = {"NETTCPRETRANS", sysprt_NETTCPRETRANS, NULL}; /*******************************************************************/ static char * sysprt_NETTCPINERR(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="tcpie "; val2valstr(sstat->net.tcp.InErrs, buf+6, 6, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_NETTCPINERR = {"NETTCPINERR", sysprt_NETTCPINERR, NULL}; /*******************************************************************/ static char * sysprt_NETTCPORESET(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="tcpor "; val2valstr(sstat->net.tcp.OutRsts, buf+6, 6, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_NETTCPORESET = {"NETTCPORESET", sysprt_NETTCPORESET, NULL}; /*******************************************************************/ static char * sysprt_NETTCPCSUMERR(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="csumie "; val2valstr(sstat->net.tcp.InCsumErrors, buf+7, 5, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_NETTCPCSUMERR = {"NETTCPCSUMERR", sysprt_NETTCPCSUMERR, NULL}; /*******************************************************************/ static char * sysprt_NETUDPNOPORT(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="udpnp "; val2valstr(sstat->net.udpv4.NoPorts, buf+6, 6, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_NETUDPNOPORT = {"NETUDPNOPORT", sysprt_NETUDPNOPORT, NULL}; /*******************************************************************/ static char * sysprt_NETUDPINERR(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="udpie "; val2valstr(sstat->net.udpv4.InErrors, buf+6, 6, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_NETUDPINERR = {"NETUDPINERR", sysprt_NETUDPINERR, NULL}; /*******************************************************************/ static char * sysprt_NETUDPI(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="udpi "; count_t udpin = sstat->net.udpv4.InDatagrams + sstat->net.udpv6.Udp6InDatagrams; val2valstr(udpin, buf+5, 7, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_NETUDPI = {"NETUDPI", sysprt_NETUDPI, NULL}; /*******************************************************************/ static char * sysprt_NETUDPO(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="udpo "; count_t udpout = sstat->net.udpv4.OutDatagrams + sstat->net.udpv6.Udp6OutDatagrams; val2valstr(udpout, buf+5, 7, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_NETUDPO = {"NETUDPO", sysprt_NETUDPO, NULL}; /*******************************************************************/ static char * sysprt_NETNETWORK(struct sstat *sstat, extraparam *notused, int badness, int *color) { return "network "; } sys_printdef syspdef_NETNETWORK = {"NETNETWORK", sysprt_NETNETWORK, NULL}; /*******************************************************************/ static char * sysprt_NETIPI(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="ipi "; count_t ipin = sstat->net.ipv4.InReceives + sstat->net.ipv6.Ip6InReceives; val2valstr(ipin, buf+4, 8, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_NETIPI = {"NETIPI", sysprt_NETIPI, NULL}; /*******************************************************************/ static char * sysprt_NETIPO(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="ipo "; count_t ipout = sstat->net.ipv4.OutRequests + sstat->net.ipv6.Ip6OutRequests; val2valstr(ipout, buf+4, 8, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_NETIPO = {"NETIPO", sysprt_NETIPO, NULL}; /*******************************************************************/ static char * sysprt_NETIPFRW(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="ipfrw "; count_t ipfrw = sstat->net.ipv4.ForwDatagrams + sstat->net.ipv6.Ip6OutForwDatagrams; val2valstr(ipfrw, buf+6, 6, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_NETIPFRW = {"NETIPFRW", sysprt_NETIPFRW, NULL}; /*******************************************************************/ static char * sysprt_NETIPDELIV(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="deliv "; count_t ipindel = sstat->net.ipv4.InDelivers + sstat->net.ipv6.Ip6InDelivers; val2valstr(ipindel, buf+6, 6, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_NETIPDELIV = {"NETIPDELIV", sysprt_NETIPDELIV, NULL}; /*******************************************************************/ static char * sysprt_NETICMPIN(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="icmpi "; count_t icmpin = sstat->net.icmpv4.InMsgs+ sstat->net.icmpv6.Icmp6InMsgs; val2valstr(icmpin , buf+6, 6, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_NETICMPIN = {"NETICMPIN", sysprt_NETICMPIN, NULL}; /*******************************************************************/ static char * sysprt_NETICMPOUT(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="icmpo "; count_t icmpin = sstat->net.icmpv4.OutMsgs+ sstat->net.icmpv6.Icmp6OutMsgs; val2valstr(icmpin , buf+6, 6, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_NETICMPOUT = {"NETICMPOUT", sysprt_NETICMPOUT, NULL}; /*******************************************************************/ static char * sysprt_NETNAME(struct sstat *sstat, extraparam *as, int badness, int *color) { count_t busy; count_t ival = sstat->intf.intf[as->index].rbyte/125/as->nsecs; count_t oval = sstat->intf.intf[as->index].sbyte/125/as->nsecs; static char buf[16] = "ethxxxx ----"; // 012345678901 *color = -1; if (sstat->intf.intf[as->index].speed) /* speed known? */ { if (sstat->intf.intf[as->index].duplex) busy = (ival > oval ? ival : oval) / (sstat->intf.intf[as->index].speed *10); else busy = (ival + oval) / (sstat->intf.intf[as->index].speed *10); // especially with wireless, the speed might have dropped // temporarily to a very low value (snapshot) // then it might be better to take the speed of the previous // sample if (busy > 100 && sstat->intf.intf[as->index].speed < sstat->intf.intf[as->index].speedp) { sstat->intf.intf[as->index].speed = sstat->intf.intf[as->index].speedp; if (sstat->intf.intf[as->index].duplex) busy = (ival > oval ? ival : oval) / (sstat->intf.intf[as->index].speed *10); else busy = (ival + oval) / (sstat->intf.intf[as->index].speed *10); } if( busy < -99 ) { // when we get wrong values, show wrong values busy = -99; } else if( busy > 999 ) { busy = 999; } snprintf(buf, sizeof(buf)-1, "%-7.7s %3lld%%", sstat->intf.intf[as->index].name, busy); } else { snprintf(buf, sizeof(buf)-1, "%-7.7s ----", sstat->intf.intf[as->index].name); strcpy(buf+8, "----"); } return buf; } sys_printdef syspdef_NETNAME = {"NETNAME", sysprt_NETNAME, NULL}; /*******************************************************************/ static char * sysprt_NETPCKI(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="pcki "; *color = -1; val2valstr(sstat->intf.intf[as->index].rpack, buf+5, 7, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_NETPCKI = {"NETPCKI", sysprt_NETPCKI, NULL}; /*******************************************************************/ static char * sysprt_NETPCKO(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="pcko "; *color = -1; val2valstr(sstat->intf.intf[as->index].spack, buf+5, 7, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_NETPCKO = {"NETPCKO", sysprt_NETPCKO, NULL}; /*******************************************************************/ /* ** convert byte-transfers to bit-transfers (* 8) ** convert bit-transfers to kilobit-transfers (/ 1000) ** per second */ static char *makenetspeed(count_t val, int nsecs) { char c; static char buf[16]="si ?bps"; // 012345678901 val=val/125/nsecs; // convert to Kbps if (val < 10000) { c='K'; } else if (val < (count_t)10000 * 1000) { val/=1000; c = 'M'; } else if (val < (count_t)10000 * 1000 * 1000) { val/=1000 * 1000; c = 'G'; } else { val = val / 1000 / 1000 / 1000; c = 'T'; } if(val < -999) { // when we get wrong values, show wrong values val = -999; } else if(val > 9999) { val = 9999; } snprintf(buf+3, sizeof(buf)-3, "%4lld %cbps", val, c); return buf; } /*******************************************************************/ static char * sysprt_NETSPEEDMAX(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]; count_t speed = sstat->intf.intf[as->index].speed; *color = -1; if (speed < 0 ) speed = 0; if (speed < 10000) { snprintf(buf, sizeof buf, "sp %4lld Mbps", speed); } else { speed /= 1000; if (speed > 9999) { speed = 9999; } snprintf(buf, sizeof buf, "sp %4lld Gbps", speed); } return buf; } sys_printdef syspdef_NETSPEEDMAX = {"NETSPEEDMAX", sysprt_NETSPEEDMAX, NULL}; /*******************************************************************/ static char * sysprt_NETSPEEDIN(struct sstat *sstat, extraparam *as, int badness, int *color) { *color = -1; char *ps=makenetspeed(sstat->intf.intf[as->index].rbyte,as->nsecs); ps[0]='s'; ps[1]='i'; return ps; } sys_printdef syspdef_NETSPEEDIN = {"NETSPEEDIN", sysprt_NETSPEEDIN, NULL}; /*******************************************************************/ static char * sysprt_NETSPEEDOUT(struct sstat *sstat, extraparam *as, int badness, int *color) { *color = -1; char *ps=makenetspeed(sstat->intf.intf[as->index].sbyte,as->nsecs); ps[0]='s'; ps[1]='o'; return ps; } sys_printdef syspdef_NETSPEEDOUT = {"NETSPEEDOUT", sysprt_NETSPEEDOUT, NULL}; /*******************************************************************/ static char * sysprt_NETCOLLIS(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="coll "; val2valstr(sstat->intf.intf[as->index].scollis, buf+5, 7, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_NETCOLLIS = {"NETCOLLIS", sysprt_NETCOLLIS, NULL}; /*******************************************************************/ static char * sysprt_NETMULTICASTIN(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="mlti "; val2valstr(sstat->intf.intf[as->index].rmultic, buf+5, 7, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_NETMULTICASTIN = {"NETMULTICASTIN", sysprt_NETMULTICASTIN, NULL}; /*******************************************************************/ static char * sysprt_NETRCVERR(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="erri "; val2valstr(sstat->intf.intf[as->index].rerrs, buf+5, 7, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_NETRCVERR = {"NETRCVERR", sysprt_NETRCVERR, NULL}; /*******************************************************************/ static char * sysprt_NETSNDERR(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="erro "; val2valstr(sstat->intf.intf[as->index].serrs, buf+5, 7, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_NETSNDERR = {"NETSNDERR", sysprt_NETSNDERR, NULL}; /*******************************************************************/ static char * sysprt_NETRCVDROP(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="drpi "; val2valstr(sstat->intf.intf[as->index].rdrop, buf+5, 7, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_NETRCVDROP = {"NETRCVDROP", sysprt_NETRCVDROP, NULL}; /*******************************************************************/ static char * sysprt_NETSNDDROP(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="drpo "; val2valstr(sstat->intf.intf[as->index].sdrop, buf+5, 7, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_NETSNDDROP = {"NETSNDDROP", sysprt_NETSNDDROP, NULL}; /*******************************************************************/ static char * sysprt_IFBNAME(struct sstat *sstat, extraparam *as, int badness, int *color) { count_t busy; count_t ival = sstat->ifb.ifb[as->index].rcvb/125/as->nsecs; count_t oval = sstat->ifb.ifb[as->index].sndb/125/as->nsecs; int len; static char buf[32] = "ethxxxx ----", tmp[32], *ps=tmp; // 012345678901 *color = -1; if (sstat->ifb.ifb[as->index].rate) busy = (ival > oval ? ival : oval) * sstat->ifb.ifb[as->index].lanes / (sstat->ifb.ifb[as->index].rate * 10); else busy = 0; snprintf(tmp, sizeof tmp, "%s/%d", sstat->ifb.ifb[as->index].ibname, sstat->ifb.ifb[as->index].portnr); len = strlen(ps); if (len > 7) ps = ps + len - 7; snprintf(buf, sizeof buf, "%-7.7s %3lld%%", ps, busy); return buf; } sys_printdef syspdef_IFBNAME = {"IFBNAME", sysprt_IFBNAME, NULL}; /*******************************************************************/ static char * sysprt_IFBPCKI(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="pcki "; *color = -1; val2valstr(sstat->ifb.ifb[as->index].rcvp, buf+5, 7, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_IFBPCKI = {"IFBPCKI", sysprt_IFBPCKI, NULL}; /*******************************************************************/ static char * sysprt_IFBPCKO(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="pcko "; *color = -1; val2valstr(sstat->ifb.ifb[as->index].sndp, buf+5, 7, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_IFBPCKO = {"IFBPCKO", sysprt_IFBPCKO, NULL}; /*******************************************************************/ static char * sysprt_IFBSPEEDMAX(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[64]; count_t rate = sstat->ifb.ifb[as->index].rate; *color = -1; if (rate < 10000) { snprintf(buf, sizeof buf, "sp %4lld Mbps", rate); } else { rate /= 1000; if (rate > 9999) { rate = 9999; } snprintf(buf, sizeof buf, "sp %4lld Gbps", rate); } return buf; } sys_printdef syspdef_IFBSPEEDMAX = {"IFBSPEEDMAX", sysprt_IFBSPEEDMAX, NULL}; /*******************************************************************/ static char * sysprt_IFBLANES(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="lanes "; val2valstr(sstat->ifb.ifb[as->index].lanes, buf+6, 6, 0, 0); return buf; } sys_printdef syspdef_IFBLANES = {"IFBLANES", sysprt_IFBLANES, NULL}; /*******************************************************************/ static char * sysprt_IFBSPEEDIN(struct sstat *sstat, extraparam *as, int badness, int *color) { *color = -1; char *ps=makenetspeed(sstat->ifb.ifb[as->index].rcvb * sstat->ifb.ifb[as->index].lanes, as->nsecs); ps[0]='s'; ps[1]='i'; return ps; } sys_printdef syspdef_IFBSPEEDIN = {"IFBSPEEDIN", sysprt_IFBSPEEDIN, NULL}; /*******************************************************************/ static char * sysprt_IFBSPEEDOUT(struct sstat *sstat, extraparam *as, int badness, int *color) { *color = -1; char *ps=makenetspeed(sstat->ifb.ifb[as->index].sndb * sstat->ifb.ifb[as->index].lanes, as->nsecs); ps[0]='s'; ps[1]='o'; return ps; } sys_printdef syspdef_IFBSPEEDOUT = {"IFBSPEEDOUT", sysprt_IFBSPEEDOUT, NULL}; /*******************************************************************/ static char * sysprt_NFMSERVER(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16] = "srv "; char mntdev[128], *ps; memcpy(mntdev, sstat->nfs.nfsmounts.nfsmnt[as->index].mountdev, sizeof mntdev); if ( (ps = strchr(mntdev, ':')) ) // colon found? *ps = '\0'; else strcpy(mntdev, "?"); snprintf(buf+4, sizeof buf-4, "%8.8s", mntdev); return buf; } sys_printdef syspdef_NFMSERVER = {"NFMSERVER", sysprt_NFMSERVER, NULL}; /*******************************************************************/ static char * sysprt_NFMPATH(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]; char mntdev[128], *ps; int len; memcpy(mntdev, sstat->nfs.nfsmounts.nfsmnt[as->index].mountdev, sizeof mntdev); if ( (ps = strchr(mntdev, ':')) ) // colon found? ps++; else ps = mntdev; len = strlen(ps); if (len > 12) ps = ps + len - 12; snprintf(buf, sizeof buf, "%12.12s", ps); return buf; } sys_printdef syspdef_NFMPATH = {"NFMPATH", sysprt_NFMPATH, NULL}; /*******************************************************************/ static char * sysprt_NFMTOTREAD(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="read "; val2memstr(sstat->nfs.nfsmounts.nfsmnt[as->index].bytestotread, buf+6, KBFORMAT, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_NFMTOTREAD = {"NFMTOTREAD", sysprt_NFMTOTREAD, NULL}; /*******************************************************************/ static char * sysprt_NFMTOTWRITE(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="write "; val2memstr(sstat->nfs.nfsmounts.nfsmnt[as->index].bytestotwrite, buf+6, KBFORMAT, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_NFMTOTWRITE = {"NFMTOTWRITE", sysprt_NFMTOTWRITE, NULL}; /*******************************************************************/ static char * sysprt_NFMNREAD(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="nread "; val2memstr(sstat->nfs.nfsmounts.nfsmnt[as->index].bytesread, buf+6, KBFORMAT, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_NFMNREAD = {"NFMNREAD", sysprt_NFMNREAD, NULL}; /*******************************************************************/ static char * sysprt_NFMNWRITE(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="nwrit "; val2memstr(sstat->nfs.nfsmounts.nfsmnt[as->index].byteswrite, buf+6, KBFORMAT, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_NFMNWRITE = {"NFMNWRITE", sysprt_NFMNWRITE, NULL}; /*******************************************************************/ static char * sysprt_NFMDREAD(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="dread "; val2memstr(sstat->nfs.nfsmounts.nfsmnt[as->index].bytesdread, buf+6, KBFORMAT, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_NFMDREAD = {"NFMDREAD", sysprt_NFMDREAD, NULL}; /*******************************************************************/ static char * sysprt_NFMDWRITE(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="dwrit "; val2memstr(sstat->nfs.nfsmounts.nfsmnt[as->index].bytesdwrite, buf+6, KBFORMAT, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_NFMDWRITE = {"NFMDWRITE", sysprt_NFMDWRITE, NULL}; /*******************************************************************/ static char * sysprt_NFMMREAD(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="mread "; val2memstr(sstat->nfs.nfsmounts.nfsmnt[as->index].pagesmread *pagesize, buf+6, KBFORMAT, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_NFMMREAD = {"NFMMREAD", sysprt_NFMMREAD, NULL}; /*******************************************************************/ static char * sysprt_NFMMWRITE(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="mwrit "; val2memstr(sstat->nfs.nfsmounts.nfsmnt[as->index].pagesmwrite *pagesize, buf+6, KBFORMAT, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_NFMMWRITE = {"NFMMWRITE", sysprt_NFMMWRITE, NULL}; /*******************************************************************/ static char * sysprt_NFCRPCCNT(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="rpc "; val2valstr(sstat->nfs.client.rpccnt, buf+4, 8, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_NFCRPCCNT = {"NFCRPCCNT", sysprt_NFCRPCCNT, NULL}; /*******************************************************************/ static char * sysprt_NFCRPCREAD(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="read "; val2valstr(sstat->nfs.client.rpcread, buf+5, 7, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_NFCRPCREAD = {"NFCRPCREAD", sysprt_NFCRPCREAD, NULL}; /*******************************************************************/ static char * sysprt_NFCRPCWRITE(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="write "; val2valstr(sstat->nfs.client.rpcwrite, buf+6, 6, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_NFCRPCWRITE = {"NFCRPCWRITE", sysprt_NFCRPCWRITE, NULL}; /*******************************************************************/ static char * sysprt_NFCRPCRET(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="retxmit "; val2valstr(sstat->nfs.client.rpcretrans, buf+8, 4, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_NFCRPCRET = {"NFCRPCRET", sysprt_NFCRPCRET, NULL}; /*******************************************************************/ static char * sysprt_NFCRPCARF(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="autref "; val2valstr(sstat->nfs.client.rpcautrefresh, buf+7, 5, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_NFCRPCARF = {"NFCRPCARF", sysprt_NFCRPCARF, NULL}; /*******************************************************************/ static char * sysprt_NFSRPCCNT(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="rpc "; val2valstr(sstat->nfs.server.rpccnt, buf+4, 8, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_NFSRPCCNT = {"NFSRPCCNT", sysprt_NFSRPCCNT, NULL}; /*******************************************************************/ static char * sysprt_NFSRPCREAD(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="cread "; val2valstr(sstat->nfs.server.rpcread, buf+6, 6, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_NFSRPCREAD = {"NFSRPCREAD", sysprt_NFSRPCREAD, NULL}; /*******************************************************************/ static char * sysprt_NFSRPCWRITE(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="cwrit "; val2valstr(sstat->nfs.server.rpcwrite, buf+6, 6, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_NFSRPCWRITE = {"NFSRPCWRITE", sysprt_NFSRPCWRITE, NULL}; /*******************************************************************/ static char * sysprt_NFSBADFMT(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="badfmt "; val2valstr(sstat->nfs.server.rpcbadfmt, buf+7, 5, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_NFSBADFMT = {"NFSBADFMT", sysprt_NFSBADFMT, NULL}; /*******************************************************************/ static char * sysprt_NFSBADAUT(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="badaut "; val2valstr(sstat->nfs.server.rpcbadaut, buf+7, 5, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_NFSBADAUT = {"NFSBADAUT", sysprt_NFSBADAUT, NULL}; /*******************************************************************/ static char * sysprt_NFSBADCLN(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="badcln "; val2valstr(sstat->nfs.server.rpcbadcln, buf+7, 5, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_NFSBADCLN = {"NFSBADCLN", sysprt_NFSBADCLN, NULL}; /*******************************************************************/ static char * sysprt_NFSNETTCP(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="nettcp "; val2valstr(sstat->nfs.server.nettcpcnt, buf+7, 5, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_NFSNETTCP = {"NFSNETTCP", sysprt_NFSNETTCP, NULL}; /*******************************************************************/ static char * sysprt_NFSNETUDP(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="netudp "; val2valstr(sstat->nfs.server.netudpcnt, buf+7, 5, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_NFSNETUDP = {"NFSNETUDP", sysprt_NFSNETUDP, NULL}; /*******************************************************************/ static char * sysprt_NFSNRBYTES(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[32]="MBcr/s "; snprintf(buf+7, sizeof buf-7, "%5.1lf", sstat->nfs.server.nrbytes / 1024.0 / 1024.0 / as->nsecs); return buf; } sys_printdef syspdef_NFSNRBYTES = {"NFSNRBYTES", sysprt_NFSNRBYTES, NULL}; /*******************************************************************/ static char * sysprt_NFSNWBYTES(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[32]="MBcw/s "; snprintf(buf+7, sizeof buf-7, "%5.1lf", sstat->nfs.server.nwbytes / 1024.0 / 1024.0 / as->nsecs); return buf; } sys_printdef syspdef_NFSNWBYTES = {"NFSNWBYTES", sysprt_NFSNWBYTES, NULL}; /*******************************************************************/ static char * sysprt_NFSRCHITS(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="rchits "; val2valstr(sstat->nfs.server.rchits, buf+8, 4, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_NFSRCHITS = {"NFSRCHITS", sysprt_NFSRCHITS, NULL}; /*******************************************************************/ static char * sysprt_NFSRCMISS(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="rcmiss "; val2valstr(sstat->nfs.server.rcmiss, buf+8, 4, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_NFSRCMISS = {"NFSRCMISS", sysprt_NFSRCMISS, NULL}; /*******************************************************************/ static char * sysprt_NFSRCNOCA(struct sstat *sstat, extraparam *as, int badness, int *color) { static char buf[16]="rcnoca "; val2valstr(sstat->nfs.server.rcnoca, buf+8, 4, as->avgval, as->nsecs); return buf; } sys_printdef syspdef_NFSRCNOCA = {"NFSRCNOCA", sysprt_NFSRCNOCA, NULL}; /*******************************************************************/ static char * sysprt_BLANKBOX(struct sstat *sstat, extraparam *notused, int badness, int *color) { return " "; } sys_printdef syspdef_BLANKBOX = {"BLANKBOX", sysprt_BLANKBOX, NULL}; atop-2.12.1/utsnames.c0000644000203100020310000001233015064552761014055 0ustar gerlofgerlof/* ** ATOP - System & Process Monitor ** ** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as process-level. ** ** This source-file contains functions to retrieve the hostname of a ** containerized process by associating to its UTS namespace. ** ========================================================================== ** Author: Gerlof Langeveld ** E-mail: gerlof.langeveld@atoptool.nl ** Date: August 2023 (initial) ** -------------------------------------------------------------------------- ** Copyright (C) 2023 Gerlof Langeveld ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ** -------------------------------------------------------------------------- */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #define BASEPATH "/proc/1/ns/uts" #define UTSPATH "/proc/%d/ns/uts" static int mypidfd, foreignuts; // Function that fills the host name (container/pod name) of // a specific process. // When the process has a different UTS namespace then systemd, // the process' UTS namespace will be temporarily associated with atop // itself to retrieve the host name (i.e. container/pod name) of that process. // To avoid too many namespace switches, atop only reassociates with its own // UTS namespace when the resetutsname() function is called. // // Return value: // 0 - no container/pod detected // 1 - container/pod detected // int getutsname(struct tstat *curtask) { static char firstcall = 1, basepath[64], basehost[32]; int pidfd, offset; ssize_t destlen; char srcpath[64], destpath[64], tmphost[70]; // regain root privs in case of setuid root executable // regainrootprivs(); // function initialization // if (firstcall) { firstcall = 0; // determine the UTS namespace of systemd and the hostname // related to systemd // if ( (destlen = readlink(BASEPATH, basepath, sizeof basepath)) == -1) goto drop_and_return; basepath[destlen] = '\0'; // guarantee delimeter if ( (pidfd = open(BASEPATH, O_RDONLY)) == -1) goto drop_and_return; if (setns(pidfd, CLONE_NEWUTS) == -1) { close(pidfd); goto drop_and_return; } foreignuts = 1; close(pidfd); gethostname(basehost, sizeof basehost); // determine the UTS namespace of atop itself // and open that namespace for reassociation via // function resetutsname() // snprintf(srcpath, sizeof srcpath, UTSPATH, getpid()); if ( (mypidfd = open(srcpath, O_RDONLY)) == -1) basepath[0] = '\0'; resetutsname(); } // no root privileges? // if (!basepath[0]) goto drop_and_return; // check if this pid is related to myself (atop) // if (curtask->gen.pid == getpid()) goto drop_and_return; // base path is known // verify if this process is native, i.e. does it share the // UTS namespace of systemd // snprintf(srcpath, sizeof srcpath, UTSPATH, curtask->gen.pid); destlen = readlink(srcpath, destpath, sizeof destpath); destpath[destlen] = '\0'; if (strcmp(basepath, destpath) == 0) // equal? goto drop_and_return; // UTS namespace deviates from base UTS namespace // // get hostname related to this UTS namespace by associating // atop to that namespace // if ( (pidfd = open(srcpath, O_RDONLY)) == -1) goto drop_and_return; if (setns(pidfd, CLONE_NEWUTS) == -1) { close(pidfd); goto drop_and_return; } foreignuts = 1; // set boolean close(pidfd); gethostname(tmphost, sizeof tmphost); // check if the hostname is the same as the hostname of // systemd (PID 1), because some systemd-related processes have // their own UTS namespace with the same hostname // also the hostname 'localhost' might be used by various daemons // and will be skipped as well // if ( strcmp(tmphost, basehost) == 0 || strcmp(tmphost, "localhost") == 0) goto drop_and_return; // this process really seems to be container/pod related // if ( (offset = strlen(tmphost) - UTSLEN) < 0) offset = 0; strcpy(curtask->gen.utsname, tmphost+offset); // copy last part when overflow if (! droprootprivs()) mcleanstop(42, "failed to drop root privs\n"); return 1; drop_and_return: if (! droprootprivs()) mcleanstop(42, "failed to drop root privs\n"); return 0; } // Reassociate atop with its own UTS namespace // void resetutsname(void) { // switch back to my own original UTS namespace // if (foreignuts) { foreignuts = 0; regainrootprivs(); if (setns(mypidfd, CLONE_NEWUTS) == -1) ; if (! droprootprivs()) mcleanstop(42, "failed to drop root privs\n"); } } atop-2.12.1/various.c0000644000203100020310000006557615064552761013732 0ustar gerlofgerlof/* ** ATOP - System & Process Monitor ** ** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as process-level. ** ** This source-file contains various functions to a.o. format the ** time-of-day, the cpu-time consumption and the memory-occupation. ** ========================================================================== ** Author: Gerlof Langeveld ** E-mail: gerlof.langeveld@atoptool.nl ** Date: November 1996 ** LINUX-port: June 2000 ** -------------------------------------------------------------------------- ** Copyright (C) 2000-2022 Gerlof Langeveld ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ** -------------------------------------------------------------------------- */ #define _POSIX_C_SOURCE #define _XOPEN_SOURCE #define _GNU_SOURCE #define _DEFAULT_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "atop.h" #include "acctproc.h" static unsigned long long getbootlinux(long); /* ** Function convtime() converts a value (number of seconds since ** 1-1-1970) to an ascii-string in the format hh:mm:ss, stored in ** chartim (9 bytes long). */ char * convtime(time_t utime, char *chartim) { struct tm *tt; tt = localtime(&utime); snprintf(chartim, 9, "%02d:%02d:%02d", tt->tm_hour, tt->tm_min, tt->tm_sec); return chartim; } /* ** Function convdate() converts a value (number of seconds since ** 1-1-1970) to an ascii-string in the format yyyy/mm/dd, stored in ** chardat (11 bytes long). */ char * convdate(time_t utime, char *chardat) { struct tm *tt; tt = localtime(&utime); snprintf(chardat, 11, "%04u/%02u/%02u", (tt->tm_year+1900)%10000, (tt->tm_mon+1)%100, tt->tm_mday%100); return chardat; } /* ** Convert a string in format [YYYYMMDD]hh[:]mm[:][ss] into an epoch time value or ** when only the value hh[:]mm was given, take this time from midnight. ** ** Arguments: String with date-time in format [YYYYMMDD]hh[:]mm[:][ss] ** or hh[:]mm[:][ss]. ** ** Pointer to time_t containing 0 or current epoch time. ** ** Return-value: 0 - Wrong input-format ** 1 - Success */ int getbranchtime(char *itim, time_t *newtime) { register int ilen = strlen(itim); int hours, minutes, seconds; time_t epoch; struct tm tm; memset(&tm, 0, sizeof tm); /* ** verify length of input string */ if (ilen != 4 && ilen != 5 && // hhmm or hh:mm ilen != 6 && ilen != 8 && // hhmmss or hh:mm:ss ilen != 12 && ilen != 13 && // YYYYMMDDhhmm or YYYYMMDDhh:mm ilen != 14 && ilen != 16) // YYYYMMDDhhmmss or YYYYMMDDhh:mm:ss return 0; // wrong date-time format /* ** check string syntax for absolute time specified as ** YYYYMMDDhh:mm:ss or YYYYMMDDhhmmss */ if ( sscanf(itim, "%4d%2d%2d%2d:%2d:%2d", &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec) == 6 || sscanf(itim, "%4d%2d%2d%2d%2d%2d", &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec) == 6 ) { tm.tm_year -= 1900; tm.tm_mon -= 1; if (tm.tm_year < 100 || tm.tm_mon < 0 || tm.tm_mon > 11 || tm.tm_mday < 1 || tm.tm_mday > 31 || tm.tm_hour < 0 || tm.tm_hour > 23 || tm.tm_min < 0 || tm.tm_min > 59 || tm.tm_sec < 0 || tm.tm_sec > 59 ) { return 0; // wrong date-time format } tm.tm_isdst = -1; if ((epoch = mktime(&tm)) == -1) return 0; // wrong date-time format // correct date-time format *newtime = epoch; return 1; } /* ** check string syntax for absolute time specified as ** YYYYMMDDhh:mm or YYYYMMDDhhmm */ if ( sscanf(itim, "%4d%2d%2d%2d:%2d", &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min) == 5 || sscanf(itim, "%4d%2d%2d%2d%2d", &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min) == 5 ) { tm.tm_year -= 1900; tm.tm_mon -= 1; if (tm.tm_year < 100 || tm.tm_mon < 0 || tm.tm_mon > 11 || tm.tm_mday < 1 || tm.tm_mday > 31 || tm.tm_hour < 0 || tm.tm_hour > 23 || tm.tm_min < 0 || tm.tm_min > 59 ) { return 0; // wrong date-time format } tm.tm_isdst = -1; if ((epoch = mktime(&tm)) == -1) return 0; // wrong date-time format // correct date-time format *newtime = epoch; return 1; } /* ** check string syntax for relative time specified as ** hh:mm:ss or hhmmss */ if ( sscanf(itim, "%2d:%2d:%2d", &hours, &minutes, &seconds) == 3 || sscanf(itim, "%2d%2d%2d", &hours, &minutes, &seconds) == 3 ) { if ( hours < 0 || hours > 23 || minutes < 0 || minutes > 59 || seconds < 0 || seconds > 59 ) return 0; // wrong date-time format /* ** when the new time is already filled with an epoch time, ** the relative time will be on the same day as indicated by ** that epoch time ** when the new time is the time within a day or 0, the new ** time will be stored again as the time within a day. */ if (*newtime <= SECONDSINDAY) // time within the day? { *newtime = (hours * 3600) + (minutes * 60) + seconds; if (*newtime >= SECONDSINDAY) *newtime = SECONDSINDAY-1; return 1; } else { *newtime = normalize_epoch(*newtime, (hours*3600) + (minutes*60) + seconds); return 1; } } /* ** check string syntax for relative time specified as ** hh:mm or hhmm */ if ( sscanf(itim, "%2d:%2d", &hours, &minutes) == 2 || sscanf(itim, "%2d%2d", &hours, &minutes) == 2 ) { if ( hours < 0 || hours > 23 || minutes < 0 || minutes > 59 ) return 0; // wrong date-time format /* ** when the new time is already filled with an epoch time, ** the relative time will be on the same day as indicated by ** that epoch time ** when the new time is the time within a day or 0, the new ** time will be stored again as the time within a day. */ if (*newtime <= SECONDSINDAY) // time within the day? { *newtime = (hours * 3600) + (minutes * 60); if (*newtime >= SECONDSINDAY) *newtime = SECONDSINDAY-1; return 1; } else { *newtime = normalize_epoch(*newtime, (hours*3600) + (minutes*60)); return 1; } } return 0; // wrong date-time format } /* ** Normalize an epoch time with the number of seconds within a day ** Return-value: Normalized epoch */ time_t normalize_epoch(time_t epoch, long secondsinday) { struct tm tm; localtime_r(&epoch, &tm); // convert epoch to tm tm.tm_hour = 0; tm.tm_min = 0; tm.tm_sec = secondsinday; tm.tm_isdst = -1; return mktime(&tm); // convert tm to epoch } /* ** Function val2valstr() converts a positive value to an ascii-string of a ** fixed number of positions; if the value does not fit, it will be formatted ** to exponent-notation (as short as possible, so not via the standard printf- ** formatters %f or %e). The offered buffer should have a length of width+1. ** The value might even be printed as an average for the interval-time. */ char * val2valstr(count_t value, char *strvalue, int width, int avg, int nsecs) { count_t maxval, remain = 0; unsigned short exp = 0; char *suffix = ""; int strsize = width+1; if (avg && nsecs) { value = (value + (nsecs/2)) / nsecs; /* rounded value */ width = width - 2; /* subtract two positions for '/s' */ suffix = "/s"; } if (value < 0) // no negative value expected { snprintf(strvalue, strsize, "%*s%s", width, "?", suffix); return strvalue; } maxval = pow(10.0, width) - 1; if (value < maxval) { snprintf(strvalue, strsize, "%*lld%s", width, value, suffix); } else { if (width < 3) { /* ** cannot avoid ignoring width */ snprintf(strvalue, strsize, "%lld%s", value, suffix); } else { /* ** calculate new maximum value for the string, ** calculating space for 'e' (exponent) + one digit */ width -= 2; maxval = pow(10.0, width) - 1; while (value > maxval) { exp++; remain = value % 10; value /= 10; } if (remain >= 5 && value < maxval) value++; snprintf(strvalue, strsize, "%*llde%hd%s", width%100, value, exp%100, suffix); } } return strvalue; } #define DAYSECS (24*60*60) #define HOURSECS (60*60) #define MINSECS (60) /* ** Function val2elapstr() converts a value (number of seconds) ** to an ascii-string of up to max 13 positions in NNNdNNhNNmNNs ** stored in strvalue (at least 14 positions). ** returnvalue: number of bytes stored */ int val2elapstr(int value, char *strvalue) { char *p = strvalue; int rv, n = 14; if (value >= DAYSECS) { rv = snprintf(p, n, "%dd", value/DAYSECS); p += rv; n -= rv; } if (value >= HOURSECS) { rv = snprintf(p, n, "%dh", (value%DAYSECS)/HOURSECS); p += rv; n -= rv; } if (value >= MINSECS) { rv = snprintf(p, n, "%dm", (value%HOURSECS)/MINSECS); p += rv; n -= rv; } rv = snprintf(p, n, "%ds", (value%MINSECS)); p += rv; n -= rv; return p - strvalue; } /* ** Function val2cpustr() converts a value (number of milliseconds) ** to an ascii-string of 6 positions in milliseconds or minute-seconds or ** hours-minutes, stored in strvalue (at least 7 positions). */ #define MAXMSEC (count_t)100000 #define MAXSEC (count_t)6000 #define MAXMIN (count_t)6000 char * val2cpustr(count_t value, char *strvalue) { if (value < MAXMSEC) { snprintf(strvalue, 7, "%2llu.%02llus", (value/1000)%100, value%1000/10); } else { /* ** millisecs irrelevant; round to seconds */ value = (value + 500) / 1000; if (value < MAXSEC) { snprintf(strvalue, 7, "%2llum%02llus", (value/60)%100, value%60); } else { /* ** seconds irrelevant; round to minutes */ value = (value + 30) / 60; if (value < MAXMIN) { snprintf(strvalue, 7, "%2lluh%02llum", (value/60)%100, value%60); } else { /* ** minutes irrelevant; round to hours */ value = (value + 30) / 60; snprintf(strvalue, 7, "%2llud%02lluh", (value/24)%100, value%24); } } } return strvalue; } /* ** Function val2Hzstr() converts a value (in MHz) ** to an ascii-string. ** The result-string is placed in the area pointed to strvalue, ** which should be able to contain 7 positions plus null byte. */ char * val2Hzstr(count_t value, char *strvalue) { char *fformat; if (value < 1000) { snprintf(strvalue, 8, "%4lluMHz", value%10000); } else { double fval=value/1000.0; // fval is double in GHz char prefix='G'; if (fval >= 1000.0) // prepare for the future { prefix='T'; fval /= 1000.0; } if (fval < 10.0) { fformat = "%4.2f%cHz"; } else { if (fval < 100.0) fformat = "%4.1f%cHz"; else fformat = "%4.0f%cHz"; } snprintf(strvalue, 8, fformat, fval, prefix); } return strvalue; } /* ** Function val2memstr() converts a value (number of bytes) ** to an ascii-string in a specific format (indicated by pformat). ** The result-string is placed in the area pointed to strvalue, ** which should be able to contain at least 7 positions. */ #define ONEKBYTE 1024 #define ONEMBYTE 1048576 #define ONEGBYTE 1073741824L #define ONETBYTE 1099511627776LL #define ONEPBYTE 1125899906842624LL #define ONEEBYTE 1152921504606846976LL #define MAXLONGLONG 9223372036854775807LL #define MAXBYTE 999 #define MAXKBYTE (ONEKBYTE*999L) #define MAXKBYTE9 (ONEKBYTE*9L) #define MAXMBYTE (ONEMBYTE*999L) #define MAXMBYTE9 (ONEMBYTE*9L) #define MAXGBYTE (ONEGBYTE*999LL) #define MAXGBYTE9 (ONEGBYTE*9LL) #define MAXTBYTE (ONETBYTE*999LL) #define MAXTBYTE9 (ONETBYTE*9LL) #define MAXPBYTE (ONEPBYTE*999LL) #define MAXPBYTE9 (ONEPBYTE*9LL) #define MAXEBYTE (ONEEBYTE*999LL) #define MAXEBYTE8 (ONEEBYTE*7LL+(ONEEBYTE-1)) char * val2memstr(count_t value, char *strvalue, int pformat, int avgval, int nsecs) { char aformat; /* advised format */ count_t verifyval; char *suffix = ""; int basewidth = 6; /* ** notice that the value can be negative, in which case the ** modulo-value should be evaluated and an extra position should ** be reserved for the sign */ if (value < 0) verifyval = -value * 10; else verifyval = value; /* ** verify if printed value is required per second (average) or total */ if (avgval && nsecs) { value = llround((double)((double)value/(double)nsecs)); verifyval = llround((double)((double)verifyval/(double)nsecs)); basewidth -= 2; suffix = "/s"; if (verifyval <= MAXBYTE) /* bytes ? */ aformat = BFORMAT; else if (verifyval <= MAXKBYTE9) /* kbytes 1-9 ? */ aformat = KBFORMAT; else if (verifyval <= MAXKBYTE) /* kbytes ? */ aformat = KBFORMAT_INT; else if (verifyval <= MAXMBYTE9) /* mbytes 1-9 ? */ aformat = MBFORMAT; else if (verifyval <= MAXMBYTE) /* mbytes 10-999 ? */ aformat = MBFORMAT_INT; else if (verifyval <= MAXGBYTE9) /* gbytes 1-9 ? */ aformat = GBFORMAT; else if (verifyval <= MAXGBYTE) /* gbytes 10-999 ? */ aformat = GBFORMAT_INT; else if (verifyval <= MAXTBYTE9) /* tbytes 1-9 ? */ aformat = TBFORMAT; else if (verifyval <= MAXTBYTE) /* tbytes 10-999? */ aformat = TBFORMAT_INT; else if (verifyval <= MAXPBYTE9) /* pbytes 1-9 ? */ aformat = PBFORMAT; else if (verifyval <= MAXPBYTE) /* pbytes 10-999 ? */ aformat = PBFORMAT_INT; else if (verifyval <= MAXEBYTE8) /* ebytes 1-8 ? */ aformat = EBFORMAT; else aformat = OVFORMAT; /* max long long */ } else /* ** printed value per interval (normal mode) */ { /* ** determine which format will be used on bases of the value itself */ if (verifyval <= MAXBYTE) /* bytes ? */ aformat = BFORMAT; else if (verifyval <= MAXKBYTE) /* kbytes ? */ aformat = KBFORMAT; else if (verifyval <= MAXMBYTE) /* mbytes ? */ aformat = MBFORMAT; else if (verifyval <= MAXGBYTE) /* gbytes ? */ aformat = GBFORMAT; else if (verifyval <= MAXTBYTE) /* tbytes? */ aformat = TBFORMAT; else if (verifyval <= MAXPBYTE) /* pbytes? */ aformat = PBFORMAT; else aformat = EBFORMAT; /* ebytes! */ } /* ** check if this is also the preferred format */ if (aformat <= pformat) aformat = pformat; switch (aformat) { case BFORMAT: snprintf(strvalue, 7, "%*lldB%s", basewidth-1, value, suffix); break; case KBFORMAT: snprintf(strvalue, 7, "%*.1lfK%s", basewidth-1, (double)((double)value/ONEKBYTE), suffix); break; case KBFORMAT_INT: snprintf(strvalue, 7, "%*lldK%s", basewidth-1, llround((double)((double)value/ONEKBYTE)), suffix); break; case MBFORMAT: snprintf(strvalue, 7, "%*.1lfM%s", basewidth-1, (double)((double)value/ONEMBYTE), suffix); break; case MBFORMAT_INT: snprintf(strvalue, 7, "%*lldM%s", basewidth-1, llround((double)((double)value/ONEMBYTE)), suffix); break; case GBFORMAT: snprintf(strvalue, 7, "%*.1lfG%s", basewidth-1, (double)((double)value/ONEGBYTE), suffix); break; case GBFORMAT_INT: snprintf(strvalue, 7, "%*lldG%s", basewidth-1, llround((double)((double)value/ONEGBYTE)), suffix); break; case TBFORMAT: snprintf(strvalue, 7, "%*.1lfT%s", basewidth-1, (double)((double)value/ONETBYTE), suffix); break; case TBFORMAT_INT: snprintf(strvalue, 7, "%*lldT%s", basewidth-1, llround((double)((double)value/ONETBYTE)), suffix); break; case PBFORMAT: snprintf(strvalue, 7, "%*.1lfP%s", basewidth-1, (double)((double)value/ONEPBYTE), suffix); break; case PBFORMAT_INT: snprintf(strvalue, 7, "%*lldP%s", basewidth-1, llround((double)((double)value/ONEPBYTE)), suffix); break; case EBFORMAT: snprintf(strvalue, 7, "%*.1lfE%s", basewidth-1, (double)((double)value/ONEEBYTE), suffix); break; default: snprintf(strvalue, 7, "OVFLOW"); } // check if overflow occurred during the formatting // by checking the last byte of the formatted string // switch ( *(strvalue+5) ) { case 's': // in case of per-second value case 'B': case 'K': case 'M': case 'G': case 'T': case 'P': case 'E': break; default: snprintf(strvalue, 7, "OVFLOW"); } return strvalue; } /* ** Function numeric() checks if the ascii-string contains ** a numeric (positive) value. ** Returns 1 (true) if so, or 0 (false). */ int numeric(char *ns) { register char *s = ns; while (*s) if (*s < '0' || *s > '9') return(0); /* false */ else s++; return(1); /* true */ } /* ** Function getboot() returns the boot-time of this system ** as number of jiffies since 1-1-1970. */ unsigned long long getboot(void) { static unsigned long long boottime; if (!boottime) /* do this only once */ boottime = getbootlinux(hertz); return boottime; } /* ** LINUX SPECIFIC: ** Determine boot-time of this system (as number of jiffies since 1-1-1970). */ static unsigned long long getbootlinux(long hertz) { int cpid; char tmpbuf[1280]; FILE *fp; unsigned long startticks; unsigned long long bootjiffies = 0; struct timespec ts; /* ** dirty hack to get the boottime, since the ** Linux 2.6 kernel (2.6.5) does not return a proper ** boottime-value with the times() system call :-( */ if ( (cpid = fork()) == 0 ) { /* ** child just waiting to be killed by parent */ pause(); } else { /* ** parent determines start-time (in jiffies since boot) ** of the child and calculates the boottime in jiffies ** since 1-1-1970 */ (void) clock_gettime(CLOCK_REALTIME, &ts); // get current bootjiffies = 1LL * ts.tv_sec * hertz + 1LL * ts.tv_nsec * hertz / 1000000000LL; snprintf(tmpbuf, sizeof tmpbuf, "/proc/%d/stat", cpid); if ( (fp = fopen(tmpbuf, "r")) != NULL) { if ( fscanf(fp, "%*d (%*[^)]) %*c %*d %*d %*d %*d " "%*d %*d %*d %*d %*d %*d %*d %*d " "%*d %*d %*d %*d %*d %*d %lu", &startticks) == 1) { bootjiffies -= startticks; } fclose(fp); } /* ** kill the child and get rid of the zombie */ kill(cpid, SIGKILL); (void) wait((int *)0); } return bootjiffies; } /* ** generic pointer verification after malloc */ void ptrverify(const void *ptr, const char *errormsg, ...) { if (!ptr) { va_list args; acctswoff(); netatop_signoff(); generic_end(); va_start(args, errormsg); vfprintf(stderr, errormsg, args); va_end(args); exit(13); } } /* ** cleanup, give error message and exit */ void mcleanstop(int exitcode, const char *errormsg, ...) { va_list args; acctswoff(); netatop_signoff(); generic_end(); va_start(args, errormsg); vfprintf(stderr, errormsg, args); va_end(args); exit(exitcode); } /* ** cleanup and exit */ void cleanstop(int exitcode) { acctswoff(); netatop_signoff(); generic_end(); exit(exitcode); } /* ** determine if we are running with root privileges ** returns: boolean */ int rootprivs(void) { uid_t ruid, euid, suid; getresuid(&ruid, &euid, &suid); return !suid; } /* ** drop the root privileges that might be obtained via setuid-bit ** ** this action may only fail with errno EPERM (normal situation when ** atop has not been started with setuid-root privs); when this ** action fails with EAGAIN or ENOMEM, atop should not continue ** without root privs being dropped... */ int droprootprivs(void) { if (seteuid( getuid() ) == -1 && errno != EPERM) return 0; /* false */ else return 1; /* true */ } /* ** regain the root privileges that might be dropped earlier */ void regainrootprivs(void) { int liResult; // this will fail for non-privileged processes liResult = seteuid(0); if (liResult != 0) { } } /* ** try to set the highest OOM priority */ void set_oom_score_adj(void) { int fd; char val[] = "-999"; /* suggested by Gerlof, always set -999 */ /* ** set OOM score adj to avoid to lost necessary log of system. ** ignored if not running under superuser privileges! */ fd = open("/proc/self/oom_score_adj", O_RDWR); if ( fd < 0 ) { return; } if ( write(fd, val, strlen(val)) < 0 ) ; close(fd); } /* hypervisor enum, move this into header if actually in use */ enum { HYPER_NONE = 0, HYPER_XEN, HYPER_KVM, HYPER_MSHV, HYPER_VMWARE, HYPER_IBM, HYPER_VSERVER, HYPER_UML, HYPER_INNOTEK, HYPER_HITACHI, HYPER_PARALLELS, HYPER_VBOX, HYPER_OS400, HYPER_PHYP, HYPER_SPAR, HYPER_WSL, }; #if defined(__x86_64__) || defined(__i386__) #define HYPERVISOR_INFO_LEAF 0x40000000 static inline void x86_cpuid(unsigned int op, unsigned int *eax, unsigned int *ebx, unsigned int *ecx, unsigned int *edx) { __asm__( #if defined(__PIC__) && defined(__i386__) "xchg %%ebx, %%esi;" "cpuid;" "xchg %%esi, %%ebx;" : "=S" (*ebx), #else "cpuid;" : "=b" (*ebx), #endif "=a" (*eax), "=c" (*ecx), "=d" (*edx) : "1" (op), "c"(0)); } static int get_hypervisor(void) { unsigned int eax = 0, ebx = 0, ecx = 0, edx = 0, hyper = HYPER_NONE; char hyper_vendor_id[13]; memset(hyper_vendor_id, 0, sizeof(hyper_vendor_id)); x86_cpuid(HYPERVISOR_INFO_LEAF, &eax, &ebx, &ecx, &edx); memcpy(hyper_vendor_id + 0, &ebx, 4); memcpy(hyper_vendor_id + 4, &ecx, 4); memcpy(hyper_vendor_id + 8, &edx, 4); hyper_vendor_id[12] = '\0'; if (!hyper_vendor_id[0]) return hyper; if (!strncmp("XenVMMXenVMM", hyper_vendor_id, 12)) hyper = HYPER_XEN; else if (!strncmp("KVMKVMKVM", hyper_vendor_id, 9)) hyper = HYPER_KVM; else if (!strncmp("Microsoft Hv", hyper_vendor_id, 12)) hyper = HYPER_MSHV; else if (!strncmp("VMwareVMware", hyper_vendor_id, 12)) hyper = HYPER_VMWARE; else if (!strncmp("UnisysSpar64", hyper_vendor_id, 12)) hyper = HYPER_SPAR; return hyper; } #else /* ! (__x86_64__ || __i386__) */ static int get_hypervisor(void) { return HYPER_NONE; } #endif int run_in_guest(void) { return get_hypervisor() != HYPER_NONE; } /* ** return maximum number of digits for PID/TID */ int getpidwidth(void) { FILE *fp; char linebuf[64]; int numdigits = 5; if ( (fp = fopen("/proc/sys/kernel/pid_max", "r")) != NULL) { if ( fgets(linebuf, sizeof(linebuf), fp) != NULL) { numdigits = strlen(linebuf) - 1; } fclose(fp); } return numdigits; } /* ** Convert UID number to user name by maintaining a hash list ** instead of calling getpwuid() for each conversion. */ #define UG2NHASH 1024 struct u2n { struct u2n *next; uid_t uid; char *uname; }; char * uid2name(uid_t uid) { static char firstcall = 1; static struct u2n *u2n_hash[UG2NHASH]; int hash; struct u2n *this; if (firstcall) { struct passwd *pwd; // setup hash list for UIDs with user names // while ( (pwd = getpwent())) { hash = pwd->pw_uid & (UG2NHASH-1); this = malloc(sizeof *this); ptrverify(this, "Malloc failed for u2n\n"); this->next = u2n_hash[hash]; u2n_hash[hash] = this; this->uid = pwd->pw_uid; this->uname = malloc( strlen(pwd->pw_name) + 1 ); ptrverify(this->uname, "Malloc failed for u2n name\n"); strcpy(this->uname, pwd->pw_name); } endpwent(); firstcall = 0; } // find UID in hash list and translate to name // for (hash = uid & (UG2NHASH-1), this = u2n_hash[hash]; this; this = this->next) { if (this->uid == uid) return this->uname; } return NULL; } /* ** Convert GID number to group name by maintaining a hash list ** instead of calling getgruid() for each conversion. */ struct g2n { struct g2n *next; gid_t gid; char *gname; }; char * gid2name(gid_t gid) { static char firstcall = 1; static struct g2n *g2n_hash[UG2NHASH]; int hash; struct g2n *this; if (firstcall) { struct group *group; // setup hash list for UIDs with user names // while ( (group = getgrent())) { hash = group->gr_gid & (UG2NHASH-1); this = malloc(sizeof *this); ptrverify(this, "Malloc failed for g2n\n"); this->next = g2n_hash[hash]; g2n_hash[hash] = this; this->gid = group->gr_gid; this->gname = malloc( strlen(group->gr_name) + 1 ); ptrverify(this->gname, "Malloc failed for g2n name\n"); strcpy(this->gname, group->gr_name); } endgrent(); firstcall = 0; } // find UID in hash list and translate to name // for (hash = gid & (UG2NHASH-1), this = g2n_hash[hash]; this; this = this->next) { if (this->gid == gid) return this->gname; } return NULL; } /* ** copy a string to a destination buffer that will always ** be null-terminated ** 'dstsize' is supposed to be the total size of the ** destination buffer including null-byte */ void safe_strcpy(char *dst, const char *src, size_t dstsize) { if (dstsize == 0) return; dst[0] = '\0'; strncat(dst, src, dstsize - 1); } atop-2.12.1/versdate.h0000644000203100020310000000014215064552761014036 0ustar gerlofgerlof#ifndef __ATOP_VERSDATA__ #define __ATOP_VERSDATA__ #define ATOPDATE "2025/09/23 19:13:15" #endif atop-2.12.1/version.c0000644000203100020310000000102315064552761013700 0ustar gerlofgerlof/* No manual changes in this file */ #include #include #include "version.h" #include "versdate.h" static char atopversion[] = ATOPVERS; static char atopdate[] = ATOPDATE; char * getstrvers(void) { static char vers[256]; snprintf(vers, sizeof vers, "Version: %s - %s ", atopversion, atopdate); return vers; } unsigned short getnumvers(void) { int vers1, vers2; sscanf(atopversion, "%u.%u", &vers1, &vers2); return (unsigned short) ((vers1 << 8) + vers2); } atop-2.12.1/version.h0000644000203100020310000000021715064552761013711 0ustar gerlofgerlof#ifndef __ATOP_VERSION__ #define __ATOP_VERSION__ #define ATOPVERS "2.12.1" char *getstrvers(void); unsigned short getnumvers(void); #endif atop-2.12.1/ChangeLog0000644000203100020310000005055715064552761013641 0ustar gerlofgerlofcommit 4492ec7f757d88c61b656b39236339a1602be1e7 Author: Gerlof Langeveld Date: Tue Sep 23 19:16:03 2025 +0200 Prepare version 2.12.1 M version.h commit 472b157b0f3eef07908011ab147cff8368782a24 Author: Gerlof Langeveld Date: Sun Sep 21 14:17:02 2025 +0200 Allow samples with same timestamp in rawlog (#346) When atop is terminated and restarted right away (like in the case of 'systemctl restart atop') two samples might get same timestamp. This should be allowed when extending an existing raw log. Apart from that, only a check is done on the cgroup version (v2) of the existing raw log instead of checking on all features. M rawlog.c commit d4b7240e1007e20c412af783983658c73f72d992 Author: Gerlof Langeveld Date: Sat Jun 14 12:34:53 2025 +0200 Prepare version 2.12 M atopconvert.c A prev/cgroups_212.h A prev/photoproc_212.h A prev/photosyst_212.h M version.h commit e922b4e0f31d35ba6a420bbfcb8a219d690b861d Author: Gerlof Langeveld Date: Sat Jun 14 10:03:10 2025 +0200 Remove unused function definition M atophide.c commit 9f40a3330aa326a15d5497b59e4b81386f7814e9 Author: Gerlof Langeveld Date: Sat Jun 14 09:53:06 2025 +0200 Improved error messages M showgeneric.c commit 6fc059ccb18a4bc67429c588995b14c727bdf0e4 Merge: 9528f60 298f8fb Author: Gerlof Langeveld Date: Mon Jun 9 20:28:57 2025 +0200 Merge pull request #343 from jbd/patch-1 command-line may contain a carriage return commit 9528f60c00deef4df413c09c99607f5d6d1559c4 Author: Gerlof Langeveld Date: Mon Jun 9 20:15:47 2025 +0200 Occasional difference threads vs processes (issue #342) Occasionally tolerate that the number of threads can be 1 or 2 smaller than the number of processes due to a possible race condition while gathering both amounts. This situation might specifically occur in the early boot phase when only single-threaded processes are running. M photoproc.c commit 298f8fbffe65135c0a03d9ab6844c5afba264640 Author: Jean-Baptiste Denis Date: Thu Jun 5 15:54:15 2025 +0200 command-line may contain a carriage return We have some issue when /proc/[pid]/cmdline contains a carriage return (don't ask why). It messes up atop output, whether it is interactive or parsable. Let's convert it to space like the null-bytes, linefeed and tabs. M photoproc.c commit 861e8e60bbb88bf7d5447ef84018b883b8f0f901 Author: Gerlof Langeveld Date: Mon Jun 2 12:12:51 2025 +0200 Definition of zswap usage in raw log The definition of the use of zswap should be stored in the raw log instead of checking this on the system on which the raw log is shown. M atop.c M atop.h M photosyst.c M photosyst.h M showsys.c commit cb1cd60fc7b432869150072d6b38288aae6f369e Author: Gerlof Langeveld Date: Sat May 31 15:25:51 2025 +0200 Definition of real NUMA in raw log The definition of the use of real NUMA (versus fake NUMA) should be stored in the raw log instead of checking this on the system on which the raw log is shown. M atop.c M atop.h M photosyst.c M photosyst.h M showlinux.c commit 19e7bbec262ef84f99497264058fda723b81d9ac Author: Gerlof Langeveld Date: Sat May 31 14:41:17 2025 +0200 Avoid divide by zero when InfiniBand rate is 0 M showsys.c commit 2a37b3d1a8a7dbe9b8cacabf01ec985b6f1e1770 Author: Gerlof Langeveld Date: Sat May 31 14:39:04 2025 +0200 Update comment about structure of raw log M rawlog.h commit 63377ebd3839c5c5b5659f4ba3ad80e20757b116 Author: Gerlof Langeveld Date: Sun May 25 19:59:05 2025 +0200 Add maxlineifb, maxlinenuma and maxlinellc M man/atoprc.5 commit c7540b772dfa63dc3d2b9063af67d418020f27db Author: Gerlof Langeveld Date: Sun May 25 19:40:32 2025 +0200 Recognize fake NUMA (also solves #74) In case of fake NUMA (which is used e.g. in case of Raspberry Pi 5) no NUMA-specific stats will be shown. Usually two lines are shown for every NUMA node which is a waste of screen lines for fake NUMA systems. Also a related buffer overflow and many potential buffer overflows have been corrected. M atop.c M atopcat.c M netlink.c M photosyst.c M photosyst.h M showlinux.c M showprocs.c M showsys.c commit f5acb32437d80ccb8b7d7293c31802521c6084b2 Author: Gerlof Langeveld Date: Thu May 22 15:39:31 2025 +0200 Remove atophide executable with make clean M Makefile commit 9448449cb88e526f749f26d79347716ea5f064fc Author: Gerlof Langeveld Date: Mon May 12 14:17:55 2025 +0200 Removed explicit prototype for getresuid M various.c commit ba9b68a916cf58dd5dd93d99dd745cd75345dde8 Author: Gerlof Langeveld Date: Sat May 10 14:45:11 2025 +0200 Forbid concatenate cgroups/nocgroups combi M atopcat.c commit 2303567d4353dacfe5ed908cc813af91816aa41f Author: Gerlof Langeveld Date: Fri May 9 11:31:45 2025 +0200 More sanity checks before extending raw log M rawlog.c commit 2bf011c9ac198a654f91770f7ca39f79cdea3eee Author: Gerlof Langeveld Date: Fri Apr 25 15:46:24 2025 +0200 Raw file integrity when expanding (issue #332) Verify the integrity of an existing raw file before expanding with the -w flag by reading the existing sample headers. M rawlog.c commit c0b5194d2eddad9f3e503b5852d413e6d5601661 Author: Gerlof Langeveld Date: Sat Apr 19 15:04:47 2025 +0200 Consistency check reading raw file (issue #332) A consistency check has been added while reading a raw file to determine if a sample has been completely written to the raw file. M rawlog.c commit a754ace399264673d82c9c055499e79d231c5920 Author: Gerlof Langeveld Date: Sat Apr 19 13:57:14 2025 +0200 Revert to consistent raw file size (issue #332) The writev() system call does not guarantee that data buffers are either completely written or none at all (e.g. when the disk space is exhausted while writing to a raw file). Therefore, the return value is checked for complete writing now instead of value -1 (error). M rawlog.c commit e831680db430c21ca885e10ac64f51d533c778df Author: Gerlof Langeveld Date: Sat Apr 19 10:36:51 2025 +0200 Add flags -k and -K to man page M man/atop.1 commit 80ba49aee740f2c47ad90878406424d67c32c65d Author: Gerlof Langeveld Date: Sat Apr 12 11:17:17 2025 +0200 Cleanup reading of raw log files (issue #333) Remove the automatic unzip of raw log files and the search for alternative atop-versions in case of raw log incompatibility. M rawlog.c commit e96ec10bca41d39c8bb5738b73283a62d7f4f5ae Merge: 3ce7bd9 f4de797 Author: Gerlof Langeveld Date: Sat Apr 12 10:39:58 2025 +0200 Merge pull request #331 from jrtc27/argv0-memcmp Fix memory-unsafe and buggy argv[0] string comparison commit 3ce7bd906316acf9f89a758684ccec7139ceaf1d Author: Gerlof Langeveld Date: Sat Apr 12 10:33:57 2025 +0200 Corrected typo (issue #329) M atop.c commit 48caa742bc2eb0af42e8482a02d5df2931c9aeeb Author: Gerlof Langeveld Date: Sat Apr 12 10:28:36 2025 +0200 Definition twindir[] to include file (isue #335) M atop.c M atop.h M showgeneric.c commit 6656f6f92409a92282b061497252d4955322631e Author: Gerlof Langeveld Date: Sat Apr 12 09:56:58 2025 +0200 Safer string copy (issue #335) Function strncpy() replaced by internal function safe_strcpy() that always takes care of null-termination. M acctproc.c M atop.c M atop.h M atophide.c M atopsar.c M gpucom.c M ifprop.c M photosyst.c M showgeneric.c M various.c commit 45744c60661cc926f9e972ac682b65026d93ab76 Author: Gerlof Langeveld Date: Sat Apr 5 12:24:44 2025 +0200 Add warning about usage of -z flag (issue #337) An explicit warning message has been added to the man page of atop and to the usage message that atop shows: do not use this flag when writing a publicly readable raw file. M atop.c M man/atop.1 commit 542b7f7ac52926ca272129dba81d7db80279bb98 Author: Gerlof Langeveld Date: Sat Mar 29 15:13:50 2025 +0100 Fix security vulnerability CVE-2025-31160 (#334) Atop will not connect to the TCP port of 'atopgpud' daemon any more by default. The flag -k can be used explicitly when 'atopgpud' is active. Also the code to parse the received strings is improved to avoid future issues with heap corruption. The flag -K has been implemented to connect to netatop/netatop-bpf. M atop.c M atop.h M gpucom.c M photoproc.c commit f4de7972767d9079bed11e64c83775a3024dfbef Author: Jessica Clarke Date: Wed Mar 26 21:38:17 2025 +0000 Fix memory-unsafe and buggy argv[0] string comparison memcmp assumes both its arguments have as many bytes as is specified, but argv[0] may not be at least 7 bytes long (including when run as atop, where it will have 5 bytes including the terminator). This can lead to buffer overreads, whether in the system memcmp implementation or in compiler-inlined versions (as is the case when compiling with LLVM, for example). Moreover, 7 was the incorrect count to be using anyway as it won't check that "atopsar" is terminated, so will return true if argv[0] merely has "atopsar" as a prefix. Fix both of these by using strcmp instead. M atop.c commit 77e658ea04f4901adf44c73cdd98bd4f0e664be0 Author: Gerlof Langeveld Date: Wed Mar 26 18:58:47 2025 +0100 Restored check for munmap call (solves #330) A check has been restored to avoid that the munmap() call is used on a NULL pointer since the behavior of munmap() is undefined in that case. M netatopif.c commit 037a6d3e4ace6c7be6c5dcf0c286c013e3c884cc Author: Gerlof Langeveld Date: Sat Mar 15 10:38:18 2025 +0100 Add network interface errors to -P and -J output Many error counters that were gathered per network interface (like number of input/output packets dropped) are also added to the parseable output (-PNET) and JSON output (-JNET). M json.c M man/atop.1 M parseable.c commit 8d1799bff61461ef151aed6e05b05cacb6475648 Merge: 9f2eaf8 bfd20dd Author: Gerlof Langeveld Date: Fri Mar 14 16:35:31 2025 +0100 Merge pull request #327 from GermanAizek/remove-excess-checks Removed excess checks before free() commit 9f2eaf8e47fe84400b933a29ca1853dafb3b5db1 Merge: a0e96f1 04853e2 Author: Gerlof Langeveld Date: Fri Mar 14 16:03:15 2025 +0100 Merge pull request #325 from GermanAizek/guard-bit-shifting-64bit Fixed possible wrong result bit shifting on 64bit after left op type overflow commit a0e96f124f93b178ae9dba616e8abc87763b3bc6 Merge: 69d5d9c 35aeb64 Author: Gerlof Langeveld Date: Fri Mar 14 16:01:41 2025 +0100 Merge pull request #324 from GermanAizek/begin-check-and-array-access-by-index Fixed possible access out-of-bounds items array better check index before using commit 69d5d9c594dd865874a6f62f746af84e8d26592e Author: Gerlof Langeveld Date: Fri Mar 14 15:57:20 2025 +0100 Modified char type variable to int (issue #323) M atopacctd.c commit ece93ebc7b9ebbb73f92b2238a87b78ddbd4ca98 Author: Gerlof Langeveld Date: Fri Mar 14 15:39:30 2025 +0100 Redefine function prototypes (solves issue #322) Certain function prototypes did not explicitly define the calling argument types, specifically the prototypes for the convert functions defined in the 'detail_printdef' structure. These incomplete definitions cause a lot of errors when using gcc 1.15 which is based on C23. M atop.c M atopacctd.c M atopcat.c M atopconvert.c M atophide.c M atopsar.c M cgroups.c M drawbar.c M gpucom.c M netatopif.c M photoproc.c M photosyst.c M rawlog.c M showgeneric.c M showlinux.h M showprocs.c M various.c commit bfd20dd1575ee1e237c7296c9b7736b944de1a50 Author: Herman Semenov Date: Wed Mar 12 05:15:26 2025 +0300 Removed excess checks before free() C89: 4.10.3.2 The free function. The free function causes the space pointed to by ptr to be deallocated, that is, made available for further allocation. If ptr is a null pointer, no action occurs. M atop.c M cgroups.c M deviate.c M gpucom.c M netatopif.c M showgeneric.c commit 04853e2de7c4ace7371274007586fadbcde730d8 Author: Herman Semenov Date: Wed Mar 12 04:43:50 2025 +0300 Fixed possible wrong result bit shifting on 64bit after left op type overflow M showprocs.c commit 070c75cfe5b11eca1b9d068aec298048d44b6199 Author: Herman Semenov Date: Wed Mar 12 04:40:29 2025 +0300 Fixed possible wrong result bit shifting on 64bit after left operand type overflow M cgroups.c commit 35aeb648d89af64292f81cfdf8526ea9d21ac0d0 Author: Herman Semenov Date: Wed Mar 12 04:26:00 2025 +0300 Fixed possible access out-of-bounds items array better check index before using M showlinux.c commit 7c96e867244db8f4362227ed27be355deb072d57 Author: Gerlof Langeveld Date: Sat Mar 1 12:14:56 2025 +0100 Highlight current sort criterium in text mode Highlight the column that is used as sort criterium in text mode 1) to be consistent with the output of cgroups stats ('G') and 2) to be prepared for the possibility to sort on other columns in the future (under investigation) M showprocs.c commit a993131cc79a97ba90f1d134abe631cd25385afa Author: Gerlof Langeveld Date: Wed Feb 12 16:00:38 2025 +0100 Build requirement glib2-devel added to .spec file M rpmspec/atop.specsystemd commit 3462fe6f4b6edd372168531e840ef6663c1378d3 Author: Gerlof Langeveld Date: Thu Jan 30 14:29:47 2025 +0100 Branch to end of raw log file (solves issue #319) With the key 'Z' it is possible to jump to the end of the raw log file (when reading raw log file or in twin mode). M atop.c M drawbar.c M man/atop.1 M rawlog.c M showgeneric.c M showgeneric.h commit 3c1d6aa7fa1867d6df0672b7db74c2a0bbffcadc Merge: 63ec829 64d8add Author: Gerlof Langeveld Date: Thu Jan 30 12:40:32 2025 +0100 Merge pull request #320 from bytedance/acct_bug /run/pacct_source abnormally large commit 64d8addca5ce491f16468dd87083b9cd6124e4b4 Author: liuting.0xffff Date: Thu Dec 26 20:10:35 2024 +0800 /run/pacct_source abnormally large M atopacctd.c commit 63ec829f7af9901ed17554556391f709394efd30 Author: Gerlof Langeveld Date: Sat Dec 7 10:18:51 2024 +0100 Also allow combining -B with other output channels M atop.c commit 56f6aae7f43729933b942c9ee1edc8c8577973ec Author: Gerlof Langeveld Date: Sat Dec 7 10:04:07 2024 +0100 Allow simultaneous output streams (solves #317) When starting atop, simultaneous output streams can be specified. This allows viewing the output in a full screen session or writing the output in parseable/JSON output while writing the samples to a raw file simultaneously. Examples: - Present full screen session (by explicitly defining one of the flags that are typical for a full screen session, like -g) while recording the samples in the file /tmp/rawout: atop -g -w /tmp/rawout - Generate JSON output and parseable output while recording the samples in the file /tmp/rawout with an interval of 4 seconds: atop -JCPU,MEM -PCPU,MEM -w /tmp/rawout 4 M atop.c M atop.h M atopsar.c M man/atop.1 M rawlog.c M various.c commit c9a7ee2ed08f625db398e2500a0e12f37ab3f5a0 Author: Gerlof Langeveld Date: Wed Nov 20 20:17:44 2024 +0100 Redesign of PSI for memory M drawbar.c commit 95ef95c42dd25e0fc1509533700f476fed7f975d Author: Gerlof Langeveld Date: Mon Nov 18 13:02:46 2024 +0100 Add PSI bar graphs for CPU, memory and disks Three bar graphs have been added for processors, disks and memory the show the PSI percentages 'some' (S) and 'full' (F). In this way a clear overview is given about the state of your critical hardware resources on system level. M drawbar.c M man/atop.1 M showgeneric.c M showgeneric.h commit 328041f35a582e7c3ec6230d9a9f43c8fadd16a5 Merge: 6d2733b 7478a80 Author: Gerlof Langeveld Date: Mon Nov 18 09:39:56 2024 +0100 Merge branch 'master' into barpsi commit 7478a809988d351ae05a734c3757294e11bc9af9 Author: Gerlof Langeveld Date: Mon Nov 18 09:37:02 2024 +0100 Remove double wrefresh call for memory graph Avoid screen flashing when drawing the memory graphs that was caused by a double call to wrefresh(). M drawbar.c commit 6d2733b6b3677dd74ab7b668c79d34ea31851b5d Author: Gerlof Langeveld Date: Mon Nov 18 09:34:31 2024 +0100 Add PSI bar graph for CPU and disks M drawbar.c M photosyst.c commit c48ae8512d3682ef7aebeba8bd31676683a11668 Author: Gerlof Langeveld Date: Thu Oct 24 11:41:35 2024 +0200 Add link to cheatsheet for keys M README.md commit 9228272a4aec00329255b1f4c5a5aee33eddfde3 Author: Gerlof Langeveld Date: Sat Oct 12 10:51:05 2024 +0200 UID to user name conversion via hash list M atop.h M showprocs.c M various.c commit bce8217582a2545456578970eb9557bd45d1f5fb Author: Gerlof Langeveld Date: Sat Oct 12 10:49:44 2024 +0200 Improve description of user selection (also UID) M man/atop.1 M man/atoprc.5 M showgeneric.c commit 631defdd22e3b6872c440d368d7acf459150eea5 Author: Gerlof Langeveld Date: Sat Sep 28 13:26:17 2024 +0200 Makefile: pass additional CFLAGS and LDFLAGS Allow user to pass CFLAGS and LDFLAGS which is needed for some build systems. For example, Buildroot needs to disable optimizations on some CPU architectures. Integrates pull request #314 M Makefile commit 119711710442327f4cd3c8a23199c646b1833c77 Author: Gerlof Langeveld Date: Sat Sep 28 12:35:10 2024 +0200 Introduce -I flag (solves issue #309 and #68) With the -I flag the translation from a UID/GID to a name will be suppressed, so all UIDs and GIDs will be shown as numbers. This can be useful to view a raw log file from another system (to avoid wrong translations using info from the local system). Besides, when a user name or group name is longer than 8 characters it will automatically be presented as a number instead of showing a truncated name (even if the -I flag has not been used). M atop.c M atop.h M man/atop.1 M showgeneric.c M showprocs.c commit 0e2b493ed418d55be1b79e1bab809fa7d0bef494 Merge: e530cfd 9569abf Author: Gerlof Langeveld Date: Sat Sep 28 11:28:14 2024 +0200 Merge branch 'clan-makefile' commit 9569abf13eb5b47b52a05335286ef67e2a9dfe17 Author: Gerlof Langeveld Date: Sat Sep 28 11:27:54 2024 +0200 Correct no-stringop-trunction to no-stringop-truncation M Makefile commit f5bc540e50ebb1e1c868a2d522f63b2ae22b2fdd Author: Z. Liu Date: Fri Aug 30 22:42:48 2024 +0800 -Wno-stringop-truncation is available for gcc only if compiled by clang, see following warning: warning: unknown warning option '-Wno-stringop-truncation'; did you mean '-Wno-format-truncation'? [-Wunknown-warning-option] so detect CC is clang or gcc, add option only for gcc M Makefile atop-2.12.1/45atoppm0000755000203100020310000000224215064552761013452 0ustar gerlofgerlof#!/bin/sh . "${PM_FUNCTIONS}" LOGPATH=/var/log/atop BINPATH=/usr/bin PIDFILE=/var/run/atop.pid INTERVAL=600 # interval 10 minutes CURDAY=`date +%Y%m%d` # current date in same format # If the system suspends, one final sample will be taken for the logfile # suspend_atop() { if [ -e $PIDFILE ] && ps -p `cat $PIDFILE` | grep 'atop$' > /dev/null then kill -USR2 `cat $PIDFILE` # final sample and terminate CNT=0 while ps -p `cat $PIDFILE` > /dev/null do let CNT+=1 if [ $CNT -gt 5 ] then break; fi sleep 1 done fi } # If the system resumes, a new atop will be started (similar to boot) # resume_atop() { # in case atop is running, stop it # if [ -e $PIDFILE ] && ps -p `cat $PIDFILE` | grep 'atop$' > /dev/null then kill -TERM `cat $PIDFILE` rm $PIDFILE sleep 1 fi # start atop # $BINPATH/atop -R -w $LOGPATH/atop_$CURDAY $INTERVAL \ > $LOGPATH/daily.log 2>&1 & echo $! > $PIDFILE # delete logfiles older than four weeks # ( sleep 3; find $LOGPATH -name 'atop_*' -mtime +28 -exec rm {} \; )& exit 0 } case "$1" in hibernate|suspend) suspend_atop ;; thaw|resume) resume_atop ;; *) exit $NA ;; esac atop-2.12.1/atop-pm.sh0000755000203100020310000000022215064552761013763 0ustar gerlofgerlof#!/bin/sh case "$1" in pre) /usr/bin/systemctl stop atop exit 0 ;; post) /usr/bin/systemctl start atop exit 0 ;; *) exit 1 ;; esac atop-2.12.1/Makefile0000644000203100020310000002021515064552761013513 0ustar gerlofgerlof# Makefile for System & Process Monitor ATOP (Linux version) # # Gerlof Langeveld - gerlof.langeveld@atoptool.nl # DESTDIR = BINPATH = /usr/bin SBINPATH = /usr/sbin SCRPATH = /usr/share/atop LOGPATH = /var/log/atop MAN1PATH = /usr/share/man/man1 MAN5PATH = /usr/share/man/man5 MAN8PATH = /usr/share/man/man8 INIPATH = /etc/init.d DEFPATH = /etc/default SYSDPATH = /lib/systemd/system CRNPATH = /etc/cron.d ROTPATH = /etc/logrotate.d PMPATH1 = /usr/lib/pm-utils/sleep.d PMPATH2 = /usr/lib64/pm-utils/sleep.d PMPATHD = /usr/lib/systemd/system-sleep PKG_CONFIG ?= pkg-config override CFLAGS := -O2 -I. -Wall $(shell $(PKG_CONFIG) --cflags glib-2.0) -Wmissing-prototypes -Wmissing-declarations -Wformat-security $(CFLAGS) # -DNOPERFEVENT # -DHTTPSTATS CC_CHECK := $(shell echo | $(CC) -dM -E - | grep -q __clang__ && echo clang || echo gcc) ifeq ($(CC_CHECK),gcc) override CFLAGS += -Wno-stringop-truncation endif override LDFLAGS := $(shell $(PKG_CONFIG) --libs glib-2.0) $(LDFLAGS) OBJMOD0 = version.o OBJMOD1 = various.o deviate.o procdbase.o OBJMOD2 = acctproc.o photoproc.o photosyst.o cgroups.o rawlog.o ifprop.o parseable.o OBJMOD3 = showgeneric.o drawbar.o showlinux.o showsys.o showprocs.o OBJMOD4 = atopsar.o netatopif.o netatopbpfif.o gpucom.o json.o utsnames.o ALLMODS = $(OBJMOD0) $(OBJMOD1) $(OBJMOD2) $(OBJMOD3) $(OBJMOD4) VERS = $(shell ./atop -V 2>/dev/null| sed -e 's/^[^ ]* //' -e 's/ .*//') all: atop atopsar atopacctd atopconvert atopcat atophide atop: atop.o $(ALLMODS) Makefile $(CC) atop.o $(ALLMODS) -o atop -lncursesw -lz -lm -lrt $(LDFLAGS) atopsar: atop ln -sf atop atopsar atopacctd: atopacctd.o netlink.o $(CC) atopacctd.o netlink.o -o atopacctd $(LDFLAGS) atopconvert: atopconvert.o $(CC) atopconvert.o -o atopconvert -lz $(LDFLAGS) atopcat: atopcat.o $(CC) atopcat.o -o atopcat $(LDFLAGS) atophide: atophide.o $(CC) atophide.o -o atophide -lz $(LDFLAGS) clean: rm -f *.o atop atopsar atopacctd atopconvert atopcat atophide versdate.h distr: rm -f *.o atop tar czvf /tmp/atop.tar.gz * # default install is based on systemd # install: genericinstall if [ ! -d $(DESTDIR)$(SYSDPATH) ]; \ then mkdir -p $(DESTDIR)$(SYSDPATH); fi if [ ! -d $(DESTDIR)$(PMPATHD) ]; \ then mkdir -p $(DESTDIR)$(PMPATHD); fi # cp atop.service $(DESTDIR)$(SYSDPATH) chmod 0644 $(DESTDIR)$(SYSDPATH)/atop.service cp atopgpu.service $(DESTDIR)$(SYSDPATH) chmod 0644 $(DESTDIR)$(SYSDPATH)/atopgpu.service cp atop-rotate.service $(DESTDIR)$(SYSDPATH) chmod 0644 $(DESTDIR)$(SYSDPATH)/atop-rotate.service cp atop-rotate.timer $(DESTDIR)$(SYSDPATH) chmod 0644 $(DESTDIR)$(SYSDPATH)/atop-rotate.timer cp atopacct.service $(DESTDIR)$(SYSDPATH) chmod 0644 $(DESTDIR)$(SYSDPATH)/atopacct.service cp atop-pm.sh $(DESTDIR)$(PMPATHD) chmod 0711 $(DESTDIR)$(PMPATHD)/atop-pm.sh # # only when making on target system: # if [ -z "$(DESTDIR)" -a -f /bin/systemctl ]; \ then /bin/systemctl disable --now atop 2> /dev/null; \ /bin/systemctl disable --now atopacct 2> /dev/null; \ /bin/systemctl daemon-reload; \ /bin/systemctl enable --now atopacct; \ /bin/systemctl enable --now atop; \ /bin/systemctl enable --now atop-rotate.timer; \ fi # explicitly use sysvinstall for System V init based systems # sysvinstall: genericinstall if [ ! -d $(DESTDIR)$(INIPATH) ]; \ then mkdir -p $(DESTDIR)$(INIPATH); fi if [ ! -d $(DESTDIR)$(SCRPATH) ]; \ then mkdir -p $(DESTDIR)$(SCRPATH); fi if [ ! -d $(DESTDIR)$(CRNPATH) ]; \ then mkdir -p $(DESTDIR)$(CRNPATH); fi if [ ! -d $(DESTDIR)$(ROTPATH) ]; \ then mkdir -p $(DESTDIR)$(ROTPATH); fi # cp atop.init $(DESTDIR)$(INIPATH)/atop cp atopacct.init $(DESTDIR)$(INIPATH)/atopacct cp atop.cronsysv $(DESTDIR)$(CRNPATH)/atop cp atop.daily $(DESTDIR)$(SCRPATH) chmod 0711 $(DESTDIR)$(SCRPATH)/atop.daily touch $(DESTDIR)$(LOGPATH)/dummy_before touch $(DESTDIR)$(LOGPATH)/dummy_after # if [ -d $(DESTDIR)$(PMPATH1) ]; \ then cp 45atoppm $(DESTDIR)$(PMPATH1); \ chmod 0711 $(DESTDIR)$(PMPATH1)/45atoppm; \ fi if [ -d $(DESTDIR)$(PMPATH2) ]; \ then cp 45atoppm $(DESTDIR)$(PMPATH2); \ chmod 0711 $(DESTDIR)$(PMPATH2)/45atoppm; \ fi # # # only when making on target system: # if [ -z "$(DESTDIR)" -a -f /sbin/chkconfig ]; \ then /sbin/chkconfig --del atop 2> /dev/null; \ /sbin/chkconfig --add atop; \ /sbin/chkconfig --del atopacct 2> /dev/null; \ /sbin/chkconfig --add atopacct; \ fi if [ -z "$(DESTDIR)" -a -f /usr/sbin/update-rc.d ]; \ then update-rc.d atop defaults; \ update-rc.d atopacct defaults; \ fi if [ -z "$(DESTDIR)" -a -f /sbin/service ]; \ then /sbin/service atopacct start; \ sleep 2; \ /sbin/service atop start; \ fi genericinstall: atop atopacctd atopconvert atopcat atophide if [ ! -d $(DESTDIR)$(LOGPATH) ]; \ then mkdir -p $(DESTDIR)$(LOGPATH); fi if [ ! -d $(DESTDIR)$(DEFPATH) ]; \ then mkdir -p $(DESTDIR)$(DEFPATH); fi if [ ! -d $(DESTDIR)$(BINPATH) ]; \ then mkdir -p $(DESTDIR)$(BINPATH); fi if [ ! -d $(DESTDIR)$(SBINPATH) ]; \ then mkdir -p $(DESTDIR)$(SBINPATH); fi if [ ! -d $(DESTDIR)$(MAN1PATH) ]; \ then mkdir -p $(DESTDIR)$(MAN1PATH); fi if [ ! -d $(DESTDIR)$(MAN5PATH) ]; \ then mkdir -p $(DESTDIR)$(MAN5PATH); fi if [ ! -d $(DESTDIR)$(MAN8PATH) ]; \ then mkdir -p $(DESTDIR)$(MAN8PATH); fi # touch $(DESTDIR)$(DEFPATH)/atop chmod 644 $(DESTDIR)$(DEFPATH)/atop # cp atop $(DESTDIR)$(BINPATH)/atop chmod 0711 $(DESTDIR)$(BINPATH)/atop ln -sf atop $(DESTDIR)$(BINPATH)/atopsar cp atopacctd $(DESTDIR)$(SBINPATH)/atopacctd chmod 0700 $(DESTDIR)$(SBINPATH)/atopacctd cp atopgpud $(DESTDIR)$(SBINPATH)/atopgpud chmod 0700 $(DESTDIR)$(SBINPATH)/atopgpud cp atop $(DESTDIR)$(BINPATH)/atop-$(VERS) ln -sf atop-$(VERS) $(DESTDIR)$(BINPATH)/atopsar-$(VERS) cp atopconvert $(DESTDIR)$(BINPATH)/atopconvert chmod 0711 $(DESTDIR)$(BINPATH)/atopconvert cp atopcat $(DESTDIR)$(BINPATH)/atopcat chmod 0711 $(DESTDIR)$(BINPATH)/atopcat cp atophide $(DESTDIR)$(BINPATH)/atophide chmod 0711 $(DESTDIR)$(BINPATH)/atophide cp man/atop.1 $(DESTDIR)$(MAN1PATH) cp man/atopsar.1 $(DESTDIR)$(MAN1PATH) cp man/atopconvert.1 $(DESTDIR)$(MAN1PATH) cp man/atopcat.1 $(DESTDIR)$(MAN1PATH) cp man/atophide.1 $(DESTDIR)$(MAN1PATH) cp man/atoprc.5 $(DESTDIR)$(MAN5PATH) cp man/atopacctd.8 $(DESTDIR)$(MAN8PATH) cp man/atopgpud.8 $(DESTDIR)$(MAN8PATH) ########################################################################## versdate.h: ./mkdate atop.o: atop.h photoproc.h photosyst.h acctproc.h showgeneric.h atopsar.o: atop.h photoproc.h photosyst.h rawlog.o: atop.h photoproc.h photosyst.h rawlog.h showgeneric.h various.o: atop.h acctproc.h ifprop.o: atop.h photosyst.h ifprop.h parseable.o: atop.h photoproc.h photosyst.h cgroups.h parseable.h deviate.o: atop.h photoproc.h photosyst.h procdbase.o: atop.h photoproc.h acctproc.o: atop.h photoproc.h atopacctd.h acctproc.h netatop.h netatopif.o: atop.h photoproc.h netatopd.h netatop.h netatopbpfif.o: atop.h photoproc.h netatop.h photoproc.o: atop.h photoproc.h photosyst.o: atop.h photosyst.h cgroups.o: atop.h cgroups.h showgeneric.o: atop.h photoproc.h photosyst.h cgroups.h showgeneric.h showlinux.h showlinux.o: atop.h photoproc.h photosyst.h cgroups.h showgeneric.h showlinux.h showsys.o: atop.h photoproc.h photosyst.h showgeneric.h showprocs.o: atop.h photoproc.h photosyst.h cgroups.h showgeneric.h showlinux.h drawbar.o: atop.h photosyst.h showgeneric.h version.o: version.c version.h versdate.h gpucom.o: atop.h photoproc.h photosyst.h atopacctd.o: atop.h photoproc.h acctproc.h atopacctd.h version.h versdate.h atopconvert.o: atop.h photoproc.h photosyst.h rawlog.h atopcat.o: atop.h rawlog.h atophide.o: atop.h photoproc.h photosyst.h rawlog.h atop-2.12.1/README0000644000203100020310000000727015064552761012741 0ustar gerlofgerlofCOPYRIGHT NOTICE ---------------- For all files that are not marked differently: Copyright Gerlof Langeveld 2007-2024 and licensed under the GPL v2 (or any later version). DEPENDENCIES ------------ Install the following packages to be able to build atop (package name can be different depending on the Linux distro): * zlib-devel or libz-dev or zlib1g-dev * ncurses-devel or libncurses5-dev/libncursesw5-dev * glib2-devel or libglib2.0-dev Install the following packages to be able to execute atop (package name can be different depending on the Linux distro): * zlib or zlib1g * ncurses or libncurses5/libncursesw5 * glib2 or libglib2.0 INSTALLING AND USING ATOP FROM TARBALL -------------------------------------- For interactive use, it is sufficient to install ATOP with the command (as root): make install (systemd based) or make sysvinstall (System V init based) For automatic logging in compressed binary format, see the description in the manual-page. NETWORK-RELATED COUNTERS PER PROCESS/THREAD ------------------------------------------- The kernel module 'netatop' can be downloaded and installed separately from www.atoptool.nl/downloadnetatop.php This module is optional and can be used to gather network statistics per process/thread as described in www.atoptool.nl/netatop.php Alternatively, the BPF implementation 'netatop-bpf' can be downloaded and installed from https://github.com/bytedance/netatop-bpf PROCESS ACCOUNTING WITH PSACCT/ACCT PACKAGE ------------------------------------------- Preferably, process accounting should be handled by the atopacctd daemon which is implicitly installed when installing atop. This daemon takes care that process accounting is only active when at least one atop process is running. Besides, when process accounting is activated, atopacctd takes care that the disk utilization is minimal. See the man page of atopacctd for further details. When the psacct or acct package is installed in parallel with the atop package and you want to enable/start the (ps)acct service for permanent process accounting, the atopacct.service refuses to activate the atopacctd daemon to avoid clashes. When the (ps)acct service is enabled, atop automatically uses the process accounting file that is used by this package. See also the section PROCESS ACCOUNTING in the man page of atop. OVERVIEW OF RELATED PROGRAMS ---------------------------- When installing atop the following programs are provided: atop - System and process monitor (live and from raw log) atopacctd - Daemon: handles process accounting records to be used by atop atopgpud - Daemon: gathers metrics from Nvidia GPUs to be used by atop atopsar - System activity reports (live and from raw log) atopcat - Concatenate raw log files and provide raw log information atophide - Make extractions from raw logs and/or anonymize raw logs atopconvert - Convert raw log to newer version SERVICE ACTIVATION AFTER INSTALLATING PACKAGE --------------------------------------------- After the package has been installed, be sure that the related services are activated. To support handling of terminated processes using process accounting, enable the atopacct service: systemctl enable --now atopacct Notice that this service should not be enabled when the package psacct or acct (depends on the Linux distribution) has been installed and enabled. In that case atop uses the daily process accounting file created via that package. To support maintaining daily log files to keep track of long-term analysis information, enable the atop service and the related timer: systemctl enable --now atop systemctl enable --now atop-rotate.timer Gerlof Langeveld gerlof.langeveld@atoptool.nl atop-2.12.1/AUTHORS0000644000203100020310000000077415064552761013133 0ustar gerlofgerlofCreated and maintained by: Gerlof Langeveld Contributions by: Jan-Christiaan van Winkel Zhenwei Pi Fei Li Nathan Scott rpungartnik (Roberto) Vincent Lefèvre Hunter1016 Fabrice Fontaine Georgiy Komarov gleventhal SjonHortensius sylmarch (Sylvian) 0pointerexception (Wilko) 7h3w1zz (Jacob) Sam James Peter Wang Meinhard Zhou Zachary P. Landau Daniel Brooks Ike Devolder codebling Justin Kromlinger Alastair Young roadrunner2 liutingjieni Algebra970 Donatas Abraitis Marc Haber atop-2.12.1/COPYING0000644000203100020310000004313115064552761013110 0ustar gerlofgerlof GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. atop-2.12.1/mkdate0000755000203100020310000000042315064552761013245 0ustar gerlofgerlof#!/bin/sh # Make a new versdate.h with the current date filled # CURDATE=$(date +%Y/%m/%d\ %H:%M:%S) echo "#ifndef __ATOP_VERSDATA__" > versdate.h echo "#define __ATOP_VERSDATA__" >> versdate.h echo "#define ATOPDATE \"$CURDATE\"" >> versdate.h echo "#endif" >> versdate.h atop-2.12.1/atopacct.init0000755000203100020310000000333515064552761014545 0ustar gerlofgerlof#!/bin/sh # # atopacctd Startup script for the atopacctd daemon # # chkconfig: 2345 91 9 # description: Process accounting control # ### BEGIN INIT INFO # Provides: atopacct # Required-Start: $local_fs $remote_fs # Required-Stop: $local_fs $remote_fs # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: This daemon switches on process accounting and # transfers the process accounting records 'realtime' # to small shadow files to avoid huge disk space usage # Description: Process accounting control ### END INIT INFO # Check existance of binaries [ -f /usr/sbin/atopacctd ] || exit 0 RETVAL=0 # See how we were called. case "$1" in start) # Check if process accounting already in use via separate psacct package # for PACCTFILE in /var/account/pacct /var/log/pacct /var/log/account/pacct do if [ -f "$PACCTFILE" ] # file exists? then BEFORSIZE=$(ls -lL "$PACCTFILE" | awk '{print $5}') AFTERSIZE=$(ls -lL "$PACCTFILE" | awk '{print $5}') # verify if accounting file grows, so is in use # if [ $BEFORSIZE -lt $AFTERSIZE ] then echo Process accounting already used by psacct! >&2 exit $RETVAL # do not start atopacctd fi fi done # Check if atopacctd runs already # if ps -e | grep -q atopacctd$ then : else # Start atopacctd rm -rf /run/pacct_shadow.d 2> /dev/null /usr/sbin/atopacctd fi touch /var/lock/subsys/atopacctd ;; stop) # Check if atopacctd runs # if ps -e | grep -q atopacctd$ then kill $(ps -e | grep atopacctd$ | sed 's/^ *//' | cut -d' ' -f1) fi rm -f /var/lock/subsys/atopacctd ;; status) ;; reload) ;; restart) ;; *) echo "Usage: $0 [start|stop]" exit 1 esac exit $RETVAL atop-2.12.1/atopacct.service0000644000203100020310000000054315064552761015235 0ustar gerlofgerlof[Unit] Description=Atop process accounting daemon Documentation=man:atopacctd(8) Before=atop.service [Service] Type=forking PIDFile=/run/atopacctd.pid ExecStartPre=/bin/sh -c 'if systemctl -q is-active acct psacct; then echo "Process accounting already in use by (ps)acct"; exit 1; fi' ExecStart=/usr/sbin/atopacctd [Install] WantedBy=multi-user.target atop-2.12.1/atopgpu.service0000644000203100020310000000031215064552761015110 0ustar gerlofgerlof[Unit] Description=Atop GPU stats daemon Documentation=man:atopgpud(8) Before=atop.service [Service] ExecStart=/usr/sbin/atopgpud Type=oneshot RemainAfterExit=yes [Install] WantedBy=multi-user.target atop-2.12.1/atop.rc.openrc0000644000203100020310000000041115064552761014625 0ustar gerlofgerlof#!/sbin/openrc-run # Copyright 1999-2021 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 description="Resource-specific view of processes" pidfile="/var/run/atop.pid" command="/usr/share/atop/atop.daily" command_background="true" atop-2.12.1/atopacct.rc.openrc0000644000203100020310000000203215064552761015461 0ustar gerlofgerlof#!/sbin/openrc-run # Copyright 1999-2021 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 description="Resource-specific view of processes" command="/usr/sbin/atopacctd" lockfile="/var/lock/subsys/atopacctd" start_pre() { # Check if process accounting already in use via psacct for PACCTFILE in /var/account/pacct /var/log/pacct ; do if [ -f "${PACCTFILE}" ] ; then BEFORSIZE=$(stat -c %s "${PACCTFILE}") AFTERSIZE=$(stat -c %s "${PACCTFILE}") # verify if accounting file grows, so is in use if [ ${BEFORSIZE} -lt ${AFTERSIZE} ] ; then ewarn "Process accounting already used by psacct!" return 1 fi fi done checkpath -d -q ${lockfile%/*} || return 1 } start() { ebegin "Starting atopacctd" start-stop-daemon --start --exec ${command} touch ${lockfile} eend $? } stop() { ebegin "Stopping atopacctd" start-stop-daemon --stop --exec ${command} rm ${lockfile} eend $? } atop-2.12.1/atop.cronsysv0000644000203100020310000000011715064552761014625 0ustar gerlofgerlof# daily restart of atop at midnight 0 0 * * * root /usr/share/atop/atop.daily& atop-2.12.1/atop.daily0000755000203100020310000000267415064552761014056 0ustar gerlofgerlof#!/bin/sh LOGOPTS="" # default options LOGINTERVAL=600 # default interval in seconds LOGGENERATIONS=28 # default number of days LOGPATH=/var/log/atop # default log location mkdir -p "$LOGPATH" # allow administrator to overrule the variables # defined above # DEFAULTSFILE=/etc/default/atop # possibility to overrule vars if [ -e "$DEFAULTSFILE" ] then . "$DEFAULTSFILE" # validate overruled variables # (LOGOPTS and LOGINTERVAL are implicitly by atop) # case "$LOGGENERATIONS" in ''|*[!0-9]*) echo non-numerical value for LOGGENERATIONS >&2 exit 1;; esac fi CURDAY=`date +%Y%m%d` BINPATH=/usr/bin PIDFILE=/var/run/atop.pid # verify if atop still runs for daily logging # if [ -e "$PIDFILE" ] && ps -p `cat "$PIDFILE"` | grep 'atop$' > /dev/null then kill -USR2 `cat "$PIDFILE"` # final sample and terminate CNT=0 while ps -p `cat "$PIDFILE"` > /dev/null do CNT=$((CNT + 1)) if [ $CNT -gt 5 ] then break; fi sleep 1 done rm "$PIDFILE" fi # delete logfiles older than N days (configurable) # start a child shell that activates another child shell in # the background to avoid a zombie # ( (sleep 3; find "$LOGPATH" -name 'atop_*' -mtime +"$LOGGENERATIONS" -exec rm {} \;)& ) # activate atop with an interval of S seconds (configurable), # replacing the current shell # echo $$ > $PIDFILE exec $BINPATH/atop $LOGOPTS -w "$LOGPATH"/atop_"$CURDAY" "$LOGINTERVAL" > "$LOGPATH/daily.log" 2>&1 atop-2.12.1/atop.init0000755000203100020310000000255115064552761013711 0ustar gerlofgerlof#!/bin/sh # # atop Startup script for the Atop process logging in background # # chkconfig: 2345 96 4 # description: Advanced system and process activity monitor # ### BEGIN INIT INFO # Provides: atop # Required-Start: $local_fs $remote_fs # Required-Stop: $local_fs $remote_fs # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Advanced system and process activity monitor # Description: Advanced system and process activity monitor ### END INIT INFO # Check existance of binaries [ -f /usr/bin/atop ] || exit 0 PIDFILE=/var/run/atop.pid RETVAL=0 # See how we were called. case "$1" in start) # Check if atop runs already # if [ -e $PIDFILE ] && ps -p `cat $PIDFILE` | grep 'atop$' > /dev/null then : else # Start atop /usr/share/atop/atop.daily& fi touch /var/lock/subsys/atop ;; stop) # Check if atop runs # if [ -e $PIDFILE ] && ps -p `cat $PIDFILE` | grep 'atop$' > /dev/null then kill -USR2 `cat $PIDFILE` # final sample and terminate CNT=0 while ps -p `cat $PIDFILE` > /dev/null do CNT=$((CNT + 1)) if [ $CNT -gt 5 ] then break; fi sleep 1 done rm $PIDFILE fi rm -f /var/lock/subsys/atop ;; status) ;; reload) /usr/share/atop/atop.daily& ;; restart) /usr/share/atop/atop.daily& ;; *) echo "Usage: $0 [start|stop|status|reload|restart]" exit 1 esac exit $RETVAL atop-2.12.1/atop.default0000644000203100020310000000010315064552761014356 0ustar gerlofgerlofLOGOPTS="" LOGINTERVAL=600 LOGGENERATIONS=28 LOGPATH=/var/log/atop atop-2.12.1/atop.service0000644000203100020310000000140715064552761014402 0ustar gerlofgerlof[Unit] Description=Atop advanced performance monitor Documentation=man:atop(1) [Service] Type=simple Environment="LOGOPTS=" Environment="LOGINTERVAL=600" Environment="LOGGENERATIONS=28" Environment="LOGPATH=/var/log/atop" EnvironmentFile=/etc/default/atop ExecStartPre=/bin/sh -c 'test -d "${LOGPATH}" || mkdir -p "${LOGPATH}"' ExecStartPre=/bin/sh -c 'test -n "$LOGINTERVAL" -a "$LOGINTERVAL" -eq "$LOGINTERVAL"' ExecStartPre=/bin/sh -c 'test -n "$LOGGENERATIONS" -a "$LOGGENERATIONS" -eq "$LOGGENERATIONS"' ExecStart=/bin/sh -c 'exec /usr/bin/atop ${LOGOPTS} -w "${LOGPATH}/atop_$(date +%%Y%%m%%d)" ${LOGINTERVAL}' ExecStartPost=/usr/bin/find "${LOGPATH}" -name "atop_*" -mtime +${LOGGENERATIONS} -exec rm -v {} \; KillSignal=SIGUSR2 [Install] WantedBy=multi-user.target atop-2.12.1/atop-rotate.service0000644000203100020310000000023215064552761015671 0ustar gerlofgerlof[Unit] Description=Restart atop daemon to rotate logs Documentation=man:atop(1) [Service] Type=oneshot ExecStart=/usr/bin/systemctl restart atop.service atop-2.12.1/atop-rotate.timer0000644000203100020310000000017415064552761015356 0ustar gerlofgerlof[Unit] Description=Daily atop restart Documentation=man:atop(1) [Timer] OnCalendar=daily [Install] WantedBy=timers.target atop-2.12.1/atopgpud0000755000203100020310000004623515064552761013636 0ustar gerlofgerlof#!/usr/bin/python3 -Es # ============================================================== # Daemon that gathers statistical information from all # Nvidia GPUs in the current system. Every second, it gathers # the statistics of every GPU and maintains cumulative counters, # globally and per process. # # Client processes can connect to this daemon on TCP port 59123. # Clients can send requests of two bytes, consisting of one byte # request code followed by one byte integer version number. # The request code can be 'T' to obtain the GPU types or 'S' to # obtain all statistical counters. # The response of the daemon starts with a 4-byte integer. The # first byte is the version of the response format and the # subsequent three bytes indicate the length (big endian) of the # response string that follows. See the formatters for the layout # of the response string, later on in this source code. # # Dependencies: pip/pip3 install nvidia-ml-py # # This program can be executed by python2 or python3 (just change # the first line of this source file). # -------------------------------------------------------------- # Author: Gerlof Langeveld # Date: July 2018 (initial) # # Copyright (C) 2018 Gerlof Langeveld # # This program is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation; either version 2, or (at your option) any # later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # ============================================================== import os import sys import time import socket import struct import logging import logging.handlers as loghand import threading GPUDPORT = 59123 # TCP port number server COMPUTE = 1 # task support bit value ACCOUNT = 2 # task support bit value # ================================= # GPU related bookkeeping # ================================= gpulist = [] # list with per-GPU bookkeeping cliterm = {} # dict with one entry per client (client # socket as key), that contains a dict with # the terminated per-process bookkeepings # that still have to be received by this client # (pid of terminated process as key) gpulock = threading.Lock() # mutex for access to gpulist/cliterm # ================================= # per-GPU class # ================================= class Stats(object): pass # generic statistics container class GpuProp(object): ############################### # initialization method to setup # properties ############################### def __init__(self, num): gpuhandle = pynvml.nvmlDeviceGetHandleByIndex(num) pciInfo = pynvml.nvmlDeviceGetPciInfo(gpuhandle) self.gpuhandle = gpuhandle self.stats = Stats() self.stats.busid = pciInfo.busId self.stats.devname = pynvml.nvmlDeviceGetName(gpuhandle) # nvml backward compatibility if type(self.stats.busid) == bytes: self.stats.busid = self.stats.busid.decode('ascii', errors='replace') if type(self.stats.devname) == bytes: self.stats.devname = self.stats.devname.decode('ascii', errors='replace') self.stats.devname = self.stats.devname.replace(' ', '_') self.stats.tasksupport = 0 # process stats support try: procinfo = pynvml.nvmlDeviceGetComputeRunningProcesses(gpuhandle) self.stats.tasksupport |= COMPUTE # compute support except Exception: pass # no compute support try: pynvml.nvmlDeviceSetAccountingMode(gpuhandle, True) pynvml.nvmlDeviceSetPersistenceMode(gpuhandle, True) # NVIDIA advise self.stats.tasksupport |= ACCOUNT # account support except Exception as e: pass self.stats.gpupercnow = 0 # perc of time that GPU was busy self.stats.mempercnow = 0 # perc of time that memory was rd/wr self.stats.memtotalnow = 0 # in Kb self.stats.memusednow = 0 # in Kb self.stats.gpusamples = 0 self.stats.gpuperccum = 0 # perc of time that GPU was busy self.stats.memperccum = 0 # perc of time that memory was rd/wr self.stats.memusedcum = 0 # in KiB self.stats.procstats = {} # stats of active processes (key = pid) ############################### # method to fetch counters and values ############################### def readstats(self): self.stats.gpusamples += 1 # ----------------------------- # get rates (utilization percentages) # ----------------------------- try: rates = pynvml.nvmlDeviceGetUtilizationRates(self.gpuhandle) self.stats.gpupercnow = rates.gpu self.stats.mempercnow = rates.memory self.stats.gpuperccum += rates.gpu self.stats.memperccum += rates.memory except pynvml.NVMLError as err: self.stats.gpupercnow = -1 self.stats.mempercnow = -1 self.stats.gpuperccum = -1 self.stats.memperccum = -1 # ----------------------------- # get memory occupation GPU-wide # ----------------------------- try: meminfo = pynvml.nvmlDeviceGetMemoryInfo(self.gpuhandle) self.stats.memtotalnow = meminfo.total // 1024 self.stats.memusednow = meminfo.used // 1024 self.stats.memusedcum += meminfo.used // 1024 # in KiB except pynvml.NVMLError as err: pass # ----------------------------- # get per-process statistics # ----------------------------- try: procinfo = pynvml.nvmlDeviceGetComputeRunningProcesses( self.gpuhandle) # ------------------------- # build list with pids from # the previous interval # ------------------------- actprocs = list(self.stats.procstats.keys()) # ------------------------- # handle proc stats of this # interval # ------------------------- for proc in procinfo: pid = proc.pid # --------------------- # new process? # create new stats # --------------------- if pid not in actprocs: self.stats.procstats[pid] = Stats() self.stats.procstats[pid].memnow = 0 # in KiB self.stats.procstats[pid].memcum = 0 # in KiB self.stats.procstats[pid].sample = 0 self.stats.procstats[pid].gpubusy = -1 self.stats.procstats[pid].membusy = -1 self.stats.procstats[pid].timems = -1 else: actprocs.remove(pid) # --------------------- # maintain proc stats # --------------------- if proc.usedGpuMemory: self.stats.procstats[pid].memnow = proc.usedGpuMemory//1024 self.stats.procstats[pid].memcum += proc.usedGpuMemory//1024 self.stats.procstats[pid].sample += 1 if self.stats.tasksupport & ACCOUNT: try: stats = pynvml.nvmlDeviceGetAccountingStats(self.gpuhandle, pid) self.stats.procstats[pid].gpubusy = stats.gpuUtilization self.stats.procstats[pid].membusy = stats.memoryUtilization self.stats.procstats[pid].timems = stats.time except Exception: pass # ------------------------- # determine which processes # have terminated since # previous sample # ------------------------- for pid in actprocs: for client in cliterm: cliterm[client][pid] = self.stats.procstats[pid] del self.stats.procstats[pid] except pynvml.NVMLError as err: pass ############################### # obtain current statistics ############################### def getstats(self): return self.stats # ================================= # Main function # ================================= def main(): # ----------------------------- # initialize GPU access, # specifically to detect of it # succeeds # ----------------------------- try: pynvml.nvmlInit() except Exception: logging.error("Shared lib 'libnvidia-ml' probably not installed!") sys.exit() # ----------------------------- # open IPv6 stream socket # ----------------------------- try: mysock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM, 0) except Exception as sockex: try: mysock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) except Exception as sockex: logging.error("Socket creation fails") sys.exit(1) # ----------------------------- # bind to local port and # make socket passive # ----------------------------- try: mysock.bind( ("", GPUDPORT) ) mysock.listen(32) except Exception as sockex: logging.error("Socket binding to port %d fails", GPUDPORT) sys.exit(1) # ----------------------------- # release parent process # (daemonize) # ----------------------------- try: if os.fork(): sys.exit(0) # parent process exits; child continues... except Exception: logging.error("Failed to fork child") # ----------------------------- # initialize GPU access for the # child process # ----------------------------- try: pynvml.nvmlInit() except Exception: pass # ----------------------------- # determine number of GPUs in # this system # ----------------------------- gpunum = pynvml.nvmlDeviceGetCount() logging.info("Number of GPUs: %d", gpunum) if gpunum == 0: logging.info("Terminated (no GPUs available)") sys.exit() # ----------------------------- # initialize per-GPU bookkeeping # ----------------------------- for i in range(gpunum): gpulist.append( GpuProp(i) ) # ----------------------------- # kick off new thread to fetch # statistics periodically # ----------------------------- t = threading.Thread(target=gpuscanner, args=(1,)) t.daemon = True t.start() logging.info("Initialization succeeded") # ----------------------------- # main thread: # await connect of client # ----------------------------- while True: newsock, peeraddr = mysock.accept() # ------------------------- # create new thread to # serve this client # ------------------------- t = threading.Thread(target=serveclient, args=(newsock, peeraddr)) t.daemon = True t.start() # =========================================== # Thread start function: # Serve new client that has just # connected. # # ------------------------------------------- # Protocol between client and server: # # - client transmits request # consisting of two bytes # # byte 0: type of request # 'S' get statistical counters # 'T' get type of each GPU # # byte 1: integer version number # response layout might change # so the client asks for a # specific response version # # - server transmits response # consisting of a four bytes integer # in big endian byte order # # byte 0: version number, preferably # as requested by the client # # byte 1-3: length of the response string # that follows # # followed by the response string that is # version specific (see gpuformatters) # =========================================== def serveclient(sock, peer): # ----------------------------- # create per client bookkeeping # for terminated processes # ----------------------------- with gpulock: cliterm[sock] = {} # ----------------------------- # main loop # ----------------------------- while True: # ------------------------- # wait for request # ------------------------- try: rcvbuf = sock.recv(20) except Exception as sockex: logging.error("Receive error: %s", sockex) sock.close() break # ------------------------- # connection closed by peer? # ------------------------- if not rcvbuf: sock.close() break logging.debug("Received: %s", rcvbuf) # ------------------------- # request has wrong length? # ------------------------- if len(rcvbuf) != 2: logging.error('Wrong request length: %d', len(rcvbuf)) sock.close() break # ------------------------- # valid request: # get statistical counters? # ------------------------- try: command = chr(rcvbuf[0]) # Python3 version = rcvbuf[1] except Exception: command = rcvbuf[0] # Python2 version = ord(rcvbuf[1]) if command == 'S': if version == 0 or version >= len(gpuformatters): version = len(gpuformatters)-1 xmitbuf = gpuformatters[version](sock).encode('ascii', errors='replace') # ------------------------- # valid request: # get GPU types? # ------------------------- elif command == 'T': if version == 0 or version >= len(gpudevnames): version = len(gpudevnames)-1 xmitbuf = gpudevnames[version]().encode('ascii', errors='replace') # ------------------------- # invalid request! # ------------------------- else: logging.error('Wrong request from client: %s', command) sock.close() break # ------------------------- # transmit GPU statistics # as bytes # ------------------------- logging.debug("Send: %s", xmitbuf) prelude = struct.pack(">I", (version << 24) + len(xmitbuf)) try: sock.send(prelude) sock.send(xmitbuf) except Exception as sockex: logging.error("Send error: %s", sockex) sock.close() break # ----------------------------- # delete per client bookkeeping # of terminated processes # ----------------------------- with gpulock: del cliterm[sock] # ----------------------------- # END OF CLIENT THREAD # ----------------------------- # ================================= # Generate sequence of device names # ================================= def gpudevname_v1(): # ----------------------------- # main loop: # - get device name of every GPU # - convert into one string # with format: # numgpus@busid devname tasksupport@busid devname tasksupport@... # ----------------------------- strbuf = str( len(gpulist) ) with gpulock: for i, gpu in enumerate(gpulist): s = gpu.getstats() strbuf += "@{:s} {:s} {:d}".format( s.busid, s.devname, s.tasksupport) return strbuf gpudevnames = [None, gpudevname_v1] # ================================= # Convert statistics of all GPUs # into parseable string # ================================= def gpuformatter_v1(clisock): # ----------------------------- # main loop: # - get statistics for every GPU # - convert stats to one string # with format: # numgpus@gpu0 stats#pid stats#pid stats@gpu1 stats#pid stats@.... # ----------------------------- strbuf = "" with gpulock: for i, gpu in enumerate(gpulist): s = gpu.getstats() # --------------------- # generic GPU stats # --------------------- strbuf += "@{:d} {:d} {:d} {:d} {:d} {:d} {:d} {:d}".format( s.gpupercnow, s.mempercnow, s.memtotalnow, s.memusednow, s.gpusamples, s.gpuperccum, s.memperccum, s.memusedcum); # --------------------- # active processes for # this GPU # --------------------- for pid, stat in s.procstats.items(): strbuf += "#A {:d} {:d} {:d} {:d} {:d} {:d} {:d}".format(pid, stat.gpubusy, stat.membusy, stat.timems, stat.memnow, stat.memcum, stat.sample) # --------------------- # terminated processes # for this GPU # --------------------- for pid, stat in cliterm[clisock].items(): strbuf += "#E {:d} {:d} {:d} {:d} {:d} {:d} {:d}".format(pid, stat.gpubusy, stat.membusy, stat.timems, stat.memnow, stat.memcum, stat.sample) cliterm[clisock].clear() return strbuf gpuformatters = [None, gpuformatter_v1] # ================================= # Thread start function: # Scan all GPUs with a particular # interval to obtain their stats # ================================= def gpuscanner(scaninterval): # ----------------------------- # main loop: # - get statistics for every GPU # - sleep for interval # ----------------------------- while True: with gpulock: for gpu in gpulist: gpu.readstats() time.sleep(scaninterval) # ========================================================================== # ----------------------------- # initialize logging # ----------------------------- if '-v' in sys.argv: loglevel = logging.DEBUG else: loglevel = logging.INFO fm = logging.Formatter('atopgpud %(levelname)s: %(message)s') fh = loghand.SysLogHandler('/dev/log', facility=loghand.SysLogHandler.LOG_DAEMON) fh.setFormatter(fm) fh.setLevel(loglevel) lg = logging.getLogger() # root logger lg.addHandler(fh) lg.setLevel(loglevel) # ----------------------------- # load module pynvml # ----------------------------- try: import pynvml except Exception: logging.error("Python module 'pynvml' not installed!") sys.exit(1) try: # ----------------------------- # call main function # ----------------------------- main() finally: # ----------------------------- # shutdown GPU access # ----------------------------- try: pynvml.nvmlShutdown() except Exception: pass atop-2.12.1/prev/0000755000203100020310000000000015064552761013027 5ustar gerlofgerlofatop-2.12.1/prev/photosyst_202.h0000644000203100020310000002016515064552761015643 0ustar gerlofgerlof#define MAXCPU_22 2048 #define MAXDSK_22 1024 #define MAXLVM_22 2048 #define MAXMDD_22 256 #define MAXINTF_22 128 #define MAXCONTAINER_22 128 #define MAXNFSMOUNT_22 64 #define MAXDKNAM 32 /************************************************************************/ struct memstat_22 { count_t physmem; // number of physical pages count_t freemem; // number of free pages count_t buffermem; // number of buffer pages count_t slabmem; // number of slab pages count_t cachemem; // number of cache pages count_t cachedrt; // number of cache pages (dirty) count_t totswap; // number of pages in swap count_t freeswap; // number of free swap pages count_t pgscans; // number of page scans count_t pgsteal; // number of page steals count_t allocstall; // try to free pages forced count_t swouts; // number of pages swapped out count_t swins; // number of pages swapped in count_t commitlim; // commit limit in pages count_t committed; // number of reserved pages count_t shmem; // tot shmem incl. tmpfs (pages) count_t shmrss; // resident shared memory (pages) count_t shmswp; // swapped shared memory (pages) count_t slabreclaim; // reclaimable slab (pages) count_t tothugepage; // total huge pages (huge pages) count_t freehugepage; // free huge pages (huge pages) count_t hugepagesz; // huge page size (bytes) count_t vmwballoon; // vmware claimed balloon pages count_t cfuture[8]; // reserved for future use }; /************************************************************************/ struct netstat_22 { struct ipv4_stats ipv4; struct icmpv4_stats_without_InCsumErrors icmpv4; struct udpv4_stats udpv4; struct ipv6_stats ipv6; struct icmpv6_stats icmpv6; struct udpv6_stats udpv6; struct tcp_stats_without_InCsumErrors tcp; }; /************************************************************************/ struct freqcnt_22 { count_t maxfreq;/* frequency in MHz */ count_t cnt; /* number of clock ticks times state */ count_t ticks; /* number of total clock ticks */ /* if zero, cnt is actul freq */ }; struct percpu_22 { int cpunr; count_t stime; /* system time in clock ticks */ count_t utime; /* user time in clock ticks */ count_t ntime; /* nice time in clock ticks */ count_t itime; /* idle time in clock ticks */ count_t wtime; /* iowait time in clock ticks */ count_t Itime; /* irq time in clock ticks */ count_t Stime; /* softirq time in clock ticks */ count_t steal; /* steal time in clock ticks */ count_t guest; /* guest time in clock ticks */ struct freqcnt_22 freqcnt;/* frequency scaling info */ count_t cfuture[4]; /* reserved for future use */ }; struct cpustat_22 { count_t nrcpu; /* number of cpu's */ count_t devint; /* number of device interrupts */ count_t csw; /* number of context switches */ count_t nprocs; /* number of processes started */ float lavg1; /* load average last minute */ float lavg5; /* load average last 5 minutes */ float lavg15; /* load average last 15 minutes */ count_t cfuture[4]; /* reserved for future use */ struct percpu_22 all; struct percpu_22 cpu[MAXCPU_22]; }; /************************************************************************/ struct perdsk_22 { char name[MAXDKNAM]; /* empty string for last */ count_t nread; /* number of read transfers */ count_t nrsect; /* number of sectors read */ count_t nwrite; /* number of write transfers */ count_t nwsect; /* number of sectors written */ count_t io_ms; /* number of millisecs spent for I/O */ count_t avque; /* average queue length */ count_t cfuture[4]; /* reserved for future use */ }; struct dskstat_22 { int ndsk; /* number of physical disks */ int nmdd; /* number of md volumes */ int nlvm; /* number of logical volumes */ struct perdsk_22 dsk[MAXDSK_22]; struct perdsk_22 mdd[MAXMDD_22]; struct perdsk_22 lvm[MAXLVM_22]; }; /************************************************************************/ struct perintf_22 { char name[16]; /* empty string for last */ count_t rbyte; /* number of read bytes */ count_t rpack; /* number of read packets */ count_t rerrs; /* receive errors */ count_t rdrop; /* receive drops */ count_t rfifo; /* receive fifo */ count_t rframe; /* receive framing errors */ count_t rcompr; /* receive compressed */ count_t rmultic;/* receive multicast */ count_t rfuture[4]; /* reserved for future use */ count_t sbyte; /* number of written bytes */ count_t spack; /* number of written packets */ count_t serrs; /* transmit errors */ count_t sdrop; /* transmit drops */ count_t sfifo; /* transmit fifo */ count_t scollis;/* collisions */ count_t scarrier;/* transmit carrier */ count_t scompr; /* transmit compressed */ count_t sfuture[4]; /* reserved for future use */ char type; /* interface type ('e'/'w'/'?') */ long speed; /* interface speed in megabits/second */ long speedp; /* previous interface speed */ char duplex; /* full duplex (boolean) */ count_t cfuture[4]; /* reserved for future use */ }; struct intfstat_22 { int nrintf; struct perintf_22 intf[MAXINTF_22]; }; /************************************************************************/ struct pernfsmount_22 { char mountdev[128]; /* mountdevice */ count_t age; /* number of seconds mounted */ count_t bytesread; /* via normal reads */ count_t byteswrite; /* via normal writes */ count_t bytesdread; /* via direct reads */ count_t bytesdwrite; /* via direct writes */ count_t bytestotread; /* via reads */ count_t bytestotwrite; /* via writes */ count_t pagesmread; /* via mmap reads */ count_t pagesmwrite; /* via mmap writes */ count_t future[8]; }; struct nfsstat_22 { struct { count_t netcnt; count_t netudpcnt; count_t nettcpcnt; count_t nettcpcon; count_t rpccnt; count_t rpcbadfmt; count_t rpcbadaut; count_t rpcbadcln; count_t rpcread; count_t rpcwrite; count_t rchits; /* repcache hits */ count_t rcmiss; /* repcache misses */ count_t rcnoca; /* uncached requests */ count_t nrbytes; /* read bytes */ count_t nwbytes; /* written bytes */ count_t future[8]; } server; struct { count_t rpccnt; count_t rpcretrans; count_t rpcautrefresh; count_t rpcread; count_t rpcwrite; count_t future[8]; } client; struct { int nrmounts; struct pernfsmount_22 nfsmnt[MAXNFSMOUNT_22]; } nfsmounts; }; /************************************************************************/ struct percontainer_22 { unsigned long ctid; /* container id */ unsigned long numproc; /* number of processes */ count_t system; /* */ count_t user; /* */ count_t nice; /* */ count_t uptime; /* */ count_t physpages; /* */ }; struct contstat_22 { int nrcontainer; struct percontainer_22 cont[MAXCONTAINER_22]; }; /************************************************************************/ /* ** experimental stuff for access to local HTTP daemons */ struct wwwstat_22 { count_t accesses; /* total number of HTTP-requests */ count_t totkbytes; /* total kbytes transfer for HTTP-req */ count_t uptime; /* number of seconds since startup */ int bworkers; /* number of busy httpd-daemons */ int iworkers; /* number of idle httpd-daemons */ }; /************************************************************************/ struct sstat_22 { struct cpustat_22 cpu; struct memstat_22 mem; struct netstat_22 net; struct intfstat_22 intf; struct dskstat_22 dsk; struct nfsstat_22 nfs; struct contstat_22 cfs; struct wwwstat_22 www; }; atop-2.12.1/prev/cgroups_211.h0000644000203100020310000000426215064552761015251 0ustar gerlofgerlof// structure containing general info and metrics per cgroup (directory) // struct cstat_211 { // GENERAL INFO struct cggen_211 { int structlen; // struct length including rounded name int sequence; // sequence number in chain/array int parentseq; // parent sequence number in chain/array int depth; // cgroup tree depth starting from 0 int nprocs; // number of processes in cgroup int procsbelow; // number of processes in cgroups below int namelen; // cgroup name length (at end of struct) int fullnamelen; // cgroup path length int ifuture[4]; long namehash; // cgroup name hash of // full path name excluding slashes long lfuture[4]; } gen; // CONFIGURATION INFO struct cgconf_211 { int cpuweight; // -1=max, -2=undefined int cpumax; // -1=max, -2=undefined (perc) count_t memmax; // -1=max, -2=undefined (pages) count_t swpmax; // -1=max, -2=undefined (pages) int dskweight; // -1=max, -2=undefined int ifuture[5]; count_t cfuture[5]; } conf; // CPU STATISTICS struct cgcpu_211 { count_t utime; // time user text (usec) -1=undefined count_t stime; // time system text (usec) -1=undefined count_t somepres; // some pressure (microsec) count_t fullpres; // full pressure (microsec) count_t cfuture[5]; } cpu; // MEMORY STATISTICS struct cgmem_211 { count_t current; // current memory (pages) -1=undefined count_t anon; // anonymous memory (pages) -1=undefined count_t file; // file memory (pages) -1=undefined count_t kernel; // kernel memory (pages) -1=undefined count_t shmem; // shared memory (pages) -1=undefined count_t somepres; // some pressure (microsec) count_t fullpres; // full pressure (microsec) count_t cfuture[5]; } mem; // DISK I/O STATISTICS struct cgdsk_211 { count_t rbytes; // total bytes read on all physical disks count_t wbytes; // total bytes written on all physical disks count_t rios; // total read I/Os on all physical disks count_t wios; // total write I/Os on all physical disks count_t somepres; // some pressure (microsec) count_t fullpres; // full pressure (microsec) count_t cfuture[5]; } dsk; // cgroup name with variable length char cgname[]; }; atop-2.12.1/prev/netstats_wrong.h0000644000203100020310000000267315064552761016271 0ustar gerlofgerlofstruct tcp_stats_without_InCsumErrors { count_t RtoAlgorithm; count_t RtoMin; count_t RtoMax; count_t MaxConn; count_t ActiveOpens; count_t PassiveOpens; count_t AttemptFails; count_t EstabResets; count_t CurrEstab; count_t InSegs; count_t OutSegs; count_t RetransSegs; count_t InErrs; count_t OutRsts; }; struct icmpv4_stats_without_InCsumErrors { count_t InMsgs; count_t InErrors; count_t InDestUnreachs; count_t InTimeExcds; count_t InParmProbs; count_t InSrcQuenchs; count_t InRedirects; count_t InEchos; count_t InEchoReps; count_t InTimestamps; count_t InTimestampReps; count_t InAddrMasks; count_t InAddrMaskReps; count_t OutMsgs; count_t OutErrors; count_t OutDestUnreachs; count_t OutTimeExcds; count_t OutParmProbs; count_t OutSrcQuenchs; count_t OutRedirects; count_t OutEchos; count_t OutEchoReps; count_t OutTimestamps; count_t OutTimestampReps; count_t OutAddrMasks; count_t OutAddrMaskReps; }; struct netstat_wrong { struct ipv4_stats ipv4; struct icmpv4_stats_without_InCsumErrors icmpv4; struct udpv4_stats udpv4; struct ipv6_stats ipv6; struct icmpv6_stats icmpv6; struct udpv6_stats udpv6; struct tcp_stats_without_InCsumErrors tcp; }; atop-2.12.1/prev/photoproc_200.h0000644000203100020310000000661115064552761015602 0ustar gerlofgerlof/* ** structure containing only relevant process-info extracted ** from kernel's process-administration */ struct tstat_20 { /* GENERAL TASK INFO */ struct gen_20 { int tgid; /* threadgroup identification */ int pid; /* process identification */ int ppid; /* parent process identification*/ int ruid; /* real user identification */ int euid; /* eff. user identification */ int suid; /* saved user identification */ int fsuid; /* fs user identification */ int rgid; /* real group identification */ int egid; /* eff. group identification */ int sgid; /* saved group identification */ int fsgid; /* fs group identification */ int nthr; /* number of threads in tgroup */ char name[PNAMLEN+1];/* process name string */ char isproc; /* boolean: process level? */ char state; /* process state ('E' = exited) */ int excode; /* process exit status */ time_t btime; /* process start time (epoch) */ time_t elaps; /* process elaps time (hertz) */ char cmdline[CMDLEN+1];/* command-line string */ int nthrslpi; /* # threads in state 'S' */ int nthrslpu; /* # threads in state 'D' */ int nthrrun; /* # threads in state 'R' */ int ifuture[4]; /* reserved */ } gen; /* CPU STATISTICS */ struct cpu_20 { count_t utime; /* time user text (ticks) */ count_t stime; /* time system text (ticks) */ int nice; /* nice value */ int prio; /* priority */ int rtprio; /* realtime priority */ int policy; /* scheduling policy */ int curcpu; /* current processor */ int sleepavg; /* sleep average percentage */ int ifuture[4]; /* reserved for future use */ count_t cfuture[4]; /* reserved for future use */ } cpu; /* DISK STATISTICS */ struct dsk_20 { count_t rio; /* number of read requests */ count_t rsz; /* cumulative # sectors read */ count_t wio; /* number of write requests */ count_t wsz; /* cumulative # sectors written */ count_t cwsz; /* cumulative # written sectors */ /* being cancelled */ count_t cfuture[4]; /* reserved for future use */ } dsk; /* MEMORY STATISTICS */ struct mem_20 { count_t minflt; /* number of page-reclaims */ count_t majflt; /* number of page-faults */ count_t vexec; /* virtmem execfile (Kb) */ count_t vmem; /* virtual memory (Kb) */ count_t rmem; /* resident memory (Kb) */ count_t vgrow; /* virtual growth (Kb) */ count_t rgrow; /* resident growth (Kb) */ count_t vdata; /* virtmem data (Kb) */ count_t vstack; /* virtmem stack (Kb) */ count_t vlibs; /* virtmem libexec (Kb) */ count_t vswap; /* swap space used (Kb) */ } mem; /* NETWORK STATISTICS */ struct net_20 { count_t tcpsnd; /* number of TCP-packets sent */ count_t tcpssz; /* cumulative size packets sent */ count_t tcprcv; /* number of TCP-packets recved */ count_t tcprsz; /* cumulative size packets rcvd */ count_t udpsnd; /* number of UDP-packets sent */ count_t udpssz; /* cumulative size packets sent */ count_t udprcv; /* number of UDP-packets recved */ count_t udprsz; /* cumulative size packets sent */ count_t avail1; /* */ count_t avail2; /* */ count_t cfuture[4]; /* reserved for future use */ } net; }; atop-2.12.1/prev/photoproc_201.h0000644000203100020310000000701615064552761015603 0ustar gerlofgerlof/* ** structure containing only relevant process-info extracted ** from kernel's process-administration */ struct tstat_21 { /* GENERAL TASK INFO */ struct gen_21 { int tgid; /* threadgroup identification */ int pid; /* process identification */ int ppid; /* parent process identification*/ int ruid; /* real user identification */ int euid; /* eff. user identification */ int suid; /* saved user identification */ int fsuid; /* fs user identification */ int rgid; /* real group identification */ int egid; /* eff. group identification */ int sgid; /* saved group identification */ int fsgid; /* fs group identification */ int nthr; /* number of threads in tgroup */ char name[PNAMLEN+1];/* process name string */ char isproc; /* boolean: process level? */ char state; /* process state ('E' = exited) */ int excode; /* process exit status */ time_t btime; /* process start time (epoch) */ time_t elaps; /* process elaps time (hertz) */ char cmdline[CMDLEN+1];/* command-line string */ int nthrslpi; /* # threads in state 'S' */ int nthrslpu; /* # threads in state 'D' */ int nthrrun; /* # threads in state 'R' */ int envid; /* OpenVZ support */ int ifuture[4]; /* reserved */ } gen; /* CPU STATISTICS */ struct cpu_21 { count_t utime; /* time user text (ticks) */ count_t stime; /* time system text (ticks) */ int nice; /* nice value */ int prio; /* priority */ int rtprio; /* realtime priority */ int policy; /* scheduling policy */ int curcpu; /* current processor */ int sleepavg; /* sleep average percentage */ int ifuture[4]; /* reserved for future use */ count_t cfuture[4]; /* reserved for future use */ } cpu; /* DISK STATISTICS */ struct dsk_21 { count_t rio; /* number of read requests */ count_t rsz; /* cumulative # sectors read */ count_t wio; /* number of write requests */ count_t wsz; /* cumulative # sectors written */ count_t cwsz; /* cumulative # written sectors */ /* being cancelled */ count_t cfuture[4]; /* reserved for future use */ } dsk; /* MEMORY STATISTICS */ struct mem_21 { count_t minflt; /* number of page-reclaims */ count_t majflt; /* number of page-faults */ count_t vexec; /* virtmem execfile (Kb) */ count_t vmem; /* virtual memory (Kb) */ count_t rmem; /* resident memory (Kb) */ count_t pmem; /* resident memory (Kb) */ count_t vgrow; /* virtual growth (Kb) */ count_t rgrow; /* resident growth (Kb) */ count_t vdata; /* virtmem data (Kb) */ count_t vstack; /* virtmem stack (Kb) */ count_t vlibs; /* virtmem libexec (Kb) */ count_t vswap; /* swap space used (Kb) */ count_t cfuture[4]; /* reserved for future use */ } mem; /* NETWORK STATISTICS */ struct net_21 { count_t tcpsnd; /* number of TCP-packets sent */ count_t tcpssz; /* cumulative size packets sent */ count_t tcprcv; /* number of TCP-packets recved */ count_t tcprsz; /* cumulative size packets rcvd */ count_t udpsnd; /* number of UDP-packets sent */ count_t udpssz; /* cumulative size packets sent */ count_t udprcv; /* number of UDP-packets recved */ count_t udprsz; /* cumulative size packets sent */ count_t avail1; /* */ count_t avail2; /* */ count_t cfuture[4]; /* reserved for future use */ } net; }; atop-2.12.1/prev/photoproc_202.h0000644000203100020310000000707315064552761015607 0ustar gerlofgerlof/* ** structure containing only relevant process-info extracted ** from kernel's process-administration */ struct tstat_22 { /* GENERAL TASK INFO */ struct gen_22 { int tgid; /* threadgroup identification */ int pid; /* process identification */ int ppid; /* parent process identification*/ int ruid; /* real user identification */ int euid; /* eff. user identification */ int suid; /* saved user identification */ int fsuid; /* fs user identification */ int rgid; /* real group identification */ int egid; /* eff. group identification */ int sgid; /* saved group identification */ int fsgid; /* fs group identification */ int nthr; /* number of threads in tgroup */ char name[PNAMLEN+1];/* process name string */ char isproc; /* boolean: process level? */ char state; /* process state ('E' = exited) */ int excode; /* process exit status */ time_t btime; /* process start time (epoch) */ time_t elaps; /* process elaps time (hertz) */ char cmdline[CMDLEN+1];/* command-line string */ int nthrslpi; /* # threads in state 'S' */ int nthrslpu; /* # threads in state 'D' */ int nthrrun; /* # threads in state 'R' */ int ctid; /* OpenVZ container ID */ int vpid; /* OpenVZ virtual PID */ int ifuture[5]; /* reserved */ } gen; /* CPU STATISTICS */ struct cpu_22 { count_t utime; /* time user text (ticks) */ count_t stime; /* time system text (ticks) */ int nice; /* nice value */ int prio; /* priority */ int rtprio; /* realtime priority */ int policy; /* scheduling policy */ int curcpu; /* current processor */ int sleepavg; /* sleep average percentage */ int ifuture[4]; /* reserved for future use */ count_t cfuture[4]; /* reserved for future use */ } cpu; /* DISK STATISTICS */ struct dsk_22 { count_t rio; /* number of read requests */ count_t rsz; /* cumulative # sectors read */ count_t wio; /* number of write requests */ count_t wsz; /* cumulative # sectors written */ count_t cwsz; /* cumulative # written sectors */ /* being cancelled */ count_t cfuture[4]; /* reserved for future use */ } dsk; /* MEMORY STATISTICS */ struct mem_22 { count_t minflt; /* number of page-reclaims */ count_t majflt; /* number of page-faults */ count_t vexec; /* virtmem execfile (Kb) */ count_t vmem; /* virtual memory (Kb) */ count_t rmem; /* resident memory (Kb) */ count_t pmem; /* resident memory (Kb) */ count_t vgrow; /* virtual growth (Kb) */ count_t rgrow; /* resident growth (Kb) */ count_t vdata; /* virtmem data (Kb) */ count_t vstack; /* virtmem stack (Kb) */ count_t vlibs; /* virtmem libexec (Kb) */ count_t vswap; /* swap space used (Kb) */ count_t cfuture[4]; /* reserved for future use */ } mem; /* NETWORK STATISTICS */ struct net_22 { count_t tcpsnd; /* number of TCP-packets sent */ count_t tcpssz; /* cumulative size packets sent */ count_t tcprcv; /* number of TCP-packets recved */ count_t tcprsz; /* cumulative size packets rcvd */ count_t udpsnd; /* number of UDP-packets sent */ count_t udpssz; /* cumulative size packets sent */ count_t udprcv; /* number of UDP-packets recved */ count_t udprsz; /* cumulative size packets sent */ count_t avail1; /* */ count_t avail2; /* */ count_t cfuture[4]; /* reserved for future use */ } net; }; atop-2.12.1/prev/photoproc_203.h0000644000203100020310000000715415064552761015610 0ustar gerlofgerlof/* ** structure containing only relevant process-info extracted ** from kernel's process-administration */ struct tstat_23 { /* GENERAL TASK INFO */ struct gen_23 { int tgid; /* threadgroup identification */ int pid; /* process identification */ int ppid; /* parent process identification*/ int ruid; /* real user identification */ int euid; /* eff. user identification */ int suid; /* saved user identification */ int fsuid; /* fs user identification */ int rgid; /* real group identification */ int egid; /* eff. group identification */ int sgid; /* saved group identification */ int fsgid; /* fs group identification */ int nthr; /* number of threads in tgroup */ char name[PNAMLEN+1];/* process name string */ char isproc; /* boolean: process level? */ char state; /* process state ('E' = exited) */ int excode; /* process exit status */ time_t btime; /* process start time (epoch) */ time_t elaps; /* process elaps time (hertz) */ char cmdline[CMDLEN+1];/* command-line string */ int nthrslpi; /* # threads in state 'S' */ int nthrslpu; /* # threads in state 'D' */ int nthrrun; /* # threads in state 'R' */ int ctid; /* OpenVZ container ID */ int vpid; /* OpenVZ virtual PID */ int wasinactive; /* boolean: task inactive */ char container[16]; /* Docker container id (12 pos) */ } gen; /* CPU STATISTICS */ struct cpu_23 { count_t utime; /* time user text (ticks) */ count_t stime; /* time system text (ticks) */ int nice; /* nice value */ int prio; /* priority */ int rtprio; /* realtime priority */ int policy; /* scheduling policy */ int curcpu; /* current processor */ int sleepavg; /* sleep average percentage */ int ifuture[4]; /* reserved for future use */ count_t cfuture[4]; /* reserved for future use */ } cpu; /* DISK STATISTICS */ struct dsk_23 { count_t rio; /* number of read requests */ count_t rsz; /* cumulative # sectors read */ count_t wio; /* number of write requests */ count_t wsz; /* cumulative # sectors written */ count_t cwsz; /* cumulative # written sectors */ /* being cancelled */ count_t cfuture[4]; /* reserved for future use */ } dsk; /* MEMORY STATISTICS */ struct mem_23 { count_t minflt; /* number of page-reclaims */ count_t majflt; /* number of page-faults */ count_t vexec; /* virtmem execfile (Kb) */ count_t vmem; /* virtual memory (Kb) */ count_t rmem; /* resident memory (Kb) */ count_t pmem; /* resident memory (Kb) */ count_t vgrow; /* virtual growth (Kb) */ count_t rgrow; /* resident growth (Kb) */ count_t vdata; /* virtmem data (Kb) */ count_t vstack; /* virtmem stack (Kb) */ count_t vlibs; /* virtmem libexec (Kb) */ count_t vswap; /* swap space used (Kb) */ count_t cfuture[4]; /* reserved for future use */ } mem; /* NETWORK STATISTICS */ struct net_23 { count_t tcpsnd; /* number of TCP-packets sent */ count_t tcpssz; /* cumulative size packets sent */ count_t tcprcv; /* number of TCP-packets recved */ count_t tcprsz; /* cumulative size packets rcvd */ count_t udpsnd; /* number of UDP-packets sent */ count_t udpssz; /* cumulative size packets sent */ count_t udprcv; /* number of UDP-packets recved */ count_t udprsz; /* cumulative size packets sent */ count_t avail1; /* */ count_t avail2; /* */ count_t cfuture[4]; /* reserved for future use */ } net; }; atop-2.12.1/prev/photoproc_204.h0000644000203100020310000001032615064552761015604 0ustar gerlofgerlof/* ** structure containing only relevant process-info extracted ** from kernel's process-administration */ struct tstat_24 { /* GENERAL TASK INFO */ struct gen_24 { int tgid; /* threadgroup identification */ int pid; /* process identification */ int ppid; /* parent process identification*/ int ruid; /* real user identification */ int euid; /* eff. user identification */ int suid; /* saved user identification */ int fsuid; /* fs user identification */ int rgid; /* real group identification */ int egid; /* eff. group identification */ int sgid; /* saved group identification */ int fsgid; /* fs group identification */ int nthr; /* number of threads in tgroup */ char name[PNAMLEN+1];/* process name string */ char isproc; /* boolean: process level? */ char state; /* process state ('E' = exited) */ int excode; /* process exit status */ time_t btime; /* process start time (epoch) */ time_t elaps; /* process elaps time (hertz) */ char cmdline[CMDLEN+1];/* command-line string */ int nthrslpi; /* # threads in state 'S' */ int nthrslpu; /* # threads in state 'D' */ int nthrrun; /* # threads in state 'R' */ int ctid; /* OpenVZ container ID */ int vpid; /* OpenVZ virtual PID */ int wasinactive; /* boolean: task inactive */ char container[16]; /* Docker container id (12 pos) */ } gen; /* CPU STATISTICS */ struct cpu_24 { count_t utime; /* time user text (ticks) */ count_t stime; /* time system text (ticks) */ int nice; /* nice value */ int prio; /* priority */ int rtprio; /* realtime priority */ int policy; /* scheduling policy */ int curcpu; /* current processor */ int sleepavg; /* sleep average percentage */ int ifuture[4]; /* reserved for future use */ count_t cfuture[4]; /* reserved for future use */ } cpu; /* DISK STATISTICS */ struct dsk_24 { count_t rio; /* number of read requests */ count_t rsz; /* cumulative # sectors read */ count_t wio; /* number of write requests */ count_t wsz; /* cumulative # sectors written */ count_t cwsz; /* cumulative # written sectors */ /* being cancelled */ count_t cfuture[4]; /* reserved for future use */ } dsk; /* MEMORY STATISTICS */ struct mem_24 { count_t minflt; /* number of page-reclaims */ count_t majflt; /* number of page-faults */ count_t vexec; /* virtmem execfile (Kb) */ count_t vmem; /* virtual memory (Kb) */ count_t rmem; /* resident memory (Kb) */ count_t pmem; /* resident memory (Kb) */ count_t vgrow; /* virtual growth (Kb) */ count_t rgrow; /* resident growth (Kb) */ count_t vdata; /* virtmem data (Kb) */ count_t vstack; /* virtmem stack (Kb) */ count_t vlibs; /* virtmem libexec (Kb) */ count_t vswap; /* swap space used (Kb) */ count_t cfuture[4]; /* reserved for future use */ } mem; /* NETWORK STATISTICS */ struct net_24 { count_t tcpsnd; /* number of TCP-packets sent */ count_t tcpssz; /* cumulative size packets sent */ count_t tcprcv; /* number of TCP-packets recved */ count_t tcprsz; /* cumulative size packets rcvd */ count_t udpsnd; /* number of UDP-packets sent */ count_t udpssz; /* cumulative size packets sent */ count_t udprcv; /* number of UDP-packets recved */ count_t udprsz; /* cumulative size packets sent */ count_t avail1; /* */ count_t avail2; /* */ count_t cfuture[4]; /* reserved for future use */ } net; struct gpu_24 { char state; // A - active, E - Exit, '\0' - no use char cfuture[3]; // short nrgpus; // number of GPUs for this process int32_t gpulist; // bitlist with GPU numbers int gpubusy; // gpu busy perc process lifetime -1 = n/a int membusy; // memory busy perc process lifetime -1 = n/a count_t timems; // milliseconds accounting -1 = n/a // value 0 for active process, // value > 0 after termination count_t memnow; // current memory consumption in KiB count_t memcum; // cumulative memory consumption in KiB count_t sample; // number of samples } gpu; }; atop-2.12.1/prev/photoproc_205.h0000644000203100020310000001032615064552761015605 0ustar gerlofgerlof/* ** structure containing only relevant process-info extracted ** from kernel's process-administration */ struct tstat_25 { /* GENERAL TASK INFO */ struct gen_25 { int tgid; /* threadgroup identification */ int pid; /* process identification */ int ppid; /* parent process identification*/ int ruid; /* real user identification */ int euid; /* eff. user identification */ int suid; /* saved user identification */ int fsuid; /* fs user identification */ int rgid; /* real group identification */ int egid; /* eff. group identification */ int sgid; /* saved group identification */ int fsgid; /* fs group identification */ int nthr; /* number of threads in tgroup */ char name[PNAMLEN+1];/* process name string */ char isproc; /* boolean: process level? */ char state; /* process state ('E' = exited) */ int excode; /* process exit status */ time_t btime; /* process start time (epoch) */ time_t elaps; /* process elaps time (hertz) */ char cmdline[CMDLEN+1];/* command-line string */ int nthrslpi; /* # threads in state 'S' */ int nthrslpu; /* # threads in state 'D' */ int nthrrun; /* # threads in state 'R' */ int ctid; /* OpenVZ container ID */ int vpid; /* OpenVZ virtual PID */ int wasinactive; /* boolean: task inactive */ char container[16]; /* Docker container id (12 pos) */ } gen; /* CPU STATISTICS */ struct cpu_25 { count_t utime; /* time user text (ticks) */ count_t stime; /* time system text (ticks) */ int nice; /* nice value */ int prio; /* priority */ int rtprio; /* realtime priority */ int policy; /* scheduling policy */ int curcpu; /* current processor */ int sleepavg; /* sleep average percentage */ int ifuture[4]; /* reserved for future use */ count_t cfuture[4]; /* reserved for future use */ } cpu; /* DISK STATISTICS */ struct dsk_25 { count_t rio; /* number of read requests */ count_t rsz; /* cumulative # sectors read */ count_t wio; /* number of write requests */ count_t wsz; /* cumulative # sectors written */ count_t cwsz; /* cumulative # written sectors */ /* being cancelled */ count_t cfuture[4]; /* reserved for future use */ } dsk; /* MEMORY STATISTICS */ struct mem_25 { count_t minflt; /* number of page-reclaims */ count_t majflt; /* number of page-faults */ count_t vexec; /* virtmem execfile (Kb) */ count_t vmem; /* virtual memory (Kb) */ count_t rmem; /* resident memory (Kb) */ count_t pmem; /* resident memory (Kb) */ count_t vgrow; /* virtual growth (Kb) */ count_t rgrow; /* resident growth (Kb) */ count_t vdata; /* virtmem data (Kb) */ count_t vstack; /* virtmem stack (Kb) */ count_t vlibs; /* virtmem libexec (Kb) */ count_t vswap; /* swap space used (Kb) */ count_t cfuture[4]; /* reserved for future use */ } mem; /* NETWORK STATISTICS */ struct net_25 { count_t tcpsnd; /* number of TCP-packets sent */ count_t tcpssz; /* cumulative size packets sent */ count_t tcprcv; /* number of TCP-packets recved */ count_t tcprsz; /* cumulative size packets rcvd */ count_t udpsnd; /* number of UDP-packets sent */ count_t udpssz; /* cumulative size packets sent */ count_t udprcv; /* number of UDP-packets recved */ count_t udprsz; /* cumulative size packets sent */ count_t avail1; /* */ count_t avail2; /* */ count_t cfuture[4]; /* reserved for future use */ } net; struct gpu_25 { char state; // A - active, E - Exit, '\0' - no use char cfuture[3]; // short nrgpus; // number of GPUs for this process int32_t gpulist; // bitlist with GPU numbers int gpubusy; // gpu busy perc process lifetime -1 = n/a int membusy; // memory busy perc process lifetime -1 = n/a count_t timems; // milliseconds accounting -1 = n/a // value 0 for active process, // value > 0 after termination count_t memnow; // current memory consumption in KiB count_t memcum; // cumulative memory consumption in KiB count_t sample; // number of samples } gpu; }; atop-2.12.1/prev/photoproc_206.h0000644000203100020310000001055415064552761015611 0ustar gerlofgerlof/* ** structure containing only relevant process-info extracted ** from kernel's process-administration */ struct tstat_26 { /* GENERAL TASK INFO */ struct gen_26 { int tgid; /* threadgroup identification */ int pid; /* process identification */ int ppid; /* parent process identification*/ int ruid; /* real user identification */ int euid; /* eff. user identification */ int suid; /* saved user identification */ int fsuid; /* fs user identification */ int rgid; /* real group identification */ int egid; /* eff. group identification */ int sgid; /* saved group identification */ int fsgid; /* fs group identification */ int nthr; /* number of threads in tgroup */ char name[PNAMLEN+1];/* process name string */ char isproc; /* boolean: process level? */ char state; /* process state ('E' = exited) */ int excode; /* process exit status */ time_t btime; /* process start time (epoch) */ time_t elaps; /* process elaps time (hertz) */ char cmdline[CMDLEN+1];/* command-line string */ int nthrslpi; /* # threads in state 'S' */ int nthrslpu; /* # threads in state 'D' */ int nthrrun; /* # threads in state 'R' */ int ctid; /* OpenVZ container ID */ int vpid; /* OpenVZ virtual PID */ int wasinactive; /* boolean: task inactive */ char container[16]; /* Docker container id (12 pos) */ } gen; /* CPU STATISTICS */ struct cpu_26 { count_t utime; /* time user text (ticks) */ count_t stime; /* time system text (ticks) */ int nice; /* nice value */ int prio; /* priority */ int rtprio; /* realtime priority */ int policy; /* scheduling policy */ int curcpu; /* current processor */ int sleepavg; /* sleep average percentage */ int ifuture[4]; /* reserved for future use */ char wchan[16]; /* wait channel string */ count_t rundelay; /* schedstat rundelay (nanosec) */ count_t cfuture[1]; /* reserved for future use */ } cpu; /* DISK STATISTICS */ struct dsk_26 { count_t rio; /* number of read requests */ count_t rsz; /* cumulative # sectors read */ count_t wio; /* number of write requests */ count_t wsz; /* cumulative # sectors written */ count_t cwsz; /* cumulative # written sectors */ /* being cancelled */ count_t cfuture[4]; /* reserved for future use */ } dsk; /* MEMORY STATISTICS */ struct mem_26 { count_t minflt; /* number of page-reclaims */ count_t majflt; /* number of page-faults */ count_t vexec; /* virtmem execfile (Kb) */ count_t vmem; /* virtual memory (Kb) */ count_t rmem; /* resident memory (Kb) */ count_t pmem; /* resident memory (Kb) */ count_t vgrow; /* virtual growth (Kb) */ count_t rgrow; /* resident growth (Kb) */ count_t vdata; /* virtmem data (Kb) */ count_t vstack; /* virtmem stack (Kb) */ count_t vlibs; /* virtmem libexec (Kb) */ count_t vswap; /* swap space used (Kb) */ count_t vlock; /* virtual locked (Kb) */ count_t cfuture[3]; /* reserved for future use */ } mem; /* NETWORK STATISTICS */ struct net_26 { count_t tcpsnd; /* number of TCP-packets sent */ count_t tcpssz; /* cumulative size packets sent */ count_t tcprcv; /* number of TCP-packets recved */ count_t tcprsz; /* cumulative size packets rcvd */ count_t udpsnd; /* number of UDP-packets sent */ count_t udpssz; /* cumulative size packets sent */ count_t udprcv; /* number of UDP-packets recved */ count_t udprsz; /* cumulative size packets sent */ count_t avail1; /* */ count_t avail2; /* */ count_t cfuture[4]; /* reserved for future use */ } net; struct gpu_26 { char state; // A - active, E - Exit, '\0' - no use char cfuture[3]; // short nrgpus; // number of GPUs for this process int32_t gpulist; // bitlist with GPU numbers int gpubusy; // gpu busy perc process lifetime -1 = n/a int membusy; // memory busy perc process lifetime -1 = n/a count_t timems; // milliseconds accounting -1 = n/a // value 0 for active process, // value > 0 after termination count_t memnow; // current memory consumption in KiB count_t memcum; // cumulative memory consumption in KiB count_t sample; // number of samples } gpu; }; atop-2.12.1/prev/photoproc_207.h0000644000203100020310000001055415064552761015612 0ustar gerlofgerlof/* ** structure containing only relevant process-info extracted ** from kernel's process-administration */ struct tstat_27 { /* GENERAL TASK INFO */ struct gen_27 { int tgid; /* threadgroup identification */ int pid; /* process identification */ int ppid; /* parent process identification*/ int ruid; /* real user identification */ int euid; /* eff. user identification */ int suid; /* saved user identification */ int fsuid; /* fs user identification */ int rgid; /* real group identification */ int egid; /* eff. group identification */ int sgid; /* saved group identification */ int fsgid; /* fs group identification */ int nthr; /* number of threads in tgroup */ char name[PNAMLEN+1];/* process name string */ char isproc; /* boolean: process level? */ char state; /* process state ('E' = exited) */ int excode; /* process exit status */ time_t btime; /* process start time (epoch) */ time_t elaps; /* process elaps time (hertz) */ char cmdline[CMDLEN+1];/* command-line string */ int nthrslpi; /* # threads in state 'S' */ int nthrslpu; /* # threads in state 'D' */ int nthrrun; /* # threads in state 'R' */ int ctid; /* OpenVZ container ID */ int vpid; /* OpenVZ virtual PID */ int wasinactive; /* boolean: task inactive */ char container[16]; /* Docker container id (12 pos) */ } gen; /* CPU STATISTICS */ struct cpu_27 { count_t utime; /* time user text (ticks) */ count_t stime; /* time system text (ticks) */ int nice; /* nice value */ int prio; /* priority */ int rtprio; /* realtime priority */ int policy; /* scheduling policy */ int curcpu; /* current processor */ int sleepavg; /* sleep average percentage */ int ifuture[4]; /* reserved for future use */ char wchan[16]; /* wait channel string */ count_t rundelay; /* schedstat rundelay (nanosec) */ count_t cfuture[1]; /* reserved for future use */ } cpu; /* DISK STATISTICS */ struct dsk_27 { count_t rio; /* number of read requests */ count_t rsz; /* cumulative # sectors read */ count_t wio; /* number of write requests */ count_t wsz; /* cumulative # sectors written */ count_t cwsz; /* cumulative # written sectors */ /* being cancelled */ count_t cfuture[4]; /* reserved for future use */ } dsk; /* MEMORY STATISTICS */ struct mem_27 { count_t minflt; /* number of page-reclaims */ count_t majflt; /* number of page-faults */ count_t vexec; /* virtmem execfile (Kb) */ count_t vmem; /* virtual memory (Kb) */ count_t rmem; /* resident memory (Kb) */ count_t pmem; /* resident memory (Kb) */ count_t vgrow; /* virtual growth (Kb) */ count_t rgrow; /* resident growth (Kb) */ count_t vdata; /* virtmem data (Kb) */ count_t vstack; /* virtmem stack (Kb) */ count_t vlibs; /* virtmem libexec (Kb) */ count_t vswap; /* swap space used (Kb) */ count_t vlock; /* virtual locked (Kb) */ count_t cfuture[3]; /* reserved for future use */ } mem; /* NETWORK STATISTICS */ struct net_27 { count_t tcpsnd; /* number of TCP-packets sent */ count_t tcpssz; /* cumulative size packets sent */ count_t tcprcv; /* number of TCP-packets recved */ count_t tcprsz; /* cumulative size packets rcvd */ count_t udpsnd; /* number of UDP-packets sent */ count_t udpssz; /* cumulative size packets sent */ count_t udprcv; /* number of UDP-packets recved */ count_t udprsz; /* cumulative size packets sent */ count_t avail1; /* */ count_t avail2; /* */ count_t cfuture[4]; /* reserved for future use */ } net; struct gpu_27 { char state; // A - active, E - Exit, '\0' - no use char cfuture[3]; // short nrgpus; // number of GPUs for this process int32_t gpulist; // bitlist with GPU numbers int gpubusy; // gpu busy perc process lifetime -1 = n/a int membusy; // memory busy perc process lifetime -1 = n/a count_t timems; // milliseconds accounting -1 = n/a // value 0 for active process, // value > 0 after termination count_t memnow; // current memory consumption in KiB count_t memcum; // cumulative memory consumption in KiB count_t sample; // number of samples } gpu; }; atop-2.12.1/prev/photoproc_208.h0000644000203100020310000001150515064552761015610 0ustar gerlofgerlof/* ** structure containing only relevant process-info extracted ** from kernel's process-administration */ struct tstat_28 { /* GENERAL TASK INFO */ struct gen_28 { int tgid; /* threadgroup identification */ int pid; /* process identification */ int ppid; /* parent process identification*/ int ruid; /* real user identification */ int euid; /* eff. user identification */ int suid; /* saved user identification */ int fsuid; /* fs user identification */ int rgid; /* real group identification */ int egid; /* eff. group identification */ int sgid; /* saved group identification */ int fsgid; /* fs group identification */ int nthr; /* number of threads in tgroup */ char name[PNAMLEN+1];/* process name string */ char isproc; /* boolean: process level? */ char state; /* process state ('E' = exited) */ int excode; /* process exit status */ time_t btime; /* process start time (epoch) */ time_t elaps; /* process elaps time (hertz) */ char cmdline[CMDLEN+1];/* command-line string */ int nthrslpi; /* # threads in state 'S' */ int nthrslpu; /* # threads in state 'D' */ int nthrrun; /* # threads in state 'R' */ int ctid; /* OpenVZ container ID */ int vpid; /* OpenVZ virtual PID */ int wasinactive; /* boolean: task inactive */ char container[16]; /* Docker container id (12 pos) */ char cgpath[CGRLEN]; /* cgroup v2 path name */ } gen; /* CPU STATISTICS */ struct cpu_28 { count_t utime; /* time user text (ticks) */ count_t stime; /* time system text (ticks) */ int nice; /* nice value */ int prio; /* priority */ int rtprio; /* realtime priority */ int policy; /* scheduling policy */ int curcpu; /* current processor */ int sleepavg; /* sleep average percentage */ int cgcpuweight; /* cgroup cpu.weight */ int cgcpumax; /* cgroup cpu.max percentage */ int cgcpumaxr; /* restrictive percentage */ int ifuture[3]; /* reserved for future use */ char wchan[16]; /* wait channel string */ count_t rundelay; /* schedstat rundelay (nanosec) */ count_t blkdelay; /* blkio delay (ticks) */ count_t cfuture[3]; /* reserved for future use */ } cpu; /* DISK STATISTICS */ struct dsk_28 { count_t rio; /* number of read requests */ count_t rsz; /* cumulative # sectors read */ count_t wio; /* number of write requests */ count_t wsz; /* cumulative # sectors written */ count_t cwsz; /* cumulative # written sectors */ /* being cancelled */ count_t cfuture[4]; /* reserved for future use */ } dsk; /* MEMORY STATISTICS */ struct mem_28 { count_t minflt; /* number of page-reclaims */ count_t majflt; /* number of page-faults */ count_t vexec; /* virtmem execfile (Kb) */ count_t vmem; /* virtual memory (Kb) */ count_t rmem; /* resident memory (Kb) */ count_t pmem; /* resident memory (Kb) */ count_t vgrow; /* virtual growth (Kb) */ count_t rgrow; /* resident growth (Kb) */ count_t vdata; /* virtmem data (Kb) */ count_t vstack; /* virtmem stack (Kb) */ count_t vlibs; /* virtmem libexec (Kb) */ count_t vswap; /* swap space used (Kb) */ count_t vlock; /* virtual locked (Kb) */ count_t cgmemmax; /* cgroup memory.max (Kb) */ count_t cgmemmaxr; /* restrictive memory.max (Kb) */ count_t cgswpmax; /* cgroup memory.swap.max (Kb) */ count_t cgswpmaxr; /* restrictive swap.max (Kb) */ count_t cfuture[3]; /* reserved for future use */ } mem; /* NETWORK STATISTICS */ struct net_28 { count_t tcpsnd; /* number of TCP-packets sent */ count_t tcpssz; /* cumulative size packets sent */ count_t tcprcv; /* number of TCP-packets recved */ count_t tcprsz; /* cumulative size packets rcvd */ count_t udpsnd; /* number of UDP-packets sent */ count_t udpssz; /* cumulative size packets sent */ count_t udprcv; /* number of UDP-packets recved */ count_t udprsz; /* cumulative size packets sent */ count_t avail1; /* */ count_t avail2; /* */ count_t cfuture[4]; /* reserved for future use */ } net; struct gpu_28 { char state; // A - active, E - Exit, '\0' - no use char cfuture[3]; // short nrgpus; // number of GPUs for this process int32_t gpulist; // bitlist with GPU numbers int gpubusy; // gpu busy perc process lifetime -1 = n/a int membusy; // memory busy perc process lifetime -1 = n/a count_t timems; // milliseconds accounting -1 = n/a // value 0 for active process, // value > 0 after termination count_t memnow; // current memory consumption in KiB count_t memcum; // cumulative memory consumption in KiB count_t sample; // number of samples } gpu; }; atop-2.12.1/prev/photoproc_209.h0000644000203100020310000001150515064552761015611 0ustar gerlofgerlof/* ** structure containing only relevant process-info extracted ** from kernel's process-administration */ struct tstat_29 { /* GENERAL TASK INFO */ struct gen_29 { int tgid; /* threadgroup identification */ int pid; /* process identification */ int ppid; /* parent process identification*/ int ruid; /* real user identification */ int euid; /* eff. user identification */ int suid; /* saved user identification */ int fsuid; /* fs user identification */ int rgid; /* real group identification */ int egid; /* eff. group identification */ int sgid; /* saved group identification */ int fsgid; /* fs group identification */ int nthr; /* number of threads in tgroup */ char name[PNAMLEN+1];/* process name string */ char isproc; /* boolean: process level? */ char state; /* process state ('E' = exited) */ int excode; /* process exit status */ time_t btime; /* process start time (epoch) */ time_t elaps; /* process elaps time (hertz) */ char cmdline[CMDLEN+1];/* command-line string */ int nthrslpi; /* # threads in state 'S' */ int nthrslpu; /* # threads in state 'D' */ int nthrrun; /* # threads in state 'R' */ int ctid; /* OpenVZ container ID */ int vpid; /* OpenVZ virtual PID */ int wasinactive; /* boolean: task inactive */ char container[16]; /* Docker container id (12 pos) */ char cgpath[CGRLEN]; /* cgroup v2 path name */ } gen; /* CPU STATISTICS */ struct cpu_29 { count_t utime; /* time user text (ticks) */ count_t stime; /* time system text (ticks) */ int nice; /* nice value */ int prio; /* priority */ int rtprio; /* realtime priority */ int policy; /* scheduling policy */ int curcpu; /* current processor */ int sleepavg; /* sleep average percentage */ int cgcpuweight; /* cgroup cpu.weight */ int cgcpumax; /* cgroup cpu.max percentage */ int cgcpumaxr; /* restrictive percentage */ int ifuture[3]; /* reserved for future use */ char wchan[16]; /* wait channel string */ count_t rundelay; /* schedstat rundelay (nanosec) */ count_t blkdelay; /* blkio delay (ticks) */ count_t cfuture[3]; /* reserved for future use */ } cpu; /* DISK STATISTICS */ struct dsk_29 { count_t rio; /* number of read requests */ count_t rsz; /* cumulative # sectors read */ count_t wio; /* number of write requests */ count_t wsz; /* cumulative # sectors written */ count_t cwsz; /* cumulative # written sectors */ /* being cancelled */ count_t cfuture[4]; /* reserved for future use */ } dsk; /* MEMORY STATISTICS */ struct mem_29 { count_t minflt; /* number of page-reclaims */ count_t majflt; /* number of page-faults */ count_t vexec; /* virtmem execfile (Kb) */ count_t vmem; /* virtual memory (Kb) */ count_t rmem; /* resident memory (Kb) */ count_t pmem; /* resident memory (Kb) */ count_t vgrow; /* virtual growth (Kb) */ count_t rgrow; /* resident growth (Kb) */ count_t vdata; /* virtmem data (Kb) */ count_t vstack; /* virtmem stack (Kb) */ count_t vlibs; /* virtmem libexec (Kb) */ count_t vswap; /* swap space used (Kb) */ count_t vlock; /* virtual locked (Kb) */ count_t cgmemmax; /* cgroup memory.max (Kb) */ count_t cgmemmaxr; /* restrictive memory.max (Kb) */ count_t cgswpmax; /* cgroup memory.swap.max (Kb) */ count_t cgswpmaxr; /* restrictive swap.max (Kb) */ count_t cfuture[3]; /* reserved for future use */ } mem; /* NETWORK STATISTICS */ struct net_29 { count_t tcpsnd; /* number of TCP-packets sent */ count_t tcpssz; /* cumulative size packets sent */ count_t tcprcv; /* number of TCP-packets recved */ count_t tcprsz; /* cumulative size packets rcvd */ count_t udpsnd; /* number of UDP-packets sent */ count_t udpssz; /* cumulative size packets sent */ count_t udprcv; /* number of UDP-packets recved */ count_t udprsz; /* cumulative size packets sent */ count_t avail1; /* */ count_t avail2; /* */ count_t cfuture[4]; /* reserved for future use */ } net; struct gpu_29 { char state; // A - active, E - Exit, '\0' - no use char cfuture[3]; // short nrgpus; // number of GPUs for this process int32_t gpulist; // bitlist with GPU numbers int gpubusy; // gpu busy perc process lifetime -1 = n/a int membusy; // memory busy perc process lifetime -1 = n/a count_t timems; // milliseconds accounting -1 = n/a // value 0 for active process, // value > 0 after termination count_t memnow; // current memory consumption in KiB count_t memcum; // cumulative memory consumption in KiB count_t sample; // number of samples } gpu; }; atop-2.12.1/prev/photoproc_210.h0000644000203100020310000001174515064552761015607 0ustar gerlofgerlof/* ** structure containing only relevant process-info extracted ** from kernel's process-administration */ struct tstat_210 { /* GENERAL TASK INFO */ struct gen_210 { int tgid; /* threadgroup identification */ int pid; /* process identification */ int ppid; /* parent process identification*/ int ruid; /* real user identification */ int euid; /* eff. user identification */ int suid; /* saved user identification */ int fsuid; /* fs user identification */ int rgid; /* real group identification */ int egid; /* eff. group identification */ int sgid; /* saved group identification */ int fsgid; /* fs group identification */ int nthr; /* number of threads in tgroup */ char name[PNAMLEN+1];/* process name string */ char isproc; /* boolean: process level? */ char state; /* process state ('E' = exited) */ int excode; /* process exit status */ time_t btime; /* process start time (epoch) */ time_t elaps; /* process elaps time (hertz) */ char cmdline[CMDLEN+1];/* command-line string */ int nthrslpi; /* # threads in state 'S' */ int nthrslpu; /* # threads in state 'D' */ int nthrrun; /* # threads in state 'R' */ int nthridle; /* # threads in state 'I' */ int ctid; /* OpenVZ container ID */ int vpid; /* OpenVZ virtual PID */ int wasinactive; /* boolean: task inactive */ char utsname[UTSLEN+1];/* UTS name container or pod */ char cgpath[CGRLEN]; /* cgroup v2 path name */ } gen; /* CPU STATISTICS */ struct cpu_210 { count_t utime; /* time user text (ticks) */ count_t stime; /* time system text (ticks) */ int nice; /* nice value */ int prio; /* priority */ int rtprio; /* realtime priority */ int policy; /* scheduling policy */ int curcpu; /* current processor */ int sleepavg; /* sleep average percentage */ int cgcpuweight; /* cgroup cpu.weight */ int cgcpumax; /* cgroup cpu.max percentage */ int cgcpumaxr; /* restrictive percentage */ int ifuture[3]; /* reserved for future use */ char wchan[16]; /* wait channel string */ count_t rundelay; /* schedstat rundelay (nanosec) */ count_t blkdelay; /* blkio delay (ticks) */ count_t nvcsw; /* voluntary cxt switch counts */ count_t nivcsw; /* involuntary csw counts */ count_t cfuture[3]; /* reserved for future use */ } cpu; /* DISK STATISTICS */ struct dsk_210 { count_t rio; /* number of read requests */ count_t rsz; /* cumulative # sectors read */ count_t wio; /* number of write requests */ count_t wsz; /* cumulative # sectors written */ count_t cwsz; /* cumulative # written sectors */ /* being cancelled */ count_t cfuture[4]; /* reserved for future use */ } dsk; /* MEMORY STATISTICS */ struct mem_210 { count_t minflt; /* number of page-reclaims */ count_t majflt; /* number of page-faults */ count_t vexec; /* virtmem execfile (Kb) */ count_t vmem; /* virtual memory (Kb) */ count_t rmem; /* resident memory (Kb) */ count_t pmem; /* resident memory (Kb) */ count_t vgrow; /* virtual growth (Kb) */ count_t rgrow; /* resident growth (Kb) */ count_t vdata; /* virtmem data (Kb) */ count_t vstack; /* virtmem stack (Kb) */ count_t vlibs; /* virtmem libexec (Kb) */ count_t vswap; /* swap space used (Kb) */ count_t vlock; /* virtual locked (Kb) */ count_t cgmemmax; /* cgroup memory.max (Kb) */ count_t cgmemmaxr; /* restrictive memory.max (Kb) */ count_t cgswpmax; /* cgroup memory.swap.max (Kb) */ count_t cgswpmaxr; /* restrictive swap.max (Kb) */ count_t cfuture[3]; /* reserved for future use */ } mem; /* NETWORK STATISTICS */ struct net_210 { count_t tcpsnd; /* number of TCP-packets sent */ count_t tcpssz; /* cumulative size packets sent */ count_t tcprcv; /* number of TCP-packets recved */ count_t tcprsz; /* cumulative size packets rcvd */ count_t udpsnd; /* number of UDP-packets sent */ count_t udpssz; /* cumulative size packets sent */ count_t udprcv; /* number of UDP-packets recved */ count_t udprsz; /* cumulative size packets sent */ count_t avail1; /* */ count_t avail2; /* */ count_t cfuture[4]; /* reserved for future use */ } net; struct gpu_210 { char state; // A - active, E - Exit, '\0' - no use char cfuture[3]; // short nrgpus; // number of GPUs for this process int32_t gpulist; // bitlist with GPU numbers int gpubusy; // gpu busy perc process lifetime -1 = n/a int membusy; // memory busy perc process lifetime -1 = n/a count_t timems; // milliseconds accounting -1 = n/a // value 0 for active process, // value > 0 after termination count_t memnow; // current memory consumption in KiB count_t memcum; // cumulative memory consumption in KiB count_t sample; // number of samples } gpu; }; atop-2.12.1/prev/photoproc_211.h0000644000203100020310000001134015064552761015577 0ustar gerlofgerlof/* ** structure containing only relevant process-info extracted ** from kernel's process-administration */ struct tstat_211 { /* GENERAL TASK INFO */ struct gen_211 { int tgid; /* threadgroup identification */ int pid; /* process identification */ int ppid; /* parent process identification*/ int ruid; /* real user identification */ int euid; /* eff. user identification */ int suid; /* saved user identification */ int fsuid; /* fs user identification */ int rgid; /* real group identification */ int egid; /* eff. group identification */ int sgid; /* saved group identification */ int fsgid; /* fs group identification */ int nthr; /* number of threads in tgroup */ char name[PNAMLEN+1];/* process name string */ char isproc; /* boolean: process level? */ char state; /* process state ('E' = exited) */ int excode; /* process exit status */ time_t btime; /* process start time (epoch) */ time_t elaps; /* process elaps time (hertz) */ char cmdline[CMDLEN+1];/* command-line string */ int nthrslpi; /* # threads in state 'S' */ int nthrslpu; /* # threads in state 'D' */ int nthrrun; /* # threads in state 'R' */ int nthridle; /* # threads in state 'I' */ int ctid; /* OpenVZ container ID */ int vpid; /* OpenVZ virtual PID */ int wasinactive; /* boolean: task inactive */ char utsname[UTSLEN+1];/* UTS name container or pod */ int cgroupix; /* index in devchain -1=invalid */ /* lazy filling (parsable/json) */ int ifuture[4]; /* reserved for future use */ } gen; /* CPU STATISTICS */ struct cpu_211 { count_t utime; /* time user text (ticks) */ count_t stime; /* time system text (ticks) */ int nice; /* nice value */ int prio; /* priority */ int rtprio; /* realtime priority */ int policy; /* scheduling policy */ int curcpu; /* current processor */ int sleepavg; /* sleep average percentage */ int ifuture[6]; /* reserved for future use */ char wchan[16]; /* wait channel string */ count_t rundelay; /* schedstat rundelay (nanosec) */ count_t blkdelay; /* blkio delay (ticks) */ count_t nvcsw; /* voluntary cxt switch counts */ count_t nivcsw; /* involuntary csw counts */ count_t cfuture[3]; /* reserved for future use */ } cpu; /* DISK STATISTICS */ struct dsk_211 { count_t rio; /* number of read requests */ count_t rsz; /* cumulative # sectors read */ count_t wio; /* number of write requests */ count_t wsz; /* cumulative # sectors written */ count_t cwsz; /* cumulative # written sectors */ /* being cancelled */ count_t cfuture[4]; /* reserved for future use */ } dsk; /* MEMORY STATISTICS */ struct mem_211 { count_t minflt; /* number of page-reclaims */ count_t majflt; /* number of page-faults */ count_t vexec; /* virtmem execfile (Kb) */ count_t vmem; /* virtual memory (Kb) */ count_t rmem; /* resident memory (Kb) */ count_t pmem; /* resident memory (Kb) */ count_t vgrow; /* virtual growth (Kb) */ count_t rgrow; /* resident growth (Kb) */ count_t vdata; /* virtmem data (Kb) */ count_t vstack; /* virtmem stack (Kb) */ count_t vlibs; /* virtmem libexec (Kb) */ count_t vswap; /* swap space used (Kb) */ count_t vlock; /* virtual locked (Kb) */ count_t cfuture[7]; /* reserved for future use */ } mem; /* NETWORK STATISTICS */ struct net_211 { count_t tcpsnd; /* number of TCP-packets sent */ count_t tcpssz; /* cumulative size packets sent */ count_t tcprcv; /* number of TCP-packets recved */ count_t tcprsz; /* cumulative size packets rcvd */ count_t udpsnd; /* number of UDP-packets sent */ count_t udpssz; /* cumulative size packets sent */ count_t udprcv; /* number of UDP-packets recved */ count_t udprsz; /* cumulative size packets sent */ count_t avail1; /* */ count_t avail2; /* */ count_t cfuture[4]; /* reserved for future use */ } net; struct gpu_211 { char state; // A - active, E - Exit, '\0' - no use char bfuture[3]; // short nrgpus; // number of GPUs for this process int32_t gpulist; // bitlist with GPU numbers int gpubusy; // gpu busy perc process lifetime -1 = n/a int membusy; // memory busy perc process lifetime -1 = n/a count_t timems; // milliseconds accounting -1 = n/a // value 0 for active process, // value > 0 after termination count_t memnow; // current memory consumption in KiB count_t memcum; // cumulative memory consumption in KiB count_t sample; // number of samples count_t cfuture[3]; // } gpu; }; atop-2.12.1/prev/photosyst_200.h0000644000203100020310000001352415064552761015642 0ustar gerlofgerlof#define MAXCPU_20 2048 #define MAXDSK_20 512 #define MAXLVM_20 1024 #define MAXMDD_20 256 #define MAXINTF_20 128 /************************************************************************/ struct memstat_20 { count_t physmem; // number of physical pages count_t freemem; // number of free pages count_t buffermem; // number of buffer pages count_t slabmem; // number of slab pages count_t cachemem; // number of cache pages count_t cachedrt; // number of cache pages (dirty) count_t totswap; // number of pages in swap count_t freeswap; // number of free swap pages count_t pgscans; // number of page scans count_t pgsteal; // number of page steals count_t allocstall; // try to free pages forced count_t swouts; // number of pages swapped out count_t swins; // number of pages swapped in count_t commitlim; // commit limit in pages count_t committed; // number of reserved pages count_t shmem; // tot shmem incl. tmpfs (pages) count_t shmrss; // resident shared memory (pages) count_t shmswp; // swapped shared memory (pages) count_t slabreclaim; // reclaimable slab (pages) }; /************************************************************************/ struct netstat_20 { struct ipv4_stats ipv4; struct icmpv4_stats_without_InCsumErrors icmpv4; struct udpv4_stats udpv4; struct ipv6_stats ipv6; struct icmpv6_stats icmpv6; struct udpv6_stats udpv6; struct tcp_stats_without_InCsumErrors tcp; }; /************************************************************************/ struct freqcnt_20 { count_t maxfreq;/* frequency in MHz */ count_t cnt; /* number of clock ticks times state */ count_t ticks; /* number of total clock ticks */ /* if zero, cnt is actul freq */ }; struct percpu_20 { int cpunr; count_t stime; /* system time in clock ticks */ count_t utime; /* user time in clock ticks */ count_t ntime; /* nice time in clock ticks */ count_t itime; /* idle time in clock ticks */ count_t wtime; /* iowait time in clock ticks */ count_t Itime; /* irq time in clock ticks */ count_t Stime; /* softirq time in clock ticks */ count_t steal; /* steal time in clock ticks */ count_t guest; /* guest time in clock ticks */ struct freqcnt_20 freqcnt;/* frequency scaling info */ count_t cfuture[1]; /* reserved for future use */ }; struct cpustat_20 { count_t nrcpu; /* number of cpu's */ count_t devint; /* number of device interrupts */ count_t csw; /* number of context switches */ count_t nprocs; /* number of processes started */ float lavg1; /* load average last minute */ float lavg5; /* load average last 5 minutes */ float lavg15; /* load average last 15 minutes */ count_t cfuture[4]; /* reserved for future use */ struct percpu_20 all; struct percpu_20 cpu[MAXCPU_20]; }; /************************************************************************/ struct perdsk_20 { char name[MAXDKNAM]; /* empty string for last */ count_t nread; /* number of read transfers */ count_t nrsect; /* number of sectors read */ count_t nwrite; /* number of write transfers */ count_t nwsect; /* number of sectors written */ count_t io_ms; /* number of millisecs spent for I/O */ count_t avque; /* average queue length */ count_t cfuture[4]; /* reserved for future use */ }; struct dskstat_20 { int ndsk; /* number of physical disks */ int nmdd; /* number of md volumes */ int nlvm; /* number of logical volumes */ struct perdsk_20 dsk[MAXDSK_20]; struct perdsk_20 mdd[MAXMDD_20]; struct perdsk_20 lvm[MAXLVM_20]; }; /************************************************************************/ struct perintf_20 { char name[16]; /* empty string for last */ count_t rbyte; /* number of read bytes */ count_t rpack; /* number of read packets */ count_t rerrs; /* receive errors */ count_t rdrop; /* receive drops */ count_t rfifo; /* receive fifo */ count_t rframe; /* receive framing errors */ count_t rcompr; /* receive compressed */ count_t rmultic;/* receive multicast */ count_t rfuture[4]; /* reserved for future use */ count_t sbyte; /* number of written bytes */ count_t spack; /* number of written packets */ count_t serrs; /* transmit errors */ count_t sdrop; /* transmit drops */ count_t sfifo; /* transmit fifo */ count_t scollis;/* collisions */ count_t scarrier;/* transmit carrier */ count_t scompr; /* transmit compressed */ count_t sfuture[4]; /* reserved for future use */ long speed; /* interface speed in megabits/second */ char duplex; /* full duplex (boolean) */ count_t cfuture[4]; /* reserved for future use */ }; struct intfstat_20 { int nrintf; struct perintf_20 intf[MAXINTF_20]; }; /************************************************************************/ /* ** experimental stuff for access to local HTTP daemons */ struct wwwstat_20 { count_t accesses; /* total number of HTTP-requests */ count_t totkbytes; /* total kbytes transfer for HTTP-req */ count_t uptime; /* number of seconds since startup */ int bworkers; /* number of busy httpd-daemons */ int iworkers; /* number of idle httpd-daemons */ }; /************************************************************************/ struct sstat_20 { struct cpustat_20 cpu; struct memstat_20 mem; struct netstat_20 net; struct intfstat_20 intf; struct dskstat_20 dsk; struct wwwstat_20 www; }; atop-2.12.1/prev/photosyst_201.h0000644000203100020310000001415715064552761015646 0ustar gerlofgerlof#define MAXCPU_21 2048 #define MAXDSK_21 1024 #define MAXLVM_21 2048 #define MAXMDD_21 256 #define MAXINTF_21 128 #define MAXDKNAM 32 /************************************************************************/ struct memstat_21 { count_t physmem; // number of physical pages count_t freemem; // number of free pages count_t buffermem; // number of buffer pages count_t slabmem; // number of slab pages count_t cachemem; // number of cache pages count_t cachedrt; // number of cache pages (dirty) count_t totswap; // number of pages in swap count_t freeswap; // number of free swap pages count_t pgscans; // number of page scans count_t pgsteal; // number of page steals count_t allocstall; // try to free pages forced count_t swouts; // number of pages swapped out count_t swins; // number of pages swapped in count_t commitlim; // commit limit in pages count_t committed; // number of reserved pages count_t shmem; // tot shmem incl. tmpfs (pages) count_t shmrss; // resident shared memory (pages) count_t shmswp; // swapped shared memory (pages) count_t slabreclaim; // reclaimable slab (pages) count_t tothugepage; // total huge pages (huge pages) count_t freehugepage; // free huge pages (huge pages) count_t hugepagesz; // huge page size (bytes) count_t vmwballoon; // vmware claimed balloon pages count_t cfuture[8]; // reserved for future use }; /************************************************************************/ struct netstat_21 { struct ipv4_stats ipv4; struct icmpv4_stats_without_InCsumErrors icmpv4; struct udpv4_stats udpv4; struct ipv6_stats ipv6; struct icmpv6_stats icmpv6; struct udpv6_stats udpv6; struct tcp_stats_without_InCsumErrors tcp; }; /************************************************************************/ struct freqcnt_21 { count_t maxfreq;/* frequency in MHz */ count_t cnt; /* number of clock ticks times state */ count_t ticks; /* number of total clock ticks */ /* if zero, cnt is actul freq */ }; struct percpu_21 { int cpunr; count_t stime; /* system time in clock ticks */ count_t utime; /* user time in clock ticks */ count_t ntime; /* nice time in clock ticks */ count_t itime; /* idle time in clock ticks */ count_t wtime; /* iowait time in clock ticks */ count_t Itime; /* irq time in clock ticks */ count_t Stime; /* softirq time in clock ticks */ count_t steal; /* steal time in clock ticks */ count_t guest; /* guest time in clock ticks */ struct freqcnt_21 freqcnt;/* frequency scaling info */ count_t cfuture[4]; /* reserved for future use */ }; struct cpustat_21 { count_t nrcpu; /* number of cpu's */ count_t devint; /* number of device interrupts */ count_t csw; /* number of context switches */ count_t nprocs; /* number of processes started */ float lavg1; /* load average last minute */ float lavg5; /* load average last 5 minutes */ float lavg15; /* load average last 15 minutes */ count_t cfuture[4]; /* reserved for future use */ struct percpu_21 all; struct percpu_21 cpu[MAXCPU_21]; }; /************************************************************************/ struct perdsk_21 { char name[MAXDKNAM]; /* empty string for last */ count_t nread; /* number of read transfers */ count_t nrsect; /* number of sectors read */ count_t nwrite; /* number of write transfers */ count_t nwsect; /* number of sectors written */ count_t io_ms; /* number of millisecs spent for I/O */ count_t avque; /* average queue length */ count_t cfuture[4]; /* reserved for future use */ }; struct dskstat_21 { int ndsk; /* number of physical disks */ int nmdd; /* number of md volumes */ int nlvm; /* number of logical volumes */ struct perdsk_21 dsk[MAXDSK_21]; struct perdsk_21 mdd[MAXMDD_21]; struct perdsk_21 lvm[MAXLVM_21]; }; /************************************************************************/ struct perintf_21 { char name[16]; /* empty string for last */ count_t rbyte; /* number of read bytes */ count_t rpack; /* number of read packets */ count_t rerrs; /* receive errors */ count_t rdrop; /* receive drops */ count_t rfifo; /* receive fifo */ count_t rframe; /* receive framing errors */ count_t rcompr; /* receive compressed */ count_t rmultic;/* receive multicast */ count_t rfuture[4]; /* reserved for future use */ count_t sbyte; /* number of written bytes */ count_t spack; /* number of written packets */ count_t serrs; /* transmit errors */ count_t sdrop; /* transmit drops */ count_t sfifo; /* transmit fifo */ count_t scollis;/* collisions */ count_t scarrier;/* transmit carrier */ count_t scompr; /* transmit compressed */ count_t sfuture[4]; /* reserved for future use */ long speed; /* interface speed in megabits/second */ char duplex; /* full duplex (boolean) */ count_t cfuture[4]; /* reserved for future use */ }; struct intfstat_21 { int nrintf; struct perintf_21 intf[MAXINTF_21]; }; /************************************************************************/ /* ** experimental stuff for access to local HTTP daemons */ struct wwwstat_21 { count_t accesses; /* total number of HTTP-requests */ count_t totkbytes; /* total kbytes transfer for HTTP-req */ count_t uptime; /* number of seconds since startup */ int bworkers; /* number of busy httpd-daemons */ int iworkers; /* number of idle httpd-daemons */ }; /************************************************************************/ struct sstat_21 { struct cpustat_21 cpu; struct memstat_21 mem; struct netstat_21 net; struct intfstat_21 intf; struct dskstat_21 dsk; struct wwwstat_21 www; }; atop-2.12.1/prev/photosyst_203.h0000644000203100020310000002016215064552761015641 0ustar gerlofgerlof#define MAXCPU_23 2048 #define MAXDSK_23 1024 #define MAXLVM_23 2048 #define MAXMDD_23 256 #define MAXINTF_23 128 #define MAXCONTAINER_23 128 #define MAXNFSMOUNT_23 64 #define MAXDKNAM 32 /************************************************************************/ struct memstat_23 { count_t physmem; // number of physical pages count_t freemem; // number of free pages count_t buffermem; // number of buffer pages count_t slabmem; // number of slab pages count_t cachemem; // number of cache pages count_t cachedrt; // number of cache pages (dirty) count_t totswap; // number of pages in swap count_t freeswap; // number of free swap pages count_t pgscans; // number of page scans count_t pgsteal; // number of page steals count_t allocstall; // try to free pages forced count_t swouts; // number of pages swapped out count_t swins; // number of pages swapped in count_t commitlim; // commit limit in pages count_t committed; // number of reserved pages count_t shmem; // tot shmem incl. tmpfs (pages) count_t shmrss; // resident shared memory (pages) count_t shmswp; // swapped shared memory (pages) count_t slabreclaim; // reclaimable slab (pages) count_t tothugepage; // total huge pages (huge pages) count_t freehugepage; // free huge pages (huge pages) count_t hugepagesz; // huge page size (bytes) count_t vmwballoon; // vmware claimed balloon pages count_t cfuture[8]; // reserved for future use }; /************************************************************************/ struct netstat_23 { struct ipv4_stats ipv4; struct icmpv4_stats_without_InCsumErrors icmpv4; struct udpv4_stats udpv4; struct ipv6_stats ipv6; struct icmpv6_stats icmpv6; struct udpv6_stats udpv6; struct tcp_stats_without_InCsumErrors tcp; }; /************************************************************************/ struct freqcnt_23 { count_t maxfreq;/* frequency in MHz */ count_t cnt; /* number of clock ticks times state */ count_t ticks; /* number of total clock ticks */ /* if zero, cnt is actul freq */ }; struct percpu_23 { int cpunr; count_t stime; /* system time in clock ticks */ count_t utime; /* user time in clock ticks */ count_t ntime; /* nice time in clock ticks */ count_t itime; /* idle time in clock ticks */ count_t wtime; /* iowait time in clock ticks */ count_t Itime; /* irq time in clock ticks */ count_t Stime; /* softirq time in clock ticks */ count_t steal; /* steal time in clock ticks */ count_t guest; /* guest time in clock ticks */ struct freqcnt_23 freqcnt;/* frequency scaling info */ count_t cfuture[4]; /* reserved for future use */ }; struct cpustat_23 { count_t nrcpu; /* number of cpu's */ count_t devint; /* number of device interrupts */ count_t csw; /* number of context switches */ count_t nprocs; /* number of processes started */ float lavg1; /* load average last minute */ float lavg5; /* load average last 5 minutes */ float lavg15; /* load average last 15 minutes */ count_t cfuture[4]; /* reserved for future use */ struct percpu_23 all; struct percpu_23 cpu[MAXCPU_23]; }; /************************************************************************/ struct perdsk_23 { char name[MAXDKNAM]; /* empty string for last */ count_t nread; /* number of read transfers */ count_t nrsect; /* number of sectors read */ count_t nwrite; /* number of write transfers */ count_t nwsect; /* number of sectors written */ count_t io_ms; /* number of millisecs spent for I/O */ count_t avque; /* average queue length */ count_t cfuture[4]; /* reserved for future use */ }; struct dskstat_23 { int ndsk; /* number of physical disks */ int nmdd; /* number of md volumes */ int nlvm; /* number of logical volumes */ struct perdsk_23 dsk[MAXDSK_23]; struct perdsk_23 mdd[MAXMDD_23]; struct perdsk_23 lvm[MAXLVM_23]; }; /************************************************************************/ struct perintf_23 { char name[16]; /* empty string for last */ count_t rbyte; /* number of read bytes */ count_t rpack; /* number of read packets */ count_t rerrs; /* receive errors */ count_t rdrop; /* receive drops */ count_t rfifo; /* receive fifo */ count_t rframe; /* receive framing errors */ count_t rcompr; /* receive compressed */ count_t rmultic;/* receive multicast */ count_t rfuture[4]; /* reserved for future use */ count_t sbyte; /* number of written bytes */ count_t spack; /* number of written packets */ count_t serrs; /* transmit errors */ count_t sdrop; /* transmit drops */ count_t sfifo; /* transmit fifo */ count_t scollis;/* collisions */ count_t scarrier;/* transmit carrier */ count_t scompr; /* transmit compressed */ count_t sfuture[4]; /* reserved for future use */ char type; /* interface type ('e'/'w'/'?') */ long speed; /* interface speed in megabits/second */ long speedp; /* previous interface speed */ char duplex; /* full duplex (boolean) */ count_t cfuture[4]; /* reserved for future use */ }; struct intfstat_23 { int nrintf; struct perintf_23 intf[MAXINTF_23]; }; /************************************************************************/ struct pernfsmount_23 { char mountdev[128]; /* mountdevice */ count_t age; /* number of seconds mounted */ count_t bytesread; /* via normal reads */ count_t byteswrite; /* via normal writes */ count_t bytesdread; /* via direct reads */ count_t bytesdwrite; /* via direct writes */ count_t bytestotread; /* via reads */ count_t bytestotwrite; /* via writes */ count_t pagesmread; /* via mmap reads */ count_t pagesmwrite; /* via mmap writes */ count_t future[8]; }; struct nfsstat_23 { struct { count_t netcnt; count_t netudpcnt; count_t nettcpcnt; count_t nettcpcon; count_t rpccnt; count_t rpcbadfmt; count_t rpcbadaut; count_t rpcbadcln; count_t rpcread; count_t rpcwrite; count_t rchits; /* repcache hits */ count_t rcmiss; /* repcache misses */ count_t rcnoca; /* uncached requests */ count_t nrbytes; /* read bytes */ count_t nwbytes; /* written bytes */ count_t future[8]; } server; struct { count_t rpccnt; count_t rpcretrans; count_t rpcautrefresh; count_t rpcread; count_t rpcwrite; count_t future[8]; } client; struct { int nrmounts; struct pernfsmount_23 nfsmnt[MAXNFSMOUNT_23]; } nfsmounts; }; /************************************************************************/ struct percontainer_23 { unsigned long ctid; /* container id */ unsigned long numproc; /* number of processes */ count_t system; /* */ count_t user; /* */ count_t nice; /* */ count_t uptime; /* */ count_t physpages; /* */ }; struct contstat_23 { int nrcontainer; struct percontainer_23 cont[MAXCONTAINER_23]; }; /************************************************************************/ /* ** experimental stuff for access to local HTTP daemons */ struct wwwstat_23 { count_t accesses; /* total number of HTTP-requests */ count_t totkbytes; /* total kbytes transfer for HTTP-req */ count_t uptime; /* number of seconds since startup */ int bworkers; /* number of busy httpd-daemons */ int iworkers; /* number of idle httpd-daemons */ }; /************************************************************************/ struct sstat_23 { struct cpustat_23 cpu; struct memstat_23 mem; struct netstat_23 net; struct intfstat_23 intf; struct dskstat_23 dsk; struct nfsstat_23 nfs; struct contstat_23 cfs; struct wwwstat_23 www; }; atop-2.12.1/prev/photosyst_204.h0000644000203100020310000002471515064552761015652 0ustar gerlofgerlof#define MAXCPU_24 2048 #define MAXDSK_24 1024 #define MAXLVM_24 2048 #define MAXMDD_24 256 #define MAXINTF_24 128 #define MAXCONTAINER_24 128 #define MAXNFSMOUNT_24 64 #define MAXIBPORT_24 32 #define MAXGPU_24 32 /************************************************************************/ struct memstat_24 { count_t physmem; // number of physical pages count_t freemem; // number of free pages count_t buffermem; // number of buffer pages count_t slabmem; // number of slab pages count_t cachemem; // number of cache pages count_t cachedrt; // number of cache pages (dirty) count_t totswap; // number of pages in swap count_t freeswap; // number of free swap pages count_t pgscans; // number of page scans count_t pgsteal; // number of page steals count_t allocstall; // try to free pages forced count_t swouts; // number of pages swapped out count_t swins; // number of pages swapped in count_t commitlim; // commit limit in pages count_t committed; // number of reserved pages count_t shmem; // tot shmem incl. tmpfs (pages) count_t shmrss; // resident shared memory (pages) count_t shmswp; // swapped shared memory (pages) count_t slabreclaim; // reclaimable slab (pages) count_t tothugepage; // total huge pages (huge pages) count_t freehugepage; // free huge pages (huge pages) count_t hugepagesz; // huge page size (bytes) count_t vmwballoon; // vmware claimed balloon pages count_t cfuture[8]; // reserved for future use }; /************************************************************************/ struct netstat_24 { struct ipv4_stats ipv4; struct icmpv4_stats_without_InCsumErrors icmpv4; struct udpv4_stats udpv4; struct ipv6_stats ipv6; struct icmpv6_stats icmpv6; struct udpv6_stats udpv6; struct tcp_stats_without_InCsumErrors tcp; }; /************************************************************************/ struct freqcnt_24 { count_t maxfreq;/* frequency in MHz */ count_t cnt; /* number of clock ticks times state */ count_t ticks; /* number of total clock ticks */ /* if zero, cnt is actul freq */ }; struct percpu_24 { int cpunr; count_t stime; /* system time in clock ticks */ count_t utime; /* user time in clock ticks */ count_t ntime; /* nice time in clock ticks */ count_t itime; /* idle time in clock ticks */ count_t wtime; /* iowait time in clock ticks */ count_t Itime; /* irq time in clock ticks */ count_t Stime; /* softirq time in clock ticks */ count_t steal; /* steal time in clock ticks */ count_t guest; /* guest time in clock ticks */ struct freqcnt_24 freqcnt;/* frequency scaling info */ count_t instr; /* CPU instructions */ count_t cycle; /* CPU cycles */ count_t cfuture[2]; /* reserved for future use */ }; struct cpustat_24 { count_t nrcpu; /* number of cpu's */ count_t devint; /* number of device interrupts */ count_t csw; /* number of context switches */ count_t nprocs; /* number of processes started */ float lavg1; /* load average last minute */ float lavg5; /* load average last 5 minutes */ float lavg15; /* load average last 15 minutes */ count_t cfuture[4]; /* reserved for future use */ struct percpu_24 all; struct percpu_24 cpu[MAXCPU_24]; }; /************************************************************************/ struct perdsk_24 { char name[MAXDKNAM]; /* empty string for last */ count_t nread; /* number of read transfers */ count_t nrsect; /* number of sectors read */ count_t nwrite; /* number of write transfers */ count_t nwsect; /* number of sectors written */ count_t io_ms; /* number of millisecs spent for I/O */ count_t avque; /* average queue length */ count_t cfuture[4]; /* reserved for future use */ }; struct dskstat_24 { int ndsk; /* number of physical disks */ int nmdd; /* number of md volumes */ int nlvm; /* number of logical volumes */ struct perdsk_24 dsk[MAXDSK_24]; struct perdsk_24 mdd[MAXMDD_24]; struct perdsk_24 lvm[MAXLVM_24]; }; /************************************************************************/ struct perintf_24 { char name[16]; /* empty string for last */ count_t rbyte; /* number of read bytes */ count_t rpack; /* number of read packets */ count_t rerrs; /* receive errors */ count_t rdrop; /* receive drops */ count_t rfifo; /* receive fifo */ count_t rframe; /* receive framing errors */ count_t rcompr; /* receive compressed */ count_t rmultic;/* receive multicast */ count_t rfuture[4]; /* reserved for future use */ count_t sbyte; /* number of written bytes */ count_t spack; /* number of written packets */ count_t serrs; /* transmit errors */ count_t sdrop; /* transmit drops */ count_t sfifo; /* transmit fifo */ count_t scollis;/* collisions */ count_t scarrier;/* transmit carrier */ count_t scompr; /* transmit compressed */ count_t sfuture[4]; /* reserved for future use */ char type; /* interface type ('e'/'w'/'?') */ long speed; /* interface speed in megabits/second */ long speedp; /* previous interface speed */ char duplex; /* full duplex (boolean) */ count_t cfuture[4]; /* reserved for future use */ }; struct intfstat_24 { int nrintf; struct perintf_24 intf[MAXINTF_24]; }; /************************************************************************/ struct pernfsmount_24 { char mountdev[128]; /* mountdevice */ count_t age; /* number of seconds mounted */ count_t bytesread; /* via normal reads */ count_t byteswrite; /* via normal writes */ count_t bytesdread; /* via direct reads */ count_t bytesdwrite; /* via direct writes */ count_t bytestotread; /* via reads */ count_t bytestotwrite; /* via writes */ count_t pagesmread; /* via mmap reads */ count_t pagesmwrite; /* via mmap writes */ count_t future[8]; }; struct nfsstat_24 { struct { count_t netcnt; count_t netudpcnt; count_t nettcpcnt; count_t nettcpcon; count_t rpccnt; count_t rpcbadfmt; count_t rpcbadaut; count_t rpcbadcln; count_t rpcread; count_t rpcwrite; count_t rchits; /* repcache hits */ count_t rcmiss; /* repcache misses */ count_t rcnoca; /* uncached requests */ count_t nrbytes; /* read bytes */ count_t nwbytes; /* written bytes */ count_t future[8]; } server; struct { count_t rpccnt; count_t rpcretrans; count_t rpcautrefresh; count_t rpcread; count_t rpcwrite; count_t future[8]; } client; struct { int nrmounts; struct pernfsmount_24 nfsmnt[MAXNFSMOUNT_24]; } nfsmounts; }; /************************************************************************/ struct psi_24 { float avg10; // average pressure last 10 seconds float avg60; // average pressure last 60 seconds float avg300; // average pressure last 300 seconds count_t total; // total number of milliseconds }; struct pressure_24 { char present; /* pressure stats supported? */ char future[3]; struct psi_24 cpusome; /* pressure stall info 'some' */ struct psi_24 memsome; /* pressure stall info 'some' */ struct psi_24 memfull; /* pressure stall info 'full' */ struct psi_24 iosome; /* pressure stall info 'some' */ struct psi_24 iofull; /* pressure stall info 'full' */ }; /************************************************************************/ struct percontainer_24 { unsigned long ctid; /* container id */ unsigned long numproc; /* number of processes */ count_t system; /* */ count_t user; /* */ count_t nice; /* */ count_t uptime; /* */ count_t physpages; /* */ }; struct contstat_24 { int nrcontainer; struct percontainer_24 cont[MAXCONTAINER_24]; }; /************************************************************************/ /* ** experimental stuff for access to local HTTP daemons */ struct wwwstat_24 { count_t accesses; /* total number of HTTP-requests */ count_t totkbytes; /* total kbytes transfer for HTTP-req */ count_t uptime; /* number of seconds since startup */ int bworkers; /* number of busy httpd-daemons */ int iworkers; /* number of idle httpd-daemons */ }; /************************************************************************/ struct pergpu_24 { char taskstats; // GPU task statistics supported? unsigned char nrprocs; // number of processes using GPU char type[MAXGPUTYPE+1]; // GPU type char busid[MAXGPUBUS+1]; // GPU bus identification int gpunr; // GPU number int gpupercnow; // processor percentage last second // -1 if not supported int mempercnow; // memory percentage last second // -1 if not supported count_t memtotnow; // total memory in KiB count_t memusenow; // used memory in KiB count_t samples; // number of samples count_t gpuperccum; // cumulative processor busy percentage // -1 if not supported count_t memperccum; // cumulative memory percentage // -1 if not supported count_t memusecum; // cumulative used memory in KiB }; struct gpustat_24 { int nrgpus; // total number of GPUs struct pergpu_24 gpu[MAXGPU_24]; }; /************************************************************************/ struct perifb_24 { char ibname[MAXIBNAME]; // InfiniBand controller short portnr; // InfiniBand controller port short lanes; // number of lanes (traffic factor) count_t rate; // transfer rate in megabits/sec count_t rcvb; // bytes received count_t sndb; // bytes transmitted count_t rcvp; // packets received count_t sndp; // packets transmitted }; struct ifbstat_24 { int nrports; // total number of IB ports struct perifb_24 ifb[MAXIBPORT_24]; }; /************************************************************************/ struct sstat_24 { struct cpustat_24 cpu; struct memstat_24 mem; struct netstat_24 net; struct intfstat_24 intf; struct dskstat_24 dsk; struct nfsstat_24 nfs; struct contstat_24 cfs; struct pressure_24 psi; struct gpustat_24 gpu; struct ifbstat_24 ifb; struct wwwstat_24 www; }; atop-2.12.1/prev/photosyst_205.h0000644000203100020310000002471515064552761015653 0ustar gerlofgerlof#define MAXCPU_25 2048 #define MAXDSK_25 1024 #define MAXLVM_25 2048 #define MAXMDD_25 256 #define MAXINTF_25 128 #define MAXCONTAINER_25 128 #define MAXNFSMOUNT_25 64 #define MAXIBPORT_25 32 #define MAXGPU_25 32 /************************************************************************/ struct memstat_25 { count_t physmem; // number of physical pages count_t freemem; // number of free pages count_t buffermem; // number of buffer pages count_t slabmem; // number of slab pages count_t cachemem; // number of cache pages count_t cachedrt; // number of cache pages (dirty) count_t totswap; // number of pages in swap count_t freeswap; // number of free swap pages count_t pgscans; // number of page scans count_t pgsteal; // number of page steals count_t allocstall; // try to free pages forced count_t swouts; // number of pages swapped out count_t swins; // number of pages swapped in count_t commitlim; // commit limit in pages count_t committed; // number of reserved pages count_t shmem; // tot shmem incl. tmpfs (pages) count_t shmrss; // resident shared memory (pages) count_t shmswp; // swapped shared memory (pages) count_t slabreclaim; // reclaimable slab (pages) count_t tothugepage; // total huge pages (huge pages) count_t freehugepage; // free huge pages (huge pages) count_t hugepagesz; // huge page size (bytes) count_t vmwballoon; // vmware claimed balloon pages count_t cfuture[8]; // reserved for future use }; /************************************************************************/ struct netstat_25 { struct ipv4_stats ipv4; struct icmpv4_stats_without_InCsumErrors icmpv4; struct udpv4_stats udpv4; struct ipv6_stats ipv6; struct icmpv6_stats icmpv6; struct udpv6_stats udpv6; struct tcp_stats_without_InCsumErrors tcp; }; /************************************************************************/ struct freqcnt_25 { count_t maxfreq;/* frequency in MHz */ count_t cnt; /* number of clock ticks times state */ count_t ticks; /* number of total clock ticks */ /* if zero, cnt is actul freq */ }; struct percpu_25 { int cpunr; count_t stime; /* system time in clock ticks */ count_t utime; /* user time in clock ticks */ count_t ntime; /* nice time in clock ticks */ count_t itime; /* idle time in clock ticks */ count_t wtime; /* iowait time in clock ticks */ count_t Itime; /* irq time in clock ticks */ count_t Stime; /* softirq time in clock ticks */ count_t steal; /* steal time in clock ticks */ count_t guest; /* guest time in clock ticks */ struct freqcnt_25 freqcnt;/* frequency scaling info */ count_t instr; /* CPU instructions */ count_t cycle; /* CPU cycles */ count_t cfuture[2]; /* reserved for future use */ }; struct cpustat_25 { count_t nrcpu; /* number of cpu's */ count_t devint; /* number of device interrupts */ count_t csw; /* number of context switches */ count_t nprocs; /* number of processes started */ float lavg1; /* load average last minute */ float lavg5; /* load average last 5 minutes */ float lavg15; /* load average last 15 minutes */ count_t cfuture[4]; /* reserved for future use */ struct percpu_25 all; struct percpu_25 cpu[MAXCPU_25]; }; /************************************************************************/ struct perdsk_25 { char name[MAXDKNAM]; /* empty string for last */ count_t nread; /* number of read transfers */ count_t nrsect; /* number of sectors read */ count_t nwrite; /* number of write transfers */ count_t nwsect; /* number of sectors written */ count_t io_ms; /* number of millisecs spent for I/O */ count_t avque; /* average queue length */ count_t cfuture[4]; /* reserved for future use */ }; struct dskstat_25 { int ndsk; /* number of physical disks */ int nmdd; /* number of md volumes */ int nlvm; /* number of logical volumes */ struct perdsk_25 dsk[MAXDSK_25]; struct perdsk_25 mdd[MAXMDD_25]; struct perdsk_25 lvm[MAXLVM_25]; }; /************************************************************************/ struct perintf_25 { char name[16]; /* empty string for last */ count_t rbyte; /* number of read bytes */ count_t rpack; /* number of read packets */ count_t rerrs; /* receive errors */ count_t rdrop; /* receive drops */ count_t rfifo; /* receive fifo */ count_t rframe; /* receive framing errors */ count_t rcompr; /* receive compressed */ count_t rmultic;/* receive multicast */ count_t rfuture[4]; /* reserved for future use */ count_t sbyte; /* number of written bytes */ count_t spack; /* number of written packets */ count_t serrs; /* transmit errors */ count_t sdrop; /* transmit drops */ count_t sfifo; /* transmit fifo */ count_t scollis;/* collisions */ count_t scarrier;/* transmit carrier */ count_t scompr; /* transmit compressed */ count_t sfuture[4]; /* reserved for future use */ char type; /* interface type ('e'/'w'/'?') */ long speed; /* interface speed in megabits/second */ long speedp; /* previous interface speed */ char duplex; /* full duplex (boolean) */ count_t cfuture[4]; /* reserved for future use */ }; struct intfstat_25 { int nrintf; struct perintf_25 intf[MAXINTF_25]; }; /************************************************************************/ struct pernfsmount_25 { char mountdev[128]; /* mountdevice */ count_t age; /* number of seconds mounted */ count_t bytesread; /* via normal reads */ count_t byteswrite; /* via normal writes */ count_t bytesdread; /* via direct reads */ count_t bytesdwrite; /* via direct writes */ count_t bytestotread; /* via reads */ count_t bytestotwrite; /* via writes */ count_t pagesmread; /* via mmap reads */ count_t pagesmwrite; /* via mmap writes */ count_t future[8]; }; struct nfsstat_25 { struct { count_t netcnt; count_t netudpcnt; count_t nettcpcnt; count_t nettcpcon; count_t rpccnt; count_t rpcbadfmt; count_t rpcbadaut; count_t rpcbadcln; count_t rpcread; count_t rpcwrite; count_t rchits; /* repcache hits */ count_t rcmiss; /* repcache misses */ count_t rcnoca; /* uncached requests */ count_t nrbytes; /* read bytes */ count_t nwbytes; /* written bytes */ count_t future[8]; } server; struct { count_t rpccnt; count_t rpcretrans; count_t rpcautrefresh; count_t rpcread; count_t rpcwrite; count_t future[8]; } client; struct { int nrmounts; struct pernfsmount_25 nfsmnt[MAXNFSMOUNT_25]; } nfsmounts; }; /************************************************************************/ struct psi_25 { float avg10; // average pressure last 10 seconds float avg60; // average pressure last 60 seconds float avg300; // average pressure last 300 seconds count_t total; // total number of milliseconds }; struct pressure_25 { char present; /* pressure stats supported? */ char future[3]; struct psi_25 cpusome; /* pressure stall info 'some' */ struct psi_25 memsome; /* pressure stall info 'some' */ struct psi_25 memfull; /* pressure stall info 'full' */ struct psi_25 iosome; /* pressure stall info 'some' */ struct psi_25 iofull; /* pressure stall info 'full' */ }; /************************************************************************/ struct percontainer_25 { unsigned long ctid; /* container id */ unsigned long numproc; /* number of processes */ count_t system; /* */ count_t user; /* */ count_t nice; /* */ count_t uptime; /* */ count_t physpages; /* */ }; struct contstat_25 { int nrcontainer; struct percontainer_25 cont[MAXCONTAINER_25]; }; /************************************************************************/ /* ** experimental stuff for access to local HTTP daemons */ struct wwwstat_25 { count_t accesses; /* total number of HTTP-requests */ count_t totkbytes; /* total kbytes transfer for HTTP-req */ count_t uptime; /* number of seconds since startup */ int bworkers; /* number of busy httpd-daemons */ int iworkers; /* number of idle httpd-daemons */ }; /************************************************************************/ struct pergpu_25 { char taskstats; // GPU task statistics supported? unsigned char nrprocs; // number of processes using GPU char type[MAXGPUTYPE+1]; // GPU type char busid[MAXGPUBUS+1]; // GPU bus identification int gpunr; // GPU number int gpupercnow; // processor percentage last second // -1 if not supported int mempercnow; // memory percentage last second // -1 if not supported count_t memtotnow; // total memory in KiB count_t memusenow; // used memory in KiB count_t samples; // number of samples count_t gpuperccum; // cumulative processor busy percentage // -1 if not supported count_t memperccum; // cumulative memory percentage // -1 if not supported count_t memusecum; // cumulative used memory in KiB }; struct gpustat_25 { int nrgpus; // total number of GPUs struct pergpu_25 gpu[MAXGPU_25]; }; /************************************************************************/ struct perifb_25 { char ibname[MAXIBNAME]; // InfiniBand controller short portnr; // InfiniBand controller port short lanes; // number of lanes (traffic factor) count_t rate; // transfer rate in megabits/sec count_t rcvb; // bytes received count_t sndb; // bytes transmitted count_t rcvp; // packets received count_t sndp; // packets transmitted }; struct ifbstat_25 { int nrports; // total number of IB ports struct perifb_25 ifb[MAXIBPORT_25]; }; /************************************************************************/ struct sstat_25 { struct cpustat_25 cpu; struct memstat_25 mem; struct netstat_25 net; struct intfstat_25 intf; struct dskstat_25 dsk; struct nfsstat_25 nfs; struct contstat_25 cfs; struct pressure_25 psi; struct gpustat_25 gpu; struct ifbstat_25 ifb; struct wwwstat_25 www; }; atop-2.12.1/prev/photosyst_206.h0000644000203100020310000002517115064552761015651 0ustar gerlofgerlof#define MAXCPU_26 2048 #define MAXDSK_26 1024 #define MAXLVM_26 2048 #define MAXMDD_26 256 #define MAXINTF_26 128 #define MAXCONTAINER_26 128 #define MAXNFSMOUNT_26 64 #define MAXIBPORT_26 32 #define MAXGPU_26 32 /************************************************************************/ struct memstat_26 { count_t physmem; // number of physical pages count_t freemem; // number of free pages count_t buffermem; // number of buffer pages count_t slabmem; // number of slab pages count_t cachemem; // number of cache pages count_t cachedrt; // number of cache pages (dirty) count_t totswap; // number of pages in swap count_t freeswap; // number of free swap pages count_t pgscans; // number of page scans count_t pgsteal; // number of page steals count_t allocstall; // try to free pages forced count_t swouts; // number of pages swapped out count_t swins; // number of pages swapped in count_t commitlim; // commit limit in pages count_t committed; // number of reserved pages count_t shmem; // tot shmem incl. tmpfs (pages) count_t shmrss; // resident shared memory (pages) count_t shmswp; // swapped shared memory (pages) count_t slabreclaim; // reclaimable slab (pages) count_t tothugepage; // total huge pages (huge pages) count_t freehugepage; // free huge pages (huge pages) count_t hugepagesz; // huge page size (bytes) count_t vmwballoon; // vmware claimed balloon pages count_t zfsarcsize; // zfsonlinux ARC size (pages) count_t swapcached; // swap cache (pages) count_t cfuture[6]; // reserved for future use }; /************************************************************************/ struct netstat_26 { struct ipv4_stats ipv4; struct icmpv4_stats_without_InCsumErrors icmpv4; struct udpv4_stats udpv4; struct ipv6_stats ipv6; struct icmpv6_stats icmpv6; struct udpv6_stats udpv6; struct tcp_stats_without_InCsumErrors tcp; }; /************************************************************************/ struct freqcnt_26 { count_t maxfreq;/* frequency in MHz */ count_t cnt; /* number of clock ticks times state */ count_t ticks; /* number of total clock ticks */ /* if zero, cnt is actul freq */ }; struct percpu_26 { int cpunr; count_t stime; /* system time in clock ticks */ count_t utime; /* user time in clock ticks */ count_t ntime; /* nice time in clock ticks */ count_t itime; /* idle time in clock ticks */ count_t wtime; /* iowait time in clock ticks */ count_t Itime; /* irq time in clock ticks */ count_t Stime; /* softirq time in clock ticks */ count_t steal; /* steal time in clock ticks */ count_t guest; /* guest time in clock ticks */ struct freqcnt_26 freqcnt;/* frequency scaling info */ count_t instr; /* CPU instructions */ count_t cycle; /* CPU cycles */ count_t cfuture[2]; /* reserved for future use */ }; struct cpustat_26 { count_t nrcpu; /* number of cpu's */ count_t devint; /* number of device interrupts */ count_t csw; /* number of context switches */ count_t nprocs; /* number of processes started */ float lavg1; /* load average last minute */ float lavg5; /* load average last 5 minutes */ float lavg15; /* load average last 15 minutes */ count_t cfuture[4]; /* reserved for future use */ struct percpu_26 all; struct percpu_26 cpu[MAXCPU_26]; }; /************************************************************************/ struct perdsk_26 { char name[MAXDKNAM]; /* empty string for last */ count_t nread; /* number of read transfers */ count_t nrsect; /* number of sectors read */ count_t nwrite; /* number of write transfers */ count_t nwsect; /* number of sectors written */ count_t io_ms; /* number of millisecs spent for I/O */ count_t avque; /* average queue length */ count_t cfuture[4]; /* reserved for future use */ }; struct dskstat_26 { int ndsk; /* number of physical disks */ int nmdd; /* number of md volumes */ int nlvm; /* number of logical volumes */ struct perdsk_26 dsk[MAXDSK_26]; struct perdsk_26 mdd[MAXMDD_26]; struct perdsk_26 lvm[MAXLVM_26]; }; /************************************************************************/ struct perintf_26 { char name[16]; /* empty string for last */ count_t rbyte; /* number of read bytes */ count_t rpack; /* number of read packets */ count_t rerrs; /* receive errors */ count_t rdrop; /* receive drops */ count_t rfifo; /* receive fifo */ count_t rframe; /* receive framing errors */ count_t rcompr; /* receive compressed */ count_t rmultic;/* receive multicast */ count_t rfuture[4]; /* reserved for future use */ count_t sbyte; /* number of written bytes */ count_t spack; /* number of written packets */ count_t serrs; /* transmit errors */ count_t sdrop; /* transmit drops */ count_t sfifo; /* transmit fifo */ count_t scollis;/* collisions */ count_t scarrier;/* transmit carrier */ count_t scompr; /* transmit compressed */ count_t sfuture[4]; /* reserved for future use */ char type; /* interface type ('e'/'w'/'?') */ long speed; /* interface speed in megabits/second */ long speedp; /* previous interface speed */ char duplex; /* full duplex (boolean) */ count_t cfuture[4]; /* reserved for future use */ }; struct intfstat_26 { int nrintf; struct perintf_26 intf[MAXINTF_26]; }; /************************************************************************/ struct pernfsmount_26 { char mountdev[128]; /* mountdevice */ count_t age; /* number of seconds mounted */ count_t bytesread; /* via normal reads */ count_t byteswrite; /* via normal writes */ count_t bytesdread; /* via direct reads */ count_t bytesdwrite; /* via direct writes */ count_t bytestotread; /* via reads */ count_t bytestotwrite; /* via writes */ count_t pagesmread; /* via mmap reads */ count_t pagesmwrite; /* via mmap writes */ count_t future[8]; }; struct nfsstat_26 { struct { count_t netcnt; count_t netudpcnt; count_t nettcpcnt; count_t nettcpcon; count_t rpccnt; count_t rpcbadfmt; count_t rpcbadaut; count_t rpcbadcln; count_t rpcread; count_t rpcwrite; count_t rchits; /* repcache hits */ count_t rcmiss; /* repcache misses */ count_t rcnoca; /* uncached requests */ count_t nrbytes; /* read bytes */ count_t nwbytes; /* written bytes */ count_t future[8]; } server; struct { count_t rpccnt; count_t rpcretrans; count_t rpcautrefresh; count_t rpcread; count_t rpcwrite; count_t future[8]; } client; struct { int nrmounts; struct pernfsmount_26 nfsmnt[MAXNFSMOUNT_26]; } nfsmounts; }; /************************************************************************/ struct psi_26 { float avg10; // average pressure last 10 seconds float avg60; // average pressure last 60 seconds float avg300; // average pressure last 300 seconds count_t total; // total number of milliseconds }; struct pressure_26 { char present; /* pressure stats supported? */ char future[3]; struct psi_26 cpusome; /* pressure stall info 'some' */ struct psi_26 memsome; /* pressure stall info 'some' */ struct psi_26 memfull; /* pressure stall info 'full' */ struct psi_26 iosome; /* pressure stall info 'some' */ struct psi_26 iofull; /* pressure stall info 'full' */ }; /************************************************************************/ struct percontainer_26 { unsigned long ctid; /* container id */ unsigned long numproc; /* number of processes */ count_t system; /* */ count_t user; /* */ count_t nice; /* */ count_t uptime; /* */ count_t physpages; /* */ }; struct contstat_26 { int nrcontainer; struct percontainer_26 cont[MAXCONTAINER_26]; }; /************************************************************************/ /* ** experimental stuff for access to local HTTP daemons */ #define HTTPREQ "GET /server-status?auto HTTP/1.1\nHost: localhost\n\n" struct wwwstat_26 { count_t accesses; /* total number of HTTP-requests */ count_t totkbytes; /* total kbytes transfer for HTTP-req */ count_t uptime; /* number of seconds since startup */ int bworkers; /* number of busy httpd-daemons */ int iworkers; /* number of idle httpd-daemons */ }; /************************************************************************/ struct pergpu_26 { char taskstats; // GPU task statistics supported? unsigned char nrprocs; // number of processes using GPU char type[MAXGPUTYPE+1]; // GPU type char busid[MAXGPUBUS+1]; // GPU bus identification int gpunr; // GPU number int gpupercnow; // processor percentage last second // -1 if not supported int mempercnow; // memory percentage last second // -1 if not supported count_t memtotnow; // total memory in KiB count_t memusenow; // used memory in KiB count_t samples; // number of samples count_t gpuperccum; // cumulative processor busy percentage // -1 if not supported count_t memperccum; // cumulative memory percentage // -1 if not supported count_t memusecum; // cumulative used memory in KiB }; struct gpustat_26 { int nrgpus; // total number of GPUs struct pergpu_26 gpu[MAXGPU_26]; }; /************************************************************************/ struct perifb_26 { char ibname[MAXIBNAME]; // InfiniBand controller short portnr; // InfiniBand controller port short lanes; // number of lanes (traffic factor) count_t rate; // transfer rate in megabits/sec count_t rcvb; // bytes received count_t sndb; // bytes transmitted count_t rcvp; // packets received count_t sndp; // packets transmitted }; struct ifbstat_26 { int nrports; // total number of IB ports struct perifb_26 ifb[MAXIBPORT_26]; }; /************************************************************************/ struct sstat_26 { struct cpustat_26 cpu; struct memstat_26 mem; struct netstat_26 net; struct intfstat_26 intf; struct dskstat_26 dsk; struct nfsstat_26 nfs; struct contstat_26 cfs; struct pressure_26 psi; struct gpustat_26 gpu; struct ifbstat_26 ifb; struct wwwstat_26 www; }; atop-2.12.1/prev/photosyst_207.h0000644000203100020310000003222615064552761015651 0ustar gerlofgerlof#define MAXCPU_27 2048 #define MAXDSK_27 1024 #define MAXNUMA_27 1024 #define MAXLVM_27 2048 #define MAXMDD_27 256 #define MAXINTF_27 128 #define MAXCONTAINER_27 128 #define MAXNFSMOUNT_27 64 #define MAXIBPORT_27 32 #define MAXGPU_27 32 #define MAXGPUBUS_27 12 #define MAXGPUTYPE_27 12 #define MAXDKNAM_27 32 #define MAXIBNAME_27 12 /************************************************************************/ struct memstat_27 { count_t physmem; // number of physical pages count_t freemem; // number of free pages count_t buffermem; // number of buffer pages count_t slabmem; // number of slab pages count_t cachemem; // number of cache pages count_t cachedrt; // number of cache pages (dirty) count_t totswap; // number of pages in swap count_t freeswap; // number of free swap pages count_t pgscans; // number of page scans count_t pgsteal; // number of page steals count_t allocstall; // try to free pages forced count_t swouts; // number of pages swapped out count_t swins; // number of pages swapped in count_t commitlim; // commit limit in pages count_t committed; // number of reserved pages count_t shmem; // tot shmem incl. tmpfs (pages) count_t shmrss; // resident shared memory (pages) count_t shmswp; // swapped shared memory (pages) count_t slabreclaim; // reclaimable slab (pages) count_t tothugepage; // total huge pages (huge pages) count_t freehugepage; // free huge pages (huge pages) count_t hugepagesz; // huge page size (bytes) count_t vmwballoon; // vmware claimed balloon pages count_t zfsarcsize; // zfsonlinux ARC size (pages) count_t swapcached; // swap cache (pages) count_t ksmsharing; // saved i.e. deduped memory (pages) count_t ksmshared; // current size shared pages (pages) count_t zswstored; // zswap stored pages (pages) count_t zswtotpool; // total pool size (pages) count_t oomkills; // number of oom killings count_t compactstall; // counter for process stalls count_t pgmigrate; // counter for migrated successfully (pages) count_t numamigrate; // counter for numa migrated (pages) count_t cfuture[9]; // reserved for future use }; /************************************************************************/ struct mempernuma_27 { float frag; // fragmentation level for this numa count_t totmem; // number of physical pages for this numa count_t freemem; // number of free pages for this numa count_t filepage; // number of file pages for this numa count_t dirtymem; // number of cache pages (dirty) for this numa count_t slabmem; // number of slab pages for this numa count_t slabreclaim; // reclaimable slab (pages) for this numa count_t active; // number of pages used more recently for this numa count_t inactive; // number of pages less recently used for this numa count_t shmem; // tot shmem incl. tmpfs (pages) for this numa count_t tothp; // total huge pages (huge pages) for this numa }; struct memnuma_27 { count_t nrnuma; /* the counts of numa */ struct mempernuma_27 numa[MAXNUMA_27]; }; struct cpupernuma_27 { count_t nrcpu; // number of cpu's count_t stime; // accumulate system time in clock ticks for per numa count_t utime; // accumulate user time in clock ticks for per numa count_t ntime; // accumulate nice time in clock ticks for per numa count_t itime; // accumulate idle time in clock ticks for per numa count_t wtime; // accumulate iowait time in clock ticks for per numa count_t Itime; // accumulate irq time in clock ticks for per numa count_t Stime; // accumulate softirq time in clock ticks for per numa count_t steal; // accumulate steal time in clock ticks for per numa count_t guest; // accumulate guest time in clock ticks for per numa }; struct cpunuma_27 { count_t nrnuma; /* the counts of numa */ struct cpupernuma_27 numa[MAXNUMA_27]; }; /************************************************************************/ struct netstat_27 { struct ipv4_stats ipv4; struct icmpv4_stats_without_InCsumErrors icmpv4; struct udpv4_stats udpv4; struct ipv6_stats ipv6; struct icmpv6_stats icmpv6; struct udpv6_stats udpv6; struct tcp_stats_without_InCsumErrors tcp; }; /************************************************************************/ struct freqcnt_27 { count_t maxfreq;/* frequency in MHz */ count_t cnt; /* number of clock ticks times state */ count_t ticks; /* number of total clock ticks */ /* if zero, cnt is actul freq */ }; struct percpu_27 { int cpunr; count_t stime; /* system time in clock ticks */ count_t utime; /* user time in clock ticks */ count_t ntime; /* nice time in clock ticks */ count_t itime; /* idle time in clock ticks */ count_t wtime; /* iowait time in clock ticks */ count_t Itime; /* irq time in clock ticks */ count_t Stime; /* softirq time in clock ticks */ count_t steal; /* steal time in clock ticks */ count_t guest; /* guest time in clock ticks */ struct freqcnt_27 freqcnt;/* frequency scaling info */ count_t instr; /* CPU instructions */ count_t cycle; /* CPU cycles */ count_t cfuture[6]; /* reserved for future use */ }; struct cpustat_27 { count_t nrcpu; /* number of cpu's */ count_t devint; /* number of device interrupts */ count_t csw; /* number of context switches */ count_t nprocs; /* number of processes started */ float lavg1; /* load average last minute */ float lavg5; /* load average last 5 minutes */ float lavg15; /* load average last 15 minutes */ count_t cfuture[4]; /* reserved for future use */ struct percpu_27 all; struct percpu_27 cpu[MAXCPU_27]; }; /************************************************************************/ struct perdsk_27 { char name[MAXDKNAM_27]; /* empty string for last */ count_t nread; /* number of read transfers */ count_t nrsect; /* number of sectors read */ count_t nwrite; /* number of write transfers */ count_t nwsect; /* number of sectors written */ count_t io_ms; /* number of millisecs spent for I/O */ count_t avque; /* average queue length */ count_t ndisc; /* number of discards (-1 = unavailable)*/ count_t ndsect; /* number of sectors discarded */ count_t cfuture[2]; /* reserved for future use */ }; struct dskstat_27 { int ndsk; /* number of physical disks */ int nmdd; /* number of md volumes */ int nlvm; /* number of logical volumes */ struct perdsk_27 dsk[MAXDSK_27]; struct perdsk_27 mdd[MAXMDD_27]; struct perdsk_27 lvm[MAXLVM_27]; }; /************************************************************************/ struct perintf_27 { char name[16]; /* empty string for last */ count_t rbyte; /* number of read bytes */ count_t rpack; /* number of read packets */ count_t rerrs; /* receive errors */ count_t rdrop; /* receive drops */ count_t rfifo; /* receive fifo */ count_t rframe; /* receive framing errors */ count_t rcompr; /* receive compressed */ count_t rmultic;/* receive multicast */ count_t rfuture[4]; /* reserved for future use */ count_t sbyte; /* number of written bytes */ count_t spack; /* number of written packets */ count_t serrs; /* transmit errors */ count_t sdrop; /* transmit drops */ count_t sfifo; /* transmit fifo */ count_t scollis;/* collisions */ count_t scarrier;/* transmit carrier */ count_t scompr; /* transmit compressed */ count_t sfuture[4]; /* reserved for future use */ char type; /* interface type ('e'/'w'/'v'/'?') */ long speed; /* interface speed in megabits/second */ long speedp; /* previous interface speed */ char duplex; /* full duplex (boolean) */ count_t cfuture[4]; /* reserved for future use */ }; struct intfstat_27 { int nrintf; struct perintf_27 intf[MAXINTF_27]; }; /************************************************************************/ struct pernfsmount_27 { char mountdev[128]; /* mountdevice */ count_t age; /* number of seconds mounted */ count_t bytesread; /* via normal reads */ count_t byteswrite; /* via normal writes */ count_t bytesdread; /* via direct reads */ count_t bytesdwrite; /* via direct writes */ count_t bytestotread; /* via reads */ count_t bytestotwrite; /* via writes */ count_t pagesmread; /* via mmap reads */ count_t pagesmwrite; /* via mmap writes */ count_t future[8]; }; struct nfsstat_27 { struct { count_t netcnt; count_t netudpcnt; count_t nettcpcnt; count_t nettcpcon; count_t rpccnt; count_t rpcbadfmt; count_t rpcbadaut; count_t rpcbadcln; count_t rpcread; count_t rpcwrite; count_t rchits; /* repcache hits */ count_t rcmiss; /* repcache misses */ count_t rcnoca; /* uncached requests */ count_t nrbytes; /* read bytes */ count_t nwbytes; /* written bytes */ count_t future[8]; } server; struct { count_t rpccnt; count_t rpcretrans; count_t rpcautrefresh; count_t rpcread; count_t rpcwrite; count_t future[8]; } client; struct { int nrmounts; struct pernfsmount_27 nfsmnt[MAXNFSMOUNT_27]; } nfsmounts; }; /************************************************************************/ struct psi_27 { float avg10; // average pressure last 10 seconds float avg60; // average pressure last 60 seconds float avg300; // average pressure last 300 seconds count_t total; // total number of milliseconds }; struct pressure_27 { char present; /* pressure stats supported? */ char future[3]; struct psi_27 cpusome; /* pressure stall info 'some' */ struct psi_27 memsome; /* pressure stall info 'some' */ struct psi_27 memfull; /* pressure stall info 'full' */ struct psi_27 iosome; /* pressure stall info 'some' */ struct psi_27 iofull; /* pressure stall info 'full' */ }; /************************************************************************/ struct percontainer_27 { unsigned long ctid; /* container id */ unsigned long numproc; /* number of processes */ count_t system; /* */ count_t user; /* */ count_t nice; /* */ count_t uptime; /* */ count_t physpages; /* */ }; struct contstat_27 { int nrcontainer; struct percontainer_27 cont[MAXCONTAINER_27]; }; /************************************************************************/ /* ** experimental stuff for access to local HTTP daemons */ #define HTTPREQ "GET /server-status?auto HTTP/1.1\nHost: localhost\n\n" struct wwwstat_27 { count_t accesses; /* total number of HTTP-requests */ count_t totkbytes; /* total kbytes transfer for HTTP-req */ count_t uptime; /* number of seconds since startup */ int bworkers; /* number of busy httpd-daemons */ int iworkers; /* number of idle httpd-daemons */ }; #if HTTPSTATS int getwwwstat_27(unsigned short, struct wwwstat_27 *); #endif /************************************************************************/ struct pergpu_27 { char taskstats; // GPU task statistics supported? unsigned char nrprocs; // number of processes using GPU char type[MAXGPUTYPE_27+1]; // GPU type char busid[MAXGPUBUS_27+1]; // GPU bus identification int gpunr; // GPU number int gpupercnow; // processor percentage last second // -1 if not supported int mempercnow; // memory percentage last second // -1 if not supported count_t memtotnow; // total memory in KiB count_t memusenow; // used memory in KiB count_t samples; // number of samples count_t gpuperccum; // cumulative processor busy percentage // -1 if not supported count_t memperccum; // cumulative memory percentage // -1 if not supported count_t memusecum; // cumulative used memory in KiB }; struct gpustat_27 { int nrgpus; // total number of GPUs struct pergpu_27 gpu[MAXGPU_27]; }; /************************************************************************/ struct perifb_27 { char ibname[MAXIBNAME_27]; // InfiniBand controller short portnr; // InfiniBand controller port short lanes; // number of lanes (traffic factor) count_t rate; // transfer rate in megabits/sec count_t rcvb; // bytes received count_t sndb; // bytes transmitted count_t rcvp; // packets received count_t sndp; // packets transmitted }; struct ifbstat_27 { int nrports; // total number of IB ports struct perifb_27 ifb[MAXIBPORT_27]; }; /************************************************************************/ struct sstat_27 { struct cpustat_27 cpu; struct memstat_27 mem; struct netstat_27 net; struct intfstat_27 intf; struct memnuma_27 memnuma; struct cpunuma_27 cpunuma; struct dskstat_27 dsk; struct nfsstat_27 nfs; struct contstat_27 cfs; struct pressure_27 psi; struct gpustat_27 gpu; struct ifbstat_27 ifb; struct wwwstat_27 www; }; atop-2.12.1/prev/photosyst_208.h0000644000203100020310000003332315064552761015651 0ustar gerlofgerlof#define MAXCPU_28 2048 #define MAXDSK_28 1024 #define MAXNUMA_28 1024 #define MAXLVM_28 2048 #define MAXMDD_28 256 #define MAXINTF_28 128 #define MAXCONTAINER_28 128 #define MAXNFSMOUNT_28 64 #define MAXIBPORT_28 32 #define MAXGPU_28 32 #define MAXGPUBUS_28 12 #define MAXGPUTYPE_28 12 #define MAXLLC_28 256 #define MAXDKNAM_28 32 #define MAXIBNAME_28 12 /************************************************************************/ struct memstat_28 { count_t physmem; // number of physical pages count_t freemem; // number of free pages count_t buffermem; // number of buffer pages count_t slabmem; // number of slab pages count_t cachemem; // number of cache pages count_t cachedrt; // number of cache pages (dirty) count_t totswap; // number of pages in swap count_t freeswap; // number of free swap pages count_t pgscans; // number of page scans count_t pgsteal; // number of page steals count_t allocstall; // try to free pages forced count_t swouts; // number of pages swapped out count_t swins; // number of pages swapped in count_t tcpsock; // number of pages allocated by TCP sockets count_t udpsock; // number of pages allocated by UDP sockets count_t commitlim; // commit limit in pages count_t committed; // number of reserved pages count_t shmem; // tot shmem incl. tmpfs (pages) count_t shmrss; // resident shared memory (pages) count_t shmswp; // swapped shared memory (pages) count_t slabreclaim; // reclaimable slab (pages) count_t tothugepage; // total huge pages (huge pages) count_t freehugepage; // free huge pages (huge pages) count_t hugepagesz; // huge page size (bytes) count_t vmwballoon; // vmware claimed balloon pages count_t zfsarcsize; // zfsonlinux ARC size (pages) count_t swapcached; // swap cache (pages) count_t ksmsharing; // saved i.e. deduped memory (pages) count_t ksmshared; // current size shared pages (pages) count_t zswstored; // zswap stored pages (pages) count_t zswtotpool; // total pool size (pages) count_t oomkills; // number of oom killings count_t compactstall; // counter for process stalls count_t pgmigrate; // counter for migrated successfully (pages) count_t numamigrate; // counter for numa migrated (pages) count_t pgouts; // total number of pages written to block device count_t pgins; // total number of pages read from block device count_t pagetables; // page tables of processes (pages) count_t cfuture[4]; // reserved for future use }; /************************************************************************/ struct mempernuma_28 { int numanr; float frag; // fragmentation level for this numa count_t totmem; // number of physical pages for this numa count_t freemem; // number of free pages for this numa count_t filepage; // number of file pages for this numa count_t dirtymem; // number of cache pages (dirty) for this numa count_t slabmem; // number of slab pages for this numa count_t slabreclaim; // reclaimable slab (pages) for this numa count_t active; // number of pages used more recently for this numa count_t inactive; // number of pages less recently used for this numa count_t shmem; // tot shmem incl. tmpfs (pages) for this numa count_t tothp; // total huge pages (huge pages) for this numa }; struct memnuma_28 { count_t nrnuma; /* the counts of numa */ struct mempernuma_28 numa[MAXNUMA_28]; }; struct cpupernuma_28 { int numanr; count_t nrcpu; // number of cpu's count_t stime; // accumulate system time in clock ticks for per numa count_t utime; // accumulate user time in clock ticks for per numa count_t ntime; // accumulate nice time in clock ticks for per numa count_t itime; // accumulate idle time in clock ticks for per numa count_t wtime; // accumulate iowait time in clock ticks for per numa count_t Itime; // accumulate irq time in clock ticks for per numa count_t Stime; // accumulate softirq time in clock ticks for per numa count_t steal; // accumulate steal time in clock ticks for per numa count_t guest; // accumulate guest time in clock ticks for per numa }; struct cpunuma_28 { count_t nrnuma; /* the counts of numa */ struct cpupernuma_28 numa[MAXNUMA_28]; }; /************************************************************************/ struct netstat_28 { struct ipv4_stats ipv4; struct icmpv4_stats icmpv4; struct udpv4_stats udpv4; struct ipv6_stats ipv6; struct icmpv6_stats icmpv6; struct udpv6_stats udpv6; struct tcp_stats_without_InCsumErrors tcp; }; /************************************************************************/ struct freqcnt_28 { count_t maxfreq;/* frequency in MHz */ count_t cnt; /* number of clock ticks times state */ count_t ticks; /* number of total clock ticks */ /* if zero, cnt is actual freq */ }; struct percpu_28 { int cpunr; count_t stime; /* system time in clock ticks */ count_t utime; /* user time in clock ticks */ count_t ntime; /* nice time in clock ticks */ count_t itime; /* idle time in clock ticks */ count_t wtime; /* iowait time in clock ticks */ count_t Itime; /* irq time in clock ticks */ count_t Stime; /* softirq time in clock ticks */ count_t steal; /* steal time in clock ticks */ count_t guest; /* guest time in clock ticks */ struct freqcnt_28 freqcnt;/* frequency scaling info */ count_t instr; /* CPU instructions */ count_t cycle; /* CPU cycles */ count_t cfuture[6]; /* reserved for future use */ }; struct cpustat_28 { count_t nrcpu; /* number of cpu's */ count_t devint; /* number of device interrupts */ count_t csw; /* number of context switches */ count_t nprocs; /* number of processes started */ float lavg1; /* load average last minute */ float lavg5; /* load average last 5 minutes */ float lavg15; /* load average last 15 minutes */ count_t cfuture[4]; /* reserved for future use */ struct percpu_28 all; struct percpu_28 cpu[MAXCPU_28]; }; /************************************************************************/ struct perdsk_28 { char name[MAXDKNAM_28]; /* empty string for last */ count_t nread; /* number of read transfers */ count_t nrsect; /* number of sectors read */ count_t nwrite; /* number of write transfers */ count_t nwsect; /* number of sectors written */ count_t io_ms; /* number of millisecs spent for I/O */ count_t avque; /* average queue length */ count_t ndisc; /* number of discards (-1 = unavailable)*/ count_t ndsect; /* number of sectors discarded */ count_t inflight; /* number of inflight I/O */ count_t cfuture[3]; /* reserved for future use */ }; struct dskstat_28 { int ndsk; /* number of physical disks */ int nmdd; /* number of md volumes */ int nlvm; /* number of logical volumes */ struct perdsk_28 dsk[MAXDSK_28]; struct perdsk_28 mdd[MAXMDD_28]; struct perdsk_28 lvm[MAXLVM_28]; }; /************************************************************************/ struct perintf_28 { char name[16]; /* empty string for last */ count_t rbyte; /* number of read bytes */ count_t rpack; /* number of read packets */ count_t rerrs; /* receive errors */ count_t rdrop; /* receive drops */ count_t rfifo; /* receive fifo */ count_t rframe; /* receive framing errors */ count_t rcompr; /* receive compressed */ count_t rmultic;/* receive multicast */ count_t rfuture[4]; /* reserved for future use */ count_t sbyte; /* number of written bytes */ count_t spack; /* number of written packets */ count_t serrs; /* transmit errors */ count_t sdrop; /* transmit drops */ count_t sfifo; /* transmit fifo */ count_t scollis;/* collisions */ count_t scarrier;/* transmit carrier */ count_t scompr; /* transmit compressed */ count_t sfuture[4]; /* reserved for future use */ char type; /* interface type ('e'/'w'/'v'/'?') */ long speed; /* interface speed in megabits/second */ long speedp; /* previous interface speed */ char duplex; /* full duplex (boolean) */ count_t cfuture[4]; /* reserved for future use */ }; struct intfstat_28 { int nrintf; struct perintf_28 intf[MAXINTF_28]; }; /************************************************************************/ struct pernfsmount_28 { char mountdev[128]; /* mountdevice */ count_t age; /* number of seconds mounted */ count_t bytesread; /* via normal reads */ count_t byteswrite; /* via normal writes */ count_t bytesdread; /* via direct reads */ count_t bytesdwrite; /* via direct writes */ count_t bytestotread; /* via reads */ count_t bytestotwrite; /* via writes */ count_t pagesmread; /* via mmap reads */ count_t pagesmwrite; /* via mmap writes */ count_t future[8]; }; struct nfsstat_28 { struct { count_t netcnt; count_t netudpcnt; count_t nettcpcnt; count_t nettcpcon; count_t rpccnt; count_t rpcbadfmt; count_t rpcbadaut; count_t rpcbadcln; count_t rpcread; count_t rpcwrite; count_t rchits; /* repcache hits */ count_t rcmiss; /* repcache misses */ count_t rcnoca; /* uncached requests */ count_t nrbytes; /* read bytes */ count_t nwbytes; /* written bytes */ count_t future[8]; } server; struct { count_t rpccnt; count_t rpcretrans; count_t rpcautrefresh; count_t rpcread; count_t rpcwrite; count_t future[8]; } client; struct { int nrmounts; struct pernfsmount nfsmnt[MAXNFSMOUNT_28]; } nfsmounts; }; /************************************************************************/ struct psi_28 { float avg10; // average pressure last 10 seconds float avg60; // average pressure last 60 seconds float avg300; // average pressure last 300 seconds count_t total; // total number of milliseconds }; struct pressure_28 { char present; /* pressure stats supported? */ char future[3]; struct psi_28 cpusome; /* pressure stall info 'some' */ struct psi_28 memsome; /* pressure stall info 'some' */ struct psi_28 memfull; /* pressure stall info 'full' */ struct psi_28 iosome; /* pressure stall info 'some' */ struct psi_28 iofull; /* pressure stall info 'full' */ }; /************************************************************************/ struct percontainer_28 { unsigned long ctid; /* container id */ unsigned long numproc; /* number of processes */ count_t system; /* */ count_t user; /* */ count_t nice; /* */ count_t uptime; /* */ count_t physpages; /* */ }; struct contstat_28 { int nrcontainer; struct percontainer_28 cont[MAXCONTAINER_28]; }; /************************************************************************/ /* ** experimental stuff for access to local HTTP daemons */ #define HTTPREQ "GET /server-status?auto HTTP/1.1\nHost: localhost\n\n" struct wwwstat_28 { count_t accesses; /* total number of HTTP-requests */ count_t totkbytes; /* total kbytes transfer for HTTP-req */ count_t uptime; /* number of seconds since startup */ int bworkers; /* number of busy httpd-daemons */ int iworkers; /* number of idle httpd-daemons */ }; /************************************************************************/ struct pergpu_28 { char taskstats; // GPU task statistics supported? unsigned char nrprocs; // number of processes using GPU char type[MAXGPUTYPE_28+1]; // GPU type char busid[MAXGPUBUS_28+1]; // GPU bus identification int gpunr; // GPU number int gpupercnow; // processor percentage last second // -1 if not supported int mempercnow; // memory percentage last second // -1 if not supported count_t memtotnow; // total memory in KiB count_t memusenow; // used memory in KiB count_t samples; // number of samples count_t gpuperccum; // cumulative processor busy percentage // -1 if not supported count_t memperccum; // cumulative memory percentage // -1 if not supported count_t memusecum; // cumulative used memory in KiB }; struct gpustat_28 { int nrgpus; // total number of GPUs struct pergpu_28 gpu[MAXGPU_28]; }; /************************************************************************/ struct perifb_28 { char ibname[MAXIBNAME_28]; // InfiniBand controller short portnr; // InfiniBand controller port short lanes; // number of lanes (traffic factor) count_t rate; // transfer rate in megabits/sec count_t rcvb; // bytes received count_t sndb; // bytes transmitted count_t rcvp; // packets received count_t sndp; // packets transmitted }; struct ifbstat_28 { int nrports; // total number of IB ports struct perifb_28 ifb[MAXIBPORT_28]; }; /************************************************************************/ struct perllc_28 { unsigned char id; float occupancy; count_t mbm_local; count_t mbm_total; }; struct llcstat_28 { unsigned char nrllcs; // total number of LLC struct perllc_28 perllc[MAXLLC_28]; }; /************************************************************************/ struct sstat_28 { struct cpustat_28 cpu; struct memstat_28 mem; struct netstat_28 net; struct intfstat_28 intf; struct memnuma_28 memnuma; struct cpunuma_28 cpunuma; struct dskstat_28 dsk; struct nfsstat_28 nfs; struct contstat_28 cfs; struct pressure_28 psi; struct gpustat_28 gpu; struct ifbstat_28 ifb; struct llcstat_28 llc; struct wwwstat_28 www; }; atop-2.12.1/prev/photosyst_209.h0000644000203100020310000003332615064552761015655 0ustar gerlofgerlof#define MAXCPU_29 2048 #define MAXDSK_29 1024 #define MAXNUMA_29 1024 #define MAXLVM_29 2048 #define MAXMDD_29 256 #define MAXINTF_29 128 #define MAXCONTAINER_29 128 #define MAXNFSMOUNT_29 64 #define MAXIBPORT_29 32 #define MAXGPU_29 32 #define MAXGPUBUS_29 12 #define MAXGPUTYPE_29 12 #define MAXLLC_29 256 #define MAXDKNAM_29 32 #define MAXIBNAME_29 12 /************************************************************************/ struct memstat_29 { count_t physmem; // number of physical pages count_t freemem; // number of free pages count_t buffermem; // number of buffer pages count_t slabmem; // number of slab pages count_t cachemem; // number of cache pages count_t cachedrt; // number of cache pages (dirty) count_t totswap; // number of pages in swap count_t freeswap; // number of free swap pages count_t pgscans; // number of page scans count_t pgsteal; // number of page steals count_t allocstall; // try to free pages forced count_t swouts; // number of pages swapped out count_t swins; // number of pages swapped in count_t tcpsock; // number of pages allocated by TCP sockets count_t udpsock; // number of pages allocated by UDP sockets count_t commitlim; // commit limit in pages count_t committed; // number of reserved pages count_t shmem; // tot shmem incl. tmpfs (pages) count_t shmrss; // resident shared memory (pages) count_t shmswp; // swapped shared memory (pages) count_t slabreclaim; // reclaimable slab (pages) count_t tothugepage; // total huge pages (huge pages) count_t freehugepage; // free huge pages (huge pages) count_t hugepagesz; // huge page size (bytes) count_t vmwballoon; // vmware claimed balloon pages count_t zfsarcsize; // zfsonlinux ARC size (pages) count_t swapcached; // swap cache (pages) count_t ksmsharing; // saved i.e. deduped memory (pages) count_t ksmshared; // current size shared pages (pages) count_t zswstored; // zswap stored pages (pages) count_t zswtotpool; // total pool size (pages) count_t oomkills; // number of oom killings count_t compactstall; // counter for process stalls count_t pgmigrate; // counter for migrated successfully (pages) count_t numamigrate; // counter for numa migrated (pages) count_t pgouts; // total number of pages written to block device count_t pgins; // total number of pages read from block device count_t pagetables; // page tables of processes (pages) count_t cfuture[4]; // reserved for future use }; /************************************************************************/ struct mempernuma_29 { int numanr; float frag; // fragmentation level for this numa count_t totmem; // number of physical pages for this numa count_t freemem; // number of free pages for this numa count_t filepage; // number of file pages for this numa count_t dirtymem; // number of cache pages (dirty) for this numa count_t slabmem; // number of slab pages for this numa count_t slabreclaim; // reclaimable slab (pages) for this numa count_t active; // number of pages used more recently for this numa count_t inactive; // number of pages less recently used for this numa count_t shmem; // tot shmem incl. tmpfs (pages) for this numa count_t tothp; // total huge pages (huge pages) for this numa }; struct memnuma_29 { count_t nrnuma; /* the counts of numa */ struct mempernuma_29 numa[MAXNUMA_29]; }; struct cpupernuma_29 { int numanr; count_t nrcpu; // number of cpu's count_t stime; // accumulate system time in clock ticks for per numa count_t utime; // accumulate user time in clock ticks for per numa count_t ntime; // accumulate nice time in clock ticks for per numa count_t itime; // accumulate idle time in clock ticks for per numa count_t wtime; // accumulate iowait time in clock ticks for per numa count_t Itime; // accumulate irq time in clock ticks for per numa count_t Stime; // accumulate softirq time in clock ticks for per numa count_t steal; // accumulate steal time in clock ticks for per numa count_t guest; // accumulate guest time in clock ticks for per numa }; struct cpunuma_29 { count_t nrnuma; /* the counts of numa */ struct cpupernuma_29 numa[MAXNUMA_29]; }; /************************************************************************/ struct netstat_29 { struct ipv4_stats ipv4; struct icmpv4_stats icmpv4; struct udpv4_stats udpv4; struct ipv6_stats ipv6; struct icmpv6_stats icmpv6; struct udpv6_stats udpv6; struct tcp_stats_without_InCsumErrors tcp; }; /************************************************************************/ struct freqcnt_29 { count_t maxfreq;/* frequency in MHz */ count_t cnt; /* number of clock ticks times state */ count_t ticks; /* number of total clock ticks */ /* if zero, cnt is actual freq */ }; struct percpu_29 { int cpunr; count_t stime; /* system time in clock ticks */ count_t utime; /* user time in clock ticks */ count_t ntime; /* nice time in clock ticks */ count_t itime; /* idle time in clock ticks */ count_t wtime; /* iowait time in clock ticks */ count_t Itime; /* irq time in clock ticks */ count_t Stime; /* softirq time in clock ticks */ count_t steal; /* steal time in clock ticks */ count_t guest; /* guest time in clock ticks */ struct freqcnt_29 freqcnt;/* frequency scaling info */ count_t instr; /* CPU instructions */ count_t cycle; /* CPU cycles */ count_t cfuture[6]; /* reserved for future use */ }; struct cpustat_29 { count_t nrcpu; /* number of cpu's */ count_t devint; /* number of device interrupts */ count_t csw; /* number of context switches */ count_t nprocs; /* number of processes started */ float lavg1; /* load average last minute */ float lavg5; /* load average last 5 minutes */ float lavg15; /* load average last 15 minutes */ count_t cfuture[4]; /* reserved for future use */ struct percpu_29 all; struct percpu_29 cpu[MAXCPU_29]; }; /************************************************************************/ struct perdsk_29 { char name[MAXDKNAM_29]; /* empty string for last */ count_t nread; /* number of read transfers */ count_t nrsect; /* number of sectors read */ count_t nwrite; /* number of write transfers */ count_t nwsect; /* number of sectors written */ count_t io_ms; /* number of millisecs spent for I/O */ count_t avque; /* average queue length */ count_t ndisc; /* number of discards (-1 = unavailable)*/ count_t ndsect; /* number of sectors discarded */ count_t inflight; /* number of inflight I/O */ count_t cfuture[3]; /* reserved for future use */ }; struct dskstat_29 { int ndsk; /* number of physical disks */ int nmdd; /* number of md volumes */ int nlvm; /* number of logical volumes */ struct perdsk_29 dsk[MAXDSK_29]; struct perdsk_29 mdd[MAXMDD_29]; struct perdsk_29 lvm[MAXLVM_29]; }; /************************************************************************/ struct perintf_29 { char name[16]; /* empty string for last */ count_t rbyte; /* number of read bytes */ count_t rpack; /* number of read packets */ count_t rerrs; /* receive errors */ count_t rdrop; /* receive drops */ count_t rfifo; /* receive fifo */ count_t rframe; /* receive framing errors */ count_t rcompr; /* receive compressed */ count_t rmultic;/* receive multicast */ count_t rfuture[4]; /* reserved for future use */ count_t sbyte; /* number of written bytes */ count_t spack; /* number of written packets */ count_t serrs; /* transmit errors */ count_t sdrop; /* transmit drops */ count_t sfifo; /* transmit fifo */ count_t scollis;/* collisions */ count_t scarrier;/* transmit carrier */ count_t scompr; /* transmit compressed */ count_t sfuture[4]; /* reserved for future use */ char type; /* interface type ('e'/'w'/'v'/'?') */ long speed; /* interface speed in megabits/second */ long speedp; /* previous interface speed */ char duplex; /* full duplex (boolean) */ count_t cfuture[4]; /* reserved for future use */ }; struct intfstat_29 { int nrintf; struct perintf_29 intf[MAXINTF_29]; }; /************************************************************************/ struct pernfsmount_29 { char mountdev[128]; /* mountdevice */ count_t age; /* number of seconds mounted */ count_t bytesread; /* via normal reads */ count_t byteswrite; /* via normal writes */ count_t bytesdread; /* via direct reads */ count_t bytesdwrite; /* via direct writes */ count_t bytestotread; /* via reads */ count_t bytestotwrite; /* via writes */ count_t pagesmread; /* via mmap reads */ count_t pagesmwrite; /* via mmap writes */ count_t future[8]; }; struct nfsstat_29 { struct { count_t netcnt; count_t netudpcnt; count_t nettcpcnt; count_t nettcpcon; count_t rpccnt; count_t rpcbadfmt; count_t rpcbadaut; count_t rpcbadcln; count_t rpcread; count_t rpcwrite; count_t rchits; /* repcache hits */ count_t rcmiss; /* repcache misses */ count_t rcnoca; /* uncached requests */ count_t nrbytes; /* read bytes */ count_t nwbytes; /* written bytes */ count_t future[8]; } server; struct { count_t rpccnt; count_t rpcretrans; count_t rpcautrefresh; count_t rpcread; count_t rpcwrite; count_t future[8]; } client; struct { int nrmounts; struct pernfsmount_29 nfsmnt[MAXNFSMOUNT_29]; } nfsmounts; }; /************************************************************************/ struct psi_29 { float avg10; // average pressure last 10 seconds float avg60; // average pressure last 60 seconds float avg300; // average pressure last 300 seconds count_t total; // total number of milliseconds }; struct pressure_29 { char present; /* pressure stats supported? */ char future[3]; struct psi_29 cpusome; /* pressure stall info 'some' */ struct psi_29 memsome; /* pressure stall info 'some' */ struct psi_29 memfull; /* pressure stall info 'full' */ struct psi_29 iosome; /* pressure stall info 'some' */ struct psi_29 iofull; /* pressure stall info 'full' */ }; /************************************************************************/ struct percontainer_29 { unsigned long ctid; /* container id */ unsigned long numproc; /* number of processes */ count_t system; /* */ count_t user; /* */ count_t nice; /* */ count_t uptime; /* */ count_t physpages; /* */ }; struct contstat_29 { int nrcontainer; struct percontainer_29 cont[MAXCONTAINER_29]; }; /************************************************************************/ /* ** experimental stuff for access to local HTTP daemons */ #define HTTPREQ "GET /server-status?auto HTTP/1.1\nHost: localhost\n\n" struct wwwstat_29 { count_t accesses; /* total number of HTTP-requests */ count_t totkbytes; /* total kbytes transfer for HTTP-req */ count_t uptime; /* number of seconds since startup */ int bworkers; /* number of busy httpd-daemons */ int iworkers; /* number of idle httpd-daemons */ }; /************************************************************************/ struct pergpu_29 { char taskstats; // GPU task statistics supported? unsigned char nrprocs; // number of processes using GPU char type[MAXGPUTYPE_29+1]; // GPU type char busid[MAXGPUBUS_29+1]; // GPU bus identification int gpunr; // GPU number int gpupercnow; // processor percentage last second // -1 if not supported int mempercnow; // memory percentage last second // -1 if not supported count_t memtotnow; // total memory in KiB count_t memusenow; // used memory in KiB count_t samples; // number of samples count_t gpuperccum; // cumulative processor busy percentage // -1 if not supported count_t memperccum; // cumulative memory percentage // -1 if not supported count_t memusecum; // cumulative used memory in KiB }; struct gpustat_29 { int nrgpus; // total number of GPUs struct pergpu_29 gpu[MAXGPU_29]; }; /************************************************************************/ struct perifb_29 { char ibname[MAXIBNAME_29]; // InfiniBand controller short portnr; // InfiniBand controller port short lanes; // number of lanes (traffic factor) count_t rate; // transfer rate in megabits/sec count_t rcvb; // bytes received count_t sndb; // bytes transmitted count_t rcvp; // packets received count_t sndp; // packets transmitted }; struct ifbstat_29 { int nrports; // total number of IB ports struct perifb_29 ifb[MAXIBPORT_29]; }; /************************************************************************/ struct perllc_29 { unsigned char id; float occupancy; count_t mbm_local; count_t mbm_total; }; struct llcstat_29 { unsigned char nrllcs; // total number of LLC struct perllc_29 perllc[MAXLLC_29]; }; /************************************************************************/ struct sstat_29 { struct cpustat_29 cpu; struct memstat_29 mem; struct netstat_29 net; struct intfstat_29 intf; struct memnuma_29 memnuma; struct cpunuma_29 cpunuma; struct dskstat_29 dsk; struct nfsstat_29 nfs; struct contstat_29 cfs; struct pressure_29 psi; struct gpustat_29 gpu; struct ifbstat_29 ifb; struct llcstat_29 llc; struct wwwstat_29 www; }; atop-2.12.1/prev/photosyst_210.h0000644000203100020310000003452115064552761015643 0ustar gerlofgerlof#define MAXCPU_210 2048 #define MAXDSK_210 1024 #define MAXNUMA_210 1024 #define MAXLVM_210 2048 #define MAXMDD_210 256 #define MAXINTF_210 128 #define MAXCONTAINER_210 128 #define MAXNFSMOUNT_210 64 #define MAXIBPORT_210 32 #define MAXGPU_210 32 #define MAXGPUBUS_210 12 #define MAXGPUTYPE_210 12 #define MAXLLC_210 256 #define MAXDKNAM_210 32 #define MAXIBNAME_210 12 /************************************************************************/ struct memstat_210 { count_t physmem; // number of physical pages count_t freemem; // number of free pages count_t buffermem; // number of buffer pages count_t slabmem; // number of slab pages count_t cachemem; // number of cache pages count_t cachedrt; // number of cache pages (dirty) count_t totswap; // number of pages in swap count_t freeswap; // number of free swap pages count_t pgscans; // number of page scans count_t pgsteal; // number of page steals count_t allocstall; // try to free pages forced count_t swouts; // number of pages swapped out count_t swins; // number of pages swapped in count_t tcpsock; // number of pages allocated by TCP sockets count_t udpsock; // number of pages allocated by UDP sockets count_t commitlim; // commit limit in pages count_t committed; // number of reserved pages count_t shmem; // tot shmem incl. tmpfs (pages) count_t shmrss; // resident shared memory (pages) count_t shmswp; // swapped shared memory (pages) count_t slabreclaim; // reclaimable slab (pages) count_t stothugepage; // total huge pages (huge pages) - small count_t sfreehugepage; // free huge pages (huge pages) - small count_t shugepagesz; // huge page size (bytes) - small count_t vmwballoon; // vmware claimed balloon pages count_t zfsarcsize; // zfsonlinux ARC size (pages) count_t swapcached; // swap cache (pages) count_t ksmsharing; // saved i.e. deduped memory (pages) count_t ksmshared; // current size shared pages (pages) count_t zswapped; // zswap stored pages decompressed (pages) count_t zswap; // zswap current pool size compressed (pages) count_t oomkills; // number of oom killings count_t compactstall; // counter for process stalls count_t pgmigrate; // counter for migrated successfully (pages) count_t numamigrate; // counter for numa migrated (pages) count_t pgouts; // total number of pages written to block device count_t pgins; // total number of pages read from block device count_t pagetables; // page tables of processes (pages) count_t zswouts; // number of pages swapped out to zswap count_t zswins; // number of pages swapped in from zswap count_t ltothugepage; // total huge pages (huge pages) - large count_t lfreehugepage; // free huge pages (huge pages) - large count_t lhugepagesz; // huge page size (bytes) - large count_t availablemem; // available memory (pages) count_t anonhugepage; // anonymous transparent huge pages // (in units of 'normal' pages) count_t cfuture[4]; // reserved for future use }; /************************************************************************/ struct mempernuma_210 { int numanr; float frag; // fragmentation level for this numa count_t totmem; // number of physical pages for this numa count_t freemem; // number of free pages for this numa count_t filepage; // number of file pages for this numa count_t dirtymem; // number of cache pages (dirty) for this numa count_t slabmem; // number of slab pages for this numa count_t slabreclaim; // reclaimable slab (pages) for this numa count_t active; // number of pages used more recently for this numa count_t inactive; // number of pages less recently used for this numa count_t shmem; // tot shmem incl. tmpfs (pages) for this numa count_t tothp; // total huge pages (huge pages) for this numa count_t freehp; // total free pages (huge pages) for this numa }; struct memnuma_210 { count_t nrnuma; /* the counts of numa */ struct mempernuma_210 numa[MAXNUMA_210]; }; struct cpupernuma_210 { int numanr; count_t nrcpu; // number of cpu's count_t stime; // accumulate system time in clock ticks for per numa count_t utime; // accumulate user time in clock ticks for per numa count_t ntime; // accumulate nice time in clock ticks for per numa count_t itime; // accumulate idle time in clock ticks for per numa count_t wtime; // accumulate iowait time in clock ticks for per numa count_t Itime; // accumulate irq time in clock ticks for per numa count_t Stime; // accumulate softirq time in clock ticks for per numa count_t steal; // accumulate steal time in clock ticks for per numa count_t guest; // accumulate guest time in clock ticks for per numa }; struct cpunuma_210 { count_t nrnuma; /* the counts of numa */ struct cpupernuma_210 numa[MAXNUMA_210]; }; /************************************************************************/ struct netstat_210 { struct ipv4_stats ipv4; struct icmpv4_stats icmpv4; struct udpv4_stats udpv4; struct ipv6_stats ipv6; struct icmpv6_stats icmpv6; struct udpv6_stats udpv6; struct tcp_stats tcp; }; /************************************************************************/ struct freqcnt_210 { count_t maxfreq;/* frequency in MHz */ count_t cnt; /* number of clock ticks times state */ count_t ticks; /* number of total clock ticks */ /* if zero, cnt is actual freq */ }; struct percpu_210 { int cpunr; count_t stime; /* system time in clock ticks */ count_t utime; /* user time in clock ticks */ count_t ntime; /* nice time in clock ticks */ count_t itime; /* idle time in clock ticks */ count_t wtime; /* iowait time in clock ticks */ count_t Itime; /* irq time in clock ticks */ count_t Stime; /* softirq time in clock ticks */ count_t steal; /* steal time in clock ticks */ count_t guest; /* guest time in clock ticks */ struct freqcnt_210 freqcnt;/* frequency scaling info */ count_t instr; /* CPU instructions */ count_t cycle; /* CPU cycles */ count_t cfuture[6]; /* reserved for future use */ }; struct cpustat_210 { count_t nrcpu; /* number of cpu's */ count_t devint; /* number of device interrupts */ count_t csw; /* number of context switches */ count_t nprocs; /* number of processes started */ float lavg1; /* load average last minute */ float lavg5; /* load average last 5 minutes */ float lavg15; /* load average last 15 minutes */ count_t cfuture[4]; /* reserved for future use */ struct percpu_210 all; struct percpu_210 cpu[MAXCPU_210]; }; /************************************************************************/ struct perdsk_210 { char name[MAXDKNAM_210]; /* empty string for last */ count_t nread; /* number of read transfers */ count_t nrsect; /* number of sectors read */ count_t nwrite; /* number of write transfers */ count_t nwsect; /* number of sectors written */ count_t io_ms; /* number of millisecs spent for I/O */ count_t avque; /* average queue length */ count_t ndisc; /* number of discards (-1 = unavailable)*/ count_t ndsect; /* number of sectors discarded */ count_t inflight; /* number of inflight I/O */ count_t cfuture[3]; /* reserved for future use */ }; struct dskstat_210 { int ndsk; /* number of physical disks */ int nmdd; /* number of md volumes */ int nlvm; /* number of logical volumes */ struct perdsk_210 dsk[MAXDSK_210]; struct perdsk_210 mdd[MAXMDD_210]; struct perdsk_210 lvm[MAXLVM_210]; }; /************************************************************************/ struct perintf_210 { char name[16]; /* empty string for last */ count_t rbyte; /* number of read bytes */ count_t rpack; /* number of read packets */ count_t rerrs; /* receive errors */ count_t rdrop; /* receive drops */ count_t rfifo; /* receive fifo */ count_t rframe; /* receive framing errors */ count_t rcompr; /* receive compressed */ count_t rmultic;/* receive multicast */ count_t rfuture[4]; /* reserved for future use */ count_t sbyte; /* number of written bytes */ count_t spack; /* number of written packets */ count_t serrs; /* transmit errors */ count_t sdrop; /* transmit drops */ count_t sfifo; /* transmit fifo */ count_t scollis;/* collisions */ count_t scarrier;/* transmit carrier */ count_t scompr; /* transmit compressed */ count_t sfuture[4]; /* reserved for future use */ char type; /* interface type ('e'/'w'/'v'/'?') */ long speed; /* interface speed in megabits/second */ long speedp; /* previous interface speed */ char duplex; /* full duplex (boolean) */ count_t cfuture[4]; /* reserved for future use */ }; struct intfstat_210 { int nrintf; struct perintf_210 intf[MAXINTF_210]; }; /************************************************************************/ struct pernfsmount_210 { char mountdev[128]; /* mountdevice */ count_t age; /* number of seconds mounted */ count_t bytesread; /* via normal reads */ count_t byteswrite; /* via normal writes */ count_t bytesdread; /* via direct reads */ count_t bytesdwrite; /* via direct writes */ count_t bytestotread; /* via reads */ count_t bytestotwrite; /* via writes */ count_t pagesmread; /* via mmap reads */ count_t pagesmwrite; /* via mmap writes */ count_t future[8]; }; struct nfsstat_210 { struct { count_t netcnt; count_t netudpcnt; count_t nettcpcnt; count_t nettcpcon; count_t rpccnt; count_t rpcbadfmt; count_t rpcbadaut; count_t rpcbadcln; count_t rpcread; count_t rpcwrite; count_t rchits; /* repcache hits */ count_t rcmiss; /* repcache misses */ count_t rcnoca; /* uncached requests */ count_t nrbytes; /* read bytes */ count_t nwbytes; /* written bytes */ count_t future[8]; } server; struct { count_t rpccnt; count_t rpcretrans; count_t rpcautrefresh; count_t rpcread; count_t rpcwrite; count_t future[8]; } client; struct { int nrmounts; struct pernfsmount_210 nfsmnt[MAXNFSMOUNT_210]; } nfsmounts; }; /************************************************************************/ struct psi_210 { float avg10; // average pressure last 10 seconds float avg60; // average pressure last 60 seconds float avg300; // average pressure last 300 seconds count_t total; // total number of milliseconds }; struct pressure_210 { char present; /* pressure stats supported? */ char future[3]; struct psi_210 cpusome; /* pressure stall info 'some' */ struct psi_210 memsome; /* pressure stall info 'some' */ struct psi_210 memfull; /* pressure stall info 'full' */ struct psi_210 iosome; /* pressure stall info 'some' */ struct psi_210 iofull; /* pressure stall info 'full' */ }; /************************************************************************/ struct percontainer_210 { unsigned long ctid; /* container id */ unsigned long numproc; /* number of processes */ count_t system; /* */ count_t user; /* */ count_t nice; /* */ count_t uptime; /* */ count_t physpages; /* */ }; struct contstat_210 { int nrcontainer; struct percontainer_210 cont[MAXCONTAINER_210]; }; /************************************************************************/ /* ** experimental stuff for access to local HTTP daemons */ #define HTTPREQ "GET /server-status?auto HTTP/1.1\nHost: localhost\n\n" struct wwwstat_210 { count_t accesses; /* total number of HTTP-requests */ count_t totkbytes; /* total kbytes transfer for HTTP-req */ count_t uptime; /* number of seconds since startup */ int bworkers; /* number of busy httpd-daemons */ int iworkers; /* number of idle httpd-daemons */ }; /************************************************************************/ struct pergpu_210 { char taskstats; // GPU task statistics supported? unsigned char nrprocs; // number of processes using GPU char type[MAXGPUTYPE_210+1]; // GPU type char busid[MAXGPUBUS_210+1]; // GPU bus identification int gpunr; // GPU number int gpupercnow; // processor percentage last second // -1 if not supported int mempercnow; // memory percentage last second // -1 if not supported count_t memtotnow; // total memory in KiB count_t memusenow; // used memory in KiB count_t samples; // number of samples count_t gpuperccum; // cumulative processor busy percentage // -1 if not supported count_t memperccum; // cumulative memory percentage // -1 if not supported count_t memusecum; // cumulative used memory in KiB }; struct gpustat_210 { int nrgpus; // total number of GPUs struct pergpu_210 gpu[MAXGPU_210]; }; /************************************************************************/ struct perifb_210 { char ibname[MAXIBNAME_210]; // InfiniBand controller short portnr; // InfiniBand controller port short lanes; // number of lanes (traffic factor) count_t rate; // transfer rate in megabits/sec count_t rcvb; // bytes received count_t sndb; // bytes transmitted count_t rcvp; // packets received count_t sndp; // packets transmitted }; struct ifbstat_210 { int nrports; // total number of IB ports struct perifb_210 ifb[MAXIBPORT_210]; }; /************************************************************************/ struct perllc_210 { unsigned char id; float occupancy; count_t mbm_local; count_t mbm_total; }; struct llcstat_210 { int nrllcs; // total number of LLC struct perllc_210 perllc[MAXLLC_210]; }; /************************************************************************/ struct sstat_210 { struct cpustat_210 cpu; struct memstat_210 mem; struct netstat_210 net; struct intfstat_210 intf; struct memnuma_210 memnuma; struct cpunuma_210 cpunuma; struct dskstat_210 dsk; struct nfsstat_210 nfs; struct contstat_210 cfs; struct pressure_210 psi; struct gpustat_210 gpu; struct ifbstat_210 ifb; struct llcstat_210 llc; struct wwwstat_210 www; }; atop-2.12.1/prev/photosyst_211.h0000644000203100020310000003474215064552761015651 0ustar gerlofgerlof#define MAXCPU_211 2048 #define MAXDSK_211 1024 #define MAXNUMA_211 1024 #define MAXLVM_211 2048 #define MAXMDD_211 256 #define MAXINTF_211 128 #define MAXCONTAINER_211 128 #define MAXNFSMOUNT_211 64 #define MAXIBPORT_211 32 #define MAXGPU_211 32 #define MAXGPUBUS_211 12 #define MAXGPUTYPE_211 12 #define MAXLLC_211 256 #define MAXDKNAM_211 32 #define MAXIBNAME_211 12 /************************************************************************/ struct memstat_211 { count_t physmem; // number of physical pages count_t freemem; // number of free pages count_t buffermem; // number of buffer pages count_t slabmem; // number of slab pages count_t cachemem; // number of cache pages count_t cachedrt; // number of cache pages (dirty) count_t totswap; // number of pages in swap count_t freeswap; // number of free swap pages count_t pgscans; // number of page scans count_t pgsteal; // number of page steals count_t allocstall; // try to free pages forced count_t swouts; // number of pages swapped out count_t swins; // number of pages swapped in count_t tcpsock; // number of pages allocated by TCP sockets count_t udpsock; // number of pages allocated by UDP sockets count_t commitlim; // commit limit in pages count_t committed; // number of reserved pages count_t shmem; // tot shmem incl. tmpfs (pages) count_t shmrss; // resident shared memory (pages) count_t shmswp; // swapped shared memory (pages) count_t slabreclaim; // reclaimable slab (pages) count_t stothugepage; // total huge pages (huge pages) - small count_t sfreehugepage; // free huge pages (huge pages) - small count_t shugepagesz; // huge page size (bytes) - small count_t vmwballoon; // vmware claimed balloon pages count_t zfsarcsize; // zfsonlinux ARC size (pages) count_t swapcached; // swap cache (pages) count_t ksmsharing; // saved i.e. deduped memory (pages) count_t ksmshared; // current size shared pages (pages) count_t zswapped; // zswap stored pages decompressed (pages) count_t zswap; // zswap current pool size compressed (pages) count_t oomkills; // number of oom killings count_t compactstall; // counter for process stalls count_t pgmigrate; // counter for migrated successfully (pages) count_t numamigrate; // counter for numa migrated (pages) count_t pgouts; // total number of pages written to block device count_t pgins; // total number of pages read from block device count_t pagetables; // page tables of processes (pages) count_t zswouts; // number of pages swapped out to zswap count_t zswins; // number of pages swapped in from zswap count_t ltothugepage; // total huge pages (huge pages) - large count_t lfreehugepage; // free huge pages (huge pages) - large count_t lhugepagesz; // huge page size (bytes) - large count_t availablemem; // available memory (pages) count_t anonhugepage; // anonymous transparent huge pages // (in units of 'normal' pages) count_t cfuture[5]; // reserved for future use }; /************************************************************************/ struct mempernuma_211 { int numanr; float frag; // fragmentation level for this numa count_t totmem; // number of physical pages for this numa count_t freemem; // number of free pages for this numa count_t filepage; // number of file pages for this numa count_t dirtymem; // number of cache pages (dirty) for this numa count_t slabmem; // number of slab pages for this numa count_t slabreclaim; // reclaimable slab (pages) for this numa count_t active; // number of pages used more recently for this numa count_t inactive; // number of pages less recently used for this numa count_t shmem; // tot shmem incl. tmpfs (pages) for this numa count_t tothp; // total huge pages (huge pages) for this numa count_t freehp; // total free pages (huge pages) for this numa count_t cfuture[2]; // reserved for future use }; struct memnuma_211 { count_t nrnuma; /* the counts of numa */ struct mempernuma_211 numa[MAXNUMA_211]; }; struct cpupernuma_211 { int numanr; count_t nrcpu; // number of cpu's count_t stime; // accumulate system time in clock ticks for per numa count_t utime; // accumulate user time in clock ticks for per numa count_t ntime; // accumulate nice time in clock ticks for per numa count_t itime; // accumulate idle time in clock ticks for per numa count_t wtime; // accumulate iowait time in clock ticks for per numa count_t Itime; // accumulate irq time in clock ticks for per numa count_t Stime; // accumulate softirq time in clock ticks for per numa count_t steal; // accumulate steal time in clock ticks for per numa count_t guest; // accumulate guest time in clock ticks for per numa count_t cfuture[2]; // reserved for future use }; struct cpunuma_211 { count_t nrnuma; /* the counts of numa */ struct cpupernuma_211 numa[MAXNUMA_211]; }; /************************************************************************/ struct netstat_211 { struct ipv4_stats ipv4; struct icmpv4_stats icmpv4; struct udpv4_stats udpv4; struct ipv6_stats ipv6; struct icmpv6_stats icmpv6; struct udpv6_stats udpv6; struct tcp_stats tcp; }; /************************************************************************/ struct freqcnt_211 { count_t maxfreq;/* frequency in MHz */ count_t cnt; /* number of clock ticks times state */ count_t ticks; /* number of total clock ticks */ /* if zero, cnt is actual freq */ }; struct percpu_211 { int cpunr; count_t stime; /* system time in clock ticks */ count_t utime; /* user time in clock ticks */ count_t ntime; /* nice time in clock ticks */ count_t itime; /* idle time in clock ticks */ count_t wtime; /* iowait time in clock ticks */ count_t Itime; /* irq time in clock ticks */ count_t Stime; /* softirq time in clock ticks */ count_t steal; /* steal time in clock ticks */ count_t guest; /* guest time in clock ticks */ struct freqcnt_211 freqcnt;/* frequency scaling info */ count_t instr; /* CPU instructions */ count_t cycle; /* CPU cycles */ count_t cfuture[6]; /* reserved for future use */ }; struct cpustat_211 { count_t nrcpu; /* number of cpu's */ count_t devint; /* number of device interrupts */ count_t csw; /* number of context switches */ count_t nprocs; /* number of processes started */ float lavg1; /* load average last minute */ float lavg5; /* load average last 5 minutes */ float lavg15; /* load average last 15 minutes */ count_t cfuture[4]; /* reserved for future use */ struct percpu_211 all; struct percpu_211 cpu[MAXCPU_211]; }; /************************************************************************/ struct perdsk_211 { char name[MAXDKNAM_211]; /* empty string for last */ count_t nread; /* number of read transfers */ count_t nrsect; /* number of sectors read */ count_t nwrite; /* number of write transfers */ count_t nwsect; /* number of sectors written */ count_t io_ms; /* number of millisecs spent for I/O */ count_t avque; /* average queue length */ count_t ndisc; /* number of discards (-1 = unavailable)*/ count_t ndsect; /* number of sectors discarded */ count_t inflight; /* number of inflight I/O */ count_t cfuture[3]; /* reserved for future use */ }; struct dskstat_211 { int ndsk; /* number of physical disks */ int nmdd; /* number of md volumes */ int nlvm; /* number of logical volumes */ struct perdsk_211 dsk[MAXDSK_211]; struct perdsk_211 mdd[MAXMDD_211]; struct perdsk_211 lvm[MAXLVM_211]; }; /************************************************************************/ struct perintf_211 { char name[16]; /* empty string for last */ count_t rbyte; /* number of read bytes */ count_t rpack; /* number of read packets */ count_t rerrs; /* receive errors */ count_t rdrop; /* receive drops */ count_t rfifo; /* receive fifo */ count_t rframe; /* receive framing errors */ count_t rcompr; /* receive compressed */ count_t rmultic;/* receive multicast */ count_t rfuture[4]; /* reserved for future use */ count_t sbyte; /* number of written bytes */ count_t spack; /* number of written packets */ count_t serrs; /* transmit errors */ count_t sdrop; /* transmit drops */ count_t sfifo; /* transmit fifo */ count_t scollis;/* collisions */ count_t scarrier;/* transmit carrier */ count_t scompr; /* transmit compressed */ count_t sfuture[4]; /* reserved for future use */ char type; /* interface type ('e'/'w'/'v'/'?') */ long speed; /* interface speed in megabits/second */ long speedp; /* previous interface speed */ char duplex; /* full duplex (boolean) */ count_t cfuture[4]; /* reserved for future use */ }; struct intfstat_211 { int nrintf; struct perintf_211 intf[MAXINTF_211]; }; /************************************************************************/ struct pernfsmount_211 { char mountdev[128]; /* mountdevice */ count_t age; /* number of seconds mounted */ count_t bytesread; /* via normal reads */ count_t byteswrite; /* via normal writes */ count_t bytesdread; /* via direct reads */ count_t bytesdwrite; /* via direct writes */ count_t bytestotread; /* via reads */ count_t bytestotwrite; /* via writes */ count_t pagesmread; /* via mmap reads */ count_t pagesmwrite; /* via mmap writes */ count_t future[8]; }; struct nfsstat_211 { struct { count_t netcnt; count_t netudpcnt; count_t nettcpcnt; count_t nettcpcon; count_t rpccnt; count_t rpcbadfmt; count_t rpcbadaut; count_t rpcbadcln; count_t rpcread; count_t rpcwrite; count_t rchits; /* repcache hits */ count_t rcmiss; /* repcache misses */ count_t rcnoca; /* uncached requests */ count_t nrbytes; /* read bytes */ count_t nwbytes; /* written bytes */ count_t future[8]; } server; struct { count_t rpccnt; count_t rpcretrans; count_t rpcautrefresh; count_t rpcread; count_t rpcwrite; count_t future[8]; } client; struct { int nrmounts; struct pernfsmount_211 nfsmnt[MAXNFSMOUNT_211]; } nfsmounts; }; /************************************************************************/ struct psi_211 { float avg10; // average pressure last 10 seconds float avg60; // average pressure last 60 seconds float avg300; // average pressure last 300 seconds count_t total; // total number of milliseconds }; struct pressure_211 { char present; /* pressure stats supported? */ char future[3]; struct psi_211 cpusome; /* pressure stall info 'some' */ struct psi_211 memsome; /* pressure stall info 'some' */ struct psi_211 memfull; /* pressure stall info 'full' */ struct psi_211 iosome; /* pressure stall info 'some' */ struct psi_211 iofull; /* pressure stall info 'full' */ }; /************************************************************************/ struct percontainer_211 { unsigned long ctid; /* container id */ unsigned long numproc; /* number of processes */ count_t system; /* */ count_t user; /* */ count_t nice; /* */ count_t uptime; /* */ count_t physpages; /* */ }; struct contstat_211 { int nrcontainer; struct percontainer_211 cont[MAXCONTAINER_211]; }; /************************************************************************/ /* ** experimental stuff for access to local HTTP daemons */ #define HTTPREQ "GET /server-status?auto HTTP/1.1\nHost: localhost\n\n" struct wwwstat_211 { count_t accesses; /* total number of HTTP-requests */ count_t totkbytes; /* total kbytes transfer for HTTP-req */ count_t uptime; /* number of seconds since startup */ int bworkers; /* number of busy httpd-daemons */ int iworkers; /* number of idle httpd-daemons */ }; /************************************************************************/ struct pergpu_211 { char taskstats; // GPU task statistics supported? unsigned char nrprocs; // number of processes using GPU char type[MAXGPUTYPE_211+1]; // GPU type char busid[MAXGPUBUS_211+1]; // GPU bus identification int gpunr; // GPU number int gpupercnow; // processor percentage last second // -1 if not supported int mempercnow; // memory percentage last second // -1 if not supported count_t memtotnow; // total memory in KiB count_t memusenow; // used memory in KiB count_t samples; // number of samples count_t gpuperccum; // cumulative processor busy percentage // -1 if not supported count_t memperccum; // cumulative memory percentage // -1 if not supported count_t memusecum; // cumulative used memory in KiB }; struct gpustat_211 { int nrgpus; // total number of GPUs struct pergpu_211 gpu[MAXGPU_211]; }; /************************************************************************/ struct perifb_211 { char ibname[MAXIBNAME_211]; // InfiniBand controller short portnr; // InfiniBand controller port short lanes; // number of lanes (traffic factor) count_t rate; // transfer rate in megabits/sec count_t rcvb; // bytes received count_t sndb; // bytes transmitted count_t rcvp; // packets received count_t sndp; // packets transmitted count_t cfuture[4]; // reserved for future use }; struct ifbstat_211 { int nrports; // total number of IB ports struct perifb_211 ifb[MAXIBPORT_211]; }; /************************************************************************/ struct perllc_211 { unsigned char id; float occupancy; count_t mbm_local; count_t mbm_total; }; struct llcstat_211 { int nrllcs; // total number of LLC struct perllc_211 perllc[MAXLLC_211]; }; /************************************************************************/ struct sstat_211 { struct cpustat_211 cpu; struct memstat_211 mem; struct netstat_211 net; struct intfstat_211 intf; struct memnuma_211 memnuma; struct cpunuma_211 cpunuma; struct dskstat_211 dsk; struct nfsstat_211 nfs; struct contstat_211 cfs; struct pressure_211 psi; struct gpustat_211 gpu; struct ifbstat_211 ifb; struct llcstat_211 llc; struct wwwstat_211 www; }; atop-2.12.1/prev/cgroups_212.h0000644000203100020310000000426215064552761015252 0ustar gerlofgerlof// structure containing general info and metrics per cgroup (directory) // struct cstat_212 { // GENERAL INFO struct cggen_212 { int structlen; // struct length including rounded name int sequence; // sequence number in chain/array int parentseq; // parent sequence number in chain/array int depth; // cgroup tree depth starting from 0 int nprocs; // number of processes in cgroup int procsbelow; // number of processes in cgroups below int namelen; // cgroup name length (at end of struct) int fullnamelen; // cgroup path length int ifuture[4]; long namehash; // cgroup name hash of // full path name excluding slashes long lfuture[4]; } gen; // CONFIGURATION INFO struct cgconf_212 { int cpuweight; // -1=max, -2=undefined int cpumax; // -1=max, -2=undefined (perc) count_t memmax; // -1=max, -2=undefined (pages) count_t swpmax; // -1=max, -2=undefined (pages) int dskweight; // -1=max, -2=undefined int ifuture[5]; count_t cfuture[5]; } conf; // CPU STATISTICS struct cgcpu_212 { count_t utime; // time user text (usec) -1=undefined count_t stime; // time system text (usec) -1=undefined count_t somepres; // some pressure (microsec) count_t fullpres; // full pressure (microsec) count_t cfuture[5]; } cpu; // MEMORY STATISTICS struct cgmem_212 { count_t current; // current memory (pages) -1=undefined count_t anon; // anonymous memory (pages) -1=undefined count_t file; // file memory (pages) -1=undefined count_t kernel; // kernel memory (pages) -1=undefined count_t shmem; // shared memory (pages) -1=undefined count_t somepres; // some pressure (microsec) count_t fullpres; // full pressure (microsec) count_t cfuture[5]; } mem; // DISK I/O STATISTICS struct cgdsk_212 { count_t rbytes; // total bytes read on all physical disks count_t wbytes; // total bytes written on all physical disks count_t rios; // total read I/Os on all physical disks count_t wios; // total write I/Os on all physical disks count_t somepres; // some pressure (microsec) count_t fullpres; // full pressure (microsec) count_t cfuture[5]; } dsk; // cgroup name with variable length char cgname[]; }; atop-2.12.1/prev/photoproc_212.h0000644000203100020310000001134015064552761015600 0ustar gerlofgerlof/* ** structure containing only relevant process-info extracted ** from kernel's process-administration */ struct tstat_212 { /* GENERAL TASK INFO */ struct gen_212 { int tgid; /* threadgroup identification */ int pid; /* process identification */ int ppid; /* parent process identification*/ int ruid; /* real user identification */ int euid; /* eff. user identification */ int suid; /* saved user identification */ int fsuid; /* fs user identification */ int rgid; /* real group identification */ int egid; /* eff. group identification */ int sgid; /* saved group identification */ int fsgid; /* fs group identification */ int nthr; /* number of threads in tgroup */ char name[PNAMLEN+1];/* process name string */ char isproc; /* boolean: process level? */ char state; /* process state ('E' = exited) */ int excode; /* process exit status */ time_t btime; /* process start time (epoch) */ time_t elaps; /* process elaps time (hertz) */ char cmdline[CMDLEN+1];/* command-line string */ int nthrslpi; /* # threads in state 'S' */ int nthrslpu; /* # threads in state 'D' */ int nthrrun; /* # threads in state 'R' */ int nthridle; /* # threads in state 'I' */ int ctid; /* OpenVZ container ID */ int vpid; /* OpenVZ virtual PID */ int wasinactive; /* boolean: task inactive */ char utsname[UTSLEN+1];/* UTS name container or pod */ int cgroupix; /* index in devchain -1=invalid */ /* lazy filling (parsable/json) */ int ifuture[4]; /* reserved for future use */ } gen; /* CPU STATISTICS */ struct cpu_212 { count_t utime; /* time user text (ticks) */ count_t stime; /* time system text (ticks) */ int nice; /* nice value */ int prio; /* priority */ int rtprio; /* realtime priority */ int policy; /* scheduling policy */ int curcpu; /* current processor */ int sleepavg; /* sleep average percentage */ int ifuture[6]; /* reserved for future use */ char wchan[16]; /* wait channel string */ count_t rundelay; /* schedstat rundelay (nanosec) */ count_t blkdelay; /* blkio delay (ticks) */ count_t nvcsw; /* voluntary cxt switch counts */ count_t nivcsw; /* involuntary csw counts */ count_t cfuture[3]; /* reserved for future use */ } cpu; /* DISK STATISTICS */ struct dsk_212 { count_t rio; /* number of read requests */ count_t rsz; /* cumulative # sectors read */ count_t wio; /* number of write requests */ count_t wsz; /* cumulative # sectors written */ count_t cwsz; /* cumulative # written sectors */ /* being cancelled */ count_t cfuture[4]; /* reserved for future use */ } dsk; /* MEMORY STATISTICS */ struct mem_212 { count_t minflt; /* number of page-reclaims */ count_t majflt; /* number of page-faults */ count_t vexec; /* virtmem execfile (Kb) */ count_t vmem; /* virtual memory (Kb) */ count_t rmem; /* resident memory (Kb) */ count_t pmem; /* resident memory (Kb) */ count_t vgrow; /* virtual growth (Kb) */ count_t rgrow; /* resident growth (Kb) */ count_t vdata; /* virtmem data (Kb) */ count_t vstack; /* virtmem stack (Kb) */ count_t vlibs; /* virtmem libexec (Kb) */ count_t vswap; /* swap space used (Kb) */ count_t vlock; /* virtual locked (Kb) */ count_t cfuture[7]; /* reserved for future use */ } mem; /* NETWORK STATISTICS */ struct net_212 { count_t tcpsnd; /* number of TCP-packets sent */ count_t tcpssz; /* cumulative size packets sent */ count_t tcprcv; /* number of TCP-packets recved */ count_t tcprsz; /* cumulative size packets rcvd */ count_t udpsnd; /* number of UDP-packets sent */ count_t udpssz; /* cumulative size packets sent */ count_t udprcv; /* number of UDP-packets recved */ count_t udprsz; /* cumulative size packets sent */ count_t avail1; /* */ count_t avail2; /* */ count_t cfuture[4]; /* reserved for future use */ } net; struct gpu_212 { char state; // A - active, E - Exit, '\0' - no use char bfuture[3]; // short nrgpus; // number of GPUs for this process int32_t gpulist; // bitlist with GPU numbers int gpubusy; // gpu busy perc process lifetime -1 = n/a int membusy; // memory busy perc process lifetime -1 = n/a count_t timems; // milliseconds accounting -1 = n/a // value 0 for active process, // value > 0 after termination count_t memnow; // current memory consumption in KiB count_t memcum; // cumulative memory consumption in KiB count_t sample; // number of samples count_t cfuture[3]; // } gpu; }; atop-2.12.1/prev/photosyst_212.h0000644000203100020310000003473615064552761015655 0ustar gerlofgerlof#define MAXCPU_212 2048 #define MAXDSK_212 1024 #define MAXNUMA_212 1024 #define MAXLVM_212 2048 #define MAXMDD_212 256 #define MAXINTF_212 128 #define MAXCONTAINER_212 128 #define MAXNFSMOUNT_212 64 #define MAXIBPORT_212 32 #define MAXGPU_212 32 #define MAXGPUBUS_212 12 #define MAXGPUTYPE_212 12 #define MAXLLC_212 256 #define MAXDKNAM_212 32 #define MAXIBNAME_212 12 /************************************************************************/ struct memstat_212 { count_t physmem; // number of physical pages count_t freemem; // number of free pages count_t buffermem; // number of buffer pages count_t slabmem; // number of slab pages count_t cachemem; // number of cache pages count_t cachedrt; // number of cache pages (dirty) count_t totswap; // number of pages in swap count_t freeswap; // number of free swap pages count_t pgscans; // number of page scans count_t pgsteal; // number of page steals count_t allocstall; // try to free pages forced count_t swouts; // number of pages swapped out count_t swins; // number of pages swapped in count_t tcpsock; // number of pages allocated by TCP sockets count_t udpsock; // number of pages allocated by UDP sockets count_t commitlim; // commit limit in pages count_t committed; // number of reserved pages count_t shmem; // tot shmem incl. tmpfs (pages) count_t shmrss; // resident shared memory (pages) count_t shmswp; // swapped shared memory (pages) count_t slabreclaim; // reclaimable slab (pages) count_t stothugepage; // total huge pages (huge pages) - small count_t sfreehugepage; // free huge pages (huge pages) - small count_t shugepagesz; // huge page size (bytes) - small count_t vmwballoon; // vmware claimed balloon pages count_t zfsarcsize; // zfsonlinux ARC size (pages) count_t swapcached; // swap cache (pages) count_t ksmsharing; // saved i.e. deduped memory (pages) count_t ksmshared; // current size shared pages (pages) count_t zswapped; // zswap stored pages decompressed (pages) count_t zswap; // zswap current pool size compressed (pages) count_t oomkills; // number of oom killings count_t compactstall; // counter for process stalls count_t pgmigrate; // counter for migrated successfully (pages) count_t numamigrate; // counter for numa migrated (pages) count_t pgouts; // total number of pages written to block device count_t pgins; // total number of pages read from block device count_t pagetables; // page tables of processes (pages) count_t zswouts; // number of pages swapped out to zswap count_t zswins; // number of pages swapped in from zswap count_t ltothugepage; // total huge pages (huge pages) - large count_t lfreehugepage; // free huge pages (huge pages) - large count_t lhugepagesz; // huge page size (bytes) - large count_t availablemem; // available memory (pages) count_t anonhugepage; // anonymous transparent huge pages // (in units of 'normal' pages) count_t cfuture[5]; // reserved for future use }; /************************************************************************/ struct mempernuma_212 { int numanr; float frag; // fragmentation level for this numa count_t totmem; // number of physical pages for this numa count_t freemem; // number of free pages for this numa count_t filepage; // number of file pages for this numa count_t dirtymem; // number of cache pages (dirty) for this numa count_t slabmem; // number of slab pages for this numa count_t slabreclaim; // reclaimable slab (pages) for this numa count_t active; // number of pages used more recently for this numa count_t inactive; // number of pages less recently used for this numa count_t shmem; // tot shmem incl. tmpfs (pages) for this numa count_t tothp; // total huge pages (huge pages) for this numa count_t freehp; // total free pages (huge pages) for this numa count_t cfuture[2]; // reserved for future use }; struct memnuma_212 { count_t nrnuma; /* the counts of numa */ struct mempernuma_212 numa[MAXNUMA_212]; }; struct cpupernuma_212 { int numanr; count_t nrcpu; // number of cpu's count_t stime; // accumulate system time in clock ticks for per numa count_t utime; // accumulate user time in clock ticks for per numa count_t ntime; // accumulate nice time in clock ticks for per numa count_t itime; // accumulate idle time in clock ticks for per numa count_t wtime; // accumulate iowait time in clock ticks for per numa count_t Itime; // accumulate irq time in clock ticks for per numa count_t Stime; // accumulate softirq time in clock ticks for per numa count_t steal; // accumulate steal time in clock ticks for per numa count_t guest; // accumulate guest time in clock ticks for per numa count_t cfuture[2]; // reserved for future use }; struct cpunuma_212 { count_t nrnuma; /* the counts of numa */ struct cpupernuma_212 numa[MAXNUMA_212]; }; /************************************************************************/ struct netstat_212 { struct ipv4_stats ipv4; struct icmpv4_stats icmpv4; struct udpv4_stats udpv4; struct ipv6_stats ipv6; struct icmpv6_stats icmpv6; struct udpv6_stats udpv6; struct tcp_stats tcp; }; /************************************************************************/ struct freqcnt_212 { count_t maxfreq;/* frequency in MHz */ count_t cnt; /* number of clock ticks times state */ count_t ticks; /* number of total clock ticks */ /* if zero, cnt is actual freq */ }; struct percpu_212 { int cpunr; count_t stime; /* system time in clock ticks */ count_t utime; /* user time in clock ticks */ count_t ntime; /* nice time in clock ticks */ count_t itime; /* idle time in clock ticks */ count_t wtime; /* iowait time in clock ticks */ count_t Itime; /* irq time in clock ticks */ count_t Stime; /* softirq time in clock ticks */ count_t steal; /* steal time in clock ticks */ count_t guest; /* guest time in clock ticks */ struct freqcnt_212 freqcnt;/* frequency scaling info */ count_t instr; /* CPU instructions */ count_t cycle; /* CPU cycles */ count_t cfuture[6]; /* reserved for future use */ }; struct cpustat_212 { count_t nrcpu; /* number of cpu's */ count_t devint; /* number of device interrupts */ count_t csw; /* number of context switches */ count_t nprocs; /* number of processes started */ float lavg1; /* load average last minute */ float lavg5; /* load average last 5 minutes */ float lavg15; /* load average last 15 minutes */ count_t cfuture[4]; /* reserved for future use */ struct percpu_212 all; struct percpu_212 cpu[MAXCPU_212]; }; /************************************************************************/ struct perdsk_212 { char name[MAXDKNAM_212]; /* empty string for last */ count_t nread; /* number of read transfers */ count_t nrsect; /* number of sectors read */ count_t nwrite; /* number of write transfers */ count_t nwsect; /* number of sectors written */ count_t io_ms; /* number of millisecs spent for I/O */ count_t avque; /* average queue length */ count_t ndisc; /* number of discards (-1 = unavailable)*/ count_t ndsect; /* number of sectors discarded */ count_t inflight; /* number of inflight I/O */ count_t cfuture[3]; /* reserved for future use */ }; struct dskstat_212 { int ndsk; /* number of physical disks */ int nmdd; /* number of md volumes */ int nlvm; /* number of logical volumes */ struct perdsk_212 dsk[MAXDSK_212]; struct perdsk_212 mdd[MAXMDD_212]; struct perdsk_212 lvm[MAXLVM_212]; }; /************************************************************************/ struct perintf_212 { char name[16]; /* empty string for last */ count_t rbyte; /* number of read bytes */ count_t rpack; /* number of read packets */ count_t rerrs; /* receive errors */ count_t rdrop; /* receive drops */ count_t rfifo; /* receive fifo */ count_t rframe; /* receive framing errors */ count_t rcompr; /* receive compressed */ count_t rmultic;/* receive multicast */ count_t rfuture[4]; /* reserved for future use */ count_t sbyte; /* number of written bytes */ count_t spack; /* number of written packets */ count_t serrs; /* transmit errors */ count_t sdrop; /* transmit drops */ count_t sfifo; /* transmit fifo */ count_t scollis;/* collisions */ count_t scarrier;/* transmit carrier */ count_t scompr; /* transmit compressed */ count_t sfuture[4]; /* reserved for future use */ char type; /* interface type ('e'/'w'/'v'/'?') */ long speed; /* interface speed in megabits/second */ long speedp; /* previous interface speed */ char duplex; /* full duplex (boolean) */ count_t cfuture[4]; /* reserved for future use */ }; struct intfstat_212 { int nrintf; struct perintf_212 intf[MAXINTF_212]; }; /************************************************************************/ struct pernfsmount_212 { char mountdev[128]; /* mountdevice */ count_t age; /* number of seconds mounted */ count_t bytesread; /* via normal reads */ count_t byteswrite; /* via normal writes */ count_t bytesdread; /* via direct reads */ count_t bytesdwrite; /* via direct writes */ count_t bytestotread; /* via reads */ count_t bytestotwrite; /* via writes */ count_t pagesmread; /* via mmap reads */ count_t pagesmwrite; /* via mmap writes */ count_t future[8]; }; struct nfsstat_212 { struct { count_t netcnt; count_t netudpcnt; count_t nettcpcnt; count_t nettcpcon; count_t rpccnt; count_t rpcbadfmt; count_t rpcbadaut; count_t rpcbadcln; count_t rpcread; count_t rpcwrite; count_t rchits; /* repcache hits */ count_t rcmiss; /* repcache misses */ count_t rcnoca; /* uncached requests */ count_t nrbytes; /* read bytes */ count_t nwbytes; /* written bytes */ count_t future[8]; } server; struct { count_t rpccnt; count_t rpcretrans; count_t rpcautrefresh; count_t rpcread; count_t rpcwrite; count_t future[8]; } client; struct { int nrmounts; struct pernfsmount nfsmnt[MAXNFSMOUNT_212]; } nfsmounts; }; /************************************************************************/ struct psi_212 { float avg10; // average pressure last 10 seconds float avg60; // average pressure last 60 seconds float avg300; // average pressure last 300 seconds count_t total; // total number of milliseconds }; struct pressure_212 { char present; /* pressure stats supported? */ char future[3]; struct psi_212 cpusome; /* pressure stall info 'some' */ struct psi_212 memsome; /* pressure stall info 'some' */ struct psi_212 memfull; /* pressure stall info 'full' */ struct psi_212 iosome; /* pressure stall info 'some' */ struct psi_212 iofull; /* pressure stall info 'full' */ }; /************************************************************************/ struct percontainer_212 { unsigned long ctid; /* container id */ unsigned long numproc; /* number of processes */ count_t system; /* */ count_t user; /* */ count_t nice; /* */ count_t uptime; /* */ count_t physpages; /* */ }; struct contstat_212 { int nrcontainer; struct percontainer_212 cont[MAXCONTAINER_212]; }; /************************************************************************/ /* ** experimental stuff for access to local HTTP daemons */ #define HTTPREQ "GET /server-status?auto HTTP/1.1\nHost: localhost\n\n" struct wwwstat_212 { count_t accesses; /* total number of HTTP-requests */ count_t totkbytes; /* total kbytes transfer for HTTP-req */ count_t uptime; /* number of seconds since startup */ int bworkers; /* number of busy httpd-daemons */ int iworkers; /* number of idle httpd-daemons */ }; /************************************************************************/ struct pergpu_212 { char taskstats; // GPU task statistics supported? unsigned char nrprocs; // number of processes using GPU char type[MAXGPUTYPE_212+1]; // GPU type char busid[MAXGPUBUS_212+1]; // GPU bus identification int gpunr; // GPU number int gpupercnow; // processor percentage last second // -1 if not supported int mempercnow; // memory percentage last second // -1 if not supported count_t memtotnow; // total memory in KiB count_t memusenow; // used memory in KiB count_t samples; // number of samples count_t gpuperccum; // cumulative processor busy percentage // -1 if not supported count_t memperccum; // cumulative memory percentage // -1 if not supported count_t memusecum; // cumulative used memory in KiB }; struct gpustat_212 { int nrgpus; // total number of GPUs struct pergpu_212 gpu[MAXGPU_212]; }; /************************************************************************/ struct perifb_212 { char ibname[MAXIBNAME_212]; // InfiniBand controller short portnr; // InfiniBand controller port short lanes; // number of lanes (traffic factor) count_t rate; // transfer rate in megabits/sec count_t rcvb; // bytes received count_t sndb; // bytes transmitted count_t rcvp; // packets received count_t sndp; // packets transmitted count_t cfuture[4]; // reserved for future use }; struct ifbstat_212 { int nrports; // total number of IB ports struct perifb_212 ifb[MAXIBPORT_212]; }; /************************************************************************/ struct perllc_212 { unsigned char id; float occupancy; count_t mbm_local; count_t mbm_total; }; struct llcstat_212 { int nrllcs; // total number of LLC struct perllc_212 perllc[MAXLLC_212]; }; /************************************************************************/ struct sstat_212 { struct cpustat_212 cpu; struct memstat_212 mem; struct netstat_212 net; struct intfstat_212 intf; struct memnuma_212 memnuma; struct cpunuma_212 cpunuma; struct dskstat_212 dsk; struct nfsstat_212 nfs; struct contstat_212 cfs; struct pressure_212 psi; struct gpustat_212 gpu; struct ifbstat_212 ifb; struct llcstat_212 llc; struct wwwstat_212 www; }; atop-2.12.1/man/0000755000203100020310000000000015064552761012626 5ustar gerlofgerlofatop-2.12.1/man/atopacctd.80000644000203100020310000001046515064552761014667 0ustar gerlofgerlof.TH ATOPACCTD 8 "January 2024" "Linux" .SH NAME .B atopacctd - process accounting daemon .SH SYNOPSIS .P .B atopacctd [-v | topdirectory] .P .SH DESCRIPTION The .I atopacctd daemon switches on the process accounting feature in the kernel and let the process accounting records be written to a file, called the source file from now. After process accounting is activated, the .I atopacctd daemon transfers every process accounting record that is available in the source file to a shadow file. Client processes (like .I atop processes) will read the shadow files instead of the process accounting source file. .br In this way, the .I atopacctd daemon operates as a 'layer' between the process accounting file that is written by the kernel and the shadow accounting files that are read by .I atop processes. .PP This approach has the following advantages: .PP .TP 3 .B o The .I atopacctd daemon takes care that the source file is kept to a limited size. As soon as its maximum size is reached, it is truncated to a size of zero again (this is not noticed by the .I atop processes). .PP .TP 3 .B o The .I atopacct daemon takes care that a shadow file is kept to a limited size. As soon as the current shadow file reaches this maximum size, the .I atopacctd daemon creates a new (subsequent) shadow file. While client processes still have the possibility to read the previous shadow file(s), the .I atopacctd daemon continues writing accounting records to the newest (current) shadow file. For this reason, the name of a shadow file consists of a 10-digit sequence number followed by the extension '.paf' (process acounting file). Old shadow files that are not used by client processes any more, are automatically removed by the garbage collector in the .I atopacctd daemon. .PP .TP 3 .B o When no client processes are active (any more), all shadow files will be deleted and no records will be transferred to a shadow file any more. As soon as at least one client is activate again, the .I atopacctd daemon continues writing shadow files. .PP The directory .B /var/run is used as the default topdirectory. Below this top-directory, the source file .B pacct_source is created to which the kernel writes the process accounting records. .br Furthermore, the subdirectory .B pacct_shadow.d is created as a 'container' for the shadow files. Apart from the shadow files, also the file .B current is maintained in this subdirectory, containing the sequence number of the current (newest) shadow file and the maximum number of records that will be written in each shadow file. .PP An alternative topdirectory can be specified as command line argument. When an alternative topdirectory is defined, also modify the configuration file .B /etc/atoprc to inform .I atop clients about this alternative location (see the .B atoprc man page). Such alternative topdirectory should be owned by root and may not be writable for the group or others (security reasons). .PP Notice that the kernel suspends writing process accounting records when the free space of the filesystem on which the process accounting file resides drops below 2%. Writing is resumed when the free space is 4% or more. These lowwater and highwater percentages can be configured via the .B /proc/sys/kernel/acct pseudo-file. .br The .I atopacctd daemon suspends transferring process accounting records to shadow files when the free space of the filesystem on which the process accounting file resides drops below 5%. Transfer is resumed when the free space is 5% or more. Log messages are generated via syslog when writing to the current shadow file is suspended or resumed. .PP The .B -v flag can be used to verify the version of the .I atopacctd daemon. .PP .SH FILES .PP .TP 5 .B /var/run/pacct_source Regular file to which the kernel writes the process accounting records. This file will be regularly truncated. .PP .TP 5 .B /var/run/pacct_shadow.d/current Regular file containing the sequence number of the current shadow file and the maximum number of records per shadow file. .PP .TP 5 .B /var/run/pacct_shadow.d/N.paf Regular files containing the process accounting records that have been copied transparently from the source file (N represents a 10-digit sequence number). .SH SEE ALSO .B atop(1), .B atopsar(1), .B atoprc(5), .B netatop(4), .B netatopd(8) .br .B https://www.atoptool.nl .SH AUTHOR Gerlof Langeveld (gerlof.langeveld@atoptool.nl) atop-2.12.1/man/atopcat.10000644000203100020310000000442215064552761014345 0ustar gerlofgerlof.TH ATOPCAT 1 "January 2024" "Linux" .SH NAME .B atopcat - concatenate raw log files to stdout .SH SYNOPSIS .P .B atopcat [-dv] rawfile [rawfile]... .P .SH DESCRIPTION The program .I atopcat can be used to concatenate several raw log files into one stream (stdout). In this way, raw log files can be merged into one larger file by redirecting stdout to a file. Alternatively, merged data from several raw log files can be transferred directly into .I atop or .I atopsar via a pipe. Options: .PP .TP 5 .B -d dry-run: read logfile(s) but do not generate output on stdout .PP .TP 5 .B -v verbose: print one line per sample containing date/time, interval length in seconds, compressed length of the system-level information, compressed length of the process-level information, compressed length of the cgroup-level information, and compressed length of the PID list. .SH EXAMPLES Concatenate the raw log files of five contiguous working days, write it into a new raw log file for that week and view that week interactively: .PP .TP 12 .B \ atopcat /var/log/atop/atop_2024021[0-4] > week_2024_7 .TP 12 .B \ atop -r week_2024_7 .PP Concatenate the raw log files of a week and view that week interactively (since .I atop reads from a pipe, previous intervals can not be retrieved while viewing): .PP .TP 12 .B \ atopcat /var/log/atop/atop_2024021[0-6] | atop -r - .PP Concatenate all raw log files of January 2024 and generate parsable output about the CPU utilization: .PP .TP 12 .B \ atopcat /var/log/atop/atop_202401?? | atop -r - -PCPU .PP Concatenate the daily raw log files of February 3 and 4, and generate a report about memory utilization from 14:00h on the first day till 11:00h on the second day: .PP .TP 12 .B \ atopcat /var/log/atop/atop_2024020[34] | .B \ atopsar -m -r - -b 202402031400 -e 202402041100 .PP Repair a raw log file from which the last interval has not been completely written (e.g. if you intend to expand the file with new samples): .PP .TP 12 .B \ atopcat /var/log/atop/atop_20240303 > /tmp/repaired .PP In the latter case, .B atopcat reports that the input file is incomplete and stops after the last consistent sample. .SH SEE ALSO .B atop(1), .B atopsar(1), .B atophide(1), .B atopconvert(1) .br .B https://www.atoptool.nl .SH AUTHOR Gerlof Langeveld (gerlof.langeveld@atoptool.nl) atop-2.12.1/man/atopconvert.10000644000203100020310000000255515064552761015263 0ustar gerlofgerlof.TH ATOPCONVERT 1 "January 2024" "Linux" .SH NAME .B atopconvert - convert raw log file to newer version .SH SYNOPSIS .P .B atopconvert [\-t .I version ] rawinput [rawoutput] .P .SH DESCRIPTION The program .I atopconvert can be used to convert the layout of a raw log file to a newer version. The only mandatory argument is the name of the raw input file. When no output file is specified on the command line, .I atopconvert only shows the version of the input file. When the name of an output file is specified, the input file will be converted to the output file, or just copied when the input file already has the required version. The program .I atopconvert converts the input file (by default) to the format used by the newest version of .I atop and writes to the output file. With the .B -t option, an alternative target version can be specified for the output file (instead of the newest version) in the format .I major.minor (example: 2.3). .br After the conversion is finished, the output file can be read by the newer version of .I atop or can even be extended by that .I atop version. .SH NOTES The raw input file should be at least of version 2.0! Files can only be upgraded to higher version, but not downgraded. .SH SEE ALSO .B atop(1), .B atopsar(1), .B atopcat(1), .B atophide(1) .br .B https://www.atoptool.nl .SH AUTHOR Gerlof Langeveld (gerlof.langeveld@atoptool.nl) atop-2.12.1/man/atop.10000644000203100020310000033305015064552761013657 0ustar gerlofgerlof.TH ATOP 1 "July 2024" "Linux" .SH NAME .B atop - Advanced System & Process Monitor .SH SYNOPSIS Live measurement in bar graph mode: .PP .TP 5 .B \ atop \-B[H] [-t [absdir]] [interval [samples]] .PP Live measurement cgroups in text mode: .PP .TP 5 .B \ atop \-G [-t [absdir]] [-2|-3|-4|-5|-6|-7|-8|-9] [\-a] [\-C|\-M|\-D|\-A] [interval [samples]] .PP Live measurement processes in text mode: .PP .TP 5 .B \ atop [-t [absdir]] [\-g|\-m|\-d|\-n|\-u|\-p|\-s|\-c|\-v|\-o|\-y|\-Y] [\-C|\-M|\-D|\-N|\-A] [\-fFX1xR] [interval [samples]] .PP Live generation of parsable output (white-space separated or JSON): .PP .TP 5 .B \ atop [\-Plabel[,label]... [-Z]] [\-Jlabel[,label]...] [interval [samples]] .PP Write raw log files: .PP .TP 5 .B \ atop \-w rawfile [\-a] [\-S] [interval [samples]] .PP Analyze raw log files in bar graph mode: .PP .TP 5 .B \ atop \-B[H] -r [rawfile|yyy...] [\-b [YYYYMMDD]hhmm[ss]] [\-e [YYYYMMDD]hhmm[ss]] .PP Analyze cgroups from raw log files in text mode: .PP .TP 5 .B \ atop \-G [-2|-3|-4|-5|-6|-7|-8|-9] [\-a] \-r [rawfile|yyy...] [\-b [YYYYMMDD]hhmm[ss]] [\-e [YYYYMMDD]hhmm[ss]] .PP Analyze processes from raw log files in text mode: .PP .TP 5 .B \ atop \-r [rawfile|yyy...] [\-b [YYYYMMDD]hhmm[ss]] [\-e [YYYYMMDD]hhmm[ss]] [\-g|\-m|\-d|\-n|\-u|\-p|\-s|\-c|\-v|\-o|\-y|\-Y] [\-C|\-M|\-D|\-N|\-A] [\-fFX1xR] .PP Generate parsable output from raw log files (white-space separated or JSON): .PP .TP 5 .B \ atop \-r [rawfile|yyy...] [\-b [YYYYMMDD]hhmm[ss]] [\-e [YYYYMMDD]hhmm[ss]] [\-Plabel[,label]... [-Z]] [\-Jlabel[,label]...] .SH DESCRIPTION The program .I atop is an interactive monitor to view the load on a Linux system. Every .I interval seconds (default: 10 seconds) information is gathered about the resource occupation on system level (CPUs, memory, disks and network interfaces) and on cgroup level (version 2). Besides, information is gathered about the processes and threads that are responsible for the utilization of the CPUs, memory and disks. Network load per process is shown only when the .I netatop kernel module or the .I netatop-bpf BPF module has been installed. .SH TWIN MODE With the .I -t flag you can run .I atop interactively in 'twin mode'. This mode allows to run a live measurement with the possibility to review and analyze an earlier sample. Meanwhile, the live measurement continues. When started in twin mode, .I atop spawns a child process that gathers the counters and writes them to a temporary raw file. The parent process reads the counters from the temporary raw file and presents them to the user. The reading of the parent process keeps in pace with the written samples of the child process for live measurements. While the gathering continues by the child process, key 'r', key 'b' or key 'T' can be pressed to review earlier samples. The parent process implicitly pauses the live measurement by pressing one of these keys or by pressing key 'z' (pause) explicitly. After browsing through the earlier samples with the keys 't' (next sample), 'T' (previous sample), 'r' (reset to begin of measurement), 'Z' (fast-forward to end of measurement) and 'b' (branch to timestamp), the live measurement can be continued by pressing key 'z' (resume after pause). The temporary raw file will be written in the .B /tmp directory by default. Optionally the absolute path name of an alternative directory can be added behind the .I -t flag (e.g. when there is not enough space in the .B /tmp directory). In any case, the parent process will terminate the child process when the measurement is finished and the temporary raw file will be removed. .SH BAR GRAPH MODE When running .I atop you can choose to view the system load in bar graph mode or in text mode. In bar graph mode the resource utilization of CPUs, memory, disks and network interfaces is shown via (character-based) bar graphs, but only on system level. When you want to view more detailed information on system level or when you want to view the resource consumption on process or thread level, you can switch to text mode by pressing the 'B' key. Alternatively, you can use the 'B' key (again) to switch from text mode to bar graph mode. .br By default, .I atop starts in text mode unless the .I -B flag is used or unless 'B' has been configured as a default flag in the .I .atoprc file (for further information about default flags, refer to the .B atoprc man page). .PP In bar graph mode the terminal will be subdivided into four character-based windows, i.e. one window for each hardware resource: .PP .TP 5 .B Processors The first bar shows the average busy percentage of all CPUs with the bar label 'Avg' (might be abbreviated to 'Av' or even just 'A'). The subsequent bars show the busy percentages of single CPUs. .br When there is not enough horizontal space to show all CPUs, only the most busy CPUs per sample will be shown after the width of each bar has been reduced to a minimum. By default, the categories of CPU consumption are shown by different colors in the bars, marked with a character 'S' (system mode), 'U' (user mode), 'I' (interrupt handling), 's' (steal) and 'G' (guest, i.e. consumed by virtual machines). .br The top of the bar might consist of an unmarked color representing a 'neutral' category. Suppose that the scale unit is 5% per line and the total busy percentage is 54% consisting of two categories of 27%. The two categories will be rounded to 25% (5 lines of 5% each) but the total busy percentage will be rounded to 55% (11 lines of 5%). Then the top line will represent a 'neutral' category. .br By pressing the 'H' key or by starting .I atop with the '-H' flag, no categories are shown. A red line is drawn in the bar graph as critical threshold. By default this value is 90% and can be modified by the 'cpucritperc' option in the configuration file (see separate .B atoprc man page). When this value is set to zero, no threshold line will be drawn. .TP 5 .B Memory and swap space Memory is presented as a column in which the specific categories of memory consumption are shown. These categories are (code, data and stack of) processes/kernel, slab caches (i.e. dynamically allocated kernel memory), shared memory, tmpfs, static huge pages, page cache and free memory. .br Swap space (if present) is also presented as a column in which the categories processes/tmpfs, shared memory and free space are shown. At the right side memory-related event counters are shown. .br The bottom three counters are colored green when there is no memory pressure. When considerable activity is noticed such counter might be colored orange and with high activity red. .br When memory pressure starts, usually memory page scanning will be activated first. When pressure increases, memory pages of processes might be swapped out to swap space (if present). .br The 'oomkills' counter (Out Of Memory killing) is most serious: it reflects the number of processes that are killed due to lack of memory (and swap). Therefore this counter shows the absolute number (not per second) of processes being killed during the last interval and will immediately be colored red when it is 1 or more. Besides, after .I atop has noticed OOM killing the 'oomkills' counter remains orange for the next 15 minutes, just in case that you have missed the OOM killing event itself. .br When there is enough vertical space in the memory window, event counters are shown about the number of memory pages being swapped in, the number of memory pages paged out to block devices and the number of memory pages paged in from block devices. Memory and swap space consumption will preferably be shown in a character-based window that vertically uses the entire screen for optimal granularity. However, when there are a lot of disks and/or network interfaces the memory and swap space consumption will be shown in a character-based window that only uses the upper half of the screen. .TP 5 .B Disks For each disk the busy percentage is shown as a bar. .br When there is not enough horizontal space to show all disks, only the most busy disks per sample will be shown. By default, categories of disk consumption are shown by different colors in the bars, marked with a character 'R' (read) and 'W' (write). .br The top of the bar might consist of an unmarked color representing a 'neutral' category. Suppose that the scale unit is 5% per line and the total busy percentage is 54% consisting of two categories of 27%. The two categories will be rounded to 25% (5 lines of 5% each) but the total busy percentage will be rounded to 55% (11 lines of 5%). Then the top line will represent a 'neutral' category. .br By pressing the 'H' key or by starting .I atop with the '-H' flag, no categories are shown. A red line is drawn in the bar graph as critical threshold. By default this value is 90% and can be modified by the 'dskcritperc' option in the configuration file (see separate .B atoprc man page). When this value is set to zero, no threshold line will be drawn. .TP 5 .B Interfaces For each non-virtual network interface a double bar graph is shown with a dedicated scale that reflects the traffic rate. One of the bars shows the transmit rate ('TX') and the other bar the receive rate ('RX'). The traffic scale of each network interface remains at its highest level. All interface scales can be reset during the measurement by pressing the 'L' key. Most often the real speed (maximum bandwidth) of network interfaces is not known, e.g. in case of the network interfaces of virtual machines. Therefore it is not possible to show the interface utilization as a percentage. However, when the real speed of an interface is known it will be shown underneath the concerning bar graph. When there is not enough horizontal space to show all network interfaces, only the most busy interfaces per sample will be shown. .PP Usually the bar graphs will not be sorted on busy percentage when there is enough horizontal space. However, after switching from text mode to bar graph mode the bar graphs might have been sorted because this was needed for the presentation in text mode. The next interval in bar graph mode shows the bars unsorted again unless the window width is unsufficient for all bars. .PP For the CPUs, disks and memory resources also a bar graph with two bars is shown reflecting the PSI values 'some' (S) and 'full' (F). The 'some' (S) percentage indicates the time in which at least some tasks were stalled on a given resource. The 'full' (F) percentage indicates the time in which all non-idle tasks were stalled on this resource simultaneously, which had severe impact on the performance. .br For some Linux distributions PSI support might be disabled by default. In that case you need to pass .B psi=1 on the kernel command line during boot. .PP The remaining part of this manual page mainly describes the information shown in text mode. When certain descriptions also apply to bar graph mode it will be mentioned explicitly. .SH TEXT MODE IN GENERAL With every interval information is shown about the resource occupation on system level (CPU, memory, disks and network layers) in the upper part of the screen. When a resource has been unused during the interval, the line is suppressed unless the 'f' key is active. In the bottom part of the screen cgroup level (key 'G') or process level information is shown (keys 'g', 'c', 's', 'm', 'd', 'n', 'v', 'u' or 'p'). By default, only cgroups are shown without assigned processes in the cgroup itself nor in the cgroups underneath, unless the 'a' key (all) is active. By default, only processes are shown that were active during the interval unless the 'a' key (all) is active. .br The intervals are repeated till the number of .I samples (specified as command argument) is reached, or till the key 'q' is pressed in interactive mode. .PP When .I atop is started, it checks whether the standard output channel is connected to a screen, or to a file/pipe. In the first case it produces screen control codes (via the ncurses library) and behaves interactively. In the second case it produces flat text output. .PP In interactive mode, the output of .I atop scales dynamically to the current dimensions of the screen/window. .br If the window is resized horizontally, columns will be added or removed automatically. For this purpose, every column has a particular weight. The columns with the highest weights that fit within the current width will be shown. .br If the window is resized vertically, lines of the process/thread list will be added or removed automatically. .PP In interactive mode the output of .I atop can be controlled by pressing particular keys. However it is also possible to specify such key as .B flag on the command line. In that case .I atop switches to the indicated mode on beforehand. This mode can be modified again interactively. Specifying such key as flag is especially useful when running .I atop with output to a pipe or file (non-interactively). These flags are the same as the keys that can be pressed in interactive mode (see section INTERACTIVE COMMANDS). .br Additional flags are available to support storage of atop-data in raw format (see section RAW DATA STORAGE). By default, .I atop produces its output full-screen unless a flag is passed to direct the output to a raw log (-w) or direct the output in parseable (-P) or JSON output (-J). It is possible however to produce full-screen output while the output is also directed in another way. In that case a flag that relates to full-screen output (like -g) has to be passed on the command line explicitly. The status line in the initial screen shows if .I atop runs with restricted view (as unprivileged user) or unrestricted view (as privileged user). In case of restricted view .I atop does not have the privileges (no root identity nor the necessary capabilities) to retrieve all counter values on system level and on process level. .SH PROCESS ACCOUNTING With every interval, .I atop reads the kernel administration to obtain information about all running processes. However, it is likely that processes have terminated during the interval. These processes might have consumed system resources during this interval before they terminated. Therefore, .I atop tries to read the process accounting records that contain the accounting information of terminated processes and report these processes too. Only when the process accounting mechanism in the kernel is activated, the kernel writes such process accounting record to a file for every process that terminates. .PP There are various ways for .I atop to get access to the process accounting records (tried in this order): .PP .TP 4 1. When the environment variable ATOPACCT is set, it specifies the name of the process accounting file. In that case, process accounting for this file should have been activated on beforehand. Before opening this file for reading, .I atop drops its root privileges (if any). .br When this environment variable is present but its contents is empty, process accounting will not be used at all. .PP .TP 4 2. .B This is the preferred way of handling process accounting records! .br When the .I atopacctd daemon is active, it has activated the process accounting mechanism in the kernel and transfers to original accounting records to shadow files. In that case, .I atop drops its root privileges and opens the current shadow file for reading. .br This way is preferred, because the .I atopacctd daemon maintains full control of the size of the original process accounting file written by the kernel and the shadow files read by the .I atop process(es). The .I atopacct service will be activated before the .I atop service to enable .I atop to detect that process accounting is managed by the .I atopacctd daemon. As a forking service, .I atopacctd takes care that all directories and files are initialized before the parent process dies. The child process continues as the daemon process. For further information, refer to the .B atopacctd man page. .PP .TP 4 3. When the .I atopacctd daemon is not active, .I atop verifies if the process accounting mechanism has been switched on via the separate .B psacct or .B acct package (the package name depends on the Linux distro). In that case, one of the files .B /var/log/pacct, .B /var/account/pacct or .B /var/log/account/pacct is in use as process accounting file and .I atop opens this file for reading. .PP .TP 4 4. As a last possibility, .I atop itself tries to activate the process accounting mechanism (requires root privileges) using the file .B /var/cache/atop.d/atop.acct (to be written by the kernel, to be read by .I atop itself). Process accounting remains active as long as at least one .I atop process is alive. Whenever the last .I atop process stops (either by pressing 'q' or by 'kill \-15'), it deactivates the process accounting mechanism again. Therefore you should never terminate .I atop by 'kill \-9', because then it has no chance to stop process accounting. As a result, the accounting file may consume a lot of disk space after a while. .br To avoid that the process accounting file consumes too much disk space, .I atop verifies at the end of every sample if the size of the process accounting file exceeds 200 MiB and if this .I atop process is the only one that is currently using the file. In that case the file is truncated to a size of zero. Notice that root-privileges are required to switch on/off process accounting in the kernel. You can start .I atop as a root user or specify setuid-root privileges to the executable file. In the latter case, .I atop switches on process accounting and drops the root-privileges again. .br If .I atop does not run with root-privileges, it does not show information about finished processes. It indicates this situation with the message 'no procacct' in the top-right corner (instead of the counter that shows the number of exited processes). .PP When during one interval a lot of processes have finished, .I atop might grow tremendously in memory when reading all process accounting records at the end of the interval. To avoid such excessive growth .I atop will never read more than 50 MiB with process information from the process accounting file per interval (approx. 54000 finished processes). In interactive mode a warning is given whenever processes have been skipped for this reason. .PP .SH COLORS For the resource consumption on system level, .I atop uses colors in text mode to indicate that a critical occupation percentage has been (almost) reached. A critical occupation percentage means that is likely that this load causes a noticeable negative performance influence for applications using this resource. The critical percentage depends on the type of resource: e.g. the performance influence of a disk with a busy percentage of 80% might be more noticeable for applications/users than a CPU with a busy percentage of 90%. .br Currently .I atop uses the following default values to calculate a weighted percentage per resource: .PP .TP 5 .B \ Processor A busy percentage of 90% or higher is considered 'critical' (also in bar graph mode). .TP 5 .B \ Disk A busy percentage of 90% or higher is considered 'critical'. .TP 5 .B \ Network A busy percentage of 90% or higher for the load of an interface is considered 'critical'. .TP 5 .B \ Memory An occupation percentage of 90% is considered 'critical'. Notice that this occupation percentage is the accumulated memory consumption of the kernel (including slab) and all processes. The memory for the page cache ('cache' and 'buff' in the MEM-line) and the reclaimable part of the slab ('slrec') is not implied! .br If the number of pages swapped out ('swout' in the PAG-line) is larger than 10 per second, the memory resource is considered 'critical'. A value of at least 1 per second is considered 'almost critical'. .br If the committed virtual memory exceeds the limit ('vmcom' and 'vmlim' in the SWP-line), the SWP-line is colored due to overcommitting the system. .TP 5 .B \ Swap An occupation percentage of 80% is considered 'critical' because swap space might be completely exhausted in the near future. It is not critical from a performance point-of-view. .PP These default values can be modified in the configuration file (see separate .B atoprc man page). .PP When a resource exceeds its critical occupation percentage, the concerning values in the screen line are colored red by default. .br When a resource exceeds (by default) 80% of its critical percentage (so it is almost critical), the concerning values in the screen line are colored cyan by default. This 'almost critical percentage' (one value for all resources) can be also modified in the configuration file (see separate .B atoprc man page). .br The default colors red and cyan can be modified in the configuration file as well (see separate .B atoprc man page). .PP With the key 'x' (or flag \-x), the use of colors can be suppressed in text mode. The use of colors is however mandatory in case of bar graph mode. .SH NETATOP OR NETATOP-BPF MODULE Per-process and per-thread network activity can be measured by the .I netatop kernel module or the .I netatop-bpf BPF module that can be separately installed. .br When .I atop has been started with the '-K' flag, it verifies if the .I netatop or .I netatop-bpf module is currently active. If so, .I atop obtains the relevant network counters from this module for each interval and shows the number of sent and received packets per process/thread in the generic screen. Besides, detailed counters can be requested by pressing the 'n' key. .br When the .I netatopd daemon is running in combination with the .I netatop module, .I atop also reads the network counters of exited processes that are logged by this daemon (comparable with process accounting). .PP More information about the optional .I netatop kernel module and the .I netatopd daemon can be found in the concerning man-pages and on the website mentioned at the end of this manual page. .SH GPU STATISTICS GATHERING GPU statistics can be gathered by .I atopgpud which is a separate data collection daemon process. It gathers cumulative utilization counters of every Nvidia GPU in the system, as well as utilization counters of every process that uses a GPU. When .I atop has been started with the '-k' flag, it reads these GPU utilization counters with every interval when the daemon is active. The .I atopgpud daemon is written in Python, so a Python interpreter should be installed on the target system. For the gathering of the statistics, the .I pynvml module is used by the daemon. Be sure that this module is installed on the target system before activating the daemon, by running the command .I pip as root user: .PP .B \ pip install nvidia-ml-py .PP The .I atopgpud daemon is installed by default as part of the .B atop package, but it is .I not automatically enabled. The daemon can be enabled and started now by running the following commands (as root): .PP .B \ systemctl enable atopgpu .br .B \ systemctl start atopgpu .PP Find a description about the utilization counters in the section OUTPUT DESCRIPTION. .SH INTERACTIVE COMMANDS When running .I atop interactively (no output redirection), keys can be pressed to control the output. In general, lower case keys can be used to show other information for the active processes while certain upper case keys can be used to influence the sort order of the active process/thread list. Some of these keys can also be used to switch from bar graph mode to particular detailed process information in text mode. .PP .TP 5 .B g Show generic output (default). Per process the following fields are shown in case of a window-width of 80 positions: process-id, CPU consumption during the last interval in system and user mode, the virtual and resident memory growth of the process. .br The data transfer per process for read/write on disk can only be shown when .I atop runs with root privileges. .br When the optional module .I netatop or .I netatop-bpf is loaded, the data transfer for send/receive of network packets is shown for each process. .br The last columns contain the state, the occupation percentage for the chosen resource (default: CPU) and the process name. When more than 80 positions are available, other information is added. .PP .TP 5 .B m Show memory related output. Per process the following fields are shown in case of a window width of 80 positions: process-id, minor and major memory faults, size of virtual shared text, total virtual process size, total resident process size, virtual and resident growth during last interval, memory occupation percentage and process name. When more than 80 positions are available, other information is added. For memory consumption, always all processes are shown (also the processes that were not active during the interval). .PP .TP 5 .B d Show disk-related output. When .I atop runs with root privileges, the following fields are shown: process-id, amount of data read from disk, amount of data written to disk, amount of data that was written but has been withdrawn again (WCANCL), disk occupation percentage and process name. .PP .TP 5 .B n Show network related output. Per process the following fields are shown in case of a window width of 80 positions: process-id, thread-id, total bandwidth for received packets, total bandwidth for sent packets, number of received TCP packets with the average size per packet (in bytes), number of sent TCP packets with the average size per packet (in bytes), number of received UDP packets with the average size per packet (in bytes), number of sent UDP packets with the average size per packet (in bytes), the network occupation percentage and process name. .br This information can only be shown when the optional module .I netatop or .I netatop-bpf is installed. When more than 80 positions are available, other information is added. .PP .TP 5 .B s Show scheduling characteristics. Per process the following fields are shown in case of a window width of 80 positions: process-id, number of threads in state 'running' (R), number of threads in state 'interruptible sleeping' (S), number of threads in state 'uninterruptible sleeping' (D), number of threads in state 'idle' (I), scheduling policy (normal timesharing, realtime round-robin, realtime fifo), nice value, priority, realtime priority, current processor, status, exit code, state, the occupation percentage for the chosen resource and the process name. When more than 80 positions are available, other information is added. .PP .TP 5 .B v Show various process characteristics. Per process the following fields are shown in case of a window width of 80 positions: process-id, user name and group, start date and time, status (e.g. exit code if the process has finished), state, the occupation percentage for the chosen resource and the process name. When more than 80 positions are available, other information is added. .PP .TP 5 .B c Show the command line of the process. Per process the following fields are shown: process-id, the occupation percentage for the chosen resource and the command line including arguments. .PP .TP 5 .B G Show cgroup v2 information. Show a hierarchical structure of cgroups and related metrics. Optionally, the processes assigned to each cgroup can be shown. With the keys/flags '2' till '7' the level depth of the cgroups can be chosen ('7' is default). With key/flag '8' the assigned active processes are shown as well, except the kernel processes (usually in the root cgroup). With key/flag '9' all active processes are shown, including the kernel processes. With key/flag 'a' (toggle) all cgroups and all processes are shown instead of only the active cgroups and processes. A cgroup is considered inactive when no processes are assigned to that cgroup nor to the cgroups underneath. With key/flag 'C' the output is sorted on CPU consumption, with key/flag 'M' sorted on memory consumption and with key/flag 'D' sorted on disk consumption. .PP .TP 5 .B e Show GPU utilization. Per process at least the following fields are shown: process-id, range of GPU numbers on which the process currently runs, GPU busy percentage on all GPUs, memory busy percentage (i.e. read and write accesses on memory) on all GPUs, memory occupation at the moment of the sample, average memory occupation during the sample, and GPU percentage. When the .I atopgpud daemon does not run with root privileges, the GPU busy percentage and the memory busy percentage are not available on process level. In that case, the GPU percentage on process level reflects the GPU memory occupation instead of the GPU busy percentage (which is preferred). .PP .TP 5 .B o Show the user-defined line of the process. In the configuration file the keyword .I ownprocline can be specified with the description of a user-defined output-line. .br Refer to the man-page of .B atoprc for a detailed description. .PP .TP 5 .B y Show the individual threads within a process (toggle). Single-threaded processes are still shown as one line. .br For multi-threaded processes, one line represents the process while additional lines show the activity per individual thread (in a different color). Depending on the option 'a' (all or active toggle), all threads are shown or only the threads that were active during the last interval. Depending on the option 'Y' (sort threads), the threads per process will be sorted on the chosen sort criterium or not. .br Whether this key is active or not can be seen in the header line. .PP .TP 5 .B Y Sort the threads per process when combined with option 'y' (toggle). .PP .TP 5 .B u Show the process activity accumulated per user. Per user the following fields are shown: number of processes active or terminated during last interval (or in total if combined with command 'a'), accumulated CPU consumption during last interval in system and user mode, the current virtual and resident memory space consumed by active processes (or all processes of the user if combined with command 'a'). .br When .I atop runs with root privileges, the accumulated read and write throughput on disk is shown. When the optional module .I netatop or .I netatop-bpf has been installed, the accumulated number of received and sent network packets is shown. .br The last columns contain the accumulated occupation percentage for the chosen resource (default: CPU) and the user name. .PP .TP 5 .B p Show the process activity accumulated per program (i.e. process name). Per program the following fields are shown: number of processes active or terminated during last interval (or in total if combined with command 'a'), accumulated CPU consumption during last interval in system and user mode, the current virtual and resident memory space consumed by active processes (or all processes of the user if combined with command 'a'). .br When .I atop runs with root privileges, the accumulated read and write throughput on disk is shown. When the optional module .I netatop or .I netatop-bpf has been installed, the accumulated number of received and sent network packets is shown. .br The last columns contain the accumulated occupation percentage for the chosen resource (default: CPU) and the program name. .PP .TP 5 .B j Show the process activity accumulated per container/pod. Per container (e.g. Docker/Podman) or pod (e.g. Kubernetes) the following fields are shown: number of processes active or terminated during last interval (or in total if combined with command 'a'), accumulated CPU consumption during last interval in system and user mode, the current virtual and resident memory space consumed by active processes (or all processes of the user if combined with command 'a'). .br When .I atop runs with root privileges, the accumulated read and write throughput on disk is shown. When the optional module .I netatop or .I netatop-bpf has been installed, the accumulated number of received and sent network packets is shown. .br The last columns contain the accumulated occupation percentage for the chosen resource (default: CPU) and the container/pod name (CID/POD). .PP .TP 5 .B C Sort the current list in the order of CPU consumption (default). The one-but-last column changes to 'CPU'. .PP .TP 5 .B E Sort the current list in the order of GPU utilization (preferred, but only applicable when the .I atopgpud daemon runs under root privileges) or the order of GPU memory occupation). The one-but-last column changes to 'GPU'. .PP .TP 5 .B M Sort the current list in the order of resident memory consumption. The one-but-last column changes to 'MEM'. In case of sorting on memory, the full process list will be shown (not only the active processes). .PP .TP 5 .B D Sort the current list in the order of disk accesses issued. The one-but-last column changes to 'DSK'. .PP .TP 5 .B N Sort the current list in the order of network bandwidth (received and transmitted). The one-but-last column changes to 'NET'. .PP .TP 5 .B A Sort the current list automatically in the order of the most busy system resource during this interval. The one-but-last column shows either 'ACPU', 'AMEM', 'ADSK' or 'ANET' (the preceding 'A' indicates automatic sorting-order). The most busy resource is determined by comparing the weighted busy-percentages of the system resources, as described earlier in the section COLORS. .br This option remains valid until another sorting-order is explicitly selected again. .br A sorting order for disk is only possible when .I atop runs with root privileges. .br A sorting order for network is only possible when the optional module .I netatop or .I netatop-bpf is loaded. .PP Miscellaneous interactive commands: .PP .TP 5 .B ? Request for help information (also the key 'h' can be pressed). .PP .TP 5 .B V Request for version information (version number and date). .PP .TP 5 .B R Gather and calculate the proportional set size of processes (toggle). Gathering of all values that are needed to calculate the PSIZE of a process is a very time-consuming task, so this key should only be active when analyzing the resident memory consumption of processes. .PP .TP 5 .B W Get the WCHAN per thread (toggle). Gathering of the WCHAN string per thread is a relatively time-consuming task, so this key should only be made active when analyzing the reason for threads to be in sleep state. .PP .TP 5 .B x Suppress colors to highlight critical resources (toggle). .br Whether this key is active or not can be seen in the header line. .PP .TP 5 .B z The pause key can be used to freeze the current situation in order to investigate the output on the screen. While .I atop is paused, the keys described above can be pressed to show other information about the current list of processes. Whenever the pause key is pressed again, atop will continue with a next sample. .br The pause key can be used in text mode and bar graph mode. .PP .TP 5 .B i Modify the interval timer (default: 10 seconds). If an interval timer of 0 is entered, the interval timer is switched off. In that case a new sample can only be triggered manually by pressing the key 't'. .br The interval can be modified in text mode and bar graph mode. .PP .TP 5 .B t Trigger a new sample manually. This key can be pressed if the current sample should be finished before the timer has exceeded, or if no timer is set at all (interval timer defined as 0). In the latter case .I atop can be used as a stopwatch to measure the load being caused by a particular application transaction, without knowing on beforehand how many seconds this transaction will last. .br This key can be used in text mode and bar graph mode. When viewing the contents of a raw file this key can be used to show the next sample from the file. This key can also be used when viewing raw data via a pipe. .PP .TP 5 .B T When viewing the contents of a raw file this key can be used to show the previous sample from the file, however not when reading raw data from a pipe. .br This key can be used in text mode and bar graph mode. .PP .TP 5 .B b When viewing the contents of a raw file, this key can be used to branch to a certain timestamp within the file either forward or backward. When viewing raw data from a pipe only forward branches are possible. .br This key can be used in text mode and bar graph mode. .PP .TP 5 .B r Reset all counters to zero to see the system and process activity since boot again. .br This key can be used in text mode and bar graph mode. When viewing the contents of a raw file, this key can be used to rewind to the beginning of the file again (except when reading raw data from a pipe). .PP .TP 5 .B Z When viewing the contents of a raw file this key can be used to fast-forward to the end of the file, however not when reading raw data from a pipe. .br This key can be used in text mode and bar graph mode. .PP .TP 5 .B U Specify a regular expression string for specific user names or a numerical UID. From now on, only (active) processes will be shown from a user which matches the regular expression or numerical UID. The system statistics are still system wide. If the Enter-key is pressed without specifying a name, (active) processes of all users will be shown again. .br Whether this key is active or not can be seen in the header line. .PP .TP 5 .B I Specify a list with one or more PIDs to be selected. From now on, only processes will be shown with a PID which matches one of the given list. The system statistics are still system wide. If the Enter-key is pressed without specifying a PID, all (active) processes will be shown again. .br Whether this key is active or not can be seen in the header line. .PP .TP 5 .B P Specify a search string for specific process names as a regular expression. From now on, only processes will be shown with a name which matches the regular expression. The system statistics are still system wide. If the Enter-key is pressed without specifying a name, all (active) processes will be shown again. .br Whether this key is active or not can be seen in the header line. .PP .TP 5 .B / Specify a specific command line search string as a regular expression. From now on, only processes will be shown with a command line which matches the regular expression. The system statistics are still system wide. If the Enter-key is pressed without specifying a string, all (active) processes will be shown again. .br Whether this key is active or not can be seen in the header line. .PP .TP 5 .B J Specify a container id (e.g. Docker or Podman) or pod name (e.g. Kubernetes) of maximum 15 characters. In case the name is longer, the last 15 characters are expected. From now on, only processes will be shown that run in that specific container or pod. The system statistics are still system wide. If the Enter-key is pressed without specifying a container id or pod name, all (active) processes will be shown again. .br Whether this key is active or not can be seen in the header line. .PP .TP 5 .B Q Specify a comma-separated list of process/thread state characters. From now on, only processes/threads will be shown that are in those specific states. Accepted states are: R (running), S (sleeping), D (disk sleep), I (idle), T (stopped), t (tracing stop), X (dead), Z (zombie) and P (parked). The system statistics are still system wide. If the Enter-key is pressed without specifying a state, all (active) processes/threads will be shown again. .br Whether this key is active or not can be seen in the header line. .PP .TP 5 .B S Specify search strings for specific logical volume names, specific disk names and specific network interface names. All search strings are interpreted as a regular expressions. From now on, only those system resources are shown that match the concerning regular expression. If the Enter-key is pressed without specifying a search string, all (active) system resources of that type will be shown again. .br Whether this key is active or not can be seen in the header line. .PP .TP 5 .B a The 'all/active' key can be used to toggle between only showing/accumulating the processes that were active during the last interval (default) or showing/accumulating all processes. .br Whether this key is active or not can be seen in the header line. .PP .TP 5 .B X By default, .I atop shows/accumulates the processes that are alive and the processes that are exited during the last interval. With this key (toggle), showing/accumulating the processes that are exited can be suppressed. .br Whether this key is active or not can be seen in the header line. .PP .TP 5 .B f Show a fixed (maximum) number of header lines for system resources (toggle). By default only the lines are shown about system resources (CPUs, paging, logical volumes, disks, network interfaces) that really have been active during the last interval. With this key you can force .I atop to show lines of inactive resources as well. .br Whether this key is active or not can be seen in the header line. .PP .TP 5 .B F Suppress sorting of system resources (toggle). By default system resources (CPUs, logical volumes, disks, network interfaces) are sorted on utilization. .br Whether this key is active or not can be seen in the header line. .PP .TP 5 .B 1 Show relevant counters as an average per second (in the format '..../s') instead of as a total during the interval (toggle). .br Whether this key is active or not can be seen in the header line. .PP .TP 5 .B l Limit the number of system level lines for the counters per-cpu, the active disks and the network interfaces. By default lines are shown of all CPUs, disks and network interfaces which have been active during the last interval. Limiting these lines can be useful on systems with huge number CPUs, disks or interfaces in order to be able to run .I atop on a screen/window with e.g. only 24 lines. .br For all mentioned resources the maximum number of lines can be specified interactively. When using the flag .B -l the maximum number of per-cpu lines is set to 0, the maximum number of disk lines to 5 and the maximum number of interface lines to 3. These values can be modified again in interactive mode. .PP .TP 5 .B k Send a signal to an active process (a.k.a. kill a process). .PP .TP 5 .B q Quit the program. .br This key can be used in text mode and bar graph mode. .PP .TP 5 .B PgDn Show the next page of the process/thread list. .br With the arrow-down key the list can be scrolled downwards with single lines. .PP .TP 5 .B ^F Show the next page of the process/thread list (forward). .br With the arrow-down key the list can be scrolled downwards with single lines. .PP .TP 5 .B PgUp Show the previous page of the process/thread list. .br With the arrow-up key the list can be scrolled upwards with single lines. .PP .TP 5 .B ^B Show the previous page of the process/thread list (backward). .br With the arrow-up key the list can be scrolled upwards with single lines. .PP .TP 5 .B ^L Redraw the screen. .SH RAW DATA STORAGE In order to store system and process level statistics for long-term analysis (e.g. to check the system load and the active processes running yesterday between 3:00 and 4:00 PM), .I atop can store the system and process level statistics in compressed binary format in a raw file with the flag .B -w followed by the filename. If this file already exists and is recognized as a raw data file, .I atop will append new samples to the file (starting with a sample which reflects the activity since boot). If the file does not exist, it will be created. .br All information about system, processes and thread activity is stored in the raw file. .br The interval (default: 10 seconds) and number of samples (default: infinite) can be passed as last arguments. Instead of the number of samples, the flag .B -S can be used to indicate that .I atop should finish anyhow before midnight. .PP A raw file can be read and visualized again with the flag .B -r followed by the filename. If no filename is specified, the file .BI /var/log/atop/atop_ YYYYMMDD is opened for input (where .I YYYYMMDD are digits representing the current date). If a filename is specified in the format YYYYMMDD (representing any valid date), the file .BI /var/log/atop/atop_ YYYYMMDD is opened. If a filename with the symbolic name .BI y is specified, yesterday's daily logfile is opened (this can be repeated so 'yyyy' indicates the logfile of four days ago). If the filename .BI - is used, stdin will be read. .br The samples from the file can be viewed interactively by using the key 't' to show the next sample, the key 'T' to show the previous sample, the key 'b' to branch to a particular time, the key 'r' to rewind to the begin of the file or the 'Z' key to fast-forward to the end of the file. These keys can be used in text mode as well as in bar graph mode. .br When output is redirected to a file or pipe, .B atop prints all samples in plain ASCII. The default line length is 80 characters in that case. With the flag .B -L followed by an alternate line length, more (or less) columns will be shown. .br With the flag .B -b (begin time) and/or .B -e (end time) followed by a time argument of the form [YYYYMMDD]hhmm[ss], a certain time period within the raw file can be selected. .PP Every day at midnight .B atop is restarted by the .BI atop-rotate.timer and .BI atop-rotate.service unit files, to write compressed binary data to the file .BI /var/log/atop/atop_ YYYYMMDD with an interval of 10 minutes by default. .br Furthermore all raw files are removed that are older than 28 days (by default). .br The mentioned default values can be overruled in the file .B /etc/default/atop that might contain other values for .B LOGOPTS (by default without any flag), .B LOGINTERVAL (in seconds, by default 600), .B LOGGENERATIONS (in days, by default 28), and .B LOGPATH (directory in which logfiles are stored). .PP Unfortunately, it is not always possible to keep the format of the raw files compatible in newer versions of .B atop especially when many new counters have to be maintained. Therefore, the program .B atopconvert is installed to convert a raw file created by an older version of .B atop to a raw file that can be read by a newer version of .B atop (see the man page of .B atopconvert for more details). .SH OUTPUT DESCRIPTION The header line always shows the host name, the end date and end time when the sample has been taken, the current options that are active (only in text mode), and the elapsed time (duration) of the sample. The verb 'PAUSED' will be shown behind the time when .B atop is currently paused with the 'z' key. The first sample shows the system level activity since boot (the elapsed time in the header shows the time since boot). .PP In text mode, .I atop first shows the lines related to system level activity for every sample. If a particular system resource has not been used during the interval, the entire line related to this resource is suppressed. So the number of system level lines may vary for each sample. .br After that a list is shown of processes which have been active during the last interval. This list is sorted on CPU consumption by default, but this order can be changed by the keys which are previously described. .PP If values have to be shown by .I atop which do not fit in the column width, another format is used. If e.g. a CPU consumption of 233216 milliseconds should be shown in a column width of 4 positions, it is shown as '233s' (in seconds). For large memory figures, another unit is chosen if the value does not fit (Mb instead of Kb, Gb instead of Mb, Tb instead of Gb, etcetera). For other values, a kind of exponent notation is used (value 123456789 shown in a column of 5 positions gives 123e6). .SH OUTPUT DESCRIPTION - SYSTEM LEVEL The system level information in text mode consists of the following output lines: .PP .TP 5 .B PRC Process and thread level totals. .br This line contains the total CPU time consumed in system mode ('sys') and in user mode ('user'), the total number of processes present at this moment ('#proc'), the total number of threads present at this moment in state 'running' ('#trun'), 'sleeping interruptible' ('#tslpi'), 'sleeping uninterruptible' ('#tslpu') and 'idle' ('#tidle'), the number of zombie processes ('#zombie'), the number of clone system calls ('clones'), and the number of processes that ended during the interval ('#exit') when process accounting is used. Instead of '#exit' the last column may indicate that process accounting could not be activated ('no procacct'). .br If the screen-width does not allow all of these counters, only a relevant subset is shown. .PP .TP 5 .B CPU CPU utilization. .br At least one line is shown for the total occupation of all CPUs together. .br In case of a multi-processor system, an additional line is shown for every individual processor (with 'cpu' in lower case), sorted on activity. Inactive CPUs will not be shown by default. The lines showing the per-cpu occupation contain the CPU number in the field combined with the wait percentage. Every line contains the percentage of CPU time spent in kernel mode by all active processes ('sys'), the percentage of CPU time consumed in user mode ('user') for all active processes (including processes running with a nice value larger than zero), the percentage of CPU time spent for interrupt handling ('irq') including softirq, the percentage of unused CPU time while no processes were waiting for disk I/O ('idle'), and the percentage of unused CPU time while at least one process was waiting for disk I/O ('wait'). .br In case of per-cpu occupation, the CPU number and the wait percentage ('w') for that CPU. The number of lines showing the per-cpu occupation can be limited. For virtual machines, the steal-percentage ('steal') shows the percentage of CPU time stolen by other virtual machines running on the same hardware. .br For physical machines hosting one or more virtual machines, the guest percentage ('guest') shows the percentage of CPU time used by the virtual machines. Notice that this percentage overlaps the user percentage! When PMC performance monitoring counters are supported by the CPU and the kernel (and .I atop runs with root privileges), the number of instructions per CPU cycle ('ipc') is shown. The first sample always shows the value 'initial', because the counters are just activated at the moment that .I atop is started. .br When the .I CPU busy percentage is high and the IPC is less than 1.0, it is likely that the CPU is frequently waiting for memory access during instruction execution (larger CPU caches or faster memory might be helpful to improve performance). When the .I CPU busy percentage is high and the IPC is greater than 1.0, it is likely that the CPU is instruction-bound (more/faster cores might be helpful to improve performance). .br Furthermore, per CPU the effective number of cycles ('cycl') is shown. This value can reach the current CPU frequency if such CPU is 100% busy. When an idle CPU is halted, the number of effective cycles can be (considerably) lower than the current frequency. .br Notice that the .I average instructions per cycle and number of cycles is shown in the CPU line for all CPUs. .br Beware that reading the cycle counter in virtual machines (guests) might introduce performance delays. Therefore this metric is by default disabled in virtual machines. However, with the keyword 'perfevents' in the atoprc file this metric can be explicitly set to 'enable' or 'disable' (see separate man-page of atoprc). .br See also: http://www.brendangregg.com/blog/2017-05-09/cpu-utilization-is-wrong.html In case of frequency scaling, all previously mentioned CPU percentages are relative to the used scaling of the CPU during the interval. If a CPU has been active for e.g. 50% in user mode during the interval while the frequency scaling of that CPU was 40%, only 20% of the full capacity of the CPU has been used in user mode. .br In case that the kernel module 'cpufreq_stats' is active (after issuing 'modprobe cpufreq_stats'), the .I average frequency ('avgf') and the .I average scaling percentage ('avgscal') is shown. Otherwise the .I current frequency ('curf') and the .I current scaling percentage ('curscal') is shown at the moment that the sample is taken. Notice that .I average values for frequency and scaling are shown in the CPU line for every CPU. If the screen-width does not allow all of these counters, only a relevant subset is shown. .PP .TP 5 .B CPL CPU load information. .br This line contains the load average figures reflecting the number of threads that are available to run on a CPU (i.e. part of the runqueue) or that are waiting for disk I/O. These figures are averaged over 1 ('avg1'), 5 ('avg5') and 15 ('avg15') minutes. .br Furthermore the number of context switches ('csw'), the number of serviced interrupts ('intr') and the number of available CPUs are shown. If the screen-width does not allow all of these counters, only a relevant subset is shown. .PP .TP 5 .B GPU GPU utilization (Nvidia). .br Read the section GPU STATISTICS GATHERING in this document to find the details about the activation of the .I atopgpud daemon. In the first column of every line, the bus-id (last nine characters) and the GPU number are shown. The subsequent columns show the percentage of time that one or more kernels were executing on the GPU ('gpubusy'), the percentage of time that global (device) memory was being read or written ('membusy'), the occupation percentage of memory ('memocc'), the total memory ('total'), the memory being in use at the moment of the sample ('used'), the average memory being in use during the sample time ('usavg'), the number of processes being active on the GPU at the moment of the sample ('#proc'), and the type of GPU. If the screen-width does not allow all of these counters, only a relevant subset is shown. .br The number of lines showing the GPUs can be limited. .PP .TP 5 .B MEM Memory occupation (two lines). .br These lines contain the total amount of physical memory ('tot'), the amount of memory which is currently free ('free'), the amount of memory that is available for new workloads without pushing the system into swap ('avail'), the amount of memory in use as page cache including the total resident shared memory ('cache'), the amount of memory within the page cache that has to be flushed to disk ('dirty'), the amount of memory used for filesystem meta data ('buff'), the amount of memory being used for kernel mallocs ('slab'), the amount of slab memory that is reclaimable ('slrec'), the resident size of SYSV shared memory including tmpfs but excluding static huge pages ('shmem'), the resident size of SYSV shared memory including static huge pages ('shrss'), the amount of SYSV shared memory that is currently swapped ('shswp'), the amount of memory that is currently used for page tables ('pgtab'), the number of NUMA nodes in this system ('numnode'), the amount of memory that is currently claimed by vmware's balloon driver ('vmbal'), the amount of memory that is currently claimed by the ARC (cache) of ZFSonlinux ('zfarc'), the amount of memory for anonymous transparent huge pages ('anthp'), the amount of memory that is claimed for huge pages ('hptot'), the amount of huge page memory that is really in use ('hpuse'), the amount of memory that is used for TCP sockets ('tcps'), and the amount of memory that is used for UDP sockets ('udps'). If the screen-width does not allow all of these counters, only a relevant subset is shown. .PP .TP 5 .B SWP Swap occupation and overcommit info. .br This line contains the total amount of swap space on disk ('tot'), the amount of free swap space ('free'), the size of the swap cache ('swcac'), the size of compressed storage used for zswap ('zswap'), the real (decompressed) size of the pages stored in zswap ('zstor'), the total size of the memory used for KSM ('ksuse', i.e. shared), and the total size of the memory saved (deduped) by KSM ('kssav', i.e. sharing). .br Furthermore the committed virtual memory space ('vmcom') and the maximum limit of the committed space ('vmlim', which is by default swap size plus 50% of memory size) is shown. The committed space is the reserved virtual space for all allocations of private memory space for processes. The kernel only verifies whether the committed space exceeds the limit if strict overcommit handling is configured (vm.overcommit_memory is 2). .PP .TP 5 .B LLC Last-Level Cache of CPU info. .br This line contains the total memory bandwidth of LLC ('tot'), the bandwidth of the local NUMA node ('loc'), and the percentage of LLC in use ('LLCXX YY%'). Note that this feature depends on the 'resctrl' pseudo filesystem. Be sure that the kernel is built with the relevant config and take care that the pseudo-filesystem is mounted: .B \ mount -t resctrl resctrl -o mba_MBps /sys/fs/resctrl (on Intel) .br .B \ mount -t resctrl resctrl -o cdp \ \ \ \ \ /sys/fs/resctrl (on AMD) .PP .TP 5 .B NUM Memory utilization per NUMA node (not shown for single NUMA node). .br This line shows the total amount of physical memory of this node ('tot'), the amount of free memory ('free'), the amount of memory for cached file data ('file'), modified cached file data ('dirty'), recently used memory ('activ'), less recently used memory ('inact'), memory being used for kernel mallocs ('slab'), the amount of slab memory that is reclaimable ('slrec'), shared memory including tmpfs ('shmem'), total huge pages ('hptot'), used huge pages('hpuse'), and the fragmentation percentage ('frag'). .PP .TP 5 .B NUC CPU utilization per NUMA node (not shown for single NUMA node). .br This line shows the utilization percentages of all CPUs related to this NUMA node, categorized for system mode ('sys'), user mode ('user'), user mode for niced processes ('niced'), idle mode ('idle'), wait mode ('w' preceded by the node number), irq mode ('irq'), softirq mode ('sirq'), steal mode ('steal'), and guest mode ('guest') overlapping user mode. .PP .TP 5 .B PAG Paging frequency. .br This line contains the number of scanned pages ('scan') due to the fact that free memory drops below a particular threshold, the number of reclaimed pages('steal') due to the fact that free memory drops below a particular threshold, the number times that the kernel tries to reclaim pages due to an urgent need ('stall'),the number of process stalls to run memory compaction to allocate huge pages ('compact'), the number of NUMA pages migrated ('numamig'), and the total number of memory pages migrated successfully e.g. between NUMA nodes or for compaction ('migrate') are shown. .br Also the number of memory pages the system read from block devices ('pgin'), the number of memory pages the system wrote to block devices ('pgout'), the number of memory pages swapped in from zswap ('zswin'), the number of memory pages swapped out to zswap ('zswout'), the number of memory pages the system read from swap space ('swin'), the number of memory pages the system wrote to swap space ('swout'), and the number of out-of-memory kills ('oomkill'). .PP .TP 5 .B PSI Pressure Stall Information. .br This line contains percentages about resource pressure related to CPU, memory and disk I/O. Certain percentages refer to 'some' meaning that some processes/threads were delayed due to resource overload. Other percentages refer to 'full' meaning a loss of overall throughput due to resource overload. .br The values 'cpusome', 'memsome', 'memfull', 'iosome' and 'iofull' show the pressure percentage during the entire interval. .br The values 'cs' (cpu some), 'ms' (memory some), 'mf' (memory full), 'is' (I/O some) and 'if' (I/O full) each show three percentages separated by slashes: pressure percentage over the last 10, 60 and 300 seconds. .br For some Linux distributions PSI support might be disabled by default. In that case you need to pass .B psi=1 on the kernel command line during boot. .PP .TP 5 .B LVM/MDD/DSK Logical volume/multiple device/disk utilization. .br Per active unit one line is produced, sorted on unit activity. Such line shows the name (e.g. VolGroup00-lvtmp for a logical volume or sda for a hard disk), the percentage of elapsed time during which I/O requests were issued to the device ('busy') (note that for devices serving requests in parallel, such as RAID arrays, SSD and NVMe, this number does not reflect their performance limits), the number of read requests issued ('read'), the number of write requests issued ('write'), the number of discard requests issued ('discrd') if supported by kernel version, the number of KiBytes per read ('KiB/r'), the number of KiBytes per write ('KiB/w'), the number of KiBytes per discard ('KiB/d') if supported by kernel version, the number of MiBytes per second throughput for reads ('MBr/s'), the number of MiBytes per second throughput for writes ('MBw/s'), requests issued to the device driver but not completed ('inflt'), the average queue depth while busy ('avq') and the average number of milliseconds needed by a request ('avio') for seek, latency and data transfer. .br If the screen-width does not allow all of these counters, only a relevant subset is shown. The number of lines showing the units can be limited per class (LVM, MDD or DSK) with the 'l' key or statically (see separate man-page of atoprc). By specifying the value 0 for a particular class, no lines will be shown any more for that class. .PP .TP 5 .B NFM Network Filesystem (NFS) mount at the client side. .br For each NFS-mounted filesystem, a line is shown that contains the mounted server directory, the name of the server ('srv'), the total number of bytes physically read from the server ('read') and the total number of bytes physically written to the server ('write'). Data transfer is subdivided in the number of bytes read via normal read() system calls ('nread'), the number of bytes written via normal read() system calls ('nwrit'), the number of bytes read via direct I/O ('dread'), the number of bytes written via direct I/O ('dwrit'), the number of bytes read via memory mapped I/O pages ('mread'), and the number of bytes written via memory mapped I/O pages ('mwrit'). .PP .TP 5 .B NFC Network Filesystem (NFS) client side counters. .br This line contains the number of RPC calls issues by local processes ('rpc'), the number of read RPC calls ('read') and write RPC calls ('rpwrite') issued to the NFS server, the number of RPC calls being retransmitted ('retxmit') and the number of authorization refreshes ('autref'). .PP .TP 5 .B NFS Network Filesystem (NFS) server side counters. .br This line contains the number of RPC calls received from NFS clients ('rpc'), the number of read RPC calls received ('cread'), the number of write RPC calls received ('cwrit'), the number of Megabytes/second returned to read requests by clients ('MBcr/s'), the number of Megabytes/second passed in write requests by clients ('MBcw/s'), the number of network requests handled via TCP ('nettcp'), the number of network requests handled via UDP ('netudp'), the number of reply cache hits ('rchits'), the number of reply cache misses ('rcmiss') and the number of uncached requests ('rcnoca'). Furthermore some error counters indicating the number of requests with a bad format ('badfmt') or a bad authorization ('badaut'), and a counter indicating the number of bad clients ('badcln'). .PP .TP 5 .B NET Network utilization (TCP/IP). .br One line is shown for activity of the transport layer (TCP and UDP), one line for the IP layer and one line per active interface. .br For the transport layer, counters are shown concerning the number of received TCP segments including those received in error ('tcpi'), the number of transmitted TCP segments excluding those containing only retransmitted octets ('tcpo'), the number of UDP datagrams received ('udpi'), the number of UDP datagrams transmitted ('udpo'), the number of active TCP opens ('tcpao'), the number of passive TCP opens ('tcppo'), the number of TCP output retransmissions ('tcprs'), the number of TCP input errors ('tcpie'), the number of TCP output resets ('tcpor'), the number of UDP no ports ('udpnp'), the number of UDP input errors ('udpie'), and the number of TCP incorrect checksums ('csumie'). .br If the screen-width does not allow all of these counters, only a relevant subset is shown. .br These counters are related to IPv4 and IPv6 combined. For the IP layer, counters are shown concerning the number of IP datagrams received from interfaces, including those received in error ('ipi'), the number of IP datagrams that local higher-layer protocols offered for transmission ('ipo'), the number of received IP datagrams which were forwarded to other interfaces ('ipfrw'), the number of IP datagrams which were delivered to local higher-layer protocols ('deliv'), the number of received ICMP datagrams ('icmpi'), and the number of transmitted ICMP datagrams ('icmpo'). .br If the screen-width does not allow all of these counters, only a relevant subset is shown. .br These counters are related to IPv4 and IPv6 combined. For every active network interface one line is shown, sorted on the interface activity. Such line shows the name of the interface and its busy percentage in the first column. The busy percentage for half duplex is determined by comparing the interface speed with the number of bits transmitted and received per second; for full duplex the interface speed is compared with the highest of either the transmitted or the received bits. When the interface speed can not be determined (e.g. for the loopback interface), '---' is shown instead of the percentage. .br Furthermore the number of received packets ('pcki'), the number of transmitted packets ('pcko'), the line speed of the interface ('sp'), the effective amount of bits received per second ('si'), the effective amount of bits transmitted per second ('so'), the number of collisions ('coll'), the number of received multicast packets ('mlti'), the number of errors while receiving a packet ('erri'), the number of errors while transmitting a packet ('erro'), the number of received packets dropped ('drpi'), and the number of transmitted packets dropped ('drpo'). .br If the screen-width does not allow all of these counters, only a relevant subset is shown. .br The number of lines showing the network interfaces can be limited. .PP .TP 5 .B IFB Infiniband utilization. .br For every active Infiniband port one line is shown, sorted on activity. Such line shows the name of the port and its busy percentage in the first column. The busy percentage is determined by taking the highest of either the transmitted or the received bits during the interval, multiplying that value by the number of lanes and comparing it against the maximum port speed. .br Furthermore the number of received packets divided by the number of lanes ('pcki'), the number of transmitted packets divided by the number of lanes ('pcko'), the maximum line speed ('sp'), the effective amount of bits received per second ('si'), the effective amount of bits transmitted per second ('so'), and the number of lanes ('lanes'). .br If the screen-width does not allow all of these counters, only a relevant subset is shown. .br The number of lines showing the Infiniband ports can be limited. .SH OUTPUT DESCRIPTION - CGROUPS LEVEL (V2) In the bottom part of the screen a hierarchical list of cgroups can be shown. By default, cgroups without assigned processes in the cgroup itself and the cgroups underneath will be suppressed. By pressing the 'a' key (toggle) all cgroups will be shown. When depth level '8' or '9' has been selected (by pressing the corresponding key) the assigned processes are shown as well. By default, only the processes that were active during the interval. By pressing the 'a' key (toggle) all processes will be shown. .PP Per cgroup the following fields may be shown (in alphabetical order), depending on the current width of your window: .PP .TP 9 .B CGROUP Name of the cgroup (version 2), i.e. the directory name in the cgroup hierarchy. This root cgroup '/' corresponds to the cgroup root directory, which is usually '/sys/fs/cgroup'. .PP .TP 9 .B CPUBUSY The consumed CPU percentage by this cgroup and all cgroups underneath (as a percentage of one CPU). Value -1 means undefined. .PP .TP 9 .B CPUMAX The 'cpu.max' value of this cgroup (version 2), calculated as percentage of one CPU. Value -1 means maximum, while value -2 means undefined. .PP .TP 9 .B CPUPS The CPU pressure percentage ('some') in this cgroup and cgroups underneath. .br For some Linux distributions PSI support might be disabled by default. In that case you need to pass .B psi=1 on the kernel command line during boot. .PP .TP 9 .B CPUWGT The 'cpu.weight' value of this cgroup (version 2). Value -2 means undefined. .PP .TP 9 .B DISKIO The amount of data transferred to/from physical disks in this cgroup and the cgroups underneath. Value -1 means undefined. .PP .TP 9 .B DSKPS The disk pressure percentage ('full') in this cgroup and cgroups underneath. .br For some Linux distributions PSI support might be disabled by default. In that case you need to pass .B psi=1 on the kernel command line during boot. .PP .TP 9 .B IOWGT The 'io.weight' value of this cgroup (version 2). Value -2 means undefined. .PP .TP 9 .B MEMMAX The 'memory.max' value of this cgroup (version 2). Value -1 means maximum, while value -2 means undefined. .PP .TP 9 .B MEMORY The current memory occupation of this cgroup and all cgroups underneath. Value -1 means undefined. .PP .TP 9 .B MEMPS The memory pressure percentage ('full') in this cgroup and cgroups underneath. .br For some Linux distributions PSI support might be disabled by default. In that case you need to pass .B psi=1 on the kernel command line during boot. .PP .TP 9 .B NPROCS The number of processes assigned to this cgroup. .PP .TP 9 .B PBELOW The number of processes assigned to the cgroups underneath this cgroup. .PP .TP 9 .B SWPMAX The 'memory.swap.max' value of this cgroup (version 2). Value -1 means maximum, while value -2 means undefined. .SH OUTPUT DESCRIPTION - PROCESS LEVEL In the bottom part of the screen, a list of processes can be shown from which the resource utilization has changed during the last interval. These processes might have used CPU time or might have issued disk or network requests. However a process is also shown if part of it has been paged out due to lack of memory (while the process itself was in sleep state). .PP Per process the following fields may be shown (in alphabetical order), depending on the current output mode as described in the section INTERACTIVE COMMANDS and depending on the current width of your window: .PP .TP 9 .B AVGRSZ The average size of one read-action on disk. .PP .TP 9 .B AVGWSZ The average size of one write-action on disk. .PP .TP 9 .B BANDWI Total bandwidth for received TCP and UDP packets consumed by this process (bits-per-second). This value can be compared with the value 'si' on interface level (used bandwidth per interface). .br This information will only be shown when the optional module .I netatop or .I netatop-bpf is loaded. .PP .TP 9 .B BANDWO Total bandwidth for sent TCP and UDP packets consumed by this process (bits-per-second). This value can be compared with the value 'so' on interface level (used bandwidth per interface). .br This information will only be shown when the optional module .I netatop or .I netatop-bpf is loaded. .PP .TP 9 .B BDELAY Aggregated block I/O delay, i.e. time waiting for disk I/O. .PP .TP 9 .B CID/POD Container id (e.g. Docker or Podman) or pod name (e.g. Kubernetes) referring to the container/pod in which the process/thread is running. When a pod name is longer than 15 characters, only the last 15 characters are shown. If a process has been started and finished during the last interval, a '?' is shown because the container id or pod name is not part of the standard process accounting record. This column will only be shown when .I atop runs with superuser privileges and when at least one containerized process is detected. .PP .TP 9 .B CMD The name of the process. This name can be surrounded by "less/greater than" signs ('') which means that the process has finished during the last interval. A single accounting record is written for the entire process on termination of the last thread in the process. When the main thread exits, the process name is changed to the thread name. .br Behind the abbreviation 'CMD' in the header line, the current page number and the total number of pages of the process/thread list are shown. .PP .TP 9 .B COMMAND-LINE The full command line of the process (including arguments). If the length of the command line exceeds the length of the screen line, the arrow keys -> and <- can be used for horizontal scroll. The '-z ' command line option can be used to prepend matching environment variables to the displayed command line. POSIX Extended Regular Expression syntax are used (see regex(3)). When a matching environment variable is too long (exceeding the buffer that should contain the command line), it will be truncated. Usually only the environment variables will be shown of the processes that are running under your own user ID, unless you are a privileged user. .br WARNING: Don't use this flag when .I writing to a (publicly readable) raw file! You might expose sensitive environment variables to the readers of that raw file. Behind the verb 'COMMAND-LINE' in the header line, the current page number and the total number of pages of the process/thread list are shown. .PP .TP 9 .B CPU The occupation percentage of this process related to the available capacity for this resource on system level. .PP .TP 9 .B CPUNR The identification of the CPU the (main) thread is running on or has recently been running on. .PP .TP 9 .B CTID Container ID (OpenVZ). If a process has been started and finished during the last interval, a '?' is shown because the container ID is not part of the standard process accounting record. .PP .TP 9 .B DSK The occupation percentage of this process related to the total load that is produced by all processes (i.e. total disk accesses by all processes during the last interval). .br This information is shown when per process "storage accounting" is active in the kernel. .PP .TP 9 .B EGID Effective group-id under which this process executes. When the name is longer than 8 or when the .I -I flag is used, the number is shown instead of the name. .PP .TP 9 .B ENDATE Date that the process has been finished. If the process is still running, this field shows 'active'. .PP .TP 9 .B ENTIME Time that the process has been finished. If the process is still running, this field shows 'active'. .PP .TP 9 .B ENVID Virtual environment identified (OpenVZ only). .PP .TP 9 .B EUID Effective user-id under which this process executes. When the name is longer than 8 or when the .I -I flag is used, the number is shown instead of the name. .PP .TP 9 .B EXC The exit code of a terminated process (second position of column 'ST' is E) or the fatal signal number (second position of column 'ST' is S or C). .PP .TP 9 .B FSGID Filesystem group-id under which this process executes. When the name is longer than 8 or when the .I -I flag is used, the number is shown instead of the name. .PP .TP 9 .B FSUID Filesystem user-id under which this process executes. When the name is longer than 8 or when the .I -I flag is used, the number is shown instead of the name. .PP .TP 9 .B GPU When the .I atopgpud daemon does not run with root privileges, the GPU percentage reflects the GPU memory occupation percentage (memory of all GPUs is 100%). .br When the .I atopgpud daemon runs with root privileges, the GPU percentage reflects the GPU busy percentage. .PP .TP 9 .B GPUBUSY Busy percentage on all GPUs (one GPU is 100%). .br When the .I atopgpud daemon does not run with root privileges, this value is not available. .PP .TP 9 .B GPUNUMS Comma-separated list of GPUs used by the process during the interval. When the comma-separated list exceeds the width of the column, a hexadecimal value is shown. .PP .TP 9 .B LOCKSZ The virtual amount of memory being locked (i.e. non-swappable) by this process (or user). .PP .TP 9 .B MAJFLT The number of page faults issued by this process that have been solved by creating/loading the requested memory page. .PP .TP 9 .B MEM The occupation percentage of this process related to the available capacity for this resource on system level. .PP .TP 9 .B MEMAVG Average memory occupation during the interval on all used GPUs. .PP .TP 9 .B MEMBUSY Busy percentage of memory on all GPUs (one GPU is 100%), i.e. the time needed for read and write accesses on memory. .br When the .I atopgpud daemon does not run with root privileges, this value is not available. .PP .TP 9 .B MEMNOW Memory occupation at the moment of the sample on all used GPUs. .PP .TP 9 .B MINFLT The number of page faults issued by this process that have been solved by reclaiming the requested memory page from the free list of pages. .PP .TP 9 .B NET The occupation percentage of this process related to the total load that is produced by all processes (i.e. consumed network bandwidth of all processes during the last interval). .br This information will only be shown when the optional module .I netatop or .I netatop-bpf is loaded. .PP .TP 9 .B NICE The more or less static priority that can be given to a process on a scale from -20 (high priority) to +19 (low priority). .PP .TP 9 .B NIVCSW Number of times the process/thread was context-switched involuntarily, in case that the time slice expired. .PP .TP 9 .B NPROCS The number of active and terminated processes accumulated for this user or program. .PP .TP 9 .B NVCSW Number of times that the process/thread was context-switched voluntarily in case of a blocking system call, e.g. to wait for an I/O operation to complete. .PP .TP 9 .B PID Process-id. If a process has been started and finished during the last interval, a '?' is shown because the process-id is not part of the standard process accounting record. .PP .TP 9 .B POLI The policies 'norm' (normal, which is SCHED_OTHER), 'btch' (batch) and 'idle' refer to timesharing processes. The policies 'fifo' (SCHED_FIFO) and 'rr' (round robin, which is SCHED_RR) refer to realtime processes. .PP .TP 9 .B PPID Parent process-id. If a process has been started and finished during the last interval, value 0 is shown because the parent process-id is not part of the standard process accounting record. .PP .TP 9 .B PRI The process' priority ranges from 0 (highest priority) to 139 (lowest priority). Priority 0 to 99 are used for realtime processes (fixed priority independent of their behavior) and priority 100 to 139 for timesharing processes (variable priority depending on their recent CPU consumption and the nice value). .PP .TP 9 .B PSIZE The proportional memory size of this process (or user). .br Every process shares resident memory with other processes. E.g. when a particular program is started several times, the code pages (text) are only loaded once in memory and shared by all incarnations. Also the code of shared libraries is shared by all processes using that shared library, as well as shared memory and memory-mapped files. For the PSIZE calculation of a process, the resident memory of a process that is shared with other processes is divided by the number of sharers. This means, that every process is accounted for a proportional part of that memory. Accumulating the PSIZE values of all processes in the system gives a reliable impression of the total resident memory consumed by all processes. .br Since gathering of all values that are needed to calculate the PSIZE is a very time-consuming task, the 'R' key (or '-R' flag) should be active. Gathering these values also requires superuser privileges (otherwise '?K' is shown in the output). .br If a process has finished during the last interval, no value is shown since the proportional memory size is not part of the standard process accounting record. .PP .TP 9 .B RDDSK The read data transfer issued physically on disk (so reading from the disk cache is not accounted for). .br Unfortunately, the kernel aggregates the data transfer of a process to the data transfer of its parent process when terminating, so you might see transfers for (parent) processes like cron, bash or init, that are not really issued by them. .PP .TP 9 .B RDELAY Runqueue delay, i.e. time spent waiting on a runqueue. .PP .TP 9 .B RGID The real group-id under which the process executes. When the name is longer than 8 or when the .I -I flag is used, the number is shown instead of the name. .PP .TP 9 .B RGROW The amount of resident memory that the process has grown during the last interval. A resident growth can be caused by touching memory pages which were not physically created/loaded before (load-on-demand). Note that a resident growth can also be negative e.g. when part of the process is paged out due to lack of memory or when the process frees dynamically allocated memory. For a process which started during the last interval, the resident growth reflects the total resident size of the process at that moment. .br If a process has finished during the last interval, no value is shown since resident memory occupation is not part of the standard process accounting record. .PP .TP 9 .B RNET The number of TCP- and UDP packets received by this process. This information will only be shown when the optional module .I netatop or .I netatop-bpf is installed. .br If a process has finished during the last interval, no value is shown since network counters are not part of the standard process accounting record. .PP .TP 9 .B RSIZE The total resident memory usage consumed by this process (or user). Notice that the RSIZE of a process includes all resident memory used by that process, even if certain memory parts are shared with other processes (see also the explanation of PSIZE). .br If a process has finished during the last interval, no value is shown since resident memory occupation is not part of the standard process accounting record. .PP .TP 9 .B RTPR Realtime priority according the POSIX standard. Value can be 0 for a timesharing process (policy 'norm', 'btch' or 'idle') or ranges from 1 (lowest) till 99 (highest) for a realtime process (policy 'rr' or 'fifo'). .PP .TP 9 .B RUID The real user-id under which the process executes. When the name is longer than 8 or when the .I -I flag is used, the number is shown instead of the name. .PP .TP 9 .B S The current state of the (main) thread: 'R' for running (currently processing or in the runqueue), 'S' for sleeping interruptible (wait for an event to occur), 'D' for sleeping non-interruptible, 'Z' for zombie (waiting to be synchronized with its parent process), 'T' for stopped (suspended or traced), 'W' for swapping, and 'E' (exit) for processes which have finished during the last interval. .PP .TP 9 .B SGID The saved group-id of the process. When the name is longer than 8 or when the .I -I flag is used, the number is shown instead of the name. .PP .TP 9 .B SNET The number of TCP and UDP packets transmitted by this process. This information will only be shown when the optional module .I netatop or .I netatop-bpf is installed. .PP .TP 9 .B ST The status of a process. .br The first position indicates if the process has been started during the last interval (the value .I N means 'new process'). The second position indicates if the process has been finished during the last interval. .br The value .I E means 'exit' on the process' own initiative; the exit code is displayed in the column 'EXC'. .br The value .I S means that the process has been terminated unvoluntarily by a signal; the signal number is displayed in the in the column 'EXC'. .br The value .I C means that the process has been terminated unvoluntarily by a signal, producing a core dump in its current directory; the signal number is displayed in the column 'EXC'. .PP .TP 9 .B STDATE The start date of the process. .PP .TP 9 .B STTIME The start time of the process. .PP .TP 9 .B SUID The saved user-id of the process. When the name is longer than 8 or when the .I -I flag is used, the number is shown instead of the name. .PP .TP 9 .B SWAPSZ The swap space consumed by this process (or user). .PP .TP 9 .B SYSCPU CPU time consumption of this process in system mode (kernel mode), usually due to system call handling. .PP .TP 9 .B TCPRASZ The average size of a received TCP buffer in bytes. This information will only be shown when the optional module .I netatop or .I netatop-bpf is installed. .PP .TP 9 .B TCPRCV The number of TCP packets received for this process. This information will only be shown when the optional module .I netatop or .I netatop-bpf is installed. .PP .TP 9 .B TCPSASZ The average size of a transmitted TCP buffer in bytes. This information will only be shown when the optional module .I netatop or .I netatop-bpf is installed. .PP .TP 9 .B TCPSND The number of TCP packets transmitted for this process. This information will only be shown when the optional module .I netatop or .I netatop-bpf is installed. .PP .TP 9 .B THR Total number of threads within this process. All related threads are contained in a thread group, represented by .I atop as one line or as a separate line when the 'y' key (or -y flag) is active. On Linux 2.4 systems it is hardly possible to determine which threads (i.e. processes) are related to the same thread group. Every thread is represented by .I atop as a separate line. .PP .TP 9 .B TID Thread-id. All threads within a process run with the same PID but with a different TID. This value is shown for individual threads in multi-threaded processes (when using the key 'y'). .PP .TP 9 .B TIDLE Number of threads within this process that are in the state 'idle' (I), i.e. uninterruptible sleeping threads that do not count for the load average. .PP .TP 9 .B TRUN Number of threads within this process that are in the state 'running' (R). .PP .TP 9 .B TSLPI Number of threads within this process that are in the state 'interruptible sleeping' (S). .PP .TP 9 .B TSLPU Number of threads within this process that are in the state 'uninterruptible sleeping' (D). .PP .TP 9 .B UDPRASZ The average size of a received UDP packet in bytes. This information will only be shown when the optional module .I netatop or .I netatop-bpf is installed. .PP .TP 9 .B UDPRCV The number of UDP packets received by this process. This information will only be shown when the optional module .I netatop or .I netatop-bpf is installed. .PP .TP 9 .B UDPSASZ The average size of a transmitted UDP packets in bytes. This information will only be shown when the optional module .I netatop or .I netatop-bpf is installed. .PP .TP 9 .B UDPSND The number of UDP packets transmitted by this process. This information will only be shown when the optional module .I netatop or .I netatop-bpf is installed. .PP .TP 9 .B USRCPU CPU time consumption of this process in user mode, due to processing the own program text. .PP .TP 9 .B VDATA The virtual memory size of the private data used by this process (including heap and shared library data). .PP .TP 9 .B VGROW The amount of virtual memory that the process has grown during the last interval. A virtual growth can be caused by e.g. issuing a malloc() or attaching a shared memory segment. Note that a virtual growth can also be negative by e.g. issuing a free() or detaching a shared memory segment. For a process which started during the last interval, the virtual growth reflects the total virtual size of the process at that moment. .br If a process has finished during the last interval, no value is shown since virtual memory occupation is not part of the standard process accounting record. .PP .TP 9 .B VPID Virtual process-id (within an OpenVZ container). If a process has been started and finished during the last interval, a '?' is shown because the virtual process-id is not part of the standard process accounting record. .PP .TP 9 .B VSIZE The total virtual memory usage consumed by this process (or user). .br If a process has finished during the last interval, no value is shown since virtual memory occupation is not part of the standard process accounting record. .PP .TP 9 .B VSLIBS The virtual memory size of the (shared) text of all shared libraries used by this process. .PP .TP 9 .B VSTACK The virtual memory size of the (private) stack used by this process .PP .TP 9 .B VSTEXT The virtual memory size of the (shared) text of the executable program. .PP .TP 9 .B WCHAN Wait channel of thread in sleep state, i.e. the name of the kernel function in which the thread has been put asleep. .br Since determining the name string of the kernel function is a relatively time-consuming task, the 'W' key (or '-W' flag) should be active. .PP .TP 9 .B WRDSK The write data transfer issued physically on disk (so writing to the disk cache is not accounted for). This counter is maintained for the application process that writes its data to the cache (assuming that this data is physically transferred to disk later on). Notice that disk I/O needed for swapping is not taken into account. .br Unfortunately, the kernel aggregates the data transfer of a process to the data transfer of its parent process when terminating, so you might see transfers for (parent) processes like cron, bash or init, that are not really issued by them. .PP .TP 9 .B WCANCL The write data transfer previously accounted for this process or another process that has been cancelled. Suppose that a process writes new data to a file and that data is removed again before the cache buffers have been flushed to disk. Then the original process shows the written data as WRDSK, while the process that removes/truncates the file shows the unflushed removed data as WCANCL. .SH PARSABLE OUTPUT With the flag .B -P followed by a list of one or more labels (comma-separated), parsable output is produced for each sample. The labels that can be specified for system-level statistics correspond to the labels (first verb of each line) that can be found in the interactive output: "CPU", "cpu", "CPL", "GPU", "MEM", "SWP", "PAG", "PSI", "LVM", "MDD", "DSK", "NFM", "NFC", "NFS", "NET", "IFB", "LLC", "NUM" and "NUC". For cgroup-level statistics the label "CGR" is available. For process-level statistics special labels are available: "PRG" (general), "PRC" (CPU), "PRE" (GPU), "PRM" (memory), "PRD" (disk, but requires special privileges) and "PRN" (network, only if the optional module .I netatop or .I netatop-bpf is installed). .br With the label "ALL", all system and process level statistics are shown. .PP The command and command line in the parsable output might contain spaces and are therefore by default surrounded by parenthesis. However, since a space is often used as separator between the fields by parsing tools, with the additional flag .B -Z it is possible to exchange the spaces in the command (line) by underscores and omit the parenthesis. .PP For every interval all requested lines are shown whereafter .B atop shows a line just containing the label "SEP" as a separator before the lines for the next sample are generated. .br When a sample contains the values since boot, .B atop shows a line just containing the label "RESET" before the lines for this sample are generated. .PP The first part of each output-line consists of the following six fields: .B label (the name of the label), .B host (the name of this machine), .B epoch (the time of this interval as number of seconds since 1-1-1970), .B date (date of this interval in format YYYY/MM/DD), .B time (time of this interval in format HH:MM:SS), and .B interval (number of seconds elapsed for this interval). .PP The subsequent fields of each output-line depend on the label: .PP .TP 9 .B CPU Subsequent fields: total number of clock-ticks per second for this machine, number of processors, consumption for all CPUs in system mode (clock-ticks), consumption for all CPUs in user mode (clock-ticks), consumption for all CPUs in user mode for niced processes (clock-ticks), consumption for all CPUs in idle mode (clock-ticks), consumption for all CPUs in wait mode (clock-ticks), consumption for all CPUs in irq mode (clock-ticks), consumption for all CPUs in softirq mode (clock-ticks), consumption for all CPUs in steal mode (clock-ticks), consumption for all CPUs in guest mode (clock-ticks) overlapping user mode, frequency of all CPUs, frequency percentage of all CPUs, instructions executed by all CPUs and cycles for all CPUs. .TP 9 .B cpu Subsequent fields: total number of clock-ticks per second for this machine, processor-number, consumption for this CPU in system mode (clock-ticks), consumption for this CPU in user mode (clock-ticks), consumption for this CPU in user mode for niced processes (clock-ticks), consumption for this CPU in idle mode (clock-ticks), consumption for this CPU in wait mode (clock-ticks), consumption for this CPU in irq mode (clock-ticks), consumption for this CPU in softirq mode (clock-ticks), consumption for this CPU in steal mode (clock-ticks), consumption for this CPU in guest mode (clock-ticks) overlapping user mode, frequency of this CPU, frequency percentage of this CPU, instructions executed by this CPU and cycles for this CPU. .TP 9 .B CPL Subsequent fields: number of processors, load average for last minute, load average for last five minutes, load average for last fifteen minutes, number of context-switches, and number of device interrupts. .TP 9 .B GPU Subsequent fields: GPU number, bus-id string, type of GPU string, GPU busy percentage during last second (-1 if not available), memory busy percentage during last second (-1 if not available), total memory size (KiB), used memory (KiB) at this moment, number of samples taken during interval, cumulative GPU busy percentage during the interval (to be divided by the number of samples for the average busy percentage, -1 if not available), cumulative memory busy percentage during the interval (to be divided by the number of samples for the average busy percentage, -1 if not available), and cumulative memory occupation during the interval (to be divided by the number of samples for the average occupation). .TP 9 .B MEM Subsequent fields: page size for this machine (in bytes), size of physical memory (pages), size of free memory (pages), size of page cache (pages), size of buffer cache (pages), size of slab (pages), dirty pages in cache (pages), reclaimable part of slab (pages), total size of vmware's balloon pages (pages), total size of shared memory (pages), size of resident shared memory (pages), size of swapped shared memory (pages), smaller huge page size (in bytes), total size of smaller huge pages (huge pages), size of free smaller huge pages (huge pages), size of ARC (cache) of ZFSonlinux (pages), size of sharing pages for KSM (pages), size of shared pages for KSM (pages), size of memory used for TCP sockets (pages), size of memory used for UDP sockets (pages), size of pagetables (pages), larger huge page size (in bytes), total size of larger huge pages (huge pages), size of free larger huge pages (huge pages), size of available memory (pages) for new workloads without swapping, and size of anonymous transparent huge pages ('normal' pages). .TP 9 .B SWP Subsequent fields: page size for this machine (in bytes), size of swap (pages), size of free swap (pages), size of swap cache (pages), size of committed space (pages), limit for committed space (pages), size of the swap cache (pages), the real (decompressed) size of the pages stored in zswap (pages), and the size of compressed storage used for zswap (pages). .TP 9 .B LLC Subsequent fields: LLC id, percentage of LLC in use, total memory bandwidth of this LLC (in bytes), and memory bandwidth on local NUMA node of this LLC (in bytes). .TP 9 .B PAG Subsequent fields: page size for this machine (in bytes), number of page scans, number of allocstalls, 0 (future use), number of swapins, number of swapouts, number of oomkills (-1 when counter not present), number of process stalls to run memory compaction, number of pages successfully migrated in total, number of NUMA pages migrated, number of pages read from block devices, number of pages written to block devices, number of swapins from zswap, and number of swapouts to zswap. .TP 9 .B PSI Subsequent fields: PSI statistics present on this system (n or y), CPU some avg10, CPU some avg60, CPU some avg300, CPU some accumulated microseconds during interval, memory some avg10, memory some avg60, memory some avg300, memory some accumulated microseconds during interval, memory full avg10, memory full avg60, memory full avg300, memory full accumulated microseconds during interval, I/O some avg10, I/O some avg60, I/O some avg300, I/O some accumulated microseconds during interval, I/O full avg10, I/O full avg60, I/O full avg300, and I/O full accumulated microseconds during interval. .TP 9 .B LVM/MDD/DSK For every logical volume/multiple device/hard disk one line is shown. .br Subsequent fields: name, number of milliseconds spent for I/O, number of reads issued, number of sectors transferred for reads, number of writes issued, number of sectors transferred for write, number of discards issued (-1 if not supported), number of sectors transferred for discards, number of requests currently in flight (not yet completed), and the average queue depth while the disk was busy. .TP 9 .B NFM Subsequent fields: mounted NFS filesystem, total number of bytes read, total number of bytes written, number of bytes read by normal system calls, number of bytes written by normal system calls, number of bytes read by direct I/O, number of bytes written by direct I/O, number of pages read by memory-mapped I/O, and number of pages written by memory-mapped I/O. .TP 9 .B NFC Subsequent fields: number of transmitted RPCs, number of transmitted read RPCs, number of transmitted write RPCs, number of RPC retransmissions, and number of authorization refreshes. .TP 9 .B NFS Subsequent fields: number of handled RPCs, number of received read RPCs, number of received write RPCs, number of bytes read by clients, number of bytes written by clients, number of RPCs with bad format, number of RPCs with bad authorization, number of RPCs from bad client, total number of handled network requests, number of handled network requests via TCP, number of handled network requests via UDP, number of handled TCP connections, number of hits on reply cache, number of misses on reply cache, and number of uncached requests. .TP 9 .B NET First, one line is produced for the upper layers of the TCP/IP stack. .br Subsequent fields: the verb "upper", number of packets received by TCP, number of packets transmitted by TCP, number of packets received by UDP, number of packets transmitted by UDP, number of packets received by IP, number of packets transmitted by IP, number of packets delivered to higher layers by IP, number of packets forwarded by IP, number of input errors (UDP), number of noport errors (UDP), number of active opens (TCP), number of passive opens (TCP), number of passive opens (TCP), number of established connections at this moment (TCP), number of retransmitted segments (TCP), number of input errors (TCP), number of output resets (TCP), and number of checksum errors on received packets (TCP). Next, one line is shown for every interface. .br Subsequent fields: name of the interface, number of packets received by the interface, number of bytes received by the interface, number of packets transmitted by the interface, number of bytes transmitted by the interface, interface speed, duplex mode (0=half, 1=full), receive errors, transmit errors, receive drops, transmit drops, receive fifo, transmit fifo, receive compressed, transmit compressed, receive framing errors, receive multicast, transmit collisions, and transmit carrier. .TP 9 .B IFB Subsequent fields: name of the InfiniBand interface, port number, number of lanes, maximum rate (Mbps), number of bytes received, number of bytes transmitted, number of packets received, and number of packets transmitted. .TP 9 .B NUM Subsequent fields: NUMA node number, page size for this machine (in bytes), the fragmentation percentage of this node, size of physical memory (pages), size of free memory (pages), recently (active) used memory (pages), less recently (inactive) used memory (pages), size of cached file data (pages), dirty pages in cache (pages), slab memory being used for kernel mallocs (pages), slab memory that is reclaimable (pages), shared memory including tmpfs (pages), total huge pages (huge pages), and free huge pages (huge pages). .TP 9 .B NUC Subsequent fields: NUMA node number, number of processors for this node, consumption for node CPUs in system mode (clock-ticks), consumption for node CPUs in user mode (clock-ticks), consumption for node CPUs in user mode for niced processes (clock-ticks), consumption for node CPUs in idle mode (clock-ticks), consumption for node CPUs in wait mode (clock-ticks), consumption for node CPUs in irq mode (clock-ticks), consumption for node CPUs in softirq mode (clock-ticks), consumption for node CPUs in steal mode (clock-ticks), and consumption for node CPUs in guest mode (clock-ticks) overlapping user mode. .TP 9 .B CGR For every cgroup (level) one or two lines are shown. The first line shows the utilization and configuration values of the cgroup. The optional second line shows the PIDs of the processes assigned to this cgroup. Subsequent fields of first line: character 'C', full path name of cgroup, number of processes assigned to this group, number of processes assigned to the cgroups underneath, CPU usage in user mode (microseconds), CPU usage in system mode (microseconds), CPU weight, CPU max, current memory usage (pages), anonymous memory usage (pages), file memory usage (pages), kernel memory usage (pages), shared memory usage (pages), memory max (pages), swap max (pages), number of bytes read from disk, number of bytes written to disk, number of read requests from disk, number of write requests to disk, the disk weight, cpu some pressure (microseconds), cpu total pressure (microseconds), memory some pressure (microseconds), memory total pressure (microseconds), disk some pressure (microseconds), and disk total pressure (microseconds). Subsequent fields of second line (only when processes assigned to this cgroup): character 'P', full path name of cgroup, and list of PIDs separated by spaces. .TP 9 .B PRG For every process one line is shown. .br Subsequent fields: PID (unique ID of task), name (between parenthesis or underscores for spaces), state, real uid, real gid, TGID (group number of related tasks/threads), total number of threads, exit code (in case of fatal signal: signal number + 256), start time (epoch), full command line (between parenthesis or underscores for spaces), PPID, number of threads in state 'running' (R), number of threads in state 'interruptible sleeping' (S), number of threads in state 'uninterruptible sleeping' (D), effective uid, effective gid, saved uid, saved gid, filesystem uid, filesystem gid, elapsed time of terminated process (hertz), is_process (y/n), OpenVZ virtual pid (VPID), OpenVZ container id (CTID), container/pod name (CID/POD), indication if the task is newly started during this interval ('N'), cgroup v2 path name (between parenthesis or underscores for spaces), end time (epoch or 0 if still active), and number of threads in state 'idle' (I). .TP 9 .B PRC For every process one line is shown. .br Subsequent fields: PID, name (between parenthesis or underscores for spaces), state, total number of clock-ticks per second for this machine, CPU-consumption in user mode (clockticks), CPU-consumption in system mode (clockticks), nice value, priority, realtime priority, scheduling policy, current CPU (-1 for exited process), sleep average, TGID (group number of related tasks/threads), is_process (y/n), runqueue delay in nanoseconds for this thread or for all threads (in case of process), wait channel of this thread (between parenthesis or underscores for spaces), block I/O delay (clockticks), cgroup v2 'cpu.max' calculated as percentage (-3 means no cgroup v2 support, -2 means undefined and -1 means maximum), cgroup v2 most restrictive 'cpu.max' in upper directories calculated as percentage (-3 means no cgroup v2 support, -2 means undefined and -1 means maximum), number of voluntary context switches, and number of involuntary context switches. .TP 9 .B PRE For every process one line is shown. .br Subsequent fields: PID, name (between parenthesis or underscores for spaces), process state, GPU state (A for active, E for exited, N for no GPU user), number of GPUs used by this process, bitlist reflecting used GPUs, GPU busy percentage during interval, memory busy percentage during interval, memory occupation (KiB) at this moment cumulative memory occupation (KiB) during interval, and number of samples taken during interval. .TP 9 .B PRM For every process one line is shown. .br Subsequent fields: PID, name (between parenthesis or underscores for spaces), state, page size for this machine (in bytes), virtual memory size (KiB), resident memory size (KiB), shared text memory size (KiB), virtual memory growth (KiB), resident memory growth (KiB), number of minor page faults, number of major page faults, virtual library exec size (KiB), virtual data size (KiB), virtual stack size (KiB), swap space used (KiB), TGID (group number of related tasks/threads), is_process (y/n), proportional set size (KiB) if in 'R' option is specified, virtually locked memory space (KiB), cgroup v2 'memory.max' in KiB (-3 means no cgroup v2 support, -2 means undefined and -1 means maximum), cgroup v2 most restrictive 'memory.max' in upper directories in KiB (-3 means no cgroup v2 support, -2 means undefined and -1 means maximum), cgroup v2 'memory.swap.max' in KiB (-3 means no cgroup v2 support, -2 means undefined and -1 means maximum), and cgroup v2 most restrictive 'memory.swap.max' in upper directories in KiB (-3 means no cgroup v2 support, -2 means undefined and -1 means maximum). .TP 9 .B PRD For every process one line is shown. .br Subsequent fields: PID, name (between parenthesis or underscores for spaces), state, obsoleted kernel patch installed ('n'), standard io statistics used ('y' or 'n'), number of reads on disk, cumulative number of sectors read, number of writes on disk, cumulative number of sectors written, cancelled number of written sectors, TGID (group number of related tasks/threads), obsoleted value ('n'), and is_process (y/n). .TP 9 .B PRN For every process one line is shown. .br Subsequent fields: PID, name (between parenthesis or underscores for spaces), state, kernel module .I netatop or .I netatop-bpf installed ('y' or 'n'), number of TCP-packets transmitted, cumulative size of TCP-packets transmitted, number of TCP-packets received, cumulative size of TCP-packets received, number of UDP-packets transmitted, cumulative size of UDP-packets transmitted, number of UDP-packets received, cumulative size of UDP-packets transmitted, number of raw packets transmitted (obsolete, always 0), number of raw packets received (obsolete, always 0), TGID (group number of related tasks/threads) and is_process (y/n). .br If the kernel module is not active, the network I/O counters per process are not relevant. .PP .SH JSON OUTPUT With the flag .B -J followed by a list of one or more labels (comma-separated), JSON output is produced for each sample. The syntax and name of JSON labels are the same as for the parsable output. .SH SIGNALS By sending the SIGUSR1 signal to .I atop a new sample will be forced, even if the current timer interval has not exceeded yet. The behavior is similar to pressing the 't' key in an interactive session. .PP By sending the SIGUSR2 signal to .I atop a final sample will be forced after which .I atop will terminate. .SH EXAMPLES Monitor the current system load in text mode with an interval of (default) 10 seconds: .PP .TP 12 .B \ atop .PP Monitor the current system load as bar graphs with an interval of 5 seconds: .PP .TP 12 .B \ atop -B 5 .PP Monitor the current system load in text mode with an interval of 4 seconds with the possibility to browse back and forth while measuring continues (twin mode): .PP .TP 12 .B \ atop -t 4 .PP Live monitoring of the current system load while writing the measurement to a raw log in parallel (to be viewed later on): .PP .TP 12 .B \ atop -g -w /tmp/sessionx .PP View the cgroups with the related processes (except kernel processes) with the possibility to review previous samples during the live measurement: .PP .TP 12 .B \ atop -G8 -t .PP Store information about the system and process activity in binary compressed form to a file with an interval of ten minutes during an hour: .PP .TP 12 .B \ atop -w /tmp/atop.raw 600 6 .PP View the contents of this file interactively: .PP .B \ atop -r /tmp/atop.raw .PP View the processor and disk utilization of this file in parsable format: .PP .B \ atop -PCPU,DSK -r /tmp/atop.raw .PP View the contents of today's standard logfile interactively: .PP .B \ atop -r .PP View the contents of the standard logfile of the day before yesterday interactively: .PP .B \ atop -r yy .PP View the contents of the standard logfile of 2024, November 5 from 02:00 PM onwards interactively: .PP .B \ atop -r 20241105 -b 1400 .PP Read the contents of today's logfile and show the CPU utilization on system level as parseable output and JSON output: .PP .B \ atop -r -PCPU -JCPU .PP Concatenate all raw log files of November 2024 and generate parsable output about the CPU utilization: .PP .TP 12 .B \ atopcat /var/log/atop/atop_202403?? | atop -r - -PCPU .PP Monitor the system load and write it to a file (in plain ASCII) with an interval of one minute during half an hour with active processes sorted on memory consumption: .PP .TP 12 .B \ atop -M 60 30 > /log/atop.mem .PP .SH FILES .PP .TP 5 .B /var/run/pacct_shadow.d/ Directory containing the process accounting shadow files that are used by .I atop when the .I atopacctd daemon is active. .PP .TP 5 .B /var/cache/atop.d/atop.acct File in which the kernel writes the accounting records when .I atop itself has activated the process accounting mechanism. .PP .TP 5 .B /etc/atoprc Configuration file containing system-wide default values. For further information about the default values, refer to the .B atoprc man page). .PP .TP 5 .B ~/.atoprc Configuration file containing personal default values. For further information about the default values, refer to the .B atoprc man page). .PP .TP 5 .B /etc/default/atop Configuration file to overrule the settings of .I atop that runs in the background to create the daily logfile. This file is created when .I atop is installed. The default settings are: LOGOPTS="" .br LOGINTERVAL=600 .br LOGGENERATIONS=28 .PP .TP 5 .BI /var/log/atop/atop_ YYYYMMDD Raw file, where .I YYYYMMDD are digits representing the current date. This name is used by .B atop running in the background as default name for the output file, and by .B atop as default name for the input file when using the .B -r flag. .br All binary system and process level data in this file has been stored in compressed format. .PP .TP 5 .BI /var/run/netatop.log File that contains the netpertask structs containing the network counters of exited processes. These structs are written by the .I netatopd daemon (which is related to the .I netatop module) and read by .I atop after reading the standard process accounting records. .SH SEE ALSO .B atopsar(1), .B atopconvert(1), .B atopcat(1), .B atophide(1), .B atoprc(5), .B atopacctd(8), .B netatop(4), .B netatopd(8), .B atopgpud(8), .B logrotate(8) .br .B https://www.atoptool.nl .SH AUTHOR Gerlof Langeveld (gerlof.langeveld@atoptool.nl) .br JC van Winkel atop-2.12.1/man/atophide.10000644000203100020310000000216715064552761014513 0ustar gerlofgerlof.TH ATOPHIDE 1 "January 2024" "Linux" .SH NAME .B atophide - partly copy raw log file and/or anonymize raw log .SH SYNOPSIS .P .B atophide [-a] [\-b .I YYYYMMDDhhmm ] [\-e .I YYYYMMDDhhmm ] rawinput rawoutput .P .SH DESCRIPTION The program .I atophide can be used to make an extraction from an input raw log to an output raw log, specifying a begin time with the .B -b and/or an end time with the .B -e flag. With the .B -a flag the output rawlog will be anonymized: .PP .TP 5 .B Host name The host name in the header line will be replaced by 'anonymized'. .PP .TP 5 .B Command names Command names will be replaced by place holders, except the names of the standard commands and the names of kernel processes. .PP .TP 5 .B Command arguments The command line arguments of .I all commands will be wiped. .PP .TP 5 .B Logical volumes Logical volume names will be replaced by place holders. .PP .TP 5 .B NFS shares NFS mounted shared volume names will be replaced by place holders. .SH SEE ALSO .B atop(1), .B atopsar(1), .B atopcat(1), .B atopconvert(1) .br .B https://www.atoptool.nl .SH AUTHOR Gerlof Langeveld (gerlof.langeveld@atoptool.nl) atop-2.12.1/man/atopsar.10000644000203100020310000010005315064552761014360 0ustar gerlofgerlof.TH ATOPSAR 1 "January 2024" "Linux" .SH NAME .B atopsar - Advanced System Activity Report (atop related) .SH SYNOPSIS .P .B atopsar [\-flags...] [\-r .I file|date|- ] [\-R .I cnt ] [\-b .I [YYYYMMDD]hhmm[ss] ] [\-e .I [YYYYMMDD]hhmm[ss] ] .br .B atopsar [\-flags...] .I interval [ .I samples ] .P .SH DESCRIPTION The program .I atopsar can be used to report statistics on system level. .PP In the first synopsis line (no sampling interval specified), .I atopsar extracts data from a raw logfile that has been recorded previously by the program .I atop (option .B -w of the .I atop program). .br You can specify the name of the logfile with the .B -r option of the .I atopsar program. When a daily logfile of .I atop is used, named .B /var/log/atop/atop_YYYYMMDD (where YYYYMMDD reflects the date), the required date of the form YYYYMMDD can be specified with the .B -r option instead of the filename, or the symbolic name 'y' can be used for yesterday's daily logfile (this can be repeated so 'yyyy' indicates the logfile of four days ago), or the filename '-' can be used to read raw data from stdin. If the .B -r option is not specified at all, today's daily logfile is used by default. .br The starting and ending times of the report can be defined using the options .B -b and .B -e followed by a time argument of the form [YYYYMMDD]hhmm[ss]. .PP In the second synopsis line, .B atopsar reads actual activity counters from the kernel with the specified .I interval (in seconds) and the specified number of .I samples (optionally). When .B atopsar is activated in this way it immediately sends the output for every requested report to standard output. If only one type of report is requested, the header is printed once and after every .I interval seconds the statistical counters are shown for that period. If several reports are requested, a header is printed per sample followed by the statistical counters for that period. .PP Some generic flags can be specified to influence the behaviour of the .B atopsar program: .PP .TP 5 .B -S By default the timestamp at the beginning of a line is suppressed if more lines are shown for one interval. With this flag a timestamp is given for every output-line (easier for post-processing). .PP .TP 5 .B -a By default certain resources as disks and network interfaces are only shown when they were active during the interval. With this flag all resources of a given type are shown, even if they were inactive during the interval. .PP .TP 5 .B -x By default .B atopsar only uses colors if output is directed to a terminal (window). These colors might indicate that a critical occupation percentage has been reached (red) or has been almost reached (cyan) for a particular resource. See the man-page of .B atop for a detailed description of this feature (section COLORS). .br With the flag .B -x the use of colors is suppressed unconditionally. .PP .TP 5 .B -C By default .B atopsar only uses colors if output is directed to a terminal (window). These colors might indicate that a critical occupation percentage has been reached (red) or has been almost reached (cyan) for a particular resource. See the man-page of .B atop for a detailed description of this feature (section COLORS). .br With the flag .B -C colors will always be used, even if output is not directed to a terminal. .PP .TP 5 .B -M Use markers at the end of a line to indicate that a critical occupation percentage has been reached ('*') or has been almost reached ('+') for particular resources. The marker '*' is similar to the color red and the marker '+' to the color cyan. See the man-page of .B atop for a detailed description of these colors (section COLORS). .PP .TP 5 .B -H Repeat the header line within a report for every .I N detail lines. The value of .I N is determined dynamically in case of output to a tty/window (depending on the number of lines); for output to a file or pipe this value is 23. .PP .TP 5 .B -R Summarize .I cnt samples into one sample. When the logfile contains e.g. samples of 10 minutes, the use of the flag '\-R 6' shows a report with one sample for every hour. .PP Other flags are used to define which reports are required: .PP .TP 5 .B -A Show all possible reports. .PP .TP 5 .B -c Report about CPU utilization (in total and per cpu). .PP .TP 5 .B -g Report about GPU utilization (per GPU). .PP .TP 5 .B -p Report about processor-related matters, like load-averages and hardware interrupts. .PP .TP 5 .B -P Report about processes. .PP .TP 5 .B -m Current memory- and swap-occupation. .PP .TP 5 .B -s Report about paging- and swapping-activity, and overcommitment. .PP .TP 5 .B -B Report about Pressure Stall Information (PSI). .PP .TP 5 .B -l Report about utilization of logical volumes. .PP .TP 5 .B -f Report about utilization of multiple devices. .PP .TP 5 .B -d Report about utilization of disks. .PP .TP 5 .B -n Report about NFS mounted filesystems on NFS client. .PP .TP 5 .B -j Report about NFS client activity. .PP .TP 5 .B -J Report about NFS server activity. .PP .TP 5 .B -i Report about the network interfaces. .PP .TP 5 .B -I Report about errors for network-interfaces. .PP .TP 5 .B -w Report about IP version 4 network traffic. .PP .TP 5 .B -W Report about errors for IP version 4 traffic. .PP .TP 5 .B -y General report about ICMP version 4 layer activity. .PP .TP 5 .B -Y Per-type report about ICMP version 4 layer activity. .PP .TP 5 .B -u Report about UDP version 4 network traffic. .PP .TP 5 .B -z Report about IP version 6 network traffic. .PP .TP 5 .B -Z Report about errors for IP version 6 traffic. .PP .TP 5 .B -k General report about ICMP version 6 layer activity. .PP .TP 5 .B -K Per-type report about ICMP version 6 layer activity. .PP .TP 5 .B -U Report about UDP version 6 network traffic. .PP .TP 5 .B -t Report about TCP network traffic. .PP .TP 5 .B -T Report about errors for TCP-traffic. .PP .TP 5 .B -h Report about Infiniband utilization. .PP .TP 5 .B -O Report about top-3 processes consuming most processor capacity. This report is only available when using a log file (not when specifying an interval). .PP .TP 5 .B -G Report about top-3 processes consuming most resident memory. This report is only available when using a log file (not when specifying an interval). .PP .TP 5 .B -D Report about top-3 processes issuing most disk transfers. This report is only available when using a log file (not when specifying an interval). .PP .TP 5 .B -N Report about top-3 processes issuing most IPv4/IPv6 socket transfers. This report is only available when using a log file (not when specifying an interval). .SH OUTPUT DESCRIPTION Depending on the requested report, a number of columns with output values are produced. The values are mostly presented as a number of events per second. .PP The output for the flag .B -c contains the following columns per cpu: .TP 12 .B usr% Percentage of cpu-time consumed in user mode (program text) for all active processes running with a nice value of zero (default) or a negative nice value (which means a higher priority than usual). The cpu consumption in user mode of processes with a nice value larger than zero (lower priority) is indicated in the nice%-column. .TP 12 .B nice% Percentage of cpu time consumed in user mode (i.e. program text) for all processes running witn a nice value larger than zero (which means with a lower priority than average). .TP 12 .B sys% Percentage of cpu time consumed in system mode (kernel text) for all active processes. A high percentage usually indicates a lot of system calls being issued. .TP 12 .B irq% Percentage of cpu time consumed for handling of device interrupts. .TP 12 .B softirq% Percentage of cpu time consumed for soft interrupt handling. .TP 12 .B steal% Percentage of cpu time stolen by other virtual machines running on the same hardware. .TP 12 .B guest% Percentage of cpu time used by other virtual machines running on the same hardware (overlaps with usr%/nice%). .TP 12 .B wait% Percentage of unused cpu time while at least one of the processes in wait-state awaits completion of disk I/O. .TP 12 .B idle% Percentage of unused cpu time because all processes are in a wait-state but not waiting for disk-I/O. .PP The output for the flag .B -g contains the following columns per GPU: .TP 12 .B busaddr GPU number and bus-ID (separated by '/'). .TP 12 .B gpubusy GPU busy percentage during interval. .TP 12 .B membusy GPU memory busy percentage during interval, i.e. time to issue read and write accesses on memory. .TP 12 .B memocc Percentage of memory occupation at this moment. .TP 12 .B memtot Total memory available. .TP 12 .B memuse Used GPU memory at this moment. .TP 12 .B gputype Type of GPU. .PP The output for the flag .B -p contains the following values: .TP 12 .B pswch/s Number of process switches (also called context switches) per second on this cpu. A process switch occurs at the moment that an active thread (i.e. the thread using a cpu) enters a wait state or has used its time slice completely; another thread will then be chosen to use the cpu. .TP 12 .B devintr/s Number of hardware interrupts handled per second on this cpu. .TP 12 .B clones/s The number of new threads started per second. .TP 12 .B loadavg1 Load average reflecting the average number of threads in the runqueue or in non-interruptible wait state (usually waiting for disk or tape I/O) during the last minute. .TP 12 .B loadavg5 Load average reflecting the average number of threads in the runqueue or in non-interruptible wait state (usually waiting for disk or tape I/O) during the last 5 minutes. .TP 12 .B loadavg15 Load average reflecting the average number of threads in the runqueue or in non-interruptible wait state (usually waiting for disk or tape I/O) during the last 15 minutes. .PP The output for the flag .B -P contains information about the processes and threads: .TP 12 .B clones/s The number of new threads started per second. .TP 12 .B pexit/s .TP 12 .B curproc Total number of processes present in the system. .TP 12 .B curzomb Number of zombie processes present in the system. .TP 12 .B trun Total number of threads present in the system in state 'running'. .TP 12 .B tslpi Total number of threads present in the system in state 'interruptible sleeping'. .TP 12 .B tslpu Total number of threads present in the system in state 'uninterruptible sleeping'. .TP 12 .B tidle Total number of threads present in the system in state 'idle' (uninterruptible sleeping but not counted in the load average). .PP The output for the flag .B -m contains information about the memory- and swap-utilization: .TP 12 .B memtotal Total usable main memory size. .TP 12 .B memfree Available main memory size at this moment (snapshot). .TP 12 .B buffers Main memory used at this moment to cache metadata-blocks (snapshot). .TP 12 .B cached Main memory used at this moment to cache data-blocks (snapshot). .TP 12 .B dirty Amount of memory in the page cache that still has to be flushed to disk at this moment (snapshot). .TP 12 .B slabmem Main memory used at this moment for dynamically allocated memory by the kernel (snapshot). .TP 12 .B swptotal Total swap space size at this moment (snapshot). .TP 12 .B swpfree Available swap space at this moment (snapshot). .PP The output for the flag .B -s contains information about the frequency of swapping: .TP 12 .B pagescan/s Number of scanned pages per second due to the fact that free memory drops below a particular threshold. .TP 12 .B swapin/s The number of memory-pages the system read from the swap-device per second. .TP 12 .B swapout/s The number of memory-pages the system wrote to the swap-device per second. .TP 12 .B oomkill The number of processes being killed during the last interval due to lack of memory/swap. The value -1 means that this counter is not supported by the current kernel version. .TP 12 .B commitspc The committed virtual memory space i.e. the reserved virtual space for all allocations of private memory space for processes. .TP 12 .B commitlim The maximum limit for the committed space, which is by default swap size plus 50% of memory size. The kernel only verifies whether the committed space exceeds the limit if strict overcommit handling is configured (vm.overcommit_memory is 2). .PP The output for the flag .B -B contains the Pressure Stall Information (PSI): .TP 12 .B cpusome Average pressure percentage during the interval for the category 'CPU some'. .TP 12 .B memsome Average pressure percentage during the interval for the category 'memory some'. .TP 12 .B memfull Average pressure percentage during the interval for the category 'memory full'. .TP 12 .B iosome Average pressure percentage during the interval for the category 'I/O some'. .TP 12 .B iofull Average pressure percentage during the interval for the category 'I/O full'. .PP The output for the flags .B -l (LVM), .B -f (MD), and .B -d (hard disk) contains the following columns per active unit: .TP 12 .B disk Name. .TP 12 .B busy Busy-percentage of the unit (i.e. the portion of time that the device was busy handling requests). .TP 12 .B read/s Number of read-requests issued per second on this unit. .TP 12 .B KB/read Average number of Kbytes transferred per read-request for this unit. .TP 12 .B writ/s Number of write-requests (including discard requests) issued per second on this unit. .TP 12 .B KB/writ Average number of Kbytes transferred per write-request for this unit. .TP 12 .B avque Average number of requests outstanding in the queue during the time that the unit is busy. .TP 12 .B avserv Average number of milliseconds needed by a request on this unit (seek, latency and data-transfer). .PP The output for the flag .B -n contains information about activity on NFS mounted filesystems (client): .TP 12 .B mounted_device Mounted device containing server name and server directory being mounted. .TP 12 .B physread/s Kilobytes data physically read from the NFS server by processes running on the NFS client. .TP 12 .B KBwrite/s Kilobytes data physically written to the NFS server by processes running on the NFS client. .br When the NFS filesystem was mounted during the interval, the state 'M' is shown. .PP The output for the flag .B -j contains information about NFS client activity: .TP 12 .B rpc/s Number of RPC calls per second issued to NFS server(s). .TP 12 .B rpcread/s Number of read RPC calls per second issued to NFS server(s). .TP 12 .B rpcwrite/s Number of write RPC calls per second issued to NFS server(s). .TP 12 .B retrans/s Number of retransmitted RPC calls per second. .TP 12 .B autrefresh/s Number of authorization refreshes per second. .PP The output for the flag .B -J contains information about NFS server activity: .TP 12 .B rpc/s Number of RPC calls per second received from NFS client(s). .TP 12 .B rpcread/s Number of read RPC calls per second received from NFS client(s). .TP 12 .B rpcwrite/s Number of write RPC calls per second received from NFS client(s). .TP 12 .B MBcr/s Number of Megabytes per second returned to read requests by clients. .TP 12 .B MBcw/s Number of Megabytes per second passed in write requests by clients. .TP 12 .B nettcp/s Number of requests per second handled via TCP. .TP 12 .B netudp/s Number of requests per second handled via UDP. .PP The output for the flag .B -i provides information about utilization of network interfaces: .TP 12 .B interf Name of interface. .TP 12 .B busy Busy percentage for this interface. If the linespeed of this interface could not be determined (for virtual interfaces or in case that .B atop or .B atopsar had no root-privileges), a question mark is shown. .TP 12 .B ipack/s Number of packets received from this interface per second. .TP 12 .B opack/s Number of packets transmitted to this interface per second. .TP 12 .B iKbyte/s Number of Kbytes received from this interface per second. .TP 12 .B oKbyte/s Number of Kbytes transmitted via this interface per second. .TP 12 .B imbps/s Effective number of megabits received per second. .TP 12 .B ombps/s Effective number of megabits transmitted per second. .TP 12 .B maxmbps/s Linespeed as number of megabits per second. If the linespeed could not be determined (for virtual interfaces or in case that .B atop or .B atopsar had no root-privileges), value 0 is shown. .br The linespeed is followed by the indication 'f' (full duplex) or 'h' (half duplex). .PP The output for the flag .B -I provides information about the failures that were detected for network interfaces: .TP 12 .B interf Name of interface. .TP 12 .B ierr/s Number of bad packets received from this interface per second. .TP 12 .B oerr/s Number of times that packet transmission to this interface failed per second. .TP 12 .B coll/s Number of collisions encountered per second while transmitting packets. .TP 12 .B idrop/s Number of received packets dropped per second due to lack of buffer-space in the local system. .TP 12 .B odrop/s Number of transmitted packets dropped per second due to lack of buffer-space in the local system. .TP 12 .B iframe/s Number of frame alignment-errors encountered per second on received packets. .TP 12 .B ocarrier/s Number of carrier-errors encountered per second on transmitted packets. .PP The output for the flag .B -w provides information about the utilization of the IPv4-layer (formal SNMP-names between brackets): .TP 12 .B inrecv/s Number of IP datagrams received from interfaces per second, including those received in error (ipInReceives). .TP 12 .B outreq/s Number of IP datagrams that local higher-layer protocols supplied to IP in requests for transmission per second (ipOutRequests). .TP 12 .B indeliver/s Number of received IP datagrams that have been successfully delivered to higher protocol-layers per second (ipInDelivers). .TP 12 .B forward/s Number of received IP datagrams per second for which this entity was not their final IP destination, as a result of which an attempt was made to forward (ipForwDatagrams). .TP 12 .B reasmok/s Number of IP datagrams successfully reassembled per second (ipReasmOKs). .TP 12 .B fragcreat/s Number of IP datagram fragments generated per second at this entity (ipFragCreates). .PP The output for the flag .B -W provides information about the failures that were detected in the IPv4-layer (formal SNMP-names between brackets): .TP 12 .B in: dsc/s Number of input IP datagrams per second for which no problems were encountered to prevent their continued processing but that were discarded, e.g. for lack of buffer space (ipInDiscards). .TP 12 .B in: hder/s Number of input IP datagrams per second discarded due to errors in the IP header (ipInHdrErrors). .TP 12 .B in: ader/s Number of input IP datagrams per second discarded because the IP address in the destination field was not valid to be received by this entity (ipInAddrErrors). .TP 12 .B in: unkp/s Number of inbound packets per second that were discarded because of an unknown or unsupported protocol (ipInUnknownProtos). .TP 12 .B in: ratim/s Number of timeout-situations per second while other fragments were expected for successful reassembly (ipReasmTimeout). .TP 12 .B in: rfail/s Number of failures detected per second by the IP reassembly algorithm (ipReasmFails). .TP 12 .B out: dsc/s Number of output IP datagrams per second for which no problems were encountered to prevent their continued processing but that were discarded, e.g. for lack of buffer space (ipOutDiscards). .TP 12 .B out: nrt/s Number of IP datagrams per second discarded because no route could be found (ipOutNoRoutes). .PP The output for the flag .B -y provides information about the general utilization of the ICMPv4-layer and some information per type of ICMP-message (formal SNMP-names between brackets): .TP 12 .B intot/s Number of ICMP messages (any type) received per second at this entity (icmpInMsgs). .TP 12 .B outtot/s Number of ICMP messages (any type) transmitted per second from this entity (icmpOutMsgs). .TP 12 .B inecho/s Number of ICMP Echo (request) messages received per second (icmpInEchos). .TP 12 .B inerep/s Number of ICMP Echo-Reply messages received per second (icmpInEchoReps). .TP 12 .B otecho/s Number of ICMP Echo (request) messages transmitted per second (icmpOutEchos). .TP 12 .B oterep/s Number of ICMP Echo-Reply messages transmitted per second (icmpOutEchoReps). .PP The output for the flag .B -Y provides information about other types of ICMPv4-messages (formal SNMP-names between brackets): .TP 12 .B ierr/s Number of ICMP messages received per second but determined to have ICMP-specific errors (icmpInErrors). .TP 12 .B isq/s Number of ICMP Source Quench messages received per second (icmpInSrcQuenchs). .TP 12 .B ird/s Number of ICMP Redirect messages received per second (icmpInRedirects). .TP 12 .B idu/s Number of ICMP Destination Unreachable messages received per second (icmpInDestUnreachs). .TP 12 .B ite/s Number of ICMP Time Exceeded messages received per second (icmpOutTimeExcds). .TP 12 .B oerr/s Number of ICMP messages transmitted per second but determined to have ICMP-specific errors (icmpOutErrors). .TP 12 .B osq/s Number of ICMP Source Quench messages transmitted per second (icmpOutSrcQuenchs). .TP 12 .B ord/s Number of ICMP Redirect messages transmitted per second (icmpOutRedirects). .TP 12 .B odu/s Number of ICMP Destination Unreachable messages transmitted per second (icmpOutDestUnreachs). .TP 12 .B ote/s Number of ICMP Time Exceeded messages transmitted per second (icmpOutTimeExcds). .PP The output for the flag .B -u provides information about the utilization of the UDPv4-layer (formal SNMP-names between brackets): .TP 12 .B indgram/s Number of UDP datagrams per second delivered to UDP users (udpInDatagrams). .TP 12 .B outdgram/s Number of UDP datagrams transmitted per second from this entity (udpOutDatagrams). .TP 12 .B inerr/s Number of received UDP datagrams per second that could not be delivered for reasons other than the lack of an application at the destination port (udpInErrors). .TP 12 .B noport/s Number of received UDP datagrams per second for which there was no application at the destination port (udpNoPorts). .PP The output for the flag .B -z provides information about the utilization of the IPv6-layer (formal SNMP-names between brackets): .TP 12 .B inrecv/s Number of input IPv6-datagrams received from interfaces per second, including those received in error (ipv6IfStatsInReceives). .TP 12 .B outreq/s Number of IPv6-datagrams per second that local higher-layer protocols supplied to IP in requests for transmission (ipv6IfStatsOutRequests). This counter does not include any forwarded datagrams. .TP 12 .B inmc/s Number of multicast packets per second that have been received by the interface (ipv6IfStatsInMcastPkts). .TP 12 .B outmc/s Number of multicast packets per second that have been transmitted to the interface (ipv6IfStatsOutMcastPkts). .TP 12 .B indeliv/s Number of IP datagrams successfully delivered per second to IPv6 user-protocols, including ICMP (ipv6IfStatsInDelivers). .TP 12 .B reasmok/s Number of IPv6 datagrams successfully reassembled per second (ipv6IfStatsReasmOKs). .TP 12 .B fragcre/s Number of IPv6 datagram fragments generated per second at this entity (ipv6IfStatsOutFragCreates). .PP The output for the flag .B -Z provides information about the failures that were detected in the IPv6-layer (formal SNMP-names between brackets): .TP 12 .B in: dsc/s Number of input IPv6 datagrams per second for which no problems were encountered to prevent their continued processing but that were discarded, e.g. for lack of buffer space (ipv6IfStatsInDiscards). .TP 12 .B in: hder/s Number of input datagrams per second discarded due to errors in the IPv6 header (ipv6IfStatsInHdrErrors). .TP 12 .B in: ader/s Number of input datagrams per second discarded because the IPv6 address in the destination field was not valid to be received by this entity (ipv6IfStatsInAddrErrors). .TP 12 .B in: unkp/s Number of locally-addressed datagrams per second that were discarded because of an unknown or unsupported protocol (ipv6IfStatsInUnknownProtos). .TP 12 .B in: ratim/s Number of timeout-situations per second while other IPv6 fragments were expected for successful reassembly (ipv6ReasmTimeout). .TP 12 .B in: rfail/s Number of failures detected per second by the IPv6 reassembly-algorithm (ipv6IfStatsReasmFails). .TP 12 .B out: dsc/s Number of output IPv6 datagrams per second for which no problems were encountered to prevent their continued processing but that were discarded, e.g. for lack of buffer space (ipv6IfStatsOutDiscards). .TP 12 .B out: nrt/s Number of IPv6 datagrams per second discarded because no route could be found (ipv6IfStatsInNoRoutes). .PP The output for the flag .B -k provides information about the general utilization of the ICMPv6-layer and some information per type of ICMP-message (formal SNMP-names between brackets): .TP 12 .B intot/s Number of ICMPv6 messages (any type) received per second at the interface (ipv6IfIcmpInMsgs). .TP 12 .B outtot/s Number of ICMPv6 messages (any type) transmitted per second from this entity (ipv6IfIcmpOutMsgs). .TP 12 .B inerr/s Number of ICMPv6 messages received per second that had ICMP-specific errors, such as bad ICMP checksums, bad length, etc (ipv6IfIcmpInErrors). .TP 12 .B innsol/s Number of ICMP Neighbor Solicit messages received per second (ipv6IfIcmpInNeighborSolicits). .TP 12 .B innadv/s Number of ICMP Neighbor Advertisement messages received per second (ipv6IfIcmpInNeighborAdvertisements). .TP 12 .B otnsol/s Number of ICMP Neighbor Solicit messages transmitted per second (ipv6IfIcmpOutNeighborSolicits). .TP 12 .B otnadv/s Number of ICMP Neighbor Advertisement messages transmitted per second (ipv6IfIcmpOutNeighborAdvertisements). .PP The output for the flag .B -K provides information about other types of ICMPv6-messages (formal SNMP-names between brackets): .TP 12 .B iecho/s Number of ICMP Echo (request) messages received per second (ipv6IfIcmpInEchos). .TP 12 .B ierep/s Number of ICMP Echo-Reply messages received per second (ipv6IfIcmpInEchoReplies). .TP 12 .B oerep/s Number of ICMP Echo-Reply messages transmitted per second (ipv6IfIcmpOutEchoReplies). .TP 12 .B idu/s Number of ICMP Destination Unreachable messages received per second (ipv6IfIcmpInDestUnreachs). .TP 12 .B odu/s Number of ICMP Destination Unreachable messages transmitted per second (ipv6IfIcmpOutDestUnreachs). .TP 12 .B ird/s Number of ICMP Redirect messages received per second (ipv6IfIcmpInRedirects). .TP 12 .B ord/s Number of ICMP Redirect messages transmitted per second (ipv6IfIcmpOutRedirect). .TP 12 .B ite/s Number of ICMP Time Exceeded messages received per second (ipv6IfIcmpInTimeExcds). .TP 12 .B ote/s Number of ICMP Time Exceeded messages transmitted per second (ipv6IfIcmpOutTimeExcds). .PP The output for the flag .B -U provides information about the utilization of the UDPv6-layer (formal SNMP-names between brackets): .TP 12 .B indgram/s Number of UDPv6 datagrams per second delivered to UDP users (udpInDatagrams), .TP 12 .B outdgram/s Number of UDPv6 datagrams transmitted per second from this entity (udpOutDatagrams), .TP 12 .B inerr/s Number of received UDPv6 datagrams per second that could not be delivered for reasons other than the lack of an application at the destination port (udpInErrors). .TP 12 .B noport/s Number of received UDPv6 datagrams per second for which there was no application at the destination port (udpNoPorts). .PP The output for the flag .B -t provides information about the utilization of the TCP-layer (formal SNMP-names between brackets): .TP 12 .B insegs/s Number of received segments per second, including those received in error (tcpInSegs). .TP 12 .B outsegs/s Number of transmitted segments per second, excluding those containing only retransmitted octets (tcpOutSegs). .TP 12 .B actopen/s Number of active opens per second that have been supported by this entity (tcpActiveOpens). .TP 12 .B pasopen/s Number of passive opens per second that have been supported by this entity (tcpPassiveOpens). .TP 12 .B nowopen Number of connections currently open (snapshot), for which the state is either ESTABLISHED or CLOSE-WAIT (tcpCurrEstab). .PP The output for the flag .B -T provides information about the failures that were detected in the TCP-layer (formal SNMP-names between brackets): .TP 12 .B inerr/s Number of received segments per second received in error (tcpInErrs). .TP 12 .B retrans/s Number of retransmitted segments per second (tcpRetransSegs). .TP 12 .B attfail/s Number of failed connection attempts per second that have occurred at this entity (tcpAttemptFails). .TP 12 .B estabreset/s Number of resets per second that have occurred at this entity (tcpEstabResets). .TP 12 .B outreset/s Number of transmitted segments per second containing the RST flag (tcpOutRsts). .PP The output for the flag .B -h provides information about utilization of Infiniband ports: .TP 12 .B controller Name of controller. .TP 12 .B port Controller port. .TP 12 .B busy Busy percentage for this port. .TP 12 .B ipack/s Number of packets received from this port per second. .TP 12 .B opack/s Number of packets transmitted to this port per second. .TP 12 .B igbps/s Effective number of gigabits received per second. .TP 12 .B ogbps/s Effective number of gigabits transmitted per second. .TP 12 .B maxgbps/s Maximum rate as number of gigabits per second. .TP 12 .B lanes Number of lanes. .PP The output for the flag .B -O provides information about the top-3 of processes with the highest processor consumption: .TP 12 .B pid Process-id (if zero, the process has exited while the pid could not be determined). .TP 12 .B command The name of the process. .TP 12 .B cpu% The percentage of cpu-capacity being consumed. This value can exceed 100% for a multithreaded process running on a multiprocessor machine. .PP The output for the flag .B -G provides information about the top-3 of processes with the highest memory consumption: .TP 12 .B pid Process-id (if zero, the process has exited while the pid could not be determined). .TP 12 .B command The name of the process. .TP 12 .B mem% The percentage of resident memory-utilization by this process. .PP The output for the flag .B -D provides information about the top-3 of processes that issue the most read and write accesses to disk: .TP 12 .B pid Process-id (if zero, the process has exited while the pid could not be determined). .TP 12 .B command The name of the process. .TP 12 .B dsk% The percentage of read and write accesses related to the total number of read and write accesses issued on disk by all processes, so a high percentage does not imply a high disk load on system level. .PP The output for the flag .B -N provides information about the top-3 of processes that issue the most socket transfers for IPv4/IPv6: .TP 12 .B pid Process-id (if zero, the process has exited while the pid could not be determined). .TP 12 .B command The name of the process. .TP 12 .B net% The percentage of socket transfers related to the total number of transfers issued by all processes, so a high percentage does not imply a high network load on system level. .SH EXAMPLES To see today's cpu-activity so far (supposed that .B atop is logging in the background): .PP .TP 12 .B \ atopsar .PP To see the memory occupation for June 5, 2018 between 10:00 and 12:30 (supposed that .B atop has been logging daily in the background): .PP .TP 12 .B \ atopsar -m -r /var/log/atop_20180605 -b 10:00 -e 12:30 .br \ .br or .TP 12 .B \ atopsar -m -r 20180605 -b 10:00 -e 12:30 .br \ .br or, suppose it is June 8, 2018 at this moment .TP 12 .B \ atopsar -m -r yyy -b 10:00 -e 12:30 .PP Write a logfile with .B atop to record the system behaviour for 30 minutes (30 samples of one minute) and produce all available reports afterwards: .PP .TP 12 .B \ atop -w /tmp/atoplog 60 30 .TP 12 .B \ atopsar -A -r /tmp/atoplog .PP To watch TCP activity evolve for ten minutes (10 samples with sixty seconds interval): .PP .TP 12 .B \ atopsar -t 60 10 .PP To watch the header-lines ('_' as last character) of all reports with only the detail-lines showing critical resource consumption (marker '*' or '+' as last character): .PP .TP 12 .B \ atopsar -AM | grep '[_*+]$' .PP .SH FILES .PP .TP 5 .B /etc/atoprc Configuration file containing system-wide default values (mainly flags). See related man-page. .PP .TP 5 .B ~/.atoprc Configuration file containing personal default values (mainly flags). See related man-page. .PP .TP 5 .BI /var/log/atop/atop_ YYYYMMDD Daily data file, where .I YYYYMMDD are digits representing the date. .SH SEE ALSO .B atop(1), .B atoprc(5), .B atopcat(1), .B atophide(1), .B atopconvert(1), .B atopacctd(8), .B netatop(4), .B netatopd(8) .br .B https://www.atoptool.nl .SH AUTHOR Gerlof Langeveld (gerlof.langeveld@atoptool.nl) atop-2.12.1/man/atoprc.50000644000203100020310000003016415064552761014210 0ustar gerlofgerlof.TH ATOPRC 5 "January 2024" "Linux" .SH NAME .B atoprc - atop/atopsar related rcfile .SH DESCRIPTION This manual page documents the rcfile of the .I atop and .I atopsar commands. These commands can be used to monitor the system and process load on a Linux system. .PP The atoprc file contains the default settings. These settings are read during startup, first from the system-wide rcfile .I /etc/atoprc and after that from the user-specific rcfile .I ~/.atoprc (so system-wide settings can be overruled by an individual user). The options in both rcfiles are identical. .PP .SH OPTIONS .PP The rcfile contains keyword-value pairs, one on every line (blank lines and lines starting with a #-sign are ignored). .br The following keywords can be specified: .PP .TP 4 .B flags A list of default flags for .B atop can be defined here. The flags which are allowed are 't', 'B', 'H', 'G', '2' till '9', 'g', 'm', 'd', 'n', 'u', 'p', 's', 'c', 'v', \&'C', 'M', 'D', 'N', 'A', \&'a', 'y', 'Y', 'f', 'F', 'R', '1', 'e', 'E' and 'x'. .PP .TP 4 .B twindir The default absolute path name of the temporary directory in which the twin file should be allocated. .PP .TP 4 .B interval The default interval value in seconds. .PP .TP 4 .B linelen The length of a screen line when sending output to a file or pipe (default 80). .PP .TP 4 .B username Regular expression or one numerical UID to select the users for which (active) processes will be shown. .PP .TP 4 .B procname The default regular expression for the process names to be shown. .PP .TP 4 .B maxlinecpu The maximum number of active CPUs that will be shown. .PP .TP 4 .B maxlinegpu The maximum number of active GPUs that will be shown. .PP .TP 4 .B maxlinelvm The maximum number of active logical volumes that will be shown. .PP .TP 4 .B maxlinemdd The maximum number of active multiple devices that will be shown. .PP .TP 4 .B maxlinedisk The maximum number of active disks that will be shown. .PP .TP 4 .B maxlinenfsm The maximum number of NFS mounts that will be shown on an NFS client. .PP .TP 4 .B maxlineintf The maximum number of active network interfaces that will be shown. .PP .TP 4 .B maxlinecont The maximum number of active containers that will be shown. .PP .TP 4 .B maxlineifb The maximum number of Infiniband ports that will be shown. .PP .TP 4 .B maxlinenuma The maximum number of NUMA node metrics that will be shown. Notice that two lines are shown for each NUMA node. .PP .TP 4 .B maxlinellc The maximum number of lines with LLC metrics that will be shown. .PP .TP 4 .B cpucritperc The busy percentage considered critical for a processor (see section COLORS in the man-page of the .I atop command). This percentage is used to determine a weighted percentage for line coloring and sorting of active processes in text mode. When this value is zero, no line coloring or automatic sorting is performed for this resource. .PP .TP 4 .B dskcritperc The busy percentage considered critical for a disk (see section COLORS in the man-page of the .I atop command). This percentage is used to determine a weighted percentage for line coloring and sorting of active processes in text mode. When this value is zero, no line coloring or automatic sorting is performed for this resource. .PP .TP 4 .B netcritperc The busy percentage considered critical for a network interface (see section COLORS in the man-page of the .I atop command). This percentage is used to determine a weighted percentage for line coloring and sorting of active processes in text mode. When this value is zero, no line coloring or automatic sorting is performed for this resource. .PP .TP 4 .B memcritperc The percentage considered critical for memory utilization (see section COLORS in the man-page of the .I atop command). This percentage is used to determine a weighted percentage for line coloring and sorting of active processes in text mode. When this value is zero, no line coloring or automatic sorting is performed for this resource. .PP .TP 4 .B swpcritperc The occupation percentage considered critical for swap space (see section COLORS in the man-page of the .I atop command). This percentage is used to determine a weighted percentage for line coloring and sorting of active processes in text mode. When this value is zero, no line coloring or automatic sorting is performed for this resource. .PP .TP 4 .B swoutcritsec The number of pages swapped out per second considered critical for for memory utilization (see section COLORS in the man-page of the .I atop command). This threshold is used in combination with 'memcritperc' to determine a weighted percentage for line coloring and sorting of active processes in text mode. When this value is zero, no line coloring or automatic sorting is performed for this resource. .PP .TP 4 .B almostcrit A percentage of the critical percentage to determine if the resource is almost critical (see section COLORS in the man-page of the .I atop command). When this value is zero, no line coloring for `almost critical' is performed. .PP .TP 4 .B cpubarwidth Number of columns used per bar in the processor bar graph. The default value is 0 which means that the bar width will be scaled automatically (the wider the terminal, the more columns per bar up to a maximum of three). With the value 1, 2 or 3 the number of bars can be statically pinned to that number of columns, with one column of white space in between the bars. .PP .TP 4 .B colorinfo Definition of color name for information messages (default: green) in text mode. .br Allowed colors are: red green yellow blue magenta cyan black white. .PP .TP 4 .B colorthread Definition of color name for thread-specific lines when using the 'y' option (default: yellow). .br Allowed colors are: red green yellow blue magenta cyan black white. .PP .TP 4 .B coloralmost Definition of color name for almost critical resources (default: cyan) in text mode. .br Allowed colors are: red green yellow blue magenta cyan black white. .PP .TP 4 .B colorcritical Definition of color name for critical resources (default: red) in text mode. .br Allowed colors are: red green yellow blue magenta cyan black white. .PP .TP 4 .B atopsarflags A list of default flags for .B atopsar can be defined here. The flags that are allowed are 'S', 'x', 'C', 'M', 'H', 'a', 'A' and the flags to select one or more specific reports. .PP .TP 4 .B perfevents Defines whether or not the CPU cycle counter should be retrieved by .B atop via the 'perf' counters. The values 'auto' (default), 'enable' or 'disable' can be specified. In case of 'auto', the CPU cycle counter will not be retrieved on virtual machines due to the overhead of reading this counter in a guest. .PP .TP 4 .B pacctdir The name of the topdirectory used by the .B atopacctd daemon. In this directory, the daemon creates a subdirectory .B pacct_shadow.d in which files will be written containing the process accounting records. The default topdirectory is .B /var/run and this option only has to be specified when the .B atopacctd daemon is started with an alternative topdirectory as command line argument. .br This option can only be specified in the .B /etc/atoprc file (on system level)! .PP An example of the .B /etc/atoprc or .B ~/.atoprc file: .TP 8 \ .br flags\ \ \ \ \ \ \ \ \ Aaf .br interval\ \ \ \ \ \ 5 .br username .br procname .br maxlinecpu\ \ \ \ 4 .br maxlinedisk\ \ \ 10 .br maxlineintf\ \ \ 5 .br cpucritperc\ \ \ 80 .br almostcrit\ \ \ \ 90 .br atopsarflags\ \ CMH .br ownprocline\ \ \ PID:50 VGROW:40 RGROW:45 COMMAND-LINE:50 .br ownpagline\ \ \ \ PAGSCAN:3 BLANKBOX:0 PAGSWIN:3 PAGSWOUT:7 .PP The keywords 'ownprocline' and 'ownpagline' are explained in the subsequent section. .SH OWN DEFINITION OF OUTPUT LINE Via the rcfile it is possible to define the layout of the output lines yourself, i.e. you can define the layout of one line with process information with the keyword 'ownprocline' (to be selected with the key 'o' or the flag \-o) and you can redefine all lines with system information. .PP The layout of an output-line can be defined as follows (notice that this should be specified as one line in the rcfile): .PP \ \ \ keyword\ \ \ : [: ...] .PP The .B columnid is the symbolic name of a column that should shown at this position in the output line. .br The .B prio is a positive integer value that determines which columns have precedence whenever not all specified columns fit into the current screen-width. The higher value, the higher priority. .br The column-specifications should be separated by a space. The order in which columns have been specified is the order in which they will be shown, with respect to their priority (columns that do not fit, will be dropped dynamically). .PP A special columnid for system lines is 'BLANKBOX'. This indicates that an empty column is required at this position. Also this special columnid is followed by a priority (usually low). .PP The following definition can be specified for process information: .PP .TP 4 .B ownprocline The columnids are the names of the columns that are shown in the normal output of the process-related lines that are shown by .I atop such as 'PID', 'CMD', 'S', .... The only exception is the special columnid 'SORTITEM' that is used to show one of the columns CPU%/DSK%/MEM%/NET%, depending on the chosen sort-criterium. .br An example of a user-defined process line: .PP .TP 8 \ ownprocline\ \ \ PID:20 PPID:10 SYSCPU:15 USRCPU:15 VGROW:14 VSIZE:12 RGROW:14 RSIZE:12 ST:8 EXC:7 S:11 SORTITEM:18 CMD:20 .PP The following definitions are used internally by .I atop as the default system lines (you can redefine each of them in the rcfile as one line): .PP .TP 4 .B ownsysprcline Redefinition of line labeled with 'PRC': .PP .TP 8 \ ownsysprcline\ \ \ PRCSYS:8 PRCUSER:8 BLANKBOX:0 PRCNPROC:7 PRCNZOMBIE:5 PRCCLONES:4 BLANKBOX:0 PRCNNEXIT:6 .PP .TP 4 .B ownallcpuline Redefinition of line labeled with 'CPU' for total CPU-utilization: .PP .TP 8 \ ownallcpuline\ \ \ CPUSYS:8 CPUUSER:7 CPUIRQ:4 BLANKBOX:0 CPUIDLE:5 CPUWAIT:6 BLANKBOX:0 CPUSTEAL:1 CPUGUEST:3 .PP .TP 4 .B ownonecpuline Redefinition of line labeled with 'CPU' for utilization of one CPU: .PP .TP 8 \ ownonecpuline\ \ \ CPUISYS:8 CPUIUSER:7 CPUIIRQ:4 BLANKBOX:0 CPUIIDLE:5 CPUIWAIT:6 BLANKBOX:0 CPUISTEAL:1 CPUIGUEST:3 .PP .TP 4 .B owncplline Redefinition of line labeled with 'CPL': .PP .TP 8 \ owncplline\ \ \ CPLAVG1:4 CPLAVG5:3 CPLAVG15:2 BLANKBOX:0 CPLCSW:6 CPLINTR:5 BLANKBOX:0 CPLNUMCPU:1 .PP .TP 4 .B ownmemline Redefinition of line labeled with 'MEM': .PP .TP 8 \ ownmemline\ \ \ MEMTOT:2 MEMFREE:5 MEMCACHE:3 MEMDIRTY:1 MEMBUFFER:3 MEMSLAB:3 BLANKBOX:0 BLANKBOX:0 BLANKBOX:0 BLANKBOX:0 .PP .TP 4 .B ownswpline Redefinition of line labeled with 'SWP': .PP .TP 8 \ ownswpline\ \ \ SWPTOT:3 SWPFREE:4 BLANKBOX:0 BLANKBOX:0 BLANKBOX:0 BLANKBOX:0 BLANKBOX:0 BLANKBOX:0 SWPCOMMITTED:5 SWPCOMMITLIM:6 .PP .TP 4 .B ownpagline Redefinition of line labeled with 'PAG': .PP .TP 8 \ ownpagline\ \ \ PAGSCAN:3 PAGSTALL:1 BLANKBOX:0 PAGSWIN:4 PAGSWOUT:3 .PP .TP 4 .B owndskline Redefinition of lines labeled with 'LVM', 'MDD' and 'DSK': .PP .TP 8 \ owndskline\ \ \ DSKNAME:8 DSKBUSY:7 DSKNREAD:6 DSKNWRITE:6 DSKKBPERRD:4 DSKKBPERWR:4 DSKMBPERSECRD:5 DSKMBPERSECWR:5 DSKAVQUEUE:1 DSKAVIO:5 .PP .TP 4 .B ownnettrline Redefinition of line labeled with 'NET' for transport: .PP .TP 8 \ ownnettrline\ \ \ NETTRANSPORT:9 NETTCPI:8 NETTCPO:8 NETUDPI:8 NETUDPO:8 NETTCPACTOPEN:6 NETTCPPASVOPEN:5 NETTCPRETRANS:4 NETTCPINERR:3 NETTCPORESET:20 NETUDPNOPORT:1 NETUDPINERR:3 .PP .TP 4 .B ownnetnetline Redefinition of line labeled with 'NET' for network: .PP .TP 8 \ ownnetnetline\ \ \ NETNETWORK:5 NETIPI:4 NETIPO:4 NETIPFRW:4 NETIPDELIV:4 BLANKBOX:0 BLANKBOX:0 BLANKBOX:0 NETICMPIN:1 NETICMPOUT:1 .PP .TP 4 .B ownnetifline Redefinition of line labeled with 'NET' for interfaces: .PP .TP 8 \ ownnetifline\ \ \ NETNAME:8 NETPCKI:7 NETPCKO:7 NETSPEEDIN:6 NETSPEEDOUT:6 NETCOLLIS:3 NETMULTICASTIN:2 NETRCVERR:5 NETSNDERR:5 NETRCVDROP:4 NETSNDDROP:4 .PP The lines above are shown in the order as shown by .I atop in combination with the .B -f flag (in a very wide window you should be able to see all of the columns). .SH SEE ALSO .B atop(1), .B atopsar(1), .B atopacctd(8), .B netatop(4), .B netatopd(8), .B logrotate(8) .br .B https://www.atoptool.nl .SH AUTHOR Gerlof Langeveld (gerlof.langeveld@atoptool.nl) .br JC van Winkel atop-2.12.1/man/atopgpud.80000644000203100020310000001001115064552761014533 0ustar gerlofgerlof.TH ATOPGPUD 8 "January 2024" "Linux" .SH NAME .B atopgpud - GPU statistics daemon .SH SYNOPSIS .P .B atopgpud [-v] .PP .SH DESCRIPTION The .I atopgpud daemon gathers statistical information from all Nvidia GPUs in the current system. With a sampling rate of one second, it maintains the statistics of every GPU, globally (system level) and per process. When .I atopgpud is active on the target system, .I atop connects to this daemon via a TCP socket and obtains all GPU statistics with every interval. .PP The approach to gather all GPU statistics in a separate daemon is required, because the Nvidia driver only offers the GPU busy percentage of the last second. Suppose that .I atop runs with a 10-minute interval and would fetch the GPU busy percentage directly from the Nvidia driver, it would reflect the busy percentage of the last second instead of the average busy percentage during 600 seconds. Therefore, the .I atopgpud daemon fetches the GPU busy percentage every second and accumulates this into a counter that can be retrieved by .I atop regularly. The same approach applies to other GPU statistics. .PP When the .I atopgpud daemon runs with root privileges, more process level counters (i.e. GPU busy and GPU memory busy per process) are provided that are otherwise not applicable. .PP Notice that certain GPU statistics are only delivered for specific GPU types. For older or less sophisticated GPUs, the value -1 is returned for counters that are not maintained. In the output of .I atop these counters are shown as 'N/A'. .PP When no (Nvidia) GPUs can be found in the target system, .I atopgpud immediately terminates with exit code 0. .PP Log messages are written via the .I rsyslogd daemon with facility 'daemon'. With the -v flag (verbose), .I atopgpud also logs debug messages. .PP .SH INSTALLATION The .I atopgpud daemon is written in Python, so a Python interpreter should be installed on the target system. This can either be Python version 2 or Python version 3 (the code of .I atopgpud is written in a generic way). Take care that the first line of the .I atopgpud script contains the proper command name to activate a Python interpreter that is installed on the target system! .PP The .I atopgpud daemon depends on the Python module .I pynvml to interface with the Nvidia driver. This module can be installed by the .I pip or .I pip3 command and is usually packaged under the name .I nvidia-ml-py .br Finally, the .I pynvml module is a Python wrapper around the .I libnvidia-ml shared library that needs to be installed as well. .PP After installing the .I atop package, the .I atopgpud is not automatically started, nor will the service be enabed by default. When you want to activate this service (permanently), enter the following commands (as root): .PP .B \ systemctl enable atopgpu .br .B \ systemctl start atopgpu .PP .SH INTERFACE DESCRIPTION Client processes can connect to the .I atopgpud daemon on TCP port 59123. Subsequently, such client can send a request of two bytes, consisting of one byte request code followed by one byte integer being the API version number. .br The request code in the first byte can be 'T' to obtain information about the GPU types installed in this system (usually only requested once). .br The request code can be 'S' to obtain all statistical counter values (requested for every interval). .PP The response of the daemon starts with a 4-byte integer. The first byte is the API version number that determines the response format while the subsequent three bytes indicate the length (big endian order) of the response string that follows. .br In the response strings the character '@' introduces system level information of one specific GPU and the character '#' introduces process level information related to that GPU. .br For further details about the meaning of the counters in a response string, please consult the source code. .PP .SH SEE ALSO .B atop(1), .B atopsar(1), .B atoprc(5), .B netatop(4), .B netatopd(8), .B atopacctd(8) .br .B https://www.atoptool.nl .SH AUTHOR Gerlof Langeveld (gerlof.langeveld@atoptool.nl)