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 sem_post(&goodtodisplay
);
83 static void handle_sigterm(int signal
)
85 pthread_cancel(keyboard_thread
);
93 halfdelay(DEFAULT_DELAY
);
95 intrflush(stdscr
, false);
101 init_pair(1, COLOR_RED
, COLOR_BLACK
); /* - */
102 init_pair(2, COLOR_GREEN
, COLOR_BLACK
); /* + */
103 init_pair(3, COLOR_BLACK
, COLOR_WHITE
); /* keys */
104 init_pair(4, COLOR_WHITE
, COLOR_GREEN
); /* keys activated */
105 init_pair(5, COLOR_WHITE
, COLOR_BLUE
); /* select line */
106 init_pair(6, COLOR_WHITE
, COLOR_GREEN
); /* selected process */
108 termtype
= getenv("TERM");
109 if (!strcmp(termtype
, "xterm") || !strcmp(termtype
, "xterm-color") ||
110 !strcmp(termtype
, "vt220")) {
111 define_key("\033[H", KEY_HOME
);
112 define_key("\033[F", KEY_END
);
113 define_key("\033OP", KEY_F(1));
114 define_key("\033OQ", KEY_F(2));
115 define_key("\033OR", KEY_F(3));
116 define_key("\033OS", KEY_F(4));
117 define_key("\0330U", KEY_F(6));
118 define_key("\033[11~", KEY_F(1));
119 define_key("\033[12~", KEY_F(2));
120 define_key("\033[13~", KEY_F(3));
121 define_key("\033[14~", KEY_F(4));
122 define_key("\033[16~", KEY_F(6));
123 define_key("\033[17;2~", KEY_F(18));
125 signal(SIGTERM
, handle_sigterm
);
126 signal(SIGINT
, handle_sigterm
);
127 mousemask(BUTTON1_CLICKED
, NULL
);
131 WINDOW
*create_window(int height
, int width
, int startx
, int starty
)
134 win
= newwin(height
, width
, startx
, starty
);
140 WINDOW
*create_window_no_border(int height
, int width
, int startx
, int starty
)
143 win
= newwin(height
, width
, startx
, starty
);
148 void print_digit(WINDOW
*win
, int digit
)
151 wattron(win
, COLOR_PAIR(1));
152 wprintw(win
, "%d", digit
);
153 wattroff(win
, COLOR_PAIR(1));
154 } else if (digit
> 0) {
155 wattron(win
, COLOR_PAIR(2));
156 wprintw(win
, "+%d", digit
);
157 wattroff(win
, COLOR_PAIR(2));
163 void print_digits(WINDOW
*win
, int first
, int second
)
166 print_digit(win
, first
);
168 print_digit(win
, second
);
172 void print_headers(int line
, char *desc
, int value
, int first
, int second
)
174 wattron(header
, A_BOLD
);
175 mvwprintw(header
, line
, 4, "%s", desc
);
176 wattroff(header
, A_BOLD
);
177 mvwprintw(header
, line
, 16, "%d", value
);
178 wmove(header
, line
, 24);
179 print_digits(header
, first
, second
);
180 wmove(header
, line
, 40);
183 void set_window_title(WINDOW
*win
, char *title
)
185 wattron(win
, A_BOLD
);
186 mvwprintw(win
, 0, 1, title
);
187 wattroff(win
, A_BOLD
);
190 void print_log(char *str
)
193 int current_line
= 1;
194 int current_char
= 1;
196 /* rotate the line buffer */
197 if (nb_log_lines
>= MAX_LOG_LINES
) {
198 tmp
= strndup(log_lines
, MAX_LINE_LENGTH
* MAX_LOG_LINES
+ MAX_LOG_LINES
);
199 tmp2
= strchr(tmp
, '\n');
200 memset(log_lines
, '\0', strlen(log_lines
));
201 strncat(log_lines
, tmp2
+ 1, strlen(tmp2
) - 1);
202 log_lines
[strlen(log_lines
)] = '\n';
203 log_lines
[strlen(log_lines
)] = '\0';
208 strncat(log_lines
, str
, MAX_LINE_LENGTH
- 1);
210 if (nb_log_lines
< MAX_LOG_LINES
)
211 log_lines
[strlen(log_lines
)] = '\n';
212 log_lines
[strlen(log_lines
)] = '\0';
216 set_window_title(status
, "Status");
217 for (i
= 0; i
< strlen(log_lines
); i
++) {
218 if (log_lines
[i
] == '\n') {
219 wmove(status
, ++current_line
, 1);
222 mvwprintw(status
, current_line
, current_char
++, "%c",
229 int process_selected(struct processtop
*process
)
232 struct processtop
*stored_process
;
234 for (i
= 0; i
< selected_processes
->len
; i
++) {
235 stored_process
= g_ptr_array_index(selected_processes
, i
);
238 if (stored_process
->tid
== process
->tid
)
244 void update_selected_processes()
247 struct processtop
*stored_process
;
249 if (process_selected(selected_process
)) {
250 for (i
= 0; i
< selected_processes
->len
; i
++) {
251 stored_process
= g_ptr_array_index(selected_processes
, i
);
254 if (stored_process
->tid
== selected_process
->tid
)
255 g_ptr_array_remove(selected_processes
,
257 print_log("Process removed");
260 g_ptr_array_add(selected_processes
, selected_process
);
261 print_log("Process added");
265 void print_key(WINDOW
*win
, char *key
, char *desc
, int toggle
)
272 wattron(win
, COLOR_PAIR(pair
));
273 wprintw(footer
, "%s", key
);
274 wattroff(win
, COLOR_PAIR(pair
));
275 wprintw(footer
, ":%s", desc
);
280 sem_wait(&update_display_sem
);
283 print_key(footer
, "F2", "CPUtop ", current_view
== cpu
);
284 print_key(footer
, "F3", "PerfTop ", current_view
== perf
);
285 print_key(footer
, "F4", "IOTop ", current_view
== iostream
);
286 print_key(footer
, "Enter", "Details ", current_view
== process_details
);
287 print_key(footer
, "Space", "Highlight ", 0);
288 print_key(footer
, "q", "Quit ", 0);
289 print_key(footer
, "r", "Pref ", 0);
290 print_key(footer
, "t", "Threads ", toggle_threads
);
291 print_key(footer
, "p", "Pause ", toggle_pause
);
294 sem_post(&update_display_sem
);
301 set_window_title(header
, "Statistics for interval [gathering data...[");
302 wattron(header
, A_BOLD
);
303 mvwprintw(header
, 1, 4, "CPUs");
304 mvwprintw(header
, 2, 4, "Threads");
305 mvwprintw(header
, 3, 4, "FDs");
306 wattroff(header
, A_BOLD
);
310 static void scale_unit(uint64_t bytes
, char *ret
)
312 if (bytes
>= 1000000000)
313 sprintf(ret
, "%" PRIu64
"G", bytes
/1000000000);
314 if (bytes
>= 1000000)
315 sprintf(ret
, "%" PRIu64
"M", bytes
/1000000);
316 else if (bytes
>= 1000)
317 sprintf(ret
, "%" PRIu64
"K", bytes
/1000);
319 sprintf(ret
, "%" PRIu64
, bytes
);
325 struct processtop
*tmp
;
328 for (i
= 0; i
< data
->process_table
->len
; i
++) {
329 tmp
= g_ptr_array_index(data
->process_table
, i
);
330 total
+= tmp
->fileread
;
331 total
+= tmp
->filewrite
;
339 struct tm start
, end
;
340 uint64_t ts_nsec_start
, ts_nsec_end
;
343 ts_nsec_start
= data
->start
% NSEC_PER_SEC
;
344 start
= format_timestamp(data
->start
);
346 ts_nsec_end
= data
->end
% NSEC_PER_SEC
;
347 end
= format_timestamp(data
->end
);
351 set_window_title(header
, "Statistics for interval ");
352 wattron(header
, A_BOLD
);
354 wprintw(header
, "[%02d:%02d:%02d.%09" PRIu64
", %02d:%02d:%02d.%09" PRIu64
"[",
355 start
.tm_hour
, start
.tm_min
, start
.tm_sec
, ts_nsec_start
,
356 end
.tm_hour
, end
.tm_min
, end
.tm_sec
, ts_nsec_end
);
357 mvwprintw(header
, 1, 4, "CPUs");
358 wattroff(header
, A_BOLD
);
359 wprintw(header
, "\t%d\t(max/cpu : %0.2f%)", data
->cpu_table
->len
,
360 100.0/data
->cpu_table
->len
);
361 print_headers(2, "Threads", data
->nbthreads
, data
->nbnewthreads
,
362 -1*(data
->nbdeadthreads
));
363 print_headers(3, "FDs", data
->nbfiles
, data
->nbnewfiles
,
364 -1*(data
->nbclosedfiles
));
365 scale_unit(total_io(), io
);
366 mvwprintw(header
, 3, 43, "%sB/sec", io
);
370 gint
sort_by_cpu_desc(gconstpointer p1
, gconstpointer p2
)
372 struct processtop
*n1
= *(struct processtop
**)p1
;
373 struct processtop
*n2
= *(struct processtop
**)p2
;
374 unsigned long totaln1
= n1
->totalcpunsec
;
375 unsigned long totaln2
= n2
->totalcpunsec
;
377 if (totaln1
< totaln2
)
379 if (totaln1
== totaln2
)
384 gint
sort_by_tid_desc(gconstpointer p1
, gconstpointer p2
)
386 struct processtop
*n1
= *(struct processtop
**)p1
;
387 struct processtop
*n2
= *(struct processtop
**)p2
;
388 unsigned long totaln1
= n1
->tid
;
389 unsigned long totaln2
= n2
->tid
;
391 if (totaln1
< totaln2
)
393 if (totaln1
== totaln2
)
398 gint
sort_by_pid_desc(gconstpointer p1
, gconstpointer p2
)
400 struct processtop
*n1
= *(struct processtop
**)p1
;
401 struct processtop
*n2
= *(struct processtop
**)p2
;
402 unsigned long totaln1
= n1
->pid
;
403 unsigned long totaln2
= n2
->pid
;
405 if (totaln1
< totaln2
)
407 if (totaln1
== totaln2
)
412 gint
sort_by_process_read_desc(gconstpointer p1
, gconstpointer p2
)
414 struct processtop
*n1
= *(struct processtop
**)p1
;
415 struct processtop
*n2
= *(struct processtop
**)p2
;
416 unsigned long totaln1
= n1
->fileread
;
417 unsigned long totaln2
= n2
->fileread
;
419 if (totaln1
< totaln2
)
421 if (totaln1
== totaln2
)
426 gint
sort_by_process_write_desc(gconstpointer p1
, gconstpointer p2
)
428 struct processtop
*n1
= *(struct processtop
**)p1
;
429 struct processtop
*n2
= *(struct processtop
**)p2
;
430 unsigned long totaln1
= n1
->filewrite
;
431 unsigned long totaln2
= n2
->filewrite
;
433 if (totaln1
< totaln2
)
435 if (totaln1
== totaln2
)
440 gint
sort_by_process_total_desc(gconstpointer p1
, gconstpointer p2
)
442 struct processtop
*n1
= *(struct processtop
**)p1
;
443 struct processtop
*n2
= *(struct processtop
**)p2
;
444 unsigned long totaln1
= n1
->totalfilewrite
+ n1
->totalfileread
;
445 unsigned long totaln2
= n2
->totalfilewrite
+ n2
->totalfileread
;
447 if (totaln1
< totaln2
)
449 if (totaln1
== totaln2
)
454 gint
sort_by_file_read_desc(gconstpointer p1
, gconstpointer p2
)
456 struct files
*n1
= *(struct files
**)p1
;
457 struct files
*n2
= *(struct files
**)p2
;
458 unsigned long totaln1
;
459 unsigned long totaln2
;
464 if (totaln1
< totaln2
)
466 if (totaln1
== totaln2
)
471 gint
sort_by_file_write_desc(gconstpointer p1
, gconstpointer p2
)
473 struct files
*n1
= *(struct files
**)p1
;
474 struct files
*n2
= *(struct files
**)p2
;
475 unsigned long totaln1
;
476 unsigned long totaln2
;
481 if (totaln1
< totaln2
)
483 if (totaln1
== totaln2
)
488 gint
sort_by_file_fd_desc(gconstpointer p1
, gconstpointer p2
)
490 struct files
*n1
= *(struct files
**)p1
;
491 struct files
*n2
= *(struct files
**)p2
;
492 unsigned long totaln1
;
493 unsigned long totaln2
;
498 if (totaln1
< totaln2
)
500 if (totaln1
== totaln2
)
505 gint
sort_by_cpu_group_by_threads_desc(gconstpointer p1
, gconstpointer p2
)
507 struct processtop
*n1
= *(struct processtop
**)p1
;
508 struct processtop
*n2
= *(struct processtop
**)p2
;
509 unsigned long totaln1
= n1
->threadstotalcpunsec
;
510 unsigned long totaln2
= n2
->threadstotalcpunsec
;
512 if (totaln1
< totaln2
)
514 if (totaln1
== totaln2
)
519 void update_cputop_display()
522 int header_offset
= 2;
523 struct processtop
*tmp
;
524 unsigned long elapsed
;
526 int nblinedisplayed
= 0;
527 int current_line
= 0;
530 elapsed
= data
->end
- data
->start
;
531 maxcputime
= elapsed
* data
->cpu_table
->len
/ 100.0;
533 if (cputopview
[0].sort
== 1)
534 g_ptr_array_sort(data
->process_table
, sort_by_cpu_desc
);
535 else if (cputopview
[1].sort
== 1)
536 g_ptr_array_sort(data
->process_table
, sort_by_pid_desc
);
537 else if (cputopview
[2].sort
== 1)
538 g_ptr_array_sort(data
->process_table
, sort_by_tid_desc
);
539 else if (cputopview
[3].sort
== 1)
540 g_ptr_array_sort(data
->process_table
, sort_by_cpu_desc
);
542 g_ptr_array_sort(data
->process_table
, sort_by_cpu_desc
);
544 set_window_title(center
, "CPU Top");
545 wattron(center
, A_BOLD
);
547 for (i
= 0; i
< 4; i
++) {
548 if (cputopview
[i
].sort
) {
549 wattron(center
, A_UNDERLINE
);
550 pref_current_sort
= i
;
552 mvwprintw(center
, 1, column
, cputopview
[i
].title
);
553 wattroff(center
, A_UNDERLINE
);
556 wattroff(center
, A_BOLD
);
558 max_center_lines
= LINES
- 5 - 7 - 1 - header_offset
;
560 /* iterate the process (thread) list */
561 for (i
= list_offset
; i
< data
->process_table
->len
&&
562 nblinedisplayed
< max_center_lines
; i
++) {
563 tmp
= g_ptr_array_index(data
->process_table
, i
);
564 if (tmp
->pid
!= tmp
->tid
)
565 if (toggle_threads
== -1)
568 if (process_selected(tmp
)) {
569 wattron(center
, COLOR_PAIR(6));
570 mvwhline(center
, current_line
+ header_offset
, 1, ' ', COLS
-3);
572 if (current_line
== selected_line
) {
573 selected_process
= tmp
;
574 wattron(center
, COLOR_PAIR(5));
575 mvwhline(center
, current_line
+ header_offset
, 1, ' ', COLS
-3);
578 mvwprintw(center
, current_line
+ header_offset
, 1, "%1.2f",
579 tmp
->totalcpunsec
/ maxcputime
);
581 mvwprintw(center
, current_line
+ header_offset
, 11, "%d", tmp
->pid
);
583 mvwprintw(center
, current_line
+ header_offset
, 21, "%d", tmp
->tid
);
585 mvwprintw(center
, current_line
+ header_offset
, 31, "%s", tmp
->comm
);
586 wattroff(center
, COLOR_PAIR(6));
587 wattroff(center
, COLOR_PAIR(5));
593 gint
sort_perf(gconstpointer p1
, gconstpointer p2
, gpointer key
)
595 struct processtop
*n1
= *(struct processtop
**) p1
;
596 struct processtop
*n2
= *(struct processtop
**) p2
;
598 struct perfcounter
*tmp1
, *tmp2
;
599 unsigned long totaln2
= 0;
600 unsigned long totaln1
= 0;
605 tmp1
= g_hash_table_lookup(n1
->perf
, key
);
609 totaln1
= tmp1
->count
;
611 tmp2
= g_hash_table_lookup(n2
->perf
, key
);
615 totaln2
= tmp2
->count
;
617 if (totaln1
< totaln2
)
619 if (totaln1
== totaln2
) {
622 if (totaln1
< totaln2
)
629 void print_key_title(char *key
, int line
)
631 wattron(center
, A_BOLD
);
632 mvwprintw(center
, line
, 1, "%s", key
);
633 mvwprintw(center
, line
, 30, " ");
634 wattroff(center
, A_BOLD
);
637 void update_process_details()
639 unsigned long elapsed
;
641 struct processtop
*tmp
;
642 struct files
*file_tmp
;
645 char filename_buf
[COLS
];
648 GPtrArray
*newfilearray
= g_ptr_array_new();
650 struct perfcounter
*perfn1
, *perfn2
;
653 set_window_title(center
, "Process details");
656 tmp
= find_process_tid(data
,
657 selected_process
->tid
,
658 selected_process
->comm
);
659 elapsed
= data
->end
- data
->start
;
660 maxcputime
= elapsed
* data
->cpu_table
->len
/ 100.0;
662 print_key_title("Name", line
++);
663 wprintw(center
, "%s", selected_process
->comm
);
664 print_key_title("TID", line
++);
665 wprintw(center
, "%d", selected_process
->tid
);
667 print_key_title("Does not exit at this time", 3);
671 print_key_title("PID", line
++);
672 wprintw(center
, "%d", tmp
->pid
);
673 print_key_title("PPID", line
++);
674 wprintw(center
, "%d", tmp
->ppid
);
675 print_key_title("CPU", line
++);
676 wprintw(center
, "%1.2f %%", tmp
->totalcpunsec
/maxcputime
);
678 print_key_title("READ B/s", line
++);
679 scale_unit(tmp
->fileread
, unit
);
680 wprintw(center
, "%s", unit
);
682 print_key_title("WRITE B/s", line
++);
683 scale_unit(tmp
->filewrite
, unit
);
684 wprintw(center
, "%s", unit
);
686 g_hash_table_iter_init(&iter
, global_perf_liszt
);
687 while (g_hash_table_iter_next (&iter
, &key
, (gpointer
) &perfn1
)) {
688 print_key_title((char *) key
, line
++);
689 perfn2
= g_hash_table_lookup(tmp
->perf
, (char *) key
);
690 wprintw(center
, "%d", perfn2
? perfn2
->count
: 0);
694 wattron(center
, A_BOLD
);
696 for (i
= 0; i
< 3; i
++) {
697 if (fileview
[i
].sort
) {
698 pref_current_sort
= i
;
699 wattron(center
, A_UNDERLINE
);
701 mvwprintw(center
, line
, column
, fileview
[i
].title
);
702 wattroff(center
, A_UNDERLINE
);
705 mvwprintw(center
, line
++, column
, "FILENAME");
706 wattroff(center
, A_BOLD
);
709 * since the process_files_table array could contain NULL file structures,
710 * and that the positions inside the array is important (it is the FD), we
711 * need to create a temporary array that we can sort.
713 for (i
= 0; i
< tmp
->process_files_table
->len
; i
++) {
714 file_tmp
= g_ptr_array_index(tmp
->process_files_table
, i
);
716 g_ptr_array_add(newfilearray
, file_tmp
);
719 if (fileview
[0].sort
== 1)
720 g_ptr_array_sort(newfilearray
, sort_by_file_fd_desc
);
721 else if (fileview
[1].sort
== 1)
722 g_ptr_array_sort(newfilearray
, sort_by_file_read_desc
);
723 else if (fileview
[2].sort
== 1)
724 g_ptr_array_sort(newfilearray
, sort_by_file_write_desc
);
726 g_ptr_array_sort(newfilearray
, sort_by_file_read_desc
);
728 for (i
= selected_line
; i
< newfilearray
->len
&&
729 i
< (selected_line
+ max_center_lines
- line
+ 2); i
++) {
730 file_tmp
= g_ptr_array_index(newfilearray
, i
);
733 mvwprintw(center
, line
+ j
, 1, "%d", file_tmp
->fd
);
734 scale_unit(file_tmp
->read
, unit
);
735 mvwprintw(center
, line
+ j
, 11, "%s", unit
);
736 scale_unit(file_tmp
->write
, unit
);
737 mvwprintw(center
, line
+ j
, 21, "%s", unit
);
738 snprintf(filename_buf
, COLS
- 25, "%s", file_tmp
->name
);
739 mvwprintw(center
, line
+ j
, 31, "%s", filename_buf
);
742 g_ptr_array_free(newfilearray
, TRUE
);
748 int nblinedisplayed
= 0;
749 int current_line
= 0;
750 struct processtop
*tmp
;
751 int header_offset
= 2;
753 struct perfcounter
*perfn1
, *perfn2
;
754 char *perf_key
= NULL
;
759 set_window_title(center
, "Perf Top");
760 wattron(center
, A_BOLD
);
761 mvwprintw(center
, 1, 1, "PID");
762 mvwprintw(center
, 1, 11, "TID");
763 mvwprintw(center
, 1, 22, "NAME");
766 g_hash_table_iter_init(&iter
, global_perf_liszt
);
767 while (g_hash_table_iter_next (&iter
, &key
, (gpointer
) &perfn1
)) {
768 if (perfn1
->visible
) {
770 /* pref_current_sort = i; */
771 wattron(center
, A_UNDERLINE
);
773 /* + 5 to strip the "perf_" prefix */
774 mvwprintw(center
, 1, perf_row
, "%s",
776 wattroff(center
, A_UNDERLINE
);
780 perf_key
= (char *) key
;
783 wattroff(center
, A_BOLD
);
785 g_ptr_array_sort_with_data(data
->process_table
, sort_perf
, perf_key
);
787 for (i
= 0; i
< data
->process_table
->len
&&
788 nblinedisplayed
< max_center_lines
; i
++) {
789 tmp
= g_ptr_array_index(data
->process_table
, i
);
790 if (tmp
->pid
!= tmp
->tid
)
791 if (toggle_threads
== -1)
794 if (process_selected(tmp
)) {
795 wattron(center
, COLOR_PAIR(6));
796 mvwhline(center
, current_line
+ header_offset
, 1, ' ', COLS
-3);
798 if (current_line
== selected_line
) {
799 selected_process
= tmp
;
800 wattron(center
, COLOR_PAIR(5));
801 mvwhline(center
, current_line
+ header_offset
, 1, ' ', COLS
-3);
804 mvwprintw(center
, current_line
+ header_offset
, 1, "%d", tmp
->pid
);
805 mvwprintw(center
, current_line
+ header_offset
, 11, "%d", tmp
->tid
);
806 mvwprintw(center
, current_line
+ header_offset
, 22, "%s", tmp
->comm
);
808 g_hash_table_iter_init(&iter
, global_perf_liszt
);
811 while (g_hash_table_iter_next (&iter
, &key
, (gpointer
) &perfn1
)) {
812 if (perfn1
->visible
) {
813 perfn2
= g_hash_table_lookup(tmp
->perf
, (char *) key
);
815 value
= perfn2
->count
;
818 mvwprintw(center
, current_line
+ header_offset
,
819 perf_row
, "%d", value
);
824 wattroff(center
, COLOR_PAIR(6));
825 wattroff(center
, COLOR_PAIR(5));
831 void update_iostream()
834 int header_offset
= 2;
835 struct processtop
*tmp
;
836 int nblinedisplayed
= 0;
837 int current_line
= 0;
842 set_window_title(center
, "IO Top");
843 wattron(center
, A_BOLD
);
844 mvwprintw(center
, 1, 1, "PID");
845 mvwprintw(center
, 1, 11, "TID");
846 mvwprintw(center
, 1, 22, "NAME");
848 for (i
= 0; i
< 3; i
++) {
849 if (iostreamtopview
[i
].sort
) {
850 pref_current_sort
= i
;
851 wattron(center
, A_UNDERLINE
);
853 mvwprintw(center
, 1, column
, iostreamtopview
[i
].title
);
854 wattroff(center
, A_UNDERLINE
);
857 wattroff(center
, A_BOLD
);
858 wattroff(center
, A_UNDERLINE
);
860 if (iostreamtopview
[0].sort
== 1)
861 g_ptr_array_sort(data
->process_table
, sort_by_process_read_desc
);
862 else if (iostreamtopview
[1].sort
== 1)
863 g_ptr_array_sort(data
->process_table
, sort_by_process_write_desc
);
864 else if (iostreamtopview
[2].sort
== 1)
865 g_ptr_array_sort(data
->process_table
, sort_by_process_total_desc
);
867 g_ptr_array_sort(data
->process_table
, sort_by_process_total_desc
);
869 for (i
= list_offset
; i
< data
->process_table
->len
&&
870 nblinedisplayed
< max_center_lines
; i
++) {
871 tmp
= g_ptr_array_index(data
->process_table
, i
);
872 if (tmp
->pid
!= tmp
->tid
)
873 if (toggle_threads
== -1)
876 if (process_selected(tmp
)) {
877 wattron(center
, COLOR_PAIR(6));
878 mvwhline(center
, current_line
+ header_offset
, 1, ' ', COLS
-3);
880 if (current_line
== selected_line
) {
881 selected_process
= tmp
;
882 wattron(center
, COLOR_PAIR(5));
883 mvwhline(center
, current_line
+ header_offset
, 1, ' ', COLS
-3);
886 mvwprintw(center
, current_line
+ header_offset
, 1, "%d", tmp
->pid
);
888 mvwprintw(center
, current_line
+ header_offset
, 11, "%d", tmp
->tid
);
890 mvwprintw(center
, current_line
+ header_offset
, 22, "%s", tmp
->comm
);
892 /* READ (bytes/sec) */
893 scale_unit(tmp
->fileread
, unit
);
894 mvwprintw(center
, current_line
+ header_offset
, 40, "%s", unit
);
896 /* WRITE (bytes/sec) */
897 scale_unit(tmp
->filewrite
, unit
);
898 mvwprintw(center
, current_line
+ header_offset
, 52, "%s", unit
);
901 total
= tmp
->totalfileread
+ tmp
->totalfilewrite
;
903 scale_unit(total
, unit
);
904 mvwprintw(center
, current_line
+ header_offset
, 64, "%s", unit
);
906 wattroff(center
, COLOR_PAIR(6));
907 wattroff(center
, COLOR_PAIR(5));
913 void update_current_view()
915 sem_wait(&update_display_sem
);
922 switch (current_view
) {
924 update_cputop_display();
929 case process_details
:
930 update_process_details();
936 update_cputop_display();
943 sem_post(&update_display_sem
);
946 void update_process_detail_sort(int *line_selected
)
953 if (*line_selected
> (size
- 1))
954 *line_selected
= size
- 1;
955 else if (*line_selected
< 0)
958 if (fileview
[*line_selected
].sort
== 1)
959 fileview
[*line_selected
].reverse
= 1;
960 for (i
= 0; i
< size
; i
++)
961 fileview
[i
].sort
= 0;
962 fileview
[*line_selected
].sort
= 1;
965 void update_process_detail_pref(int *line_selected
, int toggle_view
, int toggle_sort
)
972 if (pref_panel_window
) {
973 del_panel(pref_panel
);
974 delwin(pref_panel_window
);
978 pref_panel_window
= create_window(size
+ 2, 30, 10, 10);
979 pref_panel
= new_panel(pref_panel_window
);
981 werase(pref_panel_window
);
982 box(pref_panel_window
, 0 , 0);
983 set_window_title(pref_panel_window
, "Process Detail Preferences ");
984 wattron(pref_panel_window
, A_BOLD
);
985 mvwprintw(pref_panel_window
, size
+ 1, 1,
986 " 's' : sort, space : toggle");
987 wattroff(pref_panel_window
, A_BOLD
);
989 if (*line_selected
> (size
- 1))
990 *line_selected
= size
- 1;
991 else if (*line_selected
< 0)
993 if (toggle_sort
== 1) {
994 update_process_detail_sort(line_selected
);
995 update_current_view();
998 for (i
= 0; i
< size
; i
++) {
999 if (i
== *line_selected
) {
1000 wattron(pref_panel_window
, COLOR_PAIR(5));
1001 mvwhline(pref_panel_window
, i
+ 1, 1, ' ', 30 - 2);
1003 if (fileview
[i
].sort
== 1)
1004 wattron(pref_panel_window
, A_BOLD
);
1005 mvwprintw(pref_panel_window
, i
+ 1, 1, "[-] %s",
1007 wattroff(pref_panel_window
, A_BOLD
);
1008 wattroff(pref_panel_window
, COLOR_PAIR(5));
1015 void update_iostream_sort(int *line_selected
)
1021 if (*line_selected
> (size
- 1))
1022 *line_selected
= size
- 1;
1023 else if (*line_selected
< 0)
1025 if (iostreamtopview
[*line_selected
].sort
== 1)
1026 iostreamtopview
[*line_selected
].reverse
= 1;
1027 for (i
= 0; i
< size
; i
++)
1028 iostreamtopview
[i
].sort
= 0;
1029 iostreamtopview
[*line_selected
].sort
= 1;
1033 void update_iostream_pref(int *line_selected
, int toggle_view
, int toggle_sort
)
1040 if (pref_panel_window
) {
1041 del_panel(pref_panel
);
1042 delwin(pref_panel_window
);
1046 pref_panel_window
= create_window(size
+ 2, 30, 10, 10);
1047 pref_panel
= new_panel(pref_panel_window
);
1049 werase(pref_panel_window
);
1050 box(pref_panel_window
, 0 , 0);
1051 set_window_title(pref_panel_window
, "IOTop Preferences ");
1052 wattron(pref_panel_window
, A_BOLD
);
1053 mvwprintw(pref_panel_window
, size
+ 1, 1,
1054 " 's' : sort, space : toggle");
1055 wattroff(pref_panel_window
, A_BOLD
);
1057 if (*line_selected
> (size
- 1))
1058 *line_selected
= size
- 1;
1059 else if (*line_selected
< 0)
1061 if (toggle_sort
== 1) {
1062 update_iostream_sort(line_selected
);
1063 update_current_view();
1066 for (i
= 0; i
< size
; i
++) {
1067 if (i
== *line_selected
) {
1068 wattron(pref_panel_window
, COLOR_PAIR(5));
1069 mvwhline(pref_panel_window
, i
+ 1, 1, ' ', 30 - 2);
1071 if (iostreamtopview
[i
].sort
== 1)
1072 wattron(pref_panel_window
, A_BOLD
);
1073 mvwprintw(pref_panel_window
, i
+ 1, 1, "[-] %s",
1074 iostreamtopview
[i
].title
);
1075 wattroff(pref_panel_window
, A_BOLD
);
1076 wattroff(pref_panel_window
, COLOR_PAIR(5));
1083 void update_cpu_sort(int *line_selected
)
1088 if (*line_selected
> (size
- 1))
1089 *line_selected
= size
- 1;
1090 else if (*line_selected
< 0)
1093 /* special case, we don't support sorting by procname for now */
1094 if (*line_selected
!= 3) {
1095 if (cputopview
[*line_selected
].sort
== 1)
1096 cputopview
[*line_selected
].reverse
= 1;
1097 for (i
= 0; i
< size
; i
++)
1098 cputopview
[i
].sort
= 0;
1099 cputopview
[*line_selected
].sort
= 1;
1103 void update_cpu_pref(int *line_selected
, int toggle_view
, int toggle_sort
)
1110 if (pref_panel_window
) {
1111 del_panel(pref_panel
);
1112 delwin(pref_panel_window
);
1116 pref_panel_window
= create_window(size
+ 2, 30, 10, 10);
1117 pref_panel
= new_panel(pref_panel_window
);
1119 werase(pref_panel_window
);
1120 box(pref_panel_window
, 0 , 0);
1121 set_window_title(pref_panel_window
, "CPUTop Preferences ");
1122 wattron(pref_panel_window
, A_BOLD
);
1123 mvwprintw(pref_panel_window
, size
+ 1, 1,
1124 " 's' : sort, space : toggle");
1125 wattroff(pref_panel_window
, A_BOLD
);
1127 if (*line_selected
> (size
- 1))
1128 *line_selected
= size
- 1;
1129 else if (*line_selected
< 0)
1131 if (toggle_sort
== 1) {
1132 update_cpu_sort(line_selected
);
1133 update_current_view();
1136 for (i
= 0; i
< size
; i
++) {
1137 if (i
== *line_selected
) {
1138 wattron(pref_panel_window
, COLOR_PAIR(5));
1139 mvwhline(pref_panel_window
, i
+ 1, 1, ' ', 30 - 2);
1141 if (cputopview
[i
].sort
== 1)
1142 wattron(pref_panel_window
, A_BOLD
);
1143 mvwprintw(pref_panel_window
, i
+ 1, 1, "[-] %s",
1144 cputopview
[i
].title
);
1145 wattroff(pref_panel_window
, A_BOLD
);
1146 wattroff(pref_panel_window
, COLOR_PAIR(5));
1153 void update_perf_sort(int *line_selected
)
1156 struct perfcounter
*perf
;
1160 size
= g_hash_table_size(global_perf_liszt
);
1161 if (*line_selected
> (size
- 1))
1162 *line_selected
= size
- 1;
1163 else if (*line_selected
< 0)
1167 perflist
= g_list_first(g_hash_table_get_keys(global_perf_liszt
));
1169 perf
= g_hash_table_lookup(global_perf_liszt
, perflist
->data
);
1170 if (i
!= *line_selected
)
1175 perflist
= g_list_next(perflist
);
1179 void update_perf_pref(int *line_selected
, int toggle_view
, int toggle_sort
)
1182 struct perfcounter
*perf
;
1188 if (pref_panel_window
) {
1189 del_panel(pref_panel
);
1190 delwin(pref_panel_window
);
1192 size
= g_hash_table_size(global_perf_liszt
);
1194 pref_panel_window
= create_window(size
+ 2, 30, 10, 10);
1195 pref_panel
= new_panel(pref_panel_window
);
1197 werase(pref_panel_window
);
1198 box(pref_panel_window
, 0 , 0);
1199 set_window_title(pref_panel_window
, "Perf Preferences ");
1200 wattron(pref_panel_window
, A_BOLD
);
1201 mvwprintw(pref_panel_window
, g_hash_table_size(global_perf_liszt
) + 1, 1,
1202 " 's' : sort, space : toggle");
1203 wattroff(pref_panel_window
, A_BOLD
);
1205 if (*line_selected
> (size
- 1))
1206 *line_selected
= size
- 1;
1207 else if (*line_selected
< 0)
1210 if (toggle_sort
== 1) {
1211 update_perf_sort(line_selected
);
1212 update_current_view();
1216 perflist
= g_list_first(g_hash_table_get_keys(global_perf_liszt
));
1218 perf
= g_hash_table_lookup(global_perf_liszt
, perflist
->data
);
1219 if (i
== *line_selected
&& toggle_view
== 1) {
1220 perf
->visible
= perf
->visible
== 1 ? 0:1;
1221 update_current_view();
1223 if (i
== *line_selected
) {
1224 wattron(pref_panel_window
, COLOR_PAIR(5));
1225 mvwhline(pref_panel_window
, i
+ 1, 1, ' ', 30 - 2);
1227 if (perf
->sort
== 1)
1228 wattron(pref_panel_window
, A_BOLD
);
1229 mvwprintw(pref_panel_window
, i
+ 1, 1, "[%c] %s",
1230 perf
->visible
== 1 ? 'x' : ' ',
1231 (char *) perflist
->data
+ 5);
1232 wattroff(pref_panel_window
, A_BOLD
);
1233 wattroff(pref_panel_window
, COLOR_PAIR(5));
1235 perflist
= g_list_next(perflist
);
1241 int update_preference_panel(int *line_selected
, int toggle_view
, int toggle_sort
)
1245 switch(current_view
) {
1247 update_perf_pref(line_selected
, toggle_view
, toggle_sort
);
1250 update_cpu_pref(line_selected
, toggle_view
, toggle_sort
);
1253 update_iostream_pref(line_selected
, toggle_view
, toggle_sort
);
1255 case process_details
:
1256 update_process_detail_pref(line_selected
, toggle_view
, toggle_sort
);
1266 int update_sort(int *line_selected
)
1270 switch(current_view
) {
1272 update_perf_sort(line_selected
);
1275 update_cpu_sort(line_selected
);
1278 update_iostream_sort(line_selected
);
1280 case process_details
:
1281 update_process_detail_sort(line_selected
);
1292 void toggle_pref_panel(void)
1296 if (pref_panel_visible
) {
1297 hide_panel(pref_panel
);
1298 pref_panel_visible
= 0;
1300 ret
= update_preference_panel(&pref_line_selected
, 0, 0);
1303 show_panel(pref_panel
);
1304 pref_panel_visible
= 1;
1310 void display(unsigned int index
)
1312 last_display_index
= index
;
1313 currently_displayed_index
= index
;
1314 data
= g_ptr_array_index(copies
, index
);
1317 max_elements
= data
->process_table
->len
;
1318 update_current_view();
1324 void pause_display()
1328 sem_wait(&pause_sem
);
1331 void resume_display()
1334 print_log("Resume");
1335 sem_post(&pause_sem
);
1338 void *handle_keyboard(void *p
)
1341 while((ch
= getch())) {
1343 /* Move the cursor and scroll */
1346 if (pref_panel_visible
) {
1347 pref_line_selected
++;
1348 update_preference_panel(&pref_line_selected
, 0, 0);
1350 if (selected_line
< (max_center_lines
- 1) &&
1351 selected_line
< max_elements
- 1) {
1354 } else if (selected_in_list
< (max_elements
- 1)
1355 && (list_offset
< (max_elements
- max_center_lines
))) {
1359 update_current_view();
1366 if (pref_panel_visible
) {
1367 if (pref_line_selected
> 0)
1368 pref_line_selected
--;
1369 update_preference_panel(&pref_line_selected
, 0, 0);
1371 if (selected_line
> 0) {
1374 } else if (selected_in_list
> 0 && list_offset
> 0) {
1378 update_current_view();
1384 /* Navigate the history with arrows */
1386 if (currently_displayed_index
> 0) {
1387 currently_displayed_index
--;
1388 print_log("Going back in time");
1390 print_log("Cannot rewind, last data is already displayed");
1392 data
= g_ptr_array_index(copies
, currently_displayed_index
);
1393 max_elements
= data
->process_table
->len
;
1395 /* we force to pause the display when moving in time */
1396 if (toggle_pause
< 0)
1399 update_current_view();
1403 if (currently_displayed_index
< last_display_index
) {
1404 currently_displayed_index
++;
1405 print_log("Going forward in time");
1406 data
= g_ptr_array_index(copies
, currently_displayed_index
);
1407 max_elements
= data
->process_table
->len
;
1408 update_current_view();
1411 print_log("Manually moving forward");
1413 if (toggle_pause
> 0) {
1414 sem_post(&pause_sem
);
1415 update_current_view();
1416 sem_wait(&pause_sem
);
1422 if (pref_panel_visible
) {
1423 update_preference_panel(&pref_line_selected
, 1, 0);
1425 update_selected_processes();
1426 update_current_view();
1430 if (pref_panel_visible
)
1431 update_preference_panel(&pref_line_selected
, 0, 1);
1434 /* perf uses a hashtable, it is ordered backward */
1435 if (current_view
== perf
) {
1436 pref_current_sort
--;
1437 } else if (!pref_panel_visible
) {
1438 pref_current_sort
++;
1440 update_sort(&pref_current_sort
);
1441 update_current_view();
1444 /* perf uses a hashtable, it is ordered backward */
1445 if (current_view
== perf
) {
1446 pref_current_sort
++;
1447 } else if (!pref_panel_visible
) {
1448 pref_current_sort
--;
1450 update_sort(&pref_current_sort
);
1451 update_current_view();
1454 case 13: /* FIXME : KEY_ENTER ?? */
1455 if (pref_panel_visible
)
1457 if (current_view
!= process_details
) {
1458 previous_view
= current_view
;
1459 current_view
= process_details
;
1461 current_view
= previous_view
;
1462 previous_view
= process_details
;
1465 update_current_view();
1469 if (pref_panel_visible
)
1470 toggle_pref_panel();
1473 update_current_view();
1476 if (pref_panel_visible
)
1477 toggle_pref_panel();
1480 update_current_view();
1483 if (pref_panel_visible
)
1484 toggle_pref_panel();
1485 current_view
= perf
;
1487 update_current_view();
1490 if (pref_panel_visible
)
1491 toggle_pref_panel();
1492 current_view
= iostream
;
1494 update_current_view();
1499 /* exit keyboard thread */
1503 toggle_threads
*= -1;
1504 update_current_view();
1507 if (toggle_pause
< 0) {
1514 toggle_pref_panel();
1516 /* ESCAPE, but slow to process, don't know why */
1518 if (pref_panel_visible
)
1519 toggle_pref_panel();
1520 else if (current_view
== process_details
) {
1521 current_view
= previous_view
;
1522 previous_view
= process_details
;
1524 update_current_view();
1528 update_current_view();
1536 void init_view_headers()
1538 cputopview
[0].title
= strdup("CPU(%)");
1539 cputopview
[0].sort
= 1;
1540 cputopview
[1].title
= strdup("PID");
1541 cputopview
[2].title
= strdup("TID");
1542 cputopview
[3].title
= strdup("NAME");
1544 iostreamtopview
[0].title
= strdup("R (B/sec)");
1545 iostreamtopview
[1].title
= strdup("W (B/sec)");
1546 iostreamtopview
[2].title
= strdup("Total (B)");
1547 iostreamtopview
[2].sort
= 1;
1549 fileview
[0].title
= strdup("FD");
1550 fileview
[1].title
= strdup("READ");
1551 fileview
[1].sort
= 1;
1552 fileview
[2].title
= strdup("WRITE");
1557 selected_processes
= g_ptr_array_new();
1558 sem_init(&update_display_sem
, 0, 1);
1559 init_view_headers();
1562 header
= create_window(5, COLS
- 1, 0, 0);
1563 center
= create_window(LINES
- 5 - 7, COLS
- 1, 5, 0);
1564 status
= create_window(MAX_LOG_LINES
+ 2, COLS
- 1, LINES
- 7, 0);
1565 footer
= create_window(1, COLS
- 1, LINES
- 1, 0);
1567 print_log("Starting display");
1569 main_panel
= new_panel(center
);
1576 pthread_create(&keyboard_thread
, NULL
, handle_keyboard
, (void *)NULL
);