From abe5f48234b934883bc45fa836d08dfec1a56798 Mon Sep 17 00:00:00 2001 From: Song Liu Date: Thu, 5 Dec 2019 00:32:07 -0800 Subject: [PATCH] Add support to monitor bpf programs. BPF is very important component for modern Linux systems, and getting more features and adoptions. This commit enables atop to monitor BPF programs. The output looks like: ATOP - kerneltest002 2020/06/16 17:01:12 -------------- 10s elapsed PRC | sys 2.72s | user 4.85s | #proc 761 | #zombie 0 | #exit 250 | CPU | sys 29% | user 50% | irq 0% | idle 7915% | wait 8% | CPL | avg1 1.68 | avg5 1.05 | avg15 0.72 | csw 160979 | intr 66341 | [...] BPF_PROG_ID NAME TOTAL_TIME_NS RUN_CNT CPU AVG_TIME_NS 894 tracepoint__sch 83882 11 0% 7625.64 893 tracepoint__sch 43231 5 0% 8646.20 892 tracepoint__tas 34818 4 0% 8704.50 PID SYSCPU USRCPU VGROW RGROW RDDSK WRDSK EXC THR S CPUNR CPU CMD 1/113 2669644 0.45s 1.08s 603.1M 23100K 0K 0K - 10 S 59 15% squashfuse_ll To build atop with BPF monitoring, we need pass in option to make as: ATOP_BPF_SUPPORT=1 make -j Atop periodically enables monitoring of BPF programs calling: bpf_enable_stats(BPF_STATS_RUN_TIME); Since monitoring of BPF program has non-trivial overhead to the bpf programs, the following options are added to only monitor BPF program less often: bpfsamplerate, default 1 bpfsampleinterval, default 1 bpf stats is enabled for bpfsampleinterval seconds every bpfsamplerate atop intervals. bpfsampleinterval must be smaller than atop interval. Changes v1 => v2: 1. Instead of using unsafe sysctl, using a safe new API to enable BPF runtime stats. 2. Change output columns: remove "TYPE", add "CPU" for cpu %. --- Makefile | 12 ++- atop.c | 94 ++++++++++++----- atop.h | 15 ++- atopsar.c | 93 ++++++++--------- parseable.c | 12 +-- parseable.h | 2 +- photobpf.c | 277 ++++++++++++++++++++++++++++++++++++++++++++++++++ photobpf.h | 75 ++++++++++++++ rawlog.c | 109 +++++++++++++++++--- rawlog.h | 7 +- showgeneric.c | 85 ++++++++++------ various.c | 37 +++---- 12 files changed, 662 insertions(+), 156 deletions(-) create mode 100644 photobpf.c create mode 100644 photobpf.h diff --git a/Makefile b/Makefile index a7200352..7d7a350e 100644 --- a/Makefile +++ b/Makefile @@ -28,12 +28,18 @@ OBJMOD3 = showgeneric.o showlinux.o showsys.o showprocs.o OBJMOD4 = atopsar.o netatopif.o gpucom.o ALLMODS = $(OBJMOD0) $(OBJMOD1) $(OBJMOD2) $(OBJMOD3) $(OBJMOD4) +ifneq ($(ATOP_BPF_SUPPORT),) + ALLMODS += photobpf.o + ATOP_BPF_LDFLAGS = -lbpf + CFLAGS += -DATOP_BPF_SUPPORT +endif + VERS = $(shell ./atop -V 2>/dev/null| sed -e 's/^[^ ]* //' -e 's/ .*//') all: atop atopsar atopacctd atopconvert atopcat atop: atop.o $(ALLMODS) Makefile - $(CC) atop.o $(ALLMODS) -o atop -lncursesw -lz -lm -lrt $(LDFLAGS) + $(CC) atop.o $(ALLMODS) -o atop -lncursesw -lz -lm -lrt $(ATOP_BPF_LDFLAGS) $(LDFLAGS) atopsar: atop ln -sf atop atopsar @@ -187,7 +193,7 @@ versdate.h: ./mkdate atop.o: atop.h photoproc.h photosyst.h acctproc.h showgeneric.h -atopsar.o: atop.h photoproc.h photosyst.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 @@ -200,7 +206,7 @@ photoproc.o: atop.h photoproc.h photosyst.o: atop.h photosyst.h showgeneric.o: atop.h photoproc.h photosyst.h showgeneric.h showlinux.h showlinux.o: atop.h photoproc.h photosyst.h showgeneric.h showlinux.h -showsys.o: atop.h photoproc.h photosyst.h showgeneric.h +showsys.o: atop.h photoproc.h photosyst.h showgeneric.h showprocs.o: atop.h photoproc.h photosyst.h showgeneric.h showlinux.h version.o: version.c version.h versdate.h gpucom.o: atop.h photoproc.h photosyst.h diff --git a/atop.c b/atop.c index 8bac6723..3e1c4027 100644 --- a/atop.c +++ b/atop.c @@ -1,11 +1,11 @@ /* ** ATOP - System & Process Monitor ** -** The program 'atop' offers the possibility to view the activity of +** 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. +** 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 @@ -35,7 +35,7 @@ ** -------------------------------------------------------------------------- ** ** After initialization, the main-function calls the ENGINE. -** For every cycle (so after another interval) the ENGINE calls various +** For every cycle (so after another interval) the ENGINE calls various ** functions as shown below: ** ** +---------------------------------------------------------------------+ @@ -48,15 +48,15 @@ ** | | ^ | ^ | ^ | ^ | | | ** +---|-----|--------|-----|--------|----|--------|----|--------|----|--+ ** | | | | | | | | | | -** +--V-----|--+ +--V-----|--+ +--V----|--+ +--V----|--+ +--V----|-+ +** +--V-----|--+ +--V-----|--+ +--V----|--+ +--V----|--+ +--V----|-+ ** | | | | | | | | | | ** | photosyst | | photoproc | | acct | | deviate | | print | ** | | | | |photoproc | | ...syst | | | ** | | | | | | | ...proc | | | -** +-----------+ +-----------+ +----------+ +----------+ +---------+ +** +-----------+ +-----------+ +----------+ +----------+ +---------+ ** ^ ^ ^ ^ | ** | | | | | -** | | | V V +** | | | V V ** ______ _________ __________ ________ _________ ** / \ / \ / \ / \ / \ ** /proc /proc accounting task screen or @@ -84,8 +84,8 @@ ** When all counters have been gathered, functions are called to calculate ** the difference between the current counter-values and the counter-values ** of the previous cycle. These functions operate on the system-level -** as well as on the task-level counters. -** These differences are stored in a new structure(-table). +** as well as on the task-level counters. +** These differences are stored in a new structure(-table). ** ** - deviatsyst() ** Calculates the differences between the current system-level @@ -98,7 +98,7 @@ ** 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 +** 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 @@ -112,7 +112,7 @@ ** 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. +** at runtime. ** ** $Log: atop.c,v $ ** Revision 1.49 2010/10/23 14:01:00 gerlof @@ -296,6 +296,7 @@ #include "showgeneric.h" #include "parseable.h" #include "gpucom.h" +#include "photobpf.h" #define allflags "ab:cde:fghijklmnopqrstuvwxyz1ABCDEFGHIJKL:MNOP:QRSTUVWXYZ" #define MAXFL 64 /* maximum number of command-line flags */ @@ -322,6 +323,16 @@ char threadview = 0; /* boolean: show individual threads */ char calcpss = 0; /* boolean: read/calculate process PSS */ char getwchan = 0; /* boolean: obtain wchan string */ +/* +** arguments for bpf stats sampling +** We enable bpf stats for bpfsampleinterval seconds every bpfsamplerate +** atop intervals. bpfsampleinterval must be smaller than atop interval. +** +** If bpfsamplerate == 0, disable sampling of bpf stats. +*/ +unsigned int bpfsamplerate = 1; +unsigned int bpfsampleinterval = 1; + unsigned short hertz; unsigned int pagesize; unsigned int nrgpus; @@ -391,6 +402,9 @@ void do_almostcrit(char *, char *); void do_atopsarflags(char *, char *); void do_pacctdir(char *, char *); void do_perfevents(char *, char *); +void do_bpflines(char *, char *); +void do_bpfsamplerate(char *, char *); +void do_bpfsampleinterval(char *, char *); static struct { char *tag; @@ -440,6 +454,9 @@ static struct { { "atopsarflags", do_atopsarflags, 0, }, { "perfevents", do_perfevents, 0, }, { "pacctdir", do_pacctdir, 1, }, + { "bpflines", do_bpflines, 0, }, + { "bpfsamplerate", do_bpfsamplerate, 0, }, + { "bpfsampleinterval", do_bpfsampleinterval, 0, }, }; /* @@ -466,6 +483,8 @@ main(int argc, char *argv[]) exit(42); } + photo_bpf_check(); + /* ** preserve command arguments to allow restart of other version */ @@ -497,12 +516,12 @@ main(int argc, char *argv[]) if ( memcmp(p, "atopsar", 7) == 0) return atopsar(argc, argv); - /* - ** interpret command-line arguments & flags + /* + ** interpret command-line arguments & flags */ if (argc > 1) { - /* + /* ** gather all flags for visualization-functions ** ** generic flags will be handled here; @@ -600,17 +619,17 @@ main(int argc, char *argv[]) } /* - ** get optional interval-value and optional number of samples + ** 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]) ) @@ -766,6 +785,7 @@ engine(void) gpupending=0; /* boolean: request sent */ struct gpupidstat *gp = NULL; + struct bstats *bstats = NULL; /* ** initialization: allocate required memory dynamically @@ -817,6 +837,8 @@ engine(void) if (nrgpus) supportflags |= GPUSTAT; + if (system_support_bpf()) + supportflags |= BPFSTAT; /* ** MAIN-LOOP: ** - Wait for the requested number of seconds or for other trigger @@ -838,11 +860,15 @@ engine(void) /* ** if the limit-flag is specified: ** check if the next sample is expected before midnight; - ** if not, stop atop now + ** if not, stop atop now */ if (midnightflag && (curtime+interval) > timelimit) break; + if ((supportflags & BPFSTAT) && + bpfsamplerate && sampcnt % bpfsamplerate == 0) + bstats = get_devbstats(); + /* ** wait for alarm-signal to arrive (except first sample) ** or wait for SIGUSR1/SIGUSR2 @@ -859,13 +885,13 @@ engine(void) curtime = time(0); /* seconds since 1-1-1970 */ /* - ** send request for statistics to atopgpud + ** send request for statistics to atopgpud */ if (nrgpus) gpupending = gpud_statrequest(); /* - ** take a snapshot of the current system-level statistics + ** take a snapshot of the current system-level statistics ** and calculate the deviations (i.e. calculate the activity ** during the last sample) */ @@ -918,7 +944,7 @@ engine(void) curtime-pretime > 0 ? curtime-pretime : 1); /* - ** take a snapshot of the current task-level statistics + ** take a snapshot of the current task-level statistics ** and calculate the deviations (i.e. calculate the activity ** during the last sample) ** @@ -1013,10 +1039,14 @@ engine(void) ** the deviations */ lastcmd = (vis.show_samp)( curtime, - curtime-pretime > 0 ? curtime-pretime : 1, - &devtstat, devsstat, - nprocexit, noverflow, sampcnt==0); + curtime-pretime > 0 ? curtime-pretime : 1, + &devtstat, devsstat, bstats, + nprocexit, noverflow, sampcnt==0); + if (bstats) { + free(bstats->bpfall); + bstats = NULL; + } /* ** release dynamically allocated memory */ @@ -1065,7 +1095,7 @@ prusage(char *myname) printf("\t -%c show version information\n", MVERSION); printf("\t -%c show or log all processes (i.s.o. active processes " "only)\n", MALLPROC); - printf("\t -%c calculate proportional set size (PSS) per process\n", + 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 parseable output for specified label(s)\n"); @@ -1146,6 +1176,18 @@ do_linelength(char *name, char *val) linelen = get_posval(name, val); } +void +do_bpfsamplerate(char *name, char *val) +{ + bpfsamplerate = get_posval(name, val); +} + +void +do_bpfsampleinterval(char *name, char *val) +{ + bpfsampleinterval = get_posval(name, val); +} + /* ** read RC-file and modify defaults accordingly */ @@ -1196,7 +1238,7 @@ readrc(char *path, int syslevel) default: if (tagname[0] == '#') continue; - + if (tagvalue[0] != '#') break; diff --git a/atop.h b/atop.h index 65890d77..7443102c 100644 --- a/atop.h +++ b/atop.h @@ -42,8 +42,9 @@ struct tstat; struct devtstat; struct sstat; struct netpertask; +struct bstats; -/* +/* ** miscellaneous flags */ #define RRBOOT 0x0001 @@ -57,7 +58,7 @@ struct netpertask; struct visualize { char (*show_samp) (time_t, int, - struct devtstat *, struct sstat *, + struct devtstat *, struct sstat *, struct bstats *, int, unsigned int, char); void (*show_error) (const char *, ...); void (*show_end) (void); @@ -105,6 +106,9 @@ extern int netbadness; extern int pagbadness; extern int almostcrit; +extern int bpflines; +extern unsigned int bpfsampleinterval; + /* ** bit-values for supportflags */ @@ -114,9 +118,10 @@ extern int almostcrit; #define NETATOPD 0x00000020 #define DOCKSTAT 0x00000040 #define GPUSTAT 0x00000080 +#define BPFSTAT 0x00000100 /* -** in rawlog file, the four least significant bits +** 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 */ @@ -126,7 +131,7 @@ extern int almostcrit; ** structure containing the start-addresses of functions for visualization */ char generic_samp (time_t, int, - struct devtstat *, struct sstat *, + struct devtstat *, struct sstat *, struct bstats *, int, unsigned int, char); void generic_error(const char *, ...); void generic_end (void); @@ -168,7 +173,7 @@ int contcompar(const void *, const void *); count_t subcount(count_t, count_t); int rawread(void); char rawwrite (time_t, int, - struct devtstat *, struct sstat *, + struct devtstat *, struct sstat *, struct bstats *, int, unsigned int, char); int numeric(char *); diff --git a/atopsar.c b/atopsar.c index fcd3f059..c6349c93 100644 --- a/atopsar.c +++ b/atopsar.c @@ -1,7 +1,7 @@ /* ** ATOP - System & Process Monitor ** -** The program 'atop'/'atopsar' offers the possibility to view the activity of +** 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 @@ -81,7 +81,7 @@ static char *datemsg = "-------------------------- analysis " /* ** structure definition for print-functions */ -struct pridef { +struct pridef { char wanted; /* selected option (boolean) */ char *cntcat; /* used categories of counters */ char flag; /* flag on command line */ @@ -108,7 +108,7 @@ 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 devtstat *, struct sstat *, struct bstats *, int, unsigned int, char); static void reportheader(struct utsname *, time_t); @@ -123,12 +123,12 @@ atopsar(int argc, char *argv[]) usecolors = 't'; - /* - ** interpret command-line arguments & flags + /* + ** interpret command-line arguments & flags */ if (argc > 1) { - /* + /* ** gather all flags for the print-functions */ flaglist = malloc(pricnt+32); @@ -226,7 +226,7 @@ atopsar(int argc, char *argv[]) default: /* gather report-flags */ for (i=0; i < pricnt; i++) { - if (pridef[i].flag == c && + if (pridef[i].flag == c && pridef[i].wanted == 0 ) { pridef[i].wanted = 1; @@ -244,7 +244,7 @@ atopsar(int argc, char *argv[]) /* ** get optional interval-value and - ** optional number of samples + ** optional number of samples */ if (optind < argc && optind < MAXFL) { @@ -253,11 +253,11 @@ atopsar(int argc, char *argv[]) if (!numeric(argv[optind])) pratopsaruse(argv[0]); - + interval = atoi(argv[optind]); - + optind++; - + if (optind < argc) { if (!numeric(argv[optind]) ) @@ -488,7 +488,7 @@ engine(void) gpustats = gpud_statrequest(); /* - ** take a snapshot of the current system-level statistics + ** take a snapshot of the current system-level statistics ** and calculate the deviations (i.e. calculate the activity ** during the last sample) */ @@ -589,9 +589,9 @@ reportlive(time_t curtime, int numsecs, struct sstat *ss) printf(COLSETHEAD); printf("%s ", convtime(curtime-numsecs, timebuf)); - + (pridef[i].prihead)(osvers, osrel, ossub); - + if (usecolors) printf(COLRESET); @@ -601,7 +601,7 @@ reportlive(time_t curtime, int numsecs, struct sstat *ss) ** 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, @@ -613,7 +613,7 @@ reportlive(time_t curtime, int numsecs, struct sstat *ss) ** do not call function again */ pridef[i].wanted = 0; - + if (--numreports == 0) cleanstop(1); } @@ -654,7 +654,7 @@ reportlive(time_t curtime, int numsecs, struct sstat *ss) printf(COLSETHEAD); printf("%s ", convtime(curtime, timebuf)); - + (pridef[i].prihead)(osvers, osrel, ossub); if (usecolors) @@ -672,10 +672,10 @@ reportlive(time_t curtime, int numsecs, struct sstat *ss) ** 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, + osvers, osrel, ossub, stampalways ? timebuf : " ", 0, 0, 0, 0, 0, 0) ) ) { @@ -701,7 +701,7 @@ reportlive(time_t curtime, int numsecs, struct sstat *ss) printf(COLSETHEAD); printf("%s ", convtime(curtime, timebuf)); - + (pridef[i].prihead)(osvers, osrel, ossub); if (usecolors) @@ -720,6 +720,7 @@ reportlive(time_t curtime, int numsecs, struct sstat *ss) static char reportraw(time_t curtime, int numsecs, struct devtstat *devtstat, struct sstat *sstat, + struct bstats *bstats, int nexit, unsigned int noverflow, char flags) { static char firstcall = 1; @@ -1191,7 +1192,7 @@ do_atopsarflags(char *name, char *val) default: /* gather report-flags */ for (j=0; j < pricnt; j++) { - if (pridef[j].flag == val[i] && + if (pridef[j].flag == val[i] && pridef[j].wanted == 0 ) { pridef[j].wanted = 1; @@ -1486,7 +1487,7 @@ memline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, if (membadness) mbadness = ((ss->mem.physmem - ss->mem.freemem - ss->mem.cachemem - ss->mem.buffermem) - * 100.0 / ss->mem.physmem) + * 100.0 / ss->mem.physmem) * 100 / membadness; else mbadness = 0; @@ -1818,7 +1819,7 @@ nfmline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, else state = ' '; - printf("%-38s %10.3lfK %10.3lfK %c\n", + printf("%-38s %10.3lfK %10.3lfK %c\n", pn, (double)ss->nfs.nfsmounts.nfsmnt[i].bytestotread / 1024 / deltasec, @@ -1943,7 +1944,7 @@ ibline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, preprint(badness); - printf("%-10s %4hd %4.0f%% %7.1lf %7.1lf %5lld %5lld %7lld %5d", + printf("%-10s %4hd %4.0f%% %7.1lf %7.1lf %5lld %5lld %7lld %5d", ss->ifb.ifb[i].ibname, ss->ifb.ifb[i].portnr, busy, @@ -2080,7 +2081,7 @@ ifline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, preprint(badness); printf("%-6s %4s %7.1lf %7.1lf %8.0lf %8.0lf " - "%5lld %5lld %7ld %c", + "%5lld %5lld %7ld %c", pn, busyval, (double)ss->intf.intf[i].rpack / deltasec, (double)ss->intf.intf[i].spack / deltasec, @@ -2140,7 +2141,7 @@ IFline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, pn = ss->intf.intf[i].name; printf("%-6s %6.2lf %6.2lf %6.2lf %7.2lf %7.2lf " - "%8.2lf %10.2lf\n", + "%8.2lf %10.2lf\n", pn, (double)ss->intf.intf[i].rerrs / deltasec, (double)ss->intf.intf[i].serrs / deltasec, @@ -2177,7 +2178,7 @@ ipv4line(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie) { - printf("%8.1lf %8.1lf %11.1lf %9.1lf %9.1lf %11.1lf\n", + 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, @@ -2201,7 +2202,7 @@ IPv4line(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, int ppres, int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie) { printf(" %5.1lf %6.1lf %6.1lf %6.1lf %7.1lf %7.1lf " - " %5.1lf %5.1lf\n", + " %5.1lf %5.1lf\n", (double)ss->net.ipv4.InDiscards / deltasec, (double)ss->net.ipv4.InHdrErrors / deltasec, (double)ss->net.ipv4.InAddrErrors / deltasec, @@ -2229,12 +2230,12 @@ icmpv4line(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, 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, + 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; } @@ -2253,7 +2254,7 @@ ICMPv4line(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, int ppres, int ntrun, int ntslpi, int ntslpu, 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", + "%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, @@ -2307,7 +2308,7 @@ ipv6line(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie) { - printf("%8.1lf %8.1lf %6.1lf %7.1lf %9.1lf %9.1lf %9.1lf\n", + 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, @@ -2332,7 +2333,7 @@ IPv6line(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, int ppres, int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie) { printf(" %5.1lf %6.1lf %6.1lf %6.1lf %7.1lf %7.1lf " - " %5.1lf %5.1lf\n", + " %5.1lf %5.1lf\n", (double)ss->net.ipv6.Ip6InDiscards / deltasec, (double)ss->net.ipv6.Ip6InHdrErrors / deltasec, (double)ss->net.ipv6.Ip6InAddrErrors / deltasec, @@ -2360,13 +2361,13 @@ icmpv6line(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, int osvers, int osrel, int ossub, char *tstamp, int ppres, int ntrun, int ntslpi, int ntslpu, 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, + 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; @@ -2386,7 +2387,7 @@ ICMPv6line(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, int ppres, int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie) { printf("%7.2lf %7.2lf %7.2lf %5.2lf %5.2lf " - "%5.2lf %5.2lf %5.2lf %5.2lf\n", + "%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, @@ -2487,7 +2488,7 @@ httpline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, printf("%10.2lf %8.2lf %9.2lf %11d %11d\n", (double)ss->www.accesses / deltasec, (double)ss->www.totkbytes / deltasec, - ss->www.accesses ? + ss->www.accesses ? (double)ss->www.totkbytes*1024/ss->www.accesses : 0, ss->www.iworkers, ss->www.bworkers); diff --git a/parseable.c b/parseable.c index a3e1e26d..05626ebf 100644 --- a/parseable.c +++ b/parseable.c @@ -165,7 +165,7 @@ parsedef(char *pd) } /* - ** check list of comma-separated labels + ** check list of comma-separated labels */ while (pd < ep) { @@ -173,7 +173,7 @@ parsedef(char *pd) ** exchange comma by null-byte */ if ( (p = strchr(pd, ',')) ) - *p = 0; + *p = 0; else p = ep-1; @@ -223,7 +223,7 @@ parsedef(char *pd) */ char parseout(time_t curtime, int numsecs, - struct devtstat *devtstat, struct sstat *sstat, + struct devtstat *devtstat, struct sstat *sstat, struct bstats *bstats, int nexit, unsigned int noverflow, char flag) { register int i; @@ -274,15 +274,15 @@ parseout(time_t curtime, int numsecs, ** print functions for system-level statistics */ void -calc_freqscale(count_t maxfreq, count_t cnt, count_t ticks, +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) + if (maxfreq && ticks) { *freq=cnt/ticks; *freqperc=100* *freq / maxfreq; - } + } else if (maxfreq) // max frequency is known so % can be calculated { *freq=cnt; diff --git a/parseable.h b/parseable.h index 4d592cd7..5b4aba4b 100644 --- a/parseable.h +++ b/parseable.h @@ -1,4 +1,4 @@ int parsedef(char *); char parseout(time_t, int, - struct devtstat *, struct sstat *, + struct devtstat *, struct sstat *, struct bstats *, int, unsigned int, char); diff --git a/photobpf.c b/photobpf.c new file mode 100644 index 00000000..48791d14 --- /dev/null +++ b/photobpf.c @@ -0,0 +1,277 @@ +/* +** 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 bpf-porgram counters. +** ================================================================ +** Author: Song Liu +** E-mail: song@kernel.org +** Date: Dec 2019 +** +** 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 "atop.h" +#include "photobpf.h" +#include "showgeneric.h" + +#define BPF_STATS_MIN_ALLOC 8 + +/* +** check configurations: +** bpfsampleinterval < interval +*/ +void +photo_bpf_check(void) +{ + if (bpfsampleinterval >= interval) + mcleanstop(1, "bpfsampleinterval must be smaller than interval"); +} + +/* boolean */ +int +system_support_bpf(void) +{ + int bpf_stats_fd = bpf_enable_stats(BPF_STATS_RUN_TIME); + + /* check bpf_enable_stats() returns valid fd */ + if (bpf_stats_fd < 0) + return 0; + close(bpf_stats_fd); + return 1; +} + +/* +** Get a snapshot of all bpf program stats +*/ +static struct bstats * +get_allbstats(void) +{ + int err; + __u32 id = 0; + unsigned int bufsize = 0; + struct bstats *curbstats = malloc(sizeof(struct bstats)); + + ptrverify(curbstats, "Malloc failed for bstats\n"); + + memset(curbstats, 0, sizeof(struct bstats)); + + while (true) { + int fd; + struct bpf_prog_info info = {}; + __u32 len = sizeof(struct bpf_prog_info); + struct bstat *bstat; + + err = bpf_prog_get_next_id(id, &id); + if (err) + break; + if (curbstats->nbpfall >= bufsize) { + bufsize += BPF_STATS_MIN_ALLOC; + curbstats->bpfall = realloc( + curbstats->bpfall, + bufsize * sizeof(struct bstat)); + ptrverify(curbstats->bpfall, + "Malloc failed for %u bstats\n", + bufsize); + } + + fd = bpf_prog_get_fd_by_id(id); + if (fd < 0) { + if (errno == ENOENT) + continue; + break; + } + + err = bpf_obj_get_info_by_fd(fd, &info, &len); + close(fd); + if (err) + break; + + bstat = curbstats->bpfall + curbstats->nbpfall; + + bstat->type = info.type; + bstat->id = info.id; + memcpy(bstat->name, info.name, BPF_OBJ_NAME_LEN); + bstat->run_time_ns = info.run_time_ns; + bstat->run_cnt = info.run_cnt; + + curbstats->nbpfall++; + } + + return curbstats; +} + +static int +bstatcompar(const void *a, const void *b) +{ + struct bstat *sa = (struct bstat *)a; + struct bstat *sb = (struct bstat *)b; + + if (sa->run_time_ns < sb->run_time_ns) + return 1; + if (sa->run_time_ns > sb->run_time_ns) + return -1; + + return 0; +} + +/* +** return deviate bpf program stats, sorted by run_time_ns. +** programs with zero run_time_ns are ingored. +*/ +struct bstats * +get_devbstats(void) +{ + int i, j; + struct bstats *prebstats; + struct bstats *curbstats; + int bpf_stats_fd; + + if (!(supportflags & BPFSTAT)) { + printf("NO support bpf\n"); + return NULL; + } + + /* bpf_enable_stats() enables stats and returns a valid fd */ + bpf_stats_fd = bpf_enable_stats(BPF_STATS_RUN_TIME); + + if (bpf_stats_fd < 0) { + supportflags &= ~BPFSTAT; + return NULL; + } + + prebstats = get_allbstats(); + sleep(bpfsampleinterval); + /* close the fd to disable stats */ + close(bpf_stats_fd); + curbstats = get_allbstats(); + + /* + ** deviate, save result in curbstats + */ + i = 0; + j = 0; + while (i < prebstats->nbpfall && j < curbstats->nbpfall) { + struct bstat *prebstat = prebstats->bpfall + i; + struct bstat *curbstat = curbstats->bpfall + j; + + if (prebstat->id < curbstat->id) { + i++; + } else if (prebstat->id > curbstat->id) { + j++; + } else { + curbstat->run_cnt -= prebstat->run_cnt; + curbstat->run_time_ns -= prebstat->run_time_ns; + i++; + j++; + } + } + + free(prebstats->bpfall); + + /* + ** sort result based on run_time_ns + */ + qsort(curbstats->bpfall, curbstats->nbpfall, + sizeof(curbstats->bpfall[0]), + bstatcompar); + + /* + ** drop stats with zero run time + */ + for (i = 0; i < curbstats->nbpfall; i++) + if (curbstats->bpfall[i].run_time_ns == 0) { + curbstats->bpfall = realloc(curbstats->bpfall, + i * sizeof(struct bstat)); + curbstats->nbpfall = i; + break; + } + + return curbstats; +} + +/* +** Print bpf stats. +** For less than 76 columns, skip. +** For 76 or more columens, show +** BPF_PROG_ID 11 bytes +** NAME 17+ bytes +** TOTAL_TIME_NS 15 bytes +** RUN_CNT 13 bytes +** CPU % 8 bytes +** AVG_TIME_NS 12 bytes +*/ +int +pribpf(struct bstats *devbstat, int curline) +{ + int i; + int maxw = screen ? COLS : linelen; + int namewidth = maxw - 59; + + if ((supportflags & BPFSTAT) == 0 || maxw < 76 || !devbstat) + return curline; + + curline += 1; + if (screen) { + move(curline, 0); + attron(A_REVERSE); + } else { + printg("\n\n"); + } + printg("%11s%*s%15s%13s%8s%12s", "BPF_PROG_ID", namewidth, "NAME", "TOTAL_TIME_NS", + "RUN_CNT", "CPU", "AVG_TIME_NS"); + + if (screen) + attroff(A_REVERSE); + else + printg("\n"); + + for (i = 0; i < devbstat->nbpfall; i++) { + float avgtime; + struct bstat *bstat = devbstat->bpfall + i; + + /* + ** show at most bpflines on screen + */ + if ((screen && i >= bpflines)) + break; + + curline += 1; + if (screen) + move(curline, 0); + else + printg("\n"); + + avgtime = (bstat->run_cnt == 0) ? 0.0 : + (float)(bstat->run_time_ns) / bstat->run_cnt; + + printg("%11d%*s%15llu%13llu%7llu%%%12.2f", bstat->id, namewidth, + bstat->name, bstat->run_time_ns, bstat->run_cnt, + bstat->run_time_ns / bpfsampleinterval / 10000000ULL, + avgtime); + } + + return curline; +} diff --git a/photobpf.h b/photobpf.h new file mode 100644 index 00000000..f4100d87 --- /dev/null +++ b/photobpf.h @@ -0,0 +1,75 @@ +/* +** 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 bpf-porgram-level counters maintained and +** functions to access the bpf-program-database. +** ================================================================ +** Author: Song Liu +** E-mail: song@kernel.org +** Date: Dec 2019 +** +** 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 + +/* +** structure containing information about a bpf program +*/ +struct bstat { + __u32 type; + __u32 id; + char name[BPF_OBJ_NAME_LEN]; + __u64 run_time_ns; + __u64 run_cnt; +} __attribute__((aligned(8))); + +/* +** structure containing information about all bpf programs, or a deviate of +** two snapshots. +*/ +struct bstats { + struct bstat *bpfall; + + unsigned int nbpfall; +}; + +#ifdef ATOP_BPF_SUPPORT +void photo_bpf_check(); +int system_support_bpf(void); + +struct bstats *get_devbstats(void); +int pribpf(struct bstats *devbstat, int curline); +#else +static inline void +photo_bpf_check(void) {} + +static inline int +system_support_bpf(void) +{ + return 0; +} + +static inline struct bstats * +get_devbstats(void) +{ + return NULL; +} + +static inline int +pribpf(struct bstats *devbstat, int curline) +{ + return curline; +} + +#endif diff --git a/rawlog.c b/rawlog.c index d2cb7d1e..05c1215d 100644 --- a/rawlog.c +++ b/rawlog.c @@ -1,7 +1,7 @@ /* ** ATOP - System & Process Monitor ** -** The program 'atop' offers the possibility to view the activity of +** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as process-level. ** ========================================================================== ** Author: Gerlof Langeveld @@ -52,12 +52,14 @@ #include "photoproc.h" #include "photosyst.h" #include "rawlog.h" +#include "photobpf.h" -#define BASEPATH "/var/log/atop" +#define BASEPATH "/var/log/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 getrawbstat(int, struct bstat *, int, int); static int rawwopen(void); static int readchunk(int, void *, int); static int lookslikedatetome(char *); @@ -69,8 +71,8 @@ static void try_other_version(int, int); ** (file is opened/created during the first call) */ char -rawwrite(time_t curtime, int numsecs, - struct devtstat *devtstat, struct sstat *sstat, +rawwrite(time_t curtime, int numsecs, + struct devtstat *devtstat, struct sstat *sstat, struct bstats *bstats, int nexit, unsigned int noverflow, char flag) { static int rawfd = -1; @@ -79,10 +81,17 @@ rawwrite(time_t curtime, int numsecs, struct stat filestat; Byte scompbuf[sizeof(struct sstat)], *pcompbuf; + Byte *bcompbuf; unsigned long scomplen = sizeof scompbuf; unsigned long pcomplen = sizeof(struct tstat) * devtstat->ntaskall; - struct iovec iov[3]; + unsigned long bcomplen; + struct iovec iov[4]; + struct bstats dummybstats = {NULL, 0}; + + if (bstats == NULL) + bstats = &dummybstats; + bcomplen = sizeof(struct bstat) * bstats->nbpfall; /* ** first call: @@ -115,6 +124,17 @@ rawwrite(time_t curtime, int numsecs, testcompval(rv, "compress"); + bcompbuf = malloc(bcomplen); + + ptrverify(bcompbuf, "Malloc failed for compression buffer\n"); + + if (bcomplen) { + rv = compress(bcompbuf, &bcomplen, (Byte *)bstats->bpfall, + (unsigned long)bcomplen); + + testcompval(rv, "compress"); + } + /* ** fill record header and write to file */ @@ -135,6 +155,8 @@ rawwrite(time_t curtime, int numsecs, rr.totzomb = devtstat->totzombie; rr.scomplen = scomplen; rr.pcomplen = pcomplen; + rr.bcomplen = bcomplen; + rr.totbpf = bstats->nbpfall; if (flag&RRBOOT) rr.flags |= RRBOOT; @@ -170,7 +192,10 @@ rawwrite(time_t curtime, int numsecs, iov[2].iov_base = pcompbuf; iov[2].iov_len = pcomplen; - if ( writev(rawfd, iov, 3) == -1) + iov[3].iov_base = bcompbuf; + iov[3].iov_len = bcomplen; + + if ( writev(rawfd, iov, 4) == -1) { fprintf(stderr, "%s - ", rawname); if ( ftruncate(rawfd, filestat.st_size) == -1) @@ -184,6 +209,7 @@ rawwrite(time_t curtime, int numsecs, } free(pcompbuf); + free(bcompbuf); return '\0'; } @@ -225,6 +251,7 @@ rawwopen() if ( rh.sstatlen != sizeof(struct sstat) || rh.tstatlen != sizeof(struct tstat) || + rh.bstatlen != sizeof(struct bstat) || rh.rawheadlen != sizeof(struct rawheader) || rh.rawreclen != sizeof(struct rawrecord) ) { @@ -268,6 +295,7 @@ rawwopen() rh.aversion = getnumvers() | 0x8000; rh.sstatlen = sizeof(struct sstat); rh.tstatlen = sizeof(struct tstat); + rh.bstatlen = sizeof(struct bstat); rh.rawheadlen = sizeof(struct rawheader); rh.rawreclen = sizeof(struct rawrecord); rh.supportflags = supportflags | RAWLOGNG; @@ -303,6 +331,7 @@ rawread(void) struct rawrecord rr; struct sstat sstat; static struct devtstat devtstat; + struct bstats bstats = {NULL, 0}; struct stat filestat; @@ -328,7 +357,7 @@ rawread(void) tp = localtime(&timenow); snprintf(rawname, RAWNAMESZ, "%s/atop_%04d%02d%02d", - BASEPATH, + BASEPATH, tp->tm_year+1900, tp->tm_mon+1, tp->tm_mday); @@ -340,7 +369,7 @@ rawread(void) ** the full pathname of the raw file */ case 8: - if ( access(rawname, F_OK) == 0) + if ( access(rawname, F_OK) == 0) break; /* existing file */ if (lookslikedatetome(rawname)) @@ -350,7 +379,7 @@ rawread(void) strncpy(savedname, rawname, RAWNAMESZ-1); snprintf(rawname, RAWNAMESZ, "%s/atop_%s", - BASEPATH, + BASEPATH, savedname); break; } @@ -362,7 +391,7 @@ rawread(void) ** of y's). */ default: - if ( access(rawname, F_OK) == 0) + if ( access(rawname, F_OK) == 0) break; /* existing file */ /* @@ -382,7 +411,7 @@ rawread(void) tp = localtime(&timenow); snprintf(rawname, RAWNAMESZ, "%s/atop_%04d%02d%02d", - BASEPATH, + BASEPATH, tp->tm_year+1900, tp->tm_mon+1, tp->tm_mday); @@ -479,6 +508,7 @@ rawread(void) */ if (rh.sstatlen != sizeof(struct sstat) || rh.tstatlen != sizeof(struct tstat) || + rh.bstatlen != sizeof(struct bstat) || rh.rawheadlen != sizeof(struct rawheader) || rh.rawreclen != sizeof(struct rawrecord) ) { @@ -591,7 +621,7 @@ rawread(void) offsize+= OFFCHUNK; } } - + /* ** check if this sample is within the time-range ** specified with the -b and -e flags (if any) @@ -599,7 +629,7 @@ rawread(void) if ( (begintime > cursortime) ) { lastcmd = 1; - + if (isregular) { static off_t curr_pos = -1; @@ -721,6 +751,21 @@ rawread(void) devtstat.totslpu = rr.totslpu; devtstat.totzombie = rr.totzomb; + bstats.nbpfall = rr.totbpf; + + if (rr.totbpf) { + bstats.bpfall = malloc( + sizeof(struct bstat) * rr.totbpf); + + ptrverify(bstats.bpfall, + "Malloc failed for %d stored bpf stats\n", + rr.totbpf); + + if ( !getrawbstat(rawfd, bstats.bpfall, + rr.bcomplen, rr.totbpf) ) + cleanstop(7); + } + /* ** activate the installed print-function to visualize ** the system- and process-level statistics @@ -778,7 +823,7 @@ rawread(void) { lastcmd = (vis.show_samp)(rr.curtime, rr.interval, - &devtstat, &sstat, + &devtstat, &sstat, &bstats, rr.nexit, rr.noverflow, flags); } while (!isregular && @@ -790,6 +835,9 @@ rawread(void) free(devtstat.taskall); free(devtstat.procall); free(devtstat.procactive); + free(bstats.bpfall); + bstats.bpfall = NULL; + bstats.nbpfall = 0; switch (lastcmd) { @@ -878,7 +926,7 @@ getrawsstat(int rawfd, struct sstat *sp, int complen) } /* -** read the process-level statistics from the current offset +** read the bpf statistics from the current offset */ static int getrawtstat(int rawfd, struct tstat *pp, int complen, int ndeviat) @@ -906,7 +954,36 @@ getrawtstat(int rawfd, struct tstat *pp, int complen, int ndeviat) return 1; } -/* +/* +** read the process-level statistics from the current offset +*/ +static int +getrawbstat(int rawfd, struct bstat *bp, int complen, int nbpfall) +{ + Byte *compbuf; + unsigned long uncomplen = sizeof(struct bstat) * nbpfall; + 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 *)bp, &uncomplen, compbuf, complen); + + testcompval(rv, "uncompress"); + + free(compbuf); + + return 1; +} + +/* ** verify if a particular ascii-string is in the format yyyymmdd */ static int diff --git a/rawlog.h b/rawlog.h index 951736d5..07b39847 100644 --- a/rawlog.h +++ b/rawlog.h @@ -30,7 +30,8 @@ struct rawheader { 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 bstatlen; /* length of struct bstat */ + char cfuture[4]; /* future use */ unsigned int pagesize; /* size of memory page (bytes) */ int supportflags; /* used features */ @@ -59,5 +60,7 @@ struct rawrecord { unsigned int totzomb; /* number of zombie processes */ unsigned int nexit; /* number of exited processes */ unsigned int noverflow; /* number of overflow processes */ - unsigned int ifuture[6]; /* future use */ + unsigned int bcomplen; /* length of compressed bstat's */ + unsigned int totbpf; /* number of bstat's */ + unsigned int ifuture[4]; /* future use */ }; diff --git a/showgeneric.c b/showgeneric.c index a0800f6e..7404d4ba 100644 --- a/showgeneric.c +++ b/showgeneric.c @@ -1,9 +1,9 @@ /* -** ATOP - System & Process Monitor +** 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. ** ========================================================================== @@ -28,7 +28,7 @@ ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ** -------------------------------------------------------------------------- -** +** ** $Log: showgeneric.c,v $ ** Revision 1.71 2010/10/25 19:08:32 gerlof ** When the number of lines is too small for the system-level @@ -277,6 +277,7 @@ #include "atop.h" #include "photoproc.h" #include "photosyst.h" +#include "photobpf.h" #include "showgeneric.h" #include "showlinux.h" @@ -321,11 +322,11 @@ static void generic_init(void); static int (*procsort[])(const void *, const void *) = { - [MSORTCPU&0x1f]=compcpu, - [MSORTMEM&0x1f]=compmem, - [MSORTDSK&0x1f]=compdsk, - [MSORTNET&0x1f]=compnet, - [MSORTGPU&0x1f]=compgpu, + [MSORTCPU&0x1f]=compcpu, + [MSORTMEM&0x1f]=compmem, + [MSORTDSK&0x1f]=compdsk, + [MSORTNET&0x1f]=compnet, + [MSORTGPU&0x1f]=compgpu, }; extern proc_printpair ownprocs[]; @@ -335,12 +336,15 @@ extern proc_printpair ownprocs[]; */ int startoffset; +int bpflines = 3; + /* ** print the deviation-counters on process- and system-level */ char generic_samp(time_t curtime, int nsecs, - struct devtstat *devtstat, struct sstat *sstat, + struct devtstat *devtstat, struct sstat *sstat, + struct bstats *bstats, int nexit, unsigned int noverflow, char flag) { static int callnr = 0; @@ -370,7 +374,7 @@ generic_samp(time_t curtime, int nsecs, ** 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 */ @@ -416,6 +420,11 @@ generic_samp(time_t curtime, int nsecs, char threadallowed = 0; + /* + ** reserve 2 lines for proc and bpflines+1 lines for bpf programs + */ + int proc_bpf_lines = + (bstats && bstats->nbpfall) ? bpflines + 1 : 2; if (callnr == 0) /* first call? */ generic_init(); @@ -425,7 +434,7 @@ generic_samp(time_t curtime, int nsecs, startoffset = 0; /* - ** compute the total capacity of this system for the + ** compute the total capacity of this system for the ** four main resources */ totalcap(&syscap, sstat, devtstat->procactive, devtstat->nprocactive); @@ -508,11 +517,11 @@ generic_samp(time_t curtime, int nsecs, int lenavail = (screen ? COLS : linelen) - 50 - seclen - utsnodenamelen; int len1 = lenavail / 3; - int len2 = lenavail - len1 - len1; + int len2 = lenavail - len1 - len1; printg("ATOP - %s%*s%s %s%*s%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%" - "*s%s elapsed", - utsname.nodename, len1, "", + "*s%s elapsed", + utsname.nodename, len1, "", format1, format2, len1, "", threadview ? MTHREAD : '-', fixedhead ? MSYSFIXED : '-', @@ -545,7 +554,7 @@ generic_samp(time_t curtime, int nsecs, if (noverflow) { - snprintf(statbuf, sizeof statbuf, + snprintf(statbuf, sizeof statbuf, "Only %d exited processes handled " "-- %u skipped!", nexit, noverflow); @@ -574,7 +583,7 @@ generic_samp(time_t curtime, int nsecs, ** limit the number of variable resource lines ** and try again */ - if (screen && curline+2 > LINES) + if (screen && curline+proc_bpf_lines > LINES) { curline = 2; @@ -583,7 +592,7 @@ generic_samp(time_t curtime, int nsecs, move(curline, 0); limitedlines(); - + curline = prisyst(sstat, curline, nsecs, avgval, fixedhead, &syssel, &autoorder, maxcpulines, maxgpulines, @@ -596,7 +605,7 @@ generic_samp(time_t curtime, int nsecs, ** if system-wide statistics still do not fit, ** the window is really to small */ - if (curline+2 > LINES) + if (curline+proc_bpf_lines > LINES) { endwin(); // finish curses interface @@ -668,6 +677,10 @@ generic_samp(time_t curtime, int nsecs, } } + if (screen) + move(curline, 0); + curline = pribpf(bstats, curline); + /* ** select the required list with tasks to be shown ** @@ -747,7 +760,7 @@ generic_samp(time_t curtime, int nsecs, "Malloc failed for %d pcum ptrs\n", nproc); for (i=0; i < nproc; i++) - { + { /* fill pointers */ pcumlist[i] = tpcumlist+i; } @@ -852,7 +865,7 @@ generic_samp(time_t curtime, int nsecs, suppressexit ) continue; - sellist[nsel++] = curlist[i]; + sellist[nsel++] = curlist[i]; } curlist = sellist; @@ -863,7 +876,7 @@ generic_samp(time_t curtime, int nsecs, } /* - ** sort the list in required order + ** sort the list in required order ** (default CPU-consumption) and print the list */ if (showorder == MSORTAUTO) @@ -949,7 +962,7 @@ generic_samp(time_t curtime, int nsecs, for (t = pcur - tall + 1; t < devtstat->ntaskall && pcur->gen.tgid && - pcur->gen.tgid == + pcur->gen.tgid == (tall+t)->gen.tgid; t++) { @@ -1046,7 +1059,7 @@ generic_samp(time_t curtime, int nsecs, if (tsklist) free(tsklist); if (sellist) free(sellist); - return lastchar; + return lastchar; /* ** stop it @@ -1983,7 +1996,7 @@ generic_samp(time_t curtime, int nsecs, break; /* - ** per-process PSS calculation wanted + ** per-process PSS calculation wanted */ case MCALCPSS: if (calcpss) @@ -1999,7 +2012,7 @@ generic_samp(time_t curtime, int nsecs, break; /* - ** per-thread WCHAN definition + ** per-thread WCHAN definition */ case MGETWCHAN: if (getwchan) @@ -2126,7 +2139,7 @@ generic_samp(time_t curtime, int nsecs, break; /* - ** reset statistics + ** reset statistics */ case MRESET: getalarm(0); /* restart the clock */ @@ -2183,7 +2196,7 @@ generic_samp(time_t curtime, int nsecs, /* ** handle arrow up to go one line up */ - case KEY_UP: + case KEY_UP: if (firstproc > 0) firstproc -= 1; break; @@ -2212,7 +2225,7 @@ generic_samp(time_t curtime, int nsecs, ** handle screen resize */ case KEY_RESIZE: - snprintf(statbuf, sizeof statbuf, + snprintf(statbuf, sizeof statbuf, "Window resized to %dx%d...", COLS, LINES); statmsg = statbuf; @@ -2268,7 +2281,7 @@ cumusers(struct tstat **curprocs, struct tstat *curusers, int numprocs) if ((*curprocs)->gen.state == 'E' && suppressexit) continue; - + if ( curusers->gen.ruid != (*curprocs)->gen.ruid ) { if (curusers->gen.pid) @@ -2356,7 +2369,7 @@ cumconts(struct tstat **curprocs, struct tstat *curconts, int numprocs) if ((*curprocs)->gen.state == 'E' && suppressexit) continue; - + if ( strcmp(curconts->gen.container, (*curprocs)->gen.container) != 0) { @@ -2593,7 +2606,7 @@ limitedlines(void) } /* -** get a numerical value from the user and verify +** get a numerical value from the user and verify */ static long getnumval(char *ask, long valuenow, int statline) @@ -3022,7 +3035,7 @@ showhelp(int helpline) scrollok(helpwin, 1); /* - ** show help-lines + ** show help-lines */ for (i=0, shown=0; i < helplines; i++, shown++) { @@ -3035,7 +3048,7 @@ showhelp(int helpline) { wmove (helpwin, winlines-1, 0); wclrtoeol(helpwin); - wprintw (helpwin, "Press 'q' to leave help, " + wprintw (helpwin, "Press 'q' to leave help, " "space for next page or " "other key for next line... "); @@ -3283,6 +3296,12 @@ do_maxcont(char *name, char *val) maxcontlines = get_posval(name, val); } +void +do_bpflines(char *name, char *val) +{ + bpflines = get_posval(name, val); +} + struct colmap { char *colname; diff --git a/various.c b/various.c index 3d5f1b84..3fa8e558 100644 --- a/various.c +++ b/various.c @@ -5,7 +5,7 @@ ** 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. +** time-of-day, the cpu-time consumption and the memory-occupation. ** ========================================================================== ** Author: Gerlof Langeveld ** E-mail: gerlof.langeveld@atoptool.nl @@ -115,6 +115,7 @@ #include "atop.h" #include "acctproc.h" +#include "photobpf.h" /* ** Function convtime() converts a value (number of seconds since @@ -193,7 +194,7 @@ getbranchtime(char *itim, time_t *newtime) 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_mday < 1 || tm.tm_mday > 31 || tm.tm_hour < 0 || tm.tm_hour > 23 || tm.tm_min < 0 || tm.tm_min > 59 ) { @@ -250,7 +251,7 @@ getbranchtime(char *itim, time_t *newtime) /* ** Normalize an epoch time with the number of seconds within a day -** Return-value: Normalized epoch +** Return-value: Normalized epoch */ time_t normalize_epoch(time_t epoch, long secondsinday) @@ -259,9 +260,9 @@ normalize_epoch(time_t epoch, long secondsinday) localtime_r(&epoch, &tm); // convert epoch to tm - tm.tm_hour = 0; - tm.tm_min = 0; - tm.tm_sec = secondsinday; + tm.tm_hour = 0; + tm.tm_min = 0; + tm.tm_sec = secondsinday; tm.tm_isdst = -1; return mktime(&tm); // convert tm to epoch @@ -269,7 +270,7 @@ normalize_epoch(time_t epoch, long secondsinday) /* -** Function val2valstr() converts a positive value to an ascii-string of a +** 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 string should have a length of width+1. @@ -352,17 +353,17 @@ val2elapstr(int value, char *strvalue) { char *p=strvalue; - if (value >= DAYSECS) + if (value >= DAYSECS) { p+=sprintf(p, "%dd", value/DAYSECS); } - if (value >= HOURSECS) + if (value >= HOURSECS) { p+=sprintf(p, "%dh", (value%DAYSECS)/HOURSECS); } - if (value >= MINSECS) + if (value >= MINSECS) { p+=sprintf(p, "%dm", (value%HOURSECS)/MINSECS); } @@ -396,7 +397,7 @@ val2cpustr(count_t value, char *strvalue) */ value = (value + 500) / 1000; - if (value < MAXSEC) + if (value < MAXSEC) { sprintf(strvalue, "%2lldm%02llds", value/60, value%60); } @@ -407,7 +408,7 @@ val2cpustr(count_t value, char *strvalue) */ value = (value + 30) / 60; - if (value < MAXMIN) + if (value < MAXMIN) { sprintf(strvalue, "%2lldh%02lldm", value/60, value%60); @@ -429,7 +430,7 @@ val2cpustr(count_t value, char *strvalue) } /* -** Function val2Hzstr() converts a value (in MHz) +** 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. @@ -450,7 +451,7 @@ val2Hzstr(count_t value, char *strvalue) if (fval >= 1000.0) // prepare for the future { - prefix='T'; + prefix='T'; fval /= 1000.0; } @@ -520,7 +521,7 @@ val2memstr(count_t value, char *strvalue, int pformat, int avgval, int nsecs) basewidth -= 2; suffix = "/s"; } - + /* ** determine which format will be used on bases of the value itself */ @@ -561,7 +562,7 @@ val2memstr(count_t value, char *strvalue, int pformat, int avgval, int nsecs) case MBFORMAT: sprintf(strvalue, "%*.1lfM%s", - basewidth-1, (double)((double)value/ONEMBYTE), suffix); + basewidth-1, (double)((double)value/ONEMBYTE), suffix); break; case GBFORMAT: @@ -588,7 +589,7 @@ val2memstr(count_t value, char *strvalue, int pformat, int avgval, int nsecs) /* -** Function numeric() checks if the ascii-string contains +** Function numeric() checks if the ascii-string contains ** a numeric (positive) value. ** Returns 1 (true) if so, or 0 (false). */ @@ -607,7 +608,7 @@ numeric(char *ns) /* -** Function getboot() returns the boot-time of this system +** Function getboot() returns the boot-time of this system ** as number of jiffies since 1-1-1970. */ unsigned long long