diff --git a/source/tools/detect/sched/getdelays/Makefile b/source/tools/detect/sched/getdelays/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..9a99e7ae6da8489d89b92a02c9d61702ba5d9327 --- /dev/null +++ b/source/tools/detect/sched/getdelays/Makefile @@ -0,0 +1,9 @@ + +newdirs := $(shell find ./ -type d) + +bpfsrcs := $(wildcard bpf/*.bpf.c) +csrcs := $(wildcard *.c) +target := getdelays +EXTRA_LDFLAGS += -lsysak + +include $(SRC)/mk/bpf.mk diff --git a/source/tools/detect/sched/getdelays/README.md b/source/tools/detect/sched/getdelays/README.md new file mode 100644 index 0000000000000000000000000000000000000000..70f95b8103290040475ea03155ac7e4cde7ac903 --- /dev/null +++ b/source/tools/detect/sched/getdelays/README.md @@ -0,0 +1,66 @@ +# getdelays 功能说明 +检测IO/内存回收/内存交换/任务抢占/中断等事件引发的任务延时情况 +getdelays是一个基于netlink和eBPF实现的任务延迟监测分析工具,工具取得指定周期内指定任务由于系统的IO、内存回收、内存交换、任务抢占、中断打断等因素的干扰造成的延时时间。 +## 构建 + +### getdelays内核依赖 +getdelays内核上依赖于CONFIG_TASKSTATS,多数发行版都支持该选项 + +### getdelays编译依赖 +getdelays的编译要求clang10.1以上版本 +``` +make +``` + +## 运行 +要运行getdelays,请保证如下位置之一有内核btf相关文件: +- /sys/kernel/btf/vmlinux +- /boot/vmlinux- +- /lib/modules//vmlinux- +- /lib/modules//build/vmlinux +- /usr/lib/modules//kernel/vmlinux +- /usr/lib/debug/boot/vmlinux- +- /usr/lib/debug/boot/vmlinux-.debug +- /usr/lib/debug/lib/modules//vmlinux + +## 使用 +### 命令行参数 +``` +USAGE: getdelays [--help] <-t TGID|-P PID> [-f ./res.log] [span times] + +EXAMPLES: + getdelays -p 123 # trace pid 123(for threads only) + getdelays -t 123 # trace tgid 123 + getdelays -p 123 -f a.log # record result to a.log (default:/var/log/sysak/getdelays.log) + getdelays -p 123 10 # monitor for 10 seconds + + -f, --logfile=LOGFILE logfile for result + -p, --pid=PID Thread PID to trace + -t, --tid=TGID Process TGID to trace + -v, --verbose Verbose debug output + -?, --help Give this help list + --usage Give a short usage message + -V, --version Print program version + +``` +### 使用示例 +抓取进程8733中所有线程的延时信息,持续60秒,结果存放在当前目录a.log文件中 +``` +sudo sysak getdelays -t 8733 -f a.log 60 +``` +### 结果说明 +``` +$cat a.log +WHAT count delay total delay average (ms) +CPU 12698 10.453 0.001 +IO 0 0.000 0.000 +SWAP 0 0.000 0.000 +RECLAIM 0 0.000 0.000 +IRQ 372 1.877 0.005 +``` +分析上面日志a.log读取的内容,进程8733中各个线程60秒内的延迟信息如下: +- 被抢占12698次,抢占时间10.453ms,平均抢占时间0.001ms +- 发生IO事件0次 +- 发生SWAP-IN事件0次 +- 发生内存回收事件0次 +- 发生中断372次,中断总时间1.877ms,平均每次中断的时间为0.005ms diff --git a/source/tools/detect/sched/getdelays/bpf/getdelays.bpf.c b/source/tools/detect/sched/getdelays/bpf/getdelays.bpf.c new file mode 100644 index 0000000000000000000000000000000000000000..fa510dfe53e97b0716bad5997f98186b3cd49d52 --- /dev/null +++ b/source/tools/detect/sched/getdelays/bpf/getdelays.bpf.c @@ -0,0 +1,110 @@ +#include +#include +#include "../getdelays.h" + +#define TASK_RUNNING 0 +#define _(P) ({typeof(P) val = 0; bpf_probe_read(&val, sizeof(val), &P); val;}) + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 4); + __type(key, u32); + __type(value, struct args); +} argmap SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 10240); + __type(key, u32); + __type(value, struct irq_acct); +} dlymap SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); + __uint(key_size, sizeof(u32)); + __uint(value_size, sizeof(u32)); +} events SEC(".maps"); + +/* + * the return value type can only be assigned to 0, + * so it can be int ,long , long long and the unsinged version + * */ +#define GETARG_FROM_ARRYMAP(map,argp,type,member)({ \ + type retval = 0; \ + int i = 0; \ + argp = bpf_map_lookup_elem(&map, &i); \ + if (argp) { \ + retval = _(argp->member); \ + } \ + retval; \ + }) + +SEC("kretprobe/irq_enter") +int sysak_irq_enter_prog(struct pt_regs *ctx) +{ + u32 cpu, type; + u32 pid, target_pid; + struct irq_acct delays; + struct task_struct *p; + struct args *argp; + + p = (void *)bpf_get_current_task(); + target_pid = GETARG_FROM_ARRYMAP(argmap, argp, pid_t, targ_pid); + type = GETARG_FROM_ARRYMAP(argmap, argp, int, type); + if (type == TASKSTATS_CMD_ATTR_TGID) + pid = _(p->tgid); + else if (type == TASKSTATS_CMD_ATTR_PID) + pid = _(p->pid); + else + return 0; + if (pid == target_pid) { + u64 now; + struct irq_acct *delaysp; + + now = bpf_ktime_get_ns(); + delaysp = bpf_map_lookup_elem(&dlymap, &pid); + if (!delaysp) { + __builtin_memset(&delays, 0, sizeof(delays)); + delays.stamp = now; + bpf_map_update_elem(&dlymap, &pid, &delays, 0); + } else { + delaysp->stamp = now; + } + } + return 0; +} + +SEC("kprobe/irq_exit") +int sysak_irq_exit_prog(struct pt_regs *ctx) +{ + u32 pid, type; + struct args *argp; + struct task_struct *p; + struct irq_acct *delaysp; + + p = (void *)bpf_get_current_task(); + type = GETARG_FROM_ARRYMAP(argmap, argp, int, type); + if (type == TASKSTATS_CMD_ATTR_TGID) + pid = _(p->tgid); + else if (type == TASKSTATS_CMD_ATTR_PID) + pid = _(p->pid); + else + return 0; + delaysp = bpf_map_lookup_elem(&dlymap, &pid); + if (delaysp) { + //int cpuid; + u64 now, delta; + + now = bpf_ktime_get_ns(); + delta = now - delaysp->stamp; + //cpuid = bpf_get_smp_processor_id(); + /* todo:if delta > thesh then; touch event_poll */ + delaysp->delay = delaysp->delay + delta; + delaysp->stamp = now; + delaysp->cnt = delaysp->cnt + 1; + delaysp->pid = pid; + } + return 0; +} + +char LICENSE[] SEC("license") = "GPL"; diff --git a/source/tools/detect/sched/getdelays/getdelays.c b/source/tools/detect/sched/getdelays/getdelays.c new file mode 100644 index 0000000000000000000000000000000000000000..299a09ac35f7d946fe2c9a665e059d7bae5a8ccb --- /dev/null +++ b/source/tools/detect/sched/getdelays/getdelays.c @@ -0,0 +1,327 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "getdelays.h" +#include "stat_nl.h" +#include "pidComm.h" +#include "bpf/getdelays.skel.h" + +unsigned int nr_cpus; +FILE *filep = NULL; +static volatile sig_atomic_t exiting = 0; +char log_dir[] = "/var/log/sysak/"; +char defaultfile[] = "/var/log/sysak/getdelays.log"; +char filename[256] = {0}; + +/* env.type = TASKSTATS_CMD_ATTR_PID or + * TASKSTATS_CMD_ATTR_TGID + **/ + +struct env { + int type; + pid_t pid; + unsigned long span; + __u64 threshold; + bool verbose; +} env = { + .span = 0, + .verbose = false, + .pid = 0, + .type = TASKSTATS_CMD_ATTR_UNSPEC, +}; + +const char *argp_program_version = "getdelays 0.1"; +const char argp_program_doc[] = +"Get a task'delays cased by irq/IO/preempted/mm reclaim/mm swap.\n" +"\n" +"USAGE: getdelays [--help] <-t TGID|-P PID> [-f ./res.log] [span times]\n" +"\n" +"EXAMPLES:\n" +" getdelays -p 123 # trace pid 123(use for threads only)\n" +" getdelays -t 123 # trace tgid 123\n" +" getdelays -p 123 -f a.log # record result to a.log (default to /var/log/sysak/getdelays.log)\n" +" getdelays -p 123 10 # monitor for 10 seconds\n"; + +static const struct argp_option opts[] = { + { "pid", 'p', "PID", 0, "Thread PID to trace"}, + { "tid", 't', "TGID", 0, "Process TGID to trace"}, + { "verbose", 'v', NULL, 0, "Verbose debug output" }, + { "logfile", 'f', "LOGFILE", 0, "logfile for result"}, + { NULL, 'h', NULL, OPTION_HIDDEN, "Show the full help" }, + {}, +}; + +static void bump_memlock_rlimit(void) +{ + struct rlimit rlim_new = { + .rlim_cur = RLIM_INFINITY, + .rlim_max = RLIM_INFINITY, + }; + + if (setrlimit(RLIMIT_MEMLOCK, &rlim_new)) { + fprintf(stderr, "Failed to increase RLIMIT_MEMLOCK limit!\n"); + exit(1); + } +} + +static int prepare_dictory(char *path) +{ + int ret; + + ret = mkdir(path, 0777); + if (ret < 0 && errno != EEXIST) + return errno; + else + return 0; +} + +static error_t parse_arg(int key, char *arg, struct argp_state *state) +{ + static int pos_args; + int pid; + unsigned long span; + + switch (key) { + case 'h': + argp_state_help(state, stderr, ARGP_HELP_STD_HELP); + break; + case 'v': + env.verbose = true; + break; + case 't': + errno = 0; + if (env.type == TASKSTATS_CMD_ATTR_PID) { + argp_usage(state); + return -EINVAL; + } + pid = strtol(arg, NULL, 10); + if (errno || pid <= 0) { + fprintf(stderr, "Invalid PID: %s\n", arg); + argp_usage(state); + return -EINVAL; + } + env.pid = pid; + env.type = TASKSTATS_CMD_ATTR_TGID; + break; + case 'p': + if (env.type == TASKSTATS_CMD_ATTR_TGID) { + argp_usage(state); + return -EINVAL; + } + errno = 0; + pid = strtol(arg, NULL, 10); + if (errno || pid < 0) { + fprintf(stderr, "Invalid TID: %s\n", arg); + argp_usage(state); + return -EINVAL; + } + env.pid = pid; + env.type = TASKSTATS_CMD_ATTR_PID; + break; + case 'f': + if (strlen(arg) < 2) + strncpy(filename, defaultfile, sizeof(filename)); + else + strncpy(filename, arg, sizeof(filename)); + filep = fopen(filename, "w+"); + if (!filep) { + int ret = errno; + fprintf(stderr, "%s :fopen %s\n", + strerror(errno), filename); + return ret; + } + break; + case ARGP_KEY_ARG: + if (pos_args++) { + fprintf(stderr, + "Unrecognized positional argument: %s\n", arg); + argp_usage(state); + } + errno = 0; + span = strtoul(arg, NULL, 10); + if (errno || span <= 0) { + fprintf(stderr, "Invalid SPAN: %s\n", arg); + argp_usage(state); + } + env.span = span; + break; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) +{ + if (level == LIBBPF_DEBUG && !env.verbose) + return 0; + return vfprintf(stderr, format, args); +} + +static void sig_alarm(int signo) +{ + exiting = 1; +} + +static void sig_int(int signo) +{ + exiting = 1; +} + +void handle_event(void *ctx, int cpu, void *data, __u32 data_sz) +{ + struct event tmpe, *e; + const struct event *ep = data; + struct tm *tm; + char ts[64]; + time_t t; + + tmpe = *ep; + e = &tmpe; + time(&t); + tm = localtime(&t); + strftime(ts, sizeof(ts), "%F %H:%M:%S", tm); + e->delay = e->delay/(1000*1000); + fprintf(filep, "%-21s %-6d %-16s %-8d %-10llu\n", + ts, e->cpuid, e->task, e->pid, e->delay); +} + +void handle_lost_events(void *ctx, int cpu, __u64 lost_cnt) +{ + printf("Lost %llu events on CPU #%d!\n", lost_cnt, cpu); +} + +struct msgtemplate msg1, msg2; +int get_nl_stats(int type, pid_t pid, struct msgtemplate *msg, struct taskstats **tstats); +void print_delayacct_sum(FILE *out, struct taskstats *t1, struct taskstats *t2, struct irq_acct *irqst); + +int main(int argc, char **argv) +{ + struct irq_acct dlyact; + struct taskstats *stats1, *stats2, *tstats; + int i, err, arg_fd, delay_fd; + static const struct argp argp = { + .options = opts, + .parser = parse_arg, + .doc = argp_program_doc, + }; + struct perf_buffer *pb = NULL; + struct getdelays_bpf *obj; + struct perf_buffer_opts pb_opts = {}; + struct args args = {}; + + err = prepare_dictory(log_dir); + if (err) + return err; + + err = argp_parse(&argp, argc, argv, 0, NULL, NULL); + if (err) + return err; + if (!filep) { + filep = fopen(defaultfile, "w+"); + if (!filep) { + err = errno; + fprintf(stderr, "%s :fopen %s\n", + strerror(errno), defaultfile); + return err; + } + } + + libbpf_set_print(libbpf_print_fn); + bump_memlock_rlimit(); + + obj = getdelays_bpf__open(); + if (!obj) { + fprintf(stderr, "failed to open BPF object\n"); + return 1; + } + + err = getdelays_bpf__load(obj); + if (err) { + fprintf(stderr, "failed to load BPF object: %d\n", err); + goto cleanup; + } + + i = 0; + delay_fd = bpf_map__fd(obj->maps.dlymap); + arg_fd = bpf_map__fd(obj->maps.argmap); + args.type = env.type; + args.targ_pid = env.pid; + args.threshold = env.threshold; + + pb_opts.sample_cb = handle_event; + pb = perf_buffer__new(bpf_map__fd(obj->maps.events), 64, &pb_opts); + if (!pb) { + err = errno; + fprintf(stderr, "failed to open perf buffer: %d\n", err); + goto cleanup; + } + + if (signal(SIGINT, sig_int) == SIG_ERR || + signal(SIGALRM, sig_alarm) == SIG_ERR) { + err = errno; + fprintf(stderr, "can't set signal handler: %s\n", strerror(errno)); + goto cleanup; + } + + if (env.span) + alarm(env.span); + + err = bpf_map_update_elem(arg_fd, &i, &args, 0); + if (err) { + fprintf(stderr, "Failed to update flag map\n"); + goto cleanup; + } + memset(&dlyact, 0, sizeof(dlyact)); + err = bpf_map_update_elem(delay_fd, &args.targ_pid, &dlyact, 0); + if (err) { + fprintf(stderr, "Failed to update flag map\n"); + goto cleanup; + } + + err = get_nl_stats(env.type, env.pid, &msg1, &tstats); + if (err) + goto cleanup; + stats1 = tstats; + + err = getdelays_bpf__attach(obj); + if (err) { + fprintf(stderr, "failed to attach BPF programs\n"); + goto cleanup; + } + + while (!exiting) { + err = perf_buffer__poll(pb, 100); + if (err < 0 && err != -EINTR) { + fprintf(stderr, "error polling perf buffer: %s\n", strerror(-err)); + goto cleanup; + } + /* reset err to return 0 if exiting */ + err = 0; + } + + err = get_nl_stats(env.type, args.targ_pid, &msg2, &tstats); + if (err) + goto cleanup; + stats2 = tstats; + err = bpf_map_lookup_elem(delay_fd, &args.targ_pid, &dlyact); + if (err) { + fprintf(stderr, "bpf_map_lookup_elem: %s\n", strerror(-err)); + memset(&dlyact, 0, sizeof(dlyact)); + } + print_delayacct_sum(filep, stats1, stats2, &dlyact); +cleanup: + perf_buffer__free(pb); + getdelays_bpf__destroy(obj); + + return err != 0; +} diff --git a/source/tools/detect/sched/getdelays/getdelays.h b/source/tools/detect/sched/getdelays/getdelays.h new file mode 100644 index 0000000000000000000000000000000000000000..7998a3ab0509f6f6368bec406037c3e87d336ada --- /dev/null +++ b/source/tools/detect/sched/getdelays/getdelays.h @@ -0,0 +1,33 @@ +#ifndef __RUNQSLOWER_H +#define __RUNQSLOWER_H + +#define TASK_COMM_LEN 16 +#define CPU_ARRY_LEN 4 +#define CONID_LEN 13 + +struct event { + char task[TASK_COMM_LEN]; + char prev_task[TASK_COMM_LEN]; + + __u64 delay; + __u64 stamp; + pid_t pid; + pid_t prev_pid; + int cpuid; +}; + +struct irq_acct { + __u64 cnt; + __u64 delay, stamp; + pid_t pid, pad; +}; + +struct args { + __u64 threshold; + pid_t targ_pid; + pid_t filter_pid; + int type; + pid_t pad; +}; + +#endif /* __RUNQSLOWER_H */ diff --git a/source/tools/detect/sched/getdelays/stat_nl.c b/source/tools/detect/sched/getdelays/stat_nl.c new file mode 100644 index 0000000000000000000000000000000000000000..c5949c7138b613140c42f6b680fb2487a6891e76 --- /dev/null +++ b/source/tools/detect/sched/getdelays/stat_nl.c @@ -0,0 +1,266 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "stat_nl.h" +#include "getdelays.h" + +int DEBUG; +static 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 + 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; +} + +#define average_ms(t, c) (t / 1000000ULL / (c ? c : 1)) + +void print_delayacct_sum(FILE *out, struct taskstats *t1, struct taskstats *t2, struct irq_acct *irqst) +{ + struct taskstats t; + + t.cpu_count = t2->cpu_count - t1->cpu_count; + t.cpu_delay_total = t2->cpu_delay_total - t1->cpu_delay_total; + t.blkio_delay_total = t2->blkio_delay_total - t1->blkio_delay_total; + t.blkio_count = t2->blkio_count - t1->blkio_count; + t.swapin_count = t2->swapin_count - t1->swapin_count; + t.swapin_delay_total = t2->swapin_delay_total - t1->swapin_delay_total; + t.freepages_count = t2->freepages_count - t1->freepages_count; + t.freepages_delay_total = t2->freepages_delay_total - t1->freepages_delay_total; + + fprintf(out, "%-12s%-15s%-15s%-15s(ms)\n", "WHAT", "count", "delay total", "delay average"); + fprintf(out, "%-12s%-15llu%-15.3f%-15.3f\n", "CPU", + (unsigned long long)t.cpu_count, + (double)t.cpu_delay_total/1000000, + average_ms((double)t.cpu_delay_total, t.cpu_count)); + fprintf(out, "%-12s%-15llu%-15.3f%-15.3f\n", "IO", + (unsigned long long)t.blkio_count, + (double)t.blkio_delay_total/1000000, + average_ms((double)t.blkio_delay_total, t.blkio_count)); + fprintf(out, "%-12s%-15llu%-15.3f%-15.3f\n", "SWAP", + (unsigned long long)t.swapin_count, + (double)t.swapin_delay_total/1000000, + average_ms((double)t.swapin_delay_total, t.swapin_count)); + fprintf(out, "%-12s%-15llu%-15.3f%-15.3f\n", "RECLAIM", + (unsigned long long)t.freepages_count, + (double)t.freepages_delay_total/1000000, + average_ms((double)t.freepages_delay_total, t.freepages_count)); + fprintf(out, "%-12s%-15llu%-15.3f%-15.3f\n", "IRQ", + (unsigned long long)irqst->cnt, + (double)irqst->delay/1000000, + average_ms((double)irqst->delay, irqst->cnt)); +} + +int get_nl_stats(int cmd_type, pid_t pid, struct msgtemplate *msg, struct taskstats **tstats) +{ + pid_t rtid = 0; + __u16 id; + char name[64]; + __u32 mypid; + struct nlattr *na; + int loop = 0, len = 0; + struct sockaddr_nl local; + int rc, nl_fd, rep_len, aggr_len, len2; + + nl_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); + if (nl_fd < 0) { + rc = errno; + fprintf(stderr,"socket:%s", strerror(errno)); + return rc; + } + memset(&local, 0, sizeof(local)); + local.nl_family = AF_NETLINK; + + if (bind(nl_fd, (struct sockaddr *) &local, sizeof(local)) < 0) { + rc = errno; + fprintf(stderr,"bind:%s", strerror(errno)); + close(nl_fd); + return rc; + } + strcpy(name, TASKSTATS_GENL_NAME); + mypid = getpid(); + rc = send_cmd(nl_fd, GENL_ID_CTRL, mypid, CTRL_CMD_GETFAMILY, + CTRL_ATTR_FAMILY_NAME, (void *)name, + strlen(TASKSTATS_GENL_NAME)+1); + if (rc < 0) { + rc = errno; + fprintf(stderr,"send_cmd1:%s", strerror(errno)); + close(nl_fd); + return rc; + } + rep_len = recv(nl_fd, msg, sizeof(struct msgtemplate), 0); + if (msg->n.nlmsg_type == NLMSG_ERROR || + (rep_len < 0) || !NLMSG_OK((&msg->n), rep_len)) { + fprintf(stderr,"recv FAMILY_ID:%s", strerror(errno)); + close(nl_fd); + return -1; + } + na = (struct nlattr *) GENLMSG_DATA(msg); + na = (struct nlattr *) ((char *) na + NLA_ALIGN(na->nla_len)); + id = 0; + if (na->nla_type == CTRL_ATTR_FAMILY_ID) { + id = *(__u16 *) NLA_DATA(na); + } + if (!id) { + fprintf(stderr,"bad family ID\n"); + close(nl_fd); + return -1; + } + if (pid) { + rc = send_cmd(nl_fd, id, mypid, TASKSTATS_CMD_GET, + cmd_type, &pid, sizeof(__u32)); + if (rc < 0) { + rc = errno; + fprintf(stderr,"send_cmd2:%s", strerror(errno)); + close(nl_fd); + return rc; + } + } else { + close(nl_fd); + return -1; + } + + memset(msg, 0, sizeof(struct msgtemplate)); + do { + rep_len = recv(nl_fd, msg, sizeof(struct msgtemplate), 0); + if (rep_len < 0) { + rep_len = errno; + fprintf(stderr,"recv2.0:%s", strerror(errno)); + close(nl_fd); + return rep_len; + } + if (msg->n.nlmsg_type == NLMSG_ERROR || + !NLMSG_OK((&msg->n), rep_len)) { + struct nlmsgerr *err = NLMSG_DATA(&msg); + fprintf(stderr,"recv2.1:%s", strerror(err->error)); + close(nl_fd); + return err->error; + } + + 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: + case TASKSTATS_TYPE_AGGR_PID: + aggr_len = NLA_PAYLOAD(na->nla_len); + len2 = 0; + na = (struct nlattr *) NLA_DATA(na); + while (len2 < aggr_len) { + switch (na->nla_type) { + case TASKSTATS_TYPE_PID: + if (DEBUG) { + rtid = *(int *) NLA_DATA(na); + printf("PID\t%d\n", rtid); + } + break; + case TASKSTATS_TYPE_TGID: + if (DEBUG) { + rtid = *(int *) NLA_DATA(na); + printf("TGID\t%d\n", rtid); + } + break; + case TASKSTATS_TYPE_STATS: + *tstats = ((struct taskstats *) NLA_DATA(na)); + if (nl_fd) { + if (write(nl_fd, NLA_DATA(na), na->nla_len) < 0) { + fprintf(stderr,"write error\n"); + } + } + if (!loop) + goto done; + break; + case TASKSTATS_TYPE_NULL: + break; + default: + fprintf(stderr, "Unknown nested" + " nla_type %d\n", + na->nla_type); + break; + } + len2 += NLA_ALIGN(na->nla_len); + na = (struct nlattr *)((char *)na + + NLA_ALIGN(na->nla_len)); + } + break; + default: + fprintf(stderr, "Unknown nla_type %d\n", + na->nla_type); + case TASKSTATS_TYPE_NULL: + break; + } + na = (struct nlattr *) (GENLMSG_DATA(msg) + len); + } + } while (loop); +done: + close(nl_fd); + return 0; +} + +#ifdef SELF_TEST +int main(int argc, char *argv[]) +{ + int ret; + pid_t pid; + struct msgtemplate msg; + struct taskstats *tstats; + + if (argc != 2) { + printf("usage: %s pid\n", argv[0]); + return -1; + } + pid = strtol(argv[1], NULL, 10); + if (errno || pid <= 0) { + fprintf(stderr, "Invalid PID: %s\n", argv[1]); + return errno; + } + + ret = get_nl_stats(pid, &msg, tstats); + if (!ret) { + print_delayacct_sum(*tstats); + } +} +#endif diff --git a/source/tools/detect/sched/getdelays/stat_nl.h b/source/tools/detect/sched/getdelays/stat_nl.h new file mode 100644 index 0000000000000000000000000000000000000000..5112df89aa304df2eb30c0093c333e7e35dda3db --- /dev/null +++ b/source/tools/detect/sched/getdelays/stat_nl.h @@ -0,0 +1,15 @@ +#include +#include + +#define MAX_MSG_SIZE 1024 +#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) + +struct msgtemplate { + struct nlmsghdr n; + struct genlmsghdr g; + char buf[MAX_MSG_SIZE]; +}; +