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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 549012c was d0c82c5, checked in by Martin Decky <martin@…>, 15 years ago

perfect CPU cycles accounting, cherry-picked and adopted from lp:~ersin/helenos/measure2

  • Property mode set to 100644
File size: 10.3 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 <unistd.h>
41#include <task.h>
42#include <thread.h>
43#include <sys/time.h>
44#include <arch/barrier.h>
45#include <errno.h>
46#include "screen.h"
47#include "input.h"
48#include "top.h"
49
50#define NAME "top"
51
52#define UPDATE_INTERVAL 1
53
54#define DAY 86400
55#define HOUR 3600
56#define MINUTE 60
57
58op_mode_t op_mode = OP_TASKS;
59bool excs_all = false;
60
61static const char *read_data(data_t *target)
62{
63 /* Initialize data */
64 target->load = NULL;
65 target->cpus = NULL;
66 target->cpus_perc = NULL;
67 target->tasks = NULL;
68 target->tasks_perc = NULL;
69 target->threads = NULL;
70 target->exceptions = NULL;
71 target->exceptions_perc = NULL;
72 target->physmem = NULL;
73
74 /* Get current time */
75 struct timeval time;
76 if (gettimeofday(&time, NULL) != EOK)
77 return "Cannot get time of day";
78
79 target->hours = (time.tv_sec % DAY) / HOUR;
80 target->minutes = (time.tv_sec % HOUR) / MINUTE;
81 target->seconds = time.tv_sec % MINUTE;
82
83 /* Get uptime */
84 sysarg_t uptime = stats_get_uptime();
85 target->udays = uptime / DAY;
86 target->uhours = (uptime % DAY) / HOUR;
87 target->uminutes = (uptime % HOUR) / MINUTE;
88 target->useconds = uptime % MINUTE;
89
90 /* Get load */
91 target->load = stats_get_load(&(target->load_count));
92 if (target->load == NULL)
93 return "Cannot get system load";
94
95 /* Get CPUs */
96 target->cpus = stats_get_cpus(&(target->cpus_count));
97 if (target->cpus == NULL)
98 return "Cannot get CPUs";
99
100 target->cpus_perc =
101 (perc_cpu_t *) calloc(target->cpus_count, sizeof(perc_cpu_t));
102 if (target->cpus_perc == NULL)
103 return "Not enough memory for CPU utilization";
104
105 /* Get tasks */
106 target->tasks = stats_get_tasks(&(target->tasks_count));
107 if (target->tasks == NULL)
108 return "Cannot get tasks";
109
110 target->tasks_perc =
111 (perc_task_t *) calloc(target->tasks_count, sizeof(perc_task_t));
112 if (target->tasks_perc == NULL)
113 return "Not enough memory for task utilization";
114
115 /* Get threads */
116 target->threads = stats_get_threads(&(target->threads_count));
117 if (target->threads == NULL)
118 return "Cannot get threads";
119
120 /* Get Exceptions */
121 target->exceptions = stats_get_exceptions(&(target->exceptions_count));
122 if (target->exceptions == NULL)
123 return "Cannot get exceptions";
124
125 target->exceptions_perc =
126 (perc_exc_t *) calloc(target->exceptions_count, sizeof(perc_exc_t));
127 if (target->exceptions_perc == NULL)
128 return "Not enough memory for exception utilization";
129
130 /* Get physical memory */
131 target->physmem = stats_get_physmem();
132 if (target->physmem == NULL)
133 return "Cannot get physical memory";
134
135 return NULL;
136}
137
138/** Computes percentage differencies from old_data to new_data
139 *
140 * @param old_data Pointer to old data strucutre.
141 * @param new_data Pointer to actual data where percetages are stored.
142 *
143 */
144static const char *compute_percentages(data_t *old_data, data_t *new_data)
145{
146 /* Allocate memory */
147
148 uint64_t *ucycles_diff = calloc(new_data->tasks_count,
149 sizeof(uint64_t));
150 if (ucycles_diff == NULL)
151 return "Not enough memory for user utilization";
152
153 uint64_t *kcycles_diff = calloc(new_data->tasks_count,
154 sizeof(uint64_t));
155 if (kcycles_diff == NULL) {
156 free(ucycles_diff);
157 return "Not enough memory for kernel utilization";
158 }
159
160 uint64_t *ecycles_diff = calloc(new_data->exceptions_count,
161 sizeof(uint64_t));
162 if (ecycles_diff == NULL) {
163 free(ucycles_diff);
164 free(kcycles_diff);
165 return "Not enough memory for exception cycles utilization";
166 }
167
168 uint64_t *ecount_diff = calloc(new_data->exceptions_count,
169 sizeof(uint64_t));
170 if (ecount_diff == NULL) {
171 free(ucycles_diff);
172 free(kcycles_diff);
173 free(ecycles_diff);
174 return "Not enough memory for exception count utilization";
175 }
176
177 /* For each CPU: Compute total cycles and divide it between
178 user and kernel */
179
180 size_t i;
181 for (i = 0; i < new_data->cpus_count; i++) {
182 uint64_t idle =
183 new_data->cpus[i].idle_cycles - old_data->cpus[i].idle_cycles;
184 uint64_t busy =
185 new_data->cpus[i].busy_cycles - old_data->cpus[i].busy_cycles;
186 uint64_t sum = idle + busy;
187
188 FRACTION_TO_FLOAT(new_data->cpus_perc[i].idle, idle * 100, sum);
189 FRACTION_TO_FLOAT(new_data->cpus_perc[i].busy, busy * 100, sum);
190 }
191
192 /* For all tasks compute sum and differencies of all cycles */
193
194 uint64_t virtmem_total = 0;
195 uint64_t ucycles_total = 0;
196 uint64_t kcycles_total = 0;
197
198 for (i = 0; i < new_data->tasks_count; i++) {
199 /* Match task with the previous instance */
200
201 bool found = false;
202 size_t j;
203 for (j = 0; j < old_data->tasks_count; j++) {
204 if (new_data->tasks[i].task_id == old_data->tasks[j].task_id) {
205 found = true;
206 break;
207 }
208 }
209
210 if (!found) {
211 /* This is newly borned task, ignore it */
212 ucycles_diff[i] = 0;
213 kcycles_diff[i] = 0;
214 continue;
215 }
216
217 ucycles_diff[i] =
218 new_data->tasks[i].ucycles - old_data->tasks[j].ucycles;
219 kcycles_diff[i] =
220 new_data->tasks[i].kcycles - old_data->tasks[j].kcycles;
221
222 virtmem_total += new_data->tasks[i].virtmem;
223 ucycles_total += ucycles_diff[i];
224 kcycles_total += kcycles_diff[i];
225 }
226
227 /* For each task compute percential change */
228
229 for (i = 0; i < new_data->tasks_count; i++) {
230 FRACTION_TO_FLOAT(new_data->tasks_perc[i].virtmem,
231 new_data->tasks[i].virtmem * 100, virtmem_total);
232 FRACTION_TO_FLOAT(new_data->tasks_perc[i].ucycles,
233 ucycles_diff[i] * 100, ucycles_total);
234 FRACTION_TO_FLOAT(new_data->tasks_perc[i].kcycles,
235 kcycles_diff[i] * 100, kcycles_total);
236 }
237
238 /* For all exceptions compute sum and differencies of cycles */
239
240 uint64_t ecycles_total = 0;
241 uint64_t ecount_total = 0;
242
243 for (i = 0; i < new_data->exceptions_count; i++) {
244 /*
245 * March exception with the previous instance.
246 * This is quite paranoid since exceptions do not
247 * usually disappear, but it does not hurt.
248 */
249
250 bool found = false;
251 size_t j;
252 for (j = 0; j < old_data->exceptions_count; j++) {
253 if (new_data->exceptions[i].id == old_data->exceptions[j].id) {
254 found = true;
255 break;
256 }
257 }
258
259 if (!found) {
260 /* This is a new exception, ignore it */
261 ecycles_diff[i] = 0;
262 ecount_diff[i] = 0;
263 continue;
264 }
265
266 ecycles_diff[i] =
267 new_data->exceptions[i].cycles - old_data->exceptions[j].cycles;
268 ecount_diff[i] =
269 new_data->exceptions[i].count - old_data->exceptions[i].count;
270
271 ecycles_total += ecycles_diff[i];
272 ecount_total += ecount_diff[i];
273 }
274
275 /* For each exception compute percential change */
276
277 for (i = 0; i < new_data->exceptions_count; i++) {
278 FRACTION_TO_FLOAT(new_data->exceptions_perc[i].cycles,
279 ecycles_diff[i] * 100, ecycles_total);
280 FRACTION_TO_FLOAT(new_data->exceptions_perc[i].count,
281 ecount_diff[i] * 100, ecount_total);
282 }
283
284 /* Cleanup */
285
286 free(ucycles_diff);
287 free(kcycles_diff);
288 free(ecycles_diff);
289 free(ecount_diff);
290
291 return NULL;
292}
293
294static void free_data(data_t *target)
295{
296 if (target->load != NULL)
297 free(target->load);
298
299 if (target->cpus != NULL)
300 free(target->cpus);
301
302 if (target->cpus_perc != NULL)
303 free(target->cpus_perc);
304
305 if (target->tasks != NULL)
306 free(target->tasks);
307
308 if (target->tasks_perc != NULL)
309 free(target->tasks_perc);
310
311 if (target->threads != NULL)
312 free(target->threads);
313
314 if (target->exceptions != NULL)
315 free(target->exceptions);
316
317 if (target->exceptions_perc != NULL)
318 free(target->exceptions_perc);
319
320 if (target->physmem != NULL)
321 free(target->physmem);
322}
323
324int main(int argc, char *argv[])
325{
326 data_t data;
327 data_t data_prev;
328 const char *ret = NULL;
329
330 screen_init();
331 printf("Reading initial data...\n");
332
333 if ((ret = read_data(&data_prev)) != NULL)
334 goto out;
335
336 /* Compute some rubbish to have initialised values */
337 if ((ret = compute_percentages(&data_prev, &data_prev)) != NULL)
338 goto out;
339
340 /* And paint screen until death */
341 while (true) {
342 int c = tgetchar(UPDATE_INTERVAL);
343 if (c < 0) {
344 if ((ret = read_data(&data)) != NULL) {
345 free_data(&data);
346 goto out;
347 }
348
349 if ((ret = compute_percentages(&data_prev, &data)) != NULL) {
350 free_data(&data);
351 goto out;
352 }
353
354 print_data(&data);
355 free_data(&data_prev);
356 data_prev = data;
357
358 continue;
359 }
360
361 switch (c) {
362 case 't':
363 print_warning("Showing task statistics");
364 op_mode = OP_TASKS;
365 break;
366 case 'i':
367 print_warning("Showing IPC statistics");
368 op_mode = OP_IPC;
369 break;
370 case 'e':
371 print_warning("Showing exception statistics");
372 op_mode = OP_EXCS;
373 break;
374 case 'h':
375 print_warning("Showing help");
376 op_mode = OP_HELP;
377 break;
378 case 'q':
379 goto out;
380 case 'a':
381 if (op_mode == OP_EXCS) {
382 excs_all = !excs_all;
383 if (excs_all)
384 print_warning("Showing all exceptions");
385 else
386 print_warning("Showing only hot exceptions");
387 break;
388 }
389 default:
390 print_warning("Unknown command \"%c\", use \"h\" for help", c);
391 break;
392 }
393 }
394
395out:
396 screen_done();
397 free_data(&data_prev);
398
399 if (ret != NULL) {
400 fprintf(stderr, "%s: %s\n", NAME, ret);
401 return 1;
402 }
403
404 return 0;
405}
406
407/** @}
408 */
Note: See TracBrowser for help on using the repository browser.