2 * Copyright (C) 2011 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;
47 int last_display_index
, currently_displayed_index
;
49 struct processtop
*selected_process
= NULL
;
52 int selected_line
= 0; /* select bar position */
53 int selected_in_list
= 0; /* selection relative to the whole list */
54 int list_offset
= 0; /* first index in the list to display (scroll) */
56 char log_lines
[MAX_LINE_LENGTH
* MAX_LOG_LINES
+ MAX_LOG_LINES
];
58 int max_elements
= 80;
60 int toggle_threads
= 1;
61 int toggle_pause
= -1;
64 GPtrArray
*selected_processes
;
66 pthread_t keyboard_thread
;
68 struct header_view cputopview
[4];
69 struct header_view iostreamtopview
[3];
70 struct header_view fileview
[3];
79 static void handle_sigterm(int signal
)
88 halfdelay(DEFAULT_DELAY
);
90 intrflush(stdscr
, false);
96 init_pair(1, COLOR_RED
, COLOR_BLACK
); /* - */
97 init_pair(2, COLOR_GREEN
, COLOR_BLACK
); /* + */
98 init_pair(3, COLOR_BLACK
, COLOR_WHITE
); /* keys */
99 init_pair(4, COLOR_WHITE
, COLOR_GREEN
); /* keys activated */
100 init_pair(5, COLOR_WHITE
, COLOR_BLUE
); /* select line */
101 init_pair(6, COLOR_WHITE
, COLOR_GREEN
); /* selected process */
103 termtype
= getenv("TERM");
104 if (!strcmp(termtype
, "xterm") || !strcmp(termtype
, "xterm-color") ||
105 !strcmp(termtype
, "vt220")) {
106 define_key("\033[H", KEY_HOME
);
107 define_key("\033[F", KEY_END
);
108 define_key("\033OP", KEY_F(1));
109 define_key("\033OQ", KEY_F(2));
110 define_key("\033OR", KEY_F(3));
111 define_key("\033OS", KEY_F(4));
112 define_key("\0330U", KEY_F(6));
113 define_key("\033[11~", KEY_F(1));
114 define_key("\033[12~", KEY_F(2));
115 define_key("\033[13~", KEY_F(3));
116 define_key("\033[14~", KEY_F(4));
117 define_key("\033[16~", KEY_F(6));
118 define_key("\033[17;2~", KEY_F(18));
120 signal(SIGTERM
, handle_sigterm
);
121 mousemask(BUTTON1_CLICKED
, NULL
);
125 WINDOW
*create_window(int height
, int width
, int startx
, int starty
)
128 win
= newwin(height
, width
, startx
, starty
);
134 WINDOW
*create_window_no_border(int height
, int width
, int startx
, int starty
)
137 win
= newwin(height
, width
, startx
, starty
);
142 void print_digit(WINDOW
*win
, int digit
)
145 wattron(win
, COLOR_PAIR(1));
146 wprintw(win
, "%d", digit
);
147 wattroff(win
, COLOR_PAIR(1));
148 } else if (digit
> 0) {
149 wattron(win
, COLOR_PAIR(2));
150 wprintw(win
, "+%d", digit
);
151 wattroff(win
, COLOR_PAIR(2));
157 void print_digits(WINDOW
*win
, int first
, int second
)
160 print_digit(win
, first
);
162 print_digit(win
, second
);
166 void print_headers(int line
, char *desc
, int value
, int first
, int second
)
168 wattron(header
, A_BOLD
);
169 mvwprintw(header
, line
, 4, "%s", desc
);
170 wattroff(header
, A_BOLD
);
171 mvwprintw(header
, line
, 16, "%d", value
);
172 wmove(header
, line
, 24);
173 print_digits(header
, first
, second
);
174 wmove(header
, line
, 40);
177 void set_window_title(WINDOW
*win
, char *title
)
179 wattron(win
, A_BOLD
);
180 mvwprintw(win
, 0, 1, title
);
181 wattroff(win
, A_BOLD
);
184 void print_log(char *str
)
187 int current_line
= 1;
188 int current_char
= 1;
190 /* rotate the line buffer */
191 if (nb_log_lines
>= MAX_LOG_LINES
) {
192 tmp
= strndup(log_lines
, MAX_LINE_LENGTH
* MAX_LOG_LINES
+ MAX_LOG_LINES
);
193 tmp2
= strchr(tmp
, '\n');
194 memset(log_lines
, '\0', strlen(log_lines
));
195 strncat(log_lines
, tmp2
+ 1, strlen(tmp2
) - 1);
196 log_lines
[strlen(log_lines
)] = '\n';
197 log_lines
[strlen(log_lines
)] = '\0';
202 strncat(log_lines
, str
, MAX_LINE_LENGTH
- 1);
204 if (nb_log_lines
< MAX_LOG_LINES
)
205 log_lines
[strlen(log_lines
)] = '\n';
206 log_lines
[strlen(log_lines
)] = '\0';
210 set_window_title(status
, "Status");
211 for (i
= 0; i
< strlen(log_lines
); i
++) {
212 if (log_lines
[i
] == '\n') {
213 wmove(status
, ++current_line
, 1);
216 mvwprintw(status
, current_line
, current_char
++, "%c",
223 int process_selected(struct processtop
*process
)
226 struct processtop
*stored_process
;
228 for (i
= 0; i
< selected_processes
->len
; i
++) {
229 stored_process
= g_ptr_array_index(selected_processes
, i
);
232 if (stored_process
->tid
== process
->tid
)
238 void update_selected_processes()
241 struct processtop
*stored_process
;
243 if (process_selected(selected_process
)) {
244 for (i
= 0; i
< selected_processes
->len
; i
++) {
245 stored_process
= g_ptr_array_index(selected_processes
, i
);
248 if (stored_process
->tid
== selected_process
->tid
)
249 g_ptr_array_remove(selected_processes
,
251 print_log("Process removed");
254 g_ptr_array_add(selected_processes
, selected_process
);
255 print_log("Process added");
259 void print_key(WINDOW
*win
, char *key
, char *desc
, int toggle
)
266 wattron(win
, COLOR_PAIR(pair
));
267 wprintw(footer
, "%s", key
);
268 wattroff(win
, COLOR_PAIR(pair
));
269 wprintw(footer
, ":%s", desc
);
274 sem_wait(&update_display_sem
);
277 print_key(footer
, "F2", "CPUtop ", current_view
== cpu
);
278 print_key(footer
, "F3", "PerfTop ", current_view
== perf
);
279 print_key(footer
, "F4", "IOTop ", current_view
== iostream
);
280 print_key(footer
, "Enter", "Details ", current_view
== process_details
);
281 print_key(footer
, "Space", "Highlight ", 0);
282 print_key(footer
, "q", "Quit ", 0);
283 print_key(footer
, "r", "Pref ", 0);
284 print_key(footer
, "t", "Threads ", toggle_threads
);
285 print_key(footer
, "p", "Pause ", toggle_pause
);
288 sem_post(&update_display_sem
);
295 set_window_title(header
, "Statistics for interval [gathering data...[");
296 wattron(header
, A_BOLD
);
297 mvwprintw(header
, 1, 4, "CPUs");
298 mvwprintw(header
, 2, 4, "Threads");
299 mvwprintw(header
, 3, 4, "FDs");
300 wattroff(header
, A_BOLD
);
304 struct tm
format_timestamp(uint64_t timestamp
)
307 uint64_t ts_sec
= 0, ts_nsec
;
311 ts_sec
+= ts_nsec
/ NSEC_PER_SEC
;
312 ts_nsec
= ts_nsec
% NSEC_PER_SEC
;
314 time_s
= (time_t) ts_sec
;
316 localtime_r(&time_s
, &tm
);
321 static void scale_unit(uint64_t bytes
, char *ret
)
323 if (bytes
>= 1000000000)
324 sprintf(ret
, "%" PRIu64
"G", bytes
/1000000000);
325 if (bytes
>= 1000000)
326 sprintf(ret
, "%" PRIu64
"M", bytes
/1000000);
327 else if (bytes
>= 1000)
328 sprintf(ret
, "%" PRIu64
"K", bytes
/1000);
330 sprintf(ret
, "%" PRIu64
, bytes
);
335 struct processtop
*tmp
;
338 for (i
= 0; i
< data
->process_table
->len
; i
++) {
339 tmp
= g_ptr_array_index(data
->process_table
, i
);
340 total
+= tmp
->fileread
;
341 total
+= tmp
->filewrite
;
349 struct tm start
, end
;
350 uint64_t ts_nsec_start
, ts_nsec_end
;
353 ts_nsec_start
= data
->start
% NSEC_PER_SEC
;
354 start
= format_timestamp(data
->start
);
356 ts_nsec_end
= data
->end
% NSEC_PER_SEC
;
357 end
= format_timestamp(data
->end
);
361 set_window_title(header
, "Statistics for interval ");
362 wattron(header
, A_BOLD
);
364 wprintw(header
, "[%02d:%02d:%02d.%09" PRIu64
", %02d:%02d:%02d.%09" PRIu64
"[",
365 start
.tm_hour
, start
.tm_min
, start
.tm_sec
, ts_nsec_start
,
366 end
.tm_hour
, end
.tm_min
, end
.tm_sec
, ts_nsec_end
);
367 mvwprintw(header
, 1, 4, "CPUs");
368 wattroff(header
, A_BOLD
);
369 wprintw(header
, "\t%d\t(max/cpu : %0.2f%)", data
->cpu_table
->len
,
370 100.0/data
->cpu_table
->len
);
371 print_headers(2, "Threads", data
->nbthreads
, data
->nbnewthreads
,
372 -1*(data
->nbdeadthreads
));
373 print_headers(3, "FDs", data
->nbfiles
, data
->nbnewfiles
,
374 -1*(data
->nbclosedfiles
));
375 scale_unit(total_io(), io
);
376 mvwprintw(header
, 3, 43, "%sB/sec", io
);
380 gint
sort_by_cpu_desc(gconstpointer p1
, gconstpointer p2
)
382 struct processtop
*n1
= *(struct processtop
**)p1
;
383 struct processtop
*n2
= *(struct processtop
**)p2
;
384 unsigned long totaln1
= n1
->totalcpunsec
;
385 unsigned long totaln2
= n2
->totalcpunsec
;
387 if (totaln1
< totaln2
)
389 if (totaln1
== totaln2
)
394 gint
sort_by_tid_desc(gconstpointer p1
, gconstpointer p2
)
396 struct processtop
*n1
= *(struct processtop
**)p1
;
397 struct processtop
*n2
= *(struct processtop
**)p2
;
398 unsigned long totaln1
= n1
->tid
;
399 unsigned long totaln2
= n2
->tid
;
401 if (totaln1
< totaln2
)
403 if (totaln1
== totaln2
)
408 gint
sort_by_pid_desc(gconstpointer p1
, gconstpointer p2
)
410 struct processtop
*n1
= *(struct processtop
**)p1
;
411 struct processtop
*n2
= *(struct processtop
**)p2
;
412 unsigned long totaln1
= n1
->pid
;
413 unsigned long totaln2
= n2
->pid
;
415 if (totaln1
< totaln2
)
417 if (totaln1
== totaln2
)
422 gint
sort_by_process_read_desc(gconstpointer p1
, gconstpointer p2
)
424 struct processtop
*n1
= *(struct processtop
**)p1
;
425 struct processtop
*n2
= *(struct processtop
**)p2
;
426 unsigned long totaln1
= n1
->fileread
;
427 unsigned long totaln2
= n2
->fileread
;
429 if (totaln1
< totaln2
)
431 if (totaln1
== totaln2
)
436 gint
sort_by_process_write_desc(gconstpointer p1
, gconstpointer p2
)
438 struct processtop
*n1
= *(struct processtop
**)p1
;
439 struct processtop
*n2
= *(struct processtop
**)p2
;
440 unsigned long totaln1
= n1
->filewrite
;
441 unsigned long totaln2
= n2
->filewrite
;
443 if (totaln1
< totaln2
)
445 if (totaln1
== totaln2
)
450 gint
sort_by_process_total_desc(gconstpointer p1
, gconstpointer p2
)
452 struct processtop
*n1
= *(struct processtop
**)p1
;
453 struct processtop
*n2
= *(struct processtop
**)p2
;
454 unsigned long totaln1
= n1
->totalfilewrite
+ n1
->totalfileread
;
455 unsigned long totaln2
= n2
->totalfilewrite
+ n2
->totalfileread
;
457 if (totaln1
< totaln2
)
459 if (totaln1
== totaln2
)
464 gint
sort_by_file_read_desc(gconstpointer p1
, gconstpointer p2
)
466 struct files
*n1
= *(struct files
**)p1
;
467 struct files
*n2
= *(struct files
**)p2
;
468 unsigned long totaln1
;
469 unsigned long totaln2
;
474 if (totaln1
< totaln2
)
476 if (totaln1
== totaln2
)
481 gint
sort_by_file_write_desc(gconstpointer p1
, gconstpointer p2
)
483 struct files
*n1
= *(struct files
**)p1
;
484 struct files
*n2
= *(struct files
**)p2
;
485 unsigned long totaln1
;
486 unsigned long totaln2
;
491 if (totaln1
< totaln2
)
493 if (totaln1
== totaln2
)
498 gint
sort_by_file_fd_desc(gconstpointer p1
, gconstpointer p2
)
500 struct files
*n1
= *(struct files
**)p1
;
501 struct files
*n2
= *(struct files
**)p2
;
502 unsigned long totaln1
;
503 unsigned long totaln2
;
508 if (totaln1
< totaln2
)
510 if (totaln1
== totaln2
)
515 gint
sort_by_cpu_group_by_threads_desc(gconstpointer p1
, gconstpointer p2
)
517 struct processtop
*n1
= *(struct processtop
**)p1
;
518 struct processtop
*n2
= *(struct processtop
**)p2
;
519 unsigned long totaln1
= n1
->threadstotalcpunsec
;
520 unsigned long totaln2
= n2
->threadstotalcpunsec
;
522 if (totaln1
< totaln2
)
524 if (totaln1
== totaln2
)
529 void update_cputop_display()
532 int header_offset
= 2;
533 struct processtop
*tmp
;
534 unsigned long elapsed
;
536 int nblinedisplayed
= 0;
537 int current_line
= 0;
540 elapsed
= data
->end
- data
->start
;
541 maxcputime
= elapsed
* data
->cpu_table
->len
/ 100.0;
543 if (cputopview
[0].sort
== 1)
544 g_ptr_array_sort(data
->process_table
, sort_by_cpu_desc
);
545 else if (cputopview
[1].sort
== 1)
546 g_ptr_array_sort(data
->process_table
, sort_by_pid_desc
);
547 else if (cputopview
[2].sort
== 1)
548 g_ptr_array_sort(data
->process_table
, sort_by_tid_desc
);
549 else if (cputopview
[3].sort
== 1)
550 g_ptr_array_sort(data
->process_table
, sort_by_cpu_desc
);
552 g_ptr_array_sort(data
->process_table
, sort_by_cpu_desc
);
554 set_window_title(center
, "CPU Top");
555 wattron(center
, A_BOLD
);
557 for (i
= 0; i
< 4; i
++) {
558 if (cputopview
[i
].sort
)
559 wattron(center
, A_UNDERLINE
);
560 mvwprintw(center
, 1, column
, cputopview
[i
].title
);
561 wattroff(center
, A_UNDERLINE
);
564 wattroff(center
, A_BOLD
);
566 max_center_lines
= LINES
- 5 - 7 - 1 - header_offset
;
568 /* iterate the process (thread) list */
569 for (i
= list_offset
; i
< data
->process_table
->len
&&
570 nblinedisplayed
< max_center_lines
; i
++) {
571 tmp
= g_ptr_array_index(data
->process_table
, i
);
572 if (tmp
->pid
!= tmp
->tid
)
573 if (toggle_threads
== -1)
576 if (process_selected(tmp
)) {
577 wattron(center
, COLOR_PAIR(6));
578 mvwhline(center
, current_line
+ header_offset
, 1, ' ', COLS
-3);
580 if (current_line
== selected_line
) {
581 selected_process
= tmp
;
582 wattron(center
, COLOR_PAIR(5));
583 mvwhline(center
, current_line
+ header_offset
, 1, ' ', COLS
-3);
586 mvwprintw(center
, current_line
+ header_offset
, 1, "%1.2f",
587 tmp
->totalcpunsec
/ maxcputime
);
589 mvwprintw(center
, current_line
+ header_offset
, 11, "%d", tmp
->pid
);
591 mvwprintw(center
, current_line
+ header_offset
, 21, "%d", tmp
->tid
);
593 mvwprintw(center
, current_line
+ header_offset
, 31, "%s", tmp
->comm
);
594 wattroff(center
, COLOR_PAIR(6));
595 wattroff(center
, COLOR_PAIR(5));
601 gint
sort_perf(gconstpointer p1
, gconstpointer p2
, gpointer key
)
603 struct processtop
*n1
= *(struct processtop
**) p1
;
604 struct processtop
*n2
= *(struct processtop
**) p2
;
606 struct perfcounter
*tmp1
, *tmp2
;
607 unsigned long totaln2
= 0;
608 unsigned long totaln1
= 0;
613 tmp1
= g_hash_table_lookup(n1
->perf
, key
);
617 totaln1
= tmp1
->count
;
619 tmp2
= g_hash_table_lookup(n2
->perf
, key
);
623 totaln2
= tmp2
->count
;
625 if (totaln1
< totaln2
)
627 if (totaln1
== totaln2
) {
630 if (totaln1
< totaln2
)
637 void print_key_title(char *key
, int line
)
639 wattron(center
, A_BOLD
);
640 mvwprintw(center
, line
, 1, "%s", key
);
641 mvwprintw(center
, line
, 30, " ");
642 wattroff(center
, A_BOLD
);
645 void update_process_details()
647 unsigned long elapsed
;
649 struct processtop
*tmp
;
650 struct files
*file_tmp
;
653 char filename_buf
[COLS
];
656 GPtrArray
*newfilearray
= g_ptr_array_new();
658 struct perfcounter
*perfn1
, *perfn2
;
661 set_window_title(center
, "Process details");
664 tmp
= find_process_tid(data
,
665 selected_process
->tid
,
666 selected_process
->comm
);
667 elapsed
= data
->end
- data
->start
;
668 maxcputime
= elapsed
* data
->cpu_table
->len
/ 100.0;
670 print_key_title("Name", line
++);
671 wprintw(center
, "%s", selected_process
->comm
);
672 print_key_title("TID", line
++);
673 wprintw(center
, "%d", selected_process
->tid
);
675 print_key_title("Does not exit at this time", 3);
679 print_key_title("PID", line
++);
680 wprintw(center
, "%d", tmp
->pid
);
681 print_key_title("PPID", line
++);
682 wprintw(center
, "%d", tmp
->ppid
);
683 print_key_title("CPU", line
++);
684 wprintw(center
, "%1.2f %%", tmp
->totalcpunsec
/maxcputime
);
686 print_key_title("READ B/s", line
++);
687 scale_unit(tmp
->fileread
, unit
);
688 wprintw(center
, "%s", unit
);
690 print_key_title("WRITE B/s", line
++);
691 scale_unit(tmp
->filewrite
, unit
);
692 wprintw(center
, "%s", unit
);
694 g_hash_table_iter_init(&iter
, global_perf_liszt
);
695 while (g_hash_table_iter_next (&iter
, &key
, (gpointer
) &perfn1
)) {
696 print_key_title((char *) key
, line
++);
697 perfn2
= g_hash_table_lookup(tmp
->perf
, (char *) key
);
698 wprintw(center
, "%d", perfn2
? perfn2
->count
: 0);
702 wattron(center
, A_BOLD
);
704 for (i
= 0; i
< 3; i
++) {
705 if (fileview
[i
].sort
)
706 wattron(center
, A_UNDERLINE
);
707 mvwprintw(center
, line
, column
, fileview
[i
].title
);
708 wattroff(center
, A_UNDERLINE
);
711 mvwprintw(center
, line
++, column
, "FILENAME");
712 wattroff(center
, A_BOLD
);
715 * since the process_files_table array could contain NULL file structures,
716 * and that the positions inside the array is important (it is the FD), we
717 * need to create a temporary array that we can sort.
719 for (i
= 0; i
< tmp
->process_files_table
->len
; i
++) {
720 file_tmp
= g_ptr_array_index(tmp
->process_files_table
, i
);
722 g_ptr_array_add(newfilearray
, file_tmp
);
725 if (fileview
[0].sort
== 1)
726 g_ptr_array_sort(newfilearray
, sort_by_file_fd_desc
);
727 else if (fileview
[1].sort
== 1)
728 g_ptr_array_sort(newfilearray
, sort_by_file_read_desc
);
729 else if (fileview
[2].sort
== 1)
730 g_ptr_array_sort(newfilearray
, sort_by_file_write_desc
);
732 g_ptr_array_sort(newfilearray
, sort_by_file_read_desc
);
734 for (i
= selected_line
; i
< newfilearray
->len
&&
735 i
< (selected_line
+ max_center_lines
- line
+ 2); i
++) {
736 file_tmp
= g_ptr_array_index(newfilearray
, i
);
739 mvwprintw(center
, line
+ j
, 1, "%d", file_tmp
->fd
);
740 scale_unit(file_tmp
->read
, unit
);
741 mvwprintw(center
, line
+ j
, 11, "%s", unit
);
742 scale_unit(file_tmp
->write
, unit
);
743 mvwprintw(center
, line
+ j
, 21, "%s", unit
);
744 snprintf(filename_buf
, COLS
- 25, "%s", file_tmp
->name
);
745 mvwprintw(center
, line
+ j
, 31, "%s", filename_buf
);
748 g_ptr_array_free(newfilearray
, TRUE
);
754 int nblinedisplayed
= 0;
755 int current_line
= 0;
756 struct processtop
*tmp
;
757 int header_offset
= 2;
759 struct perfcounter
*perfn1
, *perfn2
;
760 char *perf_key
= NULL
;
765 set_window_title(center
, "Perf Top");
766 wattron(center
, A_BOLD
);
767 mvwprintw(center
, 1, 1, "PID");
768 mvwprintw(center
, 1, 11, "TID");
769 mvwprintw(center
, 1, 22, "NAME");
772 g_hash_table_iter_init(&iter
, global_perf_liszt
);
773 while (g_hash_table_iter_next (&iter
, &key
, (gpointer
) &perfn1
)) {
774 if (perfn1
->visible
) {
775 /* + 5 to strip the "perf_" prefix */
776 mvwprintw(center
, 1, perf_row
, "%s",
781 perf_key
= (char *) key
;
785 wattroff(center
, A_BOLD
);
787 g_ptr_array_sort_with_data(data
->process_table
, sort_perf
, perf_key
);
789 for (i
= 0; i
< data
->process_table
->len
&&
790 nblinedisplayed
< max_center_lines
; i
++) {
791 tmp
= g_ptr_array_index(data
->process_table
, i
);
792 if (tmp
->pid
!= tmp
->tid
)
793 if (toggle_threads
== -1)
796 if (process_selected(tmp
)) {
797 wattron(center
, COLOR_PAIR(6));
798 mvwhline(center
, current_line
+ header_offset
, 1, ' ', COLS
-3);
800 if (current_line
== selected_line
) {
801 selected_process
= tmp
;
802 wattron(center
, COLOR_PAIR(5));
803 mvwhline(center
, current_line
+ header_offset
, 1, ' ', COLS
-3);
806 mvwprintw(center
, current_line
+ header_offset
, 1, "%d", tmp
->pid
);
807 mvwprintw(center
, current_line
+ header_offset
, 11, "%d", tmp
->tid
);
808 mvwprintw(center
, current_line
+ header_offset
, 22, "%s", tmp
->comm
);
810 g_hash_table_iter_init(&iter
, global_perf_liszt
);
813 while (g_hash_table_iter_next (&iter
, &key
, (gpointer
) &perfn1
)) {
814 if (perfn1
->visible
) {
815 perfn2
= g_hash_table_lookup(tmp
->perf
, (char *) key
);
817 value
= perfn2
->count
;
820 mvwprintw(center
, current_line
+ header_offset
,
821 perf_row
, "%d", value
);
826 wattroff(center
, COLOR_PAIR(6));
827 wattroff(center
, COLOR_PAIR(5));
833 void update_iostream()
836 int header_offset
= 2;
837 struct processtop
*tmp
;
838 int nblinedisplayed
= 0;
839 int current_line
= 0;
844 set_window_title(center
, "IO Top");
845 wattron(center
, A_BOLD
);
846 mvwprintw(center
, 1, 1, "PID");
847 mvwprintw(center
, 1, 11, "TID");
848 mvwprintw(center
, 1, 22, "NAME");
850 for (i
= 0; i
< 3; i
++) {
851 if (iostreamtopview
[i
].sort
)
852 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_pref(int *line_selected
, int toggle_view
, int toggle_sort
)
953 if (pref_panel_window
) {
954 del_panel(pref_panel
);
955 delwin(pref_panel_window
);
959 pref_panel_window
= create_window(size
+ 2, 30, 10, 10);
960 pref_panel
= new_panel(pref_panel_window
);
962 werase(pref_panel_window
);
963 box(pref_panel_window
, 0 , 0);
964 set_window_title(pref_panel_window
, "Process Detail Preferences ");
965 wattron(pref_panel_window
, A_BOLD
);
966 mvwprintw(pref_panel_window
, size
+ 1, 1,
967 " 's' : sort, space : toggle");
968 wattroff(pref_panel_window
, A_BOLD
);
970 if (*line_selected
> (size
- 1))
971 *line_selected
= size
- 1;
972 if (toggle_sort
== 1) {
973 if (fileview
[*line_selected
].sort
== 1)
974 fileview
[*line_selected
].reverse
= 1;
975 for (i
= 0; i
< size
; i
++)
976 fileview
[i
].sort
= 0;
977 fileview
[*line_selected
].sort
= 1;
978 update_current_view();
981 for (i
= 0; i
< size
; i
++) {
982 if (i
== *line_selected
) {
983 wattron(pref_panel_window
, COLOR_PAIR(5));
984 mvwhline(pref_panel_window
, i
+ 1, 1, ' ', 30 - 2);
986 if (fileview
[i
].sort
== 1)
987 wattron(pref_panel_window
, A_BOLD
);
988 mvwprintw(pref_panel_window
, i
+ 1, 1, "[-] %s",
990 wattroff(pref_panel_window
, A_BOLD
);
991 wattroff(pref_panel_window
, COLOR_PAIR(5));
998 void update_iostream_pref(int *line_selected
, int toggle_view
, int toggle_sort
)
1005 if (pref_panel_window
) {
1006 del_panel(pref_panel
);
1007 delwin(pref_panel_window
);
1011 pref_panel_window
= create_window(size
+ 2, 30, 10, 10);
1012 pref_panel
= new_panel(pref_panel_window
);
1014 werase(pref_panel_window
);
1015 box(pref_panel_window
, 0 , 0);
1016 set_window_title(pref_panel_window
, "IOTop Preferences ");
1017 wattron(pref_panel_window
, A_BOLD
);
1018 mvwprintw(pref_panel_window
, size
+ 1, 1,
1019 " 's' : sort, space : toggle");
1020 wattroff(pref_panel_window
, A_BOLD
);
1022 if (*line_selected
> (size
- 1))
1023 *line_selected
= size
- 1;
1024 if (toggle_sort
== 1) {
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;
1030 update_current_view();
1033 for (i
= 0; i
< size
; i
++) {
1034 if (i
== *line_selected
) {
1035 wattron(pref_panel_window
, COLOR_PAIR(5));
1036 mvwhline(pref_panel_window
, i
+ 1, 1, ' ', 30 - 2);
1038 if (iostreamtopview
[i
].sort
== 1)
1039 wattron(pref_panel_window
, A_BOLD
);
1040 mvwprintw(pref_panel_window
, i
+ 1, 1, "[-] %s",
1041 iostreamtopview
[i
].title
);
1042 wattroff(pref_panel_window
, A_BOLD
);
1043 wattroff(pref_panel_window
, COLOR_PAIR(5));
1050 void update_cpu_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
, "CPUTop 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 if (toggle_sort
== 1) {
1077 /* special case, we don't support sorting by procname for now */
1078 if (*line_selected
!= 3) {
1079 if (cputopview
[*line_selected
].sort
== 1)
1080 cputopview
[*line_selected
].reverse
= 1;
1081 for (i
= 0; i
< size
; i
++)
1082 cputopview
[i
].sort
= 0;
1083 cputopview
[*line_selected
].sort
= 1;
1084 update_current_view();
1088 for (i
= 0; i
< size
; i
++) {
1089 if (i
== *line_selected
) {
1090 wattron(pref_panel_window
, COLOR_PAIR(5));
1091 mvwhline(pref_panel_window
, i
+ 1, 1, ' ', 30 - 2);
1093 if (cputopview
[i
].sort
== 1)
1094 wattron(pref_panel_window
, A_BOLD
);
1095 mvwprintw(pref_panel_window
, i
+ 1, 1, "[-] %s",
1096 cputopview
[i
].title
);
1097 wattroff(pref_panel_window
, A_BOLD
);
1098 wattroff(pref_panel_window
, COLOR_PAIR(5));
1105 void update_perf_pref(int *line_selected
, int toggle_view
, int toggle_sort
)
1108 struct perfcounter
*perf
;
1114 if (pref_panel_window
) {
1115 del_panel(pref_panel
);
1116 delwin(pref_panel_window
);
1118 size
= g_hash_table_size(global_perf_liszt
);
1120 pref_panel_window
= create_window(size
+ 2, 30, 10, 10);
1121 pref_panel
= new_panel(pref_panel_window
);
1123 werase(pref_panel_window
);
1124 box(pref_panel_window
, 0 , 0);
1125 set_window_title(pref_panel_window
, "Perf Preferences ");
1126 wattron(pref_panel_window
, A_BOLD
);
1127 mvwprintw(pref_panel_window
, g_hash_table_size(global_perf_liszt
) + 1, 1,
1128 " 's' : sort, space : toggle");
1129 wattroff(pref_panel_window
, A_BOLD
);
1131 if (toggle_sort
== 1) {
1133 perflist
= g_list_first(g_hash_table_get_keys(global_perf_liszt
));
1135 perf
= g_hash_table_lookup(global_perf_liszt
, perflist
->data
);
1136 if (i
!= *line_selected
)
1141 perflist
= g_list_next(perflist
);
1143 update_current_view();
1147 perflist
= g_list_first(g_hash_table_get_keys(global_perf_liszt
));
1149 perf
= g_hash_table_lookup(global_perf_liszt
, perflist
->data
);
1150 if (i
== *line_selected
&& toggle_view
== 1) {
1151 perf
->visible
= perf
->visible
== 1 ? 0:1;
1152 update_current_view();
1154 if (i
== *line_selected
) {
1155 wattron(pref_panel_window
, COLOR_PAIR(5));
1156 mvwhline(pref_panel_window
, i
+ 1, 1, ' ', 30 - 2);
1158 if (perf
->sort
== 1)
1159 wattron(pref_panel_window
, A_BOLD
);
1160 mvwprintw(pref_panel_window
, i
+ 1, 1, "[%c] %s",
1161 perf
->visible
== 1 ? 'x' : ' ',
1162 (char *) perflist
->data
+ 5);
1163 wattroff(pref_panel_window
, A_BOLD
);
1164 wattroff(pref_panel_window
, COLOR_PAIR(5));
1166 perflist
= g_list_next(perflist
);
1172 int update_preference_panel(int *line_selected
, int toggle_view
, int toggle_sort
)
1176 switch(current_view
) {
1178 update_perf_pref(line_selected
, toggle_view
, toggle_sort
);
1181 update_cpu_pref(line_selected
, toggle_view
, toggle_sort
);
1184 update_iostream_pref(line_selected
, toggle_view
, toggle_sort
);
1186 case process_details
:
1187 update_process_detail_pref(line_selected
, toggle_view
, toggle_sort
);
1197 void toggle_pref_panel(void)
1201 if (pref_panel_visible
) {
1202 hide_panel(pref_panel
);
1203 pref_panel_visible
= 0;
1205 ret
= update_preference_panel(&pref_line_selected
, 0, 0);
1208 show_panel(pref_panel
);
1209 pref_panel_visible
= 1;
1215 void display(unsigned int index
)
1217 last_display_index
= index
;
1218 currently_displayed_index
= index
;
1219 data
= g_ptr_array_index(copies
, index
);
1222 max_elements
= data
->process_table
->len
;
1223 update_current_view();
1229 void pause_display()
1233 sem_wait(&pause_sem
);
1236 void resume_display()
1239 print_log("Resume");
1240 sem_post(&pause_sem
);
1243 void *handle_keyboard(void *p
)
1246 while((ch
= getch())) {
1248 /* Move the cursor and scroll */
1251 if (pref_panel_visible
) {
1252 pref_line_selected
++;
1253 update_preference_panel(&pref_line_selected
, 0, 0);
1255 if (selected_line
< (max_center_lines
- 1) &&
1256 selected_line
< max_elements
- 1) {
1259 } else if (selected_in_list
< (max_elements
- 1)
1260 && (list_offset
< (max_elements
- max_center_lines
))) {
1264 update_current_view();
1271 if (pref_panel_visible
) {
1272 if (pref_line_selected
> 0)
1273 pref_line_selected
--;
1274 update_preference_panel(&pref_line_selected
, 0, 0);
1276 if (selected_line
> 0) {
1279 } else if (selected_in_list
> 0 && list_offset
> 0) {
1283 update_current_view();
1289 /* Navigate the history with arrows */
1291 if (currently_displayed_index
> 0) {
1292 currently_displayed_index
--;
1293 print_log("Going back in time");
1295 print_log("Cannot rewind, last data is already displayed");
1297 data
= g_ptr_array_index(copies
, currently_displayed_index
);
1298 max_elements
= data
->process_table
->len
;
1300 /* we force to pause the display when moving in time */
1301 if (toggle_pause
< 0)
1304 update_current_view();
1308 if (currently_displayed_index
< last_display_index
) {
1309 currently_displayed_index
++;
1310 print_log("Going forward in time");
1311 data
= g_ptr_array_index(copies
, currently_displayed_index
);
1312 max_elements
= data
->process_table
->len
;
1313 update_current_view();
1316 print_log("Manually moving forward");
1318 if (toggle_pause
> 0) {
1319 sem_post(&pause_sem
);
1320 update_current_view();
1321 sem_wait(&pause_sem
);
1327 if (pref_panel_visible
) {
1328 update_preference_panel(&pref_line_selected
, 1, 0);
1330 update_selected_processes();
1331 update_current_view();
1335 if (pref_panel_visible
)
1336 update_preference_panel(&pref_line_selected
, 0, 1);
1339 case 13: /* FIXME : KEY_ENTER ?? */
1340 if (pref_panel_visible
)
1342 if (current_view
!= process_details
) {
1343 previous_view
= current_view
;
1344 current_view
= process_details
;
1346 current_view
= previous_view
;
1347 previous_view
= process_details
;
1349 update_current_view();
1353 if (pref_panel_visible
)
1354 toggle_pref_panel();
1357 update_current_view();
1360 if (pref_panel_visible
)
1361 toggle_pref_panel();
1364 update_current_view();
1367 if (pref_panel_visible
)
1368 toggle_pref_panel();
1369 current_view
= perf
;
1371 update_current_view();
1374 if (pref_panel_visible
)
1375 toggle_pref_panel();
1376 current_view
= iostream
;
1378 update_current_view();
1385 toggle_threads
*= -1;
1386 update_current_view();
1389 if (toggle_pause
< 0) {
1396 toggle_pref_panel();
1398 /* ESCAPE, but slow to process, don't know why */
1400 if (pref_panel_visible
)
1401 toggle_pref_panel();
1402 else if (current_view
== process_details
) {
1403 current_view
= previous_view
;
1404 previous_view
= process_details
;
1406 update_current_view();
1410 update_current_view();
1418 void init_view_headers()
1420 cputopview
[0].title
= strdup("CPU(%)");
1421 cputopview
[0].sort
= 1;
1422 cputopview
[1].title
= strdup("TGID");
1423 cputopview
[2].title
= strdup("PID");
1424 cputopview
[3].title
= strdup("NAME");
1426 iostreamtopview
[0].title
= strdup("R (B/sec)");
1427 iostreamtopview
[1].title
= strdup("W (B/sec)");
1428 iostreamtopview
[2].title
= strdup("Total (B)");
1429 iostreamtopview
[2].sort
= 1;
1431 fileview
[0].title
= strdup("FD");
1432 fileview
[1].title
= strdup("READ");
1433 fileview
[1].sort
= 1;
1434 fileview
[2].title
= strdup("WRITE");
1439 selected_processes
= g_ptr_array_new();
1440 sem_init(&update_display_sem
, 0, 1);
1441 init_view_headers();
1444 header
= create_window(5, COLS
- 1, 0, 0);
1445 center
= create_window(LINES
- 5 - 7, COLS
- 1, 5, 0);
1446 status
= create_window(MAX_LOG_LINES
+ 2, COLS
- 1, LINES
- 7, 0);
1447 footer
= create_window(1, COLS
- 1, LINES
- 1, 0);
1449 print_log("Starting display");
1451 main_panel
= new_panel(center
);
1458 pthread_create(&keyboard_thread
, NULL
, handle_keyboard
, (void *)NULL
);