aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/goldfish/patches-2.6.30/0129--ARM-goldfish-qemutrace-Kernel-instrumentation-fo.patch
diff options
context:
space:
mode:
authorFelix Fietkau <nbd@openwrt.org>2009-06-14 20:42:33 +0000
committerFelix Fietkau <nbd@openwrt.org>2009-06-14 20:42:33 +0000
commitf88360279f91387dc61725226ad38c3c95d3790a (patch)
tree66d21f3483914eb7e674c45f3497ab8912094ac0 /target/linux/goldfish/patches-2.6.30/0129--ARM-goldfish-qemutrace-Kernel-instrumentation-fo.patch
parenta6d8f8be9e06c5ffb03f815c8c76ba743c64aa3d (diff)
downloadupstream-f88360279f91387dc61725226ad38c3c95d3790a.tar.gz
upstream-f88360279f91387dc61725226ad38c3c95d3790a.tar.bz2
upstream-f88360279f91387dc61725226ad38c3c95d3790a.zip
add the 'goldfish' target, useful for experimenting with virtual phone hardware (includes the emulator)
SVN-Revision: 16459
Diffstat (limited to 'target/linux/goldfish/patches-2.6.30/0129--ARM-goldfish-qemutrace-Kernel-instrumentation-fo.patch')
-rw-r--r--target/linux/goldfish/patches-2.6.30/0129--ARM-goldfish-qemutrace-Kernel-instrumentation-fo.patch854
1 files changed, 854 insertions, 0 deletions
diff --git a/target/linux/goldfish/patches-2.6.30/0129--ARM-goldfish-qemutrace-Kernel-instrumentation-fo.patch b/target/linux/goldfish/patches-2.6.30/0129--ARM-goldfish-qemutrace-Kernel-instrumentation-fo.patch
new file mode 100644
index 0000000000..c8463ff240
--- /dev/null
+++ b/target/linux/goldfish/patches-2.6.30/0129--ARM-goldfish-qemutrace-Kernel-instrumentation-fo.patch
@@ -0,0 +1,854 @@
+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
+@@ -1333,6 +1333,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.
+ *
+@@ -1434,6 +1438,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
+@@ -2747,6 +2747,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.
+@@ -2789,6 +2793,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
+@@ -903,6 +903,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).
+ */
+@@ -1209,6 +1214,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);
+
+@@ -1935,6 +1944,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 */