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 */
141141struct 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;
165166static int running = 1 ;
166167static struct container_stats container_stats ;
167168static 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 */
181183static 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 */
201216static 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 */
9231016int 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