diff --git a/Makefile b/Makefile index 61d89ef..4ea80b7 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ CRNPATH = /etc/cron.d ROTPATH = /etc/logrotate.d CFLAGS = -O -I. -Wall # -DHTTPSTATS -LDFLAGS = -lncurses -lm -lz +LDFLAGS = -lncurses -lm -lz -static OBJMOD0 = version.o OBJMOD1 = various.o deviate.o procdbase.o OBJMOD2 = acctproc.o photoproc.o photosyst.o rawlog.o ifprop.o parseable.o diff --git a/atop b/atop deleted file mode 100755 index f00850b..0000000 Binary files a/atop and /dev/null differ diff --git a/atop.daily b/atop.daily index e0d994f..b9faef5 100755 --- a/atop.daily +++ b/atop.daily @@ -3,7 +3,7 @@ CURDAY=`date +%Y%m%d` LOGPATH=/var/log/atop BINPATH=/usr/bin PIDFILE=/var/run/atop.pid -INTERVAL=600 # interval 10 minutes +INTERVAL=60 # interval 10 minutes # verify if atop still runs for daily logging # diff --git a/atop.h b/atop.h index da05be3..d4bc5b5 100644 --- a/atop.h +++ b/atop.h @@ -116,6 +116,7 @@ char *val2memstr(count_t, char *, int, int, int); char *val2cpustr(count_t, char *); int compcpu(const void *, const void *); +int compwt(const void *, const void *); int compdsk(const void *, const void *); int compmem(const void *, const void *); int compnet(const void *, const void *); diff --git a/deviate.c b/deviate.c index 4cec6e5..eb713ef 100644 --- a/deviate.c +++ b/deviate.c @@ -272,6 +272,9 @@ deviatproc(struct pstat *aproc, int npresent, devstat->cpu.utime = subcount(curstat->cpu.utime, prestat.cpu.utime); + devstat->wait = + subcount(curstat->wait, prestat.wait); + devstat->dsk.rio = subcount(curstat->dsk.rio, prestat.dsk.rio); devstat->dsk.rsz = diff --git a/photoproc.c b/photoproc.c index bda8542..cb3805d 100644 --- a/photoproc.c +++ b/photoproc.c @@ -132,6 +132,7 @@ static const char rcsid[] = "$Id: photoproc.c,v 1.31 2008/03/06 08:38:14 gerlof Exp $"; + #include #include #include @@ -427,6 +428,7 @@ fillproc(struct pstat *curproc) fclose(fp); } + /* ** store required info in process-structure */ @@ -459,6 +461,8 @@ fillproc(struct pstat *curproc) curproc->mem.rgrow = 0; /* calculated later */ curproc->mem.shtext = (endcode-startcode)/1024; + curproc->wait = io_wait(curproc->gen.pid); + curproc->dsk.rio = dskrio; curproc->dsk.rsz = dskrsz; curproc->dsk.wio = dskwio; @@ -595,3 +599,186 @@ countprocs(void) return nr; } + +/* + * Create a raw netlink socket and bind + * Parts borrowed from kernel doc directory + */ +static int create_nl_socket(int protocol) +{ + int fd; + struct sockaddr_nl local; + + fd = socket(AF_NETLINK, SOCK_RAW, protocol); + if (fd < 0) + return -1; + + memset(&local, 0, sizeof(local)); + local.nl_family = AF_NETLINK; + + if (bind(fd, (struct sockaddr *) &local, sizeof(local)) < 0) + goto error; + + return fd; +error: + close(fd); + return -1; +} + + +/* Parts borrowed from kernel doc directory */ +int send_cmd(int sd, __u16 nlmsg_type, __u32 nlmsg_pid, + __u8 genl_cmd, __u16 nla_type, + void *nla_data, int nla_len) +{ + struct nlattr *na; + struct sockaddr_nl nladdr; + int r, 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(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + while ((r = sendto(sd, buf, buflen, 0, (struct sockaddr *) &nladdr, + sizeof(nladdr))) < buflen) { + if (r > 0) { + buf += r; + buflen -= r; + } else if (errno != EAGAIN) + return -1; + } + return 0; +} + + +/* + * Probe the controller in genetlink to find the family id + * for the TASKSTATS family + * Parts borrowed from kernel doc directory + */ +int get_family_id(int sd) +{ + char name[100]; + struct { + struct nlmsghdr n; + struct genlmsghdr g; + char buf[256]; + } ans; + + int id = 0, rc; + struct nlattr *na; + int rep_len; + + strcpy(name, TASKSTATS_GENL_NAME); + rc = send_cmd(sd, GENL_ID_CTRL, getpid(), CTRL_CMD_GETFAMILY, + CTRL_ATTR_FAMILY_NAME, (void *)name, + strlen(TASKSTATS_GENL_NAME)+1); + + rep_len = recv(sd, &ans, sizeof(ans), 0); + if (ans.n.nlmsg_type == NLMSG_ERROR || + (rep_len < 0) || !NLMSG_OK((&ans.n), rep_len)) + return 0; + + na = (struct nlattr *) GENLMSG_DATA(&ans); + na = (struct nlattr *) ((char *) na + NLA_ALIGN(na->nla_len)); + if (na->nla_type == CTRL_ATTR_FAMILY_ID) { + id = *(__u16 *) NLA_DATA(na); + } + return id; +} + +/* + * Get number of nanoseconds that a process has been waiting for io + * Parts borrowed from kernel doc directory +*/ +__u64 io_wait(int pid) +{ + int rc, rep_len, aggr_len, len2, cmd_type; + __u16 id; + __u32 mypid; + + struct nlattr *na; + static int nl_sd = -1; + int len = 0; + + struct msgtemplate msg; + + if (nl_sd == -1 && (nl_sd = create_nl_socket(NETLINK_GENERIC)) < 0) + /*Couldn't create netlink socket*/ + return 0; + + mypid = getpid(); + id = get_family_id(nl_sd); + if (!id) { + return 0; + } + + cmd_type = TASKSTATS_CMD_ATTR_PID; + + rc = send_cmd(nl_sd, id, mypid, TASKSTATS_CMD_GET, + cmd_type, &pid, sizeof(__u32)); + if (rc < 0) { + goto done; + } + + rep_len = recv(nl_sd, &msg, sizeof(msg), 0); + + if (msg.n.nlmsg_type == NLMSG_ERROR || + !NLMSG_OK((&msg.n), rep_len)) { + //struct nlmsgerr *err = NLMSG_DATA(&msg); + goto done; + } + + rep_len = GENLMSG_PAYLOAD(&msg.n); + + na = (struct nlattr *) GENLMSG_DATA(&msg); + len = 0; + while (len < rep_len) { + len += NLA_ALIGN(na->nla_len); + switch (na->nla_type) { + case TASKSTATS_TYPE_AGGR_TGID: + /* Fall through */ + case TASKSTATS_TYPE_AGGR_PID: + aggr_len = NLA_PAYLOAD(na->nla_len); + len2 = 0; + /* For nested attributes, na follows */ + na = (struct nlattr *) NLA_DATA(na); + while (len2 < aggr_len) { + switch (na->nla_type) { + case TASKSTATS_TYPE_PID: + break; + case TASKSTATS_TYPE_STATS: + return (__u64) ((struct taskstats *) NLA_DATA(na)) -> blkio_delay_total; + default: + break; + } + len2 += NLA_ALIGN(na->nla_len); + na = (struct nlattr *) ((char *) na + len2); + } + break; + + default: + break; + } + na = (struct nlattr *) (GENLMSG_DATA(&msg) + len); + } +done: + close(nl_sd); + return 0; +} diff --git a/photoproc.h b/photoproc.h index ebdd03f..50d6e1d 100644 --- a/photoproc.h +++ b/photoproc.h @@ -26,6 +26,23 @@ #define PNAMLEN 15 #define CMDLEN 68 +/* Stuff for io_delay */ +#include +#include +#include +#include + +/* + * Generic macros for dealing with netlink sockets. Might be duplicated + * elsewhere. It is recommended that commercial grade applications use + * libnl or libnetlink and use the interfaces provided by the library + */ +#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) +/**/ + /* ** structure containing only relevant process-info extracted ** from kernel's process-administration @@ -74,6 +91,9 @@ struct pstat { count_t cfuture[4]; /* reserved for future use */ } dsk; + /* IO WAIT STATS */ + __u64 wait; /* io wait */ + /* MEMORY STATISTICS */ struct mem { count_t minflt; /* number of page-reclaims */ @@ -110,6 +130,14 @@ struct pinfo { struct pstat pstat; /* per-process statistics */ }; +/* Netlink message */ +#define MAX_MSG_SIZE 1024 +struct msgtemplate { + struct nlmsghdr n; + struct genlmsghdr g; + char buf[MAX_MSG_SIZE]; +}; + /* ** prototypes of process-database functions */ @@ -126,5 +154,6 @@ int pdb_srchresidue(struct pstat *, struct pinfo **); */ int deviatproc(struct pstat *, int, struct pstat *, int, int, struct pstat *, int *); -int photoproc(struct pstat *, int); unsigned int countprocs(void); +int photoproc(struct pstat *, int); +__u64 io_wait(int); diff --git a/showgeneric.c b/showgeneric.c index 257e04b..8b17919 100644 --- a/showgeneric.c +++ b/showgeneric.c @@ -244,6 +244,7 @@ static long getnumval(char *, long, int); static int (*procsort[])(const void *, const void *) = { [MSORTCPU]=compcpu, + [MSORTWT] =compwt, [MSORTMEM]=compmem, [MSORTDSK]=compdsk, [MSORTNET]=compnet, @@ -296,6 +297,10 @@ generic_samp(time_t curtime, int nsecs, case MSORTDSK: showorder = MSORTDSK; break; + + case MSORTWT: + showorder = MSORTWT; + break; case MSORTNET: showorder = MSORTNET; @@ -316,6 +321,12 @@ generic_samp(time_t curtime, int nsecs, showorder = MSORTCPU; break; + case MPROCWT: + + showtype = MPROCWT; + showorder = MSORTWT; + break; + case MPROCDSK: if ( !(supportflags & (PATCHSTAT|IOSTAT)) ) { @@ -746,6 +757,13 @@ generic_samp(time_t curtime, int nsecs, showorder = MSORTCPU; firstproc = 0; break; + /* + ** sort in IO delay order + */ + case MSORTWT: + showorder = MSORTWT; + firstproc = 0; + break; /* ** sort in memory-consumption order @@ -794,6 +812,17 @@ generic_samp(time_t curtime, int nsecs, firstproc = 0; break; + /* + ** IO wait figures per process + */ + case MPROCWT: + showtype = MPROCWT; + + //if (showorder != MSORTAUTO) + showorder = MSORTWT; + + firstproc = 0; + break; /* ** memory-specific figures per process @@ -1506,7 +1535,8 @@ static struct helptext { {"Figures shown for active processes:\n", ' '}, {"\t'%c' - generic info (default)\n", MPROCGEN}, {"\t'%c' - memory details\n", MPROCMEM}, - {"\t'%c' - disk details\n", MPROCDSK}, + {"\t'%c' - IO wait details\n", MPROCWT}, + {"\t'%c' - disk details\n", MPROCDSK}, {"\t'%c' - network details\n", MPROCNET}, {"\t'%c' - scheduling and thread-group info\n", MPROCSCH}, {"\t'%c' - various info (ppid, user/group, date/time, status, " @@ -1521,7 +1551,8 @@ static struct helptext { {"Sort list of active processes in order of:\n", ' '}, {"\t'%c' - cpu activity\n", MSORTCPU}, {"\t'%c' - memory consumption\n", MSORTMEM}, - {"\t'%c' - disk activity\n", MSORTDSK}, + {"\t'%c' - disk activity\n", MSORTDSK}, + {"\t'%c' - IO wait time\n", MSORTWT}, {"\t'%c' - network activity\n", MSORTNET}, {"\t'%c' - most active system resource (auto mode)\n", MSORTAUTO}, {"\n", ' '}, @@ -1653,7 +1684,9 @@ generic_usage(void) printf("\t -%c show memory-related process-info\n", MPROCMEM); printf("\t -%c show disk-related process-info\n", - MPROCDSK); + MPROCDSK); + printf("\t -%c show IO wait related process-info\n", + MPROCWT); printf("\t -%c show network-related process-info\n", MPROCNET); printf("\t -%c show scheduling-related process-info\n", diff --git a/showgeneric.h b/showgeneric.h index 0712962..5003e44 100644 --- a/showgeneric.h +++ b/showgeneric.h @@ -30,6 +30,7 @@ struct syscap { count_t availmem; count_t availdsk; count_t availnet; + __u64 availwt; }; struct selection { @@ -58,11 +59,13 @@ struct selection { #define MPROCSCH 's' #define MPROCVAR 'v' #define MPROCARG 'c' +#define MPROCWT 'w' #define MCUMUSER 'u' #define MCUMPROC 'p' #define MSORTCPU 'C' +#define MSORTWT 'W' #define MSORTDSK 'D' #define MSORTMEM 'M' #define MSORTNET 'N' diff --git a/showlinux.c b/showlinux.c index 20fe406..b4b1bc7 100644 --- a/showlinux.c +++ b/showlinux.c @@ -235,8 +235,9 @@ int almostcrit = 80; /* percentage */ ** table with column headers for sorted process list */ static char *columnhead[] = { - [MSORTCPU]= "CPU", [MSORTMEM]= "MEM", - [MSORTDSK]= "DSK", [MSORTNET]= "NET", + [MSORTCPU]= "CPU", [MSORTWT]= "IOW", + [MSORTMEM]= "MEM", [MSORTDSK]= "DSK", + [MSORTNET]= "NET" }; /* @@ -332,10 +333,14 @@ char *totprochdr = "\nNPROCS SYSCPU USRCPU VSIZE RSIZE RDDSK " char *totproclin = "%s %s %s %s %s %s %s %s %s " "%3.0lf%% %.14s\n"; /***************************************************************************/ +char *procwthdr = "\n PID WAIT(ms) USERNAME COMMAND-LINE " + " %4d/%-4d\n"; +char *procwtlina = "%5d %9llu %-8.14s %.54s\n"; +/***************************************************************************/ /* ** calculate the total consumption on system-level for the -** four main resources +** five main resources */ void totalcap(struct syscap *psc, struct sstat *sstat, struct pstat *pstat, int nact) @@ -359,6 +364,7 @@ totalcap(struct syscap *psc, struct sstat *sstat, struct pstat *pstat, int nact) /* ** calculate total number of accesses which have been ** issued by the active processes for disk and for network + ** Also calculate total IO wait */ for (psc->availnet=psc->availdsk=0, i=0; i < nact; i++) { @@ -371,6 +377,8 @@ totalcap(struct syscap *psc, struct sstat *sstat, struct pstat *pstat, int nact) psc->availdsk += (pstat+i)->dsk.rio; psc->availdsk += (pstat+i)->dsk.wio; + + psc->availwt += (pstat+i)->wait; } } else @@ -379,6 +387,8 @@ totalcap(struct syscap *psc, struct sstat *sstat, struct pstat *pstat, int nact) { psc->availdsk += (pstat+i)->dsk.rsz; psc->availdsk += (pstat+i)->dsk.wsz; + + psc->availwt += (pstat+i)->wait; } } } @@ -645,6 +655,58 @@ showgenproc(struct pstat *curstat, double perc, int nsecs, int avgval) } /* +** print a line of IO wait figures about one process +*/ +void +showwtproc(struct pstat *curstat, double perc, int nsecs, int avgval) +{ + struct passwd *pwd; + char numuser[16], *username; + + /* + ** determine the user-name of process + */ + if ( (pwd = getpwuid(curstat->gen.ruid)) == NULL) + { + snprintf(numuser, sizeof numuser, "%u", curstat->gen.ruid); + username = numuser; + } + else + { + username = pwd->pw_name; + } + + if (curstat->gen.state != 'E') + { + /* + ** show process-info of active process + */ + printg(procwtlina, + curstat->gen.pid, + (__u64) curstat->wait / 1000000, + username, + curstat->gen.cmdline[0] ? + curstat->gen.cmdline : curstat->gen.name); + } + else + { + /* + ** show process-info of exited process + */ + if (curstat->gen.pid == 0) + ; + else + printg(procwtlina, + curstat->gen.pid, + (__u64) curstat->wait, + username, + curstat->gen.cmdline[0] ? + curstat->gen.cmdline : curstat->gen.name); + + } + +} +/* ** print a line of memory-figures about one process */ void @@ -1276,6 +1338,10 @@ priphead(int curlist, int totlist, char showtype, char showorder, char autosort) printg(totprochdr, columnprefix, columnhead[order], curlist, totlist); break; + + case MPROCWT: + printg(procwthdr, curlist, totlist); + break; } } @@ -1339,6 +1405,19 @@ priproc(struct pstat *pstat, int firstproc, int lastproc, int curline, } break; + case MSORTWT: + perc = 0.0; + + if (sb->availwt) + { + perc = (double)curstat->wait * + 100.0 / sb->availwt; + + if (perc > 100.0) + perc = 100.0; + } + break; + case MSORTDSK: perc = 0.0; @@ -1426,6 +1505,10 @@ priproc(struct pstat *pstat, int firstproc, int lastproc, int curline, showgenproc(curstat, perc, nsecs, avgval); break; + case MPROCWT: + showwtproc(curstat, perc, nsecs, avgval); + break; + case MPROCMEM: showmemproc(curstat, perc, nsecs, avgval); break; @@ -2015,6 +2098,17 @@ compcpu(const void *a, const void *b) } int +compwt(const void *a, const void *b) +{ + register __u64 acpu = ((struct pstat *)a)->wait; + register __u64 bcpu = ((struct pstat *)b)->wait; + + if (acpu < bcpu) return 1; + if (acpu > bcpu) return -1; + return compcpu(a, b); +} + +int compdsk(const void *a, const void *b) { register count_t adsk = ((struct pstat *)a)->dsk.rio +