atop-2.10.0/0000775000203100020310000000000014545501444012045 5ustar gerlofgerlofatop-2.10.0/acctproc.c0000664000203100020310000006562214545501443014021 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); strncpy(api->gen.name, acctrec.ac_comm, PNAMLEN); api->gen.name[PNAMLEN] = '\0'; 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); strncpy(api->gen.name, acctrec_v3.ac_comm, PNAMLEN); api->gen.name[PNAMLEN] = '\0'; 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.10.0/acctproc.h0000664000203100020310000001262314545501443014017 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.10.0/atopacctd.c0000664000203100020310000006215614545501443014164 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. */ #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 char 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)); exit(7); } 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.10.0/atopacctd.h0000664000203100020310000000451714545501443014166 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.10.0/atop.c0000664000203100020310000007263114545501443013164 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-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 ** -------------------------------------------------------------------------- ** ** 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----|-+ ** | | | | | | | | | | ** | photosyst | | photoproc | | acct | | deviate | | print | ** | | | | |photoproc | | ...syst | | | ** | | | | | | | ...proc | | | ** +-----------+ +-----------+ +----------+ +----------+ +---------+ ** ^ ^ ^ ^ | ** | | | | | ** | | | V V ** ______ _________ __________ ________ _________ ** / \ / \ / \ / \ / \ ** /proc /proc accounting task screen or ** file database file ** \______/ \_________/ \__________/ \________/ \_________/ ** ** - photosyst() ** Takes a snapshot of the counters related to resource-usage on ** system-level (cpu, disk, memory, network). ** This code is UNIX-flavor dependent; in case of Linux the counters ** are retrieved from /proc. ** ** - 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. ** This code is UNIX-flavor dependent; in case of Linux the counters ** are retrieved from /proc. ** ** - 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 the system-level ** as well as on the task-level counters. ** These differences are stored in a new structure(-table). ** ** - 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. */ #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 "showgeneric.h" #include "showlinux.h" #include "parseable.h" #include "json.h" #include "gpucom.h" #include "netatop.h" #define allflags "ab:cde:fghijklmnopqrstuvwxyz:1ABCDEFGHIJ:KL:MNOP:QRSTUVWXYZ" #define MAXFL 64 /* 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 linelen = 80; char acctreason; /* accounting not active (return val) */ char rawname[RAWNAMESZ]; char rawreadflag; 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 */ 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 visualize vis = {generic_samp, generic_error, generic_end, generic_usage}; /* ** argument values */ static char awaittrigger; /* boolean: awaiting trigger */ static unsigned int nsamples = 0xffffffff; static char midnightflag; static char rawwriteflag; /* ** 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, }, { "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); 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 ( memcmp(p, "atopsar", 7) == 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 ? */ rawwriteflag++; if (optind >= argc) prusage(argv[0]); strncpy(rawname, argv[optind++], RAWNAMESZ-1); vis.show_samp = rawwrite; break; case 'B': /* bar graphs ? */ displaymode = 'D'; break; case 'H': /* bar graphs ? */ barmono = 1; break; case 'r': /* reading of raw data ? */ if (optind < argc) { if (*(argv[optind]) == '-') { if (strlen(argv[optind]) == 1) { strcpy(rawname, "/dev/stdin"); optind++; } } else { strncpy(rawname, argv[optind], RAWNAMESZ-1); optind++; } } rawreadflag++; break; case 'S': /* midnight limit ? */ midnightflag++; 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]); vis.show_samp = parseout; break; case 'J': /* json output? */ if ( !jsondef(optarg) ) prusage(argv[0]); vis.show_samp = jsonout; break; case 'L': /* line length */ if ( !numeric(optarg) ) prusage(argv[0]); linelen = atoi(optarg); break; case MALLPROC: /* all processes per sample ? */ 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; default: /* gather other flags */ flaglist[i++] = c; } } /* ** 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]); } } } /* ** 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 raw data from a file must be viewed */ if (rawreadflag) { rawread(); cleanstop(0); } /* ** determine start-time for gathering current statistics */ curtime = getboot() / hertz; /* ** 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); /* ** 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 */ 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"); /* ** 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 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 */ 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) gpupending = 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 && gpupending) { nrgpuproc = gpud_statresponse(nrgpus, cursstat->gpu.gpu, &gp); gpupending = 0; // 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, &gp); // persistent failure? if (nrgpuproc == -1) nrgpus = 0; } } } 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) gpumergeproc(curtpres, ntaskpres, curpexit, nprocexit, gp, nrgpuproc); /* ** calculate deviations */ deviattask(curtpres, ntaskpres, curpexit, nprocexit, &devtstat, devsstat); if (supportflags & NETATOPBPF) { g_hash_table_destroy(ghash_net); ghash_net = NULL; } /* ** activate the installed print-function to visualize ** the deviations */ lastcmd = (vis.show_samp)( curtime, curtime-pretime > 0 ? curtime-pretime : 1, &devtstat, devsstat, nprocexit, noverflow, sampcnt==0); /* ** release dynamically allocated memory */ if (nprocexit > 0) free(curpexit); free(curtpres); if ((supportflags & NETATOPD) && (nprocexitnet > 0)) { netatop_exiterase(); } if (gp) free(gp); if (lastcmd == 'r') /* 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(); } } /* end of main-loop */ } /* ** print usage of this command */ void prusage(char *myname) { printf("Usage: %s [-flags] [interval [samples]]\n", myname); printf("\t\tor\n"); printf("Usage: %s -w file [-S] [-%c] [interval [samples]]\n", myname, MALLPROC); printf(" %s -r [file] [-b [YYYYMMDD]hhmm[ss]] [-e [YYYYMMDD]hhmm[ss]] [-flags]\n", myname); printf("\n"); printf("\tgeneric flags:\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 version information\n", MVERSION); printf("\t -%c show or log all processes (i.s.o. active processes " "only)\n", MALLPROC); 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"); if (vis.show_usage) (*vis.show_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); } } atop-2.10.0/atopcat.c0000664000203100020310000001505614545501443013652 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 ** -------------------------------------------------------------------------- */ #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; unsigned short aversion; // 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; 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\n", "date", "time", "interval", "comprsys", "comprproc"); } } 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); } } // read every raw record followed by the compressed // system-level and process-level stats // while ( read(fd, &rr, sizeof rr) == sizeof rr ) { if (beverbose) { fprintf(stderr, "%19s %12u %8u %9u %s\n", convepoch(rr.curtime), rr.interval, rr.scomplen, rr.pcomplen, 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); } // read system-level and process-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); break; } } 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); break; } } if (!dryrun) { // write raw record followed by the compressed // system-level and process-level stats // 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); } } // free dynamically allocated buffers // free(sstat); free(pstat); } 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); sprintf(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.10.0/atopconvert.c0000664000203100020310000013127514545501443014565 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-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 #include #include #include #include #include #include #include #include #include #include #include #include "atop.h" #include "photosyst.h" #include "photoproc.h" #include "rawlog.h" #include "prev/netstats_wrong.h" #include "prev/photosyst_20.h" #include "prev/photoproc_20.h" #include "prev/photosyst_21.h" #include "prev/photoproc_21.h" #include "prev/photosyst_22.h" #include "prev/photoproc_22.h" #include "prev/photosyst_23.h" #include "prev/photoproc_23.h" #include "prev/photosyst_24.h" #include "prev/photoproc_24.h" #include "prev/photosyst_25.h" #include "prev/photoproc_25.h" #include "prev/photosyst_26.h" #include "prev/photoproc_26.h" #include "prev/photosyst_27.h" #include "prev/photoproc_27.h" #include "prev/photosyst_28.h" #include "prev/photoproc_28.h" #include "prev/photosyst_29.h" #include "prev/photoproc_29.h" #include "prev/photosyst_210.h" #include "prev/photoproc_210.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 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); /////////////////////////////////////////////////////////////// // Conversion functions // -------------------- // The structures with system 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 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].filepage = n27->numa[i].filepage; 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)); } /////////////////////////////////////////////////////////////// // 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 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 sstat; 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 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 // 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; } convs[] = { {SETVERSION(2,0), sizeof(struct sstat_20), &sstat_20, sizeof(struct tstat_20), 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}, }, {SETVERSION(2,1), // 2.0 --> 2.1 sizeof(struct sstat_21), &sstat_21, sizeof(struct tstat_21), 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}, }, {SETVERSION(2,2), // 2.1 --> 2.2 sizeof(struct sstat_22), &sstat_22, sizeof(struct tstat_22), 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}, }, {SETVERSION(2,3), // 2.2 --> 2.3 sizeof(struct sstat_23), &sstat_23, sizeof(struct tstat_23), 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}, }, {SETVERSION(2,4), // 2.3 --> 2.4 sizeof(struct sstat_24), &sstat_24, sizeof(struct tstat_24), 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}, }, {SETVERSION(2,5), // 2.4 --> 2.5 sizeof(struct sstat_25), &sstat_25, sizeof(struct tstat_25), 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}, }, {SETVERSION(2,6), // 2.5 --> 2.6 sizeof(struct sstat_26), &sstat_26, sizeof(struct tstat_26), 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}, }, {SETVERSION(2,7), // 2.6 --> 2.7 sizeof(struct sstat_27), &sstat_27, sizeof(struct tstat_27), 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}, }, {SETVERSION(2,8), // 2.7 --> 2.8 sizeof(struct sstat_28), &sstat_28, sizeof(struct tstat_28), 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, justcopy}, {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}, }, {SETVERSION(2,9), // 2.8 --> 2.9 sizeof(struct sstat_29), &sstat_29, sizeof(struct tstat_29), 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}, }, {SETVERSION(2,10), // 2.9 --> 2.10 sizeof(struct sstat_210), &sstat_210, sizeof(struct tstat_210), 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}, }, }; 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, int); static void copy_file(int, int); static void convert_samples(int, int, struct rawheader *, int, int); static void do_sconvert(struct sconvstruct *, struct sconvstruct *); static void do_tconvert(void *, void *, struct tconvstruct *, struct tconvstruct *); static int getrawsstat(int, struct sstat *, int); static int getrawtstat(int, struct tstat *, int, int); static void testcompval(int, char *); int main(int argc, char *argv[]) { int ifd, ofd; struct rawheader irh, orh; int i, versionix, targetix = -1; 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); } 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); } // 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.tstatlen = convs[targetix].tstatlen; if (orh.pidwidth == 0) // no pid width know 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); 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) { struct rawrecord irr, orr; int i, t; count_t count = 0; while ( read(ifd, &irr, irh->rawreclen) == irh->rawreclen) { count++; // read compressed system-level statistics and decompress // if ( !getrawsstat(ifd, convs[ivix].sstat, 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, irr.pcomplen, irr.ndeviat) ) 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[ivix].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); } // write new sample to output file // orr = irr; writesamp(ofd, &orr, convs[ovix].sstat, convs[ovix].sstatlen, convs[ovix].tstat, convs[ovix].tstatlen, irr.ndeviat); // cleanup // free(convs[ovix].tstat); } 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 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, 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 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, int ntask) { 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 record"); exit(7); } free(pcompbuf); } // // 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); } } /* ** 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.10.0/atop.h0000664000203100020310000001431714545501443013166 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 OVFORMAT 11 typedef long long count_t; struct tstat; struct devtstat; struct sstat; 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 struct visualize { char (*show_samp) (time_t, int, struct devtstat *, struct sstat *, int, unsigned int, char); void (*show_error) (const char *, ...); void (*show_end) (void); void (*show_usage) (void); }; /* ** 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 linelen; extern char acctreason; extern char deviatonly; extern char usecolors; extern char threadview; extern char calcpss; extern char getwchan; extern char rawname[]; extern char rawreadflag; extern char rmspaces; extern time_t begintime, endtime, cursortime; // epoch or time in day extern char flaglist[]; extern struct visualize vis; 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 /* ** 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 *, 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 *); 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 *, 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.10.0/atophide.c0000644000203100020310000004340314545501443014007 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 ** -------------------------------------------------------------------------- */ #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); 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; 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); 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); // anonymize command lines and hostname // if (anonflag) anonymize(&sstat, tstatp, rr.ndeviat); // write record header, system-level stats and process-level stats // writesamp(ofd, &rr, &sstat, sizeof sstat, tstatp, sizeof *tstatp, rr.ndeviat); // cleanup // free(tstatp); } // 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 // - 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', MAXDKNAM); strncpy(ssp->dsk.lvm[i].name, standin, MAXDKNAM-1); } // 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); strncpy(ssp->nfs.nfsmounts.nfsmnt[i].mountdev, standin, sizeof ssp->nfs.nfsmounts.nfsmnt[i].mountdev - 1); } // 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', PNAMLEN+1); strncpy(tsp->gen.name, standin, PNAMLEN); memset(tsp->gen.cmdline, '\0', CMDLEN+1); strncpy(tsp->gen.cmdline, standin, CMDLEN); } } } // 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 the system-level statistics from the current offset // static void writesamp(int ofd, struct rawrecord *rr, void *sstat, int sstatlen, void *tstat, int tstatlen, int ntask) { 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); } // 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; } atop-2.10.0/atopsar.c0000644000203100020310000022172214545501443013665 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 ** -------------------------------------------------------------------------- */ #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 "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 *, 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 ? */ strncpy(rawname, optarg, RAWNAMESZ-1); if (strcmp(rawname, "-") == 0) strcpy(rawname, "/dev/stdin"); 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 */ vis.show_samp = 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, 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.10.0/deviate.c0000664000203100020310000016250214545501443013637 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 */ if (devtstat->taskall) free(devtstat->taskall); if (devtstat->procall) free(devtstat->procall); if (devtstat->procactive) 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; devstat->cpu.cgcpuweight = curstat->cpu.cgcpuweight; devstat->cpu.cgcpumax = curstat->cpu.cgcpumax; devstat->cpu.cgcpumaxr = curstat->cpu.cgcpumaxr; 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; devstat->mem.cgmemmax = curstat->mem.cgmemmax; devstat->mem.cgmemmaxr = curstat->mem.cgmemmaxr; devstat->mem.cgswpmax = curstat->mem.cgswpmax; devstat->mem.cgswpmaxr = curstat->mem.cgswpmaxr; 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.10.0/drawbar.c0000644000203100020310000020374714545501443013645 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) ** -------------------------------------------------------------------------- ** 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 ** -------------------------------------------------------------------------- */ ///////////////////////////////////////////////////////////////////////////// // 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. ///////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include #include #include #include "atop.h" #include "photoproc.h" #include "showgeneric.h" #include "photosyst.h" extern char usecolors; // 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); 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); ///////////////////////////////////////////////////// // 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 or alarm expiration // switch (lastchar = headergetch(curtime, nsecs, statusmsg, statuscol)) { case ERR: // alarm expired? case 0: return lastchar; 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) break; getalarm(0); return lastchar; case MSAMPPREV: // manual trigger for previous sample? if (!rawreadflag) { beep(); break; } if (paused) break; getalarm(0); return lastchar; case MRESET: // reset to begin? getalarm(0); paused = 0; return lastchar; case MSAMPBRANCH: // branch to other time? // only possible when viewing raw file // if (!rawreadflag) { beep(); break; } 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 (paused) { paused=0; if (!rawreadflag) alarm(1); } else { paused=1; alarm(0); // stop the clock } break; case MINTERVAL: // modify interval? // not possible 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 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; 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 ('Avg') // - for more than one CPU, one label is added ('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 = COLORCPUSYS; vertvals->category[0].clab = 'S'; vertvals->category[0].cval = sstat->cpu.all.stime * 100 / alltics; vertvals->category[1].ccol = COLORCPUUSR; vertvals->category[1].clab = 'U'; vertvals->category[1].cval = (sstat->cpu.all.utime + sstat->cpu.all.ntime - sstat->cpu.all.guest) * 100 / alltics; vertvals->category[2].ccol = COLORCPUIDLE; vertvals->category[2].clab = 'I'; vertvals->category[2].cval = (sstat->cpu.all.Stime + sstat->cpu.all.Itime) * 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 = COLORCPUSYS; (vertvals+i+1)->category[0].clab = 'S'; (vertvals+i+1)->category[0].cval = sstat->cpu.cpu[i].stime * 100 / alltics; (vertvals+i+1)->category[1].ccol = COLORCPUUSR; (vertvals+i+1)->category[1].clab = 'U'; (vertvals+i+1)->category[1].cval = (sstat->cpu.cpu[i].utime + sstat->cpu.cpu[i].ntime - sstat->cpu.cpu[i].guest) *100/alltics; (vertvals+i+1)->category[2].ccol = COLORCPUIDLE; (vertvals+i+1)->category[2].clab = 'I'; (vertvals+i+1)->category[2].cval = (sstat->cpu.cpu[i].Stime + sstat->cpu.cpu[i].Itime) *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; } } } // draw bar graph showing busy percentages of CPUs // drawvertbars(&wincpu, 100.0, cpubadness, numlabs, numcpus == 1 ? 0 : 1, vertvals, labellen, "Busy%", "Processors", 0); } ///////////////////////////////////////////////////// // 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; // 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; } } drawvertbars(&windsk, 100.0, dskbadness, numdisks, 0, vertvals, labellen, "Busy%", "Disks", 3); } ///////////////////////////////////////////////////// // 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); // 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 // // 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) { 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; 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; availcols = w->ncols - xindent; // 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); wrefresh(w->win); if (vvporig != vvp) // reallocated by sortvertbars()? free(vvp); 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 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, color; int usedlines, freelines, cachelines, tmpfslines, slablines, shmemlines, hugelines; int memorycol = 1, 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"); wrefresh(w->win); // 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; } // show events // mvwprintw(w->win, curline, eventcol, " Events "); if (barlines > 1) // show oomkilling? curline = drawevent(w, curline, eventcol, severitycolor(killseverity), " oomkills ", " %8ld ", sstat->mem.oomkills); if (barlines > 4) // show swapouts? curline = drawevent(w, curline, eventcol, severitycolor(swapseverity), " swapouts ", "%7ld/s ", sstat->mem.swouts/nsecs); if (barlines > 7) // show pagescans? curline = drawevent(w, curline, eventcol, severitycolor(scanseverity), " pagscans ", "%7ld/s ", sstat->mem.pgscans / nsecs); if (barlines > 10) // show swapins? curline = drawevent(w, curline, eventcol, COLORMEMBAR, " swapins ", "%7ld/s ", sstat->mem.swins / nsecs); if (barlines > 13) // show pageouts? curline = drawevent(w, curline, eventcol, COLORMEMBAR, " pagouts ", "%7ld/s ", sstat->mem.pgouts / nsecs); if (barlines > 16) // show pageins? curline = drawevent(w, curline, eventcol, COLORMEMBAR, " pageins ", "%7ld/s ", sstat->mem.pgins / nsecs); 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); 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); } wrefresh(headwin); // wait for keystroke // return mvwgetch(headwin, 1, 0); } ///////////////////////////////////////////////////// // 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 25 #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: 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 or // live measurement // if (rawreadflag) { mvwprintw(helpwin, line++, 2, "Raw file viewing:"); mvwprintw(helpwin, line++, 2, " '%c' - show next sample in raw file", MSAMPNEXT); mvwprintw(helpwin, line++, 2, " '%c' - show previous sample in raw file", MSAMPPREV); mvwprintw(helpwin, line++, 2, " '%c' - rewind to begin of raw file", MRESET); mvwprintw(helpwin, line++, 2, " '%c' - branch to certain time in raw file", MSAMPBRANCH); } 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); // 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; } atop-2.10.0/gpucom.c0000664000203100020310000003146614545501443013514 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. */ #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 void gputype_parse(char *); static void gpustat_parse(int, char *, int, struct pergpu *, struct gpupidstat *); static void gpuparse(int, char *, struct pergpu *); static void 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"); goto close_and_return; } buf[length] = '\0'; gputype_parse(buf); numgpus = numgpus <= MAXGPU ? numgpus : MAXGPU; return numgpus; close_and_return: close(actsock); actsock = -1; return 0; } /* ** Transmit status request for all GPUs. ** ** Calling parameters: ** void ** ** Return value: ** 0 in case of failure ** 1 in case of success */ 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; 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 pids = 0; 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 ** and malloc space to parse these stats */ for (p=buf; *p; p++) { if (*p == PIDDELIM) pids++; } if (gps) { if (pids) { *gps = malloc(pids * sizeof(struct gpupidstat)); ptrverify(gps, "Malloc failed for gpu pidstats\n"); memset(*gps, 0, pids * sizeof(struct gpupidstat)); } else { *gps = NULL; } } /* ** parse stats string for per-gpu stats */ gpustat_parse(version, buf, maxgpu, ggs, gps ? *gps : NULL); free(buf); return pids; close_and_return: if (buf) free(buf); close(actsock); actsock = -1; return -1; } /* ** Receive given number of bytes from given socket ** into given buffer address */ 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 */ static void gputype_parse(char *buf) { char *p, *start, **bp, **tp, *cp; /* ** determine number of GPUs */ if ( sscanf(buf, "%d@", &numgpus) != 1) { close(actsock); actsock = -1; return; } for (p=buf; *p; p++) // search for first delimiter { if (*p == GPUDELIM) { p++; break; } } /* ** 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; ; p++) { if (*p == ' ' || *p == '\0' || *p == GPUDELIM) { switch(field) { case 0: if (p-start <= MAXGPUBUS) *bp++ = start; else *bp++ = p - MAXGPUBUS; break; case 1: if (p-start <= MAXGPUTYPE) *tp++ = start; else *tp++ = p - MAXGPUTYPE; break; case 2: *cp++ = *start; break; } field++; start = p+1; if (*p == '\0') break; if (*p == GPUDELIM) field = 0; *p = '\0'; } } *bp = NULL; *tp = NULL; } } /* ** 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). */ static void gpustat_parse(int version, char *buf, int maxgpu, struct pergpu *gg, struct gpupidstat *gp) { char *p, *start, delimlast; int gpunum = 0; /* ** parse stats string */ for (p=start=buf, delimlast=DUMMY; gpunum <= maxgpu; p++) { char delimnow; if (*p != '\0' && *p != GPUDELIM && *p != PIDDELIM) continue; /* ** next delimiter or end-of-string found */ delimnow = *p; *p = 0; switch (delimlast) { case DUMMY: break; case GPUDELIM: gpuparse(version, start, gg); strcpy(gg->type, gputypes[gpunum]); strcpy(gg->busid, gpubusid[gpunum]); gpunum++; gg++; break; case PIDDELIM: if (gp) { pidparse(version, start, gp); gp->gpu.nrgpus++; gp->gpu.gpulist = 1<<(gpunum-1); gp++; (gg-1)->nrprocs++; } } if (delimnow == 0 || *(p+1) == 0) break; start = p+1; delimlast = delimnow; } } /* ** Parse GPU statistics string */ static void gpuparse(int version, char *p, struct pergpu *gg) { switch (version) { case 1: (void) 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)); gg->nrprocs = 0; break; } } /* ** Parse PID statistics string */ static void pidparse(int version, char *p, struct gpupidstat *gp) { switch (version) { case 1: (void) 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)); break; } } /* ** 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.10.0/gpucom.h0000644000203100020310000000314314545501443013506 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.10.0/ifprop.c0000664000203100020310000002333414545501443013514 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); strncpy((void *)&ifreq.ifr_ifrn.ifrn_name, p->name, sizeof ifreq.ifr_ifrn.ifrn_name-1); #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); strncpy(iwreq.ifr_ifrn.ifrn_name, p->name, sizeof iwreq.ifr_ifrn.ifrn_name-1); 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.10.0/ifprop.h0000664000203100020310000000320214545501443013511 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.10.0/json.c0000664000203100020310000007060414545501443013170 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 "json.h" #define LEN_HP_SIZE 64 #define LINE_BUF_SIZE 1024 static void json_print_CPU(char *, struct sstat *, struct tstat *, int); static void json_print_cpu(char *, struct sstat *, struct tstat *, int); static void json_print_CPL(char *, struct sstat *, struct tstat *, int); static void json_print_GPU(char *, struct sstat *, struct tstat *, int); static void json_print_MEM(char *, struct sstat *, struct tstat *, int); static void json_print_SWP(char *, struct sstat *, struct tstat *, int); static void json_print_PAG(char *, struct sstat *, struct tstat *, int); static void json_print_PSI(char *, struct sstat *, struct tstat *, int); static void json_print_LVM(char *, struct sstat *, struct tstat *, int); static void json_print_MDD(char *, struct sstat *, struct tstat *, int); static void json_print_DSK(char *, struct sstat *, struct tstat *, int); static void json_print_NFM(char *, struct sstat *, struct tstat *, int); static void json_print_NFC(char *, struct sstat *, struct tstat *, int); static void json_print_NFS(char *, struct sstat *, struct tstat *, int); static void json_print_NET(char *, struct sstat *, struct tstat *, int); static void json_print_IFB(char *, struct sstat *, struct tstat *, int); static void json_print_NUM(char *, struct sstat *, struct tstat *, int); static void json_print_NUC(char *, struct sstat *, struct tstat *, int); static void json_print_LLC(char *, struct sstat *, struct tstat *, int); static void json_print_PRG(char *, struct sstat *, struct tstat *, int); static void json_print_PRC(char *, struct sstat *, struct tstat *, int); static void json_print_PRM(char *, struct sstat *, struct tstat *, int); static void json_print_PRD(char *, struct sstat *, struct tstat *, int); static void json_print_PRN(char *, struct sstat *, struct tstat *, int); static void json_print_PRE(char *, struct sstat *, struct tstat *, int); /* ** table with possible labels and the corresponding ** print-function for json style output */ struct labeldef { char *label; int valid; void (*prifunc)(char *, struct sstat *, struct tstat *, int); }; static struct labeldef labeldef[] = { { "CPU", 0, json_print_CPU }, { "cpu", 0, json_print_cpu }, { "CPL", 0, json_print_CPL }, { "GPU", 0, json_print_GPU }, { "MEM", 0, json_print_MEM }, { "SWP", 0, json_print_SWP }, { "PAG", 0, json_print_PAG }, { "PSI", 0, json_print_PSI }, { "LVM", 0, json_print_LVM }, { "MDD", 0, json_print_MDD }, { "DSK", 0, json_print_DSK }, { "NFM", 0, json_print_NFM }, { "NFC", 0, json_print_NFC }, { "NFS", 0, json_print_NFS }, { "NET", 0, json_print_NET }, { "IFB", 0, json_print_IFB }, { "NUM", 0, json_print_NUM }, { "NUC", 0, json_print_NUC }, { "LLC", 0, json_print_LLC }, { "PRG", 0, json_print_PRG }, { "PRC", 0, json_print_PRC }, { "PRM", 0, json_print_PRM }, { "PRD", 0, json_print_PRD }, { "PRN", 0, json_print_PRN }, { "PRE", 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, int nexit, unsigned int noverflow, char flag) { register int i, j, k; 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; /* prepare generic columns */ snprintf(header, sizeof header, "\"%s\"", labeldef[i].label); /* call all print-functions */ (labeldef[i].prifunc)(header, sstat, devtstat->taskall, devtstat->ntaskall); } 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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, " "\"spack\": %lld, " "\"sbyte\": %lld, " "\"serrs\": %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].spack, ss->intf.intf[i].sbyte, ss->intf.intf[i].serrs, 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) { 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) { 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) { 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) { 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 process-level statistics */ static void json_print_PRG(char *hp, struct sstat *ss, struct tstat *ps, int nact) { register int i, exitcode; static char st[3]; 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(", "); } /* using getpwuid() & getpwuid to convert ruid & euid to string seems better, but the two functions take a long time */ printf("{\"pid\": %d, " "\"name\": \"(%.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:"-", ps->gen.cgpath[0] ? ps->gen.cgpath:"-"); } printf("]"); } static void json_print_PRC(char *hp, struct sstat *ss, struct tstat *ps, int nact) { 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, " "\"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->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, ps->gen.cgpath[0] ? ps->gen.cgpath:"-"); } printf("]"); } static void json_print_PRM(char *hp, struct sstat *ss, struct tstat *ps, int nact) { 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, " "\"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->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, ps->gen.cgpath[0] ? ps->gen.cgpath:"-"); } printf("]"); } static void json_print_PRD(char *hp, struct sstat *ss, struct tstat *ps, int nact) { 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, " "\"rio\": %lld, " "\"rsz\": %lld, " "\"wio\": %lld, " "\"wsz\": %lld, " "\"cwsz\": %lld}", ps->gen.pid, 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) { 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, " "\"tcpsnd\": \"%lld\", " "\"tcpssz\": \"%lld\", " "\"tcprcv\": \"%lld\", " "\"tcprsz\": \"%lld\", " "\"udpsnd\": \"%lld\", " "\"udpssz\": \"%lld\", " "\"udprcv\": \"%lld\", " "\"udprsz\": \"%lld\"}", ps->gen.pid, 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) { 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, " "\"gpustate\": \"%c\", " "\"nrgpus\": %d, " "\"gpulist\": \"%x\", " "\"gpubusy\": %d, " "\"membusy\": %d, " "\"memnow\": %lld, " "\"memcum\": %lld, " "\"sample\": %lld}", ps->gen.pid, 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.10.0/json.h0000664000203100020310000000263214545501443013171 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 *, int, unsigned int, char); atop-2.10.0/netatopbpfif.c0000664000203100020310000001110014545501443014662 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.10.0/netatopd.h0000644000203100020310000000330414545501443014031 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.10.0/netatop.h0000664000203100020310000000477414545501443013703 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.10.0/netatopif.c0000644000203100020310000002663114545501443014207 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. */ #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.10.0/netlink.c0000664000203100020310000001343414545501443013661 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 */ sprintf(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.10.0/netstats.h0000664000203100020310000001061014545501443014060 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.10.0/parseable.c0000664000203100020310000006064214545501443014156 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-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 "atop.h" #include "photosyst.h" #include "photoproc.h" #include "parseable.h" void print_CPU(char *, struct sstat *, struct tstat *, int); void print_cpu(char *, struct sstat *, struct tstat *, int); void print_CPL(char *, struct sstat *, struct tstat *, int); void print_GPU(char *, struct sstat *, struct tstat *, int); void print_MEM(char *, struct sstat *, struct tstat *, int); void print_SWP(char *, struct sstat *, struct tstat *, int); void print_PAG(char *, struct sstat *, struct tstat *, int); void print_PSI(char *, struct sstat *, struct tstat *, int); void print_LVM(char *, struct sstat *, struct tstat *, int); void print_MDD(char *, struct sstat *, struct tstat *, int); void print_DSK(char *, struct sstat *, struct tstat *, int); void print_NFM(char *, struct sstat *, struct tstat *, int); void print_NFC(char *, struct sstat *, struct tstat *, int); void print_NFS(char *, struct sstat *, struct tstat *, int); void print_NET(char *, struct sstat *, struct tstat *, int); void print_IFB(char *, struct sstat *, struct tstat *, int); void print_NUM(char *, struct sstat *, struct tstat *, int); void print_NUC(char *, struct sstat *, struct tstat *, int); void print_LLC(char *, struct sstat *, struct tstat *, int); void print_PRG(char *, struct sstat *, struct tstat *, int); void print_PRC(char *, struct sstat *, struct tstat *, int); void print_PRM(char *, struct sstat *, struct tstat *, int); void print_PRD(char *, struct sstat *, struct tstat *, int); void print_PRN(char *, struct sstat *, struct tstat *, int); void print_PRE(char *, struct sstat *, struct tstat *, int); static void calc_freqscale(count_t, count_t, count_t, count_t *, int *); static char *spaceformat(char *, char *); static int cgroupv2max(int, int); /* ** table with possible labels and the corresponding ** print-function for parsable output */ struct labeldef { char *label; int valid; void (*prifunc)(char *, struct sstat *, struct tstat *, int); }; static struct labeldef labeldef[] = { { "CPU", 0, print_CPU }, { "cpu", 0, print_cpu }, { "CPL", 0, print_CPL }, { "GPU", 0, print_GPU }, { "MEM", 0, print_MEM }, { "SWP", 0, print_SWP }, { "PAG", 0, print_PAG }, { "PSI", 0, print_PSI }, { "LVM", 0, print_LVM }, { "MDD", 0, print_MDD }, { "DSK", 0, print_DSK }, { "NFM", 0, print_NFM }, { "NFC", 0, print_NFC }, { "NFS", 0, print_NFS }, { "NET", 0, print_NET }, { "IFB", 0, print_IFB }, { "NUM", 0, print_NUM }, { "NUC", 0, print_NUC }, { "LLC", 0, print_LLC }, { "PRG", 0, print_PRG }, { "PRC", 0, print_PRC }, { "PRM", 0, print_PRM }, { "PRD", 0, print_PRD }, { "PRN", 0, print_PRN }, { "PRE", 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, int nexit, unsigned int noverflow, char flag) { register int i; 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) { /* ** prepare generic columns */ convdate(curtime, datestr); convtime(curtime, timestr); snprintf(header, sizeof header, "%s %s %ld %s %s %d", labeldef[i].label, utsname.nodename, curtime, datestr, timestr, numsecs); /* ** call a selected print-function */ (labeldef[i].prifunc)(header, sstat, devtstat->taskall, devtstat->ntaskall); } } /* ** 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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\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); } } void print_IFB(char *hp, struct sstat *ss, struct tstat *ps, int nact) { 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) { 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) { 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) { 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 process-level statistics */ void print_PRG(char *hp, struct sstat *ss, struct tstat *ps, int nact) { register int i, exitcode; char namout[PNAMLEN+1+2], cmdout[CMDLEN+1+2], pathout[CGRLEN+1]; for (i=0; i < nact; i++, ps++) { 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(ps->gen.cgpath, pathout), ps->gen.state == 'E' ? ps->gen.btime + ps->gen.elaps/hertz : 0, ps->gen.nthridle); } } void print_PRC(char *hp, struct sstat *ss, struct tstat *ps, int nact) { register int i; char namout[PNAMLEN+1+2], wchanout[20]; for (i=0; i < nact; i++, ps++) { 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, cgroupv2max(ps->gen.isproc, ps->cpu.cgcpumax), cgroupv2max(ps->gen.isproc, ps->cpu.cgcpumaxr), ps->cpu.nvcsw, ps->cpu.nivcsw); } } void print_PRM(char *hp, struct sstat *ss, struct tstat *ps, int nact) { register int i; char namout[PNAMLEN+1+2]; for (i=0; i < nact; i++, ps++) { 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, cgroupv2max(ps->gen.isproc, ps->mem.cgmemmax), cgroupv2max(ps->gen.isproc, ps->mem.cgmemmaxr), cgroupv2max(ps->gen.isproc, ps->mem.cgswpmax), cgroupv2max(ps->gen.isproc, ps->mem.cgswpmaxr)); } } void print_PRD(char *hp, struct sstat *ss, struct tstat *ps, int nact) { 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) { 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) { 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; } /* ** return proper integer for cgroup v2 maximum values */ static int cgroupv2max(int isproc, int max) { if (! (supportflags&CGROUPV2)) return -3; if (! isproc) return -2; return max; } atop-2.10.0/parseable.h0000664000203100020310000000262114545501443014154 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 *, int, unsigned int, char); #endif atop-2.10.0/photoproc.c0000644000203100020310000007307414545501443014236 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 ** -------------------------------------------------------------------------- */ #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" /* ATOP-extension line of /proc/pid/stat */ #define ATOPSTAT "%lld %llu %lld %llu %lld %llu %lld %llu " \ "%lld %llu %lld %llu %lld %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 *); static int proccgroupv2(struct tstat *); static struct cgroupv2vals * alloccgroupv2(char *, int); static struct cgroupv2vals * findhashcgroupv2(char *, int *); void fillcgroupv2(struct cgroupv2vals *, char *, char *, int); int readcgroupv2(char *, char *, char *, int, long []); static void wipecgroupv2(void); 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[1024], 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); } /* ** check if this kernel offers cgroups version 2 */ if ( (fp = fopen("/proc/1/cgroup", "r")) ) { char line[128]; while (fgets(line, sizeof line, fp)) { if (memcmp(line, "0::", 3) == 0) // equal? { supportflags |= CGROUPV2; break; } } 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 kernel module is not active on this system, ** netatop-bpf will try tp run; */ if (!(supportflags & NETATOPD)) { netatop_bpf_probe(); } /* ** if netatop-bpf is not active on this system, ** kernel module will try to run; */ if (!(supportflags & NETATOPBPF)) { netatop_probe(); } /* ** if netatop-bpf is active on this system, skip call */ if (supportflags & NETATOPBPF) { netatop_bpf_gettask(); } if (supportflags & CGROUPV2) wipecgroupv2(); 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 */ /* ** cgroups v2: determine cgroup for process and register most ** relevant limits */ if (supportflags & CGROUPV2) proccgroupv2(curtask); /* ** 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 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 */ 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); if (nrthread < nrproc) mcleanstop(53, "#threads (%ld) < #procs (%ld)\n", nrthread, nrproc); 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 environmentvariables */ #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 '\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; } /* ** CGROUP V2 specific items */ #define CGROUPROOT "/sys/fs/cgroup" #define CGROUPNHASH 64 #define CGROUPMASK 0x3f #define MAXSLASH 16 // max. number of slashes in relative path struct cgroupv2vals { char *path; int cpuweight; // -1=max, -2=undefined int cpumax; // -1=max, -2=undefined (perc) int cpumaxr; // -1=max, -2=undefined (perc) long long memmax; // -1=max, -2=undefined (KiB) long long memmaxr; // -1=max, -2=undefined (KiB) long long swpmax; // -1=max, -2=undefined (KiB) long long swpmaxr; // -1=max, -2=undefined (KiB) struct cgroupv2vals *next; }; static struct cgroupv2vals *cgrouphash[CGROUPNHASH]; /* ** get cgroup related to process from /proc//cgroup ** return code: 0 - no cgroup v2 in use ** 1 - cgroup v2 in use ** 2 - cgroup version can not be determined */ static int proccgroupv2(struct tstat *curtask) { FILE *fp; char line[1024], *relpath, abspath[1200]; int hash, pathlen, restlen, nslash; struct cgroupv2vals *pvals = NULL, *ptarget; char *p, *slashes[MAXSLASH]; /* ** open the cgroup file of the current process and ** read the line that starts with '0::' for cgroup v2 */ curtask->gen.cgpath[0] = '\0'; if ( (fp = fopen("cgroup", "r")) ) { while (fgets(line, sizeof line, fp)) { if ( memcmp(line, "0::", 3) == 0) // equal? { line[ strlen(line)-1 ] = '\0'; // remove newline relpath = line+3; strncpy(curtask->gen.cgpath, relpath, sizeof curtask->gen.cgpath); curtask->gen.cgpath[sizeof curtask->gen.cgpath -1] = '\0'; break; } } fclose(fp); if (curtask->gen.cgpath[0] == '\0') return 0; // no cgroupv2 support } else // open failed; no permission { return 2; } /* ** cgroup v2 pathname of this process is known; ** prepare absolute pathname of cgroup */ pathlen = snprintf(abspath, sizeof abspath, "%s%s/", CGROUPROOT, relpath); restlen = sizeof abspath - pathlen -1; abspath[sizeof abspath - 1] = '\0'; // guarantee delimiter relpath = abspath + sizeof CGROUPROOT - 1; /* ** cycle through all directory levels for values that ** might limit the values in the current cgroup (e.g. cpu.max) */ for (nslash=0, p=relpath; *p && nslash 0; nslash--) { *slashes[nslash] = '\0'; pvals = findhashcgroupv2(relpath, &hash); // search in cache if (!pvals) // not found in cache { // allocate new cache entry pvals = alloccgroupv2(relpath, hash); // fill info in new cache entry fillcgroupv2(pvals, abspath, slashes[nslash], restlen); } /* ** if the target cgroup is not defined, ** determine the target cgroup (lowest level dir) */ if (! ptarget) { ptarget = pvals; continue; } /* ** in case of a higher cgroup, check the restrictive values ** for the target cgroup */ switch (pvals->cpumax) { case -1: if (ptarget->cpumaxr == -2) ptarget->cpumaxr = -1; break; case -2: break; default: if (ptarget->cpumaxr == -1 || ptarget->cpumaxr == -2) { ptarget->cpumaxr = pvals->cpumax; break; } if (ptarget->cpumaxr > pvals->cpumax) ptarget->cpumaxr = pvals->cpumax; } switch (pvals->memmax) { case -1: if (ptarget->memmaxr == -2) ptarget->memmaxr = -1; break; case -2: break; default: if (ptarget->memmaxr == -1 || ptarget->memmaxr == -2) { ptarget->memmaxr = pvals->memmax; break; } if (ptarget->memmaxr > pvals->memmax) ptarget->memmaxr = pvals->memmax; } switch (pvals->swpmax) { case -1: if (ptarget->swpmaxr == -2) ptarget->swpmaxr = -1; break; case -2: break; default: if (ptarget->swpmaxr == -1 || ptarget->swpmaxr == -2) { ptarget->swpmaxr = pvals->swpmax; break; } if (ptarget->swpmaxr > pvals->swpmax) ptarget->swpmaxr = pvals->swpmax; } } curtask->cpu.cgcpuweight = ptarget->cpuweight; curtask->cpu.cgcpumax = ptarget->cpumax; curtask->cpu.cgcpumaxr = ptarget->cpumaxr; curtask->mem.cgmemmax = ptarget->memmax; curtask->mem.cgmemmaxr = ptarget->memmaxr; curtask->mem.cgswpmax = ptarget->swpmax; curtask->mem.cgswpmaxr = ptarget->swpmaxr; return 1; } /* ** determine the most relevant values of this cgroup */ void fillcgroupv2(struct cgroupv2vals *pvals, char *abspath, char *extpath, int restlen) { long retvals[2]; *extpath++ = '/'; // replace slash /* ** get cpu.weight limitation */ pvals->cpuweight = -2; // initial value (undefined) switch (readcgroupv2(abspath, extpath, "cpu.weight", restlen, retvals)) { case 1: pvals->cpuweight = retvals[0]; break; } /* ** get cpu.max limitation */ pvals->cpumax = -2; // initial value (undefined) switch (readcgroupv2(abspath, extpath, "cpu.max", restlen, retvals)) { case 2: if (retvals[0] == -1) pvals->cpumax = -1; else pvals->cpumax = retvals[0] * 100 / retvals[1]; break; } pvals->cpumaxr = pvals->cpumax; // set temporary restrictive /* ** get memory.max limitation */ pvals->memmax = -2; // initial value (undefined) switch (readcgroupv2(abspath, extpath, "memory.max", restlen, retvals)) { case 1: if (retvals[0] == -1) pvals->memmax = -1; else pvals->memmax = retvals[0] / 1024; // KiB break; } pvals->memmaxr = pvals->memmax; // set temporary restrictive /* ** get memory.swap.max limitation */ pvals->swpmax = -2; // initial value (undefined) switch (readcgroupv2(abspath, extpath, "memory.swap.max", restlen, retvals)) { case 1: if (retvals[0] == -1) pvals->swpmax = -1; else pvals->swpmax = retvals[0] / 1024; // KiB break; } pvals->swpmaxr = pvals->swpmax; // set temporary restrictive } /* ** 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 */ int readcgroupv2(char *abspath, char *extpath, char *fname, int restlen, long retvals[]) { char line[64]; int n; FILE *fp; strncpy(extpath, fname, restlen); // complete absolute path of file if ( (fp = fopen(abspath, "r")) ) { char firststr[16]; if (! fgets(line, sizeof line, fp)) { fclose(fp); return 0; } fclose(fp); switch (n = sscanf(line, "%15s %ld", 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; } /* ** find existing info about cgroup in cgroupv2 cache ** ** return value: pointer to structure that has been found, or ** NULL (not found) */ static struct cgroupv2vals * findhashcgroupv2(char *relpath, int *phash) { struct cgroupv2vals *p; char *s; int hash = 0; for (s = relpath; *s++; ) // calculate simple hash for this cgroup hash += *s; // by accumulating all path characters *phash = hash; // search hash list of earlier accessed cgroups within this interval for (p = cgrouphash[hash&CGROUPMASK]; p; p = p->next) { if ( strcmp(relpath, p->path) == 0) // found? return p; } return NULL; } /* ** allocate new hash entry in cgroupv2 cache ** ** return value: pointer to newly allocated structure */ static struct cgroupv2vals * alloccgroupv2(char *relpath, int hash) { struct cgroupv2vals *p; p = malloc(sizeof(struct cgroupv2vals)); ptrverify(p, "Malloc failed for new cgroup values\n"); p->path = malloc(strlen(relpath)+1); ptrverify(p->path, "Malloc failed for path in new cgroup values\n"); strcpy(p->path, relpath); p->next = cgrouphash[hash&CGROUPMASK]; // add new entry to hash chain cgrouphash[hash&CGROUPMASK] = p; return p; } /* ** clear entire cgroupv2 cache */ static void wipecgroupv2(void) { int i; struct cgroupv2vals *p, *pnext; for (i=0; i < CGROUPNHASH; i++) { for (p = cgrouphash[i]; p; p = pnext) { pnext = p->next; free(p->path); free(p); } cgrouphash[i] = 0; } } atop-2.10.0/photoproc.h0000664000203100020310000001635114545501443014240 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 */ char cgpath[CGRLEN]; /* cgroup v2 path name */ } 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 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 { 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 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 { 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 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; }; 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; struct tstat **procall; struct tstat **procactive; 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.10.0/photosyst.c0000664000203100020310000021141214545501443014265 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-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 #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 /* return value of isdisk() */ #define NONTYPE 0 #define DSKTYPE 1 #define MDDTYPE 2 #define LVMTYPE 3 /* 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(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[1024]; 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; if (si->cpu.nrcpu <= SCALINGMAXCPU) { 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; didone=1; } si->cpu.cpu[i].freqcnt.maxfreq = maxfreq; si->cpu.cpu[i].freqcnt.cnt = sum; si->cpu.cpu[i].freqcnt.ticks = hits; fclose(fp); } else { // governor statistics not available 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; didone=1; } fclose(fp); } 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"); sprintf(lhugepagetot, "%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"); sprintf(lhugepagefree, "%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(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(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 ** 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) { if ( fgets(linebuf, sizeof(linebuf), fp) != NULL) { nr = sscanf(linebuf, psiformat, &psitype, &psitemp.avg10, &psitemp.avg60, &psitemp.avg300, &psitemp.total); if (nr == 5) // complete line ? memmove(&(si->psi.cpusome), &psitemp, sizeof psitemp); } 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) { strncpy(px->name, curname, maxlen-1); *(px->name+maxlen-1) = 0; } 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 */ strncpy(dmp->name, dentry->d_name, MAXDKNAM); dmp->name[MAXDKNAM-1] = 0; 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 */ strncpy(px->name, dmp->name, maxlen-1); *(px->name+maxlen-1) = 0; return; } dmp = dmp->next; } /* ** info not found in hash list; fill original name */ strncpy(px->name, curname, maxlen-1); *(px->name+maxlen-1) = 0; } /* ** this table is used in the function isdisk() ** ** 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 retval; } validdisk[] = { { "^ram[0-9][0-9]*$", {0}, (void *)0, NONTYPE, }, { "^loop[0-9][0-9]*$", {0}, (void *)0, NONTYPE, }, { "^sd[a-z][a-z]*$", {0}, nullmodname, DSKTYPE, }, { "^dm-[0-9][0-9]*$", {0}, lvmmapname, LVMTYPE, }, { "^md[0-9][0-9]*$", {0}, nullmodname, MDDTYPE, }, { "^vd[a-z][a-z]*$", {0}, nullmodname, DSKTYPE, }, { "^nvme[0-9][0-9]*n[0-9][0-9]*$", {0}, nullmodname, DSKTYPE, }, { "^nvme[0-9][0-9]*c[0-9][0-9]*n[0-9][0-9]*$", {0}, nullmodname, DSKTYPE, }, { "^nbd[0-9][0-9]*$", {0}, nullmodname, DSKTYPE, }, { "^hd[a-z]$", {0}, nullmodname, DSKTYPE, }, { "^rd/c[0-9][0-9]*d[0-9][0-9]*$", {0}, nullmodname, DSKTYPE, }, { "^cciss/c[0-9][0-9]*d[0-9][0-9]*$", {0}, nullmodname, DSKTYPE, }, { "^fio[a-z][a-z]*$", {0}, nullmodname, DSKTYPE, }, { "/host.*/bus.*/target.*/lun.*/disc", {0}, abbrevname1, DSKTYPE, }, { "^xvd[a-z][a-z]*[0-9]*$", {0}, nullmodname, DSKTYPE, }, { "^dasd[a-z][a-z]*$", {0}, nullmodname, DSKTYPE, }, { "^mmcblk[0-9][0-9]*$", {0}, nullmodname, DSKTYPE, }, { "^emcpower[a-z][a-z]*$", {0}, nullmodname, DSKTYPE, }, { "^rbd[0-9][0-9]*$", {0}, nullmodname, DSKTYPE, }, { "^rbd[0-9][0-9]*p[0-9][0-9]*$", {0}, nullmodname, DSKTYPE, }, }; static int isdisk(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) { /* ** 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; } /* ** 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; } #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; firstcall = 0; /* ** 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)); cs->all.instr += cs->cpu[i].instr; 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); } liResult = read(*(fdc+i), &(cs->cpu[i].cycle), sizeof(count_t)); cs->all.cycle += cs->cpu[i].cycle; 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 /* ! 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.10.0/photosyst.h0000644000203100020310000003636414545501443014303 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[4]; // 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 }; 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 }; 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 }; 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 *); #endif atop-2.10.0/procdbase.c0000664000203100020310000001652514545501443014163 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.10.0/rawlog.c0000644000203100020310000005504614545501443013513 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 ** -------------------------------------------------------------------------- */ #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 "showgeneric.h" #include "photoproc.h" #include "photosyst.h" #include "rawlog.h" #define BASEPATH "/var/log/atop" #define BINPATH "/usr/bin/atop" static int getrawrec (int, struct rawrecord *, int); static int getrawsstat(int, struct sstat *, int); static int getrawtstat(int, struct tstat *, int, int); static int rawwopen(void); static int readchunk(int, void *, int); static int lookslikedatetome(char *); static void testcompval(int, char *); static void try_other_version(int, int); /* ** 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, 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; unsigned long scomplen = sizeof scompbuf; unsigned long pcomplen = sizeof(struct tstat) * devtstat->ntaskall; struct iovec iov[3]; /* ** 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- and process-level statistics */ rv = compress(scompbuf, &scomplen, (Byte *)sstat, (unsigned long)sizeof *sstat); testcompval(rv, "compress"); pcompbuf = malloc(pcomplen); ptrverify(pcompbuf, "Malloc failed for compression buffer\n"); rv = compress(pcompbuf, &pcomplen, (Byte *)devtstat->taskall, (unsigned long)pcomplen); testcompval(rv, "compress"); /* ** 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.scomplen = scomplen; rr.pcomplen = pcomplen; 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; /* ** use 1-writev to make record operation atomic ** to avoid write uncompleted record data. */ 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; if ( writev(rawfd, iov, 3) == -1) { fprintf(stderr, "%s - ", rawname); if ( ftruncate(rawfd, filestat.st_size) == -1) mcleanstop(8, "failed to write raw/status/process record to %s\n", rawname); mcleanstop(7, "failed to write raw/status/process record to %s\n", rawname); } free(pcompbuf); 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; int fd; /* ** check if the file exists already */ if ( (fd = open(rawname, O_RDWR)) >= 0) { /* ** read and verify header record */ if ( read(fd, &rh, sizeof rh) < sizeof rh) mcleanstop(7, "%s - cannot read header\n", rawname); if (rh.magic != MYMAGIC) mcleanstop(7, "file %s exists but does not contain raw " "atop output (wrong magic number)\n", rawname); if ( rh.sstatlen != sizeof(struct sstat) || rh.tstatlen != sizeof(struct tstat) || rh.rawheadlen != sizeof(struct rawheader) || rh.rawreclen != sizeof(struct rawrecord) ) { fprintf(stderr, "existing file %s has incompatible header\n", rawname); 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); } (void) lseek(fd, (off_t) 0, SEEK_END); return fd; } /* ** file does not exist (or can not be opened) */ if ( (fd = creat(rawname, 0666)) == -1) { fprintf(stderr, "%s - ", rawname); perror("create raw file"); cleanstop(7); } memset(&rh, 0, sizeof rh); rh.magic = MYMAGIC; rh.aversion = getnumvers() | 0x8000; rh.sstatlen = sizeof(struct sstat); rh.tstatlen = sizeof(struct tstat); 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 - ", rawname); perror("write raw header"); cleanstop(7); } return fd; } /* ** read the contents of a raw file */ #define OFFCHUNK 256 int rawread(void) { int i, j, rawfd, len, isregular = 1; char *py; struct rawheader rh; struct rawrecord rr; struct sstat sstat; static struct devtstat devtstat; 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(rawname) ) { /* ** if no filename is specified, assemble the name of the raw file */ case 0: timenow = time(0); tp = localtime(&timenow); snprintf(rawname, 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(rawname, F_OK) == 0) break; /* existing file */ if (lookslikedatetome(rawname)) { char savedname[16]; strcpy(savedname, rawname); // no overflow (len=8) snprintf(rawname, 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(rawname, 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(rawname, py) == 0 ) { timenow = time(0); timenow -= len*3600*24; tp = localtime(&timenow); snprintf(rawname, 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(rawname, &filestat) == -1) { fprintf(stderr, "%s - ", rawname); 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 */ if ( (rawfd = open(rawname, O_RDONLY)) == -1) { char command[512], tmpname1[200], tmpname2[200]; /* ** check if a compressed raw file is present */ snprintf(tmpname1, sizeof tmpname1, "%s.gz", rawname); if ( access(tmpname1, F_OK|R_OK) == -1) { fprintf(stderr, "%s - ", rawname); perror("open raw file"); cleanstop(7); } /* ** compressed raw file to be decompressed via gunzip */ fprintf(stderr, "Decompressing logfile ....\n"); snprintf(tmpname2, sizeof tmpname2, "/tmp/atopwrkXXXXXX"); rawfd = mkstemp(tmpname2); if (rawfd == -1) { fprintf(stderr, "%s - ", rawname); perror("creating decompression temp file"); cleanstop(7); } snprintf(command, sizeof command, "gunzip -c %s > %s", tmpname1, tmpname2); const int system_res = system (command); unlink(tmpname2); if (system_res) { fprintf(stderr, "%s - gunzip failed", rawname); 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", rawname); cleanstop(7); } /* ** magic okay, but file-layout might have been modified */ if (rh.sstatlen != sizeof(struct sstat) || rh.tstatlen != sizeof(struct tstat) || rh.rawheadlen != sizeof(struct rawheader) || rh.rawreclen != sizeof(struct rawrecord) ) { fprintf(stderr, "raw file %s has incompatible format\n", rawname); 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); if (((rh.aversion >> 8) & 0x7f) != (getnumvers() >> 8) || (rh.aversion & 0xff) != (getnumvers() & 0x7f) ) { try_other_version((rh.aversion >> 8) & 0x7f, rh.aversion & 0xff); } 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 ( getrawrec(rawfd, &rr, rh.rawreclen) == 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, 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); ptrverify(dummybuf, "Malloc rawlog pipe buffer failed\n"); readchunk(rawfd, dummybuf, rr.scomplen+rr.pcomplen); 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 ** statistics and decompress */ if ( !getrawsstat(rawfd, &sstat, rr.scomplen) ) cleanstop(7); /* ** allocate space, read compressed process-level ** statistics 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; /* ** activate the installed print-function to visualize ** the system- and process-level statistics */ 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 { lastcmd = (vis.show_samp)(rr.curtime, rr.interval, &devtstat, &sstat, rr.nexit, rr.noverflow, flags); } while (!isregular && ( lastcmd == MSAMPPREV || lastcmd == MRESET || (lastcmd == MSAMPBRANCH && begintime < cursortime) )); free(devtstat.taskall); free(devtstat.procall); free(devtstat.procactive); 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 MSAMPBRANCH: if (begintime < cursortime && isregular) { lseek(rawfd, *offlist, SEEK_SET); offcur = 1; } } } begintime = 0; // allow earlier times from now on if (isregular) { 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) { return readchunk(rawfd, prr, rrlen); } /* ** 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; } /* ** 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 */ } 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); } } 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: return 0; // EOF case -1: perror("read raw file"); cleanstop(9); default: len -= n; p += n; } } return (char *)p - (char *)buf; } /* ** try to activate another atop- or atopsar-version ** to read this logfile */ static void try_other_version(int majorversion, int minorversion) { char tmpbuf[1024]; extern char **argvp; int fds; struct rlimit rlimit; int setresuid(uid_t, uid_t, uid_t); /* ** prepare name of executable file ** the current pathname (if any) is stripped off */ snprintf(tmpbuf, sizeof tmpbuf, "%s-%d.%d", BINPATH, majorversion, minorversion); fprintf(stderr, "trying to activate %s....\n", tmpbuf); /* ** be sure no open file descriptors are passed ** except stdin, stdout en stderr */ (void) getrlimit(RLIMIT_NOFILE, &rlimit); for (fds=3; fds < rlimit.rlim_cur; fds++) close(fds); /* ** be absolutely sure not to pass setuid-root privileges ** to the loaded program; errno EAGAIN and ENOMEM are not ** acceptable! */ if ( setresuid(getuid(), getuid(), getuid()) == -1 && errno != EPERM) { fprintf(stderr, "not possible to drop root-privileges!\n"); exit(1); } /* ** load alternative executable image ** at this moment the saved-uid might still be set ** to 'root' but this is reset at the moment of exec */ (void) execvp(tmpbuf, argvp); /* ** point of no return, except when exec failed */ fprintf(stderr, "activation of %s failed!\n", tmpbuf); } atop-2.10.0/rawlog.h0000664000203100020310000000775614545501443013527 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 | sample 1 ** compressed process-level statistics / ** ** rawrecord \ ** compressed system-level statistics | sample 2 ** compressed process-level statistics / ** ** 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 ifuture[6]; /* future use */ }; struct rawrecord { time_t curtime; /* current time (epoch) */ unsigned short flags; /* various flags */ unsigned short sfuture[3]; /* 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 ifuture[5]; /* future use */ }; #endif atop-2.10.0/showgeneric.c0000664000203100020310000024056214545501443014536 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 ** -------------------------------------------------------------------------- */ #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 "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 exited 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; int paused; /* boolean: currently in pause-mode */ 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 generic_init(void); static char text_samp(time_t, int, struct devtstat *, struct sstat *, 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 proc_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, 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, 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 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 if (supportflags & IOSTAT) { showtype = MPROCDSK; if (showorder != MSORTAUTO) showorder = MSORTDSK; } else { showtype = MPROCGEN; showorder = MSORTCPU; } 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- and system-level ** in text mode */ static char text_samp(time_t curtime, int nsecs, struct devtstat *devtstat, struct sstat *sstat, int nexit, unsigned int noverflow, char flag) { register int i, curline, statline, nproc; int firstproc = 0, plistsz, 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; /* ** 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; int ncurlist; /* ** 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 exited 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) - 52 - seclen - utsnodenamelen; int len1 = lenavail / 3; int len2 = lenavail - len1 - len1; printg("ATOP - %s%*s%s %s%*s%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 ? '-' : MALLPROC, 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 : '-', len2, "", buf); if (screen) attroff(A_REVERSE); else printg("\n"); /* ** print cumulative system- and user-time for all processes */ pricumproc(sstat, devtstat, nexit, noverflow, avgval, nsecs); if (noverflow) { snprintf(statbuf, sizeof statbuf, "Only %d exited 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) { viewmsg = "Rawfile view"; } else { if (rootprivs()) viewmsg = "Unrestricted view (privileged)"; else viewmsg = "Restricted view (unprivileged)"; } if (screen) { if (usecolors) attron(COLOR_PAIR(FGCOLORINFO)); attron(A_BLINK); } printg(initmsg); if (screen) { if (usecolors) attroff(COLOR_PAIR(FGCOLORINFO)); attroff(A_BLINK); printg("%*s", COLS - strlen(initmsg) - strlen(viewmsg), " "); } else { printg("%*s", 80 - strlen(initmsg) - strlen(viewmsg), " "); } if (screen) { if (usecolors) attron(COLOR_PAIR(FGCOLORALMOST)); attron(A_BLINK); } printg(viewmsg); if (screen) { if (usecolors) attroff(COLOR_PAIR(FGCOLORALMOST)); attroff(A_BLINK); } } } /* ** 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 */ if (sellist) // remove previous list if needed free(sellist); 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) plistsz = LINES-curline-2; else if (threadview && threadallowed) plistsz = devtstat->ntaskactive; else plistsz = ncurlist; if (ncurlist > 0 && plistsz > 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) { if (tsklist) 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 (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); } priphead(firstproc/plistsz+1, (ncurlist-1)/plistsz+1, &showtype, &curorder, showorder == MSORTAUTO ? 1 : 0, sstat->cpu.nrcpu); if (screen) { attroff(A_REVERSE); clrtobot(); } /* ** print the list */ priproc(curlist, firstproc, ncurlist, curline+2, firstproc/plistsz+1, (ncurlist-1)/plistsz+1, showtype, curorder, &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) { /* ** show blinking pause-indication if necessary */ if (paused) { move(statline, COLS-6); attron(A_BLINK); attron(A_REVERSE); printw("PAUSED"); attroff(A_REVERSE); attroff(A_BLINK); } /* ** await input-character or interval-timer expiration */ switch ( (lastchar = mvgetch(statline, 0)) ) { /* ** timer expired */ case ERR: case 0: timeout(0); (void) getch(); timeout(-1); if (tpcumlist) free(tpcumlist); if (pcumlist) free(pcumlist); if (tucumlist) free(tucumlist); if (ucumlist) free(ucumlist); if (tccumlist) free(tccumlist); if (ccumlist) free(ccumlist); if (tsklist) free(tsklist); if (sellist) free(sellist); return lastchar; /* ** stop it */ case MQUIT: move(LINES-1, 0); clrtoeol(); refresh(); cleanstop(0); /* ** switch to bar graph mode */ case MBARGRAPH: erase(); refresh(); if (tpcumlist) free(tpcumlist); if (pcumlist) free(pcumlist); if (tucumlist) free(tucumlist); if (ucumlist) free(ucumlist); if (tccumlist) free(tccumlist); if (ccumlist) free(ccumlist); if (tsklist) free(tsklist); if (sellist) free(sellist); return lastchar; /* ** manual trigger for next sample */ case MSAMPNEXT: if (paused) break; getalarm(0); if (tpcumlist) free(tpcumlist); if (pcumlist) free(pcumlist); if (tucumlist) free(tucumlist); if (ucumlist) free(ucumlist); if (tccumlist) free(tccumlist); if (ccumlist) free(ccumlist); if (tsklist) free(tsklist); if (sellist) free(sellist); return lastchar; /* ** manual trigger for previous sample */ case MSAMPPREV: if (!rawreadflag) { statmsg = "Only allowed when viewing " "raw file!"; beep(); break; } if (paused) break; if (tpcumlist) free(tpcumlist); if (pcumlist) free(pcumlist); if (tucumlist) free(tucumlist); if (ucumlist) free(ucumlist); if (tccumlist) free(tccumlist); if (ccumlist) free(ccumlist); if (tsklist) free(tsklist); if (sellist) free(sellist); return lastchar; /* ** branch to certain time stamp */ case MSAMPBRANCH: if (!rawreadflag) { statmsg = "Only allowed when viewing " "raw file!"; beep(); break; } if (paused) break; 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; } if (tpcumlist) free(tpcumlist); if (pcumlist) free(pcumlist); if (tucumlist) free(tucumlist); if (ucumlist) free(ucumlist); if (tccumlist) free(tccumlist); if (ccumlist) free(ccumlist); if (tsklist) free(tsklist); if (sellist) free(sellist); return lastchar; /* ** sort order automatically depending on ** most busy resource */ case MSORTAUTO: showorder = MSORTAUTO; firstproc = 0; break; /* ** sort in cpu-activity order */ case MSORTCPU: showorder = MSORTCPU; firstproc = 0; break; /* ** sort in memory-consumption order */ case MSORTMEM: showorder = MSORTMEM; firstproc = 0; break; /* ** sort in disk-activity order */ case MSORTDSK: if ( !(supportflags & IOSTAT) ) { if (rawreadflag) statmsg = "No disk activity in " "raw file!"; else statmsg = "No privileges to " "view disk activity!"; break; } showorder = MSORTDSK; firstproc = 0; break; /* ** sort in network-activity order */ case MSORTNET: if ( !(supportflags & NETATOP || supportflags & NETATOPBPF)) { statmsg = "Module 'netatop' or 'netatop-bpf' not " "active or no root privs; " "request ignored!"; break; } showorder = MSORTNET; firstproc = 0; break; /* ** sort in gpu-activity order */ case MSORTGPU: if ( !(supportflags & GPUSTAT) ) { statmsg = "No GPU activity figures " "available; request ignored!"; break; } showorder = MSORTGPU; firstproc = 0; break; /* ** general figures per process */ case MPROCGEN: showtype = MPROCGEN; if (showorder != MSORTAUTO) showorder = MSORTCPU; firstproc = 0; break; /* ** memory-specific figures per process */ case MPROCMEM: showtype = MPROCMEM; if (showorder != MSORTAUTO) showorder = MSORTMEM; firstproc = 0; break; /* ** disk-specific figures per process */ case MPROCDSK: if ( !(supportflags & IOSTAT) ) { if (rawreadflag) statmsg = "No disk activity in " "raw file!"; else statmsg = "No privileges to " "view disk activity!"; break; } showtype = MPROCDSK; if (showorder != MSORTAUTO) showorder = MSORTDSK; firstproc = 0; break; /* ** network-specific figures per process */ case MPROCNET: if ( !(supportflags & NETATOP || supportflags & NETATOPBPF) ) { statmsg = "Module 'netatop' or 'netatop-bpf' not " "active or no root privs; " "request ignored!"; break; } showtype = MPROCNET; if (showorder != MSORTAUTO) showorder = MSORTNET; firstproc = 0; break; /* ** GPU-specific figures per process */ case MPROCGPU: if ( !(supportflags & GPUSTAT) ) { statmsg = "No GPU activity figures " "available (atopgpud might " "not be running); " "request ignored!"; break; } showtype = MPROCGPU; if (showorder != MSORTAUTO) showorder = MSORTGPU; firstproc = 0; break; /* ** various info per process */ case MPROCVAR: showtype = MPROCVAR; firstproc = 0; break; /* ** command line per process */ case MPROCARG: showtype = MPROCARG; firstproc = 0; break; /* ** cgroup v2 info per process */ case MPROCCGR: if ( !(supportflags & CGROUPV2) ) { statmsg = "No cgroup v2 figures " "available; request ignored!"; break; } showtype = MPROCCGR; firstproc = 0; break; /* ** own defined output per process */ case MPROCOWN: if (! ownprocs[0].f) { statmsg = "Own process line is not " "configured in rc-file; " "request ignored"; break; } showtype = MPROCOWN; firstproc = 0; break; /* ** scheduling-values per process */ case MPROCSCH: showtype = MPROCSCH; if (showorder != MSORTAUTO) showorder = MSORTCPU; firstproc = 0; break; /* ** accumulated resource consumption per user */ case MCUMUSER: statmsg = "Consumption per user; use 'a' to " "toggle between all/active processes"; showtype = MCUMUSER; firstproc = 0; break; /* ** accumulated resource consumption per program */ case MCUMPROC: statmsg = "Consumption per program; use 'a' to " "toggle between all/active processes"; showtype = MCUMPROC; firstproc = 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; firstproc = 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(3); /* force new sample */ firstproc = 0; break; /* ** send signal to process */ case MKILLPROC: if (rawreadflag) { 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) alarm(3); /* set short timer */ firstproc = 0; break; /* ** change interval timeout */ case MINTERVAL: if (rawreadflag) { statmsg = "Not possible 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(3); /* set short timer */ } else { statmsg = "No timer set; waiting for " "manual trigger ('t')....."; } firstproc = 0; break; /* ** focus on specific user */ case MSELUSER: alarm(0); /* stop the clock */ echo(); move(statline, 0); clrtoeol(); printw("Username as regular expression " "(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 */ firstproc = 0; 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 */ firstproc = 0; 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 */ firstproc = 0; 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 */ firstproc = 0; 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 process/thread states " "(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 */ firstproc = 0; 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 */ firstproc = 0; 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 */ firstproc = 0; break; /* ** toggle pause-state */ case MPAUSE: if (paused) { paused=0; clrtoeol(); refresh(); if (!rawreadflag) alarm(1); } else { paused=1; clrtoeol(); refresh(); alarm(0); /* stop the clock */ } break; /* ** toggle between modified processes and ** all processes */ case MALLPROC: if (deviatonly) { deviatonly=0; statmsg = "All processes/threads will be " "shown/accumulated..."; } else { deviatonly=1; statmsg = "Only active processes/threads " "will be shown/accumulated..."; } tlastorder = 0; firstproc = 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....."; } firstproc = 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..."; } firstproc = 0; break; /* ** per-thread view wanted with sorting on ** process level */ case MTHREAD: if (threadview) { threadview = 0; statmsg = "Thread view disabled"; firstproc = 0; } else { threadview = 1; statmsg = "Thread view enabled"; firstproc = 0; } break; /* ** sorting on thread level as well (threadview) */ case MTHRSORT: if (threadsort) { threadsort = 0; statmsg = "Thread sorting disabled for thread view"; firstproc = 0; } else { threadsort = 1; statmsg = "Thread sorting enabled for thread view"; firstproc = 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 exited processes in output */ case MSUPEXITS: if (suppressexit) { suppressexit = 0; statmsg = "Exited processes will " "be shown/accumulated"; firstproc = 0; } else { suppressexit = 1; statmsg = "Exited processes will " "not be shown/accumulated"; firstproc = 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!"; } } firstproc = 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(3); /* set short timer */ firstproc = 0; break; /* ** reset statistics */ case MRESET: getalarm(0); /* restart the clock */ paused = 0; if (tpcumlist) free(tpcumlist); if (pcumlist) free(pcumlist); if (tucumlist) free(tucumlist); if (ucumlist) free(ucumlist); if (tccumlist) free(tccumlist); if (ccumlist) free(ccumlist); if (tsklist) free(tsklist); if (sellist) free(sellist); return lastchar; /* ** 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 (firstproc < alistsz-1) firstproc += 1; break; /* ** handle arrow up to go one line up */ case KEY_UP: if (firstproc > 0) firstproc -= 1; break; /* ** handle forward */ case KEY_NPAGE: case MLISTFW: if (alistsz-firstproc > plistsz) firstproc += plistsz; break; /* ** handle backward */ case KEY_PPAGE: case MLISTBW: if (firstproc >= plistsz) firstproc -= plistsz; else firstproc = 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 */ { if (tpcumlist) free(tpcumlist); if (pcumlist) free(pcumlist); if (tucumlist) free(tucumlist); if (ucumlist) free(ucumlist); if (tccumlist) free(tccumlist); if (ccumlist) free(ccumlist); if (tsklist) free(tsklist); if (sellist) free(sellist); return '\0'; } } } /* ** 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 is 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.pid) break; i++; } if (sel->pid[i] != curstat->gen.pid) 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 */ if (sel->states[0]) { 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: if ( !(supportflags & IOSTAT) ) { if (rawreadflag) fprintf(stderr, "No disk activity in " "raw file!\n"); else fprintf(stderr, "No privileges to view " "disk activity!\n"); sleep(3); break; } showtype = MPROCDSK; showorder = MSORTDSK; break; case MPROCNET: if ( !(supportflags & NETATOP || supportflags & NETATOPBPF) ) { fprintf(stderr, "Module 'netatop' or 'netatop-bpf' not " "active; request ignored!"); sleep(3); break; } showtype = MPROCNET; showorder = MSORTNET; break; case MPROCVAR: showtype = MPROCVAR; break; case MPROCARG: showtype = MPROCARG; break; case MPROCCGR: if ( !(supportflags & CGROUPV2) ) { fprintf(stderr, "No cgroup v2 details " "available; request ignored!\n"); sleep(3); break; } showtype = MPROCCGR; 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; 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_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(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; } helptext[] = { {"Display mode:\n", ' '}, {"\t'%c' - show bar graphs for system utilization (toggle)\n", MBARGRAPH}, {"\n", ' '}, {"Information in text mode for active processes:\n", ' '}, {"\t'%c' - generic info (default)\n", MPROCGEN}, {"\t'%c' - memory details\n", MPROCMEM}, {"\t'%c' - disk details\n", MPROCDSK}, {"\t'%c' - network details\n", MPROCNET}, {"\t'%c' - cgroups v2 details\n", MPROCCGR}, {"\t'%c' - scheduling and thread-group info\n", MPROCSCH}, {"\t'%c' - GPU details\n", MPROCGPU}, {"\t'%c' - various info (ppid, user/group, date/time, status, " "exitcode)\n", MPROCVAR}, {"\t'%c' - full command line per process\n", MPROCARG}, {"\t'%c' - use own output line definition\n", MPROCOWN}, {"\n", ' '}, {"Sort list of processes in order of:\n", ' '}, {"\t'%c' - cpu activity\n", MSORTCPU}, {"\t'%c' - memory consumption\n", MSORTMEM}, {"\t'%c' - disk activity\n", MSORTDSK}, {"\t'%c' - network activity\n", MSORTNET}, {"\t'%c' - GPU activity\n", MSORTGPU}, {"\t'%c' - most active system resource (auto mode)\n", MSORTAUTO}, {"\n", ' '}, {"Accumulated figures:\n", ' '}, {"\t'%c' - total resource consumption per user\n", MCUMUSER}, {"\t'%c' - total resource consumption per program (i.e. same " "process name)\n", MCUMPROC}, {"\t'%c' - total resource consumption per container/pod\n",MCUMCONT}, {"\n", ' '}, {"Process selections (keys shown in header line):\n", ' '}, {"\t'%c' - focus on specific user name " "(regular expression)\n", MSELUSER}, {"\t'%c' - focus on specific program name " "(regular expression)\n", MSELPROC}, {"\t'%c' - focus on specific container/pod name\n", MSELCONT}, {"\t'%c' - focus on specific command line string " "(regular expression)\n", MSELARG}, {"\t'%c' - focus on specific process id (PID)\n", MSELPID}, {"\t'%c' - focus on specific process/thread state(s)\n", MSELSTATE}, {"\n", ' '}, {"System resource selections (keys shown in header line):\n",' '}, {"\t'%c' - focus on specific system resources " "(regular expression)\n", MSELSYS}, {"\n", ' '}, {"Screen-handling:\n", ' '}, {"\t^L - redraw the screen \n", ' '}, {"\tPgDn - show next page in the process list (or ^F)\n", ' '}, {"\tPgUp - show previous page in the process list (or ^B)\n", ' '}, {"\tArDn - arrow-down for next line in process list\n", ' '}, {"\tArUp arrow-up for previous line in process list\n", ' '}, {"\tArRt - arrow-right for next character in full command line\n", ' '}, {"\tArLt - arrow-left for previous character in full command line\n", ' '}, {"\n", ' '}, {"Presentation (keys shown in header line):\n", ' '}, {"\t'%c' - show threads within process (thread view) (toggle)\n", MTHREAD}, {"\t'%c' - sort threads (when combined with thread view) (toggle)\n", MTHRSORT}, {"\t'%c' - show all processes (default: active processes) (toggle)\n", MALLPROC}, {"\t'%c' - show fixed number of header lines (toggle)\n", MSYSFIXED}, {"\t'%c' - suppress sorting system resources (toggle)\n", MSYSNOSORT}, {"\t'%c' - suppress exited processes in output (toggle)\n", MSUPEXITS}, {"\t'%c' - no colors to indicate high occupation (toggle)\n", MCOLORS}, {"\t'%c' - show average-per-second i.s.o. total values (toggle)\n", MAVGVAL}, {"\t'%c' - calculate proportional set size (PSIZE) (toggle)\n", MCALCPSS}, {"\t'%c' - determine WCHAN per thread (toggle)\n", MGETWCHAN}, {"\n", ' '}, {"Raw file viewing:\n", ' '}, {"\t'%c' - show next sample in raw file\n", MSAMPNEXT}, {"\t'%c' - show previous sample in raw file\n", MSAMPPREV}, {"\t'%c' - branch to certain time in raw file\n", MSAMPBRANCH}, {"\t'%c' - rewind to begin of raw file\n", MRESET}, {"\n", ' '}, {"Miscellaneous commands:\n", ' '}, {"\t'%c' - change interval timer (0 = only manual trigger)\n", MINTERVAL}, {"\t'%c' - manual trigger to force next sample\n", MSAMPNEXT}, {"\t'%c' - reset counters to boot time values\n", MRESET}, {"\t'%c' - pause button to freeze current sample (toggle)\n", MPAUSE}, {"\n", ' '}, {"\t'%c' - limited lines for per-cpu, disk and interface resources\n", MSYSLIMIT}, {"\t'%c' - kill a process (i.e. send a signal)\n", MKILLPROC}, {"\n", ' '}, {"\t'%c' - version information\n", MVERSION}, {"\t'%c' - help information\n", MHELP1}, {"\t'%c' - help information\n", MHELP2}, {"\t'%c' - quit this program\n", MQUIT}, }; static int helplines = sizeof(helptext)/sizeof(struct helptext); static void showhelp(int helpline) { int winlines = LINES-helpline, shown, tobeshown=1, i; 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, shown=0; i < helplines; i++, shown++) { wprintw(helpwin, helptext[i].helpline, helptext[i].helparg); /* ** when the window is full, start paging interactively */ if (i >= 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); delwin(helpwin); return; } wmove(helpwin, winlines-1, 0); } } 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 exited 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 cgroup v2 info per process\n", MPROCCGR); 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; strncpy(procsel.username, val, sizeof procsel.username -1); procsel.username[sizeof procsel.username -1] = 0; 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) { strncpy(procsel.progname, val, sizeof procsel.progname -1); 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_flags(char *name, char *val) { int i; 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 MPROCCGR: showtype = MPROCCGR; break; case MPROCOWN: showtype = MPROCOWN; break; case MCUMUSER: showtype = MCUMUSER; break; case MCUMPROC: showtype = MCUMPROC; break; case MCUMCONT: showtype = MCUMCONT; break; case MALLPROC: 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; } } } atop-2.10.0/showgeneric.h0000664000203100020310000001453714545501443014544 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_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 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 COLORCPUIDLE 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 MPROCCGR 'X' #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 'G' #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 MALLPROC '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 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 pause indication */ extern int paused; /* ** 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); int priproc(struct tstat **, int, int, int, int, int, char, char, struct syscap *, int, int); void priphead(int, int, char *, char *, char, count_t); 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_flags(char *, char *); #endif atop-2.10.0/showlinux.c0000664000203100020310000024200014545501443014246 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 "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_proc_prints(proc_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 proc_printdefs */ proc_printdef *allprocpdefs[]= { &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_CGROUP_PATH, &procprt_CGRCPUWGT, &procprt_CGRCPUMAX, &procprt_CGRCPUMAXR, &procprt_CGRMEMMAX, &procprt_CGRMEMMAXR, &procprt_CGRSWPMAX, &procprt_CGRSWPMAXR, &procprt_SORTITEM, 0 }; /* * table with all proc_printdefs with PID/TID width to be initialized */ proc_printdef *idprocpdefs[]= { &procprt_PID, &procprt_TID, &procprt_PPID, &procprt_VPID, 0 }; /***************************************************************/ /* * output definitions for process data * these should be user configurable */ proc_printpair userprocs[MAXITEMS]; proc_printpair memprocs[MAXITEMS]; proc_printpair schedprocs[MAXITEMS]; proc_printpair genprocs[MAXITEMS]; proc_printpair dskprocs[MAXITEMS]; proc_printpair netprocs[MAXITEMS]; proc_printpair gpuprocs[MAXITEMS]; proc_printpair varprocs[MAXITEMS]; proc_printpair cmdprocs[MAXITEMS]; proc_printpair cgrprocs[MAXITEMS]; proc_printpair ownprocs[MAXITEMS]; proc_printpair totusers[MAXITEMS]; proc_printpair totprocs[MAXITEMS]; proc_printpair totconts[MAXITEMS]; /*****************************************************************/ /* * 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; items[i].name && 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"); sprintf(p, "%*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_proc_prints: make array of proc_printpairs ** input: string, proc_printpair array, maxentries */ static void make_proc_prints(proc_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; items[i].name && iconfigname, name)==0) { ar[i].f=allprocpdefs[j]; ar[i].prio=items[i].prio; break; } } if (allprocpdefs[j]==0) { mcleanstop(1, "atoprc - ownprocline: item %s invalid!\n", name); } } ar[i].f=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 list */ void priphead(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_proc_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_proc_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_proc_prints(dskprocs, MAXITEMS, "PID:10 TID:4 RDDSK:9 " "WRDSK:9 WCANCL:8 " "SORTITEM:10 CMD:10", "built-in dskprocs"); make_proc_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_proc_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_proc_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_proc_prints(cmdprocs, MAXITEMS, "PID:10 TID:4 S:8 SORTITEM:10 COMMAND-LINE:10", "built-in cmdprocs"); make_proc_prints(cgrprocs, MAXITEMS, "PID:10 CPUWGT:9 CPUMAX:9 CPUMAXR:9 MEMMAX:8 MMMAXR:8 " "SWPMAX:7 SWMAXR:7 SORTITEM:6 CMD:9 CGROUP-PATH:10", "built-in cgrprocs"); make_proc_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_proc_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_proc_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"); } /* ** 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: showhdrline(genprocs, curlist, totlist, *showorder, autosort); break; case MPROCMEM: showhdrline(memprocs, curlist, totlist, *showorder, autosort); break; case MPROCDSK: showhdrline(dskprocs, curlist, totlist, *showorder, autosort); break; case MPROCNET: showhdrline(netprocs, curlist, totlist, *showorder, autosort); break; case MPROCGPU: showhdrline(gpuprocs, curlist, totlist, *showorder, autosort); break; case MPROCVAR: showhdrline(varprocs, curlist, totlist, *showorder, autosort); break; case MPROCARG: showhdrline(cmdprocs, curlist, totlist, *showorder, autosort); break; case MPROCCGR: showhdrline(cgrprocs, curlist, totlist, *showorder, autosort); break; case MPROCOWN: showhdrline(ownprocs, curlist, totlist, *showorder, autosort); break; case MPROCSCH: showhdrline(schedprocs, curlist, totlist, *showorder, autosort); break; case MCUMUSER: showhdrline(totusers, curlist, totlist, *showorder, autosort); break; case MCUMPROC: showhdrline(totprocs, curlist, totlist, *showorder, autosort); break; case MCUMCONT: showhdrline(totconts, curlist, totlist, *showorder, autosort); 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_proc_prints(genprocs, MAXITEMS, format, "built-in genprocs"); } /* ** 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 MPROCCGR: showprocline(cgrprocs, 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) { 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) { 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; } extra.percputot = extra.pernumacputot / (sstat->cpu.nrcpu/sstat->cpunuma.nrnuma); 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_proc_prints(ownprocs, MAXITEMS, val, name); } atop-2.10.0/showlinux.h0000664000203100020310000004457314545501443014272 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; /* ** structure for process print-list */ typedef struct { char *head; // column header char *configname; // name as used to config print line char *(*doactiveconvert)(struct tstat *,int,int); // pointer to conv function // for active process char *(*doexitconvert) (struct tstat *,int,int); // pointer to conv function // for exited process int width; // required width int varwidth; // width may grow (eg cmd params) } proc_printdef; typedef struct { proc_printdef *f; int prio; } proc_printpair; void showsysline(sys_printpair* elemptr, struct sstat* sstat, extraparam *extra, char *labeltext, unsigned int badness); void showhdrline(proc_printpair* elemptr, int curlist, int totlist, char showorder, char autosort); void showprocline(proc_printpair* elemptr, struct tstat *curstat, double perc, int nsecs, int avgval); 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 proc_printdef *allprocpdefs[]; extern proc_printdef procprt_PID; extern proc_printdef procprt_TID; extern proc_printdef procprt_PPID; extern proc_printdef procprt_SYSCPU; extern proc_printdef procprt_USRCPU; extern proc_printdef procprt_VGROW; extern proc_printdef procprt_RGROW; extern proc_printdef procprt_MINFLT; extern proc_printdef procprt_MAJFLT; extern proc_printdef procprt_VSTEXT; extern proc_printdef procprt_VSIZE; extern proc_printdef procprt_RSIZE; extern proc_printdef procprt_PSIZE; extern proc_printdef procprt_VSLIBS; extern proc_printdef procprt_VDATA; extern proc_printdef procprt_VSTACK; extern proc_printdef procprt_SWAPSZ; extern proc_printdef procprt_LOCKSZ; extern proc_printdef procprt_CMD; extern proc_printdef procprt_RUID; extern proc_printdef procprt_EUID; extern proc_printdef procprt_SUID; extern proc_printdef procprt_FSUID; extern proc_printdef procprt_RGID; extern proc_printdef procprt_EGID; extern proc_printdef procprt_SGID; extern proc_printdef procprt_FSGID; extern proc_printdef procprt_CTID; extern proc_printdef procprt_VPID; extern proc_printdef procprt_CID; extern proc_printdef procprt_STDATE; extern proc_printdef procprt_STTIME; extern proc_printdef procprt_ENDATE; extern proc_printdef procprt_ENTIME; extern proc_printdef procprt_THR; extern proc_printdef procprt_TRUN; extern proc_printdef procprt_TSLPI; extern proc_printdef procprt_TSLPU; extern proc_printdef procprt_TIDLE; extern proc_printdef procprt_POLI; extern proc_printdef procprt_NICE; extern proc_printdef procprt_PRI; extern proc_printdef procprt_RTPR; extern proc_printdef procprt_CURCPU; extern proc_printdef procprt_ST; extern proc_printdef procprt_EXC; extern proc_printdef procprt_S; extern proc_printdef procprt_COMMAND_LINE; extern proc_printdef procprt_NPROCS; extern proc_printdef procprt_RDDSK; extern proc_printdef procprt_WRDSK; extern proc_printdef procprt_CWRDSK; extern proc_printdef procprt_WCANCEL; extern proc_printdef procprt_TCPRCV; extern proc_printdef procprt_TCPRASZ; extern proc_printdef procprt_TCPSND; extern proc_printdef procprt_TCPSASZ; extern proc_printdef procprt_UDPRCV; extern proc_printdef procprt_UDPRASZ; extern proc_printdef procprt_UDPSND; extern proc_printdef procprt_UDPSASZ; extern proc_printdef procprt_RNET; extern proc_printdef procprt_SNET; extern proc_printdef procprt_BANDWI; extern proc_printdef procprt_BANDWO; extern proc_printdef procprt_GPULIST; extern proc_printdef procprt_GPUMEMNOW; extern proc_printdef procprt_GPUMEMAVG; extern proc_printdef procprt_GPUGPUBUSY; extern proc_printdef procprt_GPUMEMBUSY; extern proc_printdef procprt_SORTITEM; extern proc_printdef procprt_RUNDELAY; extern proc_printdef procprt_BLKDELAY; extern proc_printdef procprt_WCHAN; extern proc_printdef procprt_NVCSW; extern proc_printdef procprt_NIVCSW; extern proc_printdef procprt_CGROUP_PATH; extern proc_printdef procprt_CGRCPUWGT; extern proc_printdef procprt_CGRCPUMAX; extern proc_printdef procprt_CGRCPUMAXR; extern proc_printdef procprt_CGRMEMMAX; extern proc_printdef procprt_CGRMEMMAXR; extern proc_printdef procprt_CGRSWPMAX; extern proc_printdef procprt_CGRSWPMAXR; //extern char *procprt_NRDDSK_ae(struct tstat *, int, int); //extern char *procprt_NWRDSK_a(struct tstat *, int, int); //extern char *procprt_NRDDSK_e(struct tstat *, int, int); //extern char *procprt_NWRDSK_e(struct tstat *, int, int); 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.10.0/showprocs.c0000664000203100020310000020537114545501443014247 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 format_bandw(char *, count_t); static void gettotwidth(proc_printpair *, int *, int *, int *); static int *getspacings(proc_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_CGROUP_PATH_a(struct tstat *, int, int); char *procprt_CGROUP_PATH_e(struct tstat *, int, int); char *procprt_CGRCPUWGT_a(struct tstat *, int, int); char *procprt_CGRCPUWGT_e(struct tstat *, int, int); char *procprt_CGRCPUMAX_a(struct tstat *, int, int); char *procprt_CGRCPUMAX_e(struct tstat *, int, int); char *procprt_CGRCPUMAXR_a(struct tstat *, int, int); char *procprt_CGRCPUMAXR_e(struct tstat *, int, int); char *procprt_CGRMEMMAX_a(struct tstat *, int, int); char *procprt_CGRMEMMAX_e(struct tstat *, int, int); char *procprt_CGRMEMMAXR_a(struct tstat *, int, int); char *procprt_CGRMEMMAXR_e(struct tstat *, int, int); char *procprt_CGRSWPMAX_a(struct tstat *, int, int); char *procprt_CGRSWPMAX_e(struct tstat *, int, int); char *procprt_CGRSWPMAXR_a(struct tstat *, int, int); char *procprt_CGRSWPMAXR_e(struct tstat *, int, int); char *procprt_SORTITEM_ae(struct tstat *, int, int); static char *columnhead[] = { [MSORTCPU]= "CPU", [MSORTMEM]= "MEM", [MSORTDSK]= "DSK", [MSORTNET]= "NET", [MSORTGPU]= "GPU", }; /***************************************************************/ static int *colspacings; // ugly static var, // but saves a lot of recomputations // points to table with intercolumn // spacings static proc_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 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(proc_printpair* elemptr, int *nitems, int *sumwidth, int* varwidth) { int i; int col; int varw=0; for (i=0, col=0; elemptr[i].f!=0; ++i) { col += (elemptr[i].f->varwidth ? 0 : elemptr[i].f->width); varw += elemptr[i].f->varwidth; newelems[i]=elemptr[i]; // copy element } newelems[i].f=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(proc_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].f->varwidth; memmove(newelems+lowestprio_index, newelems+lowestprio_index+1, (nitems-lowestprio_index)* sizeof(proc_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].f->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 { chead = columnhead[order]; autoindic = autosort ? "A" : " "; widen = procprt_SORTITEM.width-3; } else { chead=curelem.f->head; autoindic=""; } if (screen) { col += sprintf(buf+col, "%*s%s%*s", widen, autoindic, chead, colspacings[n], ""); } else { col += sprintf(buf+col, "%s%s ", autoindic, chead); } elemptr++; n++; } if (screen) // add page number, eat from last header if needed... { pagindiclen=sprintf(pagindic,"%d/%d", curlist, totlist); allign=COLS-col-pagindiclen; // extra spaces needed if (allign >= 0) // allign by adding spaces { sprintf(buf+col, "%*s", allign+pagindiclen, pagindic); } else if (col+allign >= 0) { // allign by removing from the right sprintf(buf+col+allign, "%s", pagindic); } } printg("%s", buf); if (!screen) { 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(proc_printpair* elemptr, struct tstat *curstat, double perc, int nsecs, int avgval) { proc_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).f!=0) { // what to print? SORTITEM, or active process or // exited process? if (curelem.f->head==0) // empty string=sortitem { printg("%*.0lf%%", procprt_SORTITEM.width-1, perc); } else if (curstat->gen.state != 'E') // active process { printg("%s", curelem.f->doactiveconvert(curstat, avgval, nsecs)); } else // exited process { printg("%s", curelem.f->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"); } } /*******************************************************************/ /* 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) sprintf(buf, "%*s", procprt_TID.width, "-"); else sprintf(buf, "%*d", procprt_TID.width, curstat->gen.pid); return buf; } proc_printdef procprt_TID = { "TID", "TID", procprt_TID_ae, procprt_TID_ae, 5}; //DYNAMIC WIDTH! /***************************************************************/ char * procprt_PID_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[64]; sprintf(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) sprintf(buf, "%*s", procprt_PID.width, "?"); else sprintf(buf, "%*d", procprt_PID.width, curstat->gen.tgid); return buf; } proc_printdef procprt_PID = { "PID", "PID", procprt_PID_a, procprt_PID_e, 5}; //DYNAMIC WIDTH! /***************************************************************/ char * procprt_PPID_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[64]; sprintf(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) sprintf(buf, "%*d", procprt_PPID.width, curstat->gen.ppid); else sprintf(buf, "%*s", procprt_PPID.width, "-"); return buf; } proc_printdef procprt_PPID = { "PPID", "PPID", procprt_PPID_a, procprt_PPID_e, 5 }; //DYNAMIC WIDTH! /***************************************************************/ char * procprt_VPID_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[64]; sprintf(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]; sprintf(buf, "%*s", procprt_VPID.width, "-"); return buf; } proc_printdef procprt_VPID = { "VPID", "VPID", procprt_VPID_a, procprt_VPID_e, 5 }; //DYNAMIC WIDTH! /***************************************************************/ char * procprt_CTID_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[32]; sprintf(buf, "%5d", curstat->gen.ctid); return buf; } char * procprt_CTID_e(struct tstat *curstat, int avgval, int nsecs) { return " -"; } proc_printdef procprt_CTID = { " CTID", "CTID", 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]) sprintf(buf, "%-15s", curstat->gen.utsname); else sprintf(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]) sprintf(buf, "%-15s", curstat->gen.utsname); else sprintf(buf, "%-15s", "?"); return buf; } proc_printdef procprt_CID = { "CID/POD ", "CID", 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; } proc_printdef procprt_SYSCPU = { "SYSCPU", "SYSCPU", 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; } proc_printdef procprt_USRCPU = { "USRCPU", "USRCPU", 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"; } proc_printdef procprt_VGROW = { " VGROW", "VGROW", 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"; } proc_printdef procprt_RGROW = { " RGROW", "RGROW", 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; } proc_printdef procprt_MINFLT = { "MINFLT", "MINFLT", 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; } proc_printdef procprt_MAJFLT = { "MAJFLT", "MAJFLT", 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"; } proc_printdef procprt_VSTEXT = { "VSTEXT", "VSTEXT", 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"; } proc_printdef procprt_VSIZE = { " VSIZE", "VSIZE", 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"; } proc_printdef procprt_RSIZE = { " RSIZE", "RSIZE", 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"; } proc_printdef procprt_PSIZE = { " PSIZE", "PSIZE", 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"; } proc_printdef procprt_VSLIBS = { "VSLIBS", "VSLIBS", 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"; } proc_printdef procprt_VDATA = { " VDATA", "VDATA", 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"; } proc_printdef procprt_VSTACK = { "VSTACK", "VSTACK", 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"; } proc_printdef procprt_SWAPSZ = { "SWAPSZ", "SWAPSZ", 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"; } proc_printdef procprt_LOCKSZ = { "LOCKSZ", "LOCKSZ", procprt_LOCKSZ_a, procprt_LOCKSZ_e, 6 }; /***************************************************************/ char * procprt_CMD_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[15]; sprintf(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]; sprintf(helpbuf, "<%.12s>", curstat->gen.name); sprintf(buf, "%-14.14s", helpbuf); return buf; } proc_printdef procprt_CMD = { "CMD ", "CMD", procprt_CMD_a, procprt_CMD_e, 14 }; /***************************************************************/ char * procprt_RUID_ae(struct tstat *curstat, int avgval, int nsecs) { static char buf[9]; struct passwd *pwd; if ( (pwd = getpwuid(curstat->gen.ruid)) ) { sprintf(buf, "%-8.8s", pwd->pw_name); } else { snprintf(buf, sizeof buf, "%-8d", curstat->gen.ruid); } return buf; } proc_printdef procprt_RUID = { "RUID ", "RUID", procprt_RUID_ae, procprt_RUID_ae, 8 }; /***************************************************************/ char * procprt_EUID_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[9]; struct passwd *pwd; if ( (pwd = getpwuid(curstat->gen.euid)) ) { sprintf(buf, "%-8.8s", pwd->pw_name); } else { snprintf(buf, sizeof buf, "%-8d", curstat->gen.euid); } return buf; } char * procprt_EUID_e(struct tstat *curstat, int avgval, int nsecs) { return "- "; } proc_printdef procprt_EUID = { "EUID ", "EUID", procprt_EUID_a, procprt_EUID_e, 8 }; /***************************************************************/ char * procprt_SUID_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[9]; struct passwd *pwd; if ( (pwd = getpwuid(curstat->gen.suid)) ) { sprintf(buf, "%-8.8s", pwd->pw_name); } else { snprintf(buf, sizeof buf, "%-8d", curstat->gen.suid); } return buf; } char * procprt_SUID_e(struct tstat *curstat, int avgval, int nsecs) { return "- "; } proc_printdef procprt_SUID = { "SUID ", "SUID", procprt_SUID_a, procprt_SUID_e, 8 }; /***************************************************************/ char * procprt_FSUID_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[9]; struct passwd *pwd; if ( (pwd = getpwuid(curstat->gen.fsuid)) ) { sprintf(buf, "%-8.8s", pwd->pw_name); } else { snprintf(buf, sizeof buf, "%-8d", curstat->gen.fsuid); } return buf; } char * procprt_FSUID_e(struct tstat *curstat, int avgval, int nsecs) { return "- "; } proc_printdef procprt_FSUID = { "FSUID ", "FSUID", procprt_FSUID_a, procprt_FSUID_e, 8 }; /***************************************************************/ char * procprt_RGID_ae(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; struct group *grp; char *groupname; char grname[16]; if ( (grp = getgrgid(curstat->gen.rgid)) ) { groupname = grp->gr_name; } else { snprintf(grname, sizeof grname, "%d",curstat->gen.rgid); groupname = grname; } sprintf(buf, "%-8.8s", groupname); return buf; } proc_printdef procprt_RGID = { "RGID ", "RGID", procprt_RGID_ae, procprt_RGID_ae, 8 }; /***************************************************************/ char * procprt_EGID_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; struct group *grp; char *groupname; char grname[16]; if ( (grp = getgrgid(curstat->gen.egid)) ) { groupname = grp->gr_name; } else { snprintf(grname, sizeof grname, "%d",curstat->gen.egid); groupname = grname; } sprintf(buf, "%-8.8s", groupname); return buf; } char * procprt_EGID_e(struct tstat *curstat, int avgval, int nsecs) { return "- "; } proc_printdef procprt_EGID = { "EGID ", "EGID", procprt_EGID_a, procprt_EGID_e, 8 }; /***************************************************************/ char * procprt_SGID_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; struct group *grp; char *groupname; char grname[16]; if ( (grp = getgrgid(curstat->gen.sgid)) ) { groupname = grp->gr_name; } else { snprintf(grname, sizeof grname, "%d",curstat->gen.sgid); groupname = grname; } sprintf(buf, "%-8.8s", groupname); return buf; } char * procprt_SGID_e(struct tstat *curstat, int avgval, int nsecs) { return "- "; } proc_printdef procprt_SGID = { "SGID ", "SGID", procprt_SGID_a, procprt_SGID_e, 8 }; /***************************************************************/ char * procprt_FSGID_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; struct group *grp; char *groupname; char grname[16]; if ( (grp = getgrgid(curstat->gen.fsgid)) ) { groupname = grp->gr_name; } else { snprintf(grname, sizeof grname,"%d",curstat->gen.fsgid); groupname = grname; } sprintf(buf, "%-8.8s", groupname); return buf; } char * procprt_FSGID_e(struct tstat *curstat, int avgval, int nsecs) { return "- "; } proc_printdef procprt_FSGID = { "FSGID ", "FSGID", 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; } proc_printdef procprt_STDATE = { " STDATE ", "STDATE", 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; } proc_printdef procprt_STTIME = { " STTIME ", "STTIME", 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; } proc_printdef procprt_ENDATE = { " ENDATE ", "ENDATE", 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; } proc_printdef procprt_ENTIME = { " ENTIME ", "ENTIME", procprt_ENTIME_a, procprt_ENTIME_e, 8 }; /***************************************************************/ char * procprt_THR_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[15]; sprintf(buf, "%4d", curstat->gen.nthr); return buf; } char * procprt_THR_e(struct tstat *curstat, int avgval, int nsecs) { return " 0"; } proc_printdef procprt_THR = { " THR", "THR", procprt_THR_a, procprt_THR_e, 4 }; /***************************************************************/ char * procprt_TRUN_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[15]; sprintf(buf, "%4d", curstat->gen.nthrrun); return buf; } char * procprt_TRUN_e(struct tstat *curstat, int avgval, int nsecs) { return " 0"; } proc_printdef procprt_TRUN = { "TRUN", "TRUN", procprt_TRUN_a, procprt_TRUN_e, 4 }; /***************************************************************/ char * procprt_TSLPI_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[15]; sprintf(buf, "%5d", curstat->gen.nthrslpi); return buf; } char * procprt_TSLPI_e(struct tstat *curstat, int avgval, int nsecs) { return " 0"; } proc_printdef procprt_TSLPI = { "TSLPI", "TSLPI", procprt_TSLPI_a, procprt_TSLPI_e, 5 }; /***************************************************************/ char * procprt_TSLPU_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[15]; sprintf(buf, "%5d", curstat->gen.nthrslpu); return buf; } char * procprt_TSLPU_e(struct tstat *curstat, int avgval, int nsecs) { return " 0"; } proc_printdef procprt_TSLPU = { "TSLPU", "TSLPU", procprt_TSLPU_a, procprt_TSLPU_e, 5 }; /***************************************************************/ char * procprt_TIDLE_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[15]; sprintf(buf, "%5d", curstat->gen.nthridle); return buf; } char * procprt_TIDLE_e(struct tstat *curstat, int avgval, int nsecs) { return " 0"; } proc_printdef procprt_TIDLE = { "TIDLE", "TIDLE", 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 "- "; } proc_printdef procprt_POLI = { "POLI", "POLI", procprt_POLI_a, procprt_POLI_e, 4 }; /***************************************************************/ char * procprt_NICE_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[15]; sprintf(buf, "%4d", curstat->cpu.nice); return buf; } char * procprt_NICE_e(struct tstat *curstat, int avgval, int nsecs) { return " -"; } proc_printdef procprt_NICE = { "NICE", "NICE", procprt_NICE_a, procprt_NICE_e, 4 }; /***************************************************************/ char * procprt_PRI_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[15]; sprintf(buf, "%3d", curstat->cpu.prio); return buf; } char * procprt_PRI_e(struct tstat *curstat, int avgval, int nsecs) { return " -"; } proc_printdef procprt_PRI = { "PRI", "PRI", procprt_PRI_a, procprt_PRI_e, 3 }; /***************************************************************/ char * procprt_RTPR_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[15]; sprintf(buf, "%4d", curstat->cpu.rtprio); return buf; } char * procprt_RTPR_e(struct tstat *curstat, int avgval, int nsecs) { return " -"; } proc_printdef procprt_RTPR = { "RTPR", "RTPR", procprt_RTPR_a, procprt_RTPR_e, 4 }; /***************************************************************/ char * procprt_CURCPU_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[15]; sprintf(buf, "%5d", curstat->cpu.curcpu); return buf; } char * procprt_CURCPU_e(struct tstat *curstat, int avgval, int nsecs) { return " -"; } proc_printdef procprt_CURCPU = { "CPUNR", "CPUNR", 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; } proc_printdef procprt_ST = { "ST", "ST", 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]; sprintf(buf, "%3d", curstat->gen.excode & 0xff ? curstat->gen.excode & 0x7f : (curstat->gen.excode>>8) & 0xff); return buf; } proc_printdef procprt_EXC = { "EXC", "EXC", 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"; } proc_printdef procprt_S = { "S", "S", procprt_S_a, procprt_S_e, 1 }; /***************************************************************/ char * procprt_COMMAND_LINE_ae(struct tstat *curstat, int avgval, int nsecs) { extern proc_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) sprintf(buf, "%-*.*s", curwidth, curwidth, pline+curoffset); else sprintf(buf, "%.*s", CMDLEN, pline+curoffset); return buf; } proc_printdef procprt_COMMAND_LINE = { "COMMAND-LINE (horizontal scroll with <- and -> keys)", "COMMAND-LINE", 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; } proc_printdef procprt_NPROCS = { "NPROCS", "NPROCS", procprt_NPROCS_ae, procprt_NPROCS_ae, 6 }; /***************************************************************/ char * procprt_RDDSK_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; val2memstr(curstat->dsk.rsz*512, buf, BFORMAT, avgval, nsecs); return buf; } char * procprt_RDDSK_e(struct tstat *curstat, int avgval, int nsecs) { return " -"; } proc_printdef procprt_RDDSK = { " RDDSK", "RDDSK", procprt_RDDSK_a, procprt_RDDSK_e, 6 }; /***************************************************************/ char * procprt_WRDSK_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; val2memstr(curstat->dsk.wsz*512, buf, BFORMAT, avgval, nsecs); return buf; } char * procprt_WRDSK_e(struct tstat *curstat, int avgval, int nsecs) { return " -"; } proc_printdef procprt_WRDSK = { " WRDSK", "WRDSK", 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; } proc_printdef procprt_CWRDSK = {" WRDSK", "CWRDSK", procprt_CWRDSK_a, procprt_WRDSK_e, 6 }; /***************************************************************/ char * procprt_WCANCEL_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; val2memstr(curstat->dsk.cwsz*512, buf, BFORMAT, avgval, nsecs); return buf; } char * procprt_WCANCEL_e(struct tstat *curstat, int avgval, int nsecs) { return " -"; } proc_printdef procprt_WCANCEL = {"WCANCL", "WCANCL", 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 " -"; } proc_printdef procprt_TCPRCV = { "TCPRCV", "TCPRCV", 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 " -"; } proc_printdef procprt_TCPRASZ = { "TCPRASZ", "TCPRASZ", 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 " -"; } proc_printdef procprt_TCPSND = { "TCPSND", "TCPSND", 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 " -"; } proc_printdef procprt_TCPSASZ = { "TCPSASZ", "TCPSASZ", 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 " -"; } proc_printdef procprt_UDPRCV = { "UDPRCV", "UDPRCV", 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 " -"; } proc_printdef procprt_UDPRASZ = { "UDPRASZ", "UDPRASZ", 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 " -"; } proc_printdef procprt_UDPSND = { "UDPSND", "UDPSND", 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 " -"; } proc_printdef procprt_UDPSASZ = { "UDPSASZ", "UDPSASZ", 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 " -"; } proc_printdef procprt_RNET = { " RNET", "RNET", 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 " -"; } proc_printdef procprt_SNET = { " SNET", "SNET", 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, 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, rkbps); return buf; } else return " -"; } proc_printdef procprt_BANDWI = { " BANDWI", "BANDWI", 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, 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, skbps); return buf; } else return " -"; } proc_printdef procprt_BANDWO = { " BANDWO", "BANDWO", procprt_BANDWO_a, procprt_BANDWO_e, 9}; /***************************************************************/ static void format_bandw(char *buf, 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'; } sprintf(buf, "%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; } proc_printdef procprt_GPULIST = { " GPUNUMS", "GPULIST", 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; } proc_printdef procprt_GPUMEMNOW = { "MEMNOW", "GPUMEM", 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; } proc_printdef procprt_GPUMEMAVG = { "MEMAVG", "GPUMEMAVG", 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; } proc_printdef procprt_GPUGPUBUSY = { "GPUBUSY", "GPUGPUBUSY", 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; } proc_printdef procprt_GPUMEMBUSY = { "MEMBUSY", "GPUMEMBUSY", 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; } proc_printdef procprt_WCHAN = { "WCHAN ", "WCHAN", 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; } proc_printdef procprt_RUNDELAY = { "RDELAY", "RDELAY", 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; } proc_printdef procprt_BLKDELAY = { "BDELAY", "BDELAY", 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 " -"; } proc_printdef procprt_NVCSW = { " NVCSW", "NVCSW", 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 " -"; } proc_printdef procprt_NIVCSW = { "NIVCSW", "NIVCSW", procprt_NIVCSW_a, procprt_NIVCSW_e, 6 }; /***************************************************************/ char * procprt_CGROUP_PATH_a(struct tstat *curstat, int avgval, int nsecs) { extern proc_printdef procprt_CGROUP_PATH; extern int startoffset; // influenced by -> and <- keys static char buf[CMDLEN+1]; char *pline = curstat->gen.cgpath[0] ? curstat->gen.cgpath:"?"; int curwidth = procprt_CGROUP_PATH.width <= CGRLEN ? procprt_CGROUP_PATH.width : CGRLEN; int pathlen = strlen(pline); int curoffset = startoffset <= pathlen ? startoffset : pathlen; if (! curstat->gen.isproc) return ""; if (screen) sprintf(buf, "%-*.*s", curwidth, curwidth, pline+curoffset); else sprintf(buf, "%.*s", CGRLEN, pline+curoffset); return buf; } char * procprt_CGROUP_PATH_e(struct tstat *curstat, int avgval, int nsecs) { return "-"; } proc_printdef procprt_CGROUP_PATH = {"CGROUP (horizontal scroll: <- and ->)", "CGROUP-PATH", procprt_CGROUP_PATH_a, procprt_CGROUP_PATH_e, 42, 0 }; /***************************************************************/ char * procprt_CGRCPUWGT_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[16]; if (! curstat->gen.isproc) return " "; if (curstat->gen.cgpath[0]) { switch (curstat->cpu.cgcpuweight) { case -2: return " -"; default: snprintf(buf, sizeof buf, "%6d", curstat->cpu.cgcpuweight); return buf; } } else return " -"; } char * procprt_CGRCPUWGT_e(struct tstat *curstat, int avgval, int nsecs) { return " -"; } proc_printdef procprt_CGRCPUWGT = { "CPUWGT", "CPUWGT", procprt_CGRCPUWGT_a, procprt_CGRCPUWGT_e, 6}; /***************************************************************/ char * procprt_CGRCPUMAX_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[16]; if (! curstat->gen.isproc) return " "; if (curstat->gen.cgpath[0]) { switch (curstat->cpu.cgcpumax) { case -1: return " max"; case -2: return " -"; default: snprintf(buf, sizeof buf, "%5d%%", curstat->cpu.cgcpumax); return buf; } } else return " -"; } char * procprt_CGRCPUMAX_e(struct tstat *curstat, int avgval, int nsecs) { return " -"; } proc_printdef procprt_CGRCPUMAX = { "CPUMAX", "CPUMAX", procprt_CGRCPUMAX_a, procprt_CGRCPUMAX_e, 6}; /***************************************************************/ char * procprt_CGRCPUMAXR_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[16]; if (! curstat->gen.isproc) return " "; if (curstat->gen.cgpath[0]) { switch (curstat->cpu.cgcpumaxr) { case -1: return " max"; case -2: return " -"; default: snprintf(buf, sizeof buf, "%6d%%", curstat->cpu.cgcpumaxr); return buf; } } else return " -"; } char * procprt_CGRCPUMAXR_e(struct tstat *curstat, int avgval, int nsecs) { return " -"; } proc_printdef procprt_CGRCPUMAXR = { "CPUMAXR", "CPUMAXR", procprt_CGRCPUMAXR_a, procprt_CGRCPUMAXR_e, 7}; /***************************************************************/ char * procprt_CGRMEMMAX_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[16]; if (! curstat->gen.isproc) return " "; if (curstat->gen.cgpath[0]) { switch (curstat->mem.cgmemmax) { case -1: return " max"; case -2: return " -"; default: val2memstr(curstat->mem.cgmemmax*1024, buf, BFORMAT, 0, 0); return buf; } } else return " -"; } char * procprt_CGRMEMMAX_e(struct tstat *curstat, int avgval, int nsecs) { return " -"; } proc_printdef procprt_CGRMEMMAX = { "MEMMAX", "MEMMAX", procprt_CGRMEMMAX_a, procprt_CGRMEMMAX_e, 6}; /***************************************************************/ char * procprt_CGRMEMMAXR_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[16]; if (! curstat->gen.isproc) return " "; if (curstat->gen.cgpath[0]) { switch (curstat->mem.cgmemmaxr) { case -1: return " max"; case -2: return " -"; default: val2memstr(curstat->mem.cgmemmaxr*1024, buf, BFORMAT, 0, 0); return buf; } } else return " -"; } char * procprt_CGRMEMMAXR_e(struct tstat *curstat, int avgval, int nsecs) { return " -"; } proc_printdef procprt_CGRMEMMAXR = { "MMMAXR", "MMMAXR", procprt_CGRMEMMAXR_a, procprt_CGRMEMMAXR_e, 6}; /***************************************************************/ char * procprt_CGRSWPMAX_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[16]; if (! curstat->gen.isproc) return " "; if (curstat->gen.cgpath[0]) { switch (curstat->mem.cgswpmax) { case -1: return " max"; case -2: return " -"; default: val2memstr(curstat->mem.cgswpmax*1024, buf, BFORMAT, 0, 0); return buf; } } else return " -"; } char * procprt_CGRSWPMAX_e(struct tstat *curstat, int avgval, int nsecs) { return " -"; } proc_printdef procprt_CGRSWPMAX = { "SWPMAX", "SWPMAX", procprt_CGRSWPMAX_a, procprt_CGRSWPMAX_e, 6}; /***************************************************************/ char * procprt_CGRSWPMAXR_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[16]; if (! curstat->gen.isproc) return " "; if (curstat->gen.cgpath[0]) { switch (curstat->mem.cgswpmaxr) { case -1: return " max"; case -2: return " -"; default: val2memstr(curstat->mem.cgswpmaxr*1024, buf, BFORMAT, 0, 0); return buf; } } else return " -"; } char * procprt_CGRSWPMAXR_e(struct tstat *curstat, int avgval, int nsecs) { return " -"; } proc_printdef procprt_CGRSWPMAXR = { "SWMAXR", "SWMAXR", procprt_CGRSWPMAXR_a, procprt_CGRSWPMAXR_e, 6}; /***************************************************************/ char * procprt_SORTITEM_ae(struct tstat *curstat, int avgval, int nsecs) { return ""; // dummy function } proc_printdef procprt_SORTITEM = // width is dynamically defined! { 0, "SORTITEM", procprt_SORTITEM_ae, procprt_SORTITEM_ae, 4}; atop-2.10.0/showsys.c0000664000203100020310000032153014545501443013733 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; sprintf(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; sprintf(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; sprintf(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]; sprintf(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]; sprintf(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; sprintf(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; sprintf(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; sprintf(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]; sprintf(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]; sprintf(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 = NULL; } 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 "); sprintf(buf+7, "%4d%%", perc); } else if (maxfreq) // max frequency is known so % can be calculated { strcpy(buf, "curscal "); sprintf(buf+7, "%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; sprintf(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; sprintf(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; sprintf(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; sprintf(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: sprintf(buf, "ipc notavail"); break; case 1: *color = FGCOLORINFO; sprintf(buf, "ipc initial"); break; default: ipc = sstat->cpu.all.instr * 100 / sstat->cpu.all.cycle / 100.0; sprintf(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: sprintf(buf, "ipc notavail"); break; case 1: *color = FGCOLORINFO; sprintf(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; sprintf(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: sprintf(buf+5, "missing"); break; case 1: *color = FGCOLORINFO; sprintf(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: sprintf(buf+5, "missing"); break; case 1: *color = FGCOLORINFO; sprintf(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) { sprintf(buf+5, ">999999"); } else if (sstat->cpu.lavg1 > 999.0) { sprintf(buf+5, "%7.0f", sstat->cpu.lavg1); } else { sprintf(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) { sprintf(buf+5, ">999999"); } else if (sstat->cpu.lavg5 > 999.0) { sprintf(buf+5, "%7.0f", sstat->cpu.lavg5); } else { sprintf(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) { sprintf(buf+6, ">99999"); } else if (sstat->cpu.lavg15 > 999.0) { sprintf(buf+6, "%6.0f", sstat->cpu.lavg15); } else { sprintf(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; sprintf(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; sprintf(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) { sprintf(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? { sprintf(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) { FILE *fp; char state; if ((fp=fopen("/sys/module/zswap/parameters/enabled", "r")) != 0) { if (fscanf(fp, "%c", &state) == 1) { if (state != 'Y') { fclose(fp); return 0; // zswap not enabled } } fclose(fp); return 1; // zswap enabled } else { return 0; // zswap not existing } } 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; sprintf(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; sprintf(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; sprintf(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; sprintf(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; sprintf(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; sprintf(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; sprintf(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]; sprintf(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]; sprintf(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; sprintf(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; sprintf(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; sprintf(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; sprintf(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; sprintf(buf, "cpubusy %3.0f%%", perc); } else sprintf(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; sprintf(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) sprintf(buf+5, "%6.0lf%%", perc); else sprintf(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]); sprintf(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); sprintf(buf+10, " s"); } else if (avioms >= 99.95) { sprintf(buf+5, "%4.0lf ms", avioms); } else if (avioms >= 9.995) { sprintf(buf+5, "%4.1lf ms", avioms); } else if (avioms >= 0.09995) { sprintf(buf+5, "%4.2lf ms", avioms); } else if (avioms >= 0.01) { sprintf(buf+5, "%4.1lf µs", avioms * 1000.0); } else if (avioms >= 0.0001) { sprintf(buf+5, "%4.2lf µs", avioms * 1000.0); } else { sprintf(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[16] = "ethxxxx ----", tmp[32], *ps=tmp; // 012345678901 *color = -1; busy = (ival > oval ? ival : oval) * sstat->ifb.ifb[as->index].lanes / (sstat->ifb.ifb[as->index].rate * 10); 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, "?"); sprintf(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; sprintf(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 "; sprintf(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 "; sprintf(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.10.0/utsnames.c0000644000203100020310000001232714545501443014052 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. // // Reurn 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.10.0/various.c0000644000203100020310000005704314545501443013707 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 ** -------------------------------------------------------------------------- */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "atop.h" #include "acctproc.h" int getresuid(uid_t *ruid, uid_t *euid, uid_t *suid); 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 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 MAXPBYTE9 ONEPBYTE*9LL 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 aformat = PBFORMAT_INT; /* pbytes! */ } 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 aformat = PBFORMAT; /* pbytes! */ } /* ** 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; default: snprintf(strvalue, 7, "!TILT!"); } // 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': 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(); if (vis.show_end) (vis.show_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(); (vis.show_end)(); va_start(args, errormsg); vfprintf(stderr, errormsg, args); va_end(args); exit(exitcode); } /* ** cleanup, give error message and exit */ void cleanstop(int exitcode) { acctswoff(); netatop_signoff(); (vis.show_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; } atop-2.10.0/versdate.h0000664000203100020310000000014214545501443014027 0ustar gerlofgerlof#ifndef __ATOP_VERSDATA__ #define __ATOP_VERSDATA__ #define ATOPDATE "2024/01/04 10:52:30" #endif atop-2.10.0/version.c0000664000203100020310000000102314545501443013671 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.10.0/version.h0000664000203100020310000000021714545501443013702 0ustar gerlofgerlof#ifndef __ATOP_VERSION__ #define __ATOP_VERSION__ #define ATOPVERS "2.10.0" char *getstrvers(void); unsigned short getnumvers(void); #endif atop-2.10.0/ChangeLog0000664000203100020310000007070614545501444013631 0ustar gerlofgerlofcommit 326e82c03d0d9e60ae9591d3aa7ff6536628b3d1 Author: Gerlof Langeveld Date: Thu Jan 4 10:56:08 2024 +0100 Prepare for atop 2.10 M AUTHORS M Makefile M atopconvert.c M man/atop.1 M man/atopacctd.8 M man/atopcat.1 M man/atopconvert.1 M man/atopgpud.8 M man/atophide.1 M man/atoprc.5 M man/atopsar.1 M photoproc.h M prev/netstats_wrong.h A prev/photoproc_210.h M prev/photosyst_20.h M prev/photosyst_21.h A prev/photosyst_210.h M prev/photosyst_22.h M prev/photosyst_23.h M prev/photosyst_24.h M prev/photosyst_25.h M prev/photosyst_26.h M prev/photosyst_27.h M prev/photosyst_28.h M prev/photosyst_29.h M version.h commit ba78f4c29be88827a28f2613353322b5bda7bacc Author: Gerlof Langeveld Date: Wed Jan 3 13:09:38 2024 +0100 Solve errors/warnings given by clang (issue #282) M atopsar.c M drawbar.c M photoproc.c M photosyst.h M utsnames.c M various.c commit e6f66819337a824cf6562ac0b0fe0c5ce4c9f6a2 Merge: db78190 e379890 Author: Gerlof Langeveld Date: Sat Dec 30 12:24:56 2023 +0100 Merge branch 'ton31337-fix/cgroup2_handling' commit e379890511816dd2f17c64d01cb5ed5d4f2ee3d9 Author: Gerlof Langeveld Date: Sat Dec 30 12:22:42 2023 +0100 Code improvements for cgroupv2 handling - removed uninitalized variable 'ret' - added break to while (fgets) loop to avoid unnecessary reading M photoproc.c commit 9f681404798f51c4bfbf2439584eb9eee09b8ce7 Merge: db78190 fc4dc01 Author: Gerlof Langeveld Date: Sat Dec 30 11:38:24 2023 +0100 Merge branch 'fix/cgroup2_handling' of github.com:ton31337/atop into ton31337-fix/cgroup2_handling commit db78190be28254698d9e6da397924470159e372c Merge: d924b10 29e5c7a Author: Gerlof Langeveld Date: Sat Dec 30 11:35:03 2023 +0100 Merge pull request #285 from ton31337/fix/add_cgroup_name json: Print cgroups'a name when using -J and the label commit d924b101c5cf63a76434a88bb30c0e561a289c0a Author: Gerlof Langeveld Date: Sat Dec 30 10:51:42 2023 +0100 Added parsable and json output related to #287 M json.c M man/atop.1 M parseable.c commit 0cc44f948abf77be4b0f9b44ce296eb0e0d4bed4 Author: Gerlof Langeveld Date: Sat Dec 30 10:43:22 2023 +0100 Solved compilation error related to #287 M showsys.c commit 1a69462816845e55cd570ed007e49ffc6a4d3715 Merge: 14cfdc0 c46351a Author: Gerlof Langeveld Date: Sat Dec 30 10:30:56 2023 +0100 Merge pull request #287 from bytedance/numa_hpuse add hpuse for per numa commit c46351a1dec702b105f2cd786b9c1ea42c521820 Author: Ting Liu Date: Thu Dec 7 16:36:02 2023 +0800 add hpuse for per numa M deviate.c M man/atop.1 M photosyst.c M photosyst.h M showlinux.c M showlinux.h M showsys.c commit 14cfdc02343693b09ccfe3b2a10cb6544aaa04fa Merge: 2ae4cbe 69f8b3b Author: Gerlof Langeveld Date: Sat Dec 2 12:05:47 2023 +0100 Merge branch 'bytedance-add-TCPInCsumErrors' commit 69f8b3b34e60aca0acee4d6c00fa07d2417b4707 Merge: 2ae4cbe 0db6e63 Author: Gerlof Langeveld Date: Sat Dec 2 12:03:58 2023 +0100 Merge branch 'add-TCPInCsumErrors' of github.com:bytedance/atop into bytedance-add-TCPInCsumErrors commit 2ae4cbe21b3dd9d4f1b9b971a5946172ae877bb7 Merge: 244f4b9 442181e Author: Gerlof Langeveld Date: Sat Dec 2 10:50:57 2023 +0100 Merge pull request #252 from bytedance/always-create-logpath Fix atop.service & atop.daily: always create $LOGPATH commit 244f4b99765b275ba563b757f875f46dfc524490 Merge: e2adfa9 808442f Author: Gerlof Langeveld Date: Sat Nov 25 11:02:01 2023 +0100 Merge pull request #283 from kappa/master Replace non-standard "ushort" type with "unsigned short" commit e2adfa986d3d82be783bd6a9467ee0cf81722f8f Merge: 3512fa1 d9844ad Author: Gerlof Langeveld Date: Sat Nov 25 10:56:45 2023 +0100 Merge pull request #281 from natoscott/fix-infiniband-sigfpe Avoid floating point exception with inactive Infiniband interfaces commit 3512fa1e8d5255850fe0e86769954bc86cf27ef2 Merge: eb77182 03c5c4d Author: Gerlof Langeveld Date: Sat Nov 25 10:55:05 2023 +0100 Merge pull request #280 from ffontaine/master atop.h: include time.h commit eb771824a0bfe52d54e633763d48b928c8c144d9 Merge: 8cadc35 062accb Author: Gerlof Langeveld Date: Sat Nov 25 10:49:16 2023 +0100 Merge branch 'bytedance-anonHugepages' commit 062accb1af91680c0247b8a65eb86d7d760ba2cd Author: Gerlof Langeveld Date: Sat Nov 25 10:47:52 2023 +0100 Add anonymous THP to parsable and JSON output M json.c M man/atop.1 M parseable.c commit f1c06c98d000f5d90960ebcf761f474382f1c818 Merge: 8cadc35 55191a1 Author: Gerlof Langeveld Date: Sat Nov 25 10:28:33 2023 +0100 Merge branch 'anonHugepages' of github.com:bytedance/atop into bytedance-anonHugepages Conflicts: deviate.c photosyst.h showlinux.c commit 29e5c7a1473efe0d550d34cd62c1a8bc761a95f2 Author: Donatas Abraitis Date: Thu Nov 23 14:30:07 2023 +0200 json: Add cgroup into PRC, PRM outputs Signed-off-by: Donatas Abraitis M json.c commit 75ed3f98e44046941b3209e672029beac1d3059f Author: Donatas Abraitis Date: Thu Nov 23 11:28:18 2023 +0200 json: Print cgroups'a name when using -J and the label Before: ``` % atop -X -J PRG 1 | jq { "pid": 514432, "name": "(php-fpm8.2)", "state": "E", "ruid": 69963, "rgid": 69963, "tgid": 514432, "nthr": 1, "st": "-E", "exitcode": 0, "btime": "1700730621", "cmdline": "(php-fpm: pool www )", "ppid": 514430, "nthrrun": 0, "nthrslpi": 0, "nthrslpu": 0, "nthridle": 0, "euid": 0, "egid": 0, "elaps": "1299", "isproc": 1, "cid": "-" }, ``` After: ``` { "pid": 526057, "name": "(php-fpm8.2)", "state": "S", "ruid": 70019, "rgid": 70019, "tgid": 526057, "nthr": 1, "st": "-E", "exitcode": 0, "btime": "1700731393", "cmdline": "(php-fpm: master process (/etc/php-fpm/php-fpm.conf) )", "ppid": 1, "nthrrun": 0, "nthrslpi": 1, "nthrslpu": 0, "nthridle": 0, "euid": 70019, "egid": 70019, "elaps": "0", "isproc": 1, "cid": "-", "cgroup": "/u000000096" }, ``` Signed-off-by: Donatas Abraitis M json.c commit fc4dc01ea05aac3176fe3929dc58911f1740dc86 Author: Donatas Abraitis Date: Thu Nov 23 10:37:47 2023 +0200 cgroups: Handle cgroups2 support more granularly If you have a system mounted with both cgroupsv1, and cgroupsv2, you have something like: ``` % cat /proc/1/cgroup 13:perf_event:/ 12:memory:/init.scope 11:rdma:/ 10:pids:/init.scope 9:misc:/ 8:cpu,cpuacct:/init.scope 7:cpuset:/ 6:freezer:/ 5:net_cls,net_prio:/ 4:hugetlb:/ 3:blkio:/init.scope 2:devices:/init.scope 1:name=systemd:/init.scope 0::/init.scope ``` With some systems you might have like this: ``` % cat /proc/1/cgroup 1:name=systemd:/ 0::/init.scope ``` In both cases cgroupsv2 is activated and working fine. Let's relax a bit the check and parse the whole file for "0::" pattern instead of just relying on the first line (which is wrong at some cases). With patched version running `./atop -X`: ``` PID CPUWGT CPUMAX CPUMAXR MEMMAX MMMAXR SWPMAX SWMAXR CPU CMD CGROUP (horizontal scroll: <- and ->) 1/11 481069 100 max 400% - max - max 7% php-fpm8.2 /u00000000d 481042 100 max 400% - max - max 6% php-fpm8.2 /u00000000f 481159 100 max 400% - max - max 6% php-fpm8.2 /u00000000c ``` Signed-off-by: Donatas Abraitis M photoproc.c commit 808442f87e25010f1322d5358bd45fa3176a6c08 Author: Alex Kapranoff Date: Tue Nov 21 15:03:32 2023 -0800 Replace non-standard "ushort" type with "unsigned short" "ushort" is a typedef provided for System V compatibility in "sys/types.h" by *most* Linux toolchains. Android sys/types.h does not have it. All other parts of Atop use the standard "unsigned short" type. There are no downsides to the change and the upside is better compatibility by following the standard. M acctproc.c commit 8cadc3540deeda885713df6b23e6bcd114225c43 Author: Gerlof Langeveld Date: Wed Nov 15 20:24:35 2023 +0100 Introduce MemAvailable value in JSON output M deviate.c M json.c M parseable.c M photosyst.c M photosyst.h M showsys.c commit 852160686ae86bb9fae9c66494b89b329b9e2f4f Author: Gerlof Langeveld Date: Tue Nov 14 21:37:55 2023 +0100 Introduce MemAvailable value in MEM line M deviate.c M man/atop.1 M parseable.c M photosyst.c M photosyst.h M showlinux.c M showlinux.h M showsys.c commit 73d85077bf1e73a4117364735ef2f76134350504 Author: Gerlof Langeveld Date: Tue Nov 7 13:02:40 2023 +0100 Proper treatment of option R for PSIZE calculation M atop.c M atop.h M showgeneric.c M various.c commit 6522982bce9d1b8e599aa16736cd960866fb1691 Author: Gerlof Langeveld Date: Tue Nov 7 13:01:40 2023 +0100 Installation of atophide M Makefile M rpmspec/atop.specsystemd commit 3f2be0a7a2558989966fdfb0d692ded662e0425d Author: Gerlof Langeveld Date: Tue Nov 7 09:08:59 2023 +0100 Verify if atop runs privileged for options d, j and J M showgeneric.c commit d9844adb4f70f660a4581cb02621ea60f4d53ef1 Author: Nathan Scott Date: Mon Oct 30 17:21:14 2023 +1100 Avoid floating point exception with inactive Infiniband interfaces (solves issue #279) M showlinux.c commit 03c5c4dd29bc7590baccb55a36d61e7f4b39c1fe Author: Fabrice Fontaine Date: Sun Oct 29 18:56:35 2023 +0100 atop.h: include time.h Include time.h to avoid the following build failure with musl: atop.h:157:1: error: unknown type name 'time_t' 157 | time_t normalize_epoch(time_t, long); | ^~~~~~ atop.h:157:1: note: 'time_t' is defined in header ''; did you forget to '#include '? atop.h:157:40: error: expected ')' before 'long' 157 | time_t normalize_epoch(time_t, long); | ^~~~~ | ) Fixes: - http://autobuild.buildroot.org/results/e7ec8d16f2299320f374a0198c8e9b18a102b037 Signed-off-by: Fabrice Fontaine M atop.h commit 879a61a8f9c4c302a49bacebebf01a5a67f80d0d Author: Gerlof Langeveld Date: Sat Oct 28 11:10:13 2023 +0200 Do not start atop services during install (issue #199) M rpmspec/atop.specsystemd commit 2ed621507a299d8ad6b71c8872d7ee5c3439e94e Author: Gerlof Langeveld Date: Sat Oct 28 10:57:30 2023 +0200 Rename legacy path /var/run to /run M atopacct.init M atopacct.service M atopacctd.c M atopacctd.h M netatop.h M netatopd.h commit f72f7c06278ec9ad2209975eca67c2e3c0b3a44b Author: Gerlof Langeveld Date: Fri Oct 6 20:06:16 2023 +0200 Avoid floating point exception when atopacctd fails (solves issue #277) M acctproc.c commit f5ed1bc7a028937736d48031e6b51df3157eb86f Merge: 5d4ff7c 73db5c1 Author: Gerlof Langeveld Date: Sun Oct 1 10:59:49 2023 +0200 Merge branch 'bytedance-bytedance/support-seconds' commit 73db5c149cbba18516fb72743e06913b618542d6 Merge: 5d4ff7c ab7fc7d Author: Gerlof Langeveld Date: Sun Oct 1 10:58:42 2023 +0200 Merge branch 'bytedance/support-seconds' of github.com:bytedance/atop into bytedance-bytedance/support-seconds commit 5d4ff7c7f3b88c910e60bdf6d75eb9019486d7e0 Author: Gerlof Langeveld Date: Fri Sep 1 16:34:46 2023 +0200 Add references to netatop-bpf in documentation and code M README M README.md M man/atop.1 M showgeneric.c commit 00d79f6636cde7742df29ca04596d6feb474c6c6 Author: Gerlof Langeveld Date: Tue Aug 29 14:21:56 2023 +0200 Bug solution: convert shmrssreal to pages before use M drawbar.c commit b332ffc79b41317111b18018ac37f800a0578658 Author: Gerlof Langeveld Date: Tue Aug 29 14:18:29 2023 +0200 Consider hostname localhost not as a container M utsnames.c commit be0d24df6a187fb761f4043321597871373ab969 Author: Gerlof Langeveld Date: Tue Aug 29 12:47:37 2023 +0200 Determine container id or pod name for containerized processes Get the hostname related to a containerized process in a uniform way, no matter if the process is activated by Docker, Podman or Kubernetes (containerd). For this purpose atop temporarily connects to the UTS namespace of a process whenever such process deviates from the UTS namespace of systemd (PID 1). This specific hostname in general is the container ID (e.g. for Docker and Podman) or pod name (e.g. for Kubernetes). This new code replaces the code that determined the container ID by manipulating the cgroup path names of containerized processes. M Makefile M atop.h M deviate.c M drawbar.c M json.c M man/atop.1 M parseable.c M photoproc.c M photoproc.h M rawlog.c M showgeneric.c M showgeneric.h M showlinux.c M showprocs.c A utsnames.c commit 5a4053c3e1b98a12abed9e21856c95001b19e945 Author: Gerlof Langeveld Date: Sat Aug 19 13:09:05 2023 +0200 Flag -z: abbreviate environment vars that are too long M man/atop.1 M photoproc.c commit 34cc726d34f3be2b25b1ee6ff63d03188cf129fd Merge: 0d6288e 99e625e Author: Gerlof Langeveld Date: Sat Aug 19 11:31:45 2023 +0200 Merge branch 'jbd-env' commit 99e625e17de7eb36f316dc28b5b82b15d4a2d009 Merge: 0d6288e 20c535e Author: Gerlof Langeveld Date: Sat Aug 19 11:29:02 2023 +0200 Merge branch 'env' of github.com:jbd/atop into jbd-env Minor cleanup of the source code. commit 0d6288e5dd3f76656de0d5bfab282f9f5aa2bde0 Merge: 8596f31 7d1c32a Author: Gerlof Langeveld Date: Sat Aug 19 09:46:41 2023 +0200 Merge pull request #275 from pizhenwei/oom-adj Use -999 as OOM adj commit 7d1c32a3ce69be87689de763777ad78b9b54e8fe Author: zhenwei pi Date: Wed Aug 9 11:23:48 2023 +0800 Use -999 as OOM adj The atop deployment usually follows the rules: - atop MUST NOT exceed the cgroup resource limitations - atop has a higher priority on OOM OOM_SCORE_ADJ_MIN(-1000) is a magic number which could *not* be killed by memory cgroup, this breaks the rules. Detail of OOM_SCORE_ADJ_MIN, see linux/mm/oom_kill.c /* * Kill provided task unless it's secured by setting * oom_score_adj to OOM_SCORE_ADJ_MIN. */ static int oom_kill_memcg_member(struct task_struct *task, void *message) { if (task->signal->oom_score_adj != OOM_SCORE_ADJ_MIN && !is_global_init(task)) { get_task_struct(task); __oom_kill_process(task, message); } return 0; } Use -999 instead of -1000 as atop default OOM adj. Signed-off-by: zhenwei pi M various.c commit 8596f31d3c23b7ed7039213c805131ed908c43c5 Author: Gerlof Langeveld Date: Sat Aug 5 13:22:45 2023 +0200 Keep track of larger huge pages as well In /proc/meminfo only metrics can be found about the smallest category of huge pages (e.g. 2MiB). With this modification atop also keeps track of the larger category of huge pages (e.g. 1GiB). M deviate.c M drawbar.c M json.c M man/atop.1 M parseable.c M photosyst.c M photosyst.h M showsys.c commit 0f6202808f4d59ad12240cba8479afe642dd01e8 Author: Gerlof Langeveld Date: Thu Aug 3 12:26:15 2023 +0200 Improved calculation of memory graph, including huge pages M drawbar.c M man/atop.1 M showgeneric.h commit d81e560489f35796bc1476d2258a9eb560da1dd1 Author: Gerlof Langeveld Date: Wed Aug 2 12:28:26 2023 +0200 Introduce zswap counters zswin and zswout M deviate.c M man/atop.1 M parseable.c M photosyst.c M photosyst.h M showlinux.c M showlinux.h M showsys.c commit 30226b9dcc0d2224c48eb875ce2877de46a648fc Author: Gerlof Langeveld Date: Mon Jul 31 20:48:12 2023 +0200 Added glib2 dependency M README commit 010f7d96a972473d4ae45075196b44d01ca6f6da Merge: 2d4c7b7 b1ac229 Author: Gerlof Langeveld Date: Sun Jul 30 18:26:25 2023 +0200 Merge branch 'bytedance-netatop-bpf2' commit b1ac2294aa1a8b4f9310b2d43f8472818be93d26 Merge: 2d4c7b7 8431894 Author: Gerlof Langeveld Date: Sat Jul 15 11:54:30 2023 +0200 Add netatop-bpf interface commit 2d4c7b73fd044c9c571a3a927ef3924f6844b438 Author: Gerlof Langeveld Date: Sat Jul 15 10:03:28 2023 +0200 Copy (part of) raw log to new raw log and optionally anonymize the data 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). Optionally the samples can be anonymized (subsitute command names, the host name, logical volume names, etcetera by place holders, and wipe the command line arguments). M .gitignore M Makefile A atophide.c M man/atop.1 M man/atopcat.1 M man/atopconvert.1 A man/atophide.1 M man/atopsar.1 commit a8dd8064ad94f0de972064071b73f2a7b52ea843 Merge: 3dbb435 089d0be Author: Gerlof Langeveld Date: Sat Jul 15 09:56:26 2023 +0200 Merge pull request #265 from bytedance/delay_container add RDELAY/BDELAY for container commit 3dbb4357975abea919605f9b4541d6053ed9bfd1 Merge: 7c75e49 5820273 Author: Gerlof Langeveld Date: Sat Jul 8 12:14:12 2023 +0200 Merge pull request #268 from bytedance/exitproc_cid Exited process copy pre cid commit 7c75e4976eabb105ba591bd5f5d48163e626be3d Merge: 35330bc 4e0c147 Author: Gerlof Langeveld Date: Sat Jul 8 12:10:26 2023 +0200 Merge pull request #269 from natoscott/bargraph-memleak Resolve a memory leak when switching to bar graph mode (covscan) commit 35330bc5c2aa74beaa6bbb2dc8a34f6fed7aaa64 Merge: c68e3f0 daf993e Author: Gerlof Langeveld Date: Sat Jul 8 11:51:32 2023 +0200 Merge pull request #267 from natoscott/drawbar-tidyup Ensure correct type format specifiers used in ptrverify calls. commit c68e3f054e5e9f9a9024b4e1dc00e0f776886efc Merge: 74f64d0 799598f Author: Gerlof Langeveld Date: Sat Jul 8 11:48:33 2023 +0200 Merge pull request #266 from Zugschlus/20230702-atoprc.5-typo Fix typo "upto" in atoprc.5 commit 4e0c147f681d2decbc220a538010173b4be86f84 Author: Nathan Scott Date: Thu Jul 6 13:48:24 2023 +1000 Resolve a memory leak when switching to bar graph mode (covscan) Tackles the following covscan issue in a MBARGRAPH switch case: >>> CID 394863: Resource leaks (RESOURCE_LEAK) >>> Variable "curlist" going out of scope leaks the storage it points to. M showgeneric.c commit 089d0be024657f42c8075668cda27f5b238fc84d Author: liutingjieni Date: Thu Jun 29 17:10:42 2023 +0800 add RDELAY/BDELAY for container M showgeneric.c M showlinux.c commit 5820273091480c65d57e024c4f22e28daef373f4 Author: liuting.0xffff Date: Wed Jul 5 16:06:21 2023 +0800 Exited process copy pre cid M deviate.c commit daf993ec47140608dab3c50d8391663531b070fe Author: Nathan Scott Date: Wed Jul 5 09:49:47 2023 +1000 Ensure correct type format specifiers used in ptrverify calls. These issues were detected by the CodeQL static analysis tool ( https://github.com/github/codeql-action#readme ) Also improved wording in some strings - the word 'failed' was duplicated accidentally in a few places. M drawbar.c commit 8431894461e563b39405a3d68e42db713853690b Author: liuting.0xffff Date: Mon Jul 3 17:09:16 2023 +0800 add NETATOPBPF flag to check M showgeneric.c M showlinux.c commit 799598f083801cd0a1d604e011d2c0d0de3ed736 Author: Marc 'Zugschlus' Haber Date: Sun Jul 2 16:38:04 2023 +0200 Fix typo "upto" in atoprc.5 M man/atoprc.5 commit 74f64d00989d85de3a88643b25a5c8f9428830f1 Author: Gerlof Langeveld Date: Sat Jul 1 23:25:25 2023 +0200 Add option -Wformat-security during compilation M Makefile commit 8f79c7f8f065cd99e828d66e00ba3210d419ba8e Author: liuting.0xffff Date: Wed Jun 28 20:46:38 2023 +0800 supplement netatop-bpf M Makefile M atop.c M atop.h M deviate.c M netatop.h A netatopbpfif.c M photoproc.c M showgeneric.c M showprocs.c commit f20faddb34c25f5e8a5a9bcb40f9d351c30d3f60 Author: Gerlof Langeveld Date: Sat Jun 10 17:53:20 2023 +0200 Solve malloc failure when starting atopsar for live measurement Only lock memory when the memory limit was successfully set to unlimited. M atopsar.c commit 573cba4175fe87c9d803b4a8c95abb96b8d98706 Author: Gerlof Langeveld Date: Sat Jun 10 14:13:02 2023 +0200 Introduce separate counter for idle threads (solves #256) A separate counter will be maintained on system level and on process level for the number of idle threads (uninterruptible sleeping threads that do not count in the load average). M atopsar.c M deviate.c M json.c M man/atop.1 M man/atopsar.1 M parseable.c M photoproc.c M photoproc.h M rawlog.c M rawlog.h M showlinux.c M showlinux.h M showprocs.c M showsys.c commit 321823f59094c000822acf7b9481ebd29e144495 Author: Gerlof Langeveld Date: Sat Jun 10 11:50:33 2023 +0200 Minor update in CMD description M man/atop.1 commit c47f9652149aed89c5c643c2841cc0ae34d8fc8a Merge: 13a4178 f2fcee5 Author: Gerlof Langeveld Date: Sat Jun 10 11:44:32 2023 +0200 Merge pull request #259 from bytedance/CMD_change fix man/atop.1: exited processes CMD may be changed commit 13a4178d1e00d429e56d2ff3334162933c320b9f Merge: c243536 957ff64 Author: Gerlof Langeveld Date: Sat Jun 10 11:39:34 2023 +0200 Merge pull request #262 from BlackIkeEagle/fix-format-security Fix drawbar mvwprintw format-security error commit 957ff648436fa4a6f08ad9a8c5ea856a5f33ef5b Author: BlackEagle Date: Fri Jun 2 19:49:51 2023 +0200 Fix drawbar mvwprintw format-security error When building with `-Werror=format-security` there are some issues with the use of mvwprintw in drawbar.c ``` export CFLAGS="-march=x86-64 -mtune=generic -O2 -pipe -fno-plt -fexceptions \ -Wp,-D_FORTIFY_SOURCE=2 -Wformat -Werror=format-security \ -fstack-clash-protection -fcf-protection" make ... cc -march=x86-64 -mtune=generic -O2 -pipe -fno-plt -fexceptions -Wp,-D_FORTIFY_SOURCE=2 -Wformat -Werror=format-security -fstack-clash-protection -fcf-protection -O2 -I. -Wall -Wno-stringop-truncation -Wmissing-prototypes -Wmissing-declarations -c -o drawbar.o drawbar.c drawbar.c: In function ‘drawevent’: drawbar.c:2058:9: error: format not a string literal and no format arguments [-Werror=format-security] 2058 | mvwprintw(w->win, line, column, text); | ^~~~~~~~~ drawbar.c: In function ‘headergetch’: drawbar.c:2108:17: error: format not a string literal and no format arguments [-Werror=format-security] 2108 | mvwprintw(headwin, 0, statcol, statusmsg); | ^~~~~~~~~ drawbar.c: In function ‘getwininput’: drawbar.c:2331:9: error: format not a string literal and no format arguments [-Werror=format-security] 2331 | mvwprintw(mywin, 1, 1, prompt); | ^~~~~~~~~ cc1: some warnings being treated as errors make: *** [: drawbar.o] Error 1 ``` By explicitly adding the format `"%s"` this error is fixed. Signed-off-by: BlackEagle M drawbar.c commit c2435367a2a96436aaaa21c2135c164b2b591f02 Author: Gerlof Langeveld Date: Fri May 26 16:03:51 2023 +0200 Bug solution: not possible to switch directly from bar mode to command args M showgeneric.c commit 55191a108f420bbae28dcec310a4f75dc7448463 Author: liutingjieni Date: Tue May 16 16:37:30 2023 +0800 supplement AnonHugepages memory consumption M deviate.c M man/atop.1 M photosyst.c M photosyst.h M showlinux.c M showlinux.h M showsys.c commit f2fcee5ba1a8cd8b5d2fc35692e4a6f441462e53 Author: Ting Liu Date: Tue Apr 18 17:07:12 2023 +0800 fix man/atop.1: exited processes CMD may be changed M man/atop.1 commit 442181e44102a2bfff9cc71782768d96bfbba73b Author: Fei Li Date: Fri Jul 22 14:52:23 2022 +0800 Fix atop.service & atop.daily: always create $LOGPATH In case users remove $LOGAPTH, create it whenever atop restarts. Or else, 'no such file or directory' error will occur. Signed-off-by: Fei Li M atop.daily M atop.service commit 0db6e63fc49be4ba067a3c1017957d577c609983 Author: Fei Li Date: Tue Jul 27 21:20:05 2021 +0800 Show InCsumErrors for TCP Sometimes the packet loss is due to a checksum error, esp, in virtual machine scenarios who usually offload the checking to its host. Signed-off-by: Fei Li M man/atop.1 M netstats.h M parseable.c M showlinux.c M showlinux.h M showsys.c commit 20c535ea017885b2afa96a0918274f375bcc7b6a Author: Jean-Baptiste Denis Date: Mon Dec 12 22:00:13 2022 +0100 Add -z option to prepend matching environment variable to displayed command line. This option can be used to capture some environment variables and displaying them by prepending them to the command line. They are stored directly in the existing curtask->gen.cmdline variable. It consumes space not available for the real command line. M atop.c M man/atop.1 M photoproc.c commit ab7fc7d9db4d62a02b9974f04aeae23c10eeb432 Author: huteng.ht Date: Fri Aug 13 15:57:13 2021 +0800 Support second level precision. When selecting certain time period through -b and -e flags within the raw file, minute level precision is not nicety enough in some circumstances. Second level precision is provided, which can be pciked up by appending [YYYYMMDD]hhmmss form to the flags, meanwhile the old form [YYYYMMDD]hhmm is still retained. M atop.c M atopsar.c M man/atop.1 M man/atopsar.1 M showgeneric.c M various.c atop-2.10.0/45atoppm0000775000203100020310000000224214545501444013444 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.10.0/atop-pm.sh0000775000203100020310000000022214545501444013755 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.10.0/Makefile0000664000203100020310000001752714545501444013521 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 CFLAGS += -O2 -I. -Wall $(shell pkg-config --cflags glib-2.0) -Wno-stringop-truncation -Wmissing-prototypes -Wmissing-declarations -Wformat-security # -DNOPERFEVENT # -DHTTPSTATS LDFLAGS = $(shell pkg-config --libs glib-2.0) OBJMOD0 = version.o OBJMOD1 = various.o deviate.o procdbase.o OBJMOD2 = acctproc.o photoproc.o photosyst.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 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 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 showgeneric.o: atop.h photoproc.h photosyst.h showgeneric.h showlinux.h showlinux.o: atop.h photoproc.h photosyst.h showgeneric.h showlinux.h showsys.o: atop.h photoproc.h photosyst.h showgeneric.h showprocs.o: atop.h photoproc.h photosyst.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.10.0/README0000664000203100020310000001101114545501444012717 0ustar gerlofgerlofCOPYRIGHT NOTICE ---------------- For all files that are not marked differently: Copyright Gerlof Langeveld 2007-2023 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 ------------------------- 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. KERNEL ISSUES WITH PROCESS ACCOUNTING ------------------------------------- Newer upstream kernels (e.g. 4.8 and 4.9) have two issuess with process accounting: 1) Sometimes process accounting does not work at all¹. Atopacctd tries to work around this issue, by retrying to initialize process accounting several times. 2) When using the NETLINK inface, the command TASKSTATS_CMD_GET consequently returns -EINVAL². Atopacctd needs NETLINK to be able to be triggered that some process in the system has finished. In this way, atopacctd can be in a blocking state as long as no processes terminate. When atopacctd detects that NETLINK fails, it switches into a polling mode to periodically try if it can read process accounting records as a workaround. This issue has to do with cpumasks and you can work-around it by building a kernel that has CONFIG_NR_CPUS configured to exactly the amount of CPUs (logical CPUs) in the system the kernel runs on. You can find this kernel option under "Processor type and features" --> "Maximum number of CPUs". [1] Bug 190271 - process accounting sometimes does not work https://bugzilla.kernel.org/show_bug.cgi?id=190271 [2] Bug 190711 - Process accounting: Using the NETLINK interface, the command TASKSTATS_CMD_GET returns -EINVAL https://bugzilla.kernel.org/show_bug.cgi?id=190711 Linux kernel mailing list thread: [REGRESSION] Two issues that prevent process accounting (taskstats) from working correctly https://lkml.org/lkml/2016/12/19/182 SERVICE ACTIVATION AFTER INSTALLATION ------------------------------------- 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.10.0/AUTHORS0000644000203100020310000000077414545501444013123 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.10.0/COPYING0000644000203100020310000004313114545501444013100 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.10.0/mkdate0000775000203100020310000000042314545501444013237 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.10.0/atopacct.init0000775000203100020310000000330714545501444014536 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 # Required-Stop: $local_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.10.0/atopacct.service0000664000203100020310000000056714545501444015235 0ustar gerlofgerlof[Unit] Description=Atop process accounting daemon Documentation=man:atopacctd(8) After=syslog.target 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.10.0/atopgpu.service0000644000203100020310000000033614545501444015106 0ustar gerlofgerlof[Unit] Description=Atop GPU stats daemon Documentation=man:atopgpud(8) After=syslog.target Before=atop.service [Service] ExecStart=/usr/sbin/atopgpud Type=oneshot RemainAfterExit=yes [Install] WantedBy=multi-user.target atop-2.10.0/atop.rc.openrc0000664000203100020310000000041114545501444014617 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.10.0/atopacct.rc.openrc0000664000203100020310000000203214545501444015453 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.10.0/atop.cronsysv0000664000203100020310000000011714545501444014617 0ustar gerlofgerlof# daily restart of atop at midnight 0 0 * * * root /usr/share/atop/atop.daily& atop-2.10.0/atop.daily0000775000203100020310000000267414545501444014050 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.10.0/atop.init0000775000203100020310000000252314545501444013702 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 # Required-Stop: $local_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.10.0/atop.default0000664000203100020310000000010314545501444014350 0ustar gerlofgerlofLOGOPTS="" LOGINTERVAL=600 LOGGENERATIONS=28 LOGPATH=/var/log/atop atop-2.10.0/atop.service0000664000203100020310000000140714545501444014374 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.10.0/atop-rotate.service0000664000203100020310000000023214545501444015663 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.10.0/atop-rotate.timer0000664000203100020310000000017414545501444015350 0ustar gerlofgerlof[Unit] Description=Daily atop restart Documentation=man:atop(1) [Timer] OnCalendar=daily [Install] WantedBy=timers.target atop-2.10.0/atopgpud0000775000203100020310000004623514545501444013630 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.10.0/prev/0000775000203100020310000000000014545501444013021 5ustar gerlofgerlofatop-2.10.0/prev/photoproc_28.h0000664000203100020310000001150514545501444015522 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.10.0/prev/photoproc_26.h0000664000203100020310000001055414545501444015523 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.10.0/prev/photosyst_210.h0000644000203100020310000003451514545501444015636 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 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.10.0/prev/photosyst_27.h0000664000203100020310000003222614545501444015563 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.10.0/prev/photosyst_25.h0000664000203100020310000002471514545501444015565 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.10.0/prev/photosyst_29.h0000664000203100020310000003332614545501444015567 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.10.0/prev/photosyst_23.h0000664000203100020310000002016214545501444015553 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.10.0/prev/photosyst_22.h0000664000203100020310000002016514545501444015555 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.10.0/prev/photoproc_23.h0000664000203100020310000000715414545501444015522 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.10.0/prev/photoproc_210.h0000664000203100020310000001174514545501444015601 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.10.0/prev/photoproc_22.h0000664000203100020310000000707314545501444015521 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.10.0/prev/photoproc_29.h0000664000203100020310000001150514545501444015523 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.10.0/prev/photoproc_20.h0000664000203100020310000000661114545501444015514 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.10.0/prev/photoproc_25.h0000664000203100020310000001032614545501444015517 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.10.0/prev/photoproc_24.h0000664000203100020310000001032614545501444015516 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.10.0/prev/netstats_wrong.h0000644000203100020310000000203014545501444016244 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; }; atop-2.10.0/prev/photoproc_21.h0000664000203100020310000000701614545501444015515 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.10.0/prev/photosyst_26.h0000664000203100020310000002517114545501444015563 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.10.0/prev/photosyst_28.h0000664000203100020310000003332314545501444015563 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.10.0/prev/photoproc_27.h0000664000203100020310000001055414545501444015524 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.10.0/prev/photosyst_20.h0000664000203100020310000001352414545501444015554 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.10.0/prev/photosyst_24.h0000664000203100020310000002471514545501444015564 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.10.0/prev/photosyst_21.h0000664000203100020310000001415714545501444015560 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.10.0/man/0000755000203100020310000000000014545501444012616 5ustar gerlofgerlofatop-2.10.0/man/atopconvert.10000664000203100020310000000255514545501444015255 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.10.0/man/atop.10000664000203100020310000031177714545501444013665 0ustar gerlofgerlof.TH ATOP 1 "January 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] [interval [samples]] .PP Live measurement in text mode: .PP .TP 5 .B \ atop [\-g|\-m|\-d|\-n|\-u|\-p|\-s|\-c|\-v|\-o|\-y|\-Y] [\-C|\-M|\-D|\-N|\-A] [\-fFG1xR] [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 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] [\-fFG1xR] .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 of the most critical hardware resources (from a performance point of view), i.e. CPUs, memory, disks and network interfaces. Besides, information is gathered about the processes (or 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 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 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 The initial screen in text mode shows if .I atop runs with restricted view (unprivileged user) or unrestricted view (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. .PP With every interval information is shown about the resource occupation on system level (CPU, memory, disks and network layers), followed by a list of processes which have been active during the last interval. Notice that all processes that were unchanged during the last interval are not shown, unless the key 'a' has been pressed or unless sorting on memory occupation is done (then inactive processes are relevant as well). If the list of active processes does not entirely fit on the screen, only the top of the list is shown (sorted in order of activity). .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). .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 gathers counters for a new interval, 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 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 notices that the daemon is active, it reads these GPU utilization counters with every interval. 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 X Show cgroup v2 information. Per process the following fields are shown: process-id, 'cpu.weight' of the cgroup the process belongs to, 'cpu.max' value (recalculated as percentage) of the cgroup the process belongs to, most restrictive 'cpu.max' value found in the upper directories, 'memory.max' value of the cgroup the process belongs to, most restrictive 'memory.max' value found in the upper directories, 'memory.swap.max' value of the cgroup the process belongs to, most restrictive 'memory.swap.max' value found in the upper directories, the command name, and the cgroup path name (horizontally scrollable). .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 U Specify a search string for specific user names as a regular expression. From now on, only (active) processes will be shown from a user which matches the regular expression. 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 G 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 or the key 'r' to rewind to the begin 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 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. .br Frequency scaling statistics are only gathered for systems with maximum 8 CPUs, since gathering of these values per CPU is very time consuming. 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 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. .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 <<<<<<< HEAD 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 - PROCESS LEVEL Following the system level information, a list of processes is shown in text mode 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 CGROUP Path name of the cgroup (version 2) to which this process belongs. This path name is relative to the cgroup root directory, which is usually '/sys/fs/cgroup'. .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. .br 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 CPUMAX The 'cpu.max' value of the cgroup (version 2) to which this process belongs, calculated as percentage of one CPU. .PP .TP 9 .B CPUMAXR The most restrictive (i.e. effective) 'cpu.max' value defined by the upper directories of the cgroup (version 2) to which this process belongs, calculated as percentage of one CPU. .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 CPUWGT The 'cpu.weight' value of the cgroup (version 2) to which this process belongs. .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. .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. .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. .PP .TP 9 .B FSUID Filesystem user-id under which this process executes. .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 MEMMAX The 'memory.max' value of the cgroup (version 2) to which this process belongs. .PP .TP 9 .B MEMNOW Memory occupation at the moment of the sample on all used GPUs. .PP .TP 9 .B MMMAXR The most restrictive (i.e. effective) 'memory.max' value defined by the upper directories of the cgroup (version 2) to which this process belongs. .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. .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. .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. .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. .PP .TP 9 .B SWPMAX The 'memory.swap.max' value of the cgroup (version 2) to which this process belongs. .PP .TP 9 .B SWAPSZ The swap space consumed by this process (or user). .PP .TP 9 .B SWMAXR The most restrictive (i.e. effective) 'memory.swap.max' value defined by the upper directories of the cgroup (version 2) to which this process belongs. .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". .br For process-level statistics special labels are available: "PRG" (general), "PRC" (CPU), "PRE" (GPU), "PRM" (memory), "PRD" (disk, only if "storage accounting" is active) 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, and duplex mode (0=half, 1=full). .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 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 To monitor the current system load in text mode with an interval of (default) 10 seconds: .PP .TP 12 .B \ atop .PP To monitor the current system load as bar graphs with an interval of 5 seconds: .PP .TP 12 .B \ atop -B 5 .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 2023, April 15 from 02:00 PM onwards interactively: .PP .B \ atop -r 20230415 -b 1400 .PP Concatenate all raw log files of March 2023 and generate parsable output about the CPU utilization: .PP .TP 12 .B \ atopcat /var/log/atop/atop_202303?? | atop -r - -PCPU .PP To 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: .TP 8 \ .br 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.10.0/man/atoprc.50000644000203100020310000002724014545501444014201 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 'B', 'H', 'g', 'm', 'd', 'n', 'u', 'p', 's', 'c', 'v', \&'C', 'M', 'D', 'N', 'A', \&'a', 'y', 'Y', 'f', 'F', 'G', 'R', '1', 'e', 'E' and 'x'. .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 The default regular expression for 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 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.10.0/man/atopacctd.80000664000203100020310000001046514545501444014661 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.10.0/man/atophide.10000664000203100020310000000216714545501444014505 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.10.0/man/atopcat.10000664000203100020310000000427314545501444014343 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 and compressed length of the process-level information. .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_2020021[0-4] > week_2020_7 .TP 12 .B \ atop -r week_2020_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_2020021[0-6] | atop -r - .PP Concatenate all raw log files of January 2020 and generate parsable output about the CPU utilization: .PP .TP 12 .B \ atopcat /var/log/atop/atop_202001?? | 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_2020020[34] | .B \ atopsar -m -r - -b 202002031400 -e 202002041100 .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_20200303 > /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.10.0/man/atopsar.10000664000203100020310000010005314545501444014352 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.10.0/man/atopgpud.80000664000203100020310000001001114545501444014525 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)