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

Last change on this file was 894afff, checked in by Martin Decky <martin@…>, 6 years ago

cstyle

  • Property mode set to 100644
File size: 12.0 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 stats
31 * @{
32 */
33/**
34 * @file
35 */
36
37#include <stdio.h>
38#include <task.h>
39#include <stats.h>
40#include <errno.h>
41#include <stdlib.h>
42#include <stdlib.h>
43#include <inttypes.h>
44#include <stdbool.h>
45#include <str.h>
46#include <arg_parse.h>
47
48#define NAME "stats"
49
50#define DAY 86400
51#define HOUR 3600
52#define MINUTE 60
53
54#define KERNEL_NAME "kernel"
55#define INIT_PREFIX "init:"
56
57typedef enum {
58 LIST_TASKS,
59 LIST_THREADS,
60 LIST_IPCCS,
61 LIST_CPUS,
62 PRINT_LOAD,
63 PRINT_UPTIME,
64 PRINT_ARCH
65} output_toggle_t;
66
67static void list_tasks(void)
68{
69 size_t count;
70 stats_task_t *stats_tasks = stats_get_tasks(&count);
71
72 if (stats_tasks == NULL) {
73 fprintf(stderr, "%s: Unable to get tasks\n", NAME);
74 return;
75 }
76
77 printf("[taskid] [thrds] [resident] [virtual] [ucycles]"
78 " [kcycles] [name\n");
79
80 for (size_t i = 0; i < count; i++) {
81 uint64_t resmem;
82 uint64_t virtmem;
83 uint64_t ucycles;
84 uint64_t kcycles;
85 const char *resmem_suffix;
86 const char *virtmem_suffix;
87 char usuffix;
88 char ksuffix;
89
90 bin_order_suffix(stats_tasks[i].resmem, &resmem, &resmem_suffix, true);
91 bin_order_suffix(stats_tasks[i].virtmem, &virtmem, &virtmem_suffix, true);
92 order_suffix(stats_tasks[i].ucycles, &ucycles, &usuffix);
93 order_suffix(stats_tasks[i].kcycles, &kcycles, &ksuffix);
94
95 printf("%-8" PRIu64 " %7zu %7" PRIu64 "%s %6" PRIu64 "%s"
96 " %8" PRIu64 "%c %8" PRIu64 "%c %s\n",
97 stats_tasks[i].task_id, stats_tasks[i].threads,
98 resmem, resmem_suffix, virtmem, virtmem_suffix,
99 ucycles, usuffix, kcycles, ksuffix, stats_tasks[i].name);
100 }
101
102 free(stats_tasks);
103}
104
105static void list_threads(task_id_t task_id, bool all)
106{
107 size_t count;
108 stats_thread_t *stats_threads = stats_get_threads(&count);
109
110 if (stats_threads == NULL) {
111 fprintf(stderr, "%s: Unable to get threads\n", NAME);
112 return;
113 }
114
115 printf("[taskid] [threadid] [state ] [prio] [cpu ] [ucycles] [kcycles]\n");
116
117 for (size_t i = 0; i < count; i++) {
118 if ((all) || (stats_threads[i].task_id == task_id)) {
119 uint64_t ucycles, kcycles;
120 char usuffix, ksuffix;
121
122 order_suffix(stats_threads[i].ucycles, &ucycles, &usuffix);
123 order_suffix(stats_threads[i].kcycles, &kcycles, &ksuffix);
124
125 printf("%-8" PRIu64 " %-10" PRIu64 " %-8s %6d ",
126 stats_threads[i].task_id, stats_threads[i].thread_id,
127 thread_get_state(stats_threads[i].state),
128 stats_threads[i].priority);
129
130 if (stats_threads[i].on_cpu)
131 printf("%6u ", stats_threads[i].cpu);
132 else
133 printf("(none) ");
134
135 printf("%8" PRIu64 "%c %8" PRIu64 "%c\n",
136 ucycles, usuffix, kcycles, ksuffix);
137 }
138 }
139
140 free(stats_threads);
141}
142
143static void list_ipccs(task_id_t task_id, bool all)
144{
145 size_t count;
146 stats_ipcc_t *stats_ipccs = stats_get_ipccs(&count);
147
148 if (stats_ipccs == NULL) {
149 fprintf(stderr, "%s: Unable to get IPC connections\n", NAME);
150 return;
151 }
152
153 printf("[caller] [callee]\n");
154
155 for (size_t i = 0; i < count; i++) {
156 if ((all) || (stats_ipccs[i].caller == task_id)) {
157 printf("%-8" PRIu64 " %-8" PRIu64 "\n",
158 stats_ipccs[i].caller, stats_ipccs[i].callee);
159 }
160 }
161
162 free(stats_ipccs);
163}
164
165static void list_cpus(void)
166{
167 size_t count;
168 stats_cpu_t *cpus = stats_get_cpus(&count);
169
170 if (cpus == NULL) {
171 fprintf(stderr, "%s: Unable to get CPU statistics\n", NAME);
172 return;
173 }
174
175 printf("[id] [MHz ] [busy cycles] [idle cycles]\n");
176
177 for (size_t i = 0; i < count; i++) {
178 printf("%-4u ", cpus[i].id);
179 if (cpus[i].active) {
180 uint64_t bcycles, icycles;
181 char bsuffix, isuffix;
182
183 order_suffix(cpus[i].busy_cycles, &bcycles, &bsuffix);
184 order_suffix(cpus[i].idle_cycles, &icycles, &isuffix);
185
186 printf("%10" PRIu16 " %12" PRIu64 "%c %12" PRIu64 "%c\n",
187 cpus[i].frequency_mhz, bcycles, bsuffix,
188 icycles, isuffix);
189 } else
190 printf("inactive\n");
191 }
192
193 free(cpus);
194}
195
196static void print_load(void)
197{
198 size_t count;
199 load_t *load = stats_get_load(&count);
200
201 if (load == NULL) {
202 fprintf(stderr, "%s: Unable to get load\n", NAME);
203 return;
204 }
205
206 printf("%s: Load average: ", NAME);
207
208 for (size_t i = 0; i < count; i++) {
209 if (i > 0)
210 printf(" ");
211
212 stats_print_load_fragment(load[i], 2);
213 }
214
215 printf("\n");
216
217 free(load);
218}
219
220static void print_uptime(void)
221{
222 struct timespec uptime;
223 getuptime(&uptime);
224
225 printf("%s: Up %lld days, %lld hours, %lld minutes, %lld seconds\n",
226 NAME, uptime.tv_sec / DAY, (uptime.tv_sec % DAY) / HOUR,
227 (uptime.tv_sec % HOUR) / MINUTE, uptime.tv_sec % MINUTE);
228}
229
230static char *escape_dot(const char *str)
231{
232 size_t size = 0;
233 for (size_t i = 0; str[i] != 0; i++) {
234 if (str[i] == '"')
235 size++;
236
237 size++;
238 }
239
240 char *escaped_str = calloc(size + 1, sizeof(char));
241 if (escaped_str == NULL)
242 return NULL;
243
244 size_t pos = 0;
245 for (size_t i = 0; str[i] != 0; i++) {
246 if (str[i] == '"') {
247 escaped_str[pos] = '\\';
248 pos++;
249 }
250
251 escaped_str[pos] = str[i];
252 pos++;
253 }
254
255 escaped_str[pos] = 0;
256
257 return escaped_str;
258}
259
260static void print_arch(void)
261{
262 size_t count_tasks;
263 stats_task_t *stats_tasks = stats_get_tasks(&count_tasks);
264
265 if (stats_tasks == NULL) {
266 fprintf(stderr, "%s: Unable to get tasks\n", NAME);
267 return;
268 }
269
270 size_t count_ipccs;
271 stats_ipcc_t *stats_ipccs = stats_get_ipccs(&count_ipccs);
272
273 if (stats_ipccs == NULL) {
274 fprintf(stderr, "%s: Unable to get IPC connections\n", NAME);
275 return;
276 }
277
278 /* Global dot language attributes */
279 printf("digraph HelenOS {\n");
280 printf("\tlayout=sfdp\n");
281 printf("\t// layout=neato\n");
282 printf("\tsplines=true\n");
283 printf("\t// splines=ortho\n");
284 printf("\tconcentrate=true\n");
285 printf("\tcenter=true\n");
286 printf("\toverlap=false\n");
287 printf("\toutputorder=edgesfirst\n");
288 printf("\tfontsize=12\n");
289 printf("\tnode [shape=component style=filled color=red "
290 "fillcolor=yellow]\n\t\n");
291
292 bool kernel_found = false;
293 task_id_t kernel_id = 0;
294
295 /* Tasks as vertices (components) */
296 for (size_t i = 0; i < count_tasks; i++) {
297 /* Kernel task */
298 bool kernel = (str_cmp(stats_tasks[i].name, KERNEL_NAME) == 0);
299
300 /* Init task */
301 bool init = str_test_prefix(stats_tasks[i].name, INIT_PREFIX);
302
303 char *escaped_name = NULL;
304
305 if (init)
306 escaped_name = escape_dot(str_suffix(stats_tasks[i].name,
307 str_length(INIT_PREFIX)));
308 else
309 escaped_name = escape_dot(stats_tasks[i].name);
310
311 if (escaped_name == NULL)
312 continue;
313
314 if (kernel) {
315 if (kernel_found) {
316 fprintf(stderr, "%s: Duplicate kernel tasks\n", NAME);
317 } else {
318 kernel_found = true;
319 kernel_id = stats_tasks[i].task_id;
320 }
321
322 printf("\ttask%" PRIu64 " [label=\"%s\" shape=invtrapezium "
323 "fillcolor=gold]\n", stats_tasks[i].task_id, escaped_name);
324 } else if (init)
325 printf("\ttask%" PRIu64 " [label=\"%s\" fillcolor=orange]\n",
326 stats_tasks[i].task_id, escaped_name);
327 else
328 printf("\ttask%" PRIu64 " [label=\"%s\"]\n", stats_tasks[i].task_id,
329 escaped_name);
330
331 free(escaped_name);
332 }
333
334 printf("\t\n");
335
336 if (kernel_found) {
337 /*
338 * Add an invisible edge from all user
339 * space tasks to the kernel to increase
340 * the kernel ranking.
341 */
342
343 for (size_t i = 0; i < count_tasks; i++) {
344 /* Skip the kernel itself */
345 if (stats_tasks[i].task_id == kernel_id)
346 continue;
347
348 printf("\ttask%" PRIu64 " -> task%" PRIu64 " [style=\"invis\"]\n",
349 stats_tasks[i].task_id, kernel_id);
350 }
351 }
352
353 printf("\t\n");
354
355 /* IPC connections as edges */
356 for (size_t i = 0; i < count_ipccs; i++) {
357 printf("\ttask%" PRIu64 " -> task%" PRIu64 "\n",
358 stats_ipccs[i].caller, stats_ipccs[i].callee);
359 }
360
361 printf("}\n");
362
363 free(stats_tasks);
364 free(stats_ipccs);
365}
366
367static void usage(const char *name)
368{
369 printf(
370 "Usage: %s [-t task_id] [-i task_id] [-at] [-ai] [-c] [-l] [-u] [-d]\n"
371 "\n"
372 "Options:\n"
373 "\t-t task_id | --task=task_id\n"
374 "\t\tList threads of the given task\n"
375 "\n"
376 "\t-i task_id | --ipcc=task_id\n"
377 "\t\tList IPC connections of the given task\n"
378 "\n"
379 "\t-at | --all-threads\n"
380 "\t\tList all threads\n"
381 "\n"
382 "\t-ai | --all-ipccs\n"
383 "\t\tList all IPC connections\n"
384 "\n"
385 "\t-c | --cpus\n"
386 "\t\tList CPUs\n"
387 "\n"
388 "\t-l | --load\n"
389 "\t\tPrint system load\n"
390 "\n"
391 "\t-u | --uptime\n"
392 "\t\tPrint system uptime\n"
393 "\n"
394 "\t-d | --design\n"
395 "\t\tPrint the current system architecture graph\n"
396 "\n"
397 "\t-h | --help\n"
398 "\t\tPrint this usage information\n"
399 "\n"
400 "Without any options all tasks are listed\n",
401 name);
402}
403
404int main(int argc, char *argv[])
405{
406 output_toggle_t output_toggle = LIST_TASKS;
407 bool toggle_all = false;
408 task_id_t task_id = 0;
409
410 for (int i = 1; i < argc; i++) {
411 int off;
412
413 /* Usage */
414 if ((off = arg_parse_short_long(argv[i], "-h", "--help")) != -1) {
415 usage(argv[0]);
416 return 0;
417 }
418
419 /* All IPC connections */
420 if ((off = arg_parse_short_long(argv[i], "-ai", "--all-ipccs")) != -1) {
421 output_toggle = LIST_IPCCS;
422 toggle_all = true;
423 continue;
424 }
425
426 /* All threads */
427 if ((off = arg_parse_short_long(argv[i], "-at", "--all-threads")) != -1) {
428 output_toggle = LIST_THREADS;
429 toggle_all = true;
430 continue;
431 }
432
433 /* IPC connections */
434 if ((off = arg_parse_short_long(argv[i], "-i", "--ipcc=")) != -1) {
435 // TODO: Support for 64b range
436 int tmp;
437 errno_t ret = arg_parse_int(argc, argv, &i, &tmp, off);
438 if (ret != EOK) {
439 printf("%s: Malformed task id '%s'\n", NAME, argv[i]);
440 return -1;
441 }
442
443 task_id = tmp;
444
445 output_toggle = LIST_IPCCS;
446 continue;
447 }
448
449 /* Tasks */
450 if ((off = arg_parse_short_long(argv[i], "-t", "--task=")) != -1) {
451 // TODO: Support for 64b range
452 int tmp;
453 errno_t ret = arg_parse_int(argc, argv, &i, &tmp, off);
454 if (ret != EOK) {
455 printf("%s: Malformed task id '%s'\n", NAME, argv[i]);
456 return -1;
457 }
458
459 task_id = tmp;
460
461 output_toggle = LIST_THREADS;
462 continue;
463 }
464
465 /* CPUs */
466 if ((off = arg_parse_short_long(argv[i], "-c", "--cpus")) != -1) {
467 output_toggle = LIST_CPUS;
468 continue;
469 }
470
471 /* Load */
472 if ((off = arg_parse_short_long(argv[i], "-l", "--load")) != -1) {
473 output_toggle = PRINT_LOAD;
474 continue;
475 }
476
477 /* Uptime */
478 if ((off = arg_parse_short_long(argv[i], "-u", "--uptime")) != -1) {
479 output_toggle = PRINT_UPTIME;
480 continue;
481 }
482
483 /* Architecture */
484 if ((off = arg_parse_short_long(argv[i], "-d", "--design")) != -1) {
485 output_toggle = PRINT_ARCH;
486 continue;
487 }
488 }
489
490 switch (output_toggle) {
491 case LIST_TASKS:
492 list_tasks();
493 break;
494 case LIST_THREADS:
495 list_threads(task_id, toggle_all);
496 break;
497 case LIST_IPCCS:
498 list_ipccs(task_id, toggle_all);
499 break;
500 case LIST_CPUS:
501 list_cpus();
502 break;
503 case PRINT_LOAD:
504 print_load();
505 break;
506 case PRINT_UPTIME:
507 print_uptime();
508 break;
509 case PRINT_ARCH:
510 print_arch();
511 break;
512 }
513
514 return 0;
515}
516
517/** @}
518 */
Note: See TracBrowser for help on using the repository browser.