From e29849217715abd12baf05c0acaa3dc7b6a773af Mon Sep 17 00:00:00 2001 From: Fei Li Date: Tue, 22 Nov 2022 17:57:05 +0800 Subject: [PATCH] Collect nropen for each process to monitor if fd leaks This patch collects and displays the current number of opened file descriptors for each process to monitor if fd leaks. As each thread has the same fds with its process, there is no need to collect each thread's fd numbers then. Considering users may set the nr_open limit to a very large number, we use getdents[64] SYSCALL directly to get fd numbers instead of glibc's readdir() for performance consideration. Also, define MAX_OPEN to be 1024 * 1024 for display even if the fd numbers may exceed 1024 * 1024. Signed-off-by: Fei Li --- deviate.c | 1 + json.c | 2 ++ man/atop.1 | 5 +++++ parseable.c | 3 ++- photoproc.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++ photoproc.h | 3 ++- showlinux.c | 3 ++- showlinux.h | 1 + showprocs.c | 28 ++++++++++++++++++++++++++ 9 files changed, 100 insertions(+), 3 deletions(-) diff --git a/deviate.c b/deviate.c index 1652a8f4..ade077a0 100644 --- a/deviate.c +++ b/deviate.c @@ -455,6 +455,7 @@ calcdiff(struct tstat *devstat, const struct tstat *curstat, devstat->cpu.cgcpuweight = curstat->cpu.cgcpuweight; devstat->cpu.cgcpumax = curstat->cpu.cgcpumax; devstat->cpu.cgcpumaxr = curstat->cpu.cgcpumaxr; + devstat->cpu.nropen = curstat->cpu.nropen; if (curstat->cpu.wchan[0]) strcpy(devstat->cpu.wchan, curstat->cpu.wchan); diff --git a/json.c b/json.c index 09ddbf99..be8e9fe7 100644 --- a/json.c +++ b/json.c @@ -1028,6 +1028,7 @@ static void json_print_PRC(char *hp, struct sstat *ss, struct tstat *ps, int nac "\"isproc\": %d, " "\"rundelay\": %lld, " "\"blkdelay\": %lld, " + "\"nropen\": %d, " "\"sleepavg\": %d}", ps->gen.pid, ps->cpu.utime, @@ -1039,6 +1040,7 @@ static void json_print_PRC(char *hp, struct sstat *ss, struct tstat *ps, int nac !!ps->gen.isproc, ps->cpu.rundelay/1000000, ps->cpu.blkdelay*1000/hertz, + ps->cpu.nropen, ps->cpu.sleepavg); } diff --git a/man/atop.1 b/man/atop.1 index a32b0f50..cfd0be42 100644 --- a/man/atop.1 +++ b/man/atop.1 @@ -1661,6 +1661,11 @@ Time that the process has been finished. If the process is still running, this field shows `active'. .PP .TP 9 +.B NROPEN +Current number of opened file descriptors (fds) for each process, at least 0. +As each thread has the same fds with its process, this filed shows `-`. +.PP +.TP 9 .B ENVID Virtual environment identified (OpenVZ only). .PP diff --git a/parseable.c b/parseable.c index ebf2207c..0f6d97b2 100644 --- a/parseable.c +++ b/parseable.c @@ -783,7 +783,7 @@ print_PRC(char *hp, struct sstat *ss, struct tstat *ps, int nact) for (i=0; i < nact; i++, ps++) { printf("%s %d %s %c %u %lld %lld %d %d %d %d %d %d %d %c " - "%llu %s %llu %d %d\n", + "%llu %s %llu %d %d %d\n", hp, ps->gen.pid, spaceformat(ps->gen.name, namout), @@ -802,6 +802,7 @@ print_PRC(char *hp, struct sstat *ss, struct tstat *ps, int nact) ps->cpu.rundelay, spaceformat(ps->cpu.wchan, wchanout), ps->cpu.blkdelay, + ps->cpu.nropen, cgroupv2max(ps->gen.isproc, ps->cpu.cgcpumax), cgroupv2max(ps->gen.isproc, ps->cpu.cgcpumaxr)); } diff --git a/photoproc.c b/photoproc.c index 861ba897..5ae58dd5 100644 --- a/photoproc.c +++ b/photoproc.c @@ -31,11 +31,14 @@ ** -------------------------------------------------------------------------- */ +#define _GNU_SOURCE #include #include +#include #include #include #include +#include #include #include #include @@ -63,6 +66,7 @@ static int proccont(struct tstat *); static void proccmd(struct tstat *); static void procsmaps(struct tstat *); static void procwchan(struct tstat *); +static void procfd(struct tstat *); static count_t procschedstat(struct tstat *); static int proccgroupv2(struct tstat *); static struct cgroupv2vals * @@ -218,6 +222,8 @@ photoproc(struct tstat *tasklist, int maxtask) if (getwchan) procwchan(curtask); + procfd(curtask); + // read network stats from netatop netatop_gettask(curtask->gen.tgid, 'g', curtask); @@ -959,6 +965,57 @@ procschedstat(struct tstat *curtask) return curtask->cpu.rundelay; } +/* +** get current number of opened file descriptors for process +** to monitor if fd leaks. +** Considering users may set the max number of open files to +** a very large number, we use getdents[64] SYSCALL directly +** instead of glibc's readdir() for performance consideration. +** Also, define MAX_OPEN to be 1024 * 1024 for display. +*/ +struct linux_dirent64 { + unsigned long d_ino; + off_t d_off; + unsigned short d_reclen; + unsigned char d_type; + char d_name[]; +}; + +#define MAX_OPEN 1024 * 1024 + +static void +procfd(struct tstat *curtask) +{ + int fd, nread, fd_num = 0; + struct linux_dirent64 *buf, *d; + unsigned int count = 0; + + if ( (fd = open("fd", O_RDONLY | O_DIRECTORY)) != -1) { + count = MAX_OPEN * sizeof(struct linux_dirent64); + buf = malloc(count); + ptrverify(buf, "Malloc failed for process's opened files\n"); + + nread = syscall(SYS_getdents64, fd, buf, count); + if (nread == -1) { + curtask->cpu.nropen = -1; + } else if (nread == 0) { + curtask->cpu.nropen = 0; + } else if (nread > 0) { + for (int bops = 0; bops < nread;) { + d = (struct linux_dirent64 *)((char *)buf + bops); + bops += d->d_reclen; + fd_num++; + } + curtask->cpu.nropen = fd_num - 2; + } + + close(fd); + free(buf); + } else { + curtask->cpu.nropen = 0; + } +} + /* ** CGROUP V2 specific items */ diff --git a/photoproc.h b/photoproc.h index 56d6e48d..80859ac6 100644 --- a/photoproc.h +++ b/photoproc.h @@ -79,7 +79,8 @@ struct tstat { int cgcpuweight; /* cgroup cpu.weight */ int cgcpumax; /* cgroup cpu.max percentage */ int cgcpumaxr; /* restrictive percentage */ - int ifuture[3]; /* reserved for future use */ + int nropen; /* number of opened files */ + int ifuture[2]; /* reserved for future use */ char wchan[16]; /* wait channel string */ count_t rundelay; /* schedstat rundelay (nanosec) */ count_t blkdelay; /* blkio delay (ticks) */ diff --git a/showlinux.c b/showlinux.c index cba40980..a56b62c0 100644 --- a/showlinux.c +++ b/showlinux.c @@ -421,6 +421,7 @@ proc_printdef *allprocpdefs[]= &procprt_STTIME, &procprt_ENDATE, &procprt_ENTIME, + &procprt_NROPEN, &procprt_THR, &procprt_TRUN, &procprt_TSLPI, @@ -1297,7 +1298,7 @@ priphead(int curlist, int totlist, char *showtype, char *showorder, "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", + "NROPEN:5 ST:6 EXC:6 S:6 SORTITEM:10 CMD:10", "built-in varprocs"); make_proc_prints(cmdprocs, MAXITEMS, diff --git a/showlinux.h b/showlinux.h index ad4ab514..cc37f7ef 100644 --- a/showlinux.h +++ b/showlinux.h @@ -416,6 +416,7 @@ extern proc_printdef procprt_STDATE; extern proc_printdef procprt_STTIME; extern proc_printdef procprt_ENDATE; extern proc_printdef procprt_ENTIME; +extern proc_printdef procprt_NROPEN; extern proc_printdef procprt_THR; extern proc_printdef procprt_TRUN; extern proc_printdef procprt_TSLPI; diff --git a/showprocs.c b/showprocs.c index 2ee7dbf3..a7bb1656 100644 --- a/showprocs.c +++ b/showprocs.c @@ -115,6 +115,8 @@ 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_NROPEN_a(struct tstat *, int, int); +char *procprt_NROPEN_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); @@ -1231,6 +1233,32 @@ proc_printdef procprt_ENTIME = { " ENTIME ", "ENTIME", procprt_ENTIME_a, procprt_ENTIME_e, 8 }; /***************************************************************/ char * +procprt_NROPEN_a(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[64]; + + if (curstat->gen.isproc) + sprintf(buf, "%*d", procprt_NROPEN.width, curstat->cpu.nropen); + else + sprintf(buf, "%*s", procprt_NROPEN.width, "-"); + + return buf; +} + +char * +procprt_NROPEN_e(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[64]; + + sprintf(buf, "%*s", procprt_NROPEN.width, "-"); + + return buf; +} + +proc_printdef procprt_NROPEN = + { " NROPEN", "NROPEN", procprt_NROPEN_a, procprt_NROPEN_e, 7 }; +/***************************************************************/ +char * procprt_THR_a(struct tstat *curstat, int avgval, int nsecs) { static char buf[15];