openwrt/target/linux/goldfish/patches-2.6.30/0129--ARM-goldfish-qemutrac...

855 lines
24 KiB
Diff

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 */