diff options
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.patch | 854 |
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 000000000..c8463ff24 --- /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 */ |