// SPDX-License-Identifier: GPL-2.0/* * delaytop.c - system-wide delay monitoring tool. * * This tool provides real-time monitoring and statistics of * system, container, and task-level delays, including CPU, * memory, IO, and IRQ. It supports both interactive (top-like), * and can output delay information for the whole system, specific * containers (cgroups), or individual tasks (PIDs). * * Key features: * - Collects per-task delay accounting statistics via taskstats. * - Collects system-wide PSI information. * - Supports sorting, filtering. * - Supports both interactive (screen refresh). * * Copyright (C) Fan Yu, ZTE Corp. 2025 * Copyright (C) Wang Yaxin, ZTE Corp. 2025 * * Compile with * gcc -I/usr/src/linux/include delaytop.c -o delaytop */ #include <stdio.h>#include <stdlib.h>#include <string.h>#include <errno.h>#include <unistd.h>#include <fcntl.h>#include <getopt.h>#include <signal.h>#include <time.h>#include <dirent.h>#include <ctype.h>#include <stdbool.h>#include <sys/types.h>#include <sys/stat.h>#include <sys/socket.h>#include <sys/select.h>#include <termios.h>#include <limits.h>#include <linux/genetlink.h>#include <linux/taskstats.h>#include <linux/cgroupstats.h>#include <stddef.h> #define PSI_PATH "/proc/pressure"#define PSI_CPU_PATH "/proc/pressure/cpu"#define PSI_MEMORY_PATH "/proc/pressure/memory"#define PSI_IO_PATH "/proc/pressure/io"#define PSI_IRQ_PATH "/proc/pressure/irq" #define NLA_NEXT(na) ((struct nlattr *)((char *)(na) + NLA_ALIGN((na)->nla_len)))#define NLA_DATA(na) ((void *)((char *)(na) + NLA_HDRLEN))#define NLA_PAYLOAD(len) (len - NLA_HDRLEN) #define GENLMSG_DATA(glh) ((void *)(NLMSG_DATA(glh) + GENL_HDRLEN))#define GENLMSG_PAYLOAD(glh) (NLMSG_PAYLOAD(glh, 0) - GENL_HDRLEN) #define TASK_COMM_LEN 16#define MAX_MSG_SIZE 1024#define MAX_TASKS 1000#define MAX_BUF_LEN 256#define SET_TASK_STAT(task_count, field) tasks[task_count].field = stats.field#define BOOL_FPRINT(stream, fmt, ...) \({ \ int ret = fprintf(stream, fmt, ##__VA_ARGS__); \ ret >= 0; \})#define TASK_AVG(task, field) average_ms((task).field##_delay_total, (task).field##_count)#define PSI_LINE_FORMAT "%-12s %6.1f%%/%6.1f%%/%6.1f%%/%8llu(ms)\n"#define DELAY_FMT_DEFAULT "%8.2f %8.2f %8.2f %8.2f\n"#define DELAY_FMT_MEMVERBOSE "%8.2f %8.2f %8.2f %8.2f %8.2f %8.2f\n"#define SORT_FIELD(name, cmd, modes) \ {#name, #cmd, \ offsetof(struct task_info, name##_delay_total), \ offsetof(struct task_info, name##_count), \ modes}#define END_FIELD {NULL, 0, 0} /* Display mode types */#define MODE_TYPE_ALL (0xFFFFFFFF)#define MODE_DEFAULT (1 << 0)#define MODE_MEMVERBOSE (1 << 1) /* PSI statistics structure */struct psi_stats { double cpu_some_avg10, cpu_some_avg60, cpu_some_avg300; unsigned long long cpu_some_total; double cpu_full_avg10, cpu_full_avg60, cpu_full_avg300; unsigned long long cpu_full_total; double memory_some_avg10, memory_some_avg60, memory_some_avg300; unsigned long long memory_some_total; double memory_full_avg10, memory_full_avg60, memory_full_avg300; unsigned long long memory_full_total; double io_some_avg10, io_some_avg60, io_some_avg300; unsigned long long io_some_total; double io_full_avg10, io_full_avg60, io_full_avg300; unsigned long long io_full_total; double irq_full_avg10, irq_full_avg60, irq_full_avg300; unsigned long long irq_full_total;}; /* Task delay information structure */struct task_info { int pid; int tgid; char command[TASK_COMM_LEN]; unsigned long long cpu_count; unsigned long long cpu_delay_total; unsigned long long blkio_count; unsigned long long blkio_delay_total; unsigned long long swapin_count; unsigned long long swapin_delay_total; unsigned long long freepages_count; unsigned long long freepages_delay_total; unsigned long long thrashing_count; unsigned long long thrashing_delay_total; unsigned long long compact_count; unsigned long long compact_delay_total; unsigned long long wpcopy_count; unsigned long long wpcopy_delay_total; unsigned long long irq_count; unsigned long long irq_delay_total; unsigned long long mem_count; unsigned long long mem_delay_total;}; /* Container statistics structure */struct container_stats { int nr_sleeping; /* Number of sleeping processes */ int nr_running; /* Number of running processes */ int nr_stopped; /* Number of stopped processes */ int nr_uninterruptible; /* Number of uninterruptible processes */ int nr_io_wait; /* Number of processes in IO wait */}; /* Delay field structure */struct field_desc { const char *name; /* Field name for cmdline argument */ const char *cmd_char; /* Interactive command */ unsigned long total_offset; /* Offset of total delay in task_info */ unsigned long count_offset; /* Offset of count in task_info */ size_t supported_modes; /* Supported display modes */}; /* Program settings structure */struct config { int delay; /* Update interval in seconds */ int iterations; /* Number of iterations, 0 == infinite */ int max_processes; /* Maximum number of processes to show */ int output_one_time; /* Output once and exit */ int monitor_pid; /* Monitor specific PID */ char *container_path; /* Path to container cgroup */ const struct field_desc *sort_field; /* Current sort field */ size_t display_mode; /* Current display mode */}; /* Global variables */static struct config cfg;static struct psi_stats psi;static struct task_info tasks[MAX_TASKS];static int task_count;static int running = 1;static struct container_stats container_stats;static const struct field_desc sort_fields[] = { SORT_FIELD(cpu, c, MODE_DEFAULT), SORT_FIELD(blkio, i, MODE_DEFAULT), SORT_FIELD(irq, q, MODE_DEFAULT), SORT_FIELD(mem, m, MODE_DEFAULT | MODE_MEMVERBOSE), SORT_FIELD(swapin, s, MODE_MEMVERBOSE), SORT_FIELD(freepages, r, MODE_MEMVERBOSE), SORT_FIELD(thrashing, t, MODE_MEMVERBOSE), SORT_FIELD(compact, p, MODE_MEMVERBOSE), SORT_FIELD(wpcopy, w, MODE_MEMVERBOSE), END_FIELD};static int sort_selected; /* Netlink socket variables */static int nl_sd = -1;static int family_id; /* Set terminal to non-canonical mode for q-to-quit */static struct termios orig_termios;static void enable_raw_mode(void){ struct termios raw; tcgetattr(STDIN_FILENO, &orig_termios); raw = orig_termios; raw.c_lflag &= ~(ICANON | ECHO); tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);}static void disable_raw_mode(void){ tcsetattr(STDIN_FILENO, TCSAFLUSH, &orig_termios);} /* Find field descriptor by command line */static const struct field_desc *get_field_by_cmd_char(char ch){ const struct field_desc *field; for (field = sort_fields; field->name != NULL; field++) { if (field->cmd_char[0] == ch) return field; } return NULL;} /* Find field descriptor by name with string comparison */static const struct field_desc *get_field_by_name(const char *name){ const struct field_desc *field; size_t field_len; for (field = sort_fields; field->name != NULL; field++) { field_len = strlen(field->name); if (field_len != strlen(name)) continue; if (strncmp(field->name, name, field_len) == 0) return field; } return NULL;} /* Find display name for a field descriptor */static const char *get_name_by_field(const struct field_desc *field){ return field ? field->name : "UNKNOWN";} /* Generate string of available field names */static void display_available_fields(size_t mode){ const struct field_desc *field; char buf[MAX_BUF_LEN]; buf[0] = '\0'; for (field = sort_fields; field->name != NULL; field++) { if (!(field->supported_modes & mode)) continue; strncat(buf, "|", MAX_BUF_LEN - strlen(buf) - 1); strncat(buf, field->name, MAX_BUF_LEN - strlen(buf) - 1); buf[MAX_BUF_LEN - 1] = '\0'; } fprintf(stderr, "Available fields: %s\n", buf);} /* Display usage information and command line options */static void usage(void){ printf("Usage: delaytop [Options]\n" "Options:\n" " -h, --help Show this help message and exit\n" " -d, --delay=SECONDS Set refresh interval (default: 2 seconds, min: 1)\n" " -n, --iterations=COUNT Set number of updates (default: 0 = infinite)\n" " -P, --processes=NUMBER Set maximum number of processes to show (default: 20, max: 1000)\n" " -o, --once Display once and exit\n" " -p, --pid=PID Monitor only the specified PID\n" " -C, --container=PATH Monitor the container at specified cgroup path\n" " -s, --sort=FIELD Sort by delay field (default: cpu)\n" " -M, --memverbose Display memory detailed information\n"); exit(0);} /* Parse command line arguments and set configuration */static void parse_args(int argc, char **argv){ int c; const struct field_desc *field; struct option long_options[] = { {"help", no_argument, 0, 'h'}, {"delay", required_argument, 0, 'd'}, {"iterations", required_argument, 0, 'n'}, {"pid", required_argument, 0, 'p'}, {"once", no_argument, 0, 'o'}, {"processes", required_argument, 0, 'P'}, {"sort", required_argument, 0, 's'}, {"container", required_argument, 0, 'C'}, {"memverbose", no_argument, 0, 'M'}, {0, 0, 0, 0} }; /* Set defaults */ cfg.delay = 2; cfg.iterations = 0; cfg.max_processes = 20; cfg.sort_field = &sort_fields[0]; /* Default sorted by CPU delay */ cfg.output_one_time = 0; cfg.monitor_pid = 0; /* 0 means monitor all PIDs */ cfg.container_path = NULL; cfg.display_mode = MODE_DEFAULT; while (1) { int option_index = 0; c = getopt_long(argc, argv, "hd:n:p:oP:C:s:M", long_options, &option_index); if (c == -1) break; switch (c) { case 'h': usage(); break; case 'd': cfg.delay = atoi(optarg); if (cfg.delay < 1) { fprintf(stderr, "Error: delay must be >= 1.\n"); exit(1); } break; case 'n': cfg.iterations = atoi(optarg); if (cfg.iterations < 0) { fprintf(stderr, "Error: iterations must be >= 0.\n"); exit(1); } break; case 'p': cfg.monitor_pid = atoi(optarg); if (cfg.monitor_pid < 1) { fprintf(stderr, "Error: pid must be >= 1.\n"); exit(1); } break; case 'o': cfg.output_one_time = 1; break; case 'P': cfg.max_processes = atoi(optarg); if (cfg.max_processes < 1) { fprintf(stderr, "Error: processes must be >= 1.\n"); exit(1); } if (cfg.max_processes > MAX_TASKS) { fprintf(stderr, "Warning: processes capped to %d.\n", MAX_TASKS); cfg.max_processes = MAX_TASKS; } break; case 'C': cfg.container_path = strdup(optarg); break; case 's': if (strlen(optarg) == 0) { fprintf(stderr, "Error: empty sort field\n"); exit(1); } field = get_field_by_name(optarg); /* Show available fields if invalid option provided */ if (!field) { fprintf(stderr, "Error: invalid sort field '%s'\n", optarg); display_available_fields(MODE_TYPE_ALL); exit(1); } cfg.sort_field = field; break; case 'M': cfg.display_mode = MODE_MEMVERBOSE; cfg.sort_field = get_field_by_name("mem"); break; default: fprintf(stderr, "Try 'delaytop --help' for more information.\n"); exit(1); } }} /* Calculate average delay in milliseconds for overall memory */static void set_mem_delay_total(struct task_info *t){ t->mem_delay_total = t->swapin_delay_total + t->freepages_delay_total + t->thrashing_delay_total + t->compact_delay_total + t->wpcopy_delay_total;} static void set_mem_count(struct task_info *t){ t->mem_count = t->swapin_count + t->freepages_count + t->thrashing_count + t->compact_count + t->wpcopy_count;} /* Create a raw netlink socket and bind */static int create_nl_socket(void){ int fd; struct sockaddr_nl local; fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); if (fd < 0) return -1; memset(&local, 0, sizeof(local)); local.nl_family = AF_NETLINK; if (bind(fd, (struct sockaddr *) &local, sizeof(local)) < 0) { fprintf(stderr, "Failed to bind socket when create nl_socket\n"); close(fd); return -1; } return fd;} /* Send a command via netlink */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 sockaddr_nl nladdr; struct nlattr *na; int r, buflen; char *buf; struct { struct nlmsghdr n; struct genlmsghdr g; char buf[MAX_MSG_SIZE]; } 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;} /* Get family ID for taskstats via netlink */static int get_family_id(int sd){ struct { struct nlmsghdr n; struct genlmsghdr g; char buf[256]; } ans; int id = 0, rc; struct nlattr *na; int rep_len; char name[100]; strncpy(name, TASKSTATS_GENL_NAME, sizeof(name) - 1); name[sizeof(name) - 1] = '\0'; rc = send_cmd(sd, GENL_ID_CTRL, getpid(), CTRL_CMD_GETFAMILY, CTRL_ATTR_FAMILY_NAME, (void *)name, strlen(TASKSTATS_GENL_NAME)+1); if (rc < 0) { fprintf(stderr, "Failed to send cmd for family id\n"); return 0; } rep_len = recv(sd, &ans, sizeof(ans), 0); if (ans.n.nlmsg_type == NLMSG_ERROR || (rep_len < 0) || !NLMSG_OK((&ans.n), rep_len)) { fprintf(stderr, "Failed to receive response for family id\n"); 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;} static int read_psi_stats(void)Lines 1–500 of 1145. Load more
// SPDX-License-Identifier: GPL-2.0 /* * delaytop.c - system-wide delay monitoring tool. * * This tool provides real-time monitoring and statistics of * system, container, and task-level delays, including CPU, * memory, IO, and IRQ. It supports both interactive (top-like), * and can output delay information for the whole system, specific * containers (cgroups), or individual tasks (PIDs). * * Key features: * - Collects per-task delay accounting statistics via taskstats. * - Collects system-wide PSI information. * - Supports sorting, filtering. * - Supports both interactive (screen refresh). * * Copyright (C) Fan Yu, ZTE Corp. 2025 * Copyright (C) Wang Yaxin, ZTE Corp. 2025 * * Compile with * gcc -I/usr/src/linux/include delaytop.c -o delaytop */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <fcntl.h> #include <getopt.h> #include <signal.h> #include <time.h> #include <dirent.h> #include <ctype.h> #include <stdbool.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/socket.h> #include <sys/select.h> #include <termios.h> #include <limits.h> #include <linux/genetlink.h> #include <linux/taskstats.h> #include <linux/cgroupstats.h> #include <stddef.h> #define PSI_PATH "/proc/pressure" #define PSI_CPU_PATH "/proc/pressure/cpu" #define PSI_MEMORY_PATH "/proc/pressure/memory" #define PSI_IO_PATH "/proc/pressure/io" #define PSI_IRQ_PATH "/proc/pressure/irq" #define NLA_NEXT(na) ((struct nlattr *)((char *)(na) + NLA_ALIGN((na)->nla_len))) #define NLA_DATA(na) ((void *)((char *)(na) + NLA_HDRLEN)) #define NLA_PAYLOAD(len) (len - NLA_HDRLEN) #define GENLMSG_DATA(glh) ((void *)(NLMSG_DATA(glh) + GENL_HDRLEN)) #define GENLMSG_PAYLOAD(glh) (NLMSG_PAYLOAD(glh, 0) - GENL_HDRLEN) #define TASK_COMM_LEN 16 #define MAX_MSG_SIZE 1024 #define MAX_TASKS 1000 #define MAX_BUF_LEN 256 #define SET_TASK_STAT(task_count, field) tasks[task_count].field = stats.field #define BOOL_FPRINT(stream, fmt, ...) \ ({ \ int ret = fprintf(stream, fmt, ##__VA_ARGS__); \ ret >= 0; \ }) #define TASK_AVG(task, field) average_ms((task).field##_delay_total, (task).field##_count) #define PSI_LINE_FORMAT "%-12s %6.1f%%/%6.1f%%/%6.1f%%/%8llu(ms)\n" #define DELAY_FMT_DEFAULT "%8.2f %8.2f %8.2f %8.2f\n" #define DELAY_FMT_MEMVERBOSE "%8.2f %8.2f %8.2f %8.2f %8.2f %8.2f\n" #define SORT_FIELD(name, cmd, modes) \ {#name, #cmd, \ offsetof(struct task_info, name##_delay_total), \ offsetof(struct task_info, name##_count), \ modes} #define END_FIELD {NULL, 0, 0} /* Display mode types */ #define MODE_TYPE_ALL (0xFFFFFFFF) #define MODE_DEFAULT (1 << 0) #define MODE_MEMVERBOSE (1 << 1) /* PSI statistics structure */ struct psi_stats { double cpu_some_avg10, cpu_some_avg60, cpu_some_avg300; unsigned long long cpu_some_total; double cpu_full_avg10, cpu_full_avg60, cpu_full_avg300; unsigned long long cpu_full_total; double memory_some_avg10, memory_some_avg60, memory_some_avg300; unsigned long long memory_some_total; double memory_full_avg10, memory_full_avg60, memory_full_avg300; unsigned long long memory_full_total; double io_some_avg10, io_some_avg60, io_some_avg300; unsigned long long io_some_total; double io_full_avg10, io_full_avg60, io_full_avg300; unsigned long long io_full_total; double irq_full_avg10, irq_full_avg60, irq_full_avg300; unsigned long long irq_full_total; }; /* Task delay information structure */ struct task_info { int pid; int tgid; char command[TASK_COMM_LEN]; unsigned long long cpu_count; unsigned long long cpu_delay_total; unsigned long long blkio_count; unsigned long long blkio_delay_total; unsigned long long swapin_count; unsigned long long swapin_delay_total; unsigned long long freepages_count; unsigned long long freepages_delay_total; unsigned long long thrashing_count; unsigned long long thrashing_delay_total; unsigned long long compact_count; unsigned long long compact_delay_total; unsigned long long wpcopy_count; unsigned long long wpcopy_delay_total; unsigned long long irq_count; unsigned long long irq_delay_total; unsigned long long mem_count; unsigned long long mem_delay_total; }; /* Container statistics structure */ struct container_stats { int nr_sleeping; /* Number of sleeping processes */ int nr_running; /* Number of running processes */ int nr_stopped; /* Number of stopped processes */ int nr_uninterruptible; /* Number of uninterruptible processes */ int nr_io_wait; /* Number of processes in IO wait */ }; /* Delay field structure */ struct field_desc { const char *name; /* Field name for cmdline argument */ const char *cmd_char; /* Interactive command */ unsigned long total_offset; /* Offset of total delay in task_info */ unsigned long count_offset; /* Offset of count in task_info */ size_t supported_modes; /* Supported display modes */ }; /* Program settings structure */ struct config { int delay; /* Update interval in seconds */ int iterations; /* Number of iterations, 0 == infinite */ int max_processes; /* Maximum number of processes to show */ int output_one_time; /* Output once and exit */ int monitor_pid; /* Monitor specific PID */ char *container_path; /* Path to container cgroup */ const struct field_desc *sort_field; /* Current sort field */ size_t display_mode; /* Current display mode */ }; /* Global variables */ static struct config cfg; static struct psi_stats psi; static struct task_info tasks[MAX_TASKS]; static int task_count; static int running = 1; static struct container_stats container_stats; static const struct field_desc sort_fields[] = { SORT_FIELD(cpu, c, MODE_DEFAULT), SORT_FIELD(blkio, i, MODE_DEFAULT), SORT_FIELD(irq, q, MODE_DEFAULT), SORT_FIELD(mem, m, MODE_DEFAULT | MODE_MEMVERBOSE), SORT_FIELD(swapin, s, MODE_MEMVERBOSE), SORT_FIELD(freepages, r, MODE_MEMVERBOSE), SORT_FIELD(thrashing, t, MODE_MEMVERBOSE), SORT_FIELD(compact, p, MODE_MEMVERBOSE), SORT_FIELD(wpcopy, w, MODE_MEMVERBOSE), END_FIELD }; static int sort_selected; /* Netlink socket variables */ static int nl_sd = -1; static int family_id; /* Set terminal to non-canonical mode for q-to-quit */ static struct termios orig_termios; static void enable_raw_mode(void) { struct termios raw; tcgetattr(STDIN_FILENO, &orig_termios); raw = orig_termios; raw.c_lflag &= ~(ICANON | ECHO); tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw); } static void disable_raw_mode(void) { tcsetattr(STDIN_FILENO, TCSAFLUSH, &orig_termios); } /* Find field descriptor by command line */ static const struct field_desc *get_field_by_cmd_char(char ch) { const struct field_desc *field; for (field = sort_fields; field->name != NULL; field++) { if (field->cmd_char[0] == ch) return field; } return NULL; } /* Find field descriptor by name with string comparison */ static const struct field_desc *get_field_by_name(const char *name) { const struct field_desc *field; size_t field_len; for (field = sort_fields; field->name != NULL; field++) { field_len = strlen(field->name); if (field_len != strlen(name)) continue; if (strncmp(field->name, name, field_len) == 0) return field; } return NULL; } /* Find display name for a field descriptor */ static const char *get_name_by_field(const struct field_desc *field) { return field ? field->name : "UNKNOWN"; } /* Generate string of available field names */ static void display_available_fields(size_t mode) { const struct field_desc *field; char buf[MAX_BUF_LEN]; buf[0] = '\0'; for (field = sort_fields; field->name != NULL; field++) { if (!(field->supported_modes & mode)) continue; strncat(buf, "|", MAX_BUF_LEN - strlen(buf) - 1); strncat(buf, field->name, MAX_BUF_LEN - strlen(buf) - 1); buf[MAX_BUF_LEN - 1] = '\0'; } fprintf(stderr, "Available fields: %s\n", buf); } /* Display usage information and command line options */ static void usage(void) { printf("Usage: delaytop [Options]\n" "Options:\n" " -h, --help Show this help message and exit\n" " -d, --delay=SECONDS Set refresh interval (default: 2 seconds, min: 1)\n" " -n, --iterations=COUNT Set number of updates (default: 0 = infinite)\n" " -P, --processes=NUMBER Set maximum number of processes to show (default: 20, max: 1000)\n" " -o, --once Display once and exit\n" " -p, --pid=PID Monitor only the specified PID\n" " -C, --container=PATH Monitor the container at specified cgroup path\n" " -s, --sort=FIELD Sort by delay field (default: cpu)\n" " -M, --memverbose Display memory detailed information\n"); exit(0); } /* Parse command line arguments and set configuration */ static void parse_args(int argc, char **argv) { int c; const struct field_desc *field; struct option long_options[] = { {"help", no_argument, 0, 'h'}, {"delay", required_argument, 0, 'd'}, {"iterations", required_argument, 0, 'n'}, {"pid", required_argument, 0, 'p'}, {"once", no_argument, 0, 'o'}, {"processes", required_argument, 0, 'P'}, {"sort", required_argument, 0, 's'}, {"container", required_argument, 0, 'C'}, {"memverbose", no_argument, 0, 'M'}, {0, 0, 0, 0} }; /* Set defaults */ cfg.delay = 2; cfg.iterations = 0; cfg.max_processes = 20; cfg.sort_field = &sort_fields[0]; /* Default sorted by CPU delay */ cfg.output_one_time = 0; cfg.monitor_pid = 0; /* 0 means monitor all PIDs */ cfg.container_path = NULL; cfg.display_mode = MODE_DEFAULT; while (1) { int option_index = 0; c = getopt_long(argc, argv, "hd:n:p:oP:C:s:M", long_options, &option_index); if (c == -1) break; switch (c) { case 'h': usage(); break; case 'd': cfg.delay = atoi(optarg); if (cfg.delay < 1) { fprintf(stderr, "Error: delay must be >= 1.\n"); exit(1); } break; case 'n': cfg.iterations = atoi(optarg); if (cfg.iterations < 0) { fprintf(stderr, "Error: iterations must be >= 0.\n"); exit(1); } break; case 'p': cfg.monitor_pid = atoi(optarg); if (cfg.monitor_pid < 1) { fprintf(stderr, "Error: pid must be >= 1.\n"); exit(1); } break; case 'o': cfg.output_one_time = 1; break; case 'P': cfg.max_processes = atoi(optarg); if (cfg.max_processes < 1) { fprintf(stderr, "Error: processes must be >= 1.\n"); exit(1); } if (cfg.max_processes > MAX_TASKS) { fprintf(stderr, "Warning: processes capped to %d.\n", MAX_TASKS); cfg.max_processes = MAX_TASKS; } break; case 'C': cfg.container_path = strdup(optarg); break; case 's': if (strlen(optarg) == 0) { fprintf(stderr, "Error: empty sort field\n"); exit(1); } field = get_field_by_name(optarg); /* Show available fields if invalid option provided */ if (!field) { fprintf(stderr, "Error: invalid sort field '%s'\n", optarg); display_available_fields(MODE_TYPE_ALL); exit(1); } cfg.sort_field = field; break; case 'M': cfg.display_mode = MODE_MEMVERBOSE; cfg.sort_field = get_field_by_name("mem"); break; default: fprintf(stderr, "Try 'delaytop --help' for more information.\n"); exit(1); } } } /* Calculate average delay in milliseconds for overall memory */ static void set_mem_delay_total(struct task_info *t) { t->mem_delay_total = t->swapin_delay_total + t->freepages_delay_total + t->thrashing_delay_total + t->compact_delay_total + t->wpcopy_delay_total; } static void set_mem_count(struct task_info *t) { t->mem_count = t->swapin_count + t->freepages_count + t->thrashing_count + t->compact_count + t->wpcopy_count; } /* Create a raw netlink socket and bind */ static int create_nl_socket(void) { int fd; struct sockaddr_nl local; fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); if (fd < 0) return -1; memset(&local, 0, sizeof(local)); local.nl_family = AF_NETLINK; if (bind(fd, (struct sockaddr *) &local, sizeof(local)) < 0) { fprintf(stderr, "Failed to bind socket when create nl_socket\n"); close(fd); return -1; } return fd; } /* Send a command via netlink */ 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 sockaddr_nl nladdr; struct nlattr *na; int r, buflen; char *buf; struct { struct nlmsghdr n; struct genlmsghdr g; char buf[MAX_MSG_SIZE]; } 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; } /* Get family ID for taskstats via netlink */ static int get_family_id(int sd) { struct { struct nlmsghdr n; struct genlmsghdr g; char buf[256]; } ans; int id = 0, rc; struct nlattr *na; int rep_len; char name[100]; strncpy(name, TASKSTATS_GENL_NAME, sizeof(name) - 1); name[sizeof(name) - 1] = '\0'; rc = send_cmd(sd, GENL_ID_CTRL, getpid(), CTRL_CMD_GETFAMILY, CTRL_ATTR_FAMILY_NAME, (void *)name, strlen(TASKSTATS_GENL_NAME)+1); if (rc < 0) { fprintf(stderr, "Failed to send cmd for family id\n"); return 0; } rep_len = recv(sd, &ans, sizeof(ans), 0); if (ans.n.nlmsg_type == NLMSG_ERROR || (rep_len < 0) || !NLMSG_OK((&ans.n), rep_len)) { fprintf(stderr, "Failed to receive response for family id\n"); 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; } static int read_psi_stats(void)