Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

tools/delaytop: add memory verbose mode support

The original delaytop tool always displayed detailed memory subsystem
breakdown, which could be overwhelming for users who only need high-level
overview.

Add flexible display control allowing users to choose their preferred
information granularity.

The new flexibility provides:
1) For quick monitoring: use normal mode to reduce visual clutter
2) For deep analysis: use verbose mode to see all memory subsystem details

Link: https://lkml.kernel.org/r/202509070012527934u0ySb3teQ4gOYKnocyNO@zte.com.cn
Signed-off-by: Fan Yu <fan.yu9@zte.com.cn>
Reviewed-by: xu xin <xu.xin16@zte.com.cn>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>

authored by

Fan Yu and committed by
Andrew Morton
99d9c55f 0471440c

+98 -30
+98 -30
tools/accounting/delaytop.c
··· 69 69 int ret = fprintf(stream, fmt, ##__VA_ARGS__); \ 70 70 ret >= 0; \ 71 71 }) 72 + #define TASK_AVG(task, field) average_ms((task).field##_delay_total, (task).field##_count) 72 73 #define PSI_LINE_FORMAT "%-12s %6.1f%%/%6.1f%%/%6.1f%%/%8llu(ms)\n" 73 - #define SORT_FIELD(name) \ 74 + #define DELAY_FMT_DEFAULT "%8.2f %8.2f %8.2f %8.2f\n" 75 + #define DELAY_FMT_MEMVERBOSE "%8.2f %8.2f %8.2f %8.2f %8.2f %8.2f\n" 76 + #define SORT_FIELD(name, modes) \ 74 77 {#name, \ 75 78 offsetof(struct task_info, name##_delay_total), \ 76 - offsetof(struct task_info, name##_count)} 79 + offsetof(struct task_info, name##_count), \ 80 + modes} 77 81 #define END_FIELD {NULL, 0, 0} 82 + 83 + /* Display mode types */ 84 + #define MODE_TYPE_ALL (0xFFFFFFFF) 85 + #define MODE_DEFAULT (1 << 0) 86 + #define MODE_MEMVERBOSE (1 << 1) 78 87 79 88 /* PSI statistics structure */ 80 89 struct psi_stats { ··· 124 115 unsigned long long wpcopy_delay_total; 125 116 unsigned long long irq_count; 126 117 unsigned long long irq_delay_total; 118 + unsigned long long mem_count; 119 + unsigned long long mem_delay_total; 127 120 }; 128 121 129 122 /* Container statistics structure */ ··· 142 131 const char *name; /* Field name for cmdline argument */ 143 132 unsigned long total_offset; /* Offset of total delay in task_info */ 144 133 unsigned long count_offset; /* Offset of count in task_info */ 134 + size_t supported_modes; /* Supported display modes */ 145 135 }; 146 136 147 137 /* Program settings structure */ ··· 154 142 int monitor_pid; /* Monitor specific PID */ 155 143 char *container_path; /* Path to container cgroup */ 156 144 const struct field_desc *sort_field; /* Current sort field */ 145 + size_t display_mode; /* Current display mode */ 157 146 }; 158 147 159 148 /* Global variables */ ··· 165 152 static int running = 1; 166 153 static struct container_stats container_stats; 167 154 static const struct field_desc sort_fields[] = { 168 - SORT_FIELD(cpu), 169 - SORT_FIELD(blkio), 170 - SORT_FIELD(irq), 171 - SORT_FIELD(swapin), 172 - SORT_FIELD(freepages), 173 - SORT_FIELD(thrashing), 174 - SORT_FIELD(compact), 175 - SORT_FIELD(wpcopy), 155 + SORT_FIELD(cpu, MODE_DEFAULT), 156 + SORT_FIELD(blkio, MODE_DEFAULT), 157 + SORT_FIELD(irq, MODE_DEFAULT), 158 + SORT_FIELD(mem, MODE_DEFAULT | MODE_MEMVERBOSE), 159 + SORT_FIELD(swapin, MODE_MEMVERBOSE), 160 + SORT_FIELD(freepages, MODE_MEMVERBOSE), 161 + SORT_FIELD(thrashing, MODE_MEMVERBOSE), 162 + SORT_FIELD(compact, MODE_MEMVERBOSE), 163 + SORT_FIELD(wpcopy, MODE_MEMVERBOSE), 176 164 END_FIELD 177 165 }; 178 166 ··· 221 207 } 222 208 223 209 /* Generate string of available field names */ 224 - static void display_available_fields(void) 210 + static void display_available_fields(size_t mode) 225 211 { 226 212 const struct field_desc *field; 227 213 char buf[MAX_BUF_LEN]; ··· 229 215 buf[0] = '\0'; 230 216 231 217 for (field = sort_fields; field->name != NULL; field++) { 218 + if (!(field->supported_modes & mode)) 219 + continue; 232 220 strncat(buf, "|", MAX_BUF_LEN - strlen(buf) - 1); 233 221 strncat(buf, field->name, MAX_BUF_LEN - strlen(buf) - 1); 234 222 buf[MAX_BUF_LEN - 1] = '\0'; ··· 251 235 " -o, --once Display once and exit\n" 252 236 " -p, --pid=PID Monitor only the specified PID\n" 253 237 " -C, --container=PATH Monitor the container at specified cgroup path\n" 254 - " -s, --sort=FIELD Sort by delay field (default: cpu)\n"); 238 + " -s, --sort=FIELD Sort by delay field (default: cpu)\n" 239 + " -M, --memverbose Display memory detailed information\n"); 255 240 exit(0); 256 241 } 257 242 ··· 270 253 {"processes", required_argument, 0, 'P'}, 271 254 {"sort", required_argument, 0, 's'}, 272 255 {"container", required_argument, 0, 'C'}, 256 + {"memverbose", no_argument, 0, 'M'}, 273 257 {0, 0, 0, 0} 274 258 }; 275 259 ··· 282 264 cfg.output_one_time = 0; 283 265 cfg.monitor_pid = 0; /* 0 means monitor all PIDs */ 284 266 cfg.container_path = NULL; 267 + cfg.display_mode = MODE_DEFAULT; 285 268 286 269 while (1) { 287 270 int option_index = 0; 288 271 289 - c = getopt_long(argc, argv, "hd:n:p:oP:C:s:", long_options, &option_index); 272 + c = getopt_long(argc, argv, "hd:n:p:oP:C:s:M", long_options, &option_index); 290 273 if (c == -1) 291 274 break; 292 275 ··· 344 325 /* Show available fields if invalid option provided */ 345 326 if (!field) { 346 327 fprintf(stderr, "Error: invalid sort field '%s'\n", optarg); 347 - display_available_fields(); 328 + display_available_fields(MODE_TYPE_ALL); 348 329 exit(1); 349 330 } 350 331 351 332 cfg.sort_field = field; 333 + break; 334 + case 'M': 335 + cfg.display_mode = MODE_MEMVERBOSE; 336 + cfg.sort_field = get_field_by_name("mem"); 352 337 break; 353 338 default: 354 339 fprintf(stderr, "Try 'delaytop --help' for more information.\n"); 355 340 exit(1); 356 341 } 357 342 } 343 + } 344 + 345 + /* Calculate average delay in milliseconds for overall memory */ 346 + static void set_mem_delay_total(struct task_info *t) 347 + { 348 + t->mem_delay_total = t->swapin_delay_total + 349 + t->freepages_delay_total + 350 + t->thrashing_delay_total + 351 + t->compact_delay_total + 352 + t->wpcopy_delay_total; 353 + } 354 + 355 + static void set_mem_count(struct task_info *t) 356 + { 357 + t->mem_count = t->swapin_count + 358 + t->freepages_count + 359 + t->thrashing_count + 360 + t->compact_count + 361 + t->wpcopy_count; 358 362 } 359 363 360 364 /* Create a raw netlink socket and bind */ ··· 653 611 SET_TASK_STAT(task_count, wpcopy_delay_total); 654 612 SET_TASK_STAT(task_count, irq_count); 655 613 SET_TASK_STAT(task_count, irq_delay_total); 614 + set_mem_count(&tasks[task_count]); 615 + set_mem_delay_total(&tasks[task_count]); 656 616 task_count++; 657 617 } 658 618 break; ··· 873 829 /* Task delay output */ 874 830 suc &= BOOL_FPRINT(out, "Top %d processes (sorted by %s delay):\n", 875 831 cfg.max_processes, get_name_by_field(cfg.sort_field)); 876 - suc &= BOOL_FPRINT(out, "%5s %5s %-17s", "PID", "TGID", "COMMAND"); 877 - suc &= BOOL_FPRINT(out, "%7s %7s %7s %7s %7s %7s %7s %7s\n", 878 - "CPU(ms)", "IO(ms)", "SWAP(ms)", "RCL(ms)", 879 - "THR(ms)", "CMP(ms)", "WP(ms)", "IRQ(ms)"); 880 832 881 - suc &= BOOL_FPRINT(out, "-----------------------------------------------"); 882 - suc &= BOOL_FPRINT(out, "----------------------------------------------\n"); 833 + suc &= BOOL_FPRINT(out, "%8s %8s %-17s", "PID", "TGID", "COMMAND"); 834 + if (cfg.display_mode == MODE_MEMVERBOSE) { 835 + suc &= BOOL_FPRINT(out, "%8s %8s %8s %8s %8s %8s\n", 836 + "MEM(ms)", "SWAP(ms)", "RCL(ms)", 837 + "THR(ms)", "CMP(ms)", "WP(ms)"); 838 + suc &= BOOL_FPRINT(out, "-----------------------"); 839 + suc &= BOOL_FPRINT(out, "-----------------------"); 840 + suc &= BOOL_FPRINT(out, "-----------------------"); 841 + suc &= BOOL_FPRINT(out, "---------------------\n"); 842 + } else { 843 + suc &= BOOL_FPRINT(out, "%8s %8s %8s %8s\n", 844 + "CPU(ms)", "IO(ms)", "IRQ(ms)", "MEM(ms)"); 845 + suc &= BOOL_FPRINT(out, "-----------------------"); 846 + suc &= BOOL_FPRINT(out, "-----------------------"); 847 + suc &= BOOL_FPRINT(out, "--------------------------\n"); 848 + } 849 + 883 850 count = task_count < cfg.max_processes ? task_count : cfg.max_processes; 884 851 885 852 for (i = 0; i < count; i++) { 886 - suc &= BOOL_FPRINT(out, "%5d %5d %-15s", 853 + suc &= BOOL_FPRINT(out, "%8d %8d %-15s", 887 854 tasks[i].pid, tasks[i].tgid, tasks[i].command); 888 - suc &= BOOL_FPRINT(out, "%7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f\n", 889 - average_ms(tasks[i].cpu_delay_total, tasks[i].cpu_count), 890 - average_ms(tasks[i].blkio_delay_total, tasks[i].blkio_count), 891 - average_ms(tasks[i].swapin_delay_total, tasks[i].swapin_count), 892 - average_ms(tasks[i].freepages_delay_total, tasks[i].freepages_count), 893 - average_ms(tasks[i].thrashing_delay_total, tasks[i].thrashing_count), 894 - average_ms(tasks[i].compact_delay_total, tasks[i].compact_count), 895 - average_ms(tasks[i].wpcopy_delay_total, tasks[i].wpcopy_count), 896 - average_ms(tasks[i].irq_delay_total, tasks[i].irq_count)); 855 + if (cfg.display_mode == MODE_MEMVERBOSE) { 856 + suc &= BOOL_FPRINT(out, DELAY_FMT_MEMVERBOSE, 857 + TASK_AVG(tasks[i], mem), 858 + TASK_AVG(tasks[i], swapin), 859 + TASK_AVG(tasks[i], freepages), 860 + TASK_AVG(tasks[i], thrashing), 861 + TASK_AVG(tasks[i], compact), 862 + TASK_AVG(tasks[i], wpcopy)); 863 + } else { 864 + suc &= BOOL_FPRINT(out, DELAY_FMT_DEFAULT, 865 + TASK_AVG(tasks[i], cpu), 866 + TASK_AVG(tasks[i], blkio), 867 + TASK_AVG(tasks[i], irq), 868 + TASK_AVG(tasks[i], mem)); 869 + } 897 870 } 898 871 899 872 suc &= BOOL_FPRINT(out, "\n"); ··· 952 891 953 892 /* Main loop */ 954 893 while (running) { 894 + /* Exit when sort field do not match display mode */ 895 + if (!(cfg.sort_field->supported_modes & cfg.display_mode)) { 896 + fprintf(stderr, "Sort field not supported in this mode\n"); 897 + display_available_fields(cfg.display_mode); 898 + break; 899 + } 900 + 955 901 /* Read PSI statistics */ 956 902 read_psi_stats(); 957 903