source: mainline/uspace/dist/src/c/demos/top/top.c

Last change on this file was bd41ac52, checked in by Jakub Jermar <jakub@…>, 7 years ago

Get rid of sys/time.h

This commit moves the POSIX-like time functionality from libc's
sys/time.h to libposix and introduces C11-like or HelenOS-specific
interfaces to libc.

Specifically, use of sys/time.h, struct timeval, suseconds_t and
gettimeofday is replaced by time.h (C11), struct timespec (C11), usec_t
(HelenOS) and getuptime / getrealtime (HelenOS).

Also attempt to fix the implementation of clock() to return microseconds
(clocks) rather than processor cycles and move it to libc.

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