2 * Copyright (C) 2011-2012 Julien Desfossez
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License Version 2 as
6 * published by the Free Software Foundation;
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 #include <semaphore.h>
27 #include "cursesdisplay.h"
28 #include "lttngtoptypes.h"
29 #include "iostreamtop.h"
32 #define DEFAULT_DELAY 15
33 #define MAX_LINE_LENGTH 50
34 #define MAX_LOG_LINES 4
36 /* to prevent concurrent updates of the different windows */
37 sem_t update_display_sem
;
40 WINDOW
*footer
, *header
, *center
, *status
;
41 WINDOW
*pref_panel_window
= NULL
;
42 PANEL
*pref_panel
, *main_panel
;
44 int pref_panel_visible
= 0;
45 int pref_line_selected
= 0;
46 int pref_current_sort
= 0;
48 int last_display_index
, currently_displayed_index
;
50 struct processtop
*selected_process
= NULL
;
53 int selected_line
= 0; /* select bar position */
54 int selected_in_list
= 0; /* selection relative to the whole list */
55 int list_offset
= 0; /* first index in the list to display (scroll) */
57 char log_lines
[MAX_LINE_LENGTH
* MAX_LOG_LINES
+ MAX_LOG_LINES
];
59 int max_elements
= 80;
61 int toggle_threads
= 1;
63 int toggle_pause
= -1;
66 GPtrArray
*selected_processes
;
68 pthread_t keyboard_thread
;
70 struct header_view cputopview
[6];
71 struct header_view iostreamtopview
[3];
72 struct header_view fileview
[3];
81 sem_post(&goodtodisplay
);
82 sem_post(&end_trace_sem
);
83 sem_post(&goodtoupdate
);
86 static void handle_sigterm(int signal
)
88 pthread_cancel(keyboard_thread
);
96 halfdelay(DEFAULT_DELAY
);
98 intrflush(stdscr
, false);
104 init_pair(1, COLOR_RED
, COLOR_BLACK
); /* - */
105 init_pair(2, COLOR_GREEN
, COLOR_BLACK
); /* + */
106 init_pair(3, COLOR_BLACK
, COLOR_WHITE
); /* keys */
107 init_pair(4, COLOR_WHITE
, COLOR_GREEN
); /* keys activated */
108 init_pair(5, COLOR_WHITE
, COLOR_BLUE
); /* select line */
109 init_pair(6, COLOR_WHITE
, COLOR_GREEN
); /* selected process */
111 termtype
= getenv("TERM");
112 if (!strcmp(termtype
, "xterm") || !strcmp(termtype
, "xterm-color") ||
113 !strcmp(termtype
, "vt220")) {
114 define_key("\033[H", KEY_HOME
);
115 define_key("\033[F", KEY_END
);
116 define_key("\033OP", KEY_F(1));
117 define_key("\033OQ", KEY_F(2));
118 define_key("\033OR", KEY_F(3));
119 define_key("\033OS", KEY_F(4));
120 define_key("\0330U", KEY_F(6));
121 define_key("\033[11~", KEY_F(1));
122 define_key("\033[12~", KEY_F(2));
123 define_key("\033[13~", KEY_F(3));
124 define_key("\033[14~", KEY_F(4));
125 define_key("\033[16~", KEY_F(6));
126 define_key("\033[17;2~", KEY_F(18));
128 signal(SIGTERM
, handle_sigterm
);
129 signal(SIGINT
, handle_sigterm
);
130 mousemask(BUTTON1_CLICKED
, NULL
);
134 WINDOW
*create_window(int height
, int width
, int startx
, int starty
)
137 win
= newwin(height
, width
, startx
, starty
);
143 WINDOW
*create_window_no_border(int height
, int width
, int startx
, int starty
)
146 win
= newwin(height
, width
, startx
, starty
);
151 void print_digit(WINDOW
*win
, int digit
)
154 wattron(win
, COLOR_PAIR(1));
155 wprintw(win
, "%d", digit
);
156 wattroff(win
, COLOR_PAIR(1));
157 } else if (digit
> 0) {
158 wattron(win
, COLOR_PAIR(2));
159 wprintw(win
, "+%d", digit
);
160 wattroff(win
, COLOR_PAIR(2));
166 void print_digits(WINDOW
*win
, int first
, int second
)
169 print_digit(win
, first
);
171 print_digit(win
, second
);
175 void print_headers(int line
, char *desc
, int value
, int first
, int second
)
177 wattron(header
, A_BOLD
);
178 mvwprintw(header
, line
, 4, "%s", desc
);
179 wattroff(header
, A_BOLD
);
180 mvwprintw(header
, line
, 16, "%d", value
);
181 wmove(header
, line
, 24);
182 print_digits(header
, first
, second
);
183 wmove(header
, line
, 40);
186 void set_window_title(WINDOW
*win
, char *title
)
188 wattron(win
, A_BOLD
);
189 mvwprintw(win
, 0, 1, title
);
190 wattroff(win
, A_BOLD
);
193 void print_log(char *str
)
196 int current_line
= 1;
197 int current_char
= 1;
199 /* rotate the line buffer */
200 if (nb_log_lines
>= MAX_LOG_LINES
) {
201 tmp
= strndup(log_lines
, MAX_LINE_LENGTH
* MAX_LOG_LINES
+ MAX_LOG_LINES
);
202 tmp2
= strchr(tmp
, '\n');
203 memset(log_lines
, '\0', strlen(log_lines
));
204 strncat(log_lines
, tmp2
+ 1, strlen(tmp2
) - 1);
205 log_lines
[strlen(log_lines
)] = '\n';
206 log_lines
[strlen(log_lines
)] = '\0';
211 strncat(log_lines
, str
, MAX_LINE_LENGTH
- 1);
213 if (nb_log_lines
< MAX_LOG_LINES
)
214 log_lines
[strlen(log_lines
)] = '\n';
215 log_lines
[strlen(log_lines
)] = '\0';
219 set_window_title(status
, "Status");
220 for (i
= 0; i
< strlen(log_lines
); i
++) {
221 if (log_lines
[i
] == '\n') {
222 wmove(status
, ++current_line
, 1);
225 mvwprintw(status
, current_line
, current_char
++, "%c",
232 int process_selected(struct processtop
*process
)
235 struct processtop
*stored_process
;
237 for (i
= 0; i
< selected_processes
->len
; i
++) {
238 stored_process
= g_ptr_array_index(selected_processes
, i
);
241 if (stored_process
->tid
== process
->tid
)
247 void update_selected_processes()
250 struct processtop
*stored_process
;
252 if (process_selected(selected_process
)) {
253 for (i
= 0; i
< selected_processes
->len
; i
++) {
254 stored_process
= g_ptr_array_index(selected_processes
, i
);
257 if (stored_process
->tid
== selected_process
->tid
)
258 g_ptr_array_remove(selected_processes
,
260 print_log("Process removed");
263 g_ptr_array_add(selected_processes
, selected_process
);
264 print_log("Process added");
268 void print_key(WINDOW
*win
, char *key
, char *desc
, int toggle
)
275 wattron(win
, COLOR_PAIR(pair
));
276 wprintw(footer
, "%s", key
);
277 wattroff(win
, COLOR_PAIR(pair
));
278 wprintw(footer
, ":%s", desc
);
283 sem_wait(&update_display_sem
);
286 print_key(footer
, "F2", "CPUtop ", current_view
== cpu
);
287 print_key(footer
, "F3", "PerfTop ", current_view
== perf
);
288 print_key(footer
, "F4", "IOTop ", current_view
== iostream
);
289 print_key(footer
, "Enter", "Details ", current_view
== process_details
);
290 print_key(footer
, "Space", "Highlight ", 0);
291 print_key(footer
, "q", "Quit ", 0);
292 print_key(footer
, "r", "Pref ", 0);
293 print_key(footer
, "t", "Threads ", toggle_threads
);
294 print_key(footer
, "v", "Virt ", toggle_virt
);
295 print_key(footer
, "p", "Pause ", toggle_pause
);
298 sem_post(&update_display_sem
);
305 set_window_title(header
, "Statistics for interval [gathering data...[");
306 wattron(header
, A_BOLD
);
307 mvwprintw(header
, 1, 4, "CPUs");
308 mvwprintw(header
, 2, 4, "Threads");
309 mvwprintw(header
, 3, 4, "FDs");
310 wattroff(header
, A_BOLD
);
314 static void scale_unit(uint64_t bytes
, char *ret
)
316 if (bytes
>= 1000000000)
317 sprintf(ret
, "%" PRIu64
"G", bytes
/1000000000);
318 if (bytes
>= 1000000)
319 sprintf(ret
, "%" PRIu64
"M", bytes
/1000000);
320 else if (bytes
>= 1000)
321 sprintf(ret
, "%" PRIu64
"K", bytes
/1000);
323 sprintf(ret
, "%" PRIu64
, bytes
);
329 struct processtop
*tmp
;
332 for (i
= 0; i
< data
->process_table
->len
; i
++) {
333 tmp
= g_ptr_array_index(data
->process_table
, i
);
334 total
+= tmp
->fileread
;
335 total
+= tmp
->filewrite
;
343 struct tm start
, end
;
344 uint64_t ts_nsec_start
, ts_nsec_end
;
347 ts_nsec_start
= data
->start
% NSEC_PER_SEC
;
348 start
= format_timestamp(data
->start
);
350 ts_nsec_end
= data
->end
% NSEC_PER_SEC
;
351 end
= format_timestamp(data
->end
);
355 set_window_title(header
, "Statistics for interval ");
356 wattron(header
, A_BOLD
);
358 wprintw(header
, "[%02d:%02d:%02d.%09" PRIu64
", %02d:%02d:%02d.%09" PRIu64
"[",
359 start
.tm_hour
, start
.tm_min
, start
.tm_sec
, ts_nsec_start
,
360 end
.tm_hour
, end
.tm_min
, end
.tm_sec
, ts_nsec_end
);
361 mvwprintw(header
, 1, 4, "CPUs");
362 wattroff(header
, A_BOLD
);
363 wprintw(header
, "\t%d\t(max/cpu : %0.2f%)", data
->cpu_table
->len
,
364 100.0/data
->cpu_table
->len
);
365 print_headers(2, "Threads", data
->nbthreads
, data
->nbnewthreads
,
366 -1*(data
->nbdeadthreads
));
367 print_headers(3, "FDs", data
->nbfiles
, data
->nbnewfiles
,
368 -1*(data
->nbclosedfiles
));
369 scale_unit(total_io(), io
);
370 mvwprintw(header
, 3, 43, "%sB/sec", io
);
374 gint
sort_by_cpu_desc(gconstpointer p1
, gconstpointer p2
)
376 struct processtop
*n1
= *(struct processtop
**)p1
;
377 struct processtop
*n2
= *(struct processtop
**)p2
;
378 unsigned long totaln1
= n1
->totalcpunsec
;
379 unsigned long totaln2
= n2
->totalcpunsec
;
381 if (totaln1
< totaln2
)
383 if (totaln1
== totaln2
)
388 gint
sort_by_tid_desc(gconstpointer p1
, gconstpointer p2
)
390 struct processtop
*n1
= *(struct processtop
**)p1
;
391 struct processtop
*n2
= *(struct processtop
**)p2
;
392 unsigned long totaln1
= n1
->tid
;
393 unsigned long totaln2
= n2
->tid
;
395 if (totaln1
< totaln2
)
397 if (totaln1
== totaln2
)
402 gint
sort_by_pid_desc(gconstpointer p1
, gconstpointer p2
)
404 struct processtop
*n1
= *(struct processtop
**)p1
;
405 struct processtop
*n2
= *(struct processtop
**)p2
;
406 unsigned long totaln1
= n1
->pid
;
407 unsigned long totaln2
= n2
->pid
;
409 if (totaln1
< totaln2
)
411 if (totaln1
== totaln2
)
416 gint
sort_by_process_read_desc(gconstpointer p1
, gconstpointer p2
)
418 struct processtop
*n1
= *(struct processtop
**)p1
;
419 struct processtop
*n2
= *(struct processtop
**)p2
;
420 unsigned long totaln1
= n1
->fileread
;
421 unsigned long totaln2
= n2
->fileread
;
423 if (totaln1
< totaln2
)
425 if (totaln1
== totaln2
)
430 gint
sort_by_process_write_desc(gconstpointer p1
, gconstpointer p2
)
432 struct processtop
*n1
= *(struct processtop
**)p1
;
433 struct processtop
*n2
= *(struct processtop
**)p2
;
434 unsigned long totaln1
= n1
->filewrite
;
435 unsigned long totaln2
= n2
->filewrite
;
437 if (totaln1
< totaln2
)
439 if (totaln1
== totaln2
)
444 gint
sort_by_process_total_desc(gconstpointer p1
, gconstpointer p2
)
446 struct processtop
*n1
= *(struct processtop
**)p1
;
447 struct processtop
*n2
= *(struct processtop
**)p2
;
448 unsigned long totaln1
= n1
->totalfilewrite
+ n1
->totalfileread
;
449 unsigned long totaln2
= n2
->totalfilewrite
+ n2
->totalfileread
;
451 if (totaln1
< totaln2
)
453 if (totaln1
== totaln2
)
458 gint
sort_by_file_read_desc(gconstpointer p1
, gconstpointer p2
)
460 struct files
*n1
= *(struct files
**)p1
;
461 struct files
*n2
= *(struct files
**)p2
;
462 unsigned long totaln1
;
463 unsigned long totaln2
;
468 if (totaln1
< totaln2
)
470 if (totaln1
== totaln2
)
475 gint
sort_by_file_write_desc(gconstpointer p1
, gconstpointer p2
)
477 struct files
*n1
= *(struct files
**)p1
;
478 struct files
*n2
= *(struct files
**)p2
;
479 unsigned long totaln1
;
480 unsigned long totaln2
;
485 if (totaln1
< totaln2
)
487 if (totaln1
== totaln2
)
492 gint
sort_by_file_fd_desc(gconstpointer p1
, gconstpointer p2
)
494 struct files
*n1
= *(struct files
**)p1
;
495 struct files
*n2
= *(struct files
**)p2
;
496 unsigned long totaln1
;
497 unsigned long totaln2
;
502 if (totaln1
< totaln2
)
504 if (totaln1
== totaln2
)
509 gint
sort_by_cpu_group_by_threads_desc(gconstpointer p1
, gconstpointer p2
)
511 struct processtop
*n1
= *(struct processtop
**)p1
;
512 struct processtop
*n2
= *(struct processtop
**)p2
;
513 unsigned long totaln1
= n1
->threadstotalcpunsec
;
514 unsigned long totaln2
= n2
->threadstotalcpunsec
;
516 if (totaln1
< totaln2
)
518 if (totaln1
== totaln2
)
523 void update_kprobes_display()
527 set_window_title(center
, "Kprobes Top");
529 wattron(center, A_BOLD);
531 for (i = 0; i < 6; i++) {
532 if (toggle_virt < 0 && (i == 3 || i == 4)) {
535 if (cputopview[i].sort) {
536 wattron(center, A_UNDERLINE);
537 pref_current_sort = i;
539 mvwprintw(center, 1, column, cputopview[i].title);
540 wattroff(center, A_UNDERLINE);
543 wattroff(center, A_BOLD);
547 void update_cputop_display()
550 int header_offset
= 2;
551 struct processtop
*tmp
;
552 unsigned long elapsed
;
554 int nblinedisplayed
= 0;
555 int current_line
= 0;
556 int current_row_offset
;
559 elapsed
= data
->end
- data
->start
;
560 maxcputime
= elapsed
* data
->cpu_table
->len
/ 100.0;
562 if (cputopview
[0].sort
== 1)
563 g_ptr_array_sort(data
->process_table
, sort_by_cpu_desc
);
564 else if (cputopview
[1].sort
== 1)
565 g_ptr_array_sort(data
->process_table
, sort_by_pid_desc
);
566 else if (cputopview
[2].sort
== 1)
567 g_ptr_array_sort(data
->process_table
, sort_by_tid_desc
);
568 else if (cputopview
[3].sort
== 1)
569 g_ptr_array_sort(data
->process_table
, sort_by_cpu_desc
);
571 g_ptr_array_sort(data
->process_table
, sort_by_cpu_desc
);
573 set_window_title(center
, "CPU Top");
574 wattron(center
, A_BOLD
);
576 for (i
= 0; i
< 6; i
++) {
577 if (toggle_virt
< 0 && (i
== 3 || i
== 4)) {
580 if (cputopview
[i
].sort
) {
581 wattron(center
, A_UNDERLINE
);
582 pref_current_sort
= i
;
584 mvwprintw(center
, 1, column
, cputopview
[i
].title
);
585 wattroff(center
, A_UNDERLINE
);
588 wattroff(center
, A_BOLD
);
590 max_center_lines
= LINES
- 5 - 7 - 1 - header_offset
;
592 /* iterate the process (thread) list */
593 for (i
= list_offset
; i
< data
->process_table
->len
&&
594 nblinedisplayed
< max_center_lines
; i
++) {
595 tmp
= g_ptr_array_index(data
->process_table
, i
);
596 current_row_offset
= 1;
597 if (!opt_tid
&& (opt_hostname
&& !lookup_hostname_list(tmp
->hostname
)))
599 if (!opt_hostname
&& (opt_tid
&& !lookup_tid_list(tmp
->pid
)))
601 if ((opt_tid
&& !lookup_tid_list(tmp
->tid
)) &&
602 (opt_hostname
&& !lookup_hostname_list(tmp
->hostname
)))
605 if (tmp
->pid
!= tmp
->tid
)
606 if (toggle_threads
== -1)
609 if (process_selected(tmp
)) {
610 wattron(center
, COLOR_PAIR(6));
611 mvwhline(center
, current_line
+ header_offset
, 1, ' ', COLS
-3);
613 if (current_line
== selected_line
) {
614 selected_process
= tmp
;
615 wattron(center
, COLOR_PAIR(5));
616 mvwhline(center
, current_line
+ header_offset
, 1, ' ', COLS
-3);
619 mvwprintw(center
, current_line
+ header_offset
,
620 current_row_offset
, "%1.2f",
621 tmp
->totalcpunsec
/ maxcputime
);
622 current_row_offset
+= 10;
624 mvwprintw(center
, current_line
+ header_offset
,
625 current_row_offset
, "%d", tmp
->pid
);
626 current_row_offset
+= 10;
628 mvwprintw(center
, current_line
+ header_offset
,
629 current_row_offset
, "%d", tmp
->tid
);
630 current_row_offset
+= 10;
631 if (toggle_virt
> 0) {
633 mvwprintw(center
, current_line
+ header_offset
,
634 current_row_offset
, "%d", tmp
->vpid
);
635 current_row_offset
+= 10;
637 mvwprintw(center
, current_line
+ header_offset
,
638 current_row_offset
, "%d", tmp
->vtid
);
639 current_row_offset
+= 10;
642 mvwprintw(center
, current_line
+ header_offset
,
643 current_row_offset
, "%s", tmp
->comm
);
644 wattroff(center
, COLOR_PAIR(6));
645 wattroff(center
, COLOR_PAIR(5));
651 gint
sort_perf(gconstpointer p1
, gconstpointer p2
, gpointer key
)
653 struct processtop
*n1
= *(struct processtop
**) p1
;
654 struct processtop
*n2
= *(struct processtop
**) p2
;
656 struct perfcounter
*tmp1
, *tmp2
;
657 unsigned long totaln2
= 0;
658 unsigned long totaln1
= 0;
663 tmp1
= g_hash_table_lookup(n1
->perf
, key
);
667 totaln1
= tmp1
->count
;
669 tmp2
= g_hash_table_lookup(n2
->perf
, key
);
673 totaln2
= tmp2
->count
;
675 if (totaln1
< totaln2
)
677 if (totaln1
== totaln2
) {
680 if (totaln1
< totaln2
)
687 void print_key_title(char *key
, int line
)
689 wattron(center
, A_BOLD
);
690 mvwprintw(center
, line
, 1, "%s", key
);
691 mvwprintw(center
, line
, 30, " ");
692 wattroff(center
, A_BOLD
);
695 void update_process_details()
697 unsigned long elapsed
;
699 struct processtop
*tmp
;
700 struct files
*file_tmp
;
703 char filename_buf
[COLS
];
706 GPtrArray
*newfilearray
= g_ptr_array_new();
708 struct perfcounter
*perfn1
, *perfn2
;
711 set_window_title(center
, "Process details");
714 tmp
= find_process_tid(data
,
715 selected_process
->tid
,
716 selected_process
->comm
);
717 elapsed
= data
->end
- data
->start
;
718 maxcputime
= elapsed
* data
->cpu_table
->len
/ 100.0;
720 print_key_title("Name", line
++);
721 wprintw(center
, "%s", selected_process
->comm
);
722 print_key_title("TID", line
++);
723 wprintw(center
, "%d", selected_process
->tid
);
725 print_key_title("Does not exit at this time", 3);
729 print_key_title("PID", line
++);
730 wprintw(center
, "%d", tmp
->pid
);
731 print_key_title("PPID", line
++);
732 wprintw(center
, "%d", tmp
->ppid
);
733 print_key_title("VPID", line
++);
734 wprintw(center
, "%d", tmp
->vpid
);
735 print_key_title("VTID", line
++);
736 wprintw(center
, "%d", tmp
->vtid
);
737 print_key_title("VPPID", line
++);
738 wprintw(center
, "%d", tmp
->vppid
);
739 print_key_title("CPU", line
++);
740 wprintw(center
, "%1.2f %%", tmp
->totalcpunsec
/maxcputime
);
742 print_key_title("READ B/s", line
++);
743 scale_unit(tmp
->fileread
, unit
);
744 wprintw(center
, "%s", unit
);
746 print_key_title("WRITE B/s", line
++);
747 scale_unit(tmp
->filewrite
, unit
);
748 wprintw(center
, "%s", unit
);
750 g_hash_table_iter_init(&iter
, global_perf_liszt
);
751 while (g_hash_table_iter_next (&iter
, &key
, (gpointer
) &perfn1
)) {
752 print_key_title((char *) key
, line
++);
753 perfn2
= g_hash_table_lookup(tmp
->perf
, (char *) key
);
754 wprintw(center
, "%d", perfn2
? perfn2
->count
: 0);
758 wattron(center
, A_BOLD
);
760 for (i
= 0; i
< 3; i
++) {
761 if (fileview
[i
].sort
) {
762 pref_current_sort
= i
;
763 wattron(center
, A_UNDERLINE
);
765 mvwprintw(center
, line
, column
, fileview
[i
].title
);
766 wattroff(center
, A_UNDERLINE
);
769 mvwprintw(center
, line
++, column
, "FILENAME");
770 wattroff(center
, A_BOLD
);
773 * since the process_files_table array could contain NULL file structures,
774 * and that the positions inside the array is important (it is the FD), we
775 * need to create a temporary array that we can sort.
777 for (i
= 0; i
< tmp
->process_files_table
->len
; i
++) {
778 file_tmp
= g_ptr_array_index(tmp
->process_files_table
, i
);
780 g_ptr_array_add(newfilearray
, file_tmp
);
783 if (fileview
[0].sort
== 1)
784 g_ptr_array_sort(newfilearray
, sort_by_file_fd_desc
);
785 else if (fileview
[1].sort
== 1)
786 g_ptr_array_sort(newfilearray
, sort_by_file_read_desc
);
787 else if (fileview
[2].sort
== 1)
788 g_ptr_array_sort(newfilearray
, sort_by_file_write_desc
);
790 g_ptr_array_sort(newfilearray
, sort_by_file_read_desc
);
792 for (i
= selected_line
; i
< newfilearray
->len
&&
793 i
< (selected_line
+ max_center_lines
- line
+ 2); i
++) {
794 file_tmp
= g_ptr_array_index(newfilearray
, i
);
797 mvwprintw(center
, line
+ j
, 1, "%d", file_tmp
->fd
);
798 scale_unit(file_tmp
->read
, unit
);
799 mvwprintw(center
, line
+ j
, 11, "%s", unit
);
800 scale_unit(file_tmp
->write
, unit
);
801 mvwprintw(center
, line
+ j
, 21, "%s", unit
);
802 snprintf(filename_buf
, COLS
- 25, "%s", file_tmp
->name
);
803 mvwprintw(center
, line
+ j
, 31, "%s", filename_buf
);
806 g_ptr_array_free(newfilearray
, TRUE
);
812 int nblinedisplayed
= 0;
813 int current_line
= 0;
814 struct processtop
*tmp
;
815 int header_offset
= 2;
817 struct perfcounter
*perfn1
, *perfn2
;
818 char *perf_key
= NULL
;
823 set_window_title(center
, "Perf Top");
824 wattron(center
, A_BOLD
);
825 mvwprintw(center
, 1, 1, "PID");
826 mvwprintw(center
, 1, 11, "TID");
827 mvwprintw(center
, 1, 22, "NAME");
830 g_hash_table_iter_init(&iter
, global_perf_liszt
);
831 while (g_hash_table_iter_next (&iter
, &key
, (gpointer
) &perfn1
)) {
832 if (perfn1
->visible
) {
834 /* pref_current_sort = i; */
835 wattron(center
, A_UNDERLINE
);
837 /* + 5 to strip the "perf_" prefix */
838 mvwprintw(center
, 1, perf_row
, "%s",
840 wattroff(center
, A_UNDERLINE
);
844 perf_key
= (char *) key
;
847 wattroff(center
, A_BOLD
);
849 g_ptr_array_sort_with_data(data
->process_table
, sort_perf
, perf_key
);
851 for (i
= 0; i
< data
->process_table
->len
&&
852 nblinedisplayed
< max_center_lines
; i
++) {
853 tmp
= g_ptr_array_index(data
->process_table
, i
);
855 if (!opt_tid
&& (opt_hostname
&& !lookup_hostname_list(tmp
->hostname
)))
857 if (!opt_hostname
&& (opt_tid
&& !lookup_tid_list(tmp
->pid
)))
859 if ((opt_tid
&& !lookup_tid_list(tmp
->tid
)) &&
860 (opt_hostname
&& !lookup_hostname_list(tmp
->hostname
)))
863 if (tmp
->pid
!= tmp
->tid
)
864 if (toggle_threads
== -1)
867 if (process_selected(tmp
)) {
868 wattron(center
, COLOR_PAIR(6));
869 mvwhline(center
, current_line
+ header_offset
, 1, ' ', COLS
-3);
871 if (current_line
== selected_line
) {
872 selected_process
= tmp
;
873 wattron(center
, COLOR_PAIR(5));
874 mvwhline(center
, current_line
+ header_offset
, 1, ' ', COLS
-3);
877 mvwprintw(center
, current_line
+ header_offset
, 1, "%d", tmp
->pid
);
878 mvwprintw(center
, current_line
+ header_offset
, 11, "%d", tmp
->tid
);
879 mvwprintw(center
, current_line
+ header_offset
, 22, "%s", tmp
->comm
);
881 g_hash_table_iter_init(&iter
, global_perf_liszt
);
884 while (g_hash_table_iter_next (&iter
, &key
, (gpointer
) &perfn1
)) {
885 if (perfn1
->visible
) {
886 perfn2
= g_hash_table_lookup(tmp
->perf
, (char *) key
);
888 value
= perfn2
->count
;
891 mvwprintw(center
, current_line
+ header_offset
,
892 perf_row
, "%d", value
);
897 wattroff(center
, COLOR_PAIR(6));
898 wattroff(center
, COLOR_PAIR(5));
904 void update_iostream()
907 int header_offset
= 2;
908 struct processtop
*tmp
;
909 int nblinedisplayed
= 0;
910 int current_line
= 0;
915 set_window_title(center
, "IO Top");
916 wattron(center
, A_BOLD
);
917 mvwprintw(center
, 1, 1, "PID");
918 mvwprintw(center
, 1, 11, "TID");
919 mvwprintw(center
, 1, 22, "NAME");
921 for (i
= 0; i
< 3; i
++) {
922 if (iostreamtopview
[i
].sort
) {
923 pref_current_sort
= i
;
924 wattron(center
, A_UNDERLINE
);
926 mvwprintw(center
, 1, column
, iostreamtopview
[i
].title
);
927 wattroff(center
, A_UNDERLINE
);
930 wattroff(center
, A_BOLD
);
931 wattroff(center
, A_UNDERLINE
);
933 if (iostreamtopview
[0].sort
== 1)
934 g_ptr_array_sort(data
->process_table
, sort_by_process_read_desc
);
935 else if (iostreamtopview
[1].sort
== 1)
936 g_ptr_array_sort(data
->process_table
, sort_by_process_write_desc
);
937 else if (iostreamtopview
[2].sort
== 1)
938 g_ptr_array_sort(data
->process_table
, sort_by_process_total_desc
);
940 g_ptr_array_sort(data
->process_table
, sort_by_process_total_desc
);
942 for (i
= list_offset
; i
< data
->process_table
->len
&&
943 nblinedisplayed
< max_center_lines
; i
++) {
944 tmp
= g_ptr_array_index(data
->process_table
, i
);
946 if (!opt_tid
&& (opt_hostname
&& !lookup_hostname_list(tmp
->hostname
)))
948 if (!opt_hostname
&& (opt_tid
&& !lookup_tid_list(tmp
->pid
)))
950 if ((opt_tid
&& !lookup_tid_list(tmp
->tid
)) &&
951 (opt_hostname
&& !lookup_hostname_list(tmp
->hostname
)))
954 if (tmp
->pid
!= tmp
->tid
)
955 if (toggle_threads
== -1)
958 if (process_selected(tmp
)) {
959 wattron(center
, COLOR_PAIR(6));
960 mvwhline(center
, current_line
+ header_offset
, 1, ' ', COLS
-3);
962 if (current_line
== selected_line
) {
963 selected_process
= tmp
;
964 wattron(center
, COLOR_PAIR(5));
965 mvwhline(center
, current_line
+ header_offset
, 1, ' ', COLS
-3);
968 mvwprintw(center
, current_line
+ header_offset
, 1, "%d", tmp
->pid
);
970 mvwprintw(center
, current_line
+ header_offset
, 11, "%d", tmp
->tid
);
972 mvwprintw(center
, current_line
+ header_offset
, 22, "%s", tmp
->comm
);
974 /* READ (bytes/sec) */
975 scale_unit(tmp
->fileread
, unit
);
976 mvwprintw(center
, current_line
+ header_offset
, 40, "%s", unit
);
978 /* WRITE (bytes/sec) */
979 scale_unit(tmp
->filewrite
, unit
);
980 mvwprintw(center
, current_line
+ header_offset
, 52, "%s", unit
);
983 total
= tmp
->totalfileread
+ tmp
->totalfilewrite
;
985 scale_unit(total
, unit
);
986 mvwprintw(center
, current_line
+ header_offset
, 64, "%s", unit
);
988 wattroff(center
, COLOR_PAIR(6));
989 wattroff(center
, COLOR_PAIR(5));
995 void update_current_view()
997 sem_wait(&update_display_sem
);
1004 switch (current_view
) {
1006 update_cputop_display();
1011 case process_details
:
1012 update_process_details();
1018 update_cputop_display();
1021 update_kprobes_display();
1028 sem_post(&update_display_sem
);
1031 void update_process_detail_sort(int *line_selected
)
1038 if (*line_selected
> (size
- 1))
1039 *line_selected
= size
- 1;
1040 else if (*line_selected
< 0)
1043 if (fileview
[*line_selected
].sort
== 1)
1044 fileview
[*line_selected
].reverse
= 1;
1045 for (i
= 0; i
< size
; i
++)
1046 fileview
[i
].sort
= 0;
1047 fileview
[*line_selected
].sort
= 1;
1050 void update_process_detail_pref(int *line_selected
, int toggle_view
, int toggle_sort
)
1057 if (pref_panel_window
) {
1058 del_panel(pref_panel
);
1059 delwin(pref_panel_window
);
1063 pref_panel_window
= create_window(size
+ 2, 30, 10, 10);
1064 pref_panel
= new_panel(pref_panel_window
);
1066 werase(pref_panel_window
);
1067 box(pref_panel_window
, 0 , 0);
1068 set_window_title(pref_panel_window
, "Process Detail Preferences ");
1069 wattron(pref_panel_window
, A_BOLD
);
1070 mvwprintw(pref_panel_window
, size
+ 1, 1,
1071 " 's' : sort, space : toggle");
1072 wattroff(pref_panel_window
, A_BOLD
);
1074 if (*line_selected
> (size
- 1))
1075 *line_selected
= size
- 1;
1076 else if (*line_selected
< 0)
1078 if (toggle_sort
== 1) {
1079 update_process_detail_sort(line_selected
);
1080 update_current_view();
1083 for (i
= 0; i
< size
; i
++) {
1084 if (i
== *line_selected
) {
1085 wattron(pref_panel_window
, COLOR_PAIR(5));
1086 mvwhline(pref_panel_window
, i
+ 1, 1, ' ', 30 - 2);
1088 if (fileview
[i
].sort
== 1)
1089 wattron(pref_panel_window
, A_BOLD
);
1090 mvwprintw(pref_panel_window
, i
+ 1, 1, "[-] %s",
1092 wattroff(pref_panel_window
, A_BOLD
);
1093 wattroff(pref_panel_window
, COLOR_PAIR(5));
1100 void update_iostream_sort(int *line_selected
)
1106 if (*line_selected
> (size
- 1))
1107 *line_selected
= size
- 1;
1108 else if (*line_selected
< 0)
1110 if (iostreamtopview
[*line_selected
].sort
== 1)
1111 iostreamtopview
[*line_selected
].reverse
= 1;
1112 for (i
= 0; i
< size
; i
++)
1113 iostreamtopview
[i
].sort
= 0;
1114 iostreamtopview
[*line_selected
].sort
= 1;
1118 void update_iostream_pref(int *line_selected
, int toggle_view
, int toggle_sort
)
1125 if (pref_panel_window
) {
1126 del_panel(pref_panel
);
1127 delwin(pref_panel_window
);
1131 pref_panel_window
= create_window(size
+ 2, 30, 10, 10);
1132 pref_panel
= new_panel(pref_panel_window
);
1134 werase(pref_panel_window
);
1135 box(pref_panel_window
, 0 , 0);
1136 set_window_title(pref_panel_window
, "IOTop Preferences ");
1137 wattron(pref_panel_window
, A_BOLD
);
1138 mvwprintw(pref_panel_window
, size
+ 1, 1,
1139 " 's' : sort, space : toggle");
1140 wattroff(pref_panel_window
, A_BOLD
);
1142 if (*line_selected
> (size
- 1))
1143 *line_selected
= size
- 1;
1144 else if (*line_selected
< 0)
1146 if (toggle_sort
== 1) {
1147 update_iostream_sort(line_selected
);
1148 update_current_view();
1151 for (i
= 0; i
< size
; i
++) {
1152 if (i
== *line_selected
) {
1153 wattron(pref_panel_window
, COLOR_PAIR(5));
1154 mvwhline(pref_panel_window
, i
+ 1, 1, ' ', 30 - 2);
1156 if (iostreamtopview
[i
].sort
== 1)
1157 wattron(pref_panel_window
, A_BOLD
);
1158 mvwprintw(pref_panel_window
, i
+ 1, 1, "[-] %s",
1159 iostreamtopview
[i
].title
);
1160 wattroff(pref_panel_window
, A_BOLD
);
1161 wattroff(pref_panel_window
, COLOR_PAIR(5));
1168 void update_cpu_sort(int *line_selected
)
1173 if (*line_selected
> (size
- 1))
1174 *line_selected
= size
- 1;
1175 else if (*line_selected
< 0)
1178 /* special case, we don't support sorting by procname for now */
1179 if (*line_selected
!= 3) {
1180 if (cputopview
[*line_selected
].sort
== 1)
1181 cputopview
[*line_selected
].reverse
= 1;
1182 for (i
= 0; i
< size
; i
++)
1183 cputopview
[i
].sort
= 0;
1184 cputopview
[*line_selected
].sort
= 1;
1188 void update_cpu_pref(int *line_selected
, int toggle_view
, int toggle_sort
)
1195 if (pref_panel_window
) {
1196 del_panel(pref_panel
);
1197 delwin(pref_panel_window
);
1201 pref_panel_window
= create_window(size
+ 2, 30, 10, 10);
1202 pref_panel
= new_panel(pref_panel_window
);
1204 werase(pref_panel_window
);
1205 box(pref_panel_window
, 0 , 0);
1206 set_window_title(pref_panel_window
, "CPUTop Preferences ");
1207 wattron(pref_panel_window
, A_BOLD
);
1208 mvwprintw(pref_panel_window
, size
+ 1, 1,
1209 " 's' : sort, space : toggle");
1210 wattroff(pref_panel_window
, A_BOLD
);
1212 if (*line_selected
> (size
- 1))
1213 *line_selected
= size
- 1;
1214 else if (*line_selected
< 0)
1216 if (toggle_sort
== 1) {
1217 update_cpu_sort(line_selected
);
1218 update_current_view();
1221 for (i
= 0; i
< size
; i
++) {
1222 if (i
== *line_selected
) {
1223 wattron(pref_panel_window
, COLOR_PAIR(5));
1224 mvwhline(pref_panel_window
, i
+ 1, 1, ' ', 30 - 2);
1226 if (cputopview
[i
].sort
== 1)
1227 wattron(pref_panel_window
, A_BOLD
);
1228 mvwprintw(pref_panel_window
, i
+ 1, 1, "[-] %s",
1229 cputopview
[i
].title
);
1230 wattroff(pref_panel_window
, A_BOLD
);
1231 wattroff(pref_panel_window
, COLOR_PAIR(5));
1238 void update_perf_sort(int *line_selected
)
1241 struct perfcounter
*perf
;
1245 size
= g_hash_table_size(global_perf_liszt
);
1246 if (*line_selected
> (size
- 1))
1247 *line_selected
= size
- 1;
1248 else if (*line_selected
< 0)
1252 perflist
= g_list_first(g_hash_table_get_keys(global_perf_liszt
));
1254 perf
= g_hash_table_lookup(global_perf_liszt
, perflist
->data
);
1255 if (i
!= *line_selected
)
1260 perflist
= g_list_next(perflist
);
1264 void update_perf_pref(int *line_selected
, int toggle_view
, int toggle_sort
)
1267 struct perfcounter
*perf
;
1273 if (pref_panel_window
) {
1274 del_panel(pref_panel
);
1275 delwin(pref_panel_window
);
1277 size
= g_hash_table_size(global_perf_liszt
);
1279 pref_panel_window
= create_window(size
+ 2, 30, 10, 10);
1280 pref_panel
= new_panel(pref_panel_window
);
1282 werase(pref_panel_window
);
1283 box(pref_panel_window
, 0 , 0);
1284 set_window_title(pref_panel_window
, "Perf Preferences ");
1285 wattron(pref_panel_window
, A_BOLD
);
1286 mvwprintw(pref_panel_window
, g_hash_table_size(global_perf_liszt
) + 1, 1,
1287 " 's' : sort, space : toggle");
1288 wattroff(pref_panel_window
, A_BOLD
);
1290 if (*line_selected
> (size
- 1))
1291 *line_selected
= size
- 1;
1292 else if (*line_selected
< 0)
1295 if (toggle_sort
== 1) {
1296 update_perf_sort(line_selected
);
1297 update_current_view();
1301 perflist
= g_list_first(g_hash_table_get_keys(global_perf_liszt
));
1303 perf
= g_hash_table_lookup(global_perf_liszt
, perflist
->data
);
1304 if (i
== *line_selected
&& toggle_view
== 1) {
1305 perf
->visible
= perf
->visible
== 1 ? 0:1;
1306 update_current_view();
1308 if (i
== *line_selected
) {
1309 wattron(pref_panel_window
, COLOR_PAIR(5));
1310 mvwhline(pref_panel_window
, i
+ 1, 1, ' ', 30 - 2);
1312 if (perf
->sort
== 1)
1313 wattron(pref_panel_window
, A_BOLD
);
1314 mvwprintw(pref_panel_window
, i
+ 1, 1, "[%c] %s",
1315 perf
->visible
== 1 ? 'x' : ' ',
1316 (char *) perflist
->data
+ 5);
1317 wattroff(pref_panel_window
, A_BOLD
);
1318 wattroff(pref_panel_window
, COLOR_PAIR(5));
1320 perflist
= g_list_next(perflist
);
1326 int update_preference_panel(int *line_selected
, int toggle_view
, int toggle_sort
)
1330 switch(current_view
) {
1332 update_perf_pref(line_selected
, toggle_view
, toggle_sort
);
1335 update_cpu_pref(line_selected
, toggle_view
, toggle_sort
);
1338 update_iostream_pref(line_selected
, toggle_view
, toggle_sort
);
1340 case process_details
:
1341 update_process_detail_pref(line_selected
, toggle_view
, toggle_sort
);
1351 int update_sort(int *line_selected
)
1355 switch(current_view
) {
1357 update_perf_sort(line_selected
);
1360 update_cpu_sort(line_selected
);
1363 update_iostream_sort(line_selected
);
1365 case process_details
:
1366 update_process_detail_sort(line_selected
);
1377 void toggle_pref_panel(void)
1381 if (pref_panel_visible
) {
1382 hide_panel(pref_panel
);
1383 pref_panel_visible
= 0;
1385 ret
= update_preference_panel(&pref_line_selected
, 0, 0);
1388 show_panel(pref_panel
);
1389 pref_panel_visible
= 1;
1395 void display(unsigned int index
)
1397 last_display_index
= index
;
1398 currently_displayed_index
= index
;
1399 data
= g_ptr_array_index(copies
, index
);
1402 max_elements
= data
->process_table
->len
;
1403 update_current_view();
1409 void pause_display()
1413 sem_wait(&pause_sem
);
1416 void resume_display()
1419 print_log("Resume");
1420 sem_post(&pause_sem
);
1423 void *handle_keyboard(void *p
)
1426 while((ch
= getch())) {
1428 /* Move the cursor and scroll */
1431 if (pref_panel_visible
) {
1432 pref_line_selected
++;
1433 update_preference_panel(&pref_line_selected
, 0, 0);
1435 if (selected_line
< (max_center_lines
- 1) &&
1436 selected_line
< max_elements
- 1) {
1439 } else if (selected_in_list
< (max_elements
- 1)
1440 && (list_offset
< (max_elements
- max_center_lines
))) {
1444 update_current_view();
1451 if (pref_panel_visible
) {
1452 if (pref_line_selected
> 0)
1453 pref_line_selected
--;
1454 update_preference_panel(&pref_line_selected
, 0, 0);
1456 if (selected_line
> 0) {
1459 } else if (selected_in_list
> 0 && list_offset
> 0) {
1463 update_current_view();
1469 /* Navigate the history with arrows */
1471 if (currently_displayed_index
> 0) {
1472 currently_displayed_index
--;
1473 print_log("Going back in time");
1475 print_log("Cannot rewind, last data is already displayed");
1477 data
= g_ptr_array_index(copies
, currently_displayed_index
);
1478 max_elements
= data
->process_table
->len
;
1480 /* we force to pause the display when moving in time */
1481 if (toggle_pause
< 0)
1484 update_current_view();
1488 if (currently_displayed_index
< last_display_index
) {
1489 currently_displayed_index
++;
1490 print_log("Going forward in time");
1491 data
= g_ptr_array_index(copies
, currently_displayed_index
);
1492 max_elements
= data
->process_table
->len
;
1493 update_current_view();
1496 print_log("Manually moving forward");
1498 if (toggle_pause
> 0) {
1499 sem_post(&pause_sem
);
1500 update_current_view();
1501 sem_wait(&pause_sem
);
1507 if (pref_panel_visible
) {
1508 update_preference_panel(&pref_line_selected
, 1, 0);
1510 update_selected_processes();
1511 update_current_view();
1515 if (pref_panel_visible
)
1516 update_preference_panel(&pref_line_selected
, 0, 1);
1519 /* perf uses a hashtable, it is ordered backward */
1520 if (current_view
== perf
) {
1521 pref_current_sort
--;
1522 } else if (!pref_panel_visible
) {
1523 pref_current_sort
++;
1525 update_sort(&pref_current_sort
);
1526 update_current_view();
1529 /* perf uses a hashtable, it is ordered backward */
1530 if (current_view
== perf
) {
1531 pref_current_sort
++;
1532 } else if (!pref_panel_visible
) {
1533 pref_current_sort
--;
1535 update_sort(&pref_current_sort
);
1536 update_current_view();
1539 case 13: /* FIXME : KEY_ENTER ?? */
1540 if (pref_panel_visible
)
1542 if (current_view
!= process_details
) {
1543 previous_view
= current_view
;
1544 current_view
= process_details
;
1546 current_view
= previous_view
;
1547 previous_view
= process_details
;
1550 update_current_view();
1554 if (pref_panel_visible
)
1555 toggle_pref_panel();
1558 update_current_view();
1561 if (pref_panel_visible
)
1562 toggle_pref_panel();
1565 update_current_view();
1568 if (pref_panel_visible
)
1569 toggle_pref_panel();
1570 current_view
= perf
;
1572 update_current_view();
1575 if (pref_panel_visible
)
1576 toggle_pref_panel();
1577 current_view
= iostream
;
1579 update_current_view();
1582 if (pref_panel_visible
)
1583 toggle_pref_panel();
1584 current_view
= kprobes
;
1586 update_current_view();
1591 /* exit keyboard thread */
1595 toggle_threads
*= -1;
1596 update_current_view();
1599 if (toggle_pause
< 0) {
1606 toggle_pref_panel();
1610 update_current_view();
1612 /* ESCAPE, but slow to process, don't know why */
1614 if (pref_panel_visible
)
1615 toggle_pref_panel();
1616 else if (current_view
== process_details
) {
1617 current_view
= previous_view
;
1618 previous_view
= process_details
;
1620 update_current_view();
1624 update_current_view();
1632 void init_view_headers()
1634 cputopview
[0].title
= strdup("CPU(%)");
1635 cputopview
[0].sort
= 1;
1636 cputopview
[1].title
= strdup("PID");
1637 cputopview
[2].title
= strdup("TID");
1638 cputopview
[3].title
= strdup("VPID");
1639 cputopview
[4].title
= strdup("VTID");
1640 cputopview
[5].title
= strdup("NAME");
1642 iostreamtopview
[0].title
= strdup("R (B/sec)");
1643 iostreamtopview
[1].title
= strdup("W (B/sec)");
1644 iostreamtopview
[2].title
= strdup("Total (B)");
1645 iostreamtopview
[2].sort
= 1;
1647 fileview
[0].title
= strdup("FD");
1648 fileview
[1].title
= strdup("READ");
1649 fileview
[1].sort
= 1;
1650 fileview
[2].title
= strdup("WRITE");
1655 selected_processes
= g_ptr_array_new();
1656 sem_init(&update_display_sem
, 0, 1);
1657 init_view_headers();
1660 header
= create_window(5, COLS
- 1, 0, 0);
1661 center
= create_window(LINES
- 5 - 7, COLS
- 1, 5, 0);
1662 status
= create_window(MAX_LOG_LINES
+ 2, COLS
- 1, LINES
- 7, 0);
1663 footer
= create_window(1, COLS
- 1, LINES
- 1, 0);
1665 print_log("Starting display");
1667 main_panel
= new_panel(center
);
1674 pthread_create(&keyboard_thread
, NULL
, handle_keyboard
, (void *)NULL
);