source: mainline/uspace/app/top/screen.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: 12.2 KB
RevLine 
[5c058d50]1/*
2 * Copyright (c) 2010 Stanislav Kozina
[dec16a2]3 * Copyright (c) 2010 Martin Decky
[5c058d50]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>
[2d37006]39#include <stdlib.h>
[5c058d50]40#include <io/console.h>
[9f1362d4]41#include <io/style.h>
[5c058d50]42#include <vfs/vfs.h>
[dec16a2]43#include <stdarg.h>
44#include <stats.h>
45#include <inttypes.h>
[b8d6783]46#include <macros.h>
[1d6dd2a]47#include <str.h>
[5c058d50]48#include "screen.h"
[79edc36]49#include "top.h"
[5c058d50]50
[bd41ac52]51static usec_t timeleft = 0;
[79ae36dd]52
53console_ctrl_t *console;
[8f56d93]54
[2d37006]55static sysarg_t warning_col = 0;
56static sysarg_t warning_row = 0;
[bd41ac52]57static usec_t warning_timeleft = 0;
[2d37006]58static char *warning_text = NULL;
59
[9f1362d4]60static void screen_style_normal(void)
61{
[79ae36dd]62 console_flush(console);
63 console_set_style(console, STYLE_NORMAL);
[9f1362d4]64}
[8f56d93]65
[9f1362d4]66static void screen_style_inverted(void)
[ee35ba0b]67{
[79ae36dd]68 console_flush(console);
69 console_set_style(console, STYLE_INVERTED);
[ee35ba0b]70}
71
[2d37006]72static void screen_style_emphasis(void)
73{
74 console_flush(console);
75 console_set_style(console, STYLE_EMPHASIS);
76}
77
[96b02eb9]78static void screen_moveto(sysarg_t col, sysarg_t row)
[5c058d50]79{
[79ae36dd]80 console_flush(console);
81 console_set_pos(console, col, row);
[5c058d50]82}
83
[96b02eb9]84static void screen_get_pos(sysarg_t *col, sysarg_t *row)
[5c058d50]85{
[79ae36dd]86 console_flush(console);
87 console_get_pos(console, col, row);
[5c058d50]88}
89
[96b02eb9]90static void screen_get_size(sysarg_t *col, sysarg_t *row)
[5c058d50]91{
[79ae36dd]92 console_flush(console);
93 console_get_size(console, col, row);
[5c058d50]94}
95
[9f1362d4]96static void screen_restart(bool clear)
[5c058d50]97{
[9f1362d4]98 screen_style_normal();
[a35b458]99
[9f1362d4]100 if (clear) {
[79ae36dd]101 console_flush(console);
102 console_clear(console);
[9f1362d4]103 }
[a35b458]104
[9f1362d4]105 screen_moveto(0, 0);
[dec16a2]106}
107
[9f1362d4]108static void screen_newline(void)
[dec16a2]109{
[96b02eb9]110 sysarg_t cols;
111 sysarg_t rows;
[9f1362d4]112 screen_get_size(&cols, &rows);
[a35b458]113
[96b02eb9]114 sysarg_t c;
115 sysarg_t r;
[9f1362d4]116 screen_get_pos(&c, &r);
[a35b458]117
[96b02eb9]118 sysarg_t i;
[9f1362d4]119 for (i = c + 1; i < cols; i++)
[1abcf1d]120 fputs(" ", stdout);
[a35b458]121
[9f1362d4]122 if (r + 1 < rows)
[1abcf1d]123 puts("");
[9f1362d4]124}
125
126void screen_init(void)
127{
[79ae36dd]128 console = console_init(stdin, stdout);
[a35b458]129
[79ae36dd]130 console_flush(console);
131 console_cursor_visibility(console, false);
[a35b458]132
[9f1362d4]133 screen_restart(true);
[5c058d50]134}
135
[9f1362d4]136void screen_done(void)
[79edc36]137{
[2d37006]138 free(warning_text);
139 warning_text = NULL;
140
[9f1362d4]141 screen_restart(true);
[a35b458]142
[79ae36dd]143 console_flush(console);
144 console_cursor_visibility(console, true);
[79edc36]145}
146
[be06914]147static void print_percent(fixed_float ffloat, unsigned int precision)
[79edc36]148{
[be06914]149 printf("%3" PRIu64 ".", ffloat.upper / ffloat.lower);
[a35b458]150
[9f1362d4]151 unsigned int i;
152 uint64_t rest = (ffloat.upper % ffloat.lower) * 10;
153 for (i = 0; i < precision; i++) {
154 printf("%" PRIu64, rest / ffloat.lower);
155 rest = (rest % ffloat.lower) * 10;
156 }
[a35b458]157
[be06914]158 printf("%%");
159}
160
161static void print_string(const char *str)
162{
[96b02eb9]163 sysarg_t cols;
164 sysarg_t rows;
[be06914]165 screen_get_size(&cols, &rows);
[a35b458]166
[96b02eb9]167 sysarg_t c;
168 sysarg_t r;
[be06914]169 screen_get_pos(&c, &r);
[a35b458]170
[0b0f4bb]171 if (c < cols) {
172 int pos = cols - c - 1;
173 printf("%.*s", pos, str);
174 }
[79edc36]175}
176
[9f1362d4]177static inline void print_global_head(data_t *data)
[dd6c71c]178{
[bd41ac52]179 printf("top - %02lld:%02lld:%02lld up "
[0b0f4bb]180 "%" PRIun " days, %02" PRIun ":%02" PRIun ":%02" PRIun ", "
181 "load average:",
[9f1362d4]182 data->hours, data->minutes, data->seconds,
183 data->udays, data->uhours, data->uminutes, data->useconds);
[a35b458]184
[dec16a2]185 size_t i;
186 for (i = 0; i < data->load_count; i++) {
[1abcf1d]187 fputs(" ", stdout);
[dec16a2]188 stats_print_load_fragment(data->load[i], 2);
189 }
[a35b458]190
[9f1362d4]191 screen_newline();
[dd6c71c]192}
193
[dec16a2]194static inline void print_task_summary(data_t *data)
[dd6c71c]195{
[0b0f4bb]196 printf("tasks: %zu total", data->tasks_count);
[9f1362d4]197 screen_newline();
[dd6c71c]198}
199
[dec16a2]200static inline void print_thread_summary(data_t *data)
[638927a]201{
[dec16a2]202 size_t total = 0;
[638927a]203 size_t running = 0;
[dec16a2]204 size_t ready = 0;
205 size_t sleeping = 0;
206 size_t lingering = 0;
[638927a]207 size_t other = 0;
[dec16a2]208 size_t invalid = 0;
[a35b458]209
[638927a]210 size_t i;
[dec16a2]211 for (i = 0; i < data->threads_count; i++) {
212 total++;
[a35b458]213
[dec16a2]214 switch (data->threads[i].state) {
215 case Running:
216 running++;
217 break;
218 case Ready:
219 ready++;
220 break;
221 case Sleeping:
222 sleeping++;
223 break;
224 case Lingering:
225 lingering++;
226 break;
227 case Entering:
228 case Exiting:
229 other++;
230 break;
231 default:
232 invalid++;
[638927a]233 }
234 }
[a35b458]235
[0b0f4bb]236 printf("threads: %zu total, %zu running, %zu ready, "
237 "%zu sleeping, %zu lingering, %zu other, %zu invalid",
[dec16a2]238 total, running, ready, sleeping, lingering, other, invalid);
[9f1362d4]239 screen_newline();
[638927a]240}
241
[dec16a2]242static inline void print_cpu_info(data_t *data)
[8b2aba5]243{
[dec16a2]244 size_t i;
245 for (i = 0; i < data->cpus_count; i++) {
[bd01a4e]246 if (data->cpus[i].active) {
[d0c82c5]247 uint64_t busy;
248 uint64_t idle;
249 char busy_suffix;
250 char idle_suffix;
[a35b458]251
[d0c82c5]252 order_suffix(data->cpus[i].busy_cycles, &busy, &busy_suffix);
253 order_suffix(data->cpus[i].idle_cycles, &idle, &idle_suffix);
[a35b458]254
[d0c82c5]255 printf("cpu%u (%4" PRIu16 " MHz): busy cycles: "
256 "%" PRIu64 "%c, idle cycles: %" PRIu64 "%c",
[bd01a4e]257 data->cpus[i].id, data->cpus[i].frequency_mhz,
[d0c82c5]258 busy, busy_suffix, idle, idle_suffix);
[1abcf1d]259 fputs(", idle: ", stdout);
[be06914]260 print_percent(data->cpus_perc[i].idle, 2);
[1abcf1d]261 fputs(", busy: ", stdout);
[be06914]262 print_percent(data->cpus_perc[i].busy, 2);
[bd01a4e]263 } else
[9f1362d4]264 printf("cpu%u inactive", data->cpus[i].id);
[a35b458]265
[9f1362d4]266 screen_newline();
[8b2aba5]267 }
268}
269
[dec16a2]270static inline void print_physmem_info(data_t *data)
[516adce]271{
[dec16a2]272 uint64_t total;
273 uint64_t unavail;
274 uint64_t used;
275 uint64_t free;
[933cadf]276 const char *total_suffix;
277 const char *unavail_suffix;
278 const char *used_suffix;
279 const char *free_suffix;
[a35b458]280
[933cadf]281 bin_order_suffix(data->physmem->total, &total, &total_suffix, false);
282 bin_order_suffix(data->physmem->unavail, &unavail, &unavail_suffix, false);
283 bin_order_suffix(data->physmem->used, &used, &used_suffix, false);
284 bin_order_suffix(data->physmem->free, &free, &free_suffix, false);
[a35b458]285
[933cadf]286 printf("memory: %" PRIu64 "%s total, %" PRIu64 "%s unavail, %"
287 PRIu64 "%s used, %" PRIu64 "%s free", total, total_suffix,
[dec16a2]288 unavail, unavail_suffix, used, used_suffix, free, free_suffix);
[9f1362d4]289 screen_newline();
290}
291
[f682f5a]292static inline void print_help_head(void)
[9f1362d4]293{
294 screen_style_inverted();
[f682f5a]295 printf("Help");
[9f1362d4]296 screen_newline();
297 screen_style_normal();
[516adce]298}
299
[f682f5a]300static inline void print_help(void)
[8f56d93]301{
[96b02eb9]302 sysarg_t cols;
303 sysarg_t rows;
[9f1362d4]304 screen_get_size(&cols, &rows);
[a35b458]305
[f682f5a]306 screen_newline();
[a35b458]307
[f682f5a]308 printf("Operation modes:");
309 screen_newline();
[a35b458]310
[f682f5a]311 printf(" t .. tasks statistics");
312 screen_newline();
[a35b458]313
[f682f5a]314 printf(" i .. IPC statistics");
315 screen_newline();
[a35b458]316
[f682f5a]317 printf(" e .. exceptions statistics");
318 screen_newline();
[a35b458]319
[f682f5a]320 printf(" a .. toggle display of all/hot exceptions");
321 screen_newline();
[8f56d93]322
[f682f5a]323 printf(" h .. toggle this help screen");
[9f1362d4]324 screen_newline();
[bdfd3c97]325
[f682f5a]326 screen_newline();
327
328 printf("Other keys:");
329 screen_newline();
[a35b458]330
[b8d6783]331 printf(" s .. choose column to sort by");
332 screen_newline();
[a35b458]333
[b8d6783]334 printf(" r .. toggle reversed sorting");
335 screen_newline();
[a35b458]336
[f682f5a]337 printf(" q .. quit");
338 screen_newline();
[a35b458]339
[96b02eb9]340 sysarg_t col;
341 sysarg_t row;
[9f1362d4]342 screen_get_pos(&col, &row);
[a35b458]343
[369a5f8]344 while (row < rows) {
345 screen_newline();
346 row++;
347 }
[bdfd3c97]348}
349
[f682f5a]350static inline void print_table_head(const table_t *table)
[8eec3c8]351{
[f682f5a]352 sysarg_t cols;
353 sysarg_t rows;
354 screen_get_size(&cols, &rows);
355
[8eec3c8]356 screen_style_inverted();
[f682f5a]357 for (size_t i = 0; i < table->num_columns; i++) {
358 const char *name = table->columns[i].name;
359 int width = table->columns[i].width;
360 if (i != 0) {
[1abcf1d]361 fputs(" ", stdout);
[f682f5a]362 }
363 if (width == 0) {
364 sysarg_t col;
365 sysarg_t row;
366 screen_get_pos(&col, &row);
367 width = cols - col - 1;
368 }
369 printf("[%-*.*s]", width - 2, width - 2, name);
370 }
[8eec3c8]371 screen_newline();
372 screen_style_normal();
373}
374
[f682f5a]375static inline void print_table(const table_t *table)
[8eec3c8]376{
[96b02eb9]377 sysarg_t cols;
378 sysarg_t rows;
[8eec3c8]379 screen_get_size(&cols, &rows);
[a35b458]380
[96b02eb9]381 sysarg_t col;
382 sysarg_t row;
[8eec3c8]383 screen_get_pos(&col, &row);
[a35b458]384
[8eec3c8]385 size_t i;
[f682f5a]386 for (i = 0; (i < table->num_fields) && (row < rows); i++) {
387 size_t column_index = i % table->num_columns;
388 int width = table->columns[column_index].width;
389 field_t *field = &table->fields[i];
[338d54a7]390 uint64_t val;
391 const char *psuffix;
392 char suffix;
[b3b7e14a]393
[f682f5a]394 if (column_index != 0) {
[1abcf1d]395 fputs(" ", stdout);
[f682f5a]396 }
[6484602]397
[f682f5a]398 if (width == 0) {
399 screen_get_pos(&col, &row);
400 width = cols - col - 1;
401 }
[6484602]402
[f682f5a]403 switch (field->type) {
[d76a329]404 case FIELD_EMPTY:
405 printf("%*s", width, "");
406 break;
407 case FIELD_UINT:
408 printf("%*" PRIu64, width, field->uint);
409 break;
[338d54a7]410 case FIELD_UINT_SUFFIX_BIN:
411 val = field->uint;
[d76a329]412 width -= 3;
[338d54a7]413 bin_order_suffix(val, &val, &psuffix, true);
414 printf("%*" PRIu64 "%s", width, val, psuffix);
[d76a329]415 break;
[338d54a7]416 case FIELD_UINT_SUFFIX_DEC:
417 val = field->uint;
[d76a329]418 width -= 1;
419 order_suffix(val, &val, &suffix);
420 printf("%*" PRIu64 "%c", width, val, suffix);
421 break;
422 case FIELD_PERCENT:
423 width -= 5; /* nnn.% */
424 if (width > 2) {
425 printf("%*s", width - 2, "");
426 width = 2;
427 }
428 print_percent(field->fixed, width);
429 break;
430 case FIELD_STRING:
431 printf("%-*.*s", width, width, field->string);
432 break;
[f682f5a]433 }
[6484602]434
[f682f5a]435 if (column_index == table->num_columns - 1) {
436 screen_newline();
437 row++;
438 }
439 }
[a35b458]440
[8eec3c8]441 while (row < rows) {
442 screen_newline();
443 row++;
444 }
445}
446
[b8d6783]447static inline void print_sort(table_t *table)
448{
449 sysarg_t cols;
450 sysarg_t rows;
451 screen_get_size(&cols, &rows);
[a35b458]452
[b8d6783]453 sysarg_t col;
454 sysarg_t row;
455 screen_get_pos(&col, &row);
456
457 size_t num = min(table->num_columns, rows - row);
458 for (size_t i = 0; i < num; i++) {
459 printf("%c - %s", table->columns[i].key, table->columns[i].name);
460 screen_newline();
461 row++;
462 }
[a35b458]463
[b8d6783]464 while (row < rows) {
465 screen_newline();
466 row++;
467 }
468}
469
[f682f5a]470static inline void print_warning(void)
[2d37006]471{
472 screen_get_pos(&warning_col, &warning_row);
473 if (warning_timeleft > 0) {
474 screen_style_emphasis();
475 print_string(warning_text);
476 screen_style_normal();
477 } else {
478 free(warning_text);
479 warning_text = NULL;
480 }
481 screen_newline();
482}
483
[79edc36]484void print_data(data_t *data)
485{
[9f1362d4]486 screen_restart(false);
487 print_global_head(data);
[dec16a2]488 print_task_summary(data);
489 print_thread_summary(data);
490 print_cpu_info(data);
491 print_physmem_info(data);
[2d37006]492 print_warning();
[a35b458]493
[f682f5a]494 switch (screen_mode) {
495 case SCREEN_TABLE:
496 print_table_head(&data->table);
497 print_table(&data->table);
[8eec3c8]498 break;
[b8d6783]499 case SCREEN_SORT:
500 print_sort(&data->table);
501 break;
[f682f5a]502 case SCREEN_HELP:
[6484602]503 print_help_head();
[b3b7e14a]504 print_help();
[bdfd3c97]505 }
[a35b458]506
[79ae36dd]507 console_flush(console);
[dec16a2]508}
509
[2d37006]510void show_warning(const char *fmt, ...)
[dec16a2]511{
[2d37006]512 sysarg_t cols;
513 sysarg_t rows;
514 screen_get_size(&cols, &rows);
515
516 size_t warning_text_size = 1 + cols * sizeof(*warning_text);
517 free(warning_text);
518 warning_text = malloc(warning_text_size);
519 if (!warning_text)
520 return;
521
[dec16a2]522 va_list args;
523 va_start(args, fmt);
[2d37006]524 vsnprintf(warning_text, warning_text_size, fmt, args);
[dec16a2]525 va_end(args);
[a35b458]526
[bd41ac52]527 warning_timeleft = SEC2USEC(2);
[2d37006]528
529 screen_moveto(warning_col, warning_row);
530 print_warning();
[79ae36dd]531 console_flush(console);
532}
533
534/** Get char with timeout
535 *
[87822ce]536 * @param sec Timeout in seconds
537 * @param rch Place to store character on success
538 * @return EOK on success, ETIMEOUT on time out, EIO on other error
[79ae36dd]539 */
[87822ce]540errno_t tgetchar(sec_t sec, int *rch)
[79ae36dd]541{
[87822ce]542 errno_t rc;
543
[79ae36dd]544 /*
545 * Reset timeleft whenever it is not positive.
546 */
[a35b458]547
[79ae36dd]548 if (timeleft <= 0)
[bd41ac52]549 timeleft = SEC2USEC(sec);
[a35b458]550
[79ae36dd]551 /*
552 * Wait to see if there is any input. If so, take it and
553 * update timeleft so that the next call to tgetchar()
554 * will not wait as long. If there is no input,
[87822ce]555 * make timeleft zero and return ETIMEOUT.
[79ae36dd]556 */
[a35b458]557
[28a5ebd]558 char32_t c = 0;
[a35b458]559
[79ae36dd]560 while (c == 0) {
[07b7c48]561 cons_event_t event;
[a35b458]562
[2d37006]563 warning_timeleft -= timeleft;
[87822ce]564 rc = console_get_event_timeout(console, &event, &timeleft);
565 if (rc == ETIMEOUT) {
[79ae36dd]566 timeleft = 0;
[87822ce]567 return ETIMEOUT;
[79ae36dd]568 }
[87822ce]569
570 if (rc != EOK)
571 return EIO;
572
[2d37006]573 warning_timeleft += timeleft;
[a35b458]574
[07b7c48]575 if (event.type == CEV_KEY && event.ev.key.type == KEY_PRESS)
576 c = event.ev.key.c;
[79ae36dd]577 }
[a35b458]578
[87822ce]579 *rch = (int) c;
580 return EOK;
[79edc36]581}
582
[5c058d50]583/** @}
584 */
Note: See TracBrowser for help on using the repository browser.