From fcd69dd4537320a25a294c82362cc7abe4fe773c Mon Sep 17 00:00:00 2001
From: Ye Wen <ywen@google.com>
Date: Fri, 14 Jul 2006 14:51:45 +0700
Subject: [PATCH 129/134] [ARM] goldfish: qemutrace: Kernel instrumentation for tracing the events.

Like fork, context switch, execve and exit.
This code is to complement Jack's tracing facility.

To turn tracing on:
echo 1 > /sysfs/qemu_trace/state
To turn tracing off: echo 0 > /sysfs/qemu_trace/state
I also added java methods to Debug.java to turn tracing on and off.
The kernel driver also supports adding dynamic symbols to the trace.
To add a symbol 'foo' with hex address 'abcd1234' to the trace:
echo 'abcd1234 foo' > /sysfs/qemu_trace/symbol

Signed-off-by: Mike Chan <mike@android.com>

[ARM] goldfish: qemutrace: Improved support for tracing thread and process names.

Added a new pseudo file /sys/qemu_trace/process_name to allow user
programs to add a trace record for a process name change.  Removed
the tracing of thread and process names from the exit() system call
because that was not sufficiently general.  Added tracing of thread
names in set_task_comm() and daemonize().  Added tracing of the
thread group id to fork() and clone().

Signed-off-by: Jack Veenstra <veenstra@google.com>
Signed-off-by: Mike Chan <mike@android.com>
---
 arch/arm/kernel/entry-armv.S              |    5 +
 drivers/misc/Kconfig                      |    5 +
 drivers/misc/Makefile                     |    1 +
 drivers/misc/qemutrace/Makefile           |    2 +
 drivers/misc/qemutrace/qemu_trace.c       |  386 +++++++++++++++++++++++++++++
 drivers/misc/qemutrace/qemu_trace.h       |   22 ++
 drivers/misc/qemutrace/qemu_trace_sysfs.c |  182 ++++++++++++++
 fs/exec.c                                 |   14 +
 kernel/exit.c                             |   14 +
 kernel/fork.c                             |    8 +
 kernel/sched.c                            |    9 +
 mm/mmap.c                                 |   13 +
 12 files changed, 661 insertions(+), 0 deletions(-)
 create mode 100644 drivers/misc/qemutrace/Makefile
 create mode 100644 drivers/misc/qemutrace/qemu_trace.c
 create mode 100644 drivers/misc/qemutrace/qemu_trace.h
 create mode 100644 drivers/misc/qemutrace/qemu_trace_sysfs.c

--- a/arch/arm/kernel/entry-armv.S
+++ b/arch/arm/kernel/entry-armv.S
@@ -733,6 +733,11 @@ ENTRY(__switch_to)
 	ldr	r0, =thread_notify_head
 	mov	r1, #THREAD_NOTIFY_SWITCH
 	bl	atomic_notifier_call_chain
+#ifdef CONFIG_QEMU_TRACE
+/*
+	mcr	p15, 0, r0, c15, c0, 0		@ signal context switch
+*/
+#endif
 	mov	r0, r5
 	ldmia	r4, {r4 - sl, fp, sp, pc}	@ Load all regs saved previously
  UNWIND(.fnend		)
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -233,6 +233,11 @@ config ISL29003
 	  This driver can also be built as a module.  If so, the module
 	  will be called isl29003.
 
+config QEMU_TRACE
+        tristate "Virtual Device for QEMU tracing"
+        ---help---
+          This is a virtual device for QEMU tracing.
+
 source "drivers/misc/c2port/Kconfig"
 source "drivers/misc/eeprom/Kconfig"
 
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -20,4 +20,5 @@ obj-$(CONFIG_SGI_GRU)		+= sgi-gru/
 obj-$(CONFIG_HP_ILO)		+= hpilo.o
 obj-$(CONFIG_ISL29003)		+= isl29003.o
 obj-$(CONFIG_C2PORT)		+= c2port/
+obj-$(CONFIG_QEMU_TRACE)	+= qemutrace/
 obj-y				+= eeprom/
--- /dev/null
+++ b/drivers/misc/qemutrace/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_QEMU_TRACE) := qemu_trace.o
+obj-$(CONFIG_QEMU_TRACE) += qemu_trace_sysfs.o
--- /dev/null
+++ b/drivers/misc/qemutrace/qemu_trace.c
@@ -0,0 +1,386 @@
+/* drivers/misc/qemutrace/qemu_trace.c
+ *
+ * Copyright (C) 2007-2008 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/miscdevice.h>
+#include <linux/pci.h>
+#include <linux/proc_fs.h>
+#include <linux/platform_device.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/sizes.h>
+#include "qemu_trace.h"
+
+/* trace device registers */
+#define TRACE_DEV_REG_SWITCH            0
+#define TRACE_DEV_REG_FORK              1
+#define TRACE_DEV_REG_EXECVE_PID        2
+#define TRACE_DEV_REG_EXECVE_VMSTART    3
+#define TRACE_DEV_REG_EXECVE_VMEND      4
+#define TRACE_DEV_REG_EXECVE_OFFSET     5
+#define TRACE_DEV_REG_EXECVE_EXEPATH    6
+#define TRACE_DEV_REG_EXIT              7
+#define TRACE_DEV_REG_CMDLINE           8
+#define TRACE_DEV_REG_CMDLINE_LEN       9
+#define TRACE_DEV_REG_MMAP_EXEPATH      10
+#define TRACE_DEV_REG_INIT_PID          11
+#define TRACE_DEV_REG_INIT_NAME         12
+#define TRACE_DEV_REG_CLONE             13
+#define TRACE_DEV_REG_UNMAP_START       14
+#define TRACE_DEV_REG_UNMAP_END         15
+#define TRACE_DEV_REG_NAME              16
+#define TRACE_DEV_REG_TGID              17
+#define TRACE_DEV_REG_DYN_SYM           50
+#define TRACE_DEV_REG_DYN_SYM_ADDR      51
+#define TRACE_DEV_REG_REMOVE_ADDR       52
+#define TRACE_DEV_REG_ENABLE            100
+
+static unsigned char __iomem *qt_base;
+static int init_called;
+
+/* PIDs that start before our device registered */
+#define MAX_INIT_PIDS   2048
+static int tb_next = 0;
+static int init_pids[MAX_INIT_PIDS];
+static DEFINE_SPINLOCK(qemu_trace_lock);
+
+void qemu_trace_start(void)
+{
+	unsigned long irq_flags;
+
+	if (qt_base == NULL)
+		return;
+	spin_lock_irqsave(&qemu_trace_lock, irq_flags);
+	writel(1, qt_base + (TRACE_DEV_REG_ENABLE << 2));
+	spin_unlock_irqrestore(&qemu_trace_lock, irq_flags);
+}
+
+void qemu_trace_stop(void)
+{
+	unsigned long irq_flags;
+
+	if (qt_base == NULL)
+		return;
+	spin_lock_irqsave(&qemu_trace_lock, irq_flags);
+	writel(0, qt_base + (TRACE_DEV_REG_ENABLE << 2));
+	spin_unlock_irqrestore(&qemu_trace_lock, irq_flags);
+}
+
+int qemu_trace_get_tracing(void)
+{
+	int val = 0;
+	if (qt_base != NULL)
+		val = readl(qt_base + (TRACE_DEV_REG_ENABLE << 2));
+	return val;
+}
+
+void qemu_trace_add_mapping(unsigned int addr, const char *symbol)
+{
+	unsigned long irq_flags;
+
+	if (qt_base == NULL)
+		return;
+
+	/* Write the address first, then the symbol name. */
+	spin_lock_irqsave(&qemu_trace_lock, irq_flags);
+	writel(addr, qt_base + (TRACE_DEV_REG_DYN_SYM_ADDR << 2));
+	writel(symbol, qt_base + (TRACE_DEV_REG_DYN_SYM << 2));
+	spin_unlock_irqrestore(&qemu_trace_lock, irq_flags);
+}
+
+void qemu_trace_remove_mapping(unsigned int addr)
+{
+	unsigned long irq_flags;
+
+	if (qt_base == NULL)
+		return;
+
+	spin_lock_irqsave(&qemu_trace_lock, irq_flags);
+	writel(addr, qt_base + (TRACE_DEV_REG_REMOVE_ADDR << 2));
+	spin_unlock_irqrestore(&qemu_trace_lock, irq_flags);
+}
+
+/* trace the context switch */
+void qemu_trace_cs(struct task_struct *next)
+{
+	unsigned long irq_flags;
+
+	if (qt_base == NULL)
+		return;
+
+	spin_lock_irqsave(&qemu_trace_lock, irq_flags);
+	writel(task_pid_nr(next), qt_base);
+	spin_unlock_irqrestore(&qemu_trace_lock, irq_flags);
+}
+EXPORT_SYMBOL(qemu_trace_cs);
+
+/* trace the execve */
+void qemu_trace_execve(int argc, char __user * __user *argv)
+{
+	unsigned long irq_flags;
+	char page[PAGE_SIZE];
+	char *ptr = page;
+
+	if (qt_base == NULL)
+		return;
+
+	while (argc-- > 0) {
+		char __user *str;
+		int len;
+		if (get_user(str, argv ++))
+			return;
+		len = strnlen_user(str, PAGE_SIZE);
+		if (len == 0)
+			return;
+		if (copy_from_user(ptr, str, len))
+			return;
+		ptr += len;
+	}
+
+	if (ptr > page) {
+		int len = ptr - page;
+		spin_lock_irqsave(&qemu_trace_lock, irq_flags);
+		writel(len, qt_base + (TRACE_DEV_REG_CMDLINE_LEN << 2));
+		writel(page, qt_base + (TRACE_DEV_REG_CMDLINE << 2));
+		spin_unlock_irqrestore(&qemu_trace_lock, irq_flags);
+	}
+}
+EXPORT_SYMBOL(qemu_trace_execve);
+
+/* trace the mmap */
+void qemu_trace_mmap(struct vm_area_struct *vma)
+{
+	unsigned long irq_flags;
+	char page[PAGE_SIZE];
+	char *p;
+
+	if (qt_base == NULL)
+		return;
+
+	if (vma->vm_file == NULL)
+		return;
+
+	p = d_path(&vma->vm_file->f_path, page, PAGE_SIZE);
+	if (IS_ERR(p))
+		return;
+
+	spin_lock_irqsave(&qemu_trace_lock, irq_flags);
+	writel(vma->vm_start, qt_base + (TRACE_DEV_REG_EXECVE_VMSTART << 2));
+	writel(vma->vm_end, qt_base + (TRACE_DEV_REG_EXECVE_VMEND << 2));
+	writel(vma->vm_pgoff * PAGE_SIZE, qt_base + (TRACE_DEV_REG_EXECVE_OFFSET << 2));
+	writel(p, qt_base + (TRACE_DEV_REG_MMAP_EXEPATH << 2));
+	spin_unlock_irqrestore(&qemu_trace_lock, irq_flags);
+}
+EXPORT_SYMBOL(qemu_trace_mmap);
+
+/* trace the munmap */
+void qemu_trace_munmap(unsigned long start, unsigned long end)
+{
+	unsigned long irq_flags;
+
+	if (qt_base == NULL)
+		return;
+
+	spin_lock_irqsave(&qemu_trace_lock, irq_flags);
+	writel(start, qt_base + (TRACE_DEV_REG_UNMAP_START << 2));
+	writel(end, qt_base + (TRACE_DEV_REG_UNMAP_END << 2));
+	spin_unlock_irqrestore(&qemu_trace_lock, irq_flags);
+}
+EXPORT_SYMBOL(qemu_trace_munmap);
+
+/* trace the fork */
+void qemu_trace_fork(struct task_struct *forked, unsigned long clone_flags)
+{
+	unsigned long irq_flags;
+
+	spin_lock_irqsave(&qemu_trace_lock, irq_flags);
+	if (qt_base == NULL) {
+		if (tb_next >= MAX_INIT_PIDS) {
+			if (!init_called)
+				printk(KERN_ERR
+				       "QEMU Trace: too many PIDs before "
+				       "device registered ignoring %d\n",
+				       forked->pid);
+		} else {
+			init_pids[tb_next] = task_pid_nr(forked);
+			tb_next++;
+		}
+	} else {
+		writel(task_tgid_nr(forked), qt_base + (TRACE_DEV_REG_TGID << 2));
+		if (clone_flags & CLONE_VM)
+			writel(task_pid_nr(forked), qt_base + (TRACE_DEV_REG_CLONE << 2));
+		else
+			writel(task_pid_nr(forked), qt_base + (TRACE_DEV_REG_FORK << 2));
+	}
+	spin_unlock_irqrestore(&qemu_trace_lock, irq_flags);
+}
+EXPORT_SYMBOL(qemu_trace_fork);
+
+/* trace the exit */
+void qemu_trace_exit(int code)
+{
+	unsigned long irq_flags;
+
+	if (qt_base == NULL)
+		return;
+
+	spin_lock_irqsave(&qemu_trace_lock, irq_flags);
+	writel(code, qt_base + (TRACE_DEV_REG_EXIT << 2));
+	spin_unlock_irqrestore(&qemu_trace_lock, irq_flags);
+}
+EXPORT_SYMBOL(qemu_trace_exit);
+
+/* trace the thread name */
+void qemu_trace_thread_name(const char *name)
+{
+	unsigned long irq_flags;
+
+	if (qt_base == NULL)
+		return;
+
+	spin_lock_irqsave(&qemu_trace_lock, irq_flags);
+	writel(name, qt_base + (TRACE_DEV_REG_NAME << 2));
+	spin_unlock_irqrestore(&qemu_trace_lock, irq_flags);
+}
+EXPORT_SYMBOL(qemu_trace_thread_name);
+
+/* trace the process name */
+void qemu_trace_process_name(const char *name)
+{
+	unsigned long irq_flags;
+
+	if (qt_base == NULL)
+		return;
+
+	spin_lock_irqsave(&qemu_trace_lock, irq_flags);
+	writel(name, qt_base + (TRACE_DEV_REG_NAME << 2));
+	spin_unlock_irqrestore(&qemu_trace_lock, irq_flags);
+}
+EXPORT_SYMBOL(qemu_trace_process_name);
+
+static void qemu_trace_pid_exec(struct task_struct *tsk)
+{
+	unsigned long irq_flags;
+	char page[PAGE_SIZE];
+	struct mm_struct *mm = get_task_mm(tsk);
+	if (mm == NULL)
+		return;
+	down_read(&mm->mmap_sem);
+	{
+		struct vm_area_struct *vma = mm->mmap;
+		while (vma) {
+			if ((vma->vm_flags & VM_EXEC) && vma->vm_file) {
+				char *p;
+				p = d_path(&vma->vm_file->f_path, page, PAGE_SIZE);
+				if (!IS_ERR(p)) {
+					spin_lock_irqsave(&qemu_trace_lock, irq_flags);
+					writel(vma->vm_start, qt_base + (TRACE_DEV_REG_EXECVE_VMSTART << 2));
+					writel(vma->vm_end, qt_base + (TRACE_DEV_REG_EXECVE_VMEND << 2));
+					writel(vma->vm_pgoff * PAGE_SIZE, qt_base + (TRACE_DEV_REG_EXECVE_OFFSET << 2));
+					writel(p, qt_base + (TRACE_DEV_REG_EXECVE_EXEPATH << 2));
+					spin_unlock_irqrestore(&qemu_trace_lock, irq_flags);
+				}
+			}
+			vma = vma->vm_next;
+		}
+	}
+	up_read(&mm->mmap_sem);
+	mmput(mm);
+}
+
+static void qemu_trace_dump_init_threads(void)
+{
+	unsigned long irq_flags;
+	int i;
+
+	for (i = 0; i < tb_next; i++) {
+		struct task_struct *tsk;
+		struct pid *pid = find_get_pid(init_pids[i]);
+		if (pid == NULL)
+			continue;
+
+		if ((tsk = get_pid_task(pid, PIDTYPE_PID)) != NULL) {
+			/* first give the pid and name */
+			task_lock(tsk);
+			spin_lock_irqsave(&qemu_trace_lock, irq_flags);
+			writel(task_tgid_nr(tsk), qt_base + (TRACE_DEV_REG_TGID << 2));
+			writel(task_pid_nr(tsk), qt_base + (TRACE_DEV_REG_INIT_PID << 2));
+			writel(tsk->comm, qt_base + (TRACE_DEV_REG_INIT_NAME << 2));
+			spin_unlock_irqrestore(&qemu_trace_lock, irq_flags);
+			task_unlock(tsk);
+			/* check if the task has execs */
+			qemu_trace_pid_exec(tsk);
+		}
+	}
+}
+
+static int qemu_trace_probe(struct platform_device *pdev)
+{
+	struct resource *r;
+
+	/* not thread safe, but this should not happen */
+	if (qt_base != NULL) {
+		printk(KERN_ERR "QEMU TRACE Device: already mapped at %p\n", qt_base);
+		return -ENODEV;
+	}
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (r == NULL)
+		return -EINVAL;
+	qt_base = ioremap(r->start, PAGE_SIZE);
+	printk(KERN_INFO "QEMU TRACE Device: The mapped IO base is %p\n", qt_base);
+
+	qemu_trace_dump_init_threads();
+
+	return 0;
+}
+
+static int qemu_trace_remove(struct platform_device *pdev)
+{
+	iounmap(qt_base);
+	qt_base = NULL;
+	return 0;
+}
+
+static struct platform_driver qemu_trace = {
+	.probe = qemu_trace_probe,
+	.remove = qemu_trace_remove,
+	.driver = {
+		.name = "qemu_trace"
+	}
+};
+
+static int __init qemu_trace_dev_init(void)
+{
+	int ret;
+	ret = platform_driver_register(&qemu_trace);
+	init_called = 1;
+	return ret;
+}
+
+static void qemu_trace_dev_exit(void)
+{
+	platform_driver_unregister(&qemu_trace);
+}
+
+
+module_init(qemu_trace_dev_init);
+module_exit(qemu_trace_dev_exit);
+
+MODULE_AUTHOR("Ye Wen <ywen@google.com>");
+MODULE_LICENSE("GPL");
--- /dev/null
+++ b/drivers/misc/qemutrace/qemu_trace.h
@@ -0,0 +1,22 @@
+/* drivers/misc/qemutrace/qemu_trace.h
+ *
+ * Copyright (C) 2007-2008 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ *
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+void qemu_trace_start(void);
+void qemu_trace_stop(void);
+int qemu_trace_get_tracing(void);
+void qemu_trace_add_mapping(unsigned int addr, const char *symbol);
+void qemu_trace_remove_mapping(unsigned int addr);
+void qemu_trace_process_name(const char *name);
--- /dev/null
+++ b/drivers/misc/qemutrace/qemu_trace_sysfs.c
@@ -0,0 +1,182 @@
+/* drivers/misc/qemu_sysfs.c
+ *
+ * Copyright (C) 2007-2008 Google, Inc.
+ * Author: Jack Veenstra
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/sysdev.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/wait.h>
+#include "qemu_trace.h"
+
+MODULE_DESCRIPTION("Qemu Trace Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.0");
+
+static struct kobject *qemu_trace_kobj;
+
+static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr, char * buf)
+{
+    int val = qemu_trace_get_tracing();
+    buf[0] = '0' + val;
+    buf[1] = '\n';
+    return 2;
+}
+
+static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr, const char * buf, size_t n)
+{
+    if (n <= 0)
+	return -EINVAL;
+    if (buf[0] == '0')
+        qemu_trace_stop();
+    else if (buf[0] == '1')
+        qemu_trace_start();
+    else
+	return -EINVAL;
+    return n;
+}
+
+static ssize_t symbol_show(struct kobject *kobj, struct kobj_attribute *attr, char * buf)
+{
+    return 0;
+}
+
+// We are expecting a string of the form "addr symbol" where 'addr' is a hex address
+// (without the leading '0x') and symbol is a newline-terminated string.  This symbol
+// with its corresponding address will be added to the trace file.
+//
+// To remove the mapping for (addr, symbol) in the trace file, write just the
+// address.  As before, the address is in hex without the leading '0x'.  It can
+// be newline-terminated or zero-terminated.
+static ssize_t symbol_store(struct kobject *kobj, struct kobj_attribute *attr, const char * buf, size_t n)
+{
+    const char *cp;
+    unsigned int addr = 0;
+    int len;
+    char *sym;
+
+    if (n <= 0 || buf == NULL)
+	return -EINVAL;
+    for (cp = buf; *cp != ' '; ++cp) {
+        unsigned int digit;
+
+        if (*cp >= '0' && *cp <= '9')
+            digit = *cp - '0';
+        else if (*cp >= 'a' && *cp <= 'f')
+            digit = *cp - 'a' + 10;
+        else if (*cp == 0 || *cp == '\n') {
+            qemu_trace_remove_mapping(addr);
+            return n;
+        } else
+            return -EINVAL;
+        addr = (addr << 4) + digit;
+    }
+    // Move past the space
+    cp += 1;
+
+    // Copy the string to a new buffer so that we can replace the newline
+    // with '\0'.
+    len = strlen(cp);
+    sym = kzalloc(len + 1, GFP_KERNEL);
+    strcpy(sym, cp);
+    if (sym[len - 1] == '\n')
+        sym[len - 1] = 0;
+
+    qemu_trace_add_mapping(addr, sym);
+    kfree(sym);
+    return n;
+}
+
+static ssize_t process_name_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+    return 0;
+}
+
+/* This expects a string that is the process name.  If the string contains
+ * a trailing newline, that is removed in the emulator tracing code because
+ * it is simpler to do it there.
+ */
+static ssize_t process_name_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t n)
+{
+    if (n <= 0 || buf == NULL)
+	return -EINVAL;
+
+    qemu_trace_process_name(buf);
+    return n;
+}
+
+
+#define qemu_trace_attr(_name) \
+static struct kobj_attribute _name##_attr = {	\
+	.attr	= {				\
+		.name = __stringify(_name),	\
+		.mode = 0666,			\
+	},					\
+	.show	= _name##_show,			\
+	.store	= _name##_store,		\
+}
+
+qemu_trace_attr(state);
+qemu_trace_attr(symbol);
+qemu_trace_attr(process_name);
+
+static struct attribute * qemu_trace_attrs[] = {
+	&state_attr.attr,
+	&symbol_attr.attr,
+	&process_name_attr.attr,
+	NULL,
+};
+
+static struct attribute_group qemu_trace_attr_group = {
+	.attrs = qemu_trace_attrs,
+};
+
+static int __init qemu_trace_init(void)
+{
+	int ret;
+
+	qemu_trace_kobj = kobject_create_and_add("qemu_trace", NULL);
+	if (qemu_trace_kobj == NULL) {
+		printk("qemu_trace_init: kobject_create_and_add failed\n");
+		ret = -ENOMEM;
+		return ret;
+	}
+	ret = sysfs_create_group(qemu_trace_kobj, &qemu_trace_attr_group);
+	if (ret) {
+		printk("qemu_trace_init: sysfs_create_group failed\n");
+		goto err;
+	}
+
+	return 0;
+
+err:
+	kobject_del(qemu_trace_kobj);
+	qemu_trace_kobj = NULL;
+	return ret;
+}
+
+static void  __exit qemu_trace_exit(void)
+{
+	sysfs_remove_group(qemu_trace_kobj, &qemu_trace_attr_group);
+	kobject_del(qemu_trace_kobj);
+}
+
+core_initcall(qemu_trace_init);
+module_exit(qemu_trace_exit);
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -59,6 +59,9 @@
 #include <asm/mmu_context.h>
 #include <asm/tlb.h>
 #include "internal.h"
+#ifdef CONFIG_QEMU_TRACE
+	void qemu_trace_thread_name(char *name);
+#endif
 
 int core_uses_pid;
 char core_pattern[CORENAME_MAX_SIZE] = "core";
@@ -922,6 +925,9 @@ void set_task_comm(struct task_struct *t
 	task_lock(tsk);
 	strlcpy(tsk->comm, buf, sizeof(tsk->comm));
 	task_unlock(tsk);
+#ifdef CONFIG_QEMU_TRACE
+	qemu_trace_thread_name(buf);
+#endif
 }
 
 int flush_old_exec(struct linux_binprm * bprm)
@@ -1245,6 +1251,10 @@ void free_bprm(struct linux_binprm *bprm
 	kfree(bprm);
 }
 
+#ifdef CONFIG_QEMU_TRACE
+extern void qemu_trace_execve(int argc, char __user * __user * argv);
+#endif
+
 /*
  * sys_execve() executes a new program.
  */
@@ -1324,6 +1334,10 @@ int do_execve(char * filename,
 		goto out;
 
 	current->flags &= ~PF_KTHREAD;
+#ifdef CONFIG_QEMU_TRACE
+        qemu_trace_execve(bprm->argc, argv);
+#endif
+
 	retval = search_binary_handler(bprm,regs);
 	if (retval < 0)
 		goto out;
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -60,6 +60,11 @@ DEFINE_TRACE(sched_process_free);
 DEFINE_TRACE(sched_process_exit);
 DEFINE_TRACE(sched_process_wait);
 
+#ifdef CONFIG_QEMU_TRACE
+void qemu_trace_thread_name(char *name);
+void qemu_trace_exit(int code);
+#endif
+
 static void exit_mm(struct task_struct * tsk);
 
 static void __unhash_process(struct task_struct *p)
@@ -426,6 +431,9 @@ void daemonize(const char *name, ...)
 	va_start(args, name);
 	vsnprintf(current->comm, sizeof(current->comm), name, args);
 	va_end(args);
+#ifdef CONFIG_QEMU_TRACE
+	qemu_trace_thread_name(current->comm);
+#endif
 
 	/*
 	 * If we were started as result of loading a module, close all of the
@@ -1012,6 +1020,12 @@ NORET_TYPE void do_exit(long code)
 	preempt_disable();
 	/* causes final put_task_struct in finish_task_switch(). */
 	tsk->state = TASK_DEAD;
+
+#ifdef CONFIG_QEMU_TRACE
+	/* Emit a trace record for the exit() call. */
+	qemu_trace_exit(code);
+#endif
+
 	schedule();
 	BUG();
 	/* Avoid "noreturn function does return".  */
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -1323,6 +1323,10 @@ struct task_struct * __cpuinit fork_idle
 	return task;
 }
 
+#ifdef CONFIG_QEMU_TRACE
+extern void qemu_trace_fork(struct task_struct *forked, unsigned long clone_flags);
+#endif
+
 /*
  *  Ok, this is the main fork-routine.
  *
@@ -1424,6 +1428,10 @@ long do_fork(unsigned long clone_flags,
 		tracehook_report_clone_complete(trace, regs,
 						clone_flags, nr, p);
 
+#ifdef CONFIG_QEMU_TRACE
+                qemu_trace_fork(p, clone_flags);
+#endif
+
 		if (clone_flags & CLONE_VFORK) {
 			freezer_do_not_count();
 			wait_for_completion(&vfork);
--- a/kernel/sched.c
+++ b/kernel/sched.c
@@ -2748,6 +2748,10 @@ asmlinkage void schedule_tail(struct tas
 		put_user(task_pid_vnr(current), current->set_child_tid);
 }
 
+#ifdef CONFIG_QEMU_TRACE
+void qemu_trace_cs(struct task_struct *next);
+#endif
+
 /*
  * context_switch - switch to the new MM and the new
  * thread's register state.
@@ -2790,6 +2794,11 @@ context_switch(struct rq *rq, struct tas
 	spin_release(&rq->lock.dep_map, 1, _THIS_IP_);
 #endif
 
+#ifdef CONFIG_QEMU_TRACE
+	/* Emit a trace record for the context switch. */
+	qemu_trace_cs(next);
+#endif
+
 	/* Here we just switch the register state and the stack. */
 	switch_to(prev, next, prev);
 
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -906,6 +906,11 @@ void vm_stat_account(struct mm_struct *m
 }
 #endif /* CONFIG_PROC_FS */
 
+#ifdef CONFIG_QEMU_TRACE
+extern void qemu_trace_mmap(struct vm_area_struct * vma);
+extern void qemu_trace_munmap(unsigned long start, unsigned long end);
+#endif
+
 /*
  * The caller must hold down_write(current->mm->mmap_sem).
  */
@@ -1212,6 +1217,10 @@ munmap_back:
 	pgoff = vma->vm_pgoff;
 	vm_flags = vma->vm_flags;
 
+#ifdef CONFIG_QEMU_TRACE
+        qemu_trace_mmap(vma);       
+#endif
+
 	if (vma_wants_writenotify(vma))
 		vma->vm_page_prot = vm_get_page_prot(vm_flags & ~VM_SHARED);
 
@@ -1938,6 +1947,10 @@ int do_munmap(struct mm_struct *mm, unsi
 	 * Remove the vma's, and unmap the actual pages
 	 */
 	detach_vmas_to_be_unmapped(mm, vma, prev, end);
+
+#ifdef CONFIG_QEMU_TRACE
+	qemu_trace_munmap(start, end);
+#endif
 	unmap_region(mm, vma, prev, start, end);
 
 	/* Fix up all other VM information */