Index: boot/Makefile.common
===================================================================
--- boot/Makefile.common	(revision 9d3133d4a726093950d655f8994e81a041e02f89)
+++ boot/Makefile.common	(revision 336db2955b9fd1ee85c8818b9408bc4e9dad9fc2)
@@ -65,4 +65,5 @@
 	$(USPACEDIR)/app/mkfat/mkfat \
 	$(USPACEDIR)/app/redir/redir \
+	$(USPACEDIR)/app/taskdump/taskdump \
 	$(USPACEDIR)/app/tester/tester \
 	$(USPACEDIR)/app/tetris/tetris \
Index: kernel/generic/include/mm/as.h
===================================================================
--- kernel/generic/include/mm/as.h	(revision 9d3133d4a726093950d655f8994e81a041e02f89)
+++ kernel/generic/include/mm/as.h	(revision 336db2955b9fd1ee85c8818b9408bc4e9dad9fc2)
@@ -36,4 +36,10 @@
 #define KERN_AS_H_
 
+#ifdef KERNEL
+#include <arch/types.h>
+#else
+#include <sys/types.h>
+#endif
+
 /** Address space area flags. */
 #define AS_AREA_READ		1
@@ -41,4 +47,16 @@
 #define AS_AREA_EXEC		4
 #define AS_AREA_CACHEABLE	8
+
+/** Address space area info exported to userspace. */
+typedef struct {
+	/** Starting address */
+	uintptr_t start_addr;
+
+	/** Area size */
+	size_t size;
+
+	/** Area flags */
+	int flags;
+} as_area_info_t;
 
 #ifdef KERNEL
@@ -268,4 +286,5 @@
 
 /* Introspection functions. */
+extern void as_get_area_info(as_t *as, as_area_info_t **obuf, size_t *osize);
 extern void as_print(as_t *as);
 
Index: kernel/generic/include/udebug/udebug.h
===================================================================
--- kernel/generic/include/udebug/udebug.h	(revision 9d3133d4a726093950d655f8994e81a041e02f89)
+++ kernel/generic/include/udebug/udebug.h	(revision 336db2955b9fd1ee85c8818b9408bc4e9dad9fc2)
@@ -96,4 +96,18 @@
  */
 UDEBUG_M_THREAD_READ,
+
+/** Read the list of the debugged task's address space areas.
+ *
+ * - ARG2 - destination address in the caller's address space
+ * - ARG3 - size of receiving buffer in bytes
+ *
+ * The kernel fills the buffer with a series of as_area_info_t structures.
+ * Upon answer, the kernel will set:
+ *
+ * - ARG2 - number of bytes that were actually copied
+ * - ARG3 - number of bytes of the complete data
+ *
+ */
+UDEBUG_M_AREAS_READ,
 
 /** Read the debugged tasks's memory.
Index: kernel/generic/include/udebug/udebug_ops.h
===================================================================
--- kernel/generic/include/udebug/udebug_ops.h	(revision 9d3133d4a726093950d655f8994e81a041e02f89)
+++ kernel/generic/include/udebug/udebug_ops.h	(revision 336db2955b9fd1ee85c8818b9408bc4e9dad9fc2)
@@ -45,5 +45,6 @@
 int udebug_stop(thread_t *t, call_t *call);
 
-int udebug_thread_read(void **buffer, size_t buf_size, size_t *n);
+int udebug_thread_read(void **buffer, size_t buf_size, size_t *stored,
+    size_t *needed);
 int udebug_args_read(thread_t *t, void **buffer);
 
Index: kernel/generic/src/mm/as.c
===================================================================
--- kernel/generic/src/mm/as.c	(revision 9d3133d4a726093950d655f8994e81a041e02f89)
+++ kernel/generic/src/mm/as.c	(revision 336db2955b9fd1ee85c8818b9408bc4e9dad9fc2)
@@ -1920,4 +1920,70 @@
 }
 
+/** Get list of adress space areas.
+ *
+ * @param as		Address space.
+ * @param obuf		Place to save pointer to returned buffer.
+ * @param osize		Place to save size of returned buffer.
+ */
+void as_get_area_info(as_t *as, as_area_info_t **obuf, size_t *osize)
+{
+	ipl_t ipl;
+	size_t area_cnt, area_idx, i;
+	link_t *cur;
+
+	as_area_info_t *info;
+	size_t isize;
+
+	ipl = interrupts_disable();
+	mutex_lock(&as->lock);
+
+	/* First pass, count number of areas. */
+
+	area_cnt = 0;
+
+	for (cur = as->as_area_btree.leaf_head.next;
+	    cur != &as->as_area_btree.leaf_head; cur = cur->next) {
+		btree_node_t *node;
+
+		node = list_get_instance(cur, btree_node_t, leaf_link);
+		area_cnt += node->keys;
+	}
+
+        isize = area_cnt * sizeof(as_area_info_t);
+	info = malloc(isize, 0);
+
+	/* Second pass, record data. */
+
+	area_idx = 0;
+
+	for (cur = as->as_area_btree.leaf_head.next;
+	    cur != &as->as_area_btree.leaf_head; cur = cur->next) {
+		btree_node_t *node;
+
+		node = list_get_instance(cur, btree_node_t, leaf_link);
+
+		for (i = 0; i < node->keys; i++) {
+			as_area_t *area = node->value[i];
+
+			ASSERT(area_idx < area_cnt);
+			mutex_lock(&area->lock);
+
+			info[area_idx].start_addr = area->base;
+			info[area_idx].size = FRAMES2SIZE(area->pages);
+			info[area_idx].flags = area->flags;
+			++area_idx;
+
+			mutex_unlock(&area->lock);
+		}
+	}
+
+	mutex_unlock(&as->lock);
+	interrupts_restore(ipl);
+
+	*obuf = info;
+	*osize = isize;
+}
+
+
 /** Print out information about address space.
  *
Index: kernel/generic/src/udebug/udebug_ipc.c
===================================================================
--- kernel/generic/src/udebug/udebug_ipc.c	(revision 9d3133d4a726093950d655f8994e81a041e02f89)
+++ kernel/generic/src/udebug/udebug_ipc.c	(revision 336db2955b9fd1ee85c8818b9408bc4e9dad9fc2)
@@ -41,4 +41,5 @@
 #include <proc/task.h>
 #include <proc/thread.h>
+#include <mm/as.h>
 #include <arch.h>
 #include <errno.h>
@@ -165,11 +166,54 @@
 static void udebug_receive_thread_read(call_t *call)
 {
+	uintptr_t uspace_addr;
+	size_t buf_size;
+	void *buffer;
+	size_t copied, needed;
+	int rc;
+
+	uspace_addr = IPC_GET_ARG2(call->data);	/* Destination address */
+	buf_size = IPC_GET_ARG3(call->data);	/* Dest. buffer size */
+
+	/*
+	 * Read thread list. Variable n will be filled with actual number
+	 * of threads times thread-id size.
+	 */
+	rc = udebug_thread_read(&buffer, buf_size, &copied, &needed);
+	if (rc < 0) {
+		IPC_SET_RETVAL(call->data, rc);
+		ipc_answer(&TASK->kb.box, call);
+		return;
+	}
+
+	/*
+	 * Make use of call->buffer to transfer data to caller's userspace
+	 */
+
+	IPC_SET_RETVAL(call->data, 0);
+	/* ARG1=dest, ARG2=size as in IPC_M_DATA_READ so that
+	   same code in process_answer() can be used 
+	   (no way to distinguish method in answer) */
+	IPC_SET_ARG1(call->data, uspace_addr);
+	IPC_SET_ARG2(call->data, copied);
+	IPC_SET_ARG3(call->data, needed);
+	call->buffer = buffer;
+
+	ipc_answer(&TASK->kb.box, call);
+}
+
+/** Process an AREAS_READ call.
+ *
+ * Returns a list of address space areas in the current task, as an array
+ * of as_area_info_t structures.
+ *
+ * @param call	The call structure.
+ */
+static void udebug_receive_areas_read(call_t *call)
+{
 	unative_t uspace_addr;
 	unative_t to_copy;
-	unsigned total_bytes;
-	unsigned buf_size;
-	void *buffer;
-	size_t n;
-	int rc;
+	size_t data_size;
+	size_t buf_size;
+	void *data;
 
 	uspace_addr = IPC_GET_ARG2(call->data);	/* Destination address */
@@ -177,20 +221,12 @@
 
 	/*
-	 * Read thread list. Variable n will be filled with actual number
-	 * of threads times thread-id size.
-	 */
-	rc = udebug_thread_read(&buffer, buf_size, &n);
-	if (rc < 0) {
-		IPC_SET_RETVAL(call->data, rc);
-		ipc_answer(&TASK->kb.box, call);
-		return;
-	}
-
-	total_bytes = n;
-
-	/* Copy MAX(buf_size, total_bytes) bytes */
-
-	if (buf_size > total_bytes)
-		to_copy = total_bytes;
+	 * Read area list.
+	 */
+	as_get_area_info(AS, (as_area_info_t **) &data, &data_size);
+
+	/* Copy MAX(buf_size, data_size) bytes */
+
+	if (buf_size > data_size)
+		to_copy = data_size;
 	else
 		to_copy = buf_size;
@@ -207,9 +243,10 @@
 	IPC_SET_ARG2(call->data, to_copy);
 
-	IPC_SET_ARG3(call->data, total_bytes);
-	call->buffer = buffer;
-
-	ipc_answer(&TASK->kb.box, call);
-}
+	IPC_SET_ARG3(call->data, data_size);
+	call->buffer = data;
+
+	ipc_answer(&TASK->kb.box, call);
+}
+
 
 /** Process an ARGS_READ call.
@@ -331,4 +368,7 @@
 		udebug_receive_thread_read(call);
 		break;
+	case UDEBUG_M_AREAS_READ:
+		udebug_receive_areas_read(call);
+		break;
 	case UDEBUG_M_ARGS_READ:
 		udebug_receive_args_read(call);
Index: kernel/generic/src/udebug/udebug_ops.c
===================================================================
--- kernel/generic/src/udebug/udebug_ops.c	(revision 9d3133d4a726093950d655f8994e81a041e02f89)
+++ kernel/generic/src/udebug/udebug_ops.c	(revision 336db2955b9fd1ee85c8818b9408bc4e9dad9fc2)
@@ -355,6 +355,7 @@
  *
  * If the sequence is longer than @a buf_size bytes, only as much hashes
- * as can fit are copied. The number of thread hashes copied is stored
- * in @a n.
+ * as can fit are copied. The number of bytes copied is stored in @a stored.
+ * The total number of thread bytes that could have been saved had there been
+ * enough space is stored in @a needed.
  *
  * The rationale for having @a buf_size is that this function is only
@@ -364,12 +365,15 @@
  * @param buffer	The buffer for storing thread hashes.
  * @param buf_size	Buffer size in bytes.
- * @param n		The actual number of hashes copied will be stored here.
- */
-int udebug_thread_read(void **buffer, size_t buf_size, size_t *n)
+ * @param stored	The actual number of bytes copied will be stored here.
+ * @param needed	Total number of hashes that could have been saved.
+ */
+int udebug_thread_read(void **buffer, size_t buf_size, size_t *stored,
+    size_t *needed)
 {
 	thread_t *t;
 	link_t *cur;
 	unative_t tid;
-	unsigned copied_ids;
+	size_t copied_ids;
+	size_t extra_ids;
 	ipl_t ipl;
 	unative_t *id_buffer;
@@ -380,5 +384,5 @@
 
 	/* Allocate a buffer to hold thread IDs */
-	id_buffer = malloc(buf_size, 0);
+	id_buffer = malloc(buf_size + 1, 0);
 
 	mutex_lock(&TASK->udebug.lock);
@@ -396,10 +400,8 @@
 	max_ids = buf_size / sizeof(unative_t);
 	copied_ids = 0;
+	extra_ids = 0;
 
 	/* FIXME: make sure the thread isn't past debug shutdown... */
 	for (cur = TASK->th_head.next; cur != &TASK->th_head; cur = cur->next) {
-		/* Do not write past end of buffer */
-		if (copied_ids >= max_ids) break;
-
 		t = list_get_instance(cur, thread_t, th_link);
 
@@ -409,8 +411,13 @@
 
 		/* Not interested in kernel threads. */
-		if ((flags & THREAD_FLAG_USPACE) != 0) {
+		if ((flags & THREAD_FLAG_USPACE) == 0)
+			continue;
+
+		if (copied_ids < max_ids) {
 			/* Using thread struct pointer as identification hash */
 			tid = (unative_t) t;
 			id_buffer[copied_ids++] = tid;
+		} else {
+			extra_ids++;
 		}
 	}
@@ -422,5 +429,6 @@
 
 	*buffer = id_buffer;
-	*n = copied_ids * sizeof(unative_t);
+	*stored = copied_ids * sizeof(unative_t);
+	*needed = (copied_ids + extra_ids) * sizeof(unative_t);
 
 	return 0;
Index: uspace/Makefile
===================================================================
--- uspace/Makefile	(revision 9d3133d4a726093950d655f8994e81a041e02f89)
+++ uspace/Makefile	(revision 336db2955b9fd1ee85c8818b9408bc4e9dad9fc2)
@@ -40,4 +40,5 @@
 	app/mkfat \
 	app/redir \
+	app/taskdump \
 	app/tester \
 	app/tetris \
Index: uspace/app/taskdump/Makefile
===================================================================
--- uspace/app/taskdump/Makefile	(revision 336db2955b9fd1ee85c8818b9408bc4e9dad9fc2)
+++ uspace/app/taskdump/Makefile	(revision 336db2955b9fd1ee85c8818b9408bc4e9dad9fc2)
@@ -0,0 +1,37 @@
+#
+# Copyright (c) 2010 Jiri Svoboda
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# - Redistributions of source code must retain the above copyright
+#   notice, this list of conditions and the following disclaimer.
+# - Redistributions in binary form must reproduce the above copyright
+#   notice, this list of conditions and the following disclaimer in the
+#   documentation and/or other materials provided with the distribution.
+# - The name of the author may not be used to endorse or promote products
+#   derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+USPACE_PREFIX = ../..
+LIBS = $(LIBC_PREFIX)/libc.a
+
+OUTPUT = taskdump
+
+SOURCES = \
+	taskdump.c
+
+include ../Makefile.common
Index: uspace/app/taskdump/taskdump.c
===================================================================
--- uspace/app/taskdump/taskdump.c	(revision 336db2955b9fd1ee85c8818b9408bc4e9dad9fc2)
+++ uspace/app/taskdump/taskdump.c	(revision 336db2955b9fd1ee85c8818b9408bc4e9dad9fc2)
@@ -0,0 +1,347 @@
+/*
+ * Copyright (c) 2010 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup taskdump
+ * @{
+ */
+/** @file
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ipc/ipc.h>
+#include <errno.h>
+#include <udebug.h>
+#include <task.h>
+#include <kernel/mm/as.h>
+#include <macros.h>
+#include <assert.h>
+#include <bool.h>
+
+#define LINE_BYTES 16
+
+
+#define DBUF_SIZE 4096
+static uint8_t data_buf[DBUF_SIZE];
+
+static int phoneid;
+static task_id_t task_id;
+static bool dump_memory;
+
+static int connect_task(task_id_t task_id);
+static int parse_args(int argc, char *argv[]);
+static void print_syntax();
+static int threads_dump(void);
+static int areas_dump(void);
+static int area_dump(as_area_info_t *area);
+static void hex_dump(uintptr_t addr, void *buffer, size_t size);
+
+int main(int argc, char *argv[])
+{
+	int rc;
+
+	printf("Task Dump Utility\n");
+	dump_memory = false;
+
+	if (parse_args(argc, argv) < 0)
+		return 1;
+
+	rc = connect_task(task_id);
+	if (rc < 0) {
+		printf("Failed connecting to task %lld.\n", task_id);
+		return 1;
+	}
+
+	printf("Dumping task %lld.\n\n", task_id);
+
+	rc = threads_dump();
+	if (rc < 0)
+		printf("Failed dumping threads.\n");
+
+	rc = areas_dump();
+	if (rc < 0)
+		printf("Failed dumping address space areas.\n");
+
+	udebug_end(phoneid);
+	ipc_hangup(phoneid);
+
+	return 0;
+}
+
+static int connect_task(task_id_t task_id)
+{
+	int rc;
+
+	rc = ipc_connect_kbox(task_id);
+
+	if (rc == ENOTSUP) {
+		printf("You do not have userspace debugging support "
+		    "compiled in the kernel.\n");
+		printf("Compile kernel with 'Support for userspace debuggers' "
+		    "(CONFIG_UDEBUG) enabled.\n");
+		return rc;
+	}
+
+	if (rc < 0) {
+		printf("Error connecting\n");
+		printf("ipc_connect_task(%lld) -> %d ", task_id, rc);
+		return rc;
+	}
+
+	phoneid = rc;
+
+	rc = udebug_begin(phoneid);
+	if (rc < 0) {
+		printf("udebug_begin() -> %d\n", rc);
+		return rc;
+	}
+
+	return 0;
+}
+
+static int parse_args(int argc, char *argv[])
+{
+	char *arg;
+	char *err_p;
+
+	task_id = 0;
+
+	--argc; ++argv;
+
+	while (argc > 0) {
+		arg = *argv;
+		if (arg[0] == '-') {
+			if (arg[1] == 't' && arg[2] == '\0') {
+				/* Task ID */
+				--argc; ++argv;
+				task_id = strtol(*argv, &err_p, 10);
+				if (*err_p) {
+					printf("Task ID syntax error\n");
+					print_syntax();
+					return -1;
+				}
+			} else if (arg[1] == 'm' && arg[2] == '\0') {
+				dump_memory = true;
+			} else {
+				printf("Uknown option '%s'\n", arg[0]);
+				print_syntax();
+				return -1;
+			}
+		} else {
+			break;
+		}
+
+		--argc; ++argv;
+	}
+
+	if (task_id == 0) {
+		printf("Missing task ID argument\n");
+		print_syntax();
+		return -1;
+	}
+
+	if (argc != 0) {
+		printf("Extra arguments\n");
+		print_syntax();
+		return -1;
+	}
+
+	return 0;
+}
+
+static void print_syntax()
+{
+	printf("Syntax: taskdump [-m] -t <task_id>\n");
+	printf("\t-m\tDump memory area contents.\n");
+	printf("\t-t <task_id>\tWhich task to dump.\n");
+}
+
+static int threads_dump(void)
+{
+	uintptr_t *thash_buf;
+	uintptr_t dummy_buf;
+	size_t buf_size, n_threads;
+
+	size_t copied;
+	size_t needed;
+	size_t i;
+	int rc;
+
+	/* TODO: See why NULL does not work. */
+	rc = udebug_thread_read(phoneid, &dummy_buf, 0, &copied, &needed);
+	if (rc < 0) {
+		printf("udebug_thread_read() -> %d\n", rc);
+		return rc;
+	}
+
+	if (needed == 0) {
+		printf("No threads.\n\n");
+		return 0;
+	}
+
+	buf_size = needed;
+	thash_buf = malloc(buf_size);
+
+	rc = udebug_thread_read(phoneid, thash_buf, buf_size, &copied, &needed);
+	if (rc < 0) {
+		printf("udebug_thread_read() -> %d\n", rc);
+		return rc;
+	}
+
+	assert(copied == buf_size);
+	assert(needed == buf_size);
+
+	n_threads = copied / sizeof(uintptr_t);
+
+	printf("Threads:\n");
+	for (i = 0; i < n_threads; i++) {
+		printf(" [%d] (hash 0x%lx)\n", 1+i, thash_buf[i]);
+	}
+	putchar('\n');
+
+	free(thash_buf);
+
+	return 0;
+}
+
+static int areas_dump(void)
+{
+	as_area_info_t *ainfo_buf;
+	as_area_info_t dummy_buf;
+	size_t buf_size, n_areas;
+
+	size_t copied;
+	size_t needed;
+	size_t i;
+	int rc;
+
+	rc = udebug_areas_read(phoneid, &dummy_buf, 0, &copied, &needed);
+	if (rc < 0) {
+		printf("udebug_areas_read() -> %d\n", rc);
+		return rc;
+	}
+
+	buf_size = needed;
+	ainfo_buf = malloc(buf_size);
+
+	rc = udebug_areas_read(phoneid, ainfo_buf, buf_size, &copied, &needed);
+	if (rc < 0) {
+		printf("udebug_areas_read() -> %d\n", rc);
+		return rc;
+	}
+
+	assert(copied == buf_size);
+	assert(needed == buf_size);
+
+	n_areas = copied / sizeof(as_area_info_t);
+
+	printf("Address space areas:\n");
+	for (i = 0; i < n_areas; i++) {
+		printf(" [%d] flags: %c%c%c%c base: 0x%lx size: 0x%lx\n", 1+i,
+		    (ainfo_buf[i].flags & AS_AREA_READ) ? 'R' : '-',
+		    (ainfo_buf[i].flags & AS_AREA_WRITE) ? 'W' : '-',
+		    (ainfo_buf[i].flags & AS_AREA_EXEC) ? 'X' : '-',
+		    (ainfo_buf[i].flags & AS_AREA_CACHEABLE) ? 'C' : '-',
+		    ainfo_buf[i].start_addr, ainfo_buf[i].size);
+
+		if (dump_memory) {
+			putchar('\n');
+			area_dump(&ainfo_buf[i]);
+			putchar('\n');
+		}
+	}
+
+	putchar('\n');
+
+	free(ainfo_buf);
+
+	return 0;
+}
+
+static int area_dump(as_area_info_t *area)
+{
+	size_t to_copy;
+	size_t total;
+	uintptr_t addr;
+	int rc;
+
+	addr = area->start_addr;
+	total = 0;
+
+	while (total < area->size) {
+		to_copy = min(area->size - total, DBUF_SIZE);
+		rc = udebug_mem_read(phoneid, data_buf, addr, to_copy);
+		if (rc < 0) {
+			printf("udebug_mem_read() failed.\n");
+			return rc;
+		}
+
+		hex_dump(addr, data_buf, to_copy);
+
+		addr += to_copy;
+		total += to_copy;
+	}
+
+	return EOK;
+}
+
+static void hex_dump(uintptr_t addr, void *buffer, size_t size)
+{
+	uint8_t *data = (uint8_t *) buffer;
+	uint8_t b;
+	size_t pos, i;
+
+	assert(addr % LINE_BYTES == 0);
+	assert(size % LINE_BYTES == 0);
+
+	pos = 0;
+
+	while (pos < size) {
+		printf("%08x:", addr + pos);
+		for (i = 0; i < LINE_BYTES; ++i) {
+			if (i % 4 == 0) putchar(' ');
+			printf(" %02x", data[pos + i]);
+		}
+		putchar('\t');
+
+		for (i = 0; i < LINE_BYTES; ++i) {
+			b = data[pos + i];
+			if (b >= 32 && b < 127) {
+				putchar(b);
+			} else {
+				putchar(' ');
+			}
+		}
+		putchar('\n');
+		pos += LINE_BYTES;
+	}
+}
+
+/** @}
+ */
Index: uspace/lib/libc/generic/udebug.c
===================================================================
--- uspace/lib/libc/generic/udebug.c	(revision 9d3133d4a726093950d655f8994e81a041e02f89)
+++ uspace/lib/libc/generic/udebug.c	(revision 336db2955b9fd1ee85c8818b9408bc4e9dad9fc2)
@@ -69,4 +69,20 @@
 }
 
+int udebug_areas_read(int phoneid, void *buffer, size_t n,
+	size_t *copied, size_t *needed)
+{
+	ipcarg_t a_copied, a_needed;
+	int rc;
+
+	rc = async_req_3_3(phoneid, IPC_M_DEBUG_ALL, UDEBUG_M_AREAS_READ,
+		(sysarg_t)buffer, n, NULL, &a_copied, &a_needed);
+
+	*copied = (size_t)a_copied;
+	*needed = (size_t)a_needed;
+
+	return rc;
+}
+
+
 int udebug_mem_read(int phoneid, void *buffer, uintptr_t addr, size_t n)
 {
Index: uspace/lib/libc/include/udebug.h
===================================================================
--- uspace/lib/libc/include/udebug.h	(revision 9d3133d4a726093950d655f8994e81a041e02f89)
+++ uspace/lib/libc/include/udebug.h	(revision 336db2955b9fd1ee85c8818b9408bc4e9dad9fc2)
@@ -47,4 +47,6 @@
 int udebug_thread_read(int phoneid, void *buffer, size_t n,
 	size_t *copied, size_t *needed);
+int udebug_areas_read(int phoneid, void *buffer, size_t n,
+	size_t *copied, size_t *needed);
 int udebug_mem_read(int phoneid, void *buffer, uintptr_t addr, size_t n);
 int udebug_args_read(int phoneid, thash_t tid, sysarg_t *buffer);
