Index: kernel/generic/include/proc/thread.h
===================================================================
--- kernel/generic/include/proc/thread.h	(revision 70e2b2dbf43c2d66a4dc6c53e5bbcdfc53bd275a)
+++ 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 70e2b2dbf43c2d66a4dc6c53e5bbcdfc53bd275a)
+++ 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 70e2b2dbf43c2d66a4dc6c53e5bbcdfc53bd275a)
+++ 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 70e2b2dbf43c2d66a4dc6c53e5bbcdfc53bd275a)
+++ 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 70e2b2dbf43c2d66a4dc6c53e5bbcdfc53bd275a)
+++ 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 70e2b2dbf43c2d66a4dc6c53e5bbcdfc53bd275a)
+++ 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 70e2b2dbf43c2d66a4dc6c53e5bbcdfc53bd275a)
+++ 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) {
