Index: kernel/generic/include/proc/thread.h
===================================================================
--- kernel/generic/include/proc/thread.h	(revision bbda5ab736025c36aa1ad4f3179adec67abe36e3)
+++ kernel/generic/include/proc/thread.h	(revision e1b674201c97668fa9f3559cca094e0e1865629a)
@@ -48,4 +48,5 @@
 #include <proc/uarg.h>
 #include <udebug/udebug.h>
+#include <sysinfo/abi.h>
 
 #define THREAD_STACK_SIZE	STACK_SIZE
@@ -69,22 +70,4 @@
 #define THREAD_FLAG_NOATTACH	(1 << 3)
 
-/** Thread states. */
-typedef enum {
-	/** It is an error, if thread is found in this state. */
-	Invalid,
-	/** State of a thread that is currently executing on some CPU. */
-	Running,
-	/** Thread in this state is waiting for an event. */
-	Sleeping,
-	/** State of threads in a run queue. */
-	Ready,
-	/** Threads are in this state before they are first readied. */
-	Entering,
-	/** After a thread calls thread_exit(), it is put into Exiting state. */
-	Exiting,
-	/** Threads that were not detached but exited are Lingering. */
-	Lingering
-} state_t;
-
 /** Thread structure. There is one per thread. */
 typedef struct thread {
@@ -253,4 +236,5 @@
 extern void thread_print_list(void);
 extern void thread_destroy(thread_t *);
+extern thread_t *thread_find_by_id(thread_id_t);
 extern void thread_update_accounting(bool);
 extern bool thread_exists(thread_t *);
Index: kernel/generic/include/sysinfo/abi.h
===================================================================
--- kernel/generic/include/sysinfo/abi.h	(revision bbda5ab736025c36aa1ad4f3179adec67abe36e3)
+++ kernel/generic/include/sysinfo/abi.h	(revision e1b674201c97668fa9f3559cca094e0e1865629a)
@@ -42,4 +42,22 @@
 /** Maximum task name size */
 #define TASK_NAME_BUFLEN  20
+
+/** Thread states */
+typedef enum {
+	/** It is an error, if thread is found in this state. */
+	Invalid,
+	/** State of a thread that is currently executing on some CPU. */
+	Running,
+	/** Thread in this state is waiting for an event. */
+	Sleeping,
+	/** State of threads in a run queue. */
+	Ready,
+	/** Threads are in this state before they are first readied. */
+	Entering,
+	/** After a thread calls thread_exit(), it is put into Exiting state. */
+	Exiting,
+	/** Threads that were not detached but exited are Lingering. */
+	Lingering
+} state_t;
 
 /** Statistics about a single CPU
@@ -89,4 +107,17 @@
 } stats_task_t;
 
+/** Statistics about a single thread
+ *
+ */
+typedef struct {
+	task_id_t task_id;
+	state_t state;
+	int priority;
+	uint64_t ucycles;
+	uint64_t kcycles;
+	bool on_cpu;
+	unsigned int cpu;
+} stats_thread_t;
+
 /** Load fixed-point value */
 typedef uint32_t load_t;
Index: kernel/generic/include/sysinfo/sysinfo.h
===================================================================
--- kernel/generic/include/sysinfo/sysinfo.h	(revision bbda5ab736025c36aa1ad4f3179adec67abe36e3)
+++ kernel/generic/include/sysinfo/sysinfo.h	(revision e1b674201c97668fa9f3559cca094e0e1865629a)
@@ -105,5 +105,5 @@
 
 /** Generated subtree function */
-typedef sysinfo_return_t (*sysinfo_fn_subtree_t)(const char *);
+typedef sysinfo_return_t (*sysinfo_fn_subtree_t)(const char *, bool);
 
 /** Sysinfo subtree (union)
Index: kernel/generic/src/proc/task.c
===================================================================
--- kernel/generic/src/proc/task.c	(revision bbda5ab736025c36aa1ad4f3179adec67abe36e3)
+++ kernel/generic/src/proc/task.c	(revision e1b674201c97668fa9f3559cca094e0e1865629a)
@@ -312,12 +312,11 @@
  * @param id Task ID.
  *
- * @return Task structure address or NULL if there is no such task
- *         ID.
+ * @return Task structure address or NULL if there is no such task ID.
  *
  */
 task_t *task_find_by_id(task_id_t id)
 {
-	avltree_node_t *node;
-	node = avltree_search(&tasks_tree, (avltree_key_t) id);
+	avltree_node_t *node =
+	    avltree_search(&tasks_tree, (avltree_key_t) id);
 	
 	if (node)
Index: kernel/generic/src/proc/thread.c
===================================================================
--- kernel/generic/src/proc/thread.c	(revision bbda5ab736025c36aa1ad4f3179adec67abe36e3)
+++ kernel/generic/src/proc/thread.c	(revision e1b674201c97668fa9f3559cca094e0e1865629a)
@@ -84,5 +84,10 @@
 	"Exiting",
 	"Lingering"
-}; 
+};
+
+typedef struct {
+	thread_id_t thread_id;
+	thread_t *thread;
+} thread_iterator_t;
 
 /** Lock protecting the threads_tree AVL tree.
@@ -726,4 +731,41 @@
 	THREAD->last_cycle = time;
 }
+
+static bool thread_search_walker(avltree_node_t *node, void *arg)
+{
+	thread_t *thread =
+	    (thread_t *) avltree_get_instance(node, thread_t, threads_tree_node);
+	thread_iterator_t *iterator = (thread_iterator_t *) arg;
+	
+	if (thread->tid == iterator->thread_id) {
+		iterator->thread = thread;
+		return false;
+	}
+	
+	return true;
+}
+
+/** Find thread structure corresponding to thread ID.
+ *
+ * The threads_lock must be already held by the caller of this function and
+ * interrupts must be disabled.
+ *
+ * @param id Thread ID.
+ *
+ * @return Thread structure address or NULL if there is no such thread ID.
+ *
+ */
+thread_t *thread_find_by_id(thread_id_t thread_id)
+{
+	thread_iterator_t iterator;
+	
+	iterator.thread_id = thread_id;
+	iterator.thread = NULL;
+	
+	avltree_walk(&threads_tree, thread_search_walker, (void *) &iterator);
+	
+	return iterator.thread;
+}
+
 
 /** Process syscall to create new thread.
Index: kernel/generic/src/sysinfo/stats.c
===================================================================
--- kernel/generic/src/sysinfo/stats.c	(revision bbda5ab736025c36aa1ad4f3179adec67abe36e3)
+++ kernel/generic/src/sysinfo/stats.c	(revision e1b674201c97668fa9f3559cca094e0e1865629a)
@@ -40,4 +40,5 @@
 #include <time/clock.h>
 #include <mm/frame.h>
+#include <proc/task.h>
 #include <proc/thread.h>
 #include <str.h>
@@ -127,15 +128,15 @@
 }
 
-/** Count number of tasks
- *
- * AVL task tree walker for counting tasks.
- *
- * @param node AVL task tree node (unused).
- * @param arg  Pointer to the counter.
+/** Count number of nodes in an AVL tree
+ *
+ * AVL tree walker for counting nodes.
+ *
+ * @param node AVL tree node (unused).
+ * @param arg  Pointer to the counter (size_t).
  *
  * @param Always true (continue the walk).
  *
  */
-static bool task_count_walker(avltree_node_t *node, void *arg)
+static bool avl_count_walker(avltree_node_t *node, void *arg)
 {
 	size_t *count = (size_t *) arg;
@@ -192,5 +193,5 @@
 	/* First walk the task tree to count the tasks */
 	size_t count = 0;
-	avltree_walk(&tasks_tree, task_count_walker, (void *) &count);
+	avltree_walk(&tasks_tree, avl_count_walker, (void *) &count);
 	
 	if (count == 0) {
@@ -228,4 +229,89 @@
 	
 	return ((void *) task_ids);
+}
+
+/** Gather threads
+ *
+ * AVL three tree walker for gathering thread IDs. Interrupts should
+ * be already disabled while walking the tree.
+ *
+ * @param node AVL thread tree node.
+ * @param arg  Pointer to the iterator into the array of thread IDs.
+ *
+ * @param Always true (continue the walk).
+ *
+ */
+static bool thread_serialize_walker(avltree_node_t *node, void *arg)
+{
+	thread_id_t **ids = (thread_id_t **) arg;
+	thread_t *thread = avltree_get_instance(node, thread_t, threads_tree_node);
+	
+	/* Interrupts are already disabled */
+	spinlock_lock(&(thread->lock));
+	
+	/* Record the ID and increment the iterator */
+	**ids = thread->tid;
+	(*ids)++;
+	
+	spinlock_unlock(&(thread->lock));
+	
+	return true;
+}
+
+/** Get thread IDs
+ *
+ * @param item    Sysinfo item (unused).
+ * @param size    Size of the returned data.
+ * @param dry_run Do not get the data, just calculate the size.
+ *
+ * @return Data containing thread IDs of all threads.
+ *         If the return value is not NULL, it should be freed
+ *         in the context of the sysinfo request.
+ */
+static void *get_stats_threads(struct sysinfo_item *item, size_t *size,
+    bool dry_run)
+{
+	/* Messing with threads structures, avoid deadlock */
+	ipl_t ipl = interrupts_disable();
+	spinlock_lock(&threads_lock);
+	
+	/* First walk the thread tree to count the threads */
+	size_t count = 0;
+	avltree_walk(&threads_tree, avl_count_walker, (void *) &count);
+	
+	if (count == 0) {
+		/* No threads found (strange) */
+		spinlock_unlock(&threads_lock);
+		interrupts_restore(ipl);
+		
+		*size = 0;
+		return NULL;
+	}
+	
+	*size = sizeof(thread_id_t) * count;
+	if (dry_run) {
+		spinlock_unlock(&threads_lock);
+		interrupts_restore(ipl);
+		return NULL;
+	}
+	
+	thread_id_t *thread_ids = (thread_id_t *) malloc(*size, FRAME_ATOMIC);
+	if (thread_ids == NULL) {
+		/* No free space for allocation */
+		spinlock_unlock(&threads_lock);
+		interrupts_restore(ipl);
+		
+		*size = 0;
+		return NULL;
+	}
+	
+	/* Walk tha thread tree again to gather the IDs */
+	thread_id_t *iterator = thread_ids;
+	avltree_walk(&threads_tree, thread_serialize_walker, (void *) &iterator);
+	
+	spinlock_unlock(&threads_lock);
+	interrupts_restore(ipl);
+	
+	return ((void *) thread_ids);
 }
 
@@ -271,5 +357,6 @@
  * but it is still reasonable for the given purpose).
  *
- * @param name Task ID (string-encoded number).
+ * @param name    Task ID (string-encoded number).
+ * @param dry_run Do not get the data, just calculate the size.
  *
  * @return Sysinfo return holder. The type of the returned
@@ -281,5 +368,5 @@
  *
  */
-static sysinfo_return_t get_stats_task(const char *name)
+static sysinfo_return_t get_stats_task(const char *name, bool dry_run)
 {
 	/* Initially no return value */
@@ -292,10 +379,4 @@
 		return ret;
 	
-	/* Allocate stats_task_t structure */
-	stats_task_t *stats_task =
-	    (stats_task_t *) malloc(sizeof(stats_task_t), FRAME_ATOMIC);
-	if (stats_task == NULL)
-		return ret;
-	
 	/* Messing with task structures, avoid deadlock */
 	ipl_t ipl = interrupts_disable();
@@ -307,27 +388,130 @@
 		spinlock_unlock(&tasks_lock);
 		interrupts_restore(ipl);
-		free(stats_task);
 		return ret;
 	}
 	
-	/* Hand-over-hand locking */
-	spinlock_lock(&task->lock);
-	spinlock_unlock(&tasks_lock);
-	
-	/* Copy task's statistics */
-	str_cpy(stats_task->name, TASK_NAME_BUFLEN, task->name);
-	stats_task->virtmem = get_task_virtmem(task->as);
-	stats_task->threads = atomic_get(&task->refcount);
-	task_get_accounting(task, &(stats_task->ucycles),
-	    &(stats_task->kcycles));
-	stats_task->ipc_info = task->ipc_info;
-	
-	spinlock_unlock(&task->lock);
+	if (dry_run) {
+		ret.tag = SYSINFO_VAL_FUNCTION_DATA;
+		ret.data.data = NULL;
+		ret.data.size = sizeof(stats_task_t);
+		
+		spinlock_unlock(&tasks_lock);
+	} else {
+		/* Allocate stats_task_t structure */
+		stats_task_t *stats_task =
+		    (stats_task_t *) malloc(sizeof(stats_task_t), FRAME_ATOMIC);
+		if (stats_task == NULL) {
+			spinlock_unlock(&tasks_lock);
+			interrupts_restore(ipl);
+			return ret;
+		}
+		
+		/* Correct return value */
+		ret.tag = SYSINFO_VAL_FUNCTION_DATA;
+		ret.data.data = (void *) stats_task;
+		ret.data.size = sizeof(stats_task_t);
+	
+		/* Hand-over-hand locking */
+		spinlock_lock(&task->lock);
+		spinlock_unlock(&tasks_lock);
+		
+		/* Copy task's statistics */
+		str_cpy(stats_task->name, TASK_NAME_BUFLEN, task->name);
+		stats_task->virtmem = get_task_virtmem(task->as);
+		stats_task->threads = atomic_get(&task->refcount);
+		task_get_accounting(task, &(stats_task->ucycles),
+		    &(stats_task->kcycles));
+		stats_task->ipc_info = task->ipc_info;
+		
+		spinlock_unlock(&task->lock);
+	}
+	
 	interrupts_restore(ipl);
 	
-	/* Correct return value */
-	ret.tag = SYSINFO_VAL_FUNCTION_DATA;
-	ret.data.data = (void *) stats_task;
-	ret.data.size = sizeof(stats_task_t);
+	return ret;
+}
+
+/** Get thread statistics
+ *
+ * Get statistics of a given thread. The thread ID is passed
+ * as a string (current limitation of the sysinfo interface,
+ * but it is still reasonable for the given purpose).
+ *
+ * @param name    Thread ID (string-encoded number).
+ * @param dry_run Do not get the data, just calculate the size.
+ *
+ * @return Sysinfo return holder. The type of the returned
+ *         data is either SYSINFO_VAL_UNDEFINED (unknown
+ *         thread ID or memory allocation error) or
+ *         SYSINFO_VAL_FUNCTION_DATA (in that case the
+ *         generated data should be freed within the
+ *         sysinfo request context).
+ *
+ */
+static sysinfo_return_t get_stats_thread(const char *name, bool dry_run)
+{
+	/* Initially no return value */
+	sysinfo_return_t ret;
+	ret.tag = SYSINFO_VAL_UNDEFINED;
+	
+	/* Parse the thread ID */
+	thread_id_t thread_id;
+	if (str_uint64(name, NULL, 0, true, &thread_id) != EOK)
+		return ret;
+	
+	/* Messing with threads structures, avoid deadlock */
+	ipl_t ipl = interrupts_disable();
+	spinlock_lock(&threads_lock);
+	
+	thread_t *thread = thread_find_by_id(thread_id);
+	if (thread == NULL) {
+		/* No thread with this ID */
+		spinlock_unlock(&threads_lock);
+		interrupts_restore(ipl);
+		return ret;
+	}
+	
+	if (dry_run) {
+		ret.tag = SYSINFO_VAL_FUNCTION_DATA;
+		ret.data.data = NULL;
+		ret.data.size = sizeof(stats_thread_t);
+		
+		spinlock_unlock(&threads_lock);
+	} else {
+		/* Allocate stats_thread_t structure */
+		stats_thread_t *stats_thread =
+		    (stats_thread_t *) malloc(sizeof(stats_thread_t), FRAME_ATOMIC);
+		if (stats_thread == NULL) {
+			spinlock_unlock(&threads_lock);
+			interrupts_restore(ipl);
+			return ret;
+		}
+		
+		/* Correct return value */
+		ret.tag = SYSINFO_VAL_FUNCTION_DATA;
+		ret.data.data = (void *) stats_thread;
+		ret.data.size = sizeof(stats_thread_t);
+	
+		/* Hand-over-hand locking */
+		spinlock_lock(&thread->lock);
+		spinlock_unlock(&threads_lock);
+		
+		/* Copy thread's statistics */
+		stats_thread->task_id = thread->task->taskid;
+		stats_thread->state = thread->state;
+		stats_thread->priority = thread->priority;
+		stats_thread->ucycles = thread->ucycles;
+		stats_thread->kcycles = thread->kcycles;
+		
+		if (thread->cpu != NULL) {
+			stats_thread->on_cpu = true;
+			stats_thread->cpu = thread->cpu->id;
+		} else
+			stats_thread->on_cpu = false;
+		
+		spinlock_unlock(&thread->lock);
+	}
+	
+	interrupts_restore(ipl);
 	
 	return ret;
@@ -477,5 +661,7 @@
 	sysinfo_set_item_fn_data("system.load", NULL, get_stats_load);
 	sysinfo_set_item_fn_data("system.tasks", NULL, get_stats_tasks);
+	sysinfo_set_item_fn_data("system.threads", NULL, get_stats_threads);
 	sysinfo_set_subtree_fn("system.tasks", NULL, get_stats_task);
+	sysinfo_set_subtree_fn("system.threads", NULL, get_stats_thread);
 }
 
Index: kernel/generic/src/sysinfo/sysinfo.c
===================================================================
--- kernel/generic/src/sysinfo/sysinfo.c	(revision bbda5ab736025c36aa1ad4f3179adec67abe36e3)
+++ kernel/generic/src/sysinfo/sysinfo.c	(revision e1b674201c97668fa9f3559cca094e0e1865629a)
@@ -112,4 +112,6 @@
  *                original pointer is used to store the value
  *                generated by a generated subtree function.
+ * @param dry_run Do not actually get any generated
+ *                binary data, just calculate the size.
  *
  * @return Found item or NULL if no item in the fixed tree
@@ -118,5 +120,5 @@
  */
 static sysinfo_item_t *sysinfo_find_item(const char *name,
-    sysinfo_item_t *subtree, sysinfo_return_t **ret)
+    sysinfo_item_t *subtree, sysinfo_return_t **ret, bool dry_run)
 {
 	ASSERT(subtree != NULL);
@@ -144,8 +146,8 @@
 				/* Recursively find in subtree */
 				return sysinfo_find_item(name + i + 1,
-				    cur->subtree.table, ret);
+				    cur->subtree.table, ret, dry_run);
 			case SYSINFO_SUBTREE_FUNCTION:
 				/* Get generated data */
-				**ret = cur->subtree.get_data(name + i + 1);
+				**ret = cur->subtree.get_data(name + i + 1, dry_run);
 				return NULL;
 			default:
@@ -593,5 +595,6 @@
 	sysinfo_return_t ret;
 	sysinfo_return_t *ret_ptr = &ret;
-	sysinfo_item_t *item = sysinfo_find_item(name, *root, &ret_ptr);
+	sysinfo_item_t *item = sysinfo_find_item(name, *root, &ret_ptr,
+	    dry_run);
 	
 	if (item != NULL) {
Index: uspace/app/tasks/tasks.c
===================================================================
--- uspace/app/tasks/tasks.c	(revision bbda5ab736025c36aa1ad4f3179adec67abe36e3)
+++ uspace/app/tasks/tasks.c	(revision e1b674201c97668fa9f3559cca094e0e1865629a)
@@ -1,4 +1,5 @@
 /*
  * Copyright (c) 2010 Stanislav Kozina
+ * Copyright (c) 2010 Martin Decky
  * All rights reserved.
  *
@@ -43,4 +44,5 @@
 #include <malloc.h>
 #include <inttypes.h>
+#include <bool.h>
 #include <arg_parse.h>
 #include "func.h"
@@ -53,15 +55,4 @@
 #define PRINT_LOAD1(x)  ((x) >> 11)
 #define PRINT_LOAD2(x)  (((x) & 0x7ff) / 2)
-
-/** Thread states */
-//static const char *thread_states[] = {
-//	"Invalid",
-//	"Running",
-//	"Sleeping",
-//	"Ready",
-//	"Entering",
-//	"Exiting",
-//	"Lingering"
-//};
 
 static void list_tasks(void)
@@ -90,5 +81,5 @@
 			
 			printf("%8" PRIu64 "%8u %8" PRIu64"%c %12"
-			    PRIu64"%c %12" PRIu64"%c %s\n", ids[i], stats_task->threads,
+			    PRIu64 "%c %12" PRIu64 "%c %s\n", ids[i], stats_task->threads,
 			    virtmem, vmsuffix, ucycles, usuffix, kcycles, ksuffix,
 			    stats_task->name);
@@ -102,39 +93,48 @@
 }
 
-static void list_threads(task_id_t task_id)
-{
-	/* TODO:
-	size_t thread_count = THREAD_COUNT;
-	thread_info_t *threads = malloc(thread_count * sizeof(thread_info_t));
-	size_t result = get_task_threads(threads, sizeof(thread_info_t) * thread_count);
-
-	while (result > thread_count) {
-		thread_count *= 2;
-		threads = realloc(threads, thread_count * sizeof(thread_info_t));
-		result = get_task_threads(threads, sizeof(thread_info_t) * thread_count);
-	}
-
-	if (result == 0) {
-		printf("No task with given pid!\n");
-		exit(1);
-	}
-
-	size_t i;
+static void list_threads(task_id_t task_id, bool all)
+{
+	size_t count;
+	thread_id_t *ids =
+	    (thread_id_t *) stats_get_threads(&count);
+	
+	if (ids == NULL) {
+		fprintf(stderr, "%s: Unable to get threads\n", NAME);
+		return;
+	}
+	
 	printf("    ID    State  CPU   Prio    [k]uCycles    [k]kcycles   Cycle fault\n");
-	for (i = 0; i < result; ++i) {
-		if (threads[i].taskid != taskid) {
-			continue;
-		}
-		uint64_t ucycles, kcycles;
-		char usuffix, ksuffix;
-		order(threads[i].ucycles, &ucycles, &usuffix);
-		order(threads[i].kcycles, &kcycles, &ksuffix);
-		printf("%6llu %-8s %4u %6d %12llu%c %12llu%c\n", threads[i].tid,
-			thread_states[threads[i].state], threads[i].cpu,
-			threads[i].priority, ucycles, usuffix,
-			kcycles, ksuffix);
-	}
-
-	free(threads); */
+	size_t i;
+	for (i = 0; i < count; i++) {
+		stats_thread_t *stats_thread = stats_get_thread(ids[i]);
+		if (stats_thread != NULL) {
+			if ((all) || (stats_thread->task_id == task_id)) {
+				uint64_t ucycles, kcycles;
+				char usuffix, ksuffix;
+				
+				order(stats_thread->ucycles, &ucycles, &usuffix);
+				order(stats_thread->kcycles, &kcycles, &ksuffix);
+				
+				if (stats_thread->on_cpu) {
+					printf("%8" PRIu64 " %-8s %4u %6d %12"
+					    PRIu64"%c %12" PRIu64"%c\n", ids[i],
+					    thread_get_state(stats_thread->state),
+					    stats_thread->cpu, stats_thread->priority,
+					    ucycles, usuffix, kcycles, ksuffix);
+				} else {
+					printf("%8" PRIu64 " %-8s ---- %6d %12"
+					    PRIu64"%c %12" PRIu64"%c\n", ids[i],
+					    thread_get_state(stats_thread->state),
+					    stats_thread->priority,
+					    ucycles, usuffix, kcycles, ksuffix);
+				}
+			}
+			
+			free(stats_thread);
+		} else if (all)
+			printf("%8" PRIu64 "\n", ids[i]);
+	}
+	
+	free(ids);
 }
 
@@ -190,5 +190,5 @@
 {
 	printf(
-	    "Usage: tasks [-t task_id] [-l] [-c]\n" \
+	    "Usage: tasks [-t task_id] [-a] [-l] [-c]\n" \
 	    "\n" \
 	    "Options:\n" \
@@ -197,4 +197,8 @@
 	    "\t\tList threads of the given task\n" \
 	    "\n" \
+	    "\t-a\n" \
+	    "\t--all\n" \
+	    "\t\tList all threads\n" \
+	    "\n" \
 	    "\t-l\n" \
 	    "\t--load\n" \
@@ -217,8 +221,9 @@
 	bool toggle_tasks = true;
 	bool toggle_threads = false;
+	bool toggle_all = false;
 	bool toggle_load = false;
 	bool toggle_cpus = false;
 	
-	task_id_t task_id;
+	task_id_t task_id = 0;
 	
 	int i;
@@ -230,4 +235,12 @@
 			usage();
 			return 0;
+		}
+		
+		/* All threads */
+		if ((off = arg_parse_short_long(argv[i], "-a", "--all")) != -1) {
+			toggle_tasks = false;
+			toggle_threads = true;
+			toggle_all = true;
+			continue;
 		}
 		
@@ -268,5 +281,5 @@
 	
 	if (toggle_threads)
-		list_threads(task_id);
+		list_threads(task_id, toggle_all);
 	
 	if (toggle_load)
Index: uspace/lib/c/generic/stats.c
===================================================================
--- uspace/lib/c/generic/stats.c	(revision bbda5ab736025c36aa1ad4f3179adec67abe36e3)
+++ uspace/lib/c/generic/stats.c	(revision e1b674201c97668fa9f3559cca094e0e1865629a)
@@ -43,4 +43,17 @@
 #define SYSINFO_STATS_MAX_PATH  64
 
+/** Thread states
+ *
+ */
+static const char *thread_states[] = {
+	"Invalid",
+	"Running",
+	"Sleeping",
+	"Ready",
+	"Entering",
+	"Exiting",
+	"Lingering"
+};
+
 /** Get CPUs statistics
  *
@@ -125,4 +138,48 @@
 	
 	return stats_task;
+}
+
+/** Get thread IDs
+ *
+ * @param count Number of IDs returned.
+ *
+ * @return Array of IDs (thread_id_t).
+ *         If non-NULL then it should be eventually freed
+ *         by free().
+ *
+ */
+thread_id_t *stats_get_threads(size_t *count)
+{
+	size_t size = 0;
+	thread_id_t *ids =
+	    (thread_id_t *) sysinfo_get_data("system.threads", &size);
+	
+	assert((size % sizeof(thread_id_t)) == 0);
+	
+	*count = size / sizeof(thread_id_t);
+	return ids;
+}
+
+/** Get single thread statistics
+ *
+ * @param thread_id Thread ID we are interested in.
+ *
+ * @return Pointer to the stats_thread_t structure.
+ *         If non-NULL then it should be eventually freed
+ *         by free().
+ *
+ */
+stats_thread_t *stats_get_thread(thread_id_t thread_id)
+{
+	char name[SYSINFO_STATS_MAX_PATH];
+	snprintf(name, SYSINFO_STATS_MAX_PATH, "system.threads.%" PRIu64, thread_id);
+	
+	size_t size = 0;
+	stats_thread_t *stats_thread =
+	    (stats_thread_t *) sysinfo_get_data(name, &size);
+	
+	assert((size == sizeof(stats_thread_t)) || (size == 0));
+	
+	return stats_thread;
 }
 
@@ -188,4 +245,12 @@
 }
 
+const char *thread_get_state(state_t state)
+{
+	if (state <= Lingering)
+		return thread_states[state];
+	
+	return thread_states[Invalid];
+}
+
 /** @}
  */
Index: uspace/lib/c/include/stats.h
===================================================================
--- uspace/lib/c/include/stats.h	(revision bbda5ab736025c36aa1ad4f3179adec67abe36e3)
+++ uspace/lib/c/include/stats.h	(revision e1b674201c97668fa9f3559cca094e0e1865629a)
@@ -37,14 +37,22 @@
 
 #include <task.h>
+#include <thread.h>
+#include <stdint.h>
+#include <bool.h>
 #include <kernel/sysinfo/abi.h>
 
 extern stats_cpu_t *stats_get_cpus(size_t *);
 extern stats_physmem_t *stats_get_physmem(void);
-extern task_id_t *stats_get_tasks(size_t *);
-extern stats_task_t *stats_get_task(task_id_t);
 extern load_t *stats_get_load(size_t *);
 extern sysarg_t stats_get_uptime(void);
 
+extern task_id_t *stats_get_tasks(size_t *);
+extern stats_task_t *stats_get_task(task_id_t);
+
+extern thread_id_t *stats_get_threads(size_t *);
+extern stats_thread_t *stats_get_thread(thread_id_t);
+
 extern void stats_print_load_fragment(load_t, unsigned int);
+extern const char *thread_get_state(state_t);
 
 #endif
