source: mainline/uspace/app/top/top.c

Last change on this file was 87822ce, checked in by Jiri Svoboda <jiri@…>, 4 years ago

Avoid infinite loop when console communication is broken

Need to make sure callers of console_get_event_timeout() can distinguish
between timeout and I/O error. Fix all callers of console_get_event()
and console_get_event_timeout() not to enter infinite loop when console
connection is broken. Also avoid setting of errno variable.

  • Property mode set to 100644
File size: 18.5 KB
Line 
1/*
2 * Copyright (c) 2010 Stanislav Kozina
3 * Copyright (c) 2010 Martin Decky
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/** @addtogroup top
31 * @brief Top utility.
32 * @{
33 */
34/**
35 * @file
36 */
37
38#include <stdio.h>
39#include <stdlib.h>
40#include <task.h>
41#include <time.h>
42#include <errno.h>
43#include <gsort.h>
44#include <str.h>
45#include "screen.h"
46#include "top.h"
47
48#define NAME "top"
49
50#define UPDATE_INTERVAL 1
51
52#define DAY 86400
53#define HOUR 3600
54#define MINUTE 60
55
56typedef enum {
57 OP_TASKS,
58 OP_IPC,
59 OP_EXCS,
60} op_mode_t;
61
62static const column_t task_columns[] = {
63 { "taskid", 't', 8 },
64 { "thrds", 'h', 7 },
65 { "resident", 'r', 10 },
66 { "%resi", 'R', 7 },
67 { "virtual", 'v', 9 },
68 { "%virt", 'V', 7 },
69 { "%user", 'U', 7 },
70 { "%kern", 'K', 7 },
71 { "name", 'd', 0 },
72};
73
74enum {
75 TASK_COL_ID = 0,
76 TASK_COL_NUM_THREADS,
77 TASK_COL_RESIDENT,
78 TASK_COL_PERCENT_RESIDENT,
79 TASK_COL_VIRTUAL,
80 TASK_COL_PERCENT_VIRTUAL,
81 TASK_COL_PERCENT_USER,
82 TASK_COL_PERCENT_KERNEL,
83 TASK_COL_NAME,
84 TASK_NUM_COLUMNS,
85};
86
87static const column_t ipc_columns[] = {
88 { "taskid", 't', 8 },
89 { "cls snt", 'c', 9 },
90 { "cls rcv", 'C', 9 },
91 { "ans snt", 'a', 9 },
92 { "ans rcv", 'A', 9 },
93 { "forward", 'f', 9 },
94 { "name", 'd', 0 },
95};
96
97enum {
98 IPC_COL_TASKID = 0,
99 IPC_COL_CLS_SNT,
100 IPC_COL_CLS_RCV,
101 IPC_COL_ANS_SNT,
102 IPC_COL_ANS_RCV,
103 IPC_COL_FORWARD,
104 IPC_COL_NAME,
105 IPC_NUM_COLUMNS,
106};
107
108static const column_t exception_columns[] = {
109 { "exc", 'e', 8 },
110 { "count", 'n', 10 },
111 { "%count", 'N', 8 },
112 { "cycles", 'c', 10 },
113 { "%cycles", 'C', 9 },
114 { "description", 'd', 0 },
115};
116
117enum {
118 EXCEPTION_COL_ID = 0,
119 EXCEPTION_COL_COUNT,
120 EXCEPTION_COL_PERCENT_COUNT,
121 EXCEPTION_COL_CYCLES,
122 EXCEPTION_COL_PERCENT_CYCLES,
123 EXCEPTION_COL_DESCRIPTION,
124 EXCEPTION_NUM_COLUMNS,
125};
126
127screen_mode_t screen_mode = SCREEN_TABLE;
128static op_mode_t op_mode = OP_TASKS;
129static size_t sort_column = TASK_COL_PERCENT_USER;
130static int sort_reverse = -1;
131static bool excs_all = false;
132
133static const char *read_data(data_t *target)
134{
135 /* Initialize data */
136 target->load = NULL;
137 target->cpus = NULL;
138 target->cpus_perc = NULL;
139 target->tasks = NULL;
140 target->tasks_perc = NULL;
141 target->threads = NULL;
142 target->exceptions = NULL;
143 target->exceptions_perc = NULL;
144 target->physmem = NULL;
145 target->ucycles_diff = NULL;
146 target->kcycles_diff = NULL;
147 target->ecycles_diff = NULL;
148 target->ecount_diff = NULL;
149 target->table.name = NULL;
150 target->table.num_columns = 0;
151 target->table.columns = NULL;
152 target->table.num_fields = 0;
153 target->table.fields = NULL;
154
155 /* Get current time */
156 struct timespec time;
157 getrealtime(&time);
158
159 target->hours = (time.tv_sec % DAY) / HOUR;
160 target->minutes = (time.tv_sec % HOUR) / MINUTE;
161 target->seconds = time.tv_sec % MINUTE;
162
163 /* Get uptime */
164 struct timespec uptime;
165 getuptime(&uptime);
166
167 target->udays = uptime.tv_sec / DAY;
168 target->uhours = (uptime.tv_sec % DAY) / HOUR;
169 target->uminutes = (uptime.tv_sec % HOUR) / MINUTE;
170 target->useconds = uptime.tv_sec % MINUTE;
171
172 /* Get load */
173 target->load = stats_get_load(&(target->load_count));
174 if (target->load == NULL)
175 return "Cannot get system load";
176
177 /* Get CPUs */
178 target->cpus = stats_get_cpus(&(target->cpus_count));
179 if (target->cpus == NULL)
180 return "Cannot get CPUs";
181
182 target->cpus_perc =
183 (perc_cpu_t *) calloc(target->cpus_count, sizeof(perc_cpu_t));
184 if (target->cpus_perc == NULL)
185 return "Not enough memory for CPU utilization";
186
187 /* Get tasks */
188 target->tasks = stats_get_tasks(&(target->tasks_count));
189 if (target->tasks == NULL)
190 return "Cannot get tasks";
191
192 target->tasks_perc =
193 (perc_task_t *) calloc(target->tasks_count, sizeof(perc_task_t));
194 if (target->tasks_perc == NULL)
195 return "Not enough memory for task utilization";
196
197 /* Get threads */
198 target->threads = stats_get_threads(&(target->threads_count));
199 if (target->threads == NULL)
200 return "Cannot get threads";
201
202 /* Get Exceptions */
203 target->exceptions = stats_get_exceptions(&(target->exceptions_count));
204 if (target->exceptions == NULL)
205 return "Cannot get exceptions";
206
207 target->exceptions_perc =
208 (perc_exc_t *) calloc(target->exceptions_count, sizeof(perc_exc_t));
209 if (target->exceptions_perc == NULL)
210 return "Not enough memory for exception utilization";
211
212 /* Get physical memory */
213 target->physmem = stats_get_physmem();
214 if (target->physmem == NULL)
215 return "Cannot get physical memory";
216
217 target->ucycles_diff = calloc(target->tasks_count,
218 sizeof(uint64_t));
219 if (target->ucycles_diff == NULL)
220 return "Not enough memory for user utilization";
221
222 /* Allocate memory for computed values */
223 target->kcycles_diff = calloc(target->tasks_count,
224 sizeof(uint64_t));
225 if (target->kcycles_diff == NULL)
226 return "Not enough memory for kernel utilization";
227
228 target->ecycles_diff = calloc(target->exceptions_count,
229 sizeof(uint64_t));
230 if (target->ecycles_diff == NULL)
231 return "Not enough memory for exception cycles utilization";
232
233 target->ecount_diff = calloc(target->exceptions_count,
234 sizeof(uint64_t));
235 if (target->ecount_diff == NULL)
236 return "Not enough memory for exception count utilization";
237
238 return NULL;
239}
240
241/** Computes percentage differencies from old_data to new_data
242 *
243 * @param old_data Pointer to old data strucutre.
244 * @param new_data Pointer to actual data where percetages are stored.
245 *
246 */
247static void compute_percentages(data_t *old_data, data_t *new_data)
248{
249 /*
250 * For each CPU: Compute total cycles and divide it between
251 * user and kernel
252 */
253
254 size_t i;
255 for (i = 0; i < new_data->cpus_count; i++) {
256 uint64_t idle =
257 new_data->cpus[i].idle_cycles - old_data->cpus[i].idle_cycles;
258 uint64_t busy =
259 new_data->cpus[i].busy_cycles - old_data->cpus[i].busy_cycles;
260 uint64_t sum = idle + busy;
261
262 FRACTION_TO_FLOAT(new_data->cpus_perc[i].idle, idle * 100, sum);
263 FRACTION_TO_FLOAT(new_data->cpus_perc[i].busy, busy * 100, sum);
264 }
265
266 /* For all tasks compute sum and differencies of all cycles */
267
268 uint64_t virtmem_total = 0;
269 uint64_t resmem_total = 0;
270 uint64_t ucycles_total = 0;
271 uint64_t kcycles_total = 0;
272
273 for (i = 0; i < new_data->tasks_count; i++) {
274 /* Match task with the previous instance */
275
276 bool found = false;
277 size_t j;
278 for (j = 0; j < old_data->tasks_count; j++) {
279 if (new_data->tasks[i].task_id == old_data->tasks[j].task_id) {
280 found = true;
281 break;
282 }
283 }
284
285 if (!found) {
286 /* This is newly borned task, ignore it */
287 new_data->ucycles_diff[i] = 0;
288 new_data->kcycles_diff[i] = 0;
289 continue;
290 }
291
292 new_data->ucycles_diff[i] =
293 new_data->tasks[i].ucycles - old_data->tasks[j].ucycles;
294 new_data->kcycles_diff[i] =
295 new_data->tasks[i].kcycles - old_data->tasks[j].kcycles;
296
297 virtmem_total += new_data->tasks[i].virtmem;
298 resmem_total += new_data->tasks[i].resmem;
299 ucycles_total += new_data->ucycles_diff[i];
300 kcycles_total += new_data->kcycles_diff[i];
301 }
302
303 /* For each task compute percential change */
304
305 for (i = 0; i < new_data->tasks_count; i++) {
306 FRACTION_TO_FLOAT(new_data->tasks_perc[i].virtmem,
307 new_data->tasks[i].virtmem * 100, virtmem_total);
308 FRACTION_TO_FLOAT(new_data->tasks_perc[i].resmem,
309 new_data->tasks[i].resmem * 100, resmem_total);
310 FRACTION_TO_FLOAT(new_data->tasks_perc[i].ucycles,
311 new_data->ucycles_diff[i] * 100, ucycles_total);
312 FRACTION_TO_FLOAT(new_data->tasks_perc[i].kcycles,
313 new_data->kcycles_diff[i] * 100, kcycles_total);
314 }
315
316 /* For all exceptions compute sum and differencies of cycles */
317
318 uint64_t ecycles_total = 0;
319 uint64_t ecount_total = 0;
320
321 for (i = 0; i < new_data->exceptions_count; i++) {
322 /*
323 * March exception with the previous instance.
324 * This is quite paranoid since exceptions do not
325 * usually disappear, but it does not hurt.
326 */
327
328 bool found = false;
329 size_t j;
330 for (j = 0; j < old_data->exceptions_count; j++) {
331 if (new_data->exceptions[i].id == old_data->exceptions[j].id) {
332 found = true;
333 break;
334 }
335 }
336
337 if (!found) {
338 /* This is a new exception, ignore it */
339 new_data->ecycles_diff[i] = 0;
340 new_data->ecount_diff[i] = 0;
341 continue;
342 }
343
344 new_data->ecycles_diff[i] =
345 new_data->exceptions[i].cycles - old_data->exceptions[j].cycles;
346 new_data->ecount_diff[i] =
347 new_data->exceptions[i].count - old_data->exceptions[i].count;
348
349 ecycles_total += new_data->ecycles_diff[i];
350 ecount_total += new_data->ecount_diff[i];
351 }
352
353 /* For each exception compute percential change */
354
355 for (i = 0; i < new_data->exceptions_count; i++) {
356 FRACTION_TO_FLOAT(new_data->exceptions_perc[i].cycles,
357 new_data->ecycles_diff[i] * 100, ecycles_total);
358 FRACTION_TO_FLOAT(new_data->exceptions_perc[i].count,
359 new_data->ecount_diff[i] * 100, ecount_total);
360 }
361}
362
363static int cmp_data(void *a, void *b, void *arg)
364{
365 field_t *fa = (field_t *)a + sort_column;
366 field_t *fb = (field_t *)b + sort_column;
367
368 if (fa->type > fb->type)
369 return 1 * sort_reverse;
370
371 if (fa->type < fb->type)
372 return -1 * sort_reverse;
373
374 switch (fa->type) {
375 case FIELD_EMPTY:
376 return 0;
377 case FIELD_UINT_SUFFIX_BIN: /* fallthrough */
378 case FIELD_UINT_SUFFIX_DEC: /* fallthrough */
379 case FIELD_UINT:
380 if (fa->uint > fb->uint)
381 return 1 * sort_reverse;
382 if (fa->uint < fb->uint)
383 return -1 * sort_reverse;
384 return 0;
385 case FIELD_PERCENT:
386 if (fa->fixed.upper * fb->fixed.lower >
387 fb->fixed.upper * fa->fixed.lower)
388 return 1 * sort_reverse;
389 if (fa->fixed.upper * fb->fixed.lower <
390 fb->fixed.upper * fa->fixed.lower)
391 return -1 * sort_reverse;
392 return 0;
393 case FIELD_STRING:
394 return str_cmp(fa->string, fb->string) * sort_reverse;
395 }
396
397 return 0;
398}
399
400static void sort_table(table_t *table)
401{
402 if (sort_column >= table->num_columns)
403 sort_column = 0;
404 /* stable sort is probably best, so we use gsort */
405 gsort((void *) table->fields, table->num_fields / table->num_columns,
406 sizeof(field_t) * table->num_columns, cmp_data, NULL);
407}
408
409static const char *fill_task_table(data_t *data)
410{
411 data->table.name = "Tasks";
412 data->table.num_columns = TASK_NUM_COLUMNS;
413 data->table.columns = task_columns;
414 data->table.num_fields = data->tasks_count * TASK_NUM_COLUMNS;
415 data->table.fields = calloc(data->table.num_fields,
416 sizeof(field_t));
417 if (data->table.fields == NULL)
418 return "Not enough memory for table fields";
419
420 field_t *field = data->table.fields;
421 for (size_t i = 0; i < data->tasks_count; i++) {
422 stats_task_t *task = &data->tasks[i];
423 perc_task_t *perc = &data->tasks_perc[i];
424 field[TASK_COL_ID].type = FIELD_UINT;
425 field[TASK_COL_ID].uint = task->task_id;
426 field[TASK_COL_NUM_THREADS].type = FIELD_UINT;
427 field[TASK_COL_NUM_THREADS].uint = task->threads;
428 field[TASK_COL_RESIDENT].type = FIELD_UINT_SUFFIX_BIN;
429 field[TASK_COL_RESIDENT].uint = task->resmem;
430 field[TASK_COL_PERCENT_RESIDENT].type = FIELD_PERCENT;
431 field[TASK_COL_PERCENT_RESIDENT].fixed = perc->resmem;
432 field[TASK_COL_VIRTUAL].type = FIELD_UINT_SUFFIX_BIN;
433 field[TASK_COL_VIRTUAL].uint = task->virtmem;
434 field[TASK_COL_PERCENT_VIRTUAL].type = FIELD_PERCENT;
435 field[TASK_COL_PERCENT_VIRTUAL].fixed = perc->virtmem;
436 field[TASK_COL_PERCENT_USER].type = FIELD_PERCENT;
437 field[TASK_COL_PERCENT_USER].fixed = perc->ucycles;
438 field[TASK_COL_PERCENT_KERNEL].type = FIELD_PERCENT;
439 field[TASK_COL_PERCENT_KERNEL].fixed = perc->kcycles;
440 field[TASK_COL_NAME].type = FIELD_STRING;
441 field[TASK_COL_NAME].string = task->name;
442 field += TASK_NUM_COLUMNS;
443 }
444
445 return NULL;
446}
447
448static const char *fill_ipc_table(data_t *data)
449{
450 data->table.name = "IPC";
451 data->table.num_columns = IPC_NUM_COLUMNS;
452 data->table.columns = ipc_columns;
453 data->table.num_fields = data->tasks_count * IPC_NUM_COLUMNS;
454 data->table.fields = calloc(data->table.num_fields,
455 sizeof(field_t));
456 if (data->table.fields == NULL)
457 return "Not enough memory for table fields";
458
459 field_t *field = data->table.fields;
460 for (size_t i = 0; i < data->tasks_count; i++) {
461 field[IPC_COL_TASKID].type = FIELD_UINT;
462 field[IPC_COL_TASKID].uint = data->tasks[i].task_id;
463 field[IPC_COL_CLS_SNT].type = FIELD_UINT_SUFFIX_DEC;
464 field[IPC_COL_CLS_SNT].uint = data->tasks[i].ipc_info.call_sent;
465 field[IPC_COL_CLS_RCV].type = FIELD_UINT_SUFFIX_DEC;
466 field[IPC_COL_CLS_RCV].uint = data->tasks[i].ipc_info.call_received;
467 field[IPC_COL_ANS_SNT].type = FIELD_UINT_SUFFIX_DEC;
468 field[IPC_COL_ANS_SNT].uint = data->tasks[i].ipc_info.answer_sent;
469 field[IPC_COL_ANS_RCV].type = FIELD_UINT_SUFFIX_DEC;
470 field[IPC_COL_ANS_RCV].uint = data->tasks[i].ipc_info.answer_received;
471 field[IPC_COL_FORWARD].type = FIELD_UINT_SUFFIX_DEC;
472 field[IPC_COL_FORWARD].uint = data->tasks[i].ipc_info.forwarded;
473 field[IPC_COL_NAME].type = FIELD_STRING;
474 field[IPC_COL_NAME].string = data->tasks[i].name;
475 field += IPC_NUM_COLUMNS;
476 }
477
478 return NULL;
479}
480
481static const char *fill_exception_table(data_t *data)
482{
483 data->table.name = "Exceptions";
484 data->table.num_columns = EXCEPTION_NUM_COLUMNS;
485 data->table.columns = exception_columns;
486 data->table.num_fields = data->exceptions_count *
487 EXCEPTION_NUM_COLUMNS;
488 data->table.fields = calloc(data->table.num_fields, sizeof(field_t));
489 if (data->table.fields == NULL)
490 return "Not enough memory for table fields";
491
492 field_t *field = data->table.fields;
493 for (size_t i = 0; i < data->exceptions_count; i++) {
494 if (!excs_all && !data->exceptions[i].hot)
495 continue;
496 field[EXCEPTION_COL_ID].type = FIELD_UINT;
497 field[EXCEPTION_COL_ID].uint = data->exceptions[i].id;
498 field[EXCEPTION_COL_COUNT].type = FIELD_UINT_SUFFIX_DEC;
499 field[EXCEPTION_COL_COUNT].uint = data->exceptions[i].count;
500 field[EXCEPTION_COL_PERCENT_COUNT].type = FIELD_PERCENT;
501 field[EXCEPTION_COL_PERCENT_COUNT].fixed = data->exceptions_perc[i].count;
502 field[EXCEPTION_COL_CYCLES].type = FIELD_UINT_SUFFIX_DEC;
503 field[EXCEPTION_COL_CYCLES].uint = data->exceptions[i].cycles;
504 field[EXCEPTION_COL_PERCENT_CYCLES].type = FIELD_PERCENT;
505 field[EXCEPTION_COL_PERCENT_CYCLES].fixed = data->exceptions_perc[i].cycles;
506 field[EXCEPTION_COL_DESCRIPTION].type = FIELD_STRING;
507 field[EXCEPTION_COL_DESCRIPTION].string = data->exceptions[i].desc;
508 field += EXCEPTION_NUM_COLUMNS;
509 }
510
511 /* in case any cold exceptions were ignored */
512 data->table.num_fields = field - data->table.fields;
513
514 return NULL;
515}
516
517static const char *fill_table(data_t *data)
518{
519 if (data->table.fields != NULL) {
520 free(data->table.fields);
521 data->table.fields = NULL;
522 }
523
524 switch (op_mode) {
525 case OP_TASKS:
526 return fill_task_table(data);
527 case OP_IPC:
528 return fill_ipc_table(data);
529 case OP_EXCS:
530 return fill_exception_table(data);
531 }
532 return NULL;
533}
534
535static void free_data(data_t *target)
536{
537 if (target->load != NULL)
538 free(target->load);
539
540 if (target->cpus != NULL)
541 free(target->cpus);
542
543 if (target->cpus_perc != NULL)
544 free(target->cpus_perc);
545
546 if (target->tasks != NULL)
547 free(target->tasks);
548
549 if (target->tasks_perc != NULL)
550 free(target->tasks_perc);
551
552 if (target->threads != NULL)
553 free(target->threads);
554
555 if (target->exceptions != NULL)
556 free(target->exceptions);
557
558 if (target->exceptions_perc != NULL)
559 free(target->exceptions_perc);
560
561 if (target->physmem != NULL)
562 free(target->physmem);
563
564 if (target->ucycles_diff != NULL)
565 free(target->ucycles_diff);
566
567 if (target->kcycles_diff != NULL)
568 free(target->kcycles_diff);
569
570 if (target->ecycles_diff != NULL)
571 free(target->ecycles_diff);
572
573 if (target->ecount_diff != NULL)
574 free(target->ecount_diff);
575
576 if (target->table.fields != NULL)
577 free(target->table.fields);
578}
579
580int main(int argc, char *argv[])
581{
582 data_t data;
583 data_t data_prev;
584 const char *ret = NULL;
585 errno_t rc;
586 int c;
587
588 screen_init();
589 printf("Reading initial data...\n");
590
591 if ((ret = read_data(&data)) != NULL)
592 goto out;
593
594 /* Compute some rubbish to have initialised values */
595 compute_percentages(&data, &data);
596
597 /* And paint screen until death */
598 while (true) {
599 rc = tgetchar(UPDATE_INTERVAL, &c);
600
601 if (rc == ETIMEOUT) { /* timeout */
602 data_prev = data;
603 if ((ret = read_data(&data)) != NULL) {
604 free_data(&data_prev);
605 goto out;
606 }
607
608 compute_percentages(&data_prev, &data);
609 free_data(&data_prev);
610
611 c = -1;
612 } else if (rc != EOK) {
613 /* Error (e.g. communication with console lost) */
614 goto out;
615 }
616
617 if (screen_mode == SCREEN_HELP && c >= 0) {
618 if (c == 'h' || c == '?')
619 c = -1;
620 /* go back to table and handle the key */
621 screen_mode = SCREEN_TABLE;
622 }
623
624 if (screen_mode == SCREEN_SORT && c >= 0) {
625 for (size_t i = 0; i < data.table.num_columns; i++) {
626 if (data.table.columns[i].key == c) {
627 sort_column = i;
628 screen_mode = SCREEN_TABLE;
629 }
630 }
631
632 c = -1;
633 }
634
635 switch (c) {
636 case -1: /* do nothing */
637 break;
638 case 't':
639 op_mode = OP_TASKS;
640 break;
641 case 'i':
642 op_mode = OP_IPC;
643 break;
644 case 'e':
645 op_mode = OP_EXCS;
646 break;
647 case 's':
648 screen_mode = SCREEN_SORT;
649 break;
650 case 'r':
651 sort_reverse = -sort_reverse;
652 break;
653 case 'h':
654 case '?':
655 screen_mode = SCREEN_HELP;
656 break;
657 case 'q':
658 goto out;
659 case 'a':
660 if (op_mode == OP_EXCS) {
661 excs_all = !excs_all;
662 if (excs_all)
663 show_warning("Showing all exceptions");
664 else
665 show_warning("Showing only hot exceptions");
666 break;
667 }
668 /* Fallthrough */
669 default:
670 show_warning("Unknown command \"%c\", use \"h\" for help", c);
671 continue; /* don't redraw */
672 }
673
674 if ((ret = fill_table(&data)) != NULL) {
675 goto out;
676 }
677 sort_table(&data.table);
678 print_data(&data);
679 }
680
681out:
682 screen_done();
683 free_data(&data);
684
685 if (ret != NULL) {
686 fprintf(stderr, "%s: %s\n", NAME, ret);
687 return 1;
688 }
689
690 return 0;
691}
692
693/** @}
694 */
Note: See TracBrowser for help on using the repository browser.