a2e3f275edd0c6bf528aad812bdd75e7551d3842
[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 cputopview cputopview[4];
69
70 void reset_ncurses()
71 {
72 curs_set(1);
73 endwin();
74 exit(0);
75 }
76
77 static void handle_sigterm(int signal)
78 {
79 reset_ncurses();
80 }
81
82 void init_screen()
83 {
84 initscr();
85 noecho();
86 halfdelay(DEFAULT_DELAY);
87 nonl();
88 intrflush(stdscr, false);
89 keypad(stdscr, true);
90 curs_set(0);
91
92 if (has_colors()) {
93 start_color();
94 init_pair(1, COLOR_RED, COLOR_BLACK); /* - */
95 init_pair(2, COLOR_GREEN, COLOR_BLACK); /* + */
96 init_pair(3, COLOR_BLACK, COLOR_WHITE); /* keys */
97 init_pair(4, COLOR_WHITE, COLOR_GREEN); /* keys activated */
98 init_pair(5, COLOR_WHITE, COLOR_BLUE); /* select line */
99 init_pair(6, COLOR_WHITE, COLOR_GREEN); /* selected process */
100 }
101 termtype = getenv("TERM");
102 if (!strcmp(termtype, "xterm") || !strcmp(termtype, "xterm-color") ||
103 !strcmp(termtype, "vt220")) {
104 define_key("\033[H", KEY_HOME);
105 define_key("\033[F", KEY_END);
106 define_key("\033OP", KEY_F(1));
107 define_key("\033OQ", KEY_F(2));
108 define_key("\033OR", KEY_F(3));
109 define_key("\033OS", KEY_F(4));
110 define_key("\0330U", KEY_F(6));
111 define_key("\033[11~", KEY_F(1));
112 define_key("\033[12~", KEY_F(2));
113 define_key("\033[13~", KEY_F(3));
114 define_key("\033[14~", KEY_F(4));
115 define_key("\033[16~", KEY_F(6));
116 define_key("\033[17;2~", KEY_F(18));
117 }
118 signal(SIGTERM, handle_sigterm);
119 mousemask(BUTTON1_CLICKED, NULL);
120 refresh();
121 }
122
123 WINDOW *create_window(int height, int width, int startx, int starty)
124 {
125 WINDOW *win;
126 win = newwin(height, width, startx, starty);
127 box(win, 0 , 0);
128 wrefresh(win);
129 return win;
130 }
131
132 WINDOW *create_window_no_border(int height, int width, int startx, int starty)
133 {
134 WINDOW *win;
135 win = newwin(height, width, startx, starty);
136 wrefresh(win);
137 return win;
138 }
139
140 void print_digit(WINDOW *win, int digit)
141 {
142 if (digit < 0) {
143 wattron(win, COLOR_PAIR(1));
144 wprintw(win, "%d", digit);
145 wattroff(win, COLOR_PAIR(1));
146 } else if (digit > 0) {
147 wattron(win, COLOR_PAIR(2));
148 wprintw(win, "+%d", digit);
149 wattroff(win, COLOR_PAIR(2));
150 } else {
151 wprintw(win, "0");
152 }
153 }
154
155 void print_digits(WINDOW *win, int first, int second)
156 {
157 wprintw(win, "(");
158 print_digit(win, first);
159 wprintw(win, ", ");
160 print_digit(win, second);
161 wprintw(win, ")");
162 }
163
164 void print_headers(int line, char *desc, int value, int first, int second)
165 {
166 wattron(header, A_BOLD);
167 mvwprintw(header, line, 4, "%s", desc);
168 wattroff(header, A_BOLD);
169 mvwprintw(header, line, 16, "%d", value);
170 wmove(header, line, 24);
171 print_digits(header, first, second);
172 wmove(header, line, 40);
173 }
174
175 void set_window_title(WINDOW *win, char *title)
176 {
177 wattron(win, A_BOLD);
178 mvwprintw(win, 0, 1, title);
179 wattroff(win, A_BOLD);
180 }
181
182 void print_log(char *str)
183 {
184 int i;
185 int current_line = 1;
186 int current_char = 1;
187 char *tmp, *tmp2;
188 /* rotate the line buffer */
189 if (nb_log_lines >= MAX_LOG_LINES) {
190 tmp = strndup(log_lines, MAX_LINE_LENGTH * MAX_LOG_LINES + MAX_LOG_LINES);
191 tmp2 = strchr(tmp, '\n');
192 memset(log_lines, '\0', strlen(log_lines));
193 strncat(log_lines, tmp2 + 1, strlen(tmp2) - 1);
194 log_lines[strlen(log_lines)] = '\n';
195 log_lines[strlen(log_lines)] = '\0';
196 free(tmp);
197 }
198 nb_log_lines++;
199
200 strncat(log_lines, str, MAX_LINE_LENGTH - 1);
201
202 if (nb_log_lines < MAX_LOG_LINES)
203 log_lines[strlen(log_lines)] = '\n';
204 log_lines[strlen(log_lines)] = '\0';
205
206 werase(status);
207 box(status, 0 , 0);
208 set_window_title(status, "Status");
209 for (i = 0; i < strlen(log_lines); i++) {
210 if (log_lines[i] == '\n') {
211 wmove(status, ++current_line, 1);
212 current_char = 1;
213 } else {
214 mvwprintw(status, current_line, current_char++, "%c",
215 log_lines[i]);
216 }
217 }
218 wrefresh(status);
219 }
220
221 int process_selected(struct processtop *process)
222 {
223 int i;
224 struct processtop *stored_process;
225
226 for (i = 0; i < selected_processes->len; i++) {
227 stored_process = g_ptr_array_index(selected_processes, i);
228 if (stored_process->tid == process->tid)
229 return 1;
230 }
231 return 0;
232 }
233
234 void update_selected_processes()
235 {
236 if (process_selected(selected_process)) {
237 g_ptr_array_remove(selected_processes, selected_process);
238 print_log("Process removed");
239 } else {
240 g_ptr_array_add(selected_processes, selected_process);
241 print_log("Process added");
242 }
243 }
244
245 void print_key(WINDOW *win, char *key, char *desc, int toggle)
246 {
247 int pair;
248 if (toggle > 0)
249 pair = 4;
250 else
251 pair = 3;
252 wattron(win, COLOR_PAIR(pair));
253 wprintw(footer, "%s", key);
254 wattroff(win, COLOR_PAIR(pair));
255 wprintw(footer, ":%s", desc);
256 }
257
258 void update_footer()
259 {
260 sem_wait(&update_display_sem);
261 werase(footer);
262 wmove(footer, 1, 1);
263 print_key(footer, "F2", "CPUtop ", current_view == cpu);
264 print_key(footer, "F3", "PerfTop ", current_view == perf);
265 print_key(footer, "F4", "IOTop ", current_view == iostream);
266 print_key(footer, "Enter", "Details ", current_view == process_details);
267 print_key(footer, "Space", "Highlight ", 0);
268 print_key(footer, "q", "Quit | ", 0);
269 print_key(footer, "P", "Perf Pref ", 0);
270 print_key(footer, "p", "Pause ", toggle_pause);
271
272 wrefresh(footer);
273 sem_post(&update_display_sem);
274 }
275
276 void basic_header()
277 {
278 werase(header);
279 box(header, 0 , 0);
280 set_window_title(header, "Statistics for interval [gathering data...[");
281 wattron(header, A_BOLD);
282 mvwprintw(header, 1, 4, "CPUs");
283 mvwprintw(header, 2, 4, "Threads");
284 mvwprintw(header, 3, 4, "FDs");
285 wattroff(header, A_BOLD);
286 wrefresh(header);
287 }
288
289 struct tm format_timestamp(uint64_t timestamp)
290 {
291 struct tm tm;
292 uint64_t ts_sec = 0, ts_nsec;
293 time_t time_s;
294
295 ts_nsec = timestamp;
296 ts_sec += ts_nsec / NSEC_PER_SEC;
297 ts_nsec = ts_nsec % NSEC_PER_SEC;
298
299 time_s = (time_t) ts_sec;
300
301 localtime_r(&time_s, &tm);
302
303 return tm;
304 }
305
306 static void scale_unit(uint64_t bytes, char *ret)
307 {
308 if (bytes >= 1000000000)
309 sprintf(ret, "%" PRIu64 "G", bytes/1000000000);
310 if (bytes >= 1000000)
311 sprintf(ret, "%" PRIu64 "M", bytes/1000000);
312 else if (bytes >= 1000)
313 sprintf(ret, "%" PRIu64 "K", bytes/1000);
314 else
315 sprintf(ret, "%" PRIu64, bytes);
316 }
317 uint64_t total_io()
318 {
319 int i;
320 struct processtop *tmp;
321 uint64_t total = 0;
322
323 for (i = 0; i < data->process_table->len; i++) {
324 tmp = g_ptr_array_index(data->process_table, i);
325 total += tmp->fileread;
326 total += tmp->filewrite;
327 }
328
329 return total;
330 }
331
332 void update_header()
333 {
334 struct tm start, end;
335 uint64_t ts_nsec_start, ts_nsec_end;
336 char io[4];
337
338 ts_nsec_start = data->start % NSEC_PER_SEC;
339 start = format_timestamp(data->start);
340
341 ts_nsec_end = data->end % NSEC_PER_SEC;
342 end = format_timestamp(data->end);
343
344 werase(header);
345 box(header, 0 , 0);
346 set_window_title(header, "Statistics for interval ");
347 wattron(header, A_BOLD);
348
349 wprintw(header, "[%02d:%02d:%02d.%09" PRIu64 ", %02d:%02d:%02d.%09" PRIu64 "[",
350 start.tm_hour, start.tm_min, start.tm_sec, ts_nsec_start,
351 end.tm_hour, end.tm_min, end.tm_sec, ts_nsec_end);
352 mvwprintw(header, 1, 4, "CPUs");
353 wattroff(header, A_BOLD);
354 wprintw(header, "\t%d\t(max/cpu : %0.2f%)", data->cpu_table->len,
355 100.0/data->cpu_table->len);
356 print_headers(2, "Threads", data->nbthreads, data->nbnewthreads,
357 -1*(data->nbdeadthreads));
358 print_headers(3, "FDs", data->nbfiles, data->nbnewfiles,
359 -1*(data->nbclosedfiles));
360 scale_unit(total_io(), io);
361 mvwprintw(header, 3, 43, "%sB/sec", io);
362 wrefresh(header);
363 }
364
365 gint sort_by_cpu_desc(gconstpointer p1, gconstpointer p2)
366 {
367 struct processtop *n1 = *(struct processtop **)p1;
368 struct processtop *n2 = *(struct processtop **)p2;
369 unsigned long totaln1 = n1->totalcpunsec;
370 unsigned long totaln2 = n2->totalcpunsec;
371
372 if (totaln1 < totaln2)
373 return 1;
374 if (totaln1 == totaln2)
375 return 0;
376 return -1;
377 }
378
379 gint sort_by_tid_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->tid;
384 unsigned long totaln2 = n2->tid;
385
386 if (totaln1 < totaln2)
387 return 1;
388 if (totaln1 == totaln2)
389 return 0;
390 return -1;
391 }
392
393 gint sort_by_pid_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->pid;
398 unsigned long totaln2 = n2->pid;
399
400 if (totaln1 < totaln2)
401 return 1;
402 if (totaln1 == totaln2)
403 return 0;
404 return -1;
405 }
406
407 gint sort_by_cpu_group_by_threads_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->threadstotalcpunsec;
412 unsigned long totaln2 = n2->threadstotalcpunsec;
413
414 if (totaln1 < totaln2)
415 return 1;
416 if (totaln1 == totaln2)
417 return 0;
418 return -1;
419 }
420
421 void update_cputop_display()
422 {
423 int i;
424 int header_offset = 2;
425 struct processtop *tmp;
426 unsigned long elapsed;
427 double maxcputime;
428 int nblinedisplayed = 0;
429 int current_line = 0;
430
431 elapsed = data->end - data->start;
432 maxcputime = elapsed * data->cpu_table->len / 100.0;
433
434 if (cputopview[0].sort == 1)
435 g_ptr_array_sort(data->process_table, sort_by_cpu_desc);
436 else if (cputopview[1].sort == 1)
437 g_ptr_array_sort(data->process_table, sort_by_pid_desc);
438 else if (cputopview[2].sort == 1)
439 g_ptr_array_sort(data->process_table, sort_by_tid_desc);
440 else if (cputopview[3].sort == 1)
441 g_ptr_array_sort(data->process_table, sort_by_cpu_desc);
442 else
443 g_ptr_array_sort(data->process_table, sort_by_cpu_desc);
444
445 set_window_title(center, "CPU Top");
446 wattron(center, A_BOLD);
447 mvwprintw(center, 1, 1, cputopview[0].title);
448 mvwprintw(center, 1, 12, cputopview[1].title);
449 mvwprintw(center, 1, 22, cputopview[2].title);
450 mvwprintw(center, 1, 32, cputopview[3].title);
451 wattroff(center, A_BOLD);
452
453 max_center_lines = LINES - 5 - 7 - 1 - header_offset;
454
455 /* iterate the process (thread) list */
456 for (i = list_offset; i < data->process_table->len &&
457 nblinedisplayed < max_center_lines; i++) {
458 tmp = g_ptr_array_index(data->process_table, i);
459
460 /* FIXME : random segfault here */
461 if (process_selected(tmp)) {
462 wattron(center, COLOR_PAIR(6));
463 mvwhline(center, current_line + header_offset, 1, ' ', COLS-3);
464 }
465 if (current_line == selected_line) {
466 selected_process = tmp;
467 wattron(center, COLOR_PAIR(5));
468 mvwhline(center, current_line + header_offset, 1, ' ', COLS-3);
469 }
470 /* CPU(%) */
471 mvwprintw(center, current_line + header_offset, 1, "%1.2f",
472 tmp->totalcpunsec / maxcputime);
473 /* TGID */
474 mvwprintw(center, current_line + header_offset, 12, "%d", tmp->pid);
475 /* PID */
476 mvwprintw(center, current_line + header_offset, 22, "%d", tmp->tid);
477 /* NAME */
478 mvwprintw(center, current_line + header_offset, 32, "%s", tmp->comm);
479 wattroff(center, COLOR_PAIR(6));
480 wattroff(center, COLOR_PAIR(5));
481 nblinedisplayed++;
482 current_line++;
483 }
484 }
485
486 gint sort_perf(gconstpointer p1, gconstpointer p2, gpointer key)
487 {
488 struct processtop *n1 = *(struct processtop **) p1;
489 struct processtop *n2 = *(struct processtop **) p2;
490
491 struct perfcounter *tmp1, *tmp2;
492 unsigned long totaln2 = 0;
493 unsigned long totaln1 = 0;
494
495 if (!key)
496 return 0;
497
498 tmp1 = g_hash_table_lookup(n1->perf, key);
499 if (!tmp1)
500 totaln1 = 0;
501 else
502 totaln1 = tmp1->count;
503
504 tmp2 = g_hash_table_lookup(n2->perf, key);
505 if (!tmp2)
506 totaln2 = 0;
507 else
508 totaln2 = tmp2->count;
509
510 if (totaln1 < totaln2)
511 return 1;
512 if (totaln1 == totaln2) {
513 totaln1 = n1->tid;
514 totaln2 = n2->tid;
515 if (totaln1 < totaln2)
516 return 1;
517 return -1;
518 }
519 return -1;
520 }
521
522 void print_key_title(char *key, int line)
523 {
524 wattron(center, A_BOLD);
525 mvwprintw(center, line, 1, "%s\t", key);
526 wattroff(center, A_BOLD);
527 }
528
529 void update_process_details()
530 {
531 unsigned long elapsed;
532 double maxcputime;
533 struct processtop *tmp;
534 struct files *file_tmp;
535 int i, j = 0;
536 char unit[4];
537 char filename_buf[COLS];
538
539 set_window_title(center, "Process details");
540
541
542 tmp = find_process_tid(data,
543 selected_process->tid,
544 selected_process->comm);
545 elapsed = data->end - data->start;
546 maxcputime = elapsed * data->cpu_table->len / 100.0;
547
548 print_key_title("Name", 1);
549 wprintw(center, "%s", selected_process->comm);
550 print_key_title("TID", 2);
551 wprintw(center, "%d", selected_process->tid);
552 if (!tmp) {
553 print_key_title("Does not exit at this time", 3);
554 return;
555 }
556
557 print_key_title("PID", 3);
558 wprintw(center, "%d", tmp->pid);
559 print_key_title("PPID", 4);
560 wprintw(center, "%d", tmp->ppid);
561 print_key_title("CPU", 5);
562 wprintw(center, "%1.2f %%", tmp->totalcpunsec/maxcputime);
563
564 print_key_title("READ B/s", 6);
565 scale_unit(tmp->fileread, unit);
566 wprintw(center, "%s", unit);
567
568 print_key_title("WRITE B/s", 7);
569 scale_unit(tmp->filewrite, unit);
570 wprintw(center, "%s", unit);
571
572 wattron(center, A_BOLD);
573 mvwprintw(center, 8, 1, "FD");
574 mvwprintw(center, 8, 10, "READ");
575 mvwprintw(center, 8, 17, "WRITE");
576 mvwprintw(center, 8, 24, "FILENAME");
577 wattroff(center, A_BOLD);
578
579 for (i = selected_line; i < tmp->process_files_table->len &&
580 i < (selected_line + max_center_lines - 7); i++) {
581 file_tmp = get_file(tmp, i);
582 if (file_tmp != NULL) {
583 mvwprintw(center, 9 + j, 1, "%d", i);
584 scale_unit(file_tmp->read, unit);
585 mvwprintw(center, 9 + j, 10, "%s", unit);
586 scale_unit(file_tmp->write, unit);
587 mvwprintw(center, 9 + j, 17, "%s", unit);
588 snprintf(filename_buf, COLS - 25, "%s", file_tmp->name);
589 mvwprintw(center, 9 + j, 24, "%s", filename_buf);
590 j++;
591 }
592 }
593 }
594
595 void update_perf()
596 {
597 int i;
598 int nblinedisplayed = 0;
599 int current_line = 0;
600 struct processtop *tmp;
601 int header_offset = 2;
602 int perf_row = 40;
603 struct perfcounter *perfn1, *perfn2;
604 char *perf_key = NULL;
605 int value;
606 GHashTableIter iter;
607 gpointer key;
608
609 set_window_title(center, "Perf Top");
610 wattron(center, A_BOLD);
611 mvwprintw(center, 1, 1, "PID");
612 mvwprintw(center, 1, 11, "TID");
613 mvwprintw(center, 1, 22, "NAME");
614
615 perf_row = 40;
616 g_hash_table_iter_init(&iter, global_perf_liszt);
617 while (g_hash_table_iter_next (&iter, &key, (gpointer) &perfn1)) {
618 if (perfn1->visible) {
619 /* + 5 to strip the "perf_" prefix */
620 mvwprintw(center, 1, perf_row, "%s",
621 (char *) key + 5);
622 perf_row += 20;
623 }
624 if (perfn1->sort) {
625 perf_key = (char *) key;
626 }
627 }
628
629 wattroff(center, A_BOLD);
630
631 g_ptr_array_sort_with_data(data->process_table, sort_perf, perf_key);
632
633 for (i = 0; i < data->process_table->len &&
634 nblinedisplayed < max_center_lines; i++) {
635 tmp = g_ptr_array_index(data->process_table, i);
636
637 if (process_selected(tmp)) {
638 wattron(center, COLOR_PAIR(6));
639 mvwhline(center, current_line + header_offset, 1, ' ', COLS-3);
640 }
641 if (current_line == selected_line) {
642 selected_process = tmp;
643 wattron(center, COLOR_PAIR(5));
644 mvwhline(center, current_line + header_offset, 1, ' ', COLS-3);
645 }
646
647 mvwprintw(center, current_line + header_offset, 1, "%d", tmp->pid);
648 mvwprintw(center, current_line + header_offset, 11, "%d", tmp->tid);
649 mvwprintw(center, current_line + header_offset, 22, "%s", tmp->comm);
650
651 g_hash_table_iter_init(&iter, global_perf_liszt);
652
653 perf_row = 40;
654 while (g_hash_table_iter_next (&iter, &key, (gpointer) &perfn1)) {
655 if (perfn1->visible) {
656 perfn2 = g_hash_table_lookup(tmp->perf, (char *) key);
657 if (perfn2)
658 value = perfn2->count;
659 else
660 value = 0;
661 mvwprintw(center, current_line + header_offset,
662 perf_row, "%d", value);
663 perf_row += 20;
664 }
665 }
666
667 wattroff(center, COLOR_PAIR(6));
668 wattroff(center, COLOR_PAIR(5));
669 nblinedisplayed++;
670 current_line++;
671 }
672 }
673
674 gint sort_by_ret_desc(gconstpointer p1, gconstpointer p2)
675 {
676 struct processtop *n1 = *(struct processtop **)p1;
677 struct processtop *n2 = *(struct processtop **)p2;
678
679 unsigned long totaln1 = n1->totalfileread + n1->totalfilewrite;
680 unsigned long totaln2 = n2->totalfileread + n2->totalfilewrite;
681
682 if (totaln1 < totaln2)
683 return 1;
684 if (totaln1 == totaln2)
685 return 0;
686 return -1;
687 }
688
689 void update_iostream()
690 {
691 int i;
692 int header_offset = 2;
693 struct processtop *tmp;
694 int nblinedisplayed = 0;
695 int current_line = 0;
696 int total = 0;
697 char unit[4];
698
699 set_window_title(center, "IO Top");
700 wattron(center, A_BOLD);
701 mvwprintw(center, 1, 1, "PID");
702 mvwprintw(center, 1, 11, "TID");
703 mvwprintw(center, 1, 22, "NAME");
704 mvwprintw(center, 1, 40, "R (B/sec)");
705 mvwprintw(center, 1, 52, "W (B/sec)");
706 mvwprintw(center, 1, 64, "Total");
707 wattroff(center, A_BOLD);
708
709 g_ptr_array_sort(data->process_table, sort_by_ret_desc);
710
711 for (i = list_offset; i < data->process_table->len &&
712 nblinedisplayed < max_center_lines; i++) {
713 tmp = g_ptr_array_index(data->process_table, i);
714
715 if (process_selected(tmp)) {
716 wattron(center, COLOR_PAIR(6));
717 mvwhline(center, current_line + header_offset, 1, ' ', COLS-3);
718 }
719 if (current_line == selected_line) {
720 selected_process = tmp;
721 wattron(center, COLOR_PAIR(5));
722 mvwhline(center, current_line + header_offset, 1, ' ', COLS-3);
723 }
724 /* TGID */
725 mvwprintw(center, current_line + header_offset, 1, "%d", tmp->pid);
726 /* PID */
727 mvwprintw(center, current_line + header_offset, 11, "%d", tmp->tid);
728 /* NAME */
729 mvwprintw(center, current_line + header_offset, 22, "%s", tmp->comm);
730
731 /* READ (bytes/sec) */
732 scale_unit(tmp->fileread, unit);
733 mvwprintw(center, current_line + header_offset, 40, "%s", unit);
734
735 /* WRITE (bytes/sec) */
736 scale_unit(tmp->filewrite, unit);
737 mvwprintw(center, current_line + header_offset, 52, "%s", unit);
738
739 /* TOTAL STREAM */
740 total = tmp->totalfileread + tmp->totalfilewrite;
741
742 scale_unit(total, unit);
743 mvwprintw(center, current_line + header_offset, 64, "%s", unit);
744
745 wattroff(center, COLOR_PAIR(6));
746 wattroff(center, COLOR_PAIR(5));
747 nblinedisplayed++;
748 current_line++;
749 }
750 }
751
752 void update_current_view()
753 {
754 sem_wait(&update_display_sem);
755 if (!data)
756 return;
757 update_header();
758
759 werase(center);
760 box(center, 0, 0);
761 switch (current_view) {
762 case cpu:
763 update_cputop_display();
764 break;
765 case perf:
766 update_perf();
767 break;
768 case process_details:
769 update_process_details();
770 break;
771 case iostream:
772 update_iostream();
773 break;
774 case tree:
775 update_cputop_display();
776 break;
777 default:
778 break;
779 }
780 update_panels();
781 doupdate();
782 sem_post(&update_display_sem);
783 }
784
785 void update_cpu_pref(int *line_selected, int toggle_view, int toggle_sort)
786 {
787 int i;
788 int size;
789
790 if (!data)
791 return;
792 if (pref_panel_window) {
793 del_panel(pref_panel);
794 delwin(pref_panel_window);
795 }
796 size = 4;
797
798 pref_panel_window = create_window(size + 2, 30, 10, 10);
799 pref_panel = new_panel(pref_panel_window);
800
801 werase(pref_panel_window);
802 box(pref_panel_window, 0 , 0);
803 set_window_title(pref_panel_window, "CPUTop Preferences ");
804 wattron(pref_panel_window, A_BOLD);
805 mvwprintw(pref_panel_window, size + 1, 1,
806 " 's' to sort");
807 wattroff(pref_panel_window, A_BOLD);
808
809 if (*line_selected > 3)
810 *line_selected = 3;
811 if (toggle_sort == 1) {
812 /* special case, we don't support sorting by procname for now */
813 if (*line_selected != 3) {
814 if (cputopview[*line_selected].sort == 1)
815 cputopview[*line_selected].reverse = 1;
816 for (i = 0; i < 4; i++)
817 cputopview[i].sort = 0;
818 cputopview[*line_selected].sort = 1;
819 update_current_view();
820 }
821 }
822
823 for (i = 0; i < 4; i++) {
824 if (i == *line_selected) {
825 wattron(pref_panel_window, COLOR_PAIR(5));
826 mvwhline(pref_panel_window, i + 1, 1, ' ', 30 - 2);
827 }
828 if (cputopview[i].sort == 1)
829 wattron(pref_panel_window, A_BOLD);
830 mvwprintw(pref_panel_window, i + 1, 1, "[x] %s",
831 cputopview[i].title);
832 wattroff(pref_panel_window, A_BOLD);
833 wattroff(pref_panel_window, COLOR_PAIR(5));
834
835 }
836 update_panels();
837 doupdate();
838 }
839
840 void update_perf_pref(int *line_selected, int toggle_view, int toggle_sort)
841 {
842 int i;
843 struct perfcounter *perf;
844 GList *perflist;
845 int size;
846
847 if (!data)
848 return;
849 if (pref_panel_window) {
850 del_panel(pref_panel);
851 delwin(pref_panel_window);
852 }
853 size = g_hash_table_size(global_perf_liszt);
854
855 pref_panel_window = create_window(size + 2, 30, 10, 10);
856 pref_panel = new_panel(pref_panel_window);
857
858 werase(pref_panel_window);
859 box(pref_panel_window, 0 , 0);
860 set_window_title(pref_panel_window, "Perf Preferences ");
861 wattron(pref_panel_window, A_BOLD);
862 mvwprintw(pref_panel_window, g_hash_table_size(global_perf_liszt) + 1, 1,
863 " 's' to sort");
864 wattroff(pref_panel_window, A_BOLD);
865
866 if (toggle_sort == 1) {
867 i = 0;
868 perflist = g_list_first(g_hash_table_get_keys(global_perf_liszt));
869 while (perflist) {
870 perf = g_hash_table_lookup(global_perf_liszt, perflist->data);
871 if (i != *line_selected)
872 perf->sort = 0;
873 else
874 perf->sort = 1;
875 i++;
876 perflist = g_list_next(perflist);
877 }
878 update_current_view();
879 }
880
881 i = 0;
882 perflist = g_list_first(g_hash_table_get_keys(global_perf_liszt));
883 while (perflist) {
884 perf = g_hash_table_lookup(global_perf_liszt, perflist->data);
885 if (i == *line_selected && toggle_view == 1) {
886 perf->visible = perf->visible == 1 ? 0:1;
887 update_current_view();
888 }
889 if (i == *line_selected) {
890 wattron(pref_panel_window, COLOR_PAIR(5));
891 mvwhline(pref_panel_window, i + 1, 1, ' ', 30 - 2);
892 }
893 if (perf->sort == 1)
894 wattron(pref_panel_window, A_BOLD);
895 mvwprintw(pref_panel_window, i + 1, 1, "[%c] %s",
896 perf->visible == 1 ? 'x' : ' ',
897 (char *) perflist->data + 5);
898 wattroff(pref_panel_window, A_BOLD);
899 wattroff(pref_panel_window, COLOR_PAIR(5));
900 i++;
901 perflist = g_list_next(perflist);
902 }
903 update_panels();
904 doupdate();
905 }
906
907 int update_preference_panel(int *line_selected, int toggle_view, int toggle_sort)
908 {
909 int ret = 0;
910
911 switch(current_view) {
912 case perf:
913 update_perf_pref(line_selected, toggle_view, toggle_sort);
914 break;
915 case cpu:
916 update_cpu_pref(line_selected, toggle_view, toggle_sort);
917 break;
918 default:
919 ret = -1;
920 break;
921 }
922
923 return ret;
924 }
925
926 void toggle_pref_panel(void)
927 {
928 int ret;
929
930 if (pref_panel_visible) {
931 hide_panel(pref_panel);
932 pref_panel_visible = 0;
933 } else {
934 ret = update_preference_panel(&pref_line_selected, 0, 0);
935 if (ret < 0)
936 return;
937 show_panel(pref_panel);
938 pref_panel_visible = 1;
939 }
940 update_panels();
941 doupdate();
942 }
943
944 void display(unsigned int index)
945 {
946 last_display_index = index;
947 currently_displayed_index = index;
948 data = g_ptr_array_index(copies, index);
949 if (!data)
950 return;
951 max_elements = data->process_table->len;
952 update_current_view();
953 update_footer();
954 update_panels();
955 doupdate();
956 }
957
958 void pause_display()
959 {
960 toggle_pause = 1;
961 print_log("Pause");
962 sem_wait(&pause_sem);
963 }
964
965 void resume_display()
966 {
967 toggle_pause = -1;
968 print_log("Resume");
969 sem_post(&pause_sem);
970 }
971
972 void *handle_keyboard(void *p)
973 {
974 int ch;
975 while((ch = getch())) {
976 switch(ch) {
977 /* Move the cursor and scroll */
978 case KEY_DOWN:
979 if (pref_panel_visible) {
980 pref_line_selected++;
981 update_preference_panel(&pref_line_selected, 0, 0);
982 } else {
983 if (selected_line < (max_center_lines - 1) &&
984 selected_line < max_elements - 1) {
985 selected_line++;
986 selected_in_list++;
987 } else if (selected_in_list < (max_elements - 1)
988 && (list_offset < (max_elements - max_center_lines))) {
989 selected_in_list++;
990 list_offset++;
991 }
992 update_current_view();
993 }
994 break;
995 case KEY_NPAGE:
996 break;
997 case KEY_UP:
998 if (pref_panel_visible) {
999 if (pref_line_selected > 0)
1000 pref_line_selected--;
1001 update_preference_panel(&pref_line_selected, 0, 0);
1002 } else {
1003 if (selected_line > 0) {
1004 selected_line--;
1005 selected_in_list--;
1006 } else if (selected_in_list > 0 && list_offset > 0) {
1007 selected_in_list--;
1008 list_offset--;
1009 }
1010 update_current_view();
1011 }
1012 break;
1013 case KEY_PPAGE:
1014 break;
1015
1016 /* Navigate the history with arrows */
1017 case KEY_LEFT:
1018 if (currently_displayed_index > 0) {
1019 currently_displayed_index--;
1020 print_log("Going back in time");
1021 } else {
1022 print_log("Cannot rewind, last data is already displayed");
1023 }
1024 data = g_ptr_array_index(copies, currently_displayed_index);
1025 max_elements = data->process_table->len;
1026
1027 /* we force to pause the display when moving in time */
1028 if (toggle_pause < 0)
1029 pause_display();
1030
1031 update_current_view();
1032 update_footer();
1033 break;
1034 case KEY_RIGHT:
1035 if (currently_displayed_index < last_display_index) {
1036 currently_displayed_index++;
1037 print_log("Going forward in time");
1038 data = g_ptr_array_index(copies, currently_displayed_index);
1039 max_elements = data->process_table->len;
1040 update_current_view();
1041 update_footer();
1042 } else {
1043 print_log("Manually moving forward");
1044 sem_post(&timer);
1045 /* we force to resume the refresh when moving forward */
1046 if (toggle_pause > 0)
1047 resume_display();
1048 }
1049
1050 break;
1051 case ' ':
1052 if (pref_panel_visible) {
1053 update_preference_panel(&pref_line_selected, 1, 0);
1054 } else {
1055 update_selected_processes();
1056 update_current_view();
1057 }
1058 break;
1059 case 's':
1060 if (pref_panel_visible)
1061 update_preference_panel(&pref_line_selected, 0, 1);
1062 break;
1063
1064 case 13: /* FIXME : KEY_ENTER ?? */
1065 if (current_view != process_details) {
1066 previous_view = current_view;
1067 current_view = process_details;
1068 } else {
1069 current_view = previous_view;
1070 previous_view = process_details;
1071 }
1072 update_current_view();
1073 break;
1074
1075 case KEY_F(1):
1076 if (pref_panel_visible)
1077 toggle_pref_panel();
1078 current_view = cpu;
1079 selected_line = 0;
1080 update_current_view();
1081 break;
1082 case KEY_F(2):
1083 if (pref_panel_visible)
1084 toggle_pref_panel();
1085 current_view = cpu;
1086 selected_line = 0;
1087 update_current_view();
1088 break;
1089 case KEY_F(3):
1090 if (pref_panel_visible)
1091 toggle_pref_panel();
1092 current_view = perf;
1093 selected_line = 0;
1094 update_current_view();
1095 break;
1096 case KEY_F(4):
1097 if (pref_panel_visible)
1098 toggle_pref_panel();
1099 current_view = iostream;
1100 selected_line = 0;
1101 update_current_view();
1102 break;
1103 case KEY_F(10):
1104 case 'q':
1105 reset_ncurses();
1106 break;
1107 case 't':
1108 toggle_threads *= -1;
1109 update_current_view();
1110 break;
1111 case 'p':
1112 if (toggle_pause < 0) {
1113 pause_display();
1114 } else {
1115 resume_display();
1116 }
1117 break;
1118 case 'P':
1119 toggle_pref_panel();
1120 break;
1121 default:
1122 if (data)
1123 update_current_view();
1124 break;
1125 }
1126 update_footer();
1127 }
1128 return NULL;
1129 }
1130
1131 void init_ncurses()
1132 {
1133 selected_processes = g_ptr_array_new();
1134 sem_init(&update_display_sem, 0, 1);
1135 init_screen();
1136
1137 header = create_window(5, COLS - 1, 0, 0);
1138 center = create_window(LINES - 5 - 7, COLS - 1, 5, 0);
1139 status = create_window(MAX_LOG_LINES + 2, COLS - 1, LINES - 7, 0);
1140 footer = create_window(1, COLS - 1, LINES - 1, 0);
1141
1142 cputopview[0].title = strdup("CPU(%)");
1143 cputopview[0].sort = 1;
1144 cputopview[1].title = strdup("TGID");
1145 cputopview[2].title = strdup("PID");
1146 cputopview[3].title = strdup("NAME");
1147 print_log("Starting display");
1148
1149 main_panel = new_panel(center);
1150
1151 current_view = cpu;
1152
1153 basic_header();
1154 update_footer();
1155
1156 pthread_create(&keyboard_thread, NULL, handle_keyboard, (void *)NULL);
1157 }
This page took 0.081956 seconds and 3 git commands to generate.