6969 int ret = fprintf(stream, fmt, ##__VA_ARGS__); \
7070 ret >= 0; \
7171})
72+ #define TASK_AVG (task , field ) average_ms((task).field##_delay_total, (task).field##_count)
7273#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 ) \
7477 {#name, \
7578 offsetof(struct task_info, name##_delay_total), \
76- offsetof(struct task_info, name##_count)}
79+ offsetof(struct task_info, name##_count), \
80+ modes}
7781#define END_FIELD {NULL, 0, 0}
7882
83+ /* Display mode types */
84+ #define MODE_TYPE_ALL (0xFFFFFFFF)
85+ #define MODE_DEFAULT (1 << 0)
86+ #define MODE_MEMVERBOSE (1 << 1)
87+
7988/* PSI statistics structure */
8089struct psi_stats {
8190 double cpu_some_avg10 , cpu_some_avg60 , cpu_some_avg300 ;
@@ -115,6 +124,8 @@ struct task_info {
115124 unsigned long long wpcopy_delay_total ;
116125 unsigned long long irq_count ;
117126 unsigned long long irq_delay_total ;
127+ unsigned long long mem_count ;
128+ unsigned long long mem_delay_total ;
118129};
119130
120131/* Container statistics structure */
@@ -131,6 +142,7 @@ struct field_desc {
131142 const char * name ; /* Field name for cmdline argument */
132143 unsigned long total_offset ; /* Offset of total delay in task_info */
133144 unsigned long count_offset ; /* Offset of count in task_info */
145+ size_t supported_modes ; /* Supported display modes */
134146};
135147
136148/* Program settings structure */
@@ -142,6 +154,7 @@ struct config {
142154 int monitor_pid ; /* Monitor specific PID */
143155 char * container_path ; /* Path to container cgroup */
144156 const struct field_desc * sort_field ; /* Current sort field */
157+ size_t display_mode ; /* Current display mode */
145158};
146159
147160/* Global variables */
@@ -152,14 +165,15 @@ static int task_count;
152165static int running = 1 ;
153166static struct container_stats container_stats ;
154167static const struct field_desc sort_fields [] = {
155- SORT_FIELD (cpu ),
156- SORT_FIELD (blkio ),
157- SORT_FIELD (irq ),
158- SORT_FIELD (swapin ),
159- SORT_FIELD (freepages ),
160- SORT_FIELD (thrashing ),
161- SORT_FIELD (compact ),
162- SORT_FIELD (wpcopy ),
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 ),
163177 END_FIELD
164178};
165179
@@ -207,14 +221,16 @@ static const char *get_name_by_field(const struct field_desc *field)
207221}
208222
209223/* Generate string of available field names */
210- static void display_available_fields (void )
224+ static void display_available_fields (size_t mode )
211225{
212226 const struct field_desc * field ;
213227 char buf [MAX_BUF_LEN ];
214228
215229 buf [0 ] = '\0' ;
216230
217231 for (field = sort_fields ; field -> name != NULL ; field ++ ) {
232+ if (!(field -> supported_modes & mode ))
233+ continue ;
218234 strncat (buf , "|" , MAX_BUF_LEN - strlen (buf ) - 1 );
219235 strncat (buf , field -> name , MAX_BUF_LEN - strlen (buf ) - 1 );
220236 buf [MAX_BUF_LEN - 1 ] = '\0' ;
@@ -235,7 +251,8 @@ static void usage(void)
235251 " -o, --once Display once and exit\n"
236252 " -p, --pid=PID Monitor only the specified PID\n"
237253 " -C, --container=PATH Monitor the container at specified cgroup path\n"
238- " -s, --sort=FIELD Sort by delay field (default: cpu)\n" );
254+ " -s, --sort=FIELD Sort by delay field (default: cpu)\n"
255+ " -M, --memverbose Display memory detailed information\n" );
239256 exit (0 );
240257}
241258
@@ -253,6 +270,7 @@ static void parse_args(int argc, char **argv)
253270 {"processes" , required_argument , 0 , 'P' },
254271 {"sort" , required_argument , 0 , 's' },
255272 {"container" , required_argument , 0 , 'C' },
273+ {"memverbose" , no_argument , 0 , 'M' },
256274 {0 , 0 , 0 , 0 }
257275 };
258276
@@ -264,11 +282,12 @@ static void parse_args(int argc, char **argv)
264282 cfg .output_one_time = 0 ;
265283 cfg .monitor_pid = 0 ; /* 0 means monitor all PIDs */
266284 cfg .container_path = NULL ;
285+ cfg .display_mode = MODE_DEFAULT ;
267286
268287 while (1 ) {
269288 int option_index = 0 ;
270289
271- c = getopt_long (argc , argv , "hd:n:p:oP:C:s:" , long_options , & option_index );
290+ c = getopt_long (argc , argv , "hd:n:p:oP:C:s:M " , long_options , & option_index );
272291 if (c == -1 )
273292 break ;
274293
@@ -325,19 +344,42 @@ static void parse_args(int argc, char **argv)
325344 /* Show available fields if invalid option provided */
326345 if (!field ) {
327346 fprintf (stderr , "Error: invalid sort field '%s'\n" , optarg );
328- display_available_fields ();
347+ display_available_fields (MODE_TYPE_ALL );
329348 exit (1 );
330349 }
331350
332351 cfg .sort_field = field ;
333352 break ;
353+ case 'M' :
354+ cfg .display_mode = MODE_MEMVERBOSE ;
355+ cfg .sort_field = get_field_by_name ("mem" );
356+ break ;
334357 default :
335358 fprintf (stderr , "Try 'delaytop --help' for more information.\n" );
336359 exit (1 );
337360 }
338361 }
339362}
340363
364+ /* Calculate average delay in milliseconds for overall memory */
365+ static void set_mem_delay_total (struct task_info * t )
366+ {
367+ t -> mem_delay_total = t -> swapin_delay_total +
368+ t -> freepages_delay_total +
369+ t -> thrashing_delay_total +
370+ t -> compact_delay_total +
371+ t -> wpcopy_delay_total ;
372+ }
373+
374+ static void set_mem_count (struct task_info * t )
375+ {
376+ t -> mem_count = t -> swapin_count +
377+ t -> freepages_count +
378+ t -> thrashing_count +
379+ t -> compact_count +
380+ t -> wpcopy_count ;
381+ }
382+
341383/* Create a raw netlink socket and bind */
342384static int create_nl_socket (void )
343385{
@@ -611,6 +653,8 @@ static void fetch_and_fill_task_info(int pid, const char *comm)
611653 SET_TASK_STAT (task_count , wpcopy_delay_total );
612654 SET_TASK_STAT (task_count , irq_count );
613655 SET_TASK_STAT (task_count , irq_delay_total );
656+ set_mem_count (& tasks [task_count ]);
657+ set_mem_delay_total (& tasks [task_count ]);
614658 task_count ++ ;
615659 }
616660 break ;
@@ -829,27 +873,44 @@ static void display_results(void)
829873 /* Task delay output */
830874 suc &= BOOL_FPRINT (out , "Top %d processes (sorted by %s delay):\n" ,
831875 cfg .max_processes , get_name_by_field (cfg .sort_field ));
832- suc &= BOOL_FPRINT (out , "%5s %5s %-17s" , "PID" , "TGID" , "COMMAND" );
833- suc &= BOOL_FPRINT (out , "%7s %7s %7s %7s %7s %7s %7s %7s\n" ,
834- "CPU(ms)" , "IO(ms)" , "SWAP(ms)" , "RCL(ms)" ,
835- "THR(ms)" , "CMP(ms)" , "WP(ms)" , "IRQ(ms)" );
836876
837- suc &= BOOL_FPRINT (out , "-----------------------------------------------" );
838- suc &= BOOL_FPRINT (out , "----------------------------------------------\n" );
877+ suc &= BOOL_FPRINT (out , "%8s %8s %-17s" , "PID" , "TGID" , "COMMAND" );
878+ if (cfg .display_mode == MODE_MEMVERBOSE ) {
879+ suc &= BOOL_FPRINT (out , "%8s %8s %8s %8s %8s %8s\n" ,
880+ "MEM(ms)" , "SWAP(ms)" , "RCL(ms)" ,
881+ "THR(ms)" , "CMP(ms)" , "WP(ms)" );
882+ suc &= BOOL_FPRINT (out , "-----------------------" );
883+ suc &= BOOL_FPRINT (out , "-----------------------" );
884+ suc &= BOOL_FPRINT (out , "-----------------------" );
885+ suc &= BOOL_FPRINT (out , "---------------------\n" );
886+ } else {
887+ suc &= BOOL_FPRINT (out , "%8s %8s %8s %8s\n" ,
888+ "CPU(ms)" , "IO(ms)" , "IRQ(ms)" , "MEM(ms)" );
889+ suc &= BOOL_FPRINT (out , "-----------------------" );
890+ suc &= BOOL_FPRINT (out , "-----------------------" );
891+ suc &= BOOL_FPRINT (out , "--------------------------\n" );
892+ }
893+
839894 count = task_count < cfg .max_processes ? task_count : cfg .max_processes ;
840895
841896 for (i = 0 ; i < count ; i ++ ) {
842- suc &= BOOL_FPRINT (out , "%5d %5d %-15s" ,
897+ suc &= BOOL_FPRINT (out , "%8d %8d %-15s" ,
843898 tasks [i ].pid , tasks [i ].tgid , tasks [i ].command );
844- suc &= BOOL_FPRINT (out , "%7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f\n" ,
845- average_ms (tasks [i ].cpu_delay_total , tasks [i ].cpu_count ),
846- average_ms (tasks [i ].blkio_delay_total , tasks [i ].blkio_count ),
847- average_ms (tasks [i ].swapin_delay_total , tasks [i ].swapin_count ),
848- average_ms (tasks [i ].freepages_delay_total , tasks [i ].freepages_count ),
849- average_ms (tasks [i ].thrashing_delay_total , tasks [i ].thrashing_count ),
850- average_ms (tasks [i ].compact_delay_total , tasks [i ].compact_count ),
851- average_ms (tasks [i ].wpcopy_delay_total , tasks [i ].wpcopy_count ),
852- average_ms (tasks [i ].irq_delay_total , tasks [i ].irq_count ));
899+ if (cfg .display_mode == MODE_MEMVERBOSE ) {
900+ suc &= BOOL_FPRINT (out , DELAY_FMT_MEMVERBOSE ,
901+ TASK_AVG (tasks [i ], mem ),
902+ TASK_AVG (tasks [i ], swapin ),
903+ TASK_AVG (tasks [i ], freepages ),
904+ TASK_AVG (tasks [i ], thrashing ),
905+ TASK_AVG (tasks [i ], compact ),
906+ TASK_AVG (tasks [i ], wpcopy ));
907+ } else {
908+ suc &= BOOL_FPRINT (out , DELAY_FMT_DEFAULT ,
909+ TASK_AVG (tasks [i ], cpu ),
910+ TASK_AVG (tasks [i ], blkio ),
911+ TASK_AVG (tasks [i ], irq ),
912+ TASK_AVG (tasks [i ], mem ));
913+ }
853914 }
854915
855916 suc &= BOOL_FPRINT (out , "\n" );
@@ -891,6 +952,13 @@ int main(int argc, char **argv)
891952
892953 /* Main loop */
893954 while (running ) {
955+ /* Exit when sort field do not match display mode */
956+ 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 ;
960+ }
961+
894962 /* Read PSI statistics */
895963 read_psi_stats ();
896964
0 commit comments