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