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;
62 int toggle_pause
= -1;
65 GPtrArray
*selected_processes
;
67 pthread_t keyboard_thread
;
69 struct header_view cputopview
[4];
70 struct header_view iostreamtopview
[3];
71 struct header_view fileview
[3];
80 static void handle_sigterm(int signal
)
89 halfdelay(DEFAULT_DELAY
);
91 intrflush(stdscr
, false);
97 init_pair(1, COLOR_RED
, COLOR_BLACK
); /* - */
98 init_pair(2, COLOR_GREEN
, COLOR_BLACK
); /* + */
99 init_pair(3, COLOR_BLACK
, COLOR_WHITE
); /* keys */
100 init_pair(4, COLOR_WHITE
, COLOR_GREEN
); /* keys activated */
101 init_pair(5, COLOR_WHITE
, COLOR_BLUE
); /* select line */
102 init_pair(6, COLOR_WHITE
, COLOR_GREEN
); /* selected process */
104 termtype
= getenv("TERM");
105 if (!strcmp(termtype
, "xterm") || !strcmp(termtype
, "xterm-color") ||
106 !strcmp(termtype
, "vt220")) {
107 define_key("\033[H", KEY_HOME
);
108 define_key("\033[F", KEY_END
);
109 define_key("\033OP", KEY_F(1));
110 define_key("\033OQ", KEY_F(2));
111 define_key("\033OR", KEY_F(3));
112 define_key("\033OS", KEY_F(4));
113 define_key("\0330U", KEY_F(6));
114 define_key("\033[11~", KEY_F(1));
115 define_key("\033[12~", KEY_F(2));
116 define_key("\033[13~", KEY_F(3));
117 define_key("\033[14~", KEY_F(4));
118 define_key("\033[16~", KEY_F(6));
119 define_key("\033[17;2~", KEY_F(18));
121 signal(SIGTERM
, handle_sigterm
);
122 mousemask(BUTTON1_CLICKED
, NULL
);
126 WINDOW
*create_window(int height
, int width
, int startx
, int starty
)
129 win
= newwin(height
, width
, startx
, starty
);
135 WINDOW
*create_window_no_border(int height
, int width
, int startx
, int starty
)
138 win
= newwin(height
, width
, startx
, starty
);
143 void print_digit(WINDOW
*win
, int digit
)
146 wattron(win
, COLOR_PAIR(1));
147 wprintw(win
, "%d", digit
);
148 wattroff(win
, COLOR_PAIR(1));
149 } else if (digit
> 0) {
150 wattron(win
, COLOR_PAIR(2));
151 wprintw(win
, "+%d", digit
);
152 wattroff(win
, COLOR_PAIR(2));
158 void print_digits(WINDOW
*win
, int first
, int second
)
161 print_digit(win
, first
);
163 print_digit(win
, second
);
167 void print_headers(int line
, char *desc
, int value
, int first
, int second
)
169 wattron(header
, A_BOLD
);
170 mvwprintw(header
, line
, 4, "%s", desc
);
171 wattroff(header
, A_BOLD
);
172 mvwprintw(header
, line
, 16, "%d", value
);
173 wmove(header
, line
, 24);
174 print_digits(header
, first
, second
);
175 wmove(header
, line
, 40);
178 void set_window_title(WINDOW
*win
, char *title
)
180 wattron(win
, A_BOLD
);
181 mvwprintw(win
, 0, 1, title
);
182 wattroff(win
, A_BOLD
);
185 void print_log(char *str
)
188 int current_line
= 1;
189 int current_char
= 1;
191 /* rotate the line buffer */
192 if (nb_log_lines
>= MAX_LOG_LINES
) {
193 tmp
= strndup(log_lines
, MAX_LINE_LENGTH
* MAX_LOG_LINES
+ MAX_LOG_LINES
);
194 tmp2
= strchr(tmp
, '\n');
195 memset(log_lines
, '\0', strlen(log_lines
));
196 strncat(log_lines
, tmp2
+ 1, strlen(tmp2
) - 1);
197 log_lines
[strlen(log_lines
)] = '\n';
198 log_lines
[strlen(log_lines
)] = '\0';
203 strncat(log_lines
, str
, MAX_LINE_LENGTH
- 1);
205 if (nb_log_lines
< MAX_LOG_LINES
)
206 log_lines
[strlen(log_lines
)] = '\n';
207 log_lines
[strlen(log_lines
)] = '\0';
211 set_window_title(status
, "Status");
212 for (i
= 0; i
< strlen(log_lines
); i
++) {
213 if (log_lines
[i
] == '\n') {
214 wmove(status
, ++current_line
, 1);
217 mvwprintw(status
, current_line
, current_char
++, "%c",
224 int process_selected(struct processtop
*process
)
227 struct processtop
*stored_process
;
229 for (i
= 0; i
< selected_processes
->len
; i
++) {
230 stored_process
= g_ptr_array_index(selected_processes
, i
);
233 if (stored_process
->tid
== process
->tid
)
239 void update_selected_processes()
242 struct processtop
*stored_process
;
244 if (process_selected(selected_process
)) {
245 for (i
= 0; i
< selected_processes
->len
; i
++) {
246 stored_process
= g_ptr_array_index(selected_processes
, i
);
249 if (stored_process
->tid
== selected_process
->tid
)
250 g_ptr_array_remove(selected_processes
,
252 print_log("Process removed");
255 g_ptr_array_add(selected_processes
, selected_process
);
256 print_log("Process added");
260 void print_key(WINDOW
*win
, char *key
, char *desc
, int toggle
)
267 wattron(win
, COLOR_PAIR(pair
));
268 wprintw(footer
, "%s", key
);
269 wattroff(win
, COLOR_PAIR(pair
));
270 wprintw(footer
, ":%s", desc
);
275 sem_wait(&update_display_sem
);
278 print_key(footer
, "F2", "CPUtop ", current_view
== cpu
);
279 print_key(footer
, "F3", "PerfTop ", current_view
== perf
);
280 print_key(footer
, "F4", "IOTop ", current_view
== iostream
);
281 print_key(footer
, "Enter", "Details ", current_view
== process_details
);
282 print_key(footer
, "Space", "Highlight ", 0);
283 print_key(footer
, "q", "Quit ", 0);
284 print_key(footer
, "r", "Pref ", 0);
285 print_key(footer
, "t", "Threads ", toggle_threads
);
286 print_key(footer
, "p", "Pause ", toggle_pause
);
289 sem_post(&update_display_sem
);
296 set_window_title(header
, "Statistics for interval [gathering data...[");
297 wattron(header
, A_BOLD
);
298 mvwprintw(header
, 1, 4, "CPUs");
299 mvwprintw(header
, 2, 4, "Threads");
300 mvwprintw(header
, 3, 4, "FDs");
301 wattroff(header
, A_BOLD
);
305 struct tm
format_timestamp(uint64_t timestamp
)
308 uint64_t ts_sec
= 0, ts_nsec
;
312 ts_sec
+= ts_nsec
/ NSEC_PER_SEC
;
313 ts_nsec
= ts_nsec
% NSEC_PER_SEC
;
315 time_s
= (time_t) ts_sec
;
317 localtime_r(&time_s
, &tm
);
322 static void scale_unit(uint64_t bytes
, char *ret
)
324 if (bytes
>= 1000000000)
325 sprintf(ret
, "%" PRIu64
"G", bytes
/1000000000);
326 if (bytes
>= 1000000)
327 sprintf(ret
, "%" PRIu64
"M", bytes
/1000000);
328 else if (bytes
>= 1000)
329 sprintf(ret
, "%" PRIu64
"K", bytes
/1000);
331 sprintf(ret
, "%" PRIu64
, bytes
);
337 struct processtop
*tmp
;
340 for (i
= 0; i
< data
->process_table
->len
; i
++) {
341 tmp
= g_ptr_array_index(data
->process_table
, i
);
342 total
+= tmp
->fileread
;
343 total
+= tmp
->filewrite
;
351 struct tm start
, end
;
352 uint64_t ts_nsec_start
, ts_nsec_end
;
355 ts_nsec_start
= data
->start
% NSEC_PER_SEC
;
356 start
= format_timestamp(data
->start
);
358 ts_nsec_end
= data
->end
% NSEC_PER_SEC
;
359 end
= format_timestamp(data
->end
);
363 set_window_title(header
, "Statistics for interval ");
364 wattron(header
, A_BOLD
);
366 wprintw(header
, "[%02d:%02d:%02d.%09" PRIu64
", %02d:%02d:%02d.%09" PRIu64
"[",
367 start
.tm_hour
, start
.tm_min
, start
.tm_sec
, ts_nsec_start
,
368 end
.tm_hour
, end
.tm_min
, end
.tm_sec
, ts_nsec_end
);
369 mvwprintw(header
, 1, 4, "CPUs");
370 wattroff(header
, A_BOLD
);
371 wprintw(header
, "\t%d\t(max/cpu : %0.2f%)", data
->cpu_table
->len
,
372 100.0/data
->cpu_table
->len
);
373 print_headers(2, "Threads", data
->nbthreads
, data
->nbnewthreads
,
374 -1*(data
->nbdeadthreads
));
375 print_headers(3, "FDs", data
->nbfiles
, data
->nbnewfiles
,
376 -1*(data
->nbclosedfiles
));
377 scale_unit(total_io(), io
);
378 mvwprintw(header
, 3, 43, "%sB/sec", io
);
382 gint
sort_by_cpu_desc(gconstpointer p1
, gconstpointer p2
)
384 struct processtop
*n1
= *(struct processtop
**)p1
;
385 struct processtop
*n2
= *(struct processtop
**)p2
;
386 unsigned long totaln1
= n1
->totalcpunsec
;
387 unsigned long totaln2
= n2
->totalcpunsec
;
389 if (totaln1
< totaln2
)
391 if (totaln1
== totaln2
)
396 gint
sort_by_tid_desc(gconstpointer p1
, gconstpointer p2
)
398 struct processtop
*n1
= *(struct processtop
**)p1
;
399 struct processtop
*n2
= *(struct processtop
**)p2
;
400 unsigned long totaln1
= n1
->tid
;
401 unsigned long totaln2
= n2
->tid
;
403 if (totaln1
< totaln2
)
405 if (totaln1
== totaln2
)
410 gint
sort_by_pid_desc(gconstpointer p1
, gconstpointer p2
)
412 struct processtop
*n1
= *(struct processtop
**)p1
;
413 struct processtop
*n2
= *(struct processtop
**)p2
;
414 unsigned long totaln1
= n1
->pid
;
415 unsigned long totaln2
= n2
->pid
;
417 if (totaln1
< totaln2
)
419 if (totaln1
== totaln2
)
424 gint
sort_by_process_read_desc(gconstpointer p1
, gconstpointer p2
)
426 struct processtop
*n1
= *(struct processtop
**)p1
;
427 struct processtop
*n2
= *(struct processtop
**)p2
;
428 unsigned long totaln1
= n1
->fileread
;
429 unsigned long totaln2
= n2
->fileread
;
431 if (totaln1
< totaln2
)
433 if (totaln1
== totaln2
)
438 gint
sort_by_process_write_desc(gconstpointer p1
, gconstpointer p2
)
440 struct processtop
*n1
= *(struct processtop
**)p1
;
441 struct processtop
*n2
= *(struct processtop
**)p2
;
442 unsigned long totaln1
= n1
->filewrite
;
443 unsigned long totaln2
= n2
->filewrite
;
445 if (totaln1
< totaln2
)
447 if (totaln1
== totaln2
)
452 gint
sort_by_process_total_desc(gconstpointer p1
, gconstpointer p2
)
454 struct processtop
*n1
= *(struct processtop
**)p1
;
455 struct processtop
*n2
= *(struct processtop
**)p2
;
456 unsigned long totaln1
= n1
->totalfilewrite
+ n1
->totalfileread
;
457 unsigned long totaln2
= n2
->totalfilewrite
+ n2
->totalfileread
;
459 if (totaln1
< totaln2
)
461 if (totaln1
== totaln2
)
466 gint
sort_by_file_read_desc(gconstpointer p1
, gconstpointer p2
)
468 struct files
*n1
= *(struct files
**)p1
;
469 struct files
*n2
= *(struct files
**)p2
;
470 unsigned long totaln1
;
471 unsigned long totaln2
;
476 if (totaln1
< totaln2
)
478 if (totaln1
== totaln2
)
483 gint
sort_by_file_write_desc(gconstpointer p1
, gconstpointer p2
)
485 struct files
*n1
= *(struct files
**)p1
;
486 struct files
*n2
= *(struct files
**)p2
;
487 unsigned long totaln1
;
488 unsigned long totaln2
;
493 if (totaln1
< totaln2
)
495 if (totaln1
== totaln2
)
500 gint
sort_by_file_fd_desc(gconstpointer p1
, gconstpointer p2
)
502 struct files
*n1
= *(struct files
**)p1
;
503 struct files
*n2
= *(struct files
**)p2
;
504 unsigned long totaln1
;
505 unsigned long totaln2
;
510 if (totaln1
< totaln2
)
512 if (totaln1
== totaln2
)
517 gint
sort_by_cpu_group_by_threads_desc(gconstpointer p1
, gconstpointer p2
)
519 struct processtop
*n1
= *(struct processtop
**)p1
;
520 struct processtop
*n2
= *(struct processtop
**)p2
;
521 unsigned long totaln1
= n1
->threadstotalcpunsec
;
522 unsigned long totaln2
= n2
->threadstotalcpunsec
;
524 if (totaln1
< totaln2
)
526 if (totaln1
== totaln2
)
531 void update_cputop_display()
534 int header_offset
= 2;
535 struct processtop
*tmp
;
536 unsigned long elapsed
;
538 int nblinedisplayed
= 0;
539 int current_line
= 0;
542 elapsed
= data
->end
- data
->start
;
543 maxcputime
= elapsed
* data
->cpu_table
->len
/ 100.0;
545 if (cputopview
[0].sort
== 1)
546 g_ptr_array_sort(data
->process_table
, sort_by_cpu_desc
);
547 else if (cputopview
[1].sort
== 1)
548 g_ptr_array_sort(data
->process_table
, sort_by_pid_desc
);
549 else if (cputopview
[2].sort
== 1)
550 g_ptr_array_sort(data
->process_table
, sort_by_tid_desc
);
551 else if (cputopview
[3].sort
== 1)
552 g_ptr_array_sort(data
->process_table
, sort_by_cpu_desc
);
554 g_ptr_array_sort(data
->process_table
, sort_by_cpu_desc
);
556 set_window_title(center
, "CPU Top");
557 wattron(center
, A_BOLD
);
559 for (i
= 0; i
< 4; i
++) {
560 if (cputopview
[i
].sort
) {
561 wattron(center
, A_UNDERLINE
);
562 pref_current_sort
= i
;
564 mvwprintw(center
, 1, column
, cputopview
[i
].title
);
565 wattroff(center
, A_UNDERLINE
);
568 wattroff(center
, A_BOLD
);
570 max_center_lines
= LINES
- 5 - 7 - 1 - header_offset
;
572 /* iterate the process (thread) list */
573 for (i
= list_offset
; i
< data
->process_table
->len
&&
574 nblinedisplayed
< max_center_lines
; i
++) {
575 tmp
= g_ptr_array_index(data
->process_table
, i
);
576 if (tmp
->pid
!= tmp
->tid
)
577 if (toggle_threads
== -1)
580 if (process_selected(tmp
)) {
581 wattron(center
, COLOR_PAIR(6));
582 mvwhline(center
, current_line
+ header_offset
, 1, ' ', COLS
-3);
584 if (current_line
== selected_line
) {
585 selected_process
= tmp
;
586 wattron(center
, COLOR_PAIR(5));
587 mvwhline(center
, current_line
+ header_offset
, 1, ' ', COLS
-3);
590 mvwprintw(center
, current_line
+ header_offset
, 1, "%1.2f",
591 tmp
->totalcpunsec
/ maxcputime
);
593 mvwprintw(center
, current_line
+ header_offset
, 11, "%d", tmp
->pid
);
595 mvwprintw(center
, current_line
+ header_offset
, 21, "%d", tmp
->tid
);
597 mvwprintw(center
, current_line
+ header_offset
, 31, "%s", tmp
->comm
);
598 wattroff(center
, COLOR_PAIR(6));
599 wattroff(center
, COLOR_PAIR(5));
605 gint
sort_perf(gconstpointer p1
, gconstpointer p2
, gpointer key
)
607 struct processtop
*n1
= *(struct processtop
**) p1
;
608 struct processtop
*n2
= *(struct processtop
**) p2
;
610 struct perfcounter
*tmp1
, *tmp2
;
611 unsigned long totaln2
= 0;
612 unsigned long totaln1
= 0;
617 tmp1
= g_hash_table_lookup(n1
->perf
, key
);
621 totaln1
= tmp1
->count
;
623 tmp2
= g_hash_table_lookup(n2
->perf
, key
);
627 totaln2
= tmp2
->count
;
629 if (totaln1
< totaln2
)
631 if (totaln1
== totaln2
) {
634 if (totaln1
< totaln2
)
641 void print_key_title(char *key
, int line
)
643 wattron(center
, A_BOLD
);
644 mvwprintw(center
, line
, 1, "%s", key
);
645 mvwprintw(center
, line
, 30, " ");
646 wattroff(center
, A_BOLD
);
649 void update_process_details()
651 unsigned long elapsed
;
653 struct processtop
*tmp
;
654 struct files
*file_tmp
;
657 char filename_buf
[COLS
];
660 GPtrArray
*newfilearray
= g_ptr_array_new();
662 struct perfcounter
*perfn1
, *perfn2
;
665 set_window_title(center
, "Process details");
668 tmp
= find_process_tid(data
,
669 selected_process
->tid
,
670 selected_process
->comm
);
671 elapsed
= data
->end
- data
->start
;
672 maxcputime
= elapsed
* data
->cpu_table
->len
/ 100.0;
674 print_key_title("Name", line
++);
675 wprintw(center
, "%s", selected_process
->comm
);
676 print_key_title("TID", line
++);
677 wprintw(center
, "%d", selected_process
->tid
);
679 print_key_title("Does not exit at this time", 3);
683 print_key_title("PID", line
++);
684 wprintw(center
, "%d", tmp
->pid
);
685 print_key_title("PPID", line
++);
686 wprintw(center
, "%d", tmp
->ppid
);
687 print_key_title("CPU", line
++);
688 wprintw(center
, "%1.2f %%", tmp
->totalcpunsec
/maxcputime
);
690 print_key_title("READ B/s", line
++);
691 scale_unit(tmp
->fileread
, unit
);
692 wprintw(center
, "%s", unit
);
694 print_key_title("WRITE B/s", line
++);
695 scale_unit(tmp
->filewrite
, unit
);
696 wprintw(center
, "%s", unit
);
698 g_hash_table_iter_init(&iter
, global_perf_liszt
);
699 while (g_hash_table_iter_next (&iter
, &key
, (gpointer
) &perfn1
)) {
700 print_key_title((char *) key
, line
++);
701 perfn2
= g_hash_table_lookup(tmp
->perf
, (char *) key
);
702 wprintw(center
, "%d", perfn2
? perfn2
->count
: 0);
706 wattron(center
, A_BOLD
);
708 for (i
= 0; i
< 3; i
++) {
709 if (fileview
[i
].sort
) {
710 pref_current_sort
= i
;
711 wattron(center
, A_UNDERLINE
);
713 mvwprintw(center
, line
, column
, fileview
[i
].title
);
714 wattroff(center
, A_UNDERLINE
);
717 mvwprintw(center
, line
++, column
, "FILENAME");
718 wattroff(center
, A_BOLD
);
721 * since the process_files_table array could contain NULL file structures,
722 * and that the positions inside the array is important (it is the FD), we
723 * need to create a temporary array that we can sort.
725 for (i
= 0; i
< tmp
->process_files_table
->len
; i
++) {
726 file_tmp
= g_ptr_array_index(tmp
->process_files_table
, i
);
728 g_ptr_array_add(newfilearray
, file_tmp
);
731 if (fileview
[0].sort
== 1)
732 g_ptr_array_sort(newfilearray
, sort_by_file_fd_desc
);
733 else if (fileview
[1].sort
== 1)
734 g_ptr_array_sort(newfilearray
, sort_by_file_read_desc
);
735 else if (fileview
[2].sort
== 1)
736 g_ptr_array_sort(newfilearray
, sort_by_file_write_desc
);
738 g_ptr_array_sort(newfilearray
, sort_by_file_read_desc
);
740 for (i
= selected_line
; i
< newfilearray
->len
&&
741 i
< (selected_line
+ max_center_lines
- line
+ 2); i
++) {
742 file_tmp
= g_ptr_array_index(newfilearray
, i
);
745 mvwprintw(center
, line
+ j
, 1, "%d", file_tmp
->fd
);
746 scale_unit(file_tmp
->read
, unit
);
747 mvwprintw(center
, line
+ j
, 11, "%s", unit
);
748 scale_unit(file_tmp
->write
, unit
);
749 mvwprintw(center
, line
+ j
, 21, "%s", unit
);
750 snprintf(filename_buf
, COLS
- 25, "%s", file_tmp
->name
);
751 mvwprintw(center
, line
+ j
, 31, "%s", filename_buf
);
754 g_ptr_array_free(newfilearray
, TRUE
);
760 int nblinedisplayed
= 0;
761 int current_line
= 0;
762 struct processtop
*tmp
;
763 int header_offset
= 2;
765 struct perfcounter
*perfn1
, *perfn2
;
766 char *perf_key
= NULL
;
771 set_window_title(center
, "Perf Top");
772 wattron(center
, A_BOLD
);
773 mvwprintw(center
, 1, 1, "PID");
774 mvwprintw(center
, 1, 11, "TID");
775 mvwprintw(center
, 1, 22, "NAME");
778 g_hash_table_iter_init(&iter
, global_perf_liszt
);
779 while (g_hash_table_iter_next (&iter
, &key
, (gpointer
) &perfn1
)) {
780 if (perfn1
->visible
) {
782 /* pref_current_sort = i; */
783 wattron(center
, A_UNDERLINE
);
785 /* + 5 to strip the "perf_" prefix */
786 mvwprintw(center
, 1, perf_row
, "%s",
788 wattroff(center
, A_UNDERLINE
);
792 perf_key
= (char *) key
;
795 wattroff(center
, A_BOLD
);
797 g_ptr_array_sort_with_data(data
->process_table
, sort_perf
, perf_key
);
799 for (i
= 0; i
< data
->process_table
->len
&&
800 nblinedisplayed
< max_center_lines
; i
++) {
801 tmp
= g_ptr_array_index(data
->process_table
, i
);
802 if (tmp
->pid
!= tmp
->tid
)
803 if (toggle_threads
== -1)
806 if (process_selected(tmp
)) {
807 wattron(center
, COLOR_PAIR(6));
808 mvwhline(center
, current_line
+ header_offset
, 1, ' ', COLS
-3);
810 if (current_line
== selected_line
) {
811 selected_process
= tmp
;
812 wattron(center
, COLOR_PAIR(5));
813 mvwhline(center
, current_line
+ header_offset
, 1, ' ', COLS
-3);
816 mvwprintw(center
, current_line
+ header_offset
, 1, "%d", tmp
->pid
);
817 mvwprintw(center
, current_line
+ header_offset
, 11, "%d", tmp
->tid
);
818 mvwprintw(center
, current_line
+ header_offset
, 22, "%s", tmp
->comm
);
820 g_hash_table_iter_init(&iter
, global_perf_liszt
);
823 while (g_hash_table_iter_next (&iter
, &key
, (gpointer
) &perfn1
)) {
824 if (perfn1
->visible
) {
825 perfn2
= g_hash_table_lookup(tmp
->perf
, (char *) key
);
827 value
= perfn2
->count
;
830 mvwprintw(center
, current_line
+ header_offset
,
831 perf_row
, "%d", value
);
836 wattroff(center
, COLOR_PAIR(6));
837 wattroff(center
, COLOR_PAIR(5));
843 void update_iostream()
846 int header_offset
= 2;
847 struct processtop
*tmp
;
848 int nblinedisplayed
= 0;
849 int current_line
= 0;
854 set_window_title(center
, "IO Top");
855 wattron(center
, A_BOLD
);
856 mvwprintw(center
, 1, 1, "PID");
857 mvwprintw(center
, 1, 11, "TID");
858 mvwprintw(center
, 1, 22, "NAME");
860 for (i
= 0; i
< 3; i
++) {
861 if (iostreamtopview
[i
].sort
) {
862 pref_current_sort
= i
;
863 wattron(center
, A_UNDERLINE
);
865 mvwprintw(center
, 1, column
, iostreamtopview
[i
].title
);
866 wattroff(center
, A_UNDERLINE
);
869 wattroff(center
, A_BOLD
);
870 wattroff(center
, A_UNDERLINE
);
872 if (iostreamtopview
[0].sort
== 1)
873 g_ptr_array_sort(data
->process_table
, sort_by_process_read_desc
);
874 else if (iostreamtopview
[1].sort
== 1)
875 g_ptr_array_sort(data
->process_table
, sort_by_process_write_desc
);
876 else if (iostreamtopview
[2].sort
== 1)
877 g_ptr_array_sort(data
->process_table
, sort_by_process_total_desc
);
879 g_ptr_array_sort(data
->process_table
, sort_by_process_total_desc
);
881 for (i
= list_offset
; i
< data
->process_table
->len
&&
882 nblinedisplayed
< max_center_lines
; i
++) {
883 tmp
= g_ptr_array_index(data
->process_table
, i
);
884 if (tmp
->pid
!= tmp
->tid
)
885 if (toggle_threads
== -1)
888 if (process_selected(tmp
)) {
889 wattron(center
, COLOR_PAIR(6));
890 mvwhline(center
, current_line
+ header_offset
, 1, ' ', COLS
-3);
892 if (current_line
== selected_line
) {
893 selected_process
= tmp
;
894 wattron(center
, COLOR_PAIR(5));
895 mvwhline(center
, current_line
+ header_offset
, 1, ' ', COLS
-3);
898 mvwprintw(center
, current_line
+ header_offset
, 1, "%d", tmp
->pid
);
900 mvwprintw(center
, current_line
+ header_offset
, 11, "%d", tmp
->tid
);
902 mvwprintw(center
, current_line
+ header_offset
, 22, "%s", tmp
->comm
);
904 /* READ (bytes/sec) */
905 scale_unit(tmp
->fileread
, unit
);
906 mvwprintw(center
, current_line
+ header_offset
, 40, "%s", unit
);
908 /* WRITE (bytes/sec) */
909 scale_unit(tmp
->filewrite
, unit
);
910 mvwprintw(center
, current_line
+ header_offset
, 52, "%s", unit
);
913 total
= tmp
->totalfileread
+ tmp
->totalfilewrite
;
915 scale_unit(total
, unit
);
916 mvwprintw(center
, current_line
+ header_offset
, 64, "%s", unit
);
918 wattroff(center
, COLOR_PAIR(6));
919 wattroff(center
, COLOR_PAIR(5));
925 void update_current_view()
927 sem_wait(&update_display_sem
);
934 switch (current_view
) {
936 update_cputop_display();
941 case process_details
:
942 update_process_details();
948 update_cputop_display();
955 sem_post(&update_display_sem
);
958 void update_process_detail_sort(int *line_selected
)
965 if (*line_selected
> (size
- 1))
966 *line_selected
= size
- 1;
967 else if (*line_selected
< 0)
970 if (fileview
[*line_selected
].sort
== 1)
971 fileview
[*line_selected
].reverse
= 1;
972 for (i
= 0; i
< size
; i
++)
973 fileview
[i
].sort
= 0;
974 fileview
[*line_selected
].sort
= 1;
977 void update_process_detail_pref(int *line_selected
, int toggle_view
, int toggle_sort
)
984 if (pref_panel_window
) {
985 del_panel(pref_panel
);
986 delwin(pref_panel_window
);
990 pref_panel_window
= create_window(size
+ 2, 30, 10, 10);
991 pref_panel
= new_panel(pref_panel_window
);
993 werase(pref_panel_window
);
994 box(pref_panel_window
, 0 , 0);
995 set_window_title(pref_panel_window
, "Process Detail Preferences ");
996 wattron(pref_panel_window
, A_BOLD
);
997 mvwprintw(pref_panel_window
, size
+ 1, 1,
998 " 's' : sort, space : toggle");
999 wattroff(pref_panel_window
, A_BOLD
);
1001 if (*line_selected
> (size
- 1))
1002 *line_selected
= size
- 1;
1003 else if (*line_selected
< 0)
1005 if (toggle_sort
== 1) {
1006 update_process_detail_sort(line_selected
);
1007 update_current_view();
1010 for (i
= 0; i
< size
; i
++) {
1011 if (i
== *line_selected
) {
1012 wattron(pref_panel_window
, COLOR_PAIR(5));
1013 mvwhline(pref_panel_window
, i
+ 1, 1, ' ', 30 - 2);
1015 if (fileview
[i
].sort
== 1)
1016 wattron(pref_panel_window
, A_BOLD
);
1017 mvwprintw(pref_panel_window
, i
+ 1, 1, "[-] %s",
1019 wattroff(pref_panel_window
, A_BOLD
);
1020 wattroff(pref_panel_window
, COLOR_PAIR(5));
1027 void update_iostream_sort(int *line_selected
)
1033 if (*line_selected
> (size
- 1))
1034 *line_selected
= size
- 1;
1035 else if (*line_selected
< 0)
1037 if (iostreamtopview
[*line_selected
].sort
== 1)
1038 iostreamtopview
[*line_selected
].reverse
= 1;
1039 for (i
= 0; i
< size
; i
++)
1040 iostreamtopview
[i
].sort
= 0;
1041 iostreamtopview
[*line_selected
].sort
= 1;
1045 void update_iostream_pref(int *line_selected
, int toggle_view
, int toggle_sort
)
1052 if (pref_panel_window
) {
1053 del_panel(pref_panel
);
1054 delwin(pref_panel_window
);
1058 pref_panel_window
= create_window(size
+ 2, 30, 10, 10);
1059 pref_panel
= new_panel(pref_panel_window
);
1061 werase(pref_panel_window
);
1062 box(pref_panel_window
, 0 , 0);
1063 set_window_title(pref_panel_window
, "IOTop Preferences ");
1064 wattron(pref_panel_window
, A_BOLD
);
1065 mvwprintw(pref_panel_window
, size
+ 1, 1,
1066 " 's' : sort, space : toggle");
1067 wattroff(pref_panel_window
, A_BOLD
);
1069 if (*line_selected
> (size
- 1))
1070 *line_selected
= size
- 1;
1071 else if (*line_selected
< 0)
1073 if (toggle_sort
== 1) {
1074 update_iostream_sort(line_selected
);
1075 update_current_view();
1078 for (i
= 0; i
< size
; i
++) {
1079 if (i
== *line_selected
) {
1080 wattron(pref_panel_window
, COLOR_PAIR(5));
1081 mvwhline(pref_panel_window
, i
+ 1, 1, ' ', 30 - 2);
1083 if (iostreamtopview
[i
].sort
== 1)
1084 wattron(pref_panel_window
, A_BOLD
);
1085 mvwprintw(pref_panel_window
, i
+ 1, 1, "[-] %s",
1086 iostreamtopview
[i
].title
);
1087 wattroff(pref_panel_window
, A_BOLD
);
1088 wattroff(pref_panel_window
, COLOR_PAIR(5));
1095 void update_cpu_sort(int *line_selected
)
1100 if (*line_selected
> (size
- 1))
1101 *line_selected
= size
- 1;
1102 else if (*line_selected
< 0)
1105 /* special case, we don't support sorting by procname for now */
1106 if (*line_selected
!= 3) {
1107 if (cputopview
[*line_selected
].sort
== 1)
1108 cputopview
[*line_selected
].reverse
= 1;
1109 for (i
= 0; i
< size
; i
++)
1110 cputopview
[i
].sort
= 0;
1111 cputopview
[*line_selected
].sort
= 1;
1115 void update_cpu_pref(int *line_selected
, int toggle_view
, int toggle_sort
)
1122 if (pref_panel_window
) {
1123 del_panel(pref_panel
);
1124 delwin(pref_panel_window
);
1128 pref_panel_window
= create_window(size
+ 2, 30, 10, 10);
1129 pref_panel
= new_panel(pref_panel_window
);
1131 werase(pref_panel_window
);
1132 box(pref_panel_window
, 0 , 0);
1133 set_window_title(pref_panel_window
, "CPUTop Preferences ");
1134 wattron(pref_panel_window
, A_BOLD
);
1135 mvwprintw(pref_panel_window
, size
+ 1, 1,
1136 " 's' : sort, space : toggle");
1137 wattroff(pref_panel_window
, A_BOLD
);
1139 if (*line_selected
> (size
- 1))
1140 *line_selected
= size
- 1;
1141 else if (*line_selected
< 0)
1143 if (toggle_sort
== 1) {
1144 update_cpu_sort(line_selected
);
1145 update_current_view();
1148 for (i
= 0; i
< size
; i
++) {
1149 if (i
== *line_selected
) {
1150 wattron(pref_panel_window
, COLOR_PAIR(5));
1151 mvwhline(pref_panel_window
, i
+ 1, 1, ' ', 30 - 2);
1153 if (cputopview
[i
].sort
== 1)
1154 wattron(pref_panel_window
, A_BOLD
);
1155 mvwprintw(pref_panel_window
, i
+ 1, 1, "[-] %s",
1156 cputopview
[i
].title
);
1157 wattroff(pref_panel_window
, A_BOLD
);
1158 wattroff(pref_panel_window
, COLOR_PAIR(5));
1165 void update_perf_sort(int *line_selected
)
1168 struct perfcounter
*perf
;
1172 size
= g_hash_table_size(global_perf_liszt
);
1173 if (*line_selected
> (size
- 1))
1174 *line_selected
= size
- 1;
1175 else if (*line_selected
< 0)
1179 perflist
= g_list_first(g_hash_table_get_keys(global_perf_liszt
));
1181 perf
= g_hash_table_lookup(global_perf_liszt
, perflist
->data
);
1182 if (i
!= *line_selected
)
1187 perflist
= g_list_next(perflist
);
1191 void update_perf_pref(int *line_selected
, int toggle_view
, int toggle_sort
)
1194 struct perfcounter
*perf
;
1200 if (pref_panel_window
) {
1201 del_panel(pref_panel
);
1202 delwin(pref_panel_window
);
1204 size
= g_hash_table_size(global_perf_liszt
);
1206 pref_panel_window
= create_window(size
+ 2, 30, 10, 10);
1207 pref_panel
= new_panel(pref_panel_window
);
1209 werase(pref_panel_window
);
1210 box(pref_panel_window
, 0 , 0);
1211 set_window_title(pref_panel_window
, "Perf Preferences ");
1212 wattron(pref_panel_window
, A_BOLD
);
1213 mvwprintw(pref_panel_window
, g_hash_table_size(global_perf_liszt
) + 1, 1,
1214 " 's' : sort, space : toggle");
1215 wattroff(pref_panel_window
, A_BOLD
);
1217 if (*line_selected
> (size
- 1))
1218 *line_selected
= size
- 1;
1219 else if (*line_selected
< 0)
1222 if (toggle_sort
== 1) {
1223 update_perf_sort(line_selected
);
1224 update_current_view();
1228 perflist
= g_list_first(g_hash_table_get_keys(global_perf_liszt
));
1230 perf
= g_hash_table_lookup(global_perf_liszt
, perflist
->data
);
1231 if (i
== *line_selected
&& toggle_view
== 1) {
1232 perf
->visible
= perf
->visible
== 1 ? 0:1;
1233 update_current_view();
1235 if (i
== *line_selected
) {
1236 wattron(pref_panel_window
, COLOR_PAIR(5));
1237 mvwhline(pref_panel_window
, i
+ 1, 1, ' ', 30 - 2);
1239 if (perf
->sort
== 1)
1240 wattron(pref_panel_window
, A_BOLD
);
1241 mvwprintw(pref_panel_window
, i
+ 1, 1, "[%c] %s",
1242 perf
->visible
== 1 ? 'x' : ' ',
1243 (char *) perflist
->data
+ 5);
1244 wattroff(pref_panel_window
, A_BOLD
);
1245 wattroff(pref_panel_window
, COLOR_PAIR(5));
1247 perflist
= g_list_next(perflist
);
1253 int update_preference_panel(int *line_selected
, int toggle_view
, int toggle_sort
)
1257 switch(current_view
) {
1259 update_perf_pref(line_selected
, toggle_view
, toggle_sort
);
1262 update_cpu_pref(line_selected
, toggle_view
, toggle_sort
);
1265 update_iostream_pref(line_selected
, toggle_view
, toggle_sort
);
1267 case process_details
:
1268 update_process_detail_pref(line_selected
, toggle_view
, toggle_sort
);
1278 int update_sort(int *line_selected
)
1282 switch(current_view
) {
1284 update_perf_sort(line_selected
);
1287 update_cpu_sort(line_selected
);
1290 update_iostream_sort(line_selected
);
1292 case process_details
:
1293 update_process_detail_sort(line_selected
);
1304 void toggle_pref_panel(void)
1308 if (pref_panel_visible
) {
1309 hide_panel(pref_panel
);
1310 pref_panel_visible
= 0;
1312 ret
= update_preference_panel(&pref_line_selected
, 0, 0);
1315 show_panel(pref_panel
);
1316 pref_panel_visible
= 1;
1322 void display(unsigned int index
)
1324 last_display_index
= index
;
1325 currently_displayed_index
= index
;
1326 data
= g_ptr_array_index(copies
, index
);
1329 max_elements
= data
->process_table
->len
;
1330 update_current_view();
1336 void pause_display()
1340 sem_wait(&pause_sem
);
1343 void resume_display()
1346 print_log("Resume");
1347 sem_post(&pause_sem
);
1350 void *handle_keyboard(void *p
)
1353 while((ch
= getch())) {
1355 /* Move the cursor and scroll */
1358 if (pref_panel_visible
) {
1359 pref_line_selected
++;
1360 update_preference_panel(&pref_line_selected
, 0, 0);
1362 if (selected_line
< (max_center_lines
- 1) &&
1363 selected_line
< max_elements
- 1) {
1366 } else if (selected_in_list
< (max_elements
- 1)
1367 && (list_offset
< (max_elements
- max_center_lines
))) {
1371 update_current_view();
1378 if (pref_panel_visible
) {
1379 if (pref_line_selected
> 0)
1380 pref_line_selected
--;
1381 update_preference_panel(&pref_line_selected
, 0, 0);
1383 if (selected_line
> 0) {
1386 } else if (selected_in_list
> 0 && list_offset
> 0) {
1390 update_current_view();
1396 /* Navigate the history with arrows */
1398 if (currently_displayed_index
> 0) {
1399 currently_displayed_index
--;
1400 print_log("Going back in time");
1402 print_log("Cannot rewind, last data is already displayed");
1404 data
= g_ptr_array_index(copies
, currently_displayed_index
);
1405 max_elements
= data
->process_table
->len
;
1407 /* we force to pause the display when moving in time */
1408 if (toggle_pause
< 0)
1411 update_current_view();
1415 if (currently_displayed_index
< last_display_index
) {
1416 currently_displayed_index
++;
1417 print_log("Going forward in time");
1418 data
= g_ptr_array_index(copies
, currently_displayed_index
);
1419 max_elements
= data
->process_table
->len
;
1420 update_current_view();
1423 print_log("Manually moving forward");
1425 if (toggle_pause
> 0) {
1426 sem_post(&pause_sem
);
1427 update_current_view();
1428 sem_wait(&pause_sem
);
1434 if (pref_panel_visible
) {
1435 update_preference_panel(&pref_line_selected
, 1, 0);
1437 update_selected_processes();
1438 update_current_view();
1442 if (pref_panel_visible
)
1443 update_preference_panel(&pref_line_selected
, 0, 1);
1446 /* perf uses a hashtable, it is ordered backward */
1447 if (current_view
== perf
) {
1448 pref_current_sort
--;
1449 } else if (!pref_panel_visible
) {
1450 pref_current_sort
++;
1452 update_sort(&pref_current_sort
);
1453 update_current_view();
1456 /* perf uses a hashtable, it is ordered backward */
1457 if (current_view
== perf
) {
1458 pref_current_sort
++;
1459 } else if (!pref_panel_visible
) {
1460 pref_current_sort
--;
1462 update_sort(&pref_current_sort
);
1463 update_current_view();
1466 case 13: /* FIXME : KEY_ENTER ?? */
1467 if (pref_panel_visible
)
1469 if (current_view
!= process_details
) {
1470 previous_view
= current_view
;
1471 current_view
= process_details
;
1473 current_view
= previous_view
;
1474 previous_view
= process_details
;
1477 update_current_view();
1481 if (pref_panel_visible
)
1482 toggle_pref_panel();
1485 update_current_view();
1488 if (pref_panel_visible
)
1489 toggle_pref_panel();
1492 update_current_view();
1495 if (pref_panel_visible
)
1496 toggle_pref_panel();
1497 current_view
= perf
;
1499 update_current_view();
1502 if (pref_panel_visible
)
1503 toggle_pref_panel();
1504 current_view
= iostream
;
1506 update_current_view();
1513 toggle_threads
*= -1;
1514 update_current_view();
1517 if (toggle_pause
< 0) {
1524 toggle_pref_panel();
1526 /* ESCAPE, but slow to process, don't know why */
1528 if (pref_panel_visible
)
1529 toggle_pref_panel();
1530 else if (current_view
== process_details
) {
1531 current_view
= previous_view
;
1532 previous_view
= process_details
;
1534 update_current_view();
1538 update_current_view();
1546 void init_view_headers()
1548 cputopview
[0].title
= strdup("CPU(%)");
1549 cputopview
[0].sort
= 1;
1550 cputopview
[1].title
= strdup("PID");
1551 cputopview
[2].title
= strdup("TID");
1552 cputopview
[3].title
= strdup("NAME");
1554 iostreamtopview
[0].title
= strdup("R (B/sec)");
1555 iostreamtopview
[1].title
= strdup("W (B/sec)");
1556 iostreamtopview
[2].title
= strdup("Total (B)");
1557 iostreamtopview
[2].sort
= 1;
1559 fileview
[0].title
= strdup("FD");
1560 fileview
[1].title
= strdup("READ");
1561 fileview
[1].sort
= 1;
1562 fileview
[2].title
= strdup("WRITE");
1567 selected_processes
= g_ptr_array_new();
1568 sem_init(&update_display_sem
, 0, 1);
1569 init_view_headers();
1572 header
= create_window(5, COLS
- 1, 0, 0);
1573 center
= create_window(LINES
- 5 - 7, COLS
- 1, 5, 0);
1574 status
= create_window(MAX_LOG_LINES
+ 2, COLS
- 1, LINES
- 7, 0);
1575 footer
= create_window(1, COLS
- 1, LINES
- 1, 0);
1577 print_log("Starting display");
1579 main_panel
= new_panel(center
);
1586 pthread_create(&keyboard_thread
, NULL
, handle_keyboard
, (void *)NULL
);