source: mainline/kernel/generic/src/sysinfo/stats.c@ e1b6742

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

export threads to user space
the "tasks" command can now print all threads or threads belonging to a task

  • Property mode set to 100644
File size: 17.1 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 generic
31 * @{
32 */
33/** @file
34 */
35
36#include <typedefs.h>
37#include <sysinfo/abi.h>
38#include <sysinfo/stats.h>
39#include <sysinfo/sysinfo.h>
40#include <time/clock.h>
41#include <mm/frame.h>
42#include <proc/task.h>
43#include <proc/thread.h>
44#include <str.h>
45#include <errno.h>
46#include <cpu.h>
47#include <arch.h>
48
49/** Bits of fixed-point precision for load */
50#define LOAD_FIXED_SHIFT 11
51
52/** 1.0 as fixed-point for load */
53#define LOAD_FIXED_1 (1 << LOAD_FIXED_SHIFT)
54
55/** Compute load in 5 second intervals */
56#define LOAD_INTERVAL 5
57
58/** Fixed-point representation of
59 *
60 * 1 / exp(5 sec / 1 min)
61 * 1 / exp(5 sec / 5 min)
62 * 1 / exp(5 sec / 15 min)
63 *
64 */
65static load_t load_exp[LOAD_STEPS] = {1884, 2014, 2037};
66
67/** Running average of the number of ready threads */
68static load_t avenrdy[LOAD_STEPS] = {0, 0, 0};
69
70/** Load calculation spinlock */
71SPINLOCK_STATIC_INITIALIZE_NAME(load_lock, "load_lock");
72
73/** Get system uptime
74 *
75 * @param item Sysinfo item (unused).
76 *
77 * @return System uptime (in secords).
78 *
79 */
80static unative_t get_stats_uptime(struct sysinfo_item *item)
81{
82 /* This doesn't have to be very accurate */
83 return uptime->seconds1;
84}
85
86/** Get statistics of all CPUs
87 *
88 * @param item Sysinfo item (unused).
89 * @param size Size of the returned data.
90 * @param dry_run Do not get the data, just calculate the size.
91 *
92 * @return Data containing several stats_cpu_t structures.
93 * If the return value is not NULL, it should be freed
94 * in the context of the sysinfo request.
95 */
96static void *get_stats_cpus(struct sysinfo_item *item, size_t *size,
97 bool dry_run)
98{
99 *size = sizeof(stats_cpu_t) * config.cpu_count;
100 if (dry_run)
101 return NULL;
102
103 /* Assumption: config.cpu_count is constant */
104 stats_cpu_t *stats_cpus = (stats_cpu_t *) malloc(*size, FRAME_ATOMIC);
105 if (stats_cpus == NULL) {
106 *size = 0;
107 return NULL;
108 }
109
110 /* Each CPU structure is locked separatelly */
111 ipl_t ipl = interrupts_disable();
112
113 size_t i;
114 for (i = 0; i < config.cpu_count; i++) {
115 spinlock_lock(&cpus[i].lock);
116
117 stats_cpus[i].id = cpus[i].id;
118 stats_cpus[i].frequency_mhz = cpus[i].frequency_mhz;
119 stats_cpus[i].busy_ticks = cpus[i].busy_ticks;
120 stats_cpus[i].idle_ticks = cpus[i].idle_ticks;
121
122 spinlock_unlock(&cpus[i].lock);
123 }
124
125 interrupts_restore(ipl);
126
127 return ((void *) stats_cpus);
128}
129
130/** Count number of nodes in an AVL tree
131 *
132 * AVL tree walker for counting nodes.
133 *
134 * @param node AVL tree node (unused).
135 * @param arg Pointer to the counter (size_t).
136 *
137 * @param Always true (continue the walk).
138 *
139 */
140static bool avl_count_walker(avltree_node_t *node, void *arg)
141{
142 size_t *count = (size_t *) arg;
143 (*count)++;
144
145 return true;
146}
147
148/** Gather tasks
149 *
150 * AVL task tree walker for gathering task IDs. Interrupts should
151 * be already disabled while walking the tree.
152 *
153 * @param node AVL task tree node.
154 * @param arg Pointer to the iterator into the array of task IDs.
155 *
156 * @param Always true (continue the walk).
157 *
158 */
159static bool task_serialize_walker(avltree_node_t *node, void *arg)
160{
161 task_id_t **ids = (task_id_t **) arg;
162 task_t *task = avltree_get_instance(node, task_t, tasks_tree_node);
163
164 /* Interrupts are already disabled */
165 spinlock_lock(&(task->lock));
166
167 /* Record the ID and increment the iterator */
168 **ids = task->taskid;
169 (*ids)++;
170
171 spinlock_unlock(&(task->lock));
172
173 return true;
174}
175
176/** Get task IDs
177 *
178 * @param item Sysinfo item (unused).
179 * @param size Size of the returned data.
180 * @param dry_run Do not get the data, just calculate the size.
181 *
182 * @return Data containing task IDs of all tasks.
183 * If the return value is not NULL, it should be freed
184 * in the context of the sysinfo request.
185 */
186static void *get_stats_tasks(struct sysinfo_item *item, size_t *size,
187 bool dry_run)
188{
189 /* Messing with task structures, avoid deadlock */
190 ipl_t ipl = interrupts_disable();
191 spinlock_lock(&tasks_lock);
192
193 /* First walk the task tree to count the tasks */
194 size_t count = 0;
195 avltree_walk(&tasks_tree, avl_count_walker, (void *) &count);
196
197 if (count == 0) {
198 /* No tasks found (strange) */
199 spinlock_unlock(&tasks_lock);
200 interrupts_restore(ipl);
201
202 *size = 0;
203 return NULL;
204 }
205
206 *size = sizeof(task_id_t) * count;
207 if (dry_run) {
208 spinlock_unlock(&tasks_lock);
209 interrupts_restore(ipl);
210 return NULL;
211 }
212
213 task_id_t *task_ids = (task_id_t *) malloc(*size, FRAME_ATOMIC);
214 if (task_ids == NULL) {
215 /* No free space for allocation */
216 spinlock_unlock(&tasks_lock);
217 interrupts_restore(ipl);
218
219 *size = 0;
220 return NULL;
221 }
222
223 /* Walk tha task tree again to gather the IDs */
224 task_id_t *iterator = task_ids;
225 avltree_walk(&tasks_tree, task_serialize_walker, (void *) &iterator);
226
227 spinlock_unlock(&tasks_lock);
228 interrupts_restore(ipl);
229
230 return ((void *) task_ids);
231}
232
233/** Gather threads
234 *
235 * AVL three tree walker for gathering thread IDs. Interrupts should
236 * be already disabled while walking the tree.
237 *
238 * @param node AVL thread tree node.
239 * @param arg Pointer to the iterator into the array of thread IDs.
240 *
241 * @param Always true (continue the walk).
242 *
243 */
244static bool thread_serialize_walker(avltree_node_t *node, void *arg)
245{
246 thread_id_t **ids = (thread_id_t **) arg;
247 thread_t *thread = avltree_get_instance(node, thread_t, threads_tree_node);
248
249 /* Interrupts are already disabled */
250 spinlock_lock(&(thread->lock));
251
252 /* Record the ID and increment the iterator */
253 **ids = thread->tid;
254 (*ids)++;
255
256 spinlock_unlock(&(thread->lock));
257
258 return true;
259}
260
261/** Get thread IDs
262 *
263 * @param item Sysinfo item (unused).
264 * @param size Size of the returned data.
265 * @param dry_run Do not get the data, just calculate the size.
266 *
267 * @return Data containing thread IDs of all threads.
268 * If the return value is not NULL, it should be freed
269 * in the context of the sysinfo request.
270 */
271static void *get_stats_threads(struct sysinfo_item *item, size_t *size,
272 bool dry_run)
273{
274 /* Messing with threads structures, avoid deadlock */
275 ipl_t ipl = interrupts_disable();
276 spinlock_lock(&threads_lock);
277
278 /* First walk the thread tree to count the threads */
279 size_t count = 0;
280 avltree_walk(&threads_tree, avl_count_walker, (void *) &count);
281
282 if (count == 0) {
283 /* No threads found (strange) */
284 spinlock_unlock(&threads_lock);
285 interrupts_restore(ipl);
286
287 *size = 0;
288 return NULL;
289 }
290
291 *size = sizeof(thread_id_t) * count;
292 if (dry_run) {
293 spinlock_unlock(&threads_lock);
294 interrupts_restore(ipl);
295 return NULL;
296 }
297
298 thread_id_t *thread_ids = (thread_id_t *) malloc(*size, FRAME_ATOMIC);
299 if (thread_ids == NULL) {
300 /* No free space for allocation */
301 spinlock_unlock(&threads_lock);
302 interrupts_restore(ipl);
303
304 *size = 0;
305 return NULL;
306 }
307
308 /* Walk tha thread tree again to gather the IDs */
309 thread_id_t *iterator = thread_ids;
310 avltree_walk(&threads_tree, thread_serialize_walker, (void *) &iterator);
311
312 spinlock_unlock(&threads_lock);
313 interrupts_restore(ipl);
314
315 return ((void *) thread_ids);
316}
317
318/** Get the size of a virtual address space
319 *
320 * @param as Address space.
321 *
322 * @return Size of the mapped virtual address space (bytes).
323 *
324 */
325static size_t get_task_virtmem(as_t *as)
326{
327 mutex_lock(&as->lock);
328
329 size_t result = 0;
330
331 /* Walk the B+ tree and count pages */
332 link_t *cur;
333 for (cur = as->as_area_btree.leaf_head.next;
334 cur != &as->as_area_btree.leaf_head; cur = cur->next) {
335 btree_node_t *node =
336 list_get_instance(cur, btree_node_t, leaf_link);
337
338 unsigned int i;
339 for (i = 0; i < node->keys; i++) {
340 as_area_t *area = node->value[i];
341
342 mutex_lock(&area->lock);
343 result += area->pages;
344 mutex_unlock(&area->lock);
345 }
346 }
347
348 mutex_unlock(&as->lock);
349
350 return result * PAGE_SIZE;
351}
352
353/** Get task statistics
354 *
355 * Get statistics of a given task. The task ID is passed
356 * as a string (current limitation of the sysinfo interface,
357 * but it is still reasonable for the given purpose).
358 *
359 * @param name Task ID (string-encoded number).
360 * @param dry_run Do not get the data, just calculate the size.
361 *
362 * @return Sysinfo return holder. The type of the returned
363 * data is either SYSINFO_VAL_UNDEFINED (unknown
364 * task ID or memory allocation error) or
365 * SYSINFO_VAL_FUNCTION_DATA (in that case the
366 * generated data should be freed within the
367 * sysinfo request context).
368 *
369 */
370static sysinfo_return_t get_stats_task(const char *name, bool dry_run)
371{
372 /* Initially no return value */
373 sysinfo_return_t ret;
374 ret.tag = SYSINFO_VAL_UNDEFINED;
375
376 /* Parse the task ID */
377 task_id_t task_id;
378 if (str_uint64(name, NULL, 0, true, &task_id) != EOK)
379 return ret;
380
381 /* Messing with task structures, avoid deadlock */
382 ipl_t ipl = interrupts_disable();
383 spinlock_lock(&tasks_lock);
384
385 task_t *task = task_find_by_id(task_id);
386 if (task == NULL) {
387 /* No task with this ID */
388 spinlock_unlock(&tasks_lock);
389 interrupts_restore(ipl);
390 return ret;
391 }
392
393 if (dry_run) {
394 ret.tag = SYSINFO_VAL_FUNCTION_DATA;
395 ret.data.data = NULL;
396 ret.data.size = sizeof(stats_task_t);
397
398 spinlock_unlock(&tasks_lock);
399 } else {
400 /* Allocate stats_task_t structure */
401 stats_task_t *stats_task =
402 (stats_task_t *) malloc(sizeof(stats_task_t), FRAME_ATOMIC);
403 if (stats_task == NULL) {
404 spinlock_unlock(&tasks_lock);
405 interrupts_restore(ipl);
406 return ret;
407 }
408
409 /* Correct return value */
410 ret.tag = SYSINFO_VAL_FUNCTION_DATA;
411 ret.data.data = (void *) stats_task;
412 ret.data.size = sizeof(stats_task_t);
413
414 /* Hand-over-hand locking */
415 spinlock_lock(&task->lock);
416 spinlock_unlock(&tasks_lock);
417
418 /* Copy task's statistics */
419 str_cpy(stats_task->name, TASK_NAME_BUFLEN, task->name);
420 stats_task->virtmem = get_task_virtmem(task->as);
421 stats_task->threads = atomic_get(&task->refcount);
422 task_get_accounting(task, &(stats_task->ucycles),
423 &(stats_task->kcycles));
424 stats_task->ipc_info = task->ipc_info;
425
426 spinlock_unlock(&task->lock);
427 }
428
429 interrupts_restore(ipl);
430
431 return ret;
432}
433
434/** Get thread statistics
435 *
436 * Get statistics of a given thread. The thread ID is passed
437 * as a string (current limitation of the sysinfo interface,
438 * but it is still reasonable for the given purpose).
439 *
440 * @param name Thread ID (string-encoded number).
441 * @param dry_run Do not get the data, just calculate the size.
442 *
443 * @return Sysinfo return holder. The type of the returned
444 * data is either SYSINFO_VAL_UNDEFINED (unknown
445 * thread ID or memory allocation error) or
446 * SYSINFO_VAL_FUNCTION_DATA (in that case the
447 * generated data should be freed within the
448 * sysinfo request context).
449 *
450 */
451static sysinfo_return_t get_stats_thread(const char *name, bool dry_run)
452{
453 /* Initially no return value */
454 sysinfo_return_t ret;
455 ret.tag = SYSINFO_VAL_UNDEFINED;
456
457 /* Parse the thread ID */
458 thread_id_t thread_id;
459 if (str_uint64(name, NULL, 0, true, &thread_id) != EOK)
460 return ret;
461
462 /* Messing with threads structures, avoid deadlock */
463 ipl_t ipl = interrupts_disable();
464 spinlock_lock(&threads_lock);
465
466 thread_t *thread = thread_find_by_id(thread_id);
467 if (thread == NULL) {
468 /* No thread with this ID */
469 spinlock_unlock(&threads_lock);
470 interrupts_restore(ipl);
471 return ret;
472 }
473
474 if (dry_run) {
475 ret.tag = SYSINFO_VAL_FUNCTION_DATA;
476 ret.data.data = NULL;
477 ret.data.size = sizeof(stats_thread_t);
478
479 spinlock_unlock(&threads_lock);
480 } else {
481 /* Allocate stats_thread_t structure */
482 stats_thread_t *stats_thread =
483 (stats_thread_t *) malloc(sizeof(stats_thread_t), FRAME_ATOMIC);
484 if (stats_thread == NULL) {
485 spinlock_unlock(&threads_lock);
486 interrupts_restore(ipl);
487 return ret;
488 }
489
490 /* Correct return value */
491 ret.tag = SYSINFO_VAL_FUNCTION_DATA;
492 ret.data.data = (void *) stats_thread;
493 ret.data.size = sizeof(stats_thread_t);
494
495 /* Hand-over-hand locking */
496 spinlock_lock(&thread->lock);
497 spinlock_unlock(&threads_lock);
498
499 /* Copy thread's statistics */
500 stats_thread->task_id = thread->task->taskid;
501 stats_thread->state = thread->state;
502 stats_thread->priority = thread->priority;
503 stats_thread->ucycles = thread->ucycles;
504 stats_thread->kcycles = thread->kcycles;
505
506 if (thread->cpu != NULL) {
507 stats_thread->on_cpu = true;
508 stats_thread->cpu = thread->cpu->id;
509 } else
510 stats_thread->on_cpu = false;
511
512 spinlock_unlock(&thread->lock);
513 }
514
515 interrupts_restore(ipl);
516
517 return ret;
518}
519
520/** Get physical memory statistics
521 *
522 * @param item Sysinfo item (unused).
523 * @param size Size of the returned data.
524 * @param dry_run Do not get the data, just calculate the size.
525 *
526 * @return Data containing stats_physmem_t.
527 * If the return value is not NULL, it should be freed
528 * in the context of the sysinfo request.
529 */
530static void *get_stats_physmem(struct sysinfo_item *item, size_t *size,
531 bool dry_run)
532{
533 *size = sizeof(stats_physmem_t);
534 if (dry_run)
535 return NULL;
536
537 stats_physmem_t *stats_physmem =
538 (stats_physmem_t *) malloc(*size, FRAME_ATOMIC);
539 if (stats_physmem == NULL) {
540 *size = 0;
541 return NULL;
542 }
543
544 zones_stats(&(stats_physmem->total), &(stats_physmem->unavail),
545 &(stats_physmem->used), &(stats_physmem->free));
546
547 return ((void *) stats_physmem);
548}
549
550/** Get system load
551 *
552 * @param item Sysinfo item (unused).
553 * @param size Size of the returned data.
554 * @param dry_run Do not get the data, just calculate the size.
555 *
556 * @return Data several load_t values.
557 * If the return value is not NULL, it should be freed
558 * in the context of the sysinfo request.
559 */
560static void *get_stats_load(struct sysinfo_item *item, size_t *size,
561 bool dry_run)
562{
563 *size = sizeof(load_t) * LOAD_STEPS;
564 if (dry_run)
565 return NULL;
566
567 load_t *stats_load = (load_t *) malloc(*size, FRAME_ATOMIC);
568 if (stats_load == NULL) {
569 *size = 0;
570 return NULL;
571 }
572
573 /* To always get consistent values acquire the spinlock */
574 ipl_t ipl = interrupts_disable();
575 spinlock_lock(&load_lock);
576
577 unsigned int i;
578 for (i = 0; i < LOAD_STEPS; i++)
579 stats_load[i] = avenrdy[i] << LOAD_FIXED_SHIFT;
580
581 spinlock_unlock(&load_lock);
582 interrupts_restore(ipl);
583
584 return ((void *) stats_load);
585}
586
587/** Calculate load
588 *
589 */
590static inline load_t load_calc(load_t load, load_t exp, size_t ready)
591{
592 load *= exp;
593 load += ready * (LOAD_FIXED_1 - exp);
594
595 return (load >> LOAD_FIXED_SHIFT);
596}
597
598/** Count threads in ready queues
599 *
600 * Should be called with interrupts disabled.
601 *
602 */
603static inline size_t get_ready_count(void)
604{
605 size_t i;
606 size_t count = 0;
607
608 for (i = 0; i < config.cpu_count; i++) {
609 spinlock_lock(&cpus[i].lock);
610
611 size_t j;
612 for (j = 0; j < RQ_COUNT; j++) {
613 spinlock_lock(&cpus[i].rq[j].lock);
614 count += cpus[i].rq[j].n;
615 spinlock_unlock(&cpus[i].rq[j].lock);
616 }
617
618 spinlock_unlock(&cpus[i].lock);
619 }
620
621 return count;
622}
623
624/** Load computation thread.
625 *
626 * Compute system load every few seconds.
627 *
628 * @param arg Unused.
629 *
630 */
631void kload(void *arg)
632{
633 thread_detach(THREAD);
634
635 while (true) {
636 /* Mutually exclude with get_stats_load() */
637 ipl_t ipl = interrupts_disable();
638 spinlock_lock(&load_lock);
639
640 size_t ready = get_ready_count() * LOAD_FIXED_1;
641
642 unsigned int i;
643 for (i = 0; i < LOAD_STEPS; i++)
644 avenrdy[i] = load_calc(avenrdy[i], load_exp[i], ready);
645
646 spinlock_unlock(&load_lock);
647 interrupts_restore(ipl);
648
649 thread_sleep(LOAD_INTERVAL);
650 }
651}
652
653/** Register sysinfo statistical items
654 *
655 */
656void stats_init(void)
657{
658 sysinfo_set_item_fn_val("system.uptime", NULL, get_stats_uptime);
659 sysinfo_set_item_fn_data("system.cpus", NULL, get_stats_cpus);
660 sysinfo_set_item_fn_data("system.physmem", NULL, get_stats_physmem);
661 sysinfo_set_item_fn_data("system.load", NULL, get_stats_load);
662 sysinfo_set_item_fn_data("system.tasks", NULL, get_stats_tasks);
663 sysinfo_set_item_fn_data("system.threads", NULL, get_stats_threads);
664 sysinfo_set_subtree_fn("system.tasks", NULL, get_stats_task);
665 sysinfo_set_subtree_fn("system.threads", NULL, get_stats_thread);
666}
667
668/** @}
669 */
Note: See TracBrowser for help on using the repository browser.