Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1755704AbcDKXhe (ORCPT ); Mon, 11 Apr 2016 19:37:34 -0400 Received: from mail-pa0-f41.google.com ([209.85.220.41]:35203 "EHLO mail-pa0-f41.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1755453AbcDKXg0 (ORCPT ); Mon, 11 Apr 2016 19:36:26 -0400 From: Andrey Vagin To: linux-kernel@vger.kernel.org Cc: Andrey Vagin , Oleg Nesterov , Andrew Morton , Cyrill Gorcunov , Pavel Emelyanov , Roger Luethi , Arnd Bergmann , Arnaldo Carvalho de Melo , David Ahern , Andy Lutomirski , Pavel Odintsov Subject: [PATCH 13/15] selftest: check the task_diag functinonality Date: Mon, 11 Apr 2016 16:35:53 -0700 Message-Id: <1460417755-18201-14-git-send-email-avagin@openvz.org> X-Mailer: git-send-email 2.5.5 In-Reply-To: <1460417755-18201-1-git-send-email-avagin@openvz.org> References: <1460417755-18201-1-git-send-email-avagin@openvz.org> Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 15181 Lines: 572 Here are two test (example) programs. task_diag - request information for two processes. test_diag_all - request information about all processes v2: Fixes from David Ahern: * task_diag: Fix 8-byte alignment for vma and vma_stats Signed-off-by: Andrey Vagin --- tools/testing/selftests/Makefile | 1 + tools/testing/selftests/task_diag/.gitignore | 4 + tools/testing/selftests/task_diag/Makefile | 16 ++ tools/testing/selftests/task_diag/_run.sh | 21 +++ tools/testing/selftests/task_diag/fork.c | 30 ++++ tools/testing/selftests/task_diag/run.sh | 1 + tools/testing/selftests/task_diag/task_diag.h | 1 + tools/testing/selftests/task_diag/task_diag_all.c | 150 ++++++++++++++++++ tools/testing/selftests/task_diag/task_diag_comm.c | 172 +++++++++++++++++++++ tools/testing/selftests/task_diag/task_diag_comm.h | 34 ++++ tools/testing/selftests/task_diag/task_proc_all.c | 35 +++++ 11 files changed, 465 insertions(+) create mode 100644 tools/testing/selftests/task_diag/.gitignore create mode 100644 tools/testing/selftests/task_diag/Makefile create mode 100755 tools/testing/selftests/task_diag/_run.sh create mode 100644 tools/testing/selftests/task_diag/fork.c create mode 100755 tools/testing/selftests/task_diag/run.sh create mode 120000 tools/testing/selftests/task_diag/task_diag.h create mode 100644 tools/testing/selftests/task_diag/task_diag_all.c create mode 100644 tools/testing/selftests/task_diag/task_diag_comm.c create mode 100644 tools/testing/selftests/task_diag/task_diag_comm.h create mode 100644 tools/testing/selftests/task_diag/task_proc_all.c diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index b04afc3..b399c8b 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -29,6 +29,7 @@ TARGETS += user TARGETS += vm TARGETS += x86 TARGETS += zram +TARGETS += task_diag #Please keep the TARGETS list alphabetically sorted # Run "make quicktest=1 run_tests" or # "make quicktest=1 kselftest from top level Makefile diff --git a/tools/testing/selftests/task_diag/.gitignore b/tools/testing/selftests/task_diag/.gitignore new file mode 100644 index 0000000..f963a1f --- /dev/null +++ b/tools/testing/selftests/task_diag/.gitignore @@ -0,0 +1,4 @@ +task_diag +task_diag_all +task_proc_all +fork diff --git a/tools/testing/selftests/task_diag/Makefile b/tools/testing/selftests/task_diag/Makefile new file mode 100644 index 0000000..69b7934 --- /dev/null +++ b/tools/testing/selftests/task_diag/Makefile @@ -0,0 +1,16 @@ +all: task_diag_all fork task_proc_all fork + +CFLAGS += -g -Wall -O2 -I/usr/include/libnl3 +LDFLAGS += -lnl-3 +TEST_PROGS := run.sh +include ../lib.mk + +task_diag_all.o: task_diag_all.c task_diag_comm.h +task_diag_comm.o: task_diag_comm.c task_diag_comm.h + +task_diag_all: task_diag_all.o task_diag_comm.o +fork: fork.c +task_proc_all: task_proc_all.c + +clean: + rm -rf task_diag task_diag_all task_diag_comm.o task_diag_all.o task_diag.o fork task_proc_all diff --git a/tools/testing/selftests/task_diag/_run.sh b/tools/testing/selftests/task_diag/_run.sh new file mode 100755 index 0000000..3f541fe --- /dev/null +++ b/tools/testing/selftests/task_diag/_run.sh @@ -0,0 +1,21 @@ +#!/bin/sh +set -o pipefail +set -e -x + +./fork 1000 + +nprocesses=`./task_diag_all all --maps | grep 'pid.*tgid.*ppid.*comm fork$' | wc -l` +nthreads=`./task_diag_all All --smaps --cred | grep 'pid.*tgid.*ppid.*comm fork$' | wc -l` +nchildren=`./task_diag_all children --pid 1 | grep 'pid.*tgid.*ppid.*comm fork$' | wc -l` + +./task_diag_all one --pid 1 --cred + +killall -9 fork + +[ "$nthreads" -eq 1000 ] && +[ "$nprocesses" -eq 1000 ] && +[ "$nchildren" -eq 1000 ] && +true || { + echo "Unexpected number of tasks $nthreads:$nprocesses" 1>&2 + exit 1 +} diff --git a/tools/testing/selftests/task_diag/fork.c b/tools/testing/selftests/task_diag/fork.c new file mode 100644 index 0000000..c6e17d1 --- /dev/null +++ b/tools/testing/selftests/task_diag/fork.c @@ -0,0 +1,30 @@ +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + int i, n; + + if (argc < 2) + return 1; + + n = atoi(argv[1]); + for (i = 0; i < n; i++) { + pid_t pid; + + pid = fork(); + if (pid < 0) { + printf("Unable to fork: %m\n"); + return 1; + } + if (pid == 0) { + while (1) + sleep(1000); + return 0; + } + } + + return 0; +} diff --git a/tools/testing/selftests/task_diag/run.sh b/tools/testing/selftests/task_diag/run.sh new file mode 100755 index 0000000..28a8550 --- /dev/null +++ b/tools/testing/selftests/task_diag/run.sh @@ -0,0 +1 @@ +unshare -p -f -m --mount-proc ./_run.sh && { echo PASS; exit 0; } || { echo FAIL; exit 1; } diff --git a/tools/testing/selftests/task_diag/task_diag.h b/tools/testing/selftests/task_diag/task_diag.h new file mode 120000 index 0000000..d20a38c --- /dev/null +++ b/tools/testing/selftests/task_diag/task_diag.h @@ -0,0 +1 @@ +../../../../include/uapi/linux/task_diag.h \ No newline at end of file diff --git a/tools/testing/selftests/task_diag/task_diag_all.c b/tools/testing/selftests/task_diag/task_diag_all.c new file mode 100644 index 0000000..d865207 --- /dev/null +++ b/tools/testing/selftests/task_diag/task_diag_all.c @@ -0,0 +1,150 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "task_diag.h" +#include "task_diag_comm.h" + +#ifndef SOL_NETLINK +#define SOL_NETLINK 270 +#endif + +#ifndef NETLINK_SCM_PID +#define NETLINK_SCM_PID 11 +#endif + +static void usage(char *name) +{ + pr_err("Usage: %s command [options]", name); + pr_err( +"Commands:\n" +"\tall - dump all processes\n" +"\tAll - dump all threads\n" +"\tthreads - dump all thread for the specified process\n" +"\tchildren - dump all thread for the specified process\n" +"\tone - dump the specified process\n" +"Options:\n" +"\t-p|--pid - PID of the required process\n" +"\t-m|--maps - dump memory regions\n" +"\t-s|--smaps - dump statistics for memory regions\n" +"\t-c|--cred - dump credentials" +); +} +int main(int argc, char *argv[]) +{ + int exit_status = 1, fd; + struct task_diag_pid *req; + char nl_req[4096]; + struct nlmsghdr *hdr = (void *)nl_req; + int last_pid = 0; + int opt, idx; + int err, size = 0; + static const char short_opts[] = "p:cms"; + static struct option long_opts[] = { + { "pid", required_argument, 0, 'p' }, + { "maps", no_argument, 0, 'm' }, + { "smaps", no_argument, 0, 's' }, + { "cred", no_argument, 0, 'c' }, + {}, + }; + + hdr->nlmsg_len = nlmsg_total_size(0); + + req = nlmsg_data(hdr); + size += nla_total_size(sizeof(*req)); + + hdr->nlmsg_len += size; + + + req->show_flags = TASK_DIAG_SHOW_BASE; + + if (argc < 2) { + pr_err("Usage: %s type pid scm_pid", argv[0]); + return 1; + } + + req->pid = 0; /* dump all tasks by default */ + + switch (argv[1][0]) { + case 'c': + req->dump_strategy = TASK_DIAG_DUMP_CHILDREN; + break; + case 't': + req->dump_strategy = TASK_DIAG_DUMP_THREAD; + break; + case 'o': + req->dump_strategy = TASK_DIAG_DUMP_ONE; + break; + case 'a': + req->dump_strategy = TASK_DIAG_DUMP_ALL; + req->pid = 0; + break; + case 'A': + req->dump_strategy = TASK_DIAG_DUMP_ALL_THREAD; + req->pid = 0; + break; + default: + usage(argv[0]); + return 1; + } + + while (1) { + idx = -1; + opt = getopt_long(argc, argv, short_opts, long_opts, &idx); + if (opt == -1) + break; + switch (opt) { + case 'p': + req->pid = atoi(optarg); + break; + case 'c': + req->show_flags |= TASK_DIAG_SHOW_CRED; + break; + case 'm': + req->show_flags |= TASK_DIAG_SHOW_VMA; + break; + case 's': + req->show_flags |= TASK_DIAG_SHOW_VMA_STAT | TASK_DIAG_SHOW_VMA; + break; + default: + usage(argv[0]); + return 1; + } + } + + fd = open("/proc/task-diag", O_RDWR); + if (fd < 0) + return -1; + + if (write(fd, hdr, hdr->nlmsg_len) != hdr->nlmsg_len) + return -1; + + while (1) { + char buf[163840]; + size = read(fd, buf, sizeof(buf)); + + if (size < 0) + goto err; + + if (size == 0) + break; + + err = nlmsg_receive(buf, size, &show_task, &last_pid); + if (err < 0) + goto err; + + if (err == 0) + break; + } + + exit_status = 0; +err: + return exit_status; +} diff --git a/tools/testing/selftests/task_diag/task_diag_comm.c b/tools/testing/selftests/task_diag/task_diag_comm.c new file mode 100644 index 0000000..65f2536 --- /dev/null +++ b/tools/testing/selftests/task_diag/task_diag_comm.c @@ -0,0 +1,172 @@ +#include +#include +#include + +#include +#include +#include + +#include "task_diag.h" +#include "task_diag_comm.h" + +int quiet; + +#define PSS_SHIFT 12 + +int nlmsg_receive(void *buf, int len, int (*cb)(struct nlmsghdr *, void *), void *args) +{ + struct nlmsghdr *hdr; + + for (hdr = (struct nlmsghdr *)buf; + NLMSG_OK(hdr, len); hdr = NLMSG_NEXT(hdr, len)) { + + if (hdr->nlmsg_type == NLMSG_DONE) { + int *len = (int *)NLMSG_DATA(hdr); + + if (*len < 0) { + pr_err("ERROR %d reported by netlink (%s)\n", + *len, strerror(-*len)); + return *len; + } + + return 0; + } + + if (hdr->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr); + + if (hdr->nlmsg_len - sizeof(*hdr) < sizeof(struct nlmsgerr)) { + pr_err("ERROR truncated\n"); + return -1; + } + + if (err->error == 0) + return 0; + + return -1; + } + if (cb && cb(hdr, args)) + return -1; + } + + return 1; +} + +int show_task(struct nlmsghdr *hdr, void *arg) +{ + int msg_len; + struct msgtemplate *msg; + struct task_diag_msg *diag_msg; + struct nlattr *na; + int *last_pid = arg; + int len; + + msg_len = NLMSG_PAYLOAD(hdr, 0); + + msg = (struct msgtemplate *)hdr; + diag_msg = NLMSG_DATA(msg); + +#if 1 + if (diag_msg->pid != *last_pid) + pr_info("Start getting information about %d\n", diag_msg->pid); + else + pr_info("Continue getting information about %d\n", diag_msg->pid); +#endif + *last_pid = diag_msg->pid; + + na = ((void *) diag_msg) + NLMSG_ALIGN(sizeof(*diag_msg)); + len = NLMSG_ALIGN(sizeof(*diag_msg)); + while (len < msg_len) { + len += NLA_ALIGN(na->nla_len); + switch (na->nla_type) { + case TASK_DIAG_BASE: + { + struct task_diag_base *msg; + + /* For nested attributes, na follows */ + msg = NLA_DATA(na); + pr_info("pid %5d tgid %5d ppid %5d sid %5d pgid %5d comm %s\n", + msg->pid, msg->tgid, msg->ppid, msg->sid, msg->pgid, msg->comm); + } + break; + + case TASK_DIAG_CRED: + { + struct task_diag_creds *creds; + + creds = NLA_DATA(na); + pr_info("uid: %d %d %d %d\n", creds->uid, + creds->euid, creds->suid, creds->fsuid); + pr_info("gid: %d %d %d %d\n", creds->uid, + creds->euid, creds->suid, creds->fsuid); + pr_info("CapInh: %08x%08x\n", + creds->cap_inheritable.cap[1], + creds->cap_inheritable.cap[0]); + pr_info("CapPrm: %08x%08x\n", + creds->cap_permitted.cap[1], + creds->cap_permitted.cap[0]); + pr_info("CapEff: %08x%08x\n", + creds->cap_effective.cap[1], + creds->cap_effective.cap[0]); + pr_info("CapBnd: %08x%08x\n", creds->cap_bset.cap[1], + creds->cap_bset.cap[0]); + } + break; + + case TASK_DIAG_VMA: + { + struct task_diag_vma *vma_tmp, vma; + + task_diag_for_each_vma(vma_tmp, na) { + char *name; + struct task_diag_vma_stat *stat_tmp, stat; + + name = task_diag_vma_name(vma_tmp); + if (name == NULL) + name = ""; + + memcpy(&vma, vma_tmp, sizeof(vma)); + pr_info("%016llx-%016llx %016llx %s\n", + vma.start, vma.end, vma.vm_flags, name); + + stat_tmp = task_diag_vma_stat(vma_tmp); + if (stat_tmp) + memcpy(&stat, stat_tmp, sizeof(stat)); + else + memset(&stat, 0, sizeof(stat)); + + pr_info( + "Size: %8llu kB\n" + "Rss: %8llu kB\n" + "Pss: %8llu kB\n" + "Shared_Clean: %8llu kB\n" + "Shared_Dirty: %8llu kB\n" + "Private_Clean: %8llu kB\n" + "Private_Dirty: %8llu kB\n" + "Referenced: %8llu kB\n" + "Anonymous: %8llu kB\n" + "AnonHugePages: %8llu kB\n" + "Swap: %8llu kB\n", + (vma.end - vma.start) >> 10, + stat.resident >> 10, + (stat.pss >> (10 + PSS_SHIFT)), + stat.shared_clean >> 10, + stat.shared_dirty >> 10, + stat.private_clean >> 10, + stat.private_dirty >> 10, + stat.referenced >> 10, + stat.anonymous >> 10, + stat.anonymous_thp >> 10, + stat.swap >> 10); + } + } + break; + default: + pr_info("Unknown nla_type %d\n", + na->nla_type); + } + na = ((void *) diag_msg) + len; + } + + return 0; +} diff --git a/tools/testing/selftests/task_diag/task_diag_comm.h b/tools/testing/selftests/task_diag/task_diag_comm.h new file mode 100644 index 0000000..40e83b7 --- /dev/null +++ b/tools/testing/selftests/task_diag/task_diag_comm.h @@ -0,0 +1,34 @@ +#ifndef __TASK_DIAG_COMM__ +#define __TASK_DIAG_COMM__ + +#include + +#include "task_diag.h" + +/* + * 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) + +#define pr_err(fmt, ...) \ + fprintf(stderr, "%s:%d" fmt"\n", __func__, __LINE__, ##__VA_ARGS__) + +#define pr_perror(fmt, ...) \ + fprintf(stderr, fmt " : %m\n", ##__VA_ARGS__) + +extern int quiet; +#define pr_info(fmt, arg...) \ + do { \ + if (!quiet) \ + printf(fmt, ##arg); \ + } while (0) \ + +int nlmsg_receive(void *buf, int len, int (*cb)(struct nlmsghdr *, void *), void *args); +extern int show_task(struct nlmsghdr *hdr, void *arg); + +#endif /* __TASK_DIAG_COMM__ */ diff --git a/tools/testing/selftests/task_diag/task_proc_all.c b/tools/testing/selftests/task_diag/task_proc_all.c new file mode 100644 index 0000000..07ee80c --- /dev/null +++ b/tools/testing/selftests/task_diag/task_proc_all.c @@ -0,0 +1,35 @@ +#include +#include +#include +#include +#include +#include + + +int main(int argc, char **argv) +{ + DIR *d; + int fd, tasks = 0; + struct dirent *de; + char buf[4096]; + + d = opendir("/proc"); + if (d == NULL) + return 1; + + while ((de = readdir(d))) { + if (de->d_name[0] < '0' || de->d_name[0] > '9') + continue; + snprintf(buf, sizeof(buf), "/proc/%s/stat", de->d_name); + fd = open(buf, O_RDONLY); + read(fd, buf, sizeof(buf)); + close(fd); + tasks++; + } + + closedir(d); + + printf("tasks: %d\n", tasks); + + return 0; +} -- 2.5.5