Skip to content

Commit 5e57515

Browse files
Fan Yuakpm00
authored andcommitted
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>
1 parent 99d9c55 commit 5e57515

1 file changed

Lines changed: 121 additions & 45 deletions

File tree

tools/accounting/delaytop.c

Lines changed: 121 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,8 @@
7373
#define PSI_LINE_FORMAT "%-12s %6.1f%%/%6.1f%%/%6.1f%%/%8llu(ms)\n"
7474
#define DELAY_FMT_DEFAULT "%8.2f %8.2f %8.2f %8.2f\n"
7575
#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, \
7878
offsetof(struct task_info, name##_delay_total), \
7979
offsetof(struct task_info, name##_count), \
8080
modes}
@@ -140,6 +140,7 @@ struct container_stats {
140140
/* Delay field structure */
141141
struct field_desc {
142142
const char *name; /* Field name for cmdline argument */
143+
const char *cmd_char; /* Interactive command */
143144
unsigned long total_offset; /* Offset of total delay in task_info */
144145
unsigned long count_offset; /* Offset of count in task_info */
145146
size_t supported_modes; /* Supported display modes */
@@ -165,17 +166,18 @@ static int task_count;
165166
static int running = 1;
166167
static struct container_stats container_stats;
167168
static const struct field_desc sort_fields[] = {
168-
SORT_FIELD(cpu, MODE_DEFAULT),
169-
SORT_FIELD(blkio, MODE_DEFAULT),
170-
SORT_FIELD(irq, MODE_DEFAULT),
171-
SORT_FIELD(mem, MODE_DEFAULT | MODE_MEMVERBOSE),
172-
SORT_FIELD(swapin, MODE_MEMVERBOSE),
173-
SORT_FIELD(freepages, MODE_MEMVERBOSE),
174-
SORT_FIELD(thrashing, MODE_MEMVERBOSE),
175-
SORT_FIELD(compact, MODE_MEMVERBOSE),
176-
SORT_FIELD(wpcopy, MODE_MEMVERBOSE),
169+
SORT_FIELD(cpu, c, MODE_DEFAULT),
170+
SORT_FIELD(blkio, i, MODE_DEFAULT),
171+
SORT_FIELD(irq, q, MODE_DEFAULT),
172+
SORT_FIELD(mem, m, MODE_DEFAULT | MODE_MEMVERBOSE),
173+
SORT_FIELD(swapin, s, MODE_MEMVERBOSE),
174+
SORT_FIELD(freepages, r, MODE_MEMVERBOSE),
175+
SORT_FIELD(thrashing, t, MODE_MEMVERBOSE),
176+
SORT_FIELD(compact, p, MODE_MEMVERBOSE),
177+
SORT_FIELD(wpcopy, w, MODE_MEMVERBOSE),
177178
END_FIELD
178179
};
180+
static int sort_selected;
179181

180182
/* Netlink socket variables */
181183
static int nl_sd = -1;
@@ -197,6 +199,19 @@ static void disable_raw_mode(void)
197199
tcsetattr(STDIN_FILENO, TCSAFLUSH, &orig_termios);
198200
}
199201

202+
/* Find field descriptor by command line */
203+
static const struct field_desc *get_field_by_cmd_char(char ch)
204+
{
205+
const struct field_desc *field;
206+
207+
for (field = sort_fields; field->name != NULL; field++) {
208+
if (field->cmd_char[0] == ch)
209+
return field;
210+
}
211+
212+
return NULL;
213+
}
214+
200215
/* Find field descriptor by name with string comparison */
201216
static const struct field_desc *get_field_by_name(const char *name)
202217
{
@@ -870,6 +885,18 @@ static void display_results(void)
870885
container_stats.nr_stopped, container_stats.nr_uninterruptible,
871886
container_stats.nr_io_wait);
872887
}
888+
889+
/* Interacive command */
890+
suc &= BOOL_FPRINT(out, "[o]sort [M]memverbose [q]quit\n");
891+
if (sort_selected) {
892+
if (cfg.display_mode == MODE_MEMVERBOSE)
893+
suc &= BOOL_FPRINT(out,
894+
"sort selection: [m]MEM [r]RCL [t]THR [p]CMP [w]WP\n");
895+
else
896+
suc &= BOOL_FPRINT(out,
897+
"sort selection: [c]CPU [i]IO [m]MEM [q]IRQ\n");
898+
}
899+
873900
/* Task delay output */
874901
suc &= BOOL_FPRINT(out, "Top %d processes (sorted by %s delay):\n",
875902
cfg.max_processes, get_name_by_field(cfg.sort_field));
@@ -919,11 +946,78 @@ static void display_results(void)
919946
perror("Error writing to output");
920947
}
921948

949+
/* Check for keyboard input with timeout based on cfg.delay */
950+
static char check_for_keypress(void)
951+
{
952+
struct timeval tv = {cfg.delay, 0};
953+
fd_set readfds;
954+
char ch = 0;
955+
956+
FD_ZERO(&readfds);
957+
FD_SET(STDIN_FILENO, &readfds);
958+
int r = select(STDIN_FILENO + 1, &readfds, NULL, NULL, &tv);
959+
960+
if (r > 0 && FD_ISSET(STDIN_FILENO, &readfds)) {
961+
read(STDIN_FILENO, &ch, 1);
962+
return ch;
963+
}
964+
965+
return 0;
966+
}
967+
968+
#define MAX_MODE_SIZE 2
969+
static void toggle_display_mode(void)
970+
{
971+
static const size_t modes[MAX_MODE_SIZE] = {MODE_DEFAULT, MODE_MEMVERBOSE};
972+
static size_t cur_index;
973+
974+
cur_index = (cur_index + 1) % MAX_MODE_SIZE;
975+
cfg.display_mode = modes[cur_index];
976+
}
977+
978+
/* Handle keyboard input: sorting selection, mode toggle, or quit */
979+
static void handle_keypress(char ch, int *running)
980+
{
981+
const struct field_desc *field;
982+
983+
/* Change sort field */
984+
if (sort_selected) {
985+
field = get_field_by_cmd_char(ch);
986+
if (field && (field->supported_modes & cfg.display_mode))
987+
cfg.sort_field = field;
988+
989+
sort_selected = 0;
990+
/* Handle mode changes or quit */
991+
} else {
992+
switch (ch) {
993+
case 'o':
994+
sort_selected = 1;
995+
break;
996+
case 'M':
997+
toggle_display_mode();
998+
for (field = sort_fields; field->name != NULL; field++) {
999+
if (field->supported_modes & cfg.display_mode) {
1000+
cfg.sort_field = field;
1001+
break;
1002+
}
1003+
}
1004+
break;
1005+
case 'q':
1006+
case 'Q':
1007+
*running = 0;
1008+
break;
1009+
default:
1010+
break;
1011+
}
1012+
}
1013+
}
1014+
9221015
/* Main function */
9231016
int main(int argc, char **argv)
9241017
{
1018+
const struct field_desc *field;
9251019
int iterations = 0;
926-
int use_q_quit = 0;
1020+
char keypress;
9271021

9281022
/* Parse command line arguments */
9291023
parse_args(argc, argv);
@@ -943,20 +1037,20 @@ int main(int argc, char **argv)
9431037
exit(1);
9441038
}
9451039

946-
if (!cfg.output_one_time) {
947-
use_q_quit = 1;
948-
enable_raw_mode();
949-
printf("Press 'q' to quit.\n");
950-
fflush(stdout);
951-
}
1040+
/* Set terminal to non-canonical mode for interaction */
1041+
enable_raw_mode();
9521042

9531043
/* Main loop */
9541044
while (running) {
955-
/* Exit when sort field do not match display mode */
1045+
/* Auto-switch sort field when not matching display mode */
9561046
if (!(cfg.sort_field->supported_modes & cfg.display_mode)) {
957-
fprintf(stderr, "Sort field not supported in this mode\n");
958-
display_available_fields(cfg.display_mode);
959-
break;
1047+
for (field = sort_fields; field->name != NULL; field++) {
1048+
if (field->supported_modes & cfg.display_mode) {
1049+
cfg.sort_field = field;
1050+
printf("Auto-switched sort field to: %s\n", field->name);
1051+
break;
1052+
}
1053+
}
9601054
}
9611055

9621056
/* Read PSI statistics */
@@ -983,32 +1077,14 @@ int main(int argc, char **argv)
9831077
if (cfg.output_one_time)
9841078
break;
9851079

986-
/* Check for 'q' key to quit */
987-
if (use_q_quit) {
988-
struct timeval tv = {cfg.delay, 0};
989-
fd_set readfds;
990-
991-
FD_ZERO(&readfds);
992-
FD_SET(STDIN_FILENO, &readfds);
993-
int r = select(STDIN_FILENO+1, &readfds, NULL, NULL, &tv);
994-
995-
if (r > 0 && FD_ISSET(STDIN_FILENO, &readfds)) {
996-
char ch = 0;
997-
998-
read(STDIN_FILENO, &ch, 1);
999-
if (ch == 'q' || ch == 'Q') {
1000-
running = 0;
1001-
break;
1002-
}
1003-
}
1004-
} else {
1005-
sleep(cfg.delay);
1006-
}
1080+
/* Keypress for interactive usage */
1081+
keypress = check_for_keypress();
1082+
if (keypress)
1083+
handle_keypress(keypress, &running);
10071084
}
10081085

10091086
/* Restore terminal mode */
1010-
if (use_q_quit)
1011-
disable_raw_mode();
1087+
disable_raw_mode();
10121088

10131089
/* Cleanup */
10141090
close(nl_sd);

0 commit comments

Comments
 (0)