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 interactive mode with keyboard controls

The original delaytop only supported static output with limited
interaction. Users had to restart the tool with different command-line
options to change sorting or display modes, which disrupted continuous
monitoring and reduced productivity during performance investigations.

Adds real-time interactive controls through keyboard input:
1) Add interactive menu system with visual prompts
2) Support dynamic sorting changes without restarting
3) Enable toggle of memory verbose mode with 'M' key

The interactive mode transforms delaytop from a static monitoring tool
into a dynamic investigation platform, allowing users to adapt the view in
real-time based on observed performance patterns.

Link: https://lkml.kernel.org/r/20250907001338580EURha20BxWFmBSrUpS8D1@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
5e57515d 99d9c55f

+121 -45
+121 -45
tools/accounting/delaytop.c
··· 73 73 #define PSI_LINE_FORMAT "%-12s %6.1f%%/%6.1f%%/%6.1f%%/%8llu(ms)\n" 74 74 #define DELAY_FMT_DEFAULT "%8.2f %8.2f %8.2f %8.2f\n" 75 75 #define DELAY_FMT_MEMVERBOSE "%8.2f %8.2f %8.2f %8.2f %8.2f %8.2f\n" 76 - #define SORT_FIELD(name, modes) \ 77 - {#name, \ 76 + #define SORT_FIELD(name, cmd, modes) \ 77 + {#name, #cmd, \ 78 78 offsetof(struct task_info, name##_delay_total), \ 79 79 offsetof(struct task_info, name##_count), \ 80 80 modes} ··· 140 140 /* Delay field structure */ 141 141 struct field_desc { 142 142 const char *name; /* Field name for cmdline argument */ 143 + const char *cmd_char; /* Interactive command */ 143 144 unsigned long total_offset; /* Offset of total delay in task_info */ 144 145 unsigned long count_offset; /* Offset of count in task_info */ 145 146 size_t supported_modes; /* Supported display modes */ ··· 166 165 static int running = 1; 167 166 static struct container_stats container_stats; 168 167 static const struct field_desc sort_fields[] = { 169 - SORT_FIELD(cpu, MODE_DEFAULT), 170 - SORT_FIELD(blkio, MODE_DEFAULT), 171 - SORT_FIELD(irq, MODE_DEFAULT), 172 - SORT_FIELD(mem, MODE_DEFAULT | MODE_MEMVERBOSE), 173 - SORT_FIELD(swapin, MODE_MEMVERBOSE), 174 - SORT_FIELD(freepages, MODE_MEMVERBOSE), 175 - SORT_FIELD(thrashing, MODE_MEMVERBOSE), 176 - SORT_FIELD(compact, MODE_MEMVERBOSE), 177 - SORT_FIELD(wpcopy, MODE_MEMVERBOSE), 168 + SORT_FIELD(cpu, c, MODE_DEFAULT), 169 + SORT_FIELD(blkio, i, MODE_DEFAULT), 170 + SORT_FIELD(irq, q, MODE_DEFAULT), 171 + SORT_FIELD(mem, m, MODE_DEFAULT | MODE_MEMVERBOSE), 172 + SORT_FIELD(swapin, s, MODE_MEMVERBOSE), 173 + SORT_FIELD(freepages, r, MODE_MEMVERBOSE), 174 + SORT_FIELD(thrashing, t, MODE_MEMVERBOSE), 175 + SORT_FIELD(compact, p, MODE_MEMVERBOSE), 176 + SORT_FIELD(wpcopy, w, MODE_MEMVERBOSE), 178 177 END_FIELD 179 178 }; 179 + static int sort_selected; 180 180 181 181 /* Netlink socket variables */ 182 182 static int nl_sd = -1; ··· 197 195 static void disable_raw_mode(void) 198 196 { 199 197 tcsetattr(STDIN_FILENO, TCSAFLUSH, &orig_termios); 198 + } 199 + 200 + /* Find field descriptor by command line */ 201 + static const struct field_desc *get_field_by_cmd_char(char ch) 202 + { 203 + const struct field_desc *field; 204 + 205 + for (field = sort_fields; field->name != NULL; field++) { 206 + if (field->cmd_char[0] == ch) 207 + return field; 208 + } 209 + 210 + return NULL; 200 211 } 201 212 202 213 /* Find field descriptor by name with string comparison */ ··· 885 870 container_stats.nr_stopped, container_stats.nr_uninterruptible, 886 871 container_stats.nr_io_wait); 887 872 } 873 + 874 + /* Interacive command */ 875 + suc &= BOOL_FPRINT(out, "[o]sort [M]memverbose [q]quit\n"); 876 + if (sort_selected) { 877 + if (cfg.display_mode == MODE_MEMVERBOSE) 878 + suc &= BOOL_FPRINT(out, 879 + "sort selection: [m]MEM [r]RCL [t]THR [p]CMP [w]WP\n"); 880 + else 881 + suc &= BOOL_FPRINT(out, 882 + "sort selection: [c]CPU [i]IO [m]MEM [q]IRQ\n"); 883 + } 884 + 888 885 /* Task delay output */ 889 886 suc &= BOOL_FPRINT(out, "Top %d processes (sorted by %s delay):\n", 890 887 cfg.max_processes, get_name_by_field(cfg.sort_field)); ··· 946 919 perror("Error writing to output"); 947 920 } 948 921 922 + /* Check for keyboard input with timeout based on cfg.delay */ 923 + static char check_for_keypress(void) 924 + { 925 + struct timeval tv = {cfg.delay, 0}; 926 + fd_set readfds; 927 + char ch = 0; 928 + 929 + FD_ZERO(&readfds); 930 + FD_SET(STDIN_FILENO, &readfds); 931 + int r = select(STDIN_FILENO + 1, &readfds, NULL, NULL, &tv); 932 + 933 + if (r > 0 && FD_ISSET(STDIN_FILENO, &readfds)) { 934 + read(STDIN_FILENO, &ch, 1); 935 + return ch; 936 + } 937 + 938 + return 0; 939 + } 940 + 941 + #define MAX_MODE_SIZE 2 942 + static void toggle_display_mode(void) 943 + { 944 + static const size_t modes[MAX_MODE_SIZE] = {MODE_DEFAULT, MODE_MEMVERBOSE}; 945 + static size_t cur_index; 946 + 947 + cur_index = (cur_index + 1) % MAX_MODE_SIZE; 948 + cfg.display_mode = modes[cur_index]; 949 + } 950 + 951 + /* Handle keyboard input: sorting selection, mode toggle, or quit */ 952 + static void handle_keypress(char ch, int *running) 953 + { 954 + const struct field_desc *field; 955 + 956 + /* Change sort field */ 957 + if (sort_selected) { 958 + field = get_field_by_cmd_char(ch); 959 + if (field && (field->supported_modes & cfg.display_mode)) 960 + cfg.sort_field = field; 961 + 962 + sort_selected = 0; 963 + /* Handle mode changes or quit */ 964 + } else { 965 + switch (ch) { 966 + case 'o': 967 + sort_selected = 1; 968 + break; 969 + case 'M': 970 + toggle_display_mode(); 971 + for (field = sort_fields; field->name != NULL; field++) { 972 + if (field->supported_modes & cfg.display_mode) { 973 + cfg.sort_field = field; 974 + break; 975 + } 976 + } 977 + break; 978 + case 'q': 979 + case 'Q': 980 + *running = 0; 981 + break; 982 + default: 983 + break; 984 + } 985 + } 986 + } 987 + 949 988 /* Main function */ 950 989 int main(int argc, char **argv) 951 990 { 991 + const struct field_desc *field; 952 992 int iterations = 0; 953 - int use_q_quit = 0; 993 + char keypress; 954 994 955 995 /* Parse command line arguments */ 956 996 parse_args(argc, argv); ··· 1037 943 exit(1); 1038 944 } 1039 945 1040 - if (!cfg.output_one_time) { 1041 - use_q_quit = 1; 1042 - enable_raw_mode(); 1043 - printf("Press 'q' to quit.\n"); 1044 - fflush(stdout); 1045 - } 946 + /* Set terminal to non-canonical mode for interaction */ 947 + enable_raw_mode(); 1046 948 1047 949 /* Main loop */ 1048 950 while (running) { 1049 - /* Exit when sort field do not match display mode */ 951 + /* Auto-switch sort field when not matching display mode */ 1050 952 if (!(cfg.sort_field->supported_modes & cfg.display_mode)) { 1051 - fprintf(stderr, "Sort field not supported in this mode\n"); 1052 - display_available_fields(cfg.display_mode); 1053 - break; 953 + for (field = sort_fields; field->name != NULL; field++) { 954 + if (field->supported_modes & cfg.display_mode) { 955 + cfg.sort_field = field; 956 + printf("Auto-switched sort field to: %s\n", field->name); 957 + break; 958 + } 959 + } 1054 960 } 1055 961 1056 962 /* Read PSI statistics */ ··· 1077 983 if (cfg.output_one_time) 1078 984 break; 1079 985 1080 - /* Check for 'q' key to quit */ 1081 - if (use_q_quit) { 1082 - struct timeval tv = {cfg.delay, 0}; 1083 - fd_set readfds; 1084 - 1085 - FD_ZERO(&readfds); 1086 - FD_SET(STDIN_FILENO, &readfds); 1087 - int r = select(STDIN_FILENO+1, &readfds, NULL, NULL, &tv); 1088 - 1089 - if (r > 0 && FD_ISSET(STDIN_FILENO, &readfds)) { 1090 - char ch = 0; 1091 - 1092 - read(STDIN_FILENO, &ch, 1); 1093 - if (ch == 'q' || ch == 'Q') { 1094 - running = 0; 1095 - break; 1096 - } 1097 - } 1098 - } else { 1099 - sleep(cfg.delay); 1100 - } 986 + /* Keypress for interactive usage */ 987 + keypress = check_for_keypress(); 988 + if (keypress) 989 + handle_keypress(keypress, &running); 1101 990 } 1102 991 1103 992 /* Restore terminal mode */ 1104 - if (use_q_quit) 1105 - disable_raw_mode(); 993 + disable_raw_mode(); 1106 994 1107 995 /* Cleanup */ 1108 996 close(nl_sd);