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;
67 pthread_t keyboard_thread
;
69 struct header_view cputopview
[6];
70 struct header_view iostreamtopview
[3];
71 struct header_view fileview
[3];
72 struct header_view kprobeview
[2];
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_BLACK
, COLOR_YELLOW
); /* select line */
109 init_pair(6, COLOR_GREEN
, COLOR_BLACK
); /* selected process */
110 init_pair(7, COLOR_RED
, COLOR_YELLOW
); /* selected process + line*/
112 termtype
= getenv("TERM");
113 if (!strcmp(termtype
, "xterm") || !strcmp(termtype
, "xterm-color") ||
114 !strcmp(termtype
, "vt220")) {
115 define_key("\033[H", KEY_HOME
);
116 define_key("\033[F", KEY_END
);
117 define_key("\033OP", KEY_F(1));
118 define_key("\033OQ", KEY_F(2));
119 define_key("\033OR", KEY_F(3));
120 define_key("\033OS", KEY_F(4));
121 define_key("\0330U", KEY_F(6));
122 define_key("\033[11~", KEY_F(1));
123 define_key("\033[12~", KEY_F(2));
124 define_key("\033[13~", KEY_F(3));
125 define_key("\033[14~", KEY_F(4));
126 define_key("\033[16~", KEY_F(6));
127 define_key("\033[17;2~", KEY_F(18));
129 signal(SIGTERM
, handle_sigterm
);
130 signal(SIGINT
, handle_sigterm
);
131 mousemask(BUTTON1_CLICKED
, NULL
);
135 WINDOW
*create_window(int height
, int width
, int startx
, int starty
)
138 win
= newwin(height
, width
, startx
, starty
);
144 WINDOW
*create_window_no_border(int height
, int width
, int startx
, int starty
)
147 win
= newwin(height
, width
, startx
, starty
);
152 void print_digit(WINDOW
*win
, int digit
)
155 wattron(win
, COLOR_PAIR(1));
156 wprintw(win
, "%d", digit
);
157 wattroff(win
, COLOR_PAIR(1));
158 } else if (digit
> 0) {
159 wattron(win
, COLOR_PAIR(2));
160 wprintw(win
, "+%d", digit
);
161 wattroff(win
, COLOR_PAIR(2));
167 void print_digits(WINDOW
*win
, int first
, int second
)
170 print_digit(win
, first
);
172 print_digit(win
, second
);
176 void print_headers(int line
, char *desc
, int value
, int first
, int second
)
178 wattron(header
, A_BOLD
);
179 mvwprintw(header
, line
, 4, "%s", desc
);
180 wattroff(header
, A_BOLD
);
181 mvwprintw(header
, line
, 16, "%d", value
);
182 wmove(header
, line
, 24);
183 print_digits(header
, first
, second
);
184 wmove(header
, line
, 40);
187 void set_window_title(WINDOW
*win
, char *title
)
189 wattron(win
, A_BOLD
);
190 mvwprintw(win
, 0, 1, title
);
191 wattroff(win
, A_BOLD
);
194 void print_log(char *str
)
197 int current_line
= 1;
198 int current_char
= 1;
200 /* rotate the line buffer */
201 if (nb_log_lines
>= MAX_LOG_LINES
) {
202 tmp
= strndup(log_lines
, MAX_LINE_LENGTH
* MAX_LOG_LINES
+ MAX_LOG_LINES
);
203 tmp2
= strchr(tmp
, '\n');
204 memset(log_lines
, '\0', strlen(log_lines
));
205 strncat(log_lines
, tmp2
+ 1, strlen(tmp2
) - 1);
206 log_lines
[strlen(log_lines
)] = '\n';
207 log_lines
[strlen(log_lines
)] = '\0';
212 strncat(log_lines
, str
, MAX_LINE_LENGTH
- 1);
214 if (nb_log_lines
< MAX_LOG_LINES
)
215 log_lines
[strlen(log_lines
)] = '\n';
216 log_lines
[strlen(log_lines
)] = '\0';
220 set_window_title(status
, "Status");
221 for (i
= 0; i
< strlen(log_lines
); i
++) {
222 if (log_lines
[i
] == '\n') {
223 wmove(status
, ++current_line
, 1);
226 mvwprintw(status
, current_line
, current_char
++, "%c",
233 int process_selected(struct processtop
*process
)
235 if (lookup_filter_tid_list(process
->tid
))
240 void update_selected_processes()
242 if (process_selected(selected_process
)) {
243 remove_filter_tid_list(selected_process
->tid
);
245 add_filter_tid_list(selected_process
->tid
, selected_process
);
249 void print_key(WINDOW
*win
, char *key
, char *desc
, int toggle
)
256 wattron(win
, COLOR_PAIR(pair
));
257 wprintw(footer
, "%s", key
);
258 wattroff(win
, COLOR_PAIR(pair
));
259 wprintw(footer
, ":%s", desc
);
264 sem_wait(&update_display_sem
);
267 print_key(footer
, "F2", "CPUtop ", current_view
== cpu
);
268 print_key(footer
, "F3", "PerfTop ", current_view
== perf
);
269 print_key(footer
, "F4", "IOTop ", current_view
== iostream
);
270 print_key(footer
, "Enter", "Details ", current_view
== process_details
);
271 print_key(footer
, "Space", "Highlight ", 0);
272 print_key(footer
, "q", "Quit ", 0);
273 print_key(footer
, "r", "Pref ", 0);
274 print_key(footer
, "t", "Threads ", toggle_threads
);
275 print_key(footer
, "v", "Virt ", toggle_virt
);
276 print_key(footer
, "p", "Pause ", toggle_pause
);
279 sem_post(&update_display_sem
);
286 set_window_title(header
, "Statistics for interval [gathering data...[");
287 wattron(header
, A_BOLD
);
288 mvwprintw(header
, 1, 4, "CPUs");
289 mvwprintw(header
, 2, 4, "Threads");
290 mvwprintw(header
, 3, 4, "FDs");
291 wattroff(header
, A_BOLD
);
295 static void scale_unit(uint64_t bytes
, char *ret
)
297 if (bytes
>= 1000000000)
298 sprintf(ret
, "%" PRIu64
"G", bytes
/1000000000);
299 if (bytes
>= 1000000)
300 sprintf(ret
, "%" PRIu64
"M", bytes
/1000000);
301 else if (bytes
>= 1000)
302 sprintf(ret
, "%" PRIu64
"K", bytes
/1000);
304 sprintf(ret
, "%" PRIu64
, bytes
);
310 struct processtop
*tmp
;
313 for (i
= 0; i
< data
->process_table
->len
; i
++) {
314 tmp
= g_ptr_array_index(data
->process_table
, i
);
315 total
+= tmp
->fileread
;
316 total
+= tmp
->filewrite
;
324 struct tm start
, end
;
325 uint64_t ts_nsec_start
, ts_nsec_end
;
328 ts_nsec_start
= data
->start
% NSEC_PER_SEC
;
329 start
= format_timestamp(data
->start
);
331 ts_nsec_end
= data
->end
% NSEC_PER_SEC
;
332 end
= format_timestamp(data
->end
);
336 set_window_title(header
, "Statistics for interval ");
337 wattron(header
, A_BOLD
);
339 wprintw(header
, "[%02d:%02d:%02d.%09" PRIu64
", %02d:%02d:%02d.%09" PRIu64
"[",
340 start
.tm_hour
, start
.tm_min
, start
.tm_sec
, ts_nsec_start
,
341 end
.tm_hour
, end
.tm_min
, end
.tm_sec
, ts_nsec_end
);
342 mvwprintw(header
, 1, 4, "CPUs");
343 wattroff(header
, A_BOLD
);
344 wprintw(header
, "\t%d\t(max/cpu : %0.2f%)", data
->cpu_table
->len
,
345 100.0/data
->cpu_table
->len
);
346 print_headers(2, "Threads", data
->nbthreads
, data
->nbnewthreads
,
347 -1*(data
->nbdeadthreads
));
348 print_headers(3, "FDs", data
->nbfiles
, data
->nbnewfiles
,
349 -1*(data
->nbclosedfiles
));
350 scale_unit(total_io(), io
);
351 mvwprintw(header
, 3, 43, "%sB/sec", io
);
355 gint
sort_by_cpu_desc(gconstpointer p1
, gconstpointer p2
)
357 struct processtop
*n1
= *(struct processtop
**)p1
;
358 struct processtop
*n2
= *(struct processtop
**)p2
;
359 unsigned long totaln1
= n1
->totalcpunsec
;
360 unsigned long totaln2
= n2
->totalcpunsec
;
362 if (totaln1
< totaln2
)
364 if (totaln1
== totaln2
)
369 gint
sort_by_tid_desc(gconstpointer p1
, gconstpointer p2
)
371 struct processtop
*n1
= *(struct processtop
**)p1
;
372 struct processtop
*n2
= *(struct processtop
**)p2
;
373 unsigned long totaln1
= n1
->tid
;
374 unsigned long totaln2
= n2
->tid
;
376 if (totaln1
< totaln2
)
378 if (totaln1
== totaln2
)
383 gint
sort_by_pid_desc(gconstpointer p1
, gconstpointer p2
)
385 struct processtop
*n1
= *(struct processtop
**)p1
;
386 struct processtop
*n2
= *(struct processtop
**)p2
;
387 unsigned long totaln1
= n1
->pid
;
388 unsigned long totaln2
= n2
->pid
;
390 if (totaln1
< totaln2
)
392 if (totaln1
== totaln2
)
397 gint
sort_by_process_read_desc(gconstpointer p1
, gconstpointer p2
)
399 struct processtop
*n1
= *(struct processtop
**)p1
;
400 struct processtop
*n2
= *(struct processtop
**)p2
;
401 unsigned long totaln1
= n1
->fileread
;
402 unsigned long totaln2
= n2
->fileread
;
404 if (totaln1
< totaln2
)
406 if (totaln1
== totaln2
)
411 gint
sort_by_process_write_desc(gconstpointer p1
, gconstpointer p2
)
413 struct processtop
*n1
= *(struct processtop
**)p1
;
414 struct processtop
*n2
= *(struct processtop
**)p2
;
415 unsigned long totaln1
= n1
->filewrite
;
416 unsigned long totaln2
= n2
->filewrite
;
418 if (totaln1
< totaln2
)
420 if (totaln1
== totaln2
)
425 gint
sort_by_process_total_desc(gconstpointer p1
, gconstpointer p2
)
427 struct processtop
*n1
= *(struct processtop
**)p1
;
428 struct processtop
*n2
= *(struct processtop
**)p2
;
429 unsigned long totaln1
= n1
->totalfilewrite
+ n1
->totalfileread
;
430 unsigned long totaln2
= n2
->totalfilewrite
+ n2
->totalfileread
;
432 if (totaln1
< totaln2
)
434 if (totaln1
== totaln2
)
439 gint
sort_by_file_read_desc(gconstpointer p1
, gconstpointer p2
)
441 struct files
*n1
= *(struct files
**)p1
;
442 struct files
*n2
= *(struct files
**)p2
;
443 unsigned long totaln1
;
444 unsigned long totaln2
;
449 if (totaln1
< totaln2
)
451 if (totaln1
== totaln2
)
456 gint
sort_by_file_write_desc(gconstpointer p1
, gconstpointer p2
)
458 struct files
*n1
= *(struct files
**)p1
;
459 struct files
*n2
= *(struct files
**)p2
;
460 unsigned long totaln1
;
461 unsigned long totaln2
;
466 if (totaln1
< totaln2
)
468 if (totaln1
== totaln2
)
473 gint
sort_by_file_fd_desc(gconstpointer p1
, gconstpointer p2
)
475 struct files
*n1
= *(struct files
**)p1
;
476 struct files
*n2
= *(struct files
**)p2
;
477 unsigned long totaln1
;
478 unsigned long totaln2
;
483 if (totaln1
< totaln2
)
485 if (totaln1
== totaln2
)
490 gint
sort_by_cpu_group_by_threads_desc(gconstpointer p1
, gconstpointer p2
)
492 struct processtop
*n1
= *(struct processtop
**)p1
;
493 struct processtop
*n2
= *(struct processtop
**)p2
;
494 unsigned long totaln1
= n1
->threadstotalcpunsec
;
495 unsigned long totaln2
= n2
->threadstotalcpunsec
;
497 if (totaln1
< totaln2
)
499 if (totaln1
== totaln2
)
504 void update_kprobes_display()
507 struct kprobes
*probe
;
508 int header_offset
= 2;
509 int current_line
= 0;
511 set_window_title(center
, "Kprobes Top ");
512 wattron(center
, A_BOLD
);
514 for (i
= 0; i
< 2; i
++) {
515 if (kprobeview
[i
].sort
) {
516 wattron(center
, A_UNDERLINE
);
517 pref_current_sort
= i
;
519 mvwprintw(center
, 1, column
, "%s", kprobeview
[i
].title
);
520 wattroff(center
, A_UNDERLINE
);
523 wattroff(center
, A_BOLD
);
525 for (i
= 0; i
< data
->kprobes_table
->len
; i
++) {
527 probe
= g_ptr_array_index(data
->kprobes_table
, i
);
528 mvwprintw(center
, current_line
+ header_offset
, column
,
529 "%s", probe
->probe_name
+ 6);
531 mvwprintw(center
, current_line
+ header_offset
, column
,
537 void update_cputop_display()
540 int header_offset
= 2;
541 struct processtop
*tmp
;
542 unsigned long elapsed
;
544 int nblinedisplayed
= 0;
545 int current_line
= 0;
546 int current_row_offset
;
549 elapsed
= data
->end
- data
->start
;
550 maxcputime
= elapsed
* data
->cpu_table
->len
/ 100.0;
552 if (cputopview
[0].sort
== 1)
553 g_ptr_array_sort(data
->process_table
, sort_by_cpu_desc
);
554 else if (cputopview
[1].sort
== 1)
555 g_ptr_array_sort(data
->process_table
, sort_by_pid_desc
);
556 else if (cputopview
[2].sort
== 1)
557 g_ptr_array_sort(data
->process_table
, sort_by_tid_desc
);
558 else if (cputopview
[3].sort
== 1)
559 g_ptr_array_sort(data
->process_table
, sort_by_cpu_desc
);
561 g_ptr_array_sort(data
->process_table
, sort_by_cpu_desc
);
563 set_window_title(center
, "CPU Top");
564 wattron(center
, A_BOLD
);
566 for (i
= 0; i
< 6; i
++) {
567 if (toggle_virt
< 0 && (i
== 3 || i
== 4)) {
570 if (cputopview
[i
].sort
) {
571 wattron(center
, A_UNDERLINE
);
572 pref_current_sort
= i
;
574 mvwprintw(center
, 1, column
, cputopview
[i
].title
);
575 wattroff(center
, A_UNDERLINE
);
578 wattroff(center
, A_BOLD
);
580 max_center_lines
= LINES
- 5 - 7 - 1 - header_offset
;
582 /* iterate the process (thread) list */
583 for (i
= list_offset
; i
< data
->process_table
->len
&&
584 nblinedisplayed
< max_center_lines
; i
++) {
585 tmp
= g_ptr_array_index(data
->process_table
, i
);
586 current_row_offset
= 1;
587 if (toggle_filter
> 0 && !lookup_filter_tid_list(tmp
->tid
))
590 if (tmp
->pid
!= tmp
->tid
)
591 if (toggle_threads
== -1)
595 if (current_line
== selected_line
) {
596 selected_process
= tmp
;
597 wattron(center
, COLOR_PAIR(5));
598 mvwhline(center
, current_line
+ header_offset
, 1, ' ', COLS
-3);
600 /* filtered process */
601 if (process_selected(tmp
)) {
602 if (current_line
== selected_line
)
603 wattron(center
, COLOR_PAIR(7));
605 wattron(center
, COLOR_PAIR(6));
608 mvwprintw(center
, current_line
+ header_offset
,
609 current_row_offset
, "%1.2f",
610 tmp
->totalcpunsec
/ maxcputime
);
611 current_row_offset
+= 10;
613 mvwprintw(center
, current_line
+ header_offset
,
614 current_row_offset
, "%d", tmp
->pid
);
615 current_row_offset
+= 10;
617 mvwprintw(center
, current_line
+ header_offset
,
618 current_row_offset
, "%d", tmp
->tid
);
619 current_row_offset
+= 10;
620 if (toggle_virt
> 0) {
622 mvwprintw(center
, current_line
+ header_offset
,
623 current_row_offset
, "%d", tmp
->vpid
);
624 current_row_offset
+= 10;
626 mvwprintw(center
, current_line
+ header_offset
,
627 current_row_offset
, "%d", tmp
->vtid
);
628 current_row_offset
+= 10;
631 mvwprintw(center
, current_line
+ header_offset
,
632 current_row_offset
, "%s", tmp
->comm
);
633 wattroff(center
, COLOR_PAIR(7));
634 wattroff(center
, COLOR_PAIR(6));
635 wattroff(center
, COLOR_PAIR(5));
641 gint
sort_perf(gconstpointer p1
, gconstpointer p2
, gpointer key
)
643 struct processtop
*n1
= *(struct processtop
**) p1
;
644 struct processtop
*n2
= *(struct processtop
**) p2
;
646 struct perfcounter
*tmp1
, *tmp2
;
647 unsigned long totaln2
= 0;
648 unsigned long totaln1
= 0;
653 tmp1
= g_hash_table_lookup(n1
->perf
, key
);
657 totaln1
= tmp1
->count
;
659 tmp2
= g_hash_table_lookup(n2
->perf
, key
);
663 totaln2
= tmp2
->count
;
665 if (totaln1
< totaln2
)
667 if (totaln1
== totaln2
) {
670 if (totaln1
< totaln2
)
677 void print_key_title(char *key
, int line
)
679 wattron(center
, A_BOLD
);
680 mvwprintw(center
, line
, 1, "%s", key
);
681 mvwprintw(center
, line
, 30, " ");
682 wattroff(center
, A_BOLD
);
685 void update_process_details()
687 unsigned long elapsed
;
689 struct processtop
*tmp
;
690 struct files
*file_tmp
;
693 char filename_buf
[COLS
];
696 GPtrArray
*newfilearray
= g_ptr_array_new();
698 struct perfcounter
*perfn1
, *perfn2
;
701 set_window_title(center
, "Process details");
704 tmp
= find_process_tid(data
,
705 selected_process
->tid
,
706 selected_process
->comm
);
707 elapsed
= data
->end
- data
->start
;
708 maxcputime
= elapsed
* data
->cpu_table
->len
/ 100.0;
710 print_key_title("Name", line
++);
711 wprintw(center
, "%s", selected_process
->comm
);
712 print_key_title("TID", line
++);
713 wprintw(center
, "%d", selected_process
->tid
);
715 print_key_title("Does not exit at this time", 3);
719 print_key_title("PID", line
++);
720 wprintw(center
, "%d", tmp
->pid
);
721 print_key_title("PPID", line
++);
722 wprintw(center
, "%d", tmp
->ppid
);
723 print_key_title("VPID", line
++);
724 wprintw(center
, "%d", tmp
->vpid
);
725 print_key_title("VTID", line
++);
726 wprintw(center
, "%d", tmp
->vtid
);
727 print_key_title("VPPID", line
++);
728 wprintw(center
, "%d", tmp
->vppid
);
729 print_key_title("CPU", line
++);
730 wprintw(center
, "%1.2f %%", tmp
->totalcpunsec
/maxcputime
);
732 print_key_title("READ B/s", line
++);
733 scale_unit(tmp
->fileread
, unit
);
734 wprintw(center
, "%s", unit
);
736 print_key_title("WRITE B/s", line
++);
737 scale_unit(tmp
->filewrite
, unit
);
738 wprintw(center
, "%s", unit
);
740 g_hash_table_iter_init(&iter
, global_perf_liszt
);
741 while (g_hash_table_iter_next (&iter
, &key
, (gpointer
) &perfn1
)) {
742 print_key_title((char *) key
, line
++);
743 perfn2
= g_hash_table_lookup(tmp
->perf
, (char *) key
);
744 wprintw(center
, "%d", perfn2
? perfn2
->count
: 0);
748 wattron(center
, A_BOLD
);
750 for (i
= 0; i
< 3; i
++) {
751 if (fileview
[i
].sort
) {
752 pref_current_sort
= i
;
753 wattron(center
, A_UNDERLINE
);
755 mvwprintw(center
, line
, column
, fileview
[i
].title
);
756 wattroff(center
, A_UNDERLINE
);
759 mvwprintw(center
, line
++, column
, "FILENAME");
760 wattroff(center
, A_BOLD
);
763 * since the process_files_table array could contain NULL file structures,
764 * and that the positions inside the array is important (it is the FD), we
765 * need to create a temporary array that we can sort.
767 for (i
= 0; i
< tmp
->process_files_table
->len
; i
++) {
768 file_tmp
= g_ptr_array_index(tmp
->process_files_table
, i
);
770 g_ptr_array_add(newfilearray
, file_tmp
);
773 if (fileview
[0].sort
== 1)
774 g_ptr_array_sort(newfilearray
, sort_by_file_fd_desc
);
775 else if (fileview
[1].sort
== 1)
776 g_ptr_array_sort(newfilearray
, sort_by_file_read_desc
);
777 else if (fileview
[2].sort
== 1)
778 g_ptr_array_sort(newfilearray
, sort_by_file_write_desc
);
780 g_ptr_array_sort(newfilearray
, sort_by_file_read_desc
);
782 for (i
= selected_line
; i
< newfilearray
->len
&&
783 i
< (selected_line
+ max_center_lines
- line
+ 2); i
++) {
784 file_tmp
= g_ptr_array_index(newfilearray
, i
);
787 mvwprintw(center
, line
+ j
, 1, "%d", file_tmp
->fd
);
788 scale_unit(file_tmp
->read
, unit
);
789 mvwprintw(center
, line
+ j
, 11, "%s", unit
);
790 scale_unit(file_tmp
->write
, unit
);
791 mvwprintw(center
, line
+ j
, 21, "%s", unit
);
792 snprintf(filename_buf
, COLS
- 25, "%s", file_tmp
->name
);
793 mvwprintw(center
, line
+ j
, 31, "%s", filename_buf
);
796 g_ptr_array_free(newfilearray
, TRUE
);
802 int nblinedisplayed
= 0;
803 int current_line
= 0;
804 struct processtop
*tmp
;
805 int header_offset
= 2;
807 struct perfcounter
*perfn1
, *perfn2
;
808 char *perf_key
= NULL
;
813 set_window_title(center
, "Perf Top");
814 wattron(center
, A_BOLD
);
815 mvwprintw(center
, 1, 1, "PID");
816 mvwprintw(center
, 1, 11, "TID");
817 mvwprintw(center
, 1, 22, "NAME");
820 g_hash_table_iter_init(&iter
, global_perf_liszt
);
821 while (g_hash_table_iter_next (&iter
, &key
, (gpointer
) &perfn1
)) {
822 if (perfn1
->visible
) {
824 /* pref_current_sort = i; */
825 wattron(center
, A_UNDERLINE
);
827 /* + 5 to strip the "perf_" prefix */
828 mvwprintw(center
, 1, perf_row
, "%s",
830 wattroff(center
, A_UNDERLINE
);
834 perf_key
= (char *) key
;
837 wattroff(center
, A_BOLD
);
839 g_ptr_array_sort_with_data(data
->process_table
, sort_perf
, perf_key
);
841 for (i
= 0; i
< data
->process_table
->len
&&
842 nblinedisplayed
< max_center_lines
; i
++) {
843 tmp
= g_ptr_array_index(data
->process_table
, i
);
845 if (toggle_filter
> 0 && !lookup_filter_tid_list(tmp
->tid
))
848 if (tmp
->pid
!= tmp
->tid
)
849 if (toggle_threads
== -1)
852 if (process_selected(tmp
)) {
853 if (current_line
== selected_line
)
854 wattron(center
, COLOR_PAIR(7));
856 wattron(center
, COLOR_PAIR(6));
858 if (current_line
== selected_line
) {
859 selected_process
= tmp
;
860 wattron(center
, COLOR_PAIR(5));
861 mvwhline(center
, current_line
+ header_offset
, 1, ' ', COLS
-3);
864 mvwprintw(center
, current_line
+ header_offset
, 1, "%d", tmp
->pid
);
865 mvwprintw(center
, current_line
+ header_offset
, 11, "%d", tmp
->tid
);
866 mvwprintw(center
, current_line
+ header_offset
, 22, "%s", tmp
->comm
);
868 g_hash_table_iter_init(&iter
, global_perf_liszt
);
871 while (g_hash_table_iter_next (&iter
, &key
, (gpointer
) &perfn1
)) {
872 if (perfn1
->visible
) {
873 perfn2
= g_hash_table_lookup(tmp
->perf
, (char *) key
);
875 value
= perfn2
->count
;
878 mvwprintw(center
, current_line
+ header_offset
,
879 perf_row
, "%d", value
);
884 wattroff(center
, COLOR_PAIR(6));
885 wattroff(center
, COLOR_PAIR(5));
891 void update_iostream()
894 int header_offset
= 2;
895 struct processtop
*tmp
;
896 int nblinedisplayed
= 0;
897 int current_line
= 0;
902 set_window_title(center
, "IO Top");
903 wattron(center
, A_BOLD
);
904 mvwprintw(center
, 1, 1, "PID");
905 mvwprintw(center
, 1, 11, "TID");
906 mvwprintw(center
, 1, 22, "NAME");
908 for (i
= 0; i
< 3; i
++) {
909 if (iostreamtopview
[i
].sort
) {
910 pref_current_sort
= i
;
911 wattron(center
, A_UNDERLINE
);
913 mvwprintw(center
, 1, column
, iostreamtopview
[i
].title
);
914 wattroff(center
, A_UNDERLINE
);
917 wattroff(center
, A_BOLD
);
918 wattroff(center
, A_UNDERLINE
);
920 if (iostreamtopview
[0].sort
== 1)
921 g_ptr_array_sort(data
->process_table
, sort_by_process_read_desc
);
922 else if (iostreamtopview
[1].sort
== 1)
923 g_ptr_array_sort(data
->process_table
, sort_by_process_write_desc
);
924 else if (iostreamtopview
[2].sort
== 1)
925 g_ptr_array_sort(data
->process_table
, sort_by_process_total_desc
);
927 g_ptr_array_sort(data
->process_table
, sort_by_process_total_desc
);
929 for (i
= list_offset
; i
< data
->process_table
->len
&&
930 nblinedisplayed
< max_center_lines
; i
++) {
931 tmp
= g_ptr_array_index(data
->process_table
, i
);
933 if (toggle_filter
> 0 && !lookup_filter_tid_list(tmp
->tid
))
936 if (tmp
->pid
!= tmp
->tid
)
937 if (toggle_threads
== -1)
940 if (process_selected(tmp
)) {
941 if (current_line
== selected_line
)
942 wattron(center
, COLOR_PAIR(7));
944 wattron(center
, COLOR_PAIR(6));
946 if (current_line
== selected_line
) {
947 selected_process
= tmp
;
948 wattron(center
, COLOR_PAIR(5));
949 mvwhline(center
, current_line
+ header_offset
, 1, ' ', COLS
-3);
952 mvwprintw(center
, current_line
+ header_offset
, 1, "%d", tmp
->pid
);
954 mvwprintw(center
, current_line
+ header_offset
, 11, "%d", tmp
->tid
);
956 mvwprintw(center
, current_line
+ header_offset
, 22, "%s", tmp
->comm
);
958 /* READ (bytes/sec) */
959 scale_unit(tmp
->fileread
, unit
);
960 mvwprintw(center
, current_line
+ header_offset
, 40, "%s", unit
);
962 /* WRITE (bytes/sec) */
963 scale_unit(tmp
->filewrite
, unit
);
964 mvwprintw(center
, current_line
+ header_offset
, 52, "%s", unit
);
967 total
= tmp
->totalfileread
+ tmp
->totalfilewrite
;
969 scale_unit(total
, unit
);
970 mvwprintw(center
, current_line
+ header_offset
, 64, "%s", unit
);
972 wattroff(center
, COLOR_PAIR(6));
973 wattroff(center
, COLOR_PAIR(5));
979 void update_current_view()
981 sem_wait(&update_display_sem
);
988 switch (current_view
) {
990 update_cputop_display();
995 case process_details
:
996 update_process_details();
1002 update_cputop_display();
1005 update_kprobes_display();
1012 sem_post(&update_display_sem
);
1015 void update_process_detail_sort(int *line_selected
)
1022 if (*line_selected
> (size
- 1))
1023 *line_selected
= size
- 1;
1024 else if (*line_selected
< 0)
1027 if (fileview
[*line_selected
].sort
== 1)
1028 fileview
[*line_selected
].reverse
= 1;
1029 for (i
= 0; i
< size
; i
++)
1030 fileview
[i
].sort
= 0;
1031 fileview
[*line_selected
].sort
= 1;
1034 void update_process_detail_pref(int *line_selected
, int toggle_view
, int toggle_sort
)
1041 if (pref_panel_window
) {
1042 del_panel(pref_panel
);
1043 delwin(pref_panel_window
);
1047 pref_panel_window
= create_window(size
+ 2, 30, 10, 10);
1048 pref_panel
= new_panel(pref_panel_window
);
1050 werase(pref_panel_window
);
1051 box(pref_panel_window
, 0 , 0);
1052 set_window_title(pref_panel_window
, "Process Detail Preferences ");
1053 wattron(pref_panel_window
, A_BOLD
);
1054 mvwprintw(pref_panel_window
, size
+ 1, 1,
1055 " 's' : sort, space : toggle");
1056 wattroff(pref_panel_window
, A_BOLD
);
1058 if (*line_selected
> (size
- 1))
1059 *line_selected
= size
- 1;
1060 else if (*line_selected
< 0)
1062 if (toggle_sort
== 1) {
1063 update_process_detail_sort(line_selected
);
1064 update_current_view();
1067 for (i
= 0; i
< size
; i
++) {
1068 if (i
== *line_selected
) {
1069 wattron(pref_panel_window
, COLOR_PAIR(5));
1070 mvwhline(pref_panel_window
, i
+ 1, 1, ' ', 30 - 2);
1072 if (fileview
[i
].sort
== 1)
1073 wattron(pref_panel_window
, A_BOLD
);
1074 mvwprintw(pref_panel_window
, i
+ 1, 1, "[-] %s",
1076 wattroff(pref_panel_window
, A_BOLD
);
1077 wattroff(pref_panel_window
, COLOR_PAIR(5));
1084 void update_iostream_sort(int *line_selected
)
1090 if (*line_selected
> (size
- 1))
1091 *line_selected
= size
- 1;
1092 else if (*line_selected
< 0)
1094 if (iostreamtopview
[*line_selected
].sort
== 1)
1095 iostreamtopview
[*line_selected
].reverse
= 1;
1096 for (i
= 0; i
< size
; i
++)
1097 iostreamtopview
[i
].sort
= 0;
1098 iostreamtopview
[*line_selected
].sort
= 1;
1102 void update_iostream_pref(int *line_selected
, int toggle_view
, int toggle_sort
)
1109 if (pref_panel_window
) {
1110 del_panel(pref_panel
);
1111 delwin(pref_panel_window
);
1115 pref_panel_window
= create_window(size
+ 2, 30, 10, 10);
1116 pref_panel
= new_panel(pref_panel_window
);
1118 werase(pref_panel_window
);
1119 box(pref_panel_window
, 0 , 0);
1120 set_window_title(pref_panel_window
, "IOTop Preferences ");
1121 wattron(pref_panel_window
, A_BOLD
);
1122 mvwprintw(pref_panel_window
, size
+ 1, 1,
1123 " 's' : sort, space : toggle");
1124 wattroff(pref_panel_window
, A_BOLD
);
1126 if (*line_selected
> (size
- 1))
1127 *line_selected
= size
- 1;
1128 else if (*line_selected
< 0)
1130 if (toggle_sort
== 1) {
1131 update_iostream_sort(line_selected
);
1132 update_current_view();
1135 for (i
= 0; i
< size
; i
++) {
1136 if (i
== *line_selected
) {
1137 wattron(pref_panel_window
, COLOR_PAIR(5));
1138 mvwhline(pref_panel_window
, i
+ 1, 1, ' ', 30 - 2);
1140 if (iostreamtopview
[i
].sort
== 1)
1141 wattron(pref_panel_window
, A_BOLD
);
1142 mvwprintw(pref_panel_window
, i
+ 1, 1, "[-] %s",
1143 iostreamtopview
[i
].title
);
1144 wattroff(pref_panel_window
, A_BOLD
);
1145 wattroff(pref_panel_window
, COLOR_PAIR(5));
1152 void update_cpu_sort(int *line_selected
)
1157 if (*line_selected
> (size
- 1))
1158 *line_selected
= size
- 1;
1159 else if (*line_selected
< 0)
1162 /* special case, we don't support sorting by procname for now */
1163 if (*line_selected
!= 3) {
1164 if (cputopview
[*line_selected
].sort
== 1)
1165 cputopview
[*line_selected
].reverse
= 1;
1166 for (i
= 0; i
< size
; i
++)
1167 cputopview
[i
].sort
= 0;
1168 cputopview
[*line_selected
].sort
= 1;
1172 void update_cpu_pref(int *line_selected
, int toggle_view
, int toggle_sort
)
1179 if (pref_panel_window
) {
1180 del_panel(pref_panel
);
1181 delwin(pref_panel_window
);
1185 pref_panel_window
= create_window(size
+ 2, 30, 10, 10);
1186 pref_panel
= new_panel(pref_panel_window
);
1188 werase(pref_panel_window
);
1189 box(pref_panel_window
, 0 , 0);
1190 set_window_title(pref_panel_window
, "CPUTop Preferences ");
1191 wattron(pref_panel_window
, A_BOLD
);
1192 mvwprintw(pref_panel_window
, size
+ 1, 1,
1193 " 's' : sort, space : toggle");
1194 wattroff(pref_panel_window
, A_BOLD
);
1196 if (*line_selected
> (size
- 1))
1197 *line_selected
= size
- 1;
1198 else if (*line_selected
< 0)
1200 if (toggle_sort
== 1) {
1201 update_cpu_sort(line_selected
);
1202 update_current_view();
1205 for (i
= 0; i
< size
; i
++) {
1206 if (i
== *line_selected
) {
1207 wattron(pref_panel_window
, COLOR_PAIR(5));
1208 mvwhline(pref_panel_window
, i
+ 1, 1, ' ', 30 - 2);
1210 if (cputopview
[i
].sort
== 1)
1211 wattron(pref_panel_window
, A_BOLD
);
1212 mvwprintw(pref_panel_window
, i
+ 1, 1, "[-] %s",
1213 cputopview
[i
].title
);
1214 wattroff(pref_panel_window
, A_BOLD
);
1215 wattroff(pref_panel_window
, COLOR_PAIR(5));
1222 void update_perf_sort(int *line_selected
)
1225 struct perfcounter
*perf
;
1229 size
= g_hash_table_size(global_perf_liszt
);
1230 if (*line_selected
> (size
- 1))
1231 *line_selected
= size
- 1;
1232 else if (*line_selected
< 0)
1236 perflist
= g_list_first(g_hash_table_get_keys(global_perf_liszt
));
1238 perf
= g_hash_table_lookup(global_perf_liszt
, perflist
->data
);
1239 if (i
!= *line_selected
)
1244 perflist
= g_list_next(perflist
);
1248 void update_perf_pref(int *line_selected
, int toggle_view
, int toggle_sort
)
1251 struct perfcounter
*perf
;
1257 if (pref_panel_window
) {
1258 del_panel(pref_panel
);
1259 delwin(pref_panel_window
);
1261 size
= g_hash_table_size(global_perf_liszt
);
1263 pref_panel_window
= create_window(size
+ 2, 30, 10, 10);
1264 pref_panel
= new_panel(pref_panel_window
);
1266 werase(pref_panel_window
);
1267 box(pref_panel_window
, 0 , 0);
1268 set_window_title(pref_panel_window
, "Perf Preferences ");
1269 wattron(pref_panel_window
, A_BOLD
);
1270 mvwprintw(pref_panel_window
, g_hash_table_size(global_perf_liszt
) + 1, 1,
1271 " 's' : sort, space : toggle");
1272 wattroff(pref_panel_window
, A_BOLD
);
1274 if (*line_selected
> (size
- 1))
1275 *line_selected
= size
- 1;
1276 else if (*line_selected
< 0)
1279 if (toggle_sort
== 1) {
1280 update_perf_sort(line_selected
);
1281 update_current_view();
1285 perflist
= g_list_first(g_hash_table_get_keys(global_perf_liszt
));
1287 perf
= g_hash_table_lookup(global_perf_liszt
, perflist
->data
);
1288 if (i
== *line_selected
&& toggle_view
== 1) {
1289 perf
->visible
= perf
->visible
== 1 ? 0:1;
1290 update_current_view();
1292 if (i
== *line_selected
) {
1293 wattron(pref_panel_window
, COLOR_PAIR(5));
1294 mvwhline(pref_panel_window
, i
+ 1, 1, ' ', 30 - 2);
1296 if (perf
->sort
== 1)
1297 wattron(pref_panel_window
, A_BOLD
);
1298 mvwprintw(pref_panel_window
, i
+ 1, 1, "[%c] %s",
1299 perf
->visible
== 1 ? 'x' : ' ',
1300 (char *) perflist
->data
+ 5);
1301 wattroff(pref_panel_window
, A_BOLD
);
1302 wattroff(pref_panel_window
, COLOR_PAIR(5));
1304 perflist
= g_list_next(perflist
);
1310 int update_preference_panel(int *line_selected
, int toggle_view
, int toggle_sort
)
1314 switch(current_view
) {
1316 update_perf_pref(line_selected
, toggle_view
, toggle_sort
);
1319 update_cpu_pref(line_selected
, toggle_view
, toggle_sort
);
1322 update_iostream_pref(line_selected
, toggle_view
, toggle_sort
);
1324 case process_details
:
1325 update_process_detail_pref(line_selected
, toggle_view
, toggle_sort
);
1335 int update_sort(int *line_selected
)
1339 switch(current_view
) {
1341 update_perf_sort(line_selected
);
1344 update_cpu_sort(line_selected
);
1347 update_iostream_sort(line_selected
);
1349 case process_details
:
1350 update_process_detail_sort(line_selected
);
1361 void toggle_pref_panel(void)
1365 if (pref_panel_visible
) {
1366 hide_panel(pref_panel
);
1367 pref_panel_visible
= 0;
1369 ret
= update_preference_panel(&pref_line_selected
, 0, 0);
1372 show_panel(pref_panel
);
1373 pref_panel_visible
= 1;
1379 void display(unsigned int index
)
1381 last_display_index
= index
;
1382 currently_displayed_index
= index
;
1383 data
= g_ptr_array_index(copies
, index
);
1386 max_elements
= data
->process_table
->len
;
1387 update_current_view();
1393 void pause_display()
1397 sem_wait(&pause_sem
);
1400 void resume_display()
1403 print_log("Resume");
1404 sem_post(&pause_sem
);
1407 void *handle_keyboard(void *p
)
1410 while((ch
= getch())) {
1412 /* Move the cursor and scroll */
1415 if (pref_panel_visible
) {
1416 pref_line_selected
++;
1417 update_preference_panel(&pref_line_selected
, 0, 0);
1419 if (selected_line
< (max_center_lines
- 1) &&
1420 selected_line
< max_elements
- 1) {
1423 } else if (selected_in_list
< (max_elements
- 1)
1424 && (list_offset
< (max_elements
- max_center_lines
))) {
1428 update_current_view();
1435 if (pref_panel_visible
) {
1436 if (pref_line_selected
> 0)
1437 pref_line_selected
--;
1438 update_preference_panel(&pref_line_selected
, 0, 0);
1440 if (selected_line
> 0) {
1443 } else if (selected_in_list
> 0 && list_offset
> 0) {
1447 update_current_view();
1453 /* Navigate the history with arrows */
1455 if (currently_displayed_index
> 0) {
1456 currently_displayed_index
--;
1457 print_log("Going back in time");
1459 print_log("Cannot rewind, last data is already displayed");
1461 data
= g_ptr_array_index(copies
, currently_displayed_index
);
1462 max_elements
= data
->process_table
->len
;
1464 /* we force to pause the display when moving in time */
1465 if (toggle_pause
< 0)
1468 update_current_view();
1472 if (currently_displayed_index
< last_display_index
) {
1473 currently_displayed_index
++;
1474 print_log("Going forward in time");
1475 data
= g_ptr_array_index(copies
, currently_displayed_index
);
1476 max_elements
= data
->process_table
->len
;
1477 update_current_view();
1480 print_log("Manually moving forward");
1482 if (toggle_pause
> 0) {
1483 sem_post(&pause_sem
);
1484 update_current_view();
1485 sem_wait(&pause_sem
);
1491 if (pref_panel_visible
) {
1492 update_preference_panel(&pref_line_selected
, 1, 0);
1494 update_selected_processes();
1495 if (toggle_filter
> 0) {
1496 max_elements
= g_hash_table_size(global_filter_list
);
1497 if (selected_line
>= max_elements
)
1498 selected_line
= max_elements
- 1;
1500 update_current_view();
1504 if (pref_panel_visible
)
1505 update_preference_panel(&pref_line_selected
, 0, 1);
1508 /* perf uses a hashtable, it is ordered backward */
1509 if (current_view
== perf
) {
1510 pref_current_sort
--;
1511 } else if (!pref_panel_visible
) {
1512 pref_current_sort
++;
1514 update_sort(&pref_current_sort
);
1515 update_current_view();
1518 /* perf uses a hashtable, it is ordered backward */
1519 if (current_view
== perf
) {
1520 pref_current_sort
++;
1521 } else if (!pref_panel_visible
) {
1522 pref_current_sort
--;
1524 update_sort(&pref_current_sort
);
1525 update_current_view();
1528 case 13: /* FIXME : KEY_ENTER ?? */
1529 if (pref_panel_visible
)
1531 if (current_view
!= process_details
) {
1532 previous_view
= current_view
;
1533 current_view
= process_details
;
1535 current_view
= previous_view
;
1536 previous_view
= process_details
;
1539 update_current_view();
1543 if (pref_panel_visible
)
1544 toggle_pref_panel();
1547 update_current_view();
1550 if (pref_panel_visible
)
1551 toggle_pref_panel();
1554 update_current_view();
1557 if (pref_panel_visible
)
1558 toggle_pref_panel();
1559 current_view
= perf
;
1561 update_current_view();
1564 if (pref_panel_visible
)
1565 toggle_pref_panel();
1566 current_view
= iostream
;
1568 update_current_view();
1571 if (pref_panel_visible
)
1572 toggle_pref_panel();
1573 current_view
= kprobes
;
1575 update_current_view();
1580 /* exit keyboard thread */
1584 toggle_filter
*= -1;
1586 if (toggle_filter
> 0)
1587 max_elements
= g_hash_table_size(global_filter_list
);
1589 max_elements
= data
->process_table
->len
;
1590 update_current_view();
1593 toggle_threads
*= -1;
1594 update_current_view();
1597 if (toggle_pause
< 0) {
1604 toggle_pref_panel();
1608 update_current_view();
1610 /* ESCAPE, but slow to process, don't know why */
1612 if (pref_panel_visible
)
1613 toggle_pref_panel();
1614 else if (current_view
== process_details
) {
1615 current_view
= previous_view
;
1616 previous_view
= process_details
;
1618 update_current_view();
1622 update_current_view();
1630 void init_view_headers()
1632 cputopview
[0].title
= strdup("CPU(%)");
1633 cputopview
[0].sort
= 1;
1634 cputopview
[1].title
= strdup("PID");
1635 cputopview
[2].title
= strdup("TID");
1636 cputopview
[3].title
= strdup("VPID");
1637 cputopview
[4].title
= strdup("VTID");
1638 cputopview
[5].title
= strdup("NAME");
1640 iostreamtopview
[0].title
= strdup("R (B/sec)");
1641 iostreamtopview
[1].title
= strdup("W (B/sec)");
1642 iostreamtopview
[2].title
= strdup("Total (B)");
1643 iostreamtopview
[2].sort
= 1;
1645 fileview
[0].title
= strdup("FD");
1646 fileview
[1].title
= strdup("READ");
1647 fileview
[1].sort
= 1;
1648 fileview
[2].title
= strdup("WRITE");
1650 kprobeview
[0].title
= strdup("NAME");
1651 kprobeview
[1].title
= strdup("HIT");
1652 kprobeview
[1].sort
= 1;
1657 sem_init(&update_display_sem
, 0, 1);
1658 init_view_headers();
1661 header
= create_window(5, COLS
- 1, 0, 0);
1662 center
= create_window(LINES
- 5 - 7, COLS
- 1, 5, 0);
1663 status
= create_window(MAX_LOG_LINES
+ 2, COLS
- 1, LINES
- 7, 0);
1664 footer
= create_window(1, COLS
- 1, LINES
- 1, 0);
1666 print_log("Starting display");
1668 main_panel
= new_panel(center
);
1675 pthread_create(&keyboard_thread
, NULL
, handle_keyboard
, (void *)NULL
);