2 * Copyright (C) 2011-2012 Julien Desfossez
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License Version 2 as
6 * published by the Free Software Foundation;
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 #include <semaphore.h>
27 #include "cursesdisplay.h"
28 #include "lttngtoptypes.h"
29 #include "iostreamtop.h"
32 #define DEFAULT_DELAY 15
33 #define MAX_LINE_LENGTH 50
34 #define MAX_LOG_LINES 4
36 /* to prevent concurrent updates of the different windows */
37 sem_t update_display_sem
;
40 WINDOW
*footer
, *header
, *center
, *status
;
41 WINDOW
*pref_panel_window
= NULL
;
42 PANEL
*pref_panel
, *main_panel
;
44 int pref_panel_visible
= 0;
45 int pref_line_selected
= 0;
46 int pref_current_sort
= 0;
48 int last_display_index
, currently_displayed_index
;
50 struct processtop
*selected_process
= NULL
;
53 int selected_line
= 0; /* select bar position */
54 int selected_in_list
= 0; /* selection relative to the whole list */
55 int list_offset
= 0; /* first index in the list to display (scroll) */
57 char log_lines
[MAX_LINE_LENGTH
* MAX_LOG_LINES
+ MAX_LOG_LINES
];
59 int max_elements
= 80;
61 int toggle_threads
= 1;
63 int toggle_pause
= -1;
66 GPtrArray
*selected_processes
;
68 pthread_t keyboard_thread
;
70 struct header_view cputopview
[6];
71 struct header_view iostreamtopview
[3];
72 struct header_view fileview
[3];
73 struct header_view kprobeview
[2];
82 sem_post(&goodtodisplay
);
83 sem_post(&end_trace_sem
);
84 sem_post(&goodtoupdate
);
87 static void handle_sigterm(int signal
)
89 pthread_cancel(keyboard_thread
);
97 halfdelay(DEFAULT_DELAY
);
99 intrflush(stdscr
, false);
100 keypad(stdscr
, true);
105 init_pair(1, COLOR_RED
, COLOR_BLACK
); /* - */
106 init_pair(2, COLOR_GREEN
, COLOR_BLACK
); /* + */
107 init_pair(3, COLOR_BLACK
, COLOR_WHITE
); /* keys */
108 init_pair(4, COLOR_WHITE
, COLOR_GREEN
); /* keys activated */
109 init_pair(5, COLOR_WHITE
, COLOR_BLUE
); /* select line */
110 init_pair(6, COLOR_WHITE
, COLOR_GREEN
); /* selected process */
112 termtype
= getenv("TERM");
113 if (!strcmp(termtype
, "xterm") || !strcmp(termtype
, "xterm-color") ||
114 !strcmp(termtype
, "vt220")) {
115 define_key("\033[H", KEY_HOME
);
116 define_key("\033[F", KEY_END
);
117 define_key("\033OP", KEY_F(1));
118 define_key("\033OQ", KEY_F(2));
119 define_key("\033OR", KEY_F(3));
120 define_key("\033OS", KEY_F(4));
121 define_key("\0330U", KEY_F(6));
122 define_key("\033[11~", KEY_F(1));
123 define_key("\033[12~", KEY_F(2));
124 define_key("\033[13~", KEY_F(3));
125 define_key("\033[14~", KEY_F(4));
126 define_key("\033[16~", KEY_F(6));
127 define_key("\033[17;2~", KEY_F(18));
129 signal(SIGTERM
, handle_sigterm
);
130 signal(SIGINT
, handle_sigterm
);
131 mousemask(BUTTON1_CLICKED
, NULL
);
135 WINDOW
*create_window(int height
, int width
, int startx
, int starty
)
138 win
= newwin(height
, width
, startx
, starty
);
144 WINDOW
*create_window_no_border(int height
, int width
, int startx
, int starty
)
147 win
= newwin(height
, width
, startx
, starty
);
152 void print_digit(WINDOW
*win
, int digit
)
155 wattron(win
, COLOR_PAIR(1));
156 wprintw(win
, "%d", digit
);
157 wattroff(win
, COLOR_PAIR(1));
158 } else if (digit
> 0) {
159 wattron(win
, COLOR_PAIR(2));
160 wprintw(win
, "+%d", digit
);
161 wattroff(win
, COLOR_PAIR(2));
167 void print_digits(WINDOW
*win
, int first
, int second
)
170 print_digit(win
, first
);
172 print_digit(win
, second
);
176 void print_headers(int line
, char *desc
, int value
, int first
, int second
)
178 wattron(header
, A_BOLD
);
179 mvwprintw(header
, line
, 4, "%s", desc
);
180 wattroff(header
, A_BOLD
);
181 mvwprintw(header
, line
, 16, "%d", value
);
182 wmove(header
, line
, 24);
183 print_digits(header
, first
, second
);
184 wmove(header
, line
, 40);
187 void set_window_title(WINDOW
*win
, char *title
)
189 wattron(win
, A_BOLD
);
190 mvwprintw(win
, 0, 1, title
);
191 wattroff(win
, A_BOLD
);
194 void print_log(char *str
)
197 int current_line
= 1;
198 int current_char
= 1;
200 /* rotate the line buffer */
201 if (nb_log_lines
>= MAX_LOG_LINES
) {
202 tmp
= strndup(log_lines
, MAX_LINE_LENGTH
* MAX_LOG_LINES
+ MAX_LOG_LINES
);
203 tmp2
= strchr(tmp
, '\n');
204 memset(log_lines
, '\0', strlen(log_lines
));
205 strncat(log_lines
, tmp2
+ 1, strlen(tmp2
) - 1);
206 log_lines
[strlen(log_lines
)] = '\n';
207 log_lines
[strlen(log_lines
)] = '\0';
212 strncat(log_lines
, str
, MAX_LINE_LENGTH
- 1);
214 if (nb_log_lines
< MAX_LOG_LINES
)
215 log_lines
[strlen(log_lines
)] = '\n';
216 log_lines
[strlen(log_lines
)] = '\0';
220 set_window_title(status
, "Status");
221 for (i
= 0; i
< strlen(log_lines
); i
++) {
222 if (log_lines
[i
] == '\n') {
223 wmove(status
, ++current_line
, 1);
226 mvwprintw(status
, current_line
, current_char
++, "%c",
233 int process_selected(struct processtop
*process
)
236 struct processtop
*stored_process
;
238 for (i
= 0; i
< selected_processes
->len
; i
++) {
239 stored_process
= g_ptr_array_index(selected_processes
, i
);
242 if (stored_process
->tid
== process
->tid
)
248 void update_selected_processes()
251 struct processtop
*stored_process
;
253 if (process_selected(selected_process
)) {
254 for (i
= 0; i
< selected_processes
->len
; i
++) {
255 stored_process
= g_ptr_array_index(selected_processes
, i
);
258 if (stored_process
->tid
== selected_process
->tid
)
259 g_ptr_array_remove(selected_processes
,
261 print_log("Process removed");
264 g_ptr_array_add(selected_processes
, selected_process
);
265 print_log("Process added");
269 void print_key(WINDOW
*win
, char *key
, char *desc
, int toggle
)
276 wattron(win
, COLOR_PAIR(pair
));
277 wprintw(footer
, "%s", key
);
278 wattroff(win
, COLOR_PAIR(pair
));
279 wprintw(footer
, ":%s", desc
);
284 sem_wait(&update_display_sem
);
287 print_key(footer
, "F2", "CPUtop ", current_view
== cpu
);
288 print_key(footer
, "F3", "PerfTop ", current_view
== perf
);
289 print_key(footer
, "F4", "IOTop ", current_view
== iostream
);
290 print_key(footer
, "Enter", "Details ", current_view
== process_details
);
291 print_key(footer
, "Space", "Highlight ", 0);
292 print_key(footer
, "q", "Quit ", 0);
293 print_key(footer
, "r", "Pref ", 0);
294 print_key(footer
, "t", "Threads ", toggle_threads
);
295 print_key(footer
, "v", "Virt ", toggle_virt
);
296 print_key(footer
, "p", "Pause ", toggle_pause
);
299 sem_post(&update_display_sem
);
306 set_window_title(header
, "Statistics for interval [gathering data...[");
307 wattron(header
, A_BOLD
);
308 mvwprintw(header
, 1, 4, "CPUs");
309 mvwprintw(header
, 2, 4, "Threads");
310 mvwprintw(header
, 3, 4, "FDs");
311 wattroff(header
, A_BOLD
);
315 static void scale_unit(uint64_t bytes
, char *ret
)
317 if (bytes
>= 1000000000)
318 sprintf(ret
, "%" PRIu64
"G", bytes
/1000000000);
319 if (bytes
>= 1000000)
320 sprintf(ret
, "%" PRIu64
"M", bytes
/1000000);
321 else if (bytes
>= 1000)
322 sprintf(ret
, "%" PRIu64
"K", bytes
/1000);
324 sprintf(ret
, "%" PRIu64
, bytes
);
330 struct processtop
*tmp
;
333 for (i
= 0; i
< data
->process_table
->len
; i
++) {
334 tmp
= g_ptr_array_index(data
->process_table
, i
);
335 total
+= tmp
->fileread
;
336 total
+= tmp
->filewrite
;
344 struct tm start
, end
;
345 uint64_t ts_nsec_start
, ts_nsec_end
;
348 ts_nsec_start
= data
->start
% NSEC_PER_SEC
;
349 start
= format_timestamp(data
->start
);
351 ts_nsec_end
= data
->end
% NSEC_PER_SEC
;
352 end
= format_timestamp(data
->end
);
356 set_window_title(header
, "Statistics for interval ");
357 wattron(header
, A_BOLD
);
359 wprintw(header
, "[%02d:%02d:%02d.%09" PRIu64
", %02d:%02d:%02d.%09" PRIu64
"[",
360 start
.tm_hour
, start
.tm_min
, start
.tm_sec
, ts_nsec_start
,
361 end
.tm_hour
, end
.tm_min
, end
.tm_sec
, ts_nsec_end
);
362 mvwprintw(header
, 1, 4, "CPUs");
363 wattroff(header
, A_BOLD
);
364 wprintw(header
, "\t%d\t(max/cpu : %0.2f%)", data
->cpu_table
->len
,
365 100.0/data
->cpu_table
->len
);
366 print_headers(2, "Threads", data
->nbthreads
, data
->nbnewthreads
,
367 -1*(data
->nbdeadthreads
));
368 print_headers(3, "FDs", data
->nbfiles
, data
->nbnewfiles
,
369 -1*(data
->nbclosedfiles
));
370 scale_unit(total_io(), io
);
371 mvwprintw(header
, 3, 43, "%sB/sec", io
);
375 gint
sort_by_cpu_desc(gconstpointer p1
, gconstpointer p2
)
377 struct processtop
*n1
= *(struct processtop
**)p1
;
378 struct processtop
*n2
= *(struct processtop
**)p2
;
379 unsigned long totaln1
= n1
->totalcpunsec
;
380 unsigned long totaln2
= n2
->totalcpunsec
;
382 if (totaln1
< totaln2
)
384 if (totaln1
== totaln2
)
389 gint
sort_by_tid_desc(gconstpointer p1
, gconstpointer p2
)
391 struct processtop
*n1
= *(struct processtop
**)p1
;
392 struct processtop
*n2
= *(struct processtop
**)p2
;
393 unsigned long totaln1
= n1
->tid
;
394 unsigned long totaln2
= n2
->tid
;
396 if (totaln1
< totaln2
)
398 if (totaln1
== totaln2
)
403 gint
sort_by_pid_desc(gconstpointer p1
, gconstpointer p2
)
405 struct processtop
*n1
= *(struct processtop
**)p1
;
406 struct processtop
*n2
= *(struct processtop
**)p2
;
407 unsigned long totaln1
= n1
->pid
;
408 unsigned long totaln2
= n2
->pid
;
410 if (totaln1
< totaln2
)
412 if (totaln1
== totaln2
)
417 gint
sort_by_process_read_desc(gconstpointer p1
, gconstpointer p2
)
419 struct processtop
*n1
= *(struct processtop
**)p1
;
420 struct processtop
*n2
= *(struct processtop
**)p2
;
421 unsigned long totaln1
= n1
->fileread
;
422 unsigned long totaln2
= n2
->fileread
;
424 if (totaln1
< totaln2
)
426 if (totaln1
== totaln2
)
431 gint
sort_by_process_write_desc(gconstpointer p1
, gconstpointer p2
)
433 struct processtop
*n1
= *(struct processtop
**)p1
;
434 struct processtop
*n2
= *(struct processtop
**)p2
;
435 unsigned long totaln1
= n1
->filewrite
;
436 unsigned long totaln2
= n2
->filewrite
;
438 if (totaln1
< totaln2
)
440 if (totaln1
== totaln2
)
445 gint
sort_by_process_total_desc(gconstpointer p1
, gconstpointer p2
)
447 struct processtop
*n1
= *(struct processtop
**)p1
;
448 struct processtop
*n2
= *(struct processtop
**)p2
;
449 unsigned long totaln1
= n1
->totalfilewrite
+ n1
->totalfileread
;
450 unsigned long totaln2
= n2
->totalfilewrite
+ n2
->totalfileread
;
452 if (totaln1
< totaln2
)
454 if (totaln1
== totaln2
)
459 gint
sort_by_file_read_desc(gconstpointer p1
, gconstpointer p2
)
461 struct files
*n1
= *(struct files
**)p1
;
462 struct files
*n2
= *(struct files
**)p2
;
463 unsigned long totaln1
;
464 unsigned long totaln2
;
469 if (totaln1
< totaln2
)
471 if (totaln1
== totaln2
)
476 gint
sort_by_file_write_desc(gconstpointer p1
, gconstpointer p2
)
478 struct files
*n1
= *(struct files
**)p1
;
479 struct files
*n2
= *(struct files
**)p2
;
480 unsigned long totaln1
;
481 unsigned long totaln2
;
486 if (totaln1
< totaln2
)
488 if (totaln1
== totaln2
)
493 gint
sort_by_file_fd_desc(gconstpointer p1
, gconstpointer p2
)
495 struct files
*n1
= *(struct files
**)p1
;
496 struct files
*n2
= *(struct files
**)p2
;
497 unsigned long totaln1
;
498 unsigned long totaln2
;
503 if (totaln1
< totaln2
)
505 if (totaln1
== totaln2
)
510 gint
sort_by_cpu_group_by_threads_desc(gconstpointer p1
, gconstpointer p2
)
512 struct processtop
*n1
= *(struct processtop
**)p1
;
513 struct processtop
*n2
= *(struct processtop
**)p2
;
514 unsigned long totaln1
= n1
->threadstotalcpunsec
;
515 unsigned long totaln2
= n2
->threadstotalcpunsec
;
517 if (totaln1
< totaln2
)
519 if (totaln1
== totaln2
)
524 void update_kprobes_display()
527 struct kprobes
*probe
;
528 int header_offset
= 2;
529 int current_line
= 0;
531 set_window_title(center
, "Kprobes Top ");
532 wattron(center
, A_BOLD
);
534 for (i
= 0; i
< 2; i
++) {
535 if (kprobeview
[i
].sort
) {
536 wattron(center
, A_UNDERLINE
);
537 pref_current_sort
= i
;
539 mvwprintw(center
, 1, column
, "%s", kprobeview
[i
].title
);
540 wattroff(center
, A_UNDERLINE
);
543 wattroff(center
, A_BOLD
);
545 for (i
= 0; i
< data
->kprobes_table
->len
; i
++) {
547 probe
= g_ptr_array_index(data
->kprobes_table
, i
);
548 mvwprintw(center
, current_line
+ header_offset
, column
,
549 "%s", probe
->probe_name
+ 6);
551 mvwprintw(center
, current_line
+ header_offset
, column
,
557 void update_cputop_display()
560 int header_offset
= 2;
561 struct processtop
*tmp
;
562 unsigned long elapsed
;
564 int nblinedisplayed
= 0;
565 int current_line
= 0;
566 int current_row_offset
;
569 elapsed
= data
->end
- data
->start
;
570 maxcputime
= elapsed
* data
->cpu_table
->len
/ 100.0;
572 if (cputopview
[0].sort
== 1)
573 g_ptr_array_sort(data
->process_table
, sort_by_cpu_desc
);
574 else if (cputopview
[1].sort
== 1)
575 g_ptr_array_sort(data
->process_table
, sort_by_pid_desc
);
576 else if (cputopview
[2].sort
== 1)
577 g_ptr_array_sort(data
->process_table
, sort_by_tid_desc
);
578 else if (cputopview
[3].sort
== 1)
579 g_ptr_array_sort(data
->process_table
, sort_by_cpu_desc
);
581 g_ptr_array_sort(data
->process_table
, sort_by_cpu_desc
);
583 set_window_title(center
, "CPU Top");
584 wattron(center
, A_BOLD
);
586 for (i
= 0; i
< 6; i
++) {
587 if (toggle_virt
< 0 && (i
== 3 || i
== 4)) {
590 if (cputopview
[i
].sort
) {
591 wattron(center
, A_UNDERLINE
);
592 pref_current_sort
= i
;
594 mvwprintw(center
, 1, column
, cputopview
[i
].title
);
595 wattroff(center
, A_UNDERLINE
);
598 wattroff(center
, A_BOLD
);
600 max_center_lines
= LINES
- 5 - 7 - 1 - header_offset
;
602 /* iterate the process (thread) list */
603 for (i
= list_offset
; i
< data
->process_table
->len
&&
604 nblinedisplayed
< max_center_lines
; i
++) {
605 tmp
= g_ptr_array_index(data
->process_table
, i
);
606 current_row_offset
= 1;
607 if (!opt_tid
&& (opt_hostname
&& !lookup_hostname_list(tmp
->hostname
)))
609 if (!opt_hostname
&& (opt_tid
&& !lookup_tid_list(tmp
->pid
)))
611 if ((opt_tid
&& !lookup_tid_list(tmp
->tid
)) &&
612 (opt_hostname
&& !lookup_hostname_list(tmp
->hostname
)))
615 if (tmp
->pid
!= tmp
->tid
)
616 if (toggle_threads
== -1)
619 if (process_selected(tmp
)) {
620 wattron(center
, COLOR_PAIR(6));
621 mvwhline(center
, current_line
+ header_offset
, 1, ' ', COLS
-3);
623 if (current_line
== selected_line
) {
624 selected_process
= tmp
;
625 wattron(center
, COLOR_PAIR(5));
626 mvwhline(center
, current_line
+ header_offset
, 1, ' ', COLS
-3);
629 mvwprintw(center
, current_line
+ header_offset
,
630 current_row_offset
, "%1.2f",
631 tmp
->totalcpunsec
/ maxcputime
);
632 current_row_offset
+= 10;
634 mvwprintw(center
, current_line
+ header_offset
,
635 current_row_offset
, "%d", tmp
->pid
);
636 current_row_offset
+= 10;
638 mvwprintw(center
, current_line
+ header_offset
,
639 current_row_offset
, "%d", tmp
->tid
);
640 current_row_offset
+= 10;
641 if (toggle_virt
> 0) {
643 mvwprintw(center
, current_line
+ header_offset
,
644 current_row_offset
, "%d", tmp
->vpid
);
645 current_row_offset
+= 10;
647 mvwprintw(center
, current_line
+ header_offset
,
648 current_row_offset
, "%d", tmp
->vtid
);
649 current_row_offset
+= 10;
652 mvwprintw(center
, current_line
+ header_offset
,
653 current_row_offset
, "%s", tmp
->comm
);
654 wattroff(center
, COLOR_PAIR(6));
655 wattroff(center
, COLOR_PAIR(5));
661 gint
sort_perf(gconstpointer p1
, gconstpointer p2
, gpointer key
)
663 struct processtop
*n1
= *(struct processtop
**) p1
;
664 struct processtop
*n2
= *(struct processtop
**) p2
;
666 struct perfcounter
*tmp1
, *tmp2
;
667 unsigned long totaln2
= 0;
668 unsigned long totaln1
= 0;
673 tmp1
= g_hash_table_lookup(n1
->perf
, key
);
677 totaln1
= tmp1
->count
;
679 tmp2
= g_hash_table_lookup(n2
->perf
, key
);
683 totaln2
= tmp2
->count
;
685 if (totaln1
< totaln2
)
687 if (totaln1
== totaln2
) {
690 if (totaln1
< totaln2
)
697 void print_key_title(char *key
, int line
)
699 wattron(center
, A_BOLD
);
700 mvwprintw(center
, line
, 1, "%s", key
);
701 mvwprintw(center
, line
, 30, " ");
702 wattroff(center
, A_BOLD
);
705 void update_process_details()
707 unsigned long elapsed
;
709 struct processtop
*tmp
;
710 struct files
*file_tmp
;
713 char filename_buf
[COLS
];
716 GPtrArray
*newfilearray
= g_ptr_array_new();
718 struct perfcounter
*perfn1
, *perfn2
;
721 set_window_title(center
, "Process details");
724 tmp
= find_process_tid(data
,
725 selected_process
->tid
,
726 selected_process
->comm
);
727 elapsed
= data
->end
- data
->start
;
728 maxcputime
= elapsed
* data
->cpu_table
->len
/ 100.0;
730 print_key_title("Name", line
++);
731 wprintw(center
, "%s", selected_process
->comm
);
732 print_key_title("TID", line
++);
733 wprintw(center
, "%d", selected_process
->tid
);
735 print_key_title("Does not exit at this time", 3);
739 print_key_title("PID", line
++);
740 wprintw(center
, "%d", tmp
->pid
);
741 print_key_title("PPID", line
++);
742 wprintw(center
, "%d", tmp
->ppid
);
743 print_key_title("VPID", line
++);
744 wprintw(center
, "%d", tmp
->vpid
);
745 print_key_title("VTID", line
++);
746 wprintw(center
, "%d", tmp
->vtid
);
747 print_key_title("VPPID", line
++);
748 wprintw(center
, "%d", tmp
->vppid
);
749 print_key_title("CPU", line
++);
750 wprintw(center
, "%1.2f %%", tmp
->totalcpunsec
/maxcputime
);
752 print_key_title("READ B/s", line
++);
753 scale_unit(tmp
->fileread
, unit
);
754 wprintw(center
, "%s", unit
);
756 print_key_title("WRITE B/s", line
++);
757 scale_unit(tmp
->filewrite
, unit
);
758 wprintw(center
, "%s", unit
);
760 g_hash_table_iter_init(&iter
, global_perf_liszt
);
761 while (g_hash_table_iter_next (&iter
, &key
, (gpointer
) &perfn1
)) {
762 print_key_title((char *) key
, line
++);
763 perfn2
= g_hash_table_lookup(tmp
->perf
, (char *) key
);
764 wprintw(center
, "%d", perfn2
? perfn2
->count
: 0);
768 wattron(center
, A_BOLD
);
770 for (i
= 0; i
< 3; i
++) {
771 if (fileview
[i
].sort
) {
772 pref_current_sort
= i
;
773 wattron(center
, A_UNDERLINE
);
775 mvwprintw(center
, line
, column
, fileview
[i
].title
);
776 wattroff(center
, A_UNDERLINE
);
779 mvwprintw(center
, line
++, column
, "FILENAME");
780 wattroff(center
, A_BOLD
);
783 * since the process_files_table array could contain NULL file structures,
784 * and that the positions inside the array is important (it is the FD), we
785 * need to create a temporary array that we can sort.
787 for (i
= 0; i
< tmp
->process_files_table
->len
; i
++) {
788 file_tmp
= g_ptr_array_index(tmp
->process_files_table
, i
);
790 g_ptr_array_add(newfilearray
, file_tmp
);
793 if (fileview
[0].sort
== 1)
794 g_ptr_array_sort(newfilearray
, sort_by_file_fd_desc
);
795 else if (fileview
[1].sort
== 1)
796 g_ptr_array_sort(newfilearray
, sort_by_file_read_desc
);
797 else if (fileview
[2].sort
== 1)
798 g_ptr_array_sort(newfilearray
, sort_by_file_write_desc
);
800 g_ptr_array_sort(newfilearray
, sort_by_file_read_desc
);
802 for (i
= selected_line
; i
< newfilearray
->len
&&
803 i
< (selected_line
+ max_center_lines
- line
+ 2); i
++) {
804 file_tmp
= g_ptr_array_index(newfilearray
, i
);
807 mvwprintw(center
, line
+ j
, 1, "%d", file_tmp
->fd
);
808 scale_unit(file_tmp
->read
, unit
);
809 mvwprintw(center
, line
+ j
, 11, "%s", unit
);
810 scale_unit(file_tmp
->write
, unit
);
811 mvwprintw(center
, line
+ j
, 21, "%s", unit
);
812 snprintf(filename_buf
, COLS
- 25, "%s", file_tmp
->name
);
813 mvwprintw(center
, line
+ j
, 31, "%s", filename_buf
);
816 g_ptr_array_free(newfilearray
, TRUE
);
822 int nblinedisplayed
= 0;
823 int current_line
= 0;
824 struct processtop
*tmp
;
825 int header_offset
= 2;
827 struct perfcounter
*perfn1
, *perfn2
;
828 char *perf_key
= NULL
;
833 set_window_title(center
, "Perf Top");
834 wattron(center
, A_BOLD
);
835 mvwprintw(center
, 1, 1, "PID");
836 mvwprintw(center
, 1, 11, "TID");
837 mvwprintw(center
, 1, 22, "NAME");
840 g_hash_table_iter_init(&iter
, global_perf_liszt
);
841 while (g_hash_table_iter_next (&iter
, &key
, (gpointer
) &perfn1
)) {
842 if (perfn1
->visible
) {
844 /* pref_current_sort = i; */
845 wattron(center
, A_UNDERLINE
);
847 /* + 5 to strip the "perf_" prefix */
848 mvwprintw(center
, 1, perf_row
, "%s",
850 wattroff(center
, A_UNDERLINE
);
854 perf_key
= (char *) key
;
857 wattroff(center
, A_BOLD
);
859 g_ptr_array_sort_with_data(data
->process_table
, sort_perf
, perf_key
);
861 for (i
= 0; i
< data
->process_table
->len
&&
862 nblinedisplayed
< max_center_lines
; i
++) {
863 tmp
= g_ptr_array_index(data
->process_table
, i
);
865 if (!opt_tid
&& (opt_hostname
&& !lookup_hostname_list(tmp
->hostname
)))
867 if (!opt_hostname
&& (opt_tid
&& !lookup_tid_list(tmp
->pid
)))
869 if ((opt_tid
&& !lookup_tid_list(tmp
->tid
)) &&
870 (opt_hostname
&& !lookup_hostname_list(tmp
->hostname
)))
873 if (tmp
->pid
!= tmp
->tid
)
874 if (toggle_threads
== -1)
877 if (process_selected(tmp
)) {
878 wattron(center
, COLOR_PAIR(6));
879 mvwhline(center
, current_line
+ header_offset
, 1, ' ', COLS
-3);
881 if (current_line
== selected_line
) {
882 selected_process
= tmp
;
883 wattron(center
, COLOR_PAIR(5));
884 mvwhline(center
, current_line
+ header_offset
, 1, ' ', COLS
-3);
887 mvwprintw(center
, current_line
+ header_offset
, 1, "%d", tmp
->pid
);
888 mvwprintw(center
, current_line
+ header_offset
, 11, "%d", tmp
->tid
);
889 mvwprintw(center
, current_line
+ header_offset
, 22, "%s", tmp
->comm
);
891 g_hash_table_iter_init(&iter
, global_perf_liszt
);
894 while (g_hash_table_iter_next (&iter
, &key
, (gpointer
) &perfn1
)) {
895 if (perfn1
->visible
) {
896 perfn2
= g_hash_table_lookup(tmp
->perf
, (char *) key
);
898 value
= perfn2
->count
;
901 mvwprintw(center
, current_line
+ header_offset
,
902 perf_row
, "%d", value
);
907 wattroff(center
, COLOR_PAIR(6));
908 wattroff(center
, COLOR_PAIR(5));
914 void update_iostream()
917 int header_offset
= 2;
918 struct processtop
*tmp
;
919 int nblinedisplayed
= 0;
920 int current_line
= 0;
925 set_window_title(center
, "IO Top");
926 wattron(center
, A_BOLD
);
927 mvwprintw(center
, 1, 1, "PID");
928 mvwprintw(center
, 1, 11, "TID");
929 mvwprintw(center
, 1, 22, "NAME");
931 for (i
= 0; i
< 3; i
++) {
932 if (iostreamtopview
[i
].sort
) {
933 pref_current_sort
= i
;
934 wattron(center
, A_UNDERLINE
);
936 mvwprintw(center
, 1, column
, iostreamtopview
[i
].title
);
937 wattroff(center
, A_UNDERLINE
);
940 wattroff(center
, A_BOLD
);
941 wattroff(center
, A_UNDERLINE
);
943 if (iostreamtopview
[0].sort
== 1)
944 g_ptr_array_sort(data
->process_table
, sort_by_process_read_desc
);
945 else if (iostreamtopview
[1].sort
== 1)
946 g_ptr_array_sort(data
->process_table
, sort_by_process_write_desc
);
947 else if (iostreamtopview
[2].sort
== 1)
948 g_ptr_array_sort(data
->process_table
, sort_by_process_total_desc
);
950 g_ptr_array_sort(data
->process_table
, sort_by_process_total_desc
);
952 for (i
= list_offset
; i
< data
->process_table
->len
&&
953 nblinedisplayed
< max_center_lines
; i
++) {
954 tmp
= g_ptr_array_index(data
->process_table
, i
);
956 if (!opt_tid
&& (opt_hostname
&& !lookup_hostname_list(tmp
->hostname
)))
958 if (!opt_hostname
&& (opt_tid
&& !lookup_tid_list(tmp
->pid
)))
960 if ((opt_tid
&& !lookup_tid_list(tmp
->tid
)) &&
961 (opt_hostname
&& !lookup_hostname_list(tmp
->hostname
)))
964 if (tmp
->pid
!= tmp
->tid
)
965 if (toggle_threads
== -1)
968 if (process_selected(tmp
)) {
969 wattron(center
, COLOR_PAIR(6));
970 mvwhline(center
, current_line
+ header_offset
, 1, ' ', COLS
-3);
972 if (current_line
== selected_line
) {
973 selected_process
= tmp
;
974 wattron(center
, COLOR_PAIR(5));
975 mvwhline(center
, current_line
+ header_offset
, 1, ' ', COLS
-3);
978 mvwprintw(center
, current_line
+ header_offset
, 1, "%d", tmp
->pid
);
980 mvwprintw(center
, current_line
+ header_offset
, 11, "%d", tmp
->tid
);
982 mvwprintw(center
, current_line
+ header_offset
, 22, "%s", tmp
->comm
);
984 /* READ (bytes/sec) */
985 scale_unit(tmp
->fileread
, unit
);
986 mvwprintw(center
, current_line
+ header_offset
, 40, "%s", unit
);
988 /* WRITE (bytes/sec) */
989 scale_unit(tmp
->filewrite
, unit
);
990 mvwprintw(center
, current_line
+ header_offset
, 52, "%s", unit
);
993 total
= tmp
->totalfileread
+ tmp
->totalfilewrite
;
995 scale_unit(total
, unit
);
996 mvwprintw(center
, current_line
+ header_offset
, 64, "%s", unit
);
998 wattroff(center
, COLOR_PAIR(6));
999 wattroff(center
, COLOR_PAIR(5));
1005 void update_current_view()
1007 sem_wait(&update_display_sem
);
1014 switch (current_view
) {
1016 update_cputop_display();
1021 case process_details
:
1022 update_process_details();
1028 update_cputop_display();
1031 update_kprobes_display();
1038 sem_post(&update_display_sem
);
1041 void update_process_detail_sort(int *line_selected
)
1048 if (*line_selected
> (size
- 1))
1049 *line_selected
= size
- 1;
1050 else if (*line_selected
< 0)
1053 if (fileview
[*line_selected
].sort
== 1)
1054 fileview
[*line_selected
].reverse
= 1;
1055 for (i
= 0; i
< size
; i
++)
1056 fileview
[i
].sort
= 0;
1057 fileview
[*line_selected
].sort
= 1;
1060 void update_process_detail_pref(int *line_selected
, int toggle_view
, int toggle_sort
)
1067 if (pref_panel_window
) {
1068 del_panel(pref_panel
);
1069 delwin(pref_panel_window
);
1073 pref_panel_window
= create_window(size
+ 2, 30, 10, 10);
1074 pref_panel
= new_panel(pref_panel_window
);
1076 werase(pref_panel_window
);
1077 box(pref_panel_window
, 0 , 0);
1078 set_window_title(pref_panel_window
, "Process Detail Preferences ");
1079 wattron(pref_panel_window
, A_BOLD
);
1080 mvwprintw(pref_panel_window
, size
+ 1, 1,
1081 " 's' : sort, space : toggle");
1082 wattroff(pref_panel_window
, A_BOLD
);
1084 if (*line_selected
> (size
- 1))
1085 *line_selected
= size
- 1;
1086 else if (*line_selected
< 0)
1088 if (toggle_sort
== 1) {
1089 update_process_detail_sort(line_selected
);
1090 update_current_view();
1093 for (i
= 0; i
< size
; i
++) {
1094 if (i
== *line_selected
) {
1095 wattron(pref_panel_window
, COLOR_PAIR(5));
1096 mvwhline(pref_panel_window
, i
+ 1, 1, ' ', 30 - 2);
1098 if (fileview
[i
].sort
== 1)
1099 wattron(pref_panel_window
, A_BOLD
);
1100 mvwprintw(pref_panel_window
, i
+ 1, 1, "[-] %s",
1102 wattroff(pref_panel_window
, A_BOLD
);
1103 wattroff(pref_panel_window
, COLOR_PAIR(5));
1110 void update_iostream_sort(int *line_selected
)
1116 if (*line_selected
> (size
- 1))
1117 *line_selected
= size
- 1;
1118 else if (*line_selected
< 0)
1120 if (iostreamtopview
[*line_selected
].sort
== 1)
1121 iostreamtopview
[*line_selected
].reverse
= 1;
1122 for (i
= 0; i
< size
; i
++)
1123 iostreamtopview
[i
].sort
= 0;
1124 iostreamtopview
[*line_selected
].sort
= 1;
1128 void update_iostream_pref(int *line_selected
, int toggle_view
, int toggle_sort
)
1135 if (pref_panel_window
) {
1136 del_panel(pref_panel
);
1137 delwin(pref_panel_window
);
1141 pref_panel_window
= create_window(size
+ 2, 30, 10, 10);
1142 pref_panel
= new_panel(pref_panel_window
);
1144 werase(pref_panel_window
);
1145 box(pref_panel_window
, 0 , 0);
1146 set_window_title(pref_panel_window
, "IOTop Preferences ");
1147 wattron(pref_panel_window
, A_BOLD
);
1148 mvwprintw(pref_panel_window
, size
+ 1, 1,
1149 " 's' : sort, space : toggle");
1150 wattroff(pref_panel_window
, A_BOLD
);
1152 if (*line_selected
> (size
- 1))
1153 *line_selected
= size
- 1;
1154 else if (*line_selected
< 0)
1156 if (toggle_sort
== 1) {
1157 update_iostream_sort(line_selected
);
1158 update_current_view();
1161 for (i
= 0; i
< size
; i
++) {
1162 if (i
== *line_selected
) {
1163 wattron(pref_panel_window
, COLOR_PAIR(5));
1164 mvwhline(pref_panel_window
, i
+ 1, 1, ' ', 30 - 2);
1166 if (iostreamtopview
[i
].sort
== 1)
1167 wattron(pref_panel_window
, A_BOLD
);
1168 mvwprintw(pref_panel_window
, i
+ 1, 1, "[-] %s",
1169 iostreamtopview
[i
].title
);
1170 wattroff(pref_panel_window
, A_BOLD
);
1171 wattroff(pref_panel_window
, COLOR_PAIR(5));
1178 void update_cpu_sort(int *line_selected
)
1183 if (*line_selected
> (size
- 1))
1184 *line_selected
= size
- 1;
1185 else if (*line_selected
< 0)
1188 /* special case, we don't support sorting by procname for now */
1189 if (*line_selected
!= 3) {
1190 if (cputopview
[*line_selected
].sort
== 1)
1191 cputopview
[*line_selected
].reverse
= 1;
1192 for (i
= 0; i
< size
; i
++)
1193 cputopview
[i
].sort
= 0;
1194 cputopview
[*line_selected
].sort
= 1;
1198 void update_cpu_pref(int *line_selected
, int toggle_view
, int toggle_sort
)
1205 if (pref_panel_window
) {
1206 del_panel(pref_panel
);
1207 delwin(pref_panel_window
);
1211 pref_panel_window
= create_window(size
+ 2, 30, 10, 10);
1212 pref_panel
= new_panel(pref_panel_window
);
1214 werase(pref_panel_window
);
1215 box(pref_panel_window
, 0 , 0);
1216 set_window_title(pref_panel_window
, "CPUTop Preferences ");
1217 wattron(pref_panel_window
, A_BOLD
);
1218 mvwprintw(pref_panel_window
, size
+ 1, 1,
1219 " 's' : sort, space : toggle");
1220 wattroff(pref_panel_window
, A_BOLD
);
1222 if (*line_selected
> (size
- 1))
1223 *line_selected
= size
- 1;
1224 else if (*line_selected
< 0)
1226 if (toggle_sort
== 1) {
1227 update_cpu_sort(line_selected
);
1228 update_current_view();
1231 for (i
= 0; i
< size
; i
++) {
1232 if (i
== *line_selected
) {
1233 wattron(pref_panel_window
, COLOR_PAIR(5));
1234 mvwhline(pref_panel_window
, i
+ 1, 1, ' ', 30 - 2);
1236 if (cputopview
[i
].sort
== 1)
1237 wattron(pref_panel_window
, A_BOLD
);
1238 mvwprintw(pref_panel_window
, i
+ 1, 1, "[-] %s",
1239 cputopview
[i
].title
);
1240 wattroff(pref_panel_window
, A_BOLD
);
1241 wattroff(pref_panel_window
, COLOR_PAIR(5));
1248 void update_perf_sort(int *line_selected
)
1251 struct perfcounter
*perf
;
1255 size
= g_hash_table_size(global_perf_liszt
);
1256 if (*line_selected
> (size
- 1))
1257 *line_selected
= size
- 1;
1258 else if (*line_selected
< 0)
1262 perflist
= g_list_first(g_hash_table_get_keys(global_perf_liszt
));
1264 perf
= g_hash_table_lookup(global_perf_liszt
, perflist
->data
);
1265 if (i
!= *line_selected
)
1270 perflist
= g_list_next(perflist
);
1274 void update_perf_pref(int *line_selected
, int toggle_view
, int toggle_sort
)
1277 struct perfcounter
*perf
;
1283 if (pref_panel_window
) {
1284 del_panel(pref_panel
);
1285 delwin(pref_panel_window
);
1287 size
= g_hash_table_size(global_perf_liszt
);
1289 pref_panel_window
= create_window(size
+ 2, 30, 10, 10);
1290 pref_panel
= new_panel(pref_panel_window
);
1292 werase(pref_panel_window
);
1293 box(pref_panel_window
, 0 , 0);
1294 set_window_title(pref_panel_window
, "Perf Preferences ");
1295 wattron(pref_panel_window
, A_BOLD
);
1296 mvwprintw(pref_panel_window
, g_hash_table_size(global_perf_liszt
) + 1, 1,
1297 " 's' : sort, space : toggle");
1298 wattroff(pref_panel_window
, A_BOLD
);
1300 if (*line_selected
> (size
- 1))
1301 *line_selected
= size
- 1;
1302 else if (*line_selected
< 0)
1305 if (toggle_sort
== 1) {
1306 update_perf_sort(line_selected
);
1307 update_current_view();
1311 perflist
= g_list_first(g_hash_table_get_keys(global_perf_liszt
));
1313 perf
= g_hash_table_lookup(global_perf_liszt
, perflist
->data
);
1314 if (i
== *line_selected
&& toggle_view
== 1) {
1315 perf
->visible
= perf
->visible
== 1 ? 0:1;
1316 update_current_view();
1318 if (i
== *line_selected
) {
1319 wattron(pref_panel_window
, COLOR_PAIR(5));
1320 mvwhline(pref_panel_window
, i
+ 1, 1, ' ', 30 - 2);
1322 if (perf
->sort
== 1)
1323 wattron(pref_panel_window
, A_BOLD
);
1324 mvwprintw(pref_panel_window
, i
+ 1, 1, "[%c] %s",
1325 perf
->visible
== 1 ? 'x' : ' ',
1326 (char *) perflist
->data
+ 5);
1327 wattroff(pref_panel_window
, A_BOLD
);
1328 wattroff(pref_panel_window
, COLOR_PAIR(5));
1330 perflist
= g_list_next(perflist
);
1336 int update_preference_panel(int *line_selected
, int toggle_view
, int toggle_sort
)
1340 switch(current_view
) {
1342 update_perf_pref(line_selected
, toggle_view
, toggle_sort
);
1345 update_cpu_pref(line_selected
, toggle_view
, toggle_sort
);
1348 update_iostream_pref(line_selected
, toggle_view
, toggle_sort
);
1350 case process_details
:
1351 update_process_detail_pref(line_selected
, toggle_view
, toggle_sort
);
1361 int update_sort(int *line_selected
)
1365 switch(current_view
) {
1367 update_perf_sort(line_selected
);
1370 update_cpu_sort(line_selected
);
1373 update_iostream_sort(line_selected
);
1375 case process_details
:
1376 update_process_detail_sort(line_selected
);
1387 void toggle_pref_panel(void)
1391 if (pref_panel_visible
) {
1392 hide_panel(pref_panel
);
1393 pref_panel_visible
= 0;
1395 ret
= update_preference_panel(&pref_line_selected
, 0, 0);
1398 show_panel(pref_panel
);
1399 pref_panel_visible
= 1;
1405 void display(unsigned int index
)
1407 last_display_index
= index
;
1408 currently_displayed_index
= index
;
1409 data
= g_ptr_array_index(copies
, index
);
1412 max_elements
= data
->process_table
->len
;
1413 update_current_view();
1419 void pause_display()
1423 sem_wait(&pause_sem
);
1426 void resume_display()
1429 print_log("Resume");
1430 sem_post(&pause_sem
);
1433 void *handle_keyboard(void *p
)
1436 while((ch
= getch())) {
1438 /* Move the cursor and scroll */
1441 if (pref_panel_visible
) {
1442 pref_line_selected
++;
1443 update_preference_panel(&pref_line_selected
, 0, 0);
1445 if (selected_line
< (max_center_lines
- 1) &&
1446 selected_line
< max_elements
- 1) {
1449 } else if (selected_in_list
< (max_elements
- 1)
1450 && (list_offset
< (max_elements
- max_center_lines
))) {
1454 update_current_view();
1461 if (pref_panel_visible
) {
1462 if (pref_line_selected
> 0)
1463 pref_line_selected
--;
1464 update_preference_panel(&pref_line_selected
, 0, 0);
1466 if (selected_line
> 0) {
1469 } else if (selected_in_list
> 0 && list_offset
> 0) {
1473 update_current_view();
1479 /* Navigate the history with arrows */
1481 if (currently_displayed_index
> 0) {
1482 currently_displayed_index
--;
1483 print_log("Going back in time");
1485 print_log("Cannot rewind, last data is already displayed");
1487 data
= g_ptr_array_index(copies
, currently_displayed_index
);
1488 max_elements
= data
->process_table
->len
;
1490 /* we force to pause the display when moving in time */
1491 if (toggle_pause
< 0)
1494 update_current_view();
1498 if (currently_displayed_index
< last_display_index
) {
1499 currently_displayed_index
++;
1500 print_log("Going forward in time");
1501 data
= g_ptr_array_index(copies
, currently_displayed_index
);
1502 max_elements
= data
->process_table
->len
;
1503 update_current_view();
1506 print_log("Manually moving forward");
1508 if (toggle_pause
> 0) {
1509 sem_post(&pause_sem
);
1510 update_current_view();
1511 sem_wait(&pause_sem
);
1517 if (pref_panel_visible
) {
1518 update_preference_panel(&pref_line_selected
, 1, 0);
1520 update_selected_processes();
1521 update_current_view();
1525 if (pref_panel_visible
)
1526 update_preference_panel(&pref_line_selected
, 0, 1);
1529 /* perf uses a hashtable, it is ordered backward */
1530 if (current_view
== perf
) {
1531 pref_current_sort
--;
1532 } else if (!pref_panel_visible
) {
1533 pref_current_sort
++;
1535 update_sort(&pref_current_sort
);
1536 update_current_view();
1539 /* perf uses a hashtable, it is ordered backward */
1540 if (current_view
== perf
) {
1541 pref_current_sort
++;
1542 } else if (!pref_panel_visible
) {
1543 pref_current_sort
--;
1545 update_sort(&pref_current_sort
);
1546 update_current_view();
1549 case 13: /* FIXME : KEY_ENTER ?? */
1550 if (pref_panel_visible
)
1552 if (current_view
!= process_details
) {
1553 previous_view
= current_view
;
1554 current_view
= process_details
;
1556 current_view
= previous_view
;
1557 previous_view
= process_details
;
1560 update_current_view();
1564 if (pref_panel_visible
)
1565 toggle_pref_panel();
1568 update_current_view();
1571 if (pref_panel_visible
)
1572 toggle_pref_panel();
1575 update_current_view();
1578 if (pref_panel_visible
)
1579 toggle_pref_panel();
1580 current_view
= perf
;
1582 update_current_view();
1585 if (pref_panel_visible
)
1586 toggle_pref_panel();
1587 current_view
= iostream
;
1589 update_current_view();
1592 if (pref_panel_visible
)
1593 toggle_pref_panel();
1594 current_view
= kprobes
;
1596 update_current_view();
1601 /* exit keyboard thread */
1605 toggle_threads
*= -1;
1606 update_current_view();
1609 if (toggle_pause
< 0) {
1616 toggle_pref_panel();
1620 update_current_view();
1622 /* ESCAPE, but slow to process, don't know why */
1624 if (pref_panel_visible
)
1625 toggle_pref_panel();
1626 else if (current_view
== process_details
) {
1627 current_view
= previous_view
;
1628 previous_view
= process_details
;
1630 update_current_view();
1634 update_current_view();
1642 void init_view_headers()
1644 cputopview
[0].title
= strdup("CPU(%)");
1645 cputopview
[0].sort
= 1;
1646 cputopview
[1].title
= strdup("PID");
1647 cputopview
[2].title
= strdup("TID");
1648 cputopview
[3].title
= strdup("VPID");
1649 cputopview
[4].title
= strdup("VTID");
1650 cputopview
[5].title
= strdup("NAME");
1652 iostreamtopview
[0].title
= strdup("R (B/sec)");
1653 iostreamtopview
[1].title
= strdup("W (B/sec)");
1654 iostreamtopview
[2].title
= strdup("Total (B)");
1655 iostreamtopview
[2].sort
= 1;
1657 fileview
[0].title
= strdup("FD");
1658 fileview
[1].title
= strdup("READ");
1659 fileview
[1].sort
= 1;
1660 fileview
[2].title
= strdup("WRITE");
1662 kprobeview
[0].title
= strdup("NAME");
1663 kprobeview
[1].title
= strdup("HIT");
1664 kprobeview
[1].sort
= 1;
1669 selected_processes
= g_ptr_array_new();
1670 sem_init(&update_display_sem
, 0, 1);
1671 init_view_headers();
1674 header
= create_window(5, COLS
- 1, 0, 0);
1675 center
= create_window(LINES
- 5 - 7, COLS
- 1, 5, 0);
1676 status
= create_window(MAX_LOG_LINES
+ 2, COLS
- 1, LINES
- 7, 0);
1677 footer
= create_window(1, COLS
- 1, LINES
- 1, 0);
1679 print_log("Starting display");
1681 main_panel
= new_panel(center
);
1688 pthread_create(&keyboard_thread
, NULL
, handle_keyboard
, (void *)NULL
);