Add Mapping for j/k to up/down
[lttngtop.git] / src / cursesdisplay.c
1 /*
2 * Copyright (C) 2011 Julien Desfossez
3 *
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;
7 *
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.
12 *
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.
16 */
17
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <signal.h>
21 #include <string.h>
22 #include <ncurses.h>
23 #include <panel.h>
24 #include <pthread.h>
25 #include <semaphore.h>
26
27 #include "cursesdisplay.h"
28 #include "lttngtoptypes.h"
29 #include "iostreamtop.h"
30 #include "common.h"
31
32 #define DEFAULT_DELAY 15
33 #define MAX_LINE_LENGTH 50
34 #define MAX_LOG_LINES 4
35
36 /* to prevent concurrent updates of the different windows */
37 sem_t update_display_sem;
38
39 char *termtype;
40 WINDOW *footer, *header, *center, *status;
41 WINDOW *pref_panel_window = NULL;
42 PANEL *pref_panel, *main_panel;
43
44 int pref_panel_visible = 0;
45 int pref_line_selected = 0;
46
47 int last_display_index, currently_displayed_index;
48
49 struct processtop *selected_process = NULL;
50 int selected_ret;
51
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) */
55 int nb_log_lines = 0;
56 char log_lines[MAX_LINE_LENGTH * MAX_LOG_LINES + MAX_LOG_LINES];
57
58 int max_elements = 80;
59
60 int toggle_threads = -1;
61 int toggle_pause = -1;
62
63 int max_center_lines;
64 GPtrArray *selected_processes;
65
66 pthread_t keyboard_thread;
67
68 struct header_view cputopview[4];
69 struct header_view iostreamtopview[3];
70 struct header_view fileview[3];
71
72 void reset_ncurses()
73 {
74 curs_set(1);
75 endwin();
76 exit(0);
77 }
78
79 static void handle_sigterm(int signal)
80 {
81 reset_ncurses();
82 }
83
84 void init_screen()
85 {
86 initscr();
87 noecho();
88 halfdelay(DEFAULT_DELAY);
89 nonl();
90 intrflush(stdscr, false);
91 keypad(stdscr, true);
92 curs_set(0);
93
94 if (has_colors()) {
95 start_color();
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 */
102 }
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));
119 }
120 signal(SIGTERM, handle_sigterm);
121 mousemask(BUTTON1_CLICKED, NULL);
122 refresh();
123 }
124
125 WINDOW *create_window(int height, int width, int startx, int starty)
126 {
127 WINDOW *win;
128 win = newwin(height, width, startx, starty);
129 box(win, 0 , 0);
130 wrefresh(win);
131 return win;
132 }
133
134 WINDOW *create_window_no_border(int height, int width, int startx, int starty)
135 {
136 WINDOW *win;
137 win = newwin(height, width, startx, starty);
138 wrefresh(win);
139 return win;
140 }
141
142 void print_digit(WINDOW *win, int digit)
143 {
144 if (digit < 0) {
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));
152 } else {
153 wprintw(win, "0");
154 }
155 }
156
157 void print_digits(WINDOW *win, int first, int second)
158 {
159 wprintw(win, "(");
160 print_digit(win, first);
161 wprintw(win, ", ");
162 print_digit(win, second);
163 wprintw(win, ")");
164 }
165
166 void print_headers(int line, char *desc, int value, int first, int second)
167 {
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);
175 }
176
177 void set_window_title(WINDOW *win, char *title)
178 {
179 wattron(win, A_BOLD);
180 mvwprintw(win, 0, 1, title);
181 wattroff(win, A_BOLD);
182 }
183
184 void print_log(char *str)
185 {
186 int i;
187 int current_line = 1;
188 int current_char = 1;
189 char *tmp, *tmp2;
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';
198 free(tmp);
199 }
200 nb_log_lines++;
201
202 strncat(log_lines, str, MAX_LINE_LENGTH - 1);
203
204 if (nb_log_lines < MAX_LOG_LINES)
205 log_lines[strlen(log_lines)] = '\n';
206 log_lines[strlen(log_lines)] = '\0';
207
208 werase(status);
209 box(status, 0 , 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);
214 current_char = 1;
215 } else {
216 mvwprintw(status, current_line, current_char++, "%c",
217 log_lines[i]);
218 }
219 }
220 wrefresh(status);
221 }
222
223 int process_selected(struct processtop *process)
224 {
225 int i;
226 struct processtop *stored_process;
227
228 for (i = 0; i < selected_processes->len; i++) {
229 stored_process = g_ptr_array_index(selected_processes, i);
230 if (!stored_process)
231 return 0;
232 if (stored_process->tid == process->tid)
233 return 1;
234 }
235 return 0;
236 }
237
238 void update_selected_processes()
239 {
240 if (process_selected(selected_process)) {
241 g_ptr_array_remove(selected_processes, selected_process);
242 print_log("Process removed");
243 } else {
244 g_ptr_array_add(selected_processes, selected_process);
245 print_log("Process added");
246 }
247 }
248
249 void print_key(WINDOW *win, char *key, char *desc, int toggle)
250 {
251 int pair;
252 if (toggle > 0)
253 pair = 4;
254 else
255 pair = 3;
256 wattron(win, COLOR_PAIR(pair));
257 wprintw(footer, "%s", key);
258 wattroff(win, COLOR_PAIR(pair));
259 wprintw(footer, ":%s", desc);
260 }
261
262 void update_footer()
263 {
264 sem_wait(&update_display_sem);
265 werase(footer);
266 wmove(footer, 1, 1);
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, "P", "Perf Pref ", 0);
274 print_key(footer, "p", "Pause ", toggle_pause);
275
276 wrefresh(footer);
277 sem_post(&update_display_sem);
278 }
279
280 void basic_header()
281 {
282 werase(header);
283 box(header, 0 , 0);
284 set_window_title(header, "Statistics for interval [gathering data...[");
285 wattron(header, A_BOLD);
286 mvwprintw(header, 1, 4, "CPUs");
287 mvwprintw(header, 2, 4, "Threads");
288 mvwprintw(header, 3, 4, "FDs");
289 wattroff(header, A_BOLD);
290 wrefresh(header);
291 }
292
293 struct tm format_timestamp(uint64_t timestamp)
294 {
295 struct tm tm;
296 uint64_t ts_sec = 0, ts_nsec;
297 time_t time_s;
298
299 ts_nsec = timestamp;
300 ts_sec += ts_nsec / NSEC_PER_SEC;
301 ts_nsec = ts_nsec % NSEC_PER_SEC;
302
303 time_s = (time_t) ts_sec;
304
305 localtime_r(&time_s, &tm);
306
307 return tm;
308 }
309
310 static void scale_unit(uint64_t bytes, char *ret)
311 {
312 if (bytes >= 1000000000)
313 sprintf(ret, "%" PRIu64 "G", bytes/1000000000);
314 if (bytes >= 1000000)
315 sprintf(ret, "%" PRIu64 "M", bytes/1000000);
316 else if (bytes >= 1000)
317 sprintf(ret, "%" PRIu64 "K", bytes/1000);
318 else
319 sprintf(ret, "%" PRIu64, bytes);
320 }
321 uint64_t total_io()
322 {
323 int i;
324 struct processtop *tmp;
325 uint64_t total = 0;
326
327 for (i = 0; i < data->process_table->len; i++) {
328 tmp = g_ptr_array_index(data->process_table, i);
329 total += tmp->fileread;
330 total += tmp->filewrite;
331 }
332
333 return total;
334 }
335
336 void update_header()
337 {
338 struct tm start, end;
339 uint64_t ts_nsec_start, ts_nsec_end;
340 char io[4];
341
342 ts_nsec_start = data->start % NSEC_PER_SEC;
343 start = format_timestamp(data->start);
344
345 ts_nsec_end = data->end % NSEC_PER_SEC;
346 end = format_timestamp(data->end);
347
348 werase(header);
349 box(header, 0 , 0);
350 set_window_title(header, "Statistics for interval ");
351 wattron(header, A_BOLD);
352
353 wprintw(header, "[%02d:%02d:%02d.%09" PRIu64 ", %02d:%02d:%02d.%09" PRIu64 "[",
354 start.tm_hour, start.tm_min, start.tm_sec, ts_nsec_start,
355 end.tm_hour, end.tm_min, end.tm_sec, ts_nsec_end);
356 mvwprintw(header, 1, 4, "CPUs");
357 wattroff(header, A_BOLD);
358 wprintw(header, "\t%d\t(max/cpu : %0.2f%)", data->cpu_table->len,
359 100.0/data->cpu_table->len);
360 print_headers(2, "Threads", data->nbthreads, data->nbnewthreads,
361 -1*(data->nbdeadthreads));
362 print_headers(3, "FDs", data->nbfiles, data->nbnewfiles,
363 -1*(data->nbclosedfiles));
364 scale_unit(total_io(), io);
365 mvwprintw(header, 3, 43, "%sB/sec", io);
366 wrefresh(header);
367 }
368
369 gint sort_by_cpu_desc(gconstpointer p1, gconstpointer p2)
370 {
371 struct processtop *n1 = *(struct processtop **)p1;
372 struct processtop *n2 = *(struct processtop **)p2;
373 unsigned long totaln1 = n1->totalcpunsec;
374 unsigned long totaln2 = n2->totalcpunsec;
375
376 if (totaln1 < totaln2)
377 return 1;
378 if (totaln1 == totaln2)
379 return 0;
380 return -1;
381 }
382
383 gint sort_by_tid_desc(gconstpointer p1, gconstpointer p2)
384 {
385 struct processtop *n1 = *(struct processtop **)p1;
386 struct processtop *n2 = *(struct processtop **)p2;
387 unsigned long totaln1 = n1->tid;
388 unsigned long totaln2 = n2->tid;
389
390 if (totaln1 < totaln2)
391 return 1;
392 if (totaln1 == totaln2)
393 return 0;
394 return -1;
395 }
396
397 gint sort_by_pid_desc(gconstpointer p1, gconstpointer p2)
398 {
399 struct processtop *n1 = *(struct processtop **)p1;
400 struct processtop *n2 = *(struct processtop **)p2;
401 unsigned long totaln1 = n1->pid;
402 unsigned long totaln2 = n2->pid;
403
404 if (totaln1 < totaln2)
405 return 1;
406 if (totaln1 == totaln2)
407 return 0;
408 return -1;
409 }
410
411 gint sort_by_process_read_desc(gconstpointer p1, gconstpointer p2)
412 {
413 struct processtop *n1 = *(struct processtop **)p1;
414 struct processtop *n2 = *(struct processtop **)p2;
415 unsigned long totaln1 = n1->fileread;
416 unsigned long totaln2 = n2->fileread;
417
418 if (totaln1 < totaln2)
419 return 1;
420 if (totaln1 == totaln2)
421 return 0;
422 return -1;
423 }
424
425 gint sort_by_process_write_desc(gconstpointer p1, gconstpointer p2)
426 {
427 struct processtop *n1 = *(struct processtop **)p1;
428 struct processtop *n2 = *(struct processtop **)p2;
429 unsigned long totaln1 = n1->filewrite;
430 unsigned long totaln2 = n2->filewrite;
431
432 if (totaln1 < totaln2)
433 return 1;
434 if (totaln1 == totaln2)
435 return 0;
436 return -1;
437 }
438
439 gint sort_by_process_total_desc(gconstpointer p1, gconstpointer p2)
440 {
441 struct processtop *n1 = *(struct processtop **)p1;
442 struct processtop *n2 = *(struct processtop **)p2;
443 unsigned long totaln1 = n1->filewrite + n1->fileread;
444 unsigned long totaln2 = n2->filewrite + n2->fileread;
445
446 if (totaln1 < totaln2)
447 return 1;
448 if (totaln1 == totaln2)
449 return 0;
450 return -1;
451 }
452
453 gint sort_by_file_read_desc(gconstpointer p1, gconstpointer p2)
454 {
455 struct files *n1 = *(struct files **)p1;
456 struct files *n2 = *(struct files **)p2;
457 unsigned long totaln1;
458 unsigned long totaln2;
459
460 totaln1 = n1->read;
461 totaln2 = n2->read;
462
463 if (totaln1 < totaln2)
464 return 1;
465 if (totaln1 == totaln2)
466 return 0;
467 return -1;
468 }
469
470 gint sort_by_file_write_desc(gconstpointer p1, gconstpointer p2)
471 {
472 struct files *n1 = *(struct files **)p1;
473 struct files *n2 = *(struct files **)p2;
474 unsigned long totaln1;
475 unsigned long totaln2;
476
477 totaln1 = n1->write;
478 totaln2 = n2->write;
479
480 if (totaln1 < totaln2)
481 return 1;
482 if (totaln1 == totaln2)
483 return 0;
484 return -1;
485 }
486
487 gint sort_by_file_fd_desc(gconstpointer p1, gconstpointer p2)
488 {
489 struct files *n1 = *(struct files **)p1;
490 struct files *n2 = *(struct files **)p2;
491 unsigned long totaln1;
492 unsigned long totaln2;
493
494 totaln1 = n1->fd;
495 totaln2 = n2->fd;
496
497 if (totaln1 < totaln2)
498 return 1;
499 if (totaln1 == totaln2)
500 return 0;
501 return -1;
502 }
503
504 gint sort_by_cpu_group_by_threads_desc(gconstpointer p1, gconstpointer p2)
505 {
506 struct processtop *n1 = *(struct processtop **)p1;
507 struct processtop *n2 = *(struct processtop **)p2;
508 unsigned long totaln1 = n1->threadstotalcpunsec;
509 unsigned long totaln2 = n2->threadstotalcpunsec;
510
511 if (totaln1 < totaln2)
512 return 1;
513 if (totaln1 == totaln2)
514 return 0;
515 return -1;
516 }
517
518 void update_cputop_display()
519 {
520 int i;
521 int header_offset = 2;
522 struct processtop *tmp;
523 unsigned long elapsed;
524 double maxcputime;
525 int nblinedisplayed = 0;
526 int current_line = 0;
527
528 elapsed = data->end - data->start;
529 maxcputime = elapsed * data->cpu_table->len / 100.0;
530
531 if (cputopview[0].sort == 1)
532 g_ptr_array_sort(data->process_table, sort_by_cpu_desc);
533 else if (cputopview[1].sort == 1)
534 g_ptr_array_sort(data->process_table, sort_by_pid_desc);
535 else if (cputopview[2].sort == 1)
536 g_ptr_array_sort(data->process_table, sort_by_tid_desc);
537 else if (cputopview[3].sort == 1)
538 g_ptr_array_sort(data->process_table, sort_by_cpu_desc);
539 else
540 g_ptr_array_sort(data->process_table, sort_by_cpu_desc);
541
542 set_window_title(center, "CPU Top");
543 wattron(center, A_BOLD);
544 mvwprintw(center, 1, 1, cputopview[0].title);
545 mvwprintw(center, 1, 12, cputopview[1].title);
546 mvwprintw(center, 1, 22, cputopview[2].title);
547 mvwprintw(center, 1, 32, cputopview[3].title);
548 wattroff(center, A_BOLD);
549
550 max_center_lines = LINES - 5 - 7 - 1 - header_offset;
551
552 /* iterate the process (thread) list */
553 for (i = list_offset; i < data->process_table->len &&
554 nblinedisplayed < max_center_lines; i++) {
555 tmp = g_ptr_array_index(data->process_table, i);
556
557 if (process_selected(tmp)) {
558 wattron(center, COLOR_PAIR(6));
559 mvwhline(center, current_line + header_offset, 1, ' ', COLS-3);
560 }
561 if (current_line == selected_line) {
562 selected_process = tmp;
563 wattron(center, COLOR_PAIR(5));
564 mvwhline(center, current_line + header_offset, 1, ' ', COLS-3);
565 }
566 /* CPU(%) */
567 mvwprintw(center, current_line + header_offset, 1, "%1.2f",
568 tmp->totalcpunsec / maxcputime);
569 /* TGID */
570 mvwprintw(center, current_line + header_offset, 12, "%d", tmp->pid);
571 /* PID */
572 mvwprintw(center, current_line + header_offset, 22, "%d", tmp->tid);
573 /* NAME */
574 mvwprintw(center, current_line + header_offset, 32, "%s", tmp->comm);
575 wattroff(center, COLOR_PAIR(6));
576 wattroff(center, COLOR_PAIR(5));
577 nblinedisplayed++;
578 current_line++;
579 }
580 }
581
582 gint sort_perf(gconstpointer p1, gconstpointer p2, gpointer key)
583 {
584 struct processtop *n1 = *(struct processtop **) p1;
585 struct processtop *n2 = *(struct processtop **) p2;
586
587 struct perfcounter *tmp1, *tmp2;
588 unsigned long totaln2 = 0;
589 unsigned long totaln1 = 0;
590
591 if (!key)
592 return 0;
593
594 tmp1 = g_hash_table_lookup(n1->perf, key);
595 if (!tmp1)
596 totaln1 = 0;
597 else
598 totaln1 = tmp1->count;
599
600 tmp2 = g_hash_table_lookup(n2->perf, key);
601 if (!tmp2)
602 totaln2 = 0;
603 else
604 totaln2 = tmp2->count;
605
606 if (totaln1 < totaln2)
607 return 1;
608 if (totaln1 == totaln2) {
609 totaln1 = n1->tid;
610 totaln2 = n2->tid;
611 if (totaln1 < totaln2)
612 return 1;
613 return -1;
614 }
615 return -1;
616 }
617
618 void print_key_title(char *key, int line)
619 {
620 wattron(center, A_BOLD);
621 mvwprintw(center, line, 1, "%s\t", key);
622 wattroff(center, A_BOLD);
623 }
624
625 void update_process_details()
626 {
627 unsigned long elapsed;
628 double maxcputime;
629 struct processtop *tmp;
630 struct files *file_tmp;
631 int i, j = 0;
632 char unit[4];
633 char filename_buf[COLS];
634 GPtrArray *newfilearray = g_ptr_array_new();
635
636 set_window_title(center, "Process details");
637
638
639 tmp = find_process_tid(data,
640 selected_process->tid,
641 selected_process->comm);
642 elapsed = data->end - data->start;
643 maxcputime = elapsed * data->cpu_table->len / 100.0;
644
645 print_key_title("Name", 1);
646 wprintw(center, "%s", selected_process->comm);
647 print_key_title("TID", 2);
648 wprintw(center, "%d", selected_process->tid);
649 if (!tmp) {
650 print_key_title("Does not exit at this time", 3);
651 return;
652 }
653
654 print_key_title("PID", 3);
655 wprintw(center, "%d", tmp->pid);
656 print_key_title("PPID", 4);
657 wprintw(center, "%d", tmp->ppid);
658 print_key_title("CPU", 5);
659 wprintw(center, "%1.2f %%", tmp->totalcpunsec/maxcputime);
660
661 print_key_title("READ B/s", 6);
662 scale_unit(tmp->fileread, unit);
663 wprintw(center, "%s", unit);
664
665 print_key_title("WRITE B/s", 7);
666 scale_unit(tmp->filewrite, unit);
667 wprintw(center, "%s", unit);
668
669 wattron(center, A_BOLD);
670 mvwprintw(center, 8, 1, "FD");
671 mvwprintw(center, 8, 10, "READ");
672 mvwprintw(center, 8, 17, "WRITE");
673 mvwprintw(center, 8, 24, "FILENAME");
674 wattroff(center, A_BOLD);
675
676 /*
677 * since the process_files_table array could contain NULL file structures,
678 * and that the positions inside the array is important (it is the FD), we
679 * need to create a temporary array that we can sort.
680 */
681 for (i = 0; i < tmp->process_files_table->len; i++) {
682 file_tmp = g_ptr_array_index(tmp->process_files_table, i);
683 if (file_tmp)
684 g_ptr_array_add(newfilearray, file_tmp);
685 }
686
687 if (fileview[0].sort == 1)
688 g_ptr_array_sort(newfilearray, sort_by_file_fd_desc);
689 else if (fileview[1].sort == 1)
690 g_ptr_array_sort(newfilearray, sort_by_file_read_desc);
691 else if (fileview[2].sort == 1)
692 g_ptr_array_sort(newfilearray, sort_by_file_write_desc);
693 else
694 g_ptr_array_sort(newfilearray, sort_by_file_read_desc);
695
696 for (i = selected_line; i < newfilearray->len &&
697 i < (selected_line + max_center_lines - 7); i++) {
698 file_tmp = g_ptr_array_index(newfilearray, i);
699 if (!file_tmp)
700 continue;
701 mvwprintw(center, 9 + j, 1, "%d", file_tmp->fd);
702 scale_unit(file_tmp->read, unit);
703 mvwprintw(center, 9 + j, 10, "%s", unit);
704 scale_unit(file_tmp->write, unit);
705 mvwprintw(center, 9 + j, 17, "%s", unit);
706 snprintf(filename_buf, COLS - 25, "%s", file_tmp->name);
707 mvwprintw(center, 9 + j, 24, "%s", filename_buf);
708 j++;
709 }
710 g_ptr_array_free(newfilearray, TRUE);
711 }
712
713 void update_perf()
714 {
715 int i;
716 int nblinedisplayed = 0;
717 int current_line = 0;
718 struct processtop *tmp;
719 int header_offset = 2;
720 int perf_row = 40;
721 struct perfcounter *perfn1, *perfn2;
722 char *perf_key = NULL;
723 int value;
724 GHashTableIter iter;
725 gpointer key;
726
727 set_window_title(center, "Perf Top");
728 wattron(center, A_BOLD);
729 mvwprintw(center, 1, 1, "PID");
730 mvwprintw(center, 1, 11, "TID");
731 mvwprintw(center, 1, 22, "NAME");
732
733 perf_row = 40;
734 g_hash_table_iter_init(&iter, global_perf_liszt);
735 while (g_hash_table_iter_next (&iter, &key, (gpointer) &perfn1)) {
736 if (perfn1->visible) {
737 /* + 5 to strip the "perf_" prefix */
738 mvwprintw(center, 1, perf_row, "%s",
739 (char *) key + 5);
740 perf_row += 20;
741 }
742 if (perfn1->sort) {
743 perf_key = (char *) key;
744 }
745 }
746
747 wattroff(center, A_BOLD);
748
749 g_ptr_array_sort_with_data(data->process_table, sort_perf, perf_key);
750
751 for (i = 0; i < data->process_table->len &&
752 nblinedisplayed < max_center_lines; i++) {
753 tmp = g_ptr_array_index(data->process_table, i);
754
755 if (process_selected(tmp)) {
756 wattron(center, COLOR_PAIR(6));
757 mvwhline(center, current_line + header_offset, 1, ' ', COLS-3);
758 }
759 if (current_line == selected_line) {
760 selected_process = tmp;
761 wattron(center, COLOR_PAIR(5));
762 mvwhline(center, current_line + header_offset, 1, ' ', COLS-3);
763 }
764
765 mvwprintw(center, current_line + header_offset, 1, "%d", tmp->pid);
766 mvwprintw(center, current_line + header_offset, 11, "%d", tmp->tid);
767 mvwprintw(center, current_line + header_offset, 22, "%s", tmp->comm);
768
769 g_hash_table_iter_init(&iter, global_perf_liszt);
770
771 perf_row = 40;
772 while (g_hash_table_iter_next (&iter, &key, (gpointer) &perfn1)) {
773 if (perfn1->visible) {
774 perfn2 = g_hash_table_lookup(tmp->perf, (char *) key);
775 if (perfn2)
776 value = perfn2->count;
777 else
778 value = 0;
779 mvwprintw(center, current_line + header_offset,
780 perf_row, "%d", value);
781 perf_row += 20;
782 }
783 }
784
785 wattroff(center, COLOR_PAIR(6));
786 wattroff(center, COLOR_PAIR(5));
787 nblinedisplayed++;
788 current_line++;
789 }
790 }
791
792 void update_iostream()
793 {
794 int i;
795 int header_offset = 2;
796 struct processtop *tmp;
797 int nblinedisplayed = 0;
798 int current_line = 0;
799 int total = 0;
800 char unit[4];
801
802 set_window_title(center, "IO Top");
803 wattron(center, A_BOLD);
804 mvwprintw(center, 1, 1, "PID");
805 mvwprintw(center, 1, 11, "TID");
806 mvwprintw(center, 1, 22, "NAME");
807 mvwprintw(center, 1, 40, "R (B/sec)");
808 mvwprintw(center, 1, 52, "W (B/sec)");
809 mvwprintw(center, 1, 64, "Total");
810 wattroff(center, A_BOLD);
811
812 if (iostreamtopview[0].sort == 1)
813 g_ptr_array_sort(data->process_table, sort_by_process_read_desc);
814 else if (iostreamtopview[1].sort == 1)
815 g_ptr_array_sort(data->process_table, sort_by_process_write_desc);
816 else if (iostreamtopview[2].sort == 1)
817 g_ptr_array_sort(data->process_table, sort_by_process_total_desc);
818 else
819 g_ptr_array_sort(data->process_table, sort_by_process_total_desc);
820
821 for (i = list_offset; i < data->process_table->len &&
822 nblinedisplayed < max_center_lines; i++) {
823 tmp = g_ptr_array_index(data->process_table, i);
824
825 if (process_selected(tmp)) {
826 wattron(center, COLOR_PAIR(6));
827 mvwhline(center, current_line + header_offset, 1, ' ', COLS-3);
828 }
829 if (current_line == selected_line) {
830 selected_process = tmp;
831 wattron(center, COLOR_PAIR(5));
832 mvwhline(center, current_line + header_offset, 1, ' ', COLS-3);
833 }
834 /* TGID */
835 mvwprintw(center, current_line + header_offset, 1, "%d", tmp->pid);
836 /* PID */
837 mvwprintw(center, current_line + header_offset, 11, "%d", tmp->tid);
838 /* NAME */
839 mvwprintw(center, current_line + header_offset, 22, "%s", tmp->comm);
840
841 /* READ (bytes/sec) */
842 scale_unit(tmp->fileread, unit);
843 mvwprintw(center, current_line + header_offset, 40, "%s", unit);
844
845 /* WRITE (bytes/sec) */
846 scale_unit(tmp->filewrite, unit);
847 mvwprintw(center, current_line + header_offset, 52, "%s", unit);
848
849 /* TOTAL STREAM */
850 total = tmp->totalfileread + tmp->totalfilewrite;
851
852 scale_unit(total, unit);
853 mvwprintw(center, current_line + header_offset, 64, "%s", unit);
854
855 wattroff(center, COLOR_PAIR(6));
856 wattroff(center, COLOR_PAIR(5));
857 nblinedisplayed++;
858 current_line++;
859 }
860 }
861
862 void update_current_view()
863 {
864 sem_wait(&update_display_sem);
865 if (!data)
866 return;
867 update_header();
868
869 werase(center);
870 box(center, 0, 0);
871 switch (current_view) {
872 case cpu:
873 update_cputop_display();
874 break;
875 case perf:
876 update_perf();
877 break;
878 case process_details:
879 update_process_details();
880 break;
881 case iostream:
882 update_iostream();
883 break;
884 case tree:
885 update_cputop_display();
886 break;
887 default:
888 break;
889 }
890 update_panels();
891 doupdate();
892 sem_post(&update_display_sem);
893 }
894
895 void update_process_detail_pref(int *line_selected, int toggle_view, int toggle_sort)
896 {
897 int i;
898 int size;
899
900 if (!data)
901 return;
902 if (pref_panel_window) {
903 del_panel(pref_panel);
904 delwin(pref_panel_window);
905 }
906 size = 3;
907
908 pref_panel_window = create_window(size + 2, 30, 10, 10);
909 pref_panel = new_panel(pref_panel_window);
910
911 werase(pref_panel_window);
912 box(pref_panel_window, 0 , 0);
913 set_window_title(pref_panel_window, "Process Detail Preferences ");
914 wattron(pref_panel_window, A_BOLD);
915 mvwprintw(pref_panel_window, size + 1, 1,
916 " 's' to sort");
917 wattroff(pref_panel_window, A_BOLD);
918
919 if (*line_selected > (size - 1))
920 *line_selected = size - 1;
921 if (toggle_sort == 1) {
922 if (fileview[*line_selected].sort == 1)
923 fileview[*line_selected].reverse = 1;
924 for (i = 0; i < size; i++)
925 fileview[i].sort = 0;
926 fileview[*line_selected].sort = 1;
927 update_current_view();
928 }
929
930 for (i = 0; i < size; i++) {
931 if (i == *line_selected) {
932 wattron(pref_panel_window, COLOR_PAIR(5));
933 mvwhline(pref_panel_window, i + 1, 1, ' ', 30 - 2);
934 }
935 if (fileview[i].sort == 1)
936 wattron(pref_panel_window, A_BOLD);
937 mvwprintw(pref_panel_window, i + 1, 1, "[x] %s",
938 fileview[i].title);
939 wattroff(pref_panel_window, A_BOLD);
940 wattroff(pref_panel_window, COLOR_PAIR(5));
941
942 }
943 update_panels();
944 doupdate();
945 }
946
947 void update_iostream_pref(int *line_selected, int toggle_view, int toggle_sort)
948 {
949 int i;
950 int size;
951
952 if (!data)
953 return;
954 if (pref_panel_window) {
955 del_panel(pref_panel);
956 delwin(pref_panel_window);
957 }
958 size = 3;
959
960 pref_panel_window = create_window(size + 2, 30, 10, 10);
961 pref_panel = new_panel(pref_panel_window);
962
963 werase(pref_panel_window);
964 box(pref_panel_window, 0 , 0);
965 set_window_title(pref_panel_window, "IOTop Preferences ");
966 wattron(pref_panel_window, A_BOLD);
967 mvwprintw(pref_panel_window, size + 1, 1,
968 " 's' to sort");
969 wattroff(pref_panel_window, A_BOLD);
970
971 if (*line_selected > (size - 1))
972 *line_selected = size - 1;
973 if (toggle_sort == 1) {
974 if (iostreamtopview[*line_selected].sort == 1)
975 iostreamtopview[*line_selected].reverse = 1;
976 for (i = 0; i < size; i++)
977 iostreamtopview[i].sort = 0;
978 iostreamtopview[*line_selected].sort = 1;
979 update_current_view();
980 }
981
982 for (i = 0; i < size; i++) {
983 if (i == *line_selected) {
984 wattron(pref_panel_window, COLOR_PAIR(5));
985 mvwhline(pref_panel_window, i + 1, 1, ' ', 30 - 2);
986 }
987 if (iostreamtopview[i].sort == 1)
988 wattron(pref_panel_window, A_BOLD);
989 mvwprintw(pref_panel_window, i + 1, 1, "[x] %s",
990 iostreamtopview[i].title);
991 wattroff(pref_panel_window, A_BOLD);
992 wattroff(pref_panel_window, COLOR_PAIR(5));
993
994 }
995 update_panels();
996 doupdate();
997 }
998
999 void update_cpu_pref(int *line_selected, int toggle_view, int toggle_sort)
1000 {
1001 int i;
1002 int size;
1003
1004 if (!data)
1005 return;
1006 if (pref_panel_window) {
1007 del_panel(pref_panel);
1008 delwin(pref_panel_window);
1009 }
1010 size = 4;
1011
1012 pref_panel_window = create_window(size + 2, 30, 10, 10);
1013 pref_panel = new_panel(pref_panel_window);
1014
1015 werase(pref_panel_window);
1016 box(pref_panel_window, 0 , 0);
1017 set_window_title(pref_panel_window, "CPUTop Preferences ");
1018 wattron(pref_panel_window, A_BOLD);
1019 mvwprintw(pref_panel_window, size + 1, 1,
1020 " 's' to sort");
1021 wattroff(pref_panel_window, A_BOLD);
1022
1023 if (*line_selected > (size - 1))
1024 *line_selected = size - 1;
1025 if (toggle_sort == 1) {
1026 /* special case, we don't support sorting by procname for now */
1027 if (*line_selected != 3) {
1028 if (cputopview[*line_selected].sort == 1)
1029 cputopview[*line_selected].reverse = 1;
1030 for (i = 0; i < size; i++)
1031 cputopview[i].sort = 0;
1032 cputopview[*line_selected].sort = 1;
1033 update_current_view();
1034 }
1035 }
1036
1037 for (i = 0; i < size; i++) {
1038 if (i == *line_selected) {
1039 wattron(pref_panel_window, COLOR_PAIR(5));
1040 mvwhline(pref_panel_window, i + 1, 1, ' ', 30 - 2);
1041 }
1042 if (cputopview[i].sort == 1)
1043 wattron(pref_panel_window, A_BOLD);
1044 mvwprintw(pref_panel_window, i + 1, 1, "[x] %s",
1045 cputopview[i].title);
1046 wattroff(pref_panel_window, A_BOLD);
1047 wattroff(pref_panel_window, COLOR_PAIR(5));
1048
1049 }
1050 update_panels();
1051 doupdate();
1052 }
1053
1054 void update_perf_pref(int *line_selected, int toggle_view, int toggle_sort)
1055 {
1056 int i;
1057 struct perfcounter *perf;
1058 GList *perflist;
1059 int size;
1060
1061 if (!data)
1062 return;
1063 if (pref_panel_window) {
1064 del_panel(pref_panel);
1065 delwin(pref_panel_window);
1066 }
1067 size = g_hash_table_size(global_perf_liszt);
1068
1069 pref_panel_window = create_window(size + 2, 30, 10, 10);
1070 pref_panel = new_panel(pref_panel_window);
1071
1072 werase(pref_panel_window);
1073 box(pref_panel_window, 0 , 0);
1074 set_window_title(pref_panel_window, "Perf Preferences ");
1075 wattron(pref_panel_window, A_BOLD);
1076 mvwprintw(pref_panel_window, g_hash_table_size(global_perf_liszt) + 1, 1,
1077 " 's' to sort");
1078 wattroff(pref_panel_window, A_BOLD);
1079
1080 if (toggle_sort == 1) {
1081 i = 0;
1082 perflist = g_list_first(g_hash_table_get_keys(global_perf_liszt));
1083 while (perflist) {
1084 perf = g_hash_table_lookup(global_perf_liszt, perflist->data);
1085 if (i != *line_selected)
1086 perf->sort = 0;
1087 else
1088 perf->sort = 1;
1089 i++;
1090 perflist = g_list_next(perflist);
1091 }
1092 update_current_view();
1093 }
1094
1095 i = 0;
1096 perflist = g_list_first(g_hash_table_get_keys(global_perf_liszt));
1097 while (perflist) {
1098 perf = g_hash_table_lookup(global_perf_liszt, perflist->data);
1099 if (i == *line_selected && toggle_view == 1) {
1100 perf->visible = perf->visible == 1 ? 0:1;
1101 update_current_view();
1102 }
1103 if (i == *line_selected) {
1104 wattron(pref_panel_window, COLOR_PAIR(5));
1105 mvwhline(pref_panel_window, i + 1, 1, ' ', 30 - 2);
1106 }
1107 if (perf->sort == 1)
1108 wattron(pref_panel_window, A_BOLD);
1109 mvwprintw(pref_panel_window, i + 1, 1, "[%c] %s",
1110 perf->visible == 1 ? 'x' : ' ',
1111 (char *) perflist->data + 5);
1112 wattroff(pref_panel_window, A_BOLD);
1113 wattroff(pref_panel_window, COLOR_PAIR(5));
1114 i++;
1115 perflist = g_list_next(perflist);
1116 }
1117 update_panels();
1118 doupdate();
1119 }
1120
1121 int update_preference_panel(int *line_selected, int toggle_view, int toggle_sort)
1122 {
1123 int ret = 0;
1124
1125 switch(current_view) {
1126 case perf:
1127 update_perf_pref(line_selected, toggle_view, toggle_sort);
1128 break;
1129 case cpu:
1130 update_cpu_pref(line_selected, toggle_view, toggle_sort);
1131 break;
1132 case iostream:
1133 update_iostream_pref(line_selected, toggle_view, toggle_sort);
1134 break;
1135 case process_details:
1136 update_process_detail_pref(line_selected, toggle_view, toggle_sort);
1137 break;
1138 default:
1139 ret = -1;
1140 break;
1141 }
1142
1143 return ret;
1144 }
1145
1146 void toggle_pref_panel(void)
1147 {
1148 int ret;
1149
1150 if (pref_panel_visible) {
1151 hide_panel(pref_panel);
1152 pref_panel_visible = 0;
1153 } else {
1154 ret = update_preference_panel(&pref_line_selected, 0, 0);
1155 if (ret < 0)
1156 return;
1157 show_panel(pref_panel);
1158 pref_panel_visible = 1;
1159 }
1160 update_panels();
1161 doupdate();
1162 }
1163
1164 void display(unsigned int index)
1165 {
1166 last_display_index = index;
1167 currently_displayed_index = index;
1168 data = g_ptr_array_index(copies, index);
1169 if (!data)
1170 return;
1171 max_elements = data->process_table->len;
1172 update_current_view();
1173 update_footer();
1174 update_panels();
1175 doupdate();
1176 }
1177
1178 void pause_display()
1179 {
1180 toggle_pause = 1;
1181 print_log("Pause");
1182 sem_wait(&pause_sem);
1183 }
1184
1185 void resume_display()
1186 {
1187 toggle_pause = -1;
1188 print_log("Resume");
1189 sem_post(&pause_sem);
1190 }
1191
1192 void *handle_keyboard(void *p)
1193 {
1194 int ch;
1195 while((ch = getch())) {
1196 switch(ch) {
1197 /* Move the cursor and scroll */
1198 case 'j':
1199 case KEY_DOWN:
1200 if (pref_panel_visible) {
1201 pref_line_selected++;
1202 update_preference_panel(&pref_line_selected, 0, 0);
1203 } else {
1204 if (selected_line < (max_center_lines - 1) &&
1205 selected_line < max_elements - 1) {
1206 selected_line++;
1207 selected_in_list++;
1208 } else if (selected_in_list < (max_elements - 1)
1209 && (list_offset < (max_elements - max_center_lines))) {
1210 selected_in_list++;
1211 list_offset++;
1212 }
1213 update_current_view();
1214 }
1215 break;
1216 case KEY_NPAGE:
1217 break;
1218 case 'k':
1219 case KEY_UP:
1220 if (pref_panel_visible) {
1221 if (pref_line_selected > 0)
1222 pref_line_selected--;
1223 update_preference_panel(&pref_line_selected, 0, 0);
1224 } else {
1225 if (selected_line > 0) {
1226 selected_line--;
1227 selected_in_list--;
1228 } else if (selected_in_list > 0 && list_offset > 0) {
1229 selected_in_list--;
1230 list_offset--;
1231 }
1232 update_current_view();
1233 }
1234 break;
1235 case KEY_PPAGE:
1236 break;
1237
1238 /* Navigate the history with arrows */
1239 case KEY_LEFT:
1240 if (currently_displayed_index > 0) {
1241 currently_displayed_index--;
1242 print_log("Going back in time");
1243 } else {
1244 print_log("Cannot rewind, last data is already displayed");
1245 }
1246 data = g_ptr_array_index(copies, currently_displayed_index);
1247 max_elements = data->process_table->len;
1248
1249 /* we force to pause the display when moving in time */
1250 if (toggle_pause < 0)
1251 pause_display();
1252
1253 update_current_view();
1254 update_footer();
1255 break;
1256 case KEY_RIGHT:
1257 if (currently_displayed_index < last_display_index) {
1258 currently_displayed_index++;
1259 print_log("Going forward in time");
1260 data = g_ptr_array_index(copies, currently_displayed_index);
1261 max_elements = data->process_table->len;
1262 update_current_view();
1263 update_footer();
1264 } else {
1265 print_log("Manually moving forward");
1266 sem_post(&timer);
1267 /* we force to resume the refresh when moving forward */
1268 if (toggle_pause > 0)
1269 resume_display();
1270 }
1271
1272 break;
1273 case ' ':
1274 if (pref_panel_visible) {
1275 update_preference_panel(&pref_line_selected, 1, 0);
1276 } else {
1277 update_selected_processes();
1278 update_current_view();
1279 }
1280 break;
1281 case 's':
1282 if (pref_panel_visible)
1283 update_preference_panel(&pref_line_selected, 0, 1);
1284 break;
1285
1286 case 13: /* FIXME : KEY_ENTER ?? */
1287 if (current_view != process_details) {
1288 previous_view = current_view;
1289 current_view = process_details;
1290 } else {
1291 current_view = previous_view;
1292 previous_view = process_details;
1293 }
1294 update_current_view();
1295 break;
1296
1297 case KEY_F(1):
1298 if (pref_panel_visible)
1299 toggle_pref_panel();
1300 current_view = cpu;
1301 selected_line = 0;
1302 update_current_view();
1303 break;
1304 case KEY_F(2):
1305 if (pref_panel_visible)
1306 toggle_pref_panel();
1307 current_view = cpu;
1308 selected_line = 0;
1309 update_current_view();
1310 break;
1311 case KEY_F(3):
1312 if (pref_panel_visible)
1313 toggle_pref_panel();
1314 current_view = perf;
1315 selected_line = 0;
1316 update_current_view();
1317 break;
1318 case KEY_F(4):
1319 if (pref_panel_visible)
1320 toggle_pref_panel();
1321 current_view = iostream;
1322 selected_line = 0;
1323 update_current_view();
1324 break;
1325 case KEY_F(10):
1326 case 'q':
1327 reset_ncurses();
1328 break;
1329 case 't':
1330 toggle_threads *= -1;
1331 update_current_view();
1332 break;
1333 case 'p':
1334 if (toggle_pause < 0) {
1335 pause_display();
1336 } else {
1337 resume_display();
1338 }
1339 break;
1340 case 'P':
1341 toggle_pref_panel();
1342 break;
1343 default:
1344 if (data)
1345 update_current_view();
1346 break;
1347 }
1348 update_footer();
1349 }
1350 return NULL;
1351 }
1352
1353 void init_view_headers()
1354 {
1355 cputopview[0].title = strdup("CPU(%)");
1356 cputopview[0].sort = 1;
1357 cputopview[1].title = strdup("TGID");
1358 cputopview[2].title = strdup("PID");
1359 cputopview[3].title = strdup("NAME");
1360
1361 iostreamtopview[0].title = strdup("R (B/sec)");
1362 iostreamtopview[1].title = strdup("W (B/sec)");
1363 iostreamtopview[2].title = strdup("Total (B)");
1364 iostreamtopview[2].sort = 1;
1365
1366 fileview[0].title = strdup("FD");
1367 fileview[1].title = strdup("READ");
1368 fileview[1].sort = 1;
1369 fileview[2].title = strdup("WRITE");
1370 }
1371
1372 void init_ncurses()
1373 {
1374 selected_processes = g_ptr_array_new();
1375 sem_init(&update_display_sem, 0, 1);
1376 init_view_headers();
1377 init_screen();
1378
1379 header = create_window(5, COLS - 1, 0, 0);
1380 center = create_window(LINES - 5 - 7, COLS - 1, 5, 0);
1381 status = create_window(MAX_LOG_LINES + 2, COLS - 1, LINES - 7, 0);
1382 footer = create_window(1, COLS - 1, LINES - 1, 0);
1383
1384 print_log("Starting display");
1385
1386 main_panel = new_panel(center);
1387
1388 current_view = cpu;
1389
1390 basic_header();
1391 update_footer();
1392
1393 pthread_create(&keyboard_thread, NULL, handle_keyboard, (void *)NULL);
1394 }
This page took 0.095173 seconds and 4 git commands to generate.