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