2 * Copyright (c) 2010 Stanislav Kozina
3 * Copyright (c) 2010 Martin Decky
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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.
51 #define UPDATE_INTERVAL 1
63 static const column_t task_columns
[] = {
66 {"resident", 'r', 10},
79 TASK_COL_PERCENT_RESIDENT
,
81 TASK_COL_PERCENT_VIRTUAL
,
82 TASK_COL_PERCENT_USER
,
83 TASK_COL_PERCENT_KERNEL
,
88 static const column_t ipc_columns
[] = {
109 static const column_t exception_columns
[] = {
115 {"description", 'd', 0},
119 EXCEPTION_COL_ID
= 0,
121 EXCEPTION_COL_PERCENT_COUNT
,
122 EXCEPTION_COL_CYCLES
,
123 EXCEPTION_COL_PERCENT_CYCLES
,
124 EXCEPTION_COL_DESCRIPTION
,
125 EXCEPTION_NUM_COLUMNS
,
128 screen_mode_t screen_mode
= SCREEN_TABLE
;
129 static op_mode_t op_mode
= OP_TASKS
;
130 static size_t sort_column
= TASK_COL_PERCENT_USER
;
131 static int sort_reverse
= -1;
132 static bool excs_all
= false;
134 static const char *read_data(data_t
*target
)
136 /* Initialize data */
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
;
156 /* Get current time */
158 if (gettimeofday(&time
, NULL
) != EOK
)
159 return "Cannot get time of day";
161 target
->hours
= (time
.tv_sec
% DAY
) / HOUR
;
162 target
->minutes
= (time
.tv_sec
% HOUR
) / MINUTE
;
163 target
->seconds
= time
.tv_sec
% MINUTE
;
166 sysarg_t uptime
= stats_get_uptime();
167 target
->udays
= uptime
/ DAY
;
168 target
->uhours
= (uptime
% DAY
) / HOUR
;
169 target
->uminutes
= (uptime
% HOUR
) / MINUTE
;
170 target
->useconds
= uptime
% MINUTE
;
173 target
->load
= stats_get_load(&(target
->load_count
));
174 if (target
->load
== NULL
)
175 return "Cannot get system load";
178 target
->cpus
= stats_get_cpus(&(target
->cpus_count
));
179 if (target
->cpus
== NULL
)
180 return "Cannot get CPUs";
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";
188 target
->tasks
= stats_get_tasks(&(target
->tasks_count
));
189 if (target
->tasks
== NULL
)
190 return "Cannot get tasks";
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";
198 target
->threads
= stats_get_threads(&(target
->threads_count
));
199 if (target
->threads
== NULL
)
200 return "Cannot get threads";
203 target
->exceptions
= stats_get_exceptions(&(target
->exceptions_count
));
204 if (target
->exceptions
== NULL
)
205 return "Cannot get exceptions";
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";
212 /* Get physical memory */
213 target
->physmem
= stats_get_physmem();
214 if (target
->physmem
== NULL
)
215 return "Cannot get physical memory";
217 target
->ucycles_diff
= calloc(target
->tasks_count
,
219 if (target
->ucycles_diff
== NULL
)
220 return "Not enough memory for user utilization";
222 /* Allocate memory for computed values */
223 target
->kcycles_diff
= calloc(target
->tasks_count
,
225 if (target
->kcycles_diff
== NULL
)
226 return "Not enough memory for kernel utilization";
228 target
->ecycles_diff
= calloc(target
->exceptions_count
,
230 if (target
->ecycles_diff
== NULL
)
231 return "Not enough memory for exception cycles utilization";
233 target
->ecount_diff
= calloc(target
->exceptions_count
,
235 if (target
->ecount_diff
== NULL
)
236 return "Not enough memory for exception count utilization";
241 /** Computes percentage differencies from old_data to new_data
243 * @param old_data Pointer to old data strucutre.
244 * @param new_data Pointer to actual data where percetages are stored.
247 static void compute_percentages(data_t
*old_data
, data_t
*new_data
)
249 /* For each CPU: Compute total cycles and divide it between
253 for (i
= 0; i
< new_data
->cpus_count
; i
++) {
255 new_data
->cpus
[i
].idle_cycles
- old_data
->cpus
[i
].idle_cycles
;
257 new_data
->cpus
[i
].busy_cycles
- old_data
->cpus
[i
].busy_cycles
;
258 uint64_t sum
= idle
+ busy
;
260 FRACTION_TO_FLOAT(new_data
->cpus_perc
[i
].idle
, idle
* 100, sum
);
261 FRACTION_TO_FLOAT(new_data
->cpus_perc
[i
].busy
, busy
* 100, sum
);
264 /* For all tasks compute sum and differencies of all cycles */
266 uint64_t virtmem_total
= 0;
267 uint64_t resmem_total
= 0;
268 uint64_t ucycles_total
= 0;
269 uint64_t kcycles_total
= 0;
271 for (i
= 0; i
< new_data
->tasks_count
; i
++) {
272 /* Match task with the previous instance */
276 for (j
= 0; j
< old_data
->tasks_count
; j
++) {
277 if (new_data
->tasks
[i
].task_id
== old_data
->tasks
[j
].task_id
) {
284 /* This is newly borned task, ignore it */
285 new_data
->ucycles_diff
[i
] = 0;
286 new_data
->kcycles_diff
[i
] = 0;
290 new_data
->ucycles_diff
[i
] =
291 new_data
->tasks
[i
].ucycles
- old_data
->tasks
[j
].ucycles
;
292 new_data
->kcycles_diff
[i
] =
293 new_data
->tasks
[i
].kcycles
- old_data
->tasks
[j
].kcycles
;
295 virtmem_total
+= new_data
->tasks
[i
].virtmem
;
296 resmem_total
+= new_data
->tasks
[i
].resmem
;
297 ucycles_total
+= new_data
->ucycles_diff
[i
];
298 kcycles_total
+= new_data
->kcycles_diff
[i
];
301 /* For each task compute percential change */
303 for (i
= 0; i
< new_data
->tasks_count
; i
++) {
304 FRACTION_TO_FLOAT(new_data
->tasks_perc
[i
].virtmem
,
305 new_data
->tasks
[i
].virtmem
* 100, virtmem_total
);
306 FRACTION_TO_FLOAT(new_data
->tasks_perc
[i
].resmem
,
307 new_data
->tasks
[i
].resmem
* 100, resmem_total
);
308 FRACTION_TO_FLOAT(new_data
->tasks_perc
[i
].ucycles
,
309 new_data
->ucycles_diff
[i
] * 100, ucycles_total
);
310 FRACTION_TO_FLOAT(new_data
->tasks_perc
[i
].kcycles
,
311 new_data
->kcycles_diff
[i
] * 100, kcycles_total
);
314 /* For all exceptions compute sum and differencies of cycles */
316 uint64_t ecycles_total
= 0;
317 uint64_t ecount_total
= 0;
319 for (i
= 0; i
< new_data
->exceptions_count
; i
++) {
321 * March exception with the previous instance.
322 * This is quite paranoid since exceptions do not
323 * usually disappear, but it does not hurt.
328 for (j
= 0; j
< old_data
->exceptions_count
; j
++) {
329 if (new_data
->exceptions
[i
].id
== old_data
->exceptions
[j
].id
) {
336 /* This is a new exception, ignore it */
337 new_data
->ecycles_diff
[i
] = 0;
338 new_data
->ecount_diff
[i
] = 0;
342 new_data
->ecycles_diff
[i
] =
343 new_data
->exceptions
[i
].cycles
- old_data
->exceptions
[j
].cycles
;
344 new_data
->ecount_diff
[i
] =
345 new_data
->exceptions
[i
].count
- old_data
->exceptions
[i
].count
;
347 ecycles_total
+= new_data
->ecycles_diff
[i
];
348 ecount_total
+= new_data
->ecount_diff
[i
];
351 /* For each exception compute percential change */
353 for (i
= 0; i
< new_data
->exceptions_count
; i
++) {
354 FRACTION_TO_FLOAT(new_data
->exceptions_perc
[i
].cycles
,
355 new_data
->ecycles_diff
[i
] * 100, ecycles_total
);
356 FRACTION_TO_FLOAT(new_data
->exceptions_perc
[i
].count
,
357 new_data
->ecount_diff
[i
] * 100, ecount_total
);
361 static int cmp_data(void *a
, void *b
, void *arg
)
363 field_t
*fa
= (field_t
*)a
+ sort_column
;
364 field_t
*fb
= (field_t
*)b
+ sort_column
;
366 if (fa
->type
> fb
->type
)
367 return 1 * sort_reverse
;
369 if (fa
->type
< fb
->type
)
370 return -1 * sort_reverse
;
375 case FIELD_UINT_SUFFIX_BIN
: /* fallthrough */
376 case FIELD_UINT_SUFFIX_DEC
: /* fallthrough */
378 if (fa
->uint
> fb
->uint
)
379 return 1 * sort_reverse
;
380 if (fa
->uint
< fb
->uint
)
381 return -1 * sort_reverse
;
384 if (fa
->fixed
.upper
* fb
->fixed
.lower
385 > fb
->fixed
.upper
* fa
->fixed
.lower
)
386 return 1 * sort_reverse
;
387 if (fa
->fixed
.upper
* fb
->fixed
.lower
388 < fb
->fixed
.upper
* fa
->fixed
.lower
)
389 return -1 * sort_reverse
;
392 return str_cmp(fa
->string
, fb
->string
) * sort_reverse
;
398 static void sort_table(table_t
*table
)
400 if (sort_column
>= table
->num_columns
)
402 /* stable sort is probably best, so we use gsort */
403 gsort((void *) table
->fields
, table
->num_fields
/ table
->num_columns
,
404 sizeof(field_t
) * table
->num_columns
, cmp_data
, NULL
);
407 static const char *fill_task_table(data_t
*data
)
409 data
->table
.name
= "Tasks";
410 data
->table
.num_columns
= TASK_NUM_COLUMNS
;
411 data
->table
.columns
= task_columns
;
412 data
->table
.num_fields
= data
->tasks_count
* TASK_NUM_COLUMNS
;
413 data
->table
.fields
= calloc(data
->table
.num_fields
,
415 if (data
->table
.fields
== NULL
)
416 return "Not enough memory for table fields";
418 field_t
*field
= data
->table
.fields
;
419 for (size_t i
= 0; i
< data
->tasks_count
; i
++) {
420 stats_task_t
*task
= &data
->tasks
[i
];
421 perc_task_t
*perc
= &data
->tasks_perc
[i
];
422 field
[TASK_COL_ID
].type
= FIELD_UINT
;
423 field
[TASK_COL_ID
].uint
= task
->task_id
;
424 field
[TASK_COL_NUM_THREADS
].type
= FIELD_UINT
;
425 field
[TASK_COL_NUM_THREADS
].uint
= task
->threads
;
426 field
[TASK_COL_RESIDENT
].type
= FIELD_UINT_SUFFIX_BIN
;
427 field
[TASK_COL_RESIDENT
].uint
= task
->resmem
;
428 field
[TASK_COL_PERCENT_RESIDENT
].type
= FIELD_PERCENT
;
429 field
[TASK_COL_PERCENT_RESIDENT
].fixed
= perc
->resmem
;
430 field
[TASK_COL_VIRTUAL
].type
= FIELD_UINT_SUFFIX_BIN
;
431 field
[TASK_COL_VIRTUAL
].uint
= task
->virtmem
;
432 field
[TASK_COL_PERCENT_VIRTUAL
].type
= FIELD_PERCENT
;
433 field
[TASK_COL_PERCENT_VIRTUAL
].fixed
= perc
->virtmem
;
434 field
[TASK_COL_PERCENT_USER
].type
= FIELD_PERCENT
;
435 field
[TASK_COL_PERCENT_USER
].fixed
= perc
->ucycles
;
436 field
[TASK_COL_PERCENT_KERNEL
].type
= FIELD_PERCENT
;
437 field
[TASK_COL_PERCENT_KERNEL
].fixed
= perc
->kcycles
;
438 field
[TASK_COL_NAME
].type
= FIELD_STRING
;
439 field
[TASK_COL_NAME
].string
= task
->name
;
440 field
+= TASK_NUM_COLUMNS
;
446 static const char *fill_ipc_table(data_t
*data
)
448 data
->table
.name
= "IPC";
449 data
->table
.num_columns
= IPC_NUM_COLUMNS
;
450 data
->table
.columns
= ipc_columns
;
451 data
->table
.num_fields
= data
->tasks_count
* IPC_NUM_COLUMNS
;
452 data
->table
.fields
= calloc(data
->table
.num_fields
,
454 if (data
->table
.fields
== NULL
)
455 return "Not enough memory for table fields";
457 field_t
*field
= data
->table
.fields
;
458 for (size_t i
= 0; i
< data
->tasks_count
; i
++) {
459 field
[IPC_COL_TASKID
].type
= FIELD_UINT
;
460 field
[IPC_COL_TASKID
].uint
= data
->tasks
[i
].task_id
;
461 field
[IPC_COL_CLS_SNT
].type
= FIELD_UINT_SUFFIX_DEC
;
462 field
[IPC_COL_CLS_SNT
].uint
= data
->tasks
[i
].ipc_info
.call_sent
;
463 field
[IPC_COL_CLS_RCV
].type
= FIELD_UINT_SUFFIX_DEC
;
464 field
[IPC_COL_CLS_RCV
].uint
= data
->tasks
[i
].ipc_info
.call_received
;
465 field
[IPC_COL_ANS_SNT
].type
= FIELD_UINT_SUFFIX_DEC
;
466 field
[IPC_COL_ANS_SNT
].uint
= data
->tasks
[i
].ipc_info
.answer_sent
;
467 field
[IPC_COL_ANS_RCV
].type
= FIELD_UINT_SUFFIX_DEC
;
468 field
[IPC_COL_ANS_RCV
].uint
= data
->tasks
[i
].ipc_info
.answer_received
;
469 field
[IPC_COL_FORWARD
].type
= FIELD_UINT_SUFFIX_DEC
;
470 field
[IPC_COL_FORWARD
].uint
= data
->tasks
[i
].ipc_info
.forwarded
;
471 field
[IPC_COL_NAME
].type
= FIELD_STRING
;
472 field
[IPC_COL_NAME
].string
= data
->tasks
[i
].name
;
473 field
+= IPC_NUM_COLUMNS
;
479 static const char *fill_exception_table(data_t
*data
)
481 data
->table
.name
= "Exceptions";
482 data
->table
.num_columns
= EXCEPTION_NUM_COLUMNS
;
483 data
->table
.columns
= exception_columns
;
484 data
->table
.num_fields
= data
->exceptions_count
*
485 EXCEPTION_NUM_COLUMNS
;
486 data
->table
.fields
= calloc(data
->table
.num_fields
, sizeof(field_t
));
487 if (data
->table
.fields
== NULL
)
488 return "Not enough memory for table fields";
490 field_t
*field
= data
->table
.fields
;
491 for (size_t i
= 0; i
< data
->exceptions_count
; i
++) {
492 if (!excs_all
&& !data
->exceptions
[i
].hot
)
494 field
[EXCEPTION_COL_ID
].type
= FIELD_UINT
;
495 field
[EXCEPTION_COL_ID
].uint
= data
->exceptions
[i
].id
;
496 field
[EXCEPTION_COL_COUNT
].type
= FIELD_UINT_SUFFIX_DEC
;
497 field
[EXCEPTION_COL_COUNT
].uint
= data
->exceptions
[i
].count
;
498 field
[EXCEPTION_COL_PERCENT_COUNT
].type
= FIELD_PERCENT
;
499 field
[EXCEPTION_COL_PERCENT_COUNT
].fixed
= data
->exceptions_perc
[i
].count
;
500 field
[EXCEPTION_COL_CYCLES
].type
= FIELD_UINT_SUFFIX_DEC
;
501 field
[EXCEPTION_COL_CYCLES
].uint
= data
->exceptions
[i
].cycles
;
502 field
[EXCEPTION_COL_PERCENT_CYCLES
].type
= FIELD_PERCENT
;
503 field
[EXCEPTION_COL_PERCENT_CYCLES
].fixed
= data
->exceptions_perc
[i
].cycles
;
504 field
[EXCEPTION_COL_DESCRIPTION
].type
= FIELD_STRING
;
505 field
[EXCEPTION_COL_DESCRIPTION
].string
= data
->exceptions
[i
].desc
;
506 field
+= EXCEPTION_NUM_COLUMNS
;
509 /* in case any cold exceptions were ignored */
510 data
->table
.num_fields
= field
- data
->table
.fields
;
515 static const char *fill_table(data_t
*data
)
517 if (data
->table
.fields
!= NULL
) {
518 free(data
->table
.fields
);
519 data
->table
.fields
= NULL
;
524 return fill_task_table(data
);
526 return fill_ipc_table(data
);
528 return fill_exception_table(data
);
533 static void free_data(data_t
*target
)
535 if (target
->load
!= NULL
)
538 if (target
->cpus
!= NULL
)
541 if (target
->cpus_perc
!= NULL
)
542 free(target
->cpus_perc
);
544 if (target
->tasks
!= NULL
)
547 if (target
->tasks_perc
!= NULL
)
548 free(target
->tasks_perc
);
550 if (target
->threads
!= NULL
)
551 free(target
->threads
);
553 if (target
->exceptions
!= NULL
)
554 free(target
->exceptions
);
556 if (target
->exceptions_perc
!= NULL
)
557 free(target
->exceptions_perc
);
559 if (target
->physmem
!= NULL
)
560 free(target
->physmem
);
562 if (target
->ucycles_diff
!= NULL
)
563 free(target
->ucycles_diff
);
565 if (target
->kcycles_diff
!= NULL
)
566 free(target
->kcycles_diff
);
568 if (target
->ecycles_diff
!= NULL
)
569 free(target
->ecycles_diff
);
571 if (target
->ecount_diff
!= NULL
)
572 free(target
->ecount_diff
);
574 if (target
->table
.fields
!= NULL
)
575 free(target
->table
.fields
);
578 int main(int argc
, char *argv
[])
582 const char *ret
= NULL
;
585 printf("Reading initial data...\n");
587 if ((ret
= read_data(&data
)) != NULL
)
590 /* Compute some rubbish to have initialised values */
591 compute_percentages(&data
, &data
);
593 /* And paint screen until death */
595 int c
= tgetchar(UPDATE_INTERVAL
);
597 if (c
< 0) { /* timeout */
599 if ((ret
= read_data(&data
)) != NULL
) {
600 free_data(&data_prev
);
604 compute_percentages(&data_prev
, &data
);
605 free_data(&data_prev
);
610 if (screen_mode
== SCREEN_HELP
&& c
>= 0) {
611 if (c
== 'h' || c
== '?')
613 /* go back to table and handle the key */
614 screen_mode
= SCREEN_TABLE
;
617 if (screen_mode
== SCREEN_SORT
&& c
>= 0) {
618 for (size_t i
= 0; i
< data
.table
.num_columns
; i
++) {
619 if (data
.table
.columns
[i
].key
== c
) {
621 screen_mode
= SCREEN_TABLE
;
629 case -1: /* do nothing */
641 screen_mode
= SCREEN_SORT
;
644 sort_reverse
= -sort_reverse
;
648 screen_mode
= SCREEN_HELP
;
653 if (op_mode
== OP_EXCS
) {
654 excs_all
= !excs_all
;
656 show_warning("Showing all exceptions");
658 show_warning("Showing only hot exceptions");
663 show_warning("Unknown command \"%c\", use \"h\" for help", c
);
664 continue; /* don't redraw */
667 if ((ret
= fill_table(&data
)) != NULL
) {
670 sort_table(&data
.table
);
679 fprintf(stderr
, "%s: %s\n", NAME
, ret
);