underline for sorted column in cputop
[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, "r", "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 int column;
528
529 elapsed = data->end - data->start;
530 maxcputime = elapsed * data->cpu_table->len / 100.0;
531
532 if (cputopview[0].sort == 1)
533 g_ptr_array_sort(data->process_table, sort_by_cpu_desc);
534 else if (cputopview[1].sort == 1)
535 g_ptr_array_sort(data->process_table, sort_by_pid_desc);
536 else if (cputopview[2].sort == 1)
537 g_ptr_array_sort(data->process_table, sort_by_tid_desc);
538 else if (cputopview[3].sort == 1)
539 g_ptr_array_sort(data->process_table, sort_by_cpu_desc);
540 else
541 g_ptr_array_sort(data->process_table, sort_by_cpu_desc);
542
543 set_window_title(center, "CPU Top");
544 wattron(center, A_BOLD);
545 column = 1;
546 for (i = 0; i < 4; i++) {
547 if (cputopview[i].sort)
548 wattron(center, A_UNDERLINE);
549 mvwprintw(center, 1, column, cputopview[i].title);
550 wattroff(center, A_UNDERLINE);
551 column += 10;
552 }
553 wattroff(center, A_BOLD);
554
555 max_center_lines = LINES - 5 - 7 - 1 - header_offset;
556
557 /* iterate the process (thread) list */
558 for (i = list_offset; i < data->process_table->len &&
559 nblinedisplayed < max_center_lines; i++) {
560 tmp = g_ptr_array_index(data->process_table, i);
561
562 if (process_selected(tmp)) {
563 wattron(center, COLOR_PAIR(6));
564 mvwhline(center, current_line + header_offset, 1, ' ', COLS-3);
565 }
566 if (current_line == selected_line) {
567 selected_process = tmp;
568 wattron(center, COLOR_PAIR(5));
569 mvwhline(center, current_line + header_offset, 1, ' ', COLS-3);
570 }
571 /* CPU(%) */
572 mvwprintw(center, current_line + header_offset, 1, "%1.2f",
573 tmp->totalcpunsec / maxcputime);
574 /* TGID */
575 mvwprintw(center, current_line + header_offset, 11, "%d", tmp->pid);
576 /* PID */
577 mvwprintw(center, current_line + header_offset, 21, "%d", tmp->tid);
578 /* NAME */
579 mvwprintw(center, current_line + header_offset, 31, "%s", tmp->comm);
580 wattroff(center, COLOR_PAIR(6));
581 wattroff(center, COLOR_PAIR(5));
582 nblinedisplayed++;
583 current_line++;
584 }
585 }
586
587 gint sort_perf(gconstpointer p1, gconstpointer p2, gpointer key)
588 {
589 struct processtop *n1 = *(struct processtop **) p1;
590 struct processtop *n2 = *(struct processtop **) p2;
591
592 struct perfcounter *tmp1, *tmp2;
593 unsigned long totaln2 = 0;
594 unsigned long totaln1 = 0;
595
596 if (!key)
597 return 0;
598
599 tmp1 = g_hash_table_lookup(n1->perf, key);
600 if (!tmp1)
601 totaln1 = 0;
602 else
603 totaln1 = tmp1->count;
604
605 tmp2 = g_hash_table_lookup(n2->perf, key);
606 if (!tmp2)
607 totaln2 = 0;
608 else
609 totaln2 = tmp2->count;
610
611 if (totaln1 < totaln2)
612 return 1;
613 if (totaln1 == totaln2) {
614 totaln1 = n1->tid;
615 totaln2 = n2->tid;
616 if (totaln1 < totaln2)
617 return 1;
618 return -1;
619 }
620 return -1;
621 }
622
623 void print_key_title(char *key, int line)
624 {
625 wattron(center, A_BOLD);
626 mvwprintw(center, line, 1, "%s\t", key);
627 wattroff(center, A_BOLD);
628 }
629
630 void update_process_details()
631 {
632 unsigned long elapsed;
633 double maxcputime;
634 struct processtop *tmp;
635 struct files *file_tmp;
636 int i, j = 0;
637 char unit[4];
638 char filename_buf[COLS];
639 GPtrArray *newfilearray = g_ptr_array_new();
640
641 set_window_title(center, "Process details");
642
643
644 tmp = find_process_tid(data,
645 selected_process->tid,
646 selected_process->comm);
647 elapsed = data->end - data->start;
648 maxcputime = elapsed * data->cpu_table->len / 100.0;
649
650 print_key_title("Name", 1);
651 wprintw(center, "%s", selected_process->comm);
652 print_key_title("TID", 2);
653 wprintw(center, "%d", selected_process->tid);
654 if (!tmp) {
655 print_key_title("Does not exit at this time", 3);
656 return;
657 }
658
659 print_key_title("PID", 3);
660 wprintw(center, "%d", tmp->pid);
661 print_key_title("PPID", 4);
662 wprintw(center, "%d", tmp->ppid);
663 print_key_title("CPU", 5);
664 wprintw(center, "%1.2f %%", tmp->totalcpunsec/maxcputime);
665
666 print_key_title("READ B/s", 6);
667 scale_unit(tmp->fileread, unit);
668 wprintw(center, "%s", unit);
669
670 print_key_title("WRITE B/s", 7);
671 scale_unit(tmp->filewrite, unit);
672 wprintw(center, "%s", unit);
673
674 wattron(center, A_BOLD);
675 mvwprintw(center, 8, 1, "FD");
676 mvwprintw(center, 8, 10, "READ");
677 mvwprintw(center, 8, 17, "WRITE");
678 mvwprintw(center, 8, 24, "FILENAME");
679 wattroff(center, A_BOLD);
680
681 /*
682 * since the process_files_table array could contain NULL file structures,
683 * and that the positions inside the array is important (it is the FD), we
684 * need to create a temporary array that we can sort.
685 */
686 for (i = 0; i < tmp->process_files_table->len; i++) {
687 file_tmp = g_ptr_array_index(tmp->process_files_table, i);
688 if (file_tmp)
689 g_ptr_array_add(newfilearray, file_tmp);
690 }
691
692 if (fileview[0].sort == 1)
693 g_ptr_array_sort(newfilearray, sort_by_file_fd_desc);
694 else if (fileview[1].sort == 1)
695 g_ptr_array_sort(newfilearray, sort_by_file_read_desc);
696 else if (fileview[2].sort == 1)
697 g_ptr_array_sort(newfilearray, sort_by_file_write_desc);
698 else
699 g_ptr_array_sort(newfilearray, sort_by_file_read_desc);
700
701 for (i = selected_line; i < newfilearray->len &&
702 i < (selected_line + max_center_lines - 7); i++) {
703 file_tmp = g_ptr_array_index(newfilearray, i);
704 if (!file_tmp)
705 continue;
706 mvwprintw(center, 9 + j, 1, "%d", file_tmp->fd);
707 scale_unit(file_tmp->read, unit);
708 mvwprintw(center, 9 + j, 10, "%s", unit);
709 scale_unit(file_tmp->write, unit);
710 mvwprintw(center, 9 + j, 17, "%s", unit);
711 snprintf(filename_buf, COLS - 25, "%s", file_tmp->name);
712 mvwprintw(center, 9 + j, 24, "%s", filename_buf);
713 j++;
714 }
715 g_ptr_array_free(newfilearray, TRUE);
716 }
717
718 void update_perf()
719 {
720 int i;
721 int nblinedisplayed = 0;
722 int current_line = 0;
723 struct processtop *tmp;
724 int header_offset = 2;
725 int perf_row = 40;
726 struct perfcounter *perfn1, *perfn2;
727 char *perf_key = NULL;
728 int value;
729 GHashTableIter iter;
730 gpointer key;
731
732 set_window_title(center, "Perf Top");
733 wattron(center, A_BOLD);
734 mvwprintw(center, 1, 1, "PID");
735 mvwprintw(center, 1, 11, "TID");
736 mvwprintw(center, 1, 22, "NAME");
737
738 perf_row = 40;
739 g_hash_table_iter_init(&iter, global_perf_liszt);
740 while (g_hash_table_iter_next (&iter, &key, (gpointer) &perfn1)) {
741 if (perfn1->visible) {
742 /* + 5 to strip the "perf_" prefix */
743 mvwprintw(center, 1, perf_row, "%s",
744 (char *) key + 5);
745 perf_row += 20;
746 }
747 if (perfn1->sort) {
748 perf_key = (char *) key;
749 }
750 }
751
752 wattroff(center, A_BOLD);
753
754 g_ptr_array_sort_with_data(data->process_table, sort_perf, perf_key);
755
756 for (i = 0; i < data->process_table->len &&
757 nblinedisplayed < max_center_lines; i++) {
758 tmp = g_ptr_array_index(data->process_table, i);
759
760 if (process_selected(tmp)) {
761 wattron(center, COLOR_PAIR(6));
762 mvwhline(center, current_line + header_offset, 1, ' ', COLS-3);
763 }
764 if (current_line == selected_line) {
765 selected_process = tmp;
766 wattron(center, COLOR_PAIR(5));
767 mvwhline(center, current_line + header_offset, 1, ' ', COLS-3);
768 }
769
770 mvwprintw(center, current_line + header_offset, 1, "%d", tmp->pid);
771 mvwprintw(center, current_line + header_offset, 11, "%d", tmp->tid);
772 mvwprintw(center, current_line + header_offset, 22, "%s", tmp->comm);
773
774 g_hash_table_iter_init(&iter, global_perf_liszt);
775
776 perf_row = 40;
777 while (g_hash_table_iter_next (&iter, &key, (gpointer) &perfn1)) {
778 if (perfn1->visible) {
779 perfn2 = g_hash_table_lookup(tmp->perf, (char *) key);
780 if (perfn2)
781 value = perfn2->count;
782 else
783 value = 0;
784 mvwprintw(center, current_line + header_offset,
785 perf_row, "%d", value);
786 perf_row += 20;
787 }
788 }
789
790 wattroff(center, COLOR_PAIR(6));
791 wattroff(center, COLOR_PAIR(5));
792 nblinedisplayed++;
793 current_line++;
794 }
795 }
796
797 void update_iostream()
798 {
799 int i;
800 int header_offset = 2;
801 struct processtop *tmp;
802 int nblinedisplayed = 0;
803 int current_line = 0;
804 int total = 0;
805 char unit[4];
806
807 set_window_title(center, "IO Top");
808 wattron(center, A_BOLD);
809 mvwprintw(center, 1, 1, "PID");
810 mvwprintw(center, 1, 11, "TID");
811 mvwprintw(center, 1, 22, "NAME");
812 mvwprintw(center, 1, 40, "R (B/sec)");
813 mvwprintw(center, 1, 52, "W (B/sec)");
814 mvwprintw(center, 1, 64, "Total");
815 wattroff(center, A_BOLD);
816
817 if (iostreamtopview[0].sort == 1)
818 g_ptr_array_sort(data->process_table, sort_by_process_read_desc);
819 else if (iostreamtopview[1].sort == 1)
820 g_ptr_array_sort(data->process_table, sort_by_process_write_desc);
821 else if (iostreamtopview[2].sort == 1)
822 g_ptr_array_sort(data->process_table, sort_by_process_total_desc);
823 else
824 g_ptr_array_sort(data->process_table, sort_by_process_total_desc);
825
826 for (i = list_offset; i < data->process_table->len &&
827 nblinedisplayed < max_center_lines; i++) {
828 tmp = g_ptr_array_index(data->process_table, i);
829
830 if (process_selected(tmp)) {
831 wattron(center, COLOR_PAIR(6));
832 mvwhline(center, current_line + header_offset, 1, ' ', COLS-3);
833 }
834 if (current_line == selected_line) {
835 selected_process = tmp;
836 wattron(center, COLOR_PAIR(5));
837 mvwhline(center, current_line + header_offset, 1, ' ', COLS-3);
838 }
839 /* TGID */
840 mvwprintw(center, current_line + header_offset, 1, "%d", tmp->pid);
841 /* PID */
842 mvwprintw(center, current_line + header_offset, 11, "%d", tmp->tid);
843 /* NAME */
844 mvwprintw(center, current_line + header_offset, 22, "%s", tmp->comm);
845
846 /* READ (bytes/sec) */
847 scale_unit(tmp->fileread, unit);
848 mvwprintw(center, current_line + header_offset, 40, "%s", unit);
849
850 /* WRITE (bytes/sec) */
851 scale_unit(tmp->filewrite, unit);
852 mvwprintw(center, current_line + header_offset, 52, "%s", unit);
853
854 /* TOTAL STREAM */
855 total = tmp->totalfileread + tmp->totalfilewrite;
856
857 scale_unit(total, unit);
858 mvwprintw(center, current_line + header_offset, 64, "%s", unit);
859
860 wattroff(center, COLOR_PAIR(6));
861 wattroff(center, COLOR_PAIR(5));
862 nblinedisplayed++;
863 current_line++;
864 }
865 }
866
867 void update_current_view()
868 {
869 sem_wait(&update_display_sem);
870 if (!data)
871 return;
872 update_header();
873
874 werase(center);
875 box(center, 0, 0);
876 switch (current_view) {
877 case cpu:
878 update_cputop_display();
879 break;
880 case perf:
881 update_perf();
882 break;
883 case process_details:
884 update_process_details();
885 break;
886 case iostream:
887 update_iostream();
888 break;
889 case tree:
890 update_cputop_display();
891 break;
892 default:
893 break;
894 }
895 update_panels();
896 doupdate();
897 sem_post(&update_display_sem);
898 }
899
900 void update_process_detail_pref(int *line_selected, int toggle_view, int toggle_sort)
901 {
902 int i;
903 int size;
904
905 if (!data)
906 return;
907 if (pref_panel_window) {
908 del_panel(pref_panel);
909 delwin(pref_panel_window);
910 }
911 size = 3;
912
913 pref_panel_window = create_window(size + 2, 30, 10, 10);
914 pref_panel = new_panel(pref_panel_window);
915
916 werase(pref_panel_window);
917 box(pref_panel_window, 0 , 0);
918 set_window_title(pref_panel_window, "Process Detail Preferences ");
919 wattron(pref_panel_window, A_BOLD);
920 mvwprintw(pref_panel_window, size + 1, 1,
921 " 's' : sort, space : toggle");
922 wattroff(pref_panel_window, A_BOLD);
923
924 if (*line_selected > (size - 1))
925 *line_selected = size - 1;
926 if (toggle_sort == 1) {
927 if (fileview[*line_selected].sort == 1)
928 fileview[*line_selected].reverse = 1;
929 for (i = 0; i < size; i++)
930 fileview[i].sort = 0;
931 fileview[*line_selected].sort = 1;
932 update_current_view();
933 }
934
935 for (i = 0; i < size; i++) {
936 if (i == *line_selected) {
937 wattron(pref_panel_window, COLOR_PAIR(5));
938 mvwhline(pref_panel_window, i + 1, 1, ' ', 30 - 2);
939 }
940 if (fileview[i].sort == 1)
941 wattron(pref_panel_window, A_BOLD);
942 mvwprintw(pref_panel_window, i + 1, 1, "[-] %s",
943 fileview[i].title);
944 wattroff(pref_panel_window, A_BOLD);
945 wattroff(pref_panel_window, COLOR_PAIR(5));
946
947 }
948 update_panels();
949 doupdate();
950 }
951
952 void update_iostream_pref(int *line_selected, int toggle_view, int toggle_sort)
953 {
954 int i;
955 int size;
956
957 if (!data)
958 return;
959 if (pref_panel_window) {
960 del_panel(pref_panel);
961 delwin(pref_panel_window);
962 }
963 size = 3;
964
965 pref_panel_window = create_window(size + 2, 30, 10, 10);
966 pref_panel = new_panel(pref_panel_window);
967
968 werase(pref_panel_window);
969 box(pref_panel_window, 0 , 0);
970 set_window_title(pref_panel_window, "IOTop Preferences ");
971 wattron(pref_panel_window, A_BOLD);
972 mvwprintw(pref_panel_window, size + 1, 1,
973 " 's' : sort, space : toggle");
974 wattroff(pref_panel_window, A_BOLD);
975
976 if (*line_selected > (size - 1))
977 *line_selected = size - 1;
978 if (toggle_sort == 1) {
979 if (iostreamtopview[*line_selected].sort == 1)
980 iostreamtopview[*line_selected].reverse = 1;
981 for (i = 0; i < size; i++)
982 iostreamtopview[i].sort = 0;
983 iostreamtopview[*line_selected].sort = 1;
984 update_current_view();
985 }
986
987 for (i = 0; i < size; i++) {
988 if (i == *line_selected) {
989 wattron(pref_panel_window, COLOR_PAIR(5));
990 mvwhline(pref_panel_window, i + 1, 1, ' ', 30 - 2);
991 }
992 if (iostreamtopview[i].sort == 1)
993 wattron(pref_panel_window, A_BOLD);
994 mvwprintw(pref_panel_window, i + 1, 1, "[-] %s",
995 iostreamtopview[i].title);
996 wattroff(pref_panel_window, A_BOLD);
997 wattroff(pref_panel_window, COLOR_PAIR(5));
998
999 }
1000 update_panels();
1001 doupdate();
1002 }
1003
1004 void update_cpu_pref(int *line_selected, int toggle_view, int toggle_sort)
1005 {
1006 int i;
1007 int size;
1008
1009 if (!data)
1010 return;
1011 if (pref_panel_window) {
1012 del_panel(pref_panel);
1013 delwin(pref_panel_window);
1014 }
1015 size = 4;
1016
1017 pref_panel_window = create_window(size + 2, 30, 10, 10);
1018 pref_panel = new_panel(pref_panel_window);
1019
1020 werase(pref_panel_window);
1021 box(pref_panel_window, 0 , 0);
1022 set_window_title(pref_panel_window, "CPUTop Preferences ");
1023 wattron(pref_panel_window, A_BOLD);
1024 mvwprintw(pref_panel_window, size + 1, 1,
1025 " 's' : sort, space : toggle");
1026 wattroff(pref_panel_window, A_BOLD);
1027
1028 if (*line_selected > (size - 1))
1029 *line_selected = size - 1;
1030 if (toggle_sort == 1) {
1031 /* special case, we don't support sorting by procname for now */
1032 if (*line_selected != 3) {
1033 if (cputopview[*line_selected].sort == 1)
1034 cputopview[*line_selected].reverse = 1;
1035 for (i = 0; i < size; i++)
1036 cputopview[i].sort = 0;
1037 cputopview[*line_selected].sort = 1;
1038 update_current_view();
1039 }
1040 }
1041
1042 for (i = 0; i < size; i++) {
1043 if (i == *line_selected) {
1044 wattron(pref_panel_window, COLOR_PAIR(5));
1045 mvwhline(pref_panel_window, i + 1, 1, ' ', 30 - 2);
1046 }
1047 if (cputopview[i].sort == 1)
1048 wattron(pref_panel_window, A_BOLD);
1049 mvwprintw(pref_panel_window, i + 1, 1, "[-] %s",
1050 cputopview[i].title);
1051 wattroff(pref_panel_window, A_BOLD);
1052 wattroff(pref_panel_window, COLOR_PAIR(5));
1053
1054 }
1055 update_panels();
1056 doupdate();
1057 }
1058
1059 void update_perf_pref(int *line_selected, int toggle_view, int toggle_sort)
1060 {
1061 int i;
1062 struct perfcounter *perf;
1063 GList *perflist;
1064 int size;
1065
1066 if (!data)
1067 return;
1068 if (pref_panel_window) {
1069 del_panel(pref_panel);
1070 delwin(pref_panel_window);
1071 }
1072 size = g_hash_table_size(global_perf_liszt);
1073
1074 pref_panel_window = create_window(size + 2, 30, 10, 10);
1075 pref_panel = new_panel(pref_panel_window);
1076
1077 werase(pref_panel_window);
1078 box(pref_panel_window, 0 , 0);
1079 set_window_title(pref_panel_window, "Perf Preferences ");
1080 wattron(pref_panel_window, A_BOLD);
1081 mvwprintw(pref_panel_window, g_hash_table_size(global_perf_liszt) + 1, 1,
1082 " 's' : sort, space : toggle");
1083 wattroff(pref_panel_window, A_BOLD);
1084
1085 if (toggle_sort == 1) {
1086 i = 0;
1087 perflist = g_list_first(g_hash_table_get_keys(global_perf_liszt));
1088 while (perflist) {
1089 perf = g_hash_table_lookup(global_perf_liszt, perflist->data);
1090 if (i != *line_selected)
1091 perf->sort = 0;
1092 else
1093 perf->sort = 1;
1094 i++;
1095 perflist = g_list_next(perflist);
1096 }
1097 update_current_view();
1098 }
1099
1100 i = 0;
1101 perflist = g_list_first(g_hash_table_get_keys(global_perf_liszt));
1102 while (perflist) {
1103 perf = g_hash_table_lookup(global_perf_liszt, perflist->data);
1104 if (i == *line_selected && toggle_view == 1) {
1105 perf->visible = perf->visible == 1 ? 0:1;
1106 update_current_view();
1107 }
1108 if (i == *line_selected) {
1109 wattron(pref_panel_window, COLOR_PAIR(5));
1110 mvwhline(pref_panel_window, i + 1, 1, ' ', 30 - 2);
1111 }
1112 if (perf->sort == 1)
1113 wattron(pref_panel_window, A_BOLD);
1114 mvwprintw(pref_panel_window, i + 1, 1, "[%c] %s",
1115 perf->visible == 1 ? 'x' : ' ',
1116 (char *) perflist->data + 5);
1117 wattroff(pref_panel_window, A_BOLD);
1118 wattroff(pref_panel_window, COLOR_PAIR(5));
1119 i++;
1120 perflist = g_list_next(perflist);
1121 }
1122 update_panels();
1123 doupdate();
1124 }
1125
1126 int update_preference_panel(int *line_selected, int toggle_view, int toggle_sort)
1127 {
1128 int ret = 0;
1129
1130 switch(current_view) {
1131 case perf:
1132 update_perf_pref(line_selected, toggle_view, toggle_sort);
1133 break;
1134 case cpu:
1135 update_cpu_pref(line_selected, toggle_view, toggle_sort);
1136 break;
1137 case iostream:
1138 update_iostream_pref(line_selected, toggle_view, toggle_sort);
1139 break;
1140 case process_details:
1141 update_process_detail_pref(line_selected, toggle_view, toggle_sort);
1142 break;
1143 default:
1144 ret = -1;
1145 break;
1146 }
1147
1148 return ret;
1149 }
1150
1151 void toggle_pref_panel(void)
1152 {
1153 int ret;
1154
1155 if (pref_panel_visible) {
1156 hide_panel(pref_panel);
1157 pref_panel_visible = 0;
1158 } else {
1159 ret = update_preference_panel(&pref_line_selected, 0, 0);
1160 if (ret < 0)
1161 return;
1162 show_panel(pref_panel);
1163 pref_panel_visible = 1;
1164 }
1165 update_panels();
1166 doupdate();
1167 }
1168
1169 void display(unsigned int index)
1170 {
1171 last_display_index = index;
1172 currently_displayed_index = index;
1173 data = g_ptr_array_index(copies, index);
1174 if (!data)
1175 return;
1176 max_elements = data->process_table->len;
1177 update_current_view();
1178 update_footer();
1179 update_panels();
1180 doupdate();
1181 }
1182
1183 void pause_display()
1184 {
1185 toggle_pause = 1;
1186 print_log("Pause");
1187 sem_wait(&pause_sem);
1188 }
1189
1190 void resume_display()
1191 {
1192 toggle_pause = -1;
1193 print_log("Resume");
1194 sem_post(&pause_sem);
1195 }
1196
1197 void *handle_keyboard(void *p)
1198 {
1199 int ch;
1200 while((ch = getch())) {
1201 switch(ch) {
1202 /* Move the cursor and scroll */
1203 case 'j':
1204 case KEY_DOWN:
1205 if (pref_panel_visible) {
1206 pref_line_selected++;
1207 update_preference_panel(&pref_line_selected, 0, 0);
1208 } else {
1209 if (selected_line < (max_center_lines - 1) &&
1210 selected_line < max_elements - 1) {
1211 selected_line++;
1212 selected_in_list++;
1213 } else if (selected_in_list < (max_elements - 1)
1214 && (list_offset < (max_elements - max_center_lines))) {
1215 selected_in_list++;
1216 list_offset++;
1217 }
1218 update_current_view();
1219 }
1220 break;
1221 case KEY_NPAGE:
1222 break;
1223 case 'k':
1224 case KEY_UP:
1225 if (pref_panel_visible) {
1226 if (pref_line_selected > 0)
1227 pref_line_selected--;
1228 update_preference_panel(&pref_line_selected, 0, 0);
1229 } else {
1230 if (selected_line > 0) {
1231 selected_line--;
1232 selected_in_list--;
1233 } else if (selected_in_list > 0 && list_offset > 0) {
1234 selected_in_list--;
1235 list_offset--;
1236 }
1237 update_current_view();
1238 }
1239 break;
1240 case KEY_PPAGE:
1241 break;
1242
1243 /* Navigate the history with arrows */
1244 case KEY_LEFT:
1245 if (currently_displayed_index > 0) {
1246 currently_displayed_index--;
1247 print_log("Going back in time");
1248 } else {
1249 print_log("Cannot rewind, last data is already displayed");
1250 }
1251 data = g_ptr_array_index(copies, currently_displayed_index);
1252 max_elements = data->process_table->len;
1253
1254 /* we force to pause the display when moving in time */
1255 if (toggle_pause < 0)
1256 pause_display();
1257
1258 update_current_view();
1259 update_footer();
1260 break;
1261 case KEY_RIGHT:
1262 if (currently_displayed_index < last_display_index) {
1263 currently_displayed_index++;
1264 print_log("Going forward in time");
1265 data = g_ptr_array_index(copies, currently_displayed_index);
1266 max_elements = data->process_table->len;
1267 update_current_view();
1268 update_footer();
1269 } else {
1270 print_log("Manually moving forward");
1271 sem_post(&timer);
1272 if (toggle_pause > 0) {
1273 sem_post(&pause_sem);
1274 update_current_view();
1275 sem_wait(&pause_sem);
1276 }
1277 }
1278
1279 break;
1280 case ' ':
1281 if (pref_panel_visible) {
1282 update_preference_panel(&pref_line_selected, 1, 0);
1283 } else {
1284 update_selected_processes();
1285 update_current_view();
1286 }
1287 break;
1288 case 's':
1289 if (pref_panel_visible)
1290 update_preference_panel(&pref_line_selected, 0, 1);
1291 break;
1292
1293 case 13: /* FIXME : KEY_ENTER ?? */
1294 if (pref_panel_visible)
1295 break;
1296 if (current_view != process_details) {
1297 previous_view = current_view;
1298 current_view = process_details;
1299 } else {
1300 current_view = previous_view;
1301 previous_view = process_details;
1302 }
1303 update_current_view();
1304 break;
1305
1306 case KEY_F(1):
1307 if (pref_panel_visible)
1308 toggle_pref_panel();
1309 current_view = cpu;
1310 selected_line = 0;
1311 update_current_view();
1312 break;
1313 case KEY_F(2):
1314 if (pref_panel_visible)
1315 toggle_pref_panel();
1316 current_view = cpu;
1317 selected_line = 0;
1318 update_current_view();
1319 break;
1320 case KEY_F(3):
1321 if (pref_panel_visible)
1322 toggle_pref_panel();
1323 current_view = perf;
1324 selected_line = 0;
1325 update_current_view();
1326 break;
1327 case KEY_F(4):
1328 if (pref_panel_visible)
1329 toggle_pref_panel();
1330 current_view = iostream;
1331 selected_line = 0;
1332 update_current_view();
1333 break;
1334 case KEY_F(10):
1335 case 'q':
1336 reset_ncurses();
1337 break;
1338 case 't':
1339 toggle_threads *= -1;
1340 update_current_view();
1341 break;
1342 case 'p':
1343 if (toggle_pause < 0) {
1344 pause_display();
1345 } else {
1346 resume_display();
1347 }
1348 break;
1349 case 'r':
1350 toggle_pref_panel();
1351 break;
1352 /* ESCAPE, but slow to process, don't know why */
1353 case 27:
1354 if (pref_panel_visible)
1355 toggle_pref_panel();
1356 else if (current_view == process_details) {
1357 current_view = previous_view;
1358 previous_view = process_details;
1359 }
1360 update_current_view();
1361 break;
1362 default:
1363 if (data)
1364 update_current_view();
1365 break;
1366 }
1367 update_footer();
1368 }
1369 return NULL;
1370 }
1371
1372 void init_view_headers()
1373 {
1374 cputopview[0].title = strdup("CPU(%)");
1375 cputopview[0].sort = 1;
1376 cputopview[1].title = strdup("TGID");
1377 cputopview[2].title = strdup("PID");
1378 cputopview[3].title = strdup("NAME");
1379
1380 iostreamtopview[0].title = strdup("R (B/sec)");
1381 iostreamtopview[1].title = strdup("W (B/sec)");
1382 iostreamtopview[2].title = strdup("Total (B)");
1383 iostreamtopview[2].sort = 1;
1384
1385 fileview[0].title = strdup("FD");
1386 fileview[1].title = strdup("READ");
1387 fileview[1].sort = 1;
1388 fileview[2].title = strdup("WRITE");
1389 }
1390
1391 void init_ncurses()
1392 {
1393 selected_processes = g_ptr_array_new();
1394 sem_init(&update_display_sem, 0, 1);
1395 init_view_headers();
1396 init_screen();
1397
1398 header = create_window(5, COLS - 1, 0, 0);
1399 center = create_window(LINES - 5 - 7, COLS - 1, 5, 0);
1400 status = create_window(MAX_LOG_LINES + 2, COLS - 1, LINES - 7, 0);
1401 footer = create_window(1, COLS - 1, LINES - 1, 0);
1402
1403 print_log("Starting display");
1404
1405 main_panel = new_panel(center);
1406
1407 current_view = cpu;
1408
1409 basic_header();
1410 update_footer();
1411
1412 pthread_create(&keyboard_thread, NULL, handle_keyboard, (void *)NULL);
1413 }
This page took 0.104564 seconds and 4 git commands to generate.