Index: boot/arch/amd64/Makefile.inc
===================================================================
--- boot/arch/amd64/Makefile.inc	(revision 06a195bc5890a55fd6f733495812e905812949fc)
+++ boot/arch/amd64/Makefile.inc	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
@@ -52,4 +52,5 @@
 	$(USPACEDIR)/app/tetris/tetris \
 	$(USPACEDIR)/app/tester/tester \
+	$(USPACEDIR)/app/trace/trace \
 	$(USPACEDIR)/app/klog/klog \
 	$(USPACEDIR)/app/bdsh/bdsh
Index: boot/arch/arm32/loader/Makefile
===================================================================
--- boot/arch/arm32/loader/Makefile	(revision 06a195bc5890a55fd6f733495812e905812949fc)
+++ boot/arch/arm32/loader/Makefile	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
@@ -107,4 +107,5 @@
 	$(USPACEDIR)/app/tetris/tetris \
 	$(USPACEDIR)/app/tester/tester \
+	$(USPACEDIR)/app/trace/trace \
 	$(USPACEDIR)/app/klog/klog \
 	$(USPACEDIR)/app/bdsh/bdsh
Index: boot/arch/ia32/Makefile.inc
===================================================================
--- boot/arch/ia32/Makefile.inc	(revision 06a195bc5890a55fd6f733495812e905812949fc)
+++ boot/arch/ia32/Makefile.inc	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
@@ -51,4 +51,5 @@
 	$(USPACEDIR)/app/tetris/tetris \
 	$(USPACEDIR)/app/tester/tester \
+	$(USPACEDIR)/app/trace/trace \
 	$(USPACEDIR)/app/klog/klog \
 	$(USPACEDIR)/app/bdsh/bdsh
Index: boot/arch/ia64/loader/Makefile
===================================================================
--- boot/arch/ia64/loader/Makefile	(revision 06a195bc5890a55fd6f733495812e905812949fc)
+++ boot/arch/ia64/loader/Makefile	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
@@ -102,4 +102,5 @@
 	$(USPACEDIR)/app/tetris/tetris \
 	$(USPACEDIR)/app/tester/tester \
+	$(USPACEDIR)/app/trace/trace \
 	$(USPACEDIR)/app/klog/klog
 
Index: boot/arch/mips32/loader/Makefile
===================================================================
--- boot/arch/mips32/loader/Makefile	(revision 06a195bc5890a55fd6f733495812e905812949fc)
+++ boot/arch/mips32/loader/Makefile	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
@@ -107,4 +107,5 @@
 	$(USPACEDIR)/app/tetris/tetris \
 	$(USPACEDIR)/app/tester/tester \
+	$(USPACEDIR)/app/trace/trace \
 	$(USPACEDIR)/app/bdsh/bdsh \
 	$(USPACEDIR)/app/klog/klog
Index: boot/arch/ppc32/loader/Makefile
===================================================================
--- boot/arch/ppc32/loader/Makefile	(revision 06a195bc5890a55fd6f733495812e905812949fc)
+++ boot/arch/ppc32/loader/Makefile	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
@@ -102,4 +102,5 @@
 	$(USPACEDIR)/app/tetris/tetris \
 	$(USPACEDIR)/app/tester/tester \
+	$(USPACEDIR)/app/trace/trace \
 	$(USPACEDIR)/app/klog/klog \
 	$(USPACEDIR)/app/bdsh/bdsh
Index: boot/arch/ppc64/loader/Makefile
===================================================================
--- boot/arch/ppc64/loader/Makefile	(revision 06a195bc5890a55fd6f733495812e905812949fc)
+++ boot/arch/ppc64/loader/Makefile	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
@@ -90,4 +90,5 @@
 	$(USPACEDIR)/app/tetris/tetris \
 	$(USPACEDIR)/app/tester/tester \
+	$(USPACEDIR)/app/trace/trace \
 	$(USPACEDIR)/app/klog/klog
 
Index: kernel/Makefile
===================================================================
--- kernel/Makefile	(revision 06a195bc5890a55fd6f733495812e905812949fc)
+++ kernel/Makefile	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
@@ -149,4 +149,8 @@
 		DEFS += -DCONFIG_VESA_BPP=$(CONFIG_VESA_BPP)
 	endif
+endif
+
+ifeq ($(CONFIG_UDEBUG),y)
+	DEFS += -DCONFIG_UDEBUG
 endif
 
@@ -284,4 +288,15 @@
 	generic/src/sysinfo/sysinfo.c
 
+## Udebug interface sources
+#
+
+ifeq ($(CONFIG_UDEBUG),y)
+GENERIC_SOURCES += \
+	generic/src/ipc/ipc_kbox.c \
+	generic/src/udebug/udebug.c \
+	generic/src/udebug/udebug_ops.c \
+	generic/src/udebug/udebug_ipc.c
+endif
+
 ## Test sources
 #
Index: kernel/generic/include/ipc/ipc.h
===================================================================
--- kernel/generic/include/ipc/ipc.h	(revision 06a195bc5890a55fd6f733495812e905812949fc)
+++ kernel/generic/include/ipc/ipc.h	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
@@ -195,4 +195,10 @@
  */
 #define IPC_M_DATA_READ		7
+
+/** Debug the recipient.
+ * - ARG1 - specifies the debug method (from udebug_method_t)
+ * - other arguments are specific to the debug method
+ */
+#define IPC_M_DEBUG_ALL		8
 
 /* Well-known methods */
@@ -308,4 +314,6 @@
 extern void ipc_backsend_err(phone_t *, call_t *, unative_t);
 extern void ipc_print_task(task_id_t);
+extern void ipc_answerbox_slam_phones(answerbox_t *, bool);
+extern void ipc_cleanup_call_list(link_t *);
 
 extern answerbox_t *ipc_phone_0;
Index: kernel/generic/include/ipc/ipc_kbox.h
===================================================================
--- kernel/generic/include/ipc/ipc_kbox.h	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
+++ kernel/generic/include/ipc/ipc_kbox.h	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2008 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 genericipc
+ * @{
+ */
+/** @file
+ */
+
+#ifndef KERN_IPC_KBOX_H_
+#define KERN_IPC_KBOX_H_
+
+#include <typedefs.h>
+
+extern int ipc_connect_kbox(task_id_t);
+extern void ipc_kbox_cleanup(void);
+
+#endif
+
+/** @}
+ */
Index: kernel/generic/include/ipc/sysipc.h
===================================================================
--- kernel/generic/include/ipc/sysipc.h	(revision 06a195bc5890a55fd6f733495812e905812949fc)
+++ kernel/generic/include/ipc/sysipc.h	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
@@ -58,4 +58,5 @@
     irq_code_t *ucode);
 unative_t sys_ipc_unregister_irq(inr_t inr, devno_t devno);
+unative_t sys_ipc_connect_kbox(sysarg64_t *task_id);
 
 #endif
Index: kernel/generic/include/proc/task.h
===================================================================
--- kernel/generic/include/proc/task.h	(revision 06a195bc5890a55fd6f733495812e905812949fc)
+++ kernel/generic/include/proc/task.h	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
@@ -53,4 +53,5 @@
 #include <mm/tlb.h>
 #include <proc/scheduler.h>
+#include <udebug/udebug.h>
 
 struct thread;
@@ -94,4 +95,18 @@
 	 */
 	atomic_t active_calls;
+
+#ifdef CONFIG_UDEBUG
+	/** Debugging stuff */
+	udebug_task_t udebug;
+
+	/** Kernel answerbox */
+	answerbox_t kernel_box;
+	/** Thread used to service kernel answerbox */
+	struct thread *kb_thread;
+	/** Kbox thread creation vs. begin of cleanup mutual exclusion */
+	mutex_t kb_cleanup_lock;
+	/** True if cleanup of kbox has already started */
+	bool kb_finished;
+#endif
 	
 	/** Architecture specific task data. */
Index: kernel/generic/include/proc/thread.h
===================================================================
--- kernel/generic/include/proc/thread.h	(revision 06a195bc5890a55fd6f733495812e905812949fc)
+++ kernel/generic/include/proc/thread.h	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
@@ -47,4 +47,5 @@
 #include <mm/tlb.h>
 #include <proc/uarg.h>
+#include <udebug/udebug.h>
 
 #define THREAD_STACK_SIZE	STACK_SIZE
@@ -204,4 +205,10 @@
 	/** Thread's kernel stack. */
 	uint8_t *kstack;
+
+#ifdef CONFIG_UDEBUG
+	/** Debugging stuff */
+	udebug_thread_t udebug;
+#endif
+
 } thread_t;
 
Index: kernel/generic/include/syscall/syscall.h
===================================================================
--- kernel/generic/include/syscall/syscall.h	(revision 06a195bc5890a55fd6f733495812e905812949fc)
+++ kernel/generic/include/syscall/syscall.h	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
@@ -79,4 +79,5 @@
 	
 	SYS_DEBUG_ENABLE_CONSOLE,
+	SYS_IPC_CONNECT_KBOX,
 	SYSCALL_END
 } syscall_t;
Index: kernel/generic/include/udebug/udebug.h
===================================================================
--- kernel/generic/include/udebug/udebug.h	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
+++ kernel/generic/include/udebug/udebug.h	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2008 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 generic	
+ * @{
+ */
+/** @file
+ */
+
+#ifndef KERN_UDEBUG_H_
+#define KERN_UDEBUG_H_
+
+#include <ipc/ipc.h>
+
+typedef enum { /* udebug_method_t */
+
+/** Start debugging the recipient.
+ * Causes all threads in the receiving task to stop. When they
+ * are all stoped, an answer with retval 0 is generated.
+ */
+UDEBUG_M_BEGIN = 1,
+
+/** Finish debugging the recipient.
+ * Answers all pending GO and GUARD messages.
+ */
+UDEBUG_M_END,
+
+/** Set which events should be captured.
+ */
+UDEBUG_M_SET_EVMASK,
+
+/** Make sure the debugged task is still there.
+ * This message is answered when the debugged task dies
+ * or the debugging session ends.
+ */
+UDEBUG_M_GUARD,
+
+/** Run a thread until a debugging event occurs.
+ * This message is answered when the thread stops
+ * in a debugging event.
+ *
+ * - ARG2 - id of the thread to run
+ */
+UDEBUG_M_GO,
+
+/** Stop a thread being debugged.
+ * Creates a special STOP event in the thread, causing
+ * it to answer a pending GO message (if any).
+ */
+UDEBUG_M_STOP,
+
+/** Read arguments of a syscall.
+ *
+ * - ARG2 - thread identification
+ * - ARG3 - destination address in the caller's address space
+ *
+ */
+UDEBUG_M_ARGS_READ,
+
+/** Read the list of the debugged tasks's threads.
+ *
+ * - 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 sysarg_t values
+ * (thread ids). On answer, the kernel will set:
+ *
+ * - ARG2 - number of bytes that were actually copied
+ * - ARG3 - number of bytes of the complete data
+ *
+ */
+UDEBUG_M_THREAD_READ,
+
+/** Read the debugged tasks's memory.
+ *
+ * - ARG2 - destination address in the caller's address space
+ * - ARG3 - source address in the recipient's address space
+ * - ARG4 - size of receiving buffer in bytes
+ *
+ */
+UDEBUG_M_MEM_READ,
+
+} udebug_method_t;
+
+				
+typedef enum {
+	UDEBUG_EVENT_FINISHED = 1,	/**< Debuging session has finished */
+	UDEBUG_EVENT_STOP,		/**< Stopped on DEBUG_STOP request */
+	UDEBUG_EVENT_SYSCALL_B,		/**< Before beginning syscall execution */
+	UDEBUG_EVENT_SYSCALL_E,		/**< After finishing syscall execution */
+	UDEBUG_EVENT_THREAD_B,		/**< The task created a new thread */
+	UDEBUG_EVENT_THREAD_E		/**< A thread exited */
+} udebug_event_t;
+
+#define UDEBUG_EVMASK(event) (1 << ((event) - 1))
+
+typedef enum {
+	UDEBUG_EM_FINISHED	= UDEBUG_EVMASK(UDEBUG_EVENT_FINISHED),
+	UDEBUG_EM_STOP		= UDEBUG_EVMASK(UDEBUG_EVENT_STOP),
+	UDEBUG_EM_SYSCALL_B	= UDEBUG_EVMASK(UDEBUG_EVENT_SYSCALL_B),
+	UDEBUG_EM_SYSCALL_E	= UDEBUG_EVMASK(UDEBUG_EVENT_SYSCALL_E),
+	UDEBUG_EM_THREAD_B	= UDEBUG_EVMASK(UDEBUG_EVENT_THREAD_B),
+	UDEBUG_EM_THREAD_E	= UDEBUG_EVMASK(UDEBUG_EVENT_THREAD_E),
+	UDEBUG_EM_ALL		=
+		UDEBUG_EVMASK(UDEBUG_EVENT_FINISHED) |
+		UDEBUG_EVMASK(UDEBUG_EVENT_STOP) |
+		UDEBUG_EVMASK(UDEBUG_EVENT_SYSCALL_B) |
+		UDEBUG_EVMASK(UDEBUG_EVENT_SYSCALL_E) |
+		UDEBUG_EVMASK(UDEBUG_EVENT_THREAD_B) |
+		UDEBUG_EVMASK(UDEBUG_EVENT_THREAD_E)
+} udebug_evmask_t;
+
+#ifdef KERNEL
+
+#include <synch/mutex.h>
+#include <arch/interrupt.h>
+#include <atomic.h>
+
+typedef enum {
+	/** Task is not being debugged */
+	UDEBUG_TS_INACTIVE,
+	/** BEGIN operation in progress (waiting for threads to stop) */
+	UDEBUG_TS_BEGINNING,
+	/** Debugger fully connected */
+	UDEBUG_TS_ACTIVE,
+	/** Task is shutting down, no more debug activities allowed */
+	UDEBUG_TS_SHUTDOWN
+} udebug_task_state_t;
+
+/** Debugging part of task_t structure.
+ */
+typedef struct {
+	/** Synchronize debug ops on this task / access to this structure */
+	mutex_t lock;
+	char *lock_owner;
+
+	udebug_task_state_t dt_state;
+	call_t *begin_call;
+	int not_stoppable_count;
+	struct task *debugger;
+	udebug_evmask_t evmask;
+} udebug_task_t;
+
+/** Debugging part of thread_t structure.
+ */
+typedef struct {
+	/** 
+	 * Prevent deadlock with udebug_before_thread_runs() in interrupt
+	 * handler, without actually disabling interrupts.
+	 * ==0 means "unlocked", >0 means "locked"
+	 */
+	atomic_t int_lock;
+
+	/** Synchronize debug ops on this thread / access to this structure */
+	mutex_t lock;
+
+	waitq_t go_wq;
+	call_t *go_call;
+	unative_t syscall_args[6];
+
+	/** What type of event are we stopped in or 0 if none */
+	udebug_event_t cur_event;	
+	bool stop;
+	bool stoppable;
+	bool debug_active; /**< In a debugging session */
+} udebug_thread_t;
+
+struct task;
+struct thread;
+
+void udebug_task_init(udebug_task_t *ut);
+void udebug_thread_initialize(udebug_thread_t *ut);
+
+void udebug_syscall_event(unative_t a1, unative_t a2, unative_t a3,
+    unative_t a4, unative_t a5, unative_t a6, unative_t id, unative_t rc,
+    bool end_variant);
+
+void udebug_thread_b_event(struct thread *t);
+void udebug_thread_e_event(void);
+
+void udebug_stoppable_begin(void);
+void udebug_stoppable_end(void);
+
+void udebug_before_thread_runs(void);
+
+int udebug_task_cleanup(struct task *ta);
+
+#endif
+
+#endif
+
+/** @}
+ */
Index: kernel/generic/include/udebug/udebug_ipc.h
===================================================================
--- kernel/generic/include/udebug/udebug_ipc.h	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
+++ kernel/generic/include/udebug/udebug_ipc.h	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2008 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 generic	
+ * @{
+ */
+/** @file
+ */
+
+#ifndef KERN_UDEBUG_IPC_H_
+#define KERN_UDEBUG_IPC_H_
+
+#include <ipc/ipc.h>
+
+int udebug_request_preprocess(call_t *call, phone_t *phone);
+void udebug_call_receive(call_t *call);
+
+
+#endif
+
+/** @}
+ */
Index: kernel/generic/include/udebug/udebug_ops.h
===================================================================
--- kernel/generic/include/udebug/udebug_ops.h	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
+++ kernel/generic/include/udebug/udebug_ops.h	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2008 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 generic	
+ * @{
+ */
+/** @file
+ */
+
+#ifndef KERN_UDEBUG_OPS_H_
+#define KERN_UDEBUG_OPS_H_
+
+#include <ipc/ipc.h>
+
+int udebug_begin(call_t *call);
+int udebug_end(void);
+int udebug_set_evmask(udebug_evmask_t mask);
+
+int udebug_go(thread_t *t, call_t *call);
+int udebug_stop(thread_t *t, call_t *call);
+
+int udebug_thread_read(void **buffer, size_t buf_size, size_t *n);
+int udebug_args_read(thread_t *t, void **buffer);
+
+int udebug_mem_read(unative_t uspace_addr, size_t n, void **buffer);
+
+#endif
+
+/** @}
+ */
Index: kernel/generic/src/ipc/ipc.c
===================================================================
--- kernel/generic/src/ipc/ipc.c	(revision 06a195bc5890a55fd6f733495812e905812949fc)
+++ kernel/generic/src/ipc/ipc.c	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
@@ -44,4 +44,5 @@
 #include <synch/synch.h>
 #include <ipc/ipc.h>
+#include <ipc/ipc_kbox.h>
 #include <errno.h>
 #include <mm/slab.h>
@@ -52,4 +53,5 @@
 
 #include <print.h>
+#include <console/console.h>
 #include <proc/thread.h>
 #include <arch/interrupt.h>
@@ -428,5 +430,5 @@
  * @param lst		Head of the list to be cleaned up.
  */
-static void ipc_cleanup_call_list(link_t *lst)
+void ipc_cleanup_call_list(link_t *lst)
 {
 	call_t *call;
@@ -443,4 +445,71 @@
 }
 
+/** Disconnects all phones connected to an answerbox.
+ *
+ * @param box		Answerbox to disconnect phones from.
+ * @param notify_box	If true, the answerbox will get a hangup message for
+ *			each disconnected phone.
+ */
+void ipc_answerbox_slam_phones(answerbox_t *box, bool notify_box)
+{
+	phone_t *phone;
+	DEADLOCK_PROBE_INIT(p_phonelck);
+	ipl_t ipl;
+	call_t *call;
+
+	call = notify_box ? ipc_call_alloc(0) : NULL;
+
+	/* Disconnect all phones connected to our answerbox */
+restart_phones:
+	ipl = interrupts_disable();
+	spinlock_lock(&box->lock);
+	while (!list_empty(&box->connected_phones)) {
+		phone = list_get_instance(box->connected_phones.next,
+		    phone_t, link);
+		if (SYNCH_FAILED(mutex_trylock(&phone->lock))) {
+			spinlock_unlock(&box->lock);
+			interrupts_restore(ipl);
+			DEADLOCK_PROBE(p_phonelck, DEADLOCK_THRESHOLD);
+			goto restart_phones;
+		}
+		
+		/* Disconnect phone */
+		ASSERT(phone->state == IPC_PHONE_CONNECTED);
+
+		list_remove(&phone->link);
+		phone->state = IPC_PHONE_SLAMMED;
+
+		if (notify_box) {
+			mutex_unlock(&phone->lock);
+			spinlock_unlock(&box->lock);
+			interrupts_restore(ipl);
+
+			/*
+			 * Send one message to the answerbox for each
+			 * phone. Used to make sure the kbox thread
+			 * wakes up after the last phone has been
+			 * disconnected.
+			 */
+			IPC_SET_METHOD(call->data, IPC_M_PHONE_HUNGUP);
+			call->flags |= IPC_CALL_DISCARD_ANSWER;
+			_ipc_call(phone, box, call);
+
+			/* Allocate another call in advance */
+			call = ipc_call_alloc(0);
+
+			/* Must start again */
+			goto restart_phones;
+		}
+
+		mutex_unlock(&phone->lock);
+	}
+
+	spinlock_unlock(&box->lock);
+	interrupts_restore(ipl);
+
+	/* Free unused call */
+	if (call) ipc_call_free(call);
+}
+
 /** Cleans up all IPC communication of the current task.
  *
@@ -452,6 +521,4 @@
 	int i;
 	call_t *call;
-	phone_t *phone;
-	DEADLOCK_PROBE_INIT(p_phonelck);
 
 	/* Disconnect all our phones ('ipc_phone_hangup') */
@@ -462,25 +529,14 @@
 	ipc_irq_cleanup(&TASK->answerbox);
 
-	/* Disconnect all phones connected to our answerbox */
-restart_phones:
+	/* Disconnect all phones connected to our regular answerbox */
+	ipc_answerbox_slam_phones(&TASK->answerbox, false);
+
+#ifdef CONFIG_UDEBUG
+	/* Clean up kbox thread and communications */
+	ipc_kbox_cleanup();
+#endif
+
+	/* Answer all messages in 'calls' and 'dispatched_calls' queues */
 	spinlock_lock(&TASK->answerbox.lock);
-	while (!list_empty(&TASK->answerbox.connected_phones)) {
-		phone = list_get_instance(TASK->answerbox.connected_phones.next,
-		    phone_t, link);
-		if (SYNCH_FAILED(mutex_trylock(&phone->lock))) {
-			spinlock_unlock(&TASK->answerbox.lock);
-			DEADLOCK_PROBE(p_phonelck, DEADLOCK_THRESHOLD);
-			goto restart_phones;
-		}
-		
-		/* Disconnect phone */
-		ASSERT(phone->state == IPC_PHONE_CONNECTED);
-		phone->state = IPC_PHONE_SLAMMED;
-		list_remove(&phone->link);
-
-		mutex_unlock(&phone->lock);
-	}
-
-	/* Answer all messages in 'calls' and 'dispatched_calls' queues */
 	ipc_cleanup_call_list(&TASK->answerbox.dispatched_calls);
 	ipc_cleanup_call_list(&TASK->answerbox.calls);
Index: kernel/generic/src/ipc/ipc_kbox.c
===================================================================
--- kernel/generic/src/ipc/ipc_kbox.c	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
+++ kernel/generic/src/ipc/ipc_kbox.c	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
@@ -0,0 +1,221 @@
+/*
+ * Copyright (c) 2008 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 genericipc
+ * @{
+ */
+/** @file
+ */
+
+#include <synch/synch.h>
+#include <synch/spinlock.h>
+#include <synch/mutex.h>
+#include <ipc/ipc.h>
+#include <ipc/ipcrsc.h>
+#include <arch.h>
+#include <errno.h>
+#include <print.h>
+#include <udebug/udebug_ipc.h>
+#include <ipc/ipc_kbox.h>
+
+void ipc_kbox_cleanup(void)
+{
+	bool have_kb_thread;
+
+	/* Only hold kb_cleanup_lock while setting kb_finished - this is enough */
+	mutex_lock(&TASK->kb_cleanup_lock);
+	TASK->kb_finished = true;
+	mutex_unlock(&TASK->kb_cleanup_lock);
+
+	have_kb_thread = (TASK->kb_thread != NULL);
+
+	/* From now on nobody will try to connect phones or attach kbox threads */
+
+	/*
+	 * Disconnect all phones connected to our kbox. Passing true for
+	 * notify_box causes a HANGUP message to be inserted for each
+	 * disconnected phone. This ensures the kbox thread is going to
+	 * wake up and terminate.
+	 */
+	ipc_answerbox_slam_phones(&TASK->kernel_box, have_kb_thread);
+	
+	if (have_kb_thread) {
+		printf("join kb_thread..\n");
+		thread_join(TASK->kb_thread);
+		thread_detach(TASK->kb_thread);
+		printf("join done\n");
+		TASK->kb_thread = NULL;
+	}
+
+	/* Answer all messages in 'calls' and 'dispatched_calls' queues */
+	spinlock_lock(&TASK->kernel_box.lock);
+	ipc_cleanup_call_list(&TASK->kernel_box.dispatched_calls);
+	ipc_cleanup_call_list(&TASK->kernel_box.calls);
+	spinlock_unlock(&TASK->kernel_box.lock);
+}
+
+
+static void kbox_thread_proc(void *arg)
+{
+	call_t *call;
+	int method;
+	bool done;
+	ipl_t ipl;
+
+	(void)arg;
+	printf("kbox_thread_proc()\n");
+	done = false;
+
+	while (!done) {
+		//printf("kbox: wait for call\n");
+		call = ipc_wait_for_call(&TASK->kernel_box, SYNCH_NO_TIMEOUT,
+			SYNCH_FLAGS_NONE);
+
+		if (call != NULL) {
+			method = IPC_GET_METHOD(call->data);
+
+			if (method == IPC_M_DEBUG_ALL) {
+				udebug_call_receive(call);
+			}
+
+			if (method == IPC_M_PHONE_HUNGUP) {
+				printf("kbox: handle hangup message\n");
+
+				/* Was it our debugger, who hung up? */
+				if (call->sender == TASK->udebug.debugger) {
+					/* Terminate debugging session (if any) */
+					printf("kbox: terminate debug session\n");
+					ipl = interrupts_disable();
+					spinlock_lock(&TASK->lock);
+					udebug_task_cleanup(TASK);
+					spinlock_unlock(&TASK->lock);
+					interrupts_restore(ipl);
+				} else {
+					printf("kbox: was not debugger\n");
+				}
+
+				printf("kbox: continue with hangup message\n");
+				IPC_SET_RETVAL(call->data, 0);
+				ipc_answer(&TASK->kernel_box, call);
+
+				ipl = interrupts_disable();
+				spinlock_lock(&TASK->lock);
+				spinlock_lock(&TASK->answerbox.lock);
+				if (list_empty(&TASK->answerbox.connected_phones)) {
+					/* Last phone has been disconnected */
+					TASK->kb_thread = NULL;
+					done = true;
+					printf("phone list is empty\n");
+				}
+				spinlock_unlock(&TASK->answerbox.lock);
+				spinlock_unlock(&TASK->lock);
+				interrupts_restore(ipl);
+			}
+		}
+	}
+
+	printf("kbox: finished\n");
+}
+
+
+/**
+ * Connect phone to a task kernel-box specified by id.
+ *
+ * Note that this is not completely atomic. For optimisation reasons,
+ * The task might start cleaning up kbox after the phone has been connected
+ * and before a kbox thread has been created. This must be taken into account
+ * in the cleanup code.
+ *
+ * @return 		Phone id on success, or negative error code.
+ */
+int ipc_connect_kbox(task_id_t taskid)
+{
+	int newphid;
+	task_t *ta;
+	thread_t *kb_thread;
+	ipl_t ipl;
+
+	ipl = interrupts_disable();
+	spinlock_lock(&tasks_lock);
+
+	ta = task_find_by_id(taskid);
+	if (ta == NULL) {
+		spinlock_unlock(&tasks_lock);
+		interrupts_restore(ipl);
+		return ENOENT;
+	}
+
+	atomic_inc(&ta->refcount);
+
+	spinlock_unlock(&tasks_lock);
+	interrupts_restore(ipl);
+
+	mutex_lock(&ta->kb_cleanup_lock);
+
+	if (atomic_predec(&ta->refcount) == 0) {
+		mutex_unlock(&ta->kb_cleanup_lock);
+		task_destroy(ta);
+		return ENOENT;
+	}
+
+	if (ta->kb_finished != false) {
+		mutex_unlock(&ta->kb_cleanup_lock);
+		return EINVAL;
+	}
+
+	newphid = phone_alloc();
+	if (newphid < 0) {
+		mutex_unlock(&ta->kb_cleanup_lock);
+		return ELIMIT;
+	}
+
+	/* Connect the newly allocated phone to the kbox */
+	ipc_phone_connect(&TASK->phones[newphid], &ta->kernel_box);
+
+	if (ta->kb_thread != NULL) {
+		mutex_unlock(&ta->kb_cleanup_lock);
+		return newphid;
+	}
+
+	/* Create a kbox thread */
+	kb_thread = thread_create(kbox_thread_proc, NULL, ta, 0, "kbox", false);
+	if (!kb_thread) {
+		mutex_unlock(&ta->kb_cleanup_lock);
+		return ENOMEM;
+	}
+
+	ta->kb_thread = kb_thread;
+	thread_ready(kb_thread);
+
+	mutex_unlock(&ta->kb_cleanup_lock);
+
+	return newphid;
+}
+
+/** @}
+ */
Index: kernel/generic/src/ipc/sysipc.c
===================================================================
--- kernel/generic/src/ipc/sysipc.c	(revision 06a195bc5890a55fd6f733495812e905812949fc)
+++ kernel/generic/src/ipc/sysipc.c	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
@@ -43,4 +43,6 @@
 #include <ipc/irq.h>
 #include <ipc/ipcrsc.h>
+#include <ipc/ipc_kbox.h>
+#include <udebug/udebug_ipc.h>
 #include <arch/interrupt.h>
 #include <print.h>
@@ -296,8 +298,9 @@
  *
  * @param call		Call structure with the request.
+ * @param phone		Phone that the call will be sent through.
  *
  * @return 		Return 0 on success, ELIMIT or EPERM on error.
  */
-static int request_preprocess(call_t *call)
+static int request_preprocess(call_t *call, phone_t *phone)
 {
 	int newphid;
@@ -341,4 +344,8 @@
 		}
 		break;
+#ifdef CONFIG_UDEBUG
+	case IPC_M_DEBUG_ALL:
+		return udebug_request_preprocess(call, phone);
+#endif
 	default:
 		break;
@@ -370,4 +377,5 @@
 	if (call->buffer) {
 		/* This must be an affirmative answer to IPC_M_DATA_READ. */
+		/* or IPC_M_DEBUG_ALL/UDEBUG_M_MEM_READ... */
 		uintptr_t dst = IPC_GET_ARG1(call->data);
 		size_t size = IPC_GET_ARG2(call->data);
@@ -400,5 +408,11 @@
 		}
 		IPC_SET_ARG5(call->data, phoneid);
-	} 
+	}
+	switch (IPC_GET_METHOD(call->data)) {
+	case IPC_M_DEBUG_ALL:
+		return -1;
+	default:
+		break;
+	}
 	return 0;
 }
@@ -442,5 +456,5 @@
 	IPC_SET_ARG5(call.data, 0);
 
-	if (!(res = request_preprocess(&call))) {
+	if (!(res = request_preprocess(&call, phone))) {
 		rc = ipc_call_sync(phone, &call);
 		if (rc != EOK)
@@ -482,5 +496,5 @@
 	GET_CHECK_PHONE(phone, phoneid, return ENOENT);
 
-	if (!(res = request_preprocess(&call))) {
+	if (!(res = request_preprocess(&call, phone))) {
 		rc = ipc_call_sync(phone, &call);
 		if (rc != EOK)
@@ -551,5 +565,5 @@
 	IPC_SET_ARG5(call->data, 0);
 
-	if (!(res = request_preprocess(call)))
+	if (!(res = request_preprocess(call, phone)))
 		ipc_call(phone, call);
 	else
@@ -585,5 +599,5 @@
 		return (unative_t) rc;
 	}
-	if (!(res = request_preprocess(call)))
+	if (!(res = request_preprocess(call, phone)))
 		ipc_call(phone, call);
 	else
@@ -869,4 +883,29 @@
 }
 
+#include <console/console.h>
+
+/**
+ * Syscall connect to a task by id.
+ *
+ * @return 		Phone id on success, or negative error code.
+ */
+unative_t sys_ipc_connect_kbox(sysarg64_t *uspace_taskid_arg)
+{
+#ifdef CONFIG_UDEBUG
+	sysarg64_t taskid_arg;
+	int rc;
+	
+	rc = copy_from_uspace(&taskid_arg, uspace_taskid_arg, sizeof(sysarg64_t));
+	if (rc != 0)
+		return (unative_t) rc;
+
+	printf("sys_ipc_connect_kbox(%lld, %d)\n", taskid_arg.value);
+
+	return ipc_connect_kbox(taskid_arg.value);
+#else
+	return (unative_t) ENOTSUP;
+#endif
+}
+
 /** @}
  */
Index: kernel/generic/src/main/uinit.c
===================================================================
--- kernel/generic/src/main/uinit.c	(revision 06a195bc5890a55fd6f733495812e905812949fc)
+++ kernel/generic/src/main/uinit.c	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
@@ -47,4 +47,6 @@
 #include <mm/slab.h>
 #include <arch.h>
+#include <udebug/udebug.h>
+
 
 /** Thread used to bring up userspace thread.
@@ -66,4 +68,8 @@
 	 */
 	thread_detach(THREAD);
+
+#ifdef CONFIG_UDEBUG
+	udebug_stoppable_end();
+#endif
 	
 	uarg.uspace_entry = ((uspace_arg_t *) arg)->uspace_entry;
Index: kernel/generic/src/proc/task.c
===================================================================
--- kernel/generic/src/proc/task.c	(revision 06a195bc5890a55fd6f733495812e905812949fc)
+++ kernel/generic/src/proc/task.c	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
@@ -156,5 +156,16 @@
 	ta->capabilities = 0;
 	ta->cycles = 0;
-	
+
+#ifdef CONFIG_UDEBUG
+	/* Init debugging stuff */
+	udebug_task_init(&ta->udebug);
+
+	/* Init kbox stuff */
+	ipc_answerbox_init(&ta->kernel_box, ta);
+	ta->kb_thread = NULL;
+	mutex_initialize(&ta->kb_cleanup_lock, MUTEX_PASSIVE);
+	ta->kb_finished = false;
+#endif
+
 	ipc_answerbox_init(&ta->answerbox, ta);
 	for (i = 0; i < IPC_MAX_PHONES; i++)
Index: kernel/generic/src/proc/thread.c
===================================================================
--- kernel/generic/src/proc/thread.c	(revision 06a195bc5890a55fd6f733495812e905812949fc)
+++ kernel/generic/src/proc/thread.c	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
@@ -181,4 +181,8 @@
 	}
 
+#ifdef CONFIG_UDEBUG
+	mutex_initialize(&t->udebug.lock, MUTEX_PASSIVE);
+#endif
+
 	return 0;
 }
@@ -348,4 +352,9 @@
 	t->threads_tree_node.key = (uintptr_t) t;
 
+#ifdef CONFIG_UDEBUG
+	/* Init debugging stuff */
+	udebug_thread_initialize(&t->udebug);
+#endif
+
 	/* might depend on previous initialization */
 	thread_create_arch(t);	
@@ -410,10 +419,15 @@
 
 	/*
-	 * Attach to the current task.
+	 * Attach to the specified task.
 	 */
 	ipl = interrupts_disable();
 	spinlock_lock(&task->lock);
+
 	atomic_inc(&task->refcount);
-	atomic_inc(&task->lifecount);
+
+	/* Must not count kbox thread into lifecount */
+	if (t->flags & THREAD_FLAG_USPACE)
+		atomic_inc(&task->lifecount);
+
 	list_append(&t->th_link, &task->th_head);
 	spinlock_unlock(&task->lock);
@@ -438,12 +452,17 @@
 	ipl_t ipl;
 
-	if (atomic_predec(&TASK->lifecount) == 0) {
-		/*
-		 * We are the last thread in the task that still has not exited.
-		 * With the exception of the moment the task was created, new
-		 * threads can only be created by threads of the same task.
-		 * We are safe to perform cleanup.
-		 */
-		if (THREAD->flags & THREAD_FLAG_USPACE) {
+	if (THREAD->flags & THREAD_FLAG_USPACE) {
+#ifdef CONFIG_UDEBUG
+		/* Generate udebug THREAD_E event */
+		udebug_thread_e_event();
+#endif
+		if (atomic_predec(&TASK->lifecount) == 0) {
+			/*
+			 * We are the last userspace thread in the task that
+			 * still has not exited. With the exception of the
+			 * moment the task was created, new userspace threads
+			 * can only be created by threads of the same task.
+			 * We are safe to perform cleanup.
+			 */
 			ipc_cleanup();
 			futex_cleanup();
@@ -742,4 +761,9 @@
 		thread_ready(t);
 
+#ifdef CONFIG_UDEBUG
+		/* Generate udebug THREAD_B event */
+		udebug_thread_b_event(t);
+#endif
+
 		return 0;
 	} else
Index: kernel/generic/src/syscall/syscall.c
===================================================================
--- kernel/generic/src/syscall/syscall.c	(revision 06a195bc5890a55fd6f733495812e905812949fc)
+++ kernel/generic/src/syscall/syscall.c	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
@@ -54,4 +54,5 @@
 #include <sysinfo/sysinfo.h>
 #include <console/console.h>
+#include <udebug/udebug.h>
 
 /** Print using kernel facility
@@ -102,7 +103,14 @@
 	unative_t rc;
 
-	if (id < SYSCALL_END)
+#ifdef CONFIG_UDEBUG
+	udebug_syscall_event(a1, a2, a3, a4, a5, a6, id, 0, false);
+#endif
+
+	if (id < SYSCALL_END) {	
+#ifdef CONFIG_UDEBUG
+		udebug_stoppable_begin();
+#endif
 		rc = syscall_table[id](a1, a2, a3, a4, a5, a6);
-	else {
+	} else {
 		printf("Task %" PRIu64": Unknown syscall %#" PRIxn, TASK->taskid, id);
 		task_kill(TASK->taskid);
@@ -112,4 +120,9 @@
 	if (THREAD->interrupted)
 		thread_exit();
+
+#ifdef CONFIG_UDEBUG
+	udebug_syscall_event(a1, a2, a3, a4, a5, a6, id, rc, true);
+	udebug_stoppable_end();
+#endif
 	
 	return rc;
@@ -166,5 +179,7 @@
 	
 	/* Debug calls */
-	(syshandler_t) sys_debug_enable_console
+	(syshandler_t) sys_debug_enable_console,
+
+	(syshandler_t) sys_ipc_connect_kbox
 };
 
Index: kernel/generic/src/udebug/udebug.c
===================================================================
--- kernel/generic/src/udebug/udebug.c	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
+++ kernel/generic/src/udebug/udebug.c	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
@@ -0,0 +1,502 @@
+/*
+ * Copyright (c) 2008 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 generic
+ * @{
+ */
+
+/**
+ * @file
+ * @brief	Udebug.
+ *
+ * Functions in this file are executed directly in each thread, which
+ * may or may not be the subject of debugging. The udebug_stoppable_begin/end()
+ * functions are also executed in the clock interrupt handler. To avoid
+ * deadlock, functions in this file are protected from the interrupt
+ * by locking the recursive lock THREAD->udebug.int_lock (just an atomic
+ * variable). This prevents udebug_stoppable_begin/end() from being
+ * executed in the interrupt handler (they are skipped).
+ *
+ * Functions in udebug_ops.c and udebug_ipc.c execute in different threads,
+ * so they needn't be protected from the (preemptible) interrupt-initiated
+ * code.
+ */
+ 
+#include <synch/waitq.h>
+#include <print.h>
+#include <udebug/udebug.h>
+#include <errno.h>
+#include <arch.h>
+
+static inline void udebug_int_lock(void)
+{
+	atomic_inc(&THREAD->udebug.int_lock);
+}
+
+static inline void udebug_int_unlock(void)
+{
+	atomic_dec(&THREAD->udebug.int_lock);
+}
+
+void udebug_task_init(udebug_task_t *ut)
+{
+	mutex_initialize(&ut->lock, MUTEX_PASSIVE);
+	ut->dt_state = UDEBUG_TS_INACTIVE;
+	ut->begin_call = NULL;
+	ut->not_stoppable_count = 0;
+	ut->evmask = 0;
+}
+
+void udebug_thread_initialize(udebug_thread_t *ut)
+{
+	mutex_initialize(&ut->lock, MUTEX_PASSIVE);
+	waitq_initialize(&ut->go_wq);
+
+	/*
+	 * At the beginning the thread is stoppable, so int_lock be set, too.
+	 */
+	atomic_set(&ut->int_lock, 1);
+
+	ut->go_call = NULL;
+	ut->stop = true;
+	ut->stoppable = true;
+	ut->debug_active = false;
+	ut->cur_event = 0; /* none */
+}
+
+static void udebug_wait_for_go(waitq_t *wq)
+{
+	int rc;
+	ipl_t ipl;
+
+	ipl = waitq_sleep_prepare(wq);
+
+	wq->missed_wakeups = 0;	/* Enforce blocking. */
+	rc = waitq_sleep_timeout_unsafe(wq, SYNCH_NO_TIMEOUT, SYNCH_FLAGS_NONE);
+
+	waitq_sleep_finish(wq, rc, ipl);
+}
+
+/** Do a preliminary check that a debugging session is in progress.
+ * 
+ * This only requires the THREAD->udebug.lock mutex (and not
+ * TASK->udebug.lock mutex). For an undebugged task, this will
+ * never block (while there could be collisions by different threads
+ * on the TASK mutex), thus improving SMP perormance for undebugged tasks.
+ */
+static bool udebug_thread_precheck(void)
+{
+	bool res;
+
+	mutex_lock(&THREAD->udebug.lock);
+	res = THREAD->udebug.debug_active;
+	mutex_unlock(&THREAD->udebug.lock);
+
+	return res;
+}
+
+void udebug_stoppable_begin(void)
+{
+	int nsc;
+	call_t *db_call, *go_call;
+
+	ASSERT(THREAD);
+	ASSERT(TASK);
+
+	udebug_int_lock();
+
+	/* Early check for undebugged tasks */
+	if (!udebug_thread_precheck()) {
+		udebug_int_unlock();
+		return;
+	}
+
+	mutex_lock(&TASK->udebug.lock);
+
+	nsc = --TASK->udebug.not_stoppable_count;
+
+	/* Lock order OK, THREAD->udebug.lock is after TASK->udebug.lock */
+	mutex_lock(&THREAD->udebug.lock);
+	ASSERT(THREAD->udebug.stoppable == false);
+	THREAD->udebug.stoppable = true;
+
+	if (TASK->udebug.dt_state == UDEBUG_TS_BEGINNING && nsc == 0) {
+		/*
+		 * This was the last non-stoppable thread. Reply to
+		 * DEBUG_BEGIN call.
+		 */
+
+		db_call = TASK->udebug.begin_call;
+		ASSERT(db_call);
+
+		TASK->udebug.dt_state = UDEBUG_TS_ACTIVE;
+		TASK->udebug.begin_call = NULL;
+
+		IPC_SET_RETVAL(db_call->data, 0);
+		ipc_answer(&TASK->answerbox, db_call);		
+
+	} else if (TASK->udebug.dt_state == UDEBUG_TS_ACTIVE) {
+		/*
+		 * Active debugging session
+		 */
+
+		if (THREAD->udebug.debug_active && THREAD->udebug.stop) {
+			/*
+			 * Thread was requested to stop - answer go call
+			 */
+
+			/* Make sure nobody takes this call away from us */
+			go_call = THREAD->udebug.go_call;
+			THREAD->udebug.go_call = NULL;
+			ASSERT(go_call);
+
+			IPC_SET_RETVAL(go_call->data, 0);
+			IPC_SET_ARG1(go_call->data, UDEBUG_EVENT_STOP);
+
+			THREAD->udebug.cur_event = UDEBUG_EVENT_STOP;
+
+	    		ipc_answer(&TASK->answerbox, go_call);
+		}
+	}
+
+	mutex_unlock(&THREAD->udebug.lock);
+        mutex_unlock(&TASK->udebug.lock);
+}
+
+void udebug_stoppable_end(void)
+{
+	/* Early check for undebugged tasks */
+	if (!udebug_thread_precheck()) {
+		udebug_int_unlock();
+		return;
+	}
+
+restart:
+	mutex_lock(&TASK->udebug.lock);
+	mutex_lock(&THREAD->udebug.lock);
+
+	if (THREAD->udebug.debug_active &&
+	    THREAD->udebug.stop == true) {
+		TASK->udebug.begin_call = NULL;
+		mutex_unlock(&THREAD->udebug.lock);
+		mutex_unlock(&TASK->udebug.lock);
+
+		udebug_wait_for_go(&THREAD->udebug.go_wq);
+
+		goto restart;
+		/* must try again - have to lose stoppability atomically */
+	} else {
+		++TASK->udebug.not_stoppable_count;
+		ASSERT(THREAD->udebug.stoppable == true);
+		THREAD->udebug.stoppable = false;
+
+		mutex_unlock(&THREAD->udebug.lock);
+		mutex_unlock(&TASK->udebug.lock);
+	}
+
+	udebug_int_unlock();
+}
+
+/** Upon being scheduled to run, check if the current thread should stop.
+ *
+ * This function is called from clock(). Preemption is enabled.
+ * interrupts are disabled, but since this is called after
+ * being scheduled-in, we can enable them, if we're careful enough
+ * not to allow arbitrary recursion or deadlock with the thread context.
+ */
+void udebug_before_thread_runs(void)
+{
+	ipl_t ipl;
+
+	return;
+	ASSERT(!PREEMPTION_DISABLED);
+
+	/* 
+	 * Prevent agains re-entering, such as when preempted inside this
+	 * function.
+	 */
+	if (atomic_get(&THREAD->udebug.int_lock) != 0)
+		return;
+
+	udebug_int_lock();
+
+	ipl = interrupts_enable();
+
+	/* Now we're free to do whatever we need (lock mutexes, sleep, etc.) */
+
+	/* Check if we're supposed to stop */
+	udebug_stoppable_begin();
+	udebug_stoppable_end();
+
+	interrupts_restore(ipl);
+
+	udebug_int_unlock();
+}
+
+void udebug_syscall_event(unative_t a1, unative_t a2, unative_t a3,
+    unative_t a4, unative_t a5, unative_t a6, unative_t id, unative_t rc,
+    bool end_variant)
+{
+	call_t *call;
+	udebug_event_t etype;
+
+	etype = end_variant ? UDEBUG_EVENT_SYSCALL_E : UDEBUG_EVENT_SYSCALL_B;
+
+	udebug_int_lock();
+
+	/* Early check for undebugged tasks */
+	if (!udebug_thread_precheck()) {
+		udebug_int_unlock();
+		return;
+	}
+
+	mutex_lock(&TASK->udebug.lock);
+	mutex_lock(&THREAD->udebug.lock);
+
+	/* Must only generate events when in debugging session and have go */
+	if (THREAD->udebug.debug_active != true ||
+	    THREAD->udebug.stop == true ||
+	    (TASK->udebug.evmask & UDEBUG_EVMASK(etype)) == 0) {
+		mutex_unlock(&THREAD->udebug.lock);
+		mutex_unlock(&TASK->udebug.lock);
+		return;
+	}
+
+	//printf("udebug_syscall_event\n");
+	call = THREAD->udebug.go_call;
+	THREAD->udebug.go_call = NULL;
+
+	IPC_SET_RETVAL(call->data, 0);
+	IPC_SET_ARG1(call->data, etype);
+	IPC_SET_ARG2(call->data, id);
+	IPC_SET_ARG3(call->data, rc);
+	//printf("udebug_syscall_event/ipc_answer\n");
+
+	THREAD->udebug.syscall_args[0] = a1;
+	THREAD->udebug.syscall_args[1] = a2;
+	THREAD->udebug.syscall_args[2] = a3;
+	THREAD->udebug.syscall_args[3] = a4;
+	THREAD->udebug.syscall_args[4] = a5;
+	THREAD->udebug.syscall_args[5] = a6;
+
+	/*
+	 * Make sure udebug.stop is true when going to sleep
+	 * in case we get woken up by DEBUG_END. (At which
+	 * point it must be back to the initial true value).
+	 */
+	THREAD->udebug.stop = true;
+	THREAD->udebug.cur_event = etype;
+
+	ipc_answer(&TASK->answerbox, call);
+
+	mutex_unlock(&THREAD->udebug.lock);
+	mutex_unlock(&TASK->udebug.lock);
+
+	udebug_wait_for_go(&THREAD->udebug.go_wq);
+
+	udebug_int_unlock();
+}
+
+void udebug_thread_b_event(struct thread *t)
+{
+	call_t *call;
+
+	udebug_int_lock();
+
+	mutex_lock(&TASK->udebug.lock);
+	mutex_lock(&THREAD->udebug.lock);
+
+	printf("udebug_thread_b_event\n");
+	printf("- check state\n");
+
+	/* Must only generate events when in debugging session */
+	if (THREAD->udebug.debug_active != true) {
+		printf("- debug_active: %s, udebug.stop: %s\n",
+			THREAD->udebug.debug_active ? "yes(+)" : "no(-)",
+			THREAD->udebug.stop ? "yes(-)" : "no(+)");
+		mutex_unlock(&THREAD->udebug.lock);
+		mutex_unlock(&TASK->udebug.lock);
+		return;
+	}
+
+	printf("- trigger event\n");
+
+	call = THREAD->udebug.go_call;
+	THREAD->udebug.go_call = NULL;
+	IPC_SET_RETVAL(call->data, 0);
+	IPC_SET_ARG1(call->data, UDEBUG_EVENT_THREAD_B);
+	IPC_SET_ARG2(call->data, (unative_t)t);
+
+	/*
+	 * Make sure udebug.stop is true when going to sleep
+	 * in case we get woken up by DEBUG_END. (At which
+	 * point it must be back to the initial true value).
+	 */
+	THREAD->udebug.stop = true;
+	THREAD->udebug.cur_event = UDEBUG_EVENT_THREAD_B;
+
+	ipc_answer(&TASK->answerbox, call);
+
+	mutex_unlock(&THREAD->udebug.lock);
+	mutex_unlock(&TASK->udebug.lock);
+
+	printf("- sleep\n");
+	udebug_wait_for_go(&THREAD->udebug.go_wq);
+
+	udebug_int_unlock();
+}
+
+void udebug_thread_e_event(void)
+{
+	call_t *call;
+
+	udebug_int_lock();
+
+	mutex_lock(&TASK->udebug.lock);
+	mutex_lock(&THREAD->udebug.lock);
+
+//	printf("udebug_thread_e_event\n");
+//	printf("- check state\n");
+
+	/* Must only generate events when in debugging session */
+	if (THREAD->udebug.debug_active != true) {
+/*		printf("- debug_active: %s, udebug.stop: %s\n",
+			THREAD->udebug.debug_active ? "yes(+)" : "no(-)",
+			THREAD->udebug.stop ? "yes(-)" : "no(+)");*/
+		mutex_unlock(&THREAD->udebug.lock);
+		mutex_unlock(&TASK->udebug.lock);
+		return;
+	}
+
+//	printf("- trigger event\n");
+
+	call = THREAD->udebug.go_call;
+	THREAD->udebug.go_call = NULL;
+	IPC_SET_RETVAL(call->data, 0);
+	IPC_SET_ARG1(call->data, UDEBUG_EVENT_THREAD_E);
+
+	/* Prevent any further debug activity in thread */
+	THREAD->udebug.debug_active = false;
+	THREAD->udebug.cur_event = 0;		/* none */
+	THREAD->udebug.stop = true;	/* set to initial value */
+
+	ipc_answer(&TASK->answerbox, call);
+
+	mutex_unlock(&THREAD->udebug.lock);
+	mutex_unlock(&TASK->udebug.lock);
+
+	/* Leave int_lock enabled */
+	/* This event does not sleep - debugging has finished in this thread */
+}
+
+/**
+ * Terminate task debugging session.
+ *
+ * \param ta->udebug.lock must be already locked.
+ * \return Zero on success or negative error code.
+ */
+int udebug_task_cleanup(struct task *ta)
+{
+	thread_t *t;
+	link_t *cur;
+	int flags;
+	ipl_t ipl;
+
+	printf("udebug_task_cleanup()\n");
+	printf("task %llu\n", ta->taskid);
+
+	udebug_int_lock();
+
+	if (ta->udebug.dt_state != UDEBUG_TS_BEGINNING &&
+	    ta->udebug.dt_state != UDEBUG_TS_ACTIVE) {
+		printf("udebug_task_cleanup(): task not being debugged\n");
+		return EINVAL;
+	}
+
+	/* Finish debugging of all userspace threads */
+	for (cur = ta->th_head.next; cur != &ta->th_head; cur = cur->next) {
+		t = list_get_instance(cur, thread_t, th_link);
+
+		mutex_lock(&t->udebug.lock);
+
+		ipl = interrupts_disable();
+		spinlock_lock(&t->lock);
+
+		flags = t->flags;
+
+		spinlock_unlock(&t->lock);
+		interrupts_restore(ipl);
+
+		/* Only process userspace threads */
+		if ((flags & THREAD_FLAG_USPACE) != 0) {
+			/* Prevent any further debug activity in thread */
+			t->udebug.debug_active = false;
+			t->udebug.cur_event = 0;	/* none */
+
+			/* Still has go? */
+			if (t->udebug.stop == false) {
+				/*
+				* Yes, so clear go. As debug_active == false,
+				 * this doesn't affect anything.
+				 */
+				t->udebug.stop = true;	
+
+				/* Answer GO call */
+				printf("answer GO call with EVENT_FINISHED\n");
+				IPC_SET_RETVAL(t->udebug.go_call->data, 0);
+				IPC_SET_ARG1(t->udebug.go_call->data, UDEBUG_EVENT_FINISHED);
+
+				ipc_answer(&ta->answerbox, t->udebug.go_call);
+				t->udebug.go_call = NULL;
+			} else {
+				/*
+				 * Debug_stop is already at initial value.
+				 * Yet this means the thread needs waking up.
+				 */
+
+				/*
+				 * t's lock must not be held when calling
+				 * waitq_wakeup.
+				 */
+				waitq_wakeup(&t->udebug.go_wq, WAKEUP_FIRST);
+			}
+		}
+		mutex_unlock(&t->udebug.lock);
+	}
+
+	ta->udebug.dt_state = UDEBUG_TS_INACTIVE;
+	ta->udebug.debugger = NULL;
+
+	udebug_int_unlock();
+
+	return 0;
+}
+
+
+/** @}
+ */
Index: kernel/generic/src/udebug/udebug_ipc.c
===================================================================
--- kernel/generic/src/udebug/udebug_ipc.c	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
+++ kernel/generic/src/udebug/udebug_ipc.c	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
@@ -0,0 +1,299 @@
+/*
+ * Copyright (c) 2008 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 generic
+ * @{
+ */
+
+/**
+ * @file
+ * @brief	Udebug IPC message handling.
+ */
+ 
+#include <print.h>
+#include <proc/task.h>
+#include <proc/thread.h>
+#include <arch.h>
+#include <errno.h>
+#include <ipc/ipc.h>
+#include <syscall/copy.h>
+#include <udebug/udebug.h>
+#include <udebug/udebug_ops.h>
+#include <udebug/udebug_ipc.h>
+
+int udebug_request_preprocess(call_t *call, phone_t *phone)
+{
+	switch (IPC_GET_ARG1(call->data)) {
+	/* future UDEBUG_M_REGS_WRITE, UDEBUG_M_MEM_WRITE: */
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static void udebug_receive_begin(call_t *call)
+{
+	int rc;
+
+	rc = udebug_begin(call);
+	if (rc < 0) {
+		IPC_SET_RETVAL(call->data, rc);
+		ipc_answer(&TASK->kernel_box, call);
+		return;
+	}
+
+	if (rc != 0) {
+		IPC_SET_RETVAL(call->data, 0);
+		ipc_answer(&TASK->kernel_box, call);
+	}
+}
+
+static void udebug_receive_end(call_t *call)
+{
+	int rc;
+
+	rc = udebug_end();
+
+	IPC_SET_RETVAL(call->data, rc);
+	ipc_answer(&TASK->kernel_box, call);
+}
+
+static void udebug_receive_set_evmask(call_t *call)
+{
+	int rc;
+	udebug_evmask_t mask;
+
+	mask = IPC_GET_ARG2(call->data);
+	rc = udebug_set_evmask(mask);
+
+	IPC_SET_RETVAL(call->data, rc);
+	ipc_answer(&TASK->kernel_box, call);
+}
+
+
+static void udebug_receive_go(call_t *call)
+{
+	thread_t *t;
+	int rc;
+
+	//printf("debug_go()\n");
+
+	t = (thread_t *)IPC_GET_ARG2(call->data);
+
+	rc = udebug_go(t, call);
+	if (rc < 0) {
+		IPC_SET_RETVAL(call->data, rc);
+		ipc_answer(&TASK->kernel_box, call);
+		return;
+	}
+}
+
+static void udebug_receive_stop(call_t *call)
+{
+	thread_t *t;
+	int rc;
+
+	printf("debug_stop()\n");
+
+	t = (thread_t *)IPC_GET_ARG2(call->data);
+
+	rc = udebug_stop(t, call);
+	IPC_SET_RETVAL(call->data, rc);
+	ipc_answer(&TASK->kernel_box, call);
+}
+
+static void udebug_receive_thread_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;
+
+	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, &n);
+	if (rc < 0) {
+		IPC_SET_RETVAL(call->data, rc);
+		ipc_answer(&TASK->kernel_box, call);
+		return;
+	}
+
+	total_bytes = n;
+
+	/* Copy MAX(buf_size, total_bytes) bytes */
+
+	if (buf_size > total_bytes)
+		to_copy = total_bytes;
+	else
+		to_copy = buf_size;
+
+	/*
+	 * 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, to_copy);
+
+	IPC_SET_ARG3(call->data, total_bytes);
+	call->buffer = buffer;
+
+	ipc_answer(&TASK->kernel_box, call);
+}
+
+static void udebug_receive_args_read(call_t *call)
+{
+	thread_t *t;
+	unative_t uspace_addr;
+	int rc;
+	void *buffer;
+
+	t = (thread_t *)IPC_GET_ARG2(call->data);
+
+	rc = udebug_args_read(t, &buffer);
+	if (rc != EOK) {
+		IPC_SET_RETVAL(call->data, rc);
+		ipc_answer(&TASK->kernel_box, call);
+		return;
+	}
+
+	/*
+	 * Make use of call->buffer to transfer data to caller's userspace
+	 */
+
+	uspace_addr = IPC_GET_ARG3(call->data);
+
+	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, 6 * sizeof(unative_t));
+	call->buffer = buffer;
+
+	ipc_answer(&TASK->kernel_box, call);
+}
+
+static void udebug_receive_mem_read(call_t *call)
+{
+	unative_t uspace_dst;
+	unative_t uspace_src;
+	unsigned size;
+	void *buffer;
+	int rc;
+
+	uspace_dst = IPC_GET_ARG2(call->data);
+	uspace_src = IPC_GET_ARG3(call->data);
+	size = IPC_GET_ARG4(call->data);
+
+	rc = udebug_mem_read(uspace_src, size, &buffer);
+	if (rc < 0) {
+		IPC_SET_RETVAL(call->data, rc);
+		ipc_answer(&TASK->kernel_box, call);
+		return;
+	}
+
+	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_dst);
+	IPC_SET_ARG2(call->data, size);
+	call->buffer = buffer;
+
+	ipc_answer(&TASK->kernel_box, call);
+}
+
+/**
+ * Handle a debug call received on the kernel answerbox.
+ *
+ * This is called by the kbox servicing thread.
+ */
+void udebug_call_receive(call_t *call)
+{
+	int debug_method;
+
+	debug_method = IPC_GET_ARG1(call->data);
+
+	if (debug_method != UDEBUG_M_BEGIN) {
+		/*
+		 * Verify that the sender is this task's debugger.
+		 * Note that this is the only thread that could change
+		 * TASK->debugger. Therefore no locking is necessary
+		 * and the sender can be safely considered valid until
+		 * control exits this function.
+		 */
+		if (TASK->udebug.debugger != call->sender) {
+			IPC_SET_RETVAL(call->data, EINVAL);
+			ipc_answer(&TASK->kernel_box, call);
+			return;	
+		}
+	}
+
+	switch (debug_method) {
+	case UDEBUG_M_BEGIN:
+		udebug_receive_begin(call);
+		break;
+	case UDEBUG_M_END:
+		udebug_receive_end(call);
+		break;
+	case UDEBUG_M_SET_EVMASK:
+		udebug_receive_set_evmask(call);
+		break;
+	case UDEBUG_M_GO:
+		udebug_receive_go(call);
+		break;
+	case UDEBUG_M_STOP:
+		udebug_receive_stop(call);
+		break;
+	case UDEBUG_M_THREAD_READ:
+		udebug_receive_thread_read(call);
+		break;
+	case UDEBUG_M_ARGS_READ:
+		udebug_receive_args_read(call);
+		break;
+	case UDEBUG_M_MEM_READ:
+		udebug_receive_mem_read(call);
+		break;
+	}
+}
+
+/** @}
+ */
Index: kernel/generic/src/udebug/udebug_ops.c
===================================================================
--- kernel/generic/src/udebug/udebug_ops.c	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
+++ kernel/generic/src/udebug/udebug_ops.c	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
@@ -0,0 +1,444 @@
+/*
+ * Copyright (c) 2008 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 generic
+ * @{
+ */
+
+/**
+ * @file
+ * @brief	Udebug operations.
+ */
+ 
+#include <print.h>
+#include <proc/task.h>
+#include <proc/thread.h>
+#include <arch.h>
+#include <errno.h>
+#include <syscall/copy.h>
+#include <ipc/ipc.h>
+#include <udebug/udebug.h>
+#include <udebug/udebug_ops.h>
+
+/**
+ * Prepare a thread for a debugging operation.
+ *
+ * Simply put, return thread t with t->udebug.lock held,
+ * but only if it verifies all conditions.
+ *
+ * Specifically, verifies that thread t exists, is a userspace thread,
+ * and belongs to the current task (TASK). Verifies, that the thread
+ * has (or hasn't) go according to having_go (typically false).
+ * It also locks t->udebug.lock, making sure that t->udebug.debug_active
+ * is true - that the thread is in a valid debugging session.
+ *
+ * With this verified and the t->udebug.lock mutex held, it is ensured
+ * that the thread cannot leave the debugging session, let alone cease
+ * to exist.
+ *
+ * In this function, holding the TASK->udebug.lock mutex prevents the
+ * thread from leaving the debugging session, while relaxing from
+ * the t->lock spinlock to the t->udebug.lock mutex.
+ *
+ * Returns EOK if all went well, or an error code otherwise.
+ */
+static int _thread_op_begin(thread_t *t, bool having_go)
+{
+	task_id_t taskid;
+	ipl_t ipl;
+
+	taskid = TASK->taskid;
+
+	mutex_lock(&TASK->udebug.lock);
+
+	/* thread_exists() must be called with threads_lock held */
+	ipl = interrupts_disable();
+	spinlock_lock(&threads_lock);
+
+	if (!thread_exists(t)) {
+		spinlock_unlock(&threads_lock);
+		interrupts_restore(ipl);
+		mutex_unlock(&TASK->udebug.lock);
+		return ENOENT;
+	}
+
+	/* t->lock is enough to ensure the thread's existence */
+	spinlock_lock(&t->lock);
+	spinlock_unlock(&threads_lock);
+
+	/* Verify that 't' is a userspace thread */
+	if ((t->flags & THREAD_FLAG_USPACE) == 0) {
+		/* It's not, deny its existence */
+		spinlock_unlock(&t->lock);
+		interrupts_restore(ipl);
+		mutex_unlock(&TASK->udebug.lock);
+		return ENOENT;
+	}
+
+	/* Verify debugging state */
+	if (t->udebug.debug_active != true) {
+		/* Not in debugging session or undesired GO state */
+		spinlock_unlock(&t->lock);
+		interrupts_restore(ipl);
+		mutex_unlock(&TASK->udebug.lock);
+		return ENOENT;
+	}
+
+	/*
+	 * Since the thread has debug_active == true, TASK->udebug.lock
+	 * is enough to ensure its existence and that debug_active remains
+	 * true.
+	 */
+	spinlock_unlock(&t->lock);
+	interrupts_restore(ipl);
+
+	/* Only mutex TASK->udebug.lock left */
+	
+	/* Now verify that the thread belongs to the current task */
+	if (t->task != TASK) {
+		/* No such thread belonging this task*/
+		mutex_unlock(&TASK->udebug.lock);
+		return ENOENT;
+	}
+
+	/*
+	 * Now we need to grab the thread's debug lock for synchronization
+	 * of the threads stoppability/stop state.
+	 */
+	mutex_lock(&t->udebug.lock);
+
+	/* The big task mutex is no longer needed */
+	mutex_unlock(&TASK->udebug.lock);
+
+	if (!t->udebug.stop != having_go) {
+		/* Not in debugging session or undesired GO state */
+		mutex_unlock(&t->udebug.lock);
+		return EINVAL;
+	}
+
+	/* Only t->udebug.lock left */
+
+	return EOK;	/* All went well */
+}
+
+
+static void _thread_op_end(thread_t *t)
+{
+	mutex_unlock(&t->udebug.lock);
+}
+
+/**
+ * \return 0 (ok, but not done yet), 1 (done) or negative error code.
+ */
+int udebug_begin(call_t *call)
+{
+	int reply;
+
+	thread_t *t;
+	link_t *cur;
+
+	printf("udebug_begin()\n");
+
+	mutex_lock(&TASK->udebug.lock);
+	printf("debugging task %llu\n", TASK->taskid);
+
+	if (TASK->udebug.dt_state != UDEBUG_TS_INACTIVE) {
+		mutex_unlock(&TASK->udebug.lock);
+		printf("udebug_begin(): busy error\n");
+
+		return EBUSY;
+	}
+
+	TASK->udebug.dt_state = UDEBUG_TS_BEGINNING;
+	TASK->udebug.begin_call = call;
+	TASK->udebug.debugger = call->sender;
+
+	if (TASK->udebug.not_stoppable_count == 0) {
+		TASK->udebug.dt_state = UDEBUG_TS_ACTIVE;
+		TASK->udebug.begin_call = NULL;
+		reply = 1; /* immediate reply */
+	} else {
+		reply = 0; /* no reply */
+	}
+	
+	/* Set udebug.debug_active on all of the task's userspace threads */
+
+	for (cur = TASK->th_head.next; cur != &TASK->th_head; cur = cur->next) {
+		t = list_get_instance(cur, thread_t, th_link);
+
+		mutex_lock(&t->udebug.lock);
+		if ((t->flags & THREAD_FLAG_USPACE) != 0)
+			t->udebug.debug_active = true;
+		mutex_unlock(&t->udebug.lock);
+	}
+
+	mutex_unlock(&TASK->udebug.lock);
+
+	printf("udebug_begin() done (%s)\n", 
+	    reply ? "reply" : "stoppability wait");
+
+	return reply;
+}
+
+int udebug_end(void)
+{
+	int rc;
+
+	printf("udebug_end()\n");
+
+	mutex_lock(&TASK->udebug.lock);
+	printf("task %llu\n", TASK->taskid);
+
+	rc = udebug_task_cleanup(TASK);
+
+	mutex_unlock(&TASK->udebug.lock);
+
+	return rc;
+}
+
+int udebug_set_evmask(udebug_evmask_t mask)
+{
+	printf("udebug_set_mask()\n");
+
+	printf("debugging task %llu\n", TASK->taskid);
+
+	mutex_lock(&TASK->udebug.lock);
+
+	if (TASK->udebug.dt_state != UDEBUG_TS_ACTIVE) {
+		mutex_unlock(&TASK->udebug.lock);
+		printf("udebug_set_mask(): not active debuging session\n");
+
+		return EINVAL;
+	}
+
+	TASK->udebug.evmask = mask;
+
+	mutex_unlock(&TASK->udebug.lock);
+
+	return 0;
+}
+
+
+int udebug_go(thread_t *t, call_t *call)
+{
+	int rc;
+
+//	printf("udebug_go()\n");
+
+	/* On success, this will lock t->udebug.lock */
+	rc = _thread_op_begin(t, false);
+	if (rc != EOK) {
+		return rc;
+	}
+
+	t->udebug.go_call = call;
+	t->udebug.stop = false;
+	t->udebug.cur_event = 0;	/* none */
+
+	/*
+	 * Neither t's lock nor threads_lock may be held during wakeup
+	 */
+	waitq_wakeup(&t->udebug.go_wq, WAKEUP_FIRST);
+
+	_thread_op_end(t);
+
+	return 0;
+}
+
+int udebug_stop(thread_t *t, call_t *call)
+{
+	int rc;
+
+	printf("udebug_stop()\n");
+	mutex_lock(&TASK->udebug.lock);
+
+	/*
+	 * On success, this will lock t->udebug.lock. Note that this makes sure
+	 * the thread is not stopped.
+	 */
+	rc = _thread_op_begin(t, true);
+	if (rc != EOK) {
+		return rc;
+	}
+
+	/* Take GO away from the thread */
+	t->udebug.stop = true;
+
+	if (!t->udebug.stoppable) {
+		/* Answer will be sent when the thread becomes stoppable */
+		_thread_op_end(t);
+		return 0;
+	}
+
+	/*
+	 * Answer GO call
+	 */
+	printf("udebug_stop - answering go call\n");
+
+	/* Make sure nobody takes this call away from us */
+	call = t->udebug.go_call;
+	t->udebug.go_call = NULL;
+
+	IPC_SET_RETVAL(call->data, 0);
+	IPC_SET_ARG1(call->data, UDEBUG_EVENT_STOP);
+	printf("udebug_stop/ipc_answer\n");
+
+	THREAD->udebug.cur_event = UDEBUG_EVENT_STOP;
+
+	_thread_op_end(t);
+
+	ipc_answer(&TASK->answerbox, call);
+	mutex_unlock(&TASK->udebug.lock);
+
+	printf("udebog_stop/done\n");
+	return 0;
+}
+
+int udebug_thread_read(void **buffer, size_t buf_size, size_t *n)
+{
+	thread_t *t;
+	link_t *cur;
+	unative_t tid;
+	unsigned copied_ids;
+	ipl_t ipl;
+	unative_t *id_buffer;
+	int flags;
+	size_t max_ids;
+
+	printf("udebug_thread_read()\n");
+
+	/* Allocate a buffer to hold thread IDs */
+	id_buffer = malloc(buf_size, 0);
+
+	mutex_lock(&TASK->udebug.lock);
+
+	/* Verify task state */
+	if (TASK->udebug.dt_state != UDEBUG_TS_ACTIVE) {
+		mutex_unlock(&TASK->udebug.lock);
+		return EINVAL;
+	}
+
+	ipl = interrupts_disable();
+	spinlock_lock(&TASK->lock);
+	/* Copy down the thread IDs */
+
+	max_ids = buf_size / sizeof(unative_t);
+	copied_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);
+
+		spinlock_lock(&t->lock);
+		flags = t->flags;
+		spinlock_unlock(&t->lock);
+
+		/* Not interested in kernel threads */
+		if ((flags & THREAD_FLAG_USPACE) != 0) {
+			/* Using thread struct pointer as identification hash */
+			tid = (unative_t) t;
+			id_buffer[copied_ids++] = tid;
+		}
+	}
+
+	spinlock_unlock(&TASK->lock);
+	interrupts_restore(ipl);
+
+	mutex_unlock(&TASK->udebug.lock);
+
+	*buffer = id_buffer;
+	*n = copied_ids * sizeof(unative_t);
+
+	return 0;
+}
+
+int udebug_args_read(thread_t *t, void **buffer)
+{
+	int rc;
+	unative_t *arg_buffer;
+
+//	printf("udebug_args_read()\n");
+
+	/* Prepare a buffer to hold the arguments */
+	arg_buffer = malloc(6 * sizeof(unative_t), 0);
+
+	/* On success, this will lock t->udebug.lock */
+	rc = _thread_op_begin(t, false);
+	if (rc != EOK) {
+		return rc;
+	}
+
+	/* Additionally we need to verify that we are inside a syscall */
+	if (t->udebug.cur_event != UDEBUG_EVENT_SYSCALL_B &&
+	    t->udebug.cur_event != UDEBUG_EVENT_SYSCALL_E) {
+		_thread_op_end(t);
+		return EINVAL;
+	}
+
+	/* Copy to a local buffer before releasing the lock */
+	memcpy(arg_buffer, t->udebug.syscall_args, 6 * sizeof(unative_t));
+
+	_thread_op_end(t);
+
+	*buffer = arg_buffer;
+	return 0;
+}
+
+int udebug_mem_read(unative_t uspace_addr, size_t n, void **buffer)
+{
+	void *data_buffer;
+	int rc;
+
+	/* Verify task state */
+	mutex_lock(&TASK->udebug.lock);
+
+	if (TASK->udebug.dt_state != UDEBUG_TS_ACTIVE) {
+		mutex_unlock(&TASK->udebug.lock);
+		return EBUSY;
+	}
+
+	data_buffer = malloc(n, 0);
+
+//	printf("udebug_mem_read: src=%u, size=%u\n", uspace_addr, n);
+
+	/* NOTE: this is not strictly from a syscall... but that shouldn't
+	 * be a problem */
+	rc = copy_from_uspace(data_buffer, (void *)uspace_addr, n);
+	mutex_unlock(&TASK->udebug.lock);
+
+	if (rc != 0) return rc;
+
+	*buffer = data_buffer;
+	return 0;
+}
+
+/** @}
+ */
Index: kernel/kernel.config
===================================================================
--- kernel/kernel.config	(revision 06a195bc5890a55fd6f733495812e905812949fc)
+++ kernel/kernel.config	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
@@ -145,4 +145,6 @@
 ! [ARCH=sparc64] CONFIG_VIRT_IDX_DCACHE (y/n)
 
+# Support for userspace debuggers
+! CONFIG_UDEBUG (n/y)
 
 ## Debugging configuration directives
Index: uspace/Makefile
===================================================================
--- uspace/Makefile	(revision 06a195bc5890a55fd6f733495812e905812949fc)
+++ uspace/Makefile	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
@@ -49,4 +49,5 @@
 	app/tetris \
 	app/tester \
+	app/trace \
 	app/klog \
 	app/init \
Index: uspace/app/trace/Makefile
===================================================================
--- uspace/app/trace/Makefile	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
+++ uspace/app/trace/Makefile	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
@@ -0,0 +1,78 @@
+#
+# Copyright (c) 2005 Martin Decky
+# 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.
+#
+
+## Setup toolchain
+#
+
+LIBC_PREFIX = ../../lib/libc
+SOFTINT_PREFIX = ../../lib/softint
+include $(LIBC_PREFIX)/Makefile.toolchain
+
+CFLAGS += -I../../srv/kbd/include
+
+LIBS = $(LIBC_PREFIX)/libc.a
+
+## Sources
+#
+
+OUTPUT = trace
+SOURCES = trace.c \
+	syscalls.c \
+	ipcp.c \
+	ipc_desc.c \
+	proto.c \
+	errors.c
+
+OBJECTS := $(addsuffix .o,$(basename $(SOURCES)))
+
+.PHONY: all clean depend disasm
+
+all: $(OUTPUT) disasm
+
+-include Makefile.depend
+
+clean:
+	-rm -f $(OUTPUT) $(OBJECTS) $(OUTPUT).map $(OUTPUT).disasm Makefile.depend
+
+depend:
+	$(CC) $(DEFS) $(CFLAGS) -M $(SOURCES) > Makefile.depend
+
+$(OUTPUT): $(OBJECTS) $(LIBS)
+	$(LD) -T $(LIBC_PREFIX)/arch/$(ARCH)/_link.ld $(OBJECTS) $(LIBS) $(LFLAGS) -o $@ -Map $(OUTPUT).map
+
+disasm:
+	$(OBJDUMP) -d $(OUTPUT) >$(OUTPUT).disasm
+
+%.o: %.S
+	$(CC) $(DEFS) $(AFLAGS) $(CFLAGS) -D__ASM__ -c $< -o $@
+
+%.o: %.s
+	$(AS) $(AFLAGS) $< -o $@
+
+%.o: %.c
+	$(CC) $(DEFS) $(CFLAGS) -c $< -o $@
Index: uspace/app/trace/errors.c
===================================================================
--- uspace/app/trace/errors.c	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
+++ uspace/app/trace/errors.c	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2008 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 trace
+ * @{
+ */
+/** @file
+ */
+
+#include <errno.h>
+#include "errors.h"
+
+const err_desc_t err_desc[] = {
+	[-EOK]		= { "EOK",		"No error" },
+	[-ENOENT]	= { "ENOENT",		"No such entry" },
+	[-ENOMEM]	= { "ENOMEM",		"Not enough memory" },
+	[-ELIMIT]	= { "ELIMIT",		"Limit exceeded" },
+	[-EREFUSED]	= { "EREFUSED",		"Connection refused" },
+
+	[-EFORWARD]	= { "EFORWARD",		"Forward error" },
+	[-EPERM]	= { "EPERM",		"Permission denied" },
+	[-EHANGUP]	= { "EHANGUP",		"Answerbox closed connection" },
+	[-EEXISTS]	= { "EEXISTS",		"Entry already exists" },
+	[-EBADMEM]	= { "EBADMEM",		"Bad memory pointer" },
+
+	[-ENOTSUP]	= { "ENOTSUP",		"Not supported" },
+	[-EADDRNOTAVAIL] = { "EADDRNOTAVAIL", 	"Address not available." },
+	[-ETIMEOUT]	= { "ETIMEOUT",		"Timeout expired" },
+	[-EINVAL]	= { "EINVAL",		"Invalid value" },
+	[-EBUSY]	= { "EBUSY",		"Resource is busy" },
+
+	[-EOVERFLOW]	= { "EOVERFLOW",	"The result does not fit its size." }
+};
+
+/** @}
+ */
Index: uspace/app/trace/errors.h
===================================================================
--- uspace/app/trace/errors.h	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
+++ uspace/app/trace/errors.h	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2008 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 trace
+ * @{
+ */
+/** @file
+ */
+
+#ifndef ERRORS_H_
+#define ERRORS_H_
+
+typedef struct {
+	char *name;	/**< Error value name (Exx) */
+	char *desc;	/**< Error description */
+} err_desc_t;
+
+extern const err_desc_t err_desc[];
+
+#endif
+
+/** @}
+ */
Index: uspace/app/trace/ipc_desc.c
===================================================================
--- uspace/app/trace/ipc_desc.c	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
+++ uspace/app/trace/ipc_desc.c	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2008 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 trace
+ * @{
+ */
+/** @file
+ */
+
+#include <stdlib.h>
+#include <ipc/ipc.h>
+#include "ipc_desc.h"
+
+ipc_m_desc_t ipc_methods[] = {
+	/* System methods */
+	{ IPC_M_CONNECT_TO_ME,	"CONNECT_TO_ME" },
+	{ IPC_M_CONNECT_ME_TO,	"CONNECT_ME_TO" },
+	{ IPC_M_PHONE_HUNGUP,	"PHONE_HUNGUP" },
+	{ IPC_M_SHARE_OUT,	"SHARE_OUT" },
+	{ IPC_M_SHARE_IN,	"SHARE_IN" },
+	{ IPC_M_DATA_WRITE,	"DATA_WRITE" },
+	{ IPC_M_DATA_READ,	"DATA_READ" },
+	{ IPC_M_DEBUG_ALL,	"DEBUG_ALL" },
+
+	/* Well-known methods */
+	{ IPC_M_PING,		"PING" },
+
+	/* Terminating entry */
+	{ 0, NULL }
+};
+
+/** @}
+ */
Index: uspace/app/trace/ipc_desc.h
===================================================================
--- uspace/app/trace/ipc_desc.h	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
+++ uspace/app/trace/ipc_desc.h	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2008 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 trace
+ * @{
+ */
+/** @file
+ */
+
+#ifndef IPC_DESC_H_
+#define IPC_DESC_H_
+
+typedef struct {
+	int number;
+	char *name;
+} ipc_m_desc_t;
+
+extern ipc_m_desc_t ipc_methods[];
+
+#endif
+
+/** @}
+ */
Index: uspace/app/trace/ipcp.c
===================================================================
--- uspace/app/trace/ipcp.c	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
+++ uspace/app/trace/ipcp.c	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
@@ -0,0 +1,268 @@
+/*
+ * Copyright (c) 2008 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 trace
+ * @{
+ */
+/** @file
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <libadt/hash_table.h>
+
+#include "ipc_desc.h"
+#include "proto.h"
+#include "ipcp.h"
+
+#define IPCP_CALLID_SYNC 0
+
+typedef struct {
+	int phone_hash;
+	ipc_call_t question;
+
+	int call_hash;
+
+	link_t link;
+} pending_call_t;
+
+typedef struct {
+	int server;
+	proto_t *proto;
+} connection_t;
+
+#define MAX_PHONE 64
+connection_t connections[MAX_PHONE];
+int have_conn[MAX_PHONE];
+
+#define PCALL_TABLE_CHAINS 32
+hash_table_t pending_calls;
+
+static hash_index_t pending_call_hash(unsigned long key[]);
+static int pending_call_compare(unsigned long key[], hash_count_t keys,
+    link_t *item);
+static void pending_call_remove_callback(link_t *item);
+
+hash_table_operations_t pending_call_ops = {
+	.hash = pending_call_hash,
+	.compare = pending_call_compare,
+	.remove_callback = pending_call_remove_callback
+};
+
+
+static hash_index_t pending_call_hash(unsigned long key[])
+{
+//	printf("pending_call_hash\n");
+	return key[0] % PCALL_TABLE_CHAINS;
+}
+
+static int pending_call_compare(unsigned long key[], hash_count_t keys,
+    link_t *item)
+{
+	pending_call_t *hs;
+
+//	printf("pending_call_compare\n");
+	hs = hash_table_get_instance(item, pending_call_t, link);
+
+	return key[0] == hs->call_hash;
+}
+
+static void pending_call_remove_callback(link_t *item)
+{
+//	printf("pending_call_remove_callback\n");
+}
+
+
+void ipcp_connection_set(int phone, int server, proto_t *proto)
+{
+	if (phone <0 || phone >= MAX_PHONE) return;
+	connections[phone].server = server;
+	connections[phone].proto = proto;
+	have_conn[phone] = 1;
+}
+
+void ipcp_connection_clear(int phone)
+{
+	have_conn[phone] = 0;
+	connections[phone].server = 0;
+	connections[phone].proto = NULL;
+}
+
+static void ipc_m_print(proto_t *proto, ipcarg_t method)
+{
+	ipc_m_desc_t *desc;
+	oper_t *oper;
+
+	/* FIXME: too slow */
+	desc = ipc_methods;
+	while (desc->number != 0) {
+		if (desc->number == method) {
+			printf("%s (%d)", desc->name, method);
+			return;
+		}
+
+		++desc;
+	}
+
+	if (proto != NULL) {
+		oper = proto_get_oper(proto, method);
+		if (oper != NULL) {
+			printf("%s (%d)", oper->name, method);
+			return;
+		}
+	}
+
+	printf("%d", method);
+}
+
+void ipcp_init(void)
+{
+	hash_table_create(&pending_calls, PCALL_TABLE_CHAINS, 1, &pending_call_ops);
+}
+
+void ipcp_cleanup(void)
+{
+	hash_table_destroy(&pending_calls);
+}
+
+void ipcp_call_out(int phone, ipc_call_t *call, ipc_callid_t hash)
+{
+	pending_call_t *pcall;
+	proto_t *proto;
+	unsigned long key[1];
+
+	if (have_conn[phone]) proto = connections[phone].proto;
+	else proto = NULL;
+
+//	printf("ipcp_call_out()\n");
+	printf("call id: 0x%x, phone: %d, proto: %s, method: ", hash, phone,
+		(proto ? proto->name : "n/a"));
+	ipc_m_print(proto, IPC_GET_METHOD(*call));
+	printf(" args: (%u, %u, %u, %u, %u)\n",
+	    IPC_GET_ARG1(*call),
+	    IPC_GET_ARG2(*call),
+	    IPC_GET_ARG3(*call),
+	    IPC_GET_ARG4(*call),
+	    IPC_GET_ARG5(*call)
+	);
+
+	/* Store call in hash table for response matching */
+
+	pcall = malloc(sizeof(pending_call_t));
+	pcall->phone_hash = phone;
+	pcall->question = *call;
+	pcall->call_hash = hash;
+
+	key[0] = hash;
+
+	hash_table_insert(&pending_calls, key, &pcall->link);
+}
+
+static void parse_answer(pending_call_t *pcall, ipc_call_t *answer)
+{
+	int phone;
+	ipcarg_t method;
+	ipcarg_t service;
+	int retval;
+	static proto_t proto_unknown = { .name = "unknown" };
+	proto_t *proto;
+	int cphone;
+
+//	printf("parse_answer\n");
+
+	phone = pcall->phone_hash;
+	method = IPC_GET_METHOD(pcall->question);
+	retval = IPC_GET_RETVAL(*answer);
+	printf("phone=%d, method=%d, retval=%d\n",
+		phone, method, retval);
+
+	if (phone == 0 && method == IPC_M_CONNECT_ME_TO && retval == 0) {
+		/* Connected to a service (through NS) */
+		service = IPC_GET_ARG1(pcall->question);
+		proto = proto_get_by_srv(service);
+		if (proto == NULL) proto = &proto_unknown;
+
+		cphone = IPC_GET_ARG5(*answer);
+		printf("registering connection (phone %d, protocol: %s)\n", cphone,
+			proto->name);
+		ipcp_connection_set(cphone, 0, proto);
+	}
+}
+
+void ipcp_call_in(ipc_call_t *call, ipc_callid_t hash)
+{
+	link_t *item;
+	pending_call_t *pcall;
+	unsigned long key[1];
+
+//	printf("ipcp_call_in()\n");
+/*	printf("phone: %d, method: ", call->in_phone_hash);
+	ipc_m_print(IPC_GET_METHOD(*call));
+	printf(" args: (%u, %u, %u, %u, %u)\n",
+	    IPC_GET_ARG1(*call),
+	    IPC_GET_ARG2(*call),
+	    IPC_GET_ARG3(*call),
+	    IPC_GET_ARG4(*call),
+	    IPC_GET_ARG5(*call)
+	);*/
+
+	if ((hash & IPC_CALLID_ANSWERED) == 0 && hash != IPCP_CALLID_SYNC) {
+		/* Not a response */
+		printf("Not a response (hash %d)\n", hash);
+		return;
+	}
+
+	hash = hash & ~IPC_CALLID_ANSWERED;
+	key[0] = hash;
+
+	item = hash_table_find(&pending_calls, key);
+	if (item == NULL) return; // No matching question found
+	
+	pcall = hash_table_get_instance(item, pending_call_t, link);
+
+	printf("response matched to question\n");
+	hash_table_remove(&pending_calls, key, 1);
+
+	parse_answer(pcall, call);
+	free(pcall);
+}
+
+void ipcp_call_sync(int phone, ipc_call_t *call, ipc_call_t *answer)
+{
+	ipcp_call_out(phone, call, IPCP_CALLID_SYNC);
+	ipcp_call_in(answer, IPCP_CALLID_SYNC);
+}
+
+void ipcp_hangup(int phone, int rc)
+{
+	printf("hangup phone %d -> %d\n", phone, rc);
+	ipcp_connection_clear(phone);
+}
+
+/** @}
+ */
Index: uspace/app/trace/ipcp.h
===================================================================
--- uspace/app/trace/ipcp.h	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
+++ uspace/app/trace/ipcp.h	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2008 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 trace
+ * @{
+ */
+/** @file
+ */
+
+#ifndef IPCP_H_
+#define IPCP_H_
+
+#include <ipc/ipc.h>
+#include "proto.h"
+
+void ipcp_init(void);
+void ipcp_cleanup(void);
+
+void ipcp_call_out(int phone, ipc_call_t *call, ipc_callid_t hash);
+void ipcp_call_sync(int phone, ipc_call_t *call, ipc_call_t *answer);
+void ipcp_call_in(ipc_call_t *call, ipc_callid_t hash);
+void ipcp_hangup(int phone, int rc);
+
+void ipcp_connection_set(int phone, int server, proto_t *proto);
+void ipcp_connection_clear(int phone);
+
+#endif
+
+/** @}
+ */
Index: uspace/app/trace/proto.c
===================================================================
--- uspace/app/trace/proto.c	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
+++ uspace/app/trace/proto.c	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2008 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 trace
+ * @{
+ */
+/** @file
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ipc/ipc.h>
+#include <libadt/hash_table.h>
+
+#include "proto.h"
+
+#define SRV_PROTO_TABLE_CHAINS 32
+#define METHOD_OPER_TABLE_CHAINS 32
+
+hash_table_t srv_proto;
+
+typedef struct {
+	int srv;
+	proto_t *proto;
+	link_t link;
+} srv_proto_t;
+
+typedef struct {
+	ipcarg_t method;
+	oper_t *oper;
+	link_t link;
+} method_oper_t;
+
+static hash_index_t srv_proto_hash(unsigned long key[]);
+static int srv_proto_compare(unsigned long key[], hash_count_t keys,
+    link_t *item);
+static void srv_proto_remove_callback(link_t *item);
+
+hash_table_operations_t srv_proto_ops = {
+	.hash = srv_proto_hash,
+	.compare = srv_proto_compare,
+	.remove_callback = srv_proto_remove_callback
+};
+
+static hash_index_t method_oper_hash(unsigned long key[]);
+static int method_oper_compare(unsigned long key[], hash_count_t keys,
+    link_t *item);
+static void method_oper_remove_callback(link_t *item);
+
+hash_table_operations_t method_oper_ops = {
+	.hash = method_oper_hash,
+	.compare = method_oper_compare,
+	.remove_callback = method_oper_remove_callback
+};
+
+static hash_index_t srv_proto_hash(unsigned long key[])
+{
+	return key[0] % SRV_PROTO_TABLE_CHAINS;
+}
+
+static int srv_proto_compare(unsigned long key[], hash_count_t keys,
+    link_t *item)
+{
+	srv_proto_t *sp;
+
+	sp = hash_table_get_instance(item, srv_proto_t, link);
+
+	return key[0] == sp->srv;
+}
+
+static void srv_proto_remove_callback(link_t *item)
+{
+}
+
+static hash_index_t method_oper_hash(unsigned long key[])
+{
+	return key[0] % METHOD_OPER_TABLE_CHAINS;
+}
+
+static int method_oper_compare(unsigned long key[], hash_count_t keys,
+    link_t *item)
+{
+	method_oper_t *mo;
+
+	mo = hash_table_get_instance(item, method_oper_t, link);
+
+	return key[0] == mo->method;
+}
+
+static void method_oper_remove_callback(link_t *item)
+{
+}
+
+
+void proto_init(void)
+{
+	hash_table_create(&srv_proto, SRV_PROTO_TABLE_CHAINS, 1,
+	    &srv_proto_ops);
+}
+
+void proto_cleanup(void)
+{
+	hash_table_destroy(&srv_proto);
+}
+
+void proto_register(int srv, proto_t *proto)
+{
+	srv_proto_t *sp;
+	unsigned long key;
+
+	sp = malloc(sizeof(srv_proto_t));
+	sp->srv = srv;
+	sp->proto = proto;
+	key = srv;
+
+	hash_table_insert(&srv_proto, &key, &sp->link);
+}
+
+proto_t *proto_get_by_srv(int srv)
+{
+	unsigned long key;
+	link_t *item;
+	srv_proto_t *sp;
+
+	key = srv;
+	item = hash_table_find(&srv_proto, &key);
+	if (item == NULL) return NULL;
+
+	sp = hash_table_get_instance(item, srv_proto_t, link);
+	return sp->proto;
+}
+
+static void proto_struct_init(proto_t *proto, char *name)
+{
+	proto->name = name;
+	hash_table_create(&proto->method_oper, SRV_PROTO_TABLE_CHAINS, 1,
+	    &method_oper_ops);
+}
+
+proto_t *proto_new(char *name)
+{
+	proto_t *p;
+
+	p = malloc(sizeof(proto_t));
+	proto_struct_init(p, name);
+
+	return p;
+}
+
+void proto_add_oper(proto_t *proto, int method, oper_t *oper)
+{
+	method_oper_t *mo;
+	unsigned long key;
+
+	mo = malloc(sizeof(method_oper_t));
+	mo->method = method;
+	mo->oper = oper;
+	key = method;
+
+	hash_table_insert(&proto->method_oper, &key, &mo->link);	
+}
+
+oper_t *proto_get_oper(proto_t *proto, int method)
+{
+	unsigned long key;
+	link_t *item;
+	method_oper_t *mo;
+
+	key = method;
+	item = hash_table_find(&proto->method_oper, &key);
+	if (item == NULL) return NULL;
+
+	mo = hash_table_get_instance(item, method_oper_t, link);
+	return mo->oper;
+}
+
+static void oper_struct_init(oper_t *oper, char *name)
+{
+	oper->name = name;
+}
+
+oper_t *oper_new(char *name)
+{
+	oper_t *o;
+
+	o = malloc(sizeof(oper_t));
+	oper_struct_init(o, name);
+
+	return o;
+}
+
+/** @}
+ */
Index: uspace/app/trace/proto.h
===================================================================
--- uspace/app/trace/proto.h	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
+++ uspace/app/trace/proto.h	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2008 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 trace
+ * @{
+ */
+/** @file
+ */
+
+#ifndef PROTO_H_
+#define PROTO_H_
+
+#include <libadt/hash_table.h>
+
+typedef struct {
+	char *name;
+} oper_t;
+
+typedef struct {
+	/** Protocol name */
+	char *name;
+
+	/** Maps method number to operation */
+	hash_table_t method_oper;
+} proto_t;
+
+/* Maps service number to protocol */
+extern hash_table_t srv_proto;
+
+void proto_init(void);
+void proto_cleanup(void);
+
+void proto_register(int srv, proto_t *proto);
+proto_t *proto_get_by_srv(int srv);
+proto_t *proto_new(char *name);
+void proto_add_oper(proto_t *proto, int method, oper_t *oper);
+oper_t *proto_get_oper(proto_t *proto, int method);
+
+oper_t *oper_new(char *name);
+
+
+#endif
+
+/** @}
+ */
Index: uspace/app/trace/syscalls.c
===================================================================
--- uspace/app/trace/syscalls.c	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
+++ uspace/app/trace/syscalls.c	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2008 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 trace
+ * @{
+ */
+/** @file
+ */
+
+#include <kernel/syscall/syscall.h>
+#include "syscalls.h"
+
+const sc_desc_t syscall_desc[] = {
+    [SYS_KLOG] ={ "klog",				3,	RV_INT_ERRNO },
+    [SYS_TLS_SET] = { "tls_set",			1,	RV_ERRNO },
+    [SYS_THREAD_CREATE] = { "thread_create",		3,	RV_ERRNO },
+    [SYS_THREAD_EXIT] = { "thread_exit",		1,	RV_ERRNO },
+    [SYS_THREAD_GET_ID] = { "thread_get_id",		1,	RV_ERRNO },
+
+    [SYS_TASK_GET_ID] = { "task_get_id",		1,	RV_ERRNO },
+    [SYS_FUTEX_SLEEP] = { "futex_sleep_timeout",	3,	RV_ERRNO },
+    [SYS_FUTEX_WAKEUP] = { "futex_wakeup",		1,	RV_ERRNO },
+
+    [SYS_AS_AREA_CREATE] = { "as_area_create",		3,	RV_ERRNO },
+    [SYS_AS_AREA_RESIZE] = { "as_area_resize",		3,	RV_ERRNO },
+    [SYS_AS_AREA_DESTROY] = { "as_area_destroy",	1,	RV_ERRNO },
+
+    [SYS_IPC_CALL_SYNC_FAST] = { "ipc_call_sync_fast",	6,	RV_ERRNO },
+    [SYS_IPC_CALL_SYNC_SLOW] = { "ipc_call_sync_slow",	3,	RV_ERRNO },
+    [SYS_IPC_CALL_ASYNC_FAST] = { "ipc_call_async_fast", 6,	RV_HASH },
+    [SYS_IPC_CALL_ASYNC_SLOW] = { "ipc_call_async_slow", 2,	RV_HASH },
+
+    [SYS_IPC_ANSWER_FAST] = { "ipc_answer_fast",	6,	RV_ERRNO },
+    [SYS_IPC_ANSWER_SLOW] = { "ipc_answer_slow",	2,	RV_ERRNO },
+    [SYS_IPC_FORWARD_FAST] = { "ipc_forward_fast",	6,	RV_ERRNO },
+    [SYS_IPC_WAIT] = { "ipc_wait_for_call",		3,	RV_HASH },
+    [SYS_IPC_HANGUP] = { "ipc_hangup",			1,	RV_ERRNO },
+    [SYS_IPC_REGISTER_IRQ] = { "ipc_register_irq",	4,	RV_ERRNO },
+    [SYS_IPC_UNREGISTER_IRQ] = { "ipc_unregister_irq",	2,	RV_ERRNO },
+
+    [SYS_CAP_GRANT] = { "cap_grant",			2,	RV_ERRNO },
+    [SYS_CAP_REVOKE] = { "cap_revoke",			2,	RV_ERRNO },
+    [SYS_PHYSMEM_MAP] = { "physmem_map",		4,	RV_ERRNO },
+    [SYS_IOSPACE_ENABLE] = { "iospace_enable",		1,	RV_ERRNO },
+    [SYS_PREEMPT_CONTROL] = { "preempt_control",	1,	RV_ERRNO },
+
+    [SYS_SYSINFO_VALID] = { "sysinfo_valid",		2,	RV_HASH },
+    [SYS_SYSINFO_VALUE] = { "sysinfo_value",		2,	RV_HASH },
+    [SYS_DEBUG_ENABLE_CONSOLE] = { "debug_enable_console", 0,	RV_ERRNO },
+    [SYS_IPC_CONNECT_KBOX] = { "ipc_connect_kbox",	1,	RV_ERRNO }
+};
+
+/** @}
+ */
Index: uspace/app/trace/syscalls.h
===================================================================
--- uspace/app/trace/syscalls.h	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
+++ uspace/app/trace/syscalls.h	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2008 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 trace
+ * @{
+ */
+/** @file
+ */
+
+#ifndef SYSCALLS_H_
+#define SYSCALLS_H_
+
+typedef enum {
+	RV_INTEGER,
+	RV_HASH,
+	RV_ERRNO,
+	RV_INT_ERRNO
+} rv_type_t;
+
+typedef struct {
+	char *name;
+	int n_args;
+	rv_type_t rv_type;
+} sc_desc_t;
+
+extern const sc_desc_t syscall_desc[];
+
+#endif
+
+/** @}
+ */
Index: uspace/app/trace/trace.c
===================================================================
--- uspace/app/trace/trace.c	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
+++ uspace/app/trace/trace.c	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
@@ -0,0 +1,542 @@
+/*
+ * Copyright (c) 2008 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 trace
+ * @{
+ */
+/** @file
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syscall.h>
+#include <ipc/ipc.h>
+#include <fibril.h>
+#include <errno.h>
+#include <udebug.h>
+#include <async.h>
+
+// Temporary: service and method names
+#include "proto.h"
+#include <ipc/services.h>
+#include "../../srv/vfs/vfs.h"
+#include "../../srv/console/console.h"
+
+#include "syscalls.h"
+#include "ipcp.h"
+#include "errors.h"
+
+#define THBUF_SIZE 64
+unsigned thread_hash_buf[THBUF_SIZE];
+unsigned n_threads;
+
+int next_thread_id;
+
+int phoneid;
+int abort_trace;
+
+unsigned thash;
+volatile int paused;
+
+void thread_trace_start(unsigned thread_hash);
+
+static proto_t *proto_console;
+
+static int task_connect(task_id_t task_id)
+{
+	int rc;
+
+	printf("ipc_connect_task(%lld)... ", task_id);
+	rc = ipc_connect_kbox(task_id);
+	printf("-> %d\n", rc);
+	phoneid = rc;
+	if (rc < 0) return rc;
+
+	printf("udebug_begin()... ");
+	rc = udebug_begin(phoneid);
+	printf("-> %d\n", rc);
+	if (rc < 0) return rc;
+
+	printf("udebug_set_evmask(0x%x)... ", UDEBUG_EM_ALL);
+	rc = udebug_set_evmask(phoneid, UDEBUG_EM_ALL);
+	printf("-> %d\n", rc);
+	if (rc < 0) return rc;
+
+	return 0;
+}
+
+static int get_thread_list(void)
+{
+	int rc;
+	size_t tb_copied;
+	size_t tb_needed;
+	int i;
+
+	printf("send IPC_M_DEBUG_THREAD_READ message\n");
+	rc = udebug_thread_read(phoneid, thread_hash_buf,
+		THBUF_SIZE*sizeof(unsigned), &tb_copied, &tb_needed);
+	printf("-> %d\n", rc);
+	if (rc < 0) return rc;
+
+	n_threads = tb_copied / sizeof(unsigned);
+
+	printf("thread IDs:");
+	for (i=0; i<n_threads; i++) {
+		printf(" %u", thread_hash_buf[i]);
+	}
+	printf("\ntotal of %u threads\n", tb_needed/sizeof(unsigned));
+
+	return 0;
+}
+
+static void print_sc_retval(int retval, rv_type_t rv_type)
+{
+	printf (" -> ");
+	if (rv_type == RV_INTEGER) {
+		printf("%d", retval);
+	} else if (rv_type == RV_HASH) {
+		printf("0x%08x", retval);
+	} else if (rv_type == RV_ERRNO) {
+		if (retval >= -15 && retval <= 0) {
+			printf("%d %s (%s)", retval,
+			    err_desc[retval].name,
+			    err_desc[retval].desc);
+		} else {
+			printf("%d", retval);
+		}
+	} else if (rv_type == RV_INT_ERRNO) {
+		if (retval >= -15 && retval < 0) {
+			printf("%d %s (%s)", retval,
+			    err_desc[retval].name,
+			    err_desc[retval].desc);
+		} else {
+			printf("%d", retval);
+		}
+	}
+	putchar('\n');
+}
+
+static void print_sc_args(unsigned *sc_args, int n)
+{
+	int i;
+
+	putchar('(');
+	if (n > 0) printf("%d", sc_args[0]);
+	for (i=1; i<n; i++) {
+		printf(", %d", sc_args[i]);
+	}
+	putchar(')');
+}
+
+static void sc_ipc_call_async_fast(unsigned *sc_args, int sc_rc)
+{
+	ipc_call_t call;
+	int phoneid;
+	
+	if (sc_rc == IPC_CALLRET_FATAL || sc_rc == IPC_CALLRET_TEMPORARY)
+		return;
+
+	phoneid = sc_args[0];
+
+	IPC_SET_METHOD(call, sc_args[1]);
+	IPC_SET_ARG1(call, sc_args[2]);
+	IPC_SET_ARG2(call, sc_args[3]);
+	IPC_SET_ARG3(call, sc_args[4]);
+	IPC_SET_ARG4(call, sc_args[5]);
+	IPC_SET_ARG5(call, 0);
+
+	ipcp_call_out(phoneid, &call, sc_rc);
+}
+
+static void sc_ipc_call_async_slow(unsigned *sc_args, int sc_rc)
+{
+	ipc_call_t call;
+	int rc;
+
+	if (sc_rc == IPC_CALLRET_FATAL || sc_rc == IPC_CALLRET_TEMPORARY)
+		return;
+
+	memset(&call, 0, sizeof(call));
+	rc = udebug_mem_read(phoneid, &call.args, sc_args[1], sizeof(call.args));
+
+	if (rc >= 0) {
+		ipcp_call_out(sc_args[0], &call, sc_rc);
+	}
+}
+
+static void sc_ipc_call_sync_fast(unsigned *sc_args)
+{
+	ipc_call_t question, reply;
+	int rc;
+	int phoneidx;
+
+//	printf("sc_ipc_call_sync_fast()\n");
+	phoneidx = sc_args[0];
+
+	IPC_SET_METHOD(question, sc_args[1]);
+	IPC_SET_ARG1(question, sc_args[2]);
+	IPC_SET_ARG2(question, sc_args[3]);
+	IPC_SET_ARG3(question, sc_args[4]);
+	IPC_SET_ARG4(question, 0);
+	IPC_SET_ARG5(question, 0);
+
+//	printf("memset\n");
+	memset(&reply, 0, sizeof(reply));
+//	printf("udebug_mem_read(phone=%d, buffer_ptr=%u, src_addr=%d, n=%d\n",
+//		phoneid, &reply.args, sc_args[5], sizeof(reply.args));
+	rc = udebug_mem_read(phoneid, &reply.args, sc_args[5], sizeof(reply.args));
+//	printf("dmr->%d\n", rc);
+	if (rc < 0) return;
+
+//	printf("call ipc_call_sync\n");
+	ipcp_call_sync(phoneidx, &question, &reply);
+}
+
+static void sc_ipc_call_sync_slow(unsigned *sc_args)
+{
+	ipc_call_t question, reply;
+	int rc;
+
+	memset(&question, 0, sizeof(question));
+	rc = udebug_mem_read(phoneid, &question.args, sc_args[1], sizeof(question.args));
+	printf("dmr->%d\n", rc);
+	if (rc < 0) return;
+
+	memset(&reply, 0, sizeof(reply));
+	rc = udebug_mem_read(phoneid, &reply.args, sc_args[2], sizeof(reply.args));
+	printf("dmr->%d\n", rc);
+	if (rc < 0) return;
+
+	ipcp_call_sync(sc_args[0], &question, &reply);
+}
+
+static void sc_ipc_wait(unsigned *sc_args, int sc_rc)
+{
+	ipc_call_t call;
+	int rc;
+
+	if (sc_rc == 0) return;
+
+	memset(&call, 0, sizeof(call));
+	rc = udebug_mem_read(phoneid, &call, sc_args[0], sizeof(call));
+//	printf("udebug_mem_read(phone %d, dest %d, app-mem src %d, size %d -> %d\n",
+//		phoneid, (int)&call, sc_args[0], sizeof(call), rc);
+
+	if (rc >= 0) {
+		ipcp_call_in(&call, sc_rc);
+	}
+}
+
+static void event_syscall_b(unsigned thread_id, unsigned thread_hash,  unsigned sc_id, int sc_rc)
+{
+	unsigned sc_args[6];
+	int rc;
+
+	/* Read syscall arguments */
+	rc = udebug_args_read(phoneid, thread_hash, sc_args);
+
+	async_serialize_start();
+
+//	printf("[%d] ", thread_id);
+
+	if (rc < 0) {
+		printf("error\n");
+		async_serialize_end();
+		return;
+	}
+
+	/* Print syscall name, id and arguments */
+	printf("%s", syscall_desc[sc_id].name);
+	print_sc_args(sc_args, syscall_desc[sc_id].n_args);
+
+	async_serialize_end();
+}
+
+static void event_syscall_e(unsigned thread_id, unsigned thread_hash,  unsigned sc_id, int sc_rc)
+{
+	unsigned sc_args[6];
+	int rv_type;
+	int rc;
+
+	/* Read syscall arguments */
+	rc = udebug_args_read(phoneid, thread_hash, sc_args);
+
+	async_serialize_start();
+
+//	printf("[%d] ", thread_id);
+
+	if (rc < 0) {
+		printf("error\n");
+		async_serialize_end();
+		return;
+	}
+
+	rv_type = syscall_desc[sc_id].rv_type;
+	print_sc_retval(sc_rc, rv_type);
+
+	switch (sc_id) {
+	case SYS_IPC_CALL_ASYNC_FAST:
+		sc_ipc_call_async_fast(sc_args, sc_rc);
+		break;
+	case SYS_IPC_CALL_ASYNC_SLOW:
+		sc_ipc_call_async_slow(sc_args, sc_rc);
+		break;
+	case SYS_IPC_CALL_SYNC_FAST:
+		sc_ipc_call_sync_fast(sc_args);
+		break;
+	case SYS_IPC_CALL_SYNC_SLOW:
+		sc_ipc_call_sync_slow(sc_args);
+		break;
+	case SYS_IPC_WAIT:
+		sc_ipc_wait(sc_args, sc_rc);
+		break;
+	default:
+		break;
+	}
+
+	async_serialize_end();
+}
+
+static void event_thread_b(unsigned hash)
+{
+	async_serialize_start();
+	printf("new thread, hash 0x%x\n", hash);
+	async_serialize_end();
+
+	thread_trace_start(hash);
+}
+
+static int trace_loop(void *thread_hash_arg)
+{
+	int rc;
+	unsigned ev_type;
+	unsigned thread_hash;
+	unsigned thread_id;
+	unsigned val0, val1;
+
+	thread_hash = (unsigned)thread_hash_arg;
+	thread_id = next_thread_id++;
+
+	printf("trace_loop(%d)\n", thread_id);	
+
+	while (!abort_trace) {
+
+		/* Run thread until an event occurs */
+		rc = udebug_go(phoneid, thread_hash,
+		    &ev_type, &val0, &val1);
+
+//		printf("rc = %d, ev_type=%d\n", rc, ev_type);
+		if (ev_type == UDEBUG_EVENT_FINISHED) {
+			printf("thread %u debugging finished\n", thread_id);
+			break;
+		}
+
+		if (rc >= 0) {
+			switch (ev_type) {
+			case UDEBUG_EVENT_SYSCALL_B:
+				event_syscall_b(thread_id, thread_hash, val0, (int)val1);
+				break;
+			case UDEBUG_EVENT_SYSCALL_E:
+				event_syscall_e(thread_id, thread_hash, val0, (int)val1);
+				break;
+			case UDEBUG_EVENT_STOP:
+				printf("stop event\n");
+				printf("waiting for resume\n");
+				while (paused) {
+					usleep(1000000);
+					fibril_yield();
+					printf(".");
+				}
+				printf("resumed\n");
+				break;
+			case UDEBUG_EVENT_THREAD_B:
+				event_thread_b(val0);
+				break;
+			case UDEBUG_EVENT_THREAD_E:
+				printf("thread 0x%x exited\n", val0);
+				abort_trace = 1;
+				break;
+			default:
+				printf("unknown event type %d\n", ev_type);
+				break;
+			}
+		}
+
+	}
+
+	printf("trace_loop(%d) exiting\n", thread_id);
+	return 0;
+}
+
+void thread_trace_start(unsigned thread_hash)
+{
+	fid_t fid;
+
+	thash = thread_hash;
+
+	fid = fibril_create(trace_loop, (void *)thread_hash);
+	if (fid == 0) {
+		printf("Warning: Failed creating fibril\n");
+	}
+	fibril_add_ready(fid);
+}
+
+static void trace_active_task(task_id_t task_id)
+{
+	int i;
+	int rc;
+	int c;
+
+	printf("Syscall Tracer\n");
+
+	rc = task_connect(task_id);
+	if (rc < 0) {
+		printf("Failed to connect to task %lld\n", task_id);
+		return;
+	}
+
+	printf("Connected to task %lld\n", task_id);
+
+	ipcp_init();
+	ipcp_connection_set(1, 0, proto_console);
+
+	rc = get_thread_list();
+	if (rc < 0) {
+		printf("Failed to get thread list (error %d)\n", rc);
+		return;
+	}
+
+	abort_trace = 0;
+
+	for (i = 0; i < n_threads; i++) {
+		thread_trace_start(thread_hash_buf[i]);
+	}
+
+	while(1) {
+		c = getchar();
+		if (c == 'q') break;
+		if (c == 'p') {
+			paused = 1;
+			rc = udebug_stop(phoneid, thash);
+			printf("stop -> %d\n", rc);
+		}
+		if (c == 'r') {
+			paused = 0;
+		}
+	}
+
+	printf("terminate debugging session...\n");
+	abort_trace = 1;
+	udebug_end(phoneid);
+	ipc_hangup(phoneid);
+
+	ipcp_cleanup();
+
+	printf("done\n");
+	return;
+}
+
+static void main_init(void)
+{
+	proto_t *p;
+	oper_t *o;
+
+	next_thread_id = 1;
+	paused = 0;
+
+	proto_init();
+
+	p = proto_new("vfs");
+	o = oper_new("read");
+	proto_add_oper(p, VFS_READ, o);
+	o = oper_new("write");
+	proto_add_oper(p, VFS_WRITE, o);
+	o = oper_new("truncate");
+	proto_add_oper(p, VFS_TRUNCATE, o);
+	o = oper_new("mount");
+	proto_add_oper(p, VFS_MOUNT, o);
+	o = oper_new("unmount");
+	proto_add_oper(p, VFS_UNMOUNT, o);
+
+	proto_register(SERVICE_VFS, p);
+
+	p = proto_new("console");
+	o = oper_new("getchar");
+	proto_add_oper(p, CONSOLE_GETCHAR, o);
+	o = oper_new("putchar");
+	proto_add_oper(p, CONSOLE_PUTCHAR, o);
+	o = oper_new("clear");
+	proto_add_oper(p, CONSOLE_CLEAR, o);
+	o = oper_new("goto");
+	proto_add_oper(p, CONSOLE_GOTO, o);
+	o = oper_new("getsize");
+	proto_add_oper(p, CONSOLE_GETSIZE, o);
+	o = oper_new("flush");
+	proto_add_oper(p, CONSOLE_FLUSH, o);
+	o = oper_new("set_style");
+	proto_add_oper(p, CONSOLE_SET_STYLE, o);
+	o = oper_new("cursor_visibility");
+	proto_add_oper(p, CONSOLE_CURSOR_VISIBILITY, o);
+	o = oper_new("flush");
+	proto_add_oper(p, CONSOLE_FLUSH, o);
+
+	proto_console = p;
+	proto_register(SERVICE_CONSOLE, p);
+}
+
+static void print_syntax()
+{
+	printf("syntax: trace <task_id>\n");
+}
+
+int main(int argc, char *argv[])
+{
+	task_id_t task_id;
+	char *err_p;
+
+	if (argc != 2) {
+		printf("Mising argument\n");
+		print_syntax();
+		return 1;
+	}
+
+	task_id = strtol(argv[1], &err_p, 10);
+
+	if (*err_p) {
+		printf("Task ID syntax error\n");
+		print_syntax();
+		return 1;
+	}
+
+	main_init();
+	trace_active_task(task_id);
+}
+
+/** @}
+ */
Index: uspace/lib/libc/Makefile
===================================================================
--- uspace/lib/libc/Makefile	(revision 06a195bc5890a55fd6f733495812e905812949fc)
+++ uspace/lib/libc/Makefile	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
@@ -79,4 +79,5 @@
 	generic/stdlib.c \
 	generic/mman.c \
+	generic/udebug.c \
 	generic/vfs/vfs.c \
 	generic/vfs/canonify.c
Index: uspace/lib/libc/generic/ipc.c
===================================================================
--- uspace/lib/libc/generic/ipc.c	(revision 06a195bc5890a55fd6f733495812e905812949fc)
+++ uspace/lib/libc/generic/ipc.c	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
@@ -910,4 +910,16 @@
 	return ipc_answer_2(callid, EOK, (ipcarg_t) dst, (ipcarg_t) size);
 }
+
+#include <kernel/syscall/sysarg64.h>
+/** Connect to a task specified by id.
+ */
+int ipc_connect_kbox(task_id_t id)
+{
+	sysarg64_t arg;
+
+	arg.value = (unsigned long long) id;
+
+	return __SYSCALL1(SYS_IPC_CONNECT_KBOX, (sysarg_t) &arg);
+}
  
 /** @}
Index: uspace/lib/libc/generic/udebug.c
===================================================================
--- uspace/lib/libc/generic/udebug.c	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
+++ uspace/lib/libc/generic/udebug.c	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2008 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 libc
+ * @{
+ */
+/** @file
+ */ 
+
+#include <udebug.h>
+#include <sys/types.h>
+#include <syscall.h>
+#include <ipc/ipc.h>
+#include <async.h>
+
+int udebug_begin(int phoneid)
+{
+	return async_req_1_0(phoneid, IPC_M_DEBUG_ALL, UDEBUG_M_BEGIN);
+}
+
+int udebug_end(int phoneid)
+{
+	return async_req_1_0(phoneid, IPC_M_DEBUG_ALL, UDEBUG_M_END);
+}
+
+int udebug_set_evmask(int phoneid, udebug_evmask_t mask)
+{
+	return async_req_2_0(phoneid, IPC_M_DEBUG_ALL, UDEBUG_M_SET_EVMASK,
+		mask);
+}
+
+int udebug_thread_read(int phoneid, void *buffer, size_t n,
+	size_t *copied, size_t *needed)
+{
+	unsigned dest_addr;
+
+	return async_req_3_3(phoneid, IPC_M_DEBUG_ALL, UDEBUG_M_THREAD_READ,
+		(sysarg_t)buffer, n, &dest_addr, copied, needed);
+}
+
+int udebug_mem_read(int phoneid, void *buffer, uintptr_t addr, size_t n)
+{
+	return async_req_4_0(phoneid, IPC_M_DEBUG_ALL, UDEBUG_M_MEM_READ,
+	    (sysarg_t)buffer, addr, n);
+}
+
+int udebug_args_read(int phoneid, thash_t tid, sysarg_t *buffer)
+{
+	return async_req_3_0(phoneid, IPC_M_DEBUG_ALL, UDEBUG_M_ARGS_READ,
+	    tid, (sysarg_t)buffer);
+}
+
+int udebug_go(int phoneid, thash_t tid, udebug_event_t *ev_type,
+    sysarg_t *val0, sysarg_t *val1)
+{
+	return async_req_2_3(phoneid, IPC_M_DEBUG_ALL, UDEBUG_M_GO,
+	    tid, (sysarg_t)ev_type, (sysarg_t)val0, (sysarg_t)val1);
+}
+
+int udebug_stop(int phoneid, thash_t tid)
+{
+	return async_req_2_0(phoneid, IPC_M_DEBUG_ALL, UDEBUG_M_STOP,
+	    tid);
+}
+
+/** @}
+ */
Index: uspace/lib/libc/include/ipc/ipc.h
===================================================================
--- uspace/lib/libc/include/ipc/ipc.h	(revision 06a195bc5890a55fd6f733495812e905812949fc)
+++ uspace/lib/libc/include/ipc/ipc.h	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
@@ -289,4 +289,8 @@
 extern int ipc_data_write_finalize(ipc_callid_t callid, void *dst, size_t size);
 
+#include <task.h>
+
+extern int ipc_connect_kbox(task_id_t id);
+
 #endif
 
Index: uspace/lib/libc/include/udebug.h
===================================================================
--- uspace/lib/libc/include/udebug.h	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
+++ uspace/lib/libc/include/udebug.h	(revision 9a1b20c34d5404b8272209d06c0239aa1a2384da)
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2008 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 libc
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBC_UDEBUG_H_
+#define LIBC_UDEBUG_H_
+
+#include <kernel/udebug/udebug.h>
+#include <sys/types.h>
+#include <libarch/types.h>
+
+typedef sysarg_t thash_t;
+
+int udebug_begin(int phoneid);
+int udebug_end(int phoneid);
+int udebug_set_evmask(int phoneid, udebug_evmask_t mask);
+int udebug_thread_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);
+int udebug_go(int phoneid, thash_t tid, udebug_event_t *ev_type,
+	sysarg_t *val0, sysarg_t *val1);
+int udebug_stop(int phoneid, thash_t tid);
+
+#endif
+
+/** @}
+ */
