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
)
82 fprintf(stderr
, "caugh signal\n");
83 pthread_cancel(keyboard_thread
);
91 halfdelay(DEFAULT_DELAY
);
93 intrflush(stdscr
, false);
99 init_pair(1, COLOR_RED
, COLOR_BLACK
); /* - */
100 init_pair(2, COLOR_GREEN
, COLOR_BLACK
); /* + */
101 init_pair(3, COLOR_BLACK
, COLOR_WHITE
); /* keys */
102 init_pair(4, COLOR_WHITE
, COLOR_GREEN
); /* keys activated */
103 init_pair(5, COLOR_WHITE
, COLOR_BLUE
); /* select line */
104 init_pair(6, COLOR_WHITE
, COLOR_GREEN
); /* selected process */
106 termtype
= getenv("TERM");
107 if (!strcmp(termtype
, "xterm") || !strcmp(termtype
, "xterm-color") ||
108 !strcmp(termtype
, "vt220")) {
109 define_key("\033[H", KEY_HOME
);
110 define_key("\033[F", KEY_END
);
111 define_key("\033OP", KEY_F(1));
112 define_key("\033OQ", KEY_F(2));
113 define_key("\033OR", KEY_F(3));
114 define_key("\033OS", KEY_F(4));
115 define_key("\0330U", KEY_F(6));
116 define_key("\033[11~", KEY_F(1));
117 define_key("\033[12~", KEY_F(2));
118 define_key("\033[13~", KEY_F(3));
119 define_key("\033[14~", KEY_F(4));
120 define_key("\033[16~", KEY_F(6));
121 define_key("\033[17;2~", KEY_F(18));
123 signal(SIGTERM
, handle_sigterm
);
124 signal(SIGINT
, handle_sigterm
);
125 mousemask(BUTTON1_CLICKED
, NULL
);
129 WINDOW
*create_window(int height
, int width
, int startx
, int starty
)
132 win
= newwin(height
, width
, startx
, starty
);
138 WINDOW
*create_window_no_border(int height
, int width
, int startx
, int starty
)
141 win
= newwin(height
, width
, startx
, starty
);
146 void print_digit(WINDOW
*win
, int digit
)
149 wattron(win
, COLOR_PAIR(1));
150 wprintw(win
, "%d", digit
);
151 wattroff(win
, COLOR_PAIR(1));
152 } else if (digit
> 0) {
153 wattron(win
, COLOR_PAIR(2));
154 wprintw(win
, "+%d", digit
);
155 wattroff(win
, COLOR_PAIR(2));
161 void print_digits(WINDOW
*win
, int first
, int second
)
164 print_digit(win
, first
);
166 print_digit(win
, second
);
170 void print_headers(int line
, char *desc
, int value
, int first
, int second
)
172 wattron(header
, A_BOLD
);
173 mvwprintw(header
, line
, 4, "%s", desc
);
174 wattroff(header
, A_BOLD
);
175 mvwprintw(header
, line
, 16, "%d", value
);
176 wmove(header
, line
, 24);
177 print_digits(header
, first
, second
);
178 wmove(header
, line
, 40);
181 void set_window_title(WINDOW
*win
, char *title
)
183 wattron(win
, A_BOLD
);
184 mvwprintw(win
, 0, 1, title
);
185 wattroff(win
, A_BOLD
);
188 void print_log(char *str
)
191 int current_line
= 1;
192 int current_char
= 1;
194 /* rotate the line buffer */
195 if (nb_log_lines
>= MAX_LOG_LINES
) {
196 tmp
= strndup(log_lines
, MAX_LINE_LENGTH
* MAX_LOG_LINES
+ MAX_LOG_LINES
);
197 tmp2
= strchr(tmp
, '\n');
198 memset(log_lines
, '\0', strlen(log_lines
));
199 strncat(log_lines
, tmp2
+ 1, strlen(tmp2
) - 1);
200 log_lines
[strlen(log_lines
)] = '\n';
201 log_lines
[strlen(log_lines
)] = '\0';
206 strncat(log_lines
, str
, MAX_LINE_LENGTH
- 1);
208 if (nb_log_lines
< MAX_LOG_LINES
)
209 log_lines
[strlen(log_lines
)] = '\n';
210 log_lines
[strlen(log_lines
)] = '\0';
214 set_window_title(status
, "Status");
215 for (i
= 0; i
< strlen(log_lines
); i
++) {
216 if (log_lines
[i
] == '\n') {
217 wmove(status
, ++current_line
, 1);
220 mvwprintw(status
, current_line
, current_char
++, "%c",
227 int process_selected(struct processtop
*process
)
230 struct processtop
*stored_process
;
232 for (i
= 0; i
< selected_processes
->len
; i
++) {
233 stored_process
= g_ptr_array_index(selected_processes
, i
);
236 if (stored_process
->tid
== process
->tid
)
242 void update_selected_processes()
245 struct processtop
*stored_process
;
247 if (process_selected(selected_process
)) {
248 for (i
= 0; i
< selected_processes
->len
; i
++) {
249 stored_process
= g_ptr_array_index(selected_processes
, i
);
252 if (stored_process
->tid
== selected_process
->tid
)
253 g_ptr_array_remove(selected_processes
,
255 print_log("Process removed");
258 g_ptr_array_add(selected_processes
, selected_process
);
259 print_log("Process added");
263 void print_key(WINDOW
*win
, char *key
, char *desc
, int toggle
)
270 wattron(win
, COLOR_PAIR(pair
));
271 wprintw(footer
, "%s", key
);
272 wattroff(win
, COLOR_PAIR(pair
));
273 wprintw(footer
, ":%s", desc
);
278 sem_wait(&update_display_sem
);
281 print_key(footer
, "F2", "CPUtop ", current_view
== cpu
);
282 print_key(footer
, "F3", "PerfTop ", current_view
== perf
);
283 print_key(footer
, "F4", "IOTop ", current_view
== iostream
);
284 print_key(footer
, "Enter", "Details ", current_view
== process_details
);
285 print_key(footer
, "Space", "Highlight ", 0);
286 print_key(footer
, "q", "Quit ", 0);
287 print_key(footer
, "r", "Pref ", 0);
288 print_key(footer
, "t", "Threads ", toggle_threads
);
289 print_key(footer
, "p", "Pause ", toggle_pause
);
292 sem_post(&update_display_sem
);
299 set_window_title(header
, "Statistics for interval [gathering data...[");
300 wattron(header
, A_BOLD
);
301 mvwprintw(header
, 1, 4, "CPUs");
302 mvwprintw(header
, 2, 4, "Threads");
303 mvwprintw(header
, 3, 4, "FDs");
304 wattroff(header
, A_BOLD
);
308 static void scale_unit(uint64_t bytes
, char *ret
)
310 if (bytes
>= 1000000000)
311 sprintf(ret
, "%" PRIu64
"G", bytes
/1000000000);
312 if (bytes
>= 1000000)
313 sprintf(ret
, "%" PRIu64
"M", bytes
/1000000);
314 else if (bytes
>= 1000)
315 sprintf(ret
, "%" PRIu64
"K", bytes
/1000);
317 sprintf(ret
, "%" PRIu64
, bytes
);
323 struct processtop
*tmp
;
326 for (i
= 0; i
< data
->process_table
->len
; i
++) {
327 tmp
= g_ptr_array_index(data
->process_table
, i
);
328 total
+= tmp
->fileread
;
329 total
+= tmp
->filewrite
;
337 struct tm start
, end
;
338 uint64_t ts_nsec_start
, ts_nsec_end
;
341 ts_nsec_start
= data
->start
% NSEC_PER_SEC
;
342 start
= format_timestamp(data
->start
);
344 ts_nsec_end
= data
->end
% NSEC_PER_SEC
;
345 end
= format_timestamp(data
->end
);
349 set_window_title(header
, "Statistics for interval ");
350 wattron(header
, A_BOLD
);
352 wprintw(header
, "[%02d:%02d:%02d.%09" PRIu64
", %02d:%02d:%02d.%09" PRIu64
"[",
353 start
.tm_hour
, start
.tm_min
, start
.tm_sec
, ts_nsec_start
,
354 end
.tm_hour
, end
.tm_min
, end
.tm_sec
, ts_nsec_end
);
355 mvwprintw(header
, 1, 4, "CPUs");
356 wattroff(header
, A_BOLD
);
357 wprintw(header
, "\t%d\t(max/cpu : %0.2f%)", data
->cpu_table
->len
,
358 100.0/data
->cpu_table
->len
);
359 print_headers(2, "Threads", data
->nbthreads
, data
->nbnewthreads
,
360 -1*(data
->nbdeadthreads
));
361 print_headers(3, "FDs", data
->nbfiles
, data
->nbnewfiles
,
362 -1*(data
->nbclosedfiles
));
363 scale_unit(total_io(), io
);
364 mvwprintw(header
, 3, 43, "%sB/sec", io
);
368 gint
sort_by_cpu_desc(gconstpointer p1
, gconstpointer p2
)
370 struct processtop
*n1
= *(struct processtop
**)p1
;
371 struct processtop
*n2
= *(struct processtop
**)p2
;
372 unsigned long totaln1
= n1
->totalcpunsec
;
373 unsigned long totaln2
= n2
->totalcpunsec
;
375 if (totaln1
< totaln2
)
377 if (totaln1
== totaln2
)
382 gint
sort_by_tid_desc(gconstpointer p1
, gconstpointer p2
)
384 struct processtop
*n1
= *(struct processtop
**)p1
;
385 struct processtop
*n2
= *(struct processtop
**)p2
;
386 unsigned long totaln1
= n1
->tid
;
387 unsigned long totaln2
= n2
->tid
;
389 if (totaln1
< totaln2
)
391 if (totaln1
== totaln2
)
396 gint
sort_by_pid_desc(gconstpointer p1
, gconstpointer p2
)
398 struct processtop
*n1
= *(struct processtop
**)p1
;
399 struct processtop
*n2
= *(struct processtop
**)p2
;
400 unsigned long totaln1
= n1
->pid
;
401 unsigned long totaln2
= n2
->pid
;
403 if (totaln1
< totaln2
)
405 if (totaln1
== totaln2
)
410 gint
sort_by_process_read_desc(gconstpointer p1
, gconstpointer p2
)
412 struct processtop
*n1
= *(struct processtop
**)p1
;
413 struct processtop
*n2
= *(struct processtop
**)p2
;
414 unsigned long totaln1
= n1
->fileread
;
415 unsigned long totaln2
= n2
->fileread
;
417 if (totaln1
< totaln2
)
419 if (totaln1
== totaln2
)
424 gint
sort_by_process_write_desc(gconstpointer p1
, gconstpointer p2
)
426 struct processtop
*n1
= *(struct processtop
**)p1
;
427 struct processtop
*n2
= *(struct processtop
**)p2
;
428 unsigned long totaln1
= n1
->filewrite
;
429 unsigned long totaln2
= n2
->filewrite
;
431 if (totaln1
< totaln2
)
433 if (totaln1
== totaln2
)
438 gint
sort_by_process_total_desc(gconstpointer p1
, gconstpointer p2
)
440 struct processtop
*n1
= *(struct processtop
**)p1
;
441 struct processtop
*n2
= *(struct processtop
**)p2
;
442 unsigned long totaln1
= n1
->totalfilewrite
+ n1
->totalfileread
;
443 unsigned long totaln2
= n2
->totalfilewrite
+ n2
->totalfileread
;
445 if (totaln1
< totaln2
)
447 if (totaln1
== totaln2
)
452 gint
sort_by_file_read_desc(gconstpointer p1
, gconstpointer p2
)
454 struct files
*n1
= *(struct files
**)p1
;
455 struct files
*n2
= *(struct files
**)p2
;
456 unsigned long totaln1
;
457 unsigned long totaln2
;
462 if (totaln1
< totaln2
)
464 if (totaln1
== totaln2
)
469 gint
sort_by_file_write_desc(gconstpointer p1
, gconstpointer p2
)
471 struct files
*n1
= *(struct files
**)p1
;
472 struct files
*n2
= *(struct files
**)p2
;
473 unsigned long totaln1
;
474 unsigned long totaln2
;
479 if (totaln1
< totaln2
)
481 if (totaln1
== totaln2
)
486 gint
sort_by_file_fd_desc(gconstpointer p1
, gconstpointer p2
)
488 struct files
*n1
= *(struct files
**)p1
;
489 struct files
*n2
= *(struct files
**)p2
;
490 unsigned long totaln1
;
491 unsigned long totaln2
;
496 if (totaln1
< totaln2
)
498 if (totaln1
== totaln2
)
503 gint
sort_by_cpu_group_by_threads_desc(gconstpointer p1
, gconstpointer p2
)
505 struct processtop
*n1
= *(struct processtop
**)p1
;
506 struct processtop
*n2
= *(struct processtop
**)p2
;
507 unsigned long totaln1
= n1
->threadstotalcpunsec
;
508 unsigned long totaln2
= n2
->threadstotalcpunsec
;
510 if (totaln1
< totaln2
)
512 if (totaln1
== totaln2
)
517 void update_cputop_display()
520 int header_offset
= 2;
521 struct processtop
*tmp
;
522 unsigned long elapsed
;
524 int nblinedisplayed
= 0;
525 int current_line
= 0;
528 elapsed
= data
->end
- data
->start
;
529 maxcputime
= elapsed
* data
->cpu_table
->len
/ 100.0;
531 if (cputopview
[0].sort
== 1)
532 g_ptr_array_sort(data
->process_table
, sort_by_cpu_desc
);
533 else if (cputopview
[1].sort
== 1)
534 g_ptr_array_sort(data
->process_table
, sort_by_pid_desc
);
535 else if (cputopview
[2].sort
== 1)
536 g_ptr_array_sort(data
->process_table
, sort_by_tid_desc
);
537 else if (cputopview
[3].sort
== 1)
538 g_ptr_array_sort(data
->process_table
, sort_by_cpu_desc
);
540 g_ptr_array_sort(data
->process_table
, sort_by_cpu_desc
);
542 set_window_title(center
, "CPU Top");
543 wattron(center
, A_BOLD
);
545 for (i
= 0; i
< 4; i
++) {
546 if (cputopview
[i
].sort
) {
547 wattron(center
, A_UNDERLINE
);
548 pref_current_sort
= i
;
550 mvwprintw(center
, 1, column
, cputopview
[i
].title
);
551 wattroff(center
, A_UNDERLINE
);
554 wattroff(center
, A_BOLD
);
556 max_center_lines
= LINES
- 5 - 7 - 1 - header_offset
;
558 /* iterate the process (thread) list */
559 for (i
= list_offset
; i
< data
->process_table
->len
&&
560 nblinedisplayed
< max_center_lines
; i
++) {
561 tmp
= g_ptr_array_index(data
->process_table
, i
);
562 if (tmp
->pid
!= tmp
->tid
)
563 if (toggle_threads
== -1)
566 if (process_selected(tmp
)) {
567 wattron(center
, COLOR_PAIR(6));
568 mvwhline(center
, current_line
+ header_offset
, 1, ' ', COLS
-3);
570 if (current_line
== selected_line
) {
571 selected_process
= tmp
;
572 wattron(center
, COLOR_PAIR(5));
573 mvwhline(center
, current_line
+ header_offset
, 1, ' ', COLS
-3);
576 mvwprintw(center
, current_line
+ header_offset
, 1, "%1.2f",
577 tmp
->totalcpunsec
/ maxcputime
);
579 mvwprintw(center
, current_line
+ header_offset
, 11, "%d", tmp
->pid
);
581 mvwprintw(center
, current_line
+ header_offset
, 21, "%d", tmp
->tid
);
583 mvwprintw(center
, current_line
+ header_offset
, 31, "%s", tmp
->comm
);
584 wattroff(center
, COLOR_PAIR(6));
585 wattroff(center
, COLOR_PAIR(5));
591 gint
sort_perf(gconstpointer p1
, gconstpointer p2
, gpointer key
)
593 struct processtop
*n1
= *(struct processtop
**) p1
;
594 struct processtop
*n2
= *(struct processtop
**) p2
;
596 struct perfcounter
*tmp1
, *tmp2
;
597 unsigned long totaln2
= 0;
598 unsigned long totaln1
= 0;
603 tmp1
= g_hash_table_lookup(n1
->perf
, key
);
607 totaln1
= tmp1
->count
;
609 tmp2
= g_hash_table_lookup(n2
->perf
, key
);
613 totaln2
= tmp2
->count
;
615 if (totaln1
< totaln2
)
617 if (totaln1
== totaln2
) {
620 if (totaln1
< totaln2
)
627 void print_key_title(char *key
, int line
)
629 wattron(center
, A_BOLD
);
630 mvwprintw(center
, line
, 1, "%s", key
);
631 mvwprintw(center
, line
, 30, " ");
632 wattroff(center
, A_BOLD
);
635 void update_process_details()
637 unsigned long elapsed
;
639 struct processtop
*tmp
;
640 struct files
*file_tmp
;
643 char filename_buf
[COLS
];
646 GPtrArray
*newfilearray
= g_ptr_array_new();
648 struct perfcounter
*perfn1
, *perfn2
;
651 set_window_title(center
, "Process details");
654 tmp
= find_process_tid(data
,
655 selected_process
->tid
,
656 selected_process
->comm
);
657 elapsed
= data
->end
- data
->start
;
658 maxcputime
= elapsed
* data
->cpu_table
->len
/ 100.0;
660 print_key_title("Name", line
++);
661 wprintw(center
, "%s", selected_process
->comm
);
662 print_key_title("TID", line
++);
663 wprintw(center
, "%d", selected_process
->tid
);
665 print_key_title("Does not exit at this time", 3);
669 print_key_title("PID", line
++);
670 wprintw(center
, "%d", tmp
->pid
);
671 print_key_title("PPID", line
++);
672 wprintw(center
, "%d", tmp
->ppid
);
673 print_key_title("CPU", line
++);
674 wprintw(center
, "%1.2f %%", tmp
->totalcpunsec
/maxcputime
);
676 print_key_title("READ B/s", line
++);
677 scale_unit(tmp
->fileread
, unit
);
678 wprintw(center
, "%s", unit
);
680 print_key_title("WRITE B/s", line
++);
681 scale_unit(tmp
->filewrite
, unit
);
682 wprintw(center
, "%s", unit
);
684 g_hash_table_iter_init(&iter
, global_perf_liszt
);
685 while (g_hash_table_iter_next (&iter
, &key
, (gpointer
) &perfn1
)) {
686 print_key_title((char *) key
, line
++);
687 perfn2
= g_hash_table_lookup(tmp
->perf
, (char *) key
);
688 wprintw(center
, "%d", perfn2
? perfn2
->count
: 0);
692 wattron(center
, A_BOLD
);
694 for (i
= 0; i
< 3; i
++) {
695 if (fileview
[i
].sort
) {
696 pref_current_sort
= i
;
697 wattron(center
, A_UNDERLINE
);
699 mvwprintw(center
, line
, column
, fileview
[i
].title
);
700 wattroff(center
, A_UNDERLINE
);
703 mvwprintw(center
, line
++, column
, "FILENAME");
704 wattroff(center
, A_BOLD
);
707 * since the process_files_table array could contain NULL file structures,
708 * and that the positions inside the array is important (it is the FD), we
709 * need to create a temporary array that we can sort.
711 for (i
= 0; i
< tmp
->process_files_table
->len
; i
++) {
712 file_tmp
= g_ptr_array_index(tmp
->process_files_table
, i
);
714 g_ptr_array_add(newfilearray
, file_tmp
);
717 if (fileview
[0].sort
== 1)
718 g_ptr_array_sort(newfilearray
, sort_by_file_fd_desc
);
719 else if (fileview
[1].sort
== 1)
720 g_ptr_array_sort(newfilearray
, sort_by_file_read_desc
);
721 else if (fileview
[2].sort
== 1)
722 g_ptr_array_sort(newfilearray
, sort_by_file_write_desc
);
724 g_ptr_array_sort(newfilearray
, sort_by_file_read_desc
);
726 for (i
= selected_line
; i
< newfilearray
->len
&&
727 i
< (selected_line
+ max_center_lines
- line
+ 2); i
++) {
728 file_tmp
= g_ptr_array_index(newfilearray
, i
);
731 mvwprintw(center
, line
+ j
, 1, "%d", file_tmp
->fd
);
732 scale_unit(file_tmp
->read
, unit
);
733 mvwprintw(center
, line
+ j
, 11, "%s", unit
);
734 scale_unit(file_tmp
->write
, unit
);
735 mvwprintw(center
, line
+ j
, 21, "%s", unit
);
736 snprintf(filename_buf
, COLS
- 25, "%s", file_tmp
->name
);
737 mvwprintw(center
, line
+ j
, 31, "%s", filename_buf
);
740 g_ptr_array_free(newfilearray
, TRUE
);
746 int nblinedisplayed
= 0;
747 int current_line
= 0;
748 struct processtop
*tmp
;
749 int header_offset
= 2;
751 struct perfcounter
*perfn1
, *perfn2
;
752 char *perf_key
= NULL
;
757 set_window_title(center
, "Perf Top");
758 wattron(center
, A_BOLD
);
759 mvwprintw(center
, 1, 1, "PID");
760 mvwprintw(center
, 1, 11, "TID");
761 mvwprintw(center
, 1, 22, "NAME");
764 g_hash_table_iter_init(&iter
, global_perf_liszt
);
765 while (g_hash_table_iter_next (&iter
, &key
, (gpointer
) &perfn1
)) {
766 if (perfn1
->visible
) {
768 /* pref_current_sort = i; */
769 wattron(center
, A_UNDERLINE
);
771 /* + 5 to strip the "perf_" prefix */
772 mvwprintw(center
, 1, perf_row
, "%s",
774 wattroff(center
, A_UNDERLINE
);
778 perf_key
= (char *) key
;
781 wattroff(center
, A_BOLD
);
783 g_ptr_array_sort_with_data(data
->process_table
, sort_perf
, perf_key
);
785 for (i
= 0; i
< data
->process_table
->len
&&
786 nblinedisplayed
< max_center_lines
; i
++) {
787 tmp
= g_ptr_array_index(data
->process_table
, i
);
788 if (tmp
->pid
!= tmp
->tid
)
789 if (toggle_threads
== -1)
792 if (process_selected(tmp
)) {
793 wattron(center
, COLOR_PAIR(6));
794 mvwhline(center
, current_line
+ header_offset
, 1, ' ', COLS
-3);
796 if (current_line
== selected_line
) {
797 selected_process
= tmp
;
798 wattron(center
, COLOR_PAIR(5));
799 mvwhline(center
, current_line
+ header_offset
, 1, ' ', COLS
-3);
802 mvwprintw(center
, current_line
+ header_offset
, 1, "%d", tmp
->pid
);
803 mvwprintw(center
, current_line
+ header_offset
, 11, "%d", tmp
->tid
);
804 mvwprintw(center
, current_line
+ header_offset
, 22, "%s", tmp
->comm
);
806 g_hash_table_iter_init(&iter
, global_perf_liszt
);
809 while (g_hash_table_iter_next (&iter
, &key
, (gpointer
) &perfn1
)) {
810 if (perfn1
->visible
) {
811 perfn2
= g_hash_table_lookup(tmp
->perf
, (char *) key
);
813 value
= perfn2
->count
;
816 mvwprintw(center
, current_line
+ header_offset
,
817 perf_row
, "%d", value
);
822 wattroff(center
, COLOR_PAIR(6));
823 wattroff(center
, COLOR_PAIR(5));
829 void update_iostream()
832 int header_offset
= 2;
833 struct processtop
*tmp
;
834 int nblinedisplayed
= 0;
835 int current_line
= 0;
840 set_window_title(center
, "IO Top");
841 wattron(center
, A_BOLD
);
842 mvwprintw(center
, 1, 1, "PID");
843 mvwprintw(center
, 1, 11, "TID");
844 mvwprintw(center
, 1, 22, "NAME");
846 for (i
= 0; i
< 3; i
++) {
847 if (iostreamtopview
[i
].sort
) {
848 pref_current_sort
= i
;
849 wattron(center
, A_UNDERLINE
);
851 mvwprintw(center
, 1, column
, iostreamtopview
[i
].title
);
852 wattroff(center
, A_UNDERLINE
);
855 wattroff(center
, A_BOLD
);
856 wattroff(center
, A_UNDERLINE
);
858 if (iostreamtopview
[0].sort
== 1)
859 g_ptr_array_sort(data
->process_table
, sort_by_process_read_desc
);
860 else if (iostreamtopview
[1].sort
== 1)
861 g_ptr_array_sort(data
->process_table
, sort_by_process_write_desc
);
862 else if (iostreamtopview
[2].sort
== 1)
863 g_ptr_array_sort(data
->process_table
, sort_by_process_total_desc
);
865 g_ptr_array_sort(data
->process_table
, sort_by_process_total_desc
);
867 for (i
= list_offset
; i
< data
->process_table
->len
&&
868 nblinedisplayed
< max_center_lines
; i
++) {
869 tmp
= g_ptr_array_index(data
->process_table
, i
);
870 if (tmp
->pid
!= tmp
->tid
)
871 if (toggle_threads
== -1)
874 if (process_selected(tmp
)) {
875 wattron(center
, COLOR_PAIR(6));
876 mvwhline(center
, current_line
+ header_offset
, 1, ' ', COLS
-3);
878 if (current_line
== selected_line
) {
879 selected_process
= tmp
;
880 wattron(center
, COLOR_PAIR(5));
881 mvwhline(center
, current_line
+ header_offset
, 1, ' ', COLS
-3);
884 mvwprintw(center
, current_line
+ header_offset
, 1, "%d", tmp
->pid
);
886 mvwprintw(center
, current_line
+ header_offset
, 11, "%d", tmp
->tid
);
888 mvwprintw(center
, current_line
+ header_offset
, 22, "%s", tmp
->comm
);
890 /* READ (bytes/sec) */
891 scale_unit(tmp
->fileread
, unit
);
892 mvwprintw(center
, current_line
+ header_offset
, 40, "%s", unit
);
894 /* WRITE (bytes/sec) */
895 scale_unit(tmp
->filewrite
, unit
);
896 mvwprintw(center
, current_line
+ header_offset
, 52, "%s", unit
);
899 total
= tmp
->totalfileread
+ tmp
->totalfilewrite
;
901 scale_unit(total
, unit
);
902 mvwprintw(center
, current_line
+ header_offset
, 64, "%s", unit
);
904 wattroff(center
, COLOR_PAIR(6));
905 wattroff(center
, COLOR_PAIR(5));
911 void update_current_view()
913 sem_wait(&update_display_sem
);
920 switch (current_view
) {
922 update_cputop_display();
927 case process_details
:
928 update_process_details();
934 update_cputop_display();
941 sem_post(&update_display_sem
);
944 void update_process_detail_sort(int *line_selected
)
951 if (*line_selected
> (size
- 1))
952 *line_selected
= size
- 1;
953 else if (*line_selected
< 0)
956 if (fileview
[*line_selected
].sort
== 1)
957 fileview
[*line_selected
].reverse
= 1;
958 for (i
= 0; i
< size
; i
++)
959 fileview
[i
].sort
= 0;
960 fileview
[*line_selected
].sort
= 1;
963 void update_process_detail_pref(int *line_selected
, int toggle_view
, int toggle_sort
)
970 if (pref_panel_window
) {
971 del_panel(pref_panel
);
972 delwin(pref_panel_window
);
976 pref_panel_window
= create_window(size
+ 2, 30, 10, 10);
977 pref_panel
= new_panel(pref_panel_window
);
979 werase(pref_panel_window
);
980 box(pref_panel_window
, 0 , 0);
981 set_window_title(pref_panel_window
, "Process Detail Preferences ");
982 wattron(pref_panel_window
, A_BOLD
);
983 mvwprintw(pref_panel_window
, size
+ 1, 1,
984 " 's' : sort, space : toggle");
985 wattroff(pref_panel_window
, A_BOLD
);
987 if (*line_selected
> (size
- 1))
988 *line_selected
= size
- 1;
989 else if (*line_selected
< 0)
991 if (toggle_sort
== 1) {
992 update_process_detail_sort(line_selected
);
993 update_current_view();
996 for (i
= 0; i
< size
; i
++) {
997 if (i
== *line_selected
) {
998 wattron(pref_panel_window
, COLOR_PAIR(5));
999 mvwhline(pref_panel_window
, i
+ 1, 1, ' ', 30 - 2);
1001 if (fileview
[i
].sort
== 1)
1002 wattron(pref_panel_window
, A_BOLD
);
1003 mvwprintw(pref_panel_window
, i
+ 1, 1, "[-] %s",
1005 wattroff(pref_panel_window
, A_BOLD
);
1006 wattroff(pref_panel_window
, COLOR_PAIR(5));
1013 void update_iostream_sort(int *line_selected
)
1019 if (*line_selected
> (size
- 1))
1020 *line_selected
= size
- 1;
1021 else if (*line_selected
< 0)
1023 if (iostreamtopview
[*line_selected
].sort
== 1)
1024 iostreamtopview
[*line_selected
].reverse
= 1;
1025 for (i
= 0; i
< size
; i
++)
1026 iostreamtopview
[i
].sort
= 0;
1027 iostreamtopview
[*line_selected
].sort
= 1;
1031 void update_iostream_pref(int *line_selected
, int toggle_view
, int toggle_sort
)
1038 if (pref_panel_window
) {
1039 del_panel(pref_panel
);
1040 delwin(pref_panel_window
);
1044 pref_panel_window
= create_window(size
+ 2, 30, 10, 10);
1045 pref_panel
= new_panel(pref_panel_window
);
1047 werase(pref_panel_window
);
1048 box(pref_panel_window
, 0 , 0);
1049 set_window_title(pref_panel_window
, "IOTop Preferences ");
1050 wattron(pref_panel_window
, A_BOLD
);
1051 mvwprintw(pref_panel_window
, size
+ 1, 1,
1052 " 's' : sort, space : toggle");
1053 wattroff(pref_panel_window
, A_BOLD
);
1055 if (*line_selected
> (size
- 1))
1056 *line_selected
= size
- 1;
1057 else if (*line_selected
< 0)
1059 if (toggle_sort
== 1) {
1060 update_iostream_sort(line_selected
);
1061 update_current_view();
1064 for (i
= 0; i
< size
; i
++) {
1065 if (i
== *line_selected
) {
1066 wattron(pref_panel_window
, COLOR_PAIR(5));
1067 mvwhline(pref_panel_window
, i
+ 1, 1, ' ', 30 - 2);
1069 if (iostreamtopview
[i
].sort
== 1)
1070 wattron(pref_panel_window
, A_BOLD
);
1071 mvwprintw(pref_panel_window
, i
+ 1, 1, "[-] %s",
1072 iostreamtopview
[i
].title
);
1073 wattroff(pref_panel_window
, A_BOLD
);
1074 wattroff(pref_panel_window
, COLOR_PAIR(5));
1081 void update_cpu_sort(int *line_selected
)
1086 if (*line_selected
> (size
- 1))
1087 *line_selected
= size
- 1;
1088 else if (*line_selected
< 0)
1091 /* special case, we don't support sorting by procname for now */
1092 if (*line_selected
!= 3) {
1093 if (cputopview
[*line_selected
].sort
== 1)
1094 cputopview
[*line_selected
].reverse
= 1;
1095 for (i
= 0; i
< size
; i
++)
1096 cputopview
[i
].sort
= 0;
1097 cputopview
[*line_selected
].sort
= 1;
1101 void update_cpu_pref(int *line_selected
, int toggle_view
, int toggle_sort
)
1108 if (pref_panel_window
) {
1109 del_panel(pref_panel
);
1110 delwin(pref_panel_window
);
1114 pref_panel_window
= create_window(size
+ 2, 30, 10, 10);
1115 pref_panel
= new_panel(pref_panel_window
);
1117 werase(pref_panel_window
);
1118 box(pref_panel_window
, 0 , 0);
1119 set_window_title(pref_panel_window
, "CPUTop Preferences ");
1120 wattron(pref_panel_window
, A_BOLD
);
1121 mvwprintw(pref_panel_window
, size
+ 1, 1,
1122 " 's' : sort, space : toggle");
1123 wattroff(pref_panel_window
, A_BOLD
);
1125 if (*line_selected
> (size
- 1))
1126 *line_selected
= size
- 1;
1127 else if (*line_selected
< 0)
1129 if (toggle_sort
== 1) {
1130 update_cpu_sort(line_selected
);
1131 update_current_view();
1134 for (i
= 0; i
< size
; i
++) {
1135 if (i
== *line_selected
) {
1136 wattron(pref_panel_window
, COLOR_PAIR(5));
1137 mvwhline(pref_panel_window
, i
+ 1, 1, ' ', 30 - 2);
1139 if (cputopview
[i
].sort
== 1)
1140 wattron(pref_panel_window
, A_BOLD
);
1141 mvwprintw(pref_panel_window
, i
+ 1, 1, "[-] %s",
1142 cputopview
[i
].title
);
1143 wattroff(pref_panel_window
, A_BOLD
);
1144 wattroff(pref_panel_window
, COLOR_PAIR(5));
1151 void update_perf_sort(int *line_selected
)
1154 struct perfcounter
*perf
;
1158 size
= g_hash_table_size(global_perf_liszt
);
1159 if (*line_selected
> (size
- 1))
1160 *line_selected
= size
- 1;
1161 else if (*line_selected
< 0)
1165 perflist
= g_list_first(g_hash_table_get_keys(global_perf_liszt
));
1167 perf
= g_hash_table_lookup(global_perf_liszt
, perflist
->data
);
1168 if (i
!= *line_selected
)
1173 perflist
= g_list_next(perflist
);
1177 void update_perf_pref(int *line_selected
, int toggle_view
, int toggle_sort
)
1180 struct perfcounter
*perf
;
1186 if (pref_panel_window
) {
1187 del_panel(pref_panel
);
1188 delwin(pref_panel_window
);
1190 size
= g_hash_table_size(global_perf_liszt
);
1192 pref_panel_window
= create_window(size
+ 2, 30, 10, 10);
1193 pref_panel
= new_panel(pref_panel_window
);
1195 werase(pref_panel_window
);
1196 box(pref_panel_window
, 0 , 0);
1197 set_window_title(pref_panel_window
, "Perf Preferences ");
1198 wattron(pref_panel_window
, A_BOLD
);
1199 mvwprintw(pref_panel_window
, g_hash_table_size(global_perf_liszt
) + 1, 1,
1200 " 's' : sort, space : toggle");
1201 wattroff(pref_panel_window
, A_BOLD
);
1203 if (*line_selected
> (size
- 1))
1204 *line_selected
= size
- 1;
1205 else if (*line_selected
< 0)
1208 if (toggle_sort
== 1) {
1209 update_perf_sort(line_selected
);
1210 update_current_view();
1214 perflist
= g_list_first(g_hash_table_get_keys(global_perf_liszt
));
1216 perf
= g_hash_table_lookup(global_perf_liszt
, perflist
->data
);
1217 if (i
== *line_selected
&& toggle_view
== 1) {
1218 perf
->visible
= perf
->visible
== 1 ? 0:1;
1219 update_current_view();
1221 if (i
== *line_selected
) {
1222 wattron(pref_panel_window
, COLOR_PAIR(5));
1223 mvwhline(pref_panel_window
, i
+ 1, 1, ' ', 30 - 2);
1225 if (perf
->sort
== 1)
1226 wattron(pref_panel_window
, A_BOLD
);
1227 mvwprintw(pref_panel_window
, i
+ 1, 1, "[%c] %s",
1228 perf
->visible
== 1 ? 'x' : ' ',
1229 (char *) perflist
->data
+ 5);
1230 wattroff(pref_panel_window
, A_BOLD
);
1231 wattroff(pref_panel_window
, COLOR_PAIR(5));
1233 perflist
= g_list_next(perflist
);
1239 int update_preference_panel(int *line_selected
, int toggle_view
, int toggle_sort
)
1243 switch(current_view
) {
1245 update_perf_pref(line_selected
, toggle_view
, toggle_sort
);
1248 update_cpu_pref(line_selected
, toggle_view
, toggle_sort
);
1251 update_iostream_pref(line_selected
, toggle_view
, toggle_sort
);
1253 case process_details
:
1254 update_process_detail_pref(line_selected
, toggle_view
, toggle_sort
);
1264 int update_sort(int *line_selected
)
1268 switch(current_view
) {
1270 update_perf_sort(line_selected
);
1273 update_cpu_sort(line_selected
);
1276 update_iostream_sort(line_selected
);
1278 case process_details
:
1279 update_process_detail_sort(line_selected
);
1290 void toggle_pref_panel(void)
1294 if (pref_panel_visible
) {
1295 hide_panel(pref_panel
);
1296 pref_panel_visible
= 0;
1298 ret
= update_preference_panel(&pref_line_selected
, 0, 0);
1301 show_panel(pref_panel
);
1302 pref_panel_visible
= 1;
1308 void display(unsigned int index
)
1310 last_display_index
= index
;
1311 currently_displayed_index
= index
;
1312 data
= g_ptr_array_index(copies
, index
);
1315 max_elements
= data
->process_table
->len
;
1316 update_current_view();
1322 void pause_display()
1326 sem_wait(&pause_sem
);
1329 void resume_display()
1332 print_log("Resume");
1333 sem_post(&pause_sem
);
1336 void *handle_keyboard(void *p
)
1339 while((ch
= getch())) {
1341 /* Move the cursor and scroll */
1344 if (pref_panel_visible
) {
1345 pref_line_selected
++;
1346 update_preference_panel(&pref_line_selected
, 0, 0);
1348 if (selected_line
< (max_center_lines
- 1) &&
1349 selected_line
< max_elements
- 1) {
1352 } else if (selected_in_list
< (max_elements
- 1)
1353 && (list_offset
< (max_elements
- max_center_lines
))) {
1357 update_current_view();
1364 if (pref_panel_visible
) {
1365 if (pref_line_selected
> 0)
1366 pref_line_selected
--;
1367 update_preference_panel(&pref_line_selected
, 0, 0);
1369 if (selected_line
> 0) {
1372 } else if (selected_in_list
> 0 && list_offset
> 0) {
1376 update_current_view();
1382 /* Navigate the history with arrows */
1384 if (currently_displayed_index
> 0) {
1385 currently_displayed_index
--;
1386 print_log("Going back in time");
1388 print_log("Cannot rewind, last data is already displayed");
1390 data
= g_ptr_array_index(copies
, currently_displayed_index
);
1391 max_elements
= data
->process_table
->len
;
1393 /* we force to pause the display when moving in time */
1394 if (toggle_pause
< 0)
1397 update_current_view();
1401 if (currently_displayed_index
< last_display_index
) {
1402 currently_displayed_index
++;
1403 print_log("Going forward in time");
1404 data
= g_ptr_array_index(copies
, currently_displayed_index
);
1405 max_elements
= data
->process_table
->len
;
1406 update_current_view();
1409 print_log("Manually moving forward");
1411 if (toggle_pause
> 0) {
1412 sem_post(&pause_sem
);
1413 update_current_view();
1414 sem_wait(&pause_sem
);
1420 if (pref_panel_visible
) {
1421 update_preference_panel(&pref_line_selected
, 1, 0);
1423 update_selected_processes();
1424 update_current_view();
1428 if (pref_panel_visible
)
1429 update_preference_panel(&pref_line_selected
, 0, 1);
1432 /* perf uses a hashtable, it is ordered backward */
1433 if (current_view
== perf
) {
1434 pref_current_sort
--;
1435 } else if (!pref_panel_visible
) {
1436 pref_current_sort
++;
1438 update_sort(&pref_current_sort
);
1439 update_current_view();
1442 /* perf uses a hashtable, it is ordered backward */
1443 if (current_view
== perf
) {
1444 pref_current_sort
++;
1445 } else if (!pref_panel_visible
) {
1446 pref_current_sort
--;
1448 update_sort(&pref_current_sort
);
1449 update_current_view();
1452 case 13: /* FIXME : KEY_ENTER ?? */
1453 if (pref_panel_visible
)
1455 if (current_view
!= process_details
) {
1456 previous_view
= current_view
;
1457 current_view
= process_details
;
1459 current_view
= previous_view
;
1460 previous_view
= process_details
;
1463 update_current_view();
1467 if (pref_panel_visible
)
1468 toggle_pref_panel();
1471 update_current_view();
1474 if (pref_panel_visible
)
1475 toggle_pref_panel();
1478 update_current_view();
1481 if (pref_panel_visible
)
1482 toggle_pref_panel();
1483 current_view
= perf
;
1485 update_current_view();
1488 if (pref_panel_visible
)
1489 toggle_pref_panel();
1490 current_view
= iostream
;
1492 update_current_view();
1497 /* exit keyboard thread */
1501 toggle_threads
*= -1;
1502 update_current_view();
1505 if (toggle_pause
< 0) {
1511 toggle_pref_panel();
1513 /* ESCAPE, but slow to process, don't know why */
1515 if (pref_panel_visible
)
1516 toggle_pref_panel();
1517 else if (current_view
== process_details
) {
1518 current_view
= previous_view
;
1519 previous_view
= process_details
;
1521 update_current_view();
1525 update_current_view();
1533 void init_view_headers()
1535 cputopview
[0].title
= strdup("CPU(%)");
1536 cputopview
[0].sort
= 1;
1537 cputopview
[1].title
= strdup("PID");
1538 cputopview
[2].title
= strdup("TID");
1539 cputopview
[3].title
= strdup("NAME");
1541 iostreamtopview
[0].title
= strdup("R (B/sec)");
1542 iostreamtopview
[1].title
= strdup("W (B/sec)");
1543 iostreamtopview
[2].title
= strdup("Total (B)");
1544 iostreamtopview
[2].sort
= 1;
1546 fileview
[0].title
= strdup("FD");
1547 fileview
[1].title
= strdup("READ");
1548 fileview
[1].sort
= 1;
1549 fileview
[2].title
= strdup("WRITE");
1554 selected_processes
= g_ptr_array_new();
1555 sem_init(&update_display_sem
, 0, 1);
1556 init_view_headers();
1559 header
= create_window(5, COLS
- 1, 0, 0);
1560 center
= create_window(LINES
- 5 - 7, COLS
- 1, 5, 0);
1561 status
= create_window(MAX_LOG_LINES
+ 2, COLS
- 1, LINES
- 7, 0);
1562 footer
= create_window(1, COLS
- 1, LINES
- 1, 0);
1564 print_log("Starting display");
1566 main_panel
= new_panel(center
);
1573 pthread_create(&keyboard_thread
, NULL
, handle_keyboard
, (void *)NULL
);