From fcd69dd4537320a25a294c82362cc7abe4fe773c Mon Sep 17 00:00:00 2001 From: Ye Wen 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 [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 Signed-off-by: Mike Chan --- 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 "); +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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 #include #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 */