atop-2.11.1/0000755000203100020310000000000014771753566012064 5ustar gerlofgerlofatop-2.11.1/showlinux.c0000644000203100020310000024426014771753566014300 0ustar gerlofgerlof/* ** ATOP - System & Process Monitor ** ** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as process-level. ** ** This source-file contains the Linux-specific functions to calculate ** figures to be visualized. ** ========================================================================== ** Author: Gerlof Langeveld ** Original version. ** E-mail: gerlof.langeveld@atoptool.nl ** Date: July 2002 ** ** Author: JC van Winkel - AT Computing, Nijmegen, Holland ** Complete redesign. ** Date: November 2009 ** -------------------------------------------------------------------------- ** Copyright (C) 2009-2010 JC van Winkel ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ** -------------------------------------------------------------------------- */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "atop.h" #include "cgroups.h" #include "photoproc.h" #include "photosyst.h" #include "showgeneric.h" #include "showlinux.h" static void make_proc_dynamicgen(void); static int get_perc(char *, char *); static void make_detail_prints(detail_printpair *, int, const char *, const char *); /* ** critical percentages for occupation-percentage; ** these defaults can be overruled via the config-file */ int cpubadness = 90; /* percentage */ int gpubadness = 100; /* percentage */ int membadness = 90; /* percentage */ int swpbadness = 80; /* percentage */ int dskbadness = 90; /* percentage */ int netbadness = 90; /* percentage */ int pagbadness = 10; /* number per second */ int almostcrit = 80; /* percentage */ /* * tables with all sys_printdefs */ sys_printdef *prcsyspdefs[] = { &syspdef_PRCSYS, &syspdef_PRCUSER, &syspdef_PRCNPROC, &syspdef_PRCNRUNNING, &syspdef_PRCNSLEEPING, &syspdef_PRCNDSLEEPING, &syspdef_PRCNIDLE, &syspdef_PRCNZOMBIE, &syspdef_PRCCLONES, &syspdef_PRCNNEXIT, &syspdef_BLANKBOX, 0 }; sys_printdef *cpusyspdefs[] = { &syspdef_CPUSYS, &syspdef_CPUUSER, &syspdef_CPUIRQ, &syspdef_CPUIDLE, &syspdef_CPUWAIT, &syspdef_BLANKBOX, &syspdef_CPUIPC, &syspdef_CPUCYCLE, &syspdef_CPUFREQ, &syspdef_CPUSCALE, &syspdef_CPUSTEAL, &syspdef_CPUGUEST, &syspdef_BLANKBOX, 0 }; sys_printdef *cpisyspdefs[] = { &syspdef_CPUISYS, &syspdef_CPUIUSER, &syspdef_CPUIIRQ, &syspdef_CPUIIDLE, &syspdef_CPUIWAIT, &syspdef_BLANKBOX, &syspdef_CPUIIPC, &syspdef_CPUICYCLE, &syspdef_CPUIFREQ, &syspdef_CPUISCALE, &syspdef_CPUISTEAL, &syspdef_CPUIGUEST, &syspdef_BLANKBOX, 0 }; sys_printdef *cplsyspdefs[] = { &syspdef_CPLNUMCPU, &syspdef_CPLAVG1, &syspdef_CPLAVG5, &syspdef_CPLAVG15, &syspdef_CPLCSW, &syspdef_CPLINTR, &syspdef_BLANKBOX, 0 }; sys_printdef *gpusyspdefs[] = { &syspdef_GPUBUS, &syspdef_GPUGPUPERC, &syspdef_GPUMEMPERC, &syspdef_GPUMEMOCC, &syspdef_GPUMEMTOT, &syspdef_GPUMEMUSE, &syspdef_GPUMEMAVG, &syspdef_GPUTYPE, &syspdef_GPUNRPROC, &syspdef_BLANKBOX, 0 }; sys_printdef *memsyspdefs1[] = { &syspdef_MEMTOT, &syspdef_MEMFREE, &syspdef_MEMAVAIL, &syspdef_BLANKBOX, &syspdef_MEMCACHE, &syspdef_MEMDIRTY, &syspdef_MEMBUFFER, &syspdef_BLANKBOX, &syspdef_MEMSLAB, &syspdef_RECSLAB, &syspdef_BLANKBOX, &syspdef_HUPTOT, &syspdef_HUPUSE, 0 }; sys_printdef *memsyspdefs2[] = { &syspdef_NUMNUMA, &syspdef_BLANKBOX, &syspdef_TCPSOCK, &syspdef_UDPSOCK, &syspdef_BLANKBOX, &syspdef_SHMEM, &syspdef_SHMRSS, &syspdef_SHMSWP, &syspdef_BLANKBOX, &syspdef_PAGETABS, &syspdef_BLANKBOX, &syspdef_ANONTHP, &syspdef_VMWBAL, &syspdef_BLANKBOX, &syspdef_ZFSARC, 0 }; sys_printdef *swpsyspdefs[] = { &syspdef_SWPTOT, &syspdef_SWPFREE, &syspdef_SWPCACHE, &syspdef_BLANKBOX, &syspdef_ZSWPOOL, &syspdef_ZSWSTORED, &syspdef_KSMSHARING, &syspdef_KSMSHARED, &syspdef_BLANKBOX, &syspdef_SWPCOMMITTED, &syspdef_SWPCOMMITLIM, &syspdef_BLANKBOX, 0 }; sys_printdef *pagsyspdefs[] = { &syspdef_PAGSCAN, &syspdef_PAGSTEAL, &syspdef_PAGSTALL, &syspdef_PAGCOMPACT, &syspdef_PGMIGRATE, &syspdef_NUMAMIGRATE, &syspdef_PAGSWIN, &syspdef_PAGSWOUT, &syspdef_PAGZSWIN, &syspdef_PAGZSWOUT, &syspdef_OOMKILLS, &syspdef_PAGPGIN, &syspdef_PAGPGOUT, &syspdef_BLANKBOX, 0 }; sys_printdef *memnumasyspdefs[] = { &syspdef_NUMATOT, &syspdef_NUMAFREE, &syspdef_NUMAFILEPAGE, &syspdef_NUMANR, &syspdef_NUMADIRTY, &syspdef_NUMAACTIVE, &syspdef_NUMAINACTIVE, &syspdef_NUMASLAB, &syspdef_NUMASLABRECLAIM, &syspdef_NUMASHMEM, &syspdef_NUMAFRAG, &syspdef_NUMAHUPTOT, &syspdef_NUMAHUPUSE, 0 }; sys_printdef *cpunumasyspdefs[] = { &syspdef_NUMACPUSYS, &syspdef_NUMACPUUSER, &syspdef_NUMACPUNICE, &syspdef_NUMACPUIRQ, &syspdef_NUMACPUSOFTIRQ, &syspdef_NUMACPUIDLE, &syspdef_NUMACPUWAIT, &syspdef_NUMACPUSTEAL, &syspdef_NUMACPUGUEST, &syspdef_NUMANUMCPU, 0 }; sys_printdef *llcsyspdefs[] = { &syspdef_LLCMBMTOTAL, &syspdef_LLCMBMLOCAL, &syspdef_NUMLLC, &syspdef_BLANKBOX, 0 }; sys_printdef *psisyspdefs[] = { &syspdef_PSICPUSTOT, &syspdef_PSIMEMSTOT, &syspdef_PSIMEMFTOT, &syspdef_PSIIOSTOT, &syspdef_PSIIOFTOT, &syspdef_PSICPUS, &syspdef_PSIMEMS, &syspdef_PSIMEMF, &syspdef_PSIIOS, &syspdef_PSIIOF, &syspdef_BLANKBOX, 0 }; sys_printdef *contsyspdefs[] = { &syspdef_CONTNAME, &syspdef_CONTNPROC, &syspdef_CONTCPU, &syspdef_CONTMEM, &syspdef_BLANKBOX, 0 }; sys_printdef *dsksyspdefs[] = { &syspdef_DSKNAME, &syspdef_DSKBUSY, &syspdef_DSKNREAD, &syspdef_DSKNWRITE, &syspdef_DSKNDISC, &syspdef_DSKMBPERSECWR, &syspdef_DSKMBPERSECRD, &syspdef_DSKKBPERRD, &syspdef_DSKKBPERWR, &syspdef_DSKKBPERDS, &syspdef_DSKINFLIGHT, &syspdef_DSKAVQUEUE, &syspdef_DSKAVIO, &syspdef_BLANKBOX, 0 }; sys_printdef *nfsmntsyspdefs[] = { &syspdef_NFMPATH, &syspdef_NFMSERVER, &syspdef_NFMTOTREAD, &syspdef_NFMTOTWRITE, &syspdef_NFMNREAD, &syspdef_NFMNWRITE, &syspdef_NFMDREAD, &syspdef_NFMDWRITE, &syspdef_NFMMREAD, &syspdef_NFMMWRITE, &syspdef_BLANKBOX, 0 }; sys_printdef *nfcsyspdefs[] = { &syspdef_NFCRPCCNT, &syspdef_NFCRPCREAD, &syspdef_NFCRPCWRITE, &syspdef_NFCRPCRET, &syspdef_NFCRPCARF, &syspdef_BLANKBOX, 0 }; sys_printdef *nfssyspdefs[] = { &syspdef_NFSRPCCNT, &syspdef_NFSRPCREAD, &syspdef_NFSRPCWRITE, &syspdef_NFSNRBYTES, &syspdef_NFSNWBYTES, &syspdef_NFSNETTCP, &syspdef_NFSNETUDP, &syspdef_NFSBADFMT, &syspdef_NFSBADAUT, &syspdef_NFSBADCLN, &syspdef_NFSRCHITS, &syspdef_NFSRCMISS, &syspdef_NFSRCNOCA, &syspdef_BLANKBOX, 0 }; sys_printdef *nettranssyspdefs[] = { &syspdef_NETTRANSPORT, &syspdef_NETTCPI, &syspdef_NETTCPO, &syspdef_NETUDPI, &syspdef_NETUDPO, &syspdef_NETTCPACTOPEN, &syspdef_NETTCPPASVOPEN, &syspdef_NETTCPRETRANS, &syspdef_NETTCPINERR, &syspdef_NETTCPORESET, &syspdef_NETTCPCSUMERR, &syspdef_NETUDPNOPORT, &syspdef_NETUDPINERR, &syspdef_BLANKBOX, 0 }; sys_printdef *netnetsyspdefs[] = { &syspdef_NETNETWORK, &syspdef_NETIPI, &syspdef_NETIPO, &syspdef_NETIPFRW, &syspdef_NETIPDELIV, &syspdef_NETICMPIN, &syspdef_NETICMPOUT, &syspdef_BLANKBOX, 0 }; sys_printdef *netintfsyspdefs[] = { &syspdef_NETNAME, &syspdef_NETPCKI, &syspdef_NETPCKO, &syspdef_NETSPEEDMAX, &syspdef_NETSPEEDIN, &syspdef_NETSPEEDOUT, &syspdef_NETCOLLIS, &syspdef_NETMULTICASTIN, &syspdef_NETRCVERR, &syspdef_NETSNDERR, &syspdef_NETRCVDROP, &syspdef_NETSNDDROP, &syspdef_BLANKBOX, 0 }; sys_printdef *infinisyspdefs[] = { &syspdef_IFBNAME, &syspdef_IFBPCKI, &syspdef_IFBPCKO, &syspdef_IFBSPEEDMAX, &syspdef_IFBSPEEDIN, &syspdef_IFBSPEEDOUT, &syspdef_IFBLANES, &syspdef_BLANKBOX, 0 }; /* * table with all detail_printdefs */ detail_printdef *alldetaildefs[]= { &procprt_PID, &procprt_TID, &procprt_PPID, &procprt_SYSCPU, &procprt_USRCPU, &procprt_RUNDELAY, &procprt_BLKDELAY, &procprt_WCHAN, &procprt_NVCSW, &procprt_NIVCSW, &procprt_VGROW, &procprt_RGROW, &procprt_MINFLT, &procprt_MAJFLT, &procprt_VSTEXT, &procprt_VSIZE, &procprt_RSIZE, &procprt_PSIZE, &procprt_VSLIBS, &procprt_VDATA, &procprt_VSTACK, &procprt_SWAPSZ, &procprt_LOCKSZ, &procprt_CMD, &procprt_RUID, &procprt_EUID, &procprt_SUID, &procprt_FSUID, &procprt_RGID, &procprt_EGID, &procprt_SGID, &procprt_FSGID, &procprt_CTID, &procprt_VPID, &procprt_CID, &procprt_STDATE, &procprt_STTIME, &procprt_ENDATE, &procprt_ENTIME, &procprt_THR, &procprt_TRUN, &procprt_TSLPI, &procprt_TSLPU, &procprt_TIDLE, &procprt_POLI, &procprt_NICE, &procprt_PRI, &procprt_RTPR, &procprt_CURCPU, &procprt_ST, &procprt_EXC, &procprt_S, &procprt_COMMAND_LINE, &procprt_NPROCS, &procprt_RDDSK, &procprt_WRDSK, &procprt_CWRDSK, &procprt_WCANCEL, &procprt_TCPRCV, &procprt_TCPRASZ, &procprt_TCPSND, &procprt_TCPSASZ, &procprt_UDPRCV, &procprt_UDPRASZ, &procprt_UDPSND, &procprt_UDPSASZ, &procprt_RNET, &procprt_SNET, &procprt_BANDWI, &procprt_BANDWO, &procprt_GPULIST, &procprt_GPUMEMNOW, &procprt_GPUMEMAVG, &procprt_GPUGPUBUSY, &procprt_GPUMEMBUSY, &procprt_SORTITEM, &cgroupprt_CGROUP_PATH, &cgroupprt_CGRNPROCS, &cgroupprt_CGRNPROCSB, &cgroupprt_CGRCPUBUSY, &cgroupprt_CGRCPUPSI, &cgroupprt_CGRCPUMAX, &cgroupprt_CGRCPUWGT, &cgroupprt_CGRMEMORY, &cgroupprt_CGRMEMPSI, &cgroupprt_CGRMEMMAX, &cgroupprt_CGRSWPMAX, &cgroupprt_CGRDISKIO, &cgroupprt_CGRDSKPSI, &cgroupprt_CGRDSKWGT, &cgroupprt_CGRPID, &cgroupprt_CGRCMD, 0 }; /* * table with all detail_printdefs with PID/TID width to be initialized */ detail_printdef *idprocpdefs[]= { &procprt_PID, &procprt_TID, &procprt_PPID, &procprt_VPID, &cgroupprt_CGRPID, 0 }; /***************************************************************/ /* * output definitions for process and cgroups data * these should be user configurable */ detail_printpair userprocs[MAXITEMS]; detail_printpair memprocs[MAXITEMS]; detail_printpair schedprocs[MAXITEMS]; detail_printpair genprocs[MAXITEMS]; detail_printpair dskprocs[MAXITEMS]; detail_printpair netprocs[MAXITEMS]; detail_printpair gpuprocs[MAXITEMS]; detail_printpair varprocs[MAXITEMS]; detail_printpair cmdprocs[MAXITEMS]; detail_printpair ownprocs[MAXITEMS]; detail_printpair totusers[MAXITEMS]; detail_printpair totprocs[MAXITEMS]; detail_printpair totconts[MAXITEMS]; detail_printpair gencgroups[MAXITEMS]; // cgroups data detail_printpair gencgrprocs[MAXITEMS]; // cgroups process data /*****************************************************************/ /* * output definitions for system data * these should be user configurable */ sys_printpair sysprcline[MAXITEMS]; sys_printpair allcpuline[MAXITEMS]; sys_printpair indivcpuline[MAXITEMS]; sys_printpair cplline[MAXITEMS]; sys_printpair gpuline[MAXITEMS]; sys_printpair memline1[MAXITEMS]; sys_printpair memline2[MAXITEMS]; sys_printpair swpline[MAXITEMS]; sys_printpair memnumaline[MAXITEMS]; sys_printpair cpunumaline[MAXITEMS]; sys_printpair llcline[MAXITEMS]; sys_printpair pagline[MAXITEMS]; sys_printpair psiline[MAXITEMS]; sys_printpair contline[MAXITEMS]; sys_printpair dskline[MAXITEMS]; sys_printpair nettransportline[MAXITEMS]; sys_printpair netnetline[MAXITEMS]; sys_printpair netinterfaceline[MAXITEMS]; sys_printpair infinibandline[MAXITEMS]; sys_printpair nfsmountline[MAXITEMS]; sys_printpair nfcline[MAXITEMS]; sys_printpair nfsline[MAXITEMS]; typedef struct { const char *name; int prio; } name_prio; /* ** make an string,int pair array from a string. chop based on spaces/tabs ** example: input: "ABCD:3 EFG:1 QWE:16" ** output: { { "ABCD", 3 }, {"EFG", 1}, { "QWE", 16}, { 0, 0 } } */ static void makeargv(char *line, const char *linename, name_prio *vec) { int i=0; char *p=line; char *name=0; char *prio=0; // find pair and scan it while (*p && i= INT_MAX || lprio <0) { fprintf(stderr, "atoprc - %s: item `%s` has " "invalid priority `", linename, name); while (*prio && *prio !=' ') { fputc(*prio, stderr); prio++; } fprintf(stderr, "'\n"); cleanstop(1); } vec[i].name=name; vec[i].prio=lprio; ++i; } vec[i].name=0; } /* * make_sys_prints: make array of sys_printpairs * input: string, sys_printpair array, maxentries */ static void make_sys_prints(sys_printpair *ar, int maxn, const char *pairs, sys_printdef *permissables[], const char *linename, struct sstat *sstat, extraparam *extra) { name_prio items[MAXITEMS]; int i, a, n=strlen(pairs); char str[n+1]; strcpy(str, pairs); makeargv(str, linename, items); for(i=a=0; 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_detail_prints: make array of detail_printpairs ** input: string, detail_printpair array, maxentries */ static void make_detail_prints(detail_printpair *ar, int maxn, const char *pairs, const char *linename) { name_prio items[MAXITEMS]; int n=strlen(pairs); char str[n+1]; strcpy(str, pairs); makeargv(str, linename, items); int i; for(i=0; items[i].name && iconfigname, name)==0) { ar[i].pf = alldetaildefs[j]; ar[i].prio = items[i].prio; break; } } if (alldetaildefs[j]==0) { mcleanstop(1, "atoprc - ownprocline: item %s invalid!\n", name); } } ar[i].pf=0; ar[i].prio=0; } /* ** calculate the total consumption on system-level for the ** four main resources */ void totalcap(struct syscap *psc, struct sstat *sstat, struct tstat **proclist, int nactproc) { register int i; psc->nrcpu = sstat->cpu.nrcpu; psc->availcpu = sstat->cpu.all.stime + sstat->cpu.all.utime + sstat->cpu.all.ntime + sstat->cpu.all.itime + sstat->cpu.all.wtime + sstat->cpu.all.Itime + sstat->cpu.all.Stime + sstat->cpu.all.steal; psc->availmem = sstat->mem.physmem * pagesize/1024; /* ** calculate total transfer issued by the active processes ** for disk and for network */ for (psc->availnet=psc->availdsk=0, i=0; i < nactproc; i++) { struct tstat *curstat = *(proclist+i); count_t nett_wsz; psc->availnet += curstat->net.tcpssz; psc->availnet += curstat->net.tcprsz; psc->availnet += curstat->net.udpssz; psc->availnet += curstat->net.udprsz; if (curstat->dsk.wsz > curstat->dsk.cwsz) nett_wsz = curstat->dsk.wsz - curstat->dsk.cwsz; else nett_wsz = 0; psc->availdsk += curstat->dsk.rsz; psc->availdsk += nett_wsz; } for (psc->availgpumem=i=0; i < sstat->gpu.nrgpus; i++) psc->availgpumem += sstat->gpu.gpu[i].memtotnow; psc->nrgpu = sstat->gpu.nrgpus; psc->nrmemnuma = sstat->memnuma.nrnuma; psc->nrcpunuma = sstat->cpunuma.nrnuma; } /* ** calculate cumulative system- and user-time for all active processes ** besides, initialize all counter lines on system level */ void pricumproc(struct sstat *sstat, struct devtstat *devtstat, int nexit, unsigned int noverflow, int avgval, int nsecs) { static int firsttime=1; int i; extraparam extra; for (i=0, extra.totut=extra.totst=0; i < devtstat->nprocactive; i++) { struct tstat *curstat = *(devtstat->procactive+i); extra.totut += curstat->cpu.utime; extra.totst += curstat->cpu.stime; } extra.nproc = devtstat->nprocall; extra.ntrun = devtstat->totrun; extra.ntslpi = devtstat->totslpi; extra.ntslpu = devtstat->totslpu; extra.ntidle = devtstat->totidle; extra.nzomb = devtstat->totzombie; extra.nexit = nexit; extra.noverflow = noverflow; extra.avgval = avgval; extra.nsecs = nsecs; extra.index = 0; if (firsttime) { firsttime=0; if (sysprcline[0].f == 0) { make_sys_prints(sysprcline, MAXITEMS, "PRCSYS:8 " "PRCUSER:8 " "BLANKBOX:0 " "PRCNPROC:7 " "PRCNRUNNING:5 " "PRCNSLEEPING:5 " "PRCNDSLEEPING:5 " "PRCNIDLE:5 " "PRCNZOMBIE:4 " "PRCCLONES:3 " "BLANKBOX:0 " "PRCNNEXIT:6", prcsyspdefs, "builtin sysprcline", sstat, &extra); } if (allcpuline[0].f == 0) { make_sys_prints(allcpuline, MAXITEMS, "CPUSYS:9 " "CPUUSER:8 " "CPUIRQ:6 " "BLANKBOX:0 " "CPUIDLE:7 " "CPUWAIT:7 " "CPUSTEAL:2 " "CPUGUEST:3 " "BLANKBOX:0 " "CPUIPC:5 " "CPUCYCLE:4 " "CPUFREQ:4 " "CPUSCALE:4 ", cpusyspdefs, "builtin allcpuline", sstat, &extra); } if (indivcpuline[0].f == 0) { make_sys_prints(indivcpuline, MAXITEMS, "CPUISYS:9 " "CPUIUSER:8 " "CPUIIRQ:6 " "BLANKBOX:0 " "CPUIIDLE:7 " "CPUIWAIT:7 " "CPUISTEAL:2 " "CPUIGUEST:3 " "BLANKBOX:0 " "CPUIIPC:5 " "CPUICYCLE:4 " "CPUIFREQ:4 " "CPUISCALE:4 ", cpisyspdefs, "builtin indivcpuline", sstat, &extra); } if (cplline[0].f == 0) { make_sys_prints(cplline, MAXITEMS, "CPLNUMCPU:7" "BLANKBOX:0 " "CPLAVG1:4 " "CPLAVG5:3 " "CPLAVG15:2 " "BLANKBOX:0 " "CPLCSW:6 " "CPLINTR:5 ", cplsyspdefs, "builtin cplline", sstat, &extra); } if (gpuline[0].f == 0) { make_sys_prints(gpuline, MAXITEMS, "GPUBUS:8 " "GPUGPUPERC:7 " "GPUMEMPERC:6 " "GPUMEMOCC:5 " "GPUMEMTOT:3 " "GPUMEMUSE:4 " "GPUMEMAVG:2 " "GPUNRPROC:2 " "BLANKBOX:0 " "GPUTYPE:1 ", gpusyspdefs, "builtin gpuline", NULL, NULL); } if (memline1[0].f == 0) { make_sys_prints(memline1, MAXITEMS, "MEMTOT:8 " "MEMFREE:9 " "MEMAVAIL:7 " "BLANKBOX:0 " "MEMCACHE:7 " "MEMDIRTY:5 " "MEMBUFFER:6 " "BLANKBOX:0 " "MEMSLAB:6 " "RECSLAB:2 " "BLANKBOX:0 " "HUPTOT:4 " "HUPUSE:1 ", memsyspdefs1, "builtin memline1", sstat, &extra); } if (memline2[0].f == 0) { make_sys_prints(memline2, MAXITEMS, "NUMNUMA:8 " "BLANKBOX:1 " "SHMEM:6 " "SHMRSS:4 " "SHMSWP:4 " "BLANKBOX:0 " "TCPSOCK:3 " "UDPSOCK:2 " "BLANKBOX:0 " "PAGETABS:5 " "BLANKBOX:0 " "ANONTHP:4 " "VMWBAL:4 " "BLANKBOX:0 " "ZFSARC:5 ", memsyspdefs2, "builtin memline2", sstat, &extra); } if (swpline[0].f == 0) { make_sys_prints(swpline, MAXITEMS, "SWPTOT:5 " "SWPFREE:6 " "SWPCACHE:4 " "BLANKBOX:0 " "ZSWPOOL:3 " "ZSWSTORED:3 " "BLANKBOX:0 " "KSMSHARED:2 " "KSMSHARING:2 " "BLANKBOX:0 " "SWPCOMMITTED:7 " "SWPCOMMITLIM:8", swpsyspdefs, "builtin swpline", sstat, &extra); } if (memnumaline[0].f == 0) { make_sys_prints(memnumaline, MAXITEMS, "NUMATOT:8 " "NUMAFREE:9 " "NUMAFILEPAGE:9 " "NUMANR:7 " "NUMADIRTY:5 " "NUMAACTIVE:5 " "NUMAINACTIVE:5 " "NUMASLAB:7 " "NUMASLABRECLAIM:4 " "NUMASHMEM:4 " "NUMAFRAG:6 " "NUMAHUPTOT:4 " "NUMAHUPUSE:3 ", memnumasyspdefs, "builtin memnumaline", sstat, &extra); } if (cpunumaline[0].f == 0) { make_sys_prints(cpunumaline, MAXITEMS, "NUMACPUSYS:9 " "NUMACPUUSER:8 " "NUMACPUNICE:8 " "NUMACPUIRQ:6 " "NUMACPUSOFTIRQ:6 " "NUMACPUIDLE:7 " "NUMACPUWAIT:7 " "NUMACPUSTEAL:2 " "NUMACPUGUEST:3 " "NUMANUMCPU:5", cpunumasyspdefs, "builtin cpunumaline", NULL, NULL); } if (llcline[0].f == 0) { make_sys_prints(llcline, MAXITEMS, "LLCMBMTOTAL:9 " "LLCMBMLOCAL:8 " "NUMLLC:7 " "BLANKBOX:0 ", llcsyspdefs, "builtin llcline", sstat, &extra); } if (pagline[0].f == 0) { make_sys_prints(pagline, MAXITEMS, "PAGSCAN:3 " "PAGSTEAL:2 " "PAGSTALL:1 " "PAGCOMPACT:5 " "NUMAMIGRATE:5" "PGMIGRATE:6" "PAGPGIN:6 " "PAGPGOUT:6 " "PAGZSWIN:4 " "PAGZSWOUT:7 " "PAGSWIN:5 " "PAGSWOUT:8 " "OOMKILLS:9 ", pagsyspdefs, "builtin pagline", sstat, &extra); } if (psiline[0].f == 0) { make_sys_prints(psiline, MAXITEMS, "PSICPUSTOT:7 " "PSIMEMSTOT:7 " "PSIMEMFTOT:8 " "PSIIOSTOT:7 " "PSIIOFTOT:8 " "PSICPUS:6 " "PSIMEMS:5 " "PSIMEMF:3 " "PSIIOS:4 " "PSIIOF:2 " "BLANKBOX:0 ", psisyspdefs, "builtin psiline", sstat, &extra); } if (contline[0].f == 0) { make_sys_prints(contline, MAXITEMS, "CONTNAME:8 " "CONTNPROC:7 " "CONTCPU:6 " "CONTMEM:6 " "BLANKBOX:0 " "BLANKBOX:0 ", contsyspdefs, "builtin contline", NULL, NULL); } if (dskline[0].f == 0) { make_sys_prints(dskline, MAXITEMS, "DSKNAME:9 " "DSKBUSY:8 " "DSKNREAD:7 " "DSKNWRITE:7 " "DSKNDISC:6 " "DSKKBPERRD:5 " "DSKKBPERWR:5 " "DSKKBPERDS:4 " "DSKMBPERSECRD:6 " "DSKMBPERSECWR:6 " "DSKINFLIGHT:2 " "DSKAVQUEUE:1 " "DSKAVIO:6", dsksyspdefs, "builtin dskline", sstat, &extra); } if (nfsmountline[0].f == 0) { make_sys_prints(nfsmountline, MAXITEMS, "NFMPATH:8 " "NFMSERVER:8 " "NFMTOTREAD:8 " "NFMTOTWRITE:8 " "BLANKBOX:0 " "NFMNREAD:7 " "NFMNWRITE:6 " "BLANKBOX:0 " "NFMDREAD:5 " "NFMDWRITE:4 " "BLANKBOX:0 " "NFMMREAD:3 " "NFMMWRITE:2 " "BLANKBOX:0 " "BLANKBOX:0", nfsmntsyspdefs, "builtin nfsmountline", NULL, NULL); } if (nfcline[0].f == 0) { make_sys_prints(nfcline, MAXITEMS, "NFCRPCCNT:8 " "NFCRPCREAD:7 " "NFCRPCWRITE:7 " "NFCRPCRET:5 " "NFCRPCARF:5 " "BLANKBOX:0 " "BLANKBOX:0 " "BLANKBOX:0 " "BLANKBOX:0 " "BLANKBOX:0 ", nfcsyspdefs, "builtin nfcline", sstat, &extra); } if (nfsline[0].f == 0) { make_sys_prints(nfsline, MAXITEMS, "NFSRPCCNT:8 " "NFSRPCREAD:6 " "NFSRPCWRITE:6 " "BLANKBOX:0 " "NFSNRBYTES:7 " "NFSNWBYTES:7 " "BLANKBOX:0 " "NFSNETTCP:5 " "NFSNETUDP:5 " "BLANKBOX:0 " "NFSRCHITS:3 " "NFSRCMISS:2 " "NFSRCNOCA:1 " "BLANKBOX:0 " "NFSBADFMT:4 " "NFSBADAUT:4 " "NFSBADCLN:4 ", nfssyspdefs, "builtin nfsline", sstat, &extra); } if (nettransportline[0].f == 0) { make_sys_prints(nettransportline, MAXITEMS, "NETTRANSPORT:9 " "NETTCPI:8 " "NETTCPO:8 " "NETUDPI:8 " "NETUDPO:8 " "NETTCPACTOPEN:6 " "NETTCPPASVOPEN:5 " "NETTCPRETRANS:4 " "NETTCPINERR:3 " "NETTCPORESET:2 " "NETTCPCSUMERR:2 " "NETUDPNOPORT:1 " "NETUDPINERR:3", nettranssyspdefs, "builtin nettransportline", sstat, &extra); } if (netnetline[0].f == 0) { make_sys_prints(netnetline, MAXITEMS, "NETNETWORK:5 " "NETIPI:4 " "NETIPO:4 " "NETIPFRW:4 " "NETIPDELIV:4 " "BLANKBOX:0 " "BLANKBOX:0 " "BLANKBOX:0 " "NETICMPIN:1 " "NETICMPOUT:1 ", netnetsyspdefs, "builtin netnetline", sstat, &extra); } if (netinterfaceline[0].f == 0) { make_sys_prints(netinterfaceline, MAXITEMS, "NETNAME:8 " "BLANKBOX:0 " "NETPCKI:7 " "NETPCKO:7 " "BLANKBOX:0 " "NETSPEEDMAX:5 " "NETSPEEDIN:6 " "NETSPEEDOUT:6 " "BLANKBOX:0 " "NETCOLLIS:2 " "NETMULTICASTIN:2 " "NETRCVERR:4 " "NETSNDERR:4 " "NETRCVDROP:3 " "NETSNDDROP:3", netintfsyspdefs, "builtin netinterfaceline", NULL, NULL); } if (infinibandline[0].f == 0) { make_sys_prints(infinibandline, MAXITEMS, "IFBNAME:8 " "BLANKBOX:0 " "IFBPCKI:7 " "IFBPCKO:7 " "BLANKBOX:0 " "IFBSPEEDMAX:5 " "IFBSPEEDIN:6 " "IFBSPEEDOUT:6 " "IFBLANES:4 " "BLANKBOX:0 " "BLANKBOX:0 " "BLANKBOX:0 " "BLANKBOX:0 " "BLANKBOX:0 " "BLANKBOX:0 ", infinisyspdefs, "builtin infinibandline", NULL, NULL); } } // firsttime move(1, 0); showsysline(sysprcline, sstat, &extra, "PRC", 0); } /* ** print the header for the process/cgroups list */ void prihead(int curlist, int totlist, char *showtype, char *showorder, char autosort, count_t numcpu) { static int firsttime=1; static int prev_supportflags = -1, prev_threadview = -1; /* ** determine once the layout of all per-process reports ** except for the generic report (might change dynamically) */ if (firsttime) { init_proc_prints(numcpu); make_detail_prints(memprocs, MAXITEMS, "PID:10 TID:3 MINFLT:2 MAJFLT:2 VSTEXT:4 VSLIBS:4 " "VDATA:4 VSTACK:4 LOCKSZ:3 VSIZE:6 RSIZE:7 PSIZE:5 " "VGROW:7 RGROW:8 SWAPSZ:5 RUID:1 EUID:0 " "SORTITEM:9 CMD:10", "built-in memprocs"); make_detail_prints(schedprocs, MAXITEMS, "PID:10 TID:6 CID:4 VPID:3 CTID:3 TRUN:7 TSLPI:7 " "TSLPU:7 TIDLE:7 POLI:8 NICE:9 PRI:5 RTPR:9 CPUNR:8 " "ST:8 EXC:8 S:8 RDELAY:8 BDELAY:7 WCHAN:5 " "NVCSW:7 NIVCSW:7 SORTITEM:10 CMD:10", "built-in schedprocs"); make_detail_prints(dskprocs, MAXITEMS, "PID:10 TID:4 RDDSK:9 " "WRDSK:9 WCANCL:8 " "SORTITEM:10 CMD:10", "built-in dskprocs"); make_detail_prints(netprocs, MAXITEMS, "PID:10 TID:6 " "TCPRCV:9 TCPRASZ:4 TCPSND:9 TCPSASZ:4 " "UDPRCV:8 UDPRASZ:3 UDPSND:8 UDPSASZ:3 " "BANDWI:10 BANDWO:10 " "SORTITEM:10 CMD:10", "built-in netprocs"); make_detail_prints(gpuprocs, MAXITEMS, "PID:10 TID:5 CID:4 GPULIST:8 GPUGPUBUSY:8 GPUMEMBUSY:8 " "GPUMEM:7 GPUMEMAVG:6 S:8 SORTITEM:10 CMD:10", "built-in gpuprocs"); make_detail_prints(varprocs, MAXITEMS, "PID:10 TID:4 PPID:9 CID:2 VPID:1 CTID:1 " "RUID:8 RGID:8 EUID:5 EGID:4 " "SUID:3 SGID:2 FSUID:3 FSGID:2 " "STDATE:7 STTIME:7 ENDATE:5 ENTIME:5 " "ST:6 EXC:6 S:6 SORTITEM:10 CMD:10", "built-in varprocs"); make_detail_prints(cmdprocs, MAXITEMS, "PID:10 TID:4 S:8 SORTITEM:10 COMMAND-LINE:10", "built-in cmdprocs"); make_detail_prints(totusers, MAXITEMS, "NPROCS:10 SYSCPU:9 USRCPU:9 VSIZE:6 " "RSIZE:8 PSIZE:8 LOCKSZ:3 SWAPSZ:5 RDDSK:7 CWRDSK:7 " "RNET:6 SNET:6 SORTITEM:10 RUID:10", "built-in totusers"); make_detail_prints(totprocs, MAXITEMS, "NPROCS:10 SYSCPU:9 USRCPU:9 VSIZE:6 " "RSIZE:8 PSIZE:8 LOCKSZ:3 SWAPSZ:5 RDDSK:7 CWRDSK:7 " "RNET:6 SNET:6 SORTITEM:10 CMD:10", "built-in totprocs"); make_detail_prints(totconts, MAXITEMS, "NPROCS:10 SYSCPU:9 USRCPU:9 RDELAY:8 BDELAY:7 VSIZE:6 " "RSIZE:8 PSIZE:8 LOCKSZ:3 SWAPSZ:5 RDDSK:7 CWRDSK:7 " "RNET:6 SNET:6 SORTITEM:10 CID:10", "built-in totconts"); // meant to show cgroups // make_detail_prints(gencgroups, MAXITEMS, "CGRPATH:10 CGRNPROCS:9 CGRNPROCSB:8 " "CGRCPUBUSY:7 CGRCPUPSI:4 CGRCPUMAX:3 CGRCPUWGT:2 " "CGRMEMORY:7 CGRMEMPSI:4 CGRMEMMAX:3 CGRSWPMAX:1 " "CGRDISKIO:6 CGRDSKPSI:4 CGRDSKWGT:2 CGRPID:6 CGRCMD:5", "built-in gencgroups"); } /* ** update the generic report if needed */ if (prev_supportflags != supportflags || prev_threadview != threadview) { make_proc_dynamicgen(); prev_supportflags = supportflags; prev_threadview = threadview; if (*showtype == MPROCNET && !(supportflags&NETATOP||supportflags&NETATOPBPF) ) { *showtype = MPROCGEN; *showorder = MSORTCPU; } } /* ** print the header line */ switch (*showtype) { case MPROCGEN: showprochead(genprocs, curlist, totlist, *showorder, autosort); break; case MPROCMEM: showprochead(memprocs, curlist, totlist, *showorder, autosort); break; case MPROCDSK: showprochead(dskprocs, curlist, totlist, *showorder, autosort); break; case MPROCNET: showprochead(netprocs, curlist, totlist, *showorder, autosort); break; case MPROCGPU: showprochead(gpuprocs, curlist, totlist, *showorder, autosort); break; case MPROCVAR: showprochead(varprocs, curlist, totlist, *showorder, autosort); break; case MPROCARG: showprochead(cmdprocs, curlist, totlist, *showorder, autosort); break; case MPROCOWN: showprochead(ownprocs, curlist, totlist, *showorder, autosort); break; case MPROCSCH: showprochead(schedprocs, curlist, totlist, *showorder, autosort); break; case MCUMUSER: showprochead(totusers, curlist, totlist, *showorder, autosort); break; case MCUMPROC: showprochead(totprocs, curlist, totlist, *showorder, autosort); break; case MCUMCONT: showprochead(totconts, curlist, totlist, *showorder, autosort); break; case MCGROUPS: showcgrouphead(gencgroups, curlist, totlist, *showorder); break; } } /* ** assemble the layout of the generic line, ** depending on the supported features (like ** I/O stats, network stats) and current view */ #define FORMPID "PID:10 " #define FORMTID "TID:6 " #define FORMCID "CID:5 " #define FORMCPU "SYSCPU:9 USRCPU:9 " #define FORMDEL "RDELAY:4 " #define FORMBDL "BDELAY:4 " #define FORMMEM "VGROW:8 RGROW:8 " #define FORMDSK "RDDSK:7 CWRDSK:7 " #define FORMNET "RNET:6 SNET:6 " #define FORMMSC "RUID:2 EUID:1 ST:3 EXC:3 THR:3 S:3 CPUNR:3 " #define FORMEND "SORTITEM:10 CMD:10" static void make_proc_dynamicgen() { char format[300], *p = format; memcpy(p, FORMPID, sizeof FORMPID -1); p += sizeof FORMPID -1; if (threadview) { memcpy(p, FORMTID, sizeof FORMTID -1); p += sizeof FORMTID -1; } if (supportflags & CONTAINERSTAT) { memcpy(p, FORMCID, sizeof FORMCID -1); p += sizeof FORMCID -1; } memcpy(p, FORMCPU, sizeof FORMCPU -1); p += sizeof FORMCPU -1; memcpy(p, FORMDEL, sizeof FORMDEL -1); p += sizeof FORMDEL -1; memcpy(p, FORMBDL, sizeof FORMBDL -1); p += sizeof FORMBDL -1; memcpy(p, FORMMEM, sizeof FORMMEM -1); p += sizeof FORMMEM -1; if (supportflags & IOSTAT) { memcpy(p, FORMDSK, sizeof FORMDSK -1); p += sizeof FORMDSK -1; } if (supportflags & NETATOP || supportflags & NETATOPBPF) { memcpy(p, FORMNET, sizeof FORMNET -1); p += sizeof FORMNET -1; } memcpy(p, FORMMSC, sizeof FORMMSC -1); p += sizeof FORMMSC -1; memcpy(p, FORMEND, sizeof FORMEND); p += sizeof FORMEND; make_detail_prints(genprocs, MAXITEMS, format, "built-in genprocs"); } /* ** print the list of cgroups (and processes) from the deviation list */ int pricgroup(struct cglinesel *itemlist, int firstitem, int lastitem, int curline, int curlist, int totlist, struct syscap *sb, int nsecs, int avgval) { int i; /* ** print info per cgroup */ for (i=firstitem; i < lastitem; i++) { /* ** screen filled entirely ? */ if (screen && curline >= LINES) break; if (screen) move(curline,0); /* ** print information for next line */ showcgroupline(gencgroups, (itemlist+i)->cgp, (itemlist+i)->tsp, nsecs, avgval, sb->availcpu, sb->nrcpu); curline++; } return curline; } /* ** print the list of processes from the deviation list */ int priproc(struct tstat **proclist, int firstproc, int lastproc, int curline, int curlist, int totlist, char showtype, char showorder, struct syscap *sb, int nsecs, int avgval) { register int i; register struct tstat *curstat; double perc; /* ** print info per actual process and maintain totals */ for (i=firstproc; i < lastproc; i++) { curstat = *(proclist+i); if (screen && curline >= LINES) /* screen filled entirely ? */ break; /* ** calculate occupation-percentage of this process ** depending on selected resource */ switch (showorder) { case MSORTCPU: perc = 0.0; if (sb->availcpu) { perc = (double)(curstat->cpu.stime + curstat->cpu.utime ) * 100 / (sb->availcpu / sb->nrcpu); if (perc > 100.0 * sb->nrcpu) perc = 100.0 * sb->nrcpu; if (perc > 100.0 * curstat->gen.nthr) perc = 100.0 * curstat->gen.nthr; } break; case MSORTMEM: perc = 0.0; if (sb->availmem) { perc = (double)curstat->mem.rmem * 100.0 / sb->availmem; if (perc > 100.0) perc = 100.0; } break; case MSORTDSK: perc = 0.0; if (sb->availdsk) { count_t nett_wsz; if (curstat->dsk.wsz > curstat->dsk.cwsz) nett_wsz = curstat->dsk.wsz - curstat->dsk.cwsz; else nett_wsz = 0; perc = (double)(curstat->dsk.rsz + nett_wsz) * 100.0 / sb->availdsk; if (perc > 100.0) perc = 100.0; } break; case MSORTNET: perc = 0.0; if (sb->availnet) { perc = (double)(curstat->net.tcpssz + curstat->net.tcprsz + curstat->net.udpssz + curstat->net.udprsz ) * 100.0 / sb->availnet; if (perc > 100.0) perc = 100.0; } break; case MSORTGPU: perc = 0.0; if (!curstat->gpu.state) break; if (curstat->gpu.gpubusy != -1) { perc = curstat->gpu.gpubusy; } else { perc = curstat->gpu.memnow*100 * sb->nrgpu / sb->availgpumem; } break; default: perc = 0.0; } /* ** now do the formatting of output */ if (screen) { move(curline,0); } switch (showtype) { case MPROCGEN: showprocline(genprocs, curstat, perc, nsecs, avgval); break; case MPROCMEM: showprocline(memprocs, curstat, perc, nsecs, avgval); break; case MPROCDSK: showprocline(dskprocs, curstat, perc, nsecs, avgval); break; case MPROCNET: showprocline(netprocs, curstat, perc, nsecs, avgval); break; case MPROCGPU: showprocline(gpuprocs, curstat, perc, nsecs, avgval); break; case MPROCVAR: showprocline(varprocs, curstat, perc, nsecs, avgval); break; case MPROCARG: showprocline(cmdprocs, curstat, perc, nsecs, avgval); break; case MPROCOWN: showprocline(ownprocs, curstat, perc, nsecs, avgval); break; case MPROCSCH: showprocline(schedprocs, curstat, perc, nsecs, avgval); break; case MCUMUSER: showprocline(totusers, curstat, perc, nsecs, avgval); break; case MCUMPROC: showprocline(totprocs, curstat, perc, nsecs, avgval); break; case MCUMCONT: showprocline(totconts, curstat, perc, nsecs, avgval); break; } curline++; } return curline; } /* ** print the system-wide statistics */ static void pridisklike(extraparam *, struct perdsk *, char *, char *, int, unsigned int *, int *, int, regex_t *); int prisyst(struct sstat *sstat, int curline, int nsecs, int avgval, int fixedhead, struct sselection *selp, char *highorderp, int maxcpulines, int maxgpulines, int maxdsklines, int maxmddlines, int maxlvmlines, int maxintlines, int maxifblines, int maxnfslines, int maxcontlines, int maxnumalines, int maxllclines) { extraparam extra; int lin; count_t busy; unsigned int badness, highbadness=0; extra.nsecs = nsecs; extra.avgval = avgval; /* ** CPU statistics */ extra.cputot = sstat->cpu.all.stime + sstat->cpu.all.utime + sstat->cpu.all.ntime + sstat->cpu.all.itime + sstat->cpu.all.wtime + sstat->cpu.all.Itime + sstat->cpu.all.Stime + sstat->cpu.all.steal; busy = (extra.cputot - sstat->cpu.all.itime - sstat->cpu.all.wtime) * 100.0 / extra.cputot; if (cpubadness) badness = busy * 100 / cpubadness; else badness = 0; if (highbadness < badness) { highbadness = badness; *highorderp = MSORTCPU; } if (extra.cputot == 0) extra.cputot = 1; /* avoid divide-by-zero */ extra.percputot = extra.cputot / sstat->cpu.nrcpu; if (extra.percputot == 0) extra.percputot = 1; /* avoid divide-by-zero */ if (screen) move(curline, 0); showsysline(allcpuline, sstat, &extra, "CPU", badness); curline++; if (sstat->cpu.nrcpu > 1) { for (extra.index=lin=0; extra.index < sstat->cpu.nrcpu && lin < maxcpulines; extra.index++) { extra.percputot = sstat->cpu.cpu[extra.index].stime + sstat->cpu.cpu[extra.index].utime + sstat->cpu.cpu[extra.index].ntime + sstat->cpu.cpu[extra.index].itime + sstat->cpu.cpu[extra.index].wtime + sstat->cpu.cpu[extra.index].Itime + sstat->cpu.cpu[extra.index].Stime + sstat->cpu.cpu[extra.index].steal; if (extra.percputot == (sstat->cpu.cpu[extra.index].itime + sstat->cpu.cpu[extra.index].wtime ) && !fixedhead ) continue; /* inactive cpu */ busy = (extra.percputot - sstat->cpu.cpu[extra.index].itime - sstat->cpu.cpu[extra.index].wtime) * 100.0 / extra.percputot; if (cpubadness) badness = busy * 100 / cpubadness; else badness = 0; if (highbadness < badness) { highbadness = badness; *highorderp = MSORTCPU; } if (extra.percputot == 0) extra.percputot = 1; /* avoid divide-by-zero */ if (screen) move(curline, 0); showsysline(indivcpuline, sstat, &extra, "cpu", badness); curline++; lin++; } } /* ** other CPU-related statistics */ if (screen) move(curline, 0); showsysline(cplline, sstat, &extra, "CPL", 0); curline++; /* ** GPU statistics */ if (sstat->gpu.nrgpus) { for (extra.index=0, lin=0; extra.index < sstat->gpu.nrgpus && lin < maxgpulines; extra.index++) { int totbusy; count_t avgmemuse; // notice that GPU percentage and memory percentage // are not always available; in that case both // values have the value -1 // totbusy = sstat->gpu.gpu[extra.index].gpuperccum + sstat->gpu.gpu[extra.index].memperccum; if (totbusy == -2) // metrics available? totbusy= 0; if (sstat->gpu.gpu[extra.index].samples == 0) { totbusy = sstat->gpu.gpu[extra.index].gpupercnow + sstat->gpu.gpu[extra.index].mempercnow; avgmemuse = sstat->gpu.gpu[extra.index].memusenow; } else { totbusy = totbusy / sstat->gpu.gpu[extra.index].samples; avgmemuse = sstat->gpu.gpu[extra.index].memusecum/ sstat->gpu.gpu[extra.index].samples; } if (gpubadness) badness = totbusy * 100 / gpubadness; else badness = 0; if ( totbusy > 0 || // memusage > 512 MiB (rather arbitrary)? avgmemuse > 512*1024 || fixedhead ) { showsysline(gpuline, sstat, &extra, "GPU", badness); curline++; lin++; } } } /* ** MEMORY statistics */ busy = (sstat->mem.physmem - sstat->mem.freemem - sstat->mem.cachemem - sstat->mem.buffermem - sstat->mem.slabreclaim + sstat->mem.shmem) * 100.0 / sstat->mem.physmem; if (membadness) badness = busy * 100 / membadness; else badness = 0; if (highbadness < badness) { highbadness = badness; *highorderp = MSORTMEM; } if (screen) move(curline, 0); showsysline(memline1, sstat, &extra, "MEM", badness); curline++; showsysline(memline2, sstat, &extra, "MEM", badness); curline++; /* ** SWAP statistics */ busy = (sstat->mem.totswap - sstat->mem.freeswap) * 100.0 / sstat->mem.totswap; if (swpbadness) { badness = busy * 100 / swpbadness; } else { badness = 0; } if (highbadness < badness) { highbadness = badness; *highorderp = MSORTMEM; } if (screen) move(curline, 0); showsysline(swpline, sstat, &extra, "SWP", badness); curline++; /* ** memory info related for per NUMA */ if (sstat->memnuma.nrnuma > 1) { 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_detail_prints(ownprocs, MAXITEMS, val, name); } atop-2.11.1/ifprop.h0000644000203100020310000000320214771753566013531 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.11.1/showsys.c0000644000203100020310000032154214771753566013756 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 = " -"; } 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.11.1/cgroups.h0000644000203100020310000001057514771753566013727 0ustar gerlofgerlof/* ** ATOP - System & Process Monitor ** ** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as process-level. ** ** Include-file describing cgroup-level counters to be maintained. ** ================================================================ ** Author: Gerlof Langeveld ** E-mail: gerlof.langeveld@atoptool.nl ** Date: January/February 2024 ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. */ #ifndef __CGROUPS__ #define __CGROUPS__ // structure containing general info and metrics per cgroup (directory) // struct cstat { // GENERAL INFO struct cggen { int structlen; // struct length including rounded name int sequence; // sequence number in chain/array int parentseq; // parent sequence number in chain/array int depth; // cgroup tree depth starting from 0 int nprocs; // number of processes in cgroup int procsbelow; // number of processes in cgroups below int namelen; // cgroup name length (at end of struct) int fullnamelen; // cgroup path length int ifuture[4]; long namehash; // cgroup name hash of // full path name excluding slashes long lfuture[4]; } gen; // CONFIGURATION INFO struct cgconf { int cpuweight; // -1=max, -2=undefined int cpumax; // -1=max, -2=undefined (perc) count_t memmax; // -1=max, -2=undefined (pages) count_t swpmax; // -1=max, -2=undefined (pages) int dskweight; // -1=max, -2=undefined int ifuture[5]; count_t cfuture[5]; } conf; // CPU STATISTICS struct cgcpu { count_t utime; // time user text (usec) -1=undefined count_t stime; // time system text (usec) -1=undefined count_t somepres; // some pressure (microsec) count_t fullpres; // full pressure (microsec) count_t cfuture[5]; } cpu; // MEMORY STATISTICS struct cgmem { count_t current; // current memory (pages) -1=undefined count_t anon; // anonymous memory (pages) -1=undefined count_t file; // file memory (pages) -1=undefined count_t kernel; // kernel memory (pages) -1=undefined count_t shmem; // shared memory (pages) -1=undefined count_t somepres; // some pressure (microsec) count_t fullpres; // full pressure (microsec) count_t cfuture[5]; } mem; // DISK I/O STATISTICS struct cgdsk { count_t rbytes; // total bytes read on all physical disks count_t wbytes; // total bytes written on all physical disks count_t rios; // total read I/Os on all physical disks count_t wios; // total write I/Os on all physical disks count_t somepres; // some pressure (microsec) count_t fullpres; // full pressure (microsec) count_t cfuture[5]; } dsk; // cgroup name with variable length char cgname[]; }; // structure to be used for chaining the cstat struct and pid list // that are maintained per cgroup // struct cgchainer { struct cgchainer *next; struct cgchainer *hashnext; struct cstat *cstat; // cgroup info and stats pid_t *proclist; // PID list of cgroup unsigned long vlinemask; // bit list for tree drawing: // bit '1' for continuous line char stub; // boolean for tree drawing: // true means corner }; #define CGRMAXDEPTH (sizeof(unsigned long)*8) // #bits in vlinemask // structure to be used for printing a merged list // of cgroups and processes // struct tstat; struct cglinesel { struct cgchainer *cgp; // always filled with reference to cgroup struct tstat *tsp; // filled for process info, otherwise NULL }; int mergecgrouplist(struct cglinesel **, int, struct cgchainer **, int, struct tstat **, int, char); int cgroupv2support(void); void photocgroup(void); int deviatcgroup(struct cgchainer **, int *); struct cgchainer **cgsort(struct cgchainer *, int, char); char *cggetpath(struct cgchainer *, struct cgchainer *, int); void cgwipecur(void); void cgbuildarray(struct cgchainer **, char *, char *, int); void cgfillref(struct devtstat *, struct cgchainer *, int, int); #endif atop-2.11.1/atop-rotate.service0000644000203100020310000000023214771753566015702 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.11.1/atop.init0000755000203100020310000000255114771753566013722 0ustar gerlofgerlof#!/bin/sh # # atop Startup script for the Atop process logging in background # # chkconfig: 2345 96 4 # description: Advanced system and process activity monitor # ### BEGIN INIT INFO # Provides: atop # Required-Start: $local_fs $remote_fs # Required-Stop: $local_fs $remote_fs # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Advanced system and process activity monitor # Description: Advanced system and process activity monitor ### END INIT INFO # Check existance of binaries [ -f /usr/bin/atop ] || exit 0 PIDFILE=/var/run/atop.pid RETVAL=0 # See how we were called. case "$1" in start) # Check if atop runs already # if [ -e $PIDFILE ] && ps -p `cat $PIDFILE` | grep 'atop$' > /dev/null then : else # Start atop /usr/share/atop/atop.daily& fi touch /var/lock/subsys/atop ;; stop) # Check if atop runs # if [ -e $PIDFILE ] && ps -p `cat $PIDFILE` | grep 'atop$' > /dev/null then kill -USR2 `cat $PIDFILE` # final sample and terminate CNT=0 while ps -p `cat $PIDFILE` > /dev/null do CNT=$((CNT + 1)) if [ $CNT -gt 5 ] then break; fi sleep 1 done rm $PIDFILE fi rm -f /var/lock/subsys/atop ;; status) ;; reload) /usr/share/atop/atop.daily& ;; restart) /usr/share/atop/atop.daily& ;; *) echo "Usage: $0 [start|stop|status|reload|restart]" exit 1 esac exit $RETVAL atop-2.11.1/netstats.h0000644000203100020310000001061014771753566014100 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.11.1/netatop.h0000644000203100020310000000477414771753566013723 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.11.1/version.c0000644000203100020310000000102314771753566013711 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.11.1/showgeneric.h0000644000203100020310000001501414771753566014553 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 COLORCPUINTR WHITE_BLUE3 #define COLORCPUSTEAL WHITE_BLUE4 #define COLORCPUGUEST WHITE_BLUE5 #define COLORMEMFREE WHITE_GREEN #define COLORMEMCACH WHITE_ORANGE #define COLORMEMHUGE WHITE_BLUE5 #define COLORMEMUSED WHITE_GREY #define COLORMEMSHM WHITE_BROWN1 #define COLORMEMTMP WHITE_BLUE #define COLORMEMSLAB WHITE_MAGENTA #define COLORMEMBAR WHITE_BLUE3 #define COLORDSKREAD WHITE_GREEN1 #define COLORDSKWRITE WHITE_GREEN2 #define COLORNETRECV WHITE_BROWN1 #define COLORNETSEND WHITE_BROWN2 /* ** list with keystrokes/flags */ #define MPROCGEN 'g' #define MPROCMEM 'm' #define MPROCDSK 'd' #define MPROCNET 'n' #define MPROCGPU 'e' #define MPROCSCH 's' #define MPROCVAR 'v' #define MPROCARG 'c' #define MCGROUPS 'G' #define MPROCOWN 'o' #define MCUMUSER 'u' #define MCUMPROC 'p' #define MCUMCONT 'j' #define MSORTCPU 'C' #define MSORTDSK 'D' #define MSORTMEM 'M' #define MSORTNET 'N' #define MSORTGPU 'E' #define MSORTAUTO 'A' #define MTHREAD 'y' #define MTHRSORT 'Y' #define MCALCPSS 'R' #define MGETWCHAN 'W' #define MSUPEXITS 'X' #define MCOLORS 'x' #define MSYSFIXED 'f' #define MSYSNOSORT 'F' #define MSYSLIMIT 'l' #define MRMSPACES 'Z' #define MSELUSER 'U' #define MSELPROC 'P' #define MSELCONT 'J' #define MSELPID 'I' #define MSELARG '/' #define MSELSTATE 'Q' #define MSELSYS 'S' #define MALLACTIVE 'a' #define MKILLPROC 'k' #define MLISTFW 0x06 #define MLISTBW 0x02 #define MREDRAW 0x0c #define MINTERVAL 'i' #define MPAUSE 'z' #define MQUIT 'q' #define MRESET 'r' #define MSAMPNEXT 't' #define MSAMPPREV 'T' #define MSAMPBRANCH 'b' #define MVERSION 'V' #define MAVGVAL '1' #define MHELP1 '?' #define MHELP2 'h' #define MBARGRAPH 'B' #define MBARLOWER 'L' #define MBARMONO 'H' /* ** extern values for globals */ extern int paused; extern int cgroupdepth; /* ** general function prototypes */ void totalcap (struct syscap *, struct sstat *, struct tstat **, int); void pricumproc (struct sstat *, struct devtstat *, int, unsigned int, int, int); void showgenproc(struct tstat *, double, int, int); void showmemproc(struct tstat *, double, int, int); void showdskproc(struct tstat *, double, int, int); void shownetproc(struct tstat *, double, int, int); void showvarproc(struct tstat *, double, int, int); void showschproc(struct tstat *, double, int, int); void showtotproc(struct tstat *, double, int, int); void showcmdproc(struct tstat *, double, int, int); void printg(const char *, ...); int prisyst(struct sstat *, int, int, int, int, struct sselection *, char *, int, int, int, int, int, int, int, int, int, int, int); void prihead(int, int, char *, char *, char, count_t); struct cglinesel; int pricgroup(struct cglinesel *, int, int, int, int, int, struct syscap *, int, int); int priproc(struct tstat **, int, int, int, int, int, char, char, struct syscap *, int, int); char draw_samp(time_t, int, struct sstat *, char, char); void do_username(char *, char *); void do_procname(char *, char *); void do_maxcpu(char *, char *); void do_maxgpu(char *, char *); void do_maxdisk(char *, char *); void do_maxmdd(char *, char *); void do_maxlvm(char *, char *); void do_maxintf(char *, char *); void do_maxifb(char *, char *); void do_maxnfsm(char *, char *); void do_maxcont(char *, char *); void do_maxnuma(char *, char *); void do_maxllc(char *, char *); void do_colinfo(char *, char *); void do_colalmost(char *, char *); void do_colcrit(char *, char *); void do_colthread(char *, char *); void do_twindir(char *, char *); void do_flags(char *, char *); #endif atop-2.11.1/netatopbpfif.c0000644000203100020310000001110014771753566014702 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.11.1/cgroups.c0000644000203100020310000012215514771753566013720 0ustar gerlofgerlof/* ** ATOP - System & Process Monitor ** ** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as process-/thread-level. ** ** This source-file contains functions to read and handle cgroup metrics. ** ========================================================================== ** Author: Gerlof Langeveld ** E-mail: gerlof.langeveld@atoptool.nl ** Initial date: January 2024 ** -------------------------------------------------------------------------- ** Copyright (C) 2024 Gerlof Langeveld ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ** -------------------------------------------------------------------------- */ #include #include #include #include #include #include #include #include #include #include #include "atop.h" #include "cgroups.h" #include "photosyst.h" #include "photoproc.h" #include "showgeneric.h" static void cgcalcdeviate(void); static void cgrewind(struct cgchainer **); static struct cgchainer *cgnext(struct cgchainer **, struct cgchainer **); static void cgwipe(struct cgchainer **, struct cgchainer **, struct cgchainer **, struct cgchainer **); static unsigned long walkcgroup(char *, struct cgchainer *, int, long, int, int); static void getconfig(struct cstat *, struct cstat *); static int readconfigval(char *, count_t []); static void getmetrics(struct cstat *); static void getpressure(char *, count_t *, count_t *); static long hashcalc(char *, long, int); static void hashadd(struct cgchainer *[], struct cgchainer *); static struct cgchainer *hashfind(struct cgchainer *[], long); static int cgroupfilter(struct cstat *, int, char); #define CGROUPROOT "/sys/fs/cgroup" #define CGROUPNHASH 128 // power of 2 #define CGROUPMASK (CGROUPNHASH-1) // cgroup administration // ===================== // three types of cgroup collections are maintained: // // - current linked list // ------------------- // this administration consists of a chain of cgchainer structs // that each refer to a cstat struct containing the cgroup metrics // // this chain is built while gathering the current information by the // walkcgroup() function // this function mallocs one cgchainer struct and one cstat struct // // - previous linked list // -------------------- // this administration consists of a chain of cgchainer structs // that each refer to a cstat struct containing the previous cgroup metrics // // before building a new current chain, the current chain will be // saved as the previous chain // // - deviation array // --------------- // this administration consists of an array(!) of cgchainer structs in which // the cgchainer struct are still chained to the next element in the array // // current cgroup admi // static struct cgchainer *cgcurfirst, // first in linked list *cgcurlast, // last in linked list *cgcurcursor; static unsigned int cgcursize; // total size of all cstat structs static unsigned int cgcurnum; // number of cstat structs static unsigned int cgcurprocs; // number of processes in current list static int cgcursequence; // maintain sequence number to be // assigned to every cgchainer struct // // later on this value can be used // as index in the deviation array // previous cgroup admi // static struct cgchainer *cgprefirst, // first in linked list *cgprelast, // last in linked list *cgprecursor, *cgprehash[CGROUPNHASH]; // deviation cgroup admi // static struct cgchainer *cgdevfirst, // pointer to array (!) *cgdevcursor; // Check if cgroup v2 is supported on this machine // // Return value: 1 - yes // 0 - no // int cgroupv2support(void) { FILE *fp; char line[128]; // check if this kernel offers cgroups version 2 // if ( (fp = fopen("/proc/1/cgroup", "r")) ) { while (fgets(line, sizeof line, fp)) { if (memcmp(line, "0::", 3) == 0) // equal? { supportflags |= CGROUPV2; break; } } fclose(fp); } return (supportflags&CGROUPV2) > 0; } // Gather all metrics from the entire cgroup tree, // creating a chain of cgchainer structs // void photocgroup(void) { char origdir[4096]; // wipe previous cgroup chain (not needed any more) // cgwipe(&cgprefirst, &cgprelast, &cgprecursor, cgprehash); // move current cgroup chain to previous cgroup chain // cgprefirst = cgcurfirst; cgprelast = cgcurlast; cgcurfirst = cgcurlast = cgcurcursor = NULL; // wipe deviation cgroup memory // - wipe all contiguous cstat structs (start address in first cgchainer) // - wipe pidlist (start address in first cgchainer) // - wipe all contiguous cgchainer structs // if (cgdevfirst) { free(cgdevfirst->cstat); free(cgdevfirst->proclist); free(cgdevfirst); } // save current directory and move to top directory // of cgroup fs // if ( getcwd(origdir, sizeof origdir) == NULL) mcleanstop(53, "failed to save current dir\n"); if ( chdir(CGROUPROOT) == -1) mcleanstop(54, "failed to change to " CGROUPROOT "\n"); // read all subdirectory names below the cgroup top directory // cgcursequence = 0; cgcursize = 0; cgcurnum = 0; cgcurprocs = 0; walkcgroup(".", NULL, -1, 0, 0, 0); // return to original directory // if ( chdir(origdir) == -1) mcleanstop(55, "cannot change to %s\n", origdir); } // Gather statistics from one cgroup directory level // and walk the directory tree further downwards by nested calls. // Every call to this function will add one struct to the current // cgroup chain. // // Return value: number of processes in this dir and the dirs underneath // -1 = fail // static unsigned long walkcgroup(char *dirname, struct cgchainer *cparent, int parentseq, long upperhash, int upperlen, int depth) { FILE *fp; DIR *dirp; struct dirent *entp; int namelen = strlen(dirname); int cstatlen, i; unsigned long procsbelow=0, proccnt=0, hash; struct cgchainer *ccp; // change to new directory // if ( chdir(dirname) == -1) return -1; // -------------------------------------------- // gather statistics for this cgroup directory // -------------------------------------------- // - create new cgchainer struct // ccp = calloc(1, sizeof(struct cgchainer)); ptrverify(ccp, "Malloc failed for current cgchainer\n"); // - add cgchainer to current chain // if (cgcurfirst) { cgcurlast->next = ccp; cgcurlast = ccp; } else { cgcurfirst = ccp; cgcurlast = ccp; } // - create corresponding cstat struct // with a rounded length to avoid unaligned structs later on // cstatlen = sizeof(struct cstat) + namelen + 1; if (cstatlen & 0x7) // length is not 64-bit multiple? cstatlen = ((cstatlen >> 3) + 1) << 3; // round up to 64-bit ccp->cstat = calloc(1, cstatlen); ptrverify(ccp->cstat, "Malloc failed for cstat of %d bytes\n", cstatlen); cgcursize += cstatlen; cgcurnum++; // - determine number of processes in this cgroup directory // if ( (fp = fopen("cgroup.procs", "r")) ) { char line[64]; while (fgets(line, sizeof line, fp)) proccnt++; fclose(fp); } // - create corresponding pid list to cgchainer // and fill it with the PIDs // if (proccnt) { ccp->proclist = malloc(sizeof(pid_t) * proccnt); ptrverify(ccp->proclist, "Malloc failed for proclist (%d pids)\n", proccnt); } else { ccp->proclist = NULL; } i = 0; if ( (fp = fopen("cgroup.procs", "r")) ) { char line[64]; while (fgets(line, sizeof line, fp)) { *(ccp->proclist+i) = strtol(line, NULL, 10); if (++i >= proccnt) break; } fclose(fp); } proccnt = i; // number of processes might have shrunk cgcurprocs += proccnt; // - fill basic info in current cgchainer // strcpy(ccp->cstat->cgname, dirname); // copy directory name if (*dirname == '.') // top directory? { ccp->cstat->cgname[0] = '\0'; // wipe name namelen = 0; } hash = hashcalc(ccp->cstat->cgname, upperhash, upperlen); ccp->cstat->gen.structlen = cstatlen; ccp->cstat->gen.sequence = cgcursequence++; // assign unique sequence number ccp->cstat->gen.parentseq = parentseq; ccp->cstat->gen.depth = depth; ccp->cstat->gen.nprocs = proccnt; ccp->cstat->gen.namelen = namelen; ccp->cstat->gen.fullnamelen = upperlen + namelen; // excluding slashes ccp->cstat->gen.namehash = hash; // - gather and store current cgroup configuration // getconfig(ccp->cstat, cparent ? cparent->cstat : NULL); // - gather and store current cgroup metrics // getmetrics(ccp->cstat); // -------------------------------------------- // walk subdirectories by nested calls // -------------------------------------------- // dirp = opendir("."); while ( (entp = readdir(dirp)) ) { // skip dot files/directories // if (entp->d_name[0] == '.') continue; // check if this entry represents a subdirectory // to be walked as well // if (entp->d_type == DT_DIR) procsbelow += walkcgroup(entp->d_name, ccp, ccp->cstat->gen.sequence, hash, upperlen+namelen, depth+1); } closedir(dirp); chdir(".."); ccp->cstat->gen.procsbelow = procsbelow; return procsbelow + proccnt; } // Gather configuration values for one specific cgroup // value -2: undefined // value -1: maximum // static void getconfig(struct cstat *csp, struct cstat *csparent) { count_t retvals[2]; // get cpu.weight // csp->conf.cpuweight = -2; // initial value (undefined) switch (readconfigval("cpu.weight", retvals)) { case 1: csp->conf.cpuweight = retvals[0]; break; } // get cpu.max limitation // csp->conf.cpumax = -2; // initial value (undefined) switch (readconfigval("cpu.max", retvals)) { case 2: if (retvals[0] == -1) csp->conf.cpumax = -1; // max else csp->conf.cpumax = retvals[0] * 100 / retvals[1]; break; } // get io.bpf.weight // csp->conf.dskweight = -2; // initial value (undefined) switch (readconfigval("io.bfq.weight", retvals)) { case 2: csp->conf.dskweight = retvals[1]; break; } // get memory.max limitation // csp->conf.memmax = -2; // initial value (undefined) switch (readconfigval("memory.max", retvals)) { case 1: if (retvals[0] == -1) csp->conf.memmax = -1; // max else csp->conf.memmax = retvals[0] / pagesize; break; } // get memory.swap.max limitation // csp->conf.swpmax = -2; // initial value (undefined) switch (readconfigval("memory.swap.max", retvals)) { case 1: if (retvals[0] == -1) csp->conf.swpmax = -1; // max else csp->conf.swpmax = retvals[0] / pagesize; break; } } // read line with one or two values and // fill one or (maximum) two values // in retvals (value 'max' converted into -1) // // return value: number of entries in retvals filled // static int readconfigval(char *fname, count_t retvals[]) { char line[64]; int n; FILE *fp; if ( (fp = fopen(fname, "r")) ) { char firststr[16]; if (!fgets(line, sizeof line, fp)) { fclose(fp); return 0; } fclose(fp); switch (n = sscanf(line, "%15s %llu", firststr, &retvals[1])) { case 0: return 0; case 1: case 2: if ( strcmp(firststr, "max") == 0) retvals[0] = -1; else retvals[0] = atol(firststr); return n; default: return 0; } } return 0; } // Gather metrics for one specific cgroup // static void getmetrics(struct cstat *csp) { FILE *fp; char line[256]; int cnt; // gather CPU metrics // csp->cpu.utime = -1; // undefined csp->cpu.stime = -1; // undefined cnt = 0; if ( (fp = fopen("cpu.stat", "r")) ) { while (fgets(line, sizeof line, fp) && cnt < 2) { if (memcmp(line, "user_usec ", 10) == 0) { sscanf(line, "user_usec %lld", &(csp->cpu.utime)); cnt++; continue; } if (memcmp(line, "system_usec ", 12) == 0) { sscanf(line, "system_usec %lld", &(csp->cpu.stime)); cnt++; continue; } } fclose(fp); } getpressure("cpu.pressure", &(csp->cpu.somepres), &(csp->cpu.fullpres)); // gather memory metrics // csp->mem.current = -1; // undefined csp->mem.anon = -1; // undefined csp->mem.file = -1; // undefined csp->mem.kernel = -1; // undefined csp->mem.shmem = -1; // undefined cnt = 0; if ( (fp = fopen("memory.current", "r")) ) { if (fgets(line, sizeof line, fp) != NULL) csp->mem.current = strtoll(line, NULL, 10) / pagesize; fclose(fp); } if ( (fp = fopen("memory.stat", "r")) ) { while (fgets(line, sizeof line, fp) && cnt < 4) { if (memcmp(line, "anon ", 5) == 0) { sscanf(line, "anon %lld", &(csp->mem.anon)); csp->mem.anon /= pagesize; cnt++; continue; } if (memcmp(line, "file ", 5) == 0) { sscanf(line, "file %lld", &(csp->mem.file)); csp->mem.file /= pagesize; cnt++; continue; } if (memcmp(line, "kernel ", 7) == 0) { sscanf(line, "kernel %lld", &(csp->mem.kernel)); csp->mem.kernel /= pagesize; cnt++; continue; } if (memcmp(line, "shmem ", 6) == 0) { sscanf(line, "shmem %lld", &(csp->mem.shmem)); csp->mem.shmem /= pagesize; cnt++; continue; } } fclose(fp); } getpressure("memory.pressure", &(csp->mem.somepres), &(csp->mem.fullpres)); // gather disk I/O metrics // // 253:2 rbytes=2544128 wbytes=2192896 rios=313 wios=22 dbytes=0 dios=0 // 253:1 rbytes=143278080 wbytes=3843604480 rios=34222 wios=853554 ... // 253:0 rbytes=19492288000 wbytes=105266814976 rios=386493 wios=1691322 // 11:0 // 8:0 rbytes=328956266496 wbytes=109243312640 rios=764129 wios=1415575 // csp->dsk.rbytes = 0; csp->dsk.wbytes = 0; csp->dsk.rios = 0; csp->dsk.wios = 0; if ( (fp = fopen("io.stat", "r")) ) { int major, minor; count_t rbytes, wbytes, rios, wios; while (fgets(line, sizeof line, fp)) { if (sscanf(line, "%d:%d rbytes=%lld wbytes=%lld rios=%lld wios=%lld", &major, &minor, &rbytes, &wbytes, &rios, &wios) == 6) { if (isdisk_major(major) == DSKTYPE) { csp->dsk.rbytes += rbytes; csp->dsk.wbytes += wbytes; csp->dsk.rios += rios; csp->dsk.wios += wios; } } } fclose(fp); } else { csp->dsk.rbytes = -1; // undefined csp->dsk.wbytes = -1; // undefined csp->dsk.rios = -1; // undefined csp->dsk.wios = -1; // undefined } getpressure("io.pressure", &(csp->dsk.somepres), &(csp->dsk.fullpres)); } // Get total pressure values from file with a format similar to: // // some avg10=0.00 avg60=0.00 avg300=0.00 total=9660682563 // full avg10=0.00 avg60=0.00 avg300=0.00 total=9376892229 // static void getpressure(char *fname, count_t *some, count_t *full) { char psiformat[] = "%c%*s avg10=%f avg60=%f avg300=%f total=%llu", linebuf[256], psitype; float a10, a60, a300; FILE *fp; *some = -1; // initially undefined *full = -1; // initially undefined if ( (fp = fopen(fname, "r")) != NULL) { // handle first line: 'some' pressure // if ( fgets(linebuf, sizeof(linebuf), fp) != NULL) { sscanf(linebuf, psiformat, &psitype, &a10, &a60, &a300, some); } // handle second line: 'full' pressure // if ( fgets(linebuf, sizeof(linebuf), fp) != NULL) { sscanf(linebuf, psiformat, &psitype, &a10, &a60, &a300, full); } fclose(fp); } } // Calculate deviations between current cgroup chain and // previous cgroup chain. // Notice that the deviation chain structure is identical to // the current chain structure, so they can be walked alongside. // Also the deviation cstat struct is already a copy of the current // cstat structure. // static void cgcalcdeviate(void) { struct cgchainer *dp, *pp; int insync = 1, prevfound; // walk thru the deviation chain and calculate the differences // with the previous chain // // when no cgroups have been added or removed since the // previous sample, we might assume that the previous chain // has the same order of elements as the deviation chain (insync) // // when cgroups have been added or removed since the // previous sample (!insync), we build a hash list for the // previous chain to find the correct entry based on the hashed name // cgrewind(&cgdevcursor); cgrewind(&cgprecursor); while ( (dp = cgnext(&cgdevfirst, &cgdevcursor)) ) { // find previous cgroup struct // prevfound = 0; if (insync) { // assume unchanged cgroup chain: take next from previous chain // pp = cgnext(&cgprefirst, &cgprecursor); if (pp && dp->cstat->gen.namehash == pp->cstat->gen.namehash) { prevfound = 1; } else { insync = 0; // build hash list for previous chain to find names // based on hashed name for the rest of this loop // cgrewind(&cgprecursor); while ( (pp = cgnext(&cgprefirst, &cgprecursor)) ) hashadd(cgprehash, pp); } } if (!insync) { // modified cgroup chain: search previous by name hash // if ( (pp = hashfind(cgprehash, dp->cstat->gen.namehash)) ) prevfound = 1; } // calculate deviations related to previous sample // if (prevfound) { if (dp->cstat->cpu.utime != -1) // defined? dp->cstat->cpu.utime -= pp->cstat->cpu.utime; if (dp->cstat->cpu.stime != -1) // defined? dp->cstat->cpu.stime -= pp->cstat->cpu.stime; if (dp->cstat->cpu.somepres != -1) // defined? dp->cstat->cpu.somepres -= pp->cstat->cpu.somepres; if (dp->cstat->cpu.fullpres != -1) // defined? dp->cstat->cpu.fullpres -= pp->cstat->cpu.fullpres; if (dp->cstat->mem.somepres != -1) // defined? dp->cstat->mem.somepres -= pp->cstat->mem.somepres; if (dp->cstat->mem.fullpres != -1) // defined? dp->cstat->mem.fullpres -= pp->cstat->mem.fullpres; if (dp->cstat->dsk.rbytes != -1) // defined? { dp->cstat->dsk.rbytes -= pp->cstat->dsk.rbytes; dp->cstat->dsk.wbytes -= pp->cstat->dsk.wbytes; dp->cstat->dsk.rios -= pp->cstat->dsk.rios; dp->cstat->dsk.wios -= pp->cstat->dsk.wios; } if (dp->cstat->dsk.somepres != -1) // defined? dp->cstat->dsk.somepres -= pp->cstat->dsk.somepres; if (dp->cstat->dsk.fullpres != -1) // defined? dp->cstat->dsk.fullpres -= pp->cstat->dsk.fullpres; } } } // Rewind cglist // static void cgrewind(struct cgchainer **cursor) { *cursor = NULL; } // Get next cgchainer from current cglist // static struct cgchainer * cgnext(struct cgchainer **first, struct cgchainer **cursor) { if (! *cursor) *cursor = *first; else *cursor = (*cursor)->next; if (*cursor) return *cursor; else return NULL; } // Wipe current cgroup chain // void cgwipecur(void) { cgwipe(&cgcurfirst, &cgcurlast, &cgcurcursor, NULL); } // Wipe all struct's from cgroup admi // static void cgwipe(struct cgchainer **first, struct cgchainer **last, struct cgchainer **cursor, struct cgchainer **hashlist) { struct cgchainer *cp, *cpn; if (! *first) // empty already? return; // free all chain members // for (cp=*first; cp; cp=cpn) { cpn = cp->next; if (cp->proclist) free(cp->proclist); free(cp->cstat); free(cp); } *first = *last = *cursor = NULL; if (hashlist) memset(hashlist, 0, sizeof(struct cgchainer *) * CGROUPNHASH); } // Create enough memory to copy all cstat structures from // the current list to one contiguous area. // Also create enough memory to copy all pid lists from // the current list to one contiguous area. // Next, create and fill an array of cgchainer structs. // // Returns: number of cgchainer structs // int deviatcgroup(struct cgchainer **cdpp, int *npids) { struct cgchainer *ccp = cgcurfirst; char *allc, *allp, *cp, *pp; // allocate contiguous memory areas for all cstat structs // and all pid lists // allc = calloc(1, cgcursize); allp = malloc(sizeof(pid_t) * cgcurprocs); ptrverify(allc, "Malloc failed for contiguous cstats (%d bytes)\n", cgcursize); ptrverify(allp, "Malloc failed for contiguous pids (%d pids)\n", cgcurprocs); // concatenate all current cstat structs and all pid lists // into the new memory areas // ccp = cgcurfirst; cp = allc; pp = allp; while (ccp) { int cstatlen = ccp->cstat->gen.structlen; int plistlen = ccp->cstat->gen.nprocs * sizeof(pid_t); memcpy(cp, ccp->cstat, cstatlen); cp += cstatlen; if (ccp->proclist) { memcpy(pp, ccp->proclist, plistlen); pp += plistlen; } ccp = ccp->next; } // build array of cgchainer structs and a hash list for the // concatenated cstat structs for the deviation values // cgbuildarray(&cgdevfirst, allc, allp, cgcurnum); // calculate deviation values // cgcalcdeviate(); *cdpp = cgdevfirst; *npids = cgcurprocs; return cgcurnum; } // Create concatenated array of cgchainer structs referring to // proper location in the concatenated cstat structs and // the concatenated pid lists. // void cgbuildarray(struct cgchainer **firstp, char *cstats, char *pids, int ncstats) { struct cgchainer *cdp; int i; *firstp = malloc(sizeof(struct cgchainer) * ncstats); ptrverify(firstp, "Malloc failed for contiguous cgchainers (%d)\n", ncstats); for (cdp=*firstp, i=0; i < ncstats; cdp++, i++) { cdp->next = cdp+1; cdp->hashnext = NULL; cdp->proclist = (pid_t *)pids; pids += sizeof(pid_t) * ((struct cstat *)cstats)->gen.nprocs; cdp->cstat = (struct cstat *)cstats; cstats += ((struct cstat *)cstats)->gen.structlen; } cdp = *firstp + ncstats - 1; cdp->next = NULL; // terminate chain } // Assemble full pathname of cgroup directory (from cgroup top directory) // from the deviation array. // For JSON output, double escape characters (backslashes) can be requested // vi boolean 'escdouble'. // // Returns: malloc'ed string with full path name to be freed later on // #define MAXESCAPES 16 char * cggetpath(struct cgchainer *cdp, struct cgchainer *cdbase, int escdouble) { // calculate path length including slashes (depth) and terminating 0-byte // // in case of double escapes, add a (hopefully) worst-case number of // bytes to the malloc'ed buffer to double the backslashes // // in case of the top directory (without a parent), reserve one extra // byte for the '/' character // int pathlen = cdp->cstat->gen.fullnamelen + cdp->cstat->gen.depth + 1 + (cdp->cstat->gen.sequence ? 0 : 1); char *path = calloc(1, pathlen); char *ps; ptrverify(path, "Malloc failed for concatenated cgpath (%d)\n", pathlen); // not the top directory? // if (cdp->cstat->gen.sequence) { // any other path: place all path components // including preceding slashes // while (cdp->cstat->gen.parentseq != -1) { // determine location of preceding slash // of this specific path component // ps = path + cdp->cstat->gen.fullnamelen - cdp->cstat->gen.namelen + cdp->cstat->gen.depth - 1; // store slash and path component // *ps = '/'; memcpy(ps+1, cdp->cstat->cgname, cdp->cstat->gen.namelen); // climb upwards // cdp = cdbase + cdp->cstat->gen.parentseq; } *(path+pathlen-1) = '\0'; // terminate string } else // exceptional case only for top directory { *path = '/'; *(path+1) = '\0'; // terminate string } // when double escapes are required and at least one backslash // is present, reallocate and convert the assembled path // // room for a worst-case number of backslashes is allocated // to avoid counting backslashes before transferring all bytes // if (escdouble && strchr(path, '\\')) { char *escpath, *pi, *po; pathlen += MAXESCAPES; escpath = malloc(pathlen); ptrverify(escpath, "Malloc failed for escape cgpath (%d)\n", pathlen); pi = path; po = escpath; while (*pi) { *po++ = *pi; if (*pi++ == '\\') // insert additional backslash *po++ = '\\'; if (po - escpath >= pathlen-1) break; // truncate to avoid overflow } *po = 0; free(path); // free original string path = escpath; } return path; } // =========================================================== // name hashing to find cgroup with specific path name // =========================================================== // Calculate hash for directory name. // The 'basehash' contains the hashed value of the // previous path components and the 'offset' is // the offset of this (sub)string in a larger path. // Notice that '/' characters will be ignored during // calculation of the hash value (and ignored in the offset). // static long hashcalc(char *p, long basehash, int offset) { offset += 1; // avoid multiplication by 0 while (*p) { if (*p == '/') { p++; continue; } basehash += *p++ * offset++; } return basehash; } // Add new hash cgroup directory name to hash // static void hashadd(struct cgchainer *hashlist[], struct cgchainer *cp) { long hash = cp->cstat->gen.namehash; cp->hashnext = hashlist[hash&CGROUPMASK]; hashlist[hash&CGROUPMASK] = cp; } // Find directory name in hash based on hashed name value // static struct cgchainer * hashfind(struct cgchainer *hashlist[], long hash) { struct cgchainer *cp; // search hash list // for (cp = hashlist[hash&CGROUPMASK]; cp; cp = cp->hashnext) { if (hash == cp->cstat->gen.namehash) return cp; } return NULL; } // =========================================================== // PID hashing to find the processes that are related to // a particular cgroup // =========================================================== struct pid2tstat { struct pid2tstat *hashnext; struct tstat *tstat; }; #define PIDNHASH 512 // power of 2 #define PIDMASK (PIDNHASH-1) static int compselcpu(const void *, const void *); static int compselmem(const void *, const void *); static int compseldsk(const void *, const void *); // Create a mixed list with cgroups and // processes belonging to those cgroups // // Return: number of entries (= screen lines) in the list // int mergecgrouplist(struct cglinesel **cgroupselp, int newdepth, struct cgchainer **cgchainerp, int ncgroups, struct tstat **tpp, int nprocs, char showorder) { int ic, ip, im, is; struct cglinesel *cgroupsel; struct pid2tstat *pidhash[PIDNHASH], *pidlist, *pidcur; // create a hashlist to find the PIDs in a fast way // if (newdepth == 8 || newdepth == 9) // depth requires processes? { // initialize hash list // memset(pidhash, 0, sizeof pidhash); // create one pid2tstat struct per tstat struct // pidlist = calloc(nprocs, sizeof(struct pid2tstat)); ptrverify(pidlist, "Malloc for pid2tstat structs failed (%d)\n", nprocs); pidcur = pidlist; // build hash list // for (ip=0; ip < nprocs; ip++, tpp++, pidcur++) { int hash = (*tpp)->gen.pid&PIDMASK; pidcur->hashnext = pidhash[hash]; pidcur->tstat = (*tpp); pidhash[hash] = pidcur; } } else { nprocs = 0; // ignore processes } // allocate space for merged list // cgroupsel = malloc(sizeof(struct cglinesel) * (ncgroups+nprocs)); ptrverify(cgroupsel, "Malloc for cglinesel structs failed (%d)\n", ncgroups + nprocs); *cgroupselp = cgroupsel; // fill merged list // for (ic=im=0; ic < ncgroups; ic++) { int cgroupnprocs; // take next cgroup and add it to the merged list // if ( cgroupfilter((*(cgchainerp+ic))->cstat, newdepth, showorder) ) { (cgroupsel+im)->cgp = *(cgchainerp+ic); (cgroupsel+im)->tsp = NULL; im++; } else { // suppress this cgroup // when it is a stub cgroup for this level, // pass the stub to the previous valid entry // of this level // if ( (*(cgchainerp+ic))->stub) { int j; for (j=im-1; j > 0; j--) { int depth = (*(cgchainerp+ic))->cstat->gen.depth; if (depth > (cgroupsel+j)->cgp->cstat->gen.depth) { break; } if (depth == (cgroupsel+j)->cgp->cstat->gen.depth) { (cgroupsel+j)->cgp->stub = 7; break; } else { if (depth < CGRMAXDEPTH) (cgroupsel+j)->cgp->vlinemask &= ~(1 << (depth-1)); } } } continue; } // if relevant, search for processes that belong to this cgroup // and add them to the merged list // if (!nprocs) // ignore processes anyhow? continue; cgroupnprocs = (*(cgchainerp+ic))->cstat->gen.nprocs; if (!cgroupnprocs) // no processes in this cgroup? continue; for (ip=0, is=im; ip < cgroupnprocs; ip++) { int pid = (*(cgchainerp+ic))->proclist[ip]; int hash = pid&PIDMASK; // search for tstat struct relate to this PID via hashlist // for (pidcur=pidhash[hash]; pidcur; pidcur=pidcur->hashnext) { // process tstat found?? // if (pidcur->tstat->gen.pid == pid) { // skip kernel processes in case of level 8 // if (newdepth == 9 || pidcur->tstat->mem.vmem > 0 ) { (cgroupsel+im)->cgp = *(cgchainerp+ic); (cgroupsel+im)->tsp = pidcur->tstat; im++; } break; } } } // sort the list of processes added for this cgroup // if (im-is > 1) { switch (showorder) { case MSORTCPU: qsort(cgroupsel+is, im-is, sizeof(struct cglinesel), compselcpu); break; case MSORTMEM: qsort(cgroupsel+is, im-is, sizeof(struct cglinesel), compselmem); break; case MSORTDSK: qsort(cgroupsel+is, im-is, sizeof(struct cglinesel), compseldsk); break; } } } if (nprocs) free(pidlist); return im; } // Judge if a cgroup should be selected, based on the // current requested directory level (depth) and // based on the fact if unused branches are requested // // returns 0 (false) or 1 (true) // static int cgroupfilter(struct cstat *csp, int newdepth, char showorder) { // skip lower level of cgroups when required // by entering key 2 till 8 (9=max) // if (newdepth < 9 && newdepth <= csp->gen.depth) return 0; // skip this level and lower levels if // no processes are assigned at all // if (deviatonly && showorder != MSORTMEM && csp->gen.nprocs == 0 && csp->gen.procsbelow == 0 ) return 0; return 1; } // Funtion to be called by qsort() to compare the // CPU time consumption of two processes in a cgroup. // static int compselcpu(const void *a, const void *b) { count_t acpu = ((struct cglinesel *)a)->tsp->cpu.utime + ((struct cglinesel *)a)->tsp->cpu.stime; count_t bcpu = ((struct cglinesel *)b)->tsp->cpu.utime + ((struct cglinesel *)b)->tsp->cpu.stime; if (acpu < bcpu) return 1; if (acpu > bcpu) return -1; return 0; } // Funtion to be called by qsort() to compare the // memory consumption of two processes in a cgroup. // static int compselmem(const void *a, const void *b) { count_t amem = ((struct cglinesel *)a)->tsp->mem.rmem; count_t bmem = ((struct cglinesel *)b)->tsp->mem.rmem; if (amem < bmem) return 1; if (amem > bmem) return -1; return 0; } // Funtion to be called by qsort() to compare the // disk activity of two processes in a cgroup. // static int compseldsk(const void *a, const void *b) { count_t adisk = ((struct cglinesel *)a)->tsp->dsk.rsz + ((struct cglinesel *)a)->tsp->dsk.wsz; count_t bdisk = ((struct cglinesel *)b)->tsp->dsk.rsz + ((struct cglinesel *)b)->tsp->dsk.wsz; if (adisk < bdisk) return 1; if (adisk > bdisk) return -1; return 0; } // Generate a list of pointers to cgchainer struct, sorted on // specific resource consumption in the cgroup // struct cgsorter { struct cgchainer *cgthis; struct cgsorter *cgsame; struct cgsorter *cgchild; struct cgsorter **sortlist; count_t sortval; int nrchild; }; static struct cgsorter cgroot; // struct for root with depth zero static struct cgchainer *sortlevel(int, struct cgsorter *, struct cgchainer *, int, char); static struct cgchainer **mergelevels(struct cgsorter *, int); static int mergelevel(struct cgsorter *, struct cgchainer **, unsigned long); static void createsortlist(struct cgsorter *); static int compsortval(const void *, const void *); // Main function to create a list of pointers to cgchainer structs, // in the order of resource consumption while maintaining // the proper cgroup directory structure. // // Return value: list with sorted pointers to cgchainer structs // struct cgchainer ** cgsort(struct cgchainer *cgphys, int cgsize, char showorder) { struct cgchainer **cgsorted; // reinitialize the root cgsorter struct and fill // the pointer to the root cgchainer // memset(&cgroot, 0, sizeof cgroot); cgroot.cgthis = cgphys; // build a tree structure reflecting the directories of the cgroups // (skip the first entry representing the root cgroup) // sortlevel(1, &cgroot, &cgphys[1], cgsize-1, showorder); // merge all sorted cgchainer struct pointers into one // contiguous list // cgsorted = mergelevels(&cgroot, cgsize); return cgsorted; } // Create a tree structure of cgsorter structs (by nested calls). // A cgsorter struct will be created per cgchainer struct, // with the following members: // // cgthis pointer to corresponding cgchainer struct // cgsame pointer to next cgsorter struct on same directory level // cgchild pointer to first cgsorter struct on lower directory level // sortlist pointer to list of pointers to child cgsorter structs // (same pointers as chained via cgchild) to be sorted // sortval sort value (e.g. consumed CPU time or used memory) // nrchild number of direct child members // static struct cgchainer * sortlevel(int curlevel, struct cgsorter *cgparent, struct cgchainer *cgp, int cgsize, char showorder) { int newlevel, cgleft = cgsize; struct cgchainer *cgc = cgp; struct cgsorter *cgs = NULL; // as long as cgchainer structs are left... // while (cgleft > 0) { newlevel = cgc->cstat->gen.depth; // higher level in directory tree? // - create and sort the cgchainer pointers in order of resource utilization // - return to caller to continue with higher level // if (newlevel < curlevel) { createsortlist(cgparent); return cgc; // leave this level } // same directory level: chain to same-level list // which is the child list of the higher level that called us // if (newlevel == curlevel) { // malloc and fill new cgsorter struct // cgs = malloc(sizeof(struct cgsorter)); ptrverify(cgs, "Malloc failed for cgsorter struct\n"); cgs->cgthis = cgc; cgs->cgsame = cgparent->cgchild; cgs->cgchild = 0; cgs->nrchild = 0; switch (showorder) { case MSORTCPU: cgs->sortval = cgc->cstat->cpu.utime + cgc->cstat->cpu.stime; break; case MSORTMEM: if (cgc->cstat->mem.current > 0) { cgs->sortval = cgc->cstat->mem.current; } else { cgs->sortval = cgc->cstat->mem.anon + cgc->cstat->mem.file + cgc->cstat->mem.kernel + cgc->cstat->mem.shmem; } break; case MSORTDSK: cgs->sortval = cgc->cstat->dsk.rbytes + cgc->cstat->dsk.wbytes; break; default: cgs->sortval = 0; // no sorting } // chain cgsorter struct to this level // cgparent->cgchild = cgs; cgparent->nrchild++; cgc++; cgleft--; continue; } // newlevel > curlevel? // recursively call sortlevel() for each new // directory level underneath // cgc = sortlevel(newlevel, cgs, cgc, cgleft, showorder); cgleft = cgsize - (cgc - cgp); } createsortlist(cgparent); return cgc; } // Create a list of pointers to the cgchainer structs // on the current level and sort it on resource consumption // static void createsortlist(struct cgsorter *cgparent) { int i; struct cgsorter *cgs; if (cgparent->nrchild == 1) // no sorting needed with one child { cgparent->sortlist = NULL; } else // sorting needed by creating sortlist { cgparent->sortlist = calloc(cgparent->nrchild, sizeof(struct cgsorter *)); ptrverify(cgparent->sortlist, "Malloc failed for cgsorter list\n"); // fill elements of sortlist with pointers to child cgsorters // for (i=0, cgs=cgparent->cgchild; i < cgparent->nrchild; i++, cgs = cgs->cgsame) { *((cgparent->sortlist)+i) = cgs; } // sort list on sort value // qsort(cgparent->sortlist, cgparent->nrchild, sizeof(struct cgsorter *), compsortval); } } // Function to be called by qsort() to compare // the sortvalue in two cgsorter structs // static int compsortval(const void *a, const void *b) { struct cgsorter *cga = *(struct cgsorter **)a; struct cgsorter *cgb = *(struct cgsorter **)b; if (cga->sortval < cgb->sortval) return 1; if (cga->sortval > cgb->sortval) return -1; return 0; } // Gather all sorted cgsorter structs from the entire tree // and create one array of cgchainer pointers in sorted order. // static struct cgchainer ** mergelevels(struct cgsorter *cgrootp, int cgsize) { struct cgchainer **cgpp; unsigned long vlinemask = 0; // allocate the list of pointers to be returned // cgpp = malloc(sizeof(struct cgchainer *) * cgsize); ptrverify(cgpp, "Malloc failed for cgchainer ptr list (%d)\n", cgsize); // fill the first entry with the root cgchainer struct pointer // *cgpp = cgrootp->cgthis; (*cgpp)->stub = 1; // no more entries on this level (*cgpp)->vlinemask = vlinemask; mergelevel(cgrootp, cgpp+1, vlinemask); return cgpp; } // Gather all cgsorter structs from one directory level. // To be called in a nested way for each level underneath. // static int mergelevel(struct cgsorter *cgparent, struct cgchainer **cgpp, unsigned long vlinemask) { struct cgsorter *cgs, *cgsave; int i, j, depth = cgparent->cgthis->cstat->gen.depth; switch (cgparent->nrchild) { case 0: // should never occur.... j = 0; break; case 1: // in case of one child no sortlist has been created *cgpp = cgparent->cgchild->cgthis; (*cgpp)->stub = 1; // no more entries on this level if (depth < CGRMAXDEPTH) vlinemask &= ~(1 << depth); (*cgpp)->vlinemask = vlinemask; j = 1; if (cgparent->cgchild->nrchild) // merge descendants on lower level? j += mergelevel(cgparent->cgchild, cgpp+1, vlinemask); free(cgparent->cgchild); break; default: for (i=0, j=0; i < cgparent->nrchild; i++, j++) { cgs = *((cgparent->sortlist)+i); *(cgpp+j) = cgs->cgthis; if (i == cgparent->nrchild -1) // last on this level? { (*(cgpp+j))->stub = 1; // no more entries on this level if (depth < CGRMAXDEPTH) vlinemask &= ~(1 << depth); (*(cgpp+j))->vlinemask = vlinemask; } else { (*(cgpp+j))->stub = 0; // more entries on this level if (depth < CGRMAXDEPTH) vlinemask |= 1 << depth; (*(cgpp+j))->vlinemask = vlinemask; } if (cgs->nrchild) // merge descendants on lower level? j += mergelevel(cgs, cgpp+j+1, vlinemask); } // for all cgroups of this level merged: free malloced areas // free(cgparent->sortlist); for (cgs=cgparent->cgchild; cgs; cgs=cgsave) { cgsave = cgs->cgsame; free(cgs); } } return j; } // =========================================================== // PID hashing to find the cgroup that is related to // a particular process // =========================================================== struct pid2cgchainer { struct pid2cgchainer *hashnext; pid_t pid; int cgindex; // cgchainer index }; // For every tstat struct, fill the reference (index) to // the related cgroup, represented by the cgchainer struct // void cgfillref(struct devtstat *devtstat, struct cgchainer *devchain, int ncgroups, int npids) { int ic, ip, it, hash; pid_t pid; struct pid2cgchainer *pidhash[PIDNHASH], *pidlist, *pidcur; struct cgchainer *cp; struct tstat *tp = devtstat->taskall; // create a hashlist to find the PIDs in a fast way // initialize hash list // memset(pidhash, 0, sizeof pidhash); // create one pid2cgchainer struct per cgroup pid // pidlist = calloc(npids, sizeof(struct pid2cgchainer)); ptrverify(pidlist, "Malloc for pid2cgchainer structs failed (%d)\n", npids); // build hash list to find right cgchainer (cgroup) for a PID // for (ic=0, cp=devchain, pidcur=pidlist; ic < ncgroups; ic++, cp++) { for (ip=0; ip < cp->cstat->gen.nprocs; ip++, pidcur++) { pid = cp->proclist[ip]; hash = pid & PIDMASK; pidcur->hashnext = pidhash[hash]; pidcur->pid = pid; pidcur->cgindex = ic; pidhash[hash] = pidcur; } } // connect every tstat struct to the concerning cgchainer // by filling the index // for (it=0; it < devtstat->ntaskall; it++, tp++) { tp->gen.cgroupix = -1; if (! tp->gen.isproc) // skip threads continue; pid = tp->gen.pid; hash = pid & PIDMASK; // search for cgchainer index related to this PID via hashlist // for (pidcur=pidhash[hash]; pidcur; pidcur=pidcur->hashnext) { if (pidcur->pid == pid) { tp->gen.cgroupix = pidcur->cgindex; break; } } } free(pidlist); } atop-2.11.1/acctproc.c0000644000203100020310000006562214771753566014041 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.11.1/45atoppm0000755000203100020310000000224214771753566013463 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.11.1/atopgpu.service0000644000203100020310000000031214771753566015121 0ustar gerlofgerlof[Unit] Description=Atop GPU stats daemon Documentation=man:atopgpud(8) Before=atop.service [Service] ExecStart=/usr/sbin/atopgpud Type=oneshot RemainAfterExit=yes [Install] WantedBy=multi-user.target atop-2.11.1/atopsar.c0000644000203100020310000022210314771753566013701 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 "cgroups.h" #include "gpucom.h" #define MAXFL 64 /* maximum number of command-line flags */ /* ** color definitions */ #define COLSETHEAD "\033[30;43m" /* black on yellow */ #define COLSETMED "\033[36m" /* cyan */ #define COLSETHIGH "\033[31m" /* red */ #define COLRESET "\033[00m" /* reset any color */ /* ** miscellaneous values */ static unsigned int nsamples = 9999999; static char stampalways; static char usemarkers; static char allresources; static int numreports; static time_t saved_begintime; static unsigned int repeathead = 9999999; static unsigned int summarycnt = 1; static char *datemsg = "-------------------------- analysis " "date: %s --------------------------\n"; /* ** structure definition for print-functions */ struct pridef { char wanted; /* selected option (boolean) */ char *cntcat; /* used categories of counters */ char flag; /* flag on command line */ void (*prihead)(int, int, int); /* print header of list */ int (*priline)(struct sstat *, struct tstat *, struct tstat **, int, time_t, time_t, time_t, int, int, int, char *, int, int, int, int, int, int, int); /* print counters per line (excl. time) */ char *about; /* statistics about what */ }; extern struct pridef pridef[]; /* table of print-functions */ extern int pricnt; /* total number of print-functions */ static time_t daylim; /* last second of day in epoch */ static int prinow; /* current selection */ static char coloron; /* boolean: colors active now */ /* ** local prototypes */ static void engine(void); static void pratopsaruse(char *); static void reportlive(time_t, int, struct sstat *); static char reportraw (time_t, int, struct devtstat *, struct sstat *, struct cgchainer *, int, int, int, unsigned int, char); static void reportheader(struct utsname *, time_t); static time_t daylimit(time_t); int atopsar(int argc, char *argv[]) { register int i, c; struct rlimit rlim; char *p, *flaglist; usecolors = 't'; /* ** interpret command-line arguments & flags */ if (argc > 1) { /* ** gather all flags for the print-functions */ flaglist = malloc(pricnt+32); if (!flaglist) ptrverify(flaglist, "Malloc failed for %d flags\n", pricnt+32); for (i=0; i < pricnt; i++) flaglist[i] = pridef[i].flag; flaglist[i] = 0; /* ** add generic flags */ strcat(flaglist, "b:e:SxCMHr:R:aA"); while ((c=getopt(argc, argv, flaglist)) != EOF) { switch (c) { case '?': /* usage wanted ? */ pratopsaruse(argv[0]); break; case 'b': /* begin time ? */ if ( !getbranchtime(optarg, &begintime) ) pratopsaruse(argv[0]); saved_begintime = begintime; break; case 'e': /* end time ? */ if ( !getbranchtime(optarg, &endtime) ) pratopsaruse(argv[0]); break; case 'r': /* reading of file data ? */ 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, struct cgchainer *devchain, int ncgroups, int npids, int nexit, unsigned int noverflow, char flags) { static char firstcall = 1; char timebuf[16], datebuf[16]; unsigned int rv; static unsigned int curline, headline, sampsum, totalsec, totalexit, lastnpres, lastntrun, lastntslpi, lastntslpu, lastntidle, lastnzomb; static time_t lasttime; static struct sstat totsyst; /* ** is this function still wanted? */ if ( ! pridef[prinow].wanted ) return '\0'; /* ** when this is first call to this function, ** print overall header with system information */ if (firstcall) { reportheader(&utsname, time(0)); firstcall = 0; } /* ** verify if we have passed midnight */ if (curtime > daylim) { printf(datemsg, convdate(curtime, datebuf)); daylim = daylimit(curtime); curline++; } /* ** when this is the first record for a new report, ** initialize various variables */ if (sampcnt == 1) { /* ** initialize variables for new report */ pretime = curtime; curline = 1; headline = 0; sampsum = summarycnt + 1; totalsec = 0; totalexit = 0; memset(&totsyst, 0, sizeof totsyst); return '\0'; } /* ** check if a (new) report header needs to be printed */ if (curline >= headline) { headline = curline + repeathead; /* ** print header-line */ printf("\n"); if (usecolors) printf(COLSETHEAD); printf("%s ", convtime(pretime, timebuf)); (pridef[prinow].prihead)(osvers, osrel, ossub); if (usecolors) printf(COLRESET); printf("\n"); curline+=2; } /* ** when current record contains log-restart indicator, ** print message and reinitialize variables */ if (flags & RRBOOT) { /* ** when accumulating counters, print results upto ** the *previous* record */ if (summarycnt > 1 && sampcnt <= sampsum && totalsec) { printf("%s ", convtime(lasttime, timebuf)); rv = (pridef[prinow].priline)(&totsyst, (struct tstat *)0, 0, 0, totalsec, totalsec*hertz, hertz, osvers, osrel, ossub, stampalways ? timebuf : " ", lastnpres, lastntrun, lastntslpi, lastntslpu, lastntidle, totalexit, lastnzomb); if (rv == 0) { curline++; pridef[prinow].wanted = 0; /* not call again */ if (--numreports == 0) cleanstop(1); } else { curline += rv; } } /* ** print restart-line in case of logging restarted */ printf("%s ", convtime(curtime, timebuf)); printf("......................... logging restarted " ".........................\n"); pretime = curtime; curline++; /* ** reinitialize variables */ sampsum = summarycnt + sampcnt; totalsec = 0; totalexit = 0; memset(&totsyst, 0, sizeof totsyst); return '\0'; } /* ** when no accumulation is required, ** just print current sample */ if (summarycnt == 1) { printf("%s ", convtime(curtime, timebuf)); rv = (pridef[prinow].priline) (sstat, devtstat->taskall, devtstat->procall, devtstat->nprocall, numsecs, numsecs*hertz, hertz, osvers, osrel, ossub, stampalways ? timebuf : " ", devtstat->ntaskall, devtstat->totrun, devtstat->totslpi, devtstat->totslpu, devtstat->totidle, nexit, devtstat->totzombie); if (rv == 0) { curline++; pridef[prinow].wanted = 0; /* not call again */ if (--numreports == 0) cleanstop(1); } else { curline += rv; } } else /* accumulation is required */ { char *cp = pridef[prinow].cntcat; /* ** maintain totals per category */ while (*cp) { totalsyst(*cp, sstat, &totsyst); cp++; } totalsec += numsecs; totalexit += nexit; /* ** remember some values in case the next record ** contains the log-restart indicator */ lasttime = curtime; lastnpres = devtstat->nprocall; lastntrun = devtstat->totrun; lastntslpi = devtstat->totslpi; lastntslpu = devtstat->totslpu; lastntidle = devtstat->totidle; lastnzomb = devtstat->totzombie; /* ** print line only if needed */ if (sampcnt >= sampsum || ( (flags&RRLAST) && totalsec) ) { /* ** print output line for required report */ printf("%s ", convtime(curtime, timebuf)); rv = (pridef[prinow].priline) (&totsyst, (struct tstat *)0, 0, 0, totalsec, totalsec*hertz, hertz, osvers, osrel, ossub, stampalways ? timebuf : " ", devtstat->ntaskall, devtstat->totrun, devtstat->totslpi, devtstat->totslpu, devtstat->totidle, totalexit, devtstat->totzombie); if (rv == 0) { curline++; pridef[prinow].wanted = 0; /* not call again */ if (--numreports == 0) cleanstop(1); } else { curline += rv; } sampsum = summarycnt + sampcnt; totalsec = 0; totalexit = 0; memset(&totsyst, 0, sizeof totsyst); } else { rv = 1; } } if (!rv) { /* ** print for line has failed; ** never call this function again */ pridef[prinow].wanted = 0; if (--numreports == 0) cleanstop(1); } pretime = curtime; return '\0'; } /* ** print overall header */ static void reportheader(struct utsname *uname, time_t mtime) { char cdate[16]; printf("\n%s %s %s %s %s\n\n", uname->nodename, uname->release, uname->version, uname->machine, convdate(mtime, cdate)); } /* ** print usage of atopsar command */ void pratopsaruse(char *myname) { int i; fprintf(stderr, "Usage: %s [-flags] [-r file|-|date|y...] [-R cnt] [-b time] [-e time]\n", myname); fprintf(stderr, "\t\tor\n"); fprintf(stderr, "Usage: %s [-flags] interval [samples]\n", myname); fprintf(stderr, "\n"); fprintf(stderr, "\tToday's atop logfile is used by default!\n"); fprintf(stderr, "\n"); fprintf(stderr, "\tGeneric flags:\n"); fprintf(stderr, "\t -r read statistical data from specific atop logfile\n"); fprintf(stderr, "\t (pathname, - for stdin, date in format YYYYMMDD, or y[y..])\n"); fprintf(stderr, "\t -R summarize samples into one sample\n"); fprintf(stderr, "\t -b begin showing data from specified time as [YYYYMMDD]hhmm[ss]\n"); fprintf(stderr, "\t -e finish showing data after specified time as [YYYYMMDD]hhmm[ss]\n"); fprintf(stderr, "\t -S print timestamp on every line in case of more " "resources\n"); fprintf(stderr, "\t -x never use colors to indicate overload" " (default: only if tty)\n"); fprintf(stderr, "\t -C always use colors to indicate overload" " (default: only if tty)\n"); fprintf(stderr, "\t -M use markers to indicate overload " "(* = critical, + = almost)\n"); fprintf(stderr, "\t -H repeat report headers " "(in case of tty: depending on screen lines)\n"); fprintf(stderr, "\t -a print all resources, even when inactive\n"); fprintf(stderr, "\n"); fprintf(stderr, "\tSpecific flags to select reports:\n"); fprintf(stderr, "\t -A print all available reports\n"); for (i=0; i < pricnt; i++) fprintf(stderr, "\t -%c %s\n", pridef[i].flag, pridef[i].about); fprintf(stderr, "\n"); fprintf(stderr, "Please refer to the man-page of 'atopsar' " "for more details.\n"); cleanstop(1); } /* ** calculate the epoch-value for the last second ** of the day given a certain epoch */ static time_t daylimit(time_t timval) { struct tm *tp = localtime(&timval); tp->tm_hour = 23; tp->tm_min = 59; tp->tm_sec = 59; return mktime(tp); } /* ** function to be called before printing a statistics line ** to switch on colors when necessary */ static void preprint(unsigned int badness) { if (usecolors) { if (badness >= 100) { coloron = 1; printf(COLSETHIGH); } else { if (almostcrit && badness >= almostcrit) { coloron = 1; printf(COLSETMED); } } } } /* ** function to be called after printing a statistics line ** to switch off colors when necessary and print a line feed */ static void postprint(unsigned int badness) { if (coloron) { coloron = 0; printf(COLRESET); } if (usemarkers) { if (badness >= 100) { printf(" *"); } else { if (almostcrit && badness >= almostcrit) printf(" +"); } } printf("\n"); } /* ** function to handle the default flags for atopsar as ** read from the files ~/.atoprc and /etc/atoprc */ void do_atopsarflags(char *name, char *val) { int i, j; for (i=0; val[i]; i++) { switch (val[i]) { case '-': break; case 'S': /* timestamp on every line */ stampalways = 1; break; case 'x': /* always colors for overload */ usecolors = 0; break; case 'C': /* always colors for overload */ usecolors = 'a'; break; case 'M': /* markers for overload */ usemarkers = 1; break; case 'H': /* repeat headers */ repeathead = 23; /* define default */ if (isatty(fileno(stdout))) { struct winsize wsz; if ( ioctl(1, TIOCGWINSZ, &wsz) != -1) repeathead = wsz.ws_row - 1; } break; case 'a': /* every interval all units */ allresources = 1; break; case 'A': /* all reports wanted ? */ for (j=0; j < pricnt; j++) pridef[j].wanted = 1; numreports = pricnt; break; default: /* gather report-flags */ for (j=0; j < pricnt; j++) { if (pridef[j].flag == val[i] && pridef[j].wanted == 0 ) { pridef[j].wanted = 1; numreports++; break; } } } } } /**************************************************************************/ /* Functions to print statistics */ /**************************************************************************/ /* ** CPU statistics */ static void cpuhead(int osvers, int osrel, int ossub) { printf("cpu %%usr %%nice %%sys %%irq %%softirq %%steal %%guest " " %%wait %%idle _cpu_"); } static int cpuline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { register int i, nlines = 1; count_t cputot; unsigned int badness; /* ** print overall statistics */ cputot = ss->cpu.all.stime + ss->cpu.all.utime + ss->cpu.all.ntime + ss->cpu.all.itime + ss->cpu.all.wtime + ss->cpu.all.Itime + ss->cpu.all.Stime + ss->cpu.all.steal; if (cputot == 0) cputot = 1; /* avoid divide-by-zero */ if (cpubadness) badness = ((cputot - ss->cpu.all.itime - ss->cpu.all.wtime) * 100.0 / cputot) * 100 / cpubadness; else badness = 0; preprint(badness); printf("all %5.0lf %5.0lf %4.0lf %4.0lf %8.0lf %7.0f %6.0f %6.0lf %5.0lf", (double) (ss->cpu.all.utime * 100.0) / cputot * ss->cpu.nrcpu, (double) (ss->cpu.all.ntime * 100.0) / cputot * ss->cpu.nrcpu, (double) (ss->cpu.all.stime * 100.0) / cputot * ss->cpu.nrcpu, (double) (ss->cpu.all.Itime * 100.0) / cputot * ss->cpu.nrcpu, (double) (ss->cpu.all.Stime * 100.0) / cputot * ss->cpu.nrcpu, (double) (ss->cpu.all.steal * 100.0) / cputot * ss->cpu.nrcpu, (double) (ss->cpu.all.guest * 100.0) / cputot * ss->cpu.nrcpu, (double) (ss->cpu.all.wtime * 100.0) / cputot * ss->cpu.nrcpu, (double) (ss->cpu.all.itime * 100.0) / cputot * ss->cpu.nrcpu); postprint(badness); /* ** print per-cpu statistics */ if (ss->cpu.nrcpu > 1) { for (i=0; i < ss->cpu.nrcpu; i++) { cputot = ss->cpu.cpu[i].stime + ss->cpu.cpu[i].utime + ss->cpu.cpu[i].ntime + ss->cpu.cpu[i].itime + ss->cpu.cpu[i].wtime + ss->cpu.cpu[i].Itime + ss->cpu.cpu[i].Stime + ss->cpu.cpu[i].steal; if (cputot == 0) cputot = 1; /* avoid divide-by-zero */ if (cpubadness) badness = ((cputot - ss->cpu.cpu[i].itime - ss->cpu.cpu[i].wtime) * 100.0 / cputot) * 100 / cpubadness; else badness = 0; printf("%s ", tstamp); preprint(badness); printf("%4d %5.0lf %5.0lf %4.0lf %4.0lf %8.0lf " "%7.0f %6.0lf %6.0lf %5.0lf", i, (double)(ss->cpu.cpu[i].utime * 100.0) / cputot, (double)(ss->cpu.cpu[i].ntime * 100.0) / cputot, (double)(ss->cpu.cpu[i].stime * 100.0) / cputot, (double)(ss->cpu.cpu[i].Itime * 100.0) / cputot, (double)(ss->cpu.cpu[i].Stime * 100.0) / cputot, (double)(ss->cpu.cpu[i].steal * 100.0) / cputot, (double)(ss->cpu.cpu[i].guest * 100.0) / cputot, (double)(ss->cpu.cpu[i].wtime * 100.0) / cputot, (double)(ss->cpu.cpu[i].itime * 100.0) / cputot); postprint(badness); nlines++; } } return nlines; } /* ** GPU statistics */ static void gpuhead(int osvers, int osrel, int ossub) { printf(" busaddr gpubusy membusy memocc memtot memuse gputype" " _gpu_"); } static int gpuline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { static char firstcall = 1; register long i, nlines = 0; char fmt1[16], fmt2[16]; count_t avgmemuse; for (i=0; i < ss->gpu.nrgpus; i++) /* per GPU */ { /* ** determine whether or not the GPU has been active ** during interval */ int wasactive; wasactive = ss->gpu.gpu[i].gpuperccum + ss->gpu.gpu[i].memperccum; if (wasactive == -2) // metrics not available? wasactive = 0; if (ss->gpu.gpu[i].samples == 0) avgmemuse = ss->gpu.gpu[i].memusenow; else avgmemuse = ss->gpu.gpu[i].memusecum / ss->gpu.gpu[i].samples; // memusage > 512 MiB (rather arbitrary)? // if (avgmemuse > 512*1024) wasactive = 1; /* ** print for the first sample all GPUs that are found; ** afterwards print only info about the GPUs ** that were really active during the interval */ if (!firstcall && !allresources && !wasactive) continue; if (nlines++) printf("%s ", tstamp); if (ss->gpu.gpu[i].samples == 0) ss->gpu.gpu[i].samples = 1; if (ss->gpu.gpu[i].gpuperccum == -1) strcpy(fmt1, "N/A"); else snprintf(fmt1, sizeof fmt1, "%lld%%", ss->gpu.gpu[i].gpuperccum / ss->gpu.gpu[i].samples); if (ss->gpu.gpu[i].memperccum == -1) strcpy(fmt2, "N/A"); else snprintf(fmt2, sizeof fmt2, "%lld%%", ss->gpu.gpu[i].memperccum / ss->gpu.gpu[i].samples); if (ss->gpu.gpu[i].memtotnow == 0) ss->gpu.gpu[i].memtotnow = 1; printf("%2ld/%9.9s %7s %7s %5lld%% %5lldM %5lldM %s\n", i, ss->gpu.gpu[i].busid, fmt1, fmt2, ss->gpu.gpu[i].memusenow*100/ss->gpu.gpu[i].memtotnow, ss->gpu.gpu[i].memtotnow / 1024, ss->gpu.gpu[i].memusenow / 1024, ss->gpu.gpu[i].type); } if (nlines == 0) { printf("\n"); nlines++; } firstcall = 0; return nlines; } /* ** other processor statistics */ static void prochead(int osvers, int osrel, int ossub) { printf("pswch/s devintr/s clones/s loadavg1 loadavg5 loadavg15 " " _load_"); } static int procline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { printf("%7.0lf %9.0lf %9.2lf %8.2lf %8.2lf %8.2lf\n", (double)ss->cpu.csw / deltasec, (double)ss->cpu.devint / deltasec, (double)ss->cpu.nprocs / deltasec, ss->cpu.lavg1, ss->cpu.lavg5, ss->cpu.lavg15); return 1; } /* ** process statistics */ static void taskhead(int osvers, int osrel, int ossub) { printf("clones/s pexit/s curproc curzomb trun tslpi tslpu tidle " "_procthr_"); } static int taskline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { if (ppres == 0) { printf("report not available for live measurements.....\n"); return 0; } if (ts) /* process statistics available */ { printf("%8.2lf %7.2lf %7d %7d %6d %6d %5d %5d\n", (double)ss->cpu.nprocs / deltasec, (double)pexit / deltasec, nactproc-pexit, pzombie, ntrun, ntslpi, ntslpu, ntidle); } else { printf("%8.2lf %7.2lf %7d %7d\n", (double)ss->cpu.nprocs / deltasec, (double)pexit / deltasec, nactproc-pexit, pzombie); } return 1; } /* ** memory- & swap-usage */ static void memhead(int osvers, int osrel, int ossub) { printf("memtotal memfree buffers cached dirty slabmem" " swptotal swpfree _mem_" ); } static int memline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { unsigned int mbadness, sbadness; if (membadness) mbadness = ((ss->mem.physmem - ss->mem.freemem - ss->mem.cachemem - ss->mem.buffermem + ss->mem.shmem) * 100.0 / ss->mem.physmem) * 100 / membadness; else mbadness = 0; if (swpbadness) sbadness = ((ss->mem.totswap - ss->mem.freeswap) * 100.0 / ss->mem.totswap) * 100 / swpbadness; else sbadness = 0; preprint(mbadness >= sbadness ? mbadness : sbadness); printf("%7lldM %6lldM %6lldM %5lldM %4lldM %6lldM %7lldM %6lldM", ss->mem.physmem * (pagesize / 1024) /1024, ss->mem.freemem * (pagesize / 1024) /1024, ss->mem.buffermem * (pagesize / 1024) /1024, ss->mem.cachemem * (pagesize / 1024) /1024, ss->mem.cachedrt * (pagesize / 1024) /1024, ss->mem.slabmem * (pagesize / 1024) /1024, ss->mem.totswap * (pagesize / 1024) /1024, ss->mem.freeswap * (pagesize / 1024) /1024); postprint(mbadness >= sbadness ? mbadness : sbadness); return 1; } /* ** swapping statistics */ static void swaphead(int osvers, int osrel, int ossub) { printf("pagescan/s swapin/s swapout/s oomkill" " commitspc commitlim _swap_"); } static int swapline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { unsigned int badness; if (membadness) badness = (ss->mem.swouts / deltasec * pagbadness) * 100 / membadness; else badness = 0; /* ** take care that this line is anyhow colored for ** 'almost critical' in case of swapouts > 1 per second */ if (ss->mem.swouts / deltasec > 0 && pagbadness && almostcrit && badness < almostcrit) badness = almostcrit; if (ss->mem.commitlim && ss->mem.committed > ss->mem.commitlim) badness = 100; /* force colored output */ preprint(badness); printf("%10.2lf %9.2lf %9.2lf %7lld %9lluM %9lluM", (double)ss->mem.pgscans / deltasec, (double)ss->mem.swins / deltasec, (double)ss->mem.swouts / deltasec, ss->mem.oomkills, ss->mem.committed * (pagesize / 1024) / 1024, ss->mem.commitlim * (pagesize / 1024) / 1024); postprint(badness); return 1; } /* ** PSI statistics */ static void psihead(int osvers, int osrel, int ossub) { printf("cpusome memsome memfull iosome iofull"); } static int psiline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { // calculate pressure percentages for entire interval unsigned int csperc = ss->psi.cpusome.total/(deltatic*10000/hz); unsigned int msperc = ss->psi.memsome.total/(deltatic*10000/hz); unsigned int mfperc = ss->psi.memfull.total/(deltatic*10000/hz); unsigned int isperc = ss->psi.iosome.total /(deltatic*10000/hz); unsigned int ifperc = ss->psi.iofull.total /(deltatic*10000/hz); unsigned int badness = 0; if (!ss->psi.present) { printf("no PSI stats available for this interval...\n"); return 1; } // correct percentages if needed if (csperc > 100) csperc = 100; if (msperc > 100) msperc = 100; if (mfperc > 100) mfperc = 100; if (isperc > 100) isperc = 100; if (ifperc > 100) ifperc = 100; // consider a 'some' percentage > 0 as almost critical // (I/O full tends to increase rapidly as well) if (csperc || msperc || isperc || ifperc) badness = 80; // consider a memory 'full' percentage > 0 as critical if (mfperc) badness = 100; // show results preprint(badness); printf(" %3u%% %3u%% %3u%% %3u%% %3u%%", csperc, msperc, mfperc, isperc, ifperc); postprint(badness); return 1; } /* ** disk statistics */ static void lvmhead(int osvers, int osrel, int ossub) { printf("disk busy read/s KB/read " "writ/s KB/writ avque avserv _lvm_"); } static void mddhead(int osvers, int osrel, int ossub) { printf("disk busy read/s KB/read " "writ/s KB/writ avque avserv _mdd_"); } static void dskhead(int osvers, int osrel, int ossub) { printf("disk busy read/s KB/read " "writ/s KB/writ avque avserv _dsk_"); } static int gendskline(struct sstat *ss, char *tstamp, char selector) { static char firstcall = 1; register int i, nlines = 0, nunit = 0; count_t mstot, iotot; struct perdsk *dp; unsigned int badness; switch (selector) { case 'l': dp = ss->dsk.lvm; nunit = ss->dsk.nlvm; break; case 'm': dp = ss->dsk.mdd; nunit = ss->dsk.nmdd; break; case 'd': dp = ss->dsk.dsk; nunit = ss->dsk.ndsk; break; default: return 0; } mstot = (ss->cpu.all.stime + ss->cpu.all.utime + ss->cpu.all.ntime + ss->cpu.all.itime + ss->cpu.all.wtime + ss->cpu.all.Itime + ss->cpu.all.Stime + ss->cpu.all.steal ) * (count_t)1000 / hertz / ss->cpu.nrcpu; for (i=0; i < nunit; i++, dp++) { char *pn; int len; iotot = dp->nread + dp->nwrite + (dp->ndisc != -1 ? dp->ndisc : 0); if (iotot == 0 && !firstcall && !allresources) continue; /* no activity on this disk */ /* ** disk was active during last interval; print info */ if (nlines++) printf("%s ", tstamp); if (dskbadness) badness = (dp->io_ms * 100.0 / mstot) * 100/dskbadness; else badness = 0; preprint(badness); if ( (len = strlen(dp->name)) > 14) pn = dp->name + len - 14; else pn = dp->name; printf("%-14s %3.0lf%% %6.1lf %7.1lf %7.1lf %7.1lf " "%5.1lf %9.5lf ms", pn, mstot ? (double)dp->io_ms * 100.0 / mstot : 0.0, mstot ? (double)dp->nread * 1000.0 / mstot : 0.0, dp->nread ? (double)dp->nrsect / dp->nread / 2.0 : 0.0, mstot ? (double)dp->nwrite * 1000.0 / mstot : 0.0, dp->nwrite ? (double)dp->nwsect / dp->nwrite / 2.0 : 0.0, dp->io_ms ? (double)dp->avque / dp->io_ms : 0.0, iotot ? (double)dp->io_ms / iotot : 0.0); postprint(badness); } if (nlines == 0) { printf("\n"); nlines++; } firstcall = 0; return nlines; } static int lvmline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { return gendskline(ss, tstamp, 'l'); } static int mddline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { return gendskline(ss, tstamp, 'm'); } static int dskline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { return gendskline(ss, tstamp, 'd'); } /* ** NFS client statistics */ static void nfmhead(int osvers, int osrel, int ossub) { printf("mounted_device physread/s physwrit/s" " _nfm_"); } static int nfmline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { static char firstcall = 1; register long i, nlines = 0; char *pn, state; int len; for (i=0; i < ss->nfs.nfsmounts.nrmounts; i++) /* per NFS mount */ { /* ** print for the first sample all mounts that ** are found; afterwards print only the mounts ** that were really active during the interval */ if (firstcall || allresources || ss->nfs.nfsmounts.nfsmnt[i].age < deltasec || ss->nfs.nfsmounts.nfsmnt[i].bytestotread || ss->nfs.nfsmounts.nfsmnt[i].bytestotwrite ) { if (nlines++) printf("%s ", tstamp); if ( (len = strlen(ss->nfs.nfsmounts.nfsmnt[i].mountdev)) > 38) pn = ss->nfs.nfsmounts.nfsmnt[i].mountdev + len - 38; else pn = ss->nfs.nfsmounts.nfsmnt[i].mountdev; if (ss->nfs.nfsmounts.nfsmnt[i].age < deltasec) state = 'M'; else state = ' '; printf("%-38s %10.3lfK %10.3lfK %c\n", pn, (double)ss->nfs.nfsmounts.nfsmnt[i].bytestotread / 1024 / deltasec, (double)ss->nfs.nfsmounts.nfsmnt[i].bytestotwrite / 1024 / deltasec, state); } } if (nlines == 0) { printf("\n"); nlines++; } firstcall= 0; return nlines; } static void nfchead(int osvers, int osrel, int ossub) { printf(" rpc/s rpcread/s rpcwrite/s retrans/s autrefresh/s " " _nfc_"); } static int nfcline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { printf("%10.2lf %10.2lf %10.2lf %10.2lf %12.2lf\n", (double)ss->nfs.client.rpccnt / deltasec, (double)ss->nfs.client.rpcread / deltasec, (double)ss->nfs.client.rpcwrite / deltasec, (double)ss->nfs.client.rpcretrans / deltasec, (double)ss->nfs.client.rpcautrefresh / deltasec); return 1; } static void nfshead(int osvers, int osrel, int ossub) { printf(" rpc/s rpcread/s rpcwrite/s MBcr/s MBcw/s " "nettcp/s netudp/s _nfs_"); } static int nfsline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { printf("%7.2lf %10.2lf %10.2lf %6.2lf %7.2lf %9.2lf %8.2lf\n", (double)ss->nfs.server.rpccnt / deltasec, (double)ss->nfs.server.rpcread / deltasec, (double)ss->nfs.server.rpcwrite / deltasec, (double)ss->nfs.server.nrbytes / 1024.0 / 1024.0 / deltasec, (double)ss->nfs.server.nwbytes / 1024.0 / 1024.0 / deltasec, (double)ss->nfs.server.nettcpcnt / deltasec, (double)ss->nfs.server.netudpcnt / deltasec); return 1; } /* ** network-interface statistics */ static void ibhead(int osvers, int osrel, int ossub) { printf("controller port busy ipack/s opack/s " "igbps ogbps maxgbps lanes _ib_"); } static int ibline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { static char firstcall = 1; register long i, nlines = 0; double busy; unsigned int badness; for (i=0; i < ss->ifb.nrports; i++) /* per interface */ { count_t ival, oval; /* ** print for the first sample all ports that ** are found; afterwards print only the ports ** that were really active during the interval */ if (!firstcall && !allresources && !ss->ifb.ifb[i].rcvb && !ss->ifb.ifb[i].sndb) continue; /* ** convert byte-transfers to bit-transfers (* 8) ** convert bit-transfers to gigabit-transfers (/ 1000000000) ** per second */ ival = ss->ifb.ifb[i].rcvb*ss->ifb.ifb[i].lanes/125000000/deltasec; oval = ss->ifb.ifb[i].sndb*ss->ifb.ifb[i].lanes/125000000/deltasec; /* ** calculate busy-percentage for port */ busy = (ival > oval ? ival*100 : oval*100)/ss->ifb.ifb[i].rate; if (nlines++) printf("%s ", tstamp); if (netbadness) badness = busy * 100 / netbadness; else badness = 0; preprint(badness); printf("%-10s %4hd %4.0f%% %7.1lf %7.1lf %5lld %5lld %7lld %5d", ss->ifb.ifb[i].ibname, ss->ifb.ifb[i].portnr, busy, (double)ss->ifb.ifb[i].rcvp / deltasec, (double)ss->ifb.ifb[i].sndp / deltasec, ival, oval, ss->ifb.ifb[i].rate / 1000, ss->ifb.ifb[i].lanes); postprint(badness); } if (nlines == 0) { printf("\n"); nlines++; } firstcall = 0; return nlines; } /* ** network-interface statistics */ static void ifhead(int osvers, int osrel, int ossub) { printf("interf busy ipack/s opack/s iKbyte/s oKbyte/s " "imbps ombps maxmbps_if_"); } static int ifline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { static char firstcall = 1; register long i, nlines = 0; double busy; char busyval[16], dupval; unsigned int badness; char *pn; int len; for (i=0; i < ss->intf.nrintf; i++) /* per interface */ { count_t ival, oval; /* ** print for the first sample all interfaces which ** are found; afterwards print only the interfaces ** which were really active during the interval */ if (!firstcall && !allresources && !ss->intf.intf[i].rpack && !ss->intf.intf[i].spack) continue; /* ** convert byte-transfers to bit-transfers (* 8) ** convert bit-transfers to megabit-transfers (/ 1000000) ** per second */ ival = ss->intf.intf[i].rbyte/125000/deltasec; oval = ss->intf.intf[i].sbyte/125000/deltasec; /* ** calculate busy-percentage for interface */ if (ss->intf.intf[i].speed) /* speed known? */ { if (ss->intf.intf[i].duplex) busy = (ival > oval ? ival*100 : oval*100) / ss->intf.intf[i].speed; else busy = (ival + oval) * 100 / ss->intf.intf[i].speed; // especially with wireless, the speed might have // dropped temporarily to a very low value (snapshot) // it might be better to take the speed of the // previous sample if (busy > 100 && ss->intf.intf[i].speed < ss->intf.intf[i].speedp ) { ss->intf.intf[i].speed = ss->intf.intf[i].speedp; if (ss->intf.intf[i].duplex) busy = (ival > oval ? ival*100 : oval*100) / ss->intf.intf[i].speed; else busy = (ival + oval) * 100 / ss->intf.intf[i].speed; } snprintf(busyval, sizeof busyval, "%3.0lf%%", busy); } else { strcpy(busyval, "?"); /* speed unknown */ busy = 0; } if (nlines++) printf("%s ", tstamp); if (ss->intf.intf[i].speed) { if (ss->intf.intf[i].duplex) dupval = 'f'; else dupval = 'h'; } else { dupval = ' '; } if (netbadness) badness = busy * 100 / netbadness; else badness = 0; if ( (len = strlen(ss->intf.intf[i].name)) > 6) pn = ss->intf.intf[i].name + len - 6; else pn = ss->intf.intf[i].name; preprint(badness); printf("%-6s %4s %7.1lf %7.1lf %8.0lf %8.0lf " "%5lld %5lld %7ld %c", pn, busyval, (double)ss->intf.intf[i].rpack / deltasec, (double)ss->intf.intf[i].spack / deltasec, (double)ss->intf.intf[i].rbyte / 1024 / deltasec, (double)ss->intf.intf[i].sbyte / 1024 / deltasec, ival, oval, ss->intf.intf[i].speed, dupval); postprint(badness); } if (nlines == 0) { printf("\n"); nlines++; } firstcall = 0; return nlines; } static void IFhead(int osvers, int osrel, int ossub) { printf("interf ierr/s oerr/s coll/s idrop/s odrop/s " "iframe/s ocarrier/s _if_"); } static int IFline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { static char firstcall = 1; register long i, nlines = 0; char *pn; int len; for (i=0; i < ss->intf.nrintf; i++) /* per interface */ { /* ** print for the first sample all interfaces which ** are found; afterwards print only the interfaces ** which were really active during the interval */ if (!firstcall && !allresources && !ss->intf.intf[i].rpack && !ss->intf.intf[i].spack) continue; if (nlines++) printf("%s ", tstamp); if ( (len = strlen(ss->intf.intf[i].name)) > 6) pn = ss->intf.intf[i].name + len - 6; else pn = ss->intf.intf[i].name; printf("%-6s %6.2lf %6.2lf %6.2lf %7.2lf %7.2lf " "%8.2lf %10.2lf\n", pn, (double)ss->intf.intf[i].rerrs / deltasec, (double)ss->intf.intf[i].serrs / deltasec, (double)ss->intf.intf[i].scollis / deltasec, (double)ss->intf.intf[i].rdrop / deltasec, (double)ss->intf.intf[i].sdrop / deltasec, (double)ss->intf.intf[i].rframe / deltasec, (double)ss->intf.intf[i].scarrier / deltasec); } if (nlines == 0) { printf("\n"); nlines++; } firstcall= 0; return nlines; } /* ** IP version 4 statistics */ static void ipv4head(int osvers, int osrel, int ossub) { printf("inrecv/s outreq/s indeliver/s forward/s " "reasmok/s fragcreat/s _ipv4_"); } static int ipv4line(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { printf("%8.1lf %8.1lf %11.1lf %9.1lf %9.1lf %11.1lf\n", (double)ss->net.ipv4.InReceives / deltasec, (double)ss->net.ipv4.OutRequests / deltasec, (double)ss->net.ipv4.InDelivers / deltasec, (double)ss->net.ipv4.Forwarding / deltasec, (double)ss->net.ipv4.ReasmOKs / deltasec, (double)ss->net.ipv4.FragCreates / deltasec); return 1; } static void IPv4head(int osvers, int osrel, int ossub) { printf("in: dsc/s hder/s ader/s unkp/s ratim/s rfail/s " "out: dsc/s nrt/s_ipv4_"); } static int IPv4line(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { printf(" %5.1lf %6.1lf %6.1lf %6.1lf %7.1lf %7.1lf " " %5.1lf %5.1lf\n", (double)ss->net.ipv4.InDiscards / deltasec, (double)ss->net.ipv4.InHdrErrors / deltasec, (double)ss->net.ipv4.InAddrErrors / deltasec, (double)ss->net.ipv4.InUnknownProtos / deltasec, (double)ss->net.ipv4.ReasmTimeout / deltasec, (double)ss->net.ipv4.ReasmFails / deltasec, (double)ss->net.ipv4.OutDiscards / deltasec, (double)ss->net.ipv4.OutNoRoutes / deltasec); return 1; } /* ** ICMP version 4 statistics */ static void icmpv4head(int osvers, int osrel, int ossub) { printf("intot/s outtot/s inecho/s inerep/s " "otecho/s oterep/s _icmpv4_" ); } static int icmpv4line(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { printf("%7.1lf %8.1lf %8.2lf %8.2lf %8.2lf %8.2lf\n", (double)ss->net.icmpv4.InMsgs / deltasec, (double)ss->net.icmpv4.OutMsgs / deltasec, (double)ss->net.icmpv4.InEchos / deltasec, (double)ss->net.icmpv4.OutEchos / deltasec, (double)ss->net.icmpv4.InEchoReps / deltasec, (double)ss->net.icmpv4.OutEchoReps / deltasec); return 1; } static void ICMPv4head(int osvers, int osrel, int ossub) { printf("ierr/s isq/s ird/s idu/s ite/s " "oerr/s osq/s ord/s odu/s ote/s_icmpv4_"); } static int ICMPv4line(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { printf("%6.2lf %5.2lf %5.2lf %5.2lf %5.2lf " "%6.2lf %5.2lf %5.2lf %5.2lf %5.2lf\n", (double)ss->net.icmpv4.InErrors / deltasec, (double)ss->net.icmpv4.InSrcQuenchs / deltasec, (double)ss->net.icmpv4.InRedirects / deltasec, (double)ss->net.icmpv4.InDestUnreachs / deltasec, (double)ss->net.icmpv4.InTimeExcds / deltasec, (double)ss->net.icmpv4.OutErrors / deltasec, (double)ss->net.icmpv4.OutSrcQuenchs / deltasec, (double)ss->net.icmpv4.OutRedirects / deltasec, (double)ss->net.icmpv4.OutDestUnreachs / deltasec, (double)ss->net.icmpv4.OutTimeExcds / deltasec); return 1; } /* ** UDP version 4 statistics */ static void udpv4head(int osvers, int osrel, int ossub) { printf("indgram/s outdgram/s inerr/s noport/s " " _udpv4_"); } static int udpv4line(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { printf("%9.1lf %10.1lf %7.2lf %9.2lf\n", (double)ss->net.udpv4.InDatagrams / deltasec, (double)ss->net.udpv4.OutDatagrams / deltasec, (double)ss->net.udpv4.InErrors / deltasec, (double)ss->net.udpv4.NoPorts / deltasec); return 1; } /* ** IP version 6 statistics */ static void ipv6head(int osvers, int osrel, int ossub) { printf("inrecv/s outreq/s inmc/s outmc/s indeliv/s " "reasmok/s fragcre/s _ipv6_"); } static int ipv6line(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { printf("%8.1lf %8.1lf %6.1lf %7.1lf %9.1lf %9.1lf %9.1lf\n", (double)ss->net.ipv6.Ip6InReceives / deltasec, (double)ss->net.ipv6.Ip6OutRequests / deltasec, (double)ss->net.ipv6.Ip6InMcastPkts / deltasec, (double)ss->net.ipv6.Ip6OutMcastPkts / deltasec, (double)ss->net.ipv6.Ip6InDelivers / deltasec, (double)ss->net.ipv6.Ip6ReasmOKs / deltasec, (double)ss->net.ipv6.Ip6FragCreates / deltasec); return 1; } static void IPv6head(int osvers, int osrel, int ossub) { printf("in: dsc/s hder/s ader/s unkp/s ratim/s rfail/s " "out: dsc/s nrt/s_ipv6_"); } static int IPv6line(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { printf(" %5.1lf %6.1lf %6.1lf %6.1lf %7.1lf %7.1lf " " %5.1lf %5.1lf\n", (double)ss->net.ipv6.Ip6InDiscards / deltasec, (double)ss->net.ipv6.Ip6InHdrErrors / deltasec, (double)ss->net.ipv6.Ip6InAddrErrors / deltasec, (double)ss->net.ipv6.Ip6InUnknownProtos / deltasec, (double)ss->net.ipv6.Ip6ReasmTimeout / deltasec, (double)ss->net.ipv6.Ip6ReasmFails / deltasec, (double)ss->net.ipv6.Ip6OutDiscards / deltasec, (double)ss->net.ipv6.Ip6OutNoRoutes / deltasec); return 1; } /* ** ICMP version 6 statistics */ static void icmpv6head(int osvers, int osrel, int ossub) { printf("intot/s outtot/s inerr/s innsol/s innadv/s " "otnsol/s otnadv/s _icmp6_" ); } static int icmpv6line(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { printf("%7.1lf %8.1lf %7.2lf %8.2lf %8.2lf %8.2lf %8.2lf\n", (double)ss->net.icmpv6.Icmp6InMsgs / deltasec, (double)ss->net.icmpv6.Icmp6OutMsgs / deltasec, (double)ss->net.icmpv6.Icmp6InErrors / deltasec, (double)ss->net.icmpv6.Icmp6InNeighborSolicits / deltasec, (double)ss->net.icmpv6.Icmp6InNeighborAdvertisements/ deltasec, (double)ss->net.icmpv6.Icmp6OutNeighborSolicits / deltasec, (double)ss->net.icmpv6.Icmp6OutNeighborAdvertisements /deltasec); return 1; } static void ICMPv6head(int osvers, int osrel, int ossub) { printf("iecho/s ierep/s oerep/s idu/s odu/s ird/s ord/s ite/s " "ote/s _icmpv6_"); } static int ICMPv6line(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { printf("%7.2lf %7.2lf %7.2lf %5.2lf %5.2lf " "%5.2lf %5.2lf %5.2lf %5.2lf\n", (double)ss->net.icmpv6.Icmp6InEchos / deltasec, (double)ss->net.icmpv6.Icmp6InEchoReplies / deltasec, (double)ss->net.icmpv6.Icmp6OutEchoReplies / deltasec, (double)ss->net.icmpv6.Icmp6InDestUnreachs / deltasec, (double)ss->net.icmpv6.Icmp6OutDestUnreachs / deltasec, (double)ss->net.icmpv6.Icmp6InRedirects / deltasec, (double)ss->net.icmpv6.Icmp6OutRedirects / deltasec, (double)ss->net.icmpv6.Icmp6InTimeExcds / deltasec, (double)ss->net.icmpv6.Icmp6OutTimeExcds / deltasec); return 1; } /* ** UDP version 6 statistics */ static void udpv6head(int osvers, int osrel, int ossub) { printf("indgram/s outdgram/s inerr/s noport/s " " _udpv6_"); } static int udpv6line(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { printf("%9.1lf %10.1lf %7.2lf %9.2lf\n", (double)ss->net.udpv6.Udp6InDatagrams / deltasec, (double)ss->net.udpv6.Udp6OutDatagrams / deltasec, (double)ss->net.udpv6.Udp6InErrors / deltasec, (double)ss->net.udpv6.Udp6NoPorts / deltasec); return 1; } /* ** TCP statistics */ static void tcphead(int osvers, int osrel, int ossub) { printf("insegs/s outsegs/s actopen/s pasopen/s " "nowopen _tcp_"); } static int tcpline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { printf("%8.1lf %9.1lf %9.1lf %9.1lf %7lld\n", (double)ss->net.tcp.InSegs / deltasec, (double)ss->net.tcp.OutSegs / deltasec, (double)ss->net.tcp.ActiveOpens / deltasec, (double)ss->net.tcp.PassiveOpens / deltasec, ss->net.tcp.CurrEstab); return 1; } static void TCPhead(int osvers, int osrel, int ossub) { printf("inerr/s retrans/s attfail/s " "estabreset/s outreset/s _tcp_"); } static int TCPline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { printf("%7.1lf %9.1lf %9.1lf %12.1lf %10.1lf\n", (double)ss->net.tcp.InErrs / deltasec, (double)ss->net.tcp.RetransSegs / deltasec, (double)ss->net.tcp.AttemptFails / deltasec, (double)ss->net.tcp.EstabResets / deltasec, (double)ss->net.tcp.OutRsts / deltasec); return 1; } #if HTTPSTATS static void httphead(int osvers, int osrel, int ossub) { printf("requests/s Kbytes/s bytes/req " "idleworkers busyworkers _http_"); } static int httpline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { printf("%10.2lf %8.2lf %9.2lf %11d %11d\n", (double)ss->www.accesses / deltasec, (double)ss->www.totkbytes / deltasec, ss->www.accesses ? (double)ss->www.totkbytes*1024/ss->www.accesses : 0, ss->www.iworkers, ss->www.bworkers); return 1; } #endif /* ** per-process statistics: top-3 processor consumers */ static void topchead(int osvers, int osrel, int ossub) { printf(" pid command cpu%% | pid command cpu%% | " " pid command cpu%%_top3_"); } static int topcline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { count_t availcpu; if (!ts) { printf("report not available.....\n"); return 0; } /* ** sort process list in cpu order */ qsort(ps, nactproc, sizeof(struct tstat *), compcpu); availcpu = ss->cpu.all.stime + ss->cpu.all.utime + ss->cpu.all.ntime + ss->cpu.all.itime + ss->cpu.all.wtime + ss->cpu.all.Itime + ss->cpu.all.Stime + ss->cpu.all.steal; availcpu /= ss->cpu.nrcpu; if (availcpu == 0) availcpu = 1; /* avoid divide-by-zero */ if (nactproc >= 1 && (ps[0])->cpu.stime + (ps[0])->cpu.utime > 0) printf("%5d %-8.8s %3.0lf%% | ", (ps[0])->gen.pid, (ps[0])->gen.name, (double)((ps[0])->cpu.stime + (ps[0])->cpu.utime)*100.0/availcpu); else printf("%19s | ", " "); if (nactproc >= 2 && (ps[1])->cpu.stime + (ps[1])->cpu.utime > 0) printf("%5d %-8.8s %3.0lf%% | ", (ps[1])->gen.pid, (ps[1])->gen.name, (double)((ps[1])->cpu.stime + (ps[1])->cpu.utime)*100.0/availcpu); else printf("%19s | ", " "); if (nactproc >= 3 && (ps[2])->cpu.stime + (ps[2])->cpu.utime > 0) printf("%5d %-8.8s %3.0lf%%\n", (ps[2])->gen.pid, (ps[2])->gen.name, (double)((ps[2])->cpu.stime + (ps[2])->cpu.utime)*100.0/availcpu); else printf("%19s\n", " "); return 1; } /* ** per-process statistics: top-3 memory consumers */ static void topmhead(int osvers, int osrel, int ossub) { printf(" pid command mem%% | pid command mem%% | " " pid command mem%%_top3_"); } static int topmline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { count_t availmem; if (!ts) { printf("report not available.....\n"); return 0; } /* ** sort process list in memory order */ qsort(ps, nactproc, sizeof(struct tstat *), compmem); availmem = ss->mem.physmem * pagesize/1024; if (nactproc >= 1) printf("%5d %-8.8s %3.0lf%% | ", (ps[0])->gen.pid, (ps[0])->gen.name, (double)(ps[0])->mem.rmem * 100.0 / availmem); else printf("%19s | ", " "); if (nactproc >= 2) printf("%5d %-8.8s %3.0lf%% | ", (ps[1])->gen.pid, (ps[1])->gen.name, (double)(ps[1])->mem.rmem * 100.0 / availmem); else printf("%19s | ", " "); if (nactproc >= 3) printf("%5d %-8.8s %3.0lf%%\n", (ps[2])->gen.pid, (ps[2])->gen.name, (double)(ps[2])->mem.rmem * 100.0 / availmem); else printf("%19s\n", " "); return 1; } /* ** per-process statistics: top-3 disk consumers */ static void topdhead(int osvers, int osrel, int ossub) { printf(" pid command dsk%% | pid command dsk%% | " " pid command dsk%%_top3_"); } static int topdline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { int i; count_t availdsk; if (!ts) { printf("report not available.....\n"); return 0; } if ( !(supportflags & IOSTAT) ) { printf("no per-process disk counters available.....\n"); return 0; } /* ** determine total disk accesses for all processes */ for (i=0, availdsk=0; i < nactproc; i++) { availdsk += (ps[i])->dsk.rio + (ps[i])->dsk.wio; } if (availdsk == 0) availdsk = 1; /* ** sort process list in disk order */ qsort(ps, nactproc, sizeof(struct tstat *), compdsk); if (nactproc >= 1 && (ps[0])->dsk.rio + (ps[0])->dsk.wio > 0) printf("%5d %-8.8s %3.0lf%% | ", (ps[0])->gen.pid, (ps[0])->gen.name, (double)((ps[0])->dsk.rio+(ps[0])->dsk.wio) *100.0/availdsk); else printf("%19s | ", " "); if (nactproc >= 2 && (ps[1])->dsk.rio + (ps[1])->dsk.wio > 0) printf("%5d %-8.8s %3.0lf%% | ", (ps[1])->gen.pid, (ps[1])->gen.name, (double)((ps[1])->dsk.rio+(ps[1])->dsk.wio) *100.0/availdsk); else printf("%19s | ", " "); if (nactproc >= 3 && (ps[2])->dsk.rio + (ps[2])->dsk.wio > 0) printf("%5d %-8.8s %3.0lf%%\n", (ps[2])->gen.pid, (ps[2])->gen.name, (double)((ps[2])->dsk.rio+(ps[2])->dsk.wio) *100.0/availdsk); else printf("%19s\n", " "); return 1; } /* ** per-process statistics: top-3 network consumers */ static void topnhead(int osvers, int osrel, int ossub) { printf(" pid command net%% | pid command net%% | " " pid command net%%_top3_"); } static int topnline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, time_t deltasec, time_t deltatic, time_t hz, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int ntidle, int pexit, int pzombie) { int i; count_t availnet; count_t totbytes; if (!ts) { printf("report not available.....\n"); return 0; } if ( !(supportflags & NETATOP) ) { printf("no per-process network counters available.....\n"); return 0; } /* ** determine total network accesses for all processes */ for (i=0, availnet=0; i < nactproc; i++) { availnet += (*(ps+i))->net.tcpssz + (*(ps+i))->net.tcprsz + (*(ps+i))->net.udpssz + (*(ps+i))->net.udprsz; } if (availnet == 0) availnet = 1; /* ** sort process list in network order */ qsort(ps, nactproc, sizeof(struct tstat *), compnet); if (nactproc >= 1) { totbytes = (ps[0])->net.tcpssz + (ps[0])->net.tcprsz + (ps[0])->net.udpssz + (ps[0])->net.udprsz; if (totbytes > 0) printf("%5d %-8.8s %3.0lf%% | ", (ps[0])->gen.pid, (ps[0])->gen.name, (double)totbytes * 100.0 / availnet); else printf("%19s | ", " "); } else printf("%19s | ", " "); if (nactproc >= 2) { totbytes = (ps[1])->net.tcpssz + (ps[1])->net.tcprsz + (ps[1])->net.udpssz + (ps[1])->net.udprsz; if (totbytes > 0) printf("%5d %-8.8s %3.0lf%% | ", (ps[1])->gen.pid, (ps[1])->gen.name, (double)totbytes * 100.0 / availnet); else printf("%19s | ", " "); } else printf("%19s | ", " "); if (nactproc >= 3) { totbytes = (ps[2])->net.tcpssz + (ps[2])->net.tcprsz + (ps[2])->net.udpssz + (ps[2])->net.udprsz; if (totbytes > 0) printf("%5d %-8.8s %3.0lf%%\n", (ps[2])->gen.pid, (ps[2])->gen.name, (double)totbytes * 100.0 / availnet); else printf("%19s\n", " "); } else printf("%19s\n", " "); return 1; } /*********************************************************************/ /* Function definition table. */ /* */ /* The layout of this table is as follows: */ /* Column 1: */ /* Boolean which indicates if the specified function is */ /* active during a run of 'atopsar'. When started, */ /* this boolean will be defined 'true' for all entries for */ /* which the command-line flag has been specified. Initially */ /* this column should contain 0 (false), unless this function */ /* is always required. */ /* If no flags are specified for 'atopsar', the first entry */ /* in this table is defined active (default flag). */ /* */ /* Column 2: */ /* Categories of counters used by this function. */ /* c = cpu counters, m = memory counters, */ /* d = disk counters, n = network counters */ /* */ /* Column 3: */ /* Flag which can be used as command-line argument to */ /* select the function defined in this table-entry. Be sure */ /* that a unique character is choosen. */ /* Notice that certain flags are reserved! */ /* */ /* Column 4: */ /* Entry-point of the 'printhead' function. */ /* */ /* Column 5: */ /* Entry-point of the 'printline' function. */ /* */ /* Column 6: */ /* Information about the statistics shown by the function */ /* specified by the table-entry. This text is printed as */ /* command-usage. */ /*********************************************************************/ struct pridef pridef[] = { {0, "c", 'c', cpuhead, cpuline, "cpu utilization", }, {0, "c", 'p', prochead, procline, "process(or) load", }, {0, "c", 'P', taskhead, taskline, "processes & threads", }, {0, "c", 'g', gpuhead, gpuline, "gpu utilization", }, {0, "m", 'm', memhead, memline, "memory & swapspace", }, {0, "m", 's', swaphead, swapline, "swap rate", }, {0, "cmd",'B', psihead, psiline, "pressure stall info (PSI)",}, {0, "cd", 'l', lvmhead, lvmline, "logical volume activity", }, {0, "cd", 'f', mddhead, mddline, "multiple device activity",}, {0, "cd", 'd', dskhead, dskline, "disk activity", }, {0, "n", 'h', ibhead, ibline, "infiniband utilization", }, {0, "n", 'n', nfmhead, nfmline, "NFS client mounts", }, {0, "n", 'j', nfchead, nfcline, "NFS client activity", }, {0, "n", 'J', nfshead, nfsline, "NFS server activity", }, {0, "n", 'i', ifhead, ifline, "net-interf (general)", }, {0, "n", 'I', IFhead, IFline, "net-interf (errors)", }, {0, "n", 'w', ipv4head, ipv4line, "ip v4 (general)", }, {0, "n", 'W', IPv4head, IPv4line, "ip v4 (errors)", }, {0, "n", 'y', icmpv4head, icmpv4line, "icmp v4 (general)", }, {0, "n", 'Y', ICMPv4head, ICMPv4line, "icmp v4 (per type)", }, {0, "n", 'u', udpv4head, udpv4line, "udp v4", }, {0, "n", 'z', ipv6head, ipv6line, "ip v6 (general)", }, {0, "n", 'Z', IPv6head, IPv6line, "ip v6 (errors)", }, {0, "n", 'k', icmpv6head, icmpv6line, "icmp v6 (general)", }, {0, "n", 'K', ICMPv6head, ICMPv6line, "icmp v6 (per type)", }, {0, "n", 'U', udpv6head, udpv6line, "udp v6", }, {0, "n", 't', tcphead, tcpline, "tcp (general)", }, {0, "n", 'T', TCPhead, TCPline, "tcp (errors)", }, #if HTTPSTATS {0, "n", 'o', httphead, httpline, "HTTP activity", }, #endif {0, "", 'O', topchead, topcline, "top-3 processes cpu", }, {0, "", 'G', topmhead, topmline, "top-3 processes memory", }, {0, "", 'D', topdhead, topdline, "top-3 processes disk", }, {0, "", 'N', topnhead, topnline, "top-3 processes network",}, }; int pricnt = sizeof(pridef)/sizeof(struct pridef); atop-2.11.1/showgeneric.c0000644000203100020310000026035414771753566014557 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 #include #include "atop.h" #include "photoproc.h" #include "photosyst.h" #include "cgroups.h" #include "showgeneric.h" #include "showlinux.h" static struct pselection procsel = {"", {USERSTUB, }, {0,}, "", 0, { 0, }, "", 0, { 0, }, "", "" }; static struct sselection syssel; static void showhelp(int); static int fixedhead; /* boolean: fixate header-lines */ static int sysnosort; /* boolean: suppress sort of resources */ static int threadsort; /* boolean: sort threads per process */ static int avgval; /* boolean: average values i.s.o. total */ static int suppressexit; /* boolean: suppress terminated processes */ static char showtype = MPROCGEN; static char showorder = MSORTCPU; static int maxcpulines = 999; /* maximum cpu lines */ static int maxgpulines = 999; /* maximum gpu lines */ static int maxdsklines = 999; /* maximum disk lines */ static int maxmddlines = 999; /* maximum MDD lines */ static int maxlvmlines = 999; /* maximum LVM lines */ static int maxintlines = 999; /* maximum interface lines */ static int maxifblines = 999; /* maximum infinibnd lines */ static int maxnfslines = 999; /* maximum nfs mount lines */ static int maxcontlines = 999; /* maximum container lines */ static int maxnumalines = 999; /* maximum numa lines */ static int maxllclines = 999; /* maximum llc lines */ static short colorinfo = COLOR_GREEN; static short coloralmost = COLOR_CYAN; static short colorcrit = COLOR_RED; static short colorthread = COLOR_YELLOW; static char winchange; static char keywaiting; // set after key has been pushed back int paused; // boolean: currently in pause-mode int cgroupdepth = 7; // default: cgroups without processes static int cumusers(struct tstat **, struct tstat *, int); static int cumprogs(struct tstat **, struct tstat *, int); static int cumconts(struct tstat **, struct tstat *, int); static void accumulate(struct tstat *, struct tstat *); static int procsuppress(struct tstat *, struct pselection *); static void limitedlines(void); static long getnumval(char *, long, int); static void getsigwinch(int); static void generic_init(void); static char text_samp(time_t, int, struct devtstat *, struct sstat *, struct cgchainer *, int, int, unsigned int, char); static int (*procsort[])(const void *, const void *) = { [MSORTCPU&0x1f]=compcpu, [MSORTMEM&0x1f]=compmem, [MSORTDSK&0x1f]=compdsk, [MSORTNET&0x1f]=compnet, [MSORTGPU&0x1f]=compgpu, }; extern detail_printpair ownprocs[]; /* ** global: incremented by -> key and decremented by <- key */ int startoffset; /* ** main function to handle sample generically ** to show flat text, full screen text or bar graphs */ char generic_samp(time_t curtime, int nsecs, struct devtstat *devtstat, struct sstat *sstat, struct cgchainer *cstats, int ncgroups, int npids, int nexit, unsigned int noverflow, char flag) { static char firstcall = 1; char retval, sorted = 0; if (firstcall) { generic_init(); firstcall = 0; } while (1) { if (displaymode == 'T') // text mode? { // show sample and wait for input or timer expiration // switch (retval = text_samp(curtime, nsecs, devtstat, sstat, cstats, ncgroups, nexit, noverflow, flag)) { case MBARGRAPH: displaymode = 'D'; break; default: return retval; } sorted = 1; // resources have been sorted by text mode } if (displaymode == 'D') // draw mode? { // show sample and wait for input or timer expiration // switch (retval = draw_samp(curtime, nsecs, sstat, flag, sorted)) { case MBARGRAPH: // just switch to text mode displaymode = 'T'; break; case MCGROUPS: // switch to text mode: cgroups if (supportflags & CGROUPV2) showtype = MCGROUPS; else showtype = MPROCGEN; displaymode = 'T'; break; case MPROCGEN: // switch to text mode: generic showtype = MPROCGEN; if (showorder != MSORTAUTO) showorder = MSORTCPU; displaymode = 'T'; break; case MPROCMEM: // switch to text mode: memory showtype = MPROCMEM; if (showorder != MSORTAUTO) showorder = MSORTMEM; displaymode = 'T'; break; case MPROCDSK: // switch to text mode: disk showtype = MPROCDSK; if (showorder != MSORTAUTO) showorder = MSORTDSK; displaymode = 'T'; break; case MPROCNET: // switch to text mode: network if (supportflags & NETATOP || supportflags & NETATOPBPF) { showtype = MPROCNET; if (showorder != MSORTAUTO) showorder = MSORTNET; } else { showtype = MPROCGEN; showorder = MSORTCPU; } displaymode = 'T'; break; case MPROCGPU: // switch to text mode: GPU if (supportflags & GPUSTAT) { showtype = MPROCGPU; if (showorder != MSORTAUTO) showorder = MSORTGPU; } else { showtype = MPROCGEN; showorder = MSORTCPU; } displaymode = 'T'; break; case MPROCSCH: // switch to text mode: scheduling showtype = MPROCSCH; if (showorder != MSORTAUTO) showorder = MSORTCPU; displaymode = 'T'; break; case MPROCVAR: // switch to text mode: various showtype = MPROCVAR; displaymode = 'T'; break; case MPROCARG: // switch to text mode: arguments showtype = MPROCARG; displaymode = 'T'; break; default: return retval; } } } } /* ** print the deviation-counters on process level, cgroups level and ** system level in text mode */ static char text_samp(time_t curtime, int nsecs, struct devtstat *devtstat, struct sstat *sstat, struct cgchainer *cgchainers, int ncgroups, int nexit, unsigned int noverflow, char flag) { register int i, curline, statline, nproc; int firstitem=0, slistsz, alistsz, killpid, killsig; int lastchar; char format1[16], format2[16], branchtime[32]; char *statmsg = NULL, statbuf[80], genline[80]; char *lastsortp, curorder, autoorder; char buf[33]; struct passwd *pwd; struct syscap syscap; fd_set readfds; char eventbuf[1024]; int nrfds; /* ** number of entries in the active list of cgroups/tasks ** to be displayed */ int ncurlist = 0; /* ** cgroupsel points to the merged list of cgchainer pointers (to cgroups) ** and tstat pointers (to processes), representing the cgroups information ** to be displayed (for cgroups visualization); ** ncurlist indicates the number of entries in this list ** ** this list will only be allocated 'lazy' only when ** cgroups visualization is requested ** ** cgroupsort refers to a list with cgchainer pointers in ** sorted order according to the current showorder */ struct cglinesel *cgroupsel = 0; struct cgchainer **cgroupsort = 0; char cstatdeviate = ' ', cstatdepth = ' ', cstatorder = ' '; /* ** curlist points to the active list of tstat pointers that ** should be displayed; ncurlist indicates the number of entries in ** this list */ struct tstat **curlist; /* ** tXcumlist is a list of tstat structs holding one entry ** per accumulated (per user or per program) group of processes ** ** Xcumlist contains the pointers to all structs in tXcumlist ** ** these lists will only be allocated 'lazy' ** only when accumulation is requested */ struct tstat *tpcumlist = 0; // per program accumulation struct tstat **pcumlist = 0; int npcum = 0; char plastorder = 0; struct tstat *tucumlist = 0; // per user accumulation struct tstat **ucumlist = 0; int nucum = 0; char ulastorder = 0; struct tstat *tccumlist = 0; // per container/pod accumulation struct tstat **ccumlist = 0; int nccum = 0; char clastorder = 0; /* ** tsklist contains the pointers to all structs in tstat ** sorted on process with the related threads immediately ** following the process ** ** this list will be allocated 'lazy' */ struct tstat **tsklist = 0; int ntsk = 0; char tlastorder = 0; char zipagain = 0; char tdeviate = 0; /* ** sellist contains the pointers to the structs in tstat ** that are currently selected on basis of a particular ** username (regexp), program name (regexp), container/pod name ** or suppressed terminated procs ** ** this list will be allocated 'lazy' */ struct tstat **sellist = 0; int nsel = 0; char slastorder = 0; char threadallowed = 0; startoffset = 0; /* ** compute the total capacity of this system for the ** four main resources */ totalcap(&syscap, sstat, devtstat->procactive, devtstat->nprocactive); /* ** sort per-cpu statistics on busy percentage ** sort per-logical-volume statistics on busy percentage ** sort per-multiple-device statistics on busy percentage ** sort per-disk statistics on busy percentage ** sort per-interface statistics on busy percentage (if known) */ if (!sysnosort) { if (sstat->cpu.nrcpu > 1 && maxcpulines > 0) qsort(sstat->cpu.cpu, sstat->cpu.nrcpu, sizeof sstat->cpu.cpu[0], cpucompar); if (sstat->gpu.nrgpus > 1 && maxgpulines > 0) qsort(sstat->gpu.gpu, sstat->gpu.nrgpus, sizeof sstat->gpu.gpu[0], gpucompar); if (sstat->dsk.nlvm > 1 && maxlvmlines > 0) qsort(sstat->dsk.lvm, sstat->dsk.nlvm, sizeof sstat->dsk.lvm[0], diskcompar); if (sstat->dsk.nmdd > 1 && maxmddlines > 0) qsort(sstat->dsk.mdd, sstat->dsk.nmdd, sizeof sstat->dsk.mdd[0], diskcompar); if (sstat->dsk.ndsk > 1 && maxdsklines > 0) qsort(sstat->dsk.dsk, sstat->dsk.ndsk, sizeof sstat->dsk.dsk[0], diskcompar); if (sstat->intf.nrintf > 1 && maxintlines > 0) qsort(sstat->intf.intf, sstat->intf.nrintf, sizeof sstat->intf.intf[0], intfcompar); if (sstat->ifb.nrports > 1 && maxifblines > 0) qsort(sstat->ifb.ifb, sstat->ifb.nrports, sizeof sstat->ifb.ifb[0], ifbcompar); if (sstat->nfs.nfsmounts.nrmounts > 1 && maxnfslines > 0) qsort(sstat->nfs.nfsmounts.nfsmnt, sstat->nfs.nfsmounts.nrmounts, sizeof sstat->nfs.nfsmounts.nfsmnt[0], nfsmcompar); if (sstat->cfs.nrcontainer > 1 && maxcontlines > 0) qsort(sstat->cfs.cont, sstat->cfs.nrcontainer, sizeof sstat->cfs.cont[0], contcompar); if (sstat->memnuma.nrnuma > 1 && maxnumalines > 0) qsort(sstat->memnuma.numa, sstat->memnuma.nrnuma, sizeof sstat->memnuma.numa[0], memnumacompar); if (sstat->cpunuma.nrnuma > 1 && maxnumalines > 0) qsort(sstat->cpunuma.numa, sstat->cpunuma.nrnuma, sizeof sstat->cpunuma.numa[0], cpunumacompar); if (sstat->llc.nrllcs > 1 && maxllclines > 0) qsort(sstat->llc.perllc, sstat->llc.nrllcs, sizeof sstat->llc.perllc[0], llccompar); } /* ** loop in which the system resources and the list of active ** processes are shown; the loop will be preempted by receiving ** a timer-signal or when the trigger-button is pressed. */ while (1) { curline = 1; genline[0] = '\0'; /* ** prepare screen or file output for new sample */ if (screen) werase(stdscr); else printf("\n\n"); /* ** print general headerlines */ convdate(curtime, format1); /* date to ascii string */ convtime(curtime, format2); /* time to ascii string */ if (screen) attron(A_REVERSE); int seclen = val2elapstr(nsecs, buf); int lenavail = (screen ? COLS : linelen) - 60 - seclen - utsnodenamelen; int len1 = lenavail / 3; if (len1 <= 0) len1 = 1; int len2 = lenavail - len1 - len1; printg("ATOP - %s%*s%s %s %s %*s" "%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%*s%s elapsed", utsname.nodename, len1, "", format1, format2, " ", len1, "", threadview ? MTHREAD : '-', threadsort ? MTHRSORT : '-', fixedhead ? MSYSFIXED : '-', sysnosort ? MSYSNOSORT : '-', deviatonly ? '-' : MALLACTIVE, usecolors ? '-' : MCOLORS, avgval ? MAVGVAL : '-', calcpss ? MCALCPSS : '-', getwchan ? MGETWCHAN : '-', suppressexit ? MSUPEXITS : '-', procsel.userid[0] != USERSTUB ? MSELUSER : '-', procsel.prognamesz ? MSELPROC : '-', procsel.utsname[0] ? MSELCONT : '-', procsel.pid[0] != 0 ? MSELPID : '-', procsel.argnamesz ? MSELARG : '-', procsel.states[0] ? MSELSTATE : '-', syssel.lvmnamesz + syssel.dsknamesz + syssel.itfnamesz ? MSELSYS : '-', cgroupdepth+0x30, len2, "", buf); if (screen) attroff(A_REVERSE); else printg("\n"); if (screen && paused) { if (usecolors) attron(COLOR_PAIR(FGCOLORBORDER)); attron(A_REVERSE); mvprintw(0, 27 + utsnodenamelen + len1, "PAUSED"); attroff(A_REVERSE); if (usecolors) attroff(COLOR_PAIR(FGCOLORBORDER)); } /* ** print cumulative system- and user-time for all processes */ pricumproc(sstat, devtstat, nexit, noverflow, avgval, nsecs); if (noverflow) { snprintf(statbuf, sizeof statbuf, "Only %d terminated processes handled " "-- %u skipped!", nexit, noverflow); statmsg = statbuf; } curline=2; /* ** print other lines of system-wide statistics */ if (showorder == MSORTAUTO) autoorder = MSORTCPU; else autoorder = showorder; curline = prisyst(sstat, curline, nsecs, avgval, fixedhead, &syssel, &autoorder, maxcpulines, maxgpulines, maxdsklines, maxmddlines, maxlvmlines, maxintlines, maxifblines, maxnfslines, maxcontlines, maxnumalines, maxllclines); /* ** if system-wide statistics do not fit, ** limit the number of variable resource lines ** and try again */ if (screen && curline+2 > LINES) { curline = 2; move(curline, 0); clrtobot(); move(curline, 0); limitedlines(); curline = prisyst(sstat, curline, nsecs, avgval, fixedhead, &syssel, &autoorder, maxcpulines, maxgpulines, maxdsklines, maxmddlines, maxlvmlines, maxintlines, maxifblines, maxnfslines, maxcontlines, maxnumalines, maxllclines); /* ** if system-wide statistics still do not fit, ** the window is really to small */ if (curline+2 > LINES) { endwin(); // finish curses interface fprintf(stderr, "Not enough screen-lines available " "(need at least %d lines)\n", curline+2); fprintf(stderr, "Please resize window....\n"); cleanstop(1); } else { statmsg = "Number of variable resources" " limited to fit in this window"; } } statline = curline; if (screen) move(curline, 0); if (statmsg) { if (screen) { clrtoeol(); if (usecolors) attron(COLOR_PAIR(FGCOLORINFO)); } printg(statmsg); if (screen) { if (usecolors) attroff(COLOR_PAIR(FGCOLORINFO)); } statmsg = NULL; } else { if (flag&RRBOOT) { char *initmsg = "*** System and Process Activity since Boot ***"; char *viewmsg; if (rawreadflag) { if (twinpid) viewmsg = "Twin mode"; else viewmsg = "Rawfile view"; } else { if (rootprivs()) viewmsg = "Unrestricted view (privileged)"; else viewmsg = "Restricted view (unprivileged)"; } if (screen) { if (usecolors) attron(COLOR_PAIR(FGCOLORINFO)); } printg(initmsg); if (screen) { if (usecolors) attroff(COLOR_PAIR(FGCOLORINFO)); printg("%*s", COLS - strlen(initmsg) - strlen(viewmsg), " "); } else { printg("%*s", 80 - strlen(initmsg) - strlen(viewmsg), " "); } if (screen) { if (usecolors) attron(COLOR_PAIR(FGCOLORALMOST)); } printg(viewmsg); if (screen) { if (usecolors) attroff(COLOR_PAIR(FGCOLORALMOST)); } } } if (showtype != MCGROUPS) { /* ** select the required list with tasks to be shown ** ** if cumulative figures required, accumulate resource ** consumption of all processes in the current list */ switch (showtype) { case MCUMUSER: threadallowed = 0; if (ucumlist) /* previous list still available? */ { free(ucumlist); free(tucumlist); ulastorder = 0; } if (deviatonly) nproc = devtstat->nprocactive; else nproc = devtstat->nprocall; /* ** allocate space for new (temporary) list with ** one entry per user (list has worst-case size) */ tucumlist = calloc(sizeof(struct tstat), nproc); ucumlist = malloc(sizeof(struct tstat *) * nproc); ptrverify(tucumlist, "Malloc failed for %d ucum procs\n", nproc); ptrverify(ucumlist, "Malloc failed for %d ucum ptrs\n", nproc); for (i=0; i < nproc; i++) { /* fill pointers */ ucumlist[i] = tucumlist+i; } nucum = cumusers(deviatonly ? devtstat->procactive : devtstat->procall, tucumlist, nproc); curlist = ucumlist; ncurlist = nucum; lastsortp = &ulastorder; break; case MCUMPROC: threadallowed = 0; if (pcumlist) /* previous list still available? */ { free(pcumlist); free(tpcumlist); plastorder = 0; } if (deviatonly) nproc = devtstat->nprocactive; else nproc = devtstat->nprocall; /* ** allocate space for new (temporary) list with ** one entry per program (list has worst-case size) */ tpcumlist = calloc(sizeof(struct tstat), nproc); pcumlist = malloc(sizeof(struct tstat *) * nproc); ptrverify(tpcumlist, "Malloc failed for %d pcum procs\n", nproc); ptrverify(pcumlist, "Malloc failed for %d pcum ptrs\n", nproc); for (i=0; i < nproc; i++) { /* fill pointers */ pcumlist[i] = tpcumlist+i; } npcum = cumprogs(deviatonly ? devtstat->procactive : devtstat->procall, tpcumlist, nproc); curlist = pcumlist; ncurlist = npcum; lastsortp = &plastorder; break; case MCUMCONT: threadallowed = 0; if (ccumlist) /* previous list still available? */ { free(ccumlist); free(tccumlist); clastorder = 0; } if (deviatonly) nproc = devtstat->nprocactive; else nproc = devtstat->nprocall; /* ** allocate space for new (temporary) list with ** one entry per user (list has worst-case size) */ tccumlist = calloc(sizeof(struct tstat), nproc); ccumlist = malloc(sizeof(struct tstat *) * nproc); ptrverify(tccumlist, "Malloc failed for %d ccum procs\n", nproc); ptrverify(ccumlist, "Malloc failed for %d ccum ptrs\n", nproc); for (i=0; i < nproc; i++) { /* fill pointers */ ccumlist[i] = tccumlist+i; } nccum = cumconts(deviatonly ? devtstat->procactive : devtstat->procall, tccumlist, nproc); curlist = ccumlist; ncurlist = nccum; lastsortp = &clastorder; break; default: threadallowed = 1; if (deviatonly && showtype != MPROCMEM && showorder != MSORTMEM ) { curlist = devtstat->procactive; ncurlist = devtstat->nprocactive; } else { curlist = devtstat->procall; ncurlist = devtstat->nprocall; } lastsortp = &tlastorder; if ( procsel.userid[0] == USERSTUB && !procsel.prognamesz && !procsel.utsname[0] && !procsel.states[0] && !procsel.argnamesz && !procsel.pid[0] && !suppressexit ) /* no selection wanted */ break; /* ** selection specified for tasks: ** create new (worst case) pointer list if needed */ 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) slistsz = LINES-curline-2; else if (threadview && threadallowed) slistsz = devtstat->ntaskactive; else slistsz = ncurlist; if (ncurlist > 0 && slistsz > 0) { /* ** if sorting order is changed, sort again */ if (*lastsortp != curorder) { qsort(curlist, ncurlist, sizeof(struct tstat *), procsort[(int)curorder&0x1f]); *lastsortp = curorder; zipagain = 1; } if (threadview && threadallowed) { int ntotal, j, t; if (deviatonly && showtype != MPROCMEM && showorder != MSORTMEM ) ntotal = devtstat->ntaskactive; else ntotal = devtstat->ntaskall; /* ** check if existing pointer list still usable ** if not, allocate new pointer list to be able ** to zip process list with references to threads */ if (!tsklist || ntsk != ntotal || tdeviate != deviatonly) { 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 (procsuppress(tall+t, &procsel)) continue; if (deviatonly && showtype != MPROCMEM && showorder != MSORTMEM ) { if (!(tall+t)->gen.wasinactive) tsklist[j++] = tall+t; } else { tsklist[j++] = tall+t; } } if (threadsort && j-n > 0 && curorder != MSORTMEM) { qsort(&tsklist[n], j-n, sizeof(struct tstat *), procsort[(int)curorder&0x1f]); } } zipagain = 0; } curlist = tsklist; ncurlist = j; } /* ** print the header ** first determine the column-header for the current ** sorting order of processes */ if (screen) { attron(A_REVERSE); move(curline+1, 0); } prihead(firstitem/slistsz+1, (ncurlist-1)/slistsz+1, &showtype, &curorder, showorder == MSORTAUTO ? 1 : 0, sstat->cpu.nrcpu); if (screen) { attroff(A_REVERSE); clrtobot(); } /* ** print the list */ priproc(curlist, firstitem, ncurlist, curline+2, firstitem/slistsz+1, (ncurlist-1)/slistsz+1, showtype, curorder, &syscap, nsecs, avgval); } } else // MCGROUPS: print cgroups { /* ** sort the list in required order ** (default CPU-consumption) and print the list */ if (showorder == MSORTAUTO) curorder = autoorder; else curorder = showorder; /* ** make new list with a selection (if needed) of cgroups ** merged with processes related to those cgroups */ if (cgroupsel == NULL || // not created yet cstatdeviate != deviatonly || // or not the right contents? cstatdepth != cgroupdepth || cstatorder != curorder ) { struct tstat **tp; /* ** get sorted list of pointers to the cgchainer structs ** ** when a list has been created already that is ** not suitable, first remove it */ if (cgroupsort) free(cgroupsort); cgroupsort = cgsort(cgchainers, ncgroups, curorder); /* ** determine required list of processes (all or active) ** for memory usage even non-active processes are taken */ if (deviatonly && curorder != MSORTMEM) { nproc = devtstat->nprocactive; tp = devtstat->procactive; } else { nproc = devtstat->nprocall; tp = devtstat->procall; } /* ** when a selection list has been created ** already that is not suitable, first remove it */ if (cgroupsel) free(cgroupsel); /* ** create new merged list of cgroups and processes */ ncurlist = mergecgrouplist(&cgroupsel, cgroupdepth, cgroupsort, ncgroups, tp, nproc, curorder); /* ** preserve list characteristics */ cstatdeviate = deviatonly; cstatdepth = cgroupdepth; cstatorder = curorder; } /* ** determine size of list to be displayed */ if (screen) slistsz = LINES-curline-2; else slistsz = ncurlist; if (ncurlist > 0 && slistsz > 0) { /* ** print the header */ if (screen) { attron(A_REVERSE); move(curline+1, 0); } prihead(firstitem/slistsz+1, (ncurlist-1)/slistsz+1, &showtype, &curorder, curorder == MSORTAUTO ? 1 : 0, sstat->cpu.nrcpu); if (screen) { attroff(A_REVERSE); clrtobot(); } /* ** print the list of cgroups */ pricgroup(cgroupsel, firstitem, ncurlist, curline+2, firstitem/slistsz+1, (ncurlist-1)/slistsz+1, &syscap, nsecs, avgval); } } alistsz = ncurlist; /* preserve size of active list */ /* ** in case of writing to a terminal, the user can also enter ** a character to switch options, etc */ if (screen) { /* ** refresh screen output generated sofar */ move(statline, 0); refresh(); if (twinpid && !keywaiting) // twin mode? { struct sigaction sigact, sigold; /* ** catch window size changes while in select */ memset(&sigact, 0, sizeof sigact); sigact.sa_handler = getsigwinch; sigaction(SIGWINCH, &sigact, &sigold); winchange = 0; /* ** await input character from keyboard, or ** inotify trigger in case of twin mode, or ** interval timer expiration */ FD_ZERO(&readfds); FD_SET(0, &readfds); if (!paused && fdinotify != -1) // twin mode? { FD_SET(fdinotify, &readfds); nrfds = fdinotify + 1; } else { nrfds = 1; } switch (select(nrfds, &readfds, (fd_set *)0, (fd_set *)0, (struct timeval *)0)) { case -1: /* ** window change or timer expiration? */ if (winchange) { // window change: set new dimensions struct winsize w; ioctl(0, TIOCGWINSZ, &w); resizeterm(w.ws_row, w.ws_col); lastchar = KEY_RESIZE; } else { // time interrupt lastchar = 0; } break; default: /* ** inotify trigger that new sample has been written? ** pretend as if the 't' key has been pressed ** to read that sample ** ** otherwise: read keystroke from keyboard */ if (FD_ISSET(fdinotify, &readfds)) { read(fdinotify, eventbuf, sizeof eventbuf); lastchar = MSAMPNEXT; } else { lastchar = getch(); } } sigaction(SIGWINCH, &sigold, (struct sigaction *)0); } else // no twin mode: neutral state is getch() { lastchar = getch(); keywaiting = 0; } switch (lastchar) { /* ** timer expired */ case ERR: case 0: timeout(0); (void) getch(); timeout(-1); goto free_and_return; /* ** stop it */ case MQUIT: move(LINES-1, 0); clrtoeol(); refresh(); cleanstop(0); /* ** switch to bar graph mode */ case MBARGRAPH: erase(); refresh(); goto free_and_return; /* ** manual trigger for next sample */ case MSAMPNEXT: if (paused && !twinpid) { beep(); break; } getalarm(0); goto free_and_return; /* ** manual trigger for previous sample */ case MSAMPPREV: if (!rawreadflag) { statmsg = "Only allowed in twin mode or " "when viewing raw file!"; beep(); break; } if (!paused && twinpid) { paused=1; // implicit pause in twin mode clrtoeol(); refresh(); } goto free_and_return; /* ** branch to certain time stamp */ case MSAMPBRANCH: if (!rawreadflag) { statmsg = "Only allowed in twin mode or " "when viewing raw file!"; beep(); break; } if (!paused && twinpid) { paused=1; // implicit pause in twin mode clrtoeol(); refresh(); } echo(); move(statline, 0); clrtoeol(); printw("Enter new time " "(format [YYYYMMDD]hhmm[ss]): "); branchtime[0] = '\0'; scanw("%31s\n", branchtime); noecho(); begintime = cursortime; if ( !getbranchtime(branchtime, &begintime) ) { move(statline, 0); clrtoeol(); statmsg = "Wrong time format!"; beep(); begintime = 0; break; } goto free_and_return; /* ** sort order automatically depending on ** most busy resource */ case MSORTAUTO: showorder = MSORTAUTO; firstitem = 0; break; /* ** sort in cpu-activity order */ case MSORTCPU: showorder = MSORTCPU; firstitem = 0; break; /* ** sort in memory-consumption order */ case MSORTMEM: showorder = MSORTMEM; firstitem = 0; break; /* ** sort in disk-activity order */ case MSORTDSK: showorder = MSORTDSK; firstitem = 0; break; /* ** sort in network-activity order */ case MSORTNET: if ( !(supportflags & NETATOP || supportflags & NETATOPBPF)) { statmsg = "Module 'netatop' or 'netatop-bpf' not " "active or no root privs"; break; } showorder = MSORTNET; firstitem = 0; break; /* ** sort in gpu-activity order */ case MSORTGPU: if ( !(supportflags & GPUSTAT) ) { statmsg = "No GPU activity figures " "available; request ignored!"; break; } showorder = MSORTGPU; firstitem = 0; break; /* ** general figures per process */ case MPROCGEN: showtype = MPROCGEN; if (showorder != MSORTAUTO) showorder = MSORTCPU; firstitem = 0; break; /* ** memory-specific figures per process */ case MPROCMEM: showtype = MPROCMEM; if (showorder != MSORTAUTO) showorder = MSORTMEM; firstitem = 0; break; /* ** disk-specific figures per process */ case MPROCDSK: showtype = MPROCDSK; if (showorder != MSORTAUTO) showorder = MSORTDSK; firstitem = 0; break; /* ** network-specific figures per process */ case MPROCNET: if ( !(supportflags & NETATOP || supportflags & NETATOPBPF) ) { statmsg = "Module 'netatop' or 'netatop-bpf' not " "active or no root privs; " "request ignored!"; break; } showtype = MPROCNET; if (showorder != MSORTAUTO) showorder = MSORTNET; firstitem = 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; firstitem = 0; break; /* ** various info per process */ case MPROCVAR: showtype = MPROCVAR; firstitem = 0; break; /* ** command line per process */ case MPROCARG: showtype = MPROCARG; firstitem = 0; break; /* ** cgroup v2 info per process */ case MCGROUPS: if ( !(supportflags & CGROUPV2) ) { statmsg = "No cgroup v2 metrics " "available; request ignored!"; break; } showtype = MCGROUPS; firstitem = 0; break; case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': cgroupdepth = lastchar - 0x30; firstitem = 0; break; /* ** own defined output per process */ case MPROCOWN: if (! ownprocs[0].pf) { statmsg = "Own process line is not " "configured in rc-file; " "request ignored"; break; } showtype = MPROCOWN; firstitem = 0; break; /* ** scheduling-values per process */ case MPROCSCH: showtype = MPROCSCH; if (showorder != MSORTAUTO) showorder = MSORTCPU; firstitem = 0; break; /* ** accumulated resource consumption per user */ case MCUMUSER: statmsg = "Consumption per user; use 'a' to " "toggle between all/active processes"; showtype = MCUMUSER; firstitem = 0; break; /* ** accumulated resource consumption per program */ case MCUMPROC: statmsg = "Consumption per program; use 'a' to " "toggle between all/active processes"; showtype = MCUMPROC; firstitem = 0; break; /* ** accumulated resource consumption per container/pod */ case MCUMCONT: if (!rawreadflag && !rootprivs()) { statmsg = "No privileges to get " "container/pod identity!"; break; } statmsg = "Consumption per container/pod; use 'a' to " "toggle between all/active processes"; showtype = MCUMCONT; firstitem = 0; break; /* ** help wanted? */ case MHELP1: case MHELP2: alarm(0); /* stop the clock */ move(1, 0); clrtobot(); /* blank the screen */ refresh(); showhelp(2); move(statline, 0); if (interval && !paused && !rawreadflag) alarm(1); /* force new sample */ firstitem = 0; if (twinpid) // twin mode? { // jump to last sample after awaiting input begintime = 0x7fffffff; lastchar = MSAMPBRANCH; goto free_and_return; } break; /* ** send signal to process */ case MKILLPROC: if (rawreadflag && !twinpid) { statmsg = "Not possible when viewing " "raw file!"; beep(); break; } alarm(0); /* stop the clock */ killpid = getnumval("Pid of process: ", 0, statline); switch (killpid) { case 0: case -1: break; case 1: statmsg = "Sending signal to pid 1 not " "allowed!"; beep(); break; default: clrtoeol(); killsig = getnumval("Signal [%d]: ", 15, statline); if ( kill(killpid, killsig) == -1) { statmsg = "Not possible to " "send signal to this pid!"; beep(); } } if (!paused && !twinpid) alarm(3); /* set short timer */ firstitem = 0; if (twinpid) // twin mode? { begintime = 0x7fffffff; lastchar = MSAMPBRANCH; goto free_and_return; } break; /* ** change interval timeout */ case MINTERVAL: if (rawreadflag) { statmsg = "Not possible in twin mode or " "when viewing raw file!"; beep(); break; } alarm(0); /* stop the clock */ interval = getnumval("New interval in seconds " "(now %d): ", interval, statline); if (interval) { if (!paused) alarm(1); /* set short timer */ } else { statmsg = "No timer set; waiting for " "manual trigger ('t')....."; } firstitem = 0; if (twinpid) // twin mode? { begintime = 0x7fffffff; lastchar = MSAMPBRANCH; goto free_and_return; } break; /* ** focus on specific user */ case MSELUSER: alarm(0); /* stop the clock */ echo(); move(statline, 0); clrtoeol(); printw("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 */ firstitem = 0; if (twinpid) // twin mode? { begintime = 0x7fffffff; lastchar = MSAMPBRANCH; goto free_and_return; } break; /* ** focus on specific process-name */ case MSELPROC: alarm(0); /* stop the clock */ echo(); move(statline, 0); clrtoeol(); printw("Process-name as regular " "expression (enter=no regex): "); procsel.prognamesz = 0; procsel.progname[0] = '\0'; scanw("%63s\n", procsel.progname); procsel.prognamesz = strlen(procsel.progname); if (procsel.prognamesz) { if (regcomp(&procsel.progregex, procsel.progname, REG_NOSUB)) { statmsg = "Invalid regular " "expression!"; beep(); procsel.prognamesz = 0; procsel.progname[0] = '\0'; } } noecho(); move(statline, 0); if (interval && !paused && !rawreadflag) alarm(3); /* set short timer */ firstitem = 0; if (twinpid) // twin mode? { begintime = 0x7fffffff; lastchar = MSAMPBRANCH; goto free_and_return; } break; /* ** focus on specific container/pod id */ case MSELCONT: if (!rawreadflag && !rootprivs()) { statmsg = "No privileges to get " "container/pod identity!"; beep(); break; } alarm(0); /* stop the clock */ echo(); move(statline, 0); clrtoeol(); printw("Container or pod name (enter=all, " "'host'=host processes): "); procsel.utsname[0] = '\0'; scanw("%15s", procsel.utsname); procsel.utsname[UTSLEN] = '\0'; switch (strlen(procsel.utsname)) { case 0: break; // enter key pressed case 4: // host? if (strcmp(procsel.utsname, "host") == 0) { procsel.utsname[0] = 'H'; procsel.utsname[1] = '\0'; } break; } noecho(); move(statline, 0); if (interval && !paused && !rawreadflag) alarm(3); /* set short timer */ firstitem = 0; if (twinpid) // twin mode? { begintime = 0x7fffffff; lastchar = MSAMPBRANCH; goto free_and_return; } break; /* ** focus on specific PIDs */ case MSELPID: alarm(0); /* stop the clock */ echo(); move(statline, 0); clrtoeol(); printw("Comma-separated PIDs of processes " "(enter=no selection): "); scanw("%79s\n", genline); int id = 0; char *pidp = strtok(genline, ","); while (pidp) { char *ep; if (id >= MAXPID-1) { procsel.pid[id] = 0; // stub statmsg = "Maximum number of" "PIDs reached!"; beep(); break; } procsel.pid[id] = strtol(pidp, &ep, 10); if (*ep) { statmsg = "Non-numerical PID!"; beep(); procsel.pid[0] = 0; // stub break; } id++; pidp = strtok(NULL, ","); } procsel.pid[id] = 0; // stub noecho(); move(statline, 0); if (interval && !paused && !rawreadflag) alarm(3); /* set short timer */ firstitem = 0; if (twinpid) // twin mode? { begintime = 0x7fffffff; lastchar = MSAMPBRANCH; goto free_and_return; } break; /* ** focus on specific process/thread state */ case MSELSTATE: alarm(0); /* stop the clock */ echo(); move(statline, 0); clrtoeol(); /* Linux fs/proc/array.c - task_state_array */ printw("Comma-separated thread states within process " "(R|S|D|I|T|t|X|Z|P): "); memset(procsel.states, 0, sizeof procsel.states); scanw("%15s\n", genline); char *sp = strtok(genline, ","); while (sp && *sp) { if (isspace(*sp)) { sp++; continue; } if (strlen(sp) > 1) { statmsg = "Invalid state!"; memset(procsel.states, 0, sizeof procsel.states); break; } int needed = 0; switch (*sp) { case 'R': /* running */ case 'S': /* sleeping */ case 'D': /* disk sleep */ case 'I': /* idle */ case 'T': /* stopped */ case 't': /* tracing stop */ case 'X': /* dead */ case 'Z': /* zombie */ case 'P': /* parked */ if (!strchr(procsel.states, *sp)) needed = 1; break; default: statmsg = "Invalid state!"; memset(procsel.states, 0, sizeof procsel.states); beep(); break; } if (needed) procsel.states[strlen(procsel.states)] = *sp; sp = strtok(NULL, ","); } noecho(); move(statline, 0); if (interval && !paused && !rawreadflag) alarm(3); /* set short timer */ firstitem = 0; if (twinpid) // twin mode? { begintime = 0x7fffffff; lastchar = MSAMPBRANCH; goto free_and_return; } break; /* ** focus on specific command line arguments */ case MSELARG: alarm(0); /* stop the clock */ echo(); move(statline, 0); clrtoeol(); printw("Command line string as regular " "expression (enter=no regex): "); procsel.argnamesz = 0; procsel.argname[0] = '\0'; scanw("%63s\n", procsel.argname); procsel.argnamesz = strlen(procsel.argname); if (procsel.argnamesz) { if (regcomp(&procsel.argregex, procsel.argname, REG_NOSUB)) { statmsg = "Invalid regular " "expression!"; beep(); procsel.argnamesz = 0; procsel.argname[0] = '\0'; } } noecho(); move(statline, 0); if (interval && !paused && !rawreadflag) alarm(3); /* set short timer */ firstitem = 0; if (twinpid) // twin mode? { begintime = 0x7fffffff; lastchar = MSAMPBRANCH; goto free_and_return; } break; /* ** focus on specific system resource */ case MSELSYS: alarm(0); /* stop the clock */ echo(); move(statline, 0); clrtoeol(); printw("Logical volume name as regular " "expression (enter=no specific name): "); syssel.lvmnamesz = 0; syssel.lvmname[0] = '\0'; scanw("%63s\n", syssel.lvmname); syssel.lvmnamesz = strlen(syssel.lvmname); if (syssel.lvmnamesz) { if (regcomp(&syssel.lvmregex, syssel.lvmname, REG_NOSUB)) { statmsg = "Invalid regular " "expression!"; beep(); syssel.lvmnamesz = 0; syssel.lvmname[0] = '\0'; } } move(statline, 0); clrtoeol(); printw("Disk name as regular " "expression (enter=no specific name): "); syssel.dsknamesz = 0; syssel.dskname[0] = '\0'; scanw("%63s\n", syssel.dskname); syssel.dsknamesz = strlen(syssel.dskname); if (syssel.dsknamesz) { if (regcomp(&syssel.dskregex, syssel.dskname, REG_NOSUB)) { statmsg = "Invalid regular " "expression!"; beep(); syssel.dsknamesz = 0; syssel.dskname[0] = '\0'; } } move(statline, 0); clrtoeol(); printw("Interface name as regular " "expression (enter=no specific name): "); syssel.itfnamesz = 0; syssel.itfname[0] = '\0'; scanw("%63s\n", syssel.itfname); syssel.itfnamesz = strlen(syssel.itfname); if (syssel.itfnamesz) { if (regcomp(&syssel.itfregex, syssel.itfname, REG_NOSUB)) { statmsg = "Invalid regular " "expression!"; beep(); syssel.itfnamesz = 0; syssel.itfname[0] = '\0'; } } noecho(); move(statline, 0); if (interval && !paused && !rawreadflag) alarm(3); /* set short timer */ firstitem = 0; if (twinpid) // twin mode? { begintime = 0x7fffffff; lastchar = MSAMPBRANCH; goto free_and_return; } break; /* ** toggle pause-state */ case MPAUSE: if (rawreadflag && !twinpid) { statmsg = "Just use 'T' and 't' to browse..."; break; } if (paused) { paused=0; clrtoeol(); refresh(); if (!rawreadflag) { alarm(1); /* start the clock */ } else { begintime = 0x7fffffff; lastchar = MSAMPBRANCH; goto free_and_return; } } else { paused=1; clrtoeol(); refresh(); alarm(0); /* stop the clock */ } break; /* ** toggle between modified processes and ** all processes, or between used cgroups and ** all cgroups */ case MALLACTIVE: if (deviatonly) { deviatonly=0; statmsg = "All processes/threads/cgroups will be " "shown/accumulated..."; } else { deviatonly=1; statmsg = "Only active processes/threads/cgroups " "will be shown/accumulated..."; } tlastorder = 0; firstitem = 0; break; /* ** toggle average or total values */ case MAVGVAL: if (avgval) avgval=0; else avgval=1; break; /* ** system-statistics lines: ** toggle fixed or variable */ case MSYSFIXED: if (fixedhead) { fixedhead=0; statmsg = "Only active system-resources" " will be shown ......"; } else { fixedhead=1; statmsg = "Also inactive " "system-resources will be shown....."; } firstitem = 0; break; /* ** system-statistics lines: ** toggle fixed or variable */ case MSYSNOSORT: if (sysnosort) { sysnosort=0; statmsg = "System resources will be " "sorted on utilization..."; } else { sysnosort=1; statmsg = "System resources will not " "be sorted on utilization..."; } firstitem = 0; break; /* ** per-thread view wanted with sorting on ** process level */ case MTHREAD: if (threadview) { threadview = 0; statmsg = "Thread view disabled"; firstitem = 0; } else { threadview = 1; statmsg = "Thread view enabled"; firstitem = 0; } break; /* ** sorting on thread level as well (threadview) */ case MTHRSORT: if (threadsort) { threadsort = 0; statmsg = "Thread sorting disabled for thread view"; firstitem = 0; } else { threadsort = 1; statmsg = "Thread sorting enabled for thread view"; firstitem = 0; } break; /* ** per-process PSS calculation wanted */ case MCALCPSS: if (rawreadflag) { statmsg = "PSIZE gathering depends " "on rawfile"; break; } if (calcpss) { calcpss = 0; statmsg = "PSIZE gathering disabled"; } else { calcpss = 1; if (rootprivs()) statmsg = "PSIZE gathering enabled"; else statmsg = "PSIZE gathering only " "for own processes"; } break; /* ** per-thread WCHAN definition */ case MGETWCHAN: if (getwchan) { getwchan = 0; statmsg = "WCHAN gathering disabled"; } else { getwchan = 1; statmsg = "WCHAN gathering enabled"; } break; /* ** suppression of terminated processes in output */ case MSUPEXITS: if (suppressexit) { suppressexit = 0; statmsg = "Exited processes will " "be shown/accumulated"; firstitem = 0; } else { suppressexit = 1; statmsg = "Exited processes will " "not be shown/accumulated"; firstitem = 0; } break; /* ** screen lines: ** toggle for colors */ case MCOLORS: if (usecolors) { usecolors=0; statmsg = "No colors will be used..."; } else { if (screen && has_colors()) { usecolors=1; statmsg = "Colors will be used..."; } else { statmsg="No colors supported!"; } } firstitem = 0; break; /* ** system-statistics lines: ** toggle no or all active disk */ case MSYSLIMIT: alarm(0); /* stop the clock */ maxcpulines = getnumval("Maximum lines for per-cpu " "statistics (now %d): ", maxcpulines, statline); maxgpulines = getnumval("Maximum lines for per-gpu " "statistics (now %d): ", maxgpulines, statline); if (sstat->dsk.nlvm > 0) { maxlvmlines = getnumval("Maximum lines for LVM " "statistics (now %d): ", maxlvmlines, statline); } if (sstat->dsk.nmdd > 0) { maxmddlines = getnumval("Maximum lines for MD " "device statistics (now %d): ", maxmddlines, statline); } maxdsklines = getnumval("Maximum lines for disk " "statistics (now %d): ", maxdsklines, statline); maxintlines = getnumval("Maximum lines for interface " "statistics (now %d): ", maxintlines, statline); maxifblines = getnumval("Maximum lines for infiniband " "port statistics (now %d): ", maxifblines, statline); maxnfslines = getnumval("Maximum lines for NFS mount " "statistics (now %d): ", maxnfslines, statline); maxcontlines = getnumval("Maximum lines for container " "statistics (now %d): ", maxcontlines, statline); maxnumalines = getnumval("Maximum lines for numa " "statistics (now %d): ", maxnumalines, statline); maxllclines = getnumval("Maximum lines for LLC " "statistics (now %d): ", maxllclines, statline); if (interval && !paused && !rawreadflag) alarm(1); /* set short timer */ firstitem = 0; if (twinpid) // twin mode? { begintime = 0x7fffffff; lastchar = MSAMPBRANCH; goto free_and_return; } break; /* ** reset counters */ case MRESET: getalarm(0); /* restart the clock */ paused = 0; if (twinpid) { paused=1; // implicit pause in twin mode clrtoeol(); refresh(); } goto free_and_return; /* ** show version info */ case MVERSION: statmsg = getstrvers(); break; /* ** handle redraw request */ case MREDRAW: wclear(stdscr); break; /* ** handle arrow right for command line */ case KEY_RIGHT: startoffset++; break; /* ** handle arrow left for command line */ case KEY_LEFT: if (startoffset > 0) startoffset--; break; /* ** handle arrow down to go one line down */ case KEY_DOWN: if (firstitem < alistsz-1) firstitem += 1; break; /* ** handle arrow up to go one line up */ case KEY_UP: if (firstitem > 0) firstitem -= 1; break; /* ** handle forward */ case KEY_NPAGE: case MLISTFW: if (alistsz-firstitem > slistsz) firstitem += slistsz; break; /* ** handle backward */ case KEY_PPAGE: case MLISTBW: if (firstitem >= slistsz) firstitem -= slistsz; else firstitem = 0; break; /* ** handle screen resize */ case KEY_RESIZE: snprintf(statbuf, sizeof statbuf, "Window resized to %dx%d...", COLS, LINES); statmsg = statbuf; timeout(0); (void) getch(); timeout(-1); break; /* ** unknown key-stroke */ default: beep(); } } else /* no screen */ { lastchar = '\0'; goto free_and_return; } } free_and_return: 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); if (cgroupsort) free(cgroupsort); if (cgroupsel) free(cgroupsel); return lastchar; } /* ** accumulate all processes per user in new list */ static int cumusers(struct tstat **curprocs, struct tstat *curusers, int numprocs) { register int i, numusers; /* ** sort list of active processes in order of uid (increasing) */ qsort(curprocs, numprocs, sizeof(struct tstat *), compusr); /* ** accumulate all processes per user in the new list */ for (numusers=i=0; i < numprocs; i++, curprocs++) { if (procsuppress(*curprocs, &procsel)) continue; if ((*curprocs)->gen.state == 'E' && suppressexit) continue; if ( curusers->gen.ruid != (*curprocs)->gen.ruid ) { if (curusers->gen.pid) { numusers++; curusers++; } curusers->gen.ruid = (*curprocs)->gen.ruid; } accumulate(*curprocs, curusers); } if (curusers->gen.pid) numusers++; return numusers; } /* ** accumulate all processes with the same name (i.e. same program) ** into a new list */ static int cumprogs(struct tstat **curprocs, struct tstat *curprogs, int numprocs) { register int i, numprogs; /* ** sort list of active processes in order of process-name */ qsort(curprocs, numprocs, sizeof(struct tstat *), compnam); /* ** accumulate all processes with same name in the new list */ for (numprogs=i=0; i < numprocs; i++, curprocs++) { if (procsuppress(*curprocs, &procsel)) continue; if ((*curprocs)->gen.state == 'E' && suppressexit) continue; if ( strcmp(curprogs->gen.name, (*curprocs)->gen.name) != 0) { if (curprogs->gen.pid) { numprogs++; curprogs++; } strcpy(curprogs->gen.name, (*curprocs)->gen.name); } accumulate(*curprocs, curprogs); } if (curprogs->gen.pid) numprogs++; return numprogs; } /* ** accumulate all processes per container/pod in new list */ static int cumconts(struct tstat **curprocs, struct tstat *curconts, int numprocs) { register int i, numconts; /* ** sort list of active processes in order of container/pod (increasing) */ qsort(curprocs, numprocs, sizeof(struct tstat *), compcon); /* ** accumulate all processes per container/pod in the new list */ for (numconts=i=0; i < numprocs; i++, curprocs++) { if (procsuppress(*curprocs, &procsel)) continue; if ((*curprocs)->gen.state == 'E' && suppressexit) continue; if ( strcmp(curconts->gen.utsname, (*curprocs)->gen.utsname) != 0) { if (curconts->gen.pid) { numconts++; curconts++; } strcpy(curconts->gen.utsname, (*curprocs)->gen.utsname); } accumulate(*curprocs, curconts); } if (curconts->gen.pid) numconts++; return numconts; } /* ** accumulate relevant counters from individual task to ** combined task */ static void accumulate(struct tstat *curproc, struct tstat *curstat) { count_t nett_wsz; curstat->gen.pid++; /* misuse as counter */ curstat->gen.isproc = 1; curstat->gen.nthr += curproc->gen.nthr; curstat->cpu.utime += curproc->cpu.utime; curstat->cpu.stime += curproc->cpu.stime; curstat->cpu.nvcsw += curproc->cpu.nvcsw; curstat->cpu.nivcsw += curproc->cpu.nivcsw; curstat->cpu.rundelay += curproc->cpu.rundelay; curstat->cpu.blkdelay += curproc->cpu.blkdelay; if (curproc->dsk.wsz > curproc->dsk.cwsz) nett_wsz = curproc->dsk.wsz -curproc->dsk.cwsz; else nett_wsz = 0; curstat->dsk.rio += curproc->dsk.rsz; curstat->dsk.wio += nett_wsz; curstat->dsk.rsz = curstat->dsk.rio; curstat->dsk.wsz = curstat->dsk.wio; curstat->net.tcpsnd += curproc->net.tcpsnd; curstat->net.tcprcv += curproc->net.tcprcv; curstat->net.udpsnd += curproc->net.udpsnd; curstat->net.udprcv += curproc->net.udprcv; curstat->net.tcpssz += curproc->net.tcpssz; curstat->net.tcprsz += curproc->net.tcprsz; curstat->net.udpssz += curproc->net.udpssz; curstat->net.udprsz += curproc->net.udprsz; if (curproc->gen.state != 'E') { if (curproc->mem.pmem != -1) // no errors? curstat->mem.pmem += curproc->mem.pmem; curstat->mem.vmem += curproc->mem.vmem; curstat->mem.rmem += curproc->mem.rmem; curstat->mem.vlibs += curproc->mem.vlibs; curstat->mem.vdata += curproc->mem.vdata; curstat->mem.vstack += curproc->mem.vstack; curstat->mem.vswap += curproc->mem.vswap; curstat->mem.vlock += curproc->mem.vlock; curstat->mem.rgrow += curproc->mem.rgrow; curstat->mem.vgrow += curproc->mem.vgrow; if (curproc->gpu.state) // GPU is use? { int i; curstat->gpu.state = 'A'; if (curproc->gpu.gpubusy == -1) curstat->gpu.gpubusy = -1; else curstat->gpu.gpubusy += curproc->gpu.gpubusy; if (curproc->gpu.membusy == -1) curstat->gpu.membusy = -1; else curstat->gpu.membusy += curproc->gpu.membusy; curstat->gpu.memnow += curproc->gpu.memnow; curstat->gpu.gpulist |= curproc->gpu.gpulist; curstat->gpu.nrgpus = 0; for (i=0; i < MAXGPU; i++) { if (curstat->gpu.gpulist & 1<gpu.nrgpus++; } } } } /* ** function that checks if the current process or thread must be ** selected or suppressed ** ** returns 1 (suppress) or 0 (do not suppress) */ static int procsuppress(struct tstat *curstat, struct pselection *sel) { /* ** check if only processes of a particular user ** should be shown */ if (sel->userid[0] != USERSTUB) { int u = 0; while (sel->userid[u] != USERSTUB) { if (sel->userid[u] == curstat->gen.ruid) break; u++; } if (sel->userid[u] != curstat->gen.ruid) return 1; } /* ** check if only processes with particular PIDs ** should be shown */ if (sel->pid[0]) { int i = 0; while (sel->pid[i]) { if (sel->pid[i] == curstat->gen.tgid) break; i++; } if (sel->pid[i] != curstat->gen.tgid) return 1; } /* ** check if only processes with a particular name ** should be shown */ if (sel->prognamesz && regexec(&(sel->progregex), curstat->gen.name, 0, NULL, 0)) return 1; /* ** check if only processes with a particular command line string ** should be shown */ if (sel->argnamesz) { if (curstat->gen.cmdline[0]) { if (regexec(&(sel->argregex), curstat->gen.cmdline, 0, NULL, 0)) return 1; } else { if (regexec(&(sel->argregex), curstat->gen.name, 0, NULL, 0)) return 1; } } /* ** check if only processes related to a particular container/pod ** should be shown (container/pod 'H' stands for native host processes) */ if (sel->utsname[0]) { if (sel->utsname[0] == 'H') // only host processes { if (curstat->gen.utsname[0]) return 1; } else { if (memcmp(sel->utsname, curstat->gen.utsname, 12)) return 1; } } /* ** check if only processes in specific states should be shown ** ** notice that the state of a process (i.e. the main thread) ** may be 'S' while a thread in the process might have state 'R' ** --> still show the process in that case! */ if (sel->states[0]) { // check the states of the threads of this process // if (strchr(sel->states, 'R') && curstat->gen.nthrrun) return 0; if (strchr(sel->states, 'S') && curstat->gen.nthrslpi) return 0; if (strchr(sel->states, 'D') && curstat->gen.nthrslpu) return 0; if (strchr(sel->states, 'I') && curstat->gen.nthridle) return 0; // check the state of the process itself // if (strchr(sel->states, curstat->gen.state) == NULL) return 1; } return 0; } static void limitedlines(void) { if (maxcpulines == 999) // default? maxcpulines = 0; if (maxgpulines == 999) // default? maxgpulines = 2; if (maxdsklines == 999) // default? maxdsklines = 3; if (maxmddlines == 999) // default? maxmddlines = 3; if (maxlvmlines == 999) // default? maxlvmlines = 4; if (maxintlines == 999) // default? maxintlines = 2; if (maxifblines == 999) // default? maxifblines = 2; if (maxnfslines == 999) // default? maxnfslines = 2; if (maxcontlines == 999) // default? maxcontlines = 1; if (maxnumalines == 999) // default? maxnumalines = 0; if (maxllclines == 999) // default? maxllclines = 0; } /* ** get a numerical value from the user and verify */ static long getnumval(char *ask, long valuenow, int statline) { char numval[16]; long retval; echo(); move(statline, 0); clrtoeol(); printw(ask, valuenow); numval[0] = 0; scanw("%15s", numval); move(statline, 0); noecho(); if (numval[0]) /* data entered ? */ { if ( numeric(numval) ) { retval = atol(numval); } else { beep(); clrtoeol(); printw("Value not numeric (current value kept)!"); refresh(); sleep(2); retval = valuenow; } } else { retval = valuenow; } return retval; } /* ** generic print-function which checks if printf should be used ** (to file or pipe) or curses (to screen) */ void printg(const char *format, ...) { va_list args; va_start(args, format); if (screen) vw_printw(stdscr, (char *) format, args); else vprintf(format, args); va_end(args); } /* ** initialize generic sample output functions */ static void generic_init(void) { int i; /* ** check if default sort order and/or showtype are overruled ** by command-line flags */ for (i=0; flaglist[i]; i++) { switch (flaglist[i]) { case MSORTAUTO: showorder = MSORTAUTO; break; case MSORTCPU: showorder = MSORTCPU; break; case MSORTGPU: showorder = MSORTGPU; break; case MSORTMEM: showorder = MSORTMEM; break; case MSORTDSK: showorder = MSORTDSK; break; case MSORTNET: showorder = MSORTNET; break; case MPROCGEN: showtype = MPROCGEN; showorder = MSORTCPU; break; case MPROCGPU: showtype = MPROCGPU; showorder = MSORTGPU; break; case MPROCMEM: showtype = MPROCMEM; showorder = MSORTMEM; break; case MPROCSCH: showtype = MPROCSCH; showorder = MSORTCPU; break; case MPROCDSK: showtype = MPROCDSK; showorder = MSORTDSK; break; case MPROCNET: if ( !(supportflags & NETATOP || supportflags & NETATOPBPF) ) { fprintf(stderr, "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 MCGROUPS: if ( !(supportflags & CGROUPV2) ) { fprintf(stderr, "No cgroup v2 metrics " "available; request ignored!\n"); sleep(3); break; } showtype = MCGROUPS; break; case MPROCOWN: showtype = MPROCOWN; break; case MAVGVAL: if (avgval) avgval=0; else avgval=1; break; case MCUMUSER: showtype = MCUMUSER; break; case MCUMPROC: showtype = MCUMPROC; break; case MCUMCONT: if (!rawreadflag && !rootprivs()) { fprintf(stderr, "No privileges to get " "container/pod identity!\n"); sleep(3); break; } showtype = MCUMCONT; break; case MSYSFIXED: if (fixedhead) fixedhead=0; else fixedhead=1; break; case MSYSNOSORT: if (sysnosort) sysnosort=0; else sysnosort=1; break; case MTHREAD: if (threadview) threadview = 0; else threadview = 1; break; case MTHRSORT: if (threadsort) threadsort = 0; else threadsort = 1; break; case MCALCPSS: if (rawreadflag) { fprintf(stderr, "PSIZE gathering depends on rawfile\n"); sleep(3); break; } if (calcpss) { calcpss = 0; } else { calcpss = 1; if (!rootprivs()) { fprintf(stderr, "PSIZE gathering only for own " "processes\n"); sleep(3); } } break; case MGETWCHAN: if (getwchan) getwchan = 0; else getwchan = 1; break; case MSUPEXITS: if (suppressexit) suppressexit = 0; else suppressexit = 1; break; case MCOLORS: if (usecolors) usecolors=0; else usecolors=1; break; case MSYSLIMIT: limitedlines(); break; case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': cgroupdepth = flaglist[i] - 0x30; break; default: prusage("atop"); } } /* ** set stdout output on line-basis */ setvbuf(stdout, (char *)0, _IOLBF, BUFSIZ); /* ** check if STDOUT is related to a tty or ** something else (file, pipe) */ if ( isatty(fileno(stdout)) ) screen = 1; else screen = 0; /* ** install catch-routine to finish in a controlled way ** and activate cbreak mode */ if (screen) { /* ** if stdin is not connected to a tty (might be redirected ** to pipe or file), close it and duplicate stdout (tty) ** to stdin */ if ( !isatty(fileno(stdin)) ) { (void) dup2(fileno(stdout), fileno(stdin)); } /* ** initialize screen-handling via curses */ setlocale(LC_ALL, ""); setlocale(LC_NUMERIC, "C"); initscr(); cbreak(); noecho(); keypad(stdscr, TRUE); /* ** verify minimal dimensions */ if (COLS < MINCOLUMNS || LINES < MINLINES) { endwin(); // finish ncurses interface fprintf(stderr, "Terminal size should be at least " "%d columns by %d lines\n", MINCOLUMNS, MINLINES); cleanstop(1); } if (has_colors()) { use_default_colors(); start_color(); // color definitions // init_color(COLOR_MYORANGE, 675, 500, 50); init_color(COLOR_MYGREEN, 0, 600, 100); init_color(COLOR_MYGREY, 240, 240, 240); init_color(COLOR_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; char mode; // 'l' - live, 'r' - rawlog, 'a' - all } helptext[] = { {"Display mode:\n", ' ', 'a'}, {"\t'%c' - show bar graphs for system utilization (toggle)\n", MBARGRAPH, 'a'}, {"\n", ' ', 'a'}, {"Information in text mode about cgroups v2:\n", ' ', 'a'}, {"\t'%c' - cgroups v2 metrics\n", MCGROUPS, 'a'}, {"\t 2-7 - cgroups tree level selection (default 7)\n", ' ', 'a'}, {"\t 8 - cgroups with related processes, except kernel processes\n", ' ', 'a'}, {"\t 9 - cgroups with related processes\n", ' ', 'a'}, {"\t'%c' - show all cgroups/processes i.s.o. only active ones (toggle)\n", MALLACTIVE, 'a'}, {"\t'%c' - sort on cpu activity\n", MSORTCPU, 'a'}, {"\t'%c' - sort on memory utilization\n", MSORTMEM, 'a'}, {"\t'%c' - sort on disk transfer rate\n", MSORTDSK, 'a'}, {"\n", ' ', 'a'}, {"Information in text mode about processes:\n", ' ', 'a'}, {"\t'%c' - generic info (default)\n", MPROCGEN, 'a'}, {"\t'%c' - memory details\n", MPROCMEM, 'a'}, {"\t'%c' - disk details\n", MPROCDSK, 'a'}, {"\t'%c' - network details\n", MPROCNET, 'a'}, {"\t'%c' - scheduling and thread-group info\n", MPROCSCH, 'a'}, {"\t'%c' - GPU details\n", MPROCGPU, 'a'}, {"\t'%c' - various info (ppid, user/group, date/time, status, " "exitcode)\n", MPROCVAR, 'a'}, {"\t'%c' - full command line per process\n", MPROCARG, 'a'}, {"\t'%c' - use own output line definition\n", MPROCOWN, 'a'}, {"\n", ' ', 'a'}, {"Sort list of processes in order of:\n", ' ', 'a'}, {"\t'%c' - cpu activity\n", MSORTCPU, 'a'}, {"\t'%c' - memory consumption\n", MSORTMEM, 'a'}, {"\t'%c' - disk activity\n", MSORTDSK, 'a'}, {"\t'%c' - network activity\n", MSORTNET, 'a'}, {"\t'%c' - GPU activity\n", MSORTGPU, 'a'}, {"\t'%c' - most active system resource (auto mode)\n", MSORTAUTO, 'a'}, {"\n", ' ', 'a'}, {"Raw file viewing and twin mode:\n", ' ', 'r'}, {"\t'%c' - show next sample\n", MSAMPNEXT, 'r'}, {"\t'%c' - show previous sample\n", MSAMPPREV, 'r'}, {"\t'%c' - branch to certain time\n", MSAMPBRANCH, 'r'}, {"\t'%c' - rewind to begin\n", MRESET, 'r'}, {"\t'%c' - pause button to freeze or continue (twin mode)\n", MPAUSE, 'r'}, {"\n", ' ', 'r'}, {"Accumulated process figures:\n", ' ', 'a'}, {"\t'%c' - total resource consumption per user\n", MCUMUSER, 'a'}, {"\t'%c' - total resource consumption per program (i.e. same " "process name)\n", MCUMPROC, 'a'}, {"\t'%c' - total resource consumption per container/pod\n",MCUMCONT, 'a'}, {"\n", ' ', 'a'}, {"Process selections (keys shown in header line):\n", ' ', 'a'}, {"\t'%c' - focus on specific user name " "(regular expression)\n", MSELUSER, 'a'}, {"\t'%c' - focus on specific program name " "(regular expression)\n", MSELPROC, 'a'}, {"\t'%c' - focus on specific container/pod name\n", MSELCONT, 'a'}, {"\t'%c' - focus on specific command line string " "(regular expression)\n", MSELARG, 'a'}, {"\t'%c' - focus on specific process id (PID)\n", MSELPID, 'a'}, {"\t'%c' - focus on specific process/thread state(s)\n", MSELSTATE, 'a'}, {"\n", ' ', 'a'}, {"System resource selections (keys shown in header line):\n",' ', 'a'}, {"\t'%c' - focus on specific system resources " "(regular expression)\n", MSELSYS, 'a'}, {"\n", ' ', 'a'}, {"Screen handling:\n", ' ', 'a'}, {"\t^L - redraw the screen \n", ' ', 'a'}, {"\tPgDn - show next page in the process list (or ^F)\n", ' ', 'a'}, {"\tPgUp - show previous page in the process list (or ^B)\n", ' ', 'a'}, {"\tArDn - arrow-down for next line in process list\n", ' ', 'a'}, {"\tArUp arrow-up for previous line in process list\n", ' ', 'a'}, {"\tArRt - arrow-right for next character in full command line\n", ' ', 'a'}, {"\tArLt - arrow-left for previous character in full command line\n", ' ', 'a'}, {"\n", ' ', 'a'}, {"Presentation (keys shown in header line):\n", ' ', 'a'}, {"\t'%c' - show all processes/threads (i.s.o. active) (toggle)\n", MALLACTIVE, 'a'}, {"\t'%c' - show threads within process (thread view) (toggle)\n", MTHREAD, 'a'}, {"\t'%c' - sort threads (when combined with thread view) (toggle)\n", MTHRSORT, 'a'}, {"\t'%c' - show fixed number of header lines (toggle)\n", MSYSFIXED, 'a'}, {"\t'%c' - suppress sorting system resources (toggle)\n", MSYSNOSORT, 'a'}, {"\t'%c' - suppress terminated processes in output (toggle)\n", MSUPEXITS, 'a'}, {"\t'%c' - no colors to indicate high occupation (toggle)\n", MCOLORS, 'a'}, {"\t'%c' - show average-per-second i.s.o. total values (toggle)\n", MAVGVAL, 'a'}, {"\t'%c' - calculate proportional set size (PSIZE) (toggle)\n", MCALCPSS, 'a'}, {"\t'%c' - determine WCHAN per thread (toggle)\n", MGETWCHAN, 'a'}, {"\n", ' '}, {"Miscellaneous commands:\n", ' '}, {"\t'%c' - change interval timer (0 = only manual trigger)\n", MINTERVAL, 'l'}, {"\t'%c' - manual trigger to force next sample\n", MSAMPNEXT, 'l'}, {"\t'%c' - reset counters to boot time values\n", MRESET, 'l'}, {"\t'%c' - pause button to freeze current sample (toggle)\n", MPAUSE, 'l'}, {"\n", ' ', 'l'}, {"\t'%c' - limit lines for per-cpu, disk and interface resources\n", MSYSLIMIT, 'a'}, {"\t'%c' - kill a process (i.e. send a signal)\n", MKILLPROC, 'a'}, {"\n", ' '}, {"\t'%c' - version information\n", MVERSION, 'a'}, {"\t'%c' - help information\n", MHELP1, 'a'}, {"\t'%c' - help information\n", MHELP2, 'a'}, {"\t'%c' - quit this program\n", MQUIT, 'a'}, }; static int helplines = sizeof(helptext)/sizeof(struct helptext); static void showhelp(int helpline) { int winlines = LINES-helpline, shown, tobeshown=1, i, lines; WINDOW *helpwin; /* ** create a new window for the help-info in which scrolling is ** allowed */ helpwin = newwin(winlines, COLS, helpline, 0); scrollok(helpwin, 1); /* ** show help-lines */ for (i=0, lines=0, shown=0; i < helplines; i++) { if (rawreadflag) // implies twin mode { if (helptext[i].mode == 'l') continue; } else { if (helptext[i].mode == 'r') continue; } wprintw(helpwin, helptext[i].helpline, helptext[i].helparg); /* ** when the window is full, start paging interactively */ if (lines >= winlines-2 && shown >= tobeshown) { int inputkey; wmove(helpwin, winlines-1, 0); wclrtoeol(helpwin); wprintw(helpwin, "Press q (leave help), " "space (next page), " "Enter (next line) or select key..."); keypad(helpwin, 1); // recognize keypad keys switch (inputkey = wgetch(helpwin)) { case KEY_NPAGE: case KEY_PPAGE: case KEY_UP: case KEY_DOWN: case KEY_LEFT: case KEY_RIGHT: break; // ignore keypad keys case 'q': delwin(helpwin); return; case ' ': shown = 0; tobeshown = winlines-1; break; case '\n': shown = 0; tobeshown = 1; break; default: ungetch(inputkey); keywaiting = 1; delwin(helpwin); return; } wmove(helpwin, winlines-1, 0); } lines++; shown++; } wmove(helpwin, winlines-1, 0); wclrtoeol(helpwin); wprintw(helpwin, "End of help - press 'q' to leave help..."); while (wgetch(helpwin) != 'q'); delwin(helpwin); } /* ** function to be called to print error-messages */ void generic_error(const char *format, ...) { va_list args; va_start(args, format); vfprintf(stderr, format, args); va_end (args); } /* ** function to be called when the program stops */ void generic_end(void) { endwin(); } /* ** function to be called when usage-info is required */ void generic_usage(void) { printf("\t -%c show fixed number of lines with system statistics\n", MSYSFIXED); printf("\t -%c suppress sorting of system resources\n", MSYSNOSORT); printf("\t -%c suppress terminated processes in output\n", MSUPEXITS); printf("\t -%c show limited number of lines for certain resources\n", MSYSLIMIT); printf("\t -%c show threads within process\n", MTHREAD); printf("\t -%c sort threads (when combined with '%c')\n", MTHRSORT, MTHREAD); printf("\t -%c show average-per-second i.s.o. total values\n\n", MAVGVAL); printf("\t -%c no colors in case of high occupation\n", MCOLORS); printf("\t -%c show general process-info (default)\n", MPROCGEN); printf("\t -%c show memory-related process-info\n", MPROCMEM); printf("\t -%c show disk-related process-info\n", MPROCDSK); printf("\t -%c show network-related process-info\n", MPROCNET); printf("\t -%c show scheduling-related process-info\n", MPROCSCH); printf("\t -%c show various process-info (ppid, user/group, " "date/time)\n", MPROCVAR); printf("\t -%c show command line per process\n", MPROCARG); printf("\t -%c show own defined process-info\n", MPROCOWN); printf("\t -%c show cumulated process-info per user\n", MCUMUSER); printf("\t -%c show cumulated process-info per program " "(i.e. same name)\n", MCUMPROC); printf("\t -%c show cumulated process-info per container/pod\n\n", MCUMCONT); printf("\t -%c sort processes in order of cpu consumption " "(default)\n", MSORTCPU); printf("\t -%c sort processes in order of memory consumption\n", MSORTMEM); printf("\t -%c sort processes in order of disk activity\n", MSORTDSK); printf("\t -%c sort processes in order of network activity\n", MSORTNET); printf("\t -%c sort processes in order of GPU activity\n", MSORTGPU); printf("\t -%c sort processes in order of most active resource " "(auto mode)\n", MSORTAUTO); } /* ** functions to handle a particular tag in the /etc/atoprc and .atoprc file */ void do_username(char *name, char *val) { struct passwd *pwd; 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_twindir(char *name, char *val) { extern char twindir[]; strncpy(twindir, val, RAWNAMESZ-1); } void do_flags(char *name, char *val) { int i; extern char twinmodeflag; for (i=0; val[i]; i++) { switch (val[i]) { case '-': break; case MBARGRAPH: displaymode = 'D'; break; case MBARMONO: barmono = 1; break; case MSORTCPU: showorder = MSORTCPU; break; case MSORTGPU: showorder = MSORTGPU; break; case MSORTMEM: showorder = MSORTMEM; break; case MSORTDSK: showorder = MSORTDSK; break; case MSORTNET: showorder = MSORTNET; break; case MSORTAUTO: showorder = MSORTAUTO; break; case MPROCGEN: showtype = MPROCGEN; showorder = MSORTCPU; break; case MPROCGPU: showtype = MPROCGPU; showorder = MSORTGPU; break; case MPROCMEM: showtype = MPROCMEM; showorder = MSORTMEM; break; case MPROCDSK: showtype = MPROCDSK; showorder = MSORTDSK; break; case MPROCNET: showtype = MPROCNET; showorder = MSORTNET; break; case MPROCVAR: showtype = MPROCVAR; break; case MPROCSCH: showtype = MPROCSCH; showorder = MSORTCPU; break; case MPROCARG: showtype = MPROCARG; break; case MPROCOWN: showtype = MPROCOWN; break; case MCUMUSER: showtype = MCUMUSER; break; case MCUMPROC: showtype = MCUMPROC; break; case MCUMCONT: showtype = MCUMCONT; break; case MCGROUPS: showtype = MCGROUPS; break; case MALLACTIVE: deviatonly = 0; break; case MAVGVAL: avgval=1; break; case MSYSFIXED: fixedhead = 1; break; case MSYSNOSORT: sysnosort = 1; break; case MTHREAD: threadview = 1; break; case MTHRSORT: threadsort = 1; break; case MCOLORS: usecolors = 0; break; case MCALCPSS: calcpss = 1; break; case MGETWCHAN: getwchan = 1; break; case MSUPEXITS: suppressexit = 1; break; case '2': // cgroup level case '3': case '4': case '5': case '6': case '7': case '8': case '9': cgroupdepth = val[i] - 0x30; break; case 't': twinmodeflag++; break; } } } static void getsigwinch(int signr) { winchange = 1; } atop-2.11.1/showlinux.h0000644000203100020310000004525714771753566014312 0ustar gerlofgerlof/* ** ATOP - System & Process Monitor ** ** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as process-level. ** ** This source-file contains the Linux-specific functions to calculate ** figures to be visualized. ** ========================================================================== ** Author: JC van Winkel - AT Computing, Nijmegen, Holland ** E-mail: jc@ATComputing.nl ** Date: November 2009 ** -------------------------------------------------------------------------- ** Copyright (C) 2009 JC van Winkel ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ** -------------------------------------------------------------------------- */ #ifndef __SHOWLINUX__ #define __SHOWLINUX__ #define MAXITEMS 80 /* The maximum number of items per line */ /* * structure for extra parameters for system related data */ typedef struct { count_t totut; count_t totst; int nact; int nproc; int ntrun; int ntslpi; int ntslpu; int ntidle; int nzomb; int nexit; int noverflow; int avgval; int nsecs; count_t mstot; count_t iotot; struct perdsk *perdsk; int index; count_t cputot; count_t pernumacputot; count_t percputot; } extraparam; /*************************************************************** * * structure for system print list * * configname name as used to identify this field when configuring * the print line * doformat pointer to function that formats this field into a * string of 12 positions, to be returned as char pointer * dovalidate pointer to function that determines if this is a * valid (i.e. relevant) field on this system, returning * 0 (false) or non-zero (true) * when this function pointer is NULL, true is considered */ typedef struct { char *configname; char* (*doformat)(struct sstat *, extraparam *, int, int *); int (*dovalidate)(struct sstat *); } sys_printdef; /* * structure for system print-list with priority * in case of lack of screen space, lowest priority items will be * removed first */ typedef struct { sys_printdef *f; int prio; } sys_printpair; void showsysline(sys_printpair* elemptr, struct sstat* sstat, extraparam *extra, char *labeltext, unsigned int badness); /* ** structure for process and cgroup print list */ typedef struct { char *head; // column header char *configname; // name as used to config print line char *(*doactiveconvert)(); // pointer to conv function // for active process char *(*doexitconvert)(); // pointer to conv function // for exited process char sortcrit; // sort criterion similar to showorder int width; // required width int varwidth; // width may grow (eg cmd params) } detail_printdef; typedef struct { detail_printdef *pf; int prio; } detail_printpair; void showprochead(detail_printpair *, int, int, char, char); void showprocline(detail_printpair *, struct tstat *, double, int, int); void showcgrouphead(detail_printpair *, int, int, char); void showcgroupline(detail_printpair *, struct cgchainer *, struct tstat *, int, int, count_t, int); void do_cpucritperc(char *, char *); void do_gpucritperc(char *, char *); void do_memcritperc(char *, char *); void do_swpcritperc(char *, char *); void do_dskcritperc(char *, char *); void do_netcritperc(char *, char *); void do_swoutcritsec(char *, char *); void do_almostcrit(char *, char *); void do_ownsysprcline(char *, char *); void do_ownallcpuline(char *, char *); void do_ownindivcpuline(char *, char *); void do_owncplline(char *, char *); void do_owngpuline(char *, char *); void do_ownmemline(char *, char *); void do_ownswpline(char *, char *); void do_ownpagline(char *, char *); void do_ownmemnumaline(char *, char *); void do_owncpunumaline(char *, char *); void do_ownllcline(char *, char *); void do_owndskline(char *, char *); void do_ownnettransportline(char *, char *); void do_ownnetnetline(char *, char *); void do_ownnetinterfaceline(char *, char *); void do_owninfinibandline(char *, char *); void do_ownprocline(char *, char *); int get_posval(char *, char *); extern sys_printdef *prcsyspdefs[]; extern sys_printdef *cpusyspdefs[]; extern sys_printdef *cpisyspdefs[]; extern sys_printdef *cplsyspdefs[]; extern sys_printdef *memsyspdefs1[]; extern sys_printdef *memsyspdefs2[]; extern sys_printdef *swpsyspdefs[]; extern sys_printdef *pagsyspdefs[]; extern sys_printdef *numasyspdefs[]; extern sys_printdef *numacpusyspdefs[]; extern sys_printdef *dsksyspdefs[]; extern sys_printdef *nettranssyspdefs[]; extern sys_printdef *netnetsyspdefs[]; extern sys_printdef *netintfsyspdefs[]; extern sys_printdef *infinisyspdefs[]; extern sys_printdef syspdef_PRCSYS; extern sys_printdef syspdef_PRCUSER; extern sys_printdef syspdef_PRCNPROC; extern sys_printdef syspdef_PRCNRUNNING; extern sys_printdef syspdef_PRCNSLEEPING; extern sys_printdef syspdef_PRCNDSLEEPING; extern sys_printdef syspdef_PRCNIDLE; extern sys_printdef syspdef_PRCNZOMBIE; extern sys_printdef syspdef_PRCCLONES; extern sys_printdef syspdef_PRCNNEXIT; extern sys_printdef syspdef_CPUSYS; extern sys_printdef syspdef_CPUUSER; extern sys_printdef syspdef_CPUIRQ; extern sys_printdef syspdef_CPUIDLE; extern sys_printdef syspdef_CPUWAIT; extern sys_printdef syspdef_CPUISYS; extern sys_printdef syspdef_CPUIUSER; extern sys_printdef syspdef_CPUIIRQ; extern sys_printdef syspdef_CPUIIDLE; extern sys_printdef syspdef_CPUIWAIT; extern sys_printdef syspdef_CPUISTEAL; extern sys_printdef syspdef_CPUIFREQ; extern sys_printdef syspdef_CPUFREQ; extern sys_printdef syspdef_CPUSCALE; extern sys_printdef syspdef_CPUISCALE; extern sys_printdef syspdef_CPUSTEAL; extern sys_printdef syspdef_CPUISTEAL; extern sys_printdef syspdef_CPUGUEST; extern sys_printdef syspdef_CPUIGUEST; extern sys_printdef syspdef_CPUIPC; extern sys_printdef syspdef_CPUIIPC; extern sys_printdef syspdef_CPUCYCLE; extern sys_printdef syspdef_CPUICYCLE; extern sys_printdef syspdef_CPLAVG1; extern sys_printdef syspdef_CPLAVG5; extern sys_printdef syspdef_CPLAVG15; extern sys_printdef syspdef_CPLCSW; extern sys_printdef syspdef_CPLNUMCPU; extern sys_printdef syspdef_CPLINTR; extern sys_printdef syspdef_GPUBUS; extern sys_printdef syspdef_GPUTYPE; extern sys_printdef syspdef_GPUNRPROC; extern sys_printdef syspdef_GPUMEMPERC; extern sys_printdef syspdef_GPUMEMOCC; extern sys_printdef syspdef_GPUGPUPERC; extern sys_printdef syspdef_GPUMEMTOT; extern sys_printdef syspdef_GPUMEMUSE; extern sys_printdef syspdef_GPUMEMAVG; extern sys_printdef syspdef_MEMTOT; extern sys_printdef syspdef_MEMFREE; extern sys_printdef syspdef_MEMAVAIL; extern sys_printdef syspdef_MEMCACHE; extern sys_printdef syspdef_MEMDIRTY; extern sys_printdef syspdef_MEMBUFFER; extern sys_printdef syspdef_MEMSLAB; extern sys_printdef syspdef_RECSLAB; extern sys_printdef syspdef_SHMEM; extern sys_printdef syspdef_SHMRSS; extern sys_printdef syspdef_SHMSWP; extern sys_printdef syspdef_VMWBAL; extern sys_printdef syspdef_ZFSARC; extern sys_printdef syspdef_PAGETABS; extern sys_printdef syspdef_ANONTHP; extern sys_printdef syspdef_HUPTOT; extern sys_printdef syspdef_HUPUSE; extern sys_printdef syspdef_SWPTOT; extern sys_printdef syspdef_SWPFREE; extern sys_printdef syspdef_SWPCACHE; extern sys_printdef syspdef_ZSWPOOL; extern sys_printdef syspdef_ZSWSTORED; extern sys_printdef syspdef_KSMSHARING; extern sys_printdef syspdef_KSMSHARED; extern sys_printdef syspdef_SWPCOMMITTED; extern sys_printdef syspdef_SWPCOMMITLIM; extern sys_printdef syspdef_NUMNUMA; extern sys_printdef syspdef_NUMANR; extern sys_printdef syspdef_NUMATOT; extern sys_printdef syspdef_NUMAFREE; extern sys_printdef syspdef_NUMAFILEPAGE; extern sys_printdef syspdef_NUMASLAB; extern sys_printdef syspdef_NUMADIRTY; extern sys_printdef syspdef_NUMAACTIVE; extern sys_printdef syspdef_NUMAINACTIVE; extern sys_printdef syspdef_NUMASHMEM; extern sys_printdef syspdef_NUMASLABRECLAIM; extern sys_printdef syspdef_NUMAFRAG; extern sys_printdef syspdef_NUMAHUPTOT; extern sys_printdef syspdef_NUMAHUPUSE; extern sys_printdef syspdef_NUMANUMCPU; extern sys_printdef syspdef_NUMACPUSYS; extern sys_printdef syspdef_NUMACPUUSER; extern sys_printdef syspdef_NUMACPUNICE; extern sys_printdef syspdef_NUMACPUIRQ; extern sys_printdef syspdef_NUMACPUSOFTIRQ; extern sys_printdef syspdef_NUMACPUIDLE; extern sys_printdef syspdef_NUMACPUWAIT; extern sys_printdef syspdef_NUMACPUSTEAL; extern sys_printdef syspdef_NUMACPUGUEST; extern sys_printdef syspdef_LLCMBMTOTAL; extern sys_printdef syspdef_LLCMBMLOCAL; extern sys_printdef syspdef_NUMLLC; extern sys_printdef syspdef_PAGSCAN; extern sys_printdef syspdef_PAGSTEAL; extern sys_printdef syspdef_PAGSTALL; extern sys_printdef syspdef_PAGCOMPACT; extern sys_printdef syspdef_NUMAMIGRATE; extern sys_printdef syspdef_PGMIGRATE; extern sys_printdef syspdef_PAGPGIN; extern sys_printdef syspdef_PAGPGOUT; extern sys_printdef syspdef_TCPSOCK; extern sys_printdef syspdef_UDPSOCK; extern sys_printdef syspdef_PAGSWIN; extern sys_printdef syspdef_PAGSWOUT; extern sys_printdef syspdef_PAGZSWIN; extern sys_printdef syspdef_PAGZSWOUT; extern sys_printdef syspdef_OOMKILLS; extern sys_printdef syspdef_PSICPUSTOT; extern sys_printdef syspdef_PSIMEMSTOT; extern sys_printdef syspdef_PSIMEMFTOT; extern sys_printdef syspdef_PSIIOSTOT; extern sys_printdef syspdef_PSIIOFTOT; extern sys_printdef syspdef_PSICPUS; extern sys_printdef syspdef_PSIMEMS; extern sys_printdef syspdef_PSIMEMF; extern sys_printdef syspdef_PSIIOS; extern sys_printdef syspdef_PSIIOF; extern sys_printdef syspdef_CONTNAME; extern sys_printdef syspdef_CONTNPROC; extern sys_printdef syspdef_CONTCPU; extern sys_printdef syspdef_CONTMEM; extern sys_printdef syspdef_DSKNAME; extern sys_printdef syspdef_DSKBUSY; extern sys_printdef syspdef_DSKNREAD; extern sys_printdef syspdef_DSKNWRITE; extern sys_printdef syspdef_DSKNDISC; extern sys_printdef syspdef_DSKMBPERSECRD; extern sys_printdef syspdef_DSKMBPERSECWR; extern sys_printdef syspdef_DSKKBPERRD; extern sys_printdef syspdef_DSKKBPERWR; extern sys_printdef syspdef_DSKKBPERDS; extern sys_printdef syspdef_DSKINFLIGHT; extern sys_printdef syspdef_DSKAVQUEUE; extern sys_printdef syspdef_DSKAVIO; extern sys_printdef syspdef_NETTRANSPORT; extern sys_printdef syspdef_NETTCPI; extern sys_printdef syspdef_NETTCPO; extern sys_printdef syspdef_NETTCPACTOPEN; extern sys_printdef syspdef_NETTCPPASVOPEN; extern sys_printdef syspdef_NETTCPRETRANS; extern sys_printdef syspdef_NETTCPINERR; extern sys_printdef syspdef_NETTCPORESET; extern sys_printdef syspdef_NETTCPCSUMERR; extern sys_printdef syspdef_NETUDPNOPORT; extern sys_printdef syspdef_NETUDPINERR; extern sys_printdef syspdef_NETUDPI; extern sys_printdef syspdef_NETUDPO; extern sys_printdef syspdef_NETNETWORK; extern sys_printdef syspdef_NETIPI; extern sys_printdef syspdef_NETIPO; extern sys_printdef syspdef_NETIPFRW; extern sys_printdef syspdef_NETIPDELIV; extern sys_printdef syspdef_NETICMPIN; extern sys_printdef syspdef_NETICMPOUT; extern sys_printdef syspdef_NETNAME; extern sys_printdef syspdef_NETPCKI; extern sys_printdef syspdef_NETPCKO; extern sys_printdef syspdef_NETSPEEDMAX; extern sys_printdef syspdef_NETSPEEDIN; extern sys_printdef syspdef_NETSPEEDOUT; extern sys_printdef syspdef_NETCOLLIS; extern sys_printdef syspdef_NETMULTICASTIN; extern sys_printdef syspdef_NETRCVERR; extern sys_printdef syspdef_NETSNDERR; extern sys_printdef syspdef_NETRCVDROP; extern sys_printdef syspdef_NETSNDDROP; extern sys_printdef syspdef_NFMSERVER; extern sys_printdef syspdef_NFMPATH; extern sys_printdef syspdef_NFMTOTREAD; extern sys_printdef syspdef_NFMTOTWRITE; extern sys_printdef syspdef_NFMNREAD; extern sys_printdef syspdef_NFMNWRITE; extern sys_printdef syspdef_NFMDREAD; extern sys_printdef syspdef_NFMDWRITE; extern sys_printdef syspdef_NFMMREAD; extern sys_printdef syspdef_NFMMWRITE; extern sys_printdef syspdef_NFCRPCCNT; extern sys_printdef syspdef_NFCRPCREAD; extern sys_printdef syspdef_NFCRPCWRITE; extern sys_printdef syspdef_NFCRPCRET; extern sys_printdef syspdef_NFCRPCARF; extern sys_printdef syspdef_NFSRPCCNT; extern sys_printdef syspdef_NFSRPCREAD; extern sys_printdef syspdef_NFSRPCWRITE; extern sys_printdef syspdef_NFSBADFMT; extern sys_printdef syspdef_NFSBADAUT; extern sys_printdef syspdef_NFSBADCLN; extern sys_printdef syspdef_NFSNETTCP; extern sys_printdef syspdef_NFSNETUDP; extern sys_printdef syspdef_NFSNRBYTES; extern sys_printdef syspdef_NFSNWBYTES; extern sys_printdef syspdef_NFSRCHITS; extern sys_printdef syspdef_NFSRCMISS; extern sys_printdef syspdef_NFSRCNOCA; extern sys_printdef syspdef_IFBNAME; extern sys_printdef syspdef_IFBPCKI; extern sys_printdef syspdef_IFBPCKO; extern sys_printdef syspdef_IFBSPEEDMAX; extern sys_printdef syspdef_IFBLANES; extern sys_printdef syspdef_IFBSPEEDIN; extern sys_printdef syspdef_IFBSPEEDOUT; extern sys_printdef syspdef_BLANKBOX; /* ** functions that print ???? for unavailable data */ char *procprt_NOTAVAIL_4(struct tstat *curstat, int avgval, int nsecs); char *procprt_NOTAVAIL_5(struct tstat *curstat, int avgval, int nsecs); char *procprt_NOTAVAIL_6(struct tstat *curstat, int avgval, int nsecs); char *procprt_NOTAVAIL_7(struct tstat *curstat, int avgval, int nsecs); extern detail_printdef *alldetaildefs[]; extern detail_printdef procprt_PID; extern detail_printdef procprt_TID; extern detail_printdef procprt_PPID; extern detail_printdef procprt_SYSCPU; extern detail_printdef procprt_USRCPU; extern detail_printdef procprt_VGROW; extern detail_printdef procprt_RGROW; extern detail_printdef procprt_MINFLT; extern detail_printdef procprt_MAJFLT; extern detail_printdef procprt_VSTEXT; extern detail_printdef procprt_VSIZE; extern detail_printdef procprt_RSIZE; extern detail_printdef procprt_PSIZE; extern detail_printdef procprt_VSLIBS; extern detail_printdef procprt_VDATA; extern detail_printdef procprt_VSTACK; extern detail_printdef procprt_SWAPSZ; extern detail_printdef procprt_LOCKSZ; extern detail_printdef procprt_CMD; extern detail_printdef procprt_RUID; extern detail_printdef procprt_EUID; extern detail_printdef procprt_SUID; extern detail_printdef procprt_FSUID; extern detail_printdef procprt_RGID; extern detail_printdef procprt_EGID; extern detail_printdef procprt_SGID; extern detail_printdef procprt_FSGID; extern detail_printdef procprt_CTID; extern detail_printdef procprt_VPID; extern detail_printdef procprt_CID; extern detail_printdef procprt_STDATE; extern detail_printdef procprt_STTIME; extern detail_printdef procprt_ENDATE; extern detail_printdef procprt_ENTIME; extern detail_printdef procprt_THR; extern detail_printdef procprt_TRUN; extern detail_printdef procprt_TSLPI; extern detail_printdef procprt_TSLPU; extern detail_printdef procprt_TIDLE; extern detail_printdef procprt_POLI; extern detail_printdef procprt_NICE; extern detail_printdef procprt_PRI; extern detail_printdef procprt_RTPR; extern detail_printdef procprt_CURCPU; extern detail_printdef procprt_ST; extern detail_printdef procprt_EXC; extern detail_printdef procprt_S; extern detail_printdef procprt_COMMAND_LINE; extern detail_printdef procprt_NPROCS; extern detail_printdef procprt_RDDSK; extern detail_printdef procprt_WRDSK; extern detail_printdef procprt_CWRDSK; extern detail_printdef procprt_WCANCEL; extern detail_printdef procprt_TCPRCV; extern detail_printdef procprt_TCPRASZ; extern detail_printdef procprt_TCPSND; extern detail_printdef procprt_TCPSASZ; extern detail_printdef procprt_UDPRCV; extern detail_printdef procprt_UDPRASZ; extern detail_printdef procprt_UDPSND; extern detail_printdef procprt_UDPSASZ; extern detail_printdef procprt_RNET; extern detail_printdef procprt_SNET; extern detail_printdef procprt_BANDWI; extern detail_printdef procprt_BANDWO; extern detail_printdef procprt_GPULIST; extern detail_printdef procprt_GPUMEMNOW; extern detail_printdef procprt_GPUMEMAVG; extern detail_printdef procprt_GPUGPUBUSY; extern detail_printdef procprt_GPUMEMBUSY; extern detail_printdef procprt_SORTITEM; extern detail_printdef procprt_RUNDELAY; extern detail_printdef procprt_BLKDELAY; extern detail_printdef procprt_WCHAN; extern detail_printdef procprt_NVCSW; extern detail_printdef procprt_NIVCSW; extern detail_printdef cgroupprt_CGROUP_PATH; extern detail_printdef cgroupprt_CGRNPROCS; extern detail_printdef cgroupprt_CGRNPROCSB; extern detail_printdef cgroupprt_CGRCPUBUSY; extern detail_printdef cgroupprt_CGRCPUPSI; extern detail_printdef cgroupprt_CGRCPUWGT; extern detail_printdef cgroupprt_CGRCPUMAX; extern detail_printdef cgroupprt_CGRMEMORY; extern detail_printdef cgroupprt_CGRMEMPSI; extern detail_printdef cgroupprt_CGRMEMMAX; extern detail_printdef cgroupprt_CGRSWPMAX; extern detail_printdef cgroupprt_CGRDISKIO; extern detail_printdef cgroupprt_CGRDSKPSI; extern detail_printdef cgroupprt_CGRDSKWGT; extern detail_printdef cgroupprt_CGRPID; extern detail_printdef cgroupprt_CGRCMD; extern char *procprt_SNET_a(struct tstat *, int, int); extern char *procprt_SNET_e(struct tstat *, int, int); extern char *procprt_RNET_a(struct tstat *, int, int); extern char *procprt_RNET_e(struct tstat *, int, int); extern char *procprt_TCPSND_a(struct tstat *, int, int); extern char *procprt_TCPRCV_a(struct tstat *, int, int); extern char *procprt_UDPSND_a(struct tstat *, int, int); extern char *procprt_UDPRCV_a(struct tstat *, int, int); extern char *procprt_TCPSASZ_a(struct tstat *, int, int); extern char *procprt_TCPRASZ_a(struct tstat *, int, int); extern char *procprt_UDPSASZ_a(struct tstat *, int, int); extern char *procprt_UDPRASZ_a(struct tstat *, int, int); extern char *procprt_TCPSND_e(struct tstat *, int, int); extern char *procprt_TCPRCV_e(struct tstat *, int, int); extern char *procprt_UDPSND_e(struct tstat *, int, int); extern char *procprt_UDPRCV_e(struct tstat *, int, int); extern char *procprt_TCPSASZ_e(struct tstat *, int, int); extern char *procprt_TCPRASZ_e(struct tstat *, int, int); extern char *procprt_UDPSASZ_e(struct tstat *, int, int); extern char *procprt_UDPRASZ_e(struct tstat *, int, int); #endif atop-2.11.1/procdbase.c0000644000203100020310000001652514771753566014203 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.11.1/man/0000755000203100020310000000000014771753566012637 5ustar gerlofgerlofatop-2.11.1/man/atopgpud.80000644000203100020310000001001114771753566014544 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) atop-2.11.1/man/atophide.10000644000203100020310000000216714771753566014524 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.11.1/man/atopconvert.10000644000203100020310000000255514771753566015274 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.11.1/man/atopsar.10000644000203100020310000010005314771753566014371 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.11.1/man/atopacctd.80000644000203100020310000001046514771753566014700 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.11.1/man/atop.10000644000203100020310000032345014771753566013673 0ustar gerlofgerlof.TH ATOP 1 "July 2024" "Linux" .SH NAME .B atop - Advanced System & Process Monitor .SH SYNOPSIS Live measurement in bar graph mode: .PP .TP 5 .B \ atop \-B[H] [-t [absdir]] [interval [samples]] .PP Live measurement cgroups in text mode: .PP .TP 5 .B \ atop \-G [-t [absdir]] [-2|-3|-4|-5|-6|-7|-8|-9] [\-a] [\-C|\-M|\-D|\-A] [interval [samples]] .PP Live measurement processes in text mode: .PP .TP 5 .B \ atop [-t [absdir]] [\-g|\-m|\-d|\-n|\-u|\-p|\-s|\-c|\-v|\-o|\-y|\-Y] [\-C|\-M|\-D|\-N|\-A] [\-fFX1xR] [interval [samples]] .PP Live generation of parsable output (white-space separated or JSON): .PP .TP 5 .B \ atop [\-Plabel[,label]... [-Z]] [\-Jlabel[,label]...] [interval [samples]] .PP Write raw log files: .PP .TP 5 .B \ atop \-w rawfile [\-a] [\-S] [interval [samples]] .PP Analyze raw log files in bar graph mode: .PP .TP 5 .B \ atop \-B[H] -r [rawfile|yyy...] [\-b [YYYYMMDD]hhmm[ss]] [\-e [YYYYMMDD]hhmm[ss]] .PP Analyze cgroups from raw log files in text mode: .PP .TP 5 .B \ atop \-G [-2|-3|-4|-5|-6|-7|-8|-9] [\-a] \-r [rawfile|yyy...] [\-b [YYYYMMDD]hhmm[ss]] [\-e [YYYYMMDD]hhmm[ss]] .PP Analyze processes from raw log files in text mode: .PP .TP 5 .B \ atop \-r [rawfile|yyy...] [\-b [YYYYMMDD]hhmm[ss]] [\-e [YYYYMMDD]hhmm[ss]] [\-g|\-m|\-d|\-n|\-u|\-p|\-s|\-c|\-v|\-o|\-y|\-Y] [\-C|\-M|\-D|\-N|\-A] [\-fFX1xR] .PP Generate parsable output from raw log files (white-space separated or JSON): .PP .TP 5 .B \ atop \-r [rawfile|yyy...] [\-b [YYYYMMDD]hhmm[ss]] [\-e [YYYYMMDD]hhmm[ss]] [\-Plabel[,label]... [-Z]] [\-Jlabel[,label]...] .SH DESCRIPTION The program .I atop is an interactive monitor to view the load on a Linux system. Every .I interval seconds (default: 10 seconds) information is gathered about the resource occupation on system level (CPUs, memory, disks and network interfaces) and on cgroup level (version 2). Besides, information is gathered about the processes and threads that are responsible for the utilization of the CPUs, memory and disks. Network load per process is shown only when the .I netatop kernel module or the .I netatop-bpf BPF module has been installed. .SH TWIN MODE With the .I -t flag you can run .I atop interactively in 'twin mode'. This mode allows to run a live measurement with the possibility to review and analyze an earlier sample. Meanwhile, the live measurement continues. When started in twin mode, .I atop spawns a child process that gathers the counters and writes them to a temporary raw file. The parent process reads the counters from the temporary raw file and presents them to the user. The reading of the parent process keeps in pace with the written samples of the child process for live measurements. While the gathering continues by the child process, key 'r', key 'b' or key 'T' can be pressed to review earlier samples. The parent process implicitly pauses the live measurement by pressing one of these keys or by pressing key 'z' (pause) explicitly. After browsing through the earlier samples with the keys 't' (next sample), 'T' (previous sample), 'r' (reset to begin of measurement) and 'b' (branch to time tamp), the live measurement can be continued by pressing key 'z' (resume after pause). The temporary raw file will be written in the .B /tmp directory by default. Optionally the absolute path name of an alternative directory can be added behind the .I -t flag (e.g. when there is not enough space in the .B /tmp directory). In any case, the parent process will terminate the child process when the measurement is finished and the temporary raw file will be removed. .SH BAR GRAPH MODE When running .I atop you can choose to view the system load in bar graph mode or in text mode. In bar graph mode the resource utilization of CPUs, memory, disks and network interfaces is shown via (character-based) bar graphs, but only on system level. When you want to view more detailed information on system level or when you want to view the resource consumption on process or thread level, you can switch to text mode by pressing the 'B' key. Alternatively, you can use the 'B' key (again) to switch from text mode to bar graph mode. .br By default, .I atop starts in text mode unless the .I -B flag is used or unless 'B' has been configured as a default flag in the .I .atoprc file (for further information about default flags, refer to the .B atoprc man page). .PP In bar graph mode the terminal will be subdivided into four character-based windows, i.e. one window for each hardware resource: .PP .TP 5 .B Processors The first bar shows the average busy percentage of all CPUs with the bar label 'Avg' (might be abbreviated to 'Av' or even just 'A'). The subsequent bars show the busy percentages of single CPUs. .br When there is not enough horizontal space to show all CPUs, only the most busy CPUs per sample will be shown after the width of each bar has been reduced to a minimum. By default, the categories of CPU consumption are shown by different colors in the bars, marked with a character 'S' (system mode), 'U' (user mode), 'I' (interrupt handling), 's' (steal) and 'G' (guest, i.e. consumed by virtual machines). .br The top of the bar might consist of an unmarked color representing a 'neutral' category. Suppose that the scale unit is 5% per line and the total busy percentage is 54% consisting of two categories of 27%. The two categories will be rounded to 25% (5 lines of 5% each) but the total busy percentage will be rounded to 55% (11 lines of 5%). Then the top line will represent a 'neutral' category. .br By pressing the 'H' key or by starting .I atop with the '-H' flag, no categories are shown. A red line is drawn in the bar graph as critical threshold. By default this value is 90% and can be modified by the 'cpucritperc' option in the configuration file (see separate .B atoprc man page). When this value is set to zero, no threshold line will be drawn. .TP 5 .B Memory and swap space Memory is presented as a column in which the specific categories of memory consumption are shown. These categories are (code, data and stack of) processes/kernel, slab caches (i.e. dynamically allocated kernel memory), shared memory, tmpfs, static huge pages, page cache and free memory. .br Swap space (if present) is also presented as a column in which the categories processes/tmpfs, shared memory and free space are shown. At the right side memory-related event counters are shown. .br The bottom three counters are colored green when there is no memory pressure. When considerable activity is noticed such counter might be colored orange and with high activity red. .br When memory pressure starts, usually memory page scanning will be activated first. When pressure increases, memory pages of processes might be swapped out to swap space (if present). .br The 'oomkills' counter (Out Of Memory killing) is most serious: it reflects the number of processes that are killed due to lack of memory (and swap). Therefore this counter shows the absolute number (not per second) of processes being killed during the last interval and will immediately be colored red when it is 1 or more. Besides, after .I atop has noticed OOM killing the 'oomkills' counter remains orange for the next 15 minutes, just in case that you have missed the OOM killing event itself. .br When there is enough vertical space in the memory window, event counters are shown about the number of memory pages being swapped in, the number of memory pages paged out to block devices and the number of memory pages paged in from block devices. Memory and swap space consumption will preferably be shown in a character-based window that vertically uses the entire screen for optimal granularity. However, when there are a lot of disks and/or network interfaces the memory and swap space consumption will be shown in a character-based window that only uses the upper half of the screen. .TP 5 .B Disks For each disk the busy percentage is shown as a bar. .br When there is not enough horizontal space to show all disks, only the most busy disks per sample will be shown. By default, categories of disk consumption are shown by different colors in the bars, marked with a character 'R' (read) and 'W' (write). .br The top of the bar might consist of an unmarked color representing a 'neutral' category. Suppose that the scale unit is 5% per line and the total busy percentage is 54% consisting of two categories of 27%. The two categories will be rounded to 25% (5 lines of 5% each) but the total busy percentage will be rounded to 55% (11 lines of 5%). Then the top line will represent a 'neutral' category. .br By pressing the 'H' key or by starting .I atop with the '-H' flag, no categories are shown. A red line is drawn in the bar graph as critical threshold. By default this value is 90% and can be modified by the 'dskcritperc' option in the configuration file (see separate .B atoprc man page). When this value is set to zero, no threshold line will be drawn. .TP 5 .B Interfaces For each non-virtual network interface a double bar graph is shown with a dedicated scale that reflects the traffic rate. One of the bars shows the transmit rate ('TX') and the other bar the receive rate ('RX'). The traffic scale of each network interface remains at its highest level. All interface scales can be reset during the measurement by pressing the 'L' key. Most often the real speed (maximum bandwidth) of network interfaces is not known, e.g. in case of the network interfaces of virtual machines. Therefore it is not possible to show the interface utilization as a percentage. However, when the real speed of an interface is known it will be shown underneath the concerning bar graph. When there is not enough horizontal space to show all network interfaces, only the most busy interfaces per sample will be shown. .PP Usually the bar graphs will not be sorted on busy percentage when there is enough horizontal space. However, after switching from text mode to bar graph mode the bar graphs might have been sorted because this was needed for the presentation in text mode. The next interval in bar graph mode shows the bars unsorted again unless the window width is unsufficient for all bars. .PP The remaining part of this manual page mainly describes the information shown in text mode. When certain descriptions also apply to bar graph mode it will be mentioned explicitly. .SH TEXT MODE IN GENERAL With every interval information is shown about the resource occupation on system level (CPU, memory, disks and network layers) in the upper part of the screen. When a resource has been unused during the interval, the line is suppressed unless the 'f' key is active. In the bottom part of the screen cgroup level (key 'G') or process level information is shown (keys 'g', 'c', 's', 'm', 'd', 'n', 'v', 'u' or 'p'). By default, only cgroups are shown without assigned processes in the cgroup itself nor in the cgroups underneath, unless the 'a' key (all) is active. By default, only processes are shown that were active during the interval unless the 'a' key (all) is active. .br The intervals are repeated till the number of .I samples (specified as command argument) is reached, or till the key 'q' is pressed in interactive mode. .PP When .I atop is started, it checks whether the standard output channel is connected to a screen, or to a file/pipe. In the first case it produces screen control codes (via the ncurses library) and behaves interactively. In the second case it produces flat text output. .PP In interactive mode, the output of .I atop scales dynamically to the current dimensions of the screen/window. .br If the window is resized horizontally, columns will be added or removed automatically. For this purpose, every column has a particular weight. The columns with the highest weights that fit within the current width will be shown. .br If the window is resized vertically, lines of the process/thread list will be added or removed automatically. .PP In interactive mode the output of .I atop can be controlled by pressing particular keys. However it is also possible to specify such key as .B flag on the command line. In that case .I atop switches to the indicated mode on beforehand. This mode can be modified again interactively. Specifying such key as flag is especially useful when running .I atop with output to a pipe or file (non-interactively). These flags are the same as the keys that can be pressed in interactive mode (see section INTERACTIVE COMMANDS). .br Additional flags are available to support storage of atop-data in raw format (see section RAW DATA STORAGE). The status line in the initial screen shows if .I atop runs with restricted view (as unprivileged user) or unrestricted view (as privileged user). In case of restricted view .I atop does not have the privileges (no root identity nor the necessary capabilities) to retrieve all counter values on system level and on process level. .SH PROCESS ACCOUNTING With every interval, .I atop reads the kernel administration to obtain information about all running processes. However, it is likely that processes have terminated during the interval. These processes might have consumed system resources during this interval before they terminated. Therefore, .I atop tries to read the process accounting records that contain the accounting information of terminated processes and report these processes too. Only when the process accounting mechanism in the kernel is activated, the kernel writes such process accounting record to a file for every process that terminates. .PP There are various ways for .I atop to get access to the process accounting records (tried in this order): .PP .TP 4 1. When the environment variable ATOPACCT is set, it specifies the name of the process accounting file. In that case, process accounting for this file should have been activated on beforehand. Before opening this file for reading, .I atop drops its root privileges (if any). .br When this environment variable is present but its contents is empty, process accounting will not be used at all. .PP .TP 4 2. .B This is the preferred way of handling process accounting records! .br When the .I atopacctd daemon is active, it has activated the process accounting mechanism in the kernel and transfers to original accounting records to shadow files. In that case, .I atop drops its root privileges and opens the current shadow file for reading. .br This way is preferred, because the .I atopacctd daemon maintains full control of the size of the original process accounting file written by the kernel and the shadow files read by the .I atop process(es). The .I atopacct service will be activated before the .I atop service to enable .I atop to detect that process accounting is managed by the .I atopacctd daemon. As a forking service, .I atopacctd takes care that all directories and files are initialized before the parent process dies. The child process continues as the daemon process. For further information, refer to the .B atopacctd man page. .PP .TP 4 3. When the .I atopacctd daemon is not active, .I atop verifies if the process accounting mechanism has been switched on via the separate .B psacct or .B acct package (the package name depends on the Linux distro). In that case, one of the files .B /var/log/pacct, .B /var/account/pacct or .B /var/log/account/pacct is in use as process accounting file and .I atop opens this file for reading. .PP .TP 4 4. As a last possibility, .I atop itself tries to activate the process accounting mechanism (requires root privileges) using the file .B /var/cache/atop.d/atop.acct (to be written by the kernel, to be read by .I atop itself). Process accounting remains active as long as at least one .I atop process is alive. Whenever the last .I atop process stops (either by pressing 'q' or by 'kill \-15'), it deactivates the process accounting mechanism again. Therefore you should never terminate .I atop by 'kill \-9', because then it has no chance to stop process accounting. As a result, the accounting file may consume a lot of disk space after a while. .br To avoid that the process accounting file consumes too much disk space, .I atop verifies at the end of every sample if the size of the process accounting file exceeds 200 MiB and if this .I atop process is the only one that is currently using the file. In that case the file is truncated to a size of zero. Notice that root-privileges are required to switch on/off process accounting in the kernel. You can start .I atop as a root user or specify setuid-root privileges to the executable file. In the latter case, .I atop switches on process accounting and drops the root-privileges again. .br If .I atop does not run with root-privileges, it does not show information about finished processes. It indicates this situation with the message 'no procacct' in the top-right corner (instead of the counter that shows the number of exited processes). .PP When during one interval a lot of processes have finished, .I atop might grow tremendously in memory when reading all process accounting records at the end of the interval. To avoid such excessive growth .I atop will never read more than 50 MiB with process information from the process accounting file per interval (approx. 54000 finished processes). In interactive mode a warning is given whenever processes have been skipped for this reason. .PP .SH COLORS For the resource consumption on system level, .I atop uses colors in text mode to indicate that a critical occupation percentage has been (almost) reached. A critical occupation percentage means that is likely that this load causes a noticeable negative performance influence for applications using this resource. The critical percentage depends on the type of resource: e.g. the performance influence of a disk with a busy percentage of 80% might be more noticeable for applications/users than a CPU with a busy percentage of 90%. .br Currently .I atop uses the following default values to calculate a weighted percentage per resource: .PP .TP 5 .B \ Processor A busy percentage of 90% or higher is considered 'critical' (also in bar graph mode). .TP 5 .B \ Disk A busy percentage of 90% or higher is considered 'critical'. .TP 5 .B \ Network A busy percentage of 90% or higher for the load of an interface is considered 'critical'. .TP 5 .B \ Memory An occupation percentage of 90% is considered 'critical'. Notice that this occupation percentage is the accumulated memory consumption of the kernel (including slab) and all processes. The memory for the page cache ('cache' and 'buff' in the MEM-line) and the reclaimable part of the slab ('slrec') is not implied! .br If the number of pages swapped out ('swout' in the PAG-line) is larger than 10 per second, the memory resource is considered 'critical'. A value of at least 1 per second is considered 'almost critical'. .br If the committed virtual memory exceeds the limit ('vmcom' and 'vmlim' in the SWP-line), the SWP-line is colored due to overcommitting the system. .TP 5 .B \ Swap An occupation percentage of 80% is considered 'critical' because swap space might be completely exhausted in the near future. It is not critical from a performance point-of-view. .PP These default values can be modified in the configuration file (see separate .B atoprc man page). .PP When a resource exceeds its critical occupation percentage, the concerning values in the screen line are colored red by default. .br When a resource exceeds (by default) 80% of its critical percentage (so it is almost critical), the concerning values in the screen line are colored cyan by default. This 'almost critical percentage' (one value for all resources) can be also modified in the configuration file (see separate .B atoprc man page). .br The default colors red and cyan can be modified in the configuration file as well (see separate .B atoprc man page). .PP With the key 'x' (or flag \-x), the use of colors can be suppressed in text mode. The use of colors is however mandatory in case of bar graph mode. .SH NETATOP OR NETATOP-BPF MODULE Per-process and per-thread network activity can be measured by the .I netatop kernel module or the .I netatop-bpf BPF module that can be separately installed. .br When .I atop 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 G Show cgroup v2 information. Show a hierarchical structure of cgroups and related metrics. Optionally, the processes assigned to each cgroup can be shown. With the keys/flags '2' till '7' the level depth of the cgroups can be chosen ('7' is default). With key/flag '8' the assigned active processes are shown as well, except the kernel processes (usually in the root cgroup). With key/flag '9' all active processes are shown, including the kernel processes. With key/flag 'a' (toggle) all cgroups and all processes are shown instead of only the active cgroups and processes. A cgroup is considered inactive when no processes are assigned to that cgroup nor to the cgroups underneath. With key/flag 'C' the output is sorted on CPU consumption, with key/flag 'M' sorted on memory consumption and with key/flag 'D' sorted on disk consumption. .PP .TP 5 .B e Show GPU utilization. Per process at least the following fields are shown: process-id, range of GPU numbers on which the process currently runs, GPU busy percentage on all GPUs, memory busy percentage (i.e. read and write accesses on memory) on all GPUs, memory occupation at the moment of the sample, average memory occupation during the sample, and GPU percentage. When the .I atopgpud daemon does not run with root privileges, the GPU busy percentage and the memory busy percentage are not available on process level. In that case, the GPU percentage on process level reflects the GPU memory occupation instead of the GPU busy percentage (which is preferred). .PP .TP 5 .B o Show the user-defined line of the process. In the configuration file the keyword .I ownprocline can be specified with the description of a user-defined output-line. .br Refer to the man-page of .B atoprc for a detailed description. .PP .TP 5 .B y Show the individual threads within a process (toggle). Single-threaded processes are still shown as one line. .br For multi-threaded processes, one line represents the process while additional lines show the activity per individual thread (in a different color). Depending on the option 'a' (all or active toggle), all threads are shown or only the threads that were active during the last interval. Depending on the option 'Y' (sort threads), the threads per process will be sorted on the chosen sort criterium or not. .br Whether this key is active or not can be seen in the header line. .PP .TP 5 .B Y Sort the threads per process when combined with option 'y' (toggle). .PP .TP 5 .B u Show the process activity accumulated per user. Per user the following fields are shown: number of processes active or terminated during last interval (or in total if combined with command 'a'), accumulated CPU consumption during last interval in system and user mode, the current virtual and resident memory space consumed by active processes (or all processes of the user if combined with command 'a'). .br When .I atop runs with root privileges, the accumulated read and write throughput on disk is shown. When the optional module .I netatop or .I netatop-bpf has been installed, the accumulated number of received and sent network packets is shown. .br The last columns contain the accumulated occupation percentage for the chosen resource (default: CPU) and the user name. .PP .TP 5 .B p Show the process activity accumulated per program (i.e. process name). Per program the following fields are shown: number of processes active or terminated during last interval (or in total if combined with command 'a'), accumulated CPU consumption during last interval in system and user mode, the current virtual and resident memory space consumed by active processes (or all processes of the user if combined with command 'a'). .br When .I atop runs with root privileges, the accumulated read and write throughput on disk is shown. When the optional module .I netatop or .I netatop-bpf has been installed, the accumulated number of received and sent network packets is shown. .br The last columns contain the accumulated occupation percentage for the chosen resource (default: CPU) and the program name. .PP .TP 5 .B j Show the process activity accumulated per container/pod. Per container (e.g. Docker/Podman) or pod (e.g. Kubernetes) the following fields are shown: number of processes active or terminated during last interval (or in total if combined with command 'a'), accumulated CPU consumption during last interval in system and user mode, the current virtual and resident memory space consumed by active processes (or all processes of the user if combined with command 'a'). .br When .I atop runs with root privileges, the accumulated read and write throughput on disk is shown. When the optional module .I netatop or .I netatop-bpf has been installed, the accumulated number of received and sent network packets is shown. .br The last columns contain the accumulated occupation percentage for the chosen resource (default: CPU) and the container/pod name (CID/POD). .PP .TP 5 .B C Sort the current list in the order of CPU consumption (default). The one-but-last column changes to 'CPU'. .PP .TP 5 .B E Sort the current list in the order of GPU utilization (preferred, but only applicable when the .I atopgpud daemon runs under root privileges) or the order of GPU memory occupation). The one-but-last column changes to 'GPU'. .PP .TP 5 .B M Sort the current list in the order of resident memory consumption. The one-but-last column changes to 'MEM'. In case of sorting on memory, the full process list will be shown (not only the active processes). .PP .TP 5 .B D Sort the current list in the order of disk accesses issued. The one-but-last column changes to 'DSK'. .PP .TP 5 .B N Sort the current list in the order of network bandwidth (received and transmitted). The one-but-last column changes to 'NET'. .PP .TP 5 .B A Sort the current list automatically in the order of the most busy system resource during this interval. The one-but-last column shows either 'ACPU', 'AMEM', 'ADSK' or 'ANET' (the preceding 'A' indicates automatic sorting-order). The most busy resource is determined by comparing the weighted busy-percentages of the system resources, as described earlier in the section COLORS. .br This option remains valid until another sorting-order is explicitly selected again. .br A sorting order for disk is only possible when .I atop runs with root privileges. .br A sorting order for network is only possible when the optional module .I netatop or .I netatop-bpf is loaded. .PP Miscellaneous interactive commands: .PP .TP 5 .B ? Request for help information (also the key 'h' can be pressed). .PP .TP 5 .B V Request for version information (version number and date). .PP .TP 5 .B R Gather and calculate the proportional set size of processes (toggle). Gathering of all values that are needed to calculate the PSIZE of a process is a very time-consuming task, so this key should only be active when analyzing the resident memory consumption of processes. .PP .TP 5 .B W Get the WCHAN per thread (toggle). Gathering of the WCHAN string per thread is a relatively time-consuming task, so this key should only be made active when analyzing the reason for threads to be in sleep state. .PP .TP 5 .B x Suppress colors to highlight critical resources (toggle). .br Whether this key is active or not can be seen in the header line. .PP .TP 5 .B z The pause key can be used to freeze the current situation in order to investigate the output on the screen. While .I atop is paused, the keys described above can be pressed to show other information about the current list of processes. Whenever the pause key is pressed again, atop will continue with a next sample. .br The pause key can be used in text mode and bar graph mode. .PP .TP 5 .B i Modify the interval timer (default: 10 seconds). If an interval timer of 0 is entered, the interval timer is switched off. In that case a new sample can only be triggered manually by pressing the key 't'. .br The interval can be modified in text mode and bar graph mode. .PP .TP 5 .B t Trigger a new sample manually. This key can be pressed if the current sample should be finished before the timer has exceeded, or if no timer is set at all (interval timer defined as 0). In the latter case .I atop can be used as a stopwatch to measure the load being caused by a particular application transaction, without knowing on beforehand how many seconds this transaction will last. .br This key can be used in text mode and bar graph mode. When viewing the contents of a raw file this key can be used to show the next sample from the file. This key can also be used when viewing raw data via a pipe. .PP .TP 5 .B T When viewing the contents of a raw file this key can be used to show the previous sample from the file, however not when reading raw data from a pipe. .br This key can be used in text mode and bar graph mode. .PP .TP 5 .B b When viewing the contents of a raw file, this key can be used to branch to a certain timestamp within the file either forward or backward. When viewing raw data from a pipe only forward branches are possible. .br This key can be used in text mode and bar graph mode. .PP .TP 5 .B r Reset all counters to zero to see the system and process activity since boot again. .br This key can be used in text mode and bar graph mode. When viewing the contents of a raw file, this key can be used to rewind to the beginning of the file again (except when reading raw data from a pipe). .PP .TP 5 .B 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 X By default, .I atop shows/accumulates the processes that are alive and the processes that are exited during the last interval. With this key (toggle), showing/accumulating the processes that are exited can be suppressed. .br Whether this key is active or not can be seen in the header line. .PP .TP 5 .B f Show a fixed (maximum) number of header lines for system resources (toggle). By default only the lines are shown about system resources (CPUs, paging, logical volumes, disks, network interfaces) that really have been active during the last interval. With this key you can force .I atop to show lines of inactive resources as well. .br Whether this key is active or not can be seen in the header line. .PP .TP 5 .B F Suppress sorting of system resources (toggle). By default system resources (CPUs, logical volumes, disks, network interfaces) are sorted on utilization. .br Whether this key is active or not can be seen in the header line. .PP .TP 5 .B 1 Show relevant counters as an average per second (in the format '..../s') instead of as a total during the interval (toggle). .br Whether this key is active or not can be seen in the header line. .PP .TP 5 .B l Limit the number of system level lines for the counters per-cpu, the active disks and the network interfaces. By default lines are shown of all CPUs, disks and network interfaces which have been active during the last interval. Limiting these lines can be useful on systems with huge number CPUs, disks or interfaces in order to be able to run .I atop on a screen/window with e.g. only 24 lines. .br For all mentioned resources the maximum number of lines can be specified interactively. When using the flag .B -l the maximum number of per-cpu lines is set to 0, the maximum number of disk lines to 5 and the maximum number of interface lines to 3. These values can be modified again in interactive mode. .PP .TP 5 .B k Send a signal to an active process (a.k.a. kill a process). .PP .TP 5 .B q Quit the program. .br This key can be used in text mode and bar graph mode. .PP .TP 5 .B PgDn Show the next page of the process/thread list. .br With the arrow-down key the list can be scrolled downwards with single lines. .PP .TP 5 .B ^F Show the next page of the process/thread list (forward). .br With the arrow-down key the list can be scrolled downwards with single lines. .PP .TP 5 .B PgUp Show the previous page of the process/thread list. .br With the arrow-up key the list can be scrolled upwards with single lines. .PP .TP 5 .B ^B Show the previous page of the process/thread list (backward). .br With the arrow-up key the list can be scrolled upwards with single lines. .PP .TP 5 .B ^L Redraw the screen. .SH RAW DATA STORAGE In order to store system and process level statistics for long-term analysis (e.g. to check the system load and the active processes running yesterday between 3:00 and 4:00 PM), .I atop can store the system and process level statistics in compressed binary format in a raw file with the flag .B -w followed by the filename. If this file already exists and is recognized as a raw data file, .I atop will append new samples to the file (starting with a sample which reflects the activity since boot). If the file does not exist, it will be created. .br All information about system, processes and thread activity is stored in the raw file. .br The interval (default: 10 seconds) and number of samples (default: infinite) can be passed as last arguments. Instead of the number of samples, the flag .B -S can be used to indicate that .I atop should finish anyhow before midnight. .PP A raw file can be read and visualized again with the flag .B -r followed by the filename. If no filename is specified, the file .BI /var/log/atop/atop_ YYYYMMDD is opened for input (where .I YYYYMMDD are digits representing the current date). If a filename is specified in the format YYYYMMDD (representing any valid date), the file .BI /var/log/atop/atop_ YYYYMMDD is opened. If a filename with the symbolic name .BI y is specified, yesterday's daily logfile is opened (this can be repeated so 'yyyy' indicates the logfile of four days ago). If the filename .BI - is used, stdin will be read. .br The samples from the file can be viewed interactively by using the key 't' to show the next sample, the key 'T' to show the previous sample, the key 'b' to branch to a particular time 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 header line always shows the host name, the end date and end time when the sample has been taken, the current options that are active (only in text mode), and the elapsed time (duration) of the sample. The verb 'PAUSED' will be shown behind the time when .B atop is currently paused with the 'z' key. The first sample shows the system level activity since boot (the elapsed time in the header shows the time since boot). .PP In text mode, .I atop first shows the lines related to system level activity for every sample. If a particular system resource has not been used during the interval, the entire line related to this resource is suppressed. So the number of system level lines may vary for each sample. .br After that a list is shown of processes which have been active during the last interval. This list is sorted on CPU consumption by default, but this order can be changed by the keys which are previously described. .PP If values have to be shown by .I atop which do not fit in the column width, another format is used. If e.g. a CPU consumption of 233216 milliseconds should be shown in a column width of 4 positions, it is shown as '233s' (in seconds). For large memory figures, another unit is chosen if the value does not fit (Mb instead of Kb, Gb instead of Mb, Tb instead of Gb, etcetera). For other values, a kind of exponent notation is used (value 123456789 shown in a column of 5 positions gives 123e6). .SH OUTPUT DESCRIPTION - SYSTEM LEVEL The system level information in text mode consists of the following output lines: .PP .TP 5 .B PRC Process and thread level totals. .br This line contains the total CPU time consumed in system mode ('sys') and in user mode ('user'), the total number of processes present at this moment ('#proc'), the total number of threads present at this moment in state 'running' ('#trun'), 'sleeping interruptible' ('#tslpi'), 'sleeping uninterruptible' ('#tslpu') and 'idle' ('#tidle'), the number of zombie processes ('#zombie'), the number of clone system calls ('clones'), and the number of processes that ended during the interval ('#exit') when process accounting is used. Instead of '#exit' the last column may indicate that process accounting could not be activated ('no procacct'). .br If the screen-width does not allow all of these counters, only a relevant subset is shown. .PP .TP 5 .B CPU CPU utilization. .br At least one line is shown for the total occupation of all CPUs together. .br In case of a multi-processor system, an additional line is shown for every individual processor (with 'cpu' in lower case), sorted on activity. Inactive CPUs will not be shown by default. The lines showing the per-cpu occupation contain the CPU number in the field combined with the wait percentage. Every line contains the percentage of CPU time spent in kernel mode by all active processes ('sys'), the percentage of CPU time consumed in user mode ('user') for all active processes (including processes running with a nice value larger than zero), the percentage of CPU time spent for interrupt handling ('irq') including softirq, the percentage of unused CPU time while no processes were waiting for disk I/O ('idle'), and the percentage of unused CPU time while at least one process was waiting for disk I/O ('wait'). .br In case of per-cpu occupation, the CPU number and the wait percentage ('w') for that CPU. The number of lines showing the per-cpu occupation can be limited. For virtual machines, the steal-percentage ('steal') shows the percentage of CPU time stolen by other virtual machines running on the same hardware. .br For physical machines hosting one or more virtual machines, the guest percentage ('guest') shows the percentage of CPU time used by the virtual machines. Notice that this percentage overlaps the user percentage! When PMC performance monitoring counters are supported by the CPU and the kernel (and .I atop runs with root privileges), the number of instructions per CPU cycle ('ipc') is shown. The first sample always shows the value 'initial', because the counters are just activated at the moment that .I atop is started. .br When the .I CPU busy percentage is high and the IPC is less than 1.0, it is likely that the CPU is frequently waiting for memory access during instruction execution (larger CPU caches or faster memory might be helpful to improve performance). When the .I CPU busy percentage is high and the IPC is greater than 1.0, it is likely that the CPU is instruction-bound (more/faster cores might be helpful to improve performance). .br Furthermore, per CPU the effective number of cycles ('cycl') is shown. This value can reach the current CPU frequency if such CPU is 100% busy. When an idle CPU is halted, the number of effective cycles can be (considerably) lower than the current frequency. .br Notice that the .I average instructions per cycle and number of cycles is shown in the CPU line for all CPUs. .br Beware that reading the cycle counter in virtual machines (guests) might introduce performance delays. Therefore this metric is by default disabled in virtual machines. However, with the keyword 'perfevents' in the atoprc file this metric can be explicitly set to 'enable' or 'disable' (see separate man-page of atoprc). .br See also: http://www.brendangregg.com/blog/2017-05-09/cpu-utilization-is-wrong.html In case of frequency scaling, all previously mentioned CPU percentages are relative to the used scaling of the CPU during the interval. If a CPU has been active for e.g. 50% in user mode during the interval while the frequency scaling of that CPU was 40%, only 20% of the full capacity of the CPU has been used in user mode. .br In case that the kernel module 'cpufreq_stats' is active (after issuing 'modprobe cpufreq_stats'), the .I average frequency ('avgf') and the .I average scaling percentage ('avgscal') is shown. Otherwise the .I current frequency ('curf') and the .I current scaling percentage ('curscal') is shown at the moment that the sample is taken. Notice that .I average values for frequency and scaling are shown in the CPU line for every CPU. If the screen-width does not allow all of these counters, only a relevant subset is shown. .PP .TP 5 .B CPL CPU load information. .br This line contains the load average figures reflecting the number of threads that are available to run on a CPU (i.e. part of the runqueue) or that are waiting for disk I/O. These figures are averaged over 1 ('avg1'), 5 ('avg5') and 15 ('avg15') minutes. .br Furthermore the number of context switches ('csw'), the number of serviced interrupts ('intr') and the number of available CPUs are shown. If the screen-width does not allow all of these counters, only a relevant subset is shown. .PP .TP 5 .B GPU GPU utilization (Nvidia). .br Read the section GPU STATISTICS GATHERING in this document to find the details about the activation of the .I atopgpud daemon. In the first column of every line, the bus-id (last nine characters) and the GPU number are shown. The subsequent columns show the percentage of time that one or more kernels were executing on the GPU ('gpubusy'), the percentage of time that global (device) memory was being read or written ('membusy'), the occupation percentage of memory ('memocc'), the total memory ('total'), the memory being in use at the moment of the sample ('used'), the average memory being in use during the sample time ('usavg'), the number of processes being active on the GPU at the moment of the sample ('#proc'), and the type of GPU. If the screen-width does not allow all of these counters, only a relevant subset is shown. .br The number of lines showing the GPUs can be limited. .PP .TP 5 .B MEM Memory occupation (two lines). .br These lines contain the total amount of physical memory ('tot'), the amount of memory which is currently free ('free'), the amount of memory that is available for new workloads without pushing the system into swap ('avail'), the amount of memory in use as page cache including the total resident shared memory ('cache'), the amount of memory within the page cache that has to be flushed to disk ('dirty'), the amount of memory used for filesystem meta data ('buff'), the amount of memory being used for kernel mallocs ('slab'), the amount of slab memory that is reclaimable ('slrec'), the resident size of SYSV shared memory including tmpfs but excluding static huge pages ('shmem'), the resident size of SYSV shared memory including static huge pages ('shrss'), the amount of SYSV shared memory that is currently swapped ('shswp'), the amount of memory that is currently used for page tables ('pgtab'), the number of NUMA nodes in this system ('numnode'), the amount of memory that is currently claimed by vmware's balloon driver ('vmbal'), the amount of memory that is currently claimed by the ARC (cache) of ZFSonlinux ('zfarc'), the amount of memory for anonymous transparent huge pages ('anthp'), the amount of memory that is claimed for huge pages ('hptot'), the amount of huge page memory that is really in use ('hpuse'), the amount of memory that is used for TCP sockets ('tcps'), and the amount of memory that is used for UDP sockets ('udps'). If the screen-width does not allow all of these counters, only a relevant subset is shown. .PP .TP 5 .B SWP Swap occupation and overcommit info. .br This line contains the total amount of swap space on disk ('tot'), the amount of free swap space ('free'), the size of the swap cache ('swcac'), the size of compressed storage used for zswap ('zswap'), the real (decompressed) size of the pages stored in zswap ('zstor'), the total size of the memory used for KSM ('ksuse', i.e. shared), and the total size of the memory saved (deduped) by KSM ('kssav', i.e. sharing). .br Furthermore the committed virtual memory space ('vmcom') and the maximum limit of the committed space ('vmlim', which is by default swap size plus 50% of memory size) is shown. The committed space is the reserved virtual space for all allocations of private memory space for processes. The kernel only verifies whether the committed space exceeds the limit if strict overcommit handling is configured (vm.overcommit_memory is 2). .PP .TP 5 .B LLC Last-Level Cache of CPU info. .br This line contains the total memory bandwidth of LLC ('tot'), the bandwidth of the local NUMA node ('loc'), and the percentage of LLC in use ('LLCXX YY%'). Note that this feature depends on the 'resctrl' pseudo filesystem. Be sure that the kernel is built with the relevant config and take care that the pseudo-filesystem is mounted: .B \ mount -t resctrl resctrl -o mba_MBps /sys/fs/resctrl (on Intel) .br .B \ mount -t resctrl resctrl -o cdp \ \ \ \ \ /sys/fs/resctrl (on AMD) .PP .TP 5 .B NUM Memory utilization per NUMA node (not shown for single NUMA node). .br This line shows the total amount of physical memory of this node ('tot'), the amount of free memory ('free'), the amount of memory for cached file data ('file'), modified cached file data ('dirty'), recently used memory ('activ'), less recently used memory ('inact'), memory being used for kernel mallocs ('slab'), the amount of slab memory that is reclaimable ('slrec'), shared memory including tmpfs ('shmem'), total huge pages ('hptot'), used huge pages('hpuse'), and the fragmentation percentage ('frag'). .PP .TP 5 .B NUC CPU utilization per NUMA node (not shown for single NUMA node). .br This line shows the utilization percentages of all CPUs related to this NUMA node, categorized for system mode ('sys'), user mode ('user'), user mode for niced processes ('niced'), idle mode ('idle'), wait mode ('w' preceded by the node number), irq mode ('irq'), softirq mode ('sirq'), steal mode ('steal'), and guest mode ('guest') overlapping user mode. .PP .TP 5 .B PAG Paging frequency. .br This line contains the number of scanned pages ('scan') due to the fact that free memory drops below a particular threshold, the number of reclaimed pages('steal') due to the fact that free memory drops below a particular threshold, the number times that the kernel tries to reclaim pages due to an urgent need ('stall'),the number of process stalls to run memory compaction to allocate huge pages ('compact'), the number of NUMA pages migrated ('numamig'), and the total number of memory pages migrated successfully e.g. between NUMA nodes or for compaction ('migrate') are shown. .br Also the number of memory pages the system read from block devices ('pgin'), the number of memory pages the system wrote to block devices ('pgout'), the number of memory pages swapped in from zswap ('zswin'), the number of memory pages swapped out to zswap ('zswout'), the number of memory pages the system read from swap space ('swin'), the number of memory pages the system wrote to swap space ('swout'), and the number of out-of-memory kills ('oomkill'). .PP .TP 5 .B PSI Pressure Stall Information. .br This line contains percentages about resource pressure related to CPU, memory and 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 those containing only retransmitted octets ('tcpo'), the number of UDP datagrams received ('udpi'), the number of UDP datagrams transmitted ('udpo'), the number of active TCP opens ('tcpao'), the number of passive TCP opens ('tcppo'), the number of TCP output retransmissions ('tcprs'), the number of TCP input errors ('tcpie'), the number of TCP output resets ('tcpor'), the number of UDP no ports ('udpnp'), the number of UDP input errors ('udpie'), and the number of TCP incorrect checksums ('csumie'). .br If the screen-width does not allow all of these counters, only a relevant subset is shown. .br These counters are related to IPv4 and IPv6 combined. For the IP layer, counters are shown concerning the number of IP datagrams received from interfaces, including those received in error ('ipi'), the number of IP datagrams that local higher-layer protocols offered for transmission ('ipo'), the number of received IP datagrams which were forwarded to other interfaces ('ipfrw'), the number of IP datagrams which were delivered to local higher-layer protocols ('deliv'), the number of received ICMP datagrams ('icmpi'), and the number of transmitted ICMP datagrams ('icmpo'). .br If the screen-width does not allow all of these counters, only a relevant subset is shown. .br These counters are related to IPv4 and IPv6 combined. For every active network interface one line is shown, sorted on the interface activity. Such line shows the name of the interface and its busy percentage in the first column. The busy percentage for half duplex is determined by comparing the interface speed with the number of bits transmitted and received per second; for full duplex the interface speed is compared with the highest of either the transmitted or the received bits. When the interface speed can not be determined (e.g. for the loopback interface), '---' is shown instead of the percentage. .br Furthermore the number of received packets ('pcki'), the number of transmitted packets ('pcko'), the line speed of the interface ('sp'), the effective amount of bits received per second ('si'), the effective amount of bits transmitted per second ('so'), the number of collisions ('coll'), the number of received multicast packets ('mlti'), the number of errors while receiving a packet ('erri'), the number of errors while transmitting a packet ('erro'), the number of received packets dropped ('drpi'), and the number of transmitted packets dropped ('drpo'). .br If the screen-width does not allow all of these counters, only a relevant subset is shown. .br The number of lines showing the network interfaces can be limited. .PP .TP 5 .B IFB Infiniband utilization. .br For every active Infiniband port one line is shown, sorted on activity. Such line shows the name of the port and its busy percentage in the first column. The busy percentage is determined by taking the highest of either the transmitted or the received bits during the interval, multiplying that value by the number of lanes and comparing it against the maximum port speed. .br Furthermore the number of received packets divided by the number of lanes ('pcki'), the number of transmitted packets divided by the number of lanes ('pcko'), the maximum line speed ('sp'), the effective amount of bits received per second ('si'), the effective amount of bits transmitted per second ('so'), and the number of lanes ('lanes'). .br If the screen-width does not allow all of these counters, only a relevant subset is shown. .br The number of lines showing the Infiniband ports can be limited. .SH OUTPUT DESCRIPTION - CGROUPS LEVEL (V2) In the bottom part of the screen a hierarchical list of cgroups can be shown. By default, cgroups without assigned processes in the cgroup itself and the cgroups underneath will be suppressed. By pressing the 'a' key (toggle) all cgroups will be shown. When depth level '8' or '9' has been selected (by pressing the corresponding key) the assigned processes are shown as well. By default, only the processes that were active during the interval. By pressing the 'a' key (toggle) all processes will be shown. .PP Per cgroup the following fields may be shown (in alphabetical order), depending on the current width of your window: .PP .TP 9 .B CGROUP Name of the cgroup (version 2), i.e. the directory name in the cgroup hierarchy. This root cgroup '/' corresponds to the cgroup root directory, which is usually '/sys/fs/cgroup'. .PP .TP 9 .B CPUBUSY The consumed CPU percentage by this cgroup and all cgroups underneath (as a percentage of one CPU). Value -1 means undefined. .PP .TP 9 .B CPUMAX The 'cpu.max' value of this cgroup (version 2), calculated as percentage of one CPU. Value -1 means maximum, while value -2 means undefined. .PP .TP 9 .B CPUPS The CPU pressure percentage ('some') in this cgroup and cgroups underneath. .PP .TP 9 .B CPUWGT The 'cpu.weight' value of this cgroup (version 2). Value -2 means undefined. .PP .TP 9 .B DISKIO The amount of data transferred to/from physical disks in this cgroup and the cgroups underneath. Value -1 means undefined. .PP .TP 9 .B DSKPS The disk pressure percentage ('full') in this cgroup and cgroups underneath. .PP .TP 9 .B IOWGT The 'io.weight' value of this cgroup (version 2). Value -2 means undefined. .PP .TP 9 .B MEMMAX The 'memory.max' value of this cgroup (version 2). Value -1 means maximum, while value -2 means undefined. .PP .TP 9 .B MEMORY The current memory occupation of this cgroup and all cgroups underneath. Value -1 means undefined. .PP .TP 9 .B MEMPS The memory pressure percentage ('full') in this cgroup and cgroups underneath. .PP .TP 9 .B NPROCS The number of processes assigned to this cgroup. .PP .TP 9 .B PBELOW The number of processes assigned to the cgroups underneath this cgroup. .PP .TP 9 .B SWPMAX The 'memory.swap.max' value of this cgroup (version 2). Value -1 means maximum, while value -2 means undefined. .SH OUTPUT DESCRIPTION - PROCESS LEVEL In the bottom part of the screen, a list of processes can be shown from which the resource utilization has changed during the last interval. These processes might have used CPU time or might have issued disk or network requests. However a process is also shown if part of it has been paged out due to lack of memory (while the process itself was in sleep state). .PP Per process the following fields may be shown (in alphabetical order), depending on the current output mode as described in the section INTERACTIVE COMMANDS and depending on the current width of your window: .PP .TP 9 .B AVGRSZ The average size of one read-action on disk. .PP .TP 9 .B AVGWSZ The average size of one write-action on disk. .PP .TP 9 .B BANDWI Total bandwidth for received TCP and UDP packets consumed by this process (bits-per-second). This value can be compared with the value 'si' on interface level (used bandwidth per interface). .br This information will only be shown when the optional module .I netatop or .I netatop-bpf is loaded. .PP .TP 9 .B BANDWO Total bandwidth for sent TCP and UDP packets consumed by this process (bits-per-second). This value can be compared with the value 'so' on interface level (used bandwidth per interface). .br This information will only be shown when the optional module .I netatop or .I netatop-bpf is loaded. .PP .TP 9 .B BDELAY Aggregated block I/O delay, i.e. time waiting for disk I/O. .PP .TP 9 .B CID/POD Container id (e.g. Docker or Podman) or pod name (e.g. Kubernetes) referring to the container/pod in which the process/thread is running. When a pod name is longer than 15 characters, only the last 15 characters are shown. If a process has been started and finished during the last interval, a '?' is shown because the container id or pod name is not part of the standard process accounting record. This column will only be shown when .I atop runs with superuser privileges and when at least one containerized process is detected. .PP .TP 9 .B CMD The name of the process. This name can be surrounded by "less/greater than" signs ('') which means that the process has finished during the last interval. A single accounting record is written for the entire process on termination of the last thread in the process. When the main thread exits, the process name is changed to the thread name. .br Behind the abbreviation 'CMD' in the header line, the current page number and the total number of pages of the process/thread list are shown. .PP .TP 9 .B COMMAND-LINE The full command line of the process (including arguments). If the length of the command line exceeds the length of the screen line, the arrow keys -> and <- can be used for horizontal scroll. The '-z ' command line option can be used to prepend matching environment variables to the displayed command line. POSIX Extended Regular Expression syntax are used (see regex(3)). When a matching environment variable is too long (exceeding the buffer that should contain the command line), it will be truncated. .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 CPUNR The identification of the CPU the (main) thread is running on or has recently been running on. .PP .TP 9 .B CTID Container ID (OpenVZ). If a process has been started and finished during the last interval, a '?' is shown because the container ID is not part of the standard process accounting record. .PP .TP 9 .B DSK The occupation percentage of this process related to the total load that is produced by all processes (i.e. total disk accesses by all processes during the last interval). .br This information is shown when per process "storage accounting" is active in the kernel. .PP .TP 9 .B EGID Effective group-id under which this process executes. .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 MEMNOW Memory occupation at the moment of the sample on all used GPUs. .PP .TP 9 .B MINFLT The number of page faults issued by this process that have been solved by reclaiming the requested memory page from the free list of pages. .PP .TP 9 .B NET The occupation percentage of this process related to the total load that is produced by all processes (i.e. consumed network bandwidth of all processes during the last interval). .br This information will only be shown when the optional module .I netatop or .I netatop-bpf is loaded. .PP .TP 9 .B NICE The more or less static priority that can be given to a process on a scale from -20 (high priority) to +19 (low priority). .PP .TP 9 .B NIVCSW Number of times the process/thread was context-switched involuntarily, in case that the time slice expired. .PP .TP 9 .B NPROCS The number of active and terminated processes accumulated for this user or program. .PP .TP 9 .B NVCSW Number of times that the process/thread was context-switched voluntarily in case of a blocking system call, e.g. to wait for an I/O operation to complete. .PP .TP 9 .B PID Process-id. If a process has been started and finished during the last interval, a '?' is shown because the process-id is not part of the standard process accounting record. .PP .TP 9 .B POLI The policies 'norm' (normal, which is SCHED_OTHER), 'btch' (batch) and 'idle' refer to timesharing processes. The policies 'fifo' (SCHED_FIFO) and 'rr' (round robin, which is SCHED_RR) refer to realtime processes. .PP .TP 9 .B PPID Parent process-id. If a process has been started and finished during the last interval, value 0 is shown because the parent process-id is not part of the standard process accounting record. .PP .TP 9 .B PRI The process' priority ranges from 0 (highest priority) to 139 (lowest priority). Priority 0 to 99 are used for realtime processes (fixed priority independent of their behavior) and priority 100 to 139 for timesharing processes (variable priority depending on their recent CPU consumption and the nice value). .PP .TP 9 .B PSIZE The proportional memory size of this process (or user). .br Every process shares resident memory with other processes. E.g. when a particular program is started several times, the code pages (text) are only loaded once in memory and shared by all incarnations. Also the code of shared libraries is shared by all processes using that shared library, as well as shared memory and memory-mapped files. For the PSIZE calculation of a process, the resident memory of a process that is shared with other processes is divided by the number of sharers. This means, that every process is accounted for a proportional part of that memory. Accumulating the PSIZE values of all processes in the system gives a reliable impression of the total resident memory consumed by all processes. .br Since gathering of all values that are needed to calculate the PSIZE is a very time-consuming task, the 'R' key (or '-R' flag) should be active. Gathering these values also requires superuser privileges (otherwise '?K' is shown in the output). .br If a process has finished during the last interval, no value is shown since the proportional memory size is not part of the standard process accounting record. .PP .TP 9 .B RDDSK The read data transfer issued physically on disk (so reading from the disk cache is not accounted for). .br Unfortunately, the kernel aggregates the data transfer of a process to the data transfer of its parent process when terminating, so you might see transfers for (parent) processes like cron, bash or init, that are not really issued by them. .PP .TP 9 .B RDELAY Runqueue delay, i.e. time spent waiting on a runqueue. .PP .TP 9 .B RGID The real group-id under which the process executes. .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 SWAPSZ The swap space consumed by this process (or user). .PP .TP 9 .B SYSCPU CPU time consumption of this process in system mode (kernel mode), usually due to system call handling. .PP .TP 9 .B TCPRASZ The average size of a received TCP buffer in bytes. This information will only be shown when the optional module .I netatop or .I netatop-bpf is installed. .PP .TP 9 .B TCPRCV The number of TCP packets received for this process. This information will only be shown when the optional module .I netatop or .I netatop-bpf is installed. .PP .TP 9 .B TCPSASZ The average size of a transmitted TCP buffer in bytes. This information will only be shown when the optional module .I netatop or .I netatop-bpf is installed. .PP .TP 9 .B TCPSND The number of TCP packets transmitted for this process. This information will only be shown when the optional module .I netatop or .I netatop-bpf is installed. .PP .TP 9 .B THR Total number of threads within this process. All related threads are contained in a thread group, represented by .I atop as one line or as a separate line when the 'y' key (or -y flag) is active. On Linux 2.4 systems it is hardly possible to determine which threads (i.e. processes) are related to the same thread group. Every thread is represented by .I atop as a separate line. .PP .TP 9 .B TID Thread-id. All threads within a process run with the same PID but with a different TID. This value is shown for individual threads in multi-threaded processes (when using the key 'y'). .PP .TP 9 .B TIDLE Number of threads within this process that are in the state 'idle' (I), i.e. uninterruptible sleeping threads that do not count for the load average. .PP .TP 9 .B TRUN Number of threads within this process that are in the state 'running' (R). .PP .TP 9 .B TSLPI Number of threads within this process that are in the state 'interruptible sleeping' (S). .PP .TP 9 .B TSLPU Number of threads within this process that are in the state 'uninterruptible sleeping' (D). .PP .TP 9 .B UDPRASZ The average size of a received UDP packet in bytes. This information will only be shown when the optional module .I netatop or .I netatop-bpf is installed. .PP .TP 9 .B UDPRCV The number of UDP packets received by this process. This information will only be shown when the optional module .I netatop or .I netatop-bpf is installed. .PP .TP 9 .B UDPSASZ The average size of a transmitted UDP packets in bytes. This information will only be shown when the optional module .I netatop or .I netatop-bpf is installed. .PP .TP 9 .B UDPSND The number of UDP packets transmitted by this process. This information will only be shown when the optional module .I netatop or .I netatop-bpf is installed. .PP .TP 9 .B USRCPU CPU time consumption of this process in user mode, due to processing the own program text. .PP .TP 9 .B VDATA The virtual memory size of the private data used by this process (including heap and shared library data). .PP .TP 9 .B VGROW The amount of virtual memory that the process has grown during the last interval. A virtual growth can be caused by e.g. issuing a malloc() or attaching a shared memory segment. Note that a virtual growth can also be negative by e.g. issuing a free() or detaching a shared memory segment. For a process which started during the last interval, the virtual growth reflects the total virtual size of the process at that moment. .br If a process has finished during the last interval, no value is shown since virtual memory occupation is not part of the standard process accounting record. .PP .TP 9 .B VPID Virtual process-id (within an OpenVZ container). If a process has been started and finished during the last interval, a '?' is shown because the virtual process-id is not part of the standard process accounting record. .PP .TP 9 .B VSIZE The total virtual memory usage consumed by this process (or user). .br If a process has finished during the last interval, no value is shown since virtual memory occupation is not part of the standard process accounting record. .PP .TP 9 .B VSLIBS The virtual memory size of the (shared) text of all shared libraries used by this process. .PP .TP 9 .B VSTACK The virtual memory size of the (private) stack used by this process .PP .TP 9 .B VSTEXT The virtual memory size of the (shared) text of the executable program. .PP .TP 9 .B WCHAN Wait channel of thread in sleep state, i.e. the name of the kernel function in which the thread has been put asleep. .br Since determining the name string of the kernel function is a relatively time-consuming task, the 'W' key (or '-W' flag) should be active. .PP .TP 9 .B WRDSK The write data transfer issued physically on disk (so writing to the disk cache is not accounted for). This counter is maintained for the application process that writes its data to the cache (assuming that this data is physically transferred to disk later on). Notice that disk I/O needed for swapping is not taken into account. .br Unfortunately, the kernel aggregates the data transfer of a process to the data transfer of its parent process when terminating, so you might see transfers for (parent) processes like cron, bash or init, that are not really issued by them. .PP .TP 9 .B WCANCL The write data transfer previously accounted for this process or another process that has been cancelled. Suppose that a process writes new data to a file and that data is removed again before the cache buffers have been flushed to disk. Then the original process shows the written data as WRDSK, while the process that removes/truncates the file shows the unflushed removed data as WCANCL. .SH PARSABLE OUTPUT With the flag .B -P followed by a list of one or more labels (comma-separated), parsable output is produced for each sample. The labels that can be specified for system-level statistics correspond to the labels (first verb of each line) that can be found in the interactive output: "CPU", "cpu", "CPL", "GPU", "MEM", "SWP", "PAG", "PSI", "LVM", "MDD", "DSK", "NFM", "NFC", "NFS", "NET", "IFB", "LLC", "NUM" and "NUC". For cgroup-level statistics the label "CGR" is available. For process-level statistics special labels are available: "PRG" (general), "PRC" (CPU), "PRE" (GPU), "PRM" (memory), "PRD" (disk, but requires special privileges) and "PRN" (network, only if the optional module .I netatop or .I netatop-bpf is installed). .br With the label "ALL", all system and process level statistics are shown. .PP The command and command line in the parsable output might contain spaces and are therefore by default surrounded by parenthesis. However, since a space is often used as separator between the fields by parsing tools, with the additional flag .B -Z it is possible to exchange the spaces in the command (line) by underscores and omit the parenthesis. .PP For every interval all requested lines are shown whereafter .B atop shows a line just containing the label "SEP" as a separator before the lines for the next sample are generated. .br When a sample contains the values since boot, .B atop shows a line just containing the label "RESET" before the lines for this sample are generated. .PP The first part of each output-line consists of the following six fields: .B label (the name of the label), .B host (the name of this machine), .B epoch (the time of this interval as number of seconds since 1-1-1970), .B date (date of this interval in format YYYY/MM/DD), .B time (time of this interval in format HH:MM:SS), and .B interval (number of seconds elapsed for this interval). .PP The subsequent fields of each output-line depend on the label: .PP .TP 9 .B CPU Subsequent fields: total number of clock-ticks per second for this machine, number of processors, consumption for all CPUs in system mode (clock-ticks), consumption for all CPUs in user mode (clock-ticks), consumption for all CPUs in user mode for niced processes (clock-ticks), consumption for all CPUs in idle mode (clock-ticks), consumption for all CPUs in wait mode (clock-ticks), consumption for all CPUs in irq mode (clock-ticks), consumption for all CPUs in softirq mode (clock-ticks), consumption for all CPUs in steal mode (clock-ticks), consumption for all CPUs in guest mode (clock-ticks) overlapping user mode, frequency of all CPUs, frequency percentage of all CPUs, instructions executed by all CPUs and cycles for all CPUs. .TP 9 .B cpu Subsequent fields: total number of clock-ticks per second for this machine, processor-number, consumption for this CPU in system mode (clock-ticks), consumption for this CPU in user mode (clock-ticks), consumption for this CPU in user mode for niced processes (clock-ticks), consumption for this CPU in idle mode (clock-ticks), consumption for this CPU in wait mode (clock-ticks), consumption for this CPU in irq mode (clock-ticks), consumption for this CPU in softirq mode (clock-ticks), consumption for this CPU in steal mode (clock-ticks), consumption for this CPU in guest mode (clock-ticks) overlapping user mode, frequency of this CPU, frequency percentage of this CPU, instructions executed by this CPU and cycles for this CPU. .TP 9 .B CPL Subsequent fields: number of processors, load average for last minute, load average for last five minutes, load average for last fifteen minutes, number of context-switches, and number of device interrupts. .TP 9 .B GPU Subsequent fields: GPU number, bus-id string, type of GPU string, GPU busy percentage during last second (-1 if not available), memory busy percentage during last second (-1 if not available), total memory size (KiB), used memory (KiB) at this moment, number of samples taken during interval, cumulative GPU busy percentage during the interval (to be divided by the number of samples for the average busy percentage, -1 if not available), cumulative memory busy percentage during the interval (to be divided by the number of samples for the average busy percentage, -1 if not available), and cumulative memory occupation during the interval (to be divided by the number of samples for the average occupation). .TP 9 .B MEM Subsequent fields: page size for this machine (in bytes), size of physical memory (pages), size of free memory (pages), size of page cache (pages), size of buffer cache (pages), size of slab (pages), dirty pages in cache (pages), reclaimable part of slab (pages), total size of vmware's balloon pages (pages), total size of shared memory (pages), size of resident shared memory (pages), size of swapped shared memory (pages), smaller huge page size (in bytes), total size of smaller huge pages (huge pages), size of free smaller huge pages (huge pages), size of ARC (cache) of ZFSonlinux (pages), size of sharing pages for KSM (pages), size of shared pages for KSM (pages), size of memory used for TCP sockets (pages), size of memory used for UDP sockets (pages), size of pagetables (pages), larger huge page size (in bytes), total size of larger huge pages (huge pages), size of free larger huge pages (huge pages), size of available memory (pages) for new workloads without swapping, and size of anonymous transparent huge pages ('normal' pages). .TP 9 .B SWP Subsequent fields: page size for this machine (in bytes), size of swap (pages), size of free swap (pages), size of swap cache (pages), size of committed space (pages), limit for committed space (pages), size of the swap cache (pages), the real (decompressed) size of the pages stored in zswap (pages), and the size of compressed storage used for zswap (pages). .TP 9 .B LLC Subsequent fields: LLC id, percentage of LLC in use, total memory bandwidth of this LLC (in bytes), and memory bandwidth on local NUMA node of this LLC (in bytes). .TP 9 .B PAG Subsequent fields: page size for this machine (in bytes), number of page scans, number of allocstalls, 0 (future use), number of swapins, number of swapouts, number of oomkills (-1 when counter not present), number of process stalls to run memory compaction, number of pages successfully migrated in total, number of NUMA pages migrated, number of pages read from block devices, number of pages written to block devices, number of swapins from zswap, and number of swapouts to zswap. .TP 9 .B PSI Subsequent fields: PSI statistics present on this system (n or y), CPU some avg10, CPU some avg60, CPU some avg300, CPU some accumulated microseconds during interval, memory some avg10, memory some avg60, memory some avg300, memory some accumulated microseconds during interval, memory full avg10, memory full avg60, memory full avg300, memory full accumulated microseconds during interval, I/O some avg10, I/O some avg60, I/O some avg300, I/O some accumulated microseconds during interval, I/O full avg10, I/O full avg60, I/O full avg300, and I/O full accumulated microseconds during interval. .TP 9 .B LVM/MDD/DSK For every logical volume/multiple device/hard disk one line is shown. .br Subsequent fields: name, number of milliseconds spent for I/O, number of reads issued, number of sectors transferred for reads, number of writes issued, number of sectors transferred for write, number of discards issued (-1 if not supported), number of sectors transferred for discards, number of requests currently in flight (not yet completed), and the average queue depth while the disk was busy. .TP 9 .B NFM Subsequent fields: mounted NFS filesystem, total number of bytes read, total number of bytes written, number of bytes read by normal system calls, number of bytes written by normal system calls, number of bytes read by direct I/O, number of bytes written by direct I/O, number of pages read by memory-mapped I/O, and number of pages written by memory-mapped I/O. .TP 9 .B NFC Subsequent fields: number of transmitted RPCs, number of transmitted read RPCs, number of transmitted write RPCs, number of RPC retransmissions, and number of authorization refreshes. .TP 9 .B NFS Subsequent fields: number of handled RPCs, number of received read RPCs, number of received write RPCs, number of bytes read by clients, number of bytes written by clients, number of RPCs with bad format, number of RPCs with bad authorization, number of RPCs from bad client, total number of handled network requests, number of handled network requests via TCP, number of handled network requests via UDP, number of handled TCP connections, number of hits on reply cache, number of misses on reply cache, and number of uncached requests. .TP 9 .B NET First, one line is produced for the upper layers of the TCP/IP stack. .br Subsequent fields: the verb "upper", number of packets received by TCP, number of packets transmitted by TCP, number of packets received by UDP, number of packets transmitted by UDP, number of packets received by IP, number of packets transmitted by IP, number of packets delivered to higher layers by IP, number of packets forwarded by IP, number of input errors (UDP), number of noport errors (UDP), number of active opens (TCP), number of passive opens (TCP), number of passive opens (TCP), number of established connections at this moment (TCP), number of retransmitted segments (TCP), number of input errors (TCP), number of output resets (TCP), and number of checksum errors on received packets (TCP). Next, one line is shown for every interface. .br Subsequent fields: name of the interface, number of packets received by the interface, number of bytes received by the interface, number of packets transmitted by the interface, number of bytes transmitted by the interface, interface speed, 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 CGR For every cgroup (level) one or two lines are shown. The first line shows the utilization and configuration values of the cgroup. The optional second line shows the PIDs of the processes assigned to this cgroup. Subsequent fields of first line: character 'C', full path name of cgroup, number of processes assigned to this group, number of processes assigned to the cgroups underneath, CPU usage in user mode (microseconds), CPU usage in system mode (microseconds), CPU weight, CPU max, current memory usage (pages), anonymous memory usage (pages), file memory usage (pages), kernel memory usage (pages), shared memory usage (pages), memory max (pages), swap max (pages), number of bytes read from disk, number of bytes written to disk, number of read requests from disk, number of write requests to disk, the disk weight, cpu some pressure (microseconds), cpu total pressure (microseconds), memory some pressure (microseconds), memory total pressure (microseconds), disk some pressure (microseconds), and disk total pressure (microseconds). Subsequent fields of second line (only when processes assigned to this cgroup): character 'P', full path name of cgroup, and list of PIDs separated by spaces. .TP 9 .B PRG For every process one line is shown. .br Subsequent fields: PID (unique ID of task), name (between parenthesis or underscores for spaces), state, real uid, real gid, TGID (group number of related tasks/threads), total number of threads, exit code (in case of fatal signal: signal number + 256), start time (epoch), full command line (between parenthesis or underscores for spaces), PPID, number of threads in state 'running' (R), number of threads in state 'interruptible sleeping' (S), number of threads in state 'uninterruptible sleeping' (D), effective uid, effective gid, saved uid, saved gid, filesystem uid, filesystem gid, elapsed time of terminated process (hertz), is_process (y/n), OpenVZ virtual pid (VPID), OpenVZ container id (CTID), container/pod name (CID/POD), indication if the task is newly started during this interval ('N'), cgroup v2 path name (between parenthesis or underscores for spaces), end time (epoch or 0 if still active), and number of threads in state 'idle' (I). .TP 9 .B PRC For every process one line is shown. .br Subsequent fields: PID, name (between parenthesis or underscores for spaces), state, total number of clock-ticks per second for this machine, CPU-consumption in user mode (clockticks), CPU-consumption in system mode (clockticks), nice value, priority, realtime priority, scheduling policy, current CPU (-1 for exited process), sleep average, TGID (group number of related tasks/threads), is_process (y/n), runqueue delay in nanoseconds for this thread or for all threads (in case of process), wait channel of this thread (between parenthesis or underscores for spaces), block I/O delay (clockticks), cgroup v2 'cpu.max' calculated as percentage (-3 means no cgroup v2 support, -2 means undefined and -1 means maximum), cgroup v2 most restrictive 'cpu.max' in upper directories calculated as percentage (-3 means no cgroup v2 support, -2 means undefined and -1 means maximum), number of voluntary context switches, and number of involuntary context switches. .TP 9 .B PRE For every process one line is shown. .br Subsequent fields: PID, name (between parenthesis or underscores for spaces), process state, GPU state (A for active, E for exited, N for no GPU user), number of GPUs used by this process, bitlist reflecting used GPUs, GPU busy percentage during interval, memory busy percentage during interval, memory occupation (KiB) at this moment cumulative memory occupation (KiB) during interval, and number of samples taken during interval. .TP 9 .B PRM For every process one line is shown. .br Subsequent fields: PID, name (between parenthesis or underscores for spaces), state, page size for this machine (in bytes), virtual memory size (KiB), resident memory size (KiB), shared text memory size (KiB), virtual memory growth (KiB), resident memory growth (KiB), number of minor page faults, number of major page faults, virtual library exec size (KiB), virtual data size (KiB), virtual stack size (KiB), swap space used (KiB), TGID (group number of related tasks/threads), is_process (y/n), proportional set size (KiB) if in 'R' option is specified, virtually locked memory space (KiB), cgroup v2 'memory.max' in KiB (-3 means no cgroup v2 support, -2 means undefined and -1 means maximum), cgroup v2 most restrictive 'memory.max' in upper directories in KiB (-3 means no cgroup v2 support, -2 means undefined and -1 means maximum), cgroup v2 'memory.swap.max' in KiB (-3 means no cgroup v2 support, -2 means undefined and -1 means maximum), and cgroup v2 most restrictive 'memory.swap.max' in upper directories in KiB (-3 means no cgroup v2 support, -2 means undefined and -1 means maximum). .TP 9 .B PRD For every process one line is shown. .br Subsequent fields: PID, name (between parenthesis or underscores for spaces), state, obsoleted kernel patch installed ('n'), standard io statistics used ('y' or 'n'), number of reads on disk, cumulative number of sectors read, number of writes on disk, cumulative number of sectors written, cancelled number of written sectors, TGID (group number of related tasks/threads), obsoleted value ('n'), and is_process (y/n). .TP 9 .B PRN For every process one line is shown. .br Subsequent fields: PID, name (between parenthesis or underscores for spaces), state, kernel module .I netatop or .I netatop-bpf installed ('y' or 'n'), number of TCP-packets transmitted, cumulative size of TCP-packets transmitted, number of TCP-packets received, cumulative size of TCP-packets received, number of UDP-packets transmitted, cumulative size of UDP-packets transmitted, number of UDP-packets received, cumulative size of UDP-packets transmitted, number of raw packets transmitted (obsolete, always 0), number of raw packets received (obsolete, always 0), TGID (group number of related tasks/threads) and is_process (y/n). .br If the kernel module is not active, the network I/O counters per process are not relevant. .PP .SH JSON OUTPUT With the flag .B -J followed by a list of one or more labels (comma-separated), JSON output is produced for each sample. The syntax and name of JSON labels are the same as for the parsable output. .SH SIGNALS By sending the SIGUSR1 signal to .I atop a new sample will be forced, even if the current timer interval has not exceeded yet. The behavior is similar to pressing the 't' key in an interactive session. .PP By sending the SIGUSR2 signal to .I atop a final sample will be forced after which .I atop will terminate. .SH EXAMPLES Monitor the current system load in text mode with an interval of (default) 10 seconds: .PP .TP 12 .B \ atop .PP Monitor the current system load as bar graphs with an interval of 5 seconds: .PP .TP 12 .B \ atop -B 5 .PP View the cgroups with the related processes (except kernel processes) with the possibility to review previous samples during the live measurement: .PP .TP 12 .B \ atop -G8 -t .PP Store information about the system and process activity in binary compressed form to a file with an interval of ten minutes during an hour: .PP .TP 12 .B \ atop -w /tmp/atop.raw 600 6 .PP View the contents of this file interactively: .PP .B \ atop -r /tmp/atop.raw .PP View the processor and disk utilization of this file in parsable format: .PP .B \ atop -PCPU,DSK -r /tmp/atop.raw .PP View the contents of today's standard logfile interactively: .PP .B \ atop -r .PP View the contents of the standard logfile of the day before yesterday interactively: .PP .B \ atop -r yy .PP View the contents of the standard logfile of 2024, April 5 from 02:00 PM onwards interactively: .PP .B \ atop -r 20240405 -b 1400 .PP Concatenate all raw log files of March 2024 and generate parsable output about the CPU utilization: .PP .TP 12 .B \ atopcat /var/log/atop/atop_202403?? | atop -r - -PCPU .PP Monitor the system load and write it to a file (in plain ASCII) with an interval of one minute during half an hour with active processes sorted on memory consumption: .PP .TP 12 .B \ atop -M 60 30 > /log/atop.mem .PP .SH FILES .PP .TP 5 .B /var/run/pacct_shadow.d/ Directory containing the process accounting shadow files that are used by .I atop when the .I atopacctd daemon is active. .PP .TP 5 .B /var/cache/atop.d/atop.acct File in which the kernel writes the accounting records when .I atop itself has activated the process accounting mechanism. .PP .TP 5 .B /etc/atoprc Configuration file containing system-wide default values. For further information about the default values, refer to the .B atoprc man page). .PP .TP 5 .B ~/.atoprc Configuration file containing personal default values. For further information about the default values, refer to the .B atoprc man page). .PP .TP 5 .B /etc/default/atop Configuration file to overrule the settings of .I atop that runs in the background to create the daily logfile. This file is created when .I atop is installed. The default settings are: LOGOPTS="" .br LOGINTERVAL=600 .br LOGGENERATIONS=28 .PP .TP 5 .BI /var/log/atop/atop_ YYYYMMDD Raw file, where .I YYYYMMDD are digits representing the current date. This name is used by .B atop running in the background as default name for the output file, and by .B atop as default name for the input file when using the .B -r flag. .br All binary system and process level data in this file has been stored in compressed format. .PP .TP 5 .BI /var/run/netatop.log File that contains the netpertask structs containing the network counters of exited processes. These structs are written by the .I netatopd daemon (which is related to the .I netatop module) and read by .I atop after reading the standard process accounting records. .SH SEE ALSO .B atopsar(1), .B atopconvert(1), .B atopcat(1), .B atophide(1), .B atoprc(5), .B atopacctd(8), .B netatop(4), .B netatopd(8), .B atopgpud(8), .B logrotate(8) .br .B https://www.atoptool.nl .SH AUTHOR Gerlof Langeveld (gerlof.langeveld@atoptool.nl) .br JC van Winkel atop-2.11.1/man/atoprc.50000644000203100020310000002745614771753566014233 0ustar gerlofgerlof.TH ATOPRC 5 "January 2024" "Linux" .SH NAME .B atoprc - atop/atopsar related rcfile .SH DESCRIPTION This manual page documents the rcfile of the .I atop and .I atopsar commands. These commands can be used to monitor the system and process load on a Linux system. .PP The atoprc file contains the default settings. These settings are read during startup, first from the system-wide rcfile .I /etc/atoprc and after that from the user-specific rcfile .I ~/.atoprc (so system-wide settings can be overruled by an individual user). The options in both rcfiles are identical. .PP .SH OPTIONS .PP The rcfile contains keyword-value pairs, one on every line (blank lines and lines starting with a #-sign are ignored). .br The following keywords can be specified: .PP .TP 4 .B flags A list of default flags for .B atop can be defined here. The flags which are allowed are 't', 'B', 'H', 'G', '2' till '9', 'g', 'm', 'd', 'n', 'u', 'p', 's', 'c', 'v', \&'C', 'M', 'D', 'N', 'A', \&'a', 'y', 'Y', 'f', 'F', 'R', '1', 'e', 'E' and 'x'. .PP .TP 4 .B twindir The default absolute path name of the temporary directory in which the twin file should be allocated. .PP .TP 4 .B interval The default interval value in seconds. .PP .TP 4 .B linelen The length of a screen line when sending output to a file or pipe (default 80). .PP .TP 4 .B username 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.11.1/man/atopcat.10000644000203100020310000000442214771753566014356 0ustar gerlofgerlof.TH ATOPCAT 1 "January 2024" "Linux" .SH NAME .B atopcat - concatenate raw log files to stdout .SH SYNOPSIS .P .B atopcat [-dv] rawfile [rawfile]... .P .SH DESCRIPTION The program .I atopcat can be used to concatenate several raw log files into one stream (stdout). In this way, raw log files can be merged into one larger file by redirecting stdout to a file. Alternatively, merged data from several raw log files can be transferred directly into .I atop or .I atopsar via a pipe. Options: .PP .TP 5 .B -d dry-run: read logfile(s) but do not generate output on stdout .PP .TP 5 .B -v verbose: print one line per sample containing date/time, interval length in seconds, compressed length of the system-level information, compressed length of the process-level information, compressed length of the cgroup-level information, and compressed length of the PID list. .SH EXAMPLES Concatenate the raw log files of five contiguous working days, write it into a new raw log file for that week and view that week interactively: .PP .TP 12 .B \ atopcat /var/log/atop/atop_2024021[0-4] > week_2024_7 .TP 12 .B \ atop -r week_2024_7 .PP Concatenate the raw log files of a week and view that week interactively (since .I atop reads from a pipe, previous intervals can not be retrieved while viewing): .PP .TP 12 .B \ atopcat /var/log/atop/atop_2024021[0-6] | atop -r - .PP Concatenate all raw log files of January 2024 and generate parsable output about the CPU utilization: .PP .TP 12 .B \ atopcat /var/log/atop/atop_202401?? | atop -r - -PCPU .PP Concatenate the daily raw log files of February 3 and 4, and generate a report about memory utilization from 14:00h on the first day till 11:00h on the second day: .PP .TP 12 .B \ atopcat /var/log/atop/atop_2024020[34] | .B \ atopsar -m -r - -b 202402031400 -e 202402041100 .PP Repair a raw log file from which the last interval has not been completely written (e.g. if you intend to expand the file with new samples): .PP .TP 12 .B \ atopcat /var/log/atop/atop_20240303 > /tmp/repaired .PP In the latter case, .B atopcat reports that the input file is incomplete and stops after the last consistent sample. .SH SEE ALSO .B atop(1), .B atopsar(1), .B atophide(1), .B atopconvert(1) .br .B https://www.atoptool.nl .SH AUTHOR Gerlof Langeveld (gerlof.langeveld@atoptool.nl) atop-2.11.1/netatopd.h0000644000203100020310000000330414771753566014053 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.11.1/atop.h0000644000203100020310000001470214771753566013204 0ustar gerlofgerlof/* ** ATOP - System & Process Monitor ** ** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as process-level. ** ** Include-file describing miscellaneous constants and function-prototypes. ** ================================================================ ** Author: Gerlof Langeveld ** E-mail: gerlof.langeveld@atoptool.nl ** Date: November 1996 ** LINUX-port: June 2000 ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. */ #ifndef __ATOP__ #define __ATOP__ #include #define EQ 0 #define SECONDSINDAY 86400 #define RAWNAMESZ 256 /* ** memory-size formatting possibilities */ #define BFORMAT 0 #define KBFORMAT 1 #define KBFORMAT_INT 2 #define MBFORMAT 3 #define MBFORMAT_INT 4 #define GBFORMAT 5 #define GBFORMAT_INT 6 #define TBFORMAT 7 #define TBFORMAT_INT 8 #define PBFORMAT 9 #define PBFORMAT_INT 10 #define EBFORMAT 11 #define EBFORMAT_INT 12 #define OVFORMAT 13 typedef long long count_t; struct tstat; struct devtstat; struct sstat; struct cgchainer; struct netpertask; /* ** miscellaneous flags */ #define RRBOOT 0x0001 #define RRLAST 0x0002 #define RRNETATOP 0x0004 #define RRNETATOPD 0x0008 #define RRACCTACTIVE 0x0010 #define RRIOSTAT 0x0020 #define RRCONTAINERSTAT 0x0040 #define RRGPUSTAT 0x0080 #define RRCGRSTAT 0x0100 struct visualize { char (*show_samp) (time_t, int, struct devtstat *, struct sstat *, struct cgchainer *, int, int, 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 fdinotify; extern pid_t twinpid; extern int linelen; extern char acctreason; extern char deviatonly; extern char usecolors; extern char threadview; extern char calcpss; extern char getwchan; extern char rawname[]; extern char rawreadflag; extern char connectnetatop; 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 *, struct cgchainer *, int, int, int, unsigned int, char); void generic_error(const char *, ...); void generic_end (void); void generic_usage(void); /* ** miscellaneous prototypes */ int atopsar(int, char *[]); char *convtime(time_t, char *); char *convdate(time_t, char *); int getbranchtime(char *, time_t *); time_t normalize_epoch(time_t, long); char *val2valstr(count_t, char *, int, int, int); char *val2memstr(count_t, char *, int, int, int); char *val2cpustr(count_t, char *); char *val2Hzstr(count_t, char *); int val2elapstr(int, char *); int compcpu(const void *, const void *); int compdsk(const void *, const void *); int compmem(const void *, const void *); int compnet(const void *, const void *); int compgpu(const void *, const void *); int compusr(const void *, const void *); int compnam(const void *, const void *); int compcon(const void *, const void *); int cpucompar (const void *, const void *); int gpucompar (const void *, const void *); int diskcompar(const void *, const void *); int intfcompar(const void *, const void *); int ifbcompar(const void *, const void *); int nfsmcompar(const void *, const void *); int contcompar(const void *, const void *); int memnumacompar(const void *, const void *); int cpunumacompar(const void *, const void *); int llccompar(const void *, const void *); int rawread(void); char rawwrite (time_t, int, struct devtstat *, struct sstat *, struct cgchainer *, int, int, int, unsigned int, char); int numeric(char *); void getalarm(int); unsigned long long getboot(void); char *getstrvers(void); unsigned short getnumvers(void); void ptrverify(const void *, const char *, ...); void mcleanstop(int, const char *, ...); void cleanstop(int); int getpidwidth(void); void prusage(char *); int rootprivs(void); int droprootprivs(void); void regainrootprivs(void); FILE *fopen_tryroot(const char *, const char *); void netatop_ipopen(void); void netatop_probe(void); void netatop_signoff(void); void netatop_gettask(pid_t, char, struct tstat *); unsigned int netatop_exitstore(void); void netatop_exiterase(void); void netatop_exithash(char); void netatop_exitfind(unsigned long, struct tstat *, struct tstat *); void netatop_bpf_ipopen(void); void netatop_bpf_probe(void); void netatop_bpf_gettask(void); void netatop_bpf_exitfind(unsigned long, struct tstat *, struct tstat *); void set_oom_score_adj(void); int run_in_guest(void); void getusr1(int), getusr2(int); void do_pacctdir(char *, char *); void do_atopsarflags(char *, char *); int netlink_open(void); int netlink_recv(int, int); int getutsname(struct tstat *); void resetutsname(void); #endif atop-2.11.1/COPYING0000644000203100020310000004313114771753566013121 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.11.1/atop.default0000644000203100020310000000010314771753566014367 0ustar gerlofgerlofLOGOPTS="" LOGINTERVAL=600 LOGGENERATIONS=28 LOGPATH=/var/log/atop atop-2.11.1/photoproc.c0000644000203100020310000005131414771753566014251 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" static int procstat(struct tstat *, unsigned long long, char); static int procstatus(struct tstat *); static int procio(struct tstat *); static void proccmd(struct tstat *); static void procsmaps(struct tstat *); static void procwchan(struct tstat *); static count_t procschedstat(struct tstat *); extern GHashTable *ghash_net; extern char prependenv; extern regex_t envregex; unsigned long photoproc(struct tstat *tasklist, int maxtask) { static int firstcall = 1; static unsigned long long bootepoch; register struct tstat *curtask; FILE *fp; DIR *dirp; struct dirent *entp; char origdir[4096], dockstat=0; unsigned long tval=0; /* ** one-time initialization stuff */ if (firstcall) { /* ** check if this kernel offers io-statistics per task */ regainrootprivs(); if ( (fp = fopen("/proc/1/io", "r")) ) { supportflags |= IOSTAT; fclose(fp); } if (! droprootprivs()) mcleanstop(42, "failed to drop root privs\n"); /* ** find epoch time of boot moment */ bootepoch = getboot(); firstcall = 0; } /* ** probe if the netatop module and (optionally) the ** netatopd daemon are active */ regainrootprivs(); /* ** if netatop kernel module is not active on this system, ** try to connect to netatop-bpf */ if (connectnetatop && !(supportflags & NETATOP)) { netatop_bpf_probe(); } /* ** if netatop-bpf is not active on this system, ** try to connect to kernel module */ if (connectnetatop && !(supportflags & NETATOPBPF)) { netatop_probe(); } /* ** if netatop-bpf is active on this system, fetch data */ if (supportflags & NETATOPBPF) { netatop_bpf_gettask(); } if (! droprootprivs()) mcleanstop(42, "failed to drop root privs\n"); /* ** read all subdirectory-names below the /proc directory */ if ( getcwd(origdir, sizeof origdir) == NULL) mcleanstop(53, "failed to save current dir\n"); if ( chdir("/proc") == -1) mcleanstop(54, "failed to change to /proc\n"); dirp = opendir("."); while ( (entp = readdir(dirp)) && tval < maxtask ) { /* ** skip non-numerical names */ if (!isdigit(entp->d_name[0])) continue; /* ** change to the process' subdirectory */ if ( chdir(entp->d_name) != 0 ) continue; /* ** gather process-level information */ curtask = tasklist+tval; if ( !procstat(curtask, bootepoch, 1)) /* from /proc/pid/stat */ { if ( chdir("..") == -1) ; continue; } if ( !procstatus(curtask) ) /* from /proc/pid/status */ { if ( chdir("..") == -1) ; continue; } if ( !procio(curtask) ) /* from /proc/pid/io */ { if ( chdir("..") == -1) ; continue; } procschedstat(curtask); /* from /proc/pid/schedstat */ proccmd(curtask); /* from /proc/pid/cmdline */ dockstat += getutsname(curtask); /* retrieve container/pod name */ /* ** reading the smaps file for every process with every sample ** is a really 'expensive' from a CPU consumption point-of-view, ** so gathering this info is optional */ if (calcpss) procsmaps(curtask); /* from /proc/pid/smaps */ /* ** determine thread's wchan, if wanted ('expensive' from ** a CPU consumption point-of-view) */ if (getwchan) procwchan(curtask); if (supportflags & NETATOPBPF) { struct taskcount *tc = g_hash_table_lookup(ghash_net, &(curtask->gen.tgid)); if (tc) { // printf("%d %d %d %d %d\n",curtask->gen.tgid, tc->tcpsndpacks, tc->tcprcvpacks, tc->udpsndpacks, tc->udprcvpacks); curtask->net.tcpsnd = tc->tcpsndpacks; curtask->net.tcprcv = tc->tcprcvpacks; curtask->net.tcpssz = tc->tcpsndbytes; curtask->net.tcprsz = tc->tcprcvbytes; curtask->net.udpsnd = tc->udpsndpacks; curtask->net.udprcv = tc->udprcvpacks; curtask->net.udpssz = tc->udpsndbytes; curtask->net.udprsz = tc->udprcvbytes; } } else { // read network stats from netatop (if active) netatop_gettask(curtask->gen.tgid, 'g', curtask); } tval++; /* increment for process-level info */ /* ** if needed (when number of threads is larger than 1): ** read and fill new entries with thread-level info */ if (curtask->gen.nthr > 1) { DIR *dirtask; struct dirent *tent; curtask->gen.nthrrun = 0; curtask->gen.nthrslpi = 0; curtask->gen.nthrslpu = 0; curtask->gen.nthridle = 0; /* ** rundelay and blkdelay on process level only ** concerns the delays of the main thread; ** totalize the delays of all threads */ curtask->cpu.rundelay = 0; curtask->cpu.blkdelay = 0; /* ** nvcsw and nivcsw on process level only ** concerns the delays of the main thread; ** totalize the delays of all threads */ curtask->cpu.nvcsw = 0; curtask->cpu.nivcsw = 0; /* ** open underlying task directory */ if ( chdir("task") == 0 ) { unsigned long cur_nth = 0; dirtask = opendir("."); /* ** due to race condition, opendir() might ** have failed (leave task and process-level ** directories) */ if( dirtask == NULL ) { if(chdir("../..") == -1) ; continue; } while ((tent=readdir(dirtask)) && tvald_name[0] == '.' || chdir(tent->d_name) != 0 ) continue; if ( !procstat(curthr, bootepoch, 0)) { if ( chdir("..") == -1) ; continue; } if ( !procstatus(curthr) ) { if ( chdir("..") == -1) ; continue; } if ( !procio(curthr) ) { if ( chdir("..") == -1) ; continue; } /* ** determine thread's wchan, if wanted ** ('expensive' from a CPU consumption ** point-of-view) */ if (getwchan) procwchan(curthr); // totalize values of all threads curtask->cpu.rundelay += procschedstat(curthr); curtask->cpu.blkdelay += curthr->cpu.blkdelay; curtask->cpu.nvcsw += curthr->cpu.nvcsw; curtask->cpu.nivcsw += curthr->cpu.nivcsw; // continue gathering strcpy(curthr->gen.utsname, curtask->gen.utsname); switch (curthr->gen.state) { case 'R': curtask->gen.nthrrun += 1; break; case 'S': curtask->gen.nthrslpi += 1; break; case 'D': curtask->gen.nthrslpu += 1; break; case 'I': curtask->gen.nthridle += 1; break; } curthr->gen.nthr = 1; // try to read network stats from netatop's module if (!(supportflags & NETATOPBPF)) { netatop_gettask(curthr->gen.pid, 't', curthr); } // all stats read now tval++; /* increment thread-level */ cur_nth++; /* increment # threads */ if ( chdir("..") == -1) ; /* thread */ } closedir(dirtask); if ( chdir("..") == -1) ; /* leave task */ // calibrate number of threads curtask->gen.nthr = cur_nth; } } if ( chdir("..") == -1) ; /* leave process-level directry */ } closedir(dirp); if ( chdir(origdir) == -1) mcleanstop(55, "cannot change to %s\n", origdir); if (dockstat) supportflags |= CONTAINERSTAT; else supportflags &= ~CONTAINERSTAT; resetutsname(); // reassociate atop with own UTS namespace return tval; } /* ** count number of tasks in the system, i.e. ** the number of processes plus the total number of threads */ 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 environment variables */ #define ABBENVLEN 16 static void proccmd(struct tstat *curtask) { FILE *fp, *fpe; register int i, nr; ssize_t env_len = 0; register char *pc = curtask->gen.cmdline; memset(curtask->gen.cmdline, 0, CMDLEN+1); // initialize command line // prepend by environment variables (if required) // if ( prependenv && (fpe = fopen("environ", "r")) != NULL) { char *line = NULL; ssize_t nread; size_t len = 0; while ((nread = getdelim(&line, &len, '\0', fpe)) != -1) { if (nread > 0 && !regexec(&envregex, line, 0, NULL, 0)) { if (env_len + nread >= CMDLEN) { // try to add abbreviated env string // if (env_len + ABBENVLEN + 1 >= CMDLEN) { break; } else { line[ABBENVLEN-4] = '.'; line[ABBENVLEN-3] = '.'; line[ABBENVLEN-2] = '.'; line[ABBENVLEN-1] = '\0'; line[ABBENVLEN] = '\0'; nread = ABBENVLEN; } } env_len += nread; *(line+nread-1) = ' '; // modify NULL byte to space strcpy(pc, line); pc += nread; } } /* line has been (re)allocated within the first call to getdelim */ /* even if the call fails, see getline(3) */ free(line); fclose(fpe); } // add command line and parameters // if ( (fp = fopen("cmdline", "r")) != NULL) { nr = fread(pc, 1, CMDLEN-env_len, fp); fclose(fp); if (nr > 0) /* anything read? */ { for (i=0; i < nr-1; i++, pc++) { switch (*pc) { case '\0': case '\n': case '\t': *pc = ' '; } } } else { // nothing read (usually for kernel processes) // wipe prepended environment vars not to disturb // checks on an empty command line in other places // curtask->gen.cmdline[0] = '\0'; } } } /* ** determine the wait channel of a sleeping thread ** i.e. the name of the kernel function in which the thread ** has been put in sleep state) */ static void procwchan(struct tstat *curtask) { FILE *fp; register int nr = 0; if ( (fp = fopen("wchan", "r")) != NULL) { nr = fread(curtask->cpu.wchan, 1, sizeof(curtask->cpu.wchan)-1, fp); if (nr < 0) nr = 0; fclose(fp); } curtask->cpu.wchan[nr] = 0; } /* ** open file "smaps" and obtain required info ** since Linux-4.14, kernel supports "smaps_rollup" which has better ** performence. check "smaps_rollup" in first call ** if kernel supports "smaps_rollup", use "smaps_rollup" instead */ static void procsmaps(struct tstat *curtask) { FILE *fp; char line[4096]; count_t pssval; static int procsmaps_firstcall = 1; static char *smapsfile = "smaps"; if (procsmaps_firstcall) { regainrootprivs(); if ( (fp = fopen("/proc/1/smaps_rollup", "r")) ) { smapsfile = "smaps_rollup"; fclose(fp); } procsmaps_firstcall = 0; } /* ** open the file (always succeeds, even if no root privs) */ regainrootprivs(); if ( (fp = fopen(smapsfile, "r")) ) { curtask->mem.pmem = 0; while (fgets(line, sizeof line, fp)) { if (memcmp(line, "Pss:", 4) != 0) continue; // PSS line found to be accumulated sscanf(line, "Pss: %llu", &pssval); curtask->mem.pmem += pssval; } /* ** verify if fgets returned NULL due to error i.s.o. EOF */ if (ferror(fp)) curtask->mem.pmem = (unsigned long long)-1LL; fclose(fp); } else { curtask->mem.pmem = (unsigned long long)-1LL; } if (! droprootprivs()) mcleanstop(42, "failed to drop root privs\n"); } /* ** get run_delay from /proc//schedstat ** ref: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/scheduler/sched-stats.rst?h=v5.7-rc6 */ static count_t procschedstat(struct tstat *curtask) { FILE *fp; char line[4096]; count_t runtime, rundelay = 0; unsigned long pcount; static char *schedstatfile = "schedstat"; /* ** open the schedstat file */ if ( (fp = fopen(schedstatfile, "r")) ) { curtask->cpu.rundelay = 0; if (fgets(line, sizeof line, fp)) { sscanf(line, "%llu %llu %lu\n", &runtime, &rundelay, &pcount); curtask->cpu.rundelay = rundelay; } /* ** verify if fgets returned NULL due to error i.s.o. EOF */ if (ferror(fp)) curtask->cpu.rundelay = 0; fclose(fp); } else { curtask->cpu.rundelay = 0; } return curtask->cpu.rundelay; } atop-2.11.1/atopacctd.h0000644000203100020310000000451714771753566014206 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.11.1/atop-rotate.timer0000644000203100020310000000017414771753566015367 0ustar gerlofgerlof[Unit] Description=Daily atop restart Documentation=man:atop(1) [Timer] OnCalendar=daily [Install] WantedBy=timers.target atop-2.11.1/atop.service0000644000203100020310000000140714771753566014413 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.11.1/ifprop.c0000644000203100020310000002333414771753566013534 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.11.1/ChangeLog0000644000203100020310000100423214771753566013640 0ustar gerlofgerlofcommit e530cfd606d3e1a91d306187b28d7a18040e52e6 Author: Gerlof Langeveld Date: Sat Jul 27 10:40:27 2024 +0200 Prepare version 2.11 M version.h commit b6da6ce100afee0ba3bd76a2760f0af0db21c46a Author: Gerlof Langeveld Date: Sat Jul 27 10:31:32 2024 +0200 Explain time stamp in header line (solves issue #313) M man/atop.1 commit 846b3f6b4ee538a1d7c4b061209fa6a504484226 Author: Gerlof Langeveld Date: Sat Jul 13 12:14:48 2024 +0200 Prepare for version 2.11 (also solves issue #308) Ability to convert log files to version 2.11. Also solves issue that network counters were not properly converted from 2.7 to 2.8 (and further). M atopconvert.c M prev/netstats_wrong.h M prev/photosyst_210.h M rawlog.c commit dc0b95d54341425d7a28c11fa1b3ba6a156d4846 Author: Gerlof Langeveld Date: Sat Jul 13 12:12:40 2024 +0200 Prepare for version 2.11 A prev/cgroups_211.h A prev/photoproc_211.h A prev/photosyst_211.h commit 46e44d3dfdee4c6d4fef296984dfde23f2a52bd2 Author: Gerlof Langeveld Date: Sat Jul 13 10:45:05 2024 +0200 Allow /usr to be NFS-mounted (solves issue #307) M atop.init M atopacct.init commit 5b7930ce97692b7b68b5478835f13609313de3fc Author: Gerlof Langeveld Date: Sat Jul 13 09:20:18 2024 +0200 Improve error messages for incompatible logfile M rawlog.c commit de0b0ad27ddb73ce8f830a941c31d1055a728a85 Author: Gerlof Langeveld Date: Sat Jul 13 09:19:18 2024 +0200 Improve help messages (typo and order) M showgeneric.c commit 75a57b1ac9def1ebc55494f9cba3f9015d77a8b1 Author: Gerlof Langeveld Date: Fri Jun 28 14:42:48 2024 +0200 Solved: bar invisible when reaching maximum height The maximum number of lines used per graph (like CPU or disk) in bar graph mode (key 'B') is 25 lines. Whenever this number of lines is reached, the bars that reflect a 100% busy percentage suddenly disappeared due to a wrong comparison ('< MAXHEIGHT' instead of '<= MAXHEIGHT'). Issue solved. M drawbar.c M showgeneric.h commit e80711ed309418bd1a99dcdac184bb3e751853e2 Author: Gerlof Langeveld Date: Tue Jun 18 08:18:07 2024 +0200 Remove limit of 8 CPUs for frequency scaling stats M man/atop.1 commit b08ed21036ba3f06de1f947b42c7bb0da6889240 Author: Gerlof Langeveld Date: Sat Jun 15 21:19:13 2024 +0200 Allow more than 1024 open files (solves #304) When atop runs with superuser privileges, it gathers counters about executed CPU instructions and CPU cycles via perfevents. For this purpose, atop opens two file descriptors per CPU. With the default setting of 1024 open files only about 500 CPUs can be supported. Atop adapts the required number of open files now to the number of CPUs in the system. M photosyst.c commit 58c65a9f28c8302db24bb098c3e19bac734898c4 Author: Gerlof Langeveld Date: Sat Jun 15 11:11:28 2024 +0200 Changed printf specifier to lld for time_t (solves issue #306) M parseable.c commit 37c7505731fef4d3eac63a81f7a759925fa4b508 Merge: 8790b67 1492b59 Author: Gerlof Langeveld Date: Sat Jun 15 10:30:16 2024 +0200 Merge pull request #291 from natoscott/atop-typos Remove a man page merge leftover, minor comments typo fixes commit 8790b67e37dc2ca2f0d23c2f449b414e3a92216b Author: Gerlof Langeveld Date: Sat Jun 15 10:22:10 2024 +0200 Show '-' instead of '?' when frequency is zero M showsys.c commit 06142eed942bba97bd7be510991c0de400bb6025 Author: Gerlof Langeveld Date: Sat Jun 15 09:44:05 2024 +0200 Improved gathering of current CPU frequency M photosyst.c commit 43dc6fdacba3795078e64749a0cd995964c92665 Author: Gerlof Langeveld Date: Sat Jun 15 09:43:06 2024 +0200 Removed free of same buffer twice M rawlog.c commit cd2f08f4378c9010b67e496857c482e4025ae774 Author: Gerlof Langeveld Date: Sat Apr 27 11:52:54 2024 +0200 Normalize include file names of previous versions M atopconvert.c R100 prev/photoproc_20.h prev/photoproc_200.h R100 prev/photoproc_21.h prev/photoproc_201.h R100 prev/photoproc_22.h prev/photoproc_202.h R100 prev/photoproc_23.h prev/photoproc_203.h R100 prev/photoproc_24.h prev/photoproc_204.h R100 prev/photoproc_25.h prev/photoproc_205.h R100 prev/photoproc_26.h prev/photoproc_206.h R100 prev/photoproc_27.h prev/photoproc_207.h R100 prev/photoproc_28.h prev/photoproc_208.h R100 prev/photoproc_29.h prev/photoproc_209.h R100 prev/photosyst_20.h prev/photosyst_200.h R100 prev/photosyst_21.h prev/photosyst_201.h R100 prev/photosyst_22.h prev/photosyst_202.h R100 prev/photosyst_23.h prev/photosyst_203.h R100 prev/photosyst_24.h prev/photosyst_204.h R100 prev/photosyst_25.h prev/photosyst_205.h R100 prev/photosyst_26.h prev/photosyst_206.h R100 prev/photosyst_27.h prev/photosyst_207.h R100 prev/photosyst_28.h prev/photosyst_208.h R100 prev/photosyst_29.h prev/photosyst_209.h commit 0d02dee444c072fd67c6e09367062bd3f550b0b4 Author: Gerlof Langeveld Date: Sat Apr 27 11:46:36 2024 +0200 Adapt atophide to cgroup-enhanced raw logs M atophide.c M rawlog.c M rawlog.h commit 9054c7bcf07c7f87f3b75abdc65af772ce2945c2 Author: Gerlof Langeveld Date: Sat Apr 27 10:32:42 2024 +0200 Adapt atopcat to cgroup-enhanced raw logs M atopcat.c M man/atopcat.1 commit 840b8772995e4ef17cad45ff6dcb3b164339b0fc Author: Gerlof Langeveld Date: Sat Apr 27 10:32:03 2024 +0200 Add list with atop-related programs M README commit e1d759de286e71dba62ba1a33cab401266614fc0 Author: Gerlof Langeveld Date: Tue Apr 16 13:18:50 2024 +0200 Accept twin mode and twin directory in rc file M atop.c M man/atoprc.5 M showgeneric.c M showgeneric.h commit 575d2c2bbd2cb4b6f190a1ac8423ea518ea60b28 Author: Gerlof Langeveld Date: Tue Apr 16 12:38:16 2024 +0200 Fix: Avoid that twin file is opened twice M rawlog.c commit 57857a1941019fc32e868b906a99d8d152d05572 Author: Gerlof Langeveld Date: Tue Apr 16 12:22:29 2024 +0200 Add fields for future expansion M cgroups.h M photoproc.h M photosyst.h commit 6e65c1fc75c1dd8849746aab8370030e567983fd Author: Gerlof Langeveld Date: Tue Apr 16 12:21:38 2024 +0200 Cleanup README text M README commit 8b22070c9778011c4a807ee3a2a78756b9a70ede Merge: 7988ad3 d654fda Author: Gerlof Langeveld Date: Sat Apr 13 09:45:04 2024 +0200 Merge pull request #294 from C0rn3j/master atop[acct|gpu].service: Remove syslog.target commit 7988ad3cf807c523b257f2fdbb17707219e8b36e Author: Gerlof Langeveld Date: Sat Apr 13 09:36:26 2024 +0200 Double backslash in string for JSON output (solves #300) M cgroups.c M cgroups.h M json.c M parseable.c commit 67aaf738da787db4f23a5786af5df154a7a36b03 Author: Gerlof Langeveld Date: Fri Apr 12 19:38:37 2024 +0200 Renamed process name to cmd and removed () M json.c commit 97f4fcec47acae5602c833e667c84b379c55f77d Merge: e7986f3 b07f563 Author: Gerlof Langeveld Date: Fri Apr 12 19:27:52 2024 +0200 Merge pull request #299 from N-Storm/master Add process name (ps->gen.name) to JSON output commit b07f56391ce559d83529cc39556ef70efb1eb411 Author: N-Storm Date: Tue Apr 9 13:04:36 2024 -0400 Add process name (ps->gen.name) to JSON output of PRC, PRM, PRD, PRN, PRE in accordance with parsable output fields M json.c commit e7986f31724399b82e47e0c0282e2f61db431929 Author: Gerlof Langeveld Date: Sat Apr 6 21:32:36 2024 +0200 Adapt documentation for twin mode M man/atop.1 commit 5c133ce885ea623a050b422f5396eb5d0ccde2f0 Author: Gerlof Langeveld Date: Sat Apr 6 21:23:16 2024 +0200 Adapt messages for twin mode M drawbar.c M showgeneric.c commit 6045c6a928b3e5fe997e23946734e262a2c14254 Author: Gerlof Langeveld Date: Sat Apr 6 21:22:53 2024 +0200 Adapt documentation for twin mode M man/atop.1 commit 3e4003259d496cd935463623d335b680a15b1328 Author: Gerlof Langeveld Date: Sat Apr 6 20:25:29 2024 +0200 Context-specific help text M showgeneric.c commit 146a4baf00913bf395e547473594190da9e8de4f Merge: 97cbcbf a3a6a3d Author: Gerlof Langeveld Date: Sat Apr 6 17:31:20 2024 +0200 Merge branch 'twin' commit a3a6a3d25f547e272b305bdec4336cb05af67b54 Author: Gerlof Langeveld Date: Sat Apr 6 17:18:02 2024 +0200 Twin mode: live measurement with review option In twin mode atop spawns into two processes: - lower level process that gathers the counters and writes them to a temporary raw file, and - upper level process that reads the counters from the raw file and presents them to the user. The reading of the upper level process keeps in pace with the written samples of the lower level process for live measurements. However, when pressing the 'r' (reset to measurement begin), the 'b' (branch to time stamp), or the 'T' (previous sample), the upper level process implicitly pauses with the possibility to view previous samples. The 'z' (explicit pause) can also be used to pause the live measurement. When pressing the 'z' (continue after pause) the live measurement will be continued. M atop.c M atop.h M drawbar.c M rawlog.c M showgeneric.c M various.c commit 97cbcbfb0f9eb57779ad4a645a0db859c24ab7c2 Author: Gerlof Langeveld Date: Sat Mar 23 12:41:11 2024 +0100 Apply process selections to threads as well When selections are defined for processes, like particular states, the selection should also be valid for threads in case of thread view (with key 'y'). For state selection the side effect is that a process (i.e. the main thread) might have state 'S' while one of the (other) threads of this process has state 'R'. When selecting state 'R' the process with state 'S' is shown as well because one of its threads has state 'R'. Solves issues #290 and #296. M showgeneric.c commit 17fa0054b50b8bb8af4fe1d6509559ff82998323 Author: Gerlof Langeveld Date: Mon Mar 4 23:50:05 2024 +0100 Support 8 exabytes as maximum size for long long (2^63) M atop.h M various.c commit d654fda875d15603cfacd0e31c3fba1ee775e4f3 Author: Martin Rys Date: Sun Mar 3 12:52:36 2024 +0100 atop[acct|gpu].service: Remove syslog.target M atopacct.service M atopgpu.service commit 35c26b66450b950b71b0021349b4e06551e3295e Author: Gerlof Langeveld Date: Sat Mar 2 13:12:08 2024 +0100 Modified README for cgroup awareness M README.md commit 21de2a33a59b677774d6b8fe58c08e30fbcd586e Author: Gerlof Langeveld Date: Sat Mar 2 13:00:19 2024 +0100 Add cpu, memory and io pressure percentages for cgroups M cgroups.c M cgroups.h M json.c M man/atop.1 M parseable.c M showlinux.c M showlinux.h M showprocs.c commit 09d1aa9b464ed3b0e1f11e3dbd684daa2cf0baeb Author: Gerlof Langeveld Date: Fri Mar 1 13:08:04 2024 +0100 Improved tree representation for cgroups M cgroups.c M cgroups.h M showprocs.c commit eec97d53d3ed1c2cba036b90e4c7bd2409658080 Author: Gerlof Langeveld Date: Mon Feb 26 19:24:46 2024 +0100 Allow cross building of atop (solves issue #293) M Makefile commit 3978cc2865abd84590bf90597f8968877dd22a99 Author: Gerlof Langeveld Date: Fri Feb 23 08:31:13 2024 +0100 Implement cgroups support -- step 4 (issue #292) Add cgroups support to the documentation (man pages). M man/atop.1 commit 2803641658c92ca00972c0e6e3b8254334173918 Author: Gerlof Langeveld Date: Fri Feb 23 08:13:01 2024 +0100 Solved segmentation fault on systems with cgroups v1 M parseable.c commit 24fa6848c6c9eca7cb8dfb8a11464a3d5ddf2183 Author: Gerlof Langeveld Date: Tue Feb 20 23:16:37 2024 +0100 Complete cgroups values on process level (compatibility) M cgroups.c M cgroups.h M json.c M parseable.c M photoproc.c M photoproc.h commit a4a6bff3c4932bd120065e69edcb57058ad52e5a Author: Gerlof Langeveld Date: Mon Feb 19 12:32:10 2024 +0100 Solve segmentation fault for parsable/json output M cgroups.c M cgroups.h M json.c M parseable.c commit d2a10ef03cd2a3a0d11f31df1969022b87f2fd23 Author: Gerlof Langeveld Date: Sat Feb 17 13:33:05 2024 +0100 Implement cgroups support -- step 3 (issue #292) Introduce new label 'CGR' for parsable output and JSON output. When using this label, metrics are printed about each cgroup accompanied by the list of PIDs related to the cgroup. In case of parsable output, the PIDs are passed in an additional output line with marker 'P'. M atopsar.c M cgroups.h M json.c M parseable.c commit 1146a3ebd71721ac910b7a052d704578937545ef Author: Gerlof Langeveld Date: Sat Feb 17 11:38:46 2024 +0100 Implement cgroups support -- step 2 (issue #292) Store cgroups metrics and related PID list in a raw file and retrieve/rebuild the cgroups structures from a raw file. M atop.c M atop.h M atopsar.c M cgroups.c M cgroups.h M json.c M json.h M parseable.c M parseable.h M photoproc.h M rawlog.c M rawlog.h M showgeneric.c M showprocs.c commit 4d617da126001d0e00919cd825533dabb611ffea Author: Gerlof Langeveld Date: Tue Feb 13 11:01:35 2024 +0100 Added disk I/O metrics per cgroup (step 1 issue #292) M atop.c M cgroups.c M cgroups.h M photosyst.c M photosyst.h M showgeneric.c M showlinux.c M showlinux.h M showprocs.c commit 62d982d9abdd187dea4e5cf5c74ed11ad7ac71ad Author: Gerlof Langeveld Date: Sun Feb 11 22:48:27 2024 +0100 Improved memory figures for cgroups (step 1) M cgroups.c M cgroups.h M showprocs.c commit 774c24d4ed01639d4b277f94925c741a117970b8 Author: Gerlof Langeveld Date: Sat Feb 10 12:51:29 2024 +0100 Implement cgroups support -- step 1 (issue #292) Reimplement cgroups support by showing the hierarchical structure of cgroups and the related metrics with key/option 'G'. With the keys/options 2 till 7 the level depth can be chosen (7 is default). Key/option 8 also shows related active processes, except the kernel processes (usually in the root cgroup). Key/option 9 shows all active processes. With key/option 'a' all cgroups and processes are shown instead of only the active cgroups and processes. With key/option 'C' the output is ssorted on CPU consumption (default) while with key/option 'M' the output is sorted on memory consumption. M Makefile M atop.c M atop.h M atopsar.c A cgroups.c A cgroups.h M deviate.c M drawbar.c M json.c M json.h M man/atop.1 M man/atoprc.5 M parseable.c M parseable.h M photoproc.c M photoproc.h M photosyst.c M rawlog.c M showgeneric.c M showgeneric.h M showlinux.c M showlinux.h M showprocs.c commit 1492b594640ad9daa67c6b7c049ede90d074832f Author: Nathan Scott Date: Thu Feb 1 19:28:12 2024 +1100 Remove a man page merge leftover, minor comments typo fixes M man/atop.1 M photoproc.c M utsnames.c commit 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 a1caeadc179ecde015fa60ad43f06e33b0acec3e Author: Gerlof Langeveld Date: Sat May 13 10:49:21 2023 +0200 Prepare version atop 2.9.0 M version.h commit db3454393c07f73462df89d39e722fb1d82e5609 Author: Gerlof Langeveld Date: Sat May 13 10:17:41 2023 +0200 Prepare for atop 2.9 M atopconvert.c A prev/photoproc_29.h A prev/photosyst_29.h commit fab95e9104a08ff23ab73f3490d63bdaa36094e7 Author: Gerlof Langeveld Date: Sat May 13 10:16:56 2023 +0200 Avoid compiler warning by limiting PSI average 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 6a5dc86e2db59c8cfa954a6738c50b6d03b1a1dc Merge: 6423e5d e5f9489 Author: Gerlof Langeveld Date: Sun May 7 16:55:30 2023 +0200 Merge pull request #258 from bytedance/PAG_STEAL add man for PAG steal commit 6423e5d0276de2da3c794839996a5b5489ada60c Merge: 0e0a4f5 1e10331 Author: Gerlof Langeveld Date: Sun May 7 16:49:45 2023 +0200 Merge branch 'bytedance-atopacct_fail' commit 1e10331add3ffb94f43e017835a4943c99865462 Author: Gerlof Langeveld Date: Sun May 7 16:47:47 2023 +0200 Install cleanup function to avoid termination of parent process M atopacctd.c commit e5f9489fba326d3db9ff914bc9972afef0ccca74 Author: liutingjieni Date: Sat May 6 11:52:00 2023 +0800 add man for PAG steal M man/atop.1 commit ef4eaba7aefcf166e869ffd474ef61bb2ef765f6 Merge: 0e0a4f5 22bebc9 Author: Gerlof Langeveld Date: Sat Apr 29 23:04:59 2023 +0200 Merge branch 'atopacct_fail' of github.com:bytedance/atop into bytedance-atopacct_fail commit 0e0a4f5af9bb9c6a66388add69161d5aa9caabeb Author: Gerlof Langeveld Date: Sat Apr 29 10:07:07 2023 +0200 Oomkills event should not remain orange after boot values M drawbar.c commit 9517b682ff6729a4e7d810720ea7ba2a0b20e8a2 Merge: 967a629 a8082ea Author: Gerlof Langeveld Date: Fri Apr 28 15:57:42 2023 +0200 Merge pull request #249 from natoscott/fix-warnings Resolve compiler warnings from latest versions of GCC commit 967a629645eb7170af6b6dac155b8abfc3439271 Merge: 75f8e14 8d18a52 Author: Gerlof Langeveld Date: Fri Apr 28 15:52:02 2023 +0200 Merge pull request #246 from Algebra970/clean-versdate Added versdate.h to make clean target commit 75f8e144fcf2790959811e8a746d2f11f9d50cfb Author: Gerlof Langeveld Date: Sun Apr 23 22:22:38 2023 +0200 Clarified atop man page M man/atop.1 commit 2dd526cb1ac6ba5f0e23cf7bcb27083aa4bc2b6e Author: Gerlof Langeveld Date: Sun Apr 23 12:15:42 2023 +0200 Closing bracket missing in synopsis M man/atop.1 commit dd16cc037c1dc2d24b721d5f3186a2ac5a4870c2 Author: Gerlof Langeveld Date: Sat Apr 22 12:24:25 2023 +0200 Add highlight concerning bar graph mode M README.md commit 2bc0a169330215b3d5bae3dff0b9d38aa1ff8814 Merge: 2eeed8b 57851a8 Author: Gerlof Langeveld Date: Sat Apr 22 12:11:54 2023 +0200 Merge branch 'bargraph' commit 57851a8032cd79460536d2a5314d4384ea2aef81 Author: Gerlof Langeveld Date: Sat Apr 22 11:12:30 2023 +0200 Introduce bar graph mode Besides all detailed information that is supplied by atop on system and process level, a (character-based) bar graph can be shown about the utilization of the most critical system resources, i.e. processors, disks, network interfaces and memory. The original interface of atop is still compatible and is called 'text mode' from now on, while the additional interface is called 'bar graph mode'. Atop will still be started in 'text mode' unless the '-B' flag is used. Within an interactive atop session, the 'B' key can be used to switch between the two modes. The bar graph mode can be used for live monitoring as well as for viewing of raw files. M Makefile M atop.c M atop.h A drawbar.c M man/atop.1 M man/atoprc.5 M parseable.c M showgeneric.c M showgeneric.h M showlinux.c M showprocs.c M showsys.c commit 2eeed8b8ff907ac330459b6ca0a2b3a8b2860ae2 Author: Gerlof Langeveld Date: Mon Apr 3 08:35:36 2023 +0200 Freeing ethlink should depend of ifdef M ifprop.c commit 5f5a41407222a407add06d59da46b5e64a3f763d Merge: 0c7ce67 e34e936 Author: Gerlof Langeveld Date: Sat Apr 1 22:19:08 2023 +0200 Merge branch 'bytedance-recollect-nprocexit-by-reacquire-acctfd' commit e34e9366c6ce4bf0de2b9f875e0e8375618bdd1e Author: Gerlof Langeveld Date: Sat Apr 1 22:18:28 2023 +0200 Added reset to indicate shadow file to be closed M acctproc.c commit 22bebc9fc2a3d83799072d711aa2a977d4f7c481 Author: Ting Liu Date: Thu Mar 23 20:50:43 2023 +0800 fix atopacctd.c: failed to start atopacct.service The type of atopacct.service is "forking". If the parent process does not exit within 90 seconds after starting, the service is considered to have failed to start. After executing fork(), there is a situation where the child process gets scheduled before the parent process. When the child process reaches the kill() function and sends a signal to the parent process, the parent process has not yet been scheduled. After receiving the signal, the parent process executes the signal handling function but has not yet reached the pause() function. As a result, the parent process gets stuck in the pause() function and does not exit, causing the atopacct.service to fail to start. M atopacctd.c commit 90efc4bea781b71cc8f77f8f9484c80fbfd21b49 Author: Fei Li Date: Mon Jan 16 17:40:23 2023 +0800 acctatop: reacquire acctfd to collect nprocexit for some bad cases In current code, the acctfd value in acctproc.c will not be updated once atop runs. This can cause 'nprocexit is zero' problem when atopacct.service restarts but atop.service not, in which case, the previous shadow file that atop read is being '(deleted)'. As acctfd does not change, atop will continue reading that obsolete file whose size will never grow, leading to get a wrong number of exited process, e.i. always be zero from now on until atop.service restarts. Fix this by reacquiring the new shadow file fd after atopacct.service restarts. By the way, only reacquire for atopacct case; as for other cases that directly read from acct() file, restarting service does not make the nprocexit a wrong value. Besides, if the atopacct or (p)acct service is stopped for a while, ignore this situation too, as we are not sure if the action is on purpose or not. Signed-off-by: Fei Li M acctproc.c commit 6f4761381c6f669613a5339542fa2e6cbac41adf Author: Fei Li Date: Fri Jan 13 21:16:58 2023 +0800 Calibrate nprocexit to avoid atop coredumps unexpectedly There's a case that the shadow file becomes larger than its limit, e.i. (maxshadowrec * acctrecsz), and then suddenly atopacct.service restarts suddenly. In current logic supposing atopacct.service does not restart, we will continue to read a new shadowpath whose sequence is one greater than the fulled one. But this does not work if atopacct.service restarts, as the new shadowpath's sequence number will be started with zero, leading to a negative value when comparing with the fulled one. And thus return the unsigned long 'nprocexit' as a very huge positive number. What's more, after acctphotoproc(), we get a wrong acctsize (including reset acctsize to zero), and thus a dummy huge 'curpexit' struct whose partial attributes like gen/cpu/mem/disk are still zeroed. Then later, the same as 'devtstat' struct. Finally, these zeroed attributes will cause a glibc sort() coredump like "*a = 0" during compcpu(), compmem() and so on. To fix this case, - firstly, return numrecs as zero once detecting atopacct.service restarts or other bad cases, regarding as an incomplete sample; - secondly, to avoid other bad cases, calibrate nrexit during acctphotoproc(), re-calculate the real 'nprocexit' to avoid the "*a = 0" error. Signed-off-by: Fei Li Co-authored-by: Ting Liu M acctproc.c commit 0c7ce67ab72041f1829ef71cfa00f4b7bfa630d6 Merge: 7a917f3 8390f4d Author: Gerlof Langeveld Date: Sat Mar 25 12:23:06 2023 +0100 Merge pull request #248 from bytedance/fix-json-lvm json.c: fix avque counters output commit 7a917f3e20980d1488ddb8a111a5f1f460561843 Merge: 6b9691a 6809e6f Author: Gerlof Langeveld Date: Sat Mar 25 12:22:03 2023 +0100 Merge pull request #245 from xixiliguo/fix-paging-stat fix calculation for scan and steal commit 6b9691a4248e933ef608bad59ca89302a1fc154d Merge: eb158ef a273887 Author: Gerlof Langeveld Date: Sat Mar 25 12:09:08 2023 +0100 Merge pull request #239 from pizhenwei/fix-memory-leak ifprop.c: Fix possible memory leak 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 8390f4d028c328ee4d9ca604ec2f14930ddb1ac7 Author: Fei Li Date: Mon Mar 6 11:37:24 2023 +0800 json.c: fix avque counters output Fixes: 6cc222a8c528 ("Add avque counters to json data") Signed-off-by: Fei Li M json.c commit a8082ea0c401a8a5dc70b62be0027f3e7324b5d5 Author: Nathan Scott Date: Mon Mar 6 14:42:56 2023 +1100 Resolve compiler warnings from latest versions of GCC Fix an incorrect struct array null test on bdev names, i.e. "warning: the comparison will always evaluate as ‘true’ for the address of ‘name’ will never be NULL [-Waddress]" in disk, LVM and MD block device deviate.c calculations. M deviate.c commit eb158ef2b2a48607788c5cd0a8213100d6df12f8 Merge: 5073b66 4f04c2c Author: Gerlof Langeveld Date: Fri Mar 3 16:48:57 2023 +0100 Merge pull request #243 from Algebra970/master only call str.decode if nvml returned bytes commit 8d18a52efd8e77ac48cc5758f8ff5bf686f0142b Author: elad Date: Wed Mar 1 18:17:26 2023 +0200 Added versdate.h to make clean target M Makefile commit 4f04c2ca5695e025413357c608abf9e2d0397a0f Author: elad Date: Wed Mar 1 18:14:10 2023 +0200 Revert "Added versdate.h to make clean target" This reverts commit 2973c8ee71d1cc23853fc79c503571b257ae97a8. M Makefile commit 2973c8ee71d1cc23853fc79c503571b257ae97a8 Author: elad Date: Wed Mar 1 18:07:52 2023 +0200 Added versdate.h to make clean target M Makefile commit 6809e6f22c29435a244fe797e28328f37728b101 Author: peter wang Date: Wed Mar 1 21:33:46 2023 +0800 fix calculation for scan and steal M photosyst.c commit 69bf04e1a1f5e8ba7c99da1dc56a3e022ccf83c6 Author: elad Date: Sun Feb 26 15:39:00 2023 +0200 only call str.decode if nvml returned bytes M atopgpud commit 5073b66c6945d7b22acdbd9c5ed0f353cf8e0eb9 Author: Gerlof Langeveld Date: Sat Feb 25 14:49:42 2023 +0100 Add exit epoch to parseable output PRG (solves issue #242) M man/atop.1 M parseable.c commit 9e3b62584ae5cc710f222aaa1f52aa3e25cff49c Author: Gerlof Langeveld Date: Sat Feb 25 14:23:20 2023 +0100 Minor correction in man page for NVCSW/NIVCSW M man/atop.1 commit 788cb32fcd7693f5773f5f4b361908146501a943 Author: Gerlof Langeveld Date: Sat Feb 25 14:13:42 2023 +0100 Context switches (voluntary and involuntary) on process level incorrect. The context switches on process level only concern the values of the main thread instead of all threads. Corrected by accumulating values of all threads. M photoproc.c commit 999103c4983260cfdcbaad30f78030a88358ed16 Merge: 71a8690 b870681 Author: Gerlof Langeveld Date: Sat Feb 25 14:01:33 2023 +0100 Merge branch 'bytedance-photoproc-ctxSwitch' commit b870681434b8df10667f837a8d5cce5bb9d555ec Author: Gerlof Langeveld Date: Sat Feb 25 13:56:58 2023 +0100 Various modifications releated to (non)voluntary context switches. - new counters should be added at the end of line for parseable output - document parseable counters (man page) - field descriptions in alphabetical order (man page) - use val2valstr() for counters with a fixed column width M man/atop.1 M parseable.c M showlinux.c M showprocs.c commit d3eea820addf0e943bb08b2934f4cb0a7a900dad Merge: 71a8690 e22b934 Author: Gerlof Langeveld Date: Sat Feb 25 13:23:06 2023 +0100 Merge branch 'photoproc-ctxSwitch' of github.com:bytedance/atop into bytedance-photoproc-ctxSwitch commit 71a8690bccbce5b8b7794336b74eb12261214ce9 Author: Gerlof Langeveld Date: Sat Feb 25 13:12:27 2023 +0100 Merging macro protection in header files (pull request #215) M acctproc.h M atop.h M atopacctd.h M ifprop.h M mkdate M netatop.h M netatopd.h M netstats.h M parseable.h M photoproc.h M rawlog.h M showgeneric.h M showlinux.h M version.h commit e22b934636935e973136b6d1bc3285e83ddb2451 Author: Fei Li Date: Wed Nov 23 14:51:24 2022 +0800 Add nvcsw and nivcsw for each process As we all know, too much context switches can lead to poor performance. Besides, sometimes one same process deployed in two places may behave different. Thus it's necessary to collect the - nvcsw (number of times that the program was context-switched voluntarily, for instance while waiting for an I/O operation to complete) and - nivcsw (number of times the process was context-switched involuntarily, e.g. the time slice expired) two indicators to monitor the performance. Signed-off-by: Fei Li M deviate.c M json.c M man/atop.1 M parseable.c M photoproc.c M photoproc.h M showgeneric.c M showlinux.c M showlinux.h M showprocs.c commit a273887b2defb457e0e5b1c58c5c906683b58921 Author: zhenwei pi Date: Wed Feb 22 11:19:56 2023 +0800 ifprop.c: Fix possible memory leak Once 'ioctl(sockfd, SIOCETHTOOL, &ifreq)' fails, the dynamically allocated memory buffer(ethlink points to) gets orphan. Signed-off-by: zhenwei pi M ifprop.c 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 416e955f7c8e6872dc27512b3084b1528e62163c Merge: 3cd21d0 99a2581 Author: Gerlof Langeveld Date: Sat Feb 18 16:56:26 2023 +0100 Merge branch 'master' of github.com:Atoptool/atop commit 3cd21d05c690f9c0fd871bf3651c7936ff887d85 Author: Gerlof Langeveld Date: Sat Feb 18 16:55:51 2023 +0100 Code cleanup and prototype additions M Makefile M acctproc.c M atop.c M atop.h M atopacctd.c M atopconvert.c M atopsar.c M json.c M netlink.c M parseable.c M photosyst.c M photosyst.h M procdbase.c M showgeneric.c M showgeneric.h M showlinux.c M showlinux.h M showprocs.c M showsys.c M various.c M version.h commit 99a25812e50cbecdcad71fccec6d4a625b04436e Merge: ca9d6d9 6180aba Author: Gerlof Langeveld Date: Sat Feb 18 15:04:30 2023 +0100 Merge pull request #231 from bytedance/fix-atop-rotate atop-rotate.service: use restart instead of try-restart commit ca9d6d9f902ae73c1b9b78ff937900648ebfead1 Author: Gerlof Langeveld Date: Sat Feb 4 12:34:20 2023 +0100 Consistency check on number of threads (solves issue #232) M photoproc.c commit 6180aba57e07544c85dc9f8ba5d052e8bc719851 Author: Fei Li Date: Fri Jul 15 20:01:08 2022 +0800 atop-rotate.service: use restart instead of try-restart `man systemctl` says the 'try-restart' pattern: 'Restart one or more units specified on the command line if the units are running. This does nothing if units are not running.' That means atop can not be started successfully by daily atop-rotate.timer if it is already exited. Fix this by changing try-restart to restart. Signed-off-by: Fei Li M atop-rotate.service commit 72f361df6ae11d63443cf562fee1bdeb0f6218bd Author: Gerlof Langeveld Date: Sat Jan 7 19:42:31 2023 +0100 Add link to atophttpd M README.md commit 5bc3edc64ca01dd46922b34402c67c6f286399aa Author: Gerlof Langeveld Date: Sat Jan 7 14:27:52 2023 +0100 Version 2.8.1 M version.h commit a2c45992f59ce6110b24b22fc9bf65028197e96d Author: Gerlof Langeveld Date: Sat Jan 7 12:21:03 2023 +0100 Solve compiler warnings about format-truncation M various.c commit b93546d1ed03ec5b6e0c747a2571820129d60771 Author: Gerlof Langeveld Date: Sat Jan 7 12:02:37 2023 +0100 Avoid buffer overflows during value formatting M various.c commit 490c73ce516bff6dd53786e2be29bbcbc4185071 Author: Gerlof Langeveld Date: Sat Jan 7 12:00:36 2023 +0100 Correct NUMA conversion in logfiles from version 2.7 to 2.8 M atopconvert.c commit ed17722fa50f3a0be4d1d4a86336180a38a69109 Author: Gerlof Langeveld Date: Sat Jan 7 11:16:42 2023 +0100 Added contributor M AUTHORS commit 338637d911b98d67b3d3b8a25e25532659a36571 Author: Gerlof Langeveld Date: Thu Dec 29 23:32:50 2022 +0100 Rename AUTHOR in spec file M rpmspec/atop.specsystemd commit 6621b213326fdeba79e8c15214f7e78597ce391f Author: Gerlof Langeveld Date: Thu Dec 29 23:24:23 2022 +0100 Up-to-date list of contributors D AUTHOR A AUTHORS commit be2d5c43e4fd8b6f2754e2cabdd55857d5ad8da2 Author: Gerlof Langeveld Date: Thu Dec 29 22:33:12 2022 +0100 New version 2.8.0 M version.h commit d7f71c0bfc3980a0103ed23f55a8db4c72b8d6f2 Author: Gerlof Langeveld Date: Thu Dec 29 21:56:05 2022 +0100 Update timestamp of man pages M man/atop.1 M man/atopacctd.8 M man/atopcat.1 M man/atopconvert.1 M man/atopgpud.8 M man/atoprc.5 M man/atopsar.1 commit 3bfd0dbbeaf023c351235a44b92ad8b3691c4648 Author: Gerlof Langeveld Date: Thu Dec 29 15:39:34 2022 +0100 Adapt atopconvert for conversion to version 2.8 M README M atopconvert.c A prev/netstats_wrong.h A prev/photoproc_28.h M prev/photosyst_20.h M prev/photosyst_21.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 A prev/photosyst_28.h commit 33c0abcd3423e276e538d26f4850c266abdcdbf5 Merge: 3b7ad4b 6cc222a Author: Gerlof Langeveld Date: Thu Dec 29 12:11:20 2022 +0100 Merge pull request #229 from pizhenwei/json-fix Json fix commit 3b7ad4b61f94776ddc0f9755ef2f0353c3eac962 Author: Gerlof Langeveld Date: Thu Dec 29 11:59:11 2022 +0100 Atopacct.service verifies if psacct/acct is active (solves issue #227) To avoid that the atopacctd daemon and the (ps)acct service both try to activate process accounting continuously, the atopacct.service refuses to start when the (ps)acct service is already active. M atopacct.service commit 6cc222a8c528112f39f8657b84367a0a3b7c961f Author: zhenwei pi Date: Tue Dec 27 10:15:51 2022 +0800 Add avque counters to json data Signed-off-by: zhenwei pi M json.c commit be1fc512f00b36d6b80d9fe6aa73640787bf3417 Author: zhenwei pi Date: Tue Dec 27 09:36:44 2022 +0800 Add inflight counters to json data Signed-off-by: zhenwei pi M json.c commit cf4d2d268f9fefadc5b56e717eb57f7260223834 Author: zhenwei pi Date: Fri Dec 2 16:05:46 2022 +0800 Fix LLC output for Json Signed-off-by: zhenwei pi M json.c commit 66641630457e3b607391dd08bfe79b0a3ccbf416 Author: Gerlof Langeveld Date: Sat Dec 24 13:24:24 2022 +0100 Solve format-overflow/maybe-uninitialized warnings M atopsar.c M gpucom.c M photosyst.c M showprocs.c M various.c commit b6041164d738ba51eeacc9ef2676745564a33ee7 Author: Gerlof Langeveld Date: Sat Dec 24 13:22:22 2022 +0100 Reshuffle counters to ease raw file conversion M photosyst.h commit e395f48d4709bbcb03918cc752d9f8a2387e5c53 Author: Gerlof Langeveld Date: Sat Dec 24 12:13:12 2022 +0100 Recalculate average queue in parseable output M man/atop.1 M parseable.c commit 8d133c6f51f7d1424f3c4590dbb8621e971688ec Author: Gerlof Langeveld Date: Sat Dec 24 11:58:33 2022 +0100 Corrected the order of inflight/avque M man/atop.1 M parseable.c commit 3a210c1b77f5069a8be364a8107aaa686bccdcd3 Merge: e52eaaa ebfda60 Author: Gerlof Langeveld Date: Sat Dec 24 11:54:06 2022 +0100 Merge branch 'bytedance-add-inflight' commit ebfda604a89c0906dc038636887ec0e11d0590d3 Author: Gerlof Langeveld Date: Sat Dec 24 11:53:36 2022 +0100 Add inflight and avque counters to parseable data M man/atop.1 M parseable.c M showlinux.c commit a57b7ef96fba4fbb98ba92048c2ced2e90d92298 Merge: e52eaaa a232722 Author: Gerlof Langeveld Date: Sat Dec 24 11:38:27 2022 +0100 Merge branch 'add-inflight' of github.com:bytedance/atop into bytedance-add-inflight commit e52eaaa65f87ad0315cbd33362d046e2d786f5bc Merge: a082801 c6a0661 Author: Gerlof Langeveld Date: Sat Dec 24 11:17:39 2022 +0100 Merge pull request #225 from natoscott/bdelay-space-fix Fix BDELAY in priphead call to make_proc_prints for schedprocs commit a0828016b1c5e87b73b58ff427b371dc0cb93195 Merge: 7e83f05 e892d9a Author: Gerlof Langeveld Date: Sat Dec 24 11:16:40 2022 +0100 Merge pull request #224 from natoscott/deviate-container-preindex Correct the array index used for previous samples per-container CPU values commit 7e83f05e98d33f52f9c892a886584a0bdf6dec26 Merge: e40449b cff3075 Author: Gerlof Langeveld Date: Sat Dec 24 11:10:56 2022 +0100 Merge pull request #223 from natoscott/dskavio-print-fix Reinstate dropped printing of avio msec values between 99.95 and 9995 commit e40449b7b2623da00f43a2141be536c38d196d5e Merge: 21bd4fd a26b937 Author: Gerlof Langeveld Date: Sat Dec 24 10:46:42 2022 +0100 Merge pull request #222 from bytedance/display-rbd-disk photosyst: add ceph rbd disk display commit 21bd4fd643605e1b42e3e61f4ae82ce4bfadba5b Merge: d8d97f9 2b07b01 Author: Gerlof Langeveld Date: Sat Dec 24 10:43:14 2022 +0100 Merge pull request #217 from bytedance/fix-numanr photosys.c: correct the numa number for NUM and NUC commit c6a066167ee33a9cbef353439594f7dfa61cb940 Author: Nathan Scott Date: Thu Dec 15 15:16:08 2022 +1100 Fix BDELAY in priphead call to make_proc_prints for schedprocs M showlinux.c commit e892d9afae4883f3488542a664de83012e42d9c1 Author: Nathan Scott Date: Thu Dec 15 13:49:00 2022 +1100 Correct the array index used for previous samples per-container CPU values Compare to previous sample array handling for NFS, disks, interfaces, etc. M deviate.c commit cff3075a5a05f98629c3f22c6c5d8466ca46c573 Author: Nathan Scott Date: Thu Dec 15 11:27:04 2022 +1100 Reinstate dropped printing of avio msec values between 99.95 and 9995 I believe this was an accidental change in commit db9eef1d4 which has introduced a code path where no value is added to the buffer at all. M showsys.c commit a26b937efb4cac44c5a8adcd0a7981501b48b83e Author: Fei Li Date: Tue Dec 13 16:15:08 2022 +0800 photosyst: add ceph rbd disk display Ceph block devices are used in many scenarios, especially in k8s cluster, in which case an external ceph storage cluster is used to implement persistent storage for pods inside the k8s cluster. An example in /proc/diskstats is as follows: 254 288 rbd18 25359 1 2459552 18239 2 0 16 42 0 19706 11358 0 0 0 0 254 289 rbd18p1 25240 1 2453022 18190 2 0 16 42 0 19655 11337 0 0 0 0 Signed-off-by: Fei Li M photosyst.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 2b07b014153b6ccb00a4e02b134fa6cc9cec6fc2 Author: Fei Li Date: Tue Dec 6 20:48:13 2022 +0800 photosys.c: correct the numa number for NUM and NUC Current code uses array index to store the numa number, which may sort the numanr to a wrong value after the qsort() funtion in generic_samp(). Fix this by re-adding the 'numanr' member for struct mempernuma and cpupernuma to store the correct numa number. The permanent 'numanr' member can guarantee the numa number from adjusting after qsort(), while the qsort() function can continue working by sorting NUM from small to large according to free memory, and sorting NUC from small to large according to idle cpu. Besides, add NUC for JSON output. Fixes: 1fbda3f1081a ("Additions to NUMA counters") Reported-by: Qi Zheng Signed-off-by: Fei Li M deviate.c M json.c M parseable.c M photosyst.c M photosyst.h M showsys.c commit d8d97f93f2425be97e4aca6df5c91fd18913ac16 Author: Gerlof Langeveld Date: Sat Nov 26 12:56:14 2022 +0100 Timeout when setting semaphore in atop (issue #207) M acctproc.c commit e1a50559934bb9a1ca2b41eba77134abeaae3d22 Author: Gerlof Langeveld Date: Sat Nov 26 11:38:17 2022 +0100 Current CPU set to -1 for terminated process (solves issue #201) When a process has terminated (status 'E') the current CPU will be set to -1 (means: unknown) in the parsable output with label PRC. M deviate.c M man/atop.1 commit ecfc92e04f33ebd09ca0c06debc16585ec993002 Merge: 75a67ee 580099c Author: Gerlof Langeveld Date: Sat Nov 26 10:41:39 2022 +0100 Merge branch 'pizhenwei-json' commit 580099c03e344f401755ad5f8297842b7e950083 Author: Gerlof Langeveld Date: Sat Nov 26 10:41:00 2022 +0100 Typo corrections and correction in man page M json.c M man/atop.1 commit fb9edb2cabda7368b9115691ee297b71a486904c Author: zhenwei pi Date: Mon Nov 14 10:09:39 2022 +0800 Introduce JSON output by '-J xxx' It's possible to dump the record by JSON format, this is quite friendly to web service, and lot of use libraries could parse these data easily. The syntax is similar to parseable output. Co-authored-by: Fei Li Signed-off-by: Fei Li Signed-off-by: zhenwei pi M Makefile M atop.c A json.c A json.h M man/atop.1 commit 75a67eee67962ab2b2a787375de6907a008164f9 Author: Gerlof Langeveld Date: Tue Nov 1 20:17:39 2022 +0100 Add missing counter iInCsumErrors to ICMP counters M netstats.h commit a232722a59c4eeeeadf4e8f5c269d59eff6b8f70 Author: Teng Hu Date: Mon Aug 15 14:16:25 2022 +0800 supplement inflight I/O requests Inflight I/O requests count I/O requests issued to the device driver but have not yet completed, which are useful to analyze system I/O status. More than this, since plenty of processes remain in D status for a long time come from I/O troubles, inflight I/O values also help to examine these processes. Signed-off-by: Teng Hu M deviate.c M man/atop.1 M photosyst.c M photosyst.h M showlinux.c M showlinux.h M showsys.c commit be42f0b0eed45af5045c2f1d85b4bd87e22ca32f Author: Gerlof Langeveld Date: Tue Aug 30 16:55:57 2022 +0200 Man page: clarify elapsed time in PRG parseable output M man/atop.1 commit e9f3048f7e04c39d43cd19481e14797d6163556d Author: Gerlof Langeveld Date: Tue Aug 30 16:46:23 2022 +0200 Exponent calulation for high counter might exceed column width When a column width was defined of e.g. 4 positions, the value 99999 was shown as 100e3 instead of 99e3 (upward rounding caused an extra position). M various.c commit 38ae774ef76bb5472215b4e0dc7e339b2fdddff2 Author: Gerlof Langeveld Date: Tue Aug 30 16:24:22 2022 +0200 PID selection can not be undone for same interval When pressing 'I' to select particular PIDs and pressing 'I' again within the same interval to deselect by pressing the Enter-key, the deselection is ignored. Solved by reinitializing the input buffer. M showgeneric.c commit ea723065de70854c127daa9545d25df1bab4fd3d Merge: ba677a7 02479a2 Author: Gerlof Langeveld Date: Sat Aug 20 13:36:35 2022 +0200 Merge branch 'secondmem' commit 02479a2ededa76c843d59828d4e8b8886d0723a9 Author: Gerlof Langeveld Date: Sat Aug 20 13:32:02 2022 +0200 Split the MEM line into two lines and add pagetable size Since it was hardly possible to view the values in the MEM line (too many), the memory utilization has been spread over two lines. The amount of memory used for page tables has been added. 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 ba677a79c2cc987db03ea4226fd95a28351b429a Merge: 76a6774 0933e00 Author: Gerlof Langeveld Date: Sat Aug 13 13:05:26 2022 +0200 Merge branch 'bytedance-support-sockstat-mem-consumption' commit 0933e00880ebfb425ac98b106c24c825a10fbdad Author: Gerlof Langeveld Date: Sat Aug 13 13:03:04 2022 +0200 Memory usage for TCP/UDP sockets M man/atop.1 M parseable.c M photosyst.c M showlinux.c M showsys.c commit ffdbb1994b9b4ca2de3e6ca6da102a3f9162787e Merge: 76a6774 a2422e3 Author: Gerlof Langeveld Date: Sat Aug 13 11:56:44 2022 +0200 Merge branch 'support-sockstat-mem-consumption' of github.com:bytedance/atop into bytedance-support-sockstat-mem-consumption commit 76a67740379ffd13644d21ebe9e19f2e87e03273 Author: Gerlof Langeveld Date: Sat Aug 13 11:49:02 2022 +0200 Add Documentation key to atop-rotate.service/.timer (issue #206) M atop-rotate.service M atop-rotate.timer M man/atop.1 commit d24484bbc67ceaeb302cbd6294da398aea10c7b7 Author: Gerlof Langeveld Date: Sat Aug 13 11:30:54 2022 +0200 Correct typos in man pages (solves issue #205) M man/atop.1 M man/atopsar.1 commit 69599a02aa21b77aaf56fc2303b86296c557c1e0 Merge: 57a808e c9fea4e Author: Gerlof Langeveld Date: Sat Aug 13 11:13:56 2022 +0200 Merge pull request #198 from bytedance/showhdrline-fix-sprintf-oob showhdrline: fix OOB access when COLS is too small commit 57a808e3cdf0f2d4b516a211e8e915b0d972b33f Merge: 8365a75 5c326ee Author: Gerlof Langeveld Date: Sat Aug 13 10:28:46 2022 +0200 Merge pull request #203 from pizhenwei/fix-compile-error-with-http Fix compiling error with -DHTTPSTATS commit 8365a7583e08a27ed14aedd2d0909dbde0513f18 Merge: d9e0e6a 4243e34 Author: Gerlof Langeveld Date: Thu Jul 28 11:58:19 2022 +0200 Merge branch 'cgroup' commit 4243e34f6bb7444f630350843b66c8f490ecc283 Author: Gerlof Langeveld Date: Thu Jul 28 11:52:34 2022 +0200 Add cgroup support (solves issue #127) The current cgroup is registered for systems that are based on cgroup v2. Apart from the group name also relevant controller information is maintained, like the cpu.max and the memory.max (for the group itself and the most restrictive value in the parent groups). M atop.h M deviate.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 showlinux.h M showprocs.c commit 5c326ee948775afc541ecd88017c5e9d2b4099bf Author: zhenwei pi Date: Thu Jul 21 15:52:17 2022 +0800 Fix compiling error with -DHTTPSTATS Fix compiling error like this: In file included from atopconvert.c:77: prev/photosyst_27.h:327:5: error: conflicting types for ‘getwwwstat’ 327 | int getwwwstat(unsigned short, struct wwwstat_27 *); | ^~~~~~~~~~ In file included from atopconvert.c:52: photosyst.h:361:5: note: previous declaration of ‘getwwwstat’ was here 361 | int getwwwstat(unsigned short, struct wwwstat *); | ^~~~~~~~~~ Signed-off-by: zhenwei pi M prev/photosyst_27.h commit a2422e397af5ebbea1a03a36ec82e8bd77632b4a Author: Fei Li Date: Fri Jul 8 18:35:52 2022 +0800 supplement tcpsock and udpsock memory consumption In some production cases, tcp socket consumes lots of memory, which has no intersection with the statistics in /proc/meminfo file. Considering the current code does not collect and record the tcpsock or udpsock statistics reported via /proc/net/sockstat, supplement the following two to help locate memory consumption issues: - tcpsock, number of pages allocated by TCP sockets - udpsock, number of pages allocated by UDP sockets Signed-off-by: Fei Li 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 d9e0e6a703f152e3e17195afb227596d9983ca4e Merge: f2e7051 bc84472 Author: Gerlof Langeveld Date: Sat Jul 9 12:25:29 2022 +0200 Merge pull request #196 from pizhenwei/fix-man-disk-busy Fix disk busy in man page commit f2e7051a50ed116a1cd35d0e3e2b268cb3472d9e Author: Gerlof Langeveld Date: Sat Jul 9 12:10:39 2022 +0200 Store PID width in header of raw log Register the number of positions for PID and TID numbers on the measured system in the raw log. In this way, the viewing of the raw log on another system will use the proper column width for PID/TID. M atop.c M atop.h M rawlog.c M rawlog.h M showlinux.c M various.c commit c9fea4e3d55b589ec202fb651f461c7acffb8fc0 Author: Fei Li Date: Fri Jun 10 15:06:21 2022 +0800 showhdrline: fix OOB access when COLS is too small - How to reproduce: Drag the window to the far right, a "Segmentation fault (core dumped)" message occurs. - Debug: Add some print values, saying COLS=2, thus maxw. After getspacings(), elemptr becomes a NULL array again: maxw is too small, so after nitems times of memmove(), newelems is NULL. Then back to showhdrline(), as each curelem.f is zero, no chead is assigned to buf and col is still zero. However, after the calculation: allign=COLS-col-pagindiclen;//align=2-0-4=-2 < 0, previous code jumps to sprintf(buf+col+allign, "%s", pagindic), which triggers out-of-bounds access as col+allign = -2 < 0. - Fix: Fix this by judging if (col+allign >= 0) before jumping to avoid the OOB access. Reported-by: Qi Zheng Signed-off-by: Fei Li M showprocs.c commit bc84472f20c9d8c101901b5b32f04168a596af27 Author: zhenwei pi Date: Mon May 16 08:59:37 2022 +0800 Fix disk busy in man page Reference source code from Linux: https://github.com/torvalds/linux/blob/master/block/blk-core.c void update_io_ticks(struct block_device *part, unsigned long now, bool end) { unsigned long stamp; again: stamp = READ_ONCE(part->bd_stamp); if (unlikely(time_after(now, stamp))) { if (likely(cmpxchg(&part->bd_stamp, stamp, now) == stamp)) __part_stat_add(part, io_ticks, end ? now - stamp : 1); } if (part->bd_partno) { part = bdev_whole(part); goto again; } } The io_ticks field records the elapsed time during which I/O requests were issued to the device. From this design, one or more I/O requests may occurs in the same tick, so this number does not reflect the performance limits of mordern SSD/NVMe. atop reads this counter and calculates the percentage, fix the man page to introduce this. Signed-off-by: zhenwei pi M man/atop.1 commit b9d7533ef536cd07683680786c1bcf9c4e6bd9d4 Merge: f5da8d3 ed4fd0f Author: Gerlof Langeveld Date: Sat May 14 11:04:33 2022 +0200 Merge pull request #195 from pizhenwei/move-run_in_guest-into-various Move run_in_guest into various.c commit f5da8d31e7346a7873fe0748cdf136942c64bce3 Merge: 98a82fd 591bf96 Author: Gerlof Langeveld Date: Sat May 7 12:47:16 2022 +0200 Merge branch 'blockdelay' commit 591bf964db4da7d66ffd4a2920fcb1dcf5d06436 Author: Gerlof Langeveld Date: Sat May 7 12:45:58 2022 +0200 Add block I/O delay to processes/threads M deviate.c M man/atop.1 M parseable.c M photoproc.c M photoproc.h M showlinux.c M showlinux.h M showprocs.c commit ed4fd0feab628fef20e6c08f3722fdb0dfbe3c4f Author: zhenwei pi Date: Mon Apr 18 18:36:42 2022 +0800 Move run_in_guest into various.c run_in_guest tries to detect hypervisor, it works as a helper function. So move this into various.c. Signed-off-by: zhenwei pi M atop.h M photosyst.c M various.c commit 98a82fdc2258a3bb67ff6dc1dd189b81683837a1 Merge: b90e41f 9cfeb90 Author: Gerlof Langeveld Date: Sat Apr 9 12:28:32 2022 +0200 Merge branch 'pizhenwei-pgintout' commit 9cfeb9096a45c21fd5e5e5791ff2a21e2fbe86d7 Author: Gerlof Langeveld Date: Sat Apr 9 12:28:02 2022 +0200 Minor changes to text, column priority and coloring M man/atop.1 M showlinux.c M showsys.c commit b0d07a04298d5dec969b174e3396f1a93cb6fc86 Author: zhenwei pi Date: Sun Mar 20 20:11:22 2022 +0800 Support pgin & pgout Collect pgint & pgout from /proc/vmstat. This counter is provided by linux block layer, it represents the total number of memory read from/ written to all the block devices. Signed-off-by: zhenwei pi 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 b90e41f541f122b6521f476200e6b97657add417 Merge: bfd2537 52676b9 Author: Gerlof Langeveld Date: Sat Mar 12 11:37:47 2022 +0100 Merge pull request #192 from pizhenwei/fix-compiling-errors Fix compiling warnings commit bfd2537d8282f79685a742c4b40779c2839d6974 Merge: a038796 31b632b Author: Gerlof Langeveld Date: Sat Mar 12 11:30:38 2022 +0100 Merge pull request #193 from 7h3w1zz/man-typo Fix typo in man page commit a038796ec877e098c1ba265f3782386257bf6421 Merge: 5df2c9a 3bcfce3 Author: Gerlof Langeveld Date: Sat Mar 12 11:24:59 2022 +0100 Merge branch 'bytedance-calibrate-process-nthr' commit 3bcfce3e9dd747d628645b04dc39f9d1fac9b708 Author: Gerlof Langeveld Date: Sat Mar 12 11:24:34 2022 +0100 Correction on calibration (one too many) M photoproc.c commit 1876fd3474563b71a62b4f0e672f4b3ebd0d9def Merge: 5df2c9a bdadf39 Author: Gerlof Langeveld Date: Sat Mar 12 10:59:58 2022 +0100 Merge branch 'calibrate-process-nthr' of https://github.com/bytedance/atop into bytedance-calibrate-process-nthr commit 5df2c9ae74a57312f2d0da245a58d1a0c84704d5 Merge: 3dfaa7f ced67a5 Author: Gerlof Langeveld Date: Sat Mar 12 10:44:58 2022 +0100 Merge branch 'integerbeans-fix-vmwballoon-photosyst' Accepting pull request #189 with modification to stay compatible. commit ced67a5ec6f4a258f59e247a87425179b3ff015d Author: Gerlof Langeveld Date: Sat Mar 12 10:44:32 2022 +0100 Detect vmmemctl at two locations M photosyst.c commit 52676b93113120a8bd38425b439ccc517bcd406e Author: zhenwei pi Date: Mon Mar 7 15:15:13 2022 +0800 Use dup2 instead of close+dup Build atop on Ubuntu 20.04, gcc 9.3: showgeneric.c:2843:11: warning: ignoring return value of ‘dup’, declared with attribute warn_unused_result [-Wunused-result] 2843 | (void) dup(1); Use dup2 instead of close+dup to avoid compiling warning, also use fileno to avoid hard code: fd 0 -> fileno(stdin) fd 1 -> fileno(stdout) Signed-off-by: zhenwei pi M atopcat.c M atopsar.c M showgeneric.c commit afa269ba299da4b04fe651a9159d40d7f4bd2f02 Author: zhenwei pi Date: Mon Mar 7 14:41:01 2022 +0800 Fix compiling warning Build atop on Ubuntu 20.04, gcc 9.3: photosyst.c:79:17: warning: ‘%s’ directive output may be truncated writing up to 255 bytes into a region of size 231 [-Wformat-truncation=] 79 | #define NUMADIR "/sys/devices/system/node" | ^~~~~~~~~~~~~~~~~~~~~~~~~~ photosyst.c:867:28: note: in expansion of macro ‘NUMADIR’ 867 | snprintf(fn, sizeof fn, NUMADIR "/%s/meminfo", dentry->d_name); | ^~~~~~~ photosyst.c:993:38: note: format string is defined here 993 | snprintf(fn, sizeof fn, NUMADIR "/node%d/cpumap", j); Use larger buffer to avoid compiling warning. Signed-off-by: zhenwei pi M photosyst.c commit bdadf39b7a1062c69f0221250f10d5810402fbd6 Author: Fei Li Date: Tue Feb 15 15:00:22 2022 +0800 Calibrate the value of nthr for each process In current code, we firstly get one process's thread number by reading "Threads:" from /proc/pid/status; then secondly load each thread's info into devtstat->taskall by traversing each tid under /proc/pid/task/. Here, information inconsistency occurs if some processes frequently creates or deletes threads. For example: $cat /proc/6/status |grep Threads; sleep 0.1; ls /proc/6/task/ | wc -l Threads: 70 71 To fix this, calibrate the value of nthr after iterating each process's threads: re-record the number of threads and assign to each process's nthr again. Signed-off-by: Fei Li M photoproc.c commit d83d56cd7e0f26b7495901ebc186cd71b75d0c9d Author: integerbeans Date: Thu Feb 17 12:01:32 2022 +0100 photosyst.c: fixed path for vmmemctl M photosyst.c commit 31b632b6b9187cfa288796067849dee660afb756 Author: Jacob <38846015+7h3w1zz@users.noreply.github.com> Date: Tue Feb 8 14:20:58 2022 -0500 Fix typo in man page M man/atop.1 commit 3dfaa7fd985324bc05f47cc892166ecb0d866d94 Author: Gerlof Langeveld Date: Sat Feb 5 12:32:08 2022 +0100 Added link M README.md commit d75775438afec328df6087e8fd5b537c9883bd30 Author: Gerlof Langeveld Date: Tue Jan 25 17:02:42 2022 +0100 Only print current CPU frequency when supported by CPU architecture M showsys.c commit 09e8812ff705b8a2f4a46f22968c40ead7799781 Merge: 390fca3 31d4be0 Author: Gerlof Langeveld Date: Sun Jan 16 16:42:10 2022 +0100 Merge pull request #184 from pizhenwei/llc-per-sec Support per-second counter for LLC commit 390fca31bc0fc3026f9f351b2a46b93782d908fd Author: Gerlof Langeveld Date: Sun Jan 16 16:41:30 2022 +0100 Lower priority of column 'numnode' in MEM line M showlinux.c commit 31d4be081c950e859c7c62524d8812dea782720d Author: zhenwei pi Date: Fri Jan 14 15:02:18 2022 +0800 Support per-second counter for LLC Signed-off-by: zhenwei pi M showsys.c commit 69e61e071ce82933c2763bb5095caf3563da8080 Merge: 78b4193 060dd9f Author: Gerlof Langeveld Date: Wed Jan 12 17:21:28 2022 +0100 Merge branch 'pizhenwei-llc-monitor' commit 060dd9fd2552f30319a999e3074b7fa00fb66c66 Merge: 78b4193 0dfcb19 Author: Gerlof Langeveld Date: Wed Jan 12 17:21:09 2022 +0100 Fixed conflicts commit 78b419310928251b86751330fe30a7e9daef8c43 Author: Gerlof Langeveld Date: Wed Jan 12 17:01:35 2022 +0100 Reformat status line info in initial screen M showgeneric.c commit b155a541b93602053521796456ac8e957ff0fbec Merge: 7cf3f3c eec27ce Author: Gerlof Langeveld Date: Wed Jan 12 16:45:49 2022 +0100 Merge branch 'pizhenwei-llc-monitor' commit eec27cebf4de9631199c50b318973201f0637a0e Author: Gerlof Langeveld Date: Wed Jan 12 16:45:16 2022 +0100 Cosmetic changes M deviate.c M man/atop.1 M photosyst.c M showgeneric.c commit 0dfcb1996ac3f19663f6a38c30dc2ad92385db8d Author: zhenwei pi Date: Wed Jan 12 15:13:33 2022 +0800 Add LLC in atop man page Signed-off-by: zhenwei pi M man/atop.1 commit 48f32410caca06e0de8ea6066100550b683933c7 Author: zhenwei pi Date: Wed Dec 29 17:08:26 2021 +0800 Support LLC collection Since 2017, Linux started to support resctrl which can be used to control QoS of CPU cache and monitor LLC(last level cache). End user can use on an Intel platform: ~# mount -t resctrl resctrl -o mba_MBps /sys/fs/resctrl Or on a AMD platform: ~# mount -t resctrl resctrl -o cdp /sys/fs/resctrl Then we can find several directories like: /sys/fs/resctrl/mon_data/mon_L3_XX These counters can be found in doc: https://github.com/torvalds/linux/blob/master/Documentation/x86/resctrl.rst On AMD Rome/Milan, there are several dies in a socket, and each die uses it's own L3 cache. With this patch, we can distinguish memory bandwidth of each die to know workload is balanced or not. Or course, on a legacy platform, all the CPU cores share a single L3 cache in a socket, we can know the memory bandwidth and cache usage of the full socket. Test on AMD Milan(Zen3), atop shows: LLC | tot 211.3G | loc 211.2G | LLC15 100% | LLC | tot 185.0G | loc 308.1M | LLC00 100% | LLC | tot 241.5M | loc 207.8M | LLC08 94% | LLC | tot 130.1M | loc 94.5M | LLC05 20% | LLC | tot 111.0M | loc 90.5M | LLC13 85% | LLC | tot 117.1M | loc 83.0M | LLC03 11% | LLC | tot 96.0M | loc 83.5M | LLC12 75% | LLC | tot 106.6M | loc 56.1M | LLC01 42% | LLC | tot 82.7M | loc 71.9M | LLC14 82% | LLC | tot 80.8M | loc 69.0M | LLC11 82% | LLC | tot 74.3M | loc 64.7M | LLC09 85% | LLC | tot 70.8M | loc 59.2M | LLC10 77% | LLC | tot 65.4M | loc 41.5M | LLC06 15% | LLC | tot 50.5M | loc 28.1M | LLC07 16% | LLC | tot 39.2M | loc 25.1M | LLC02 6% | LLC | tot 38.7M | loc 20.8M | LLC04 4% | Signed-off-by: zhenwei pi M atop.c M atop.h M deviate.c M parseable.c M photosyst.c M photosyst.h M showgeneric.c M showgeneric.h M showlinux.c M showlinux.h M showsys.c commit 6180f2a8b10ed7dc0a7fe196fe5a70a4a73d48d4 Merge: 7cf3f3c c4437bc Author: Gerlof Langeveld Date: Tue Jan 11 16:03:40 2022 +0100 Merge branch 'llc-monitor' of https://github.com/pizhenwei/atop into pizhenwei-llc-monitor commit 7cf3f3c6b68f1ec3073569ab1b4bd4365b6757bd Merge: 1e4451b 57ff34f Author: Gerlof Langeveld Date: Tue Jan 11 14:10:48 2022 +0100 Merge pull request #183 from thesamesam/openrc-init Add OpenRC init scripts (atop, atopacct) commit 57ff34f028423f6b2a4bf6fbd0c4a1e98ad87dfc Author: Sam James Date: Sun Jan 9 08:00:25 2022 +0000 Add OpenRC init scripts (atop, atopacct) These have been used for some time in Gentoo but should work fine on e.g. Alpine too (any distribution using OpenRC). Signed-off-by: Sam James A atop.rc.openrc A atopacct.rc.openrc commit 1e4451b66fdfadc9a1913db59a341f4039974398 Author: Gerlof Langeveld Date: Sat Jan 8 13:07:20 2022 +0100 New version 2.7.1 M version.h commit 115b355cb52eee4b11ce00e5b01decde37796e90 Author: Gerlof Langeveld Date: Wed Jan 5 19:12:27 2022 +0100 Explicit type definition in atop.service (issue #180) M atop.service commit 9afc5ad3d714ea09f9ffdebd79b345fe223c7e20 Author: Gerlof Langeveld Date: Wed Jan 5 18:54:21 2022 +0100 Correct disk stats in atopsar (issue #182) A wrong disk report is shown with the -d/-l/-f flags. When the kernel supports the discard counter per disk, output lines are empty in the disk report. When the kernel doesnot support the discard counter per disk, output lines often show a negative value for for the average access time (avserv). M atopsar.c commit c4437bc58b1c205204b785493f8d306d7bf9e660 Author: zhenwei pi Date: Wed Dec 29 17:08:26 2021 +0800 Support LLC collection Since 2017, Linux started to support resctrl which can be used to control QoS of CPU cache and monitor LLC(last level cache). End user can use on an Intel platform: ~# mount -t resctrl resctrl -o mba_MBps /sys/fs/resctrl Or on a AMD platform: ~# mount -t resctrl resctrl -o cdp /sys/fs/resctrl Then we can find several directories like: /sys/fs/resctrl/mon_data/mon_L3_XX These counters can be found in doc: https://github.com/torvalds/linux/blob/master/Documentation/x86/resctrl.rst On AMD Rome/Milan, there are several dies in a socket, and each die uses it's own L3 cache. With this patch, we can distinguish memory bandwidth of each die to know workload is balanced or not. Or course, on a legacy platform, all the CPU cores share a single L3 cache in a socket, we can know the memory bandwidth and cache usage of the full socket. Test on AMD Milan(Zen3), atop shows: LLC | tot 211.3G | loc 211.2G | LLC15 100% | LLC | tot 185.0G | loc 308.1M | LLC00 100% | LLC | tot 241.5M | loc 207.8M | LLC08 94% | LLC | tot 130.1M | loc 94.5M | LLC05 20% | LLC | tot 111.0M | loc 90.5M | LLC13 85% | LLC | tot 117.1M | loc 83.0M | LLC03 11% | LLC | tot 96.0M | loc 83.5M | LLC12 75% | LLC | tot 106.6M | loc 56.1M | LLC01 42% | LLC | tot 82.7M | loc 71.9M | LLC14 82% | LLC | tot 80.8M | loc 69.0M | LLC11 82% | LLC | tot 74.3M | loc 64.7M | LLC09 85% | LLC | tot 70.8M | loc 59.2M | LLC10 77% | LLC | tot 65.4M | loc 41.5M | LLC06 15% | LLC | tot 50.5M | loc 28.1M | LLC07 16% | LLC | tot 39.2M | loc 25.1M | LLC02 6% | LLC | tot 38.7M | loc 20.8M | LLC04 4% | Signed-off-by: zhenwei pi M atop.c M atop.h M deviate.c M parseable.c M photosyst.c M photosyst.h M showgeneric.c M showgeneric.h M showlinux.c M showlinux.h M showsys.c commit ffb30c62ad5a3e7844d3d9004c174af58eb3dc60 Author: Gerlof Langeveld Date: Sat Jan 1 14:37:18 2022 +0100 Add information about service activation to README file M README commit fdf3526bd35c1a84dd11bb73110c1a1f4148e39d Author: Gerlof Langeveld Date: Fri Dec 31 11:25:13 2021 +0100 Add directory to .gitignore M .gitignore commit 5dbe750eca1e468694dc4d4d0d251a485f58ad74 Author: Gerlof Langeveld Date: Fri Dec 31 11:25:04 2021 +0100 Clarify relation between atop/atopacctd in man page M man/atop.1 commit 1b4f7d6d2e14e768f310fc6041c05c5dcc69cee7 Author: Gerlof Langeveld Date: Fri Dec 31 11:24:00 2021 +0100 Added license info M atopgpud M netlink.c commit a67368badc8b639f23268e636a1a3d8787ee90c5 Author: Gerlof Langeveld Date: Fri Dec 31 11:23:18 2021 +0100 Added generic license info to README #179 M README commit c427419d975bd94f452aeabf5df0a5d4a6be241b Author: Gerlof Langeveld Date: Sat Dec 18 11:24:21 2021 +0100 Switch to version 2.7 M version.h commit 81b71b0791755dc73ba5fafdee6211090c5e0d93 Author: Gerlof Langeveld Date: Sat Dec 18 11:21:12 2021 +0100 Describe (un)restricted view in first screen M man/atop.1 M showgeneric.c commit b027bbaa29bb6403789840a03c8dbf22469c2ed3 Author: Gerlof Langeveld Date: Sat Dec 18 11:17:39 2021 +0100 Modified behavior of "make install" Run "make install" to install atop on a systemd-based system and run "make sysvinstall" to install atop on a system V based system. M Makefile M README commit b1ada7e250ec910dcf63955e6a7ee91ffe4877c8 Author: Gerlof Langeveld Date: Sat Dec 18 10:46:25 2021 +0100 Cosmetic change M ifprop.c commit 0efb2365bbefb57a8987ddb3f0da42f8bed1a941 Author: Gerlof Langeveld Date: Fri Dec 17 23:04:12 2021 +0100 Determine (un)restricted view by verifying suid instead of euid. M showgeneric.c commit 154ca1cd1b0fc5cea5c61fa5c5ebfb7a7a785067 Author: Gerlof Langeveld Date: Thu Dec 16 22:13:24 2021 +0100 Do not ignore unknown network interface M ifprop.c commit 65832bf7b3c7c35f44e7e0461b33b01f1eff449e Author: Gerlof Langeveld Date: Thu Dec 16 22:05:18 2021 +0100 Parseable output: optionally exchange spaces in command and command line When the -Z flag is combined with the -P... flag, the spaces in the command and command line in the parseable output are exchanged by underscores and the parenthesis are suppressed to ease space-based parsing. M atop.c M atop.h M man/atop.1 M parseable.c M showgeneric.h commit ab1fbc012bc7e33594ce84c845a2cb43c5edd757 Author: Gerlof Langeveld Date: Tue Dec 14 16:25:20 2021 +0100 No setuid by default M rpmspec/atop.specsystemd commit da9d8a9885e044897bc6f8445974eebf4e6ab20b Author: Gerlof Langeveld Date: Mon Dec 13 19:53:53 2021 +0100 Correct conversion to 2.7 raw files M atopconvert.c M deviate.c M photosyst.c commit b35b9be1233a2d380c0093c645ff4b2e44e38b86 Author: Gerlof Langeveld Date: Mon Dec 13 18:54:40 2021 +0100 Corrected type M man/atop.1 commit c8e755b4bae02258810e996e388ee85d187997d9 Author: Gerlof Langeveld Date: Sat Dec 11 21:46:11 2021 +0100 Only show per-node NUMA stats when more than 1 node M showlinux.c commit a0b48f8fe4641cc693cd198d3c241f507b2a29c4 Merge: 070bf58 01cdbfb Author: Gerlof Langeveld Date: Sat Dec 11 21:41:13 2021 +0100 Merge branch 'adapt_conversion' commit 01cdbfba8b4232a97ffc2d29401ac4503a949cec Author: Gerlof Langeveld Date: Sat Dec 11 21:39:58 2021 +0100 Prepare man pages for version 2.7 M man/atop.1 M man/atopacctd.8 M man/atopcat.1 M man/atopconvert.1 M man/atopgpud.8 M man/atoprc.5 M man/atopsar.1 commit 3c3fb8611f85cedbbed1004e1e40bd60cfc99564 Author: Gerlof Langeveld Date: Sat Dec 11 21:39:35 2021 +0100 Prepare atopconvert for version 2.7 M atopconvert.c A prev/photoproc_27.h A prev/photosyst_27.h commit 070bf58021b94d4bc7d03e97c6126f2b5af3a3bc Merge: 1a8228c dd13f80 Author: Gerlof Langeveld Date: Thu Dec 2 10:29:45 2021 +0100 Merge branch 'acctcoop' commit dd13f80e80e9063e9abaf062b26e4272c9671762 Author: Gerlof Langeveld Date: Thu Dec 2 10:21:52 2021 +0100 Detect logrotation for process accounting with psacct/acct In case that the psacct/acct package is installed and the service is enabled, atop will read the process accounting records from the logfile related to that package (/var/(log/)account/pacct). Atop notices the rotation of this file now, instead of using the logrotation 'wrappers' psaccs_atop and psaccu_atop that were installed before in the /etc/logrotate.d directory (these wrappers have been removed now). M Makefile M README M acctproc.c M man/atop.1 D psaccs_atop D psaccu_atop M rpmspec/atop.specsysv commit 1a8228cb859fb754c3c9db4d08671e9ea7fbc207 Author: Gerlof Langeveld Date: Sat Nov 20 14:53:46 2021 +0100 Speed and duplex mode not correctly filled for interface In case of handshaked ETHTOOL_GLINKSETTINGS the speed and duplex mode were not correctly filled. M ifprop.c commit 0db2a32deea5135831c0fb74c9b8495c9930a291 Author: Gerlof Langeveld Date: Mon Nov 15 23:58:13 2021 +0100 Bug solution related to discards M photosyst.c commit c12ff9db0aec03c435c7a32df8b2fc12ad27a3b1 Merge: 5c08726 06c84b1 Author: Gerlof Langeveld Date: Mon Nov 15 16:54:13 2021 +0100 Merge branch 'add_discard' commit 06c84b1649ba3dc0c4603ff16460eddeecb301ee Author: Gerlof Langeveld Date: Mon Nov 15 16:48:23 2021 +0100 Support discard counter for disks and skip unused disks When the number of discards is separatedly by the kernel and provided via /proc/diskstats, it will be shows as a separate counter by atop. Furthermore, atop will ignore disks that have not been used at all (#reads, #writes and #discards are all zero) as an alternative for the solution provided by PR #170. M atopsar.c M deviate.c M man/atop.1 M man/atopsar.1 M parseable.c M photosyst.c M photosyst.h M showlinux.c M showlinux.h M showsys.c commit 98ccb68acf64bfe432e049f7665f424fe5aad8fb Merge: 5c08726 fd29747 Author: Gerlof Langeveld Date: Tue Nov 9 21:06:39 2021 +0100 Merge pull request #172 from bytedance/bytedance/fix-ioctl-parameter ifprop.c: fix ioctl parameter after handshaking with kernel commit fd29747a6539497146f779f5258032ccab154850 Author: huteng.ht Date: Mon Oct 18 22:50:52 2021 +0800 ifprop.c: fix ioctl parameter after handshaking with kernel After handshaking with kernel completed, following ioctl syscalls with SIOCETHTOOL request code and a positive value of link_mode_masks_nwords, which indicates number of 32-bit words for the supported, advertising and lp_advertising link mode bitmaps, would return with those three __u32 arrays appended to the end of struct ethtool_link_settings. Accordingly, the ioctl parameter, ifreq, should be initialized with ifreq.ifr_ifru.ifru_data pointing to a extended memory space to accommodate those arrays. Otherwise, unexpected memory corruption would occur. M ifprop.c commit 5c08726ca1fe42513f05a1bc5fd06169226e899d Author: Gerlof Langeveld Date: Sat Nov 6 12:27:28 2021 +0100 Show gigabits/sec instead of megabits/sec for Infiniband #131 M atopsar.c commit ef00513d29fe7fed09b567f48262a6c4a9c8309b Merge: 6d3109f ea332c6 Author: Gerlof Langeveld Date: Sat Nov 6 10:44:58 2021 +0100 Merge pull request #168 from xixiliguo/mem-busy Correct memory busy logic in atopsar commit 6d3109f9cf35abff4e73bc7e55506f449c8c1471 Merge: 0d18f69 2a498f1 Author: Gerlof Langeveld Date: Sat Nov 6 10:39:56 2021 +0100 Merge pull request #173 from bytedance/execvp-CVE CVE: use execvp with a specified path to avoid PATH hijacking commit 2a498f1dab5cd710fc228c708ceee8d6974b0ada Author: Fei Li Date: Tue Oct 19 16:08:21 2021 +0800 CVE: use execvp with a specified path to avoid PATH hijacking With the upgrade of atop, there are cases when we try to use the new atop version to read the incompatible log files generated by the old atop version. The current code uses execvp in try_other_version() to implement this compatibility mechanism. However, the execvp is somehow risky. The following is an example. - Preparation conditions: 1. Forge a malicious binary named atop-2.4 under /home/test, and add it to the global $PATH. 2. Supposing there's no real /usr/bin/atop-2.4 or other executable atop-2.4 except the malicious /home/test/atop-2.4. 3. Forge a malformed /var/log/atop/atop_2.4, which can do harm to current machine if called by the malicious /home/test/atop-2.4. 4. Update atop to v2.6. - Reproduce: Use the current v2.6's atop to read /var/log/atop/atop_2.4, the code will finally find out and call the malicious /home/test/atop-2.4 (`strace` can show the detail), then trigger the hijacking risk. - Fix: Let's hard-code execvp's first parameter with '/usr/bin/atop' to avoid the PATH hijacking. Signed-off-by: Fei Li Reported-by: yangqiushi.george@bytedance.com M rawlog.c commit ea332c676785f712e5682395e9a425166f1c1846 Author: peter wang Date: Sun Oct 3 22:35:41 2021 +0800 Correct memory busy logic in atopsar M atopsar.c commit 0d18f694afca1c0268e63f60b5aed7cf95e00aed Author: Gerlof Langeveld Date: Sun Sep 26 14:23:44 2021 +0200 Add parseable output for per-node NUMA counters 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 d0b2de66b7f88664b2733f3bba1e9ace5b9b40b8 Merge: dc71bc4 1fbda3f Author: Gerlof Langeveld Date: Tue Sep 21 13:28:42 2021 +0200 Merge branch 'bytedance-add-per-numa-info' commit 1fbda3f1081a3c2734a215082ff2d4f1870e5898 Author: Gerlof Langeveld Date: Tue Sep 21 13:24:52 2021 +0200 Additions to NUMA counters Table overflow checks, bug fix and manual page has been added. M deviate.c M man/atop.1 M photosyst.c M photosyst.h M showlinux.c M showlinux.h M showsys.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 commit 437beeb569aeaccc342184833473f9fb886da364 Author: Fei Li Date: Sat Aug 7 20:52:28 2021 +0800 Gather and show per numa cpu related statistics Currently there exists many scenarios of mixed services deployment, like hybrid cloud, and generally different services are running in different numa via cpu-pin technology. At this time, accumulate cpu statistics for per numa is extremely meaningful. By default, the maxnumalines is set to be zero. Specify the maxnumalines interactively if needed. Signed-off-by: Fei Li M atop.c M atop.h M deviate.c M photosyst.c M photosyst.h M showgeneric.c M showgeneric.h M showlinux.c M showlinux.h M showsys.c commit 3f5a5e523cd4ae37314c46aa479cf53e17911779 Author: Fei Li Date: Fri Jul 16 18:34:33 2021 +0800 Gather and show per numa memory related statistics Sometimes we need per numa memory related statistics to troubleshoot, let's gather per numa info by reading the /sys/devices/system/node/node$n/meminfo. By default, set maxnumalines to be zero, in case there exists too many NUMA for one machine. Besides, show memory fragmentation level for each numa. Signed-off-by: Fei Li M atop.c M atop.h M deviate.c M photosyst.c M photosyst.h M showgeneric.c M showgeneric.h M showlinux.c M showlinux.h M showsys.c commit dc71bc44af8feaf1ebc4b3e350330b2c63b2d891 Merge: 02a7c5d 14b383a Author: Gerlof Langeveld Date: Mon Aug 9 13:19:54 2021 +0200 Merge branch 'interfaces' commit 14b383a824fad8f0edfb7c52875732fdc2e6196b Author: Gerlof Langeveld Date: Mon Aug 9 13:11:32 2021 +0200 Redesign of code to handle network interfaces The old code that handles the properties of network interfaces did not scale properly for a large number of interfaces and contained bugs. The new code regularly gathers information of the network interfaces and stores it in a hash list. Besides, it takes care that stats of physical interfaces take precedence over stats of virtual interfaces in case that the number of interfaces of a system exceeds the maximum supported by atop. M deviate.c M ifprop.c M ifprop.h M photosyst.c M photosyst.h commit 02a7c5ddcf315148350474292755f4468d91372d Author: Gerlof Langeveld Date: Sun Aug 8 10:11:02 2021 +0200 Correction in handling of zswap and ksm counters M parseable.c M photosyst.c M showlinux.c commit abc63c396192c60d03acd76e357ac4bce61a349e Merge: 8b65a35 842844f Author: Gerlof Langeveld Date: Sun Aug 8 09:42:54 2021 +0200 Merge branch 'podman' commit 842844f299e577f30a9c0d90393c47de81c99a8b Author: Gerlof Langeveld Date: Sun Aug 8 09:41:52 2021 +0200 Support containers created by podman M photoproc.c commit 8b65a35be87e7524d39df206c7b63b0f43e74903 Author: Gerlof Langeveld Date: Tue Aug 3 12:36:49 2021 +0200 Cleanup old messages M showprocs.c commit 334de688127f94774f4ef71b07a7c9f108053f89 Merge: bbc5733 e3e9994 Author: Gerlof Langeveld Date: Tue Aug 3 11:29:33 2021 +0200 Merge pull request #154 from ffontaine/master photosyst.c: fix build on musl commit bbc57336ecbd11a4858d626211415db634fed5e9 Merge: 28201da ec2b21b Author: Gerlof Langeveld Date: Tue Aug 3 11:25:52 2021 +0200 Merge pull request #162 from bytedance/fix-perf Polish do_perfevents when atop built with NOPERFEVENT commit 28201dabbf443fb5edf0c9033e65d4741cf09162 Author: Gerlof Langeveld Date: Tue Aug 3 11:20:28 2021 +0200 Do not change owner to root in Makefile (issue #76) M Makefile commit f1edeaa967447fca9b887dafb45e13d1c74d7fc1 Merge: 0e85cba b5330de Author: Gerlof Langeveld Date: Tue Aug 3 10:58:39 2021 +0200 Merge branch 'debaccounting' commit b5330dee8fbfa24db10bc2fc85f23145e62ca522 Author: Gerlof Langeveld Date: Tue Aug 3 10:55:02 2021 +0200 Support process accounting log /var/log/account/pacct (issue #138) The Debian acct package logs to the file /var/log/account/pacct for process accounting. This file was not checked by atop when looking for process accounting candidates. M acctproc.c M atopacct.init M man/atop.1 M psaccs_atop M psaccu_atop commit ec2b21bbf99447a48aa5380e4d0d3c4fbfb64b72 Author: Fei Li Date: Mon Aug 2 22:20:51 2021 +0800 Polish do_perfevents when atop built with NOPERFEVENT The phenomenon is supposing one atop package whose code is older than commit: 961a6ce13 has been installed and 'perfevents disable' is also defined in atoprc. When trying to update to a version newer than that commit, the new atop will crash saying 'atop built with NOPERFEVENT, cannot use perfevents'. And the result is we have to remove the tag in atoprc before we do update, which is quite inconvenient. Although perf is forbidden when atop is built with NOPERFEVENT, defining 'perfevents disable' in atoprc can actually be allowed. Let's avoid that unfriendly crash by making the check code tolerate with the 'perfevents disable' tag in /etc/atoprc. Signed-off-by: Fei Li M photosyst.c commit 0e85cba6a6e27b80fc8ccde4c38440388895d283 Merge: c72143f 9fc4536 Author: Gerlof Langeveld Date: Mon Jul 26 09:17:50 2021 +0200 Merge branch 'showsysclean' commit 9fc4536d5470f470e50d7e59caee51574232f41c Author: Gerlof Langeveld Date: Mon Jul 26 09:15:24 2021 +0200 Code cleanup showsys.c Adapted calling parameters from void * to specific pointers and removed unnecessary variables. M showlinux.h M showsys.c commit c72143f7b3b3ebca1cd4a921823fd59ff293d524 Merge: 799d800 ae2c756 Author: Gerlof Langeveld Date: Sun Jul 25 14:39:13 2021 +0200 Merge branch 'dynamic2' commit ae2c756721f09d45e2d1034df0072c453321a6ba Author: Gerlof Langeveld Date: Sun Jul 25 14:28:50 2021 +0200 Do not use doconvert funtion to validate counter (#160) The doformat function (formerly named 'doconvert') called all sysprt_... functions to determine which fields were relevant and which fields were not relevant during a run of atop. This might lead to buffer overflows since not all variables used were initialized. This issue has been solved now by introducing a separate (optional) validation function per counter. M showlinux.c M showlinux.h M showsys.c commit 799d800173c4f374031066d1651434784bc5e9a9 Author: Gerlof Langeveld Date: Mon Jun 21 21:52:50 2021 +0200 New counter for number of NUMA pages migrated 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 90d7c082728b1e74eb2be657f2836bddbfe2ea94 Merge: b06b837 b005365 Author: Gerlof Langeveld Date: Mon Jun 21 21:32:35 2021 +0200 Merge branch 'pizhenwei-vmstat-migrate' commit b005365f87f03b491105e40dbb701261dfe94a8d Author: Gerlof Langeveld Date: Mon Jun 21 21:31:18 2021 +0200 Modify field order and priority of paging counters M showlinux.c commit 94499112dffb455e2989a18f45dbe9f8395bedf2 Author: Gerlof Langeveld Date: Mon Jun 21 21:25:42 2021 +0200 Small text modification for migrated counter M man/atop.1 commit b06b8375b713aafb9245d1a87a6024e336bca723 Merge: 87248c3 4b0b717 Author: Gerlof Langeveld Date: Sat Jun 19 17:58:33 2021 +0200 Merge branch 'correctdeviation' commit 4b0b7174dc85f7b432b1c9a0338d208eca1933d3 Author: Gerlof Langeveld Date: Sat Jun 19 17:56:32 2021 +0200 Avoid non-initialized prestat by introducing extra pointer M deviate.c commit 8f05cf05e20072dd49dc0a2414515d028fdf4cbc Author: Gerlof Langeveld Date: Sat Jun 19 17:39:56 2021 +0200 Function 'calcdiff' (deviate.c) used uninitialized prestat struct M deviate.c commit 87248c33f95e4aecdfd515356795da640783e8a2 Merge: a81d5a5 fc405dc Author: Gerlof Langeveld Date: Sat Jun 19 16:29:56 2021 +0200 Merge pull request #157 from MeinhardZh/nvme-multipath Support NVMe multipath disk statistics commit a81d5a5f2be2088fe907cf605a650aa1486fab77 Merge: 8419f02 cf383b0 Author: Gerlof Langeveld Date: Sat Jun 19 16:27:42 2021 +0200 Merge pull request #159 from bytedance/Clean-up-ignored-files Clean up ignored files commit a070595212f22ff197a6901b9418c57ee0b41de0 Author: Gerlof Langeveld Date: Sat Jun 19 16:20:30 2021 +0200 Reserver space for future counters. M photosyst.c M photosyst.h commit 84d6b2abe404d3746fcf7e6e994512d3676b971d Merge: 8419f02 508e51c Author: Gerlof Langeveld Date: Sat Jun 19 10:50:51 2021 +0200 Merge branch 'vmstat-migrate' of https://github.com/pizhenwei/atop into pizhenwei-vmstat-migrate commit cf383b011c4b8b6d37cf779133a1f2e8466eadf6 Author: Fei Li Date: Wed Jun 9 21:19:46 2021 +0800 Clean up ignored files Fix #145, and supplement commit e81b1d9c. Signed-off-by: Fei Li D atopsar commit 8419f02f5daad1f0c09b518029780a203df50b36 Merge: 33072ab 77c87ee Author: Gerlof Langeveld Date: Thu Jun 3 16:46:44 2021 +0200 Merge branch 'varsortitem' commit 77c87ee6fbc8fb2f0c4b640e453405fd76e51999 Author: Gerlof Langeveld Date: Thu Jun 3 16:41:02 2021 +0200 Variable number of positions for sortitem The sort criterion for the process list is a percentage of CPU, memory, disk, etc utilization of the process concerned. The number of positions for the percentage was fixed 3, considering a maximum of 100% (maximum 999%). However, when a process has 10 threads (or more) on a system with 10 CPUs (or more) the CPU utilization of that process can be greater than 999%. Therefore, the number of positions for the percentage depends on the number of CPUs now. M showgeneric.c M showgeneric.h M showlinux.c M showprocs.c commit 33072abd452860d4fa76e3d5ec4b9a0d92bcb284 Author: Gerlof Langeveld Date: Thu Jun 3 15:06:45 2021 +0200 Adapt man page of atop for state selection (add state 'I'). M man/atop.1 commit 8d8f9b0ea3e3cd387652f7ab6aef85ad10d1cfa4 Author: Gerlof Langeveld Date: Thu Jun 3 14:53:33 2021 +0200 Allow thread state 'I' with state selection (key 'Q') M showgeneric.c commit bb38fd80633cf11475fc18191d4cc8bf3f0e67ba Author: Gerlof Langeveld Date: Thu Jun 3 14:40:17 2021 +0200 Count idle threads as non-interruptible threads Sofar threads in state 'I' (TASK_IDLE) were not counted on process level (thread categories shown with key 's') and system level (PRC line). Idle threads will now be counted as uninterruptible threads. M photoproc.c commit f1431502fbaf22f22ee6f5a1d0b817eca53dd692 Author: Gerlof Langeveld Date: Thu Jun 3 14:29:24 2021 +0200 Uninitialized index might cause segmentation fault. M showlinux.c commit fc405dc34f280889eb31820e2e673b6a2f3e03d0 Author: zhouenhua Date: Wed May 26 17:39:34 2021 +0800 Support NVMe multipath disk statistics When NVMe disk multi-path is turned on, the kernel only records IO statistics for each multi-path. Ex, read statistics from /proc/diskstatus: 0 0 nvme0c0n1 34706049 74843 4815165730 93876201 4187610 1925219 6989322264 47813633 0 45557635 118897696 312571 0 11200562376 857547 259 1 nvme0n1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 For this case, we usually expect the right statistics from nvme0c0n1 instead of nvme0n1. Signed-off-by: enhua zhou zhouenhua@bytedance.com M photosyst.c commit 508e51cd82cffd213feb8294ec58200175533227 Author: zhenwei pi Date: Tue May 25 09:55:03 2021 +0800 Support page migration counter The kernel may migrate pages between NUMA nodes to make memory access faster, but during migration the kernel need hold the memory lock of target process and trigger TLB flush. It hurts the performance a lot. Add pgmigrate counter to show/record it for trouble shooting. Signed-off-by: zhenwei pi 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 e3e99949d2a95d3f9a39bfe8fb49354613ee918d Author: Fabrice Fontaine Date: Mon May 24 21:08:52 2021 +0200 photosyst.c: fix build on musl Build on musl is broken since version 2.6.0 and https://github.com/Atoptool/atop/commit/e889c66fbe1d0b7ae38fbcbaa46cea749257f486 because limits.h is not included: photosyst.c: In function ‘lvmmapname’: photosyst.c:1624:14: error: ‘PATH_MAX’ undeclared (first use in this function); did you mean ‘AF_MAX’? char path[PATH_MAX]; ^~~~~~~~ AF_MAX Signed-off-by: Fabrice Fontaine M photosyst.c commit b004daf963939c925dd775fca9f3cebff7381bd1 Merge: feb220c a4503da Author: Gerlof Langeveld Date: Mon May 24 12:15:06 2021 +0200 Merge branch 'ffontaine-master' commit a4503da46769329318ee3483ae818d8ff4bf5de7 Author: Gerlof Langeveld Date: Mon May 24 12:12:48 2021 +0200 Use of struct ethtool_link_settings should be conditional M ifprop.c commit 1d28e46c2f6d1ca856303d1252f24ad0e5db833e Merge: feb220c aff473e Author: Gerlof Langeveld Date: Mon May 24 11:06:46 2021 +0200 Merge branch 'master' of https://github.com/ffontaine/atop into ffontaine-master commit feb220c77f16030428a28e0365633fde2d34219d Merge: 69adbd1 918868e Author: Gerlof Langeveld Date: Sat May 8 14:29:51 2021 +0200 Merge branch 'yk144-average-per-second-rounding' commit 918868e49948df6c7f2ce7ee3fe86e4b23eee2b5 Author: Gerlof Langeveld Date: Sat May 8 14:28:45 2021 +0200 Cosmetic change (source code layout) M atop.h commit 8b0f03e55f9fe555ed9a6e64e8e2c6bc9c113c36 Merge: 69adbd1 5c9c88a Author: Gerlof Langeveld Date: Sat May 8 14:09:00 2021 +0200 Merge branch 'average-per-second-rounding' of https://github.com/yk144/atop into yk144-average-per-second-rounding commit 69adbd109a8111daba53bbaa8680677d3f65fa86 Merge: 48df66d d66ff0e Author: Gerlof Langeveld Date: Sat May 8 13:57:39 2021 +0200 Merge branch 'natoscott-state-filter' commit d66ff0e48fb6ec90ab84f786c4016fda7cd0644b Author: Gerlof Langeveld Date: Sat May 8 13:56:35 2021 +0200 Various bug fixes and in general add 'thread' to all messages M man/atop.1 M showgeneric.c commit 441e6f72e7c59974dbd036230afa70523e3c3cde Merge: 48df66d 44ea948 Author: Gerlof Langeveld Date: Sat May 8 13:11:30 2021 +0200 Merge branch 'state-filter' of https://github.com/natoscott/atop into natoscott-state-filter commit 48df66d570e640c683a2239016b0fd3ecbd41821 Merge: fed5bd5 0aa920e Author: Gerlof Langeveld Date: Sun Apr 25 15:06:43 2021 +0200 Merge branch 'bytedance-fix-initifprop' commit 0aa920ea2b130e0cfae9c38817e88549cfc6c4fc Author: Gerlof Langeveld Date: Sun Apr 25 15:06:14 2021 +0200 Properly define if interface is virtual or physical M ifprop.c commit cafda7ac55e60996bba6d376f6e977821ca40982 Merge: fed5bd5 ef183ff Author: Gerlof Langeveld Date: Sun Apr 25 14:45:23 2021 +0200 Merge branch 'fix-initifprop' of https://github.com/bytedance/atop into bytedance-fix-initifprop commit fed5bd53a291483c4d2e56ea9cf53b62e8eea5af Merge: d2ca9f2 95412fa Author: Gerlof Langeveld Date: Sun Apr 25 14:38:14 2021 +0200 Merge pull request #150 from natoscott/deprecated-function Switch to using curses vw_printw as vwprintw is deprecated commit d2ca9f2954a25b4955c661bb892817988fd03b2e Author: Gerlof Langeveld Date: Sun Apr 25 14:21:38 2021 +0200 Add license info to various include files (solves issue #144) M atopacctd.h M gpucom.h M ifprop.h M netatop.h M netatopd.h M netstats.h M parseable.h M rawlog.h commit 961a6ce137eedc722a27b6002f1ffb3c6ba86842 Author: Gerlof Langeveld Date: Sun Apr 25 13:23:24 2021 +0200 HTTP statistics modified from -h flag to -o flag (solves issue #152) The -h flag was used for the HTTP stats and Infiniband stats. For HTTP statistics (by default not activated) the -o flag can be used now. M atopsar.c M photosyst.c commit db9eef1d4a3d9414ed00f6c0da0e5dad55f2b9af Author: Gerlof Langeveld Date: Thu Apr 22 08:24:29 2021 +0200 Possible buffer overflow with huge number of seconds Number of seconds will be limited to 5 positions. M showsys.c commit aa85ccbe1ed475b1be010ba254bf327a0c12a725 Author: Gerlof Langeveld Date: Thu Apr 22 08:07:06 2021 +0200 Possible buffer overflow with average disk I/O The rounding algorithm has been improved and the buffer has been enlarged. M showsys.c commit 44ea948e94f665b5b0c1459b7323759f8944aaf8 Author: Nathan Scott Date: Mon Mar 22 16:42:45 2021 +1100 Allow process selection based on specific process states. M man/atop.1 M showgeneric.c M showgeneric.h commit 9cb119713b5e6be43671fe1856fb4bd49ff91fa7 Author: Gerlof Langeveld Date: Tue Mar 23 16:50:48 2021 +0100 Correct definition of environment variable LOGOPTS The former definition of the empty environment variable LOGOPTS in the atop.service file (Environment=LOGOPTS="") causes an empty command line parameter for atop running in the background. This empty parameter results in an interval timer definition of 0 seconds which means that atop only writes one single sample to the daily logfile and after that no more! When the environment variable is overruled by the definition LOGOPTS="" (or another value) in the file /etc/default/atop the problem is bypassed. M atop.service commit 95412fa4a3eac31e759fbec1030c6ba233514ccb Author: Nathan Scott Date: Fri Mar 12 10:45:52 2021 +1100 Switch to using curses vw_printw as vwprintw is deprecated Resolves the following compiler warning - showgeneric.c: In function ‘printg’: showgeneric.c:2724:3: warning: ‘vwprintw’ is deprecated [-Wdeprecated-declarations] M showgeneric.c commit 5c580bee1fbfa32fef5558cb3beadb5583952c98 Author: Gerlof Langeveld Date: Sat Mar 6 12:10:15 2021 +0100 Support multiple counters 'allocstall_' in /proc/vmstat. M photosyst.c commit 12984d6defae1643ed8683f8a09cbf4997bd0949 Merge: dc6856a e4e82b4 Author: Gerlof Langeveld Date: Sat Mar 6 11:51:47 2021 +0100 Merge branch 'bytedance-add-compact_stall' commit e4e82b4827f49ebf3d4b68128d2cb1231e91b333 Merge: dc6856a d0ae258 Author: Gerlof Langeveld Date: Sat Mar 6 11:46:31 2021 +0100 Merge branch 'add-compact_stall' of https://github.com/bytedance/atop into bytedance-add-compact_stall commit dc6856a1f53be7a322f585ac30690239d0416e25 Author: Gerlof Langeveld Date: Sat Feb 20 11:07:20 2021 +0100 Avoid integer overflow when calculating PSI percentages. M showsys.c commit 67eadc5b2aa22d562480c5b197f58f259eb46f66 Merge: e81b1d9 09935eb Author: Gerlof Langeveld Date: Sat Feb 20 10:57:43 2021 +0100 Merge branch 'dynamic' commit 09935eb6e35cc7408a29e11c5026b67378edfa3e Author: Gerlof Langeveld Date: Sat Feb 20 10:49:44 2021 +0100 Suppress irrelevant counters (solves issue #148) In the output of atop, counters will be suppressed when they do not contain a relevant value to avoid unnecessary column space to be used. A counter does not contain a relevant value when it is not supported by the kernel in use, when it relates to a kernel mechanism that is not in use on this machine or when the user running atop does not have enough privileges to read specific counters. To make the user aware that certain counters are missing due to a lack of privileges, a message is shown during the first sample. M atop.h M deviate.c M man/atop.1 M man/atopsar.1 M parseable.c M photosyst.c M showgeneric.c M showlinux.c M showsys.c commit fa9e907d4174df94935606bb9e84dd38bda87f8c Author: Gerlof Langeveld Date: Sat Feb 20 10:42:17 2021 +0100 Add the number of OOM kills to the -s report. The number of OOM kills are shown as an additional column in the 'atopsar -s' report. In contradiction to the other frequency columns the OOM kills are not shown as a per-second value because a single OOM kill during a longer interval might result in 0.00 kills/sec in that case. M atopsar.c commit d0ae2589d1ff48ce796b76867c0f96cd63ce7cbf Author: Fei Li Date: Sun Mar 8 11:37:34 2020 +0800 Record the number of process stalls to run memory compaction Signed-off-by: Fei Li M deviate.c M man/atop.1 M parseable.c M photosyst.c M photosyst.h M prev/photosyst_20.h M prev/photosyst_21.h M prev/photosyst_22.h M prev/photosyst_23.h M prev/photosyst_24.h M showlinux.c M showlinux.h M showsys.c commit e81b1d9cb405482b6706aeea774b2ad8fe6718ff Author: Gerlof Langeveld Date: Sat Jan 30 12:34:14 2021 +0100 Make clean removes atopsar now (issue 145) M Makefile commit 9decbba5980249129a0cb5ba3b186d07e3590c57 Author: Gerlof Langeveld Date: Sat Jan 30 12:29:54 2021 +0100 Add counter for the number of OOM kills to the PAG 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 f62fdc8429a7f170415f4bf58befee9d06ff6c0b Merge: 462cc2b c0623c0 Author: Gerlof Langeveld Date: Sat Jan 30 12:10:24 2021 +0100 Merge branch 'ksmzswap' commit c0623c070733953d46e4783ffd35f65714602efb Author: Gerlof Langeveld Date: Sat Jan 30 12:07:58 2021 +0100 Add counters for zswap and KSM. For zswap the counters stored_pages and pool_total_size are added. For KSM the counters pages_shared and pages_sharing are added. 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 aff473ee28903775e1bb35793b9c4c50ee0c7270 Author: Fabrice Fontaine Date: Tue Dec 22 12:23:33 2020 +0100 ifprop.c: fix build with kernel < 4.6 Build fails with kernel headers < 4.6 since version 2.6.0 and https://github.com/Atoptool/atop/commit/08c622ecaa5bb0bb260984ceaddc4730d1b312a7 Indeed, ethtool_link_settings and ETHTOOL_GLINKSETTINGS are only available since https://github.com/torvalds/linux/commit/3f1ac7a700d039c61d8d8b99f28d605d489a60cf Signed-off-by: Fabrice Fontaine M ifprop.c commit 462cc2ba23dfb0e36d41c6ff2e04f4c67f8aa0bc Author: Gerlof Langeveld Date: Mon Dec 21 20:38:17 2020 +0100 Set new process-related fields to zero during conversion M atopconvert.c commit 2b674f9d7c58f5e58d52b3416e3504f4c97741e2 Author: Gerlof Langeveld Date: Mon Dec 21 19:58:56 2020 +0100 Added -Wno-stringop-truncation to CFLAGS. Avoid warnings about truncating command strings with gcc version 8. M Makefile commit 008f86b758514ac747b506a12a2196e4ba5e7ef1 Author: Gerlof Langeveld Date: Sat Dec 19 16:38:18 2020 +0100 Avoid warnings with gcc 8 M atopacctd.c M rawlog.c M showsys.c commit 1bc1a03e5f88b5a11899876a752c3a83fbb671c4 Author: Gerlof Langeveld Date: Sat Dec 19 15:06:35 2020 +0100 Typo corrected. M rpmspec/atop.specsystemd commit 6ca80e13b074523f46516c5fef6414968f06db9f Author: Gerlof Langeveld Date: Sat Dec 19 14:58:56 2020 +0100 Update date in man pages. M man/atop.1 M man/atopacctd.8 M man/atopcat.1 M man/atopconvert.1 M man/atopgpud.8 M man/atoprc.5 M man/atopsar.1 commit 5f8e318def6882c866d3820cf608b6157afba767 Author: Gerlof Langeveld Date: Sat Dec 19 14:48:45 2020 +0100 Prepare atop version 2.6 M version.h commit e22eefa8b1e8a2064787a8ac9a4e7647fc2ff247 Author: Gerlof Langeveld Date: Sat Dec 19 14:45:24 2020 +0100 Support file format of atop 2.6 M atopconvert.c A prev/photoproc_26.h A prev/photosyst_26.h commit 8e2b5fbe5f853cabfe01f2be3860c84a1715d543 Author: Gerlof Langeveld Date: Sat Dec 5 11:12:12 2020 +0100 Avoid error when stopping a stopped daemon (issue #139) The -f flag has been added to the rm command to avoid an error message when trying to stop a daemon that is not active. M atop.init M atopacct.init commit 23642fcdac1fca9bddcb8cbcc2a443a18b2416d3 Merge: af71985 ebc2fe4 Author: Gerlof Langeveld Date: Sat Nov 21 13:00:25 2020 +0100 Merge branch 'threadsort' commit ebc2fe442aa5de14974a2ccd78ead83719196c1f Author: Gerlof Langeveld Date: Sat Nov 21 12:56:58 2020 +0100 Sort threads per process in threadview (option 'Y') Threads per process (threadview) will be sorted according to the current sort criterium when the 'Y' option is active. M man/atop.1 M man/atoprc.5 M showgeneric.c M showgeneric.h commit af7198583b27800f541464617902fcb7cc637f8e Author: Gerlof Langeveld Date: Sat Nov 7 11:16:43 2020 +0100 Explicitly refer to python3 instead of python Specifically needed for CentOS8 that only knows about python2 and python3. M atopgpud M rpmspec/atop.specsystemd commit 16aaba85e16b5e564362c9c6f109480632fd8313 Author: Gerlof Langeveld Date: Sat Oct 24 12:27:43 2020 +0200 Additional fields for parseable output with label NET The most important (error) counters related to TCP and UDP have been added to the parseable output with label NET. Solves issue #98 M man/atop.1 M parseable.c commit 14c0e5c751f1cd00b7191a5a355199daf11e0ef7 Author: Gerlof Langeveld Date: Sat Oct 24 11:27:35 2020 +0200 Correct man page of atop: parseable output for label PRD The obsoleted value 'n' (one but last value) was not described in the man page. This has been added now. Solves issue #136 M man/atop.1 commit 7760d5902451abbb52e45222b07af7667a806163 Author: Gerlof Langeveld Date: Sat Oct 24 11:06:35 2020 +0200 Issue garbage collection, even when system clock modified Even when the system clock (epoch) is decreased, garbare collection should continue to avoid that too many shadow files will occupy disk space. Solves issue #132 M atopacctd.c commit 25c6d641e3b6c4f424003d70b40b0239c0c2b10b Author: Gerlof Langeveld Date: Sat Oct 3 12:32:17 2020 +0200 Size of swap cache added to SWP row (issue #117) 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 90fdd81607461095d7b42525d4924c18b513cd06 Author: Gerlof Langeveld Date: Sat Oct 3 11:25:29 2020 +0200 Show PPID for terminated processes (solves issue #120). When the format of the process accounting record provides the PPID, it should also be shown in the various info (key 'v'). M showprocs.c commit 27dbe4e17634e58fdca5930e8cf2c2e06cd40821 Author: Gerlof Langeveld Date: Sat Aug 29 11:50:17 2020 +0200 When atopacctd restarts, it recreates the shadow directory When atopacctd is killed and restarted afterwards, the shadow directory /var/run/pacct_shadow.d still exists. The old directory will be renamed to a unique name (containing the PID of the running atopacctd) because files from this directory might still be opened by running atop processes. After that, a new directory will be created without intervention. Solved issue #121 M atopacctd.c commit ef183ffd59fdfd108f6d2f65ea9153c4a032d6d6 Author: Fei Li Date: Tue Apr 21 10:40:21 2020 +0800 Fix initifprop() when obtaining properties of all interfaces This patch fixes two issues: - Correctly set link_mode_masks_nwords when using ETHTOOL_GLINKSETTINGS to query devices, or else we can only obtain base fields from kernel function: ethtool_get_link_ksettings() without any link_settings filed is stored. - Considering not all net drivers realize the "phy_address" field in kernel, to determine whether or not a network interface is virtual, let's read /sys/devices/virtual/net/xxx to obtain all virtual interfaces' name, and then do compare with each ifprops[i].name to filter it. Fixes: 08c622ecaa5bb0bb260984ceaddc4730d1b312a7 Signed-off-by: Fei Li M ifprop.c commit 8c6b556a60048beafa40547dee1eff90ed728e79 Author: Gerlof Langeveld Date: Tue Aug 11 07:58:45 2020 +0200 Skip empty lines in atoprc file M atop.c M atopsar.c M various.c commit 4a215076767b4198a87dace45bdfd976d1da9c9f Merge: 678431f 93877ad Author: Gerlof Langeveld Date: Tue Aug 11 07:37:35 2020 +0200 Merge pull request #125 from bytedance/oom_score_fixed_adj Try to set the highest OOM priority for atop commit 93877ad6dc5c80b97fc91fc1f8df20c064390895 Author: zhenwei pi Date: Tue Aug 11 10:08:37 2020 +0800 Try to set the highest OOM priority for atop We usually expect that atop has highest OOM priority. when the system runs out of memory, atop still could record necessary infomation. Then we are able to analyze the OOM situation afterwards. The orignal verion of this feature: read config from atoprc, then apply to atop process. But Gerlof considers that just trying to set the highest priority is better. So rewrite this as Gerlof's advise. Signed-off-by: zhenwei pi M atop.c M atop.h M various.c commit 678431f34ad49ef6ade6652eb0bb39f07dc42e6d Merge: dd0fb8a 67ab9e3 Author: Gerlof Langeveld Date: Mon Aug 10 20:49:35 2020 +0200 Merge pull request #124 from bytedance/fix-prusage Fix prusage segmentation fault when vis.show_usage is null commit 67ab9e3d6db1c39f5e5816690995e4503d99ec29 Author: Fei Li Date: Wed Aug 14 20:15:30 2019 +0800 Fix prusage segmentation fault when vis.show_usage is null Signed-off-by: Fei Li M atop.c commit dd0fb8a41afcd9cc03323df33340a40cc6dba3ea Author: Gerlof Langeveld Date: Fri Jul 3 21:16:17 2020 +0200 Add WCHAN to parseable output (PRC). M man/atop.1 M parseable.c commit 4739704df2449766ac1ac3f2b74f3dc5f1904709 Merge: 9997130 f5742ac Author: Gerlof Langeveld Date: Fri Jul 3 20:34:40 2020 +0200 Merge branch 'wchan' commit f5742ac88ef2e554890bd9a418a403f1bc6b3912 Author: Gerlof Langeveld Date: Fri Jul 3 20:26:52 2020 +0200 Maintain WCHAN per thread When the 'W' key or the -W flag is used, the wait channel will be determined for every sleeping thread. Gathering of this value is optinal because the determination of the WCHAN string is relatively CPU-intensive for atop. The WCHAN is shown when selecting the scheduling statistics (key 's'). M atop.c M atop.h M deviate.c M man/atop.1 M photoproc.c M photoproc.h M showgeneric.c M showgeneric.h M showlinux.c M showlinux.h M showprocs.c commit 99971301394dce410976de972b9536e6db03760e Merge: ef62300 ed1f6d3 Author: Gerlof Langeveld Date: Thu Jul 2 16:22:41 2020 +0200 Merge branch 'bytedance-schedstat' commit ed1f6d30bd359f4d264edfde4e65bcafb16ad32b Author: Gerlof Langeveld Date: Thu Jul 2 16:17:30 2020 +0200 Show runqueue delay for process and threads For every process (cumulated) and thread the time spent waiting on a runqueue is shown (RDELAY). M deviate.c M man/atop.1 M parseable.c M photoproc.c M photoproc.h M showlinux.c M showprocs.c commit 2119fe754f6a75e444c1c48d7303a6754210efbe Merge: ef62300 0ffbcd9 Author: Gerlof Langeveld Date: Thu Jul 2 14:08:37 2020 +0200 Merge branch 'schedstat' of git://github.com/bytedance/atop into bytedance-schedstat commit ef62300ffa08ca978ed6d1bfe7eb65fb242aa873 Author: Gerlof Langeveld Date: Mon Jun 15 14:03:49 2020 +0200 Occasional segmentation fault when atop is combined with netatop Even when the number of exited processes via netatop was zero, still a malloc was issued. In certain circumstances a structure could have been stored at the address that was returned by malloc, resulting in a segmentation fault on a subsequent malloc. M netatopif.c commit 7838d54e81070eedfe5c7362fcca9875fa8d81c5 Author: Gerlof Langeveld Date: Mon Jun 15 13:58:47 2020 +0200 Prevent race condition when last atop stops and new one is started When the last atop incarnation terminates, the atopacctd issues a cleanup of all accounting files and writes a new'current' file. When a new atop is started meanwhile, a race condition might be introduced, which is prevented by a semaphore. M acctproc.c M atopacctd.c commit 19b0d7c12d708d788768c14b45a9ec1114334814 Merge: ebf956c eba520e Author: Gerlof Langeveld Date: Sat Jun 6 11:54:48 2020 +0200 Merge pull request #110 from xixiliguo/atop-rotate disable atop-rotate.timer when uninstall commit 0ffbcd91a346f6e3ac7a22af1b49dd956f00f0c9 Author: zhenwei pi Date: Fri May 22 19:54:09 2020 +0800 Support time spent waiting on a runqueue collection Collect run delay information from /proc//schedstat, and show in schedproc page. It's helpful for latency spike trouble shooting case. Signed-off-by: zhenwei pi M deviate.c M photoproc.c M photoproc.h M showlinux.c M showlinux.h M showprocs.c commit ebf956cc28aef6e818e32b920e8e2e73577777f8 Author: Gerlof Langeveld Date: Sat May 16 13:00:03 2020 +0200 Show ARC size for ZFSonlinux The new counter `zfarc` is introduced for memory analysis on system level showing the current size of the ARC (cache) for ZFSonlinux. 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 62961a0190e8844f936ae52162127c58a87d6392 Author: Gerlof Langeveld Date: Tue Apr 28 16:02:13 2020 +0200 Add 'New Process' indicator to parseable output. A new field is added to the output line of -PPRG indicating if the process is newly started during the interval. M man/atop.1 M parseable.c commit 3ddaaa3324a1e2ee67b6c4d85f38c13ce6139d90 Author: Gerlof Langeveld Date: Tue Apr 28 14:59:41 2020 +0200 Correct handling of locale. M README M showgeneric.c commit eba520edfdcc4ddf6bd912447bba57ce13f099db Author: root Date: Wed Apr 22 19:35:40 2020 +0800 disable atop-rotate.timer when uninstall M rpmspec/atop.specsystemd commit 62dc8de0b9a5001268a75950d7d2594d56c4db4a Author: Gerlof Langeveld Date: Sat Mar 7 14:51:00 2020 +0100 Modified locale to "C" to avoid failing sscanf for floats M showgeneric.c commit d87e22b6815d4036dae5f14bab6dca54f9c34e59 Author: Gerlof Langeveld Date: Sat Mar 7 14:50:21 2020 +0100 Modified /usr/lib/systemd/system to /lib/systemd/system M Makefile commit ff551421bff21c272de43e02b8179144f17b7fc9 Author: Gerlof Langeveld Date: Fri Mar 6 07:51:00 2020 +0100 Atopcat: add flags -d (dryrun) and -v (verbose) M atopcat.c M man/atopcat.1 commit 2990fd589f5764fafe3abef2fb2e43002070562f Author: Gerlof Langeveld Date: Mon Feb 24 19:10:09 2020 +0100 Allow forward branches when reading raw data from a pipe. M man/atop.1 M rawlog.c commit e86f32d200fa02dcaaf8bac066160768b48d7d68 Author: Gerlof Langeveld Date: Mon Feb 24 18:19:46 2020 +0100 Introduction of command 'atopcat' to concatenate rawlog files With the command 'atopcat' the contents of rawlog files can be concatenated and written to stdout. The output of this command can be redirected to a file or transferred via a pipe to atop or atopsar. In this way weekly, monthly, ... reports or extractions can be created in an easy way. M .gitignore M Makefile A atopcat.c M atopconvert.c A man/atopcat.1 M man/atopconvert.1 A rawlog.h M rpmspec/atop.specsystemd M rpmspec/atop.specsysv commit 186ab18f0cf66252db7dbd7187c9efb55f4f4188 Author: Gerlof Langeveld Date: Mon Feb 24 18:10:40 2020 +0100 Support reading raw log from pipe with option '-r -' Atop and atopsar can read raw log data from a pipe via stdin when the option '-r -' is used. Since a seek is not possible on a pipe, no rewind is possible nor viewing of previous samples. M atop.c M atopsar.c M man/atop.1 M man/atopsar.1 M rawlog.c M showgeneric.c commit c43dc50eee1c9265340c8531db80a83ec1a06b84 Merge: bc2e412 115feba Author: Gerlof Langeveld Date: Sat Feb 22 17:01:46 2020 +0100 Merge branch 'multiday' commit 115febabe00a562bb40d11da697ff1e452281fce Author: Gerlof Langeveld Date: Sat Feb 22 16:57:47 2020 +0100 Support of logfiles that span multiple days The time specification to define the begin time (-b flag) and end time (-e flag) of both atop and atopsar is [YYYYMMDD]hhmm now. This also applies to the branch key ('b') when using atop interactively. M atop.c M atop.h M atopsar.c M man/atop.1 M man/atopsar.1 M netatopif.c M rawlog.c M showgeneric.c M various.c commit bc2e412791bf5b1d206a362ec1f9ce9f23beac42 Merge: 8b9cd3f f202a31 Author: Gerlof Langeveld Date: Sat Feb 22 13:24:20 2020 +0100 Merge pull request #100 from zlandau/loadavg_crash Fix crash when system reports huge load averages commit f202a317994f563a25a8c64a38ba4445a1d4606b Author: Zachary P. Landau Date: Sun Feb 16 12:33:30 2020 -0800 Fix crash when system reports huge load averages The load average reporting functions in showsys.c use static buffer sizes. When the load averages on a machine are very large, this causes the writes to extend past the buffer. With this commit, if a number is too large then we just show '>NNNNNN'. I'm not sure if this is the best choice, so I'm open to other ideas. This is what the output looks like when we exceed the maximums: CPL | avg1 >999999 | avg5 >999999 | avg15 >99999 | csw 103117e3 | intr 88296e3 | Note that this was triggered from a kernel that is reporting clearly inaccurate numbers: $ cat /proc/loadavg 1.25 2.40 368567045.47 1/589 53576 But regardless, crashing is no fun. For future reference, I narrowed down the issue by building with -fsanitize=address. For example: ==55396==ERROR: AddressSanitizer: global-buffer-overflow on address 0x55c527d6f14f at pc 0x7f4729942df9 bp 0x7ffe1fd30710 sp 0x7ffe1fd2fea0 WRITE of size 10 at 0x55c527d6f14f thread T0 #0 0x7f4729942df8 in __interceptor_vsprintf /build/gcc/src/gcc/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:1627 #1 0x7f47299432cf in __interceptor_sprintf /build/gcc/src/gcc/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:1670 #2 0x55c527d1c167 in sysprt_CPLAVG15 (/home/kapheine/projects/atop/atop+0x74167) #3 0x55c527d2087a in showsysline (/home/kapheine/projects/atop/atop+0x7887a) #4 0x55c527d1471f in prisyst (/home/kapheine/projects/atop/atop+0x6c71f) #5 0x55c527d090ff in generic_samp (/home/kapheine/projects/atop/atop+0x610ff) #6 0x55c527ce17c9 in main (/home/kapheine/projects/atop/atop+0x397c9) #7 0x7f472952a022 in __libc_start_main (/usr/lib/libc.so.6+0x27022) #8 0x55c527ce266d in _start (/home/kapheine/projects/atop/atop+0x3a66d) 0x55c527d6f14f is located 49 bytes to the left of global variable 'buf' defined in 'showsys.c:981:54' (0x55c527d6f180) of size 15 0x55c527d6f14f is located 0 bytes to the right of global variable 'buf' defined in 'showsys.c:1000:54' (0x55c527d6f140) of size 15 M showsys.c commit 8b9cd3f8225d7d17828e42dd73f1e8b47176f11a Author: Gerlof Langeveld Date: Sat Feb 15 12:00:45 2020 +0100 Wrong checks on return values of write and pread syscalls M atop.c M atop.h M atopacctd.c M atopsar.c M rawlog.c M showgeneric.c M various.c commit 7bbfd140d2511cb9046905a51096376d7d9b0669 Merge: d4769ab ffc8ba8 Author: Gerlof Langeveld Date: Sat Feb 15 10:20:54 2020 +0100 Merge pull request #50 from Saur2000/master atop.daily, atop.init, atop-pm.sh, mkdate: Avoid using bash commit d4769ab3882d1d74219564f415823e8d29877358 Merge: dfa87a6 788c388 Author: Gerlof Langeveld Date: Sat Feb 15 10:13:55 2020 +0100 Merge pull request #99 from bytedance/logopts-man Update man about default LOGOPT '-R' commit 788c3883f5e1567bba572da92c17facd6332e898 Author: zhenwei pi Date: Tue Feb 11 09:55:13 2020 +0800 Update man about default LOGOPT '-R' Since 604b563a223130d9bcce3d3358537a6c5ce05e7a, atop has removed default LOGOPT '-R', so update man page here. Signed-off-by: zhenwei pi M man/atop.1 commit dfa87a64a68acea86a470063c02be044fbfa518c Merge: 14900df ad4b3c8 Author: Gerlof Langeveld Date: Sat Feb 8 10:25:31 2020 +0100 Merge branch 'rpungartnik-StringManipulation' commit ad4b3c8cd40b01a68f7e6bdc6bacfcf1117db1ae Author: Gerlof Langeveld Date: Sat Feb 8 10:22:40 2020 +0100 Avoid negative values to cause memory overflow, but do not set to 0. M photosyst.c M showsys.c commit 2292e0b5b9327eeae2c1078f0e266d1febef7645 Merge: 14900df e889c66 Author: Gerlof Langeveld Date: Sat Feb 8 10:00:09 2020 +0100 Merge branch 'StringManipulation' of git://github.com/rpungartnik/atop into rpungartnik-StringManipulation commit 14900df5b75bf4d97e0c47caa8ea08f9386230e1 Merge: b3356e5 b2f804c Author: Gerlof Langeveld Date: Sat Jan 18 12:07:42 2020 +0100 Merge branch 'rpungartnik-ResultCheck' commit b2f804c8b49501dc2d8e941bbdfe47e4ec2203e3 Author: Gerlof Langeveld Date: Sat Jan 18 12:05:06 2020 +0100 Avoid compiler warnings due to unused results Compiler warnings are given for return values of system calls that are not verified, even if these system calls are allowed to fail. M atopacctd.c M photosyst.c M rawlog.c M various.c commit 5a39c743c6094c3c4e141dfc7a109cecb46c0944 Merge: b3356e5 2fd755b Author: Gerlof Langeveld Date: Sat Jan 18 11:49:13 2020 +0100 Merge branch 'ResultCheck' of git://github.com/rpungartnik/atop into rpungartnik-ResultCheck commit b3356e5c46d52f23d299b885de8095add0008ac2 Merge: 95e2525 68b2a90 Author: Gerlof Langeveld Date: Sat Jan 18 11:41:14 2020 +0100 Merge branch 'master' of github.com:Atoptool/atop commit 95e252560e435f13fe4d8055de7cbdb1b6a164b5 Author: Gerlof Langeveld Date: Sat Jan 18 11:39:31 2020 +0100 Datatype double changed to long for percentage PSI percentage. M showsys.c commit 68b2a9008ef2aa72e4badd1eb2a0107555f404aa Merge: 461ebee 604b563 Author: Gerlof Langeveld Date: Tue Jan 14 10:32:37 2020 +0100 Merge pull request #97 from bytedance/logopts Drop '-R' in default log option commit 604b563a223130d9bcce3d3358537a6c5ce05e7a Author: zhenwei pi Date: Mon Jan 13 10:14:08 2020 +0800 Drop '-R' in default log option '-R' option is implemented by reading '/proc/PID/smaps' or '/proc/PID/smaps_rollup'. During atop reading smaps/smaps_rollup, atop needs hold the task->mm->mmap_sem, reference linux source code, file linux/fs/proc/task_mmu.c static void *m_start(struct seq_file *m, loff_t *ppos) { ... down_read(&mm->mmap_sem); ... } then atop walks the page table of target process and release lock. The target also needs hold the lock when mmap/mumamp/page fault handling. The lock race will hurt the performence of the target process. A typical case is the latency spike of large size memory of redis. Signed-off-by: zhenwei pi M atop.daily M atop.default M atop.service commit 461ebeecd1e5018d9b23ab6bef466232107f6833 Author: Gerlof Langeveld Date: Sat Jan 11 10:38:04 2020 +0100 Correct handling of atopsarflags from atoprc file The default setting for the options of atopsar was not correct (wrong calling parameter was used). Issue solved. M atopsar.c commit e889c66fbe1d0b7ae38fbcbaa46cea749257f486 Author: Roberto Date: Thu Jan 9 08:54:28 2020 -0300 Compilation warnings - String Manipulation Changed the size of path variable using system constant Implemented data size check during message creation to avoid buffer overflow and problems when displaying messages. Now negative numbers are presented as 0(zero) and to big numbers are presented as maximum value(ex.9999). This will avoid problems in case of invalid or unexpected data. This was tested compiling in 32 and 64 bits. M photosyst.c M showsys.c commit cd2c98dfc8f73b3c44c1df0743cf2f43f6ef7ef8 Author: Gerlof Langeveld Date: Thu Jan 9 11:41:17 2020 +0100 Show virtually locked memory per process Per process the value LOCKSZ is maintained to show the amount of memory being locked by the process (virtually). This value is shown for individual processes (key 'm') but also as accumulated value for keys 'p', 'u' and 'j'. M deviate.c M man/atop.1 M parseable.c M photoproc.c M photoproc.h M showgeneric.c M showlinux.c M showlinux.h M showprocs.c commit e0a96a343e1b476cccd32a025d70421bdc754c2f Merge: 0878ae4 4b0cc91 Author: Roberto Date: Mon Jan 6 10:13:58 2020 -0300 Merge pull request #1 from Atoptool/master update commit 4b0cc9133d9e8772a29f8a910ee48615577efd66 Merge: bcbe687 91e0340 Author: Gerlof Langeveld Date: Sat Jan 4 10:53:37 2020 +0100 Merge branch 'master' of github.com:Atoptool/atop commit bcbe68712009663f3fe54214c0fe25dc07e668b5 Merge: 7f4fa0f 5e14308 Author: Gerlof Langeveld Date: Sat Jan 4 10:52:50 2020 +0100 Merge branch 'rpungartnik-CompilationFlags' commit 5e14308568bf90d538fe9a0235df8341fe068d7b Merge: 7f4fa0f 1d22fd8 Author: Gerlof Langeveld Date: Sat Jan 4 10:51:15 2020 +0100 Remove explicit compolations from Makefile (issue #91) commit 91e0340e704b4334df4c9d1e9dda9b0adb7399e8 Merge: 7f4fa0f 95e5d72 Author: Gerlof Langeveld Date: Sat Jan 4 10:34:30 2020 +0100 Merge pull request #90 from rpungartnik/CompilationWarnings Solved some compilation warnings commit 7f4fa0f7fe3a20e0ba0e348529e1f94613d36d99 Merge: 0878ae4 fa101b4 Author: Gerlof Langeveld Date: Sat Jan 4 10:26:09 2020 +0100 Merge branch 'db48x-avio-time-scaling' commit fa101b4dc5d3729d84466f708c29959570bd0919 Author: Gerlof Langeveld Date: Sat Jan 4 10:20:50 2020 +0100 Report disk average I/O in microseconds/nanoseconds (issue #89) Very fast drives might show 0.0 ms as the average I/O time. Therefore, average I/O unit will be adapted to the smallest possible unit. Due to the use of microseconds (abbreviates to a multibyte character), the ncursesw library is used instead of ncurses library (the latter calculates the number of characters in a wrong way). M Makefile M atopsar.c M showgeneric.c M showsys.c commit e47d9f7dbf0ffcd12ce2540097fa2fa87d8ab718 Merge: 0878ae4 c20cbce Author: Gerlof Langeveld Date: Sat Jan 4 09:30:20 2020 +0100 Merge branch 'avio-time-scaling' of git://github.com/db48x/atop into db48x-avio-time-scaling commit 2fd755b83103a2cdda8c4bdd1765e2ac3a75a418 Author: Roberto Date: Fri Jan 3 22:31:07 2020 -0300 Compilation Warning - Unused Result on system calls Newer versions of gcc enforce to check the result of system calls M atopacctd.c M photosyst.c M rawlog.c M various.c commit 95e5d72c5a311c356994ec0f4743095bbf8548cc Author: Roberto Date: Thu Jan 2 21:53:15 2020 -0300 Solved some compilation warnings Resolved warning from unused rcsid variable Resolved "misleading indentation" warnings No changes in behaviour No changes in behaviour M atop.c M atopsar.c M deviate.c M photoproc.c M photosyst.c M showgeneric.c M showlinux.c M showprocs.c M showsys.c commit 1d22fd8f161e8db198f0f44fb3ce498d04f2f21b Author: Roberto Date: Thu Jan 2 20:56:32 2020 -0300 Use CFLAGS and LDFLAGS to all files M Makefile commit 0878ae435d65d2fdcf73a96bd8502fe754ea9af7 Author: Gerlof Langeveld Date: Tue Dec 31 12:44:26 2019 +0100 Show PSI pressure percentage during interval (issue #72) Atop and atopsar show the pressure percentage during the interval as most important percentage now. Atop still shows the average percentages for the last 10, 60 and 300 seconds by widening the window. M atopsar.c M man/atop.1 M man/atopsar.1 M showlinux.c M showlinux.h M showsys.c commit 08c622ecaa5bb0bb260984ceaddc4730d1b312a7 Author: Gerlof Langeveld Date: Tue Dec 24 12:20:02 2019 +0100 Suppress busy percentage for virtual network interface (issue #83) Determine whether or not a network interface is virtual (for which the interface driver usually returns 10 Mbit as speed) by checking the physical address. M ifprop.c commit c20cbce6a3885ce9cba604de829c311909909e97 Author: Daniel Brooks Date: Wed Dec 11 12:45:09 2019 -0800 show disk avio values in microseconds or nanoseconds as appropriate M showsys.c commit 802fb3a235c5fd9aa4e6cdc0fb3c31e2dca8ea4a Merge: e4343d3 1c45948 Author: Gerlof Langeveld Date: Sat Nov 23 12:45:08 2019 +0100 Merge branch 'bytedance-readahead-rawlog' commit 1c459481bbef630f335a7e6e1ec7e60daa94ebee Merge: e4343d3 a34e347 Author: Gerlof Langeveld Date: Sat Nov 23 12:36:53 2019 +0100 Merge branch 'readahead-rawlog' of git://github.com/bytedance/atop into bytedance-readahead-rawlog commit e4343d34b0840212266838267e5370fb559354f4 Merge: 1f21007 20443a3 Author: Gerlof Langeveld Date: Sat Nov 23 12:12:45 2019 +0100 Merge pull request #59 from arielnh56/patch-1 Make LOGPATH overrulable commit 1f210074196daaebd8ac3ceca5e74d9f353d67a7 Author: Gerlof Langeveld Date: Sat Nov 23 11:54:10 2019 +0100 Avoid overriding /etc/default/atop during RPM upgrade The file /etc/default/atop should not be overwritten during an RPM upgrade, so it is marked by %config(noreplace) in the RPM spec (solved issue #86). M rpmspec/atop.specsystemd M rpmspec/atop.specsysv commit 737ae433646fe789cc6f26f18f622e0ee799fe2c Merge: d292f1b dfb9cb9 Author: Gerlof Langeveld Date: Sat Nov 23 11:02:00 2019 +0100 Merge pull request #81 from jubnzv/fix-infiniband photosyst.c: fix possible error in reading InfiniBand states commit d292f1b4f4dcc582f72d8e04d511897c543c6bca Merge: 224ab67 485a453 Author: Gerlof Langeveld Date: Sat Nov 23 10:49:54 2019 +0100 Merge pull request #82 from BlackIkeEagle/defpath-not-created DEFPATH not created so touch fails commit 485a453d2ae5700874a57dffc297030254b5ae7c Author: BlackEagle Date: Mon Nov 4 09:00:54 2019 +0100 DEFPATH not created so touch fails ``` touch /build/atop/pkg/atop/etc/default/atop touch: cannot touch '/build/atop/pkg/atop/etc/default/atop': No such file or directory ``` Add DEFPATH mkdir in genericinstall to make sure the folder `$(DESTDIR)/etc/default` exits Signed-off-by: BlackEagle M Makefile commit 224ab67066810dcb3f80ab40c34bd4ab8d6a2ad7 Author: Gerlof Langeveld Date: Sat Nov 2 12:27:18 2019 +0100 Prepare for release 2.5 M .gitignore M version.h commit e34478820e033a9e581ed601245fef2ae60878b2 Author: Gerlof Langeveld Date: Sat Nov 2 12:26:16 2019 +0100 Add default values for /etc/default/atop file A atop.default M rpmspec/atop.specsystemd commit baf1458e07d37565f7aad469974c87ef0cabc547 Author: Gerlof Langeveld Date: Sat Nov 2 12:25:09 2019 +0100 Add new structs for version 2.5 to atopconvert M atopconvert.c A prev/photoproc_25.h A prev/photosyst_25.h commit 5089600aed667e01846c89fe0bf2c2b4d9b58f02 Author: Gerlof Langeveld Date: Sat Nov 2 12:23:45 2019 +0100 Modify manual dates M man/atop.1 M man/atopacctd.8 M man/atopconvert.1 M man/atopgpud.8 M man/atoprc.5 M man/atopsar.1 commit 6fa0306b8966ac0210bb013de4289951f8499ab8 Merge: a802f1a 43b50d0 Author: Gerlof Langeveld Date: Sat Oct 26 22:32:32 2019 +0200 Merge branch 'timers' commit 43b50d02d413aba226154d3ce8b4721b27b0a06c Author: Gerlof Langeveld Date: Sat Oct 26 22:28:37 2019 +0200 Modify man pages and spec files for added unit files The atop.daily script is not used any more for systemd-based systems. The man page of atop has been changed accordingly. Furthermore, the spec files have been modified to install the new unit files. M Makefile M atop-rotate.service M atop-rotate.timer D atop.cronsystemd M atop.service M man/atop.1 M rpmspec/atop.specsystemd M rpmspec/atop.specsysv commit dfb9cb957eb3464a435908e70d74533558d3aa09 Merge: c1f68ba 0ee181b Author: Georgy Komarov Date: Fri Oct 25 07:41:20 2019 +0300 Merge pull request #1 from codebling/fix-infiniband-cleanup Move `ibc` property init to original location, revert whitespace changes commit 0ee181b084bca95cb98420737bd98a0f8779a6e4 Author: Code Bling Date: Thu Oct 24 16:01:15 2019 -0400 Move `ibc` property init to original location, revert whitespace changes M photosyst.c commit c1f68ba78275ebbc0da113acc97fc56c8f0cfd7f Author: Georgy Komarov Date: Thu Oct 24 14:52:59 2019 +0300 fixup uninitialized memory usage in ibstat M photosyst.c commit f8024a674203cec60ab311879b34292a10abff5d Merge: a802f1a 97d7b82 Author: Gerlof Langeveld Date: Wed Oct 23 21:31:33 2019 +0200 Merge branch 'master' of git://github.com/SjonHortensius/atop into timers commit 791e4f8a9eeb1c1d5617f4696135ed0694366b43 Author: Georgy Komarov Date: Fri Oct 18 23:46:21 2019 +0300 photosyst: fix possible error in reading InfiniBand states Make sure that we read rate file successfully to avoid usage of uninitialized local variables. M photosyst.c commit 97d7b8225d486211a0a02521a406f83780262253 Author: Justin Kromlinger Date: Fri Oct 11 13:37:27 2019 +0200 Read environment file in systemd service, use atop defaults (#1) Read environment file in systemd service, use atop defaults instead of hardcoded M atop.service commit a34e3478e1dabe06877881abd9d808180f4c8058 Author: zhenwei pi Date: Fri Sep 27 17:27:54 2019 +0800 Readahead rawlog to make -b operation faster 1, use posix_fadvise POSIX_FADV_SEQUENTIAL to double kernel readahead buffer. 2, HDD typically has a 100~200 IOPS, atop needs to read raw records more fastly under low IOPS. So use pread syscall to load a large area(default every 4M) in page-cache. Test env: set disk iops as 200 for a virtual machine(KVM). virsh blkdeviotune stretch sdb --write-iops-sec 200 --read-iops-sec 200 --live Test cases: 1, test upstream atop without any page cache ~# vmtouch -e /var/log/atop/atop_20190916 ~# time /root/atop-upstream -r /var/log/atop/atop_20190916 -b 18:00 real 0m54.639s user 0m0.094s sys 0m0.321s 2, test upstream atop with full page cache ~# vmtouch -t /var/log/atop/atop_20190916 ~# time /root/atop-upstream -r /var/log/atop/atop_20190916 -b 18:00 real 0m1.266s user 0m0.004s sys 0m0.021s 3, test new atop without any page cache ~# vmtouch -e /var/log/atop/atop_20190916 ~# time /root/atop-new -r /var/log/atop/atop_20190916 -b 18:00 real 0m3.818s user 0m0.023s sys 0m0.170s case 1 & case 2: speed of reading rawlog effects atop performance a lot. case 1 & case 3: readahead makes performance better. Signed-off-by: zhenwei pi M rawlog.c commit a802f1a68ff0f3f06e6a4c609a87c0679bd07fce Author: Gerlof Langeveld Date: Wed Sep 18 21:43:20 2019 +0200 Allow raw data to be read from a named pipe When reading raw data from a named pipe instead of from a regular file, it is not possible to branch ('b'), reset ('r') or go to a precious sample in an interactive session. With atopsar, only one report can be generated (no combination of various reporting flags). M atop.h M atopsar.c M man/atop.1 M rawlog.c commit db74804d15e98af37ed62bfcad95a93959c9d277 Merge: b10ee34 78f99bb Author: Gerlof Langeveld Date: Wed Sep 18 12:02:31 2019 +0200 Merge pull request #69 from gleventhal/fix-lseek-segfault-on-nonseekable-piped-rawlog Check that rawlog is seekable to prevent later segfaults commit b10ee3492ee232a600aa934a724c4c687c691e79 Author: Gerlof Langeveld Date: Wed Sep 18 11:46:58 2019 +0200 Improve determination of container ID Many processes might use the cpuset cgroup without being a container. The length of the basename is verified to be at least 64 characters (length of SHA256 identifying a container). M photoproc.c commit fea96f0690f6694624d85b9cb4f216fafea09b79 Author: Gerlof Langeveld Date: Mon Sep 16 21:02:23 2019 +0200 Correction to avoid loosing first position of CID. M photoproc.c commit 3fe8f59d71cb8cd70d0fa8d4fd77f232be2922d1 Merge: 3e0b68c a3db75e Author: Gerlof Langeveld Date: Mon Sep 16 20:57:26 2019 +0200 Merge branch 'bytedance-fix-docker-container-k8s' commit a3db75e174efbe95518f5a18fe89b9aa8154e177 Author: Gerlof Langeveld Date: Mon Sep 16 20:55:04 2019 +0200 Proposed solution for K8s containers modified. Docker containers on RedHat/CentOS based systems have a prefix 'docker-' behind the last '/' that should be skipped. M photoproc.c commit 82f2fc889419555a9a1df868e6d1133c0b0169cc Author: zhenwei pi Date: Fri May 24 19:54:42 2019 +0800 Fix container compatibility for docker created by k8s Cpuset of a docker created by k8s looks like this: /kubepods/burstable/pod07dbb922-[SNAP]/223dc5e15b[SNAP] Instead of checking prefix "docker", we usually get 12 char from last '/'. Test for docker and k8s, both of them work well. 'CID' row shows the same string as the `docker ps`. Test result for k8s: ~# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES e978af4928f6 hub.byted.org/google_containers/pause-amd64:3.0 "/pause" 39 hours ago Up 39 hours k8s_POD_dp-9fe0dd7536-55fd89b474-[SNAP] ~# atop -j NPROCS SYSCPU USRCPU VSIZE RSIZE PSIZE SWAPSZ RDDSK WRDSK RNET SNET CPU CID 1/1 557 1h42m 2h57m 29.6G 1.3G 1.1G 0K 1.9G 19.0G 0 0 11% host-------- 1 0.00s 0.00s 1028K 4K 109K 0K 0K 0K 0 0 0% e978af4928f6 Signed-off-by: zhenwei pi M photoproc.c commit 3e0b68cb9858a16749c98c3d399d2373e239caa6 Merge: 1b50456 086366f Author: Gerlof Langeveld Date: Sat Aug 10 12:20:13 2019 +0200 Merge branch 'bytedance-perfevent' By default, suppress gathering of 'perf' counters on VM guests. commit 086366f9e008c711d51e8400ae9d54479224fae0 Author: Gerlof Langeveld Date: Sat Aug 10 12:18:46 2019 +0200 Adapt documentation to new keyword 'perfevents' in atoprc. M man/atop.1 M man/atoprc.5 M photosyst.c commit ea9e1814e255922dfedbb428bea48d2800f681f3 Merge: 1b50456 16abcac Author: Gerlof Langeveld Date: Sat Aug 10 10:34:48 2019 +0200 Merge branch 'perfevent' of git://github.com/bytedance/atop into bytedance-perfevent Avoid overhead using perf in VMs. commit 1b504568a4562ce6fca93275898e084a8f832d29 Author: Gerlof Langeveld Date: Sat Aug 3 12:47:11 2019 +0200 Error messages no longer wiped by clear screen By introducing function mcleanstop, error messages are shown after ncurses has closed the window (solves issue #67). M acctproc.c M atop.c M atop.h M atopsar.c M deviate.c M netatopif.c M photoproc.c M photosyst.c M rawlog.c M showlinux.c M various.c commit 1e93ac6a8e486dfda633af049c950040020373c9 Author: Gerlof Langeveld Date: Sat Aug 3 09:52:18 2019 +0200 Exchanged a TAB by spaces in front of statement (TAB was accepted by Python2 but refused by Python3). M atopgpud M man/atopgpud.8 commit 78f99bbfb6d54a420c404fd8c37dc4f48b7f8110 Author: gregg leventhal Date: Mon Jun 24 16:25:58 2019 -0400 Check if rawlog is a regular, seekable file. If the user does atop -r <(gunzip atop_2019_06_10.gz) for example, a failing lseek will result in a later segfault M rawlog.c commit 5c9c88ad15a863a2194abf1b3b8ce44f832df4d9 Author: Yaroslav K Date: Fri Jun 14 16:31:57 2019 +0300 Fixed rounding problems in the average-per-second mode. problem example: WRDSK,RDDSK sometimes displayed as 0.0G/s 0.0M/s in the average-per-second mode. solution: - more accurate rounding (llround) when calculating values for the 1/s intervals, and while converting to KB MB GB .. - values<=9 are displayed as before as float ex.:1.1M/s,3.3G/s - values 10-999 are display as int (llround) ex.:123M/s 999M/s M atop.h M showprocs.c M various.c commit 16abcac132eec4755373aa673389e67219488844 Author: zhenwei pi Date: Wed Mar 20 14:58:18 2019 +0800 Auto detect hypervisor and apply to perfevents In virtualization case, PMU in guest is emulated by hypervisor, such as vPMU is emulated by KVM. Currently perf in guest has overhead, typically rdpmc/wrmsr cause a lot of vm-exits, and guest will hit a performance drop. atop should be careful while useing perf. Support "perfevents" config in atoprc, which supports 3 modes: "enable": force using perf. "disable": force disable perf. "auto": default mode, atop will detect hypervisor. if atop is running in guest, auto disable perf, otherwise enable perf. Signed-off-by: zhenwei pi M atop.c M photosyst.c commit 7ebca1362e7158c769770006afd61b7e4ec67211 Author: Gerlof Langeveld Date: Fri Jun 7 16:06:30 2019 +0200 Cosmetic changes. M rawlog.c commit b55f28a740258e33206443612be2d4fb80464d96 Merge: a4e3664 25007e9 Author: Gerlof Langeveld Date: Fri Jun 7 15:43:31 2019 +0200 Merge pull request #60 from bytedance/writev-record Use writev to write record data commit 25007e9d927f8d37a9f8659d5d2122435409ee7d Author: zhenwei pi Date: Tue Mar 19 16:18:15 2019 +0800 Use writev to write record data Currently atop writes 3 parts of one raw record by 3 write syscall. atop may be killed during writing record, and continue to append records after the original log. uncompleted record data is generated. Ex, good case : ... rr,scompbuf,pcompbuf ... rr,scompbuf,pcompbuf ... bad case : ... rr,scompbuf[missing] ... rr,scompbuf,pcompbuf ... 1-writev syscall makes writing record as a atomic operation, and make sure each raw record is completed. Signed-off-by: zhenwei pi M rawlog.c commit a4e3664f78d8588136696102bb9624b2ec46a54d Author: Gerlof Langeveld Date: Sun May 26 16:55:01 2019 +0200 Added comment (cosmetic change) M photoproc.c commit 1338f5f47cc4e11b7afe8f6ce5543126754ae416 Merge: b54801c eea28ab Author: Gerlof Langeveld Date: Sun May 26 16:49:16 2019 +0200 Merge pull request #54 from pacepi/use-smaps-rollup Prefer to use smaps_rollup instead of smaps commit b54801ca81c06b073126f7f941db1d922a2f1e1c Merge: fa4db43 e7000f7 Author: Gerlof Langeveld Date: Sun May 26 16:28:08 2019 +0200 Merge pull request #57 from gleventhal/master Fix unchecked opendir to prevent dereferencing a NULL pointer commit 20443a367d225a466aeee77cdf549fb4bd615d87 Author: Alastair Young Date: Fri May 24 16:53:15 2019 -0700 Make LOGPATH overrulable Moved LOGPATH declaration to before DEFAULTSFILE source, so it can be overruled M atop.daily commit e7000f778465cf4e73a60604b799a4b1c0e80b15 Author: gregg leventhal Date: Thu May 2 15:03:02 2019 -0400 Fix unchecked opendir to prevent dereferrencing a NULL pointer M photoproc.c commit 65fb301d59208692da4fec1ead38e8efbed66c33 Author: Sjon Hortensius Date: Mon Mar 18 13:44:23 2019 +0100 For systemd - replace 5 files with rotate service and timer M Makefile A atop-rotate.service A atop-rotate.timer M atop.service commit eea28abb735cbf941045ade56d2c22657bf014b7 Author: zhenwei pi Date: Mon Jan 28 20:27:47 2019 +0800 Prefer to use smaps_rollup instead of smaps Since Linux-4.14, kernel supports "/proc/PID/smaps_rollup" to count process Pss. Test case on CPU Intel i7-8700K : 1, create a virtual machine with 12G memory. 2, count Pss for 3 time with smaps : 7176, 7221, 7182. average 7193(microseconds). 3, count Pss for 3 time with smaps_rollup : 1963, 1985, 1972. average 1973(microseconds). smaps_rollup has better performence than smaps. We check it in first call, and prefer to use. Signed-off-by: zhenwei pi M photoproc.c commit ffc8ba8d324243a923abe48e9758adecb03d24a4 Author: Peter Kjellerstedt Date: Tue Feb 12 21:25:23 2019 +0100 atop.daily, atop.init, atop-pm.sh, mkdate: Avoid using bash Avoid using bash and bashisms when not necesary. On some systems, e.g., embedded products, bash may not be available by default. Signed-off-by: Peter Kjellerstedt M atop-pm.sh M atop.daily M atop.init M mkdate commit fa4db436865887f3e451692b9439d2943b4b2936 Author: Gerlof Langeveld Date: Thu Feb 7 12:03:42 2019 +0100 Correction of PSI value 'io full' 'I/O full' showed the same value as 'I/O some', which has been corrected now. M showsys.c commit 58a25af78314d432a7591e536fd2d5b23666ecb4 Author: Gerlof Langeveld Date: Tue Jan 22 17:32:37 2019 +0100 Atopconvert shows version of input file and copies if needed. When only the name of the input file is given, then atopconvert shows the version of the input file. When the version of the input file is already up-to-date, still the input file is copied to the output file. M atopconvert.c M man/atopconvert.1 commit c3073ee609509857620c58aa4d050344b63696a2 Merge: 41333c6 4b0a957 Author: Gerlof Langeveld Date: Sun Jan 13 00:04:29 2019 +0100 Merge branch 'master' of github.com:Atoptool/atop commit 41333c6dab5c16b60d610bfa79f33f1f56793307 Author: Gerlof Langeveld Date: Sat Jan 12 23:43:59 2019 +0100 Various corrections. M rpmspec/atop.specsystemd commit 3ca52a7c0c9948eedd6ba25a1a902ebc6488a79f Author: Gerlof Langeveld Date: Sat Jan 12 22:31:10 2019 +0100 Prepare for version 2.4 M README.md M version.h commit 1929641c525c259bbc2563fa979ecb97063b219e Author: Gerlof Langeveld Date: Sat Jan 12 20:03:41 2019 +0100 Support for conversion of raw files to atop 2.4 M atopconvert.c M man/atopconvert.1 M photoproc.h M photosyst.h A prev/photoproc_24.h A prev/photosyst_24.h commit 46000ef14ff2cbd755d4e1b620e68218ea55f81f Author: Gerlof Langeveld Date: Sat Jan 12 13:13:37 2019 +0100 Introduction of Pressure Stall Information (PSI). The average percentages of CPU, memory and I/O are reported, as provided by kernel 4.20 onwards. M atopsar.c M deviate.c M man/atop.1 M man/atopsar.1 M parseable.c M photosyst.c M photosyst.h M showlinux.c M showlinux.h M showsys.c commit 00ffc2f338ee038e3775846b7471228935afcf54 Author: Gerlof Langeveld Date: Thu Jan 10 18:56:52 2019 +0100 Introduction of Infiniband statistics Maintain stats about bandwidth utilization of Infiniband ports, with atopsar (-h) and in atop system statistics. M atop.c M atop.h M atopsar.c M deviate.c M gpucom.c M man/atop.1 M man/atopconvert.1 M man/atopgpud.8 M man/atoprc.5 M man/atopsar.1 M parseable.c M photosyst.c M photosyst.h M showgeneric.c M showgeneric.h M showlinux.c M showlinux.h M showsys.c commit 2f71beced5f061cb1b0b783e0ede3c85e8e54b35 Author: Gerlof Langeveld Date: Tue Jan 8 20:12:00 2019 +0100 Introduction of GPU statistics A separate daemon 'atopgpud' has been developed to maintain statistics of Nvidia GPUs in the system. When this daemon is running, atop will connect to this daemon via a TCP socket. With every intreval, atop gets GPU statistics from the daemon. M Makefile M atop.c M atop.h A atopgpu.service A atopgpud M atopsar.c M deviate.c A gpucom.c A gpucom.h A man/atopgpud.8 M parseable.c M photoproc.c M photoproc.h M photosyst.h M rawlog.c M rpmspec/atop.specsystemd M showgeneric.c M showgeneric.h M showlinux.c M showlinux.h M showprocs.c M showsys.c commit 4b0a957aed6370c4268d592fa6bef78aa141fe82 Author: Gerlof Langeveld Date: Tue Jan 8 20:06:15 2019 +0100 Not enough entries created to register all tasks. Calculation has been adapted. M atop.c M photoproc.c commit 8fe68ff3ed14bd25da93ba0480ec116a7f36b731 Author: Gerlof Langeveld Date: Thu Jan 3 13:30:42 2019 +0100 Performance improvement when gathering scaling info of CPUs Retrieving the frequency scaling information for every single CPU on a system with lots of CPUs is extremely time consuming. Therefore, frequency scaling info is only gathered on systems with 8 CPUs or less. M photosyst.c commit 4297811aa2815b247e320a00236f0650e449c328 Author: Gerlof Langeveld Date: Thu Jan 3 13:26:07 2019 +0100 Performance improvement for gathering process/thread stats Gathering the process and threads statistics was too time consuming, especially for the first interval. This was extremely noticeable on systems with a lot of multi-threaded processes. M atop.c M photoproc.c M photoproc.h commit a11bf6746c7998eee7ca4b9204b536062948f91c Merge: 725dc2c 0e43049 Author: Gerlof Langeveld Date: Sat Dec 8 14:29:47 2018 +0100 Merge pull request #43 from vinc17fr/master Fixed va_list usage. commit 725dc2cb0b2785a4fa77a98a5a204c977c2a6b53 Author: Gerlof Langeveld Date: Sat Dec 8 14:25:06 2018 +0100 Removed debugging statements M Makefile commit d858714ffb0e11ea88aa582d16bb2c948a3e2fce Author: Gerlof Langeveld Date: Sat Dec 8 14:24:21 2018 +0100 Removed debugging statements M acctproc.c M atop.c commit 1a2fbeaa823f102e988a74123b660598f143d353 Author: Gerlof Langeveld Date: Thu Dec 6 21:48:49 2018 +0100 Only lock atop in memory when the hard and soft limits could be raised. M atop.c commit 0e43049493e65b412e1c0def62a09ceac2bb3110 Author: Vincent Lefevre Date: Tue Dec 4 14:17:46 2018 +0100 Fixed va_list usage. M atopconvert.c M various.c commit b5e7e1cad051e909824ddc12cd82b6e8269d114e Author: Gerlof Langeveld Date: Sun Dec 2 07:49:58 2018 +0100 Added additional debug statements. M acctproc.c commit 7051f61691e1be8bf06a1301dda21236d60e9dc1 Author: Gerlof Langeveld Date: Sat Dec 1 20:21:48 2018 +0100 Consistent type usage for number of processes present and terminated M Makefile M acctproc.c M acctproc.h M atop.c M deviate.c M photoproc.c M photoproc.h commit e163fda6396b854adfbe027380444d0402e315e9 Merge: 2fd27e4 bae7b24 Author: Gerlof Langeveld Date: Sat Dec 1 18:44:16 2018 +0100 Merge pull request #42 from vinc17fr/master Fixed type mismatch in format string. commit bae7b244b87b1af4dc0db4bc3a59532cb88cab5d Author: Vincent Lefevre Date: Mon Nov 19 14:22:16 2018 +0100 Fixed type mismatch in format string. M deviate.c commit e9a2c492f250700ed352a650d255b60deea5c18f Author: Vincent Lefevre Date: Mon Nov 19 14:04:01 2018 +0100 Fixed type mismatch in format string. M atop.c commit 2fd27e47d70920c4c61a54c311c229e11a93c9d6 Author: Gerlof Langeveld Date: Tue Oct 9 14:00:25 2018 +0200 Removed rcsid from source files. M acctproc.c M atop.c M atopsar.c M deviate.c M photoproc.c M photosyst.c M procdbase.c M showgeneric.c M showlinux.c M showprocs.c M showsys.c M various.c commit 7f48aa0294bd9be1d25e930fd6269aa78394a74f Author: Gerlof Langeveld Date: Tue Oct 9 13:57:13 2018 +0200 Added atopsar to .gitignore M .gitignore commit 7f75dcaacdc341d9b3c191b47dc4f8bcc120bf3b Author: Gerlof Langeveld Date: Tue Oct 9 13:54:35 2018 +0200 Removed .gitignore from .gitignore A .gitignore commit 2f83c2fc572162ef36659f7715a2d3527b24ef8f Merge: 6fabd47 2c0a960 Author: Gerlof Langeveld Date: Tue Oct 9 13:29:55 2018 +0200 Merge branch 'master' of github.com:Atoptool/atop commit 6fabd477c3d766a294da5fd70cf984ab58b9b9f8 Author: Gerlof Langeveld Date: Tue Oct 9 13:25:26 2018 +0200 Error message and other exit code for chdir/getcwd Previously only exit code 53 is given in case of a failing getcwd or chdir call, without further message. Now an error message is given combined with exit code 53 (current directory can not be saved), 54 (can not switch to /proc) or 55 (can not switch back to former current directory). M photoproc.c M photosyst.c commit 2c0a9606b1596a50489d4deef124d0b349ccbfbb Merge: 9f705ab 414127c Author: Gerlof Langeveld Date: Tue Oct 9 12:41:00 2018 +0200 Merge pull request #35 from SjonHortensius/patch-1 Include sysmacros.h to compile with newer gcc commit 9f705abee3cc757f0ca5403c71bad8c2b2c532b2 Author: Gerlof Langeveld Date: Tue Oct 9 12:39:25 2018 +0200 Removed MAJOR and MINOR macro's for better solution via include M photosyst.c commit 4742978c79d8faf5b519487e11fc25164cb3cb33 Author: Gerlof Langeveld Date: Sat Sep 22 12:10:34 2018 +0200 Document the behavior of signals SIGUSR1 and SIGUSR2 M man/atop.1 commit 7bf30c52a37118ff4f211e67e1736099f3d1dcb3 Author: Gerlof Langeveld Date: Sat Sep 22 11:50:50 2018 +0200 Own macro's added to determine major/minor of logical volume. The standard 'minor' and 'major' macro's are not available any more in gcc8. M photosyst.c commit 20375c1a7a3622ba2fe776e146c1a2dfc6f17fa2 Author: Gerlof Langeveld Date: Sat Sep 15 10:53:37 2018 +0200 Wrong signal number as exit code in parseable output The exit code (or signal number) in the output of the option -PPRG was wrong. Now the value represents the exit code if it is less than 256 and it represents a fatal signal number if the code is larger then 256 (then subtract 256 for the signal number). M man/atop.1 M parseable.c commit 414127c03669b4eedc85778a7bff80cf601311d8 Author: SjonHortensius Date: Fri Aug 24 18:26:58 2018 +0200 Include sysmacros.h to compile with newer gcc Older gcc throws a warning ``` photosyst.c: In function 'lvmmapname': photosyst.c:1465:13: warning: In the GNU C Library, "major" is defined by . For historical compatibility, it is currently defined by as well, but we plan to remove this soon. To use "major", include directly. If you did not intend to use a system-defined macro "major", you should undefine it after including . dmp->major = major(statbuf.st_rdev); ``` Newer gcc throws an error: ``` photosyst.c: In function ‘lvmmapname’: photosyst.c:1482:19: error: called object ‘major’ is not a function or function pointer dmp->major = major(statbuf.st_rdev); ^~~~~ photosyst.c:1437:25: note: declared here lvmmapname(unsigned int major, unsigned int minor, ~~~~~~~~~~~~~^~~~~ ``` M photosyst.c commit 8d2d68c0dc26d457871e120a57b71be91c6dcbd7 Author: Gerlof Langeveld Date: Sun Aug 5 19:34:47 2018 +0200 Added symlink atop.spec A atop.spec commit a891eddaf4d1fca4895dfb296b6092980c147aef Author: Gerlof Langeveld Date: Sun Aug 5 19:31:32 2018 +0200 Cosmetic changes to new tool atopconvert M atopconvert.c commit 21598140df014b38b1c57cdc9303386cbd869f62 Author: Gerlof Langeveld Date: Sun Aug 5 15:58:40 2018 +0200 Finalize the modification to introduce atopconvert M Makefile M atopconvert.c commit 335922298a6b9e788f7b0b8d7582f8ef6878636e Author: Gerlof Langeveld Date: Sun Aug 5 15:48:24 2018 +0200 Modified RPM spec files to install new tool atopconvert M rpmspec/atop.specsystemd M rpmspec/atop.specsysv commit acc29f246900234181abe5641ae67cecf6999dc5 Author: Gerlof Langeveld Date: Sun Aug 5 15:33:31 2018 +0200 Introduce new tool atopconvert to convert raw files The command atopconvert expect the name of an existing raw file as first argument and the name of an output raw file as second argument. The input file will be converted to the output file, which will be by default the raw format of the newest atop version. Raw input raw files created by atop 2.0 and higher are supported. M Makefile A atopconvert.c M man/atop.1 A man/atopconvert.1 A prev/photoproc_20.h A prev/photoproc_21.h A prev/photoproc_22.h A prev/photoproc_23.h A prev/photosyst_20.h A prev/photosyst_21.h A prev/photosyst_22.h A prev/photosyst_23.h commit 25db7dfbecc258757a83c31921e60e295bd48840 Author: Gerlof Langeveld Date: Thu Jul 12 15:04:42 2018 +0200 Adapt man pages to introduce new program atopconvert M man/atop.1 M man/atopsar.1 commit 07aad3dbfc8458eb23213ecaf02ddeaf34b55a35 Author: Gerlof Langeveld Date: Wed Jul 11 09:54:13 2018 +0200 Value of '#proc' should not include exited processes M showsys.c commit 4a104c7377c2f0f694d75b0936f2722bab652e46 Merge: c10ed57 3a8871b Author: Gerlof Langeveld Date: Sat Jun 9 13:40:34 2018 +0200 Merge pull request #21 from Zugschlus/makefile make sure that all rm operations are -f to allow consecutive cleans commit c10ed57346e359dafe80cda8b82ab7bf829fec1b Author: Gerlof Langeveld Date: Sat Jun 9 13:11:51 2018 +0200 Modified URL of atop website M rpmspec/atop.specsystemd M rpmspec/atop.specsysv commit 68180d5e88d4aec665582ea3d9485a89ba29ec67 Author: Gerlof Langeveld Date: Sat Jun 9 13:11:30 2018 +0200 Added depencies to build and run atop M README commit bb23382c17ce228fce315d4c398f29e80dbd34b0 Author: Gerlof Langeveld Date: Sat Jun 9 12:09:55 2018 +0200 Adapt URL in man pages (from http to https) M man/atopacctd.8 M man/atoprc.5 M man/atopsar.1 commit 96287919c311a17ff3c5e3d4b5ebb4507a8ddadf Author: Gerlof Langeveld Date: Sat Jun 9 12:06:49 2018 +0200 Modify default values in atop.daily script Add possibility to override all necessary default values in the atop.daily script, i.e. variable LOGOPTS, LOGINTERVAL and LOGGENERATIONS. M atop.daily M man/atop.1 commit dad4dab702a75b95c8276dbfe199a0d7018f8821 Merge: 2bb5204 a88a541 Author: Gerlof Langeveld Date: Sat Jun 9 10:57:11 2018 +0200 Merge branch 'configurable-options' of git://github.com/Zugschlus/atop commit 2bb5204b93d7649608b2a6525fae3c5e2e71a1e4 Merge: 4516bd3 9d3011d Author: Gerlof Langeveld Date: Sat Jun 9 10:27:53 2018 +0200 Merge pull request #23 from natoscott/master Fix string malloc length in init_proc_prints, check result commit a88a54107142d057e36be15285c972031a0e8c0a Author: Marc Haber Date: Tue Apr 24 11:42:53 2018 +0200 make atop options configurable, introduce config file M atop.daily commit 4516bd3300e7865f8ec1bb69ba80d42928487633 Author: Gerlof Langeveld Date: Mon Apr 23 13:46:23 2018 +0200 Additional rule to make versdate.h file. M Makefile commit 9d3011d6b81bd7f2fa9e07892a2c5f24ee7f4449 Author: Nathan Scott Date: Fri Jan 19 15:40:47 2018 +1100 Fix string malloc length in init_proc_prints, check result M showlinux.c commit 3a8871b1ba108d6ccf46246625983d3f830e998b Author: Marc Haber Date: Sat Dec 30 16:24:42 2017 +0100 make sure that all rm operations are -f to allow consecutive cleans M Makefile commit 2d615475d922ae611f8cc00cdca79ed32bd3ff33 Author: Gerlof Langeveld Date: Sat Dec 16 11:59:31 2017 +0100 Recognize if current system supports PMC event counters. M man/atop.1 M parseable.c M photosyst.c M showsys.c commit db7a23d193415fef62f889f285687910d675b11f Author: Gerlof Langeveld Date: Wed Nov 8 21:26:11 2017 +0100 Remove versdate.h D versdate.h commit de8179c9b483950b633753365a9889f9d4d0d149 Author: Gerlof Langeveld Date: Wed Nov 8 21:25:28 2017 +0100 Make perf_event counter for IPC optional at compile time M Makefile M showsys.c commit 1a9c8eb7acac64fde33d72c036a3a487cb51baf3 Author: Gerlof Langeveld Date: Thu Oct 5 11:19:02 2017 +0200 Add SCHED_DEADLINE as scheduling policy possibility. M showprocs.c commit 6dd88897ba984e67a526e0a4278f7bc4d023bae1 Author: Gerlof Langeveld Date: Sat Sep 23 11:20:57 2017 +0200 Minor modifications in description of NFS output line. M man/atop.1 commit ba10627812c347460f650c877c3f3eaee194571e Author: Gerlof Langeveld Date: Fri Jun 30 13:13:22 2017 +0200 Include file versdate.h hould be ignored. M versdate.h commit f2f6717a9c633668f3ce3de4ebf8e521bd6bc01f Author: Gerlof Langeveld Date: Fri Jun 30 12:53:56 2017 +0200 Introduce CPU instructions per cycle For every CPU, the average number of instructions per CPU cycle is shown as 'ipc'. Besides, a column is shown with the number of cycles executed per second as `cycl` (shown as effective Hz frequency). 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 M various.c commit 33d561dcc766ca27cb7d1e09a9b0f99c3d98e124 Author: Gerlof Langeveld Date: Fri Jun 30 12:53:08 2017 +0200 Variable date of last build. M versdate.h commit 5f6813c11b58e307b9f2b8d0eb66a5c68ec613de Author: Gerlof Langeveld Date: Wed Jun 28 21:21:00 2017 +0200 Modify the date string for every new version of atop/atopacctd M Makefile M atopacctd.c A mkdate A versdate.h M version.c M version.h commit 5bcf22b32a2b2bc83802a48ee05b3451066f1fbc Author: Gerlof Langeveld Date: Sat Sep 23 12:49:21 2017 +0200 Recognize nbd devices M photosyst.c commit 4df0bcf37f8ef698afad4a528c2665b427c8e74d Author: Gerlof Langeveld Date: Sat Sep 23 12:38:48 2017 +0200 atop.init: execute script with bash instead of sh M atop.init commit 06ec90d6fc3c7d608b03a3f3d000e97a9a847955 Merge: 0a34810 7cac234 Author: Gerlof Langeveld Date: Sat Sep 23 11:37:52 2017 +0200 Merge pull request #5 from roadrunner2/add-nvme-support Recognize nvme disks as such too. commit 0a34810d1ce704311b62adaf97d30a2f02c94bd3 Merge: d99c19f 6bb8d2d Author: Gerlof Langeveld Date: Sat Sep 23 11:22:54 2017 +0200 Merge pull request #3 from sylmarch/sylmarch-patch-nfm-parseable-output Fix bytestotwrite in NFM parseable output commit d99c19f2e64df15ebd54a7934a39a10bd8bb9ef0 Merge: 2b1ad6a 1acea75 Author: Gerlof Langeveld Date: Sat Sep 23 10:40:19 2017 +0200 Merge pull request #1 from sylmarch/sylmarch-patch-nfs-parseable-output Fix rpcread / rpcwrite in NFS parseable output commit 7cac2341ba7e2419267a7e9ee5e5871de744bedb Author: Ronald Tschalär Date: Tue May 16 18:49:14 2017 -0700 Recognize nvme disks as such too. The pattern is based on name assignment in nvme_alloc_ns() in the linux kernel in drivers/nvme/host/core.c . M photosyst.c commit 6bb8d2d1fc53a68c3b68c998feabddf1efde033e Author: Sylvain Date: Fri Apr 21 16:09:31 2017 +0200 Fix bytestotwrite in NFM parseable output Fix NFS total number of bytes physically written to the server in NFM parseable output M parseable.c commit 1acea756e9863d52f973907ac06abab333c7235f Author: Sylvain Date: Fri Apr 21 15:40:40 2017 +0200 Fix rpcread / rpcwrite in NFS parseable output Fix NFS read/write RPC calls counters in parseable output M parseable.c commit 2b1ad6a78db3f52aec17cf675f1edacefa46a2db Author: Gerlof Langeveld Date: Sat Mar 25 15:09:24 2017 +0100 Highlights added. M README.md commit 42e86fcc42ce60f8c92f3c7d5f3a6ccde47c0b33 Author: Gerlof Langeveld Date: Sat Mar 25 14:36:13 2017 +0100 Preparation for github M Makefile A version.h commit 2d2cf7e29fdc7ef66f005ed491477a8551c8f554 Author: Gerlof Langeveld Date: Sat Mar 25 14:11:32 2017 +0100 Markdown text addded A README.md commit a8d850d06efc8d70a19f55ec93fe83df51e99077 Author: Gerlof Langeveld Date: Sat Mar 25 13:06:05 2017 +0100 Preparation for github M Makefile A version.c commit 6dac6bc7dc2adf27a3414955b8791db75c6a7397 Author: Gerlof Langeveld Date: Sat Mar 25 12:46:25 2017 +0100 Preparation to move atop to github D mkdistr D mkversion commit 5f101e656a24271726d1e9cd672631b6033c36c1 Author: Gerlof Langeveld Date: Sat Mar 25 12:40:23 2017 +0100 Preparations to move atop to github T netatop.h T netatopd.h R100 atop.specsystemd rpmspec/atop.specsystemd R100 atop.specsysv rpmspec/atop.specsysv commit 8ce799fdd3bb50978c00735cc72bbeb2c70d6844 Author: Gerlof Langeveld Date: Fri Mar 24 08:54:05 2017 +0100 Adapt man page for showing full process list for memory sorting M man/atop.1 commit 3105ce8d46562cb48081cb92eaee0afbda05166c Author: Gerlof Langeveld Date: Fri Mar 24 08:35:17 2017 +0100 Show full process list when sorting on memory is active M showgeneric.c commit 35869466fc30f318ac404454c322bad039033251 Author: Gerlof Langeveld Date: Fri Mar 17 16:32:30 2017 +0100 Solved segmentation fault Segmentation fault occurred in case of a selection (e.g. on user) in combination with switching from active processes to all processes. M atop.h M photoproc.c M rawlog.c M showgeneric.c M showprocs.c commit eaeb7c500820cccc7bf25606a86c4843cd70ad31 Author: Gerlof Langeveld Date: Fri Mar 17 11:25:46 2017 +0100 Type in man page M man/atop.1 commit 630d66700ca38ddda24d1797636d498e1e600866 Author: Gerlof Langeveld Date: Thu Mar 9 08:30:57 2017 +0100 Corrected accumulated disk writes When accumulating disk writes for programs, users or containers, values were accumulated twice per process. M showgeneric.c commit 989185c62c3a023e80bb9b2a5bb2e5563cda1651 Author: Gerlof Langeveld Date: Tue Mar 7 14:10:33 2017 +0100 Determine containerid per process generically The containerid of a process is determined via the file /proc/PID/cpuset that sometimes contains ".../docker-CONTAINERID" (on e.g. Redhat/CentOS) and sometimes "/docker/CONTAINERID" (e.g. Ubuntu). Both layouts should be supported. M photoproc.c commit 6678463533947894e06b178c84213c17a5faae98 Author: Gerlof Langeveld Date: Tue Mar 7 10:23:21 2017 +0100 Code optimizations releated to Docker containers M photoproc.c M showlinux.c commit fae417d905f6425035df0d3a4c99ddfa61b54e60 Author: Gerlof Langeveld Date: Mon Mar 6 19:20:37 2017 +0100 Smaal correction on the man page description of CID. M man/atop.1 commit ef95f26bdad331b441fbeae2148b0352889256fd Author: Gerlof Langeveld Date: Mon Mar 6 15:51:47 2017 +0100 Introduce Docker container statistics Maintain the Docker container id (12 positions) per process and per thread. This container id will be shown in various lists as CID, but only if at least one Docker container is present. Furthermore, it is possible to select all processes and threads that are related to a specific container id (keystroke 'J') and to accumulate the resource consumption for all processes/threads per container (keystroke 'j'). M atop.h M man/atop.1 M parseable.c M photoproc.c M photoproc.h M showgeneric.c M showgeneric.h M showlinux.c M showlinux.h M showprocs.c commit 0dc5a116cdbe8b1ccd6a0c3784e23a0e7e7abe41 Author: Gerlof Langeveld Date: Fri Feb 24 15:55:28 2017 +0100 Solved segmentation fault in case of threadview Sometimes a process is considered to be inactive while one of its threads appears to be active. This situation could lead to a segmentation fault in case of threadview of only active tasks. It has been solved by marking the inactive process as active when later on a thread is discovered that appears to be active. M deviate.c commit b4811a24801d8c35b64ab3c1b919418e934bb957 Author: Gerlof Langeveld Date: Fri Feb 24 13:16:06 2017 +0100 Determine maximum number of digits in PID/TID Since the maximum PID number may vary (/proc/sys/kernel/pid_max), the width of PID/PPID/TID/... columns should be determined dynamically. M showlinux.c M showprocs.c commit eb30f1d229cb5ac2c4b3ffe639c1efc8cbf70b72 Author: Gerlof Langeveld Date: Fri Feb 24 09:58:40 2017 +0100 Add dependency between version.c and version.h M Makefile commit fc0a5d5f15d5ffcf2203f95d290ce4e046ad1840 Merge: f498827 0aa208a Author: Gerlof Langeveld Date: Thu Feb 23 13:57:46 2017 +0100 Polling mode when NETLINK refuses with error EINVAL Due to kernel bug 190711, the NETLINK interface gives EINVAL when it is activated. Since atopacctd misses a trigger then to read the process accounting records when processes in the system terminate, a polling loop has to be used to verify the existance of new process accounting records. commit f49882721e5dbb065038ba74ae44b85457f50645 Merge: 17b2911 51d6285 Author: Gerlof Langeveld Date: Thu Feb 23 13:53:53 2017 +0100 Retry activation of process accounting after failure According to kernel bug 190271 (process accounting sometimes does not work), the kernel sometimes returns SUCCESS to switching on process accounting while it does not work at all. commit 17b2911b172eaeafe9f0b8e0bd7e721d1766b81e Merge: e402891 e743f1b Author: Gerlof Langeveld Date: Thu Feb 23 13:52:43 2017 +0100 Modified version numbering (without dashes) commit e4028912e77cb1b092cae20173b4c3dea34971fe Author: Gerlof Langeveld Date: Thu Feb 23 13:34:09 2017 +0100 Modify the date in the man pages M man/atop.1 M man/atopacctd.8 M man/atoprc.5 M man/atopsar.1 commit eb10042167d1d3b72049619c36deeaf7f272d4fe Merge: c873d6c c8e2e3f Author: Gerlof Langeveld Date: Thu Feb 23 13:29:45 2017 +0100 Improved error handling on the NETLINK interface Properly check the error values that are returned via the field nlmsg_type. commit c873d6c1eddcbfb3bd84a350684d7d7163aae377 Merge: 5ad6f1d 2b6aab6 Author: Gerlof Langeveld Date: Thu Feb 23 13:27:49 2017 +0100 Also show xvd... disk drives without a digit at the end of the name commit 5ad6f1dffae71cae39685e2883a8a178a40a6531 Merge: 78c478a 51b4a83 Author: Gerlof Langeveld Date: Thu Feb 23 13:25:06 2017 +0100 Atopacctd should terminate upon receipt of SIGTERM signal When activating a signal catcher via the signal() function, the SA_RESTART flag is implicitly set. This means that a pending system call is automatically restarted after the received signal is handled. Instead, the sigaction() system call is used now to activate a signal catcher avoiding system calls to be restarted automatically. commit 78c478a56f4281ce5e2d28d0fa9822ad4b7d6a37 Merge: 46bc6d7 2a6f792 Author: Gerlof Langeveld Date: Thu Feb 23 13:21:03 2017 +0100 Improved activation and deactivation of process accounting When atopacctd is started, it forks itself for proper daemonizing purposes. The first process no longer exits immediately, but is killed by the child process at the moment that atopacctd has finished the initialization phase. commit 46bc6d7da1d47eeb73f55497e562019b59c527f7 Author: Gerlof Langeveld Date: Wed Feb 22 15:52:15 2017 +0100 Script atop.daily located in /usr/share/atop instead of /etc/atop.d M Makefile M atop.cronsysv M atop.init M atop.service M atop.specsystemd M atop.specsysv M man/atop.1 M psaccu_atop commit 0aa208a0af7bb384010f7332b3131c0884edcdd8 Author: Gerlof Langeveld Date: Mon Dec 19 19:17:28 2016 +0100 Implemented polling mode in case NETLINK refuses with error EINVAL. Due to kernel bug 190711, the NETLINK interface gives EINVAL when it is activated. Since atopacctd misses a trigger then to read the process accounting records when processes in the system terminate, a polling loop has to be used to verify the existance of new process accounting records. M README M atopacctd.c commit 51d6285f97b2adf8f0efbea7121f4eb2a4b4f75d Author: Gerlof Langeveld Date: Mon Dec 19 16:05:18 2016 +0100 Retry activation of process accounting if it appears not to work. According to kernel bug 190271 (process accounting sometimes does not work), the kernel sometimes returns SUCCESS to switching on process accounting while it does not work at all. M atopacctd.c commit e743f1b749d3fccb95bcef6028c238560a81d805 Author: Gerlof Langeveld Date: Thu Dec 15 10:13:48 2016 +0100 Modified version numbering. M mkdistr commit 96f0613201da8cb7e8ebd1120dd18f83abc83536 Author: Gerlof Langeveld Date: Thu Dec 15 09:12:15 2016 +0100 Adapt date in man-pages. M man/atop.1 M man/atopacctd.8 M man/atoprc.5 M man/atopsar.1 commit c8e2e3f80fc46463c3e2c798b8a4d2a106ac98a5 Author: Gerlof Langeveld Date: Thu Dec 15 09:11:25 2016 +0100 Improved error handling on the NETLINK interface. M atopacctd.c M netlink.c commit 2b6aab6454c39903a5ead4c3d5d819393ec1cea1 Author: Gerlof Langeveld Date: Wed Nov 16 14:42:49 2016 +0100 Allow xvd... disk drives without a digit at the end of the name. M photosyst.c commit 51b4a83b2babf9ca944da892092e5559d9588594 Author: Gerlof Langeveld Date: Thu Oct 27 22:05:43 2016 +0200 Take care that atopacctd terminates upon receipt of SIGTERM signal. When activating a signal catcher via the signal() function, the SA_RESTART flag is implicitly set. This means that a pending system call is automatically restarted after the received signal is handled. Instead, the sigaction system call is used now to activate a signal catcher avoiding system calls to be restarted automatically. M atopacctd.c commit f0516657b3c7261c336f1b6e869afd9baa43d197 Author: Gerlof Langeveld Date: Mon Oct 24 16:13:46 2016 +0200 Changed 'rotate 1' into 'rotate 0' (no need for another dummy file). M psaccs_atop M psaccu_atop commit 7617354131f9a9bb1503b89b82cc8ca939c0f472 Author: Gerlof Langeveld Date: Mon Oct 24 16:12:35 2016 +0200 Changed 'rotate 1' into 'rotate 0' (no need for another dummy file). M psaccs_atop M psaccu_atop commit 2a6f792ad71410a75ca633ff38cb52a678cc05ed Author: Gerlof Langeveld Date: Fri Oct 14 21:34:08 2016 +0200 Improved activation and deactivation of process accounting. M atopacctd.c commit 6484680418827937064c2aba11619369e189d94d Author: Gerlof Langeveld Date: Sat Oct 8 15:23:43 2016 +0200 Script atop.daily located in /usr/share/atop instead of /etc/atop.d M Makefile M atop.cronsysv M atop.init M atop.service M atop.specsystemd M atop.specsysv M psaccu_atop commit 101c0139e1a703479f7fb779bc258cf199eaa9fb Author: Gerlof Langeveld Date: Sat Oct 8 14:31:52 2016 +0200 Raw logging: Determine features per sample Define per sample whether or not process accounting is active and whether or not disk I/O statitics are maintained. In previous versions, this was determined for the entire log file which implied that it was impossible to append to an existing logfile when one of these features had changed. M atop.h M rawlog.c commit 36b9b09ac15519187717481ac79da25e66bce8eb Author: Gerlof Langeveld Date: Sat Oct 8 14:21:42 2016 +0200 Moved accounting file from /tmp to /var/cache. M acctproc.c M man/atop.1 commit a44bafe1895515e92881225079d56bb04053c9cf Author: Gerlof Langeveld Date: Sat Oct 8 14:18:13 2016 +0200 Added -r y... flag to usage message. M atopsar.c commit 2f7369f6911cc0fe6bb79dfa11ea845ebb036ea0 Author: Gerlof Langeveld Date: Sat Oct 8 14:16:29 2016 +0200 The flag -V has been added to the help-output. M atop.c commit a31b5a516e0dcfa5352e1047defbd6abf3c22444 Author: Gerlof Langeveld Date: Sat Oct 8 14:12:13 2016 +0200 Modified column name SNETBW to BANDWO. Modified column name RNETBW to BANDWI. M showlinux.c M showlinux.h M showprocs.c commit 5b21d88014fe4e53a5ab8f01d03ae00a2de56a18 Author: Gerlof Langeveld Date: Sat Oct 8 14:05:15 2016 +0200 Cosmetic changes in distribution script. M mkdistr commit b96168f473ca8a69884d900464cfb5f641e18d26 Author: Gerlof Langeveld Date: Sat Oct 8 14:03:55 2016 +0200 Cosmetic change. M various.c commit 90a3f939d5db6a41e1352f847f8719a89e5ddf78 Author: Gerlof Langeveld Date: Sat Oct 8 14:03:35 2016 +0200 Recalculate length of nodename when reading raw logfile. M rawlog.c commit 3fd998d0f2e43e57ff8d5d22b747efb14c1da554 Author: Gerlof Langeveld Date: Sat Oct 8 14:03:18 2016 +0200 Remove include of termio.h (not needed at all). M showgeneric.c M showlinux.c M showprocs.c M showsys.c commit 2820b1144ce403c7917cd2e09b05e78d2c9dbc07 Author: Gerlof Langeveld Date: Sun Sep 18 14:36:54 2016 +0200 Moved accounting file from /tmp to /var/cache. M acctproc.c M man/atop.1 commit e5a1be40373ebedf5bffede8968304fd3e0e8a18 Author: Gerlof Langeveld Date: Sat Sep 17 10:12:29 2016 +0200 Raw logging: dDetermine features per sample Define per sample whether or not process accounting is active and whether or not disk I/O statitics are maintained. In previous versions, this was determined for the entire log file which implied that it was impossible to append to an existing logfile when one of these features had changed. M atop.h M rawlog.c commit fec3f38c9b08459dea1a86fcdef56e97d211c1b6 Author: Gerlof Langeveld Date: Tue Sep 13 18:50:31 2016 +0200 Added -r y... flag to usage message. M atopsar.c commit e721b7194acb0f3de893d22e733f96c93172b5b7 Author: Gerlof Langeveld Date: Thu Sep 1 19:06:44 2016 +0200 Improved performance data gathering: All process and thread related data is stored now by function deviattask(), independent if the user wants to view all data. In this way, immediate reaction is garanteed for the 'a' key which allows the user to view all processes/threads or to view only the active processes/threads (previously, the 'a' key was only effective after taking a new sample). Together with this modification, the behaviour of the 'm' key (memory view) has changed by showing automatically all processes instead of only the active processes. After all, when viewing the memory consumption of the processes, it is not relevant whether or not that process has been active in the last interval. M 45atoppm M atop.c M atop.daily M atop.h M atopsar.c M deviate.c M man/atop.1 M parseable.c M parseable.h M photoproc.h M rawlog.c M showgeneric.c M showgeneric.h M showlinux.c commit 9f1bfee3f0babf88e86c8258bad977885eef279e Author: Gerlof Langeveld Date: Thu Aug 11 15:55:50 2016 +0200 The flag -V has been added to the help-output. M atop.c commit ba0cdc1474cebcbdac52b3ee9cc8516b33eeb0e8 Author: Gerlof Langeveld Date: Thu Aug 11 15:55:18 2016 +0200 With 'make clean' also the executable files are removed now. M Makefile commit 057c27d5f7b5e2b9ab06a91bed831d14dd7fe16b Author: Gerlof Langeveld Date: Mon Mar 7 12:16:06 2016 +0100 Modified column name SNETBW to BANDWO. Modified column name RNETBW to BANDWI. M showlinux.c M showlinux.h M showprocs.c commit 835d9a22dd88756709d75104550b206147c4151f Author: Gerlof Langeveld Date: Mon Mar 7 12:15:31 2016 +0100 Cosmetic change in distribution script. M mkdistr commit d600eac843b87cdfb91a6e1807d3aff5915dfdea Author: Gerlof Langeveld Date: Wed Jan 13 08:42:58 2016 +0100 Cosmetic fix. M various.c commit c2370bda4689217cefe60d1760d29fe1397436a4 Author: Gerlof Langeveld Date: Mon Nov 30 15:26:21 2015 +0100 Recalculate length of nodename when reading raw logfile. M rawlog.c commit a2306c0e1c83f5a123ce521a34d9efaa2297c26d Author: Gerlof Langeveld Date: Mon Nov 30 15:24:17 2015 +0100 Remove include of termio.h (not needed at all). M showgeneric.c M showlinux.c M showprocs.c M showsys.c commit 1c72c7dfe0843087440598e4df9dc844c2eff1eb Author: Gerlof Langeveld Date: Tue Nov 24 10:21:49 2015 +0100 The top-3 of CPU, disk and network consuming processes now only shows processes that really used capacity of that resource. Implicitly, a segmentation fault bug has been solved that occurred when less than 3 processes were available anyhow. M atopsar.c commit 1c5cac40d0bec5ea9b9625a8c61a4ad38899aad9 Author: Gerlof Langeveld Date: Tue Nov 24 08:59:37 2015 +0100 Column that show interface speed is shifted to the right, to keep input and output packets for all layers aligned. M showlinux.c commit 29012c89cacd30aa3c0f83e9f4c92b720ceda901 Author: Gerlof Langeveld Date: Tue Aug 4 19:11:36 2015 +0200 Execpath has been changed to /usr/sbin instead of /sbin (wrong directory location). M atopacct.service commit 0f502b2633bb00a340daf8ff0ac3506d486a1e09 Author: Gerlof Langeveld Date: Tue Aug 4 19:08:25 2015 +0200 Bug solution: When starting atop via sudo, atop runs into the stopped state. Caused by earlier modification to make atop a process group leader (needed for systemd). Problem solved by making atop only a process group leader when it doesn't operate interactively. M atop.c commit c0062f3e5a2604b3e8f0723e50c0cea0931d3e01 Author: Gerlof Langeveld Date: Thu Jun 25 12:54:30 2015 +0200 Modified handling of release number. M mkdistr commit 8becf63a08ba68ece7543512426cffa36e5c9c01 Author: Gerlof Langeveld Date: Thu Jun 25 12:53:44 2015 +0200 Modified handling of release number. M atop.specsystemd M atop.specsysv commit c213a40de6884129877b1519400de362a64b1f37 Author: Gerlof Langeveld Date: Thu Jun 25 12:53:14 2015 +0200 Added some blank fields for interfaces. M showlinux.c commit 67f5b44cbd2ef5ad3a59e09a6e192af489300a52 Author: Gerlof Langeveld Date: Tue Jun 23 16:55:30 2015 +0200 Minor bug-fixes. M man/atop.1 M showgeneric.c commit 6f0105972808ffd852b833ed81c222f259bc762a Author: Gerlof Langeveld Date: Tue Jun 23 16:32:16 2015 +0200 New selection possibilities for processes: Key 'I' can be used to specify one or more PIDs of processes to be selected. Key '/' can be used to specify a command line search string for processes to be selected. M man/atop.1 M showgeneric.c M showgeneric.h commit d8f641ecca0ac117c2eacd33cc6517944461a0af Author: Gerlof Langeveld Date: Tue Jun 23 14:44:49 2015 +0200 Bugfix for buffer overflow: snprintf instead of sprintf (credits: Nathan Scott). M showsys.c commit 8b82505a8125cf5b351b6de841cf6cd35740b133 Author: Gerlof Langeveld Date: Tue Jun 9 12:30:36 2015 +0200 Fine-tune statistics about data transfer to/from NFS servers. M atopsar.c M man/atop.1 M man/atopsar.1 commit 7c2b2cb6615a20e7e130a2fe00d4d029e0f5f3fa Author: Gerlof Langeveld Date: Tue Jun 9 11:03:10 2015 +0200 Bug solutions and small modifications for nfs4 mountstats. M atop.h M deviate.c M man/atop.1 M photosyst.c M showlinux.c M showprocs.c M showsys.c M various.c commit e3d0e1f03ac0cc48c5add560377933a3abf8ddbe Author: Gerlof Langeveld Date: Mon Jun 8 17:31:58 2015 +0200 Added statistics per mounted NFS filesystem (label NFM). M atop.c M atop.h M atopsar.c M deviate.c M man/atop.1 M man/atoprc.5 M man/atopsar.1 M parseable.c M photosyst.c M photosyst.h M showgeneric.c M showgeneric.h M showlinux.c M showlinux.h M showsys.c commit 314a64d9c62b27747d23c6f85011411076482f51 Author: Gerlof Langeveld Date: Mon Jun 8 10:01:05 2015 +0200 Solved various small bugs found by clang-analyzer. M atopsar.c M parseable.c M rawlog.c M showlinux.c M various.c commit 9b621bcac274df21012a42a2d131143e5857a47a Author: Gerlof Langeveld Date: Fri May 29 08:42:05 2015 +0200 Added buffers for formatting HTTPSTATS-specific output. M showlinux.c commit 66e4b0326d8663b8cc26cde42c9d1232e1742714 Author: Gerlof Langeveld Date: Fri May 22 09:58:22 2015 +0200 Accept 'xvd[a-z]' disks with a number behind it (they always seem to end with a number). M photosyst.c commit 7d5f789ddbbaea7ae0e4d2c3f78a3f735ca3a36f Author: Gerlof Langeveld Date: Mon May 18 08:58:41 2015 +0200 Rebuild of lost spec file for systemd. M atop.specsystemd commit 94f3da8d2740aaa3abfa7974afa900d985b8a9be Author: Gerlof Langeveld Date: Mon May 18 08:55:18 2015 +0200 Maintain current speed (rate) for wireless interfaces. M atopsar.c M deviate.c M ifprop.c M ifprop.h M man/atop.1 M photosyst.h M showlinux.c M showlinux.h M showsys.c commit 4ae902d49e3207c70153db2bbb8a3eed775c7c4a Author: Gerlof Langeveld Date: Sat May 9 18:01:12 2015 +0200 Support systemV and systemd RPMs. M atop.specsystemd M atop.specsysv M mkdistr commit b7b704f4406c92d9c5cda53a4c72bb37afeea744 Author: Gerlof Langeveld Date: Sat May 9 17:58:00 2015 +0200 Added defined value SPEED_UNKNOWN for older releases. M ifprop.c commit ab95f1a444a5e796641012bf3aa85a70f8056760 Author: Gerlof Langeveld Date: Sat May 9 16:27:17 2015 +0200 Support of systemv and systemd RPMs. M mkdistr commit 0f9e3cfeb31450ced24375631c6130e9fef60ae8 Author: Gerlof Langeveld Date: Sat May 9 16:21:31 2015 +0200 Support for OpenVZ containers. M atop.c M atop.h D atop.spec M deviate.c M man/atop.1 M man/atoprc.5 M parseable.c M photoproc.c M photoproc.h M photosyst.c M photosyst.h M showgeneric.c M showgeneric.h M showlinux.c M showlinux.h M showprocs.c M showsys.c commit b77825747f165a7c3bd76353d8f428676732cedd Author: Gerlof Langeveld Date: Sat May 9 16:20:24 2015 +0200 Separate RPM spec files for SystemV init and systemd. A atop.specsystemd A atop.specsysv commit 8f0148d9946344fe9b8a5eeee5a20588e0b149dd Author: Gerlof Langeveld Date: Tue Apr 7 13:02:58 2015 +0200 Addition of NFS (server/client) statistics on system level. M Makefile M atopsar.c M deviate.c M man/atop.1 M man/atopsar.1 M parseable.c M photosyst.c M photosyst.h M showlinux.c M showlinux.h M showsys.c commit 6e133e74acd987e915d7edeb4dab281ed8a055e2 Author: Gerlof Langeveld Date: Tue Mar 24 15:52:11 2015 +0100 Add NFS client and NFS server statistics. M atopsar.c M deviate.c M man/atop.1 M man/atopacctd.8 M man/atoprc.5 M man/atopsar.1 M parseable.c M photosyst.c M photosyst.h M showlinux.c M showlinux.h M showsys.c commit 6da40d213f14865356956b196475c080be1c79e6 Author: Gerlof Langeveld Date: Tue Mar 24 08:36:51 2015 +0100 Minor modification related to systemd-based install. M Makefile commit 04276882956a196e872fca17fb1dd73289eb3d1f Author: Gerlof Langeveld Date: Wed Feb 25 15:26:55 2015 +0100 Prepare atop and atopacct for use on systemd-based systems. M Makefile M README A atop-pm.sh M atop.service M atopacct.init A atopacct.service M atopacctd.c M atopacctd.h M mkdistr commit 1e2fc1c2275d71114a62fe95ca7363a6d9d1cd8a Author: Gerlof Langeveld Date: Wed Feb 25 15:20:05 2015 +0100 Avoid negative utilization counters for disks and logical volumes. These negative values might cause buffer overflows within atop. M showsys.c M various.c commit 2c5327f1a0a6797383785e6893b46f39d3a67c85 Author: Gerlof Langeveld Date: Tue Feb 24 09:34:52 2015 +0100 Avoid that process accounting is reactivated too frequently (and unnecessary). M atopacctd.c commit cfda21540a2a9b6545259ed612f8afc22b2218b8 Author: Gerlof Langeveld Date: Tue Feb 24 09:32:41 2015 +0100 Added creation of the SBINPATH directory for atopacctd. M Makefile commit 688f446ab5fb81dc6c44c065f956913682d1b03f Author: Gerlof Langeveld Date: Mon Feb 2 08:32:57 2015 +0100 Prepare systemd-based installation. A atop.cronsystemd A atop.cronsysv commit ab8db5f44f1b4edff624c4698849e391588e0cbd Author: Gerlof Langeveld Date: Mon Feb 2 08:31:33 2015 +0100 Prepare systemd-based installation. M Makefile M atop.c D atop.cron M atop.daily M atop.init M atop.service M mkdistr M psaccs_atop M psaccu_atop commit a5e0d6d0c758ce8ecc3ba6f8df2c30df8af64cc2 Author: Gerlof Langeveld Date: Mon Dec 8 09:38:47 2014 +0100 Define proper line speed for network interfaces faster than 1 Gbit/sec. M ifprop.c commit d18f067446deb39933b21ba7b2874ea63cb47e71 Author: Gerlof Langeveld Date: Fri Oct 17 14:31:27 2014 +0200 Minor change. M mkdistr commit 52e9c4a0f05f76ab81d46dc1f42ac69f5e123ae5 Author: Gerlof Langeveld Date: Fri Oct 17 14:28:10 2014 +0200 Minor correction in man-page. M man/atopacctd.8 commit c18d1354898e8871f75dedc8e8427694606b3832 Author: Gerlof Langeveld Date: Thu Aug 28 13:52:57 2014 +0200 Correction of typo's in the man pages, including a wrong counter name in the output of atop ('udpie' for UDP input errors). M Makefile M man/atop.1 M man/atopacctd.8 M man/atoprc.5 M man/atopsar.1 M showsys.c commit 4e739738da3aa5931cc0f66c7d402dea50ca6f6f Author: Gerlof Langeveld Date: Thu Aug 28 11:51:13 2014 +0200 OpenVZ support: virtual environment identifier per process (envID). M photoproc.c M photoproc.h M showlinux.c M showlinux.h M showprocs.c commit 3bd1bab7bd6fb0ec110922afbb169264a335a74f Author: Gerlof Langeveld Date: Fri Jul 25 15:31:42 2014 +0200 Activate atopacctd before activating the atop daemon. M atop.spec commit 0d02d0565b6d0df1c22be3237d145b7e0b7e68eb Author: Gerlof Langeveld Date: Fri Jul 25 14:58:19 2014 +0200 Improved help screen handling: use space bar to get next page. M showgeneric.c commit 5cebf4101c11d4680e57250635c6148b104bf4f9 Author: Gerlof Langeveld Date: Fri Jul 25 13:48:37 2014 +0200 Bug solution: when the screen-width is larger than the total number of columns to be printed, empty columns are added, however one column too many. M showsys.c commit b25ef4ec59240c58e929c93d17d42c91cf70121e Author: Gerlof Langeveld Date: Fri Jul 25 13:48:09 2014 +0200 Removed useless empty column for memory statistics. M showlinux.c commit 38163c1230c5e58890e783dd1cb29e14640e6099 Author: Gerlof Langeveld Date: Fri Jul 25 13:47:36 2014 +0200 Cosmetic change. M atopacctd.c commit 636fa8df11c99b3d6468f4c13c830c29ba79638d Author: Gerlof Langeveld Date: Thu Jul 24 16:07:01 2014 +0200 Detect if process accounting has been switched off by some other program and switch it on again. M atopacctd.c M atopacctd.h commit 268750adfc4e4a00050501d9c70d56436b4bb0a6 Author: Gerlof Langeveld Date: Sat Jun 28 11:15:17 2014 +0200 Add a warning about the aggregation of disk transfer to the parent process for the RDDSK and WRDSK counters. M man/atop.1 commit 1396164f8a5c880dc14658cc69a685babf040739 Author: Gerlof Langeveld Date: Fri Jun 20 14:51:53 2014 +0200 Add automatic start of atopacctd daemon. M Makefile commit 0dcc489d0ded0b2d2241cd9a625fd5206ddb7cd6 Author: Gerlof Langeveld Date: Fri Jun 20 14:06:08 2014 +0200 Add man page of atopacctd. M atop.spec M mkdistr commit 5295539f7b7ba1e725d20caf92eb9d28e1dd87ce Author: Gerlof Langeveld Date: Fri Jun 20 13:52:16 2014 +0200 Add man page for the atopacct daemon. A man/atopacctd.8 commit 027c748ffcb1a4ee926a3c14f2241342a2e6c5bd Author: Gerlof Langeveld Date: Fri Jun 20 13:49:14 2014 +0200 Modify the man pages and installation procedures for the introduction of the atopacctd daemon. M Makefile M atop.spec M atopacctd.c M man/atop.1 M man/atoprc.5 M man/atopsar.1 commit 366622d86d3716661ad36b86bbe833bc9ce29496 Author: Gerlof Langeveld Date: Wed Jun 18 13:24:49 2014 +0200 Introduce the 'G' key to suppress showing/accumulating exited processes in the output. M man/atop.1 M man/atoprc.5 M showgeneric.c M showgeneric.h commit a7668eb60ebda954dd8223e4a501b21afdea4cec Author: Gerlof Langeveld Date: Fri Jun 13 07:55:33 2014 +0200 Support CPU frequencies for systems with Intel P-state driver. Credits: Sjon Hortensius M photosyst.c commit 8e269cc3a8df55852490b8b99148b0f973e3d334 Author: Gerlof Langeveld Date: Fri Jun 13 07:51:55 2014 +0200 Cosmetic change. M atopacctd.c commit f12727d632052e6a82149d6e4804e354f2caae54 Author: Gerlof Langeveld Date: Fri Jun 13 07:50:25 2014 +0200 Remove rc-scripts with old sequence numbers (if any). M Makefile commit 7cec49a8d095e4d520ffe7f21499a7996137fc78 Author: Gerlof Langeveld Date: Sat Jun 7 12:47:29 2014 +0200 Doubled the %-signs to get the proper date string. M atop.service commit 9b699e1c0aea4842609f53ba0df061b568aa6d13 Author: Gerlof Langeveld Date: Sat Jun 7 12:11:52 2014 +0200 Source file netlink.c compiled without -O2 due to a strict-aliasing warning. M Makefile commit c1963ca5c51ccc8dba9866238376d49ffece962a Author: Gerlof Langeveld Date: Sat Jun 7 12:09:34 2014 +0200 Source file netlink.c compiled without -O2 due to a strict-aliasing warning. D atopacctd commit f2f369b2c79117aa03ce393e9cb54eab3bc36009 Author: Gerlof Langeveld Date: Sat Jun 7 11:29:36 2014 +0200 Rename variables to clearly distinghuish between the two semaphore groups. M acctproc.c commit 35ded9cabe93b4f6a126711d31dbe3fdfd3d3ebe Author: Gerlof Langeveld Date: Sat Jun 7 11:28:46 2014 +0200 Add the -R flag to gather PSIZE info in the background. M 45atoppm M atop.service commit d2a545c15bf4e3099d4391f24d20b079a5759872 Author: Gerlof Langeveld Date: Fri Jun 6 08:49:35 2014 +0200 Introduction of new daemon atopacctd. This daemon switches on process accounting and transfers every accounting record to an accounting shadow file. The source accounting file will regularly be truncated, while the shadow files are written with a limited size in a queued way. Non-used shadow files are deleted regularly. A atopacct.init A atopacctd A atopacctd.c A atopacctd.h A netlink.c commit b3909dd3243a7011ee605ed387692dcb0d8e17de Author: Gerlof Langeveld Date: Fri Jun 6 08:44:54 2014 +0200 Introduction of new daemon atopacctd. This daemon switches on process accounting and transfers every accounting record to an accounting shadow file. The source accounting file will regularly be truncated, while the shadow files are written with a limited size in a queued way. Non-used shadow files are deleted regularly. M Makefile M acctproc.c M acctproc.h M atop.c M atop.init M atop.spec M man/atoprc.5 M showgeneric.c commit 65abbee8fe9f4ea4ec7c875e897d1aa3a5b2763d Author: Gerlof Langeveld Date: Thu May 29 13:24:55 2014 +0200 Add the -R flag for monitoring in the background. M atop.service commit 295585730cbbe322d0dd556c45413e2ab0737099 Author: Gerlof Langeveld Date: Wed May 28 16:59:54 2014 +0200 Usage message of atop and atopsar extended with reference to the man-page. M atop.c M atopsar.c commit 7df1f797fa27f5dd53298f7511bb36811dc51e14 Author: Gerlof Langeveld Date: Wed May 28 16:46:00 2014 +0200 Added a function prototype for priline to verify the calling (cause of earlier problems). M atopsar.c commit b495b78b8b120bcf266440f6ed1f0e133573e76b Author: Gerlof Langeveld Date: Wed May 28 14:29:02 2014 +0200 Bug-solution: segmentation fault when one of the process names in the system contains a newline. M photoproc.c commit 216c4ccc9e26d1d195985f8b2c45c4008492a341 Author: Gerlof Langeveld Date: Wed May 28 13:54:36 2014 +0200 Introduce configurable colors: in the atoprc file, colors can be defined for information messages (default green), threads (default yellow), almost critical resources (default cyan) and critical resources (default red). M atop.c M man/atop.1 M man/atoprc.5 M man/atopsar.1 M showgeneric.c M showgeneric.h M showsys.c commit fd2bb81e07dbb1ac97a559e7099e717e05b80642 Author: Gerlof Langeveld Date: Wed May 28 12:17:08 2014 +0200 Cosmetic change to message. M showgeneric.c commit dc1ef5d83b72095536cfabd53adcfcfc8ec37a01 Author: Gerlof Langeveld Date: Wed May 28 12:15:56 2014 +0200 Solve floating point exception in case that the process accouting file was not open. M acctproc.c commit a94a6170476406d1ca85aa018ea71eea6d41f5bd Author: Gerlof Langeveld Date: Wed May 28 11:27:05 2014 +0200 Limit the names of network interfaces to six characters max. M atopsar.c commit 19826d8ae098b21cf733cf4e69bfb1936cddc7dd Author: Gerlof Langeveld Date: Wed May 28 11:01:13 2014 +0200 Modified names for RPM files. M mkdistr commit b71978ea7ec610305a239529345e816e1323a02b Author: Gerlof Langeveld Date: Wed May 28 10:19:16 2014 +0200 Added Requires and BuildRequires. M atop.spec commit f6d0eae38d1e9f093d15fb6fa74d353e59efd05d Author: Gerlof Langeveld Date: Wed May 28 10:18:35 2014 +0200 Cosmetic change. M Makefile commit 318890acda8a7cab6bba829fa3276e774f1bd4e0 Author: Gerlof Langeveld Date: Sun May 25 22:01:54 2014 +0200 Correction for PSIZE calculation: regain and drop root privileges. M photoproc.c commit 01be030273991089b6ded31d6a28855bf874d7be Author: Gerlof Langeveld Date: Sat May 24 14:10:55 2014 +0200 Introduction of proportional memory size (PSIZE) per process. For the resident memory parts used by a process that are shared with other processes, only a proportional part (shared memory part divided by the number of sharers) is accounted to the process. Since the gathering of this value is rather time-consuming (reading the smaps file of every process), it is optional ('R' key or '-R' flag). M atop.c M atop.daily M atop.h M deviate.c M man/atop.1 M parseable.c M photoproc.c M photoproc.h M showgeneric.c M showgeneric.h M showlinux.c M showlinux.h M showprocs.c commit 899cdbd18447ce29f901d43531a1702416ab22ad Author: Gerlof Langeveld Date: Sat May 24 11:02:18 2014 +0200 Increased number of LVMs and disks supported. M photosyst.h commit e2f7fb5bc45510b2ec14269e71a0b8ca7ae5a454 Author: Gerlof Langeveld Date: Fri May 23 23:33:40 2014 +0200 Resize message shows new dimensions. M showgeneric.c commit 0f54b4c1ebca739cda28ae5793475b06e90e0b88 Author: Gerlof Langeveld Date: Fri May 23 15:39:46 2014 +0200 The guest percentage that is shown for CPUs, is not a separate category that should be totalized with the other categories. In fact, it overlaps with the user mode percentage. So if one of the virtual machines executes a looping process, on the KVM hypervisor the user percentage and guest percentage should both show 100% instead of both 50%. M atopsar.c M deviate.c M man/atop.1 M man/atopsar.1 M procdbase.c M showlinux.c commit 3149aa8f4720e615148179ae8cc5146b8c3f5433 Author: Gerlof Langeveld Date: Fri May 23 13:28:20 2014 +0200 Useless buffer formatting removed. M atopsar.c commit e96c1fff1c7a7ca292f3aedaaa28b92d2e7b9629 Author: Gerlof Langeveld Date: Fri May 23 13:19:21 2014 +0200 Removed double disk entry for 'vd...' disks. M photosyst.c commit 239bd26a925d54d184248f00df35586d4d8d441d Author: Gerlof Langeveld Date: Sun Jan 26 15:42:31 2014 +0100 Solve segmentation vioalation with atopser (wrong number of arguments passed to priline function). M atopsar.c commit 3829e7382f348489f61f01b4e19fb4fb12bc36ef Author: Gerlof Langeveld Date: Sun Jan 26 15:37:12 2014 +0100 Solve segmentation violation when making summaries: Extra parameter added to priline-function call. M atopsar.c M man/atop.1 M rawlog.c commit fbce79c76ddb6a38a2bc22b44a95edcf9cf78a36 Author: Gerlof Langeveld Date: Sat Dec 7 12:17:07 2013 +0100 When process accounting is switched on and a child process has been created and terminated, it still might occur that no process accounting record has been written (kernel failure?). This situation resulted in a Floating Point Exception signal. M acctproc.c commit 2c7fe393518d861ff2fac3b38888389dd13c9b67 Author: Gerlof Langeveld Date: Sat Oct 26 13:07:18 2013 +0200 Add a space between two columns, that were show without white space. M parseable.c commit 948581cce702ffdf93d8e632c6ef12aa3ff6a31b Author: Gerlof Langeveld Date: Sat May 11 16:58:05 2013 +0200 Add new counter for memory that is claimed by the vmware balloon driver (to be withdrawn from this virtual machine in favour of other virtual machines). 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 3a82bbeda1627a8a133f64f4434edaeefe413759 Author: Gerlof Langeveld Date: Wed Apr 24 10:47:39 2013 +0200 Support for huge pages (total and in use). M deviate.c M man/atop.1 M photosyst.c M photosyst.h M showlinux.c M showlinux.h M showsys.c commit 22295853af2f10701f6da3243b75f55fc2eb5f6d Author: Gerlof Langeveld Date: Tue Apr 23 14:42:24 2013 +0200 Documentation improvements. M man/atop.1 M showgeneric.c commit 05266573ea93a9866121909b4bd61856103e9a28 Author: Gerlof Langeveld Date: Tue Apr 2 10:59:24 2013 +0200 Documentation improved concerning top-3 of processes (atopsar) and the netatop module (atop). M man/atop.1 M man/atopsar.1 commit 9be21d695f218120e501d459ae1bdf112b4dbba5 Author: Gerlof Langeveld Date: Thu Jan 10 13:54:08 2013 +0100 Cosmetic change: rename some variables to ease future maintenance. M atop.c M deviate.c M photoproc.h commit ffd892d16dc60950a637db45bf2af692a3d2a91e Author: Gerlof Langeveld Date: Wed Dec 19 15:45:40 2012 +0100 Under certain circumstances, the number of exited processes might be less than requested, so correct the counter nrexit. M acctproc.c M atop.c commit ba35522fbfb1c4fdb90d0dc68b0ac50e95cae73f Author: Gerlof Langeveld Date: Tue Dec 11 23:16:36 2012 +0100 Solution for the following bug: When process accounting type 2 is used by the kernel (i.e. no PID is the accounting record), atop crashes when thread view is enabled. Credits: Alex Samorukov M showgeneric.c commit c34bb60f74c25c3da24ab485ff6d41b8435609b1 Author: Gerlof Langeveld Date: Wed Nov 21 21:50:55 2012 +0100 Corrected value 'curproc' in -P report. M atopsar.c commit 321e5288151db22e643569f8b71709d1f5da6bf4 Author: Gerlof Langeveld Date: Mon Nov 19 09:26:32 2012 +0100 Maintain total number of bytes transferred for accumulated per-user/per-program view (key 'u'/'p') to show proper NET-percentage and get proper sorting order. M showgeneric.c commit cc2824f244ae33c3628dc5bc8a96bf1e64088bb7 Author: Gerlof Langeveld Date: Fri Nov 16 22:47:29 2012 +0100 Extended the number of positions for the RNET/SNET counters from 4 to 5, and shrinked SYSCPU/USRCPU from 7 to 6. M showprocs.c M showsys.c M various.c commit 57fb7e206e36e131093c7d0c4cbffe0f07d4bb20 Merge: 55b51fe 8f5f6cc Author: Gerlof Langeveld Date: Fri Nov 16 21:42:32 2012 +0100 Merge branch '2.0.1' Conflicts: mkdistr mkversion version.h commit 8f5f6ccc03b9b8ebaf8b2d65a18d3b56cb58c483 Author: Gerlof Langeveld Date: Fri Nov 16 19:27:04 2012 +0100 Version update M version.h commit 8c17df29c0d6dc82bec807cc2af8c50d69a2fbdb Author: Gerlof Langeveld Date: Fri Nov 16 19:23:20 2012 +0100 Release modification. M version.h commit b5f5f9c05343c0b818b2afef291fd27d35aa690c Author: Gerlof Langeveld Date: Wed Nov 7 08:54:04 2012 +0100 Required modifications for distribution. M atop.spec M mkdistr M mkversion M version.h commit e4f27ff6ba3585b82155af29665c017e146e5c75 Author: Gerlof Langeveld Date: Tue Nov 6 08:19:44 2012 +0100 Remark about netatop in README and solved rendering problem in atoprc.5 M README M man/atoprc.5 commit 41696c91407d65454769d8c0c94d65b410791963 Author: Gerlof Langeveld Date: Fri Nov 2 11:14:52 2012 +0100 Sequence regainprivs-fopen-dropprivs-fread-... does not work for files under /proc, so the /proc/PID/io files could not be accessed when atop is not started by a root-user. Sequence has been changed to regainprivs-fopen-fread-...-dropprivs. M photoproc.c M various.c commit 55b51feda3e2ffa450adb72203ecd9afc3474d30 Author: Gerlof Langeveld Date: Fri Nov 2 11:08:40 2012 +0100 Sequence regainprivs-fopen-dropprivs-fread-... does not work for files under /proc, so the /proc/PID/io files could not be accessed when atop is not started by a root-user. Sequence has been changed to regainprivs-fopen-fread-...-dropprivs. M photoproc.c M various.c commit 8e75554bb8f98269912ebb85a457df3fab35a54f Author: Gerlof Langeveld Date: Sat Oct 27 11:59:24 2012 +0200 Adapt to correct month. M man/atop.1 M man/atoprc.5 M man/atopsar.1 commit 1a256253c472ccca37ee1dd93ac7d6d1f3fd7e48 Author: Gerlof Langeveld Date: Sat Oct 27 11:53:49 2012 +0200 Bug fixes. M mkdistr M mkversion M version.h commit 6f61e0e5dbb3f48fd10d52b34eaac6ca49e19b31 Author: Gerlof Langeveld Date: Thu Oct 25 17:22:39 2012 +0200 Added update-rc.d M Makefile commit 643941fd3ff2055fcdcb979cdeeab62cef5771c8 Author: Gerlof Langeveld Date: Wed Oct 24 13:12:21 2012 +0200 Dynamically switch per sample to show network stats or not. In this way a running atop (e.g. the daily atop running in the background) immediately reacts on loading or unloading of the netatop module. M atop.c M atop.h M deviate.c M man/atop.1 M netatopif.c M photoproc.c M rawlog.c M showgeneric.c M showgeneric.h M showlinux.c commit f36eb4be1d7e26ed233bb101613df1a6363f8891 Author: Gerlof Langeveld Date: Mon Oct 22 09:27:14 2012 +0200 Consider Shmem value in /proc/meminfo. M man/atop.1 M showlinux.c commit fc06911932186159fd190f53379462a1a5b05147 Author: Gerlof Langeveld Date: Mon Oct 22 09:22:42 2012 +0200 Add counter shmem as total shared memory including tmpfs. Som miscellaneous cosmetic changes. M acctproc.c M deviate.c M man/atop.1 M man/atoprc.5 M man/atopsar.1 M photosyst.c M photosyst.h M showlinux.c M showlinux.h M showsys.c commit 1e3c18350475135d11c300801647b5ea8aa6b204 Author: Gerlof Langeveld Date: Sat Oct 20 17:06:33 2012 +0200 Modified formula to determine if memory is critically low: the page cache counter implies the resident shared memory (and that should not be subtracted). M showlinux.c commit 58c0aae74341a337397859854dfcf826b21ca3ef Author: Gerlof Langeveld Date: Sat Oct 20 16:59:30 2012 +0200 Gather and display the value SReclaimable from /proc/meminfo to give more adequate coloring of the 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 213f3e1160046000858edd33c59952be802d5058 Author: Gerlof Langeveld Date: Sun Oct 14 15:07:48 2012 +0200 Get rid of warnings from ignored return values (with (void)) M acctproc.c M atop.c M atop.h M atopsar.c M photoproc.c M photosyst.c M rawlog.c M various.c commit a7a17cfc03f6e4b82840bce4d61bbcdd20db52e0 Author: Gerlof Langeveld Date: Sun Oct 14 13:29:05 2012 +0200 Cosmetic change. M mkdistr commit f683dfb6c76ff6df2d68031e19d37532843509f9 Author: Gerlof Langeveld Date: Sun Oct 14 13:23:01 2012 +0200 Various modifications to cooperate properly with the netatop kernel module and the netatopd daemon. Update of man-page. Simplified the generation of the version number. M Makefile A atopsar M deviate.c M man/atop.1 M mkversion M netatopif.c M photosyst.c M showlinux.c D version.SKEL A version.h commit 6807de7c036f1f375b7f80a33525b6eff02b03f8 Author: Gerlof Langeveld Date: Sat Sep 15 12:15:31 2012 +0200 Boot time is determined now with a high resolution clock to be able to determine the start time of a process more precise (needed to make a better match with process accounting/netatop). M Makefile M atop.c M atop.h M deviate.c M photoproc.c M photosyst.c M procdbase.c M various.c commit 6526ea3da42807c7c94a055c5d90252899f8b059 Author: Gerlof Langeveld Date: Fri Sep 14 23:18:06 2012 +0200 Further cooperation implemented between atop and netatop/netatopd. M Makefile M acctproc.c M atop.c M atop.h D atopnetif.c M atopsar.c M deviate.c A netatopd.h A netatopif.c M parseable.c M photoproc.c M photoproc.h M showgeneric.c M showlinux.c M showprocs.c M various.c commit cc8cd6572d7541dfc82d0df09690b5b93bcdcf25 Author: Gerlof Langeveld Date: Fri Sep 14 23:06:54 2012 +0200 When pid is in the process accounting record, no further check on the btime (begin-time) is required. M procdbase.c commit 129895d44071290750f13ce08df7cf80f534136a Author: Gerlof Langeveld Date: Wed Aug 29 09:03:46 2012 +0200 Remove source code related to the kernel patches. Insert new code to support the kernel module 'netatop'. Add shared memory figures to MEM line. M Makefile M atop.c D atopnet.h M atopnetif.c M atopsar.c M deviate.c M man/atop.1 A netatop.h M parseable.c M photoproc.c M photosyst.c M photosyst.h M showgeneric.c M showlinux.c M showlinux.h M showprocs.c M showsys.c commit 73bffb6f37e02d5272fbfeb64ada43773fd585e6 Author: Gerlof Langeveld Date: Wed Aug 8 16:53:07 2012 +0200 WRDSK is normalized with WCANCEL to determine how much real disk transfer is done per process. M showgeneric.c M showlinux.c M showlinux.h M showprocs.c commit 361f1d2c877d6316b6339239d81c94aadde62617 Author: Gerlof Langeveld Date: Wed Aug 8 16:01:12 2012 +0200 Removed unused counters for raw network sends/receives. M acctproc.c M atopsar.c M deviate.c M parseable.c M photoproc.c M photoproc.h M showgeneric.c M showlinux.c M showlinux.h M showprocs.c commit e11e259fd35c7be4fcab96d48a818ca222b7fa51 Author: Gerlof Langeveld Date: Wed Aug 8 15:49:20 2012 +0200 Remove code for kernel patches and introduce kernel module 'atopnet'. M Makefile M acctproc.c M atop.c M atop.h A atopnet.h A atopnetif.c M atopsar.c M deviate.c M parseable.c M photoproc.c M showgeneric.c M showlinux.c M showlinux.h M showprocs.c commit 8c9de7a781247b2591d00fc9e5c9a4f3674b0491 Author: Gerlof Langeveld Date: Mon Jul 23 11:12:33 2012 +0200 Removed unused variable 'columnprefix'. M Makefile M showlinux.c commit eb5da90ccb0246ba9040c158c82589c9e5fffe39 Author: Gerlof Langeveld Date: Mon Jul 23 11:04:45 2012 +0200 Minor corrections. M Makefile M mkdistr commit 9c0c2287ed60619dc2a688b2e4242c7ded582026 Author: Gerlof Langeveld Date: Sun Jul 22 11:30:51 2012 +0200 Improved RPM handling. M Makefile M mkdistr commit b1bb45f1d9a61fcd81cebf417cccadac68ddde67 Author: Gerlof Langeveld Date: Fri Jul 20 15:19:35 2012 +0200 Add service file for systemd without installing. A atop.service M mkdistr commit 409be5867b31d37f138447110d234b2325902cdf Author: Gerlof Langeveld Date: Fri Jul 20 15:18:10 2012 +0200 Corrected typo in help messages. M showgeneric.c commit 4ea9f1ab0c500d47ae74544f90353c6190b8e317 Author: Gerlof Langeveld Date: Fri Jul 20 12:09:17 2012 +0200 Modified description. M atop.spec M man/atop.1 M man/atopsar.1 commit a98a0d1ad85aaa082fda535cbb9844a0a4591034 Author: Gerlof Langeveld Date: Fri Jul 20 12:02:17 2012 +0200 ChangeLog removed (not needed any more when using git). D ChangeLog commit af0f1fad5438374912c167f284653b87c62a3142 Author: Gerlof Langeveld Date: Fri Jul 20 12:01:06 2012 +0200 Added LSB header. M atop.init commit d5a9b854674ae451636815e539c4c056365708b2 Author: Gerlof Langeveld Date: Fri Jul 20 10:12:31 2012 +0200 Clarified comments about the use of coloring. M showsys.c commit 1b38444596097776f88415091d27efee4f2dee9f Author: Gerlof Langeveld Date: Fri Jul 20 10:01:38 2012 +0200 Solved division-by-zero when maxfreq=0 and more precise coloring of concerning CPU-values when CPU overloaded. M showsys.c commit 826caafb1aaf892e0692d4315a324093ece2da32 Author: Gerlof Langeveld Date: Fri Jul 20 10:00:53 2012 +0200 Correct typo's in description of frequency scaling. M man/atop.1 commit f266aafb596880c8fe8bcf6f4ae662889803b7c3 Author: Gerlof Langeveld Date: Thu Jul 19 08:24:33 2012 +0200 If setuid-root is set for atop, root-privileges are regained when reading /proc/pid/io (nowadays only readable for root) to obtain the disk stats per process. M photoproc.c commit 9e77a3bccfa345eb8a4a7b1a417343f80a5ae792 Author: Gerlof Langeveld Date: Thu Jul 19 07:51:43 2012 +0200 Email address updated in README and ChangeLog added to package. M README M mkdistr commit 1ee760110035966d444f057cdaf2f0784244b4c8 Author: Gerlof Langeveld Date: Wed Jul 18 11:37:29 2012 +0200 Added EMC Power device recognition. M photosyst.c commit 4aa9c986f8bb469ad38e46db46d7bc1766f4cea0 Author: Gerlof Langeveld Date: Wed Jul 18 09:25:42 2012 +0200 Better recognition of version of process accounting file. M acctproc.c commit 9c8dcc365631a38df69ddc98a94b28ca71666679 Author: Gerlof Langeveld Date: Wed Jul 18 09:21:36 2012 +0200 Improved error handling when not enough columns or lines. M man/atop.1 M man/atopsar.1 M showgeneric.c commit ab6f072b5b85df575c40fd68583052833c09bd48 Author: Gerlof Langeveld Date: Tue Jul 17 14:29:08 2012 +0200 Suppress sorting of system resources by key F or flag -F (toggle). M man/atop.1 M man/atoprc.5 M showgeneric.c M showgeneric.h commit 3b0922c3c371ffecab00f6df72bc8b8852ae8e15 Author: Gerlof Langeveld Date: Tue Jul 17 13:29:45 2012 +0200 Give clearer error message if the format of the rawlog is incompatible. M rawlog.c commit 7c17a309ef97a59a8ee1f5a593f48eeac9d46bc8 Author: Gerlof Langeveld Date: Tue Jul 17 12:57:25 2012 +0200 Create unique /tmp name for decompressed raw file. M man/atop.1 M rawlog.c commit 0f693c1f651c02ecbfbedf49bafdc879c2479184 Author: Gerlof Langeveld Date: Tue Jul 17 11:28:20 2012 +0200 Numerous cosmetic changes and man-page updates. M acctproc.c M deviate.c M man/atop.1 M man/atoprc.5 M man/atopsar.1 M photosyst.c M photosyst.h M showlinux.c M showlinux.h M showprocs.c M showsys.c commit 2a637eaa2ba734c1ae2cdc2d6caab8bc66bf7782 Author: Gerlof Langeveld Date: Fri Jun 15 10:41:45 2012 +0200 Improved screen handling. Limited maximum file-size for process accounting file (200 MiB). M acctproc.h M showgeneric.c commit 86e9448c2cd8e0f3bc1b138dfc98b6701f0afa2c Author: Gerlof Langeveld Date: Thu Jun 14 17:09:15 2012 +0200 Improved handling of process accounting: - Per sample not more than 50 MiB are allowed for process accounting records, which means about 72000 processes. This avoids atop to explode in memory when a huge amount of processes died within one sample. - When only one atop-process is busy and it detects that the process accounting file exceeds .5 GiB, it will truncate the accounting file to zero. M acctproc.c M acctproc.h M atop.c M atop.h M atopsar.c M deviate.c M mkdistr M parseable.c M parseable.h M photoproc.h M rawlog.c M showgeneric.c M showgeneric.h M showlinux.c M showlinux.h M showsys.c commit 0178cdeba8f654816c082118c046978c94869343 Author: Gerlof Langeveld Date: Wed Jun 13 15:43:55 2012 +0200 Cosmetical changes. M atop.c M showsys.c commit 428524129548ede14d535a8c2c23ff5e0af2d54c Author: Gerlof Langeveld Date: Wed Jun 13 13:12:39 2012 +0200 New key 'S' to make selections of system resources like logical volumes, disks and network interfaces (regular expression). M man/atop.1 M showgeneric.c M showgeneric.h M showlinux.c commit 3fff5cfc8e01fceb1e69ff067e73a5d86ae12c48 Author: Gerlof Langeveld Date: Wed Jun 13 12:16:54 2012 +0200 Use of arrow-keys and PgUp/PgDown for vertical scroll. Use of -> and <- keys for horizontal scroll of command line. Increment of maximum number of system resources (e.g. MAXCPU). M photoproc.h M photosyst.h M showgeneric.c M showprocs.c commit 7671b76506743c0a9d82f77674788a045ea0a427 Author: Gerlof Langeveld Date: Sat Jun 9 12:28:58 2012 +0200 Improved generation of new version. A atop.spec M mkdistr commit 2c87df01b06c588ce205c1dcdf8bd30889e27b1b Author: Gerlof Langeveld Date: Sat Jun 9 11:07:03 2012 +0200 Support statistics for virtio disks (vd*). M photosyst.c commit c0c9238f3667d4916cad4eeb16baa861de7e4527 Author: Gerlof Langeveld Date: Sat Jun 9 11:04:22 2012 +0200 Uniform handling of failing mallocs. M atop.c M atop.h M atopsar.c M deviate.c M mkdistr M photoproc.h M procdbase.c M rawlog.c M showgeneric.c M various.c commit 0682920a7b41e2007dac1470996d945e55b0fc21 Author: Gerlof Langeveld Date: Tue Jun 5 15:44:04 2012 +0200 Fetching and displaying of individual threads. M acctproc.c M acctproc.h M atop.c M atop.h M atopsar.c M deviate.c M man/atop.1 M parseable.c M parseable.h M photoproc.c M photoproc.h M procdbase.c M rawlog.c M showgeneric.c M showgeneric.h M showlinux.c M showlinux.h M showprocs.c M showsys.c commit 10f54f1caa904b98f270a92d5fdcafe259d88773 Author: Gerlof Langeveld Date: Mon May 28 11:23:43 2012 +0200 Increase maximum number of disks, lvm's, interfaces and CPUs. M photosyst.c M photosyst.h commit 27fe64e140a62220a0e7fee04431be4756c0047d Author: Gerlof Langeveld Date: Mon May 28 11:22:49 2012 +0200 Show message instead of #exits when process accounting could not be switched on. M showsys.c commit 90b7a438c226fd0ce4e8f8680ece4b67ea5a87a8 Author: Gerlof Langeveld Date: Mon May 28 11:21:53 2012 +0200 Proper handling of lock added. M atop.init commit 30165361059b75acbae534b21eee2465727f05f7 Author: Gerlof Langeveld Date: Mon Mar 5 09:06:57 2012 +0100 Add various details about the memory usage of processes, like stack size, data size, shared library size and size used on swap. M acctproc.c M deviate.c M man/atop.1 M parseable.c M photoproc.c M photoproc.h M photosyst.h M showgeneric.c M showlinux.c M showlinux.h M showprocs.c commit 5c65b96129124fcdd9105d90d11bc46a46b0ec9a Author: Gerlof Langeveld Date: Tue Sep 13 10:20:22 2011 +0200 Show new value about swap space usage per process. M acctproc.c M deviate.c M man/atop.1 M parseable.c M photoproc.c M photoproc.h M showgeneric.c M showlinux.c M showlinux.h M showprocs.c commit 25ffd8313a499b480f38e493eb50bf4c5a8bec1b Author: Gerlof Langeveld Date: Tue Sep 13 07:52:15 2011 +0200 Color vmlim and vmcom adapted. M showsys.c commit eff004b034980224bff10baf14eee598fdd5f470 Author: Gerlof Langeveld Date: Wed Sep 7 15:34:12 2011 +0200 Colors are shown now for separate system-level counters instead of for the entire line with counters. M showgeneric.c M showgeneric.h M showlinux.c M showlinux.h M showsys.c commit 2358e3fe0445973c78226d25aa13da02f1dd61d9 Author: Gerlof Langeveld Date: Tue Sep 6 09:54:42 2011 +0200 Git-based determination of the version of atop. A mkdistr A mkversion A version.SKEL D version.c commit 87852e1b1b2d87b26c222fe7abec024570465a48 Author: Gerlof Langeveld Date: Tue Sep 6 08:59:36 2011 +0200 Enhanced security: improved dropping of root privileges. M acctproc.c M atop.c M atop.h M atopsar.c M deviate.c M rawlog.c M various.c commit 1f0c1bb1b9ad52abfc62147465ccf61e25f5a7f8 Author: Gerlof Langeveld Date: Mon Sep 5 15:59:44 2011 +0200 Avoid error messages during startup of atop about not being able to activate process accounting (e.g. when starting without root privs). The reason of the error will now be displayed instead of the '#exit' counter. M acctproc.c M atop.c M atop.h M man/atop.1 M showsys.c commit fb9ed3f5079344a93cdc29bd415710ef753fcbf8 Author: Gerlof Langeveld Date: Mon Sep 5 14:04:31 2011 +0200 Initial version A 45atoppm A AUTHOR A COPYING A ChangeLog A Makefile A README A acctproc.c A acctproc.h A atop.c A atop.cron A atop.daily A atop.h A atop.init A atopsar.c A deviate.c A ifprop.c A ifprop.h A man/atop.1 A man/atoprc.5 A man/atopsar.1 A netstats.h A parseable.c A parseable.h A photoproc.c A photoproc.h A photosyst.c A photosyst.h A procdbase.c A psaccs_atop A psaccu_atop A rawlog.c A showgeneric.c A showgeneric.h A showlinux.c A showlinux.h A showprocs.c A showsys.c A various.c A version.c atop-2.11.1/various.c0000644000203100020310000006064014771753566013726 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 ONEEBYTE 1152921504606846976LL #define MAXLONGLONG 9223372036854775807LL #define MAXBYTE 999 #define MAXKBYTE (ONEKBYTE*999L) #define MAXKBYTE9 (ONEKBYTE*9L) #define MAXMBYTE (ONEMBYTE*999L) #define MAXMBYTE9 (ONEMBYTE*9L) #define MAXGBYTE (ONEGBYTE*999LL) #define MAXGBYTE9 (ONEGBYTE*9LL) #define MAXTBYTE (ONETBYTE*999LL) #define MAXTBYTE9 (ONETBYTE*9LL) #define MAXPBYTE (ONEPBYTE*999LL) #define MAXPBYTE9 (ONEPBYTE*9LL) #define MAXEBYTE (ONEEBYTE*999LL) #define MAXEBYTE8 (ONEEBYTE*7LL+(ONEEBYTE-1)) char * val2memstr(count_t value, char *strvalue, int pformat, int avgval, int nsecs) { char aformat; /* advised format */ count_t verifyval; char *suffix = ""; int basewidth = 6; /* ** notice that the value can be negative, in which case the ** modulo-value should be evaluated and an extra position should ** be reserved for the sign */ if (value < 0) verifyval = -value * 10; else verifyval = value; /* ** verify if printed value is required per second (average) or total */ if (avgval && nsecs) { value = llround((double)((double)value/(double)nsecs)); verifyval = llround((double)((double)verifyval/(double)nsecs)); basewidth -= 2; suffix = "/s"; if (verifyval <= MAXBYTE) /* bytes ? */ aformat = BFORMAT; else if (verifyval <= MAXKBYTE9) /* kbytes 1-9 ? */ aformat = KBFORMAT; else if (verifyval <= MAXKBYTE) /* kbytes ? */ aformat = KBFORMAT_INT; else if (verifyval <= MAXMBYTE9) /* mbytes 1-9 ? */ aformat = MBFORMAT; else if (verifyval <= MAXMBYTE) /* mbytes 10-999 ? */ aformat = MBFORMAT_INT; else if (verifyval <= MAXGBYTE9) /* gbytes 1-9 ? */ aformat = GBFORMAT; else if (verifyval <= MAXGBYTE) /* gbytes 10-999 ? */ aformat = GBFORMAT_INT; else if (verifyval <= MAXTBYTE9) /* tbytes 1-9 ? */ aformat = TBFORMAT; else if (verifyval <= MAXTBYTE) /* tbytes 10-999? */ aformat = TBFORMAT_INT; else if (verifyval <= MAXPBYTE9) /* pbytes 1-9 ? */ aformat = PBFORMAT; else if (verifyval <= MAXPBYTE) /* pbytes 10-999 ? */ aformat = PBFORMAT_INT; else if (verifyval <= MAXEBYTE8) /* ebytes 1-8 ? */ aformat = EBFORMAT; else aformat = OVFORMAT; /* max long long */ } else /* ** printed value per interval (normal mode) */ { /* ** determine which format will be used on bases of the value itself */ if (verifyval <= MAXBYTE) /* bytes ? */ aformat = BFORMAT; else if (verifyval <= MAXKBYTE) /* kbytes ? */ aformat = KBFORMAT; else if (verifyval <= MAXMBYTE) /* mbytes ? */ aformat = MBFORMAT; else if (verifyval <= MAXGBYTE) /* gbytes ? */ aformat = GBFORMAT; else if (verifyval <= MAXTBYTE) /* tbytes? */ aformat = TBFORMAT; else if (verifyval <= MAXPBYTE) /* pbytes? */ aformat = PBFORMAT; else aformat = EBFORMAT; /* ebytes! */ } /* ** check if this is also the preferred format */ if (aformat <= pformat) aformat = pformat; switch (aformat) { case BFORMAT: snprintf(strvalue, 7, "%*lldB%s", basewidth-1, value, suffix); break; case KBFORMAT: snprintf(strvalue, 7, "%*.1lfK%s", basewidth-1, (double)((double)value/ONEKBYTE), suffix); break; case KBFORMAT_INT: snprintf(strvalue, 7, "%*lldK%s", basewidth-1, llround((double)((double)value/ONEKBYTE)), suffix); break; case MBFORMAT: snprintf(strvalue, 7, "%*.1lfM%s", basewidth-1, (double)((double)value/ONEMBYTE), suffix); break; case MBFORMAT_INT: snprintf(strvalue, 7, "%*lldM%s", basewidth-1, llround((double)((double)value/ONEMBYTE)), suffix); break; case GBFORMAT: snprintf(strvalue, 7, "%*.1lfG%s", basewidth-1, (double)((double)value/ONEGBYTE), suffix); break; case GBFORMAT_INT: snprintf(strvalue, 7, "%*lldG%s", basewidth-1, llround((double)((double)value/ONEGBYTE)), suffix); break; case TBFORMAT: snprintf(strvalue, 7, "%*.1lfT%s", basewidth-1, (double)((double)value/ONETBYTE), suffix); break; case TBFORMAT_INT: snprintf(strvalue, 7, "%*lldT%s", basewidth-1, llround((double)((double)value/ONETBYTE)), suffix); break; case PBFORMAT: snprintf(strvalue, 7, "%*.1lfP%s", basewidth-1, (double)((double)value/ONEPBYTE), suffix); break; case PBFORMAT_INT: snprintf(strvalue, 7, "%*lldP%s", basewidth-1, llround((double)((double)value/ONEPBYTE)), suffix); break; case EBFORMAT: snprintf(strvalue, 7, "%*.1lfE%s", basewidth-1, (double)((double)value/ONEEBYTE), suffix); break; default: snprintf(strvalue, 7, "OVFLOW"); } // check if overflow occurred during the formatting // by checking the last byte of the formatted string // switch ( *(strvalue+5) ) { case 's': // in case of per-second value case 'B': case 'K': case 'M': case 'G': case 'T': case 'P': case 'E': break; default: snprintf(strvalue, 7, "OVFLOW"); } return strvalue; } /* ** Function numeric() checks if the ascii-string contains ** a numeric (positive) value. ** Returns 1 (true) if so, or 0 (false). */ int numeric(char *ns) { register char *s = ns; while (*s) if (*s < '0' || *s > '9') return(0); /* false */ else s++; return(1); /* true */ } /* ** Function getboot() returns the boot-time of this system ** as number of jiffies since 1-1-1970. */ unsigned long long getboot(void) { static unsigned long long boottime; if (!boottime) /* do this only once */ boottime = getbootlinux(hertz); return boottime; } /* ** LINUX SPECIFIC: ** Determine boot-time of this system (as number of jiffies since 1-1-1970). */ static unsigned long long getbootlinux(long hertz) { int cpid; char tmpbuf[1280]; FILE *fp; unsigned long startticks; unsigned long long bootjiffies = 0; struct timespec ts; /* ** dirty hack to get the boottime, since the ** Linux 2.6 kernel (2.6.5) does not return a proper ** boottime-value with the times() system call :-( */ if ( (cpid = fork()) == 0 ) { /* ** child just waiting to be killed by parent */ pause(); } else { /* ** parent determines start-time (in jiffies since boot) ** of the child and calculates the boottime in jiffies ** since 1-1-1970 */ (void) clock_gettime(CLOCK_REALTIME, &ts); // get current bootjiffies = 1LL * ts.tv_sec * hertz + 1LL * ts.tv_nsec * hertz / 1000000000LL; snprintf(tmpbuf, sizeof tmpbuf, "/proc/%d/stat", cpid); if ( (fp = fopen(tmpbuf, "r")) != NULL) { if ( fscanf(fp, "%*d (%*[^)]) %*c %*d %*d %*d %*d " "%*d %*d %*d %*d %*d %*d %*d %*d " "%*d %*d %*d %*d %*d %*d %lu", &startticks) == 1) { bootjiffies -= startticks; } fclose(fp); } /* ** kill the child and get rid of the zombie */ kill(cpid, SIGKILL); (void) wait((int *)0); } return bootjiffies; } /* ** generic pointer verification after malloc */ void ptrverify(const void *ptr, const char *errormsg, ...) { if (!ptr) { va_list args; acctswoff(); netatop_signoff(); 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 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.11.1/atop.c0000644000203100020310000010363514771753566013203 0ustar gerlofgerlof/* ** ATOP - System & Process Monitor ** ** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as process-level. ** ** This source-file contains the main-function, which verifies the ** calling-parameters and takes care of initialization. ** The engine-function drives the main sample-loop in which after the ** indicated interval-time a snapshot is taken of the system-level and ** process-level counters and the deviations are calculated and ** visualized for the user. ** ========================================================================== ** Author: Gerlof Langeveld ** E-mail: gerlof.langeveld@atoptool.nl ** Date: November 1996 ** Linux-port: June 2000 ** Modified: May 2001 - Ported to kernel 2.4 ** -------------------------------------------------------------------------- ** Copyright (C) 2000-2024 Gerlof Langeveld ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ** -------------------------------------------------------------------------- ** ** After initialization, the main-function calls the ENGINE. ** For every cycle (so after another interval) the ENGINE calls various ** functions as shown below: ** ** +------------------------------------------------------------------------+ ** | E N G I N E | ** | | ** | | ** | _____________________await interval-timer________________________ | ** | | ^ | ** | | _______ _______ _______ ________ ________ | | ** | | ^ | ^ | ^ | ^ | ^ | | | ** +---|---|-------|--|-------|--|-------|-----|--------|----|--------|--|--+ ** | | | | | | | | | | | | ** +--V-----+ +--V----+ +--V----+ +--V--------+ +--V-------+ +--V-----+ ** | | | | | | | | | deviate | | | ** | photo | | photo | | photo | | acct | | ..cgroup | | print | ** | cgroup | | syst | | proc | | photoproc | | ..syst | | | ** | | | | | | | | | ..proc | | | ** +--------+ +-------+ +-------+ +-----------+ +----------+ +--------+ ** ^ ^ ^ ^ | | ** | | | | | | ** | | | V V V ** _______ _____ _____ __________ ________ _________ ** / \ / \ / \ / \ / \ / \ ** /sys/fs /proc /proc accounting task screen or ** /cgroup file database file ** \_______/ \_____/ \_____/ \__________/ \________/ \_________/ ** ** - photocgroup() ** Takes a snapshot of the counters related to resource usage on ** cgroup-level v2 (cpu, disk, memory). ** ** - photosyst() ** Takes a snapshot of the counters related to resource-usage on ** system-level (cpu, disk, memory, network). ** ** - photoproc() ** Takes a snapshot of the counters related to resource-usage of ** tasks which are currently active. For this purpose the whole ** task-list is read. ** ** - acctphotoproc() ** Takes a snapshot of the counters related to resource-usage of ** tasks which have been finished during the last interval. ** For this purpose all new records in the accounting-file are read. ** ** When all counters have been gathered, functions are called to calculate ** the difference between the current counter values and the counter values ** of the previous cycle. These functions operate on cgroup level, system level ** as well as on task level. ** These differences are stored in a new structure (table). ** ** - deviatcgroup() ** Calculates the differences between the current cgroup-level ** counters and the corresponding counters of the previous cycle. ** - deviatsyst() ** Calculates the differences between the current system-level ** counters and the corresponding counters of the previous cycle. ** ** - deviattask() ** Calculates the differences between the current task-level ** counters and the corresponding counters of the previous cycle. ** The per-task counters of the previous cycle are stored in the ** task-database; this "database" is implemented as a linked list ** of taskinfo structures in memory (so no disk-accesses needed). ** Within this linked list hash-buckets are maintained for fast searches. ** The entire task-database is handled via a set of well-defined ** functions from which the name starts with "pdb_..." (see the ** source-file procdbase.c). ** The processes which have been finished during the last cycle ** are also treated by deviattask() in order to calculate what their ** resource-usage was before they finished. ** ** All information is ready to be visualized now. ** There is a structure which holds the start-address of the ** visualization-function to be called. Initially this structure contains ** the address of the generic visualization-function ("generic_samp"), but ** these addresses can be modified in the main-function depending on particular ** flags. In this way various representation-layers (ASCII, graphical, ...) ** can be linked with 'atop'; the one to use can eventually be chosen ** at runtime. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "atop.h" #include "acctproc.h" #include "ifprop.h" #include "photoproc.h" #include "photosyst.h" #include "cgroups.h" #include "showgeneric.h" #include "showlinux.h" #include "parseable.h" #include "json.h" #include "gpucom.h" #include "netatop.h" #define allflags "ab:cde:fghijklmnopqrstuvwxyz:123456789ABCDEFGHIJ:KL:MNOP:QRSTUVWXYZ" #define MAXFL 84 /* maximum number of command-line flags */ /* ** declaration of global variables */ struct utsname utsname; int utsnodenamelen; time_t pretime; /* timing info */ time_t curtime; /* timing info */ unsigned long interval = 10; unsigned long sampcnt; char screen; int fdinotify = -1; /* inotify fd for twin mode */ pid_t twinpid; /* PID of lower half for twin mode */ char twindir[RAWNAMESZ] = "/tmp"; int linelen = 80; char acctreason; /* accounting not active (return val) */ char 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 */ char connectgpud = 0; /* boolean: connect to atopgpud */ char connectnetatop = 0; /* boolean: connect to netatop(bpf) */ regex_t envregex; unsigned short hertz; unsigned int pidwidth; unsigned int pagesize; unsigned int nrgpus; int osrel; int osvers; int ossub; extern GHashTable *ghash_net; int supportflags; /* supported features */ char **argvp; struct 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; char twinmodeflag; /* ** interpretation of defaults-file /etc/atoprc and $HOME/.atop */ static void readrc(char *, int); static void do_interval(char *, char *); static void do_linelength(char *, char *); static struct { char *tag; void (*func)(char *, char *); int sysonly; } manrc[] = { { "flags", do_flags, 0, }, { "twindir", do_twindir, 0, }, { "interval", do_interval, 0, }, { "linelen", do_linelength, 0, }, { "username", do_username, 0, }, { "procname", do_procname, 0, }, { "maxlinecpu", do_maxcpu, 0, }, { "maxlinegpu", do_maxgpu, 0, }, { "maxlinedisk", do_maxdisk, 0, }, { "maxlinemdd", do_maxmdd, 0, }, { "maxlinelvm", do_maxlvm, 0, }, { "maxlineintf", do_maxintf, 0, }, { "maxlineifb", do_maxifb, 0, }, { "maxlinenfsm", do_maxnfsm, 0, }, { "maxlinecont", do_maxcont, 0, }, { "maxlinenuma", do_maxnuma, 0, }, { "maxlinellc", do_maxllc, 0, }, { "colorinfo", do_colinfo, 0, }, { "coloralmost", do_colalmost, 0, }, { "colorcritical", do_colcrit, 0, }, { "colorthread", do_colthread, 0, }, { "ownallcpuline", do_ownallcpuline, 0, }, { "ownonecpuline", do_ownindivcpuline, 0, }, { "owncplline", do_owncplline, 0, }, { "ownmemline", do_ownmemline, 0, }, { "ownswpline", do_ownswpline, 0, }, { "ownpagline", do_ownpagline, 0, }, { "ownmemnumaline", do_ownmemnumaline, 0, }, { "ownnumacpuline", do_owncpunumaline, 0, }, { "ownllcline", do_ownllcline, 0, }, { "owndskline", do_owndskline, 0, }, { "ownnettrline", do_ownnettransportline, 0, }, { "ownnetnetline", do_ownnetnetline, 0, }, { "ownnetifline", do_ownnetinterfaceline, 0, }, { "ownifbline", do_owninfinibandline, 0, }, { "ownprocline", do_ownprocline, 0, }, { "ownsysprcline", do_ownsysprcline, 0, }, { "owndskline", do_owndskline, 0, }, { "cpucritperc", do_cpucritperc, 0, }, { "gpucritperc", do_gpucritperc, 0, }, { "memcritperc", do_memcritperc, 0, }, { "swpcritperc", do_swpcritperc, 0, }, { "dskcritperc", do_dskcritperc, 0, }, { "netcritperc", do_netcritperc, 0, }, { "swoutcritsec", do_swoutcritsec, 0, }, { "almostcrit", do_almostcrit, 0, }, { "atopsarflags", do_atopsarflags, 0, }, { "perfevents", do_perfevents, 0, }, { "pacctdir", do_pacctdir, 1, }, }; /* ** internal prototypes */ static void engine(void); static void twinprepare(void); static void twinclean(void); int main(int argc, char *argv[]) { register int i; int c; char *p; struct rlimit rlim; /* ** since privileged actions will be done later on, at this stage ** the root-privileges are dropped by switching effective user-id ** to real user-id (security reasons) */ if (! droprootprivs() ) { fprintf(stderr, "not possible to drop root privs\n"); exit(42); } /* ** preserve command arguments to allow restart of other version */ argvp = argv; /* ** read defaults-files /etc/atoprc en $HOME/.atoprc (if any) */ readrc("/etc/atoprc", 1); if ( (p = getenv("HOME")) ) { char path[1024]; snprintf(path, sizeof path, "%s/.atoprc", p); readrc(path, 0); } /* ** check if we are supposed to behave as 'atopsar' ** i.e. system statistics only */ if ( (p = strrchr(argv[0], '/'))) p++; else p = argv[0]; if ( 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 '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 't': /* twin mode ? */ // optional absolute path name of directory? if (optind < argc) { if (*(argv[optind]) == '/') { strncpy(twindir, argv[optind], RAWNAMESZ-1); optind++; } } twinmodeflag++; break; case 'B': /* bar graphs ? */ displaymode = 'D'; break; case 'H': /* bar graphs ? */ barmono = 1; 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 MALLACTIVE: /* all processes/cgroups ? */ deviatonly = 0; break; case MCALCPSS: /* calculate PSS per sample ? */ if (rawreadflag) { fprintf(stderr, "PSIZE gathering depends on rawfile\n"); sleep(3); break; } calcpss = 1; if (!rootprivs()) { fprintf(stderr, "PSIZE gathering only for own " "processes\n"); sleep(3); } break; case MGETWCHAN: /* obtain wchan string? */ getwchan = 1; break; case MRMSPACES: /* remove spaces from command */ rmspaces = 1; break; case 'z': /* prepend regex matching environment variables */ if (regcomp(&envregex, optarg, REG_NOSUB|REG_EXTENDED)) { fprintf(stderr, "Invalid environment regular expression!"); prusage(argv[0]); } prependenv = 1; break; case 'k': /* try to open TCP connection to atopgpud */ connectgpud = 1; break; case 'K': /* try to open connection to netatop/netatop-bpf */ connectnetatop = 1; break; default: /* gather other flags */ flaglist[i++] = c; } } /* ** 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 twin mode wanted with two atop processes: ** ** - lower half: gather statistics and write to raw file ** - upper half: read statistics and present to user ** ** consistency checks */ if (twinmodeflag) twinprepare(); /* ** check if raw data from a file must be viewed */ if (rawreadflag) { rawread(); cleanstop(0); } /* ** be sure to be leader of an own process group when ** running as a daemon (or at least: when not interactive); ** needed for systemd */ if (rawwriteflag) (void) setpgid(0, 0); /* ** determine start-time for gathering current statistics */ curtime = getboot() / hertz; /* ** catch signals for proper close-down */ signal(SIGHUP, cleanstop); signal(SIGTERM, cleanstop); /* ** regain the root-privileges that we dropped at the beginning ** to do some privileged work */ regainrootprivs(); /* ** lock ATOP in memory to get reliable samples (also when ** memory is low and swapping is going on); ** ignored if not running under superuser privileges! */ rlim.rlim_cur = RLIM_INFINITY; rlim.rlim_max = RLIM_INFINITY; if (setrlimit(RLIMIT_MEMLOCK, &rlim) == 0) (void) mlockall(MCL_CURRENT|MCL_FUTURE); /* ** increment CPU scheduling-priority to get reliable samples (also ** during heavy CPU load); ** ignored if not running under superuser privileges! */ if ( nice(-20) == -1) ; set_oom_score_adj(); /* ** switch-on the process-accounting mechanism to register the ** (remaining) resource-usage by processes which have finished */ acctreason = acctswon(); /* ** determine properties (like speed) of all interfaces */ initifprop(); /* ** open socket to the IP layer to issue getsockopt() calls later on */ if (connectnetatop) netatop_ipopen(); /* ** since privileged activities are finished now, there is no ** need to keep running under root-privileges, so switch ** effective user-id to real user-id */ if (! droprootprivs() ) mcleanstop(42, "failed to drop root privs\n"); /* ** determine if cgroups v2 is supported */ cgroupv2support(); /* ** start the engine now ..... */ engine(); cleanstop(0); return 0; /* never reached */ } /* ** The engine() drives the main-loop of the program */ static void engine(void) { struct sigaction sigact; static time_t timelimit; /* ** reserve space for cgroup-level statistics */ static struct cgchainer *devcstat; int ncgroups = 0; int npids = 0; /* ** reserve space for system-level statistics */ static struct sstat *cursstat; /* current */ static struct sstat *presstat; /* previous */ static struct sstat *devsstat; /* deviation */ static struct sstat *hlpsstat; /* ** reserve space for task-level statistics */ static struct tstat *curtpres; /* current present list */ static unsigned long curtlen; /* size of present list */ struct tstat *curpexit; /* exited process list */ static struct devtstat devtstat; /* deviation info */ unsigned long ntaskpres; /* number of tasks present */ unsigned long nprocexit; /* number of exited procs */ unsigned long nprocexitnet; /* number of exited procs */ /* via netatopd daemon */ unsigned long noverflow; int nrgpuproc=0, /* number of GPU processes */ gpupending=0; /* boolean: request sent */ struct gpupidstat *gp = NULL; /* ** initialization: allocate required memory dynamically */ cursstat = calloc(1, sizeof(struct sstat)); presstat = calloc(1, sizeof(struct sstat)); devsstat = calloc(1, sizeof(struct sstat)); ptrverify(cursstat, "Malloc failed for current sysstats\n"); ptrverify(presstat, "Malloc failed for prev sysstats\n"); ptrverify(devsstat, "Malloc failed for deviate sysstats\n"); /* ** install the signal-handler for ALARM, USR1 and USR2 (triggers * for the next sample) */ memset(&sigact, 0, sizeof sigact); sigact.sa_handler = getusr1; sigaction(SIGUSR1, &sigact, (struct sigaction *)0); memset(&sigact, 0, sizeof sigact); sigact.sa_handler = getusr2; sigaction(SIGUSR2, &sigact, (struct sigaction *)0); memset(&sigact, 0, sizeof sigact); sigact.sa_handler = getalarm; sigaction(SIGALRM, &sigact, (struct sigaction *)0); if (interval > 0) alarm(interval); if (midnightflag) { time_t timenow = time(0); struct tm *tp = localtime(&timenow); tp->tm_hour = 23; tp->tm_min = 59; tp->tm_sec = 59; timelimit = mktime(tp); } /* ** open socket to the atopgpud daemon for GPU statistics ** if explicitly required */ if (connectgpud) { nrgpus = gpud_init(); if (nrgpus) supportflags |= GPUSTAT; } /* ** MAIN-LOOP: ** - Wait for the requested number of seconds or for other trigger ** ** - System-level counters ** get current counters ** calculate the differences with the previous sample ** ** - Process-level counters ** get current counters from running & exited processes ** calculate the differences with the previous sample ** ** - Call the print-function to visualize the differences */ for (sampcnt=0; sampcnt < nsamples; sampcnt++) { char lastcmd; /* ** if the limit-flag is specified: ** check if the next sample is expected before midnight; ** if not, stop atop now */ if (midnightflag && (curtime+interval) > timelimit) break; /* ** wait for alarm-signal to arrive (except first sample) ** or wait for SIGUSR1/SIGUSR2 */ if (sampcnt > 0 && awaittrigger) pause(); awaittrigger = 1; /* ** gather time info for this sample */ pretime = curtime; curtime = time(0); /* seconds since 1-1-1970 */ /* ** send request for statistics to atopgpud */ if (nrgpus) { if ((gpupending = gpud_statrequest()) == 0) nrgpus = 0; } /* ** take a snapshot of the current system-level metrics ** and calculate the deviations (i.e. calculate the activity ** during the last sample) */ hlpsstat = cursstat; /* swap current/prev. stats */ cursstat = presstat; presstat = hlpsstat; photosyst(cursstat); /* obtain new system-level counters */ /* ** take a snapshot of the current cgroup-level metrics ** when cgroups v2 supported */ if ( (supportflags&CGROUPV2) ) photocgroup(); /* ** receive and parse response from atopgpud */ if (nrgpus && gpupending) { nrgpuproc = gpud_statresponse(nrgpus, cursstat->gpu.gpu, &gp); gpupending = 0; // connection lost or timeout on receive? if (nrgpuproc == -1) { nrgpus = 0; supportflags &= ~GPUSTAT; } cursstat->gpu.nrgpus = nrgpus; } deviatsyst(cursstat, presstat, devsstat, curtime-pretime > 0 ? curtime-pretime : 1); /* ** take a snapshot of the current task-level statistics ** and calculate the deviations (i.e. calculate the activity ** during the last sample) ** ** first register active tasks */ curtpres = NULL; do { curtlen = counttasks(); // worst-case value curtpres = realloc(curtpres, curtlen * sizeof(struct tstat)); ptrverify(curtpres, "Malloc failed for %lu tstats\n", curtlen); memset(curtpres, 0, curtlen * sizeof(struct tstat)); } while ( (ntaskpres = photoproc(curtpres, curtlen)) == curtlen); /* ** register processes that exited during last sample; ** first determine how many processes exited ** ** the number of exited processes is limited to avoid ** that atop explodes in memory and introduces OOM killing */ nprocexit = acctprocnt(); /* number of exited processes */ if (nprocexit > MAXACCTPROCS) { noverflow = nprocexit - MAXACCTPROCS; nprocexit = MAXACCTPROCS; } else noverflow = 0; /* ** determine how many processes have been exited ** for the netatop module (only processes that have ** used the network) */ if (nprocexit > 0 && (supportflags & NETATOPD)) nprocexitnet = netatop_exitstore(); else nprocexitnet = 0; /* ** reserve space for the exited processes and read them */ if (nprocexit > 0) { curpexit = malloc(nprocexit * sizeof(struct tstat)); ptrverify(curpexit, "Malloc failed for %lu exited processes\n", nprocexit); memset(curpexit, 0, nprocexit * sizeof(struct tstat)); nprocexit = acctphotoproc(curpexit, nprocexit); /* ** reposition offset in accounting file when not ** all exited processes have been read (i.e. skip ** those processes) */ if (noverflow) acctrepos(noverflow); } else { curpexit = NULL; } /* ** merge GPU per-process stats with other per-process stats */ if (nrgpus && nrgpuproc > 0) gpumergeproc(curtpres, ntaskpres, curpexit, nprocexit, gp, nrgpuproc); /* ** calculate process-level deviations */ deviattask(curtpres, ntaskpres, curpexit, nprocexit, &devtstat, devsstat); if (supportflags & NETATOPBPF) { g_hash_table_destroy(ghash_net); ghash_net = NULL; } /* ** calculate cgroup-level v2 deviations ** ** allocation and deallocation of structs ** is arranged at a lower level */ if ( (supportflags&CGROUPV2) ) ncgroups = deviatcgroup(&devcstat, &npids); /* ** activate the installed print function to visualize ** the deviations */ lastcmd = (vis.show_samp)(curtime, curtime-pretime > 0 ? curtime-pretime : 1, &devtstat, devsstat, devcstat, ncgroups, npids, nprocexit, noverflow, sampcnt==0); /* ** release dynamically allocated memory */ if (nprocexit > 0) free(curpexit); free(curtpres); if ((supportflags & NETATOPD) && (nprocexitnet > 0)) netatop_exiterase(); free(gp); gp = NULL; // avoid double free if (lastcmd == MRESET) /* reset requested ? */ { sampcnt = -1; curtime = getboot() / hertz; // reset current time /* set current (will be 'previous') counters to 0 */ memset(cursstat, 0, sizeof(struct sstat)); /* remove all tasks in database */ pdb_makeresidue(); pdb_cleanresidue(); /* remove current cgroup info */ cgwipecur(); } } /* end of main-loop */ } /* ** print usage of this command */ void prusage(char *myname) { printf("Usage: %s [-t [absdir]] [-flags] [interval [samples]]\n", myname); printf("\t\tor\n"); printf("Usage: %s -w file [-S] [-%c] [interval [samples]]\n", myname, MALLACTIVE); printf(" %s -r [file] [-b [YYYYMMDD]hhmm[ss]] [-e [YYYYMMDD]hhmm[ss]] [-flags]\n", myname); printf("\n"); printf("\tgeneric flags:\n"); printf("\t -t twin mode: live measurement with possibility to review earlier samples\n"); printf("\t (raw file created in /tmp or in specific directory path)\n"); printf("\t -%c show bar graphs for system statistics\n", MBARGRAPH); printf("\t -%c show bar graphs without categories\n", MBARMONO); printf("\t -%c show cgroup v2 metrics\n", MCGROUPS); printf("\t -7 define cgroup v2 depth level -2 till -9 (default: -7)\n"); printf("\t -%c show version information\n", MVERSION); printf("\t -%c show all processes and cgroups (i.s.o. active only)\n", MALLACTIVE); printf("\t -%c calculate proportional set size (PSS) per process\n", MCALCPSS); printf("\t -%c determine WCHAN (string) per thread\n", MGETWCHAN); printf("\t -P generate parsable output for specified label(s)\n"); printf("\t -J generate JSON output for specified label(s)\n"); printf("\t -%c no spaces in parsable output for command (line)\n", MRMSPACES); printf("\t -L alternate line length (default 80) in case of " "non-screen output\n"); printf("\t -z prepend regex matching environment variables to " "command line\n"); printf("\t -k try to connect to external atopgpud daemon (default: do not connect)\n"); printf("\t -K try to connect to netatop/netatop-bpf interface (default: do not connect)\n"); 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); } } /* ** prepare twin mode */ #define TWINNAME "atoptwinXXXXXX" extern char twindir[]; static char *tempname; static void twinprepare(void) { char eventbuf[1024]; int tempfd; /* ** consistency checks for used options */ if (rawreadflag) { fprintf(stderr, "twin mode can not be combined with -r\n"); exit(42); } if (rawwriteflag) { fprintf(stderr, "twin mode can not be combined with -w\n"); exit(42); } if (!isatty(fileno(stdout)) ) // output to pipe or file? { fprintf(stderr, "twin mode only for interactive use\n"); exit(42); } /* ** created unique temporary file */ if (strlen(twindir) + sizeof TWINNAME + 1 >= RAWNAMESZ) { fprintf(stderr, "twin mode directoy path too long\n"); exit(42); } tempname = malloc(strlen(twindir) + sizeof TWINNAME + 1); ptrverify(tempname, "Malloc failed for temporary twin name\n"); sprintf(tempname, "%s/%s", twindir, TWINNAME); if ( (tempfd = mkstemp(tempname)) == -1) { fprintf(stderr, "%s: ", tempname); perror("twin mode file creation"); exit(42); } /* ** create lower half as child process */ switch (twinpid = fork()) { case -1: perror("fork twin process"); exit(42); case 0: // lower half: gather data and write to rawfile rawwriteflag++; vis.show_samp = rawwrite; break; default: // upper half: read from raw file and visualize rawreadflag++; /* ** created inotify instance to be awoken when the lower half ** has written a new sample to the temporary file */ if ( (fdinotify = inotify_init()) == -1) { perror("twin mode inotify init"); exit(42); } (void) inotify_add_watch(fdinotify, tempname, IN_MODIFY); /* ** arrange an automic kill of the lower half ** at the moment that the upper half terminates */ atexit(twinclean); /* ** wait for first sample to be written by lower half */ (void) read(fdinotify, eventbuf, sizeof eventbuf); } /* ** define current raw file name for both parent and child */ strncpy(rawname, tempname, RAWNAMESZ-1); } /* ** kill twin process that gathers data and ** remove the temporary raw file */ static void twinclean(void) { if (twinpid) // kill lower half process kill(twinpid, SIGTERM); (void) unlink(tempname); } atop-2.11.1/rawlog.h0000644000203100020310000001047214771753566013534 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 cstatlen; /* length of struct cstat */ int ifuture[5]; /* future use */ }; struct rawrecord { time_t curtime; /* current time (epoch) */ unsigned short flags; /* various flags */ unsigned short ncgroups; /* number of cgroups */ unsigned short sfuture[2]; /* future use */ unsigned int scomplen; /* length of compressed sstat */ unsigned int pcomplen; /* length of compressed tstat's */ unsigned int interval; /* interval (number of seconds) */ unsigned int ndeviat; /* number of tasks in list */ unsigned int nactproc; /* number of processes in list */ unsigned int ntask; /* total number of tasks */ unsigned int totproc; /* total number of processes */ unsigned int totrun; /* number of running threads */ unsigned int totslpi; /* number of sleeping threads(S)*/ unsigned int totslpu; /* number of sleeping threads(D)*/ unsigned int totzomb; /* number of zombie processes */ unsigned int nexit; /* number of exited processes */ unsigned int noverflow; /* number of overflow processes */ unsigned int totidle; /* number of idle threads(I)*/ unsigned int ccomplen; /* length of compressed cstats */ unsigned int coriglen; /* length of original cstats */ unsigned int ncgpids; /* number of cgroups pidlist */ unsigned int icomplen; /* length of compressed pidlist */ unsigned int ifuture; /* future use */ }; #endif atop-2.11.1/gpucom.h0000644000203100020310000000314314771753566013530 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.11.1/atop.cronsysv0000644000203100020310000000011714771753566014636 0ustar gerlofgerlof# daily restart of atop at midnight 0 0 * * * root /usr/share/atop/atop.daily& atop-2.11.1/atopacct.init0000755000203100020310000000333514771753566014556 0ustar gerlofgerlof#!/bin/sh # # atopacctd Startup script for the atopacctd daemon # # chkconfig: 2345 91 9 # description: Process accounting control # ### BEGIN INIT INFO # Provides: atopacct # Required-Start: $local_fs $remote_fs # Required-Stop: $local_fs $remote_fs # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: This daemon switches on process accounting and # transfers the process accounting records 'realtime' # to small shadow files to avoid huge disk space usage # Description: Process accounting control ### END INIT INFO # Check existance of binaries [ -f /usr/sbin/atopacctd ] || exit 0 RETVAL=0 # See how we were called. case "$1" in start) # Check if process accounting already in use via separate psacct package # for PACCTFILE in /var/account/pacct /var/log/pacct /var/log/account/pacct do if [ -f "$PACCTFILE" ] # file exists? then BEFORSIZE=$(ls -lL "$PACCTFILE" | awk '{print $5}') AFTERSIZE=$(ls -lL "$PACCTFILE" | awk '{print $5}') # verify if accounting file grows, so is in use # if [ $BEFORSIZE -lt $AFTERSIZE ] then echo Process accounting already used by psacct! >&2 exit $RETVAL # do not start atopacctd fi fi done # Check if atopacctd runs already # if ps -e | grep -q atopacctd$ then : else # Start atopacctd rm -rf /run/pacct_shadow.d 2> /dev/null /usr/sbin/atopacctd fi touch /var/lock/subsys/atopacctd ;; stop) # Check if atopacctd runs # if ps -e | grep -q atopacctd$ then kill $(ps -e | grep atopacctd$ | sed 's/^ *//' | cut -d' ' -f1) fi rm -f /var/lock/subsys/atopacctd ;; status) ;; reload) ;; restart) ;; *) echo "Usage: $0 [start|stop]" exit 1 esac exit $RETVAL atop-2.11.1/netlink.c0000644000203100020310000001343414771753566013701 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.11.1/atopgpud0000755000203100020310000004623514771753566013647 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.11.1/atop-pm.sh0000755000203100020310000000022214771753566013774 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.11.1/atopacct.service0000644000203100020310000000054314771753566015246 0ustar gerlofgerlof[Unit] Description=Atop process accounting daemon Documentation=man:atopacctd(8) Before=atop.service [Service] Type=forking PIDFile=/run/atopacctd.pid ExecStartPre=/bin/sh -c 'if systemctl -q is-active acct psacct; then echo "Process accounting already in use by (ps)acct"; exit 1; fi' ExecStart=/usr/sbin/atopacctd [Install] WantedBy=multi-user.target atop-2.11.1/atopacctd.c0000644000203100020310000006215614771753566014204 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.11.1/version.h0000644000203100020310000000021714771753566013722 0ustar gerlofgerlof#ifndef __ATOP_VERSION__ #define __ATOP_VERSION__ #define ATOPVERS "2.11.1" char *getstrvers(void); unsigned short getnumvers(void); #endif atop-2.11.1/utsnames.c0000644000203100020310000001233014771753566014066 0ustar gerlofgerlof/* ** ATOP - System & Process Monitor ** ** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as process-level. ** ** This source-file contains functions to retrieve the hostname of a ** containerized process by associating to its UTS namespace. ** ========================================================================== ** Author: Gerlof Langeveld ** E-mail: gerlof.langeveld@atoptool.nl ** Date: August 2023 (initial) ** -------------------------------------------------------------------------- ** Copyright (C) 2023 Gerlof Langeveld ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ** -------------------------------------------------------------------------- */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #define BASEPATH "/proc/1/ns/uts" #define UTSPATH "/proc/%d/ns/uts" static int mypidfd, foreignuts; // Function that fills the host name (container/pod name) of // a specific process. // When the process has a different UTS namespace then systemd, // the process' UTS namespace will be temporarily associated with atop // itself to retrieve the host name (i.e. container/pod name) of that process. // To avoid too many namespace switches, atop only reassociates with its own // UTS namespace when the resetutsname() function is called. // // Return value: // 0 - no container/pod detected // 1 - container/pod detected // int getutsname(struct tstat *curtask) { static char firstcall = 1, basepath[64], basehost[32]; int pidfd, offset; ssize_t destlen; char srcpath[64], destpath[64], tmphost[70]; // regain root privs in case of setuid root executable // regainrootprivs(); // function initialization // if (firstcall) { firstcall = 0; // determine the UTS namespace of systemd and the hostname // related to systemd // if ( (destlen = readlink(BASEPATH, basepath, sizeof basepath)) == -1) goto drop_and_return; basepath[destlen] = '\0'; // guarantee delimeter if ( (pidfd = open(BASEPATH, O_RDONLY)) == -1) goto drop_and_return; if (setns(pidfd, CLONE_NEWUTS) == -1) { close(pidfd); goto drop_and_return; } foreignuts = 1; close(pidfd); gethostname(basehost, sizeof basehost); // determine the UTS namespace of atop itself // and open that namespace for reassociation via // function resetutsname() // snprintf(srcpath, sizeof srcpath, UTSPATH, getpid()); if ( (mypidfd = open(srcpath, O_RDONLY)) == -1) basepath[0] = '\0'; resetutsname(); } // no root privileges? // if (!basepath[0]) goto drop_and_return; // check if this pid is related to myself (atop) // if (curtask->gen.pid == getpid()) goto drop_and_return; // base path is known // verify if this process is native, i.e. does it share the // UTS namespace of systemd // snprintf(srcpath, sizeof srcpath, UTSPATH, curtask->gen.pid); destlen = readlink(srcpath, destpath, sizeof destpath); destpath[destlen] = '\0'; if (strcmp(basepath, destpath) == 0) // equal? goto drop_and_return; // UTS namespace deviates from base UTS namespace // // get hostname related to this UTS namespace by associating // atop to that namespace // if ( (pidfd = open(srcpath, O_RDONLY)) == -1) goto drop_and_return; if (setns(pidfd, CLONE_NEWUTS) == -1) { close(pidfd); goto drop_and_return; } foreignuts = 1; // set boolean close(pidfd); gethostname(tmphost, sizeof tmphost); // check if the hostname is the same as the hostname of // systemd (PID 1), because some systemd-related processes have // their own UTS namespace with the same hostname // also the hostname 'localhost' might be used by various daemons // and will be skipped as well // if ( strcmp(tmphost, basehost) == 0 || strcmp(tmphost, "localhost") == 0) goto drop_and_return; // this process really seems to be container/pod related // if ( (offset = strlen(tmphost) - UTSLEN) < 0) offset = 0; strcpy(curtask->gen.utsname, tmphost+offset); // copy last part when overflow if (! droprootprivs()) mcleanstop(42, "failed to drop root privs\n"); return 1; drop_and_return: if (! droprootprivs()) mcleanstop(42, "failed to drop root privs\n"); return 0; } // Reassociate atop with its own UTS namespace // void resetutsname(void) { // switch back to my own original UTS namespace // if (foreignuts) { foreignuts = 0; regainrootprivs(); if (setns(mypidfd, CLONE_NEWUTS) == -1) ; if (! droprootprivs()) mcleanstop(42, "failed to drop root privs\n"); } } atop-2.11.1/photoproc.h0000644000203100020310000001613214771753566014255 0ustar gerlofgerlof/* ** ATOP - System & Process Monitor ** ** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as process-level. ** ** Include-file describing process-level counters maintained and functions ** to access the process-database. ** ================================================================ ** Author: Gerlof Langeveld ** E-mail: gerlof.langeveld@atoptool.nl ** Date: November 1996 ** LINUX-port: June 2000 ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. */ #ifndef __PHOTOPROC__ #define __PHOTOPROC__ #define PNAMLEN 15 #define CMDLEN 255 #define CGRLEN 64 #define UTSLEN 15 /* ** structure containing only relevant process-info extracted ** from kernel's process-administration */ struct tstat { /* GENERAL TASK INFO */ struct gen { int tgid; /* threadgroup identification */ int pid; /* process identification */ int ppid; /* parent process identification*/ int ruid; /* real user identification */ int euid; /* eff. user identification */ int suid; /* saved user identification */ int fsuid; /* fs user identification */ int rgid; /* real group identification */ int egid; /* eff. group identification */ int sgid; /* saved group identification */ int fsgid; /* fs group identification */ int nthr; /* number of threads in tgroup */ char name[PNAMLEN+1];/* process name string */ char isproc; /* boolean: process level? */ char state; /* process state ('E' = exited) */ int excode; /* process exit status */ time_t btime; /* process start time (epoch) */ time_t elaps; /* process elaps time (hertz) */ char cmdline[CMDLEN+1];/* command-line string */ int nthrslpi; /* # threads in state 'S' */ int nthrslpu; /* # threads in state 'D' */ int nthrrun; /* # threads in state 'R' */ int nthridle; /* # threads in state 'I' */ int ctid; /* OpenVZ container ID */ int vpid; /* OpenVZ virtual PID */ int wasinactive; /* boolean: task inactive */ char utsname[UTSLEN+1];/* UTS name container or pod */ int cgroupix; /* index in devchain -1=invalid */ /* lazy filling (parsable/json) */ int ifuture[4]; /* reserved for future use */ } gen; /* CPU STATISTICS */ struct cpu { count_t utime; /* time user text (ticks) */ count_t stime; /* time system text (ticks) */ int nice; /* nice value */ int prio; /* priority */ int rtprio; /* realtime priority */ int policy; /* scheduling policy */ int curcpu; /* current processor */ int sleepavg; /* sleep average percentage */ int ifuture[6]; /* reserved for future use */ char wchan[16]; /* wait channel string */ count_t rundelay; /* schedstat rundelay (nanosec) */ count_t blkdelay; /* blkio delay (ticks) */ count_t nvcsw; /* voluntary cxt switch counts */ count_t nivcsw; /* involuntary csw counts */ count_t cfuture[3]; /* reserved for future use */ } cpu; /* DISK STATISTICS */ struct dsk { count_t rio; /* number of read requests */ count_t rsz; /* cumulative # sectors read */ count_t wio; /* number of write requests */ count_t wsz; /* cumulative # sectors written */ count_t cwsz; /* cumulative # written sectors */ /* being cancelled */ count_t cfuture[4]; /* reserved for future use */ } dsk; /* MEMORY STATISTICS */ struct mem { count_t minflt; /* number of page-reclaims */ count_t majflt; /* number of page-faults */ count_t vexec; /* virtmem execfile (Kb) */ count_t vmem; /* virtual memory (Kb) */ count_t rmem; /* resident memory (Kb) */ count_t pmem; /* resident memory (Kb) */ count_t vgrow; /* virtual growth (Kb) */ count_t rgrow; /* resident growth (Kb) */ count_t vdata; /* virtmem data (Kb) */ count_t vstack; /* virtmem stack (Kb) */ count_t vlibs; /* virtmem libexec (Kb) */ count_t vswap; /* swap space used (Kb) */ count_t vlock; /* virtual locked (Kb) */ count_t cfuture[7]; /* reserved for future use */ } mem; /* NETWORK STATISTICS */ struct net { count_t tcpsnd; /* number of TCP-packets sent */ count_t tcpssz; /* cumulative size packets sent */ count_t tcprcv; /* number of TCP-packets recved */ count_t tcprsz; /* cumulative size packets rcvd */ count_t udpsnd; /* number of UDP-packets sent */ count_t udpssz; /* cumulative size packets sent */ count_t udprcv; /* number of UDP-packets recved */ count_t udprsz; /* cumulative size packets sent */ count_t avail1; /* */ count_t avail2; /* */ count_t cfuture[4]; /* reserved for future use */ } net; struct gpu { char state; // A - active, E - Exit, '\0' - no use char bfuture[3]; // short nrgpus; // number of GPUs for this process int32_t gpulist; // bitlist with GPU numbers int gpubusy; // gpu busy perc process lifetime -1 = n/a int membusy; // memory busy perc process lifetime -1 = n/a count_t timems; // milliseconds accounting -1 = n/a // value 0 for active process, // value > 0 after termination count_t memnow; // current memory consumption in KiB count_t memcum; // cumulative memory consumption in KiB count_t sample; // number of samples count_t cfuture[3]; // } gpu; }; struct pinfo { struct pinfo *phnext; /* next process in hash chain */ struct pinfo *prnext; /* next process in residue chain */ struct pinfo *prprev; /* prev process in residue chain */ struct tstat tstat; /* per-process statistics */ }; /* ** structure to maintains all deviation info related to one sample */ struct devtstat { struct tstat *taskall; // all processes (also exited) and threads struct tstat **procall; // all processes (also exited) struct tstat **procactive; // all processes (also exited) being active unsigned long ntaskall; unsigned long ntaskactive; unsigned long nprocall; unsigned long nprocactive; unsigned long totrun, totslpi, totslpu, totidle, totzombie; }; /* ** prototypes of process-database functions */ int pdb_gettask(int, char, time_t, struct pinfo **); void pdb_addtask(int, struct pinfo *); int pdb_deltask(int, char); int pdb_makeresidue(void); int pdb_cleanresidue(void); int pdb_srchresidue(struct tstat *, struct pinfo **); /* ** prototypes for raw process-statistics functions */ struct netpertask; void deviattask(struct tstat *, unsigned long, struct tstat *, unsigned long, struct devtstat *, struct sstat *); unsigned long photoproc(struct tstat *, int); unsigned long counttasks(void); #endif atop-2.11.1/showprocs.c0000644000203100020310000023702714771753566014272 0ustar gerlofgerlof/* ** ATOP - System & Process Monitor ** ** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as cgroupess-level. ** ** This source-file contains the Linux-specific functions to calculate ** figures to be visualized on process/thread level and cgroup level. ** ========================================================================== ** Author: JC van Winkel - AT Computing, Nijmegen, Holland ** E-mail: jc@ATComputing.nl ** Date: November 2009 ** ** Process level ** -------------------------------------------------------------------------- ** Author: Gerlof Langeveld ** E-mail: gerlof.langeveld@atoptool.nl ** Date: January 2024 ** ** Additions for cgroups ** -------------------------------------------------------------------------- ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ** -------------------------------------------------------------------------- */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "atop.h" #include "photoproc.h" #include "photosyst.h" #include "cgroups.h" #include "showgeneric.h" #include "showlinux.h" static void format_bandw(char *, count_t); static void gettotwidth(detail_printpair *, int *, int *, int *); static int *getspacings(detail_printpair *); char *procprt_TID_ae(struct tstat *, int, int); char *procprt_PID_a(struct tstat *, int, int); char *procprt_PID_e(struct tstat *, int, int); char *procprt_PPID_a(struct tstat *, int, int); char *procprt_PPID_e(struct tstat *, int, int); char *procprt_VPID_a(struct tstat *, int, int); char *procprt_VPID_e(struct tstat *, int, int); char *procprt_CTID_a(struct tstat *, int, int); char *procprt_CTID_e(struct tstat *, int, int); char *procprt_CID_a(struct tstat *, int, int); char *procprt_CID_e(struct tstat *, int, int); char *procprt_SYSCPU_ae(struct tstat *, int, int); char *procprt_USRCPU_ae(struct tstat *, int, int); char *procprt_VGROW_a(struct tstat *, int, int); char *procprt_VGROW_e(struct tstat *, int, int); char *procprt_RGROW_a(struct tstat *, int, int); char *procprt_RGROW_e(struct tstat *, int, int); char *procprt_MINFLT_ae(struct tstat *, int, int); char *procprt_MAJFLT_ae(struct tstat *, int, int); char *procprt_VSTEXT_a(struct tstat *, int, int); char *procprt_VSTEXT_e(struct tstat *, int, int); char *procprt_VSIZE_a(struct tstat *, int, int); char *procprt_VSIZE_e(struct tstat *, int, int); char *procprt_RSIZE_a(struct tstat *, int, int); char *procprt_RSIZE_e(struct tstat *, int, int); char *procprt_PSIZE_a(struct tstat *, int, int); char *procprt_PSIZE_e(struct tstat *, int, int); char *procprt_VSLIBS_a(struct tstat *, int, int); char *procprt_VSLIBS_e(struct tstat *, int, int); char *procprt_VDATA_a(struct tstat *, int, int); char *procprt_VDATA_e(struct tstat *, int, int); char *procprt_VSTACK_a(struct tstat *, int, int); char *procprt_VSTACK_e(struct tstat *, int, int); char *procprt_SWAPSZ_a(struct tstat *, int, int); char *procprt_SWAPSZ_e(struct tstat *, int, int); char *procprt_LOCKSZ_a(struct tstat *, int, int); char *procprt_LOCKSZ_e(struct tstat *, int, int); char *procprt_CMD_a(struct tstat *, int, int); char *procprt_CMD_e(struct tstat *, int, int); char *procprt_RUID_ae(struct tstat *, int, int); char *procprt_EUID_a(struct tstat *, int, int); char *procprt_EUID_e(struct tstat *, int, int); char *procprt_SUID_a(struct tstat *, int, int); char *procprt_SUID_e(struct tstat *, int, int); char *procprt_FSUID_a(struct tstat *, int, int); char *procprt_FSUID_e(struct tstat *, int, int); char *procprt_RGID_ae(struct tstat *, int, int); char *procprt_EGID_a(struct tstat *, int, int); char *procprt_EGID_e(struct tstat *, int, int); char *procprt_SGID_a(struct tstat *, int, int); char *procprt_SGID_e(struct tstat *, int, int); char *procprt_FSGID_a(struct tstat *, int, int); char *procprt_FSGID_e(struct tstat *, int, int); char *procprt_STDATE_ae(struct tstat *, int, int); char *procprt_STTIME_ae(struct tstat *, int, int); char *procprt_ENDATE_a(struct tstat *, int, int); char *procprt_ENDATE_e(struct tstat *, int, int); char *procprt_ENTIME_a(struct tstat *, int, int); char *procprt_ENTIME_e(struct tstat *, int, int); char *procprt_THR_a(struct tstat *, int, int); char *procprt_THR_e(struct tstat *, int, int); char *procprt_TRUN_a(struct tstat *, int, int); char *procprt_TRUN_e(struct tstat *, int, int); char *procprt_TSLPI_a(struct tstat *, int, int); char *procprt_TSLPI_e(struct tstat *, int, int); char *procprt_TSLPU_a(struct tstat *, int, int); char *procprt_TSLPU_e(struct tstat *, int, int); char *procprt_TIDLE_a(struct tstat *, int, int); char *procprt_TIDLE_e(struct tstat *, int, int); char *procprt_POLI_a(struct tstat *, int, int); char *procprt_POLI_e(struct tstat *, int, int); char *procprt_NICE_a(struct tstat *, int, int); char *procprt_NICE_e(struct tstat *, int, int); char *procprt_PRI_a(struct tstat *, int, int); char *procprt_PRI_e(struct tstat *, int, int); char *procprt_RTPR_a(struct tstat *, int, int); char *procprt_RTPR_e(struct tstat *, int, int); char *procprt_CURCPU_a(struct tstat *, int, int); char *procprt_CURCPU_e(struct tstat *, int, int); char *procprt_ST_a(struct tstat *, int, int); char *procprt_ST_e(struct tstat *, int, int); char *procprt_EXC_a(struct tstat *, int, int); char *procprt_EXC_e(struct tstat *, int, int); char *procprt_S_a(struct tstat *, int, int); char *procprt_S_e(struct tstat *, int, int); char *procprt_COMMAND_LINE_ae(struct tstat *, int, int); char *procprt_NPROCS_ae(struct tstat *, int, int); char *procprt_RDDSK_a(struct tstat *, int, int); char *procprt_RDDSK_e(struct tstat *, int, int); char *procprt_WRDSK_a(struct tstat *, int, int); char *procprt_WRDSK_e(struct tstat *, int, int); char *procprt_CWRDSK_a(struct tstat *, int, int); char *procprt_WCANCEL_a(struct tstat *, int, int); char *procprt_WCANCEL_e(struct tstat *, int, int); char *procprt_BANDWI_a(struct tstat *, int, int); char *procprt_BANDWI_e(struct tstat *, int, int); char *procprt_BANDWO_a(struct tstat *, int, int); char *procprt_BANDWO_e(struct tstat *, int, int); char *procprt_GPULIST_ae(struct tstat *, int, int); char *procprt_GPUMEMNOW_ae(struct tstat *, int, int); char *procprt_GPUMEMAVG_ae(struct tstat *, int, int); char *procprt_GPUGPUBUSY_ae(struct tstat *, int, int); char *procprt_GPUMEMBUSY_ae(struct tstat *, int, int); char *procprt_WCHAN_a(struct tstat *, int, int); char *procprt_WCHAN_e(struct tstat *, int, int); char *procprt_RUNDELAY_a(struct tstat *, int, int); char *procprt_RUNDELAY_e(struct tstat *, int, int); char *procprt_BLKDELAY_a(struct tstat *, int, int); char *procprt_BLKDELAY_e(struct tstat *, int, int); char *procprt_NVCSW_a(struct tstat *, int, int); char *procprt_NVCSW_e(struct tstat *, int, int); char *procprt_NIVCSW_a(struct tstat *, int, int); char *procprt_NIVCSW_e(struct tstat *, int, int); char *procprt_SORTITEM_ae(struct tstat *, int, int); char *cgroup_CGROUP_PATH(struct cgchainer *, struct tstat *, int, int, count_t, int, int *); char *cgroup_CGRNPROCS(struct cgchainer *, struct tstat *, int, int, count_t, int, int *); char *cgroup_CGRNPROCSB(struct cgchainer *, struct tstat *, int, int, count_t, int, int *); char *cgroup_CGRCPUBUSY(struct cgchainer *, struct tstat *, int, int, count_t, int, int *); char *cgroup_CGRCPUPSI(struct cgchainer *, struct tstat *, int, int, count_t, int, int *); char *cgroup_CGRCPUWGT(struct cgchainer *, struct tstat *, int, int, count_t, int, int *); char *cgroup_CGRCPUMAX(struct cgchainer *, struct tstat *, int, int, count_t, int, int *); char *cgroup_CGRMEMORY(struct cgchainer *, struct tstat *, int, int, count_t, int, int *); char *cgroup_CGRMEMPSI(struct cgchainer *, struct tstat *, int, int, count_t, int, int *); char *cgroup_CGRMEMMAX(struct cgchainer *, struct tstat *, int, int, count_t, int, int *); char *cgroup_CGRSWPMAX(struct cgchainer *, struct tstat *, int, int, count_t, int, int *); char *cgroup_CGRDISKIO(struct cgchainer *, struct tstat *, int, int, count_t, int, int *); char *cgroup_CGRDSKPSI(struct cgchainer *, struct tstat *, int, int, count_t, int, int *); char *cgroup_CGRDSKWGT(struct cgchainer *, struct tstat *, int, int, count_t, int, int *); char *cgroup_CGRPID(struct cgchainer *, struct tstat *, int, int, count_t, int, int *); char *cgroup_CGRCMD(struct cgchainer *, struct tstat *, int, int, count_t, int, int *); static char *columnhead[] = { [MSORTCPU]= "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 detail_printpair newelems[MAXITEMS]; // ugly static var, // but saves a lot of recomputations // contains the actual list of items to // be printed /***************************************************************/ /* * gettotwidth: calculate the sum of widths and number of columns * Also copys the detail_printpair elements to the static array newelems * for later removal of lower priority elements. * Params: * elemptr: the array of what to print * nitems: (ref) returns the number of printitems in the array * sumwidth: (ref) returns the total width of the printitems in the array * varwidth: (ref) returns the number of variable width items in the array */ static void gettotwidth(detail_printpair *elemptr, int *nitems, int *sumwidth, int* varwidth) { int i; int col; int varw=0; for (i=0, col=0; elemptr[i].pf!=0; ++i) { col += (elemptr[i].pf->varwidth ? 0 : elemptr[i].pf->width); varw += elemptr[i].pf->varwidth; newelems[i]=elemptr[i]; // copy element } newelems[i].pf=0; *nitems=i; *sumwidth=col; *varwidth=varw; } /***************************************************************/ /* * getspacings: determine how much extra space there is for * inter-column space. * returns an int array this number of spaces to add after each column * also removes items from the newelems array if the available width * is lower than what is needed. The lowest priority columns are * removed first. * * Note: this function is only to be called when screen is true. */ static int * getspacings(detail_printpair *elemptr) { static int spacings[MAXITEMS]; int col=0; int nitems; int varwidth=0; int j; int maxw=screen ? COLS : linelen; // for non screen: 80 columns max // get width etc; copy elemptr array to static newelms gettotwidth(elemptr, &nitems, &col, &varwidth); /* cases: * 1) nitems==1: just one column, no spacing needed. Done * * 2) total width is more than COLS: remove low prio columns * 2a) a varwidth column: no spacing needed * 2b) total width is less than COLS: compute inter spacing */ if (nitems==1) // no inter column spacing if 1 column { spacings[0]=0; return spacings; } // Check if available width is less than required. // If so, delete columns to make things fit // space required: // width + (nitems-1) * 1 space + 12 for a varwidth column. while (col + nitems-1+ 12*varwidth > maxw) { int lowestprio=999999; int lowestprio_index=-1; int i; for (i=0; iwidth; varwidth -= newelems[lowestprio_index].pf->varwidth; memmove(newelems+lowestprio_index, newelems+lowestprio_index+1, (nitems-lowestprio_index)* sizeof(detail_printpair)); // also copies final 0 entry nitems--; } /* if there is a var width column, handle that separately */ if (varwidth) { for (j=0; jvarwidth) { elemptr[j].pf->width=maxw-col-(nitems-1); // only nitems-1 in-between spaces // needed } } return spacings; } // avoid division by 0 if (nitems==1) { spacings[0]=0; return spacings; } /* fixed columns, spread whitespace over columns */ double over=(0.0+maxw-col)/(nitems-1); double todo=over; for (j=0; jhead==0) // empty header==special: SORTITEM { chead = columnhead[order]; autoindic = autosort ? "A" : " "; widen = procprt_SORTITEM.width-3; } else { chead=curelem.pf->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); align=COLS-col-pagindiclen; // extra spaces needed if (align >= 0) // align by adding spaces { sprintf(buf+col, "%*s", align+pagindiclen, pagindic); } else if (col+align >= 0) { // align by removing from the right sprintf(buf+col+align, "%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(detail_printpair* elemptr, struct tstat *curstat, double perc, int nsecs, int avgval) { detail_printpair curelem; elemptr=newelems; // point to static array int n=0; if (screen && threadview) { if (usecolors && !curstat->gen.isproc) { attron(COLOR_PAIR(FGCOLORTHR)); } else { if (!usecolors && curstat->gen.isproc) attron(A_BOLD); } } while ((curelem=*elemptr).pf!=0) { // what to print? SORTITEM, or active process or // exited process? if (curelem.pf->head==0) // empty string=sortitem { printg("%*.0lf%%", procprt_SORTITEM.width-1, perc); } else if (curstat->gen.state != 'E') // active process { printg("%s", curelem.pf->doactiveconvert(curstat, avgval, nsecs)); } else // exited process { printg("%s", curelem.pf->doexitconvert(curstat, avgval, nsecs)); } if (screen) { printg("%*s",colspacings[n], ""); } else { printg(" "); } elemptr++; n++; } if (screen && threadview) { if (usecolors && !curstat->gen.isproc) { attroff(COLOR_PAIR(FGCOLORTHR)); } else { if (!usecolors && curstat->gen.isproc) attroff(A_BOLD); } } if (!screen) { printg("\n"); } } /*******************************************************************/ /* 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; } detail_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; } detail_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; } detail_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; } detail_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 " -"; } detail_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; } detail_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; } detail_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; } detail_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"; } detail_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"; } detail_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; } detail_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; } detail_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"; } detail_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"; } detail_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"; } detail_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"; } detail_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"; } detail_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"; } detail_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"; } detail_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"; } detail_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"; } detail_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; } detail_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; } detail_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 "- "; } detail_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 "- "; } detail_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 "- "; } detail_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; } detail_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 "- "; } detail_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 "- "; } detail_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 "- "; } detail_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; } detail_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; } detail_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; } detail_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; } detail_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"; } detail_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"; } detail_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"; } detail_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"; } detail_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"; } detail_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 "- "; } detail_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 " -"; } detail_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 " -"; } detail_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 " -"; } detail_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 " -"; } detail_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; } detail_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; } detail_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"; } detail_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 detail_printdef procprt_COMMAND_LINE; extern int startoffset; // influenced by -> and <- keys static char buf[CMDLEN+1]; char *pline = curstat->gen.cmdline[0] ? curstat->gen.cmdline : curstat->gen.name; int curwidth = procprt_COMMAND_LINE.width <= CMDLEN ? procprt_COMMAND_LINE.width : CMDLEN; int cmdlen = strlen(pline); int curoffset = startoffset <= cmdlen ? startoffset : cmdlen; if (screen) sprintf(buf, "%-*.*s", curwidth, curwidth, pline+curoffset); else sprintf(buf, "%.*s", CMDLEN, pline+curoffset); return buf; } detail_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; } detail_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]; if (supportflags & IOSTAT) val2memstr(curstat->dsk.rsz*512, buf, BFORMAT, avgval, nsecs); else strcpy(buf, "nopriv"); return buf; } char * procprt_RDDSK_e(struct tstat *curstat, int avgval, int nsecs) { return " -"; } detail_printdef procprt_RDDSK = { " RDDSK", "RDDSK", procprt_RDDSK_a, procprt_RDDSK_e, ' ', 6}; /***************************************************************/ char * procprt_WRDSK_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; if (supportflags & IOSTAT) val2memstr(curstat->dsk.wsz*512, buf, BFORMAT, avgval, nsecs); else strcpy(buf, "nopriv"); return buf; } char * procprt_WRDSK_e(struct tstat *curstat, int avgval, int nsecs) { return " -"; } detail_printdef procprt_WRDSK = { " WRDSK", "WRDSK", procprt_WRDSK_a, procprt_WRDSK_e, ' ', 6}; /***************************************************************/ char * procprt_CWRDSK_a(struct tstat *curstat, int avgval, int nsecs) { count_t nett_wsz; static char buf[10]; if (curstat->dsk.wsz > curstat->dsk.cwsz) nett_wsz = curstat->dsk.wsz - curstat->dsk.cwsz; else nett_wsz = 0; val2memstr(nett_wsz*512, buf, BFORMAT, avgval, nsecs); return buf; } detail_printdef procprt_CWRDSK = {" WRDSK", "CWRDSK", procprt_CWRDSK_a, procprt_WRDSK_e, ' ', 6}; /***************************************************************/ char * procprt_WCANCEL_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; if (supportflags & IOSTAT) val2memstr(curstat->dsk.cwsz*512, buf, BFORMAT, avgval, nsecs); else strcpy(buf, "nopriv"); return buf; } char * procprt_WCANCEL_e(struct tstat *curstat, int avgval, int nsecs) { return " -"; } detail_printdef procprt_WCANCEL = {"WCANCL", "WCANCL", procprt_WCANCEL_a, procprt_WCANCEL_e, ' ', 6}; /***************************************************************/ char * procprt_TCPRCV_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; val2valstr(curstat->net.tcprcv, buf, 6, avgval, nsecs); return buf; } char * procprt_TCPRCV_e(struct tstat *curstat, int avgval, int nsecs) { if (supportflags & NETATOPD || supportflags & NETATOPBPF ) { static char buf[10]; val2valstr(curstat->net.tcprcv, buf, 6, avgval, nsecs); return buf; } else return " -"; } detail_printdef procprt_TCPRCV = { "TCPRCV", "TCPRCV", procprt_TCPRCV_a, procprt_TCPRCV_e, ' ', 6}; /***************************************************************/ char * procprt_TCPRASZ_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; int avgtcpr = curstat->net.tcprcv ? curstat->net.tcprsz / curstat->net.tcprcv : 0; val2valstr(avgtcpr, buf, 7, 0, 0); return buf; } char * procprt_TCPRASZ_e(struct tstat *curstat, int avgval, int nsecs) { if (supportflags & NETATOPD || supportflags & NETATOPBPF ) { static char buf[10]; int avgtcpr = curstat->net.tcprcv ? curstat->net.tcprsz / curstat->net.tcprcv : 0; val2valstr(avgtcpr, buf, 7, 0, 0); return buf; } else return " -"; } detail_printdef procprt_TCPRASZ = { "TCPRASZ", "TCPRASZ", procprt_TCPRASZ_a, procprt_TCPRASZ_e, ' ', 7}; /***************************************************************/ char * procprt_TCPSND_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; val2valstr(curstat->net.tcpsnd, buf, 6, avgval, nsecs); return buf; } char * procprt_TCPSND_e(struct tstat *curstat, int avgval, int nsecs) { if (supportflags & NETATOPD || supportflags & NETATOPBPF ) { static char buf[10]; val2valstr(curstat->net.tcpsnd, buf, 6, avgval, nsecs); return buf; } else return " -"; } detail_printdef procprt_TCPSND = { "TCPSND", "TCPSND", procprt_TCPSND_a, procprt_TCPSND_e, ' ', 6}; /***************************************************************/ char * procprt_TCPSASZ_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; int avgtcps = curstat->net.tcpsnd ? curstat->net.tcpssz / curstat->net.tcpsnd : 0; val2valstr(avgtcps, buf, 7, 0, 0); return buf; } char * procprt_TCPSASZ_e(struct tstat *curstat, int avgval, int nsecs) { if (supportflags & NETATOPD || supportflags & NETATOPBPF ) { static char buf[10]; int avgtcps = curstat->net.tcpsnd ? curstat->net.tcpssz / curstat->net.tcpsnd : 0; val2valstr(avgtcps, buf, 7, 0, 0); return buf; } else return " -"; } detail_printdef procprt_TCPSASZ = { "TCPSASZ", "TCPSASZ", procprt_TCPSASZ_a, procprt_TCPSASZ_e, ' ', 7}; /***************************************************************/ char * procprt_UDPRCV_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; val2valstr(curstat->net.udprcv, buf, 6, avgval, nsecs); return buf; } char * procprt_UDPRCV_e(struct tstat *curstat, int avgval, int nsecs) { if (supportflags & NETATOPD || supportflags & NETATOPBPF ) { static char buf[10]; val2valstr(curstat->net.udprcv, buf, 6, avgval, nsecs); return buf; } else return " -"; } detail_printdef procprt_UDPRCV = { "UDPRCV", "UDPRCV", procprt_UDPRCV_a, procprt_UDPRCV_e, ' ', 6}; /***************************************************************/ char * procprt_UDPRASZ_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; int avgudpr = curstat->net.udprcv ? curstat->net.udprsz / curstat->net.udprcv : 0; val2valstr(avgudpr, buf, 7, 0, 0); return buf; } char * procprt_UDPRASZ_e(struct tstat *curstat, int avgval, int nsecs) { if (supportflags & NETATOPD || supportflags & NETATOPBPF ) { static char buf[10]; int avgudpr = curstat->net.udprcv ? curstat->net.udprsz / curstat->net.udprcv : 0; val2valstr(avgudpr, buf, 7, 0, 0); return buf; } else return " -"; } detail_printdef procprt_UDPRASZ = { "UDPRASZ", "UDPRASZ", procprt_UDPRASZ_a, procprt_UDPRASZ_e, ' ', 7}; /***************************************************************/ char * procprt_UDPSND_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; val2valstr(curstat->net.udpsnd, buf, 6, avgval, nsecs); return buf; } char * procprt_UDPSND_e(struct tstat *curstat, int avgval, int nsecs) { if (supportflags & NETATOPD || supportflags & NETATOPBPF ) { static char buf[10]; val2valstr(curstat->net.udpsnd, buf, 6, avgval, nsecs); return buf; } else return " -"; } detail_printdef procprt_UDPSND = { "UDPSND", "UDPSND", procprt_UDPSND_a, procprt_UDPSND_e, ' ', 6}; /***************************************************************/ char * procprt_UDPSASZ_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; int avgudps = curstat->net.udpsnd ? curstat->net.udpssz / curstat->net.udpsnd : 0; val2valstr(avgudps, buf, 7, 0, 0); return buf; } char * procprt_UDPSASZ_e(struct tstat *curstat, int avgval, int nsecs) { if (supportflags & NETATOPD || supportflags & NETATOPBPF ) { static char buf[10]; int avgudps = curstat->net.udpsnd ? curstat->net.udpssz / curstat->net.udpsnd : 0; val2valstr(avgudps, buf, 7, 0, 0); return buf; } else return " -"; } detail_printdef procprt_UDPSASZ = { "UDPSASZ", "UDPSASZ", procprt_UDPSASZ_a, procprt_UDPSASZ_e, ' ', 7}; /***************************************************************/ char * procprt_RNET_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; val2valstr(curstat->net.tcprcv + curstat->net.udprcv , buf, 5, avgval, nsecs); return buf; } char * procprt_RNET_e(struct tstat *curstat, int avgval, int nsecs) { if (supportflags & NETATOPD || supportflags & NETATOPBPF ) { static char buf[10]; val2valstr(curstat->net.tcprcv + curstat->net.udprcv , buf, 5, avgval, nsecs); return buf; } else return " -"; } detail_printdef procprt_RNET = { " RNET", "RNET", procprt_RNET_a, procprt_RNET_e, ' ', 5}; /***************************************************************/ char * procprt_SNET_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[10]; val2valstr(curstat->net.tcpsnd + curstat->net.udpsnd, buf, 5, avgval, nsecs); return buf; } char * procprt_SNET_e(struct tstat *curstat, int avgval, int nsecs) { if (supportflags & NETATOPD || supportflags & NETATOPBPF ) { static char buf[10]; val2valstr(curstat->net.tcpsnd + curstat->net.udpsnd, buf, 5, avgval, nsecs); return buf; } else return " -"; } detail_printdef procprt_SNET = { " SNET", "SNET", 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 " -"; } detail_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 " -"; } detail_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; } detail_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; } detail_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; } detail_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; } detail_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; } detail_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; } detail_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; } detail_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; } detail_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 " -"; } detail_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 " -"; } detail_printdef procprt_NIVCSW = { "NIVCSW", "NIVCSW", procprt_NIVCSW_a, procprt_NIVCSW_e, ' ', 6}; /***************************************************************/ char * procprt_SORTITEM_ae(struct tstat *curstat, int avgval, int nsecs) { return ""; // dummy function } detail_printdef procprt_SORTITEM = // width is dynamically defined! { 0, "SORTITEM", procprt_SORTITEM_ae, procprt_SORTITEM_ae, ' ', 4}; /***************************************************************/ /* CGROUP LEVEL FORMATTING */ /***************************************************************/ /* * showcgrouphead: show header line for cgroups. * if in interactive mode, also add a page number * if in interactive mode, columns are aligned to fill out rows */ void showcgrouphead(detail_printpair *elemptr, int curlist, int totlist, char showorder) { detail_printpair curelem; char *chead=""; int col=0, curline; char pagindic[16]; int pagindiclen; int n=0; int bufsz; int maxw=screen ? COLS : linelen; colspacings = getspacings(elemptr); bufsz = maxw+1; elemptr = newelems; // point to adjusted array char buf[bufsz+2]; // long live dynamically sized auto arrays... if (screen) getyx(stdscr, curline, col); // get current line else printg("\n"); // show column by column // while ((curelem=*elemptr).pf!=0) { chead = curelem.pf->head; if (screen) { // print header, optionally colored when it is // the current sort criterion // if (showorder == curelem.pf->sortcrit) { if (usecolors) attron(COLOR_PAIR(FGCOLORINFO)); else attron(A_BOLD); } printg("%s", chead); if (showorder == curelem.pf->sortcrit) { if (usecolors) attroff(COLOR_PAIR(FGCOLORINFO)); else attroff(A_BOLD); } // print filler spaces // printg("%*s", colspacings[n], ""); } else { col += sprintf(buf+col, "%s%s ", "", chead); } elemptr++; n++; } if (screen) // add page number, eat from last header if needed... { pagindiclen = sprintf(pagindic,"%d/%d", curlist, totlist); move(curline, COLS-pagindiclen); printg("%s", pagindic); } else // no screen: print entire buffer at once { printg("%s\n", buf); } } /***************************************************************/ /* * showcgroupline: show line for cgroups. * if in interactive mode, columns are aligned to fill out rows * params: * elemptr: pointer to array of print definition structs ptrs * cgchain: the cgroup to print * tstat: the process to print (is NULL for a cgroup line) * nsecs: number of seconds elapsed between previous and this sample * avgval: is averaging out per second needed? * cputicks: total ticks elapsed * nrcpu: number of CPUs */ void showcgroupline(detail_printpair* elemptr, struct cgchainer *cgchain, struct tstat *tstat, int nsecs, int avgval, count_t cputicks, int nrcpu) { detail_printpair curelem; int n=0, color=0, linecolor=0; elemptr=newelems; // point to static array if (screen) { if (cgchain->cstat->gen.depth <= 1) // zero or first cgroup level? linecolor = FGCOLORINFO; if (tstat) // process info line? linecolor = FGCOLORBORDER; } while ((curelem=*elemptr).pf!=0) { char *buf; color = 0; buf = curelem.pf->doactiveconvert(cgchain, tstat, avgval, nsecs, cputicks, nrcpu, &color); if (screen) { if (cgchain->cstat->gen.depth == 0 && !tstat) // root cgroup? attron(A_BOLD); if (color == 0) // no explicit color from conversion fc color = linecolor; if (color) { if (usecolors) attron(COLOR_PAIR(color)); else attron(A_BOLD); } printg("%s", buf); printg("%*s",colspacings[n], ""); if (color) { if (usecolors) attroff(COLOR_PAIR(color)); else attroff(A_BOLD); } if (cgchain->cstat->gen.depth == 0 && !tstat) // root cgroup? attroff(A_BOLD); } else { printg("%s", buf); printg(" "); } elemptr++; n++; } if (!screen) printg("\n"); } /***************************************************************/ char * cgroup_CGROUP_PATH(struct cgchainer *cgchain, struct tstat *tstat, int avgval, int nsecs, count_t cputicks, int nrcpu, int *color) { static char buf[4098]; extern detail_printdef cgroupprt_CGROUP_PATH; extern int startoffset; // influenced by -> and <- keys int i; char *cgrname = cgchain->cstat->cgname; int namelen = cgchain->cstat->gen.namelen; int cgrdepth = cgchain->cstat->gen.depth; unsigned long vlinemask = cgchain->vlinemask; if (*cgrname == '\0') // root cgroup? { cgrname = "/"; namelen = 1; } int maxnamelen = cgroupprt_CGROUP_PATH.width - (cgrdepth*3); int curoffset = startoffset <= namelen ? startoffset : namelen; if (screen) { switch (cgrdepth) { case 0: sprintf(buf, "%-*s", cgroupprt_CGROUP_PATH.width, "/"); break; default: // draw continuous vertical bars for // previous levels if not stub // for (i=0; i < cgrdepth-1; i++) { if (i >= CGRMAXDEPTH || vlinemask & (1<= CGRMAXDEPTH || cgchain->stub) addch(ACS_LLCORNER); else addch(ACS_LTEE); addch(ACS_HLINE); } else // process line { if (cgrdepth >= CGRMAXDEPTH || cgchain->stub) addch(' '); else addch(ACS_VLINE); addch(' '); } sprintf(buf, " %-*.*s", maxnamelen, maxnamelen, cgrname+curoffset); } } else { sprintf(buf, "%*s%-*.*s", cgrdepth*2, "", cgroupprt_CGROUP_PATH.width - cgrdepth*2, cgroupprt_CGROUP_PATH.width - cgrdepth*2, cgrname); } return buf; } detail_printdef cgroupprt_CGROUP_PATH = {"CGROUP (scroll: <- ->) ", "CGRPATH", cgroup_CGROUP_PATH, NULL, ' ', 26, 0}; /***************************************************************/ char * cgroup_CGRNPROCS(struct cgchainer *cgchain, struct tstat *tstat, int avgval, int nsecs, count_t cputicks, int nrcpu, int *color) { static char buf[10]; if (tstat) // process info? return " "; // cgroup info val2valstr(cgchain->cstat->gen.nprocs, buf, 6, 0, 0); return buf; } detail_printdef cgroupprt_CGRNPROCS = { "NPROCS", "CGRNPROCS", cgroup_CGRNPROCS, NULL, ' ', 6}; /***************************************************************/ char * cgroup_CGRNPROCSB(struct cgchainer *cgchain, struct tstat *tstat, int avgval, int nsecs, count_t cputicks, int nrcpu, int *color) { static char buf[10]; if (tstat) // process info? return " "; // cgroup info val2valstr(cgchain->cstat->gen.procsbelow, buf, 6, 0, 0); return buf; } detail_printdef cgroupprt_CGRNPROCSB = { "PBELOW", "CGRNPROCSB", cgroup_CGRNPROCSB, NULL, ' ', 6}; /***************************************************************/ char * cgroup_CGRCPUBUSY(struct cgchainer *cgchain, struct tstat *tstat, int avgval, int nsecs, count_t cputicks, int nrcpu, int *color) { static char buf[16]; float perc; int maxperc, badness = 0; if (!tstat) // cgroup info? { if (cgchain->cstat->cpu.utime == -1) // undefined? return " -"; perc = (cgchain->cstat->cpu.utime + cgchain->cstat->cpu.stime) / (cputicks/nrcpu*100.0); maxperc = cgchain->cstat->conf.cpumax; // determine if CPU load is limited on system level // if (cpubadness) badness = perc / nrcpu * 100.0 / cpubadness; if (badness >= 100) *color = FGCOLORCRIT; // determine if CPU load is limited by cpu.max within cgroup // if (maxperc >= 0 && perc + 2 >= maxperc) *color = FGCOLORCRIT; } else // process info { perc = (tstat->cpu.utime + tstat->cpu.stime) * 100.0 / (cputicks/nrcpu); } if (perc < 1000.0) snprintf(buf, sizeof buf, "%6.2f%%", perc); else if (perc < 10000.0) snprintf(buf, sizeof buf, "%6.1f%%", perc); else snprintf(buf, sizeof buf, "%6.0f%%", perc); return buf; } detail_printdef cgroupprt_CGRCPUBUSY = { "CPUBUSY", "CGRCPUBUSY", cgroup_CGRCPUBUSY, NULL, 'C', 7}; /***************************************************************/ char * cgroup_CGRCPUPSI(struct cgchainer *cgchain, struct tstat *tstat, int avgval, int nsecs, count_t cputicks, int nrcpu, int *color) { static char buf[16]; float perc; if (tstat) // process info? return " "; // cgroup info switch (cgchain->cstat->cpu.somepres) { case -1: return " -"; default: perc = cgchain->cstat->cpu.somepres / (cputicks/nrcpu*100.0); if (perc >= 25.0) *color = FGCOLORCRIT; snprintf(buf, sizeof buf, "%4.0f%%", perc); return buf; } } detail_printdef cgroupprt_CGRCPUPSI = { "CPUPS", "CGRCPUPSI", cgroup_CGRCPUPSI, NULL, ' ', 5}; /***************************************************************/ char * cgroup_CGRCPUMAX(struct cgchainer *cgchain, struct tstat *tstat, int avgval, int nsecs, count_t cputicks, int nrcpu, int *color) { static char buf[16]; float perc; int maxperc; if (tstat) // process info? return " "; maxperc = cgchain->cstat->conf.cpumax; // when current cpu percentage is colored due to limitation // by cpu.max, also color cpu.max itself // if (cgchain->cstat->cpu.utime != -1) // cpu usage available? { perc = (cgchain->cstat->cpu.utime + cgchain->cstat->cpu.stime) / (cputicks/nrcpu*100.0); if (maxperc >= 0 && perc + 2 >= maxperc) *color = FGCOLORCRIT; } // cgroup info // switch (cgchain->cstat->conf.cpumax) { case -1: return " max"; case -2: return " -"; default: snprintf(buf, sizeof buf, "%5d%%", maxperc); return buf; } } detail_printdef cgroupprt_CGRCPUMAX = { "CPUMAX", "CGRCPUMAX", cgroup_CGRCPUMAX, NULL, ' ', 6}; /***************************************************************/ char * cgroup_CGRCPUWGT(struct cgchainer *cgchain, struct tstat *tstat, int avgval, int nsecs, count_t cputicks, int nrcpu, int *color) { static char buf[16]; if (tstat) // process info? return " "; // cgroup info switch (cgchain->cstat->conf.cpuweight) { case -2: return " -"; default: snprintf(buf, sizeof buf, "%6d", cgchain->cstat->conf.cpuweight); return buf; } } detail_printdef cgroupprt_CGRCPUWGT = { "CPUWGT", "CGRCPUWGT", cgroup_CGRCPUWGT, NULL, ' ', 6}; /***************************************************************/ char * cgroup_CGRMEMORY(struct cgchainer *cgchain, struct tstat *tstat, int avgval, int nsecs, count_t cputicks, int nrcpu, int *color) { static char buf[16]; count_t memusage, maxusage; if (!tstat) // show cgroup info? { if (cgchain->cstat->mem.current > 0) // defined? { memusage = cgchain->cstat->mem.current; } else { if (cgchain->cstat->mem.anon == -1) // undefined? return " -"; memusage = (cgchain->cstat->mem.anon + cgchain->cstat->mem.file + cgchain->cstat->mem.kernel + cgchain->cstat->mem.shmem); } maxusage = cgchain->cstat->conf.memmax; // set color if occupation percentage > 95% // if (maxusage > 0 && memusage * 100 / maxusage > 95) *color = FGCOLORCRIT; val2memstr(memusage * pagesize, buf, BFORMAT, 0, 0); } else // show process info { val2memstr(tstat->mem.rmem*1024, buf, BFORMAT, 0, 0); } return buf; } detail_printdef cgroupprt_CGRMEMORY = { "MEMORY", "CGRMEMORY", cgroup_CGRMEMORY, NULL, 'M', 6}; /***************************************************************/ char * cgroup_CGRMEMPSI(struct cgchainer *cgchain, struct tstat *tstat, int avgval, int nsecs, count_t cputicks, int nrcpu, int *color) { static char buf[16]; float perc; if (tstat) // process info? return " "; // cgroup info switch (cgchain->cstat->mem.somepres) { case -1: return " -"; default: perc = cgchain->cstat->mem.fullpres / (cputicks/nrcpu*100.0); if (perc >= 20.0) *color = FGCOLORCRIT; snprintf(buf, sizeof buf, "%4.0f%%", perc); return buf; } } detail_printdef cgroupprt_CGRMEMPSI = { "MEMPS", "CGRMEMPSI", cgroup_CGRMEMPSI, NULL, ' ', 5}; /***************************************************************/ char * cgroup_CGRMEMMAX(struct cgchainer *cgchain, struct tstat *tstat, int avgval, int nsecs, count_t cputicks, int nrcpu, int *color) { static char buf[16]; count_t memusage, maxusage; if (tstat) // process info? return " "; maxusage = cgchain->cstat->conf.memmax; if (cgchain->cstat->mem.anon != -1) // current usage defined? { memusage = (cgchain->cstat->mem.anon + cgchain->cstat->mem.file + cgchain->cstat->mem.kernel); // set color if occupation percentage > 95% // if (maxusage > 0 && memusage * 100 / maxusage > 95) *color = FGCOLORCRIT; } // cgroup info switch (cgchain->cstat->conf.memmax) { case -1: return " max"; case -2: return " -"; default: val2memstr(maxusage*pagesize, buf, BFORMAT, 0, 0); return buf; } } detail_printdef cgroupprt_CGRMEMMAX = { "MEMMAX", "CGRMEMMAX", cgroup_CGRMEMMAX, NULL, ' ', 6}; /***************************************************************/ char * cgroup_CGRSWPMAX(struct cgchainer *cgchain, struct tstat *tstat, int avgval, int nsecs, count_t cputicks, int nrcpu, int *color) { static char buf[16]; if (tstat) // process info? return " "; // cgroup info switch (cgchain->cstat->conf.swpmax) { case -1: return " max"; case -2: return " -"; default: val2memstr(cgchain->cstat->conf.swpmax*pagesize, buf, BFORMAT, 0, 0); return buf; } } detail_printdef cgroupprt_CGRSWPMAX = { "SWPMAX", "CGRSWPMAX", cgroup_CGRSWPMAX, NULL, ' ', 6}; /***************************************************************/ char * cgroup_CGRDISKIO(struct cgchainer *cgchain, struct tstat *tstat, int avgval, int nsecs, count_t cputicks, int nrcpu, int *color) { static char buf[16]; if (!tstat) // show cgroup info? { if (cgchain->cstat->dsk.rbytes == -1) // not defined? return " -"; val2memstr(cgchain->cstat->dsk.rbytes + cgchain->cstat->dsk.wbytes, buf, BFORMAT, avgval, nsecs); } else // show process info { if (supportflags & IOSTAT) val2memstr((tstat->dsk.rsz+tstat->dsk.wsz)*512, buf, BFORMAT, avgval, nsecs); else strcpy(buf, "nopriv"); } return buf; } detail_printdef cgroupprt_CGRDISKIO = { "DISKIO", "CGRDISKIO", cgroup_CGRDISKIO, NULL, 'D', 6}; /***************************************************************/ char * cgroup_CGRDSKPSI(struct cgchainer *cgchain, struct tstat *tstat, int avgval, int nsecs, count_t cputicks, int nrcpu, int *color) { static char buf[16]; float perc; if (tstat) // process info? return " "; // cgroup info switch (cgchain->cstat->dsk.somepres) { case -1: return " -"; default: perc = cgchain->cstat->dsk.fullpres / (cputicks/nrcpu*100.0); if (perc >= 25.0) *color = FGCOLORCRIT; snprintf(buf, sizeof buf, "%4.0f%%", perc); return buf; } } detail_printdef cgroupprt_CGRDSKPSI = { "DSKPS", "CGRDSKPSI", cgroup_CGRDSKPSI, NULL, ' ', 5}; /***************************************************************/ char * cgroup_CGRDSKWGT(struct cgchainer *cgchain, struct tstat *tstat, int avgval, int nsecs, count_t cputicks, int nrcpu, int *color) { static char buf[16]; if (tstat) // process info? return " "; // cgroup info switch (cgchain->cstat->conf.dskweight) { case -2: return " -"; default: snprintf(buf, sizeof buf, "%5d", cgchain->cstat->conf.dskweight); return buf; } } detail_printdef cgroupprt_CGRDSKWGT = { "IOWGT", "CGRDSKWGT", cgroup_CGRDSKWGT, NULL, ' ', 5}; /***************************************************************/ char * cgroup_CGRPID(struct cgchainer *cgchain, struct tstat *tstat, int avgval, int nsecs, count_t cputicks, int nrcpu, int *color) { static char buf[64]; if (tstat) // process info? sprintf(buf, "%*d", cgroupprt_CGRPID.width, tstat->gen.pid); else // only cgroup info sprintf(buf, "%*s", cgroupprt_CGRPID.width, " "); return buf; } detail_printdef cgroupprt_CGRPID = { "PID", "CGRPID", cgroup_CGRPID, NULL, ' ', 5}; //DYNAMIC WIDTH! /***************************************************************/ char * cgroup_CGRCMD(struct cgchainer *cgchain, struct tstat *tstat, int avgval, int nsecs, count_t cputicks, int nrcpu, int *color) { static char buf[16]; if (tstat) // process info? { sprintf(buf, "%-14.14s", tstat->gen.name); } else // cgroup info { if (cgroupdepth == 8 && cgchain->cstat->gen.depth == 0) { sprintf(buf, "[suppressed]"); *color = FGCOLORBORDER; } else { sprintf(buf, "%-14.14s", " "); } } return buf; } detail_printdef cgroupprt_CGRCMD = { "CMD ", "CGRCMD", cgroup_CGRCMD, NULL, ' ', 14}; /***************************************************************/ atop-2.11.1/parseable.c0000644000203100020310000007700214771753566014174 0ustar gerlofgerlof/* ** ATOP - System & Process Monitor ** ** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as process-level. ** ** ========================================================================== ** Author: Gerlof Langeveld ** E-mail: gerlof.langeveld@atoptool.nl ** Date: February 2007 ** -------------------------------------------------------------------------- ** Copyright (C) 2007-2024 Gerlof Langeveld ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ** -------------------------------------------------------------------------- */ #include #include #include #include #include #include #include #include "atop.h" #include "photosyst.h" #include "photoproc.h" #include "cgroups.h" #include "parseable.h" void print_CPU(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); void print_cpu(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); void print_CPL(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); void print_GPU(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); void print_MEM(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); void print_SWP(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); void print_PAG(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); void print_PSI(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); void print_LVM(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); void print_MDD(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); void print_DSK(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); void print_NFM(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); void print_NFC(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); void print_NFS(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); void print_NET(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); void print_IFB(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); void print_NUM(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); void print_NUC(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); void print_LLC(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); void print_CGR(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); void print_PRG(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); void print_PRC(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); void print_PRM(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); void print_PRD(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); void print_PRN(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); void print_PRE(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); static void calc_freqscale(count_t, count_t, count_t, count_t *, int *); static char *spaceformat(char *, char *); /* ** table with possible labels and the corresponding ** print-function for parsable output */ struct labeldef { char *label; short valid; short cgroupref; void (*prifunc)(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); }; static struct labeldef labeldef[] = { { "CPU", 0, 0, print_CPU }, { "cpu", 0, 0, print_cpu }, { "CPL", 0, 0, print_CPL }, { "GPU", 0, 0, print_GPU }, { "MEM", 0, 0, print_MEM }, { "SWP", 0, 0, print_SWP }, { "PAG", 0, 0, print_PAG }, { "PSI", 0, 0, print_PSI }, { "LVM", 0, 0, print_LVM }, { "MDD", 0, 0, print_MDD }, { "DSK", 0, 0, print_DSK }, { "NFM", 0, 0, print_NFM }, { "NFC", 0, 0, print_NFC }, { "NFS", 0, 0, print_NFS }, { "NET", 0, 0, print_NET }, { "IFB", 0, 0, print_IFB }, { "NUM", 0, 0, print_NUM }, { "NUC", 0, 0, print_NUC }, { "LLC", 0, 0, print_LLC }, { "CGR", 0, 0, print_CGR }, { "PRG", 0, 1, print_PRG }, { "PRC", 0, 1, print_PRC }, { "PRM", 0, 1, print_PRM }, { "PRD", 0, 0, print_PRD }, { "PRN", 0, 0, print_PRN }, { "PRE", 0, 0, print_PRE }, }; static int numlabels = sizeof labeldef/sizeof(struct labeldef); /* ** analyse the parse-definition string that has been ** passed as argument with the flag -P */ int parsedef(char *pd) { register int i; char *p, *ep = pd + strlen(pd); /* ** check if string passed bahind -P is not another flag */ if (*pd == '-') { fprintf(stderr, "flag -P should be followed by label list\n"); return 0; } /* ** check list of comma-separated labels */ while (pd < ep) { /* ** exchange comma by null-byte */ if ( (p = strchr(pd, ',')) ) *p = 0; else p = ep-1; /* ** check if the next label exists */ for (i=0; i < numlabels; i++) { if ( strcmp(labeldef[i].label, pd) == 0) { labeldef[i].valid = 1; break; } } /* ** non-existing label has been used */ if (i == numlabels) { /* ** check if special label 'ALL' has been used */ if ( strcmp("ALL", pd) == 0) { for (i=0; i < numlabels; i++) labeldef[i].valid = 1; break; } else { fprintf(stderr, "label %s not found\n", pd); return 0; } } pd = p+1; } setbuf(stdout, (char *)0); return 1; } /* ** produce parsable output for an interval */ char parseout(time_t curtime, int numsecs, struct devtstat *devtstat, struct sstat *sstat, struct cgchainer *devchain, int ncgroups, int npids, int nexit, unsigned int noverflow, char flag) { register int i, cgroupref_created = 0; char datestr[32], timestr[32], header[256]; /* ** print reset-label for sample-values since boot */ if (flag&RRBOOT) printf("RESET\n"); /* ** search all labels which are selected before */ for (i=0; i < numlabels; i++) { if (labeldef[i].valid) { /* ** when cgroup index is needed to map the tstat to a cgroup, ** once fill the tstat.gen.cgroupix variables */ if (supportflags & CGROUPV2 && labeldef[i].cgroupref && !cgroupref_created) { cgfillref(devtstat, devchain, ncgroups, npids); cgroupref_created = 1; } /* ** prepare generic columns */ convdate(curtime, datestr); convtime(curtime, timestr); snprintf(header, sizeof header, "%s %s %lld %s %s %d", labeldef[i].label, utsname.nodename, (long long)curtime, datestr, timestr, numsecs); /* ** call a selected print function */ (labeldef[i].prifunc)(header, sstat, devtstat->taskall, devtstat->ntaskall, devchain, ncgroups); } } /* ** print separator */ printf("SEP\n"); return '\0'; } /* ** print functions for system-level statistics */ static void calc_freqscale(count_t maxfreq, count_t cnt, count_t ticks, count_t *freq, int *freqperc) { // if ticks != 0, do full calcs if (maxfreq && ticks) { *freq=cnt/ticks; *freqperc=100* *freq / maxfreq; } else if (maxfreq) // max frequency is known so % can be calculated { *freq=cnt; *freqperc=100*cnt/maxfreq; } else if (cnt) // no max known, set % to 100 { *freq=cnt; *freqperc=100; } else // nothing is known: set freq to 0, % to 100 { *freq=0; *freqperc=100; } } void print_CPU(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *devchain, int ncgroups) { count_t maxfreq=0; count_t cnt=0; count_t ticks=0; count_t freq; int freqperc; int i; // calculate average clock frequency for (i=0; i < ss->cpu.nrcpu; i++) { cnt += ss->cpu.cpu[i].freqcnt.cnt; ticks += ss->cpu.cpu[i].freqcnt.ticks; } maxfreq = ss->cpu.cpu[0].freqcnt.maxfreq; calc_freqscale(maxfreq, cnt, ticks, &freq, &freqperc); if (ss->cpu.all.instr == 1) { ss->cpu.all.instr = 0; ss->cpu.all.cycle = 0; } printf("%s %u %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %d %lld %lld\n", hp, hertz, ss->cpu.nrcpu, ss->cpu.all.stime, ss->cpu.all.utime, ss->cpu.all.ntime, ss->cpu.all.itime, ss->cpu.all.wtime, ss->cpu.all.Itime, ss->cpu.all.Stime, ss->cpu.all.steal, ss->cpu.all.guest, freq, freqperc, ss->cpu.all.instr, ss->cpu.all.cycle ); } void print_cpu(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *devchain, int ncgroups) { register int i; count_t maxfreq=0; count_t cnt=0; count_t ticks=0; count_t freq; int freqperc; for (i=0; i < ss->cpu.nrcpu; i++) { cnt = ss->cpu.cpu[i].freqcnt.cnt; ticks = ss->cpu.cpu[i].freqcnt.ticks; maxfreq= ss->cpu.cpu[0].freqcnt.maxfreq; calc_freqscale(maxfreq, cnt, ticks, &freq, &freqperc); printf("%s %u %d %lld %lld %lld " "%lld %lld %lld %lld %lld %lld %lld %d %lld %lld\n", hp, hertz, i, ss->cpu.cpu[i].stime, ss->cpu.cpu[i].utime, ss->cpu.cpu[i].ntime, ss->cpu.cpu[i].itime, ss->cpu.cpu[i].wtime, ss->cpu.cpu[i].Itime, ss->cpu.cpu[i].Stime, ss->cpu.cpu[i].steal, ss->cpu.cpu[i].guest, freq, freqperc, ss->cpu.cpu[i].instr, ss->cpu.cpu[i].cycle ); } } void print_CPL(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *devchain, int ncgroups) { printf("%s %lld %.2f %.2f %.2f %lld %lld\n", hp, ss->cpu.nrcpu, ss->cpu.lavg1, ss->cpu.lavg5, ss->cpu.lavg15, ss->cpu.csw, ss->cpu.devint); } void print_GPU(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *devchain, int ncgroups) { int i; for (i=0; i < ss->gpu.nrgpus; i++) { printf("%s %d %s %s %d %d %lld %lld %lld %lld %lld %lld\n", hp, i, ss->gpu.gpu[i].busid, ss->gpu.gpu[i].type, ss->gpu.gpu[i].gpupercnow, ss->gpu.gpu[i].mempercnow, ss->gpu.gpu[i].memtotnow, ss->gpu.gpu[i].memusenow, ss->gpu.gpu[i].samples, ss->gpu.gpu[i].gpuperccum, ss->gpu.gpu[i].memperccum, ss->gpu.gpu[i].memusecum); } } void print_MEM(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *devchain, int ncgroups) { printf( "%s %u %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld " "%lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld " "%lld %lld %lld\n", hp, pagesize, ss->mem.physmem, ss->mem.freemem, ss->mem.cachemem, ss->mem.buffermem, ss->mem.slabmem, ss->mem.cachedrt, ss->mem.slabreclaim, ss->mem.vmwballoon != -1 ? ss->mem.vmwballoon : 0, ss->mem.shmem, ss->mem.shmrss, ss->mem.shmswp, ss->mem.shugepagesz, ss->mem.stothugepage, ss->mem.sfreehugepage, ss->mem.zfsarcsize != -1 ? ss->mem.zfsarcsize : 0, ss->mem.ksmsharing != -1 ? ss->mem.ksmsharing : 0, ss->mem.ksmshared != -1 ? ss->mem.ksmshared : 0, ss->mem.tcpsock, ss->mem.udpsock, ss->mem.pagetables, ss->mem.lhugepagesz, ss->mem.ltothugepage, ss->mem.lfreehugepage, ss->mem.availablemem, ss->mem.anonhugepage); } void print_SWP(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *devchain, int ncgroups) { printf( "%s %u %lld %lld %lld %lld %lld %lld %lld %lld\n", hp, pagesize, ss->mem.totswap, ss->mem.freeswap, ss->mem.swapcached, ss->mem.committed, ss->mem.commitlim, ss->mem.swapcached, ss->mem.zswapped, ss->mem.zswap); } void print_PAG(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *devchain, int ncgroups) { printf("%s %u %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld " "%lld %lld\n", hp, pagesize, ss->mem.pgscans, ss->mem.allocstall, (long long)0, ss->mem.swins, ss->mem.swouts, ss->mem.oomkills, ss->mem.compactstall, ss->mem.pgmigrate, ss->mem.numamigrate, ss->mem.pgins, ss->mem.pgouts, ss->mem.zswins, ss->mem.zswouts); } void print_PSI(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *devchain, int ncgroups) { printf("%s %c %.1f %.1f %.1f %llu %.1f %.1f %.1f %llu " "%.1f %.1f %.1f %llu %.1f %.1f %.1f %llu %.1f %.1f %.1f %llu\n", hp, ss->psi.present ? 'y' : 'n', ss->psi.cpusome.avg10, ss->psi.cpusome.avg60, ss->psi.cpusome.avg300, ss->psi.cpusome.total, ss->psi.memsome.avg10, ss->psi.memsome.avg60, ss->psi.memsome.avg300, ss->psi.memsome.total, ss->psi.memfull.avg10, ss->psi.memfull.avg60, ss->psi.memfull.avg300, ss->psi.memfull.total, ss->psi.iosome.avg10, ss->psi.iosome.avg60, ss->psi.iosome.avg300, ss->psi.iosome.total, ss->psi.iofull.avg10, ss->psi.iofull.avg60, ss->psi.iofull.avg300, ss->psi.iofull.total); } void print_LVM(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *devchain, int ncgroups) { register int i; for (i=0; ss->dsk.lvm[i].name[0]; i++) { printf( "%s %s %lld %lld %lld %lld %lld %lld %lld %lld %.2f\n", hp, ss->dsk.lvm[i].name, ss->dsk.lvm[i].io_ms, ss->dsk.lvm[i].nread, ss->dsk.lvm[i].nrsect, ss->dsk.lvm[i].nwrite, ss->dsk.lvm[i].nwsect, ss->dsk.lvm[i].ndisc, ss->dsk.lvm[i].ndsect, ss->dsk.lvm[i].inflight, ss->dsk.lvm[i].io_ms > 0 ? (double)ss->dsk.lvm[i].avque/ss->dsk.lvm[i].io_ms : 0.0); } } void print_MDD(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *devchain, int ncgroups) { register int i; for (i=0; ss->dsk.mdd[i].name[0]; i++) { printf( "%s %s %lld %lld %lld %lld %lld %lld %lld %lld %.2f\n", hp, ss->dsk.mdd[i].name, ss->dsk.mdd[i].io_ms, ss->dsk.mdd[i].nread, ss->dsk.mdd[i].nrsect, ss->dsk.mdd[i].nwrite, ss->dsk.mdd[i].nwsect, ss->dsk.mdd[i].ndisc, ss->dsk.mdd[i].ndsect, ss->dsk.mdd[i].inflight, ss->dsk.mdd[i].io_ms > 0 ? (double)ss->dsk.mdd[i].avque/ss->dsk.mdd[i].io_ms : 0.0); } } void print_DSK(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *devchain, int ncgroups) { register int i; for (i=0; ss->dsk.dsk[i].name[0]; i++) { printf( "%s %s %lld %lld %lld %lld %lld %lld %lld %lld %.2f\n", hp, ss->dsk.dsk[i].name, ss->dsk.dsk[i].io_ms, ss->dsk.dsk[i].nread, ss->dsk.dsk[i].nrsect, ss->dsk.dsk[i].nwrite, ss->dsk.dsk[i].nwsect, ss->dsk.dsk[i].ndisc, ss->dsk.dsk[i].ndsect, ss->dsk.dsk[i].inflight, ss->dsk.dsk[i].io_ms > 0 ? (double)ss->dsk.dsk[i].avque/ss->dsk.dsk[i].io_ms : 0.0); } } void print_NFM(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *devchain, int ncgroups) { register int i; for (i=0; i < ss->nfs.nfsmounts.nrmounts; i++) { printf("%s %s %lld %lld %lld %lld %lld %lld %lld %lld\n", hp, ss->nfs.nfsmounts.nfsmnt[i].mountdev, ss->nfs.nfsmounts.nfsmnt[i].bytestotread, ss->nfs.nfsmounts.nfsmnt[i].bytestotwrite, ss->nfs.nfsmounts.nfsmnt[i].bytesread, ss->nfs.nfsmounts.nfsmnt[i].byteswrite, ss->nfs.nfsmounts.nfsmnt[i].bytesdread, ss->nfs.nfsmounts.nfsmnt[i].bytesdwrite, ss->nfs.nfsmounts.nfsmnt[i].pagesmread, ss->nfs.nfsmounts.nfsmnt[i].pagesmwrite); } } void print_NFC(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *devchain, int ncgroups) { printf( "%s %lld %lld %lld %lld %lld\n", hp, ss->nfs.client.rpccnt, ss->nfs.client.rpcread, ss->nfs.client.rpcwrite, ss->nfs.client.rpcretrans, ss->nfs.client.rpcautrefresh); } void print_NFS(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *devchain, int ncgroups) { printf( "%s %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld " "%lld %lld %lld %lld %lld\n", hp, ss->nfs.server.rpccnt, ss->nfs.server.rpcread, ss->nfs.server.rpcwrite, ss->nfs.server.nrbytes, ss->nfs.server.nwbytes, ss->nfs.server.rpcbadfmt, ss->nfs.server.rpcbadaut, ss->nfs.server.rpcbadcln, ss->nfs.server.netcnt, ss->nfs.server.nettcpcnt, ss->nfs.server.netudpcnt, ss->nfs.server.nettcpcon, ss->nfs.server.rchits, ss->nfs.server.rcmiss, ss->nfs.server.rcnoca); } void print_NET(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *devchain, int ncgroups) { register int i; printf( "%s %s %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld\n", hp, "upper", ss->net.tcp.InSegs, ss->net.tcp.OutSegs, ss->net.udpv4.InDatagrams + ss->net.udpv6.Udp6InDatagrams, ss->net.udpv4.OutDatagrams + ss->net.udpv6.Udp6OutDatagrams, ss->net.ipv4.InReceives + ss->net.ipv6.Ip6InReceives, ss->net.ipv4.OutRequests + ss->net.ipv6.Ip6OutRequests, ss->net.ipv4.InDelivers + ss->net.ipv6.Ip6InDelivers, ss->net.ipv4.ForwDatagrams + ss->net.ipv6.Ip6OutForwDatagrams, ss->net.udpv4.InErrors + ss->net.udpv6.Udp6InErrors, ss->net.udpv4.NoPorts + ss->net.udpv6.Udp6NoPorts, ss->net.tcp.ActiveOpens, ss->net.tcp.PassiveOpens, ss->net.tcp.CurrEstab, ss->net.tcp.RetransSegs, ss->net.tcp.InErrs, ss->net.tcp.OutRsts, ss->net.tcp.InCsumErrors); for (i=0; ss->intf.intf[i].name[0]; i++) { printf( "%s %s %lld %lld %lld %lld %ld %d\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, struct cgchainer *devchain, int ncgroups) { register int i; for (i=0; i < ss->ifb.nrports; i++) { printf( "%s %s %hd %hd %lld %lld %lld %lld %lld\n", hp, ss->ifb.ifb[i].ibname, ss->ifb.ifb[i].portnr, ss->ifb.ifb[i].lanes, ss->ifb.ifb[i].rate, ss->ifb.ifb[i].rcvb, ss->ifb.ifb[i].sndb, ss->ifb.ifb[i].rcvp, ss->ifb.ifb[i].sndp); } } void print_NUM(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *devchain, int ncgroups) { register int i; for (i=0; i < ss->memnuma.nrnuma; i++) { printf( "%s %d %u %.0f %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld\n", hp, ss->memnuma.numa[i].numanr, pagesize, ss->memnuma.numa[i].frag * 100.0, ss->memnuma.numa[i].totmem, ss->memnuma.numa[i].freemem, ss->memnuma.numa[i].active, ss->memnuma.numa[i].inactive, ss->memnuma.numa[i].filepage, ss->memnuma.numa[i].dirtymem, ss->memnuma.numa[i].slabmem, ss->memnuma.numa[i].slabreclaim, ss->memnuma.numa[i].shmem, ss->memnuma.numa[i].tothp, ss->memnuma.numa[i].freehp); } } void print_NUC(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *devchain, int ncgroups) { register int i; for (i=0; i < ss->cpunuma.nrnuma; i++) { printf( "%s %d %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld\n", hp, ss->cpunuma.numa[i].numanr, ss->cpunuma.numa[i].nrcpu, ss->cpunuma.numa[i].stime, ss->cpunuma.numa[i].utime, ss->cpunuma.numa[i].ntime, ss->cpunuma.numa[i].itime, ss->cpunuma.numa[i].wtime, ss->cpunuma.numa[i].Itime, ss->cpunuma.numa[i].Stime, ss->cpunuma.numa[i].steal, ss->cpunuma.numa[i].guest); } } void print_LLC(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *devchain, int ncgroups) { register int i; for (i=0; i < ss->llc.nrllcs; i++) { printf( "%s LLC%03d %3.1f%% %lld %lld\n", hp, ss->llc.perllc[i].id, ss->llc.perllc[i].occupancy * 100, ss->llc.perllc[i].mbm_total, ss->llc.perllc[i].mbm_local); } } /* ** print functions for cgroups-level statistics */ void print_CGR(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *devchain, int ncgroups) { register int i, p; char *cgrpath; if ( !(supportflags & CGROUPV2) ) return; for (i=0; i < ncgroups; i++) { // print cgroup level metrics // cgrpath = cggetpath(devchain+i, devchain, 0); printf( "%s C %s %d %d %lld %lld %d %d " "%lld %lld %lld %lld %lld %lld %lld " "%lld %lld %lld %lld %d %lld %lld " "%lld %lld %lld %lld\n", hp, cgrpath, (devchain+i)->cstat->gen.nprocs, (devchain+i)->cstat->gen.procsbelow, (devchain+i)->cstat->cpu.utime, (devchain+i)->cstat->cpu.stime, (devchain+i)->cstat->conf.cpuweight, (devchain+i)->cstat->conf.cpumax, (devchain+i)->cstat->mem.current, (devchain+i)->cstat->mem.anon, (devchain+i)->cstat->mem.file, (devchain+i)->cstat->mem.kernel, (devchain+i)->cstat->mem.shmem, (devchain+i)->cstat->conf.memmax, (devchain+i)->cstat->conf.swpmax, (devchain+i)->cstat->dsk.rbytes, (devchain+i)->cstat->dsk.wbytes, (devchain+i)->cstat->dsk.rios, (devchain+i)->cstat->dsk.wios, (devchain+i)->cstat->conf.dskweight, (devchain+i)->cstat->cpu.somepres, (devchain+i)->cstat->cpu.fullpres, (devchain+i)->cstat->mem.somepres, (devchain+i)->cstat->mem.fullpres, (devchain+i)->cstat->dsk.somepres, (devchain+i)->cstat->dsk.fullpres); // print related pidlist in one line // if ((devchain+i)->cstat->gen.nprocs) { printf( "%s P %s", hp, cgrpath); for (p=0; p < (devchain+i)->cstat->gen.nprocs; p++) printf(" %d", (devchain+i)->proclist[p]); printf("\n"); } free(cgrpath); } } /* ** print functions for process-level statistics */ void print_PRG(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *devchain, int ncgroups) { register int i, exitcode, cgrlen, cgrpathsize = 256; char namout[PNAMLEN+1+2], cmdout[CMDLEN+1+2], defaultbuf[8], *cgrout=defaultbuf, *cgrpath; // create assumed worst-case cgroup path size // if it appears to be too small, it will later on be realloc'ed // if (supportflags & CGROUPV2) { cgrout = calloc(1, cgrpathsize); ptrverify(cgrout, "Malloc failed for output cgroup path\n"); } for (i=0; i < nact; i++, ps++) { if (supportflags & CGROUPV2 && ps->gen.cgroupix != -1) // valid cgroup index? { cgrpath = cggetpath((devchain + ps->gen.cgroupix), devchain, 0); if (cgrpathsize < (cgrlen = strlen(cgrpath) + 3)) { cgrpathsize = cgrlen; cgrout = realloc(cgrout, cgrpathsize); ptrverify(cgrout, "Realloc failed for output cgroup path\n"); } } else { cgrpath = "-"; } if (ps->gen.excode & 0xff) // killed by signal? exitcode = (ps->gen.excode & 0x7f) + 256; else exitcode = (ps->gen.excode >> 8) & 0xff; printf("%s %d %s %c %d %d %d %d %d %ld %s %d %d %d %d " "%d %d %d %d %d %d %ld %c %d %d %s %c %s %ld %d\n", hp, ps->gen.pid, spaceformat(ps->gen.name, namout), ps->gen.state, ps->gen.ruid, ps->gen.rgid, ps->gen.tgid, ps->gen.nthr, exitcode, ps->gen.btime, spaceformat(ps->gen.cmdline, cmdout), ps->gen.ppid, ps->gen.nthrrun, ps->gen.nthrslpi, ps->gen.nthrslpu, ps->gen.euid, ps->gen.egid, ps->gen.suid, ps->gen.sgid, ps->gen.fsuid, ps->gen.fsgid, ps->gen.elaps, ps->gen.isproc ? 'y':'n', ps->gen.vpid, ps->gen.ctid, ps->gen.utsname[0] ? ps->gen.utsname:"-", ps->gen.excode & ~(INT_MAX) ? 'N' : '-', spaceformat(cgrpath, cgrout), ps->gen.state == 'E' ? ps->gen.btime + ps->gen.elaps/hertz : 0, ps->gen.nthridle); if (supportflags & CGROUPV2 && ps->gen.cgroupix != -1) free(cgrpath); } if (supportflags & CGROUPV2) free(cgrout); } void print_PRC(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *devchain, int ncgroups) { register int i, cpumax; char namout[PNAMLEN+1+2], wchanout[20]; for (i=0; i < nact; i++, ps++) { if (supportflags & CGROUPV2) { if (ps->gen.cgroupix != -1) cpumax = (devchain+ps->gen.cgroupix)->cstat->conf.cpumax; else cpumax = -2; } else { cpumax = -3; } printf("%s %d %s %c %u %lld %lld %d %d %d %d %d %d %d %c " "%llu %s %llu %d %d %llu %llu\n", hp, ps->gen.pid, spaceformat(ps->gen.name, namout), ps->gen.state, hertz, ps->cpu.utime, ps->cpu.stime, ps->cpu.nice, ps->cpu.prio, ps->cpu.rtprio, ps->cpu.policy, ps->cpu.curcpu, ps->cpu.sleepavg, ps->gen.tgid, ps->gen.isproc ? 'y':'n', ps->cpu.rundelay, spaceformat(ps->cpu.wchan, wchanout), ps->cpu.blkdelay, cpumax, -2, // most restrictive cpumax no longer supported ps->cpu.nvcsw, ps->cpu.nivcsw); } } void print_PRM(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *devchain, int ncgroups) { register int i, memmax, swpmax; char namout[PNAMLEN+1+2]; for (i=0; i < nact; i++, ps++) { if (supportflags & CGROUPV2) { if (ps->gen.cgroupix != -1) { memmax = (devchain+ps->gen.cgroupix)->cstat->conf.memmax; if (memmax > 0) memmax *= pagesize / 1024; swpmax = (devchain+ps->gen.cgroupix)->cstat->conf.swpmax; if (swpmax > 0) swpmax *= pagesize / 1024; } else { memmax = -2; swpmax = -2; } } else { memmax = -3; swpmax = -3; } printf("%s %d %s %c %u %lld %lld %lld %lld %lld %lld " "%lld %lld %lld %lld %lld %d %c %lld %lld %d %d %d %d\n", hp, ps->gen.pid, spaceformat(ps->gen.name, namout), ps->gen.state, pagesize, ps->mem.vmem, ps->mem.rmem, ps->mem.vexec, ps->mem.vgrow, ps->mem.rgrow, ps->mem.minflt, ps->mem.majflt, ps->mem.vlibs, ps->mem.vdata, ps->mem.vstack, ps->mem.vswap, ps->gen.tgid, ps->gen.isproc ? 'y':'n', ps->mem.pmem == (unsigned long long)-1LL ? 0:ps->mem.pmem, ps->mem.vlock, memmax, -2, swpmax, -2); } } void print_PRD(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *devchain, int ncgroups) { register int i; char namout[PNAMLEN+1+2]; for (i=0; i < nact; i++, ps++) { printf("%s %d %s %c %c %c %lld %lld %lld %lld %lld %d n %c\n", hp, ps->gen.pid, spaceformat(ps->gen.name, namout), ps->gen.state, 'n', supportflags & IOSTAT ? 'y' : 'n', ps->dsk.rio, ps->dsk.rsz, ps->dsk.wio, ps->dsk.wsz, ps->dsk.cwsz, ps->gen.tgid, ps->gen.isproc ? 'y':'n'); } } void print_PRN(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *devchain, int ncgroups) { register int i; char namout[PNAMLEN+1+2]; for (i=0; i < nact; i++, ps++) { printf("%s %d %s %c %c %lld %lld %lld %lld %lld %lld " "%lld %lld %d %d %d %c\n", hp, ps->gen.pid, spaceformat(ps->gen.name, namout), ps->gen.state, supportflags & NETATOP ? 'y' : 'n', ps->net.tcpsnd, ps->net.tcpssz, ps->net.tcprcv, ps->net.tcprsz, ps->net.udpsnd, ps->net.udpssz, ps->net.udprcv, ps->net.udprsz, 0, 0, ps->gen.tgid, ps->gen.isproc ? 'y':'n'); } } void print_PRE(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *devchain, int ncgroups) { register int i; char namout[PNAMLEN+1+2]; for (i=0; i < nact; i++, ps++) { printf("%s %d %s %c %c %d %x %d %d %lld %lld %lld\n", hp, ps->gen.pid, spaceformat(ps->gen.name, namout), ps->gen.state, ps->gpu.state == '\0' ? 'N':ps->gpu.state, ps->gpu.nrgpus, ps->gpu.gpulist, ps->gpu.gpubusy, ps->gpu.membusy, ps->gpu.memnow, ps->gpu.memcum, ps->gpu.sample); } } /* ** Strings, like command name, might contain spaces ** and will be represented for that reason surrounded ** by parenthesis. However, this is difficult to parse ** by other tools, so the option -Z might have been ** specified to exchange all spaces by underscores while ** omitting the parenthesis in that case. ** ** This function formats the input string (istr) in the ** required format to the output string (ostr). ** Take care that the buffer pointed to by ostr is at least ** two bytes larger than the input string (for the parenthesis). ** The pointer ostr is also returned. */ static char * spaceformat(char *istr, char *ostr) { // formatting with spaces and parenthesis required? if (!rmspaces) { *ostr = '('; strcpy(ostr+1, istr); strcat(ostr, ")"); } // formatting with underscores without parenthesis required else { register char *pi = istr, *po = ostr; while (*pi) { if (*pi == ' ') { *po++ = '_'; pi++; } else { *po++ = *pi++; } } if (po == ostr) // empty string: still return underscore *po++ = '_'; *po = '\0'; // terminate output string } return ostr; } atop-2.11.1/atop.daily0000755000203100020310000000267414771753566014067 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.11.1/versdate.h0000644000203100020310000000014214771753566014047 0ustar gerlofgerlof#ifndef __ATOP_VERSDATA__ #define __ATOP_VERSDATA__ #define ATOPDATE "2025/03/29 12:20:17" #endif atop-2.11.1/deviate.c0000644000203100020310000016173514771753566013666 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; if (curstat->cpu.wchan[0]) strcpy(devstat->cpu.wchan, curstat->cpu.wchan); else devstat->cpu.wchan[0] = 0; devstat->mem.vexec = curstat->mem.vexec; devstat->mem.vmem = curstat->mem.vmem; devstat->mem.rmem = curstat->mem.rmem; devstat->mem.pmem = curstat->mem.pmem; devstat->mem.vdata = curstat->mem.vdata; devstat->mem.vstack = curstat->mem.vstack; devstat->mem.vlibs = curstat->mem.vlibs; devstat->mem.vswap = curstat->mem.vswap; devstat->mem.vlock = curstat->mem.vlock; if (curstat->gpu.state || prestat->gpu.state) // GPU use? { if (curstat->gpu.state) devstat->gpu.state = curstat->gpu.state; else devstat->gpu.state = prestat->gpu.state; devstat->gpu.nrgpus = curstat->gpu.nrgpus; devstat->gpu.gpulist = curstat->gpu.gpulist; devstat->gpu.gpubusy = curstat->gpu.gpubusy; devstat->gpu.membusy = curstat->gpu.membusy; devstat->gpu.memnow = curstat->gpu.memnow; devstat->gpu.timems = curstat->gpu.timems; } else { devstat->gpu.state = '\0'; } /* ** for inactive tasks, only the static values had to be copied, while ** all use counters have already been set to zero */ if (curstat->gen.wasinactive) return; /* ** calculate deviations for tasks that were really active ** (i.e. modified) during the sample */ devstat->cpu.stime = subcount(curstat->cpu.stime, prestat->cpu.stime); devstat->cpu.utime = subcount(curstat->cpu.utime, prestat->cpu.utime); /* ** particular kernel versions sometimes supply a smaller ** amount for consumed CPU-ticks than a previous sample; ** with unsigned calculations this results in 497 days of ** CPU-consumption so a sanity-check is needed here... */ if (devstat->cpu.stime > totusedcpu) devstat->cpu.stime = 1; if (devstat->cpu.utime > totusedcpu) devstat->cpu.utime = 1; devstat->cpu.rundelay = subcount(curstat->cpu.rundelay, prestat->cpu.rundelay); devstat->cpu.blkdelay = subcount(curstat->cpu.blkdelay, prestat->cpu.blkdelay); if (curstat->cpu.nvcsw >= prestat->cpu.nvcsw) devstat->cpu.nvcsw = subcount(curstat->cpu.nvcsw, prestat->cpu.nvcsw); else devstat->cpu.nvcsw = curstat->cpu.nvcsw; if (curstat->cpu.nivcsw >= prestat->cpu.nivcsw) devstat->cpu.nivcsw = subcount(curstat->cpu.nivcsw, prestat->cpu.nivcsw); else devstat->cpu.nivcsw = curstat->cpu.nivcsw; devstat->dsk.rio = subcount(curstat->dsk.rio, prestat->dsk.rio); devstat->dsk.rsz = subcount(curstat->dsk.rsz, prestat->dsk.rsz); devstat->dsk.wio = subcount(curstat->dsk.wio, prestat->dsk.wio); devstat->dsk.wsz = subcount(curstat->dsk.wsz, prestat->dsk.wsz); devstat->dsk.cwsz = subcount(curstat->dsk.cwsz, prestat->dsk.cwsz); devstat->mem.vgrow = curstat->mem.vmem - prestat->mem.vmem; devstat->mem.rgrow = curstat->mem.rmem - prestat->mem.rmem; devstat->mem.minflt = subcount(curstat->mem.minflt, prestat->mem.minflt); devstat->mem.majflt = subcount(curstat->mem.majflt, prestat->mem.majflt); /* ** network counters: due to an unload/load of the netatop module, ** previous counters might be larger than the current */ if (curstat->net.tcpsnd >= prestat->net.tcpsnd) devstat->net.tcpsnd = subcount(curstat->net.tcpsnd, prestat->net.tcpsnd); else devstat->net.tcpsnd = curstat->net.tcpsnd; if (curstat->net.tcpssz >= prestat->net.tcpssz) devstat->net.tcpssz = subcount(curstat->net.tcpssz, prestat->net.tcpssz); else devstat->net.tcpssz = curstat->net.tcpssz; if (curstat->net.tcprcv >= prestat->net.tcprcv) devstat->net.tcprcv = subcount(curstat->net.tcprcv, prestat->net.tcprcv); else devstat->net.tcprcv = curstat->net.tcprcv; if (curstat->net.tcprsz >= prestat->net.tcprsz) devstat->net.tcprsz = subcount(curstat->net.tcprsz, prestat->net.tcprsz); else devstat->net.tcprsz = curstat->net.tcprsz; if (curstat->net.udpsnd >= prestat->net.udpsnd) devstat->net.udpsnd = subcount(curstat->net.udpsnd, prestat->net.udpsnd); else devstat->net.udpsnd = curstat->net.udpsnd; if (curstat->net.udpssz >= prestat->net.udpssz) devstat->net.udpssz = subcount(curstat->net.udpssz, prestat->net.udpssz); else devstat->net.udpssz = curstat->net.udpssz; if (curstat->net.udprcv >= prestat->net.udprcv) devstat->net.udprcv = subcount(curstat->net.udprcv, prestat->net.udprcv); else devstat->net.udprcv = curstat->net.udprcv; if (curstat->net.udprsz >= prestat->net.udprsz) devstat->net.udprsz = subcount(curstat->net.udprsz, prestat->net.udprsz); else devstat->net.udprsz = curstat->net.udprsz; if (curstat->gpu.state) { devstat->gpu.memcum = curstat->gpu.memcum - prestat->gpu.memcum; devstat->gpu.sample = curstat->gpu.sample - prestat->gpu.sample; } } /* ** calculate the system-activity during the last sample */ void deviatsyst(struct sstat *cur, struct sstat *pre, struct sstat *dev, long interval) { register int i, j; count_t *cdev, *ccur, *cpre; struct ifprop ifprop; dev->cpu.nrcpu = cur->cpu.nrcpu; dev->cpu.devint = subcount(cur->cpu.devint, pre->cpu.devint); dev->cpu.csw = subcount(cur->cpu.csw, pre->cpu.csw); dev->cpu.nprocs = subcount(cur->cpu.nprocs, pre->cpu.nprocs); dev->cpu.all.stime = subcount(cur->cpu.all.stime, pre->cpu.all.stime); dev->cpu.all.utime = subcount(cur->cpu.all.utime, pre->cpu.all.utime); dev->cpu.all.ntime = subcount(cur->cpu.all.ntime, pre->cpu.all.ntime); dev->cpu.all.itime = subcount(cur->cpu.all.itime, pre->cpu.all.itime); dev->cpu.all.wtime = subcount(cur->cpu.all.wtime, pre->cpu.all.wtime); dev->cpu.all.Itime = subcount(cur->cpu.all.Itime, pre->cpu.all.Itime); dev->cpu.all.Stime = subcount(cur->cpu.all.Stime, pre->cpu.all.Stime); dev->cpu.all.steal = subcount(cur->cpu.all.steal, pre->cpu.all.steal); dev->cpu.all.guest = subcount(cur->cpu.all.guest, pre->cpu.all.guest); dev->cpu.all.instr = subcount(cur->cpu.all.instr, pre->cpu.all.instr); dev->cpu.all.cycle = subcount(cur->cpu.all.cycle, pre->cpu.all.cycle); for (i=0; i < dev->cpu.nrcpu; i++) { count_t ticks; dev->cpu.cpu[i].cpunr = cur->cpu.cpu[i].cpunr; dev->cpu.cpu[i].stime = subcount(cur->cpu.cpu[i].stime, pre->cpu.cpu[i].stime); dev->cpu.cpu[i].utime = subcount(cur->cpu.cpu[i].utime, pre->cpu.cpu[i].utime); dev->cpu.cpu[i].ntime = subcount(cur->cpu.cpu[i].ntime, pre->cpu.cpu[i].ntime); dev->cpu.cpu[i].itime = subcount(cur->cpu.cpu[i].itime, pre->cpu.cpu[i].itime); dev->cpu.cpu[i].wtime = subcount(cur->cpu.cpu[i].wtime, pre->cpu.cpu[i].wtime); dev->cpu.cpu[i].Itime = subcount(cur->cpu.cpu[i].Itime, pre->cpu.cpu[i].Itime); dev->cpu.cpu[i].Stime = subcount(cur->cpu.cpu[i].Stime, pre->cpu.cpu[i].Stime); dev->cpu.cpu[i].steal = subcount(cur->cpu.cpu[i].steal, pre->cpu.cpu[i].steal); dev->cpu.cpu[i].guest = subcount(cur->cpu.cpu[i].guest, pre->cpu.cpu[i].guest); dev->cpu.cpu[i].instr = subcount(cur->cpu.cpu[i].instr, pre->cpu.cpu[i].instr); dev->cpu.cpu[i].cycle = subcount(cur->cpu.cpu[i].cycle, pre->cpu.cpu[i].cycle); ticks = cur->cpu.cpu[i].freqcnt.ticks; dev->cpu.cpu[i].freqcnt.maxfreq = cur->cpu.cpu[i].freqcnt.maxfreq; dev->cpu.cpu[i].freqcnt.cnt = ticks ? subcount(cur->cpu.cpu[i].freqcnt.cnt, pre->cpu.cpu[i].freqcnt.cnt) : cur->cpu.cpu[i].freqcnt.cnt; dev->cpu.cpu[i].freqcnt.ticks = ticks ? subcount(cur->cpu.cpu[i].freqcnt.ticks, pre->cpu.cpu[i].freqcnt.ticks) : cur->cpu.cpu[i].freqcnt.ticks; } dev->cpu.lavg1 = cur->cpu.lavg1; dev->cpu.lavg5 = cur->cpu.lavg5; dev->cpu.lavg15 = cur->cpu.lavg15; dev->mem.physmem = cur->mem.physmem; dev->mem.freemem = cur->mem.freemem; dev->mem.availablemem = cur->mem.availablemem; dev->mem.buffermem = cur->mem.buffermem; dev->mem.slabmem = cur->mem.slabmem; dev->mem.slabreclaim = cur->mem.slabreclaim; dev->mem.committed = cur->mem.committed; dev->mem.commitlim = cur->mem.commitlim; dev->mem.cachemem = cur->mem.cachemem; dev->mem.cachedrt = cur->mem.cachedrt; dev->mem.totswap = cur->mem.totswap; dev->mem.freeswap = cur->mem.freeswap; dev->mem.swapcached = cur->mem.swapcached; dev->mem.pagetables = cur->mem.pagetables; dev->mem.zswap = cur->mem.zswap; dev->mem.zswapped = cur->mem.zswapped; dev->mem.shmem = cur->mem.shmem; dev->mem.shmrss = cur->mem.shmrss; dev->mem.shmswp = cur->mem.shmswp; dev->mem.stothugepage = cur->mem.stothugepage; dev->mem.sfreehugepage = cur->mem.sfreehugepage; dev->mem.shugepagesz = cur->mem.shugepagesz; dev->mem.ltothugepage = cur->mem.ltothugepage; dev->mem.lfreehugepage = cur->mem.lfreehugepage; dev->mem.lhugepagesz = cur->mem.lhugepagesz; dev->mem.anonhugepage = cur->mem.anonhugepage; dev->mem.vmwballoon = cur->mem.vmwballoon; dev->mem.zfsarcsize = cur->mem.zfsarcsize; dev->mem.ksmsharing = cur->mem.ksmsharing; dev->mem.ksmshared = cur->mem.ksmshared; dev->mem.tcpsock = cur->mem.tcpsock; dev->mem.udpsock = cur->mem.udpsock; dev->mem.pgouts = subcount(cur->mem.pgouts, pre->mem.pgouts); dev->mem.pgins = subcount(cur->mem.pgins, pre->mem.pgins); dev->mem.swouts = subcount(cur->mem.swouts, pre->mem.swouts); dev->mem.swins = subcount(cur->mem.swins, pre->mem.swins); dev->mem.zswouts = subcount(cur->mem.zswouts, pre->mem.zswouts); dev->mem.zswins = subcount(cur->mem.zswins, pre->mem.zswins); dev->mem.pgscans = subcount(cur->mem.pgscans, pre->mem.pgscans); dev->mem.pgsteal = subcount(cur->mem.pgsteal, pre->mem.pgsteal); dev->mem.allocstall = subcount(cur->mem.allocstall, pre->mem.allocstall); if (cur->mem.oomkills != -1) dev->mem.oomkills = subcount(cur->mem.oomkills, pre->mem.oomkills); else dev->mem.oomkills = -1; dev->mem.compactstall = subcount(cur->mem.compactstall, pre->mem.compactstall); dev->mem.numamigrate = subcount(cur->mem.numamigrate, pre->mem.numamigrate); dev->mem.pgmigrate = subcount(cur->mem.pgmigrate, pre->mem.pgmigrate); dev->memnuma.nrnuma = cur->memnuma.nrnuma; for (i=0; i < dev->memnuma.nrnuma; i++) { dev->memnuma.numa[i].numanr = cur->memnuma.numa[i].numanr; dev->memnuma.numa[i].totmem = cur->memnuma.numa[i].totmem; dev->memnuma.numa[i].freemem = cur->memnuma.numa[i].freemem; dev->memnuma.numa[i].filepage = cur->memnuma.numa[i].filepage; dev->memnuma.numa[i].active = cur->memnuma.numa[i].active; dev->memnuma.numa[i].inactive = cur->memnuma.numa[i].inactive; dev->memnuma.numa[i].dirtymem = cur->memnuma.numa[i].dirtymem; dev->memnuma.numa[i].shmem = cur->memnuma.numa[i].shmem; dev->memnuma.numa[i].slabmem = cur->memnuma.numa[i].slabmem; dev->memnuma.numa[i].slabreclaim = cur->memnuma.numa[i].slabreclaim; dev->memnuma.numa[i].tothp = cur->memnuma.numa[i].tothp; dev->memnuma.numa[i].freehp = cur->memnuma.numa[i].freehp; dev->memnuma.numa[i].frag = cur->memnuma.numa[i].frag; } dev->cpunuma.nrnuma = cur->cpunuma.nrnuma; if (dev->cpunuma.nrnuma > 1) { for (i=0; i < dev->cpunuma.nrnuma; i++) { dev->cpunuma.numa[i].nrcpu = cur->cpunuma.numa[i].nrcpu; dev->cpunuma.numa[i].numanr = cur->cpunuma.numa[i].numanr; dev->cpunuma.numa[i].utime = subcount(cur->cpunuma.numa[i].utime, pre->cpunuma.numa[i].utime); dev->cpunuma.numa[i].ntime = subcount(cur->cpunuma.numa[i].ntime, pre->cpunuma.numa[i].ntime); dev->cpunuma.numa[i].stime = subcount(cur->cpunuma.numa[i].stime, pre->cpunuma.numa[i].stime); dev->cpunuma.numa[i].itime = subcount(cur->cpunuma.numa[i].itime, pre->cpunuma.numa[i].itime); dev->cpunuma.numa[i].wtime = subcount(cur->cpunuma.numa[i].wtime, pre->cpunuma.numa[i].wtime); dev->cpunuma.numa[i].Itime = subcount(cur->cpunuma.numa[i].Itime, pre->cpunuma.numa[i].Itime); dev->cpunuma.numa[i].Stime = subcount(cur->cpunuma.numa[i].Stime, pre->cpunuma.numa[i].Stime); dev->cpunuma.numa[i].steal = subcount(cur->cpunuma.numa[i].steal, pre->cpunuma.numa[i].steal); dev->cpunuma.numa[i].guest = subcount(cur->cpunuma.numa[i].guest, pre->cpunuma.numa[i].guest); } } dev->psi = cur->psi; if (cur->psi.present) { dev->psi.cpusome.total = cur->psi.cpusome.total - pre->psi.cpusome.total; dev->psi.memsome.total = cur->psi.memsome.total - pre->psi.memsome.total; dev->psi.memfull.total = cur->psi.memfull.total - pre->psi.memfull.total; dev->psi.iosome.total = cur->psi.iosome.total - pre->psi.iosome.total; dev->psi.iofull.total = cur->psi.iofull.total - pre->psi.iofull.total; } /* ** structures with network-related counters are considered ** as tables of frequency-counters that have to be subtracted; ** values that do not represent a frequency are corrected afterwards */ for (cdev = (count_t *)&dev->net.ipv4, ccur = (count_t *)&cur->net.ipv4, cpre = (count_t *)&pre->net.ipv4, i = 0; i < (sizeof dev->net.ipv4 / sizeof(count_t)); cdev++, ccur++, cpre++, i++) *cdev = *ccur - *cpre; dev->net.ipv4.Forwarding = cur->net.ipv4.Forwarding; dev->net.ipv4.DefaultTTL = cur->net.ipv4.DefaultTTL; /* ------------- */ for (cdev = (count_t *)&dev->net.icmpv4, ccur = (count_t *)&cur->net.icmpv4, cpre = (count_t *)&pre->net.icmpv4, i = 0; i < (sizeof dev->net.icmpv4 / sizeof(count_t)); cdev++, ccur++, cpre++, i++) *cdev = *ccur - *cpre; /* ------------- */ for (cdev = (count_t *)&dev->net.udpv4, ccur = (count_t *)&cur->net.udpv4, cpre = (count_t *)&pre->net.udpv4, i = 0; i < (sizeof dev->net.udpv4 / sizeof(count_t)); cdev++, ccur++, cpre++, i++) *cdev = *ccur - *cpre; /* ------------- */ for (cdev = (count_t *)&dev->net.ipv6, ccur = (count_t *)&cur->net.ipv6, cpre = (count_t *)&pre->net.ipv6, i = 0; i < (sizeof dev->net.ipv6 / sizeof(count_t)); cdev++, ccur++, cpre++, i++) *cdev = *ccur - *cpre; /* ------------- */ for (cdev = (count_t *)&dev->net.icmpv6, ccur = (count_t *)&cur->net.icmpv6, cpre = (count_t *)&pre->net.icmpv6, i = 0; i < (sizeof dev->net.icmpv6 / sizeof(count_t)); cdev++, ccur++, cpre++, i++) *cdev = *ccur - *cpre; /* ------------- */ for (cdev = (count_t *)&dev->net.udpv6, ccur = (count_t *)&cur->net.udpv6, cpre = (count_t *)&pre->net.udpv6, i = 0; i < (sizeof dev->net.udpv6 / sizeof(count_t)); cdev++, ccur++, cpre++, i++) *cdev = *ccur - *cpre; /* ------------- */ for (cdev = (count_t *)&dev->net.tcp, ccur = (count_t *)&cur->net.tcp, cpre = (count_t *)&pre->net.tcp, i = 0; i < (sizeof dev->net.tcp / sizeof(count_t)); cdev++, ccur++, cpre++, i++) *cdev = *ccur - *cpre; dev->net.tcp.RtoAlgorithm = cur->net.tcp.RtoAlgorithm; dev->net.tcp.RtoMin = cur->net.tcp.RtoMin; dev->net.tcp.RtoMax = cur->net.tcp.RtoMax; dev->net.tcp.MaxConn = cur->net.tcp.MaxConn; dev->net.tcp.CurrEstab = cur->net.tcp.CurrEstab; /* ** calculate deviations for interfaces */ for (i=0; cur->intf.intf[i].name[0]; i++) { // fill current properties for each valid interface strcpy(ifprop.name, cur->intf.intf[i].name); getifprop(&ifprop); cur->intf.intf[i].type = ifprop.type; cur->intf.intf[i].speed = ifprop.speed; cur->intf.intf[i].speedp = ifprop.speed; cur->intf.intf[i].duplex = ifprop.fullduplex; } if (pre->intf.intf[0].name[0] == '\0') /* first sample? */ { for (i=0; cur->intf.intf[i].name[0]; i++) { strcpy(pre->intf.intf[i].name, cur->intf.intf[i].name); pre->intf.intf[i].type = cur->intf.intf[i].type; pre->intf.intf[i].speed = cur->intf.intf[i].speed; pre->intf.intf[i].speedp = cur->intf.intf[i].speedp; pre->intf.intf[i].duplex = cur->intf.intf[i].duplex; } } for (i=0, j=0; cur->intf.intf[i].name[0]; i++, j++) { /* ** be sure that we have the same interface ** (interfaces could have been added or removed since ** previous sample) */ if (strcmp(cur->intf.intf[i].name, pre->intf.intf[j].name) != 0) { // try to resync for (j=0; pre->intf.intf[j].name[0]; j++) { if (strcmp(cur->intf.intf[i].name, pre->intf.intf[j].name) == 0) break; } // resync not succeeded? if (! pre->intf.intf[j].name[0]) { memcpy(&dev->intf.intf[i], &cur->intf.intf[i], sizeof cur->intf.intf[i]); j = 0; continue; } } /* ** calculate interface deviations for this sample */ strcpy(dev->intf.intf[i].name, cur->intf.intf[i].name); dev->intf.intf[i].rbyte = subcount(cur->intf.intf[i].rbyte, pre->intf.intf[j].rbyte); dev->intf.intf[i].rpack = subcount(cur->intf.intf[i].rpack, pre->intf.intf[j].rpack); dev->intf.intf[i].rerrs = subcount(cur->intf.intf[i].rerrs, pre->intf.intf[j].rerrs); dev->intf.intf[i].rdrop = subcount(cur->intf.intf[i].rdrop, pre->intf.intf[j].rdrop); dev->intf.intf[i].rfifo = subcount(cur->intf.intf[i].rfifo, pre->intf.intf[j].rfifo); dev->intf.intf[i].rframe= subcount(cur->intf.intf[i].rframe, pre->intf.intf[j].rframe); dev->intf.intf[i].rcompr= subcount(cur->intf.intf[i].rcompr, pre->intf.intf[j].rcompr); dev->intf.intf[i].rmultic=subcount(cur->intf.intf[i].rmultic, pre->intf.intf[j].rmultic); dev->intf.intf[i].sbyte = subcount(cur->intf.intf[i].sbyte, pre->intf.intf[j].sbyte); dev->intf.intf[i].spack = subcount(cur->intf.intf[i].spack, pre->intf.intf[j].spack); dev->intf.intf[i].serrs = subcount(cur->intf.intf[i].serrs, pre->intf.intf[j].serrs); dev->intf.intf[i].sdrop = subcount(cur->intf.intf[i].sdrop, pre->intf.intf[j].sdrop); dev->intf.intf[i].sfifo = subcount(cur->intf.intf[i].sfifo, pre->intf.intf[j].sfifo); dev->intf.intf[i].scollis= subcount(cur->intf.intf[i].scollis, pre->intf.intf[j].scollis); dev->intf.intf[i].scarrier= subcount(cur->intf.intf[i].scarrier, pre->intf.intf[j].scarrier); dev->intf.intf[i].scompr= subcount(cur->intf.intf[i].scompr, pre->intf.intf[j].scompr); dev->intf.intf[i].type = cur->intf.intf[i].type; dev->intf.intf[i].duplex = cur->intf.intf[i].duplex; dev->intf.intf[i].speed = cur->intf.intf[i].speed; dev->intf.intf[i].speedp = pre->intf.intf[j].speed; cur->intf.intf[i].speedp = pre->intf.intf[j].speed; } dev->intf.intf[i].name[0] = '\0'; dev->intf.nrintf = i; /* ** calculate deviations for disks */ for (i=j=0; cur->dsk.dsk[i].name[0]; i++) { int realj = j; /* ** check if disk has been added or removed since ** previous interval */ if ( strcmp(cur->dsk.dsk[i].name, pre->dsk.dsk[j].name) != 0) { for (j++; pre->dsk.dsk[j].name[0]; j++) { if ( strcmp(cur->dsk.dsk[i].name, pre->dsk.dsk[j].name) == 0) break; } /* ** either the corresponding entry has been found ** in the case that a disk has been removed, or ** an empty entry has been found (all counters ** on zero) in the case that a disk has been added ** during the last sample */ } strcpy(dev->dsk.dsk[i].name, cur->dsk.dsk[i].name); dev->dsk.dsk[i].nread = subcount(cur->dsk.dsk[i].nread, pre->dsk.dsk[j].nread); dev->dsk.dsk[i].nrsect = subcount(cur->dsk.dsk[i].nrsect, pre->dsk.dsk[j].nrsect); dev->dsk.dsk[i].nwrite = subcount(cur->dsk.dsk[i].nwrite, pre->dsk.dsk[j].nwrite); dev->dsk.dsk[i].nwsect = subcount(cur->dsk.dsk[i].nwsect, pre->dsk.dsk[j].nwsect); dev->dsk.dsk[i].inflight = cur->dsk.dsk[i].inflight; dev->dsk.dsk[i].io_ms = subcount(cur->dsk.dsk[i].io_ms, pre->dsk.dsk[j].io_ms); dev->dsk.dsk[i].avque = subcount(cur->dsk.dsk[i].avque, pre->dsk.dsk[j].avque); if (cur->dsk.dsk[i].ndisc != -1) // discards supported? { dev->dsk.dsk[i].ndisc = subcount(cur->dsk.dsk[i].ndisc, pre->dsk.dsk[j].ndisc); dev->dsk.dsk[i].ndsect = subcount(cur->dsk.dsk[i].ndsect, pre->dsk.dsk[j].ndsect); } else { dev->dsk.dsk[i].ndisc = -1; dev->dsk.dsk[i].ndsect = 0; } /* ** determine new j */ if (pre->dsk.dsk[j].name[0]) // existing matching entry j++; else j = realj; // empty entry: stick to old j } dev->dsk.dsk[i].name[0] = '\0'; dev->dsk.ndsk = i; /* ** calculate deviations for multiple devices */ for (i=j=0; cur->dsk.mdd[i].name[0]; i++) { int realj = j; /* ** check if md has been added or removed since ** previous interval */ if ( strcmp(cur->dsk.mdd[i].name, pre->dsk.mdd[j].name) != 0) { for (j++; pre->dsk.mdd[j].name[0]; j++) { if ( strcmp(cur->dsk.mdd[i].name, pre->dsk.mdd[j].name) == 0) break; } /* ** either the corresponding entry has been found ** in the case that a md has been removed, or ** an empty entry has been found (all counters ** on zero) in the case that a md has been added ** during the last sample */ } strcpy(dev->dsk.mdd[i].name, cur->dsk.mdd[i].name); dev->dsk.mdd[i].nread = subcount(cur->dsk.mdd[i].nread, pre->dsk.mdd[j].nread); dev->dsk.mdd[i].nrsect = subcount(cur->dsk.mdd[i].nrsect, pre->dsk.mdd[j].nrsect); dev->dsk.mdd[i].nwrite = subcount(cur->dsk.mdd[i].nwrite, pre->dsk.mdd[j].nwrite); dev->dsk.mdd[i].nwsect = subcount(cur->dsk.mdd[i].nwsect, pre->dsk.mdd[j].nwsect); dev->dsk.mdd[i].io_ms = subcount(cur->dsk.mdd[i].io_ms, pre->dsk.mdd[j].io_ms); dev->dsk.mdd[i].avque = subcount(cur->dsk.mdd[i].avque, pre->dsk.mdd[j].avque); if (cur->dsk.mdd[i].ndisc != -1) // discards supported? { dev->dsk.mdd[i].ndisc = subcount(cur->dsk.mdd[i].ndisc, pre->dsk.mdd[j].ndisc); dev->dsk.mdd[i].ndsect = subcount(cur->dsk.mdd[i].ndsect, pre->dsk.mdd[j].ndsect); } else { dev->dsk.mdd[i].ndisc = -1; dev->dsk.mdd[i].ndsect = 0; } /* ** determine new j */ if (pre->dsk.mdd[j].name[0]) // existing matching entry j++; else j = realj; // empty entry: stick to old j } dev->dsk.mdd[i].name[0] = '\0'; dev->dsk.nmdd = i; /* ** calculate deviations for LVM logical volumes */ for (i=j=0; cur->dsk.lvm[i].name[0]; i++) { int realj = j; /* ** check if logical volume has been added or removed since ** previous interval */ if ( strcmp(cur->dsk.lvm[i].name, pre->dsk.lvm[j].name) != 0) { for (j++; pre->dsk.lvm[j].name[0]; j++) { if ( strcmp(cur->dsk.lvm[i].name, pre->dsk.lvm[j].name) == 0) break; } /* ** either the corresponding entry has been found ** in the case that a logical volume has been removed, ** or an empty entry has been found (all counters ** on zero) in the case that a logical volume has ** been added during the last sample */ } strcpy(dev->dsk.lvm[i].name, cur->dsk.lvm[i].name); dev->dsk.lvm[i].nread = subcount(cur->dsk.lvm[i].nread, pre->dsk.lvm[j].nread); dev->dsk.lvm[i].nrsect = subcount(cur->dsk.lvm[i].nrsect, pre->dsk.lvm[j].nrsect); dev->dsk.lvm[i].nwrite = subcount(cur->dsk.lvm[i].nwrite, pre->dsk.lvm[j].nwrite); dev->dsk.lvm[i].nwsect = subcount(cur->dsk.lvm[i].nwsect, pre->dsk.lvm[j].nwsect); dev->dsk.lvm[i].io_ms = subcount(cur->dsk.lvm[i].io_ms, pre->dsk.lvm[j].io_ms); dev->dsk.lvm[i].avque = subcount(cur->dsk.lvm[i].avque, pre->dsk.lvm[j].avque); if (cur->dsk.lvm[i].ndisc != -1) // discards supported? { dev->dsk.lvm[i].ndisc = subcount(cur->dsk.lvm[i].ndisc, pre->dsk.lvm[j].ndisc); dev->dsk.lvm[i].ndsect = subcount(cur->dsk.lvm[i].ndsect, pre->dsk.lvm[j].ndsect); } else { dev->dsk.lvm[i].ndisc = -1; dev->dsk.lvm[i].ndsect = 0; } /* ** determine new j */ if (pre->dsk.lvm[j].name[0]) // existing matching entry j++; else j = realj; // empty entry: stick to old j } dev->dsk.lvm[i].name[0] = '\0'; dev->dsk.nlvm = i; /* ** calculate deviations for NFS */ dev->nfs.server.netcnt = subcount(cur->nfs.server.netcnt, pre->nfs.server.netcnt); dev->nfs.server.netudpcnt = subcount(cur->nfs.server.netudpcnt, pre->nfs.server.netudpcnt); dev->nfs.server.nettcpcnt = subcount(cur->nfs.server.nettcpcnt, pre->nfs.server.nettcpcnt); dev->nfs.server.nettcpcon = subcount(cur->nfs.server.nettcpcon, pre->nfs.server.nettcpcon); dev->nfs.server.rpccnt = subcount(cur->nfs.server.rpccnt, pre->nfs.server.rpccnt); dev->nfs.server.rpcread = subcount(cur->nfs.server.rpcread, pre->nfs.server.rpcread); dev->nfs.server.rpcwrite = subcount(cur->nfs.server.rpcwrite, pre->nfs.server.rpcwrite); dev->nfs.server.rpcbadfmt = subcount(cur->nfs.server.rpcbadfmt, pre->nfs.server.rpcbadfmt); dev->nfs.server.rpcbadaut = subcount(cur->nfs.server.rpcbadaut, pre->nfs.server.rpcbadaut); dev->nfs.server.rpcbadcln = subcount(cur->nfs.server.rpcbadcln, pre->nfs.server.rpcbadcln); dev->nfs.server.rchits = subcount(cur->nfs.server.rchits, pre->nfs.server.rchits); dev->nfs.server.rcmiss = subcount(cur->nfs.server.rcmiss, pre->nfs.server.rcmiss); dev->nfs.server.rcnoca = subcount(cur->nfs.server.rcnoca, pre->nfs.server.rcnoca); dev->nfs.server.nrbytes = subcount(cur->nfs.server.nrbytes, pre->nfs.server.nrbytes); dev->nfs.server.nwbytes = subcount(cur->nfs.server.nwbytes, pre->nfs.server.nwbytes); dev->nfs.client.rpccnt = subcount(cur->nfs.client.rpccnt, pre->nfs.client.rpccnt); dev->nfs.client.rpcread = subcount(cur->nfs.client.rpcread, pre->nfs.client.rpcread); dev->nfs.client.rpcwrite = subcount(cur->nfs.client.rpcwrite, pre->nfs.client.rpcwrite); dev->nfs.client.rpcretrans = subcount(cur->nfs.client.rpcretrans, pre->nfs.client.rpcretrans); dev->nfs.client.rpcautrefresh = subcount(cur->nfs.client.rpcautrefresh, pre->nfs.client.rpcautrefresh); for (i=j=0; i < cur->nfs.nfsmounts.nrmounts; i++, j++) { /* ** check if nfsmounts have been added or removed since ** previous interval */ if ( strcmp(cur->nfs.nfsmounts.nfsmnt[i].mountdev, pre->nfs.nfsmounts.nfsmnt[j].mountdev) != 0) { for (j=0; j < pre->nfs.nfsmounts.nrmounts; j++) { if ( strcmp(cur->nfs.nfsmounts.nfsmnt[i].mountdev, pre->nfs.nfsmounts.nfsmnt[j].mountdev) == 0) break; } /* ** either the corresponding entry has been found ** in the case that a container has been removed, ** or an empty entry has been found (all counters ** on zero) in the case that a container has ** been added during the last sample */ } strcpy(dev->nfs.nfsmounts.nfsmnt[i].mountdev, cur->nfs.nfsmounts.nfsmnt[i].mountdev); dev->nfs.nfsmounts.nfsmnt[i].age = cur->nfs.nfsmounts.nfsmnt[i].age; if (dev->nfs.nfsmounts.nfsmnt[i].age <= interval) memset(&(pre->nfs.nfsmounts.nfsmnt[j]), 0, sizeof(struct pernfsmount)); dev->nfs.nfsmounts.nfsmnt[i].bytesread = subcount(cur->nfs.nfsmounts.nfsmnt[i].bytesread, pre->nfs.nfsmounts.nfsmnt[j].bytesread); dev->nfs.nfsmounts.nfsmnt[i].byteswrite = subcount(cur->nfs.nfsmounts.nfsmnt[i].byteswrite, pre->nfs.nfsmounts.nfsmnt[j].byteswrite); dev->nfs.nfsmounts.nfsmnt[i].bytesdread = subcount(cur->nfs.nfsmounts.nfsmnt[i].bytesdread, pre->nfs.nfsmounts.nfsmnt[j].bytesdread); dev->nfs.nfsmounts.nfsmnt[i].bytesdwrite = subcount(cur->nfs.nfsmounts.nfsmnt[i].bytesdwrite, pre->nfs.nfsmounts.nfsmnt[j].bytesdwrite); dev->nfs.nfsmounts.nfsmnt[i].bytestotread = subcount(cur->nfs.nfsmounts.nfsmnt[i].bytestotread, pre->nfs.nfsmounts.nfsmnt[j].bytestotread); dev->nfs.nfsmounts.nfsmnt[i].bytestotwrite = subcount(cur->nfs.nfsmounts.nfsmnt[i].bytestotwrite, pre->nfs.nfsmounts.nfsmnt[j].bytestotwrite); dev->nfs.nfsmounts.nfsmnt[i].pagesmread = subcount(cur->nfs.nfsmounts.nfsmnt[i].pagesmread, pre->nfs.nfsmounts.nfsmnt[j].pagesmread); dev->nfs.nfsmounts.nfsmnt[i].pagesmwrite = subcount(cur->nfs.nfsmounts.nfsmnt[i].pagesmwrite, pre->nfs.nfsmounts.nfsmnt[j].pagesmwrite); } dev->nfs.nfsmounts.nrmounts = cur->nfs.nfsmounts.nrmounts; /* ** calculate deviations for containers */ for (i=j=0; i < cur->cfs.nrcontainer; i++, j++) { /* ** check if containers have been added or removed since ** previous interval */ if (cur->cfs.cont[i].ctid != pre->cfs.cont[j].ctid) { for (j=0; j < pre->cfs.nrcontainer; j++) { if (cur->cfs.cont[i].ctid == pre->cfs.cont[j].ctid) break; } /* ** either the corresponding entry has been found ** in the case that a container has been removed, ** or an empty entry has been found (all counters ** on zero) in the case that a container has ** been added during the last sample */ } dev->cfs.cont[i].ctid = cur->cfs.cont[i].ctid; dev->cfs.cont[i].numproc = cur->cfs.cont[i].numproc; dev->cfs.cont[i].physpages = cur->cfs.cont[i].physpages; dev->cfs.cont[i].system = subcount(cur->cfs.cont[i].system, pre->cfs.cont[j].system); dev->cfs.cont[i].user = subcount(cur->cfs.cont[i].user, pre->cfs.cont[j].user); dev->cfs.cont[i].nice = subcount(cur->cfs.cont[i].nice, pre->cfs.cont[j].nice); dev->cfs.cont[i].uptime = subcount(cur->cfs.cont[i].uptime, pre->cfs.cont[j].uptime); } dev->cfs.nrcontainer = cur->cfs.nrcontainer; /* ** calculate deviations for GPUs */ for (i=0; i < cur->gpu.nrgpus; i++) { dev->gpu.gpu[i].gpunr = i; strcpy(dev->gpu.gpu[i].type, cur->gpu.gpu[i].type); strcpy(dev->gpu.gpu[i].busid, cur->gpu.gpu[i].busid); dev->gpu.gpu[i].taskstats = cur->gpu.gpu[i].taskstats; dev->gpu.gpu[i].nrprocs = cur->gpu.gpu[i].nrprocs; dev->gpu.gpu[i].gpupercnow = cur->gpu.gpu[i].gpupercnow; dev->gpu.gpu[i].mempercnow = cur->gpu.gpu[i].mempercnow; dev->gpu.gpu[i].memtotnow = cur->gpu.gpu[i].memtotnow; dev->gpu.gpu[i].memusenow = cur->gpu.gpu[i].memusenow; dev->gpu.gpu[i].samples = subcount(cur->gpu.gpu[i].samples, pre->gpu.gpu[i].samples); if (cur->gpu.gpu[i].gpuperccum >= 0) dev->gpu.gpu[i].gpuperccum = subcount(cur->gpu.gpu[i].gpuperccum, pre->gpu.gpu[i].gpuperccum); else dev->gpu.gpu[i].gpuperccum = -1; if (cur->gpu.gpu[i].memusecum >= 0) dev->gpu.gpu[i].memusecum = subcount(cur->gpu.gpu[i].memusecum, pre->gpu.gpu[i].memusecum); else dev->gpu.gpu[i].memusecum = -1; if (cur->gpu.gpu[i].memperccum >= 0) dev->gpu.gpu[i].memperccum = subcount(cur->gpu.gpu[i].memperccum, pre->gpu.gpu[i].memperccum); else dev->gpu.gpu[i].memperccum = -1; } dev->gpu.nrgpus = cur->gpu.nrgpus; /* ** calculate deviations for InfiniBand */ for (i=0; i < cur->ifb.nrports; i++) { strcpy(dev->ifb.ifb[i].ibname, cur->ifb.ifb[i].ibname); dev->ifb.ifb[i].portnr = cur->ifb.ifb[i].portnr; dev->ifb.ifb[i].lanes = cur->ifb.ifb[i].lanes; dev->ifb.ifb[i].rate = cur->ifb.ifb[i].rate; dev->ifb.ifb[i].rcvb = cur->ifb.ifb[i].rcvb - pre->ifb.ifb[i].rcvb; dev->ifb.ifb[i].sndb = cur->ifb.ifb[i].sndb - pre->ifb.ifb[i].sndb; dev->ifb.ifb[i].rcvp = cur->ifb.ifb[i].rcvp - pre->ifb.ifb[i].rcvp; dev->ifb.ifb[i].sndp = cur->ifb.ifb[i].sndp - pre->ifb.ifb[i].sndp; } dev->ifb.nrports = cur->ifb.nrports; /* ** calculate deviations for Last Level Cache */ for (i = 0; i < cur->llc.nrllcs; i++) { dev->llc.perllc[i].id = cur->llc.perllc[i].id; dev->llc.perllc[i].occupancy = cur->llc.perllc[i].occupancy; dev->llc.perllc[i].mbm_local = cur->llc.perllc[i].mbm_local - pre->llc.perllc[i].mbm_local; dev->llc.perllc[i].mbm_total = cur->llc.perllc[i].mbm_total - pre->llc.perllc[i].mbm_total; } dev->llc.nrllcs = cur->llc.nrllcs; #if HTTPSTATS /* ** application-specific counters */ if (cur->www.uptime >= pre->www.uptime) { dev->www.accesses = subcount(cur->www.accesses, pre->www.accesses); dev->www.totkbytes = subcount(cur->www.totkbytes, pre->www.totkbytes); } else { dev->www.accesses = cur->www.accesses; dev->www.totkbytes = cur->www.totkbytes; } dev->www.bworkers = cur->www.bworkers; dev->www.iworkers = cur->www.iworkers; #endif } /* ** add the values of a new sample to a structure holding the totals ** for the indicated category (c=cpu, m=memory, d=disk, n=network). */ void totalsyst(char category, struct sstat *new, struct sstat *tot) { register int i; count_t *ctot, *cnew; switch (category) { case 'c': /* accumulate cpu-related counters */ tot->cpu.nrcpu = new->cpu.nrcpu; tot->cpu.devint += new->cpu.devint; tot->cpu.csw += new->cpu.csw; tot->cpu.nprocs += new->cpu.nprocs; tot->cpu.all.stime += new->cpu.all.stime; tot->cpu.all.utime += new->cpu.all.utime; tot->cpu.all.ntime += new->cpu.all.ntime; tot->cpu.all.itime += new->cpu.all.itime; tot->cpu.all.wtime += new->cpu.all.wtime; tot->cpu.all.Itime += new->cpu.all.Itime; tot->cpu.all.Stime += new->cpu.all.Stime; tot->cpu.all.steal += new->cpu.all.steal; tot->cpu.all.guest += new->cpu.all.guest; if (new->cpu.nrcpu == 1) { tot->cpu.cpu[0] = tot->cpu.all; } else { for (i=0; i < new->cpu.nrcpu; i++) { tot->cpu.cpu[i].cpunr = new->cpu.cpu[i].cpunr; tot->cpu.cpu[i].stime += new->cpu.cpu[i].stime; tot->cpu.cpu[i].utime += new->cpu.cpu[i].utime; tot->cpu.cpu[i].ntime += new->cpu.cpu[i].ntime; tot->cpu.cpu[i].itime += new->cpu.cpu[i].itime; tot->cpu.cpu[i].wtime += new->cpu.cpu[i].wtime; tot->cpu.cpu[i].Itime += new->cpu.cpu[i].Itime; tot->cpu.cpu[i].Stime += new->cpu.cpu[i].Stime; tot->cpu.cpu[i].steal += new->cpu.cpu[i].steal; tot->cpu.cpu[i].guest += new->cpu.cpu[i].guest; } } tot->cpu.lavg1 = new->cpu.lavg1; tot->cpu.lavg5 = new->cpu.lavg5; tot->cpu.lavg15 = new->cpu.lavg15; break; case 'm': /* accumulate memory-related counters */ tot->mem.physmem = new->mem.physmem; tot->mem.freemem = new->mem.freemem; tot->mem.buffermem = new->mem.buffermem; tot->mem.slabmem = new->mem.slabmem; tot->mem.slabreclaim = new->mem.slabreclaim; tot->mem.committed = new->mem.committed; tot->mem.commitlim = new->mem.commitlim; tot->mem.cachemem = new->mem.cachemem; tot->mem.cachedrt = new->mem.cachedrt; tot->mem.totswap = new->mem.totswap; tot->mem.freeswap = new->mem.freeswap; tot->mem.swapcached = new->mem.swapcached; tot->mem.pagetables = new->mem.pagetables; tot->mem.zswap = new->mem.zswap; tot->mem.zswapped = new->mem.zswapped; tot->mem.shmem = new->mem.shmem; tot->mem.shmrss = new->mem.shmrss; tot->mem.shmswp = new->mem.shmswp; tot->mem.tcpsock = new->mem.tcpsock; tot->mem.udpsock = new->mem.udpsock; tot->mem.pgouts += new->mem.pgouts; tot->mem.pgins += new->mem.pgins; tot->mem.swouts += new->mem.swouts; tot->mem.swins += new->mem.swins; tot->mem.zswouts += new->mem.zswouts; tot->mem.zswins += new->mem.zswins; tot->mem.pgscans += new->mem.pgscans; tot->mem.allocstall += new->mem.allocstall; tot->mem.compactstall += new->mem.compactstall; break; case 'n': /* accumulate network-related counters */ tot->nfs.server.rpccnt += new->nfs.server.rpccnt; tot->nfs.server.rpcread += new->nfs.server.rpcread; tot->nfs.server.rpcwrite += new->nfs.server.rpcwrite; tot->nfs.server.rpcbadfmt += new->nfs.server.rpcbadfmt; tot->nfs.server.rpcbadaut += new->nfs.server.rpcbadaut; tot->nfs.server.rpcbadcln += new->nfs.server.rpcbadcln; tot->nfs.server.netcnt += new->nfs.server.netcnt; tot->nfs.server.nettcpcnt += new->nfs.server.nettcpcnt; tot->nfs.server.netudpcnt += new->nfs.server.netudpcnt; tot->nfs.server.nettcpcon += new->nfs.server.nettcpcon; tot->nfs.server.rchits += new->nfs.server.rchits; tot->nfs.server.rcmiss += new->nfs.server.rcmiss; tot->nfs.server.rcnoca += new->nfs.server.rcnoca; tot->nfs.server.nrbytes += new->nfs.server.nrbytes; tot->nfs.server.nwbytes += new->nfs.server.nwbytes; tot->nfs.client.rpccnt += new->nfs.client.rpccnt; tot->nfs.client.rpcread += new->nfs.client.rpcread; tot->nfs.client.rpcwrite += new->nfs.client.rpcwrite; tot->nfs.client.rpcretrans += new->nfs.client.rpcretrans; tot->nfs.client.rpcautrefresh += new->nfs.client.rpcautrefresh; /* ** other structures with network counters are considered ** as tables of frequency-counters that will be accumulated; ** values that do not represent a frequency are corrected ** afterwards */ for (ctot = (count_t *)&tot->net.ipv4, cnew = (count_t *)&new->net.ipv4, i = 0; i < (sizeof tot->net.ipv4 / sizeof(count_t)); ctot++, cnew++, i++) *ctot += *cnew; tot->net.ipv4.Forwarding = new->net.ipv4.Forwarding; tot->net.ipv4.DefaultTTL = new->net.ipv4.DefaultTTL; /* ------------- */ for (ctot = (count_t *)&tot->net.icmpv4, cnew = (count_t *)&new->net.icmpv4, i = 0; i < (sizeof tot->net.icmpv4 / sizeof(count_t)); ctot++, cnew++, i++) *ctot += *cnew; /* ------------- */ for (ctot = (count_t *)&tot->net.udpv4, cnew = (count_t *)&new->net.udpv4, i = 0; i < (sizeof tot->net.udpv4 / sizeof(count_t)); ctot++, cnew++, i++) *ctot += *cnew; /* ------------- */ for (ctot = (count_t *)&tot->net.ipv6, cnew = (count_t *)&new->net.ipv6, i = 0; i < (sizeof tot->net.ipv6 / sizeof(count_t)); ctot++, cnew++, i++) *ctot += *cnew; /* ------------- */ for (ctot = (count_t *)&tot->net.icmpv6, cnew = (count_t *)&new->net.icmpv6, i = 0; i < (sizeof tot->net.icmpv6 / sizeof(count_t)); ctot++, cnew++, i++) *ctot += *cnew; /* ------------- */ for (ctot = (count_t *)&tot->net.udpv6, cnew = (count_t *)&new->net.udpv6, i = 0; i < (sizeof tot->net.udpv6 / sizeof(count_t)); ctot++, cnew++, i++) *ctot += *cnew; /* ------------- */ for (ctot = (count_t *)&tot->net.tcp, cnew = (count_t *)&new->net.tcp, i = 0; i < (sizeof tot->net.tcp / sizeof(count_t)); ctot++, cnew++, i++) *ctot += *cnew; tot->net.tcp.RtoAlgorithm = new->net.tcp.RtoAlgorithm; tot->net.tcp.RtoMin = new->net.tcp.RtoMin; tot->net.tcp.RtoMax = new->net.tcp.RtoMax; tot->net.tcp.MaxConn = new->net.tcp.MaxConn; tot->net.tcp.CurrEstab = new->net.tcp.CurrEstab; for (i=0; new->intf.intf[i].name[0]; i++) { /* ** check if an interface has been added or removed; ** in that case, zero all counters */ if (strcmp(new->intf.intf[i].name, tot->intf.intf[i].name) != 0) { tot->intf.intf[i].rbyte = 0; tot->intf.intf[i].rpack = 0; tot->intf.intf[i].rerrs = 0; tot->intf.intf[i].rdrop = 0; tot->intf.intf[i].rfifo = 0; tot->intf.intf[i].rframe = 0; tot->intf.intf[i].rcompr = 0; tot->intf.intf[i].rmultic = 0; tot->intf.intf[i].sbyte = 0; tot->intf.intf[i].spack = 0; tot->intf.intf[i].serrs = 0; tot->intf.intf[i].sdrop = 0; tot->intf.intf[i].sfifo = 0; tot->intf.intf[i].scollis = 0; tot->intf.intf[i].scarrier = 0; tot->intf.intf[i].scompr = 0; } /* ** accumulate counters for this sample */ strcpy(tot->intf.intf[i].name, new->intf.intf[i].name); tot->intf.intf[i].rbyte += new->intf.intf[i].rbyte; tot->intf.intf[i].rpack += new->intf.intf[i].rpack; tot->intf.intf[i].rerrs += new->intf.intf[i].rerrs; tot->intf.intf[i].rdrop += new->intf.intf[i].rdrop; tot->intf.intf[i].rfifo += new->intf.intf[i].rfifo; tot->intf.intf[i].rframe += new->intf.intf[i].rframe; tot->intf.intf[i].rcompr += new->intf.intf[i].rcompr; tot->intf.intf[i].rmultic += new->intf.intf[i].rmultic; tot->intf.intf[i].sbyte += new->intf.intf[i].sbyte; tot->intf.intf[i].spack += new->intf.intf[i].spack; tot->intf.intf[i].serrs += new->intf.intf[i].serrs; tot->intf.intf[i].sdrop += new->intf.intf[i].sdrop; tot->intf.intf[i].sfifo += new->intf.intf[i].sfifo; tot->intf.intf[i].scollis += new->intf.intf[i].scollis; tot->intf.intf[i].scarrier+= new->intf.intf[i].scarrier; tot->intf.intf[i].scompr += new->intf.intf[i].scompr; tot->intf.intf[i].type = new->intf.intf[i].type; tot->intf.intf[i].speed = new->intf.intf[i].speed; tot->intf.intf[i].duplex = new->intf.intf[i].duplex; } tot->intf.intf[i].name[0] = '\0'; tot->intf.nrintf = i; #if HTTPSTATS tot->www.accesses += new->www.accesses; tot->www.totkbytes += new->www.totkbytes; tot->www.bworkers = new->www.bworkers; tot->www.iworkers = new->www.iworkers; #endif break; case 'd': /* accumulate disk-related counters */ for (i=0; new->dsk.dsk[i].name[0]; i++) { strcpy(tot->dsk.dsk[i].name, new->dsk.dsk[i].name); tot->dsk.dsk[i].nread += new->dsk.dsk[i].nread; tot->dsk.dsk[i].nrsect += new->dsk.dsk[i].nrsect; tot->dsk.dsk[i].nwrite += new->dsk.dsk[i].nwrite; tot->dsk.dsk[i].nwsect += new->dsk.dsk[i].nwsect; tot->dsk.dsk[i].io_ms += new->dsk.dsk[i].io_ms; tot->dsk.dsk[i].avque += new->dsk.dsk[i].avque; if (new->dsk.dsk[i].ndisc != -1) // discards? { tot->dsk.dsk[i].ndisc += new->dsk.dsk[i].ndisc; tot->dsk.dsk[i].ndsect += new->dsk.dsk[i].ndsect; } } tot->dsk.dsk[i].name[0] = '\0'; tot->dsk.ndsk = i; for (i=0; new->dsk.lvm[i].name[0]; i++) { strcpy(tot->dsk.lvm[i].name, new->dsk.lvm[i].name); tot->dsk.lvm[i].nread += new->dsk.lvm[i].nread; tot->dsk.lvm[i].nrsect += new->dsk.lvm[i].nrsect; tot->dsk.lvm[i].nwrite += new->dsk.lvm[i].nwrite; tot->dsk.lvm[i].nwsect += new->dsk.lvm[i].nwsect; tot->dsk.lvm[i].io_ms += new->dsk.lvm[i].io_ms; tot->dsk.lvm[i].avque += new->dsk.lvm[i].avque; if (new->dsk.lvm[i].ndisc != -1) // discards? { tot->dsk.lvm[i].ndisc += new->dsk.lvm[i].ndisc; tot->dsk.lvm[i].ndsect += new->dsk.lvm[i].ndsect; } } tot->dsk.lvm[i].name[0] = '\0'; tot->dsk.nlvm = i; for (i=0; new->dsk.mdd[i].name[0]; i++) { strcpy(tot->dsk.mdd[i].name, new->dsk.mdd[i].name); tot->dsk.mdd[i].nread += new->dsk.mdd[i].nread; tot->dsk.mdd[i].nrsect += new->dsk.mdd[i].nrsect; tot->dsk.mdd[i].nwrite += new->dsk.mdd[i].nwrite; tot->dsk.mdd[i].nwsect += new->dsk.mdd[i].nwsect; tot->dsk.mdd[i].io_ms += new->dsk.mdd[i].io_ms; tot->dsk.mdd[i].avque += new->dsk.mdd[i].avque; if (new->dsk.mdd[i].ndisc != -1) // discards? { tot->dsk.mdd[i].ndisc += new->dsk.lvm[i].ndisc; tot->dsk.mdd[i].ndsect += new->dsk.lvm[i].ndsect; } } tot->dsk.mdd[i].name[0] = '\0'; tot->dsk.nmdd = i; break; } } /* ** Generic function to subtract two counters taking into ** account the possibility that the counter is invalid ** (i.e. non-existing). */ static inline count_t subcount(count_t newval, count_t oldval) { if (newval == -1) // invalid counter? return -1; if (newval >= oldval) // normal situation return newval - oldval; else // counter seems to be reset return newval; } atop-2.11.1/README0000644000203100020310000000727014771753566012752 0ustar gerlofgerlofCOPYRIGHT NOTICE ---------------- For all files that are not marked differently: Copyright Gerlof Langeveld 2007-2024 and licensed under the GPL v2 (or any later version). DEPENDENCIES ------------ Install the following packages to be able to build atop (package name can be different depending on the Linux distro): * zlib-devel or libz-dev or zlib1g-dev * ncurses-devel or libncurses5-dev/libncursesw5-dev * glib2-devel or libglib2.0-dev Install the following packages to be able to execute atop (package name can be different depending on the Linux distro): * zlib or zlib1g * ncurses or libncurses5/libncursesw5 * glib2 or libglib2.0 INSTALLING AND USING ATOP FROM TARBALL -------------------------------------- For interactive use, it is sufficient to install ATOP with the command (as root): make install (systemd based) or make sysvinstall (System V init based) For automatic logging in compressed binary format, see the description in the manual-page. NETWORK-RELATED COUNTERS PER PROCESS/THREAD ------------------------------------------- The kernel module 'netatop' can be downloaded and installed separately from www.atoptool.nl/downloadnetatop.php This module is optional and can be used to gather network statistics per process/thread as described in www.atoptool.nl/netatop.php Alternatively, the BPF implementation 'netatop-bpf' can be downloaded and installed from https://github.com/bytedance/netatop-bpf PROCESS ACCOUNTING WITH PSACCT/ACCT PACKAGE ------------------------------------------- Preferably, process accounting should be handled by the atopacctd daemon which is implicitly installed when installing atop. This daemon takes care that process accounting is only active when at least one atop process is running. Besides, when process accounting is activated, atopacctd takes care that the disk utilization is minimal. See the man page of atopacctd for further details. When the psacct or acct package is installed in parallel with the atop package and you want to enable/start the (ps)acct service for permanent process accounting, the atopacct.service refuses to activate the atopacctd daemon to avoid clashes. When the (ps)acct service is enabled, atop automatically uses the process accounting file that is used by this package. See also the section PROCESS ACCOUNTING in the man page of atop. OVERVIEW OF RELATED PROGRAMS ---------------------------- When installing atop the following programs are provided: atop - System and process monitor (live and from raw log) atopacctd - Daemon: handles process accounting records to be used by atop atopgpud - Daemon: gathers metrics from Nvidia GPUs to be used by atop atopsar - System activity reports (live and from raw log) atopcat - Concatenate raw log files and provide raw log information atophide - Make extractions from raw logs and/or anonymize raw logs atopconvert - Convert raw log to newer version SERVICE ACTIVATION AFTER INSTALLATING PACKAGE --------------------------------------------- After the package has been installed, be sure that the related services are activated. To support handling of terminated processes using process accounting, enable the atopacct service: systemctl enable --now atopacct Notice that this service should not be enabled when the package psacct or acct (depends on the Linux distribution) has been installed and enabled. In that case atop uses the daily process accounting file created via that package. To support maintaining daily log files to keep track of long-term analysis information, enable the atop service and the related timer: systemctl enable --now atop systemctl enable --now atop-rotate.timer Gerlof Langeveld gerlof.langeveld@atoptool.nl atop-2.11.1/photosyst.h0000644000203100020310000003703114771753566014315 0ustar gerlofgerlof/* ** ATOP - System & Process Monitor ** ** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as process-level. ** ** Include-file describing system-level counters maintained. ** ================================================================ ** Author: Gerlof Langeveld ** E-mail: gerlof.langeveld@atoptool.nl ** Date: November 1996 ** LINUX-port: June 2000 ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. */ #ifndef __PHOTOSYST__ #define __PHOTOSYST__ #include "netstats.h" #define MAXCPU 2048 #define MAXDSK 1024 #define MAXNUMA 1024 #define MAXLVM 2048 #define MAXMDD 256 #define MAXINTF 128 #define MAXCONTAINER 128 #define MAXNFSMOUNT 64 #define MAXIBPORT 32 #define MAXGPU 32 #define MAXGPUBUS 12 #define MAXGPUTYPE 12 #define MAXLLC 256 #define MAXDKNAM 32 #define MAXIBNAME 12 /************************************************************************/ struct memstat { count_t physmem; // number of physical pages count_t freemem; // number of free pages count_t buffermem; // number of buffer pages count_t slabmem; // number of slab pages count_t cachemem; // number of cache pages count_t cachedrt; // number of cache pages (dirty) count_t totswap; // number of pages in swap count_t freeswap; // number of free swap pages count_t pgscans; // number of page scans count_t pgsteal; // number of page steals count_t allocstall; // try to free pages forced count_t swouts; // number of pages swapped out count_t swins; // number of pages swapped in count_t tcpsock; // number of pages allocated by TCP sockets count_t udpsock; // number of pages allocated by UDP sockets count_t commitlim; // commit limit in pages count_t committed; // number of reserved pages count_t shmem; // tot shmem incl. tmpfs (pages) count_t shmrss; // resident shared memory (pages) count_t shmswp; // swapped shared memory (pages) count_t slabreclaim; // reclaimable slab (pages) count_t stothugepage; // total huge pages (huge pages) - small count_t sfreehugepage; // free huge pages (huge pages) - small count_t shugepagesz; // huge page size (bytes) - small count_t vmwballoon; // vmware claimed balloon pages count_t zfsarcsize; // zfsonlinux ARC size (pages) count_t swapcached; // swap cache (pages) count_t ksmsharing; // saved i.e. deduped memory (pages) count_t ksmshared; // current size shared pages (pages) count_t zswapped; // zswap stored pages decompressed (pages) count_t zswap; // zswap current pool size compressed (pages) count_t oomkills; // number of oom killings count_t compactstall; // counter for process stalls count_t pgmigrate; // counter for migrated successfully (pages) count_t numamigrate; // counter for numa migrated (pages) count_t pgouts; // total number of pages written to block device count_t pgins; // total number of pages read from block device count_t pagetables; // page tables of processes (pages) count_t zswouts; // number of pages swapped out to zswap count_t zswins; // number of pages swapped in from zswap count_t ltothugepage; // total huge pages (huge pages) - large count_t lfreehugepage; // free huge pages (huge pages) - large count_t lhugepagesz; // huge page size (bytes) - large count_t availablemem; // available memory (pages) count_t anonhugepage; // anonymous transparent huge pages // (in units of 'normal' pages) count_t cfuture[5]; // reserved for future use }; /************************************************************************/ struct mempernuma { int numanr; float frag; // fragmentation level for this numa count_t totmem; // number of physical pages for this numa count_t freemem; // number of free pages for this numa count_t filepage; // number of file pages for this numa count_t dirtymem; // number of cache pages (dirty) for this numa count_t slabmem; // number of slab pages for this numa count_t slabreclaim; // reclaimable slab (pages) for this numa count_t active; // number of pages used more recently for this numa count_t inactive; // number of pages less recently used for this numa count_t shmem; // tot shmem incl. tmpfs (pages) for this numa count_t tothp; // total huge pages (huge pages) for this numa count_t freehp; // total free pages (huge pages) for this numa count_t cfuture[2]; // reserved for future use }; struct memnuma { count_t nrnuma; /* the counts of numa */ struct mempernuma numa[MAXNUMA]; }; struct cpupernuma { int numanr; count_t nrcpu; // number of cpu's count_t stime; // accumulate system time in clock ticks for per numa count_t utime; // accumulate user time in clock ticks for per numa count_t ntime; // accumulate nice time in clock ticks for per numa count_t itime; // accumulate idle time in clock ticks for per numa count_t wtime; // accumulate iowait time in clock ticks for per numa count_t Itime; // accumulate irq time in clock ticks for per numa count_t Stime; // accumulate softirq time in clock ticks for per numa count_t steal; // accumulate steal time in clock ticks for per numa count_t guest; // accumulate guest time in clock ticks for per numa count_t cfuture[2]; // reserved for future use }; struct cpunuma { count_t nrnuma; /* the counts of numa */ struct cpupernuma numa[MAXNUMA]; }; /************************************************************************/ struct netstat { struct ipv4_stats ipv4; struct icmpv4_stats icmpv4; struct udpv4_stats udpv4; struct ipv6_stats ipv6; struct icmpv6_stats icmpv6; struct udpv6_stats udpv6; struct tcp_stats tcp; }; /************************************************************************/ struct freqcnt { count_t maxfreq;/* frequency in MHz */ count_t cnt; /* number of clock ticks times state */ count_t ticks; /* number of total clock ticks */ /* if zero, cnt is actual freq */ }; struct percpu { int cpunr; count_t stime; /* system time in clock ticks */ count_t utime; /* user time in clock ticks */ count_t ntime; /* nice time in clock ticks */ count_t itime; /* idle time in clock ticks */ count_t wtime; /* iowait time in clock ticks */ count_t Itime; /* irq time in clock ticks */ count_t Stime; /* softirq time in clock ticks */ count_t steal; /* steal time in clock ticks */ count_t guest; /* guest time in clock ticks */ struct freqcnt freqcnt;/* frequency scaling info */ count_t instr; /* CPU instructions */ count_t cycle; /* CPU cycles */ count_t cfuture[6]; /* reserved for future use */ }; struct cpustat { count_t nrcpu; /* number of cpu's */ count_t devint; /* number of device interrupts */ count_t csw; /* number of context switches */ count_t nprocs; /* number of processes started */ float lavg1; /* load average last minute */ float lavg5; /* load average last 5 minutes */ float lavg15; /* load average last 15 minutes */ count_t cfuture[4]; /* reserved for future use */ struct percpu all; struct percpu cpu[MAXCPU]; }; /************************************************************************/ struct perdsk { char name[MAXDKNAM]; /* empty string for last */ count_t nread; /* number of read transfers */ count_t nrsect; /* number of sectors read */ count_t nwrite; /* number of write transfers */ count_t nwsect; /* number of sectors written */ count_t io_ms; /* number of millisecs spent for I/O */ count_t avque; /* average queue length */ count_t ndisc; /* number of discards (-1 = unavailable)*/ count_t ndsect; /* number of sectors discarded */ count_t inflight; /* number of inflight I/O */ count_t cfuture[3]; /* reserved for future use */ }; struct dskstat { int ndsk; /* number of physical disks */ int nmdd; /* number of md volumes */ int nlvm; /* number of logical volumes */ struct perdsk dsk[MAXDSK]; struct perdsk mdd[MAXMDD]; struct perdsk lvm[MAXLVM]; }; /************************************************************************/ struct perintf { char name[16]; /* empty string for last */ count_t rbyte; /* number of read bytes */ count_t rpack; /* number of read packets */ count_t rerrs; /* receive errors */ count_t rdrop; /* receive drops */ count_t rfifo; /* receive fifo */ count_t rframe; /* receive framing errors */ count_t rcompr; /* receive compressed */ count_t rmultic;/* receive multicast */ count_t rfuture[4]; /* reserved for future use */ count_t sbyte; /* number of written bytes */ count_t spack; /* number of written packets */ count_t serrs; /* transmit errors */ count_t sdrop; /* transmit drops */ count_t sfifo; /* transmit fifo */ count_t scollis;/* collisions */ count_t scarrier;/* transmit carrier */ count_t scompr; /* transmit compressed */ count_t sfuture[4]; /* reserved for future use */ char type; /* interface type ('e'/'w'/'v'/'?') */ long speed; /* interface speed in megabits/second */ long speedp; /* previous interface speed */ char duplex; /* full duplex (boolean) */ count_t cfuture[4]; /* reserved for future use */ }; struct intfstat { int nrintf; struct perintf intf[MAXINTF]; }; /************************************************************************/ struct pernfsmount { char mountdev[128]; /* mountdevice */ count_t age; /* number of seconds mounted */ count_t bytesread; /* via normal reads */ count_t byteswrite; /* via normal writes */ count_t bytesdread; /* via direct reads */ count_t bytesdwrite; /* via direct writes */ count_t bytestotread; /* via reads */ count_t bytestotwrite; /* via writes */ count_t pagesmread; /* via mmap reads */ count_t pagesmwrite; /* via mmap writes */ count_t future[8]; }; struct nfsstat { struct { count_t netcnt; count_t netudpcnt; count_t nettcpcnt; count_t nettcpcon; count_t rpccnt; count_t rpcbadfmt; count_t rpcbadaut; count_t rpcbadcln; count_t rpcread; count_t rpcwrite; count_t rchits; /* repcache hits */ count_t rcmiss; /* repcache misses */ count_t rcnoca; /* uncached requests */ count_t nrbytes; /* read bytes */ count_t nwbytes; /* written bytes */ count_t future[8]; } server; struct { count_t rpccnt; count_t rpcretrans; count_t rpcautrefresh; count_t rpcread; count_t rpcwrite; count_t future[8]; } client; struct { int nrmounts; struct pernfsmount nfsmnt[MAXNFSMOUNT]; } nfsmounts; }; /************************************************************************/ struct psi { float avg10; // average pressure last 10 seconds float avg60; // average pressure last 60 seconds float avg300; // average pressure last 300 seconds count_t total; // total number of milliseconds }; struct pressure { char present; /* pressure stats supported? */ char future[3]; struct psi cpusome; /* pressure stall info 'some' */ struct psi memsome; /* pressure stall info 'some' */ struct psi memfull; /* pressure stall info 'full' */ struct psi iosome; /* pressure stall info 'some' */ struct psi iofull; /* pressure stall info 'full' */ }; /************************************************************************/ struct percontainer { unsigned long ctid; /* container id */ unsigned long numproc; /* number of processes */ count_t system; /* */ count_t user; /* */ count_t nice; /* */ count_t uptime; /* */ count_t physpages; /* */ }; struct contstat { int nrcontainer; struct percontainer cont[MAXCONTAINER]; }; /************************************************************************/ /* ** experimental stuff for access to local HTTP daemons */ #define HTTPREQ "GET /server-status?auto HTTP/1.1\nHost: localhost\n\n" struct wwwstat { count_t accesses; /* total number of HTTP-requests */ count_t totkbytes; /* total kbytes transfer for HTTP-req */ count_t uptime; /* number of seconds since startup */ int bworkers; /* number of busy httpd-daemons */ int iworkers; /* number of idle httpd-daemons */ }; #if HTTPSTATS int getwwwstat(unsigned short, struct wwwstat *); #endif /************************************************************************/ struct pergpu { char taskstats; // GPU task statistics supported? unsigned char nrprocs; // number of processes using GPU char type[MAXGPUTYPE+1]; // GPU type char busid[MAXGPUBUS+1]; // GPU bus identification int gpunr; // GPU number int gpupercnow; // processor percentage last second // -1 if not supported int mempercnow; // memory percentage last second // -1 if not supported count_t memtotnow; // total memory in KiB count_t memusenow; // used memory in KiB count_t samples; // number of samples count_t gpuperccum; // cumulative processor busy percentage // -1 if not supported count_t memperccum; // cumulative memory percentage // -1 if not supported count_t memusecum; // cumulative used memory in KiB }; struct gpustat { int nrgpus; // total number of GPUs struct pergpu gpu[MAXGPU]; }; /************************************************************************/ struct perifb { char ibname[MAXIBNAME]; // InfiniBand controller short portnr; // InfiniBand controller port short lanes; // number of lanes (traffic factor) count_t rate; // transfer rate in megabits/sec count_t rcvb; // bytes received count_t sndb; // bytes transmitted count_t rcvp; // packets received count_t sndp; // packets transmitted count_t cfuture[4]; // reserved for future use }; struct ifbstat { int nrports; // total number of IB ports struct perifb ifb[MAXIBPORT]; }; /************************************************************************/ struct perllc { unsigned char id; float occupancy; count_t mbm_local; count_t mbm_total; }; struct llcstat { int nrllcs; // total number of LLC struct perllc perllc[MAXLLC]; }; /************************************************************************/ struct sstat { struct cpustat cpu; struct memstat mem; struct netstat net; struct intfstat intf; struct memnuma memnuma; struct cpunuma cpunuma; struct dskstat dsk; struct nfsstat nfs; struct contstat cfs; struct pressure psi; struct gpustat gpu; struct ifbstat ifb; struct llcstat llc; struct wwwstat www; }; /* ** prototypes */ void photosyst (struct sstat *); void deviatsyst(struct sstat *, struct sstat *, struct sstat *, long); void totalsyst (char, struct sstat *, struct sstat *); void do_perfevents(char *, char *); int isdisk_major(unsigned int); /* ** return value of isdisk_...() */ #define NONTYPE 0 #define DSKTYPE 1 #define MDDTYPE 2 #define LVMTYPE 3 #endif atop-2.11.1/gpucom.c0000644000203100020310000003534614771753566013535 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 int gputype_parse(char *); static int gpustat_parse(int, char *, int, struct pergpu *, struct gpupidstat *); static int gpuparse(int, char *, struct pergpu *); static int pidparse(int, char *, struct gpupidstat *); static int rcvuntil(int, char *, int); static int actsock = -1; static int numgpus; static char **gpubusid; // array with char* to busid strings static char **gputypes; // array with char* to type strings static char *gputasks; // array with chars with tasksupport booleans /* ** Open TCP connection to port of atopgpud and ** obtain type information of every GPU. ** ** Return value: ** number of GPUs */ int gpud_init(void) { struct sockaddr_in name; socklen_t namelen = sizeof name; char typereq[] = {'T', APIVERSION}; uint32_t prelude; char *buf; int version, length; struct timeval rcvtimeout = {2, 0}; // 2 seconds /* ** get local socket */ if ( (actsock = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror("socket creation"); return 0; } /* ** connect to server port */ memset(&name, 0, sizeof name); name.sin_family = AF_INET; name.sin_addr.s_addr = htonl(INADDR_LOOPBACK); name.sin_port = htons(GPUDPORT); if (connect(actsock, (struct sockaddr *)&name, namelen) == -1) goto close_and_return; /* ** set receive timeout, not to block atop forever ** in case something fails in the commmunication */ (void) setsockopt(actsock, SOL_SOCKET, SO_RCVTIMEO, &rcvtimeout, sizeof rcvtimeout); /* ** send request: GPU types */ if ( write(actsock, typereq, sizeof typereq) < sizeof typereq) { perror("send type request to atopgpud"); goto close_and_return; } /* ** receive response: GPU types */ if (rcvuntil(actsock, (char *)&prelude, sizeof prelude) == -1) { perror("receive prelude from atopgpud"); goto close_and_return; } prelude = ntohl(prelude); // big endian to native endianess version = (prelude >> 24) & 0xff; length = prelude & 0xffffff; if (version != APIVERSION) { fprintf(stderr, "wrong API version from atopgpud: %d %d\n", version, APIVERSION); goto close_and_return; } if (length > 8192) // sanity check { fprintf(stderr, "unexpected response length atopgpud: %d\n", length); goto close_and_return; } buf = malloc(length+1); ptrverify(buf, "Malloc failed for gpu rcvbuf\n"); if ( rcvuntil(actsock, buf, length) == -1) { perror("receive type request from atopgpud"); free(buf); goto close_and_return; } buf[length] = '\0'; if (! gputype_parse(buf)) { free(buf); goto close_and_return; } return numgpus; close_and_return: close(actsock); actsock = -1; numgpus = 0; return 0; } /* ** Transmit status request for all GPUs. ** ** Calling parameters: ** void ** ** Return value: ** 0 in case of failure ** 1 in case of success (request pending) */ int gpud_statrequest(void) { char statreq[] = {'S', APIVERSION}; if (actsock == -1) return 0; if ( write(actsock, statreq, sizeof statreq) < sizeof statreq) { close(actsock); actsock = -1; numgpus = 0; return 0; } return 1; } /* ** Receive status response for all GPUs. ** ** Calling parameters: ** *ggs pointer to allocated array of pergpu structs ** **gps pointer to pointer in which addresses to gpupidstat structs ** are returned ** can be NULL pointer is caller is not interested in proc stats ** ** Return value: ** number of gpupidstat addresses (i.e. per-process info) ** -1 in case of failure */ int gpud_statresponse(int maxgpu, struct pergpu *ggs, struct gpupidstat **gps) { uint32_t prelude; char *buf = NULL, *p; int version, length; int maxprocs = 0, nrprocs; if (actsock == -1) return -1; /* ** receive 4-bytes introducer: ** first byte: API version ** next three bytes: length of string that follows */ if ( rcvuntil(actsock, (char *)&prelude, sizeof prelude) == -1) { perror("receive 4-byte prelude from atopgpud"); goto close_and_return; } prelude = ntohl(prelude); // big endian to native endianess version = (prelude >> 24) & 0xff; length = prelude & 0xffffff; if (version != APIVERSION) { fprintf(stderr, "wrong API version from atopgpud: %d %d\n", version, APIVERSION); goto close_and_return; } if (length > 8192) // sanity check { fprintf(stderr, "unexpected response length atopgpud: %d\n", length); goto close_and_return; } buf = malloc(length+1); ptrverify(buf, "Malloc failed for gpu rcvbuf\n"); /* ** receive statistics string */ if ( rcvuntil(actsock, buf, length) == -1) { perror("receive stats string from atopgpud"); goto close_and_return; } *(buf+length) = '\0'; /* ** determine number of per-process stats in string ** and malloc space to store these stats */ for (p=buf; *p; p++) { if (*p == PIDDELIM) maxprocs++; } if (gps) { if (maxprocs) { *gps = malloc(maxprocs * sizeof(struct gpupidstat)); ptrverify(*gps, "Malloc failed for gpu pidstats\n"); memset(*gps, 0, maxprocs * sizeof(struct gpupidstat)); } else { *gps = NULL; } } /* ** parse stats string for per-gpu stats */ if ( (nrprocs = gpustat_parse(version, buf, maxgpu, ggs, gps ? *gps : NULL)) == -1) { if (gps) { free(*gps); *gps = NULL; // avoid double free later on } goto close_and_return; // inconsistent data received from atopgpud } free(buf); return nrprocs; close_and_return: free(buf); close(actsock); actsock = -1; numgpus = 0; return -1; } /* ** Receive given number of bytes from given socket ** into given buffer address ** ** Return value: number of bytes received ** -1 - failed (including end-of-connection) */ static int rcvuntil(int sock, char *buf, int size) { int remain = size, n; while (remain) { n = read(sock, buf, remain); if (n <= 0) return -1; buf += n; remain -= n; } return size; } /* ** Parse response string from server on 'T' request ** ** Store the type, busid and tasksupport of every GPU in ** static pointer tables ** ** Return value: 1 - success ** 0 - failed */ static int gputype_parse(char *buf) { char *p, *start, **bp, **tp, *cp, fails=0; /* ** determine number of GPUs */ if ( sscanf(buf, "%d@", &numgpus) != 1) return 0; numgpus = numgpus <= MAXGPU ? numgpus : MAXGPU; /* ** search for first GPU delimiter (@) */ for (p=buf; *p; p++) { if (*p == GPUDELIM) { p++; break; } } if (*p == 0) // no delimiter or no data behind delimeter? return 0; /* ** parse GPU info and build arrays of pointers to the ** busid strings, type strings and tasksupport strings. */ if (numgpus) // GPUs present anyhow? { int field; gpubusid = bp = malloc((numgpus+1) * sizeof(char *)); gputypes = tp = malloc((numgpus+1) * sizeof(char *)); gputasks = cp = malloc((numgpus) * sizeof(char )); ptrverify(gpubusid, "Malloc failed for gpu busids\n"); ptrverify(gputypes, "Malloc failed for gpu types\n"); ptrverify(gputasks, "Malloc failed for gpu tasksup\n"); for (field=0, start=p; fails == 0; p++) { if (*p == ' ' || *p == '\0' || *p == GPUDELIM) { switch(field) { case 0: if (bp - gpubusid >= numgpus) { fails++; break; // inconsistent with number of GPUs } if (p-start <= MAXGPUBUS) *bp++ = start; else *bp++ = p - MAXGPUBUS; break; case 1: if (tp - gputypes >= numgpus) { fails++; break; // inconsistent with number of GPUs } if (p-start <= MAXGPUTYPE) *tp++ = start; else *tp++ = p - MAXGPUTYPE; break; case 2: if (cp - gputasks >= numgpus) { fails++; break; // inconsistent with number of GPUs } *cp++ = *start; break; default: fails++; } field++; start = p+1; if (*p == '\0') break; if (*p == GPUDELIM) field = 0; *p = '\0'; } } *bp = NULL; *tp = NULL; /* ** verify if number of GPUs and supplied per-GPU information ** appears to be inconsistent */ if (fails || bp - gpubusid != numgpus || tp - gputypes != numgpus || cp - gputasks != numgpus) { free(gpubusid); free(gputypes); free(gputasks); return 0; } } else { return 0; } return 1; } /* ** Parse entire response string from server. ** ** Every series with counters on GPU level is introduced ** with a '@' delimiter. ** Every series with counters on process level is introduced ** with a '#' delimiter (last part of the GPU level data). ** ** Return value: valid number of processes ** -1 - failed */ static int gpustat_parse(int version, char *buf, int maxgpu, struct pergpu *gg, struct gpupidstat *gp) { char *p, *pp, *start; int gpunum, nrprocs = 0; /* ** parse stats string */ for (p=buf; *p && *p != GPUDELIM; p++) // find first GPU deimiter ; if (*p == 0) // string without GPU delimiter return -1; for (p++, start=p, gpunum=0; gpunum < maxgpu; p++) { char delimnext; // search next GPU delimiter // if (*p && *p != GPUDELIM) continue; /* ** next GPU delimiter or end-of-string found */ delimnext = *p; *p = 0; /* ** parse GPU itself */ if (! gpuparse(version, start, gg)) return -1; strncpy(gg->type, gputypes[gpunum], MAXGPUTYPE); strncpy(gg->busid, gpubusid[gpunum], MAXGPUBUS); /* ** continue searching for per-process stats for this GPU */ if (gp) { for (pp = start; pp < p; pp++) { if (*pp != PIDDELIM) continue; // new PID delimiter (#) found // if (! pidparse(version, pp+1, gp)) return -1; gp->gpu.nrgpus++; gp->gpu.gpulist = 1<nrprocs++; // per GPU nrprocs++; // total } } gpunum++; gg++; if (delimnext == 0 || *(p+1) == 0) break; start = p+1; } return nrprocs; } /* ** Parse GPU statistics string ** ** Return value: 1 - success ** 0 - failed */ static int gpuparse(int version, char *p, struct pergpu *gg) { int nr; switch (version) { case 1: nr = sscanf(p, "%d %d %lld %lld %lld %lld %lld %lld", &(gg->gpupercnow), &(gg->mempercnow), &(gg->memtotnow), &(gg->memusenow), &(gg->samples), &(gg->gpuperccum), &(gg->memperccum), &(gg->memusecum)); if (nr < 8) // parse error: unexpected data return 0; gg->nrprocs = 0; break; } return 1; } /* ** Parse PID statistics string ** ** Return value: 1 - success ** 0 - failed */ static int pidparse(int version, char *p, struct gpupidstat *gp) { int nr; switch (version) { case 1: nr = sscanf(p, "%c %ld %d %d %lld %lld %lld %lld", &(gp->gpu.state), &(gp->pid), &(gp->gpu.gpubusy), &(gp->gpu.membusy), &(gp->gpu.timems), &(gp->gpu.memnow), &(gp->gpu.memcum), &(gp->gpu.sample)); if (nr < 8) // parse error: unexpected data return 0; break; } return 1; } /* ** Merge the GPU per-process counters with the other ** per-process counters */ static int compgpupid(const void *, const void *); void gpumergeproc(struct tstat *curtpres, int ntaskpres, struct tstat *curpexit, int nprocexit, struct gpupidstat *gpuproc, int nrgpuproc) { struct gpupidstat **gpp; int t, g, gpuleft = nrgpuproc; /* ** make pointer list for elements in gpuproc */ gpp = malloc(nrgpuproc * sizeof(struct gpupidstat *)); if (!gpp) ptrverify(gpp, "Malloc failed for process list\n"); for (g=0; g < nrgpuproc; g++) gpp[g] = gpuproc + g; /* ** sort the list with pointers in order of pid */ if (nrgpuproc > 1) qsort(gpp, nrgpuproc, sizeof(struct gpupidstat *), compgpupid); /* ** accumulate entries that contain stats from same PID ** on different GPUs */ for (g=1; g < nrgpuproc; g++) { if (gpp[g-1]->pid == gpp[g]->pid) { struct gpupidstat *p = gpp[g-1], *q = gpp[g]; p->gpu.nrgpus += q->gpu.nrgpus; p->gpu.gpulist |= q->gpu.gpulist; if (p->gpu.gpubusy != -1) p->gpu.gpubusy += q->gpu.gpubusy; if (p->gpu.membusy != -1) p->gpu.membusy += q->gpu.membusy; if (p->gpu.timems != -1) p->gpu.timems += q->gpu.timems; p->gpu.memnow += q->gpu.memnow; p->gpu.memcum += q->gpu.memcum; p->gpu.sample += q->gpu.sample; if (nrgpuproc-g-1 > 0) memmove(&(gpp[g]), &(gpp[g+1]), (nrgpuproc-g-1) * sizeof p); nrgpuproc--; g--; } } /* ** merge gpu stats with sorted task list of active processes */ for (t=g=0; t < ntaskpres && g < nrgpuproc; t++) { if (curtpres[t].gen.isproc) { if (curtpres[t].gen.pid == gpp[g]->pid) { curtpres[t].gpu = gpp[g]->gpu; gpp[g++] = NULL; if (--gpuleft == 0 || g >= nrgpuproc) break; } // anyhow resync while ( curtpres[t].gen.pid > gpp[g]->pid) { if (++g >= nrgpuproc) break; } } } if (gpuleft == 0) { free(gpp); return; } /* ** compact list with pointers to remaining pids */ for (g=t=0; g < nrgpuproc; g++) { if (gpp[g] == NULL) { for (; t < nrgpuproc; t++) { if (gpp[t]) { gpp[g] = gpp[t]; gpp[t] = NULL; break; } } } } /* ** merge remaining gpu stats with task list of exited processes */ for (t=0; t < nprocexit && gpuleft; t++) { if (curpexit[t].gen.isproc) { for (g=0; g < gpuleft; g++) { if (gpp[g] == NULL) continue; if (curpexit[t].gen.pid == gpp[g]->pid) { curpexit[t].gpu = gpp[g]->gpu; gpp[g] = NULL; gpuleft--; } } } } free(gpp); } static int compgpupid(const void *a, const void *b) { return (*((struct gpupidstat **)a))->pid - (*((struct gpupidstat **)b))->pid; } atop-2.11.1/json.h0000644000203100020310000000267314771753566013216 0ustar gerlofgerlof/* ** ATOP - System & Process Monitor ** ** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as process-level. ** ========================================================================== ** Author: Fei Li & zhenwei pi ** E-mail: lifei.shirley@bytedance.com, pizhenwei@bytedance.com ** Date: August 2019 ** -------------------------------------------------------------------------- ** Copyright (C) Copyright (C) 2019-2022 bytedance.com ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ** -------------------------------------------------------------------------- */ int jsondef(char *); char jsonout(time_t, int, struct devtstat *, struct sstat *, struct cgchainer *, int, int, int, unsigned int, char); atop-2.11.1/netatopif.c0000644000203100020310000002663114771753566014231 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.11.1/json.c0000644000203100020310000011201314771753566013177 0ustar gerlofgerlof/* ** ATOP - System & Process Monitor ** ** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as process-level. ** ** ========================================================================== ** Author: Fei Li & zhenwei pi ** E-mail: lifei.shirley@bytedance.com, pizhenwei@bytedance.com ** Date: August 2019 ** -------------------------------------------------------------------------- ** Copyright (C) 2019-2022 bytedance.com ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ** -------------------------------------------------------------------------- ** ** Revision 1.1 2019/08/08 14:02:19 ** Initial revision ** Add support for json style output, basing on the parseable.c file. ** */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "atop.h" #include "photosyst.h" #include "photoproc.h" #include "cgroups.h" #include "json.h" #define LEN_HP_SIZE 64 #define LINE_BUF_SIZE 1024 static void json_print_CPU(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); static void json_print_cpu(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); static void json_print_CPL(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); static void json_print_GPU(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); static void json_print_MEM(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); static void json_print_SWP(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); static void json_print_PAG(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); static void json_print_PSI(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); static void json_print_LVM(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); static void json_print_MDD(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); static void json_print_DSK(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); static void json_print_NFM(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); static void json_print_NFC(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); static void json_print_NFS(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); static void json_print_NET(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); static void json_print_IFB(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); static void json_print_NUM(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); static void json_print_NUC(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); static void json_print_LLC(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); static void json_print_CGR(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); static void json_print_PRG(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); static void json_print_PRC(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); static void json_print_PRM(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); static void json_print_PRD(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); static void json_print_PRN(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); static void json_print_PRE(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); /* ** table with possible labels and the corresponding ** print-function for json style output */ struct labeldef { char *label; short valid; short cgroupref; void (*prifunc)(char *, struct sstat *, struct tstat *, int, struct cgchainer *, int); }; static struct labeldef labeldef[] = { { "CPU", 0, 0, json_print_CPU }, { "cpu", 0, 0, json_print_cpu }, { "CPL", 0, 0, json_print_CPL }, { "GPU", 0, 0, json_print_GPU }, { "MEM", 0, 0, json_print_MEM }, { "SWP", 0, 0, json_print_SWP }, { "PAG", 0, 0, json_print_PAG }, { "PSI", 0, 0, json_print_PSI }, { "LVM", 0, 0, json_print_LVM }, { "MDD", 0, 0, json_print_MDD }, { "DSK", 0, 0, json_print_DSK }, { "NFM", 0, 0, json_print_NFM }, { "NFC", 0, 0, json_print_NFC }, { "NFS", 0, 0, json_print_NFS }, { "NET", 0, 0, json_print_NET }, { "IFB", 0, 0, json_print_IFB }, { "NUM", 0, 0, json_print_NUM }, { "NUC", 0, 0, json_print_NUC }, { "LLC", 0, 0, json_print_LLC }, { "CGR", 0, 0, json_print_CGR }, { "PRG", 0, 1, json_print_PRG }, { "PRC", 0, 1, json_print_PRC }, { "PRM", 0, 1, json_print_PRM }, { "PRD", 0, 0, json_print_PRD }, { "PRN", 0, 0, json_print_PRN }, { "PRE", 0, 0, json_print_PRE }, }; static int numlabels = sizeof labeldef / sizeof(struct labeldef); /* ** analyse the json-definition string that has been ** passed as argument with the flag -J */ int jsondef(char *pd) { register int i; char *p, *ep = pd + strlen(pd); /* ** check if string passed behind -J is not another flag */ if (*pd == '-') { printf("json labels should be followed by label list\n"); return 0; } /* ** check list of comma-separated labels */ while (pd < ep) { /* ** exchange comma by null-byte */ if ( (p = strchr(pd, ',')) ) *p = 0; else p = ep-1; /* ** check if the next label exists */ for (i=0; i < numlabels; i++) { if ( strcmp(labeldef[i].label, pd) == 0) { labeldef[i].valid = 1; break; } } /* ** non-existing label has been used */ if (i == numlabels) { /* ** check if special label 'ALL' has been used */ if ( strcmp("ALL", pd) == 0) { for (i=0; i < numlabels; i++) labeldef[i].valid = 1; break; } else { printf("json labels not supported: %s\n", pd); return 0; } } pd = p+1; } setbuf(stdout, (char *)0); return 1; } /* ** produce json output for an interval */ char jsonout(time_t curtime, int numsecs, struct devtstat *devtstat, struct sstat *sstat, struct cgchainer *devchain, int ncgroups, int npids, int nexit, unsigned int noverflow, char flag) { register int i, j, k, cgroupref_created = 0; char header[256]; struct tstat *tmp = devtstat->taskall; printf("{\"host\": \"%s\", " "\"timestamp\": %ld, " "\"elapsed\": %d", utsname.nodename, curtime, numsecs ); /* Replace " with # in case json can not parse this out */ for (k = 0; k < devtstat->ntaskall; k++, tmp++) { for (j = 0; (j < sizeof(tmp->gen.name)) && tmp->gen.name[j]; j++) if ((tmp->gen.name[j] == '\"') || (tmp->gen.name[j] == '\\')) tmp->gen.name[j] = '#'; for (j = 0; (j < sizeof(tmp->gen.cmdline) && tmp->gen.cmdline[j]); j++) if ((tmp->gen.cmdline[j] == '\"') || (tmp->gen.cmdline[j] == '\\')) tmp->gen.cmdline[j] = '#'; } /* ** iterate all labels defined in labeldef[] */ for (i=0; i < numlabels; i++) { if (!labeldef[i].valid) continue; /* ** when cgroup index is needed to map the tstat to a cgroup, ** once fill the tstat.gen.cgroupix variables */ if (supportflags & CGROUPV2 && labeldef[i].cgroupref && !cgroupref_created) { cgfillref(devtstat, devchain, ncgroups, npids); cgroupref_created = 1; } /* prepare generic columns */ snprintf(header, sizeof header, "\"%s\"", labeldef[i].label); /* call all print-functions */ (labeldef[i].prifunc)(header, sstat, devtstat->taskall, devtstat->ntaskall, devchain, ncgroups); } printf("}\n"); return '\0'; } /* ** print functions for system-level statistics */ static void json_calc_freqscale(count_t maxfreq, count_t cnt, count_t ticks, count_t *freq, int *freqperc) { // if ticks != 0,do full calcs if (maxfreq && ticks) { *freq = cnt/ticks; *freqperc = 100* *freq / maxfreq; } else if (maxfreq) { // max frequency is known so % can be calculated *freq = cnt; *freqperc = 100 * cnt / maxfreq; } else if (cnt) { // no max known, set % to 100 *freq = cnt; *freqperc = 100; } else { // nothing is known: set freq to 0, % to 100 *freq = 0; *freqperc = 100; } } static void json_print_CPU(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *cs, int ncgroups) { count_t maxfreq=0; count_t cnt=0; count_t ticks=0; count_t freq; int freqperc; int i; // calculate average clock frequency for (i = 0; i < ss->cpu.nrcpu; i++) { cnt += ss->cpu.cpu[i].freqcnt.cnt; ticks += ss->cpu.cpu[i].freqcnt.ticks; } maxfreq = ss->cpu.cpu[0].freqcnt.maxfreq; json_calc_freqscale(maxfreq, cnt, ticks, &freq, &freqperc); if (ss->cpu.all.instr == 1) { ss->cpu.all.instr = 0; ss->cpu.all.cycle = 0; } printf(", %s: {" "\"hertz\": %u, " "\"nrcpu\": %lld, " "\"stime\": %lld, " "\"utime\": %lld, " "\"ntime\": %lld, " "\"itime\": %lld, " "\"wtime\": %lld, " "\"Itime\": %lld, " "\"Stime\": %lld, " "\"steal\": %lld, " "\"guest\": %lld, " "\"freq\": %lld, " "\"freqperc\": %d, " "\"instr\": %lld, " "\"cycle\": %lld}", hp, hertz, ss->cpu.nrcpu, ss->cpu.all.stime, ss->cpu.all.utime, ss->cpu.all.ntime, ss->cpu.all.itime, ss->cpu.all.wtime, ss->cpu.all.Itime, ss->cpu.all.Stime, ss->cpu.all.steal, ss->cpu.all.guest, freq, freqperc, ss->cpu.all.instr, ss->cpu.all.cycle ); } static void json_print_cpu(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *cs, int ncgroups) { register int i; count_t maxfreq=0; count_t cnt=0; count_t ticks=0; count_t freq; int freqperc; printf(", %s: [", hp); for (i = 0; i < ss->cpu.nrcpu; i++) { if (i > 0) { printf(", "); } cnt = ss->cpu.cpu[i].freqcnt.cnt; ticks = ss->cpu.cpu[i].freqcnt.ticks; maxfreq= ss->cpu.cpu[0].freqcnt.maxfreq; json_calc_freqscale(maxfreq, cnt, ticks, &freq, &freqperc); printf("{\"cpuid\": %d, " "\"stime\": %lld, " "\"utime\": %lld, " "\"ntime\": %lld, " "\"itime\": %lld, " "\"wtime\": %lld, " "\"Itime\": %lld, " "\"Stime\": %lld, " "\"steal\": %lld, " "\"guest\": %lld, " "\"freq\": %lld, " "\"freqperc\": %d, " "\"instr\": %lld, " "\"cycle\": %lld}", i, ss->cpu.cpu[i].stime, ss->cpu.cpu[i].utime, ss->cpu.cpu[i].ntime, ss->cpu.cpu[i].itime, ss->cpu.cpu[i].wtime, ss->cpu.cpu[i].Itime, ss->cpu.cpu[i].Stime, ss->cpu.cpu[i].steal, ss->cpu.cpu[i].guest, freq, freqperc, ss->cpu.cpu[i].instr, ss->cpu.cpu[i].cycle); } printf("]"); } static void json_print_CPL(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *cs, int ncgroups) { printf(", %s: {" "\"lavg1\": %.2f, " "\"lavg5\": %.2f, " "\"lavg15\": %.2f, " "\"csw\": %lld, " "\"devint\": %lld}", hp, ss->cpu.lavg1, ss->cpu.lavg5, ss->cpu.lavg15, ss->cpu.csw, ss->cpu.devint); } static void json_print_GPU(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *cs, int ncgroups) { int i; printf(", %s: [", hp); for (i = 0; i < ss->gpu.nrgpus; i++) { if (i > 0) { printf(", "); } printf("{\"gpuid\": %d, " "\"busid\": \"%.19s\", " "\"type\": \"%.19s\", " "\"gpupercnow\": %d, " "\"mempercnow\": %d, " "\"memtotnow\": %lld, " "\"memusenow\": %lld, " "\"samples\": %lld, " "\"gpuperccum\": %lld, " "\"memperccum\": %lld, " "\"memusecum\": %lld}", i, ss->gpu.gpu[i].busid, ss->gpu.gpu[i].type, ss->gpu.gpu[i].gpupercnow, ss->gpu.gpu[i].mempercnow, ss->gpu.gpu[i].memtotnow, ss->gpu.gpu[i].memusenow, ss->gpu.gpu[i].samples, ss->gpu.gpu[i].gpuperccum, ss->gpu.gpu[i].memperccum, ss->gpu.gpu[i].memusecum); } printf("]"); } static void json_print_MEM(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *cs, int ncgroups) { printf(", %s: {" "\"physmem\": %lld, " "\"freemem\": %lld, " "\"cachemem\": %lld, " "\"buffermem\": %lld, " "\"slabmem\": %lld, " "\"cachedrt\": %lld, " "\"slabreclaim\": %lld, " "\"vmwballoon\": %lld, " "\"shmem\": %lld, " "\"shmrss\": %lld, " "\"shmswp\": %lld, " "\"hugepagesz\": %lld, " "\"tothugepage\": %lld, " "\"freehugepage\": %lld, " "\"lhugepagesz\": %lld, " "\"ltothugepage\": %lld, " "\"lfreehugepage\": %lld, " "\"availablemem\": %lld, " "\"anonhugepage\": %lld}", hp, ss->mem.physmem * pagesize, ss->mem.freemem * pagesize, ss->mem.cachemem * pagesize, ss->mem.buffermem * pagesize, ss->mem.slabmem * pagesize, ss->mem.cachedrt * pagesize, ss->mem.slabreclaim * pagesize, ss->mem.vmwballoon * pagesize, ss->mem.shmem * pagesize, ss->mem.shmrss * pagesize, ss->mem.shmswp * pagesize, ss->mem.shugepagesz, ss->mem.stothugepage, ss->mem.sfreehugepage, ss->mem.lhugepagesz, ss->mem.ltothugepage, ss->mem.lfreehugepage, ss->mem.availablemem * pagesize, ss->mem.anonhugepage * pagesize); } static void json_print_SWP(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *cs, int ncgroups) { printf(", %s: {" "\"totswap\": %lld, " "\"freeswap\": %lld, " "\"swcac\": %lld, " "\"committed\": %lld, " "\"commitlim\": %lld}", hp, ss->mem.totswap * pagesize, ss->mem.freeswap * pagesize, ss->mem.swapcached * pagesize, ss->mem.committed * pagesize, ss->mem.commitlim * pagesize); } static void json_print_PAG(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *cs, int ncgroups) { printf(", %s: {" "\"compacts\": %lld, " "\"numamigs\": %lld, " "\"migrates\": %lld, " "\"pgscans\": %lld, " "\"allocstall\": %lld, " "\"pgins\": %lld, " "\"pgouts\": %lld, " "\"swins\": %lld, " "\"swouts\": %lld, " "\"oomkills\": %lld}", hp, ss->mem.compactstall, ss->mem.numamigrate, ss->mem.pgmigrate, ss->mem.pgscans, ss->mem.allocstall, ss->mem.pgins, ss->mem.pgouts, ss->mem.swins, ss->mem.swouts, ss->mem.oomkills); } static void json_print_PSI(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *cs, int ncgroups) { if ( !(ss->psi.present) ) return; printf(", %s: {" "\"psi\": \"%c\", " "\"cs10\": %.1f, " "\"cs60\": %.1f, " "\"cs300\": %.1f, " "\"cstot\": %llu, " "\"ms10\": %.1f, " "\"ms60\": %.1f, " "\"ms300\": %.1f, " "\"mstot\": %llu, " "\"mf10\": %.1f, " "\"mf60\": %.1f, " "\"mf300\": %.1f, " "\"mftot\": %llu, " "\"ios10\": %.1f, " "\"ios60\": %.1f, " "\"ios300\": %.1f, " "\"iostot\": %llu, " "\"iof10\": %.1f, " "\"iof60\": %.1f, " "\"iof300\": %.1f, " "\"ioftot\": %llu}", hp, ss->psi.present ? 'y' : 'n', ss->psi.cpusome.avg10, ss->psi.cpusome.avg60, ss->psi.cpusome.avg300, ss->psi.cpusome.total, ss->psi.memsome.avg10, ss->psi.memsome.avg60, ss->psi.memsome.avg300, ss->psi.memsome.total, ss->psi.memfull.avg10, ss->psi.memfull.avg60, ss->psi.memfull.avg300, ss->psi.memfull.total, ss->psi.iosome.avg10, ss->psi.iosome.avg60, ss->psi.iosome.avg300, ss->psi.iosome.total, ss->psi.iofull.avg10, ss->psi.iofull.avg60, ss->psi.iofull.avg300, ss->psi.iofull.total); } static void json_print_LVM(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *cs, int ncgroups) { register int i; printf(", %s: [", hp); for (i = 0; ss->dsk.lvm[i].name[0]; i++) { if (i > 0) { printf(", "); } printf("{\"lvmname\": \"%.19s\", " "\"io_ms\": %lld, " "\"nread\": %lld, " "\"nrsect\": %lld, " "\"nwrite\": %lld, " "\"nwsect\": %lld, " "\"avque\": %lld, " "\"inflight\": %lld}", ss->dsk.lvm[i].name, ss->dsk.lvm[i].io_ms, ss->dsk.lvm[i].nread, ss->dsk.lvm[i].nrsect, ss->dsk.lvm[i].nwrite, ss->dsk.lvm[i].nwsect, ss->dsk.lvm[i].avque, ss->dsk.lvm[i].inflight); } printf("]"); } static void json_print_MDD(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *cs, int ncgroups) { register int i; printf(", %s: [", hp); for (i = 0; ss->dsk.mdd[i].name[0]; i++) { if (i > 0) { printf(", "); } printf("{\"mddname\": \"%.19s\", " "\"io_ms\": %lld, " "\"nread\": %lld, " "\"nrsect\": %lld, " "\"nwrite\": %lld, " "\"nwsect\": %lld, " "\"avque\": %lld, " "\"inflight\": %lld}", ss->dsk.mdd[i].name, ss->dsk.mdd[i].io_ms, ss->dsk.mdd[i].nread, ss->dsk.mdd[i].nrsect, ss->dsk.mdd[i].nwrite, ss->dsk.mdd[i].nwsect, ss->dsk.mdd[i].avque, ss->dsk.mdd[i].inflight); } printf("]"); } static void json_print_DSK(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *cs, int ncgroups) { register int i; printf(", %s: [", hp); for (i = 0; ss->dsk.dsk[i].name[0]; i++) { if (i > 0) { printf(", "); } printf("{\"dskname\": \"%.19s\", " "\"io_ms\": %lld, " "\"nread\": %lld, " "\"nrsect\": %lld, " "\"ndiscrd\": %lld, " "\"nwrite\": %lld, " "\"nwsect\": %lld, " "\"avque\": %lld, " "\"inflight\": %lld}", ss->dsk.dsk[i].name, ss->dsk.dsk[i].io_ms, ss->dsk.dsk[i].nread, ss->dsk.dsk[i].nrsect, ss->dsk.dsk[i].ndisc, ss->dsk.dsk[i].nwrite, ss->dsk.dsk[i].nwsect, ss->dsk.dsk[i].avque, ss->dsk.dsk[i].inflight); } printf("]"); } static void json_print_NFM(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *cs, int ncgroups) { register int i; printf(", %s: [", hp); for (i = 0; i < ss->nfs.nfsmounts.nrmounts; i++) { if (i > 0) { printf(", "); } printf("{\"mountdev\": \"%.19s\", " "\"bytestotread\": %lld, " "\"bytestotwrite\": %lld, " "\"bytesread\": %lld, " "\"byteswrite\": %lld, " "\"bytesdread\": %lld, " "\"bytesdwrite\": %lld, " "\"pagesmread\": %lld, " "\"pagesmwrite\": %lld}", ss->nfs.nfsmounts.nfsmnt[i].mountdev, ss->nfs.nfsmounts.nfsmnt[i].bytestotread, ss->nfs.nfsmounts.nfsmnt[i].bytestotwrite, ss->nfs.nfsmounts.nfsmnt[i].bytesread, ss->nfs.nfsmounts.nfsmnt[i].byteswrite, ss->nfs.nfsmounts.nfsmnt[i].bytesdread, ss->nfs.nfsmounts.nfsmnt[i].bytesdwrite, ss->nfs.nfsmounts.nfsmnt[i].pagesmread * pagesize, ss->nfs.nfsmounts.nfsmnt[i].pagesmwrite * pagesize); } printf("]"); } static void json_print_NFC(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *cs, int ncgroups) { printf(", %s: {" "\"rpccnt\": %lld, " "\"rpcread\": %lld, " "\"rpcwrite\": %lld, " "\"rpcretrans\": %lld, " "\"rpcautrefresh\": %lld}", hp, ss->nfs.client.rpccnt, ss->nfs.client.rpcread, ss->nfs.client.rpcwrite, ss->nfs.client.rpcretrans, ss->nfs.client.rpcautrefresh); } static void json_print_NFS(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *cs, int ncgroups) { printf(", %s: {" "\"rpccnt\": %lld, " "\"rpcread\": %lld, " "\"rpcwrite\": %lld, " "\"nrbytes\": %lld, " "\"nwbytes\": %lld, " "\"rpcbadfmt\": %lld, " "\"rpcbadaut\": %lld, " "\"rpcbadcln\": %lld, " "\"netcnt\": %lld, " "\"nettcpcnt\": %lld, " "\"netudpcnt\": %lld, " "\"nettcpcon\": %lld, " "\"rchits\": %lld, " "\"rcmiss\": %lld, " "\"rcnocache\": %lld}", hp, ss->nfs.server.rpccnt, ss->nfs.server.rpcread, ss->nfs.server.rpcwrite, ss->nfs.server.nrbytes, ss->nfs.server.nwbytes, ss->nfs.server.rpcbadfmt, ss->nfs.server.rpcbadaut, ss->nfs.server.rpcbadcln, ss->nfs.server.netcnt, ss->nfs.server.nettcpcnt, ss->nfs.server.netudpcnt, ss->nfs.server.nettcpcon, ss->nfs.server.rchits, ss->nfs.server.rcmiss, ss->nfs.server.rcnoca); } static void json_print_NET(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *cs, int ncgroups) { register int i; printf(", \"NET_GENERAL\": {" "\"rpacketsTCP\": %lld, " "\"spacketsTCP\": %lld, " "\"activeOpensTCP\": %lld, " "\"passiveOpensTCP\": %lld, " "\"retransSegsTCP\": %lld, " "\"rpacketsUDP\": %lld, " "\"spacketsUDP\": %lld, " "\"rpacketsIP\": %lld, " "\"spacketsIP\": %lld, " "\"dpacketsIP\": %lld, " "\"fpacketsIP\": %lld, " "\"icmpi\" : %lld, " "\"icmpo\" : %lld}", ss->net.tcp.InSegs, ss->net.tcp.OutSegs, ss->net.tcp.ActiveOpens, ss->net.tcp.PassiveOpens, ss->net.tcp.RetransSegs, ss->net.udpv4.InDatagrams + ss->net.udpv6.Udp6InDatagrams, ss->net.udpv4.OutDatagrams + ss->net.udpv6.Udp6OutDatagrams, ss->net.ipv4.InReceives + ss->net.ipv6.Ip6InReceives, ss->net.ipv4.OutRequests + ss->net.ipv6.Ip6OutRequests, ss->net.ipv4.InDelivers + ss->net.ipv6.Ip6InDelivers, ss->net.ipv4.ForwDatagrams + ss->net.ipv6.Ip6OutForwDatagrams, ss->net.icmpv4.InMsgs + ss->net.icmpv6.Icmp6InMsgs, ss->net.icmpv4.OutMsgs + ss->net.icmpv6.Icmp6OutMsgs); printf(", %s: [", hp); for (i = 0; ss->intf.intf[i].name[0]; i++) { if (i > 0) { printf(", "); } printf("{\"name\": \"%.19s\", " "\"rpack\": %lld, " "\"rbyte\": %lld, " "\"rerrs\": %lld, " "\"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, struct cgchainer *cs, int ncgroups) { register int i; printf(", %s: [", hp); for (i = 0; i < ss->ifb.nrports; i++) { if (i > 0) { printf(", "); } printf("{\"ibname\": \"%.19s\", " "\"portnr\": \"%hd\", " "\"lanes\": \"%hd\", " "\"maxrate\": %lld, " "\"rcvb\": %lld, " "\"sndb\": %lld, " "\"rcvp\": %lld, " "\"sndp\": %lld}", ss->ifb.ifb[i].ibname, ss->ifb.ifb[i].portnr, ss->ifb.ifb[i].lanes, ss->ifb.ifb[i].rate, ss->ifb.ifb[i].rcvb, ss->ifb.ifb[i].sndb, ss->ifb.ifb[i].rcvp, ss->ifb.ifb[i].sndp); } printf("]"); } static void json_print_NUM(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *cs, int ncgroups) { register int i; printf(", %s: [", hp); for (i = 0; i < ss->memnuma.nrnuma; i++) { if (i > 0) { printf(", "); } printf("{\"numanr\": \"%d\", " "\"frag\": \"%f\", " "\"totmem\": %lld, " "\"freemem\": %lld, " "\"active\": %lld, " "\"inactive\": %lld, " "\"filepage\": %lld, " "\"dirtymem\": %lld, " "\"slabmem\": %lld, " "\"slabreclaim\": %lld, " "\"shmem\": %lld, " "\"tothp\": %lld, " "\"freehp\": %lld}", ss->memnuma.numa[i].numanr, ss->memnuma.numa[i].frag * 100.0, ss->memnuma.numa[i].totmem * pagesize, ss->memnuma.numa[i].freemem * pagesize, ss->memnuma.numa[i].active * pagesize, ss->memnuma.numa[i].inactive * pagesize, ss->memnuma.numa[i].filepage * pagesize, ss->memnuma.numa[i].dirtymem * pagesize, ss->memnuma.numa[i].slabmem * pagesize, ss->memnuma.numa[i].slabreclaim * pagesize, ss->memnuma.numa[i].shmem * pagesize, ss->memnuma.numa[i].tothp * ss->mem.shugepagesz, ss->memnuma.numa[i].freehp * ss->mem.shugepagesz); } printf("]"); } static void json_print_NUC(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *cs, int ncgroups) { register int i; printf(", %s: [", hp); for (i = 0; i < ss->cpunuma.nrnuma; i++) { if (i > 0) { printf(", "); } printf("{\"numanr\": \"%d\", " "\"stime\": %lld, " "\"utime\": %lld, " "\"ntime\": %lld, " "\"itime\": %lld, " "\"wtime\": %lld, " "\"Itime\": %lld, " "\"Stime\": %lld, " "\"steal\": %lld, " "\"guest\": %lld}", ss->cpunuma.numa[i].numanr, ss->cpunuma.numa[i].stime, ss->cpunuma.numa[i].utime, ss->cpunuma.numa[i].ntime, ss->cpunuma.numa[i].itime, ss->cpunuma.numa[i].wtime, ss->cpunuma.numa[i].Itime, ss->cpunuma.numa[i].Stime, ss->cpunuma.numa[i].steal, ss->cpunuma.numa[i].guest); } printf("]"); } static void json_print_LLC(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *cs, int ncgroups) { register int i; printf(", %s: [", hp); for (i = 0; i < ss->llc.nrllcs; i++) { if (i > 0) { printf(", "); } printf("{\"LLC\": \"%3d\", " "\"occupancy\": \"%3.1f%%\", " "\"mbm_total\": \"%lld\", " "\"mbm_local\": %lld}", ss->llc.perllc[i].id, ss->llc.perllc[i].occupancy * 100, ss->llc.perllc[i].mbm_total, ss->llc.perllc[i].mbm_local); } printf("]"); } /* ** print functions for cgroups-level statistics */ static void json_print_CGR(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *cs, int ncgroups) { if ( !(supportflags & CGROUPV2) ) return; register int i, p; char *cgrpath; printf(", %s: [", hp); for (i=0; i < ncgroups; i++) { if (i > 0) { printf(", "); } cgrpath = cggetpath(cs+i, cs, 1); // print cgroup level metrics // printf( "{\"path\": \"%s\", " "\"nprocs\": %d, " "\"procsbelow\": %d, " "\"utime\": %lld, " "\"stime\": %lld, " "\"cpuweight\": %d, " "\"cpumax\": %d, " "\"cpupsisome\": %lld, " "\"cpupsitotal\": %lld, " "\"memcurrent\": %lld, " "\"memanon\": %lld, " "\"memfile\": %lld, " "\"memkernel\": %lld, " "\"memshmem\": %lld, " "\"memmax\": %lld, " "\"swpmax\": %lld, " "\"mempsisome\": %lld, " "\"mempsitotal\": %lld, " "\"diskrbytes\": %lld, " "\"diskwbytes\": %lld, " "\"diskrios\": %lld, " "\"diskwios\": %lld, " "\"diskweight\": %d, " "\"diskpsisome\": %lld, " "\"diskpsitotal\": %lld, " "\"pidlist\": [", cgrpath, (cs+i)->cstat->gen.nprocs, (cs+i)->cstat->gen.procsbelow, (cs+i)->cstat->cpu.utime, (cs+i)->cstat->cpu.stime, (cs+i)->cstat->conf.cpuweight, (cs+i)->cstat->conf.cpumax, (cs+i)->cstat->cpu.somepres, (cs+i)->cstat->cpu.fullpres, (cs+i)->cstat->mem.current > 0 ? (cs+i)->cstat->mem.current * pagesize : (cs+i)->cstat->mem.current, (cs+i)->cstat->mem.anon > 0 ? (cs+i)->cstat->mem.anon * pagesize : (cs+i)->cstat->mem.anon, (cs+i)->cstat->mem.file > 0 ? (cs+i)->cstat->mem.file * pagesize : (cs+i)->cstat->mem.file, (cs+i)->cstat->mem.kernel > 0 ? (cs+i)->cstat->mem.kernel * pagesize : (cs+i)->cstat->mem.kernel, (cs+i)->cstat->mem.shmem > 0 ? (cs+i)->cstat->mem.shmem * pagesize : (cs+i)->cstat->mem.shmem, (cs+i)->cstat->conf.memmax > 0 ? (cs+i)->cstat->conf.memmax * pagesize : (cs+i)->cstat->conf.memmax, (cs+i)->cstat->conf.swpmax > 0 ? (cs+i)->cstat->conf.swpmax * pagesize : (cs+i)->cstat->conf.swpmax, (cs+i)->cstat->mem.somepres, (cs+i)->cstat->mem.fullpres, (cs+i)->cstat->dsk.rbytes, (cs+i)->cstat->dsk.wbytes, (cs+i)->cstat->dsk.rios, (cs+i)->cstat->dsk.wios, (cs+i)->cstat->conf.dskweight, (cs+i)->cstat->dsk.somepres, (cs+i)->cstat->dsk.fullpres); free(cgrpath); // generate related pidlist // if ((cs+i)->cstat->gen.nprocs) { for (p=0; p < (cs+i)->cstat->gen.nprocs; p++) { if (p > 0) printf(", "); printf("%d", (cs+i)->proclist[p]); } } printf("]}"); } printf("]"); } /* ** print functions for process-level statistics */ static void json_print_PRG(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *cs, int ncgroups) { register int i, exitcode; static char st[3]; char *cgrpath; printf(", %s: [", hp); for (i = 0; i < nact; i++, ps++) { /* For one thread whose pid==tgid and isproc=n, it has the same value with pid==tgid and isproc=y, thus filter it out. */ if (ps->gen.tgid == ps->gen.pid && !ps->gen.isproc) continue; if (ps->gen.excode & ~(INT_MAX)) st[0]='N'; else st[0]='-'; if (ps->gen.excode & 0xff) // killed by signal? { exitcode = (ps->gen.excode & 0x7f) + 256; if (ps->gen.excode & 0x80) st[1] = 'C'; else st[1] = 'S'; } else { exitcode = (ps->gen.excode >> 8) & 0xff; st[1] = 'E'; } if (i > 0) { printf(", "); } if (supportflags & CGROUPV2 && ps->gen.cgroupix != -1) cgrpath = cggetpath((cs + ps->gen.cgroupix), cs, 1); else cgrpath = "-"; /* ** using getpwuid() & getpwuid to convert ruid & euid to string ** seems better, but the two functions take a long time */ printf("{\"pid\": %d, " "\"cmd\": \"%.19s\", " "\"state\": \"%c\", " "\"ruid\": %d, " "\"rgid\": %d, " "\"tgid\": %d, " "\"nthr\": %d, " "\"st\": \"%s\", " "\"exitcode\": %d, " "\"btime\": \"%ld\", " "\"cmdline\": \"(%.130s)\", " "\"ppid\": %d, " "\"nthrrun\": %d, " "\"nthrslpi\": %d, " "\"nthrslpu\": %d, " "\"nthridle\": %d, " "\"euid\": %d, " "\"egid\": %d, " "\"elaps\": \"%ld\", " "\"isproc\": %d, " "\"cid\": \"%.19s\", " "\"cgroup\": \"%s\"}", ps->gen.pid, ps->gen.name, ps->gen.state, ps->gen.ruid, ps->gen.rgid, ps->gen.tgid, ps->gen.nthr, st, exitcode, ps->gen.btime, ps->gen.cmdline, ps->gen.ppid, ps->gen.nthrrun, ps->gen.nthrslpi, ps->gen.nthrslpu, ps->gen.nthridle, ps->gen.euid, ps->gen.egid, ps->gen.elaps, !!ps->gen.isproc, /* convert to boolean */ ps->gen.utsname[0] ? ps->gen.utsname:"-", cgrpath); if (supportflags & CGROUPV2 && ps->gen.cgroupix != -1) free(cgrpath); } printf("]"); } static void json_print_PRC(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *cs, int ncgroups) { register int i; char *cgrpath; printf(", %s: [", hp); for (i = 0; i < nact; i++, ps++) { if (ps->gen.tgid == ps->gen.pid && !ps->gen.isproc) continue; if (i > 0) { printf(", "); } if (supportflags & CGROUPV2 && ps->gen.cgroupix != -1) cgrpath = cggetpath((cs + ps->gen.cgroupix), cs, 1); else cgrpath = "-"; printf("{\"pid\": %d, " "\"cmd\": \"%.19s\", " "\"utime\": %lld, " "\"stime\": %lld, " "\"nice\": %d, " "\"prio\": %d, " "\"curcpu\": %d, " "\"tgid\": %d, " "\"isproc\": %d, " "\"rundelay\": %lld, " "\"blkdelay\": %lld, " "\"nvcsw\": %llu, " "\"nivcsw\": %llu, " "\"sleepavg\": %d, " "\"cgroup\": \"%s\"}", ps->gen.pid, ps->gen.name, ps->cpu.utime, ps->cpu.stime, ps->cpu.nice, ps->cpu.prio, ps->cpu.curcpu, ps->gen.tgid, !!ps->gen.isproc, ps->cpu.rundelay/1000000, ps->cpu.blkdelay*1000/hertz, ps->cpu.nvcsw, ps->cpu.nivcsw, ps->cpu.sleepavg, cgrpath); if (supportflags & CGROUPV2 && ps->gen.cgroupix != -1) free(cgrpath); } printf("]"); } static void json_print_PRM(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *cs, int ncgroups) { register int i; char *cgrpath; printf(", %s: [", hp); for (i = 0; i < nact; i++, ps++) { if (ps->gen.tgid == ps->gen.pid && !ps->gen.isproc) continue; if (i > 0) { printf(", "); } if (supportflags & CGROUPV2 && ps->gen.cgroupix != -1) cgrpath = cggetpath((cs + ps->gen.cgroupix), cs, 1); else cgrpath = "-"; printf("{\"pid\": %d, " "\"cmd\": \"%.19s\", " "\"vmem\": %lld, " "\"rmem\": %lld, " "\"vexec\": %lld, " "\"vgrow\": %lld, " "\"rgrow\": %lld, " "\"minflt\": %lld, " "\"majflt\": %lld, " "\"vlibs\": %lld, " "\"vdata\": %lld, " "\"vstack\": %lld, " "\"vlock\": %lld, " "\"vswap\": %lld, " "\"pmem\": %lld, " "\"cgroup\": \"%s\"}", ps->gen.pid, ps->gen.name, ps->mem.vmem, ps->mem.rmem, ps->mem.vexec, ps->mem.vgrow, ps->mem.rgrow, ps->mem.minflt, ps->mem.majflt, ps->mem.vlibs, ps->mem.vdata, ps->mem.vstack, ps->mem.vlock, ps->mem.vswap, ps->mem.pmem == (unsigned long long)-1LL ? 0:ps->mem.pmem, cgrpath); if (supportflags & CGROUPV2 && ps->gen.cgroupix != -1) free(cgrpath); } printf("]"); } static void json_print_PRD(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *cs, int ncgroups) { register int i; printf(", %s: [", hp); for (i = 0; i < nact; i++, ps++) { if (ps->gen.tgid == ps->gen.pid && !ps->gen.isproc) continue; if (i > 0) { printf(", "); } printf("{\"pid\": %d, " "\"cmd\": \"%.19s\", " "\"rio\": %lld, " "\"rsz\": %lld, " "\"wio\": %lld, " "\"wsz\": %lld, " "\"cwsz\": %lld}", ps->gen.pid, ps->gen.name, ps->dsk.rio, ps->dsk.rsz, ps->dsk.wio, ps->dsk.wsz, ps->dsk.cwsz); } printf("]"); } static void json_print_PRN(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *cs, int ncgroups) { if (!(supportflags & NETATOP)) return; register int i; printf(", %s: [", hp); for (i = 0; i < nact; i++, ps++) { if (ps->gen.tgid == ps->gen.pid && !ps->gen.isproc) continue; if (i > 0) { printf(", "); } printf("{\"pid\": %d, " "\"cmd\": \"%.19s\", " "\"tcpsnd\": \"%lld\", " "\"tcpssz\": \"%lld\", " "\"tcprcv\": \"%lld\", " "\"tcprsz\": \"%lld\", " "\"udpsnd\": \"%lld\", " "\"udpssz\": \"%lld\", " "\"udprcv\": \"%lld\", " "\"udprsz\": \"%lld\"}", ps->gen.pid, ps->gen.name, ps->net.tcpsnd, ps->net.tcpssz, ps->net.tcprcv, ps->net.tcprsz, ps->net.udpsnd, ps->net.udpssz, ps->net.udprcv, ps->net.udprsz); } printf("]"); } static void json_print_PRE(char *hp, struct sstat *ss, struct tstat *ps, int nact, struct cgchainer *cs, int ncgroups) { if ( !(supportflags & GPUSTAT) ) return; register int i; printf(", %s: [", hp); for (i = 0; i < nact; i++, ps++) { if (ps->gen.tgid == ps->gen.pid && !ps->gen.isproc) continue; if (i > 0) { printf(", "); } printf("{\"pid\": %d, " "\"cmd\": \"%.19s\", " "\"gpustate\": \"%c\", " "\"nrgpus\": %d, " "\"gpulist\": \"%x\", " "\"gpubusy\": %d, " "\"membusy\": %d, " "\"memnow\": %lld, " "\"memcum\": %lld, " "\"sample\": %lld}", ps->gen.pid, ps->gen.name, ps->gpu.state == '\0' ? 'N':ps->gpu.state, ps->gpu.nrgpus, ps->gpu.gpulist, ps->gpu.gpubusy, ps->gpu.membusy, ps->gpu.memnow, ps->gpu.memcum, ps->gpu.sample); } printf("]"); } atop-2.11.1/Makefile0000644000203100020310000001771514771753566013537 0ustar gerlofgerlof# Makefile for System & Process Monitor ATOP (Linux version) # # Gerlof Langeveld - gerlof.langeveld@atoptool.nl # DESTDIR = BINPATH = /usr/bin SBINPATH = /usr/sbin SCRPATH = /usr/share/atop LOGPATH = /var/log/atop MAN1PATH = /usr/share/man/man1 MAN5PATH = /usr/share/man/man5 MAN8PATH = /usr/share/man/man8 INIPATH = /etc/init.d DEFPATH = /etc/default SYSDPATH = /lib/systemd/system CRNPATH = /etc/cron.d ROTPATH = /etc/logrotate.d PMPATH1 = /usr/lib/pm-utils/sleep.d PMPATH2 = /usr/lib64/pm-utils/sleep.d PMPATHD = /usr/lib/systemd/system-sleep PKG_CONFIG ?= pkg-config 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 cgroups.o rawlog.o ifprop.o parseable.o OBJMOD3 = showgeneric.o drawbar.o showlinux.o showsys.o showprocs.o OBJMOD4 = atopsar.o netatopif.o netatopbpfif.o gpucom.o json.o utsnames.o ALLMODS = $(OBJMOD0) $(OBJMOD1) $(OBJMOD2) $(OBJMOD3) $(OBJMOD4) VERS = $(shell ./atop -V 2>/dev/null| sed -e 's/^[^ ]* //' -e 's/ .*//') all: atop atopsar atopacctd atopconvert atopcat atophide atop: atop.o $(ALLMODS) Makefile $(CC) atop.o $(ALLMODS) -o atop -lncursesw -lz -lm -lrt $(LDFLAGS) atopsar: atop ln -sf atop atopsar atopacctd: atopacctd.o netlink.o $(CC) atopacctd.o netlink.o -o atopacctd $(LDFLAGS) atopconvert: atopconvert.o $(CC) atopconvert.o -o atopconvert -lz $(LDFLAGS) atopcat: atopcat.o $(CC) atopcat.o -o atopcat $(LDFLAGS) atophide: atophide.o $(CC) atophide.o -o atophide -lz $(LDFLAGS) clean: rm -f *.o atop atopsar atopacctd atopconvert atopcat versdate.h distr: rm -f *.o atop tar czvf /tmp/atop.tar.gz * # default install is based on systemd # install: genericinstall if [ ! -d $(DESTDIR)$(SYSDPATH) ]; \ then mkdir -p $(DESTDIR)$(SYSDPATH); fi if [ ! -d $(DESTDIR)$(PMPATHD) ]; \ then mkdir -p $(DESTDIR)$(PMPATHD); fi # cp atop.service $(DESTDIR)$(SYSDPATH) chmod 0644 $(DESTDIR)$(SYSDPATH)/atop.service cp atopgpu.service $(DESTDIR)$(SYSDPATH) chmod 0644 $(DESTDIR)$(SYSDPATH)/atopgpu.service cp atop-rotate.service $(DESTDIR)$(SYSDPATH) chmod 0644 $(DESTDIR)$(SYSDPATH)/atop-rotate.service cp atop-rotate.timer $(DESTDIR)$(SYSDPATH) chmod 0644 $(DESTDIR)$(SYSDPATH)/atop-rotate.timer cp atopacct.service $(DESTDIR)$(SYSDPATH) chmod 0644 $(DESTDIR)$(SYSDPATH)/atopacct.service cp atop-pm.sh $(DESTDIR)$(PMPATHD) chmod 0711 $(DESTDIR)$(PMPATHD)/atop-pm.sh # # only when making on target system: # if [ -z "$(DESTDIR)" -a -f /bin/systemctl ]; \ then /bin/systemctl disable --now atop 2> /dev/null; \ /bin/systemctl disable --now atopacct 2> /dev/null; \ /bin/systemctl daemon-reload; \ /bin/systemctl enable --now atopacct; \ /bin/systemctl enable --now atop; \ /bin/systemctl enable --now atop-rotate.timer; \ fi # explicitly use sysvinstall for System V init based systems # sysvinstall: genericinstall if [ ! -d $(DESTDIR)$(INIPATH) ]; \ then mkdir -p $(DESTDIR)$(INIPATH); fi if [ ! -d $(DESTDIR)$(SCRPATH) ]; \ then mkdir -p $(DESTDIR)$(SCRPATH); fi if [ ! -d $(DESTDIR)$(CRNPATH) ]; \ then mkdir -p $(DESTDIR)$(CRNPATH); fi if [ ! -d $(DESTDIR)$(ROTPATH) ]; \ then mkdir -p $(DESTDIR)$(ROTPATH); fi # cp atop.init $(DESTDIR)$(INIPATH)/atop cp atopacct.init $(DESTDIR)$(INIPATH)/atopacct cp atop.cronsysv $(DESTDIR)$(CRNPATH)/atop cp atop.daily $(DESTDIR)$(SCRPATH) chmod 0711 $(DESTDIR)$(SCRPATH)/atop.daily touch $(DESTDIR)$(LOGPATH)/dummy_before touch $(DESTDIR)$(LOGPATH)/dummy_after # if [ -d $(DESTDIR)$(PMPATH1) ]; \ then cp 45atoppm $(DESTDIR)$(PMPATH1); \ chmod 0711 $(DESTDIR)$(PMPATH1)/45atoppm; \ fi if [ -d $(DESTDIR)$(PMPATH2) ]; \ then cp 45atoppm $(DESTDIR)$(PMPATH2); \ chmod 0711 $(DESTDIR)$(PMPATH2)/45atoppm; \ fi # # # only when making on target system: # if [ -z "$(DESTDIR)" -a -f /sbin/chkconfig ]; \ then /sbin/chkconfig --del atop 2> /dev/null; \ /sbin/chkconfig --add atop; \ /sbin/chkconfig --del atopacct 2> /dev/null; \ /sbin/chkconfig --add atopacct; \ fi if [ -z "$(DESTDIR)" -a -f /usr/sbin/update-rc.d ]; \ then update-rc.d atop defaults; \ update-rc.d atopacct defaults; \ fi if [ -z "$(DESTDIR)" -a -f /sbin/service ]; \ then /sbin/service atopacct start; \ sleep 2; \ /sbin/service atop start; \ fi genericinstall: atop atopacctd atopconvert atopcat atophide if [ ! -d $(DESTDIR)$(LOGPATH) ]; \ then mkdir -p $(DESTDIR)$(LOGPATH); fi if [ ! -d $(DESTDIR)$(DEFPATH) ]; \ then mkdir -p $(DESTDIR)$(DEFPATH); fi if [ ! -d $(DESTDIR)$(BINPATH) ]; \ then mkdir -p $(DESTDIR)$(BINPATH); fi if [ ! -d $(DESTDIR)$(SBINPATH) ]; \ then mkdir -p $(DESTDIR)$(SBINPATH); fi if [ ! -d $(DESTDIR)$(MAN1PATH) ]; \ then mkdir -p $(DESTDIR)$(MAN1PATH); fi if [ ! -d $(DESTDIR)$(MAN5PATH) ]; \ then mkdir -p $(DESTDIR)$(MAN5PATH); fi if [ ! -d $(DESTDIR)$(MAN8PATH) ]; \ then mkdir -p $(DESTDIR)$(MAN8PATH); fi # touch $(DESTDIR)$(DEFPATH)/atop chmod 644 $(DESTDIR)$(DEFPATH)/atop # cp atop $(DESTDIR)$(BINPATH)/atop chmod 0711 $(DESTDIR)$(BINPATH)/atop ln -sf atop $(DESTDIR)$(BINPATH)/atopsar cp atopacctd $(DESTDIR)$(SBINPATH)/atopacctd chmod 0700 $(DESTDIR)$(SBINPATH)/atopacctd cp atopgpud $(DESTDIR)$(SBINPATH)/atopgpud chmod 0700 $(DESTDIR)$(SBINPATH)/atopgpud cp atop $(DESTDIR)$(BINPATH)/atop-$(VERS) ln -sf atop-$(VERS) $(DESTDIR)$(BINPATH)/atopsar-$(VERS) cp atopconvert $(DESTDIR)$(BINPATH)/atopconvert chmod 0711 $(DESTDIR)$(BINPATH)/atopconvert cp atopcat $(DESTDIR)$(BINPATH)/atopcat chmod 0711 $(DESTDIR)$(BINPATH)/atopcat cp atophide $(DESTDIR)$(BINPATH)/atophide chmod 0711 $(DESTDIR)$(BINPATH)/atophide cp man/atop.1 $(DESTDIR)$(MAN1PATH) cp man/atopsar.1 $(DESTDIR)$(MAN1PATH) cp man/atopconvert.1 $(DESTDIR)$(MAN1PATH) cp man/atopcat.1 $(DESTDIR)$(MAN1PATH) cp man/atophide.1 $(DESTDIR)$(MAN1PATH) cp man/atoprc.5 $(DESTDIR)$(MAN5PATH) cp man/atopacctd.8 $(DESTDIR)$(MAN8PATH) cp man/atopgpud.8 $(DESTDIR)$(MAN8PATH) ########################################################################## versdate.h: ./mkdate atop.o: atop.h photoproc.h photosyst.h acctproc.h showgeneric.h atopsar.o: atop.h photoproc.h photosyst.h rawlog.o: atop.h photoproc.h photosyst.h rawlog.h showgeneric.h various.o: atop.h acctproc.h ifprop.o: atop.h photosyst.h ifprop.h parseable.o: atop.h photoproc.h photosyst.h cgroups.h parseable.h deviate.o: atop.h photoproc.h photosyst.h procdbase.o: atop.h photoproc.h acctproc.o: atop.h photoproc.h atopacctd.h acctproc.h netatop.h netatopif.o: atop.h photoproc.h netatopd.h netatop.h netatopbpfif.o: atop.h photoproc.h netatop.h photoproc.o: atop.h photoproc.h photosyst.o: atop.h photosyst.h cgroups.o: atop.h cgroups.h showgeneric.o: atop.h photoproc.h photosyst.h cgroups.h showgeneric.h showlinux.h showlinux.o: atop.h photoproc.h photosyst.h cgroups.h showgeneric.h showlinux.h showsys.o: atop.h photoproc.h photosyst.h showgeneric.h showprocs.o: atop.h photoproc.h photosyst.h cgroups.h showgeneric.h showlinux.h drawbar.o: atop.h photosyst.h showgeneric.h version.o: version.c version.h versdate.h gpucom.o: atop.h photoproc.h photosyst.h atopacctd.o: atop.h photoproc.h acctproc.h atopacctd.h version.h versdate.h atopconvert.o: atop.h photoproc.h photosyst.h rawlog.h atopcat.o: atop.h rawlog.h atophide.o: atop.h photoproc.h photosyst.h rawlog.h atop-2.11.1/photosyst.c0000644000203100020310000021254314771753566014313 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 #include #define SCALINGMAXCPU 8 // threshold for scaling info per CPU #ifndef NOPERFEVENT #include #include #endif #include #include #include // #define _GNU_SOURCE #include #include #include "atop.h" #include "ifprop.h" #include "photosyst.h" #define MAXCNT 64 /* recognize numa node */ #define NUMADIR "/sys/devices/system/node" /* recognize large huge pages */ #define HUGEPAGEDIR "/sys/kernel/mm/hugepages" /* recognize LLC monitor data */ #define LLCDIR "/sys/fs/resctrl/mon_data" #define L3SIZE "/sys/devices/system/cpu/cpu0/cache/index3/size" /* Refer to mmzone.h, the default is 11 */ #define MAX_ORDER 11 #ifndef NOPERFEVENT enum { PERF_EVENTS_AUTO = 0, PERF_EVENTS_ENABLE, PERF_EVENTS_DISABLE, }; static int perfevents = PERF_EVENTS_AUTO; static long perf_event_open(struct perf_event_attr *, pid_t, int, int, unsigned long); static void getperfevents(struct cpustat *); #endif static int get_infiniband(struct ifbstat *); static int get_ksm(struct sstat *); static int isdisk_name(unsigned int, unsigned int, char *, struct perdsk *, int); static struct bitmask *numa_allocate_cpumask(void); static void numa_bitmask_free(struct bitmask *); static int numa_parse_bitmap_v2(char *, struct bitmask *); static struct ipv6_stats ipv6_tmp; static struct icmpv6_stats icmpv6_tmp; static struct udpv6_stats udpv6_tmp; struct v6tab { char *nam; count_t *val; }; static struct v6tab v6tab[] = { {"Ip6InReceives", &ipv6_tmp.Ip6InReceives, }, {"Ip6InHdrErrors", &ipv6_tmp.Ip6InHdrErrors, }, {"Ip6InTooBigErrors", &ipv6_tmp.Ip6InTooBigErrors, }, {"Ip6InNoRoutes", &ipv6_tmp.Ip6InNoRoutes, }, {"Ip6InAddrErrors", &ipv6_tmp.Ip6InAddrErrors, }, {"Ip6InUnknownProtos", &ipv6_tmp.Ip6InUnknownProtos, }, {"Ip6InTruncatedPkts", &ipv6_tmp.Ip6InTruncatedPkts, }, {"Ip6InDiscards", &ipv6_tmp.Ip6InDiscards, }, {"Ip6InDelivers", &ipv6_tmp.Ip6InDelivers, }, {"Ip6OutForwDatagrams", &ipv6_tmp.Ip6OutForwDatagrams, }, {"Ip6OutRequests", &ipv6_tmp.Ip6OutRequests, }, {"Ip6OutDiscards", &ipv6_tmp.Ip6OutDiscards, }, {"Ip6OutNoRoutes", &ipv6_tmp.Ip6OutNoRoutes, }, {"Ip6ReasmTimeout", &ipv6_tmp.Ip6ReasmTimeout, }, {"Ip6ReasmReqds", &ipv6_tmp.Ip6ReasmReqds, }, {"Ip6ReasmOKs", &ipv6_tmp.Ip6ReasmOKs, }, {"Ip6ReasmFails", &ipv6_tmp.Ip6ReasmFails, }, {"Ip6FragOKs", &ipv6_tmp.Ip6FragOKs, }, {"Ip6FragFails", &ipv6_tmp.Ip6FragFails, }, {"Ip6FragCreates", &ipv6_tmp.Ip6FragCreates, }, {"Ip6InMcastPkts", &ipv6_tmp.Ip6InMcastPkts, }, {"Ip6OutMcastPkts", &ipv6_tmp.Ip6OutMcastPkts, }, {"Icmp6InMsgs", &icmpv6_tmp.Icmp6InMsgs, }, {"Icmp6InErrors", &icmpv6_tmp.Icmp6InErrors, }, {"Icmp6InDestUnreachs", &icmpv6_tmp.Icmp6InDestUnreachs, }, {"Icmp6InPktTooBigs", &icmpv6_tmp.Icmp6InPktTooBigs, }, {"Icmp6InTimeExcds", &icmpv6_tmp.Icmp6InTimeExcds, }, {"Icmp6InParmProblems", &icmpv6_tmp.Icmp6InParmProblems, }, {"Icmp6InEchos", &icmpv6_tmp.Icmp6InEchos, }, {"Icmp6InEchoReplies", &icmpv6_tmp.Icmp6InEchoReplies, }, {"Icmp6InGroupMembQueries", &icmpv6_tmp.Icmp6InGroupMembQueries, }, {"Icmp6InGroupMembResponses", &icmpv6_tmp.Icmp6InGroupMembResponses, }, {"Icmp6InGroupMembReductions", &icmpv6_tmp.Icmp6InGroupMembReductions, }, {"Icmp6InRouterSolicits", &icmpv6_tmp.Icmp6InRouterSolicits, }, {"Icmp6InRouterAdvertisements", &icmpv6_tmp.Icmp6InRouterAdvertisements, }, {"Icmp6InNeighborSolicits", &icmpv6_tmp.Icmp6InNeighborSolicits, }, {"Icmp6InNeighborAdvertisements", &icmpv6_tmp.Icmp6InNeighborAdvertisements, }, {"Icmp6InRedirects", &icmpv6_tmp.Icmp6InRedirects, }, {"Icmp6OutMsgs", &icmpv6_tmp.Icmp6OutMsgs, }, {"Icmp6OutDestUnreachs", &icmpv6_tmp.Icmp6OutDestUnreachs, }, {"Icmp6OutPktTooBigs", &icmpv6_tmp.Icmp6OutPktTooBigs, }, {"Icmp6OutTimeExcds", &icmpv6_tmp.Icmp6OutTimeExcds, }, {"Icmp6OutParmProblems", &icmpv6_tmp.Icmp6OutParmProblems, }, {"Icmp6OutEchoReplies", &icmpv6_tmp.Icmp6OutEchoReplies, }, {"Icmp6OutRouterSolicits", &icmpv6_tmp.Icmp6OutRouterSolicits, }, {"Icmp6OutNeighborSolicits",&icmpv6_tmp.Icmp6OutNeighborSolicits, }, {"Icmp6OutNeighborAdvertisements", &icmpv6_tmp.Icmp6OutNeighborAdvertisements, }, {"Icmp6OutRedirects", &icmpv6_tmp.Icmp6OutRedirects, }, {"Icmp6OutGroupMembResponses", &icmpv6_tmp.Icmp6OutGroupMembResponses, }, {"Icmp6OutGroupMembReductions", &icmpv6_tmp.Icmp6OutGroupMembReductions, }, {"Udp6InDatagrams", &udpv6_tmp.Udp6InDatagrams, }, {"Udp6NoPorts", &udpv6_tmp.Udp6NoPorts, }, {"Udp6InErrors", &udpv6_tmp.Udp6InErrors, }, {"Udp6OutDatagrams", &udpv6_tmp.Udp6OutDatagrams, }, }; static int v6tab_entries = sizeof(v6tab)/sizeof(struct v6tab); // The following values are used to accumulate cpu statistics per numa. // The bitmask realization is from numactl #define CPUMASK_SZ (64 * 8) #define bitsperlong (8 * sizeof(unsigned long)) #define howmany(x,y) (((x)+((y)-1))/(y)) #define longsperbits(n) howmany(n, bitsperlong) #define round_up(x,y) (((x) + (y) - 1) & ~((y)-1)) #define BITS_PER_LONG (sizeof(unsigned long) * 8) #define CPU_BYTES(x) (round_up(x, BITS_PER_LONG)/8) #define CPU_LONGS(x) (CPU_BYTES(x) / sizeof(long)) struct bitmask { unsigned long size; /* number of bits in the map */ unsigned long long *maskp; }; /* * Allocate a bitmask for cpus, of a size large enough to * match the kernel's cpumask_t. */ static struct bitmask * numa_allocate_cpumask(void) { int ncpus = CPUMASK_SZ; struct bitmask *bmp; bmp = malloc(sizeof(*bmp)); if (!bmp) ptrverify(bmp, "Malloc failed for numa bitmask"); bmp->size = ncpus; bmp->maskp = calloc(longsperbits(ncpus), sizeof(unsigned long)); ptrverify((bmp->maskp), "Malloc failed for numa maskp"); return bmp; } static void numa_bitmask_free(struct bitmask *bmp) { if (bmp == 0) return; free(bmp->maskp); bmp->maskp = (unsigned long long *)0xdeadcdef; /* double free tripwire */ free(bmp); return; } static int numa_parse_bitmap_v2(char *line, struct bitmask *mask) { int i; char *p = strchr(line, '\n'); if (!p) return -1; int ncpus = mask->size; for (i = 0; p > line;i++) { char *oldp, *endp; oldp = p; if (*p == ',') --p; while (p > line && *p != ',') --p; /* Eat two 32bit fields at a time to get longs */ if (p > line && sizeof(unsigned long) == 8) { oldp--; memmove(p, p+1, oldp-p+1); while (p > line && *p != ',') --p; } if (*p == ',') p++; if (i >= CPU_LONGS(ncpus)) return -1; mask->maskp[i] = strtoul(p, &endp, 16); if (endp != oldp) return -1; p--; } return 0; } void photosyst(struct sstat *si) { static char part_stats = 1; /* per-partition statistics ? */ static char ib_stats = 1; /* InfiniBand statistics ? */ static char ksm_stats = 1; static char *lhugepagetot; /* name of large hugepage dir total */ /* might be -1 if not applicable */ static char *lhugepagefree; /* name of large hugepage dir free */ /* might be -1 if not applicable */ static unsigned int hpsize; register int i, nr, j; count_t cnts[MAXCNT]; float lavg1, lavg5, lavg15; FILE *fp; DIR *dirp; struct dirent *dentry; char linebuf[1024], nam[64], origdir[4096]; unsigned int major, minor; struct shm_info shminfo; #if HTTPSTATS static int wwwvalid = 1; #endif memset(si, 0, sizeof(struct sstat)); if ( getcwd(origdir, sizeof origdir) == NULL) mcleanstop(54, "failed to save current dir\n"); if ( chdir("/proc") == -1) mcleanstop(54, "failed to change to /proc\n"); /* ** gather various general statistics from the file /proc/stat and ** store them in binary form */ if ( (fp = fopen("stat", "r")) != NULL) { while ( fgets(linebuf, sizeof(linebuf), fp) != NULL) { nr = sscanf(linebuf, "%s %lld %lld %lld %lld %lld %lld %lld " "%lld %lld %lld %lld %lld %lld %lld %lld ", nam, &cnts[0], &cnts[1], &cnts[2], &cnts[3], &cnts[4], &cnts[5], &cnts[6], &cnts[7], &cnts[8], &cnts[9], &cnts[10], &cnts[11], &cnts[12], &cnts[13], &cnts[14]); if (nr < 2) /* headerline ? --> skip */ continue; if ( strcmp("cpu", nam) == EQ) { si->cpu.all.utime = cnts[0]; si->cpu.all.ntime = cnts[1]; si->cpu.all.stime = cnts[2]; si->cpu.all.itime = cnts[3]; if (nr > 5) /* 2.6 kernel? */ { si->cpu.all.wtime = cnts[4]; si->cpu.all.Itime = cnts[5]; si->cpu.all.Stime = cnts[6]; if (nr > 8) /* steal support */ si->cpu.all.steal = cnts[7]; if (nr > 9) /* guest support */ si->cpu.all.guest = cnts[8]; } continue; } if ( strncmp("cpu", nam, 3) == EQ) { i = atoi(&nam[3]); if (i >= MAXCPU) { fprintf(stderr, "cpu %s exceeds maximum of %d\n", nam, MAXCPU); continue; } si->cpu.cpu[i].cpunr = i; si->cpu.cpu[i].utime = cnts[0]; si->cpu.cpu[i].ntime = cnts[1]; si->cpu.cpu[i].stime = cnts[2]; si->cpu.cpu[i].itime = cnts[3]; if (nr > 5) /* 2.6 kernel? */ { si->cpu.cpu[i].wtime = cnts[4]; si->cpu.cpu[i].Itime = cnts[5]; si->cpu.cpu[i].Stime = cnts[6]; if (nr > 8) /* steal support */ si->cpu.cpu[i].steal = cnts[7]; if (nr > 9) /* guest support */ si->cpu.cpu[i].guest = cnts[8]; } si->cpu.nrcpu++; continue; } if ( strcmp("ctxt", nam) == EQ) { si->cpu.csw = cnts[0]; continue; } if ( strcmp("intr", nam) == EQ) { si->cpu.devint = cnts[0]; continue; } if ( strcmp("processes", nam) == EQ) { si->cpu.nprocs = cnts[0]; continue; } if ( strcmp("swap", nam) == EQ) /* < 2.6 */ { si->mem.swins = cnts[0]; si->mem.swouts = cnts[1]; continue; } } fclose(fp); if (si->cpu.nrcpu == 0) si->cpu.nrcpu = 1; } /* ** gather loadaverage values from the file /proc/loadavg and ** store them in binary form */ if ( (fp = fopen("loadavg", "r")) != NULL) { if ( fgets(linebuf, sizeof(linebuf), fp) != NULL) { if ( sscanf(linebuf, "%f %f %f", &lavg1, &lavg5, &lavg15) == 3) { si->cpu.lavg1 = lavg1; si->cpu.lavg5 = lavg5; si->cpu.lavg15 = lavg15; } } fclose(fp); } /* ** gather frequency scaling info. ** sources (in order of preference): ** /sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state ** or ** /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq ** /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq ** ** store them in binary form */ static char fn[512]; int didone=0; // check governor statistics for (i = 0; i < si->cpu.nrcpu; ++i) { long long f=0; snprintf(fn, sizeof fn, "/sys/devices/system/cpu/cpu%d/cpufreq/stats/time_in_state", i); if ((fp=fopen(fn, "r")) != 0) { long long hits=0; long long maxfreq=0; long long cnt=0; long long sum=0; while (fscanf(fp, "%lld %lld", &f, &cnt) == 2) { f /= 1000; sum += (f*cnt); hits += cnt; if (f > maxfreq) maxfreq=f; } si->cpu.cpu[i].freqcnt.maxfreq = maxfreq; si->cpu.cpu[i].freqcnt.cnt = sum; si->cpu.cpu[i].freqcnt.ticks = hits; fclose(fp); didone=1; } else { break; } } if (!didone) // did not get processor freq statistics yet { long long f=0; for (i = 0; i < si->cpu.nrcpu; ++i) { snprintf(fn, sizeof fn, "/sys/devices/system/cpu/cpu%d/cpufreq/cpuinfo_max_freq", i); if ((fp=fopen(fn, "r")) != 0) { if (fscanf(fp, "%lld", &f) == 1) { // convert KHz to MHz si->cpu.cpu[i].freqcnt.maxfreq =f/1000; } fclose(fp); } else { si->cpu.cpu[i].freqcnt.maxfreq=0; } snprintf(fn, sizeof fn, "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_cur_freq", i); if ((fp=fopen(fn, "r")) != 0) { if (fscanf(fp, "%lld", &f) == 1) { // convert KHz to MHz si->cpu.cpu[i].freqcnt.cnt = f/1000; si->cpu.cpu[i].freqcnt.ticks = 0; } fclose(fp); didone=1; } else { si->cpu.cpu[i].freqcnt.cnt = 0; si->cpu.cpu[i].freqcnt.ticks = 0; } } } // for all CPUs if (!didone) // did not get processor freq statistics. // use /proc/cpuinfo { if ( (fp = fopen("cpuinfo", "r")) != NULL) { // get information from the lines // processor\t: 0 // cpu MHz\t\t: 800.000 int cpuno=-1; while ( fgets(linebuf, sizeof(linebuf), fp) != NULL) { if (memcmp(linebuf, "processor", 9)== EQ) sscanf(linebuf, "%*s %*s %d", &cpuno); if (memcmp(linebuf, "cpu MHz", 7) == EQ) { if (cpuno >= 0 && cpuno < si->cpu.nrcpu) { sscanf(linebuf, "%*s %*s %*s %lld", &(si->cpu.cpu[cpuno].freqcnt.cnt)); } } } fclose(fp); } } /* ** gather virtual memory statistics from the file /proc/vmstat and ** store them in binary form (>= kernel 2.6) */ si->mem.oomkills = -1; si->mem.allocstall = 0; si->mem.numamigrate = 0; si->mem.pgmigrate = 0; if ( (fp = fopen("vmstat", "r")) != NULL) { while ( fgets(linebuf, sizeof(linebuf), fp) != NULL) { nr = sscanf(linebuf, "%s %lld", nam, &cnts[0]); if (nr < 2) /* headerline ? --> skip */ continue; /* pgpgin & pgpgout fields in KB from vmstat */ if ( strcmp("pgpgin", nam) == EQ) { si->mem.pgins = cnts[0] * 1024 / pagesize; continue; } if ( strcmp("pgpgout", nam) == EQ) { si->mem.pgouts = cnts[0] * 1024 / pagesize; continue; } if ( strcmp("pswpin", nam) == EQ) { si->mem.swins = cnts[0]; continue; } if ( strcmp("pswpout", nam) == EQ) { si->mem.swouts = cnts[0]; continue; } if ( strncmp("pgscan_kswapd", nam, 13) == EQ) { si->mem.pgscans += cnts[0]; continue; } if ( strncmp("pgscan_direct", nam, 13) == EQ) { si->mem.pgscans += cnts[0]; continue; } if ( strncmp("pgscan_khugepaged", nam, 17) == EQ) { si->mem.pgscans += cnts[0]; continue; } if ( strncmp("pgsteal_kswapd", nam, 14) == EQ) { si->mem.pgsteal += cnts[0]; continue; } if ( strncmp("pgsteal_direct", nam, 14) == EQ) { si->mem.pgsteal += cnts[0]; continue; } if ( strncmp("pgsteal_khugepaged", nam, 18) == EQ) { si->mem.pgsteal += cnts[0]; continue; } // more counters might start with "allocstall" if ( memcmp("allocstall", nam, 10) == EQ) { si->mem.allocstall += cnts[0]; continue; } if ( strcmp("oom_kill", nam) == EQ) { si->mem.oomkills = cnts[0]; continue; } if ( strcmp("compact_stall", nam) == EQ) { si->mem.compactstall = cnts[0]; continue; } if ( strcmp("numa_pages_migrated", nam) == EQ) { si->mem.numamigrate = cnts[0]; continue; } if ( strcmp("pgmigrate_success", nam) == EQ) { si->mem.pgmigrate = cnts[0]; continue; } if ( strcmp("zswpout", nam) == EQ) { si->mem.zswouts = cnts[0]; continue; } if ( strcmp("zswpin", nam) == EQ) { si->mem.zswins = cnts[0]; continue; } } fclose(fp); } /* ** gather memory-related statistics from the file /proc/meminfo and ** store them in binary form ** ** in the file /proc/meminfo a 2.4 kernel starts with two lines ** headed by the strings "Mem:" and "Swap:" containing all required ** fields, except proper value for page cache ** if these lines are present we try to skip parsing the rest ** of the lines; if these lines are not present we should get the ** required field from other lines */ si->mem.physmem = (count_t)-1; si->mem.freemem = (count_t)-1; si->mem.buffermem = (count_t)-1; si->mem.cachemem = (count_t)-1; si->mem.slabmem = (count_t) 0; si->mem.slabreclaim = (count_t) 0; si->mem.shmem = (count_t) 0; si->mem.totswap = (count_t)-1; si->mem.freeswap = (count_t)-1; si->mem.swapcached = (count_t) 0; si->mem.committed = (count_t) 0; si->mem.pagetables = (count_t) 0; if ( (fp = fopen("meminfo", "r")) != NULL) { while ( fgets(linebuf, sizeof(linebuf), fp) != NULL) { nr = sscanf(linebuf, "%s %lld %lld %lld %lld %lld %lld %lld " "%lld %lld %lld\n", nam, &cnts[0], &cnts[1], &cnts[2], &cnts[3], &cnts[4], &cnts[5], &cnts[6], &cnts[7], &cnts[8], &cnts[9]); if (nr < 2) /* headerline ? --> skip */ continue; if ( strcmp("Mem:", nam) == EQ) { si->mem.physmem = cnts[0] / pagesize; si->mem.freemem = cnts[2] / pagesize; si->mem.buffermem = cnts[4] / pagesize; } else if ( strcmp("Swap:", nam) == EQ) { si->mem.totswap = cnts[0] / pagesize; si->mem.freeswap = cnts[2] / pagesize; } else if (strcmp("Cached:", nam) == EQ) { if (si->mem.cachemem == (count_t)-1) { si->mem.cachemem = cnts[0]*1024/pagesize; } } else if (strcmp("Dirty:", nam) == EQ) { si->mem.cachedrt = cnts[0]*1024/pagesize; } else if (strcmp("MemTotal:", nam) == EQ) { if (si->mem.physmem == (count_t)-1) { si->mem.physmem = cnts[0]*1024/pagesize; } } else if (strcmp("MemFree:", nam) == EQ) { if (si->mem.freemem == (count_t)-1) { si->mem.freemem = cnts[0]*1024/pagesize; } } else if (strcmp("MemAvailable:", nam) == EQ) { si->mem.availablemem = cnts[0]*1024/pagesize; } else if (strcmp("Buffers:", nam) == EQ) { if (si->mem.buffermem == (count_t)-1) { si->mem.buffermem = cnts[0]*1024/pagesize; } } else if (strcmp("Shmem:", nam) == EQ) { si->mem.shmem = cnts[0]*1024/pagesize; } else if (strcmp("SwapTotal:", nam) == EQ) { if (si->mem.totswap == (count_t)-1) { si->mem.totswap = cnts[0]*1024/pagesize; } } else if (strcmp("SwapFree:", nam) == EQ) { if (si->mem.freeswap == (count_t)-1) { si->mem.freeswap = cnts[0]*1024/pagesize; } } else if (strcmp("SwapCached:", nam) == EQ) { si->mem.swapcached = cnts[0]*1024/pagesize; } else if (strcmp("Slab:", nam) == EQ) { si->mem.slabmem = cnts[0]*1024/pagesize; } else if (strcmp("SReclaimable:", nam) == EQ) { si->mem.slabreclaim = cnts[0]*1024/ pagesize; } else if (strcmp("Committed_AS:", nam) == EQ) { si->mem.committed = cnts[0]*1024/ pagesize; } else if (strcmp("CommitLimit:", nam) == EQ) { si->mem.commitlim = cnts[0]*1024/ pagesize; } else if (strcmp("AnonHugePages:", nam) == EQ) { si->mem.anonhugepage = cnts[0]*1024/ pagesize; } else if (strcmp("HugePages_Total:", nam) == EQ) { si->mem.stothugepage = cnts[0]; } else if (strcmp("HugePages_Free:", nam) == EQ) { si->mem.sfreehugepage = cnts[0]; } else if (strcmp("Hugepagesize:", nam) == EQ) { si->mem.shugepagesz = cnts[0]*1024; } else if (strcmp("PageTables:", nam) == EQ) { si->mem.pagetables = cnts[0]*1024/ pagesize; } else if (strcmp("Zswap:", nam) == EQ) { si->mem.zswap = cnts[0]*1024/ pagesize; } else if (strcmp("Zswapped:", nam) == EQ) { si->mem.zswapped = cnts[0]*1024/ pagesize; } } fclose(fp); } /* ** gather values of larger huge pages that are not provided ** by /proc/meminfo ** ** all required hugepage info can be found in: ** ** /sys/kernel/mm/hugepages/hugepages-1048576kB ** /free_hugepages ** /nr_hugepages ** ** /sys/kernel/mm/hugepages/hugepages-2048kB ** /free_hugepages ** /nr_hugepages ** ** the info about the smaller hugepages (2M) is already ** read from /proc/memory ** ** first time initialization: ** find out if there is a large hugepage directory */ if (!lhugepagetot) { lhugepagetot = (char *) -1; // should be overwritten lhugepagefree = (char *) -1; // should be overwritten dirp = opendir(HUGEPAGEDIR); if (dirp) { /* ** read directory entries that start with "hugepages-" */ while ( (dentry = readdir(dirp)) ) { if (strncmp(dentry->d_name, "hugepages-", 10)) continue; hpsize = strtoul(dentry->d_name+10, NULL, 0) * 1024; if (hpsize == si->mem.shugepagesz) continue; // values already known /* ** directory name of larger hugepages found: ** store for use in subsequent intervals */ lhugepagetot = malloc(sizeof HUGEPAGEDIR + 1 + strlen(dentry->d_name) + 1 + sizeof "nr_hugepages" + 1); ptrverify(lhugepagetot, "Malloc failed for huge page total"); 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_name(0, 0, diskname, &(si->dsk.dsk[i]), MAXDKNAM) != DSKTYPE) continue; if (++i >= MAXDSK-1) break; } } si->dsk.dsk[i].name[0] = '\0'; /* set terminator for table */ si->dsk.ndsk = i; fclose(fp); if (i == 0) part_stats = 0; /* do not try again for next cycles */ } /* ** check if disk-statistics are provided (kernel 2.6 onwards) */ if ( (fp = fopen("diskstats", "r")) != NULL) { char diskname[256]; struct perdsk tmpdsk; si->dsk.ndsk = 0; si->dsk.nmdd = 0; si->dsk.nlvm = 0; while ( fgets(linebuf, sizeof(linebuf), fp) ) { /* discards are not supported in older kernels */ tmpdsk.ndisc = -1; nr = sscanf(linebuf, "%d %d %255s " // ident "%lld %*d %lld %*d " // reads "%lld %*d %lld %*d " // writes "%lld %lld %lld " // misc "%lld %*d %lld %*d", // discards &major, &minor, diskname, &tmpdsk.nread, &tmpdsk.nrsect, &tmpdsk.nwrite, &tmpdsk.nwsect, &tmpdsk.inflight, &tmpdsk.io_ms, &tmpdsk.avque, &tmpdsk.ndisc, &tmpdsk.ndsect); if (nr >= 10) /* full stats-line ? */ { /* ** when no transfers issued, skip disk (partition) */ if (tmpdsk.nread + tmpdsk.nwrite + (tmpdsk.ndisc == -1 ? 0 : tmpdsk.ndisc) == 0) continue; /* ** check if this line concerns the entire disk ** or just one of the partitions of a disk (to be ** skipped) */ switch ( isdisk_name(major, minor, diskname, &tmpdsk, MAXDKNAM) ) { case NONTYPE: continue; case DSKTYPE: if (si->dsk.ndsk < MAXDSK-1) si->dsk.dsk[si->dsk.ndsk++] = tmpdsk; break; case MDDTYPE: if (si->dsk.nmdd < MAXMDD-1) si->dsk.mdd[si->dsk.nmdd++] = tmpdsk; break; case LVMTYPE: if (si->dsk.nlvm < MAXLVM-1) si->dsk.lvm[si->dsk.nlvm++] = tmpdsk; break; } } } /* ** set terminator for table */ si->dsk.dsk[si->dsk.ndsk].name[0] = '\0'; si->dsk.mdd[si->dsk.nmdd].name[0] = '\0'; si->dsk.lvm[si->dsk.nlvm].name[0] = '\0'; fclose(fp); } /* ** get information about the shared memory statistics */ if ( shmctl(0, SHM_INFO, (struct shmid_ds *)&shminfo) != -1) { si->mem.shmrss = shminfo.shm_rss; si->mem.shmswp = shminfo.shm_swp; } /* ** NFS server statistics */ if ( (fp = fopen("net/rpc/nfsd", "r")) != NULL) { char label[32]; count_t cnt[40]; /* ** every line starts with a small label, ** followed by upto 60 counters */ while ( fgets(linebuf, sizeof(linebuf), fp) != NULL) { memset(cnt, 0, sizeof cnt); nr = sscanf(linebuf, "%31s %lld %lld %lld %lld %lld" "%lld %lld %lld %lld %lld" "%lld %lld %lld %lld %lld" "%lld %lld %lld %lld %lld" "%lld %lld %lld %lld %lld" "%lld %lld %lld %lld %lld" "%lld %lld %lld %lld %lld" "%lld %lld %lld %lld %lld", label, &cnt[0], &cnt[1], &cnt[2], &cnt[3], &cnt[4], &cnt[5], &cnt[6], &cnt[7], &cnt[8], &cnt[9], &cnt[10], &cnt[11], &cnt[12], &cnt[13], &cnt[14], &cnt[15], &cnt[16], &cnt[17], &cnt[18], &cnt[19], &cnt[20], &cnt[21], &cnt[22], &cnt[23], &cnt[24], &cnt[25], &cnt[26], &cnt[27], &cnt[28], &cnt[29], &cnt[30], &cnt[31], &cnt[32], &cnt[33], &cnt[34], &cnt[35], &cnt[36], &cnt[37], &cnt[38], &cnt[39]); if (nr < 2) // unexpected empty line ? continue; if (strcmp(label, "rc") == 0) { si->nfs.server.rchits = cnt[0]; si->nfs.server.rcmiss = cnt[1]; si->nfs.server.rcnoca = cnt[2]; continue; } if (strcmp(label, "io") == 0) { si->nfs.server.nrbytes = cnt[0]; si->nfs.server.nwbytes = cnt[1]; continue; } if (strcmp(label, "net") == 0) { si->nfs.server.netcnt = cnt[0]; si->nfs.server.netudpcnt = cnt[1]; si->nfs.server.nettcpcnt = cnt[2]; si->nfs.server.nettcpcon = cnt[3]; continue; } if (strcmp(label, "rpc") == 0) { si->nfs.server.rpccnt = cnt[0]; si->nfs.server.rpcbadfmt = cnt[1]; si->nfs.server.rpcbadaut = cnt[2]; si->nfs.server.rpcbadcln = cnt[3]; continue; } // // first counter behind 'proc..' is number of // counters that follow if (strcmp(label, "proc2") == 0) { si->nfs.server.rpcread += cnt[7]; // offset+1 si->nfs.server.rpcwrite += cnt[9]; // offset+1 continue; } if (strcmp(label, "proc3") == 0) { si->nfs.server.rpcread += cnt[7]; // offset+1 si->nfs.server.rpcwrite += cnt[8]; // offset+1 continue; } if (strcmp(label, "proc4ops") == 0) { si->nfs.server.rpcread += cnt[26]; // offset+1 si->nfs.server.rpcwrite += cnt[39]; // offset+1 continue; } } fclose(fp); } /* ** NFS client statistics */ if ( (fp = fopen("net/rpc/nfs", "r")) != NULL) { char label[32]; count_t cnt[10]; /* ** every line starts with a small label, ** followed by counters */ while ( fgets(linebuf, sizeof(linebuf), fp) != NULL) { memset(cnt, 0, sizeof cnt); nr = sscanf(linebuf, "%31s %lld %lld %lld %lld %lld" "%lld %lld %lld %lld %lld", label, &cnt[0], &cnt[1], &cnt[2], &cnt[3], &cnt[4], &cnt[5], &cnt[6], &cnt[7], &cnt[8], &cnt[9]); if (nr < 2) // unexpected empty line ? continue; if (strcmp(label, "rpc") == 0) { si->nfs.client.rpccnt = cnt[0]; si->nfs.client.rpcretrans = cnt[1]; si->nfs.client.rpcautrefresh = cnt[2]; continue; } // first counter behind 'proc..' is number of // counters that follow if (strcmp(label, "proc2") == 0) { si->nfs.client.rpcread += cnt[7]; // offset+1 si->nfs.client.rpcwrite += cnt[9]; // offset+1 continue; } if (strcmp(label, "proc3") == 0) { si->nfs.client.rpcread += cnt[7]; // offset+1 si->nfs.client.rpcwrite += cnt[8]; // offset+1 continue; } if (strcmp(label, "proc4") == 0) { si->nfs.client.rpcread += cnt[2]; // offset+1 si->nfs.client.rpcwrite += cnt[3]; // offset+1 continue; } } fclose(fp); } /* ** NFS client: per-mount statistics */ regainrootprivs(); if ( (fp = fopen("self/mountstats", "r")) != NULL) { char mountdev[128], fstype[32], label[32]; count_t cnt[8]; i = 0; while ( fgets(linebuf, sizeof(linebuf), fp) != NULL) { // if 'device' line, just remember the mounted device if (sscanf(linebuf, "device %127s mounted on %*s with fstype %31s", mountdev, fstype) == 2) { continue; } if (memcmp(fstype, "nfs", 3) != 0) continue; // this is line with NFS client stats nr = sscanf(linebuf, "%31s %lld %lld %lld %lld %lld %lld %lld %lld", label, &cnt[0], &cnt[1], &cnt[2], &cnt[3], &cnt[4], &cnt[5], &cnt[6], &cnt[7]); if (nr >= 2 ) { if (strcmp(label, "age:") == 0) { strcpy(si->nfs.nfsmounts.nfsmnt[i].mountdev, mountdev); si->nfs.nfsmounts.nfsmnt[i].age = cnt[0]; } if (strcmp(label, "bytes:") == 0) { si->nfs.nfsmounts.nfsmnt[i].bytesread = cnt[0]; si->nfs.nfsmounts.nfsmnt[i].byteswrite = cnt[1]; si->nfs.nfsmounts.nfsmnt[i].bytesdread = cnt[2]; si->nfs.nfsmounts.nfsmnt[i].bytesdwrite = cnt[3]; si->nfs.nfsmounts.nfsmnt[i].bytestotread = cnt[4]; si->nfs.nfsmounts.nfsmnt[i].bytestotwrite = cnt[5]; si->nfs.nfsmounts.nfsmnt[i].pagesmread = cnt[6]; si->nfs.nfsmounts.nfsmnt[i].pagesmwrite = cnt[7]; if (++i >= MAXNFSMOUNT-1) break; } } } si->nfs.nfsmounts.nrmounts = i; fclose(fp); } if (! droprootprivs()) mcleanstop(42, "failed to drop root privs\n"); /* ** pressure statistics in /proc/pressure (>= 4.20) ** ** cpu: some avg10=0.00 avg60=1.37 avg300=3.73 total=30995960 ** 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 functions isdisk_name() and isdick_major() ** ** table contains the names (in regexp format) of disks ** to be recognized, together with a function to modify ** the name-strings (i.e. to abbreviate long strings); ** some frequently found names (like 'loop' and 'ram') ** are also recognized to skip them as fast as possible */ static struct { char *regexp; regex_t compreg; void (*modname)(unsigned int, unsigned int, char *, struct perdsk *, int); int major; // to be filled via isdisk_name() int retval; } validdisk[] = { { "^ram[0-9][0-9]*$", {0}, (void *)0, 0, NONTYPE, }, { "^loop[0-9][0-9]*$", {0}, (void *)0, 0, NONTYPE, }, { "^sd[a-z][a-z]*$", {0}, nullmodname, 0, DSKTYPE, }, { "^dm-[0-9][0-9]*$", {0}, lvmmapname, 0, LVMTYPE, }, { "^md[0-9][0-9]*$", {0}, nullmodname, 0, MDDTYPE, }, { "^vd[a-z][a-z]*$", {0}, nullmodname, 0, DSKTYPE, }, { "^nvme[0-9][0-9]*n[0-9][0-9]*$", {0}, nullmodname, 0, DSKTYPE, }, { "^nvme[0-9][0-9]*c[0-9][0-9]*n[0-9][0-9]*$", {0}, nullmodname, 0, DSKTYPE, }, { "^nbd[0-9][0-9]*$", {0}, nullmodname, 0, DSKTYPE, }, { "^hd[a-z]$", {0}, nullmodname, 0, DSKTYPE, }, { "^rd/c[0-9][0-9]*d[0-9][0-9]*$", {0}, nullmodname, 0, DSKTYPE, }, { "^cciss/c[0-9][0-9]*d[0-9][0-9]*$", {0}, nullmodname, 0, DSKTYPE, }, { "^fio[a-z][a-z]*$", {0}, nullmodname, 0, DSKTYPE, }, { "/host.*/bus.*/target.*/lun.*/disc", {0}, abbrevname1, 0, DSKTYPE, }, { "^xvd[a-z][a-z]*[0-9]*$", {0}, nullmodname, 0, DSKTYPE, }, { "^dasd[a-z][a-z]*$", {0}, nullmodname, 0, DSKTYPE, }, { "^mmcblk[0-9][0-9]*$", {0}, nullmodname, 0, DSKTYPE, }, { "^emcpower[a-z][a-z]*$", {0}, nullmodname, 0, DSKTYPE, }, { "^rbd[0-9][0-9]*$", {0}, nullmodname, 0, DSKTYPE, }, { "^rbd[0-9][0-9]*p[0-9][0-9]*$", {0}, nullmodname, 0, DSKTYPE, }, }; static int isdisk_name(unsigned int major, unsigned int minor, char *curname, struct perdsk *px, int maxlen) { static int firstcall = 1; register int i; if (firstcall) /* compile the regular expressions */ { for (i=0; i < sizeof validdisk/sizeof validdisk[0]; i++) regcomp(&validdisk[i].compreg, validdisk[i].regexp, REG_NOSUB); firstcall = 0; } /* ** try to recognize one of the compiled regular expressions */ for (i=0; i < sizeof validdisk/sizeof validdisk[0]; i++) { if (regexec(&validdisk[i].compreg, curname, 0, NULL, 0) == 0) { /* ** store major number of driver */ if (major) validdisk[i].major = major; /* ** name-string recognized; modify name-string */ if (validdisk[i].retval != NONTYPE) (*validdisk[i].modname)(major, minor, curname, px, maxlen); return validdisk[i].retval; } } return NONTYPE; } int isdisk_major(unsigned int major) { register int i; /* ** search for the major number of the disk */ for (i=0; i < sizeof validdisk/sizeof validdisk[0]; i++) { if (major == validdisk[i].major) return validdisk[i].retval; } return NONTYPE; } /* ** get stats of all InfiniBand ports below ** /sys/class/infiniband//ports//.... */ static struct ibcachent { char *ibha; // InfiniBand Host Adaptor unsigned short port; // port number unsigned short lanes; // number of lanes count_t rate; // transfer rate in bytes/sec char *pathrcvb; // path name for received bytes char *pathsndb; // path name for transmitted bytes char *pathrcvp; // path name for received packets char *pathsndp; // path name for transmitted packets } ibcache[MAXIBPORT]; static int nib; // number of IB ports in cache static void ibprep(struct ibcachent *); static int ibstat(struct ibcachent *, struct perifb *); static int get_infiniband(struct ifbstat *si) { static int firstcall = 1; int i; // verify if InfiniBand used in this system if ( chdir("/sys/class/infiniband") == -1) return 0; // no path, no IB, so don't try again if (firstcall) { char path[PATH_MAX], *p; struct stat statbuf; struct dirent *contdent, *portdent; DIR *contp, *portp; firstcall = 0; /* ** once setup a cache with all info that is needed ** to gather the necessary stats with every subsequent ** call, including path names, etcetera. */ if ( (contp = opendir(".")) ) { /* ** read every directory-entry and search for ** subdirectories (i.e. controllers) */ while ( (contdent = readdir(contp)) ) { // skip . and .. if (contdent->d_name[0] == '.') continue; if ( stat(contdent->d_name, &statbuf) == -1 ) continue; if ( ! S_ISDIR(statbuf.st_mode) ) continue; // controller found // store controller name for cache // p = malloc( strlen(contdent->d_name)+1 ); strcpy(p, contdent->d_name); if (strlen(contdent->d_name) > MAXIBNAME-1) p[MAXIBNAME-1] = '\0'; // discover all ports // snprintf(path, sizeof path, "%s/ports", contdent->d_name); if ( (portp = opendir(path)) ) { /* ** read every directory-entry and ** search for subdirectories (i.e. ** port numbers) */ while ( (portdent = readdir(portp)) ) { int port; // skip . and .. if (portdent->d_name[0] == '.') continue; port = atoi(portdent->d_name); if (!port) continue; // valid port // fill cache info // ibcache[nib].port = port; ibcache[nib].ibha = p; ibprep(&ibcache[nib]); if (++nib >= MAXIBPORT) break; } closedir(portp); if (nib >= MAXIBPORT) break; } } closedir(contp); } } /* ** get static and variable metrics */ for (i=0; i < nib; i++) { // static metrics from cache strcpy(si->ifb[i].ibname, ibcache[i].ibha); si->ifb[i].portnr = ibcache[i].port; si->ifb[i].lanes = ibcache[i].lanes; si->ifb[i].rate = ibcache[i].rate; // variable metrics from sysfs ibstat(&(ibcache[i]), &(si->ifb[i])); } si->nrports = nib; return 1; } /* ** determine rate and number of lanes ** from /ports//rate --> e.g. "100 Gb/sec (4X EDR)" ** and assemble path names to be used for counters later on */ static void ibprep(struct ibcachent *ibc) { FILE *fp; char path[PATH_MAX], linebuf[64], speedunit; // determine port rate and number of lanes snprintf(path, sizeof path, "%s/ports/%d/rate", ibc->ibha, ibc->port); if ( (fp = fopen(path, "r")) ) { if ( fgets(linebuf, sizeof(linebuf), fp) != NULL) { (void) sscanf(linebuf, "%lld %c%*s (%hdX", &(ibc->rate), &speedunit, &(ibc->lanes)); // calculate megabits/second switch (speedunit) { case 'M': case 'm': break; case 'G': case 'g': ibc->rate *= 1000; break; case 'T': case 't': ibc->rate *= 1000000; break; } } else { ibc->lanes = 0; ibc->rate = 0; } fclose(fp); } // build all pathnames to obtain the counters // of this port later on snprintf(path, sizeof path, "%s/ports/%d/counters/port_rcv_data", ibc->ibha, ibc->port); ibc->pathrcvb = malloc( strlen(path)+1 ); strcpy(ibc->pathrcvb, path); snprintf(path, sizeof path, "%s/ports/%d/counters/port_xmit_data", ibc->ibha, ibc->port); ibc->pathsndb = malloc( strlen(path)+1 ); strcpy(ibc->pathsndb, path); snprintf(path, sizeof path, "%s/ports/%d/counters/port_rcv_packets", ibc->ibha, ibc->port); ibc->pathrcvp = malloc( strlen(path)+1 ); strcpy(ibc->pathrcvp, path); snprintf(path, sizeof path, "%s/ports/%d/counters/port_xmit_packets", ibc->ibha, ibc->port); ibc->pathsndp = malloc( strlen(path)+1 ); strcpy(ibc->pathsndp, path); } /* ** read necessary variable counters for this IB port */ static int ibstat(struct ibcachent *ibc, struct perifb *ifb) { FILE *fp; char linebuf[64]; if ( (fp = fopen(ibc->pathrcvb, "r")) ) { if ( fgets(linebuf, sizeof(linebuf), fp) != NULL) { if (sscanf(linebuf, "%lld", &(ifb->rcvb)) == 0) ifb->rcvb = 0; } else { ifb->rcvb = 0; } fclose(fp); } if ( (fp = fopen(ibc->pathsndb, "r")) ) { if ( fgets(linebuf, sizeof(linebuf), fp) != NULL) { if (sscanf(linebuf, "%lld", &(ifb->sndb)) == 0) ifb->sndb = 0; } else { ifb->sndb = 0; } fclose(fp); } if ( (fp = fopen(ibc->pathrcvp, "r")) ) { if ( fgets(linebuf, sizeof(linebuf), fp) != NULL) { if (sscanf(linebuf, "%lld", &(ifb->rcvp)) == 0) ifb->rcvp = 0; } else { ifb->rcvp = 0; } fclose(fp); } if ( (fp = fopen(ibc->pathsndp, "r")) ) { if ( fgets(linebuf, sizeof(linebuf), fp) != NULL) { if (sscanf(linebuf, "%lld", &(ifb->sndp)) == 0) ifb->sndp = 0; } else { ifb->sndp = 0; } fclose(fp); } return 1; } /* ** retrieve ksm values (if switched on) */ static int get_ksm(struct sstat *si) { FILE *fp; int state; si->mem.ksmsharing = -1; si->mem.ksmshared = -1; if ((fp=fopen("/sys/kernel/mm/ksm/run", "r")) != 0) { if (fscanf(fp, "%d", &state) == 1) { if (state == 0) { fclose(fp); return 0; // no more calling } } fclose(fp); } if ((fp=fopen("/sys/kernel/mm/ksm/pages_sharing", "r")) != 0) { if (fscanf(fp, "%llu", &(si->mem.ksmsharing)) != 1) si->mem.ksmsharing = 0; fclose(fp); } if ((fp=fopen("/sys/kernel/mm/ksm/pages_shared", "r")) != 0) { if (fscanf(fp, "%llu", &(si->mem.ksmshared)) != 1) si->mem.ksmshared = 0; fclose(fp); } return 1; } #if HTTPSTATS /* ** retrieve statistics from local HTTP daemons ** via http://localhost/server-status?auto */ int getwwwstat(unsigned short port, struct wwwstat *wp) { int sockfd, tobefound; FILE *sockfp; struct sockaddr_in sockname; char linebuf[4096]; char label[512]; long long value; memset(wp, 0, sizeof *wp); /* ** allocate a socket and connect to the local HTTP daemon */ if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) return 0; sockname.sin_family = AF_INET; sockname.sin_addr.s_addr = htonl(INADDR_LOOPBACK); sockname.sin_port = htons(port); if ( connect(sockfd, (struct sockaddr *) &sockname, sizeof sockname) == -1) { close(sockfd); return 0; } /* ** write a GET-request for /server-status */ if ( write(sockfd, HTTPREQ, sizeof HTTPREQ) < sizeof HTTPREQ) { close(sockfd); return 0; } /* ** remap socket descriptor to a stream to allow stdio calls */ sockfp = fdopen(sockfd, "r+"); /* ** read response line by line */ tobefound = 5; /* number of values to be searched */ while ( fgets(linebuf, sizeof linebuf, sockfp) && tobefound) { /* ** handle line containing status code */ if ( strncmp(linebuf, "HTTP/", 5) == 0) { sscanf(linebuf, "%511s %lld %*s\n", label, &value); if (value != 200) /* HTTP-request okay? */ { fclose(sockfp); close(sockfd); return 0; } continue; } /* ** decode line and search for the required counters */ if (sscanf(linebuf, "%511[^:]: %lld\n", label, &value) == 2) { if ( strcmp(label, "Total Accesses") == 0) { wp->accesses = value; tobefound--; } if ( strcmp(label, "Total kBytes") == 0) { wp->totkbytes = value; tobefound--; } if ( strcmp(label, "Uptime") == 0) { wp->uptime = value; tobefound--; } if ( strcmp(label, "BusyWorkers") == 0) { wp->bworkers = value; tobefound--; } if ( strcmp(label, "IdleWorkers") == 0) { wp->iworkers = value; tobefound--; } } } fclose(sockfp); close(sockfd); return 1; } #endif /* ** retrieve low-level CPU events: ** instructions and cycles per CPU */ #ifndef NOPERFEVENT void do_perfevents(char *tagname, char *tagvalue) { if (!strcmp("enable", tagvalue)) perfevents = PERF_EVENTS_ENABLE; else if (!strcmp("disable", tagvalue)) perfevents = PERF_EVENTS_DISABLE; else { if (run_in_guest()) perfevents = PERF_EVENTS_DISABLE; else perfevents = PERF_EVENTS_ENABLE; } } static int enable_perfevents() { if (perfevents == PERF_EVENTS_AUTO) do_perfevents("perfevents", "auto"); return perfevents == PERF_EVENTS_ENABLE; } static long perf_event_open(struct perf_event_attr *hwevent, pid_t pid, int cpu, int groupfd, unsigned long flags) { return syscall(__NR_perf_event_open, hwevent, pid, cpu, groupfd, flags); } static void getperfevents(struct cpustat *cs) { static int firstcall = 1, cpualloced, *fdi, *fdc; int i; int liResult; if (!enable_perfevents()) return; /* ** once initialize perf event counter retrieval */ if (firstcall) { struct perf_event_attr pea; int success=0, minfds = cs->nrcpu*2 + 32; struct rlimit rlim; firstcall = 0; /* ** for perf events two file descriptors will ** be opened permanently per CPU, so take care ** that enough open files are allowed for this process */ getrlimit(RLIMIT_NOFILE, &rlim); if (rlim.rlim_cur < minfds) // default not enough? { rlim.rlim_cur = minfds; (void) setrlimit(RLIMIT_NOFILE, &rlim); } /* ** allocate space for per-cpu file descriptors */ cpualloced = cs->nrcpu; fdi = malloc(sizeof(int) * cpualloced); fdc = malloc(sizeof(int) * cpualloced); /* ** fill perf_event_attr struct with appropriate values */ memset(&pea, 0, sizeof(struct perf_event_attr)); pea.type = PERF_TYPE_HARDWARE; pea.size = sizeof(struct perf_event_attr); pea.inherit = 1; pea.pinned = 1; regainrootprivs(); for (i=0; i < cpualloced; i++) { pea.config = PERF_COUNT_HW_INSTRUCTIONS; if ( (*(fdi+i) = perf_event_open(&pea, -1, i, -1, PERF_FLAG_FD_CLOEXEC)) >= 0) success++; pea.config = PERF_COUNT_HW_CPU_CYCLES; if ( (*(fdc+i) = perf_event_open(&pea, -1, i, -1, PERF_FLAG_FD_CLOEXEC)) >= 0) success++; } if (! droprootprivs()) mcleanstop(42, "failed to drop root privs\n"); /* ** all failed (probably no kernel support)? */ if (success == 0) { free(fdi); free(fdc); cpualloced = 0; } else { cs->all.instr = 1; cs->all.cycle = 1; } return; // initialization finished for first sample } /* ** every sample: check if counters available anyhow */ if (!cpualloced) return; /* ** retrieve counters per CPU and in total */ cs->all.instr = 0; cs->all.cycle = 0; for (i=0; i < cpualloced; i++) { if (*(fdi+i) != -1) { liResult = read(*(fdi+i), &(cs->cpu[i].instr), sizeof(count_t)); if(liResult < 0) { char lcMessage[64]; snprintf(lcMessage, sizeof(lcMessage) - 1, "%s:%d - Error %d reading instr counters\n", __FILE__, __LINE__, errno); fprintf(stderr, "%s", lcMessage); } else { cs->all.instr += cs->cpu[i].instr; } liResult = read(*(fdc+i), &(cs->cpu[i].cycle), sizeof(count_t)); if(liResult < 0) { char lcMessage[64]; snprintf(lcMessage, sizeof(lcMessage) - 1, "%s:%d - Error %d reading cycle counters\n", __FILE__, __LINE__, errno ); fprintf(stderr, "%s", lcMessage); } else { cs->all.cycle += cs->cpu[i].cycle; } } } } #else /* ! NOPERFEVENT */ void do_perfevents(char *tagname, char *tagvalue) { if (strcmp("disable", tagvalue)) mcleanstop(1, "atop built with NOPERFEVENT, cannot use perfevents\n"); } #endif atop-2.11.1/prev/0000755000203100020310000000000014771753566013040 5ustar gerlofgerlofatop-2.11.1/prev/photosyst_204.h0000644000203100020310000002471514771753566015663 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.11.1/prev/photosyst_201.h0000644000203100020310000001415714771753566015657 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.11.1/prev/photosyst_203.h0000644000203100020310000002016214771753566015652 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.11.1/prev/photosyst_209.h0000644000203100020310000003332614771753566015666 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.11.1/prev/photoproc_211.h0000644000203100020310000001134014771753566015610 0ustar gerlofgerlof/* ** structure containing only relevant process-info extracted ** from kernel's process-administration */ struct tstat_211 { /* GENERAL TASK INFO */ struct gen_211 { int tgid; /* threadgroup identification */ int pid; /* process identification */ int ppid; /* parent process identification*/ int ruid; /* real user identification */ int euid; /* eff. user identification */ int suid; /* saved user identification */ int fsuid; /* fs user identification */ int rgid; /* real group identification */ int egid; /* eff. group identification */ int sgid; /* saved group identification */ int fsgid; /* fs group identification */ int nthr; /* number of threads in tgroup */ char name[PNAMLEN+1];/* process name string */ char isproc; /* boolean: process level? */ char state; /* process state ('E' = exited) */ int excode; /* process exit status */ time_t btime; /* process start time (epoch) */ time_t elaps; /* process elaps time (hertz) */ char cmdline[CMDLEN+1];/* command-line string */ int nthrslpi; /* # threads in state 'S' */ int nthrslpu; /* # threads in state 'D' */ int nthrrun; /* # threads in state 'R' */ int nthridle; /* # threads in state 'I' */ int ctid; /* OpenVZ container ID */ int vpid; /* OpenVZ virtual PID */ int wasinactive; /* boolean: task inactive */ char utsname[UTSLEN+1];/* UTS name container or pod */ int cgroupix; /* index in devchain -1=invalid */ /* lazy filling (parsable/json) */ int ifuture[4]; /* reserved for future use */ } gen; /* CPU STATISTICS */ struct cpu_211 { count_t utime; /* time user text (ticks) */ count_t stime; /* time system text (ticks) */ int nice; /* nice value */ int prio; /* priority */ int rtprio; /* realtime priority */ int policy; /* scheduling policy */ int curcpu; /* current processor */ int sleepavg; /* sleep average percentage */ int ifuture[6]; /* reserved for future use */ char wchan[16]; /* wait channel string */ count_t rundelay; /* schedstat rundelay (nanosec) */ count_t blkdelay; /* blkio delay (ticks) */ count_t nvcsw; /* voluntary cxt switch counts */ count_t nivcsw; /* involuntary csw counts */ count_t cfuture[3]; /* reserved for future use */ } cpu; /* DISK STATISTICS */ struct dsk_211 { count_t rio; /* number of read requests */ count_t rsz; /* cumulative # sectors read */ count_t wio; /* number of write requests */ count_t wsz; /* cumulative # sectors written */ count_t cwsz; /* cumulative # written sectors */ /* being cancelled */ count_t cfuture[4]; /* reserved for future use */ } dsk; /* MEMORY STATISTICS */ struct mem_211 { count_t minflt; /* number of page-reclaims */ count_t majflt; /* number of page-faults */ count_t vexec; /* virtmem execfile (Kb) */ count_t vmem; /* virtual memory (Kb) */ count_t rmem; /* resident memory (Kb) */ count_t pmem; /* resident memory (Kb) */ count_t vgrow; /* virtual growth (Kb) */ count_t rgrow; /* resident growth (Kb) */ count_t vdata; /* virtmem data (Kb) */ count_t vstack; /* virtmem stack (Kb) */ count_t vlibs; /* virtmem libexec (Kb) */ count_t vswap; /* swap space used (Kb) */ count_t vlock; /* virtual locked (Kb) */ count_t cfuture[7]; /* reserved for future use */ } mem; /* NETWORK STATISTICS */ struct net_211 { count_t tcpsnd; /* number of TCP-packets sent */ count_t tcpssz; /* cumulative size packets sent */ count_t tcprcv; /* number of TCP-packets recved */ count_t tcprsz; /* cumulative size packets rcvd */ count_t udpsnd; /* number of UDP-packets sent */ count_t udpssz; /* cumulative size packets sent */ count_t udprcv; /* number of UDP-packets recved */ count_t udprsz; /* cumulative size packets sent */ count_t avail1; /* */ count_t avail2; /* */ count_t cfuture[4]; /* reserved for future use */ } net; struct gpu_211 { char state; // A - active, E - Exit, '\0' - no use char bfuture[3]; // short nrgpus; // number of GPUs for this process int32_t gpulist; // bitlist with GPU numbers int gpubusy; // gpu busy perc process lifetime -1 = n/a int membusy; // memory busy perc process lifetime -1 = n/a count_t timems; // milliseconds accounting -1 = n/a // value 0 for active process, // value > 0 after termination count_t memnow; // current memory consumption in KiB count_t memcum; // cumulative memory consumption in KiB count_t sample; // number of samples count_t cfuture[3]; // } gpu; }; atop-2.11.1/prev/photosyst_205.h0000644000203100020310000002471514771753566015664 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.11.1/prev/photoproc_206.h0000644000203100020310000001055414771753566015622 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.11.1/prev/photoproc_202.h0000644000203100020310000000707314771753566015620 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.11.1/prev/netstats_wrong.h0000644000203100020310000000267314771753566016302 0ustar gerlofgerlofstruct tcp_stats_without_InCsumErrors { count_t RtoAlgorithm; count_t RtoMin; count_t RtoMax; count_t MaxConn; count_t ActiveOpens; count_t PassiveOpens; count_t AttemptFails; count_t EstabResets; count_t CurrEstab; count_t InSegs; count_t OutSegs; count_t RetransSegs; count_t InErrs; count_t OutRsts; }; struct icmpv4_stats_without_InCsumErrors { count_t InMsgs; count_t InErrors; count_t InDestUnreachs; count_t InTimeExcds; count_t InParmProbs; count_t InSrcQuenchs; count_t InRedirects; count_t InEchos; count_t InEchoReps; count_t InTimestamps; count_t InTimestampReps; count_t InAddrMasks; count_t InAddrMaskReps; count_t OutMsgs; count_t OutErrors; count_t OutDestUnreachs; count_t OutTimeExcds; count_t OutParmProbs; count_t OutSrcQuenchs; count_t OutRedirects; count_t OutEchos; count_t OutEchoReps; count_t OutTimestamps; count_t OutTimestampReps; count_t OutAddrMasks; count_t OutAddrMaskReps; }; struct netstat_wrong { struct ipv4_stats ipv4; struct icmpv4_stats_without_InCsumErrors icmpv4; struct udpv4_stats udpv4; struct ipv6_stats ipv6; struct icmpv6_stats icmpv6; struct udpv6_stats udpv6; struct tcp_stats_without_InCsumErrors tcp; }; atop-2.11.1/prev/photosyst_208.h0000644000203100020310000003332314771753566015662 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.11.1/prev/photosyst_206.h0000644000203100020310000002517114771753566015662 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.11.1/prev/photoproc_207.h0000644000203100020310000001055414771753566015623 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.11.1/prev/photoproc_205.h0000644000203100020310000001032614771753566015616 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.11.1/prev/photoproc_201.h0000644000203100020310000000701614771753566015614 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.11.1/prev/photosyst_210.h0000644000203100020310000003452114771753566015654 0ustar gerlofgerlof#define MAXCPU_210 2048 #define MAXDSK_210 1024 #define MAXNUMA_210 1024 #define MAXLVM_210 2048 #define MAXMDD_210 256 #define MAXINTF_210 128 #define MAXCONTAINER_210 128 #define MAXNFSMOUNT_210 64 #define MAXIBPORT_210 32 #define MAXGPU_210 32 #define MAXGPUBUS_210 12 #define MAXGPUTYPE_210 12 #define MAXLLC_210 256 #define MAXDKNAM_210 32 #define MAXIBNAME_210 12 /************************************************************************/ struct memstat_210 { count_t physmem; // number of physical pages count_t freemem; // number of free pages count_t buffermem; // number of buffer pages count_t slabmem; // number of slab pages count_t cachemem; // number of cache pages count_t cachedrt; // number of cache pages (dirty) count_t totswap; // number of pages in swap count_t freeswap; // number of free swap pages count_t pgscans; // number of page scans count_t pgsteal; // number of page steals count_t allocstall; // try to free pages forced count_t swouts; // number of pages swapped out count_t swins; // number of pages swapped in count_t tcpsock; // number of pages allocated by TCP sockets count_t udpsock; // number of pages allocated by UDP sockets count_t commitlim; // commit limit in pages count_t committed; // number of reserved pages count_t shmem; // tot shmem incl. tmpfs (pages) count_t shmrss; // resident shared memory (pages) count_t shmswp; // swapped shared memory (pages) count_t slabreclaim; // reclaimable slab (pages) count_t stothugepage; // total huge pages (huge pages) - small count_t sfreehugepage; // free huge pages (huge pages) - small count_t shugepagesz; // huge page size (bytes) - small count_t vmwballoon; // vmware claimed balloon pages count_t zfsarcsize; // zfsonlinux ARC size (pages) count_t swapcached; // swap cache (pages) count_t ksmsharing; // saved i.e. deduped memory (pages) count_t ksmshared; // current size shared pages (pages) count_t zswapped; // zswap stored pages decompressed (pages) count_t zswap; // zswap current pool size compressed (pages) count_t oomkills; // number of oom killings count_t compactstall; // counter for process stalls count_t pgmigrate; // counter for migrated successfully (pages) count_t numamigrate; // counter for numa migrated (pages) count_t pgouts; // total number of pages written to block device count_t pgins; // total number of pages read from block device count_t pagetables; // page tables of processes (pages) count_t zswouts; // number of pages swapped out to zswap count_t zswins; // number of pages swapped in from zswap count_t ltothugepage; // total huge pages (huge pages) - large count_t lfreehugepage; // free huge pages (huge pages) - large count_t lhugepagesz; // huge page size (bytes) - large count_t availablemem; // available memory (pages) count_t anonhugepage; // anonymous transparent huge pages // (in units of 'normal' pages) count_t cfuture[4]; // reserved for future use }; /************************************************************************/ struct mempernuma_210 { int numanr; float frag; // fragmentation level for this numa count_t totmem; // number of physical pages for this numa count_t freemem; // number of free pages for this numa count_t filepage; // number of file pages for this numa count_t dirtymem; // number of cache pages (dirty) for this numa count_t slabmem; // number of slab pages for this numa count_t slabreclaim; // reclaimable slab (pages) for this numa count_t active; // number of pages used more recently for this numa count_t inactive; // number of pages less recently used for this numa count_t shmem; // tot shmem incl. tmpfs (pages) for this numa count_t tothp; // total huge pages (huge pages) for this numa count_t freehp; // total free pages (huge pages) for this numa }; struct memnuma_210 { count_t nrnuma; /* the counts of numa */ struct mempernuma_210 numa[MAXNUMA_210]; }; struct cpupernuma_210 { int numanr; count_t nrcpu; // number of cpu's count_t stime; // accumulate system time in clock ticks for per numa count_t utime; // accumulate user time in clock ticks for per numa count_t ntime; // accumulate nice time in clock ticks for per numa count_t itime; // accumulate idle time in clock ticks for per numa count_t wtime; // accumulate iowait time in clock ticks for per numa count_t Itime; // accumulate irq time in clock ticks for per numa count_t Stime; // accumulate softirq time in clock ticks for per numa count_t steal; // accumulate steal time in clock ticks for per numa count_t guest; // accumulate guest time in clock ticks for per numa }; struct cpunuma_210 { count_t nrnuma; /* the counts of numa */ struct cpupernuma_210 numa[MAXNUMA_210]; }; /************************************************************************/ struct netstat_210 { struct ipv4_stats ipv4; struct icmpv4_stats icmpv4; struct udpv4_stats udpv4; struct ipv6_stats ipv6; struct icmpv6_stats icmpv6; struct udpv6_stats udpv6; struct tcp_stats tcp; }; /************************************************************************/ struct freqcnt_210 { count_t maxfreq;/* frequency in MHz */ count_t cnt; /* number of clock ticks times state */ count_t ticks; /* number of total clock ticks */ /* if zero, cnt is actual freq */ }; struct percpu_210 { int cpunr; count_t stime; /* system time in clock ticks */ count_t utime; /* user time in clock ticks */ count_t ntime; /* nice time in clock ticks */ count_t itime; /* idle time in clock ticks */ count_t wtime; /* iowait time in clock ticks */ count_t Itime; /* irq time in clock ticks */ count_t Stime; /* softirq time in clock ticks */ count_t steal; /* steal time in clock ticks */ count_t guest; /* guest time in clock ticks */ struct freqcnt_210 freqcnt;/* frequency scaling info */ count_t instr; /* CPU instructions */ count_t cycle; /* CPU cycles */ count_t cfuture[6]; /* reserved for future use */ }; struct cpustat_210 { count_t nrcpu; /* number of cpu's */ count_t devint; /* number of device interrupts */ count_t csw; /* number of context switches */ count_t nprocs; /* number of processes started */ float lavg1; /* load average last minute */ float lavg5; /* load average last 5 minutes */ float lavg15; /* load average last 15 minutes */ count_t cfuture[4]; /* reserved for future use */ struct percpu_210 all; struct percpu_210 cpu[MAXCPU_210]; }; /************************************************************************/ struct perdsk_210 { char name[MAXDKNAM_210]; /* empty string for last */ count_t nread; /* number of read transfers */ count_t nrsect; /* number of sectors read */ count_t nwrite; /* number of write transfers */ count_t nwsect; /* number of sectors written */ count_t io_ms; /* number of millisecs spent for I/O */ count_t avque; /* average queue length */ count_t ndisc; /* number of discards (-1 = unavailable)*/ count_t ndsect; /* number of sectors discarded */ count_t inflight; /* number of inflight I/O */ count_t cfuture[3]; /* reserved for future use */ }; struct dskstat_210 { int ndsk; /* number of physical disks */ int nmdd; /* number of md volumes */ int nlvm; /* number of logical volumes */ struct perdsk_210 dsk[MAXDSK_210]; struct perdsk_210 mdd[MAXMDD_210]; struct perdsk_210 lvm[MAXLVM_210]; }; /************************************************************************/ struct perintf_210 { char name[16]; /* empty string for last */ count_t rbyte; /* number of read bytes */ count_t rpack; /* number of read packets */ count_t rerrs; /* receive errors */ count_t rdrop; /* receive drops */ count_t rfifo; /* receive fifo */ count_t rframe; /* receive framing errors */ count_t rcompr; /* receive compressed */ count_t rmultic;/* receive multicast */ count_t rfuture[4]; /* reserved for future use */ count_t sbyte; /* number of written bytes */ count_t spack; /* number of written packets */ count_t serrs; /* transmit errors */ count_t sdrop; /* transmit drops */ count_t sfifo; /* transmit fifo */ count_t scollis;/* collisions */ count_t scarrier;/* transmit carrier */ count_t scompr; /* transmit compressed */ count_t sfuture[4]; /* reserved for future use */ char type; /* interface type ('e'/'w'/'v'/'?') */ long speed; /* interface speed in megabits/second */ long speedp; /* previous interface speed */ char duplex; /* full duplex (boolean) */ count_t cfuture[4]; /* reserved for future use */ }; struct intfstat_210 { int nrintf; struct perintf_210 intf[MAXINTF_210]; }; /************************************************************************/ struct pernfsmount_210 { char mountdev[128]; /* mountdevice */ count_t age; /* number of seconds mounted */ count_t bytesread; /* via normal reads */ count_t byteswrite; /* via normal writes */ count_t bytesdread; /* via direct reads */ count_t bytesdwrite; /* via direct writes */ count_t bytestotread; /* via reads */ count_t bytestotwrite; /* via writes */ count_t pagesmread; /* via mmap reads */ count_t pagesmwrite; /* via mmap writes */ count_t future[8]; }; struct nfsstat_210 { struct { count_t netcnt; count_t netudpcnt; count_t nettcpcnt; count_t nettcpcon; count_t rpccnt; count_t rpcbadfmt; count_t rpcbadaut; count_t rpcbadcln; count_t rpcread; count_t rpcwrite; count_t rchits; /* repcache hits */ count_t rcmiss; /* repcache misses */ count_t rcnoca; /* uncached requests */ count_t nrbytes; /* read bytes */ count_t nwbytes; /* written bytes */ count_t future[8]; } server; struct { count_t rpccnt; count_t rpcretrans; count_t rpcautrefresh; count_t rpcread; count_t rpcwrite; count_t future[8]; } client; struct { int nrmounts; struct pernfsmount_210 nfsmnt[MAXNFSMOUNT_210]; } nfsmounts; }; /************************************************************************/ struct psi_210 { float avg10; // average pressure last 10 seconds float avg60; // average pressure last 60 seconds float avg300; // average pressure last 300 seconds count_t total; // total number of milliseconds }; struct pressure_210 { char present; /* pressure stats supported? */ char future[3]; struct psi_210 cpusome; /* pressure stall info 'some' */ struct psi_210 memsome; /* pressure stall info 'some' */ struct psi_210 memfull; /* pressure stall info 'full' */ struct psi_210 iosome; /* pressure stall info 'some' */ struct psi_210 iofull; /* pressure stall info 'full' */ }; /************************************************************************/ struct percontainer_210 { unsigned long ctid; /* container id */ unsigned long numproc; /* number of processes */ count_t system; /* */ count_t user; /* */ count_t nice; /* */ count_t uptime; /* */ count_t physpages; /* */ }; struct contstat_210 { int nrcontainer; struct percontainer_210 cont[MAXCONTAINER_210]; }; /************************************************************************/ /* ** experimental stuff for access to local HTTP daemons */ #define HTTPREQ "GET /server-status?auto HTTP/1.1\nHost: localhost\n\n" struct wwwstat_210 { count_t accesses; /* total number of HTTP-requests */ count_t totkbytes; /* total kbytes transfer for HTTP-req */ count_t uptime; /* number of seconds since startup */ int bworkers; /* number of busy httpd-daemons */ int iworkers; /* number of idle httpd-daemons */ }; /************************************************************************/ struct pergpu_210 { char taskstats; // GPU task statistics supported? unsigned char nrprocs; // number of processes using GPU char type[MAXGPUTYPE_210+1]; // GPU type char busid[MAXGPUBUS_210+1]; // GPU bus identification int gpunr; // GPU number int gpupercnow; // processor percentage last second // -1 if not supported int mempercnow; // memory percentage last second // -1 if not supported count_t memtotnow; // total memory in KiB count_t memusenow; // used memory in KiB count_t samples; // number of samples count_t gpuperccum; // cumulative processor busy percentage // -1 if not supported count_t memperccum; // cumulative memory percentage // -1 if not supported count_t memusecum; // cumulative used memory in KiB }; struct gpustat_210 { int nrgpus; // total number of GPUs struct pergpu_210 gpu[MAXGPU_210]; }; /************************************************************************/ struct perifb_210 { char ibname[MAXIBNAME_210]; // InfiniBand controller short portnr; // InfiniBand controller port short lanes; // number of lanes (traffic factor) count_t rate; // transfer rate in megabits/sec count_t rcvb; // bytes received count_t sndb; // bytes transmitted count_t rcvp; // packets received count_t sndp; // packets transmitted }; struct ifbstat_210 { int nrports; // total number of IB ports struct perifb_210 ifb[MAXIBPORT_210]; }; /************************************************************************/ struct perllc_210 { unsigned char id; float occupancy; count_t mbm_local; count_t mbm_total; }; struct llcstat_210 { int nrllcs; // total number of LLC struct perllc_210 perllc[MAXLLC_210]; }; /************************************************************************/ struct sstat_210 { struct cpustat_210 cpu; struct memstat_210 mem; struct netstat_210 net; struct intfstat_210 intf; struct memnuma_210 memnuma; struct cpunuma_210 cpunuma; struct dskstat_210 dsk; struct nfsstat_210 nfs; struct contstat_210 cfs; struct pressure_210 psi; struct gpustat_210 gpu; struct ifbstat_210 ifb; struct llcstat_210 llc; struct wwwstat_210 www; }; atop-2.11.1/prev/photosyst_211.h0000644000203100020310000003474214771753566015662 0ustar gerlofgerlof#define MAXCPU_211 2048 #define MAXDSK_211 1024 #define MAXNUMA_211 1024 #define MAXLVM_211 2048 #define MAXMDD_211 256 #define MAXINTF_211 128 #define MAXCONTAINER_211 128 #define MAXNFSMOUNT_211 64 #define MAXIBPORT_211 32 #define MAXGPU_211 32 #define MAXGPUBUS_211 12 #define MAXGPUTYPE_211 12 #define MAXLLC_211 256 #define MAXDKNAM_211 32 #define MAXIBNAME_211 12 /************************************************************************/ struct memstat_211 { count_t physmem; // number of physical pages count_t freemem; // number of free pages count_t buffermem; // number of buffer pages count_t slabmem; // number of slab pages count_t cachemem; // number of cache pages count_t cachedrt; // number of cache pages (dirty) count_t totswap; // number of pages in swap count_t freeswap; // number of free swap pages count_t pgscans; // number of page scans count_t pgsteal; // number of page steals count_t allocstall; // try to free pages forced count_t swouts; // number of pages swapped out count_t swins; // number of pages swapped in count_t tcpsock; // number of pages allocated by TCP sockets count_t udpsock; // number of pages allocated by UDP sockets count_t commitlim; // commit limit in pages count_t committed; // number of reserved pages count_t shmem; // tot shmem incl. tmpfs (pages) count_t shmrss; // resident shared memory (pages) count_t shmswp; // swapped shared memory (pages) count_t slabreclaim; // reclaimable slab (pages) count_t stothugepage; // total huge pages (huge pages) - small count_t sfreehugepage; // free huge pages (huge pages) - small count_t shugepagesz; // huge page size (bytes) - small count_t vmwballoon; // vmware claimed balloon pages count_t zfsarcsize; // zfsonlinux ARC size (pages) count_t swapcached; // swap cache (pages) count_t ksmsharing; // saved i.e. deduped memory (pages) count_t ksmshared; // current size shared pages (pages) count_t zswapped; // zswap stored pages decompressed (pages) count_t zswap; // zswap current pool size compressed (pages) count_t oomkills; // number of oom killings count_t compactstall; // counter for process stalls count_t pgmigrate; // counter for migrated successfully (pages) count_t numamigrate; // counter for numa migrated (pages) count_t pgouts; // total number of pages written to block device count_t pgins; // total number of pages read from block device count_t pagetables; // page tables of processes (pages) count_t zswouts; // number of pages swapped out to zswap count_t zswins; // number of pages swapped in from zswap count_t ltothugepage; // total huge pages (huge pages) - large count_t lfreehugepage; // free huge pages (huge pages) - large count_t lhugepagesz; // huge page size (bytes) - large count_t availablemem; // available memory (pages) count_t anonhugepage; // anonymous transparent huge pages // (in units of 'normal' pages) count_t cfuture[5]; // reserved for future use }; /************************************************************************/ struct mempernuma_211 { int numanr; float frag; // fragmentation level for this numa count_t totmem; // number of physical pages for this numa count_t freemem; // number of free pages for this numa count_t filepage; // number of file pages for this numa count_t dirtymem; // number of cache pages (dirty) for this numa count_t slabmem; // number of slab pages for this numa count_t slabreclaim; // reclaimable slab (pages) for this numa count_t active; // number of pages used more recently for this numa count_t inactive; // number of pages less recently used for this numa count_t shmem; // tot shmem incl. tmpfs (pages) for this numa count_t tothp; // total huge pages (huge pages) for this numa count_t freehp; // total free pages (huge pages) for this numa count_t cfuture[2]; // reserved for future use }; struct memnuma_211 { count_t nrnuma; /* the counts of numa */ struct mempernuma_211 numa[MAXNUMA_211]; }; struct cpupernuma_211 { int numanr; count_t nrcpu; // number of cpu's count_t stime; // accumulate system time in clock ticks for per numa count_t utime; // accumulate user time in clock ticks for per numa count_t ntime; // accumulate nice time in clock ticks for per numa count_t itime; // accumulate idle time in clock ticks for per numa count_t wtime; // accumulate iowait time in clock ticks for per numa count_t Itime; // accumulate irq time in clock ticks for per numa count_t Stime; // accumulate softirq time in clock ticks for per numa count_t steal; // accumulate steal time in clock ticks for per numa count_t guest; // accumulate guest time in clock ticks for per numa count_t cfuture[2]; // reserved for future use }; struct cpunuma_211 { count_t nrnuma; /* the counts of numa */ struct cpupernuma_211 numa[MAXNUMA_211]; }; /************************************************************************/ struct netstat_211 { struct ipv4_stats ipv4; struct icmpv4_stats icmpv4; struct udpv4_stats udpv4; struct ipv6_stats ipv6; struct icmpv6_stats icmpv6; struct udpv6_stats udpv6; struct tcp_stats tcp; }; /************************************************************************/ struct freqcnt_211 { count_t maxfreq;/* frequency in MHz */ count_t cnt; /* number of clock ticks times state */ count_t ticks; /* number of total clock ticks */ /* if zero, cnt is actual freq */ }; struct percpu_211 { int cpunr; count_t stime; /* system time in clock ticks */ count_t utime; /* user time in clock ticks */ count_t ntime; /* nice time in clock ticks */ count_t itime; /* idle time in clock ticks */ count_t wtime; /* iowait time in clock ticks */ count_t Itime; /* irq time in clock ticks */ count_t Stime; /* softirq time in clock ticks */ count_t steal; /* steal time in clock ticks */ count_t guest; /* guest time in clock ticks */ struct freqcnt_211 freqcnt;/* frequency scaling info */ count_t instr; /* CPU instructions */ count_t cycle; /* CPU cycles */ count_t cfuture[6]; /* reserved for future use */ }; struct cpustat_211 { count_t nrcpu; /* number of cpu's */ count_t devint; /* number of device interrupts */ count_t csw; /* number of context switches */ count_t nprocs; /* number of processes started */ float lavg1; /* load average last minute */ float lavg5; /* load average last 5 minutes */ float lavg15; /* load average last 15 minutes */ count_t cfuture[4]; /* reserved for future use */ struct percpu_211 all; struct percpu_211 cpu[MAXCPU_211]; }; /************************************************************************/ struct perdsk_211 { char name[MAXDKNAM_211]; /* empty string for last */ count_t nread; /* number of read transfers */ count_t nrsect; /* number of sectors read */ count_t nwrite; /* number of write transfers */ count_t nwsect; /* number of sectors written */ count_t io_ms; /* number of millisecs spent for I/O */ count_t avque; /* average queue length */ count_t ndisc; /* number of discards (-1 = unavailable)*/ count_t ndsect; /* number of sectors discarded */ count_t inflight; /* number of inflight I/O */ count_t cfuture[3]; /* reserved for future use */ }; struct dskstat_211 { int ndsk; /* number of physical disks */ int nmdd; /* number of md volumes */ int nlvm; /* number of logical volumes */ struct perdsk_211 dsk[MAXDSK_211]; struct perdsk_211 mdd[MAXMDD_211]; struct perdsk_211 lvm[MAXLVM_211]; }; /************************************************************************/ struct perintf_211 { char name[16]; /* empty string for last */ count_t rbyte; /* number of read bytes */ count_t rpack; /* number of read packets */ count_t rerrs; /* receive errors */ count_t rdrop; /* receive drops */ count_t rfifo; /* receive fifo */ count_t rframe; /* receive framing errors */ count_t rcompr; /* receive compressed */ count_t rmultic;/* receive multicast */ count_t rfuture[4]; /* reserved for future use */ count_t sbyte; /* number of written bytes */ count_t spack; /* number of written packets */ count_t serrs; /* transmit errors */ count_t sdrop; /* transmit drops */ count_t sfifo; /* transmit fifo */ count_t scollis;/* collisions */ count_t scarrier;/* transmit carrier */ count_t scompr; /* transmit compressed */ count_t sfuture[4]; /* reserved for future use */ char type; /* interface type ('e'/'w'/'v'/'?') */ long speed; /* interface speed in megabits/second */ long speedp; /* previous interface speed */ char duplex; /* full duplex (boolean) */ count_t cfuture[4]; /* reserved for future use */ }; struct intfstat_211 { int nrintf; struct perintf_211 intf[MAXINTF_211]; }; /************************************************************************/ struct pernfsmount_211 { char mountdev[128]; /* mountdevice */ count_t age; /* number of seconds mounted */ count_t bytesread; /* via normal reads */ count_t byteswrite; /* via normal writes */ count_t bytesdread; /* via direct reads */ count_t bytesdwrite; /* via direct writes */ count_t bytestotread; /* via reads */ count_t bytestotwrite; /* via writes */ count_t pagesmread; /* via mmap reads */ count_t pagesmwrite; /* via mmap writes */ count_t future[8]; }; struct nfsstat_211 { struct { count_t netcnt; count_t netudpcnt; count_t nettcpcnt; count_t nettcpcon; count_t rpccnt; count_t rpcbadfmt; count_t rpcbadaut; count_t rpcbadcln; count_t rpcread; count_t rpcwrite; count_t rchits; /* repcache hits */ count_t rcmiss; /* repcache misses */ count_t rcnoca; /* uncached requests */ count_t nrbytes; /* read bytes */ count_t nwbytes; /* written bytes */ count_t future[8]; } server; struct { count_t rpccnt; count_t rpcretrans; count_t rpcautrefresh; count_t rpcread; count_t rpcwrite; count_t future[8]; } client; struct { int nrmounts; struct pernfsmount_211 nfsmnt[MAXNFSMOUNT_211]; } nfsmounts; }; /************************************************************************/ struct psi_211 { float avg10; // average pressure last 10 seconds float avg60; // average pressure last 60 seconds float avg300; // average pressure last 300 seconds count_t total; // total number of milliseconds }; struct pressure_211 { char present; /* pressure stats supported? */ char future[3]; struct psi_211 cpusome; /* pressure stall info 'some' */ struct psi_211 memsome; /* pressure stall info 'some' */ struct psi_211 memfull; /* pressure stall info 'full' */ struct psi_211 iosome; /* pressure stall info 'some' */ struct psi_211 iofull; /* pressure stall info 'full' */ }; /************************************************************************/ struct percontainer_211 { unsigned long ctid; /* container id */ unsigned long numproc; /* number of processes */ count_t system; /* */ count_t user; /* */ count_t nice; /* */ count_t uptime; /* */ count_t physpages; /* */ }; struct contstat_211 { int nrcontainer; struct percontainer_211 cont[MAXCONTAINER_211]; }; /************************************************************************/ /* ** experimental stuff for access to local HTTP daemons */ #define HTTPREQ "GET /server-status?auto HTTP/1.1\nHost: localhost\n\n" struct wwwstat_211 { count_t accesses; /* total number of HTTP-requests */ count_t totkbytes; /* total kbytes transfer for HTTP-req */ count_t uptime; /* number of seconds since startup */ int bworkers; /* number of busy httpd-daemons */ int iworkers; /* number of idle httpd-daemons */ }; /************************************************************************/ struct pergpu_211 { char taskstats; // GPU task statistics supported? unsigned char nrprocs; // number of processes using GPU char type[MAXGPUTYPE_211+1]; // GPU type char busid[MAXGPUBUS_211+1]; // GPU bus identification int gpunr; // GPU number int gpupercnow; // processor percentage last second // -1 if not supported int mempercnow; // memory percentage last second // -1 if not supported count_t memtotnow; // total memory in KiB count_t memusenow; // used memory in KiB count_t samples; // number of samples count_t gpuperccum; // cumulative processor busy percentage // -1 if not supported count_t memperccum; // cumulative memory percentage // -1 if not supported count_t memusecum; // cumulative used memory in KiB }; struct gpustat_211 { int nrgpus; // total number of GPUs struct pergpu_211 gpu[MAXGPU_211]; }; /************************************************************************/ struct perifb_211 { char ibname[MAXIBNAME_211]; // InfiniBand controller short portnr; // InfiniBand controller port short lanes; // number of lanes (traffic factor) count_t rate; // transfer rate in megabits/sec count_t rcvb; // bytes received count_t sndb; // bytes transmitted count_t rcvp; // packets received count_t sndp; // packets transmitted count_t cfuture[4]; // reserved for future use }; struct ifbstat_211 { int nrports; // total number of IB ports struct perifb_211 ifb[MAXIBPORT_211]; }; /************************************************************************/ struct perllc_211 { unsigned char id; float occupancy; count_t mbm_local; count_t mbm_total; }; struct llcstat_211 { int nrllcs; // total number of LLC struct perllc_211 perllc[MAXLLC_211]; }; /************************************************************************/ struct sstat_211 { struct cpustat_211 cpu; struct memstat_211 mem; struct netstat_211 net; struct intfstat_211 intf; struct memnuma_211 memnuma; struct cpunuma_211 cpunuma; struct dskstat_211 dsk; struct nfsstat_211 nfs; struct contstat_211 cfs; struct pressure_211 psi; struct gpustat_211 gpu; struct ifbstat_211 ifb; struct llcstat_211 llc; struct wwwstat_211 www; }; atop-2.11.1/prev/photosyst_200.h0000644000203100020310000001352414771753566015653 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.11.1/prev/photoproc_203.h0000644000203100020310000000715414771753566015621 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.11.1/prev/cgroups_211.h0000644000203100020310000000426214771753566015262 0ustar gerlofgerlof// structure containing general info and metrics per cgroup (directory) // struct cstat_211 { // GENERAL INFO struct cggen_211 { int structlen; // struct length including rounded name int sequence; // sequence number in chain/array int parentseq; // parent sequence number in chain/array int depth; // cgroup tree depth starting from 0 int nprocs; // number of processes in cgroup int procsbelow; // number of processes in cgroups below int namelen; // cgroup name length (at end of struct) int fullnamelen; // cgroup path length int ifuture[4]; long namehash; // cgroup name hash of // full path name excluding slashes long lfuture[4]; } gen; // CONFIGURATION INFO struct cgconf_211 { int cpuweight; // -1=max, -2=undefined int cpumax; // -1=max, -2=undefined (perc) count_t memmax; // -1=max, -2=undefined (pages) count_t swpmax; // -1=max, -2=undefined (pages) int dskweight; // -1=max, -2=undefined int ifuture[5]; count_t cfuture[5]; } conf; // CPU STATISTICS struct cgcpu_211 { count_t utime; // time user text (usec) -1=undefined count_t stime; // time system text (usec) -1=undefined count_t somepres; // some pressure (microsec) count_t fullpres; // full pressure (microsec) count_t cfuture[5]; } cpu; // MEMORY STATISTICS struct cgmem_211 { count_t current; // current memory (pages) -1=undefined count_t anon; // anonymous memory (pages) -1=undefined count_t file; // file memory (pages) -1=undefined count_t kernel; // kernel memory (pages) -1=undefined count_t shmem; // shared memory (pages) -1=undefined count_t somepres; // some pressure (microsec) count_t fullpres; // full pressure (microsec) count_t cfuture[5]; } mem; // DISK I/O STATISTICS struct cgdsk_211 { count_t rbytes; // total bytes read on all physical disks count_t wbytes; // total bytes written on all physical disks count_t rios; // total read I/Os on all physical disks count_t wios; // total write I/Os on all physical disks count_t somepres; // some pressure (microsec) count_t fullpres; // full pressure (microsec) count_t cfuture[5]; } dsk; // cgroup name with variable length char cgname[]; }; atop-2.11.1/prev/photoproc_204.h0000644000203100020310000001032614771753566015615 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.11.1/prev/photoproc_210.h0000644000203100020310000001174514771753566015620 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.11.1/prev/photoproc_200.h0000644000203100020310000000661114771753566015613 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.11.1/prev/photoproc_209.h0000644000203100020310000001150514771753566015622 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.11.1/prev/photoproc_208.h0000644000203100020310000001150514771753566015621 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.11.1/prev/photosyst_207.h0000644000203100020310000003222614771753566015662 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.11.1/prev/photosyst_202.h0000644000203100020310000002016514771753566015654 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.11.1/rawlog.c0000644000203100020310000006626714771753566013544 0ustar gerlofgerlof/* ** ATOP - System & Process Monitor ** ** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as process-level. ** ========================================================================== ** Author: Gerlof Langeveld ** E-mail: gerlof.langeveld@atoptool.nl ** Date: September 2002 ** Date: February 2024 (added cgroup support) ** -------------------------------------------------------------------------- ** Copyright (C) 2000-2024 Gerlof Langeveld ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ** -------------------------------------------------------------------------- */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "atop.h" #include "photoproc.h" #include "photosyst.h" #include "cgroups.h" #include "showgeneric.h" #include "rawlog.h" #define BASEPATH "/var/log/atop" #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 getrawcstat(int, struct cgchainer **, unsigned long, unsigned long, unsigned long, int, int); static int rawwopen(void); static int readchunk(int, void *, int); static int lookslikedatetome(char *); static void testcompval(int, char *); 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, struct cgchainer *devchain, int ncgroups, int npids, int nexit, unsigned int noverflow, char flag) { static int rawfd = -1; struct rawrecord rr; int rv; struct stat filestat; Byte scompbuf[sizeof(struct sstat)], *pcompbuf, *ccompbuf = NULL, *icompbuf = NULL; unsigned long soriglen = sizeof scompbuf, scomplen = compressBound(soriglen), poriglen, pcomplen, coriglen, ccomplen, ioriglen, icomplen; struct iovec iov[5]; int nrvectors; /* ** first call: ** take care that the log file is opened */ if (rawfd == -1) rawfd = rawwopen(); /* ** register current size of file in order to "roll back" ** writes that have been done while not *all* writes could ** succeed, e.g. when file system full */ (void) fstat(rawfd, &filestat); /* ** compress system level metrics */ rv = compress(scompbuf, &scomplen, (Byte *)sstat, soriglen); testcompval(rv, "compress system stats"); /* ** compress process level metrics */ poriglen = sizeof(struct tstat) * devtstat->ntaskall; pcomplen = compressBound(poriglen); pcompbuf = malloc(pcomplen); ptrverify(pcompbuf, "Malloc failed for process compression buffer\n"); rv = compress(pcompbuf, &pcomplen, (Byte *)devtstat->taskall, poriglen); testcompval(rv, "compress processes"); /* ** compress cgroup level metrics */ if (supportflags & CGROUPV2) { /* ** calculate the size of all contiguous cstat structs ** and compress */ coriglen = (char *)(devchain+ncgroups-1)->cstat - (char *) devchain->cstat + (devchain+ncgroups-1)->cstat->gen.structlen; ccomplen = compressBound(coriglen); ccompbuf = malloc(ccomplen); ptrverify(ccompbuf, "Malloc failed for cgroup compression buffer\n"); rv = compress(ccompbuf, &ccomplen, (Byte *)devchain->cstat, coriglen); testcompval(rv, "compress cgroups"); /* ** calculate the size of the cgroups pidlist ** and compress */ ioriglen = npids * sizeof(pid_t); icomplen = compressBound(ioriglen); icompbuf = malloc(icomplen); ptrverify(icompbuf, "Malloc failed for cgroup compression pidlist\n"); rv = compress(icompbuf, &icomplen, (Byte *)devchain->proclist, ioriglen); nrvectors = 5; } else { coriglen = ccomplen = 0; ioriglen = icomplen = 0; nrvectors = 3; } /* ** fill record header and write to file */ memset(&rr, 0, sizeof rr); rr.curtime = curtime; rr.interval = numsecs; rr.flags = 0; rr.ndeviat = devtstat->ntaskall; rr.nactproc = devtstat->nprocactive; rr.ntask = devtstat->ntaskall; rr.nexit = nexit; rr.noverflow = noverflow; rr.totproc = devtstat->nprocall; rr.totrun = devtstat->totrun; rr.totslpi = devtstat->totslpi; rr.totslpu = devtstat->totslpu; rr.totidle = devtstat->totidle; rr.totzomb = devtstat->totzombie; rr.ncgroups = ncgroups; rr.ncgpids = npids; rr.scomplen = scomplen; rr.pcomplen = pcomplen; rr.ccomplen = ccomplen; rr.coriglen = coriglen; rr.icomplen = icomplen; if (flag&RRBOOT) rr.flags |= RRBOOT; if (supportflags & ACCTACTIVE) rr.flags |= RRACCTACTIVE; if (supportflags & IOSTAT) rr.flags |= RRIOSTAT; if (supportflags & NETATOP) rr.flags |= RRNETATOP; if (supportflags & NETATOPD) rr.flags |= RRNETATOPD; if (supportflags & CGROUPV2) rr.flags |= RRCGRSTAT; if (supportflags & CONTAINERSTAT) rr.flags |= RRCONTAINERSTAT; if (supportflags & GPUSTAT) rr.flags |= RRGPUSTAT; /* ** use 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; iov[3].iov_base = ccompbuf; iov[3].iov_len = ccomplen; iov[4].iov_base = icompbuf; iov[4].iov_len = icomplen; if ( writev(rawfd, iov, nrvectors) == -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); if (supportflags & CGROUPV2) { free(ccompbuf); free(icompbuf); } return '\0'; } /* ** open a raw file for writing ** ** if the raw file exists already: ** - read and validate the header record (be sure it is an atop-file) ** - seek to the end of the file ** ** if the raw file does not yet exist: ** - create the raw file ** - write a header record ** ** return the filedescriptor of the raw file */ static int rawwopen() { struct rawheader rh; int fd; struct stat filestats; /* ** check if the file exists already */ if ( (fd = open(rawname, O_RDWR)) >= 0 ) { /* ** check if the file already contains a file header (and records) */ if (fstat(fd, &filestats) == 0 && filestats.st_size > 0) { /* ** read and verify raw file header */ if ( read(fd, &rh, sizeof rh) < sizeof rh) mcleanstop(7, "%s - cannot read header\n", 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.cstatlen != sizeof(struct cstat) || 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); } /* ** jump to end of file, being prepared to extend with more records */ (void) lseek(fd, (off_t) 0, SEEK_END); return fd; } } else { /* ** 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); } } /* ** empty file is opened now ** write a raw file header */ memset(&rh, 0, sizeof rh); rh.magic = MYMAGIC; rh.aversion = getnumvers() | 0x8000; rh.sstatlen = sizeof(struct sstat); rh.tstatlen = sizeof(struct tstat); rh.cstatlen = sizeof(struct cstat); rh.rawheadlen = sizeof(struct rawheader); rh.rawreclen = sizeof(struct rawrecord); rh.supportflags = supportflags | RAWLOGNG; rh.osrel = osrel; rh.osvers = osvers; rh.ossub = ossub; rh.hertz = hertz; rh.pagesize = pagesize; rh.pidwidth = getpidwidth(); memcpy(&rh.utsname, &utsname, sizeof rh.utsname); if ( write(fd, &rh, sizeof rh) == -1) { fprintf(stderr, "%s - ", rawname); perror("write raw header"); cleanstop(7); } return fd; } /* ** read the contents of a raw file */ #define OFFCHUNK 256 int rawread(void) { static struct devtstat devtstat; int i, j, rawfd, len, isregular = 1; char *py; struct rawheader rh; struct rawrecord rr; struct sstat sstat; struct cgchainer *devchain = NULL; struct stat filestat; /* ** variables to maintain the offsets of the raw records ** to be able to see previous samples again */ off_t *offlist = NULL; unsigned int offsize = 0; unsigned int offcur = 0; char lastcmd = 'X', flags; time_t timenow; struct tm *tp; switch ( len = strlen(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.cstatlen != sizeof(struct cstat) || rh.rawheadlen != sizeof(struct rawheader) || rh.rawreclen != sizeof(struct rawrecord) ) { fprintf(stderr, "sstatlen: %d/%lu\n", rh.sstatlen, sizeof(struct sstat)); fprintf(stderr, "cstatlen: %d/%lu\n", rh.cstatlen, sizeof(struct cstat)); fprintf(stderr, "tstatlen: %d/%lu\n", rh.tstatlen, sizeof(struct tstat)); fprintf(stderr, "headlen: %d/%lu\n", rh.rawheadlen, sizeof(struct rawheader)); fprintf(stderr, "reclen: %d/%lu\n", rh.rawreclen, sizeof(struct rawrecord)); fprintf(stderr, "\nraw file %s has incompatible format\n", 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+rr.ccomplen+rr.icomplen, SEEK_CUR); if ((curr_pos >> READAHEADOFF) != (next_pos >> READAHEADOFF)) { int liResult; /* just read READAHEADSIZE bytes into page cache */ char *buf = malloc(READAHEADSIZE); ptrverify(buf, "Malloc failed for readahead"); liResult = pread(rawfd, buf, READAHEADSIZE, next_pos & ~(READAHEADSIZE - 1)); if(liResult == -1) { char lcMessage[64]; snprintf(lcMessage, sizeof(lcMessage) - 1, "%s:%d - Error %d in readahead\n", __FILE__, __LINE__, errno); fprintf(stderr, "%s", lcMessage); } free(buf); } curr_pos = next_pos; continue; } else // named pipe not seekable { char *dummybuf = malloc(rr.scomplen+rr.pcomplen+rr.ccomplen+rr.icomplen); ptrverify(dummybuf, "Malloc rawlog pipe buffer failed\n"); readchunk(rawfd, dummybuf, rr.scomplen+rr.pcomplen+rr.ccomplen+rr.icomplen); free(dummybuf); } continue; } begintime = 0; // allow earlier times from now on if ( (endtime && endtime < cursortime) ) { if (isregular) free(offlist); close(rawfd); return isregular; } /* ** allocate space, read compressed system-level ** metrics and decompress */ if ( !getrawsstat(rawfd, &sstat, rr.scomplen) ) cleanstop(7); /* ** allocate space, read compressed process-level ** metrics and decompress */ devtstat.taskall = malloc(sizeof(struct tstat) * rr.ndeviat); if (rr.totproc < rr.nactproc) // compat old raw files devtstat.procall = malloc(sizeof(struct tstat *) * rr.nactproc); else devtstat.procall = malloc(sizeof(struct tstat *) * rr.totproc); devtstat.procactive = malloc(sizeof(struct tstat *) * rr.nactproc); ptrverify(devtstat.taskall, "Malloc failed for %d stored tasks\n", rr.ndeviat); ptrverify(devtstat.procall, "Malloc failed for total %d processes\n", rr.totproc); ptrverify(devtstat.procactive, "Malloc failed for %d active processes\n", rr.nactproc); if ( !getrawtstat(rawfd, devtstat.taskall, rr.pcomplen, rr.ndeviat) ) cleanstop(7); for (i=j=k=l=0; i < rr.ndeviat; i++) { if ( (devtstat.taskall+i)->gen.isproc) { devtstat.procall[j++] = devtstat.taskall+i; if (! (devtstat.taskall+i)->gen.wasinactive) devtstat.procactive[k++] = devtstat.taskall+i; } if (! (devtstat.taskall+i)->gen.wasinactive) l++; } devtstat.ntaskall = i; devtstat.nprocall = j; devtstat.nprocactive = k; devtstat.ntaskactive = l; devtstat.totrun = rr.totrun; devtstat.totslpi = rr.totslpi; devtstat.totslpu = rr.totslpu; devtstat.totidle = rr.totidle; devtstat.totzombie = rr.totzomb; /* ** allocate space, read compressed cgroup-level ** metrics, the pidlist and decompress */ if (rr.flags & RRCGRSTAT) { if ( !getrawcstat(rawfd, &devchain, rr.ccomplen, rr.coriglen, rr.icomplen, rr.ncgroups, rr.ncgpids) ) cleanstop(7); } /* ** activate the installed print function to visualize ** the system metrics, process metrics and cgroup metrics */ sampcnt++; if ( (rh.supportflags & RAWLOGNG) == RAWLOGNG) { if (rr.flags & RRACCTACTIVE) supportflags |= ACCTACTIVE; else supportflags &= ~ACCTACTIVE; if (rr.flags & RRIOSTAT) supportflags |= IOSTAT; else supportflags &= ~IOSTAT; } if (rr.flags & RRNETATOP) supportflags |= NETATOP; else supportflags &= ~NETATOP; if (rr.flags & RRNETATOPD) supportflags |= NETATOPD; else supportflags &= ~NETATOPD; if (rr.flags & RRCGRSTAT) supportflags |= CGROUPV2; else supportflags &= ~CGROUPV2; if (rr.flags & RRCONTAINERSTAT) supportflags |= CONTAINERSTAT; else supportflags &= ~CONTAINERSTAT; if (rr.flags & RRGPUSTAT) supportflags |= GPUSTAT; else supportflags &= ~GPUSTAT; flags = rr.flags & RRBOOT; nrgpus = sstat.gpu.nrgpus; if (isregular) { (void) fstat(rawfd, &filestat); if ( filestat.st_size - lseek(rawfd, (off_t)0, SEEK_CUR) <= rh.rawreclen) flags |= RRLAST; } do { lastcmd = (vis.show_samp)(rr.curtime, rr.interval, &devtstat, &sstat, devchain, rr.ncgroups, rr.ncgpids, rr.nexit, rr.noverflow, flags); } while (!isregular && ( lastcmd == MSAMPPREV || lastcmd == MRESET || (lastcmd == MSAMPBRANCH && begintime < cursortime) )); free(devtstat.taskall); free(devtstat.procall); free(devtstat.procactive); if (rr.flags & RRCGRSTAT) { free(devchain->cstat); free(devchain->proclist); free(devchain); } switch (lastcmd) { case MSAMPPREV: if (offcur >= 2) offcur -= 2; else offcur = 0; lseek(rawfd, *(offlist+offcur), SEEK_SET); break; case MRESET: lseek(rawfd, *offlist, SEEK_SET); offcur = 1; break; case 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; } /* ** read the cgroup-level statistics and pidlist from the current offset */ static int getrawcstat(int rawfd, struct cgchainer **cpp, unsigned long ccomplen, unsigned long coriglen, unsigned long icomplen, int ncgroups, int npids) { Byte *ccompbuf, *corigbuf, *icompbuf, *iorigbuf; int rv; unsigned long ioriglen = npids * sizeof(pid_t); /* ** read all cstat structs */ ccompbuf = malloc(ccomplen); corigbuf = malloc(coriglen); ptrverify(ccompbuf, "Malloc failed for reading compressed cgroups\n"); ptrverify(corigbuf, "Malloc failed for decompressing cgroups\n"); if ( readchunk(rawfd, ccompbuf, ccomplen) < ccomplen) { free(ccompbuf); free(corigbuf); return 0; } rv = uncompress((Byte *)corigbuf, &coriglen, ccompbuf, ccomplen); testcompval(rv, "uncompress cgroups"); free(ccompbuf); /* ** read pidlist */ icompbuf = malloc(icomplen); iorigbuf = malloc(ioriglen); ptrverify(icompbuf, "Malloc failed for reading pidlist\n"); ptrverify(iorigbuf, "Malloc failed for decompresssed pidlist\n"); if ( readchunk(rawfd, icompbuf, icomplen) < icomplen) { free(corigbuf); free(icompbuf); free(iorigbuf); return 0; } rv = uncompress((Byte *)iorigbuf, &ioriglen, icompbuf, icomplen); testcompval(rv, "uncompress cgroups pidlist"); free(icompbuf); /* ** reconstruct an array of cgchainer structs from which ** each entry refers to one cstat struct and its own start ** entry in the pidlist */ cgbuildarray(cpp, (char *)corigbuf, (char *)iorigbuf, ncgroups); return 1; } /* ** verify if a particular ascii-string is in the format yyyymmdd */ static int lookslikedatetome(char *p) { register int i; for (i=0; i < 8; i++) if ( !isdigit(*(p+i)) ) return 0; if (*p != '2') return 0; /* adapt this in the year 3000 */ if ( *(p+4) > '1') return 0; if ( *(p+6) > '3') return 0; return 1; /* yes, looks like a date to me */ } 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\n", tmpbuf); fprintf(stderr, "use 'atopconvert' to convert this raw log!\n"); } atop-2.11.1/atopconvert.c0000644000203100020310000014514314771753566014604 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 "cgroups.h" #include "rawlog.h" #include "prev/netstats_wrong.h" #include "prev/photosyst_200.h" #include "prev/photoproc_200.h" #include "prev/photosyst_201.h" #include "prev/photoproc_201.h" #include "prev/photosyst_202.h" #include "prev/photoproc_202.h" #include "prev/photosyst_203.h" #include "prev/photoproc_203.h" #include "prev/photosyst_204.h" #include "prev/photoproc_204.h" #include "prev/photosyst_205.h" #include "prev/photoproc_205.h" #include "prev/photosyst_206.h" #include "prev/photoproc_206.h" #include "prev/photosyst_207.h" #include "prev/photoproc_207.h" #include "prev/photosyst_208.h" #include "prev/photoproc_208.h" #include "prev/photosyst_209.h" #include "prev/photoproc_209.h" #include "prev/photosyst_210.h" #include "prev/photoproc_210.h" #include "prev/photosyst_211.h" #include "prev/photoproc_211.h" #include "prev/cgroups_211.h" void justcopy(void *, void *, count_t, count_t); void scpu_to_21(void *, void *, count_t, count_t); void sdsk_to_21(void *, void *, count_t, count_t); void sint_to_22(void *, void *, count_t, count_t); void scpu_to_27(void *, void *, count_t, count_t); void smem_to_27(void *, void *, count_t, count_t); void sdsk_to_27(void *, void *, count_t, count_t); void smem_to_28(void *, void *, count_t, count_t); void snet_to_28(void *, void *, count_t, count_t); void sdsk_to_28(void *, void *, count_t, count_t); void smnu_to_28(void *, void *, count_t, count_t); void scnu_to_28(void *, void *, count_t, count_t); void sllc_to_210(void *, void *, count_t, count_t); void tgen_to_21(void *, void *, count_t, count_t); void tmem_to_21(void *, void *, count_t, count_t); void tgen_to_22(void *, void *, count_t, count_t); void tcpu_to_26(void *, void *, count_t, count_t); void tmem_to_26(void *, void *, count_t, count_t); void tcpu_to_28(void *, void *, count_t, count_t); void tmem_to_28(void *, void *, count_t, count_t); void tgen_to_210(void *, void *, count_t, count_t); void tgen_to_211(void *, void *, count_t, count_t); void tcpu_to_211(void *, void *, count_t, count_t); void tmem_to_211(void *, void *, count_t, count_t); /////////////////////////////////////////////////////////////// // 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 snet_to_28(void *old, void *new, count_t oldsize, count_t newsize) { struct netstat_wrong *n27 = old; struct netstat *n28 = new; // copy unmodified structs // memcpy(&(n28->ipv4), &(n27->ipv4), sizeof n28->ipv4); memcpy(&(n28->ipv6), &(n27->ipv6), sizeof n28->ipv6); memcpy(&(n28->udpv4), &(n27->udpv4), sizeof n28->udpv4); memcpy(&(n28->udpv6), &(n27->udpv6), sizeof n28->udpv6); memcpy(&(n28->icmpv6), &(n27->icmpv6), sizeof n28->icmpv6); // convert tcp_stats (last counter added) // memcpy(&(n28->tcp), &(n27->tcp), sizeof n27->tcp); n28->tcp.InCsumErrors = 0; // convert icmpv4_stats (counter added in the middle) // n28->icmpv4.InMsgs = n27->icmpv4.InMsgs; n28->icmpv4.InErrors = n27->icmpv4.InErrors; n28->icmpv4.InCsumErrors = 0; // new counter n28->icmpv4.InDestUnreachs = n27->icmpv4.InDestUnreachs; n28->icmpv4.InTimeExcds = n27->icmpv4.InTimeExcds; n28->icmpv4.InParmProbs = n27->icmpv4.InParmProbs; n28->icmpv4.InSrcQuenchs = n27->icmpv4.InSrcQuenchs; n28->icmpv4.InRedirects = n27->icmpv4.InRedirects; n28->icmpv4.InEchos = n27->icmpv4.InEchos; n28->icmpv4.InEchoReps = n27->icmpv4.InEchoReps; n28->icmpv4.InTimestamps = n27->icmpv4.InTimestamps; n28->icmpv4.InTimestampReps = n27->icmpv4.InTimestampReps; n28->icmpv4.InAddrMasks = n27->icmpv4.InAddrMasks; n28->icmpv4.InAddrMaskReps = n27->icmpv4.InAddrMaskReps; n28->icmpv4.OutMsgs = n27->icmpv4.OutMsgs; n28->icmpv4.OutErrors = n27->icmpv4.OutErrors; n28->icmpv4.OutDestUnreachs = n27->icmpv4.OutDestUnreachs; n28->icmpv4.OutTimeExcds = n27->icmpv4.OutTimeExcds; n28->icmpv4.OutParmProbs = n27->icmpv4.OutParmProbs; n28->icmpv4.OutSrcQuenchs = n27->icmpv4.OutSrcQuenchs; n28->icmpv4.OutRedirects = n27->icmpv4.OutRedirects; n28->icmpv4.OutEchos = n27->icmpv4.OutEchos; n28->icmpv4.OutEchoReps = n27->icmpv4.OutEchoReps; n28->icmpv4.OutTimestamps = n27->icmpv4.OutTimestamps; n28->icmpv4.OutTimestampReps = n27->icmpv4.OutTimestampReps; n28->icmpv4.OutAddrMasks = n27->icmpv4.OutAddrMasks; n28->icmpv4.OutAddrMaskReps = n27->icmpv4.OutAddrMaskReps; } void sdsk_to_28(void *old, void *new, count_t oldsize, count_t newsize) { struct dskstat_27 *d27 = old; struct dskstat_28 *d28 = new; int i; d28->ndsk = d27->ndsk; d28->nmdd = d27->nmdd; d28->nlvm = d27->nlvm; for (i=0; i < d28->ndsk; i++) memcpy(&(d28->dsk[i]), &(d27->dsk[i]), sizeof d27->dsk[i]); for (i=0; i < d28->nmdd; i++) memcpy(&(d28->mdd[i]), &(d27->mdd[i]), sizeof d27->mdd[i]); for (i=0; i < d28->nlvm; i++) memcpy(&(d28->lvm[i]), &(d27->lvm[i]), sizeof d27->lvm[i]); } void smnu_to_28(void *old, void *new, count_t oldsize, count_t newsize) { struct memnuma_27 *n27 = old; struct memnuma_28 *n28 = new; int i; n28->nrnuma = n27->nrnuma; for (i=0; i < n28->nrnuma; i++) { n28->numa[i].numanr = i; n28->numa[i].frag = n27->numa[i].frag; n28->numa[i].totmem = n27->numa[i].totmem; n28->numa[i].freemem = n27->numa[i].freemem; n28->numa[i].filepage = n27->numa[i].filepage; n28->numa[i].dirtymem = n27->numa[i].dirtymem; n28->numa[i].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)); } void tgen_to_211(void *old, void *new, count_t oldsize, count_t newsize) { // member 'cgpath' removed, member cgroupix and ifuture instead // struct gen_210 *g210 = old; struct gen_211 *g211 = new; memcpy(g211, g210, sizeof *g211); // copy base values memset(g211->ifuture, 0, sizeof g211->ifuture); g211->cgroupix = -1; } void tcpu_to_211(void *old, void *new, count_t oldsize, count_t newsize) { // set future values (released cgroup values) to zero // struct cpu_210 *c210 = old; struct cpu_211 *c211 = new; memcpy(c211, c210, sizeof *c211); // copy base values memset(c211->ifuture, 0, sizeof c211->ifuture); } void tmem_to_211(void *old, void *new, count_t oldsize, count_t newsize) { // set future values (released cgroup values) to zero // struct mem_210 *m210 = old; struct mem_211 *m211 = new; memcpy(m211, m210, sizeof *m211); // copy base values memset(m211->cfuture, 0, sizeof m211->cfuture); } /////////////////////////////////////////////////////////////// // 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_211 sstat_211; struct sstat sstat; struct cstat cstat; struct tstat_20 tstat_20; struct tstat_21 tstat_21; struct tstat_22 tstat_22; struct tstat_23 tstat_23; struct tstat_24 tstat_24; struct tstat_25 tstat_25; struct tstat_26 tstat_26; struct tstat_27 tstat_27; struct tstat_28 tstat_28; struct tstat_29 tstat_29; struct tstat_210 tstat_210; struct tstat_211 tstat_211; struct tstat tstat; struct convertall { int version; // version of raw log unsigned int sstatlen; // length of struct sstat void *sstat; // pointer to sstat struct unsigned int cstatlen; // length of struct cstat (>= 2.11) void *cstat; // pointer to cstat 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 cstat // relevant from version 2.12 onwards // 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, 0, NULL, 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, 0, NULL, 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, 0, NULL, 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, 0, NULL, 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, 0, NULL, 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, 0, NULL, 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, 0, NULL, 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, 0, NULL, 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, 0, NULL, 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, snet_to_28}, {sizeof(struct intfstat_28), &sstat_28.intf, justcopy}, {sizeof(struct dskstat_28), &sstat_28.dsk, sdsk_to_28}, {sizeof(struct nfsstat_28), &sstat_28.nfs, justcopy}, {sizeof(struct contstat_28), &sstat_28.cfs, justcopy}, {sizeof(struct wwwstat_28), &sstat_28.www, justcopy}, {sizeof(struct pressure_28), &sstat_28.psi, justcopy}, {sizeof(struct gpustat_28), &sstat_28.gpu, justcopy}, {sizeof(struct ifbstat_28), &sstat_28.ifb, justcopy}, {sizeof(struct memnuma_28), &sstat_28.memnuma, smnu_to_28}, {sizeof(struct cpunuma_28), &sstat_28.cpunuma, scnu_to_28}, {0, &sstat_28.llc, NULL}, {sizeof(struct gen_28), STROFFSET(&tstat_28.gen, &tstat_28), justcopy}, {sizeof(struct cpu_28), STROFFSET(&tstat_28.cpu, &tstat_28), tcpu_to_28}, {sizeof(struct dsk_28), STROFFSET(&tstat_28.dsk, &tstat_28), justcopy}, {sizeof(struct mem_28), STROFFSET(&tstat_28.mem, &tstat_28), tmem_to_28}, {sizeof(struct net_28), STROFFSET(&tstat_28.net, &tstat_28), justcopy}, {sizeof(struct gpu_28), STROFFSET(&tstat_28.gpu, &tstat_28), justcopy}, }, {SETVERSION(2,9), // 2.8 --> 2.9 sizeof(struct sstat_29), &sstat_29, 0, NULL, 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, 0, NULL, 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}, }, {SETVERSION(2,11), // 2.10 --> 2.11 sizeof(struct sstat_211), &sstat_211, sizeof(struct cstat_211), NULL, sizeof(struct tstat_211), NULL, {sizeof(struct cpustat_211), &sstat_211.cpu, justcopy}, {sizeof(struct memstat_211), &sstat_211.mem, justcopy}, {sizeof(struct netstat_211), &sstat_211.net, justcopy}, {sizeof(struct intfstat_211), &sstat_211.intf, justcopy}, {sizeof(struct dskstat_211), &sstat_211.dsk, justcopy}, {sizeof(struct nfsstat_211), &sstat_211.nfs, justcopy}, {sizeof(struct contstat_211), &sstat_211.cfs, justcopy}, {sizeof(struct wwwstat_211), &sstat_211.www, justcopy}, {sizeof(struct pressure_211), &sstat_211.psi, justcopy}, {sizeof(struct gpustat_211), &sstat_211.gpu, justcopy}, {sizeof(struct ifbstat_211), &sstat_211.ifb, justcopy}, {sizeof(struct memnuma_211), &sstat_211.memnuma, justcopy}, {sizeof(struct cpunuma_211), &sstat_211.cpunuma, justcopy}, {sizeof(struct llcstat_211), &sstat_211.llc, justcopy}, {sizeof(struct gen_211), STROFFSET(&tstat_211.gen, &tstat_211), tgen_to_211}, {sizeof(struct cpu_211), STROFFSET(&tstat_211.cpu, &tstat_211), tcpu_to_211}, {sizeof(struct dsk_211), STROFFSET(&tstat_211.dsk, &tstat_211), justcopy}, {sizeof(struct mem_211), STROFFSET(&tstat_211.mem, &tstat_211), tmem_to_211}, {sizeof(struct net_211), STROFFSET(&tstat_211.net, &tstat_211), justcopy}, {sizeof(struct gpu_211), STROFFSET(&tstat_211.gpu, &tstat_211), justcopy}, }, }; 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 *, unsigned long, int, int); static void testcompval(int, char *, char *); int main(int argc, char *argv[]) { int ifd, ofd; struct rawheader irh, orh; int i, versionix, targetix = -1; 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.cstatlen = convs[targetix].cstatlen; 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, convs[ivix].tstatlen * irr.ndeviat, 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); // in version 2.11 incompatible cgroups v2 metrics // are implemented and earlier metrics will be lost // if (convs[i].version == SETVERSION(2,10)) irr.flags &= ~RRCGRSTAT; } // 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, "sstat", "uncompress"); free(compbuf); return 1; } // // Function to read the process-level statistics from the current offset // static int getrawtstat(int rawfd, struct tstat *pp, unsigned long uncomplen, int complen, int ndeviat) { Byte *compbuf; int rv; compbuf = malloc(complen); ptrverify(compbuf, "Malloc failed for reading compressed procstats\n"); if ( read(rawfd, compbuf, complen) < complen) { free(compbuf); fprintf(stderr, "Failed to read %d bytes for tasks\n", complen); return 0; } rv = uncompress((Byte *)pp, &uncomplen, compbuf, complen); testcompval(rv, "tstat", "uncompress"); free(compbuf); 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, "sstat", "compress"); pcompbuf = malloc(pcomplen); ptrverify(pcompbuf, "Malloc failed for compression buffer\n"); rv = compress(pcompbuf, &pcomplen, (Byte *)tstat, (unsigned long)pcomplen); testcompval(rv, "tstat", "compress"); rr->scomplen = scomplen; rr->pcomplen = pcomplen; 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 *name, char *func) { switch (rv) { case Z_OK: case Z_STREAM_END: case Z_NEED_DICT: break; case Z_MEM_ERROR: fprintf(stderr, "%s %s: failed due to lack of memory\n", name, func); exit(7); case Z_BUF_ERROR: fprintf(stderr, "%s %s: failed due to lack of room in buffer\n", name, func); exit(7); case Z_DATA_ERROR: fprintf(stderr, "%s %s: failed due to corrupted/incomplete data\n", name, func); exit(7); default: fprintf(stderr, "%s %s: unexpected error %d\n", name, func, rv); exit(7); } } // // generic pointer verification after malloc // void ptrverify(const void *ptr, const char *errormsg, ...) { if (!ptr) { va_list args; va_start(args, errormsg); vfprintf(stderr, errormsg, args); va_end (args); exit(13); } } /* ** return maximum number of digits for PID/TID ** from the current system */ int getpidwidth(void) { FILE *fp; char linebuf[64]; int numdigits = 5; if ( (fp = fopen("/proc/sys/kernel/pid_max", "r")) != NULL) { if ( fgets(linebuf, sizeof(linebuf), fp) != NULL) { numdigits = strlen(linebuf) - 1; } fclose(fp); } return numdigits; } atop-2.11.1/parseable.h0000644000203100020310000000266114771753566014200 0ustar gerlofgerlof/* ** ATOP - System & Process Monitor ** ** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as process-level. ** ========================================================================== ** Author: Gerlof Langeveld ** E-mail: gerlof.langeveld@atoptool.nl ** Date: September 2002 ** -------------------------------------------------------------------------- ** Copyright (C) 2000-2010 Gerlof Langeveld ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ** -------------------------------------------------------------------------- */ #ifndef __PARSEABLE__ #define __PARSEABLE__ int parsedef(char *); char parseout(time_t, int, struct devtstat *, struct sstat *, struct cgchainer *, int, int, int, unsigned int, char); #endif atop-2.11.1/acctproc.h0000644000203100020310000001262314771753566014037 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.11.1/drawbar.c0000644000203100020310000021143314771753566013656 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 #include #include #include "atop.h" #include "photoproc.h" #include "showgeneric.h" #include "photosyst.h" extern char usecolors; static char winchange; static char keywaiting; // key pushed back in stream? // maximum X axis label length // #define MAXLABLEN 7 // number of columns in memory management bars // #define MEMORYBARSZ 11 #define SWAPBARSZ 8 #define EVENTBARSZ 10 // four windows are created to show graphs // // - processor stats (left upper) // - memory stats (right upper or right full) // - disk stats (left lower) // - interface stats (right lower) // // metadata for each of these windows: struct perwindow // struct perwindow { WINDOW *win; int nlines, ncols; }; static struct perwindow wincpu, winmem, windsk, winnet; // additional windows for the header line and for line drawing // static WINDOW *headwin, *midline, *colupper, *collower; // struct for calling drawvertbars() // #define MAXCAT 5 // maximum number of categories within one bar #define MAXHEIGHT 25 // maximum bar height in lines struct vertval { int barval; // total value of bar char *barlab; // bar label char basecolor; // bar color or fill color char barmap[MAXHEIGHT]; // color map char barchr[MAXHEIGHT]; // character map int numcat; // number of categories in bar struct { int cval; // per-category value int ccol; // per-category color char clab; // per-category label } category[MAXCAT]; }; // struct for calling drawnetbars() // struct netval { count_t pvals; // send bytes-per-second count_t pvalr; // recv bytes-per-second int speed; // speed in Mbits/sec int maxmbits; // bar scale char *barlab; // bar label }; // header message // static char *headmsg = "Press '?' for help"; // function prototypes // static int headergetch(time_t, int, char *, int); static int wininit(struct sstat *); static void winexit(void); static void showhelp(void); static int getwininput(char *, char *, int, char); static void colorswon(WINDOW *, int); static void colorswoff(WINDOW *, int); static int severitycolor(char); static char setseverity(long, long, long); static float getwinratio(struct sstat *, char *); static void do_cpubars(struct sstat *, int, char, char); static void do_dskbars(struct sstat *, int, char, char); static void do_netbars(struct sstat *, int, char, char); static void sortvertbars(int, int, struct vertval **); static int compvertval(const void *, const void *); static void sortnetbars(int, struct netval **); static int compnetval(const void *, const void *); static void fillbarmaps(int, struct vertval *, float, int); static int drawvertbars(struct perwindow *, float, float, int, int, struct vertval *, int, char *,char *, int); static int drawnetbars(struct perwindow *, int, struct netval *, int, char *,char *); static int drawmemory(struct perwindow *w, struct sstat *, int, time_t, char); static int drawmemlines(struct perwindow *, int, int, int, int, int, char *, char *); static int drawevent(struct perwindow *, int, int, int, char *, char *, long); static void getsigwinch(int); ///////////////////////////////////////////////////// // entry point to display the deviation counters // on system level in bar graph mode ///////////////////////////////////////////////////// char draw_samp(time_t curtime, int nsecs, struct sstat *sstat, char flag, char sorted) { static char winitialized, wassorted, initlabels, swapinuse, ttyrescaled; static time_t lasttime; static int nrdisk, nrallintf, nrintf; int lastchar, i, newinterval, statuscol; char *statusmsg = NULL, buf[32], lower=0, answer[16]; // when needed (re)initialize the windows for bar graphs // if (!winitialized || nrdisk != sstat->dsk.ndsk || nrallintf != sstat->intf.nrintf ) { // determine the number of disks and network interfaces // only the physical disks and network interfaces apply // nrdisk = sstat->dsk.ndsk; nrallintf = sstat->intf.nrintf; for (i=0, nrintf=0; i < sstat->intf.nrintf; i++) { if (sstat->intf.intf[i].type != 'v') nrintf++; } // initialize graph windows // swapinuse = sstat->mem.totswap ? 1 : 0; if (winitialized) // already initialized -> first remove winexit(); wininit(sstat); winitialized = 1; } // verify if situation related to swap usage has been // changed (in that case the memory window has // to be redefined) // if (( sstat->mem.totswap && !swapinuse) || (!sstat->mem.totswap && swapinuse) ) { swapinuse = sstat->mem.totswap ? 1 : 0; winexit(); wininit(sstat); } // main loop: // - draw bar graphs // - wait for keystroke or alarm expiration // while (1) { initlabels = sorted != wassorted; if (!initlabels && lasttime != curtime && sorted) initlabels = 1; // show processor graph // do_cpubars(sstat, nsecs, initlabels, barmono); // show disk graph // do_dskbars(sstat, nsecs, initlabels, barmono); // show network (interfaces) graph // do_netbars(sstat, nsecs, initlabels, lower); // show memory graph // drawmemory(&winmem, sstat, nsecs, curtime, flag); // reset label initialization // initlabels = 0; wassorted = sorted; // verify if status message is required // statusmsg = NULL; statuscol = FGCOLORBORDER; if (flag&RRBOOT) statusmsg = "SINCE BOOT"; if (paused) // might overrule other message statusmsg = "PAUSED"; if (ttyrescaled) { snprintf(buf, sizeof buf, " %dx%d ", LINES, COLS); statusmsg = buf; ttyrescaled = 0; // show critical color when getting close to minimum // screen width // if (COLS < MINCOLUMNS+5 || LINES < MINLINES+5) statuscol = FGCOLORCRIT; } // wait for keystroke, alarm expiration or inotify watcher // switch (lastchar = headergetch(curtime, nsecs, statusmsg, statuscol)) { case ERR: // alarm expired? case 0: return lastchar; case MCGROUPS: case MPROCGEN: case MPROCMEM: case MPROCDSK: case MPROCNET: case MPROCGPU: case MPROCSCH: case MPROCVAR: case MPROCARG: case MBARGRAPH: winexit(); // close windows winitialized = 0; return lastchar; // switch to text mode case KEY_RESIZE: // terminal window resize? winexit(); // when window gets too small for bar graph mode, // switch to text mode // if (COLS < MINCOLUMNS || LINES < MINLINES) { winitialized = 0; return MBARGRAPH; } // terminal window size still fine: // reinitialize all ncurses windows // wininit(sstat); ttyrescaled = 1; break; // redraw with current values case MSAMPNEXT: // manual trigger for next sample? if (paused && !twinpid) break; getalarm(0); return lastchar; case MSAMPPREV: // manual trigger for previous sample? if (!rawreadflag) { beep(); break; } if (!paused && twinpid) { paused=1; // implicit pause in twin mode clrtoeol(); refresh(); } getalarm(0); return lastchar; case MRESET: // reset to begin? getalarm(0); paused = 0; if (twinpid) { paused=1; // implicit pause in twin mode clrtoeol(); refresh(); } return lastchar; case MSAMPBRANCH: // branch to other time? // only possible in twin mode or when viewing raw file // if (!rawreadflag) { beep(); break; } if (!paused && twinpid) { paused=1; // implicit pause in twin mode clrtoeol(); refresh(); } if (getwininput("Enter new time (format [YYYYMMDD]hhmm): ", answer, sizeof answer, 1) >= 0) { begintime = cursortime; if ( !getbranchtime(answer, &begintime) ) { beep(); begintime = 0; break; // branch failed } return lastchar; } break; case MBARLOWER: // reset network scale? lower = 1; break; case MBARMONO: // categorized busy bars? if (barmono) barmono = 0; else barmono = 1; break; case MPAUSE: // pause key (toggle)? if (rawreadflag && !twinpid) { beep(); break; } if (paused) { paused=0; if (!rawreadflag) { alarm(1); } else { begintime = 0x7fffffff; return MSAMPBRANCH; } } else { paused=1; alarm(0); // stop the clock } break; case MINTERVAL: // modify interval? // not possible in twin mode or when viewing raw file // if (rawreadflag) { beep(); break; } alarm(0); // stop the clock if ( (newinterval = getwininput("Interval: ", answer, sizeof answer, 1)) >= 0) interval = newinterval; if (!paused) alarm(1); // set short timer break; case MQUIT: // quit entirely? winexit(); move(LINES-1, 0); clrtoeol(); refresh(); cleanstop(0); case MHELP1: // help wanted? case MHELP2: alarm(0); // stop the clock // show help lines // showhelp(); // reinitialize original windows // winexit(); wininit(sstat); if (interval && !paused && !rawreadflag) alarm(1); // force new sample if (twinpid) // twin mode? { // jump to last sample after awaiting input begintime = 0x7fffffff; return MSAMPBRANCH; } break; default: // any other key pressed? break; // ignore } } } ///////////////////////////////////////////////////// // prepare the CPU specific bar graph ///////////////////////////////////////////////////// static void do_cpubars(struct sstat *sstat, int nsecs, char initlabels, char mono) { static int labellen, numcpus, numlabs; static char *labarea, *p; static struct vertval *vertvals; count_t alltics; int i; 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 = COLORCPUINTR; vertvals->category[0].clab = 'I'; vertvals->category[0].cval = (sstat->cpu.all.Stime + sstat->cpu.all.Itime) * 100 / alltics; vertvals->category[1].ccol = COLORCPUSYS; vertvals->category[1].clab = 'S'; vertvals->category[1].cval = sstat->cpu.all.stime * 100 / alltics; vertvals->category[2].ccol = COLORCPUUSR; vertvals->category[2].clab = 'U'; vertvals->category[2].cval = (sstat->cpu.all.utime + sstat->cpu.all.ntime - sstat->cpu.all.guest) * 100 / alltics; vertvals->category[3].ccol = COLORCPUSTEAL; vertvals->category[3].clab = 's'; vertvals->category[3].cval = sstat->cpu.all.steal * 100 / alltics; vertvals->category[4].ccol = COLORCPUGUEST; vertvals->category[4].clab = 'G'; vertvals->category[4].cval = sstat->cpu.all.guest * 100 / alltics; vertvals->numcat = 5; } else { vertvals->numcat = 0; } // if more than one CPU: calculate per CPU // if (numcpus > 1) { // total ticks during last interval for CPU 0 // alltics = sstat->cpu.cpu[0].stime + sstat->cpu.cpu[0].utime + sstat->cpu.cpu[0].ntime + sstat->cpu.cpu[0].itime + sstat->cpu.cpu[0].wtime + sstat->cpu.cpu[0].Itime + sstat->cpu.cpu[0].Stime + sstat->cpu.cpu[0].steal; // busy percentage per CPU // for (i=0; i < numcpus; i++) { (vertvals+i+1)->barval = 100 - (sstat->cpu.cpu[i].itime + sstat->cpu.cpu[i].wtime ) *100/alltics; if ((vertvals+i+1)->barval < 0) (vertvals+i+1)->barval = 0; (vertvals+i+1)->basecolor = WHITE_BLUE0; if (!mono) { (vertvals+i+1)->category[0].ccol = COLORCPUINTR; (vertvals+i+1)->category[0].clab = 'I'; (vertvals+i+1)->category[0].cval = (sstat->cpu.cpu[i].Stime + sstat->cpu.cpu[i].Itime) *100/alltics; (vertvals+i+1)->category[1].ccol = COLORCPUSYS; (vertvals+i+1)->category[1].clab = 'S'; (vertvals+i+1)->category[1].cval = sstat->cpu.cpu[i].stime * 100 / alltics; (vertvals+i+1)->category[2].ccol = COLORCPUUSR; (vertvals+i+1)->category[2].clab = 'U'; (vertvals+i+1)->category[2].cval = (sstat->cpu.cpu[i].utime + sstat->cpu.cpu[i].ntime - sstat->cpu.cpu[i].guest) *100/alltics; (vertvals+i+1)->category[3].ccol = COLORCPUSTEAL; (vertvals+i+1)->category[3].clab = 's'; (vertvals+i+1)->category[3].cval = sstat->cpu.cpu[i].steal *100/alltics; (vertvals+i+1)->category[4].ccol = COLORCPUGUEST; (vertvals+i+1)->category[4].clab = 'G'; (vertvals+i+1)->category[4].cval = sstat->cpu.cpu[i].guest *100/alltics; (vertvals+i+1)->numcat = 5; } else { (vertvals+i+1)->numcat = 0; } } } // 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); int lastchar; fd_set readfds; char eventbuf[1024]; int nrfds; convdate(curtime, datestr); /* date to ascii string */ convtime(curtime, timestr); /* time to ascii string */ // calculate subdivision of areas in header line // colsunused = COLS - 35 - seclen - utsnodenamelen - headlen; fill1 = colsunused / 6; fill2 = (colsunused - fill1) / 2; fill3 = colsunused - fill1 - fill2; // fill header line // werase(headwin); wattron(headwin, A_REVERSE); wprintw(headwin, "ATOP - %s %*s%s %s%*s%s%*s%s elapsed", utsname.nodename, fill1, " ", datestr, timestr, fill2, " ", headmsg, fill3, " ", buf); wattroff(headwin, A_REVERSE); // display specific status if needed // statcol = 27 + utsnodenamelen + fill1 + 2; if (statusmsg) { colorswon(headwin, statuscol); wattron(headwin, A_REVERSE); mvwprintw(headwin, 0, statcol, "%s", statusmsg); wattroff(headwin, A_REVERSE); colorswoff(headwin, statuscol); } // wait for keystroke // wmove(headwin, 1, 0); wrefresh(headwin); if (twinpid && !keywaiting) // twin mode? { struct sigaction sigact, sigold; /* ** catch window size changes while in select */ memset(&sigact, 0, sizeof sigact); sigact.sa_handler = getsigwinch; sigaction(SIGWINCH, &sigact, &sigold); winchange = 0; /* ** await input character from keyboard, or ** inotify trigger in case of twin mode, or ** interval timer expiration */ FD_ZERO(&readfds); FD_SET(0, &readfds); if (!paused && fdinotify != -1) // twin mode? { FD_SET(fdinotify, &readfds); nrfds = fdinotify + 1; } else { nrfds = 1; } switch (select(nrfds, &readfds, (fd_set *)0, (fd_set *)0, (struct timeval *)0)) { case -1: /* ** window change or timer expiration? */ if (winchange) { // window change: set new dimensions struct winsize w; ioctl(0, TIOCGWINSZ, &w); resizeterm(w.ws_row, w.ws_col); lastchar = KEY_RESIZE; } else { // time interrupt lastchar = 0; } break; default: /* ** inotify trigger that new sample has been written? ** pretend as if the 't' key has been pressed ** to read that sample ** ** otherwise: read keystroke from keyboard */ if (FD_ISSET(fdinotify, &readfds)) { read(fdinotify, eventbuf, sizeof eventbuf); lastchar = MSAMPNEXT; } else { lastchar = wgetch(headwin); keywaiting = 0; } } sigaction(SIGWINCH, &sigold, (struct sigaction *)0); } else // no twin mode: neutral state is getch() { lastchar = wgetch(headwin); } return lastchar; } ///////////////////////////////////////////////////// // create all windows ///////////////////////////////////////////////////// static int wininit(struct sstat *sstat) { int lpw, c4c, c4m, c4d, c4n, i, avail; float col, dsk2netratio; char winmodel; // determine the ratio between the size of the // disk window and interface window // // determine window model: // 'm' - memory model (preferred) // 'i' - I/O model when lots of disks and/or interfaces are present // dsk2netratio = getwinratio(sstat, &winmodel); // cleanup underlying standard screen // werase(stdscr); refresh(); // calculate number of lines for a half-screen bar graph window // lpw = (LINES-1) / 2 - 1; // lines per window // calculate number of columns for the windows in the upper half // // - memory window gets fixed horizontal size (columns), // either with or without swap bar // // - cpu window gets rest of the colums in upper half // c4m = MEMORYBARSZ + EVENTBARSZ + 4; c4m += sstat->mem.totswap ? SWAPBARSZ+1 : 0; c4c = COLS - c4m - 1; // cpu: rest of columns // calculate number of columns for windows in lower half // // - width of the disk and the interface windows will be // calculated according to the requested ratio // depending of the model, the full screen width can be used // (I/O model) or the remaining width without the memory columns // (memory model) // avail = COLS - (winmodel == 'm' ? c4m + 1: 0); // available columns col = avail / (dsk2netratio+1.0); c4d = col * dsk2netratio; // columns for disk c4n = avail - c4d - 1; // columns for interfaces // create window of two lines for the header line // (second line only meant to 'park' the cursor) // headwin = newwin(2, COLS, 0, 0); // create window of one line as horizonal ruler between the // upper and lower half, and draw horizontal ruler // avail = winmodel=='m' ? COLS - c4m : COLS; midline = newwin(1, avail, lpw+2, 0); colorswon(midline, FGCOLORBORDER); for (i=0; i < avail; i++) { if (i == c4c && c4c != c4d) { waddch(midline, ACS_BTEE); continue; } if (i == c4d) { if (c4c != c4d) waddch(midline, ACS_TTEE); else waddch(midline, ACS_PLUS); continue; } waddch(midline, ACS_HLINE); } colorswoff(midline, FGCOLORBORDER); wrefresh(midline); // create window of one column for vertical ruler // (half or full) and draw vertical ruler // avail = winmodel=='m' ? LINES : lpw+1; // available lines colupper = newwin(avail, 1, 1, c4c); colorswon(colupper, FGCOLORBORDER); for (i=0; i < avail; i++) if (i == lpw+1) waddch(colupper, ACS_RTEE); else waddch(colupper, ACS_VLINE); colorswoff(colupper, FGCOLORBORDER); wrefresh(colupper); // create window of one column for vertical ruler // in the lower half and draw vertical ruler // collower = newwin(LINES-lpw-3, 1, lpw+3, c4d); colorswon(collower, FGCOLORBORDER); for (i=0; i < lpw+1; i++) waddch(collower, ACS_VLINE); colorswoff(collower, FGCOLORBORDER); wrefresh(collower); // create four windows for the resource graphs // and fill dimensions // wincpu.nlines = lpw; wincpu.ncols = c4c; wincpu.win = newwin(wincpu.nlines, wincpu.ncols, 2, 0); winmem.nlines = winmodel == 'i' ? lpw : LINES-2; winmem.ncols = c4m; winmem.win = newwin(winmem.nlines, winmem.ncols, 2, c4c+1); lpw = LINES - 3 - lpw; // recalc for extra line in case of odd lines windsk.nlines = lpw; windsk.ncols = c4d; windsk.win = newwin(windsk.nlines, windsk.ncols, wincpu.nlines+3, 0); lowerifscales(winnet.nlines, lpw); // lower initial scales winnet.nlines = lpw; winnet.ncols = c4n; winnet.win = newwin(winnet.nlines, winnet.ncols, wincpu.nlines+3, c4d+1); wrefresh(wincpu.win); wrefresh(winmem.win); wrefresh(windsk.win); wrefresh(winnet.win); return 1; } ///////////////////////////////////////////////////// // delete all windows ///////////////////////////////////////////////////// static void winexit(void) { delwin(wincpu.win); delwin(winmem.win); delwin(windsk.win); delwin(winnet.win); delwin(colupper); delwin(collower); delwin(midline); delwin(headwin); } ///////////////////////////////////////////////////// // create a separate window to request the user // to enter a value // // arguments // - prompt pointer to prompt string // - answer pointer to buffer in which // the answer will be returned // - maxanswer maximum size of answer buffer // - numerical boolean: convert value to integer? // // return value: // if 'numerical' is true, integer value or // -1 when input was not numeric // // if 'numerical' is false, value 0 or // -1 when no input was given ///////////////////////////////////////////////////// static int getwininput(char *prompt, char *answer, int maxanswer, char numerical) { WINDOW *mywin; int inumval = -1, numcols = strlen(prompt) + maxanswer + 1; // create a boxed window of three lines // for the conversation // mywin = newwin(3, numcols, (LINES-3)/3, (COLS-numcols)/3); box(mywin, ACS_VLINE, ACS_HLINE); // show the prompt // mvwprintw(mywin, 1, 1, "%s", prompt); // prepare reading input // echo(); // switch echoing on answer[0] = 0; if (wgetnstr(mywin, answer, maxanswer-1) != ERR) { // conversion to integer required? // if (numerical) { if (answer[0]) // data entered? { if ( numeric(answer) ) { inumval = atoi(answer); } else { beep(); wmove(mywin, 1, 1); wclrtoeol(mywin); box(mywin, ACS_VLINE, ACS_HLINE); mvwprintw(mywin, 1, 1, "Not numeric!"); wrefresh(mywin); sleep(2); } } } else { inumval = 0; } } else { beep(); } noecho(); delwin(mywin); return inumval; } ///////////////////////////////////////////////////// // create a separate window with help text and // wait for any keyboard input ///////////////////////////////////////////////////// #define HELPLINES 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: cgroups and related processes", MCGROUPS); mvwprintw(helpwin, line++, 2, " '%c' - text mode: generic info", MPROCGEN); mvwprintw(helpwin, line++, 2, " '%c' - text mode: memory details", MPROCMEM); if (supportflags & IOSTAT) mvwprintw(helpwin, line++, 2, " '%c' - text mode: disk details", MPROCDSK); if (supportflags & NETATOP) mvwprintw(helpwin, line++, 2, " '%c' - text mode: network details", MPROCNET); if (supportflags & GPUSTAT) mvwprintw(helpwin, line++, 2, " '%c' - text mode: GPU details", MPROCGPU); mvwprintw(helpwin, line++, 2, " '%c' - text mode: scheduling and thread-group info", MPROCSCH); mvwprintw(helpwin, line++, 2, " '%c' - text mode: various info", MPROCVAR); mvwprintw(helpwin, line++, 2, " '%c' - text mode: full command line per process", MPROCARG); line++; // show context dependent help text for raw file viewing/twin mode // or live measurement // if (rawreadflag) { mvwprintw(helpwin, line++, 2, "Raw file viewing or twin mode:"); mvwprintw(helpwin, line++, 2, " '%c' - show next sample", MSAMPNEXT); mvwprintw(helpwin, line++, 2, " '%c' - show previous sample", MSAMPPREV); mvwprintw(helpwin, line++, 2, " '%c' - rewind to begin", MRESET); mvwprintw(helpwin, line++, 2, " '%c' - branch to certain time", MSAMPBRANCH); mvwprintw(helpwin, line++, 2, " '%c' - pause button to freeze or continue (twin mode)", MPAUSE); } else { mvwprintw(helpwin, line++, 2, "Control:"); mvwprintw(helpwin, line++, 2, " '%c' - change interval timer (0 = only manual trigger)", MINTERVAL); mvwprintw(helpwin, line++, 2, " '%c' - manual trigger to force next sample", MSAMPNEXT); mvwprintw(helpwin, line++, 2, " '%c' - reset counters to boot time values", MRESET); mvwprintw(helpwin, line++, 2, " '%c' - pause button to freeze current sample (toggle)", MPAUSE); } line++; mvwprintw(helpwin, line++, 2, "General:"); mvwprintw(helpwin, line++, 2, " '%c' - reset network scale (otherwise keeps highest level)", MBARLOWER); mvwprintw(helpwin, line++, 2, " '%c' - busy bars with/without categories (toggle)", MBARMONO); mvwprintw(helpwin, line++, 2, " '%c' - quit this program", MQUIT); line++; mvwprintw(helpwin, line++, 2, "Select one of these keys (except '%c') or ", MQUIT); mvwprintw(helpwin, line++, 2, "any other key to leave help..."); wrefresh(helpwin); // wait for any keystroke // inputkey = wgetch(helpwin); // push this keystroke back to be received by the main loop // if (inputkey != MQUIT) { ungetch(inputkey); keywaiting = 1; } // remove help window delwin(helpwin); } ///////////////////////////////////////////////////// // switch certain color on ///////////////////////////////////////////////////// static void colorswon(WINDOW *win, int color) { if (usecolors) wattron(win, COLOR_PAIR(color)); else wattron(win, A_REVERSE); } ///////////////////////////////////////////////////// // switch certain color off ///////////////////////////////////////////////////// static void colorswoff(WINDOW *win, int color) { if (usecolors) wattroff(win, COLOR_PAIR(color)); else wattroff(win, A_REVERSE); } ///////////////////////////////////////////////////// // return background color depending on severity ///////////////////////////////////////////////////// static int severitycolor(char severity) { int color; switch (severity) { case 'n': // normal color = COLOROKAY; break; case 'w': // warning color = COLORWARN; break; case 'c': // critical color = COLORBAD; break; default: color = 0; } return color; } ///////////////////////////////////////////////////// // return character to represent the severity ///////////////////////////////////////////////////// // return value // - n normal // - w warning // - c critical // static char setseverity(long val, long cthreshold, long wthreshold) { if (val < wthreshold) return 'n'; if (val < cthreshold) return 'w'; return 'c'; } ///////////////////////////////////////////////////// // sort bar values in descending order // for CPU and disk stats ///////////////////////////////////////////////////// static void sortvertbars(int nbars, int avgbar, struct vertval **valpp) { // copy original array to be sorted // struct vertval *sortlist = malloc(sizeof(struct vertval) * nbars); ptrverify(sortlist, "Malloc failed for %d sortitems\n", nbars); memcpy(sortlist, *valpp, sizeof(struct vertval) * nbars); // sort the copied list // qsort(sortlist+avgbar, nbars-avgbar, sizeof(struct vertval), compvertval); *valpp = sortlist; } ///////////////////////////////////////////////////// // function to be called by qsort in sortvertbars() ///////////////////////////////////////////////////// static int compvertval(const void *a, const void *b) { const struct vertval *sia = a; const struct vertval *sib = b; if (sia->barval > sib->barval) return -1; if (sia->barval < sib->barval) return 1; return 0; } ///////////////////////////////////////////////////// // sort network bar values in descending order // for network stats ///////////////////////////////////////////////////// static void sortnetbars(int nbars, struct netval **valpp) { // copy original array to be sorted // struct netval *sortlist = malloc(sizeof(struct netval) * nbars); ptrverify(sortlist, "Malloc failed for %d sortitems\n", nbars); memcpy(sortlist, *valpp, sizeof(struct netval) * nbars); // sort the copied list // qsort(sortlist, nbars, sizeof(struct netval), compnetval); *valpp = sortlist; } ///////////////////////////////////////////////////// // function to be called by qsort in sortnetbars() ///////////////////////////////////////////////////// static int compnetval(const void *a, const void *b) { const struct netval *nva = a; const struct netval *nvb = b; long long v1, v2; v1 = nva->pvals + nva->pvalr; v2 = nvb->pvals + nvb->pvalr; if (v1 > v2) return -1; if (v1 < v2) return 1; return 0; } // signal catcher // static void getsigwinch(int signr) { winchange = 1; } atop-2.11.1/atophide.c0000644000203100020310000004676414771753566014046 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, void *, int, void *, int); static int getrawsstat(int, struct sstat *, int); static int getrawtstat(int, struct tstat *, int, int); static void testcompval(int, char *); static void anonymize(struct sstat *, struct tstat *, int); static char *findstandin(struct standin **, unsigned long *, char *, char *); // command names that will not be anonymized (in alphabetical order) // static char *allowedcoms[] = { "^0anacron$", "^agetty$", "^anacron$", "^atd$", "^atop", "^auditd$", "^avahi-", "^awk$", "^basename$", "^bash$", "^bc$", "^bunzip2$", "^bzip2$", "^cat$", "^chmod$", "^chmod$", "^chown$", "^chromium", "^chronyc$", "^chronyd$", "^cp$", "^cpio$", "^crond$", "^csh$", "^cut$", "^date$", "^dbus", "^dd$", "^df$", "^diff$", "^dig$", "^dircolors$", "^dirname$", "^dnf$", "^echo$", "^expr$", "^file$", "^find$", "^firefox$", "^firewalld$", "^gawk$", "^git$", "^grep$", "^grepconf.sh$", "^gunzip$", "^gzip$", "^head$", "^head$", "^host$", "^hostname$", "^hostnamectl$", "^id$", "^ip$", "^iptables$", "^irqbalance$", "^kill$", "^ksh$", "^ldconfig$", "^less$", "^ln$", "^locale$", "^locate$", "^logger$", "^logrotate$", "^ls$", "^lsmd$", "^man$", "^make$", "^mcelog$", "^mkdate$", "^mkdir$", "^mktemp$", "^modprobe$", "^more$", "^mount$", "^mv$", "^netatop", "^NetworkManager$", "^nice$", "^nl$", "^oom_", "^pr$", "^ps$", "^pwd$", "^python$", "^python3$", "^qemu-kvm$", "^readlink$", "^rm$", "^rmdir$", "^rpcbind$", "^rpc.imapd$", "^rpm$", "^rsyslogd$", "^scp$", "^sed$", "^sh$", "^sleep$", "^smartd$", "^sort$", "^ss$", "^ssh$", "^sshd$", "^stat$", "^su$", "^sudo$", "^systemctl$", "^systemd", "^tail$", "^tar$", "^tclsh$", "^tee$", "^thunderbird$", "^top$", "^touch$", "^tr$", "^tuned$", "^udevd", "^uname$", "^uniq$", "^unxz$", "^updatedb$", "^usecpu$", "^usemem$", "^vi$", "^vim$", "^vmtoolsd$", "^wc$", "^which$", "^xargs$", "^xz$", "^xzcat$", "^yum$", "^zcat$", "^zgrep$", }; static regex_t *compreg; // compiled REs of allowed command names int main(int argc, char *argv[]) { struct rawheader rh; struct rawrecord rr; struct sstat sstat; struct tstat *tstatp; struct cstat *cstatp; char *istatp; int ifd, ofd=0, writecnt = 0, anonflag = 0; int i, c, numallowedcoms = sizeof allowedcoms/sizeof(char *); char *infile, *outfile; time_t begintime = 0, endtime = 0; // verify the command line arguments: // mandatory input and output filename // if (argc < 3) prusage(argv[0]); while ((c = getopt(argc, argv, "ab:e:")) != EOF) { switch (c) { case 'a': // anonymize anonflag = 1; break; case 'b': // begin time if ( !getbranchtime(optarg, &begintime) ) prusage(argv[0]); break; case 'e': // end time if ( !getbranchtime(optarg, &endtime) ) prusage(argv[0]); break; default: prusage(argv[0]); break; } } if (optind >= argc) prusage(argv[0]); infile = argv[optind++]; if (optind >= argc) prusage(argv[0]); outfile = argv[optind++]; // open the input file and verify magic number // if ( (ifd = openin(infile)) == -1) { prusage(argv[0]); exit(2); } readin(ifd, &rh, sizeof rh); if (rh.magic != MYMAGIC) { fprintf(stderr, "File %s does not contain atop/atopsar data " "(wrong magic number)\n", infile); exit(3); } if (rh.sstatlen != sizeof(struct sstat) || rh.tstatlen != sizeof(struct tstat) || rh.rawheadlen != sizeof(struct rawheader) || rh.rawreclen != sizeof(struct rawrecord) ) { fprintf(stderr, "File %s created with incompatible version of atop " "or created on other CPU architecture\n", infile); exit(3); } // handle the output file // if (strcmp(infile, outfile) == 0) { fprintf(stderr, "Input file and output file should not be identical!\n"); exit(12); } if (anonflag) // anonymize wanted? { // allocate space for compiled command REs and compile all REs // compreg = malloc(sizeof(regex_t) * numallowedcoms); ptrverify(compreg, "Malloc failed for regex\n"); for (i=0; i < numallowedcoms; i++) regcomp(&compreg[i], allowedcoms[i], REG_NOSUB); // anonymize host name // memset(rh.utsname.nodename, '\0', sizeof rh.utsname.nodename); strcpy(rh.utsname.nodename, "anonymized"); } // read recorded samples and copy to output file // while ( read(ifd, &rr, rh.rawreclen) == rh.rawreclen) { // skip records that are recorded before specified begin time // if (begintime && begintime > rr.curtime) { (void) lseek(ifd, rr.scomplen, SEEK_CUR); (void) lseek(ifd, rr.pcomplen, SEEK_CUR); (void) lseek(ifd, rr.ccomplen, SEEK_CUR); (void) lseek(ifd, rr.icomplen, SEEK_CUR); continue; } // skip records that are recorded after specified end time // if (endtime && endtime < rr.curtime) break; // open the output file and write the rawheader once // if (writecnt++ == 0) { if ( (ofd = openout(outfile)) == -1) { prusage(argv[0]); exit(4); } writeout(ofd, &rh, sizeof rh); } // read compressed system-level statistics and decompress // if ( !getrawsstat(ifd, &sstat, rr.scomplen) ) exit(7); // read compressed process-level statistics and decompress // tstatp = malloc(sizeof(struct tstat) * rr.ndeviat); ptrverify(tstatp, "Malloc failed for %d stored tasks\n", rr.ndeviat); if ( !getrawtstat(ifd, tstatp, rr.pcomplen, rr.ndeviat) ) exit(7); // get compressed cgroup-level statistics // cstatp = malloc(rr.ccomplen); ptrverify(cstatp, "Malloc failed for compressed pidlist\n"); readin(ifd, cstatp, rr.ccomplen); // get compressed pidlist // istatp = malloc(rr.icomplen); ptrverify(istatp, "Malloc failed for compressed pidlist\n"); readin(ifd, istatp, rr.icomplen); // anonymize command lines and hostname // if (anonflag) anonymize(&sstat, tstatp, rr.ndeviat); // write record header, system-level stats, process-level stats, // cgroup-level stats and pidlist // writesamp(ofd, &rr, &sstat, sizeof sstat, tstatp, sizeof *tstatp, rr.ndeviat, cstatp, rr.ccomplen, istatp, rr.icomplen); // cleanup // free(tstatp); free(cstatp); free(istatp); } // close files // close(ifd); printf("Samples written: %d", writecnt); if (writecnt == 0) printf(" -- no output file created!\n"); else printf("\n"); return 0; } // Funtion to anonymize the command lines and host name // static struct standin *lvmhead; static unsigned long lvmsequence; static struct standin *nfshead; static unsigned long nfssequence; static struct standin *cmdhead; static unsigned long cmdsequence; static void anonymize(struct sstat *ssp, struct tstat *tsp, int ntask) { int i, r, numallowedcoms = sizeof allowedcoms/sizeof(char *); char *standin, *p; // anonimize system-level stats // - 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; } #if 0 // Function to read the cgroup-level statistics // from the current offset // static struct cstat * getrawcstat(int rawfd, unsigned long ccomplen, unsigned long coriglen) { Byte *ccompbuf, *corigbuf; int rv; /* ** read all cstat structs */ ccompbuf = malloc(ccomplen); corigbuf = malloc(coriglen); ptrverify(ccompbuf, "Malloc failed for reading compressed cgroups\n"); ptrverify(corigbuf, "Malloc failed for decompressing cgroups\n"); readin(rawfd, ccompbuf, ccomplen); rv = uncompress((Byte *)corigbuf, &coriglen, ccompbuf, ccomplen); testcompval(rv, "uncompress cgroups"); free(ccompbuf); return (struct cstat *)corigbuf; } #endif // Function to write a new output sample from the current offset // static void writesamp(int ofd, struct rawrecord *rr, void *sstat, int sstatlen, void *tstat, int tstatlen, int ntask, void *cstat, int cstatlen, void *istat, int istatlen) { int rv; Byte scompbuf[sstatlen], *pcompbuf; unsigned long scomplen = sizeof scompbuf; unsigned long pcomplen = tstatlen * ntask; /* ** compress system- and process-level statistics */ rv = compress(scompbuf, &scomplen, (Byte *)sstat, (unsigned long)sstatlen); testcompval(rv, "compress"); pcompbuf = malloc(pcomplen); ptrverify(pcompbuf, "Malloc failed for compression buffer\n"); rv = compress(pcompbuf, &pcomplen, (Byte *)tstat, (unsigned long)pcomplen); testcompval(rv, "compress"); rr->scomplen = scomplen; rr->pcomplen = pcomplen; if ( write(ofd, rr, sizeof *rr) == -1) { perror("write raw record"); exit(7); } /* ** write compressed system status structure to file */ if ( write(ofd, scompbuf, scomplen) == -1) { perror("write raw status record"); exit(7); } /* ** write compressed list of process status structures to file */ if ( write(ofd, pcompbuf, pcomplen) == -1) { perror("write raw process records"); exit(7); } free(pcompbuf); /* ** write compressed cgroup status structures to file */ if ( write(ofd, cstat, cstatlen) == -1) { perror("write raw cgroup records"); exit(7); } /* ** write compressed PID list */ if ( write(ofd, istat, istatlen) == -1) { perror("write raw pidlist"); exit(7); } } // check success of (de)compression // static void testcompval(int rv, char *func) { switch (rv) { case Z_OK: case Z_STREAM_END: case Z_NEED_DICT: break; case Z_MEM_ERROR: fprintf(stderr, "%s: failed due to lack of memory\n", func); exit(7); case Z_BUF_ERROR: fprintf(stderr, "%s: failed due to lack of room in buffer\n", func); exit(7); case Z_DATA_ERROR: fprintf(stderr, "%s: failed due to corrupted/incomplete data\n", func); exit(7); default: fprintf(stderr, "%s: unexpected error %d\n", func, rv); exit(7); } } // generic pointer verification after malloc // void ptrverify(const void *ptr, const char *errormsg, ...) { if (!ptr) { va_list args; va_start(args, errormsg); vfprintf(stderr, errormsg, args); va_end (args); exit(13); } } // Convert a string in format YYYYMMDDhhmm into an epoch time value. // // Arguments: String with date-time in format YYYYMMDDhhmm // Pointer to time_t containing 0 or current epoch time. // // Return-value: 0 - Wrong input-format // 1 - Success // int getbranchtime(char *itim, time_t *newtime) { register int ilen = strlen(itim); time_t epoch; struct tm tm; memset(&tm, 0, sizeof tm); /* ** verify length of input string */ if (ilen != 12) return 0; // wrong date-time format /* ** check string syntax for absolute time specified as ** YYYYMMDDhhmm */ if ( sscanf(itim, "%4d%2d%2d%2d%2d", &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min) != 5) return 0; tm.tm_year -= 1900; tm.tm_mon -= 1; if (tm.tm_year < 100 || tm.tm_mon < 0 || tm.tm_mon > 11 || tm.tm_mday < 1 || tm.tm_mday > 31 || tm.tm_hour < 0 || tm.tm_hour > 23 || tm.tm_min < 0 || tm.tm_min > 59 ) { return 0; // wrong date-time format } tm.tm_isdst = -1; if ((epoch = mktime(&tm)) == -1) return 0; // wrong date-time format // correct date-time format *newtime = epoch; return 1; } atop-2.11.1/atopcat.c0000644000203100020310000002006414771753566013665 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, *cstat, *istat; 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 %8s %8s\n", "date", "time", "interval", "comprsys", "comprproc", "comprcgr", "comppids"); } } else // subsequent file { if (aversion != rh.aversion) { fprintf(stderr, "Version of file %s is unequal to " "version of first file\n", infile); close(fd); exit(5); } } // read every raw record followed by the compressed // system-level stats, process-level stats, // cgroup-level stats and pidlist. // while ( read(fd, &rr, sizeof rr) == sizeof rr ) { if (beverbose) { fprintf(stderr, "%19s %12u %8u %9u %8u %8u %s\n", convepoch(rr.curtime), rr.interval, rr.scomplen, rr.pcomplen, rr.ccomplen, rr.icomplen, rr.flags&RRBOOT ? "boot" : ""); } // dynamically allocate space to read stats // if ( (sstat = malloc(rr.scomplen)) == NULL) { fprintf(stderr, "malloc failed for sstat\n"); exit(7); } if ( (pstat = malloc(rr.pcomplen)) == NULL) { fprintf(stderr, "malloc failed for pstat\n"); exit(7); } if ( (cstat = malloc(rr.ccomplen)) == NULL) { fprintf(stderr, "malloc failed for cstat\n"); exit(7); } if ( (istat = malloc(rr.icomplen)) == NULL) { fprintf(stderr, "malloc failed for istat\n"); exit(7); } // read system-level stats // if ((n = read(fd, sstat, rr.scomplen)) != rr.scomplen) { if (n == -1) { fprintf(stderr, "read file %s", infile); perror(""); exit(8); } else { fprintf(stderr, "file %s incomplete!\n", infile); free(sstat); free(pstat); free(cstat); free(istat); break; } } // read process-level stats // if ((n = read(fd, pstat, rr.pcomplen)) != rr.pcomplen) { if (n == -1) { fprintf(stderr, "read file %s", infile); perror(""); exit(8); } else { fprintf(stderr, "file %s incomplete!\n", infile); free(sstat); free(pstat); free(cstat); free(istat); break; } } // read cgroup-level stats // if ((n = read(fd, cstat, rr.ccomplen)) != rr.ccomplen) { if (n == -1) { fprintf(stderr, "read file %s", infile); perror(""); exit(8); } else { fprintf(stderr, "file %s incomplete!\n", infile); free(sstat); free(pstat); free(cstat); free(istat); break; } } // read compressed pidlist // if ((n = read(fd, istat, rr.icomplen)) != rr.icomplen) { if (n == -1) { fprintf(stderr, "read file %s", infile); perror(""); exit(8); } else { fprintf(stderr, "file %s incomplete!\n", infile); free(sstat); free(pstat); free(cstat); free(istat); break; } } if (!dryrun) { // write raw record followed by the compressed // system-level stats, process-level stats, // cgroup-level stats and pidlist // if ( write(1, &rr, sizeof rr) < sizeof rr) { fprintf(stderr, "can not write raw record\n"); exit(11); } if ( write(1, sstat, rr.scomplen) < rr.scomplen) { fprintf(stderr, "can not write sstat\n"); exit(11); } if ( write(1, pstat, rr.pcomplen) < rr.pcomplen) { fprintf(stderr, "can not write pstat\n"); exit(11); } if ( write(1, cstat, rr.ccomplen) < rr.ccomplen) { fprintf(stderr, "can not write cstat\n"); exit(11); } if ( write(1, istat, rr.icomplen) < rr.icomplen) { fprintf(stderr, "can not write istat\n"); exit(11); } } // free dynamically allocated buffers // free(sstat); free(pstat); free(cstat); free(istat); } close(fd); } return 0; } // Function to convert an epoch time to date-time format // char * convepoch(time_t utime) { struct tm *tt; static char datetime[64]; tt = localtime(&utime); 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); }