diff -Naurp xen-unstable.hg-20050823-nooprofile/linux-2.6.12-xen0/arch/xen/configs/xen0_defconfig_x86_32 xen-unstable.hg-20050823/linux-2.6.12-xen0/arch/xen/configs/xen0_defconfig_x86_32 --- xen-unstable.hg-20050823-nooprofile/linux-2.6.12-xen0/arch/xen/configs/xen0_defconfig_x86_32 2005-08-24 07:44:51.000000000 -0500 +++ xen-unstable.hg-20050823/linux-2.6.12-xen0/arch/xen/configs/xen0_defconfig_x86_32 2005-08-23 07:05:17.000000000 -0500 @@ -79,6 +79,12 @@ CONFIG_OBSOLETE_MODPARM=y CONFIG_KMOD=y # +# OProfile options +# +CONFIG_PROFILING=y +CONFIG_OPROFILE=m + +# # X86 Processor Configuration # CONFIG_XENARCH="i386" diff -Naurp xen-unstable.hg-20050823-nooprofile/linux-2.6.12-xen0/arch/xen/configs/xenU_defconfig_x86_32 xen-unstable.hg-20050823/linux-2.6.12-xen0/arch/xen/configs/xenU_defconfig_x86_32 --- xen-unstable.hg-20050823-nooprofile/linux-2.6.12-xen0/arch/xen/configs/xenU_defconfig_x86_32 2005-08-24 07:44:51.000000000 -0500 +++ xen-unstable.hg-20050823/linux-2.6.12-xen0/arch/xen/configs/xenU_defconfig_x86_32 2005-08-23 07:05:17.000000000 -0500 @@ -76,6 +76,12 @@ CONFIG_KMOD=y CONFIG_STOP_MACHINE=y # +# OProfile options +# +CONFIG_PROFILING=y +CONFIG_OPROFILE=m + +# # X86 Processor Configuration # CONFIG_XENARCH="i386" diff -Naurp xen-unstable.hg-20050823-nooprofile/linux-2.6.12-xen0/arch/xen/i386/Makefile xen-unstable.hg-20050823/linux-2.6.12-xen0/arch/xen/i386/Makefile --- xen-unstable.hg-20050823-nooprofile/linux-2.6.12-xen0/arch/xen/i386/Makefile 2005-08-24 07:44:51.000000000 -0500 +++ xen-unstable.hg-20050823/linux-2.6.12-xen0/arch/xen/i386/Makefile 2005-08-23 07:05:17.000000000 -0500 @@ -84,7 +84,6 @@ core-y += arch/xen/i386/kernel/ \ drivers-$(CONFIG_MATH_EMULATION) += arch/i386/math-emu/ drivers-$(CONFIG_PCI) += arch/xen/i386/pci/ # must be linked after kernel/ -drivers-$(CONFIG_OPROFILE) += arch/i386/oprofile/ drivers-$(CONFIG_PM) += arch/i386/power/ # for clean diff -Naurp xen-unstable.hg-20050823-nooprofile/linux-2.6.12-xen0/arch/xen/Kconfig xen-unstable.hg-20050823/linux-2.6.12-xen0/arch/xen/Kconfig --- xen-unstable.hg-20050823-nooprofile/linux-2.6.12-xen0/arch/xen/Kconfig 2005-08-24 07:44:51.000000000 -0500 +++ xen-unstable.hg-20050823/linux-2.6.12-xen0/arch/xen/Kconfig 2005-08-23 07:05:17.000000000 -0500 @@ -200,4 +200,6 @@ source "crypto/Kconfig" source "lib/Kconfig" +source "arch/xen/oprofile/Kconfig" + source "arch/xen/Kconfig.debug" diff -Naurp xen-unstable.hg-20050823-nooprofile/linux-2.6.12-xen0/arch/xen/kernel/evtchn.c xen-unstable.hg-20050823/linux-2.6.12-xen0/arch/xen/kernel/evtchn.c --- xen-unstable.hg-20050823-nooprofile/linux-2.6.12-xen0/arch/xen/kernel/evtchn.c 2005-08-24 07:44:51.000000000 -0500 +++ xen-unstable.hg-20050823/linux-2.6.12-xen0/arch/xen/kernel/evtchn.c 2005-08-23 07:05:17.000000000 -0500 @@ -44,11 +44,16 @@ #include #include +int virq_to_phys(int virq); + #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) EXPORT_SYMBOL(force_evtchn_callback); EXPORT_SYMBOL(evtchn_do_upcall); EXPORT_SYMBOL(bind_evtchn_to_irq); EXPORT_SYMBOL(unbind_evtchn_from_irq); +EXPORT_SYMBOL(virq_to_phys); +EXPORT_SYMBOL(bind_virq_to_irq); +EXPORT_SYMBOL(unbind_virq_from_irq); #endif /* @@ -180,6 +185,15 @@ static int find_unbound_irq(void) return irq; } +int virq_to_phys(int virq) +{ + int cpu = smp_processor_id(); + + if (virq >= NR_VIRQS) + return -1; + return per_cpu(virq_to_irq,cpu)[virq]; +} + int bind_virq_to_irq(int virq) { evtchn_op_t op; diff -Naurp xen-unstable.hg-20050823-nooprofile/linux-2.6.12-xen0/arch/xen/Makefile xen-unstable.hg-20050823/linux-2.6.12-xen0/arch/xen/Makefile --- xen-unstable.hg-20050823-nooprofile/linux-2.6.12-xen0/arch/xen/Makefile 2005-08-24 07:44:51.000000000 -0500 +++ xen-unstable.hg-20050823/linux-2.6.12-xen0/arch/xen/Makefile 2005-08-23 07:05:17.000000000 -0500 @@ -32,6 +32,8 @@ ifneq ($(KBUILD_SRC),) $(Q)ln -fsn ../include/asm-$(XENARCH) include2/asm endif +drivers-$(CONFIG_OPROFILE) += arch/xen/oprofile/ + include/.asm-ignore: include/asm @rm -f include/.asm-ignore @mv include/asm include/.asm-ignore diff -Naurp xen-unstable.hg-20050823-nooprofile/linux-2.6.12-xen0/arch/xen/oprofile/Kconfig xen-unstable.hg-20050823/linux-2.6.12-xen0/arch/xen/oprofile/Kconfig --- xen-unstable.hg-20050823-nooprofile/linux-2.6.12-xen0/arch/xen/oprofile/Kconfig 1969-12-31 18:00:00.000000000 -0600 +++ xen-unstable.hg-20050823/linux-2.6.12-xen0/arch/xen/oprofile/Kconfig 2005-08-23 07:05:17.000000000 -0500 @@ -0,0 +1,23 @@ + +menu "Profiling support" + depends on EXPERIMENTAL + +config PROFILING + bool "Profiling support (EXPERIMENTAL)" + help + Say Y here to enable the extended profiling support mechanisms used + by profilers such as OProfile. + + +config OPROFILE + tristate "OProfile system profiling (EXPERIMENTAL)" + depends on PROFILING + help + OProfile is a profiling system capable of profiling the + whole system, include the kernel, kernel modules, libraries, + and applications. + + If unsure, say N. + +endmenu + diff -Naurp xen-unstable.hg-20050823-nooprofile/linux-2.6.12-xen0/arch/xen/oprofile/Makefile xen-unstable.hg-20050823/linux-2.6.12-xen0/arch/xen/oprofile/Makefile --- xen-unstable.hg-20050823-nooprofile/linux-2.6.12-xen0/arch/xen/oprofile/Makefile 1969-12-31 18:00:00.000000000 -0600 +++ xen-unstable.hg-20050823/linux-2.6.12-xen0/arch/xen/oprofile/Makefile 2005-08-23 07:05:17.000000000 -0500 @@ -0,0 +1,9 @@ +obj-$(CONFIG_OPROFILE) += oprofile.o + +DRIVER_OBJS = $(addprefix ../../../drivers/oprofile/, \ + oprof.o cpu_buffer.o buffer_sync.o \ + event_buffer.o oprofile_files.o \ + oprofilefs.o oprofile_stats.o \ + timer_int.o ) + +oprofile-y := $(DRIVER_OBJS) pmc.o diff -Naurp xen-unstable.hg-20050823-nooprofile/linux-2.6.12-xen0/arch/xen/oprofile/op_counter.h xen-unstable.hg-20050823/linux-2.6.12-xen0/arch/xen/oprofile/op_counter.h --- xen-unstable.hg-20050823-nooprofile/linux-2.6.12-xen0/arch/xen/oprofile/op_counter.h 1969-12-31 18:00:00.000000000 -0600 +++ xen-unstable.hg-20050823/linux-2.6.12-xen0/arch/xen/oprofile/op_counter.h 2005-08-23 07:05:17.000000000 -0500 @@ -0,0 +1,29 @@ +/** + * @file op_counter.h + * + * @remark Copyright 2002 OProfile authors + * @remark Read the file COPYING + * + * @author John Levon + */ + +#ifndef OP_COUNTER_H +#define OP_COUNTER_H + +#define OP_MAX_COUNTER 8 + +/* Per-perfctr configuration as set via + * oprofilefs. + */ +struct op_counter_config { + unsigned long count; + unsigned long enabled; + unsigned long event; + unsigned long kernel; + unsigned long user; + unsigned long unit_mask; +}; + +extern struct op_counter_config counter_config[]; + +#endif /* OP_COUNTER_H */ diff -Naurp xen-unstable.hg-20050823-nooprofile/linux-2.6.12-xen0/arch/xen/oprofile/pmc.c xen-unstable.hg-20050823/linux-2.6.12-xen0/arch/xen/oprofile/pmc.c --- xen-unstable.hg-20050823-nooprofile/linux-2.6.12-xen0/arch/xen/oprofile/pmc.c 1969-12-31 18:00:00.000000000 -0600 +++ xen-unstable.hg-20050823/linux-2.6.12-xen0/arch/xen/oprofile/pmc.c 2005-08-23 07:31:26.000000000 -0500 @@ -0,0 +1,334 @@ +/** + * @file nmi_int.c + * + * @remark Copyright 2002 OProfile authors + * @remark Read the file COPYING + * + * @author John Levon + * + * Modified by Aravind Menon for Xen + * These modifications are: + * Copyright (C) 2005 Hewlett-Packard Co. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "op_counter.h" + +static int pmc_start(void); +static void pmc_stop(void); + +/* 0 == registered but off, 1 == registered and on */ +static int pmc_enabled = 0; +static int num_events = 0; +static int is_primary = 0; + +#ifdef CONFIG_PM + +static int pmc_suspend(struct sys_device *dev, u32 state) +{ + if (pmc_enabled == 1) + pmc_stop(); + return 0; +} + + +static int pmc_resume(struct sys_device *dev) +{ + if (pmc_enabled == 1) + pmc_start(); + return 0; +} + + +static struct sysdev_class oprofile_sysclass = { + set_kset_name("oprofile"), + .resume = pmc_resume, + .suspend = pmc_suspend, +}; + + +static struct sys_device device_oprofile = { + .id = 0, + .cls = &oprofile_sysclass, +}; + + +static int __init init_driverfs(void) +{ + int error; + if (!(error = sysdev_class_register(&oprofile_sysclass))) + error = sysdev_register(&device_oprofile); + return error; +} + + +static void __exit exit_driverfs(void) +{ + sysdev_unregister(&device_oprofile); + sysdev_class_unregister(&oprofile_sysclass); +} + +#else +#define init_driverfs() do { } while (0) +#define exit_driverfs() do { } while (0) +#endif /* CONFIG_PM */ + +unsigned long long oprofile_samples = 0; + +static irqreturn_t pmc_ovf_interrupt (int irq, void *dev_id, struct pt_regs *regs) +{ + int head, tail; + shared_info_t *s = HYPERVISOR_shared_info; + + head = s->event_head; + tail = s->event_tail; + + /* oprofile_add_sample will also handle samples from other domains */ + + if (tail > head) { + while (tail < MAX_OPROF_EVENTS) { + oprofile_add_sample_xen(s->event_log[tail].eip, + s->event_log[tail].mode, + s->event_log[tail].event); + /*printk(KERN_INFO "pmc_sample: %p, %d, %d\n", + s->event_log[tail].eip, s->event_log[tail].mode, + s->event_log[tail].event);*/ + oprofile_samples++; + tail++; + } + tail = 0; + } + while (tail < head) { + oprofile_add_sample_xen(s->event_log[tail].eip, + s->event_log[tail].mode, s->event_log[tail].event); + /*printk(KERN_INFO "pmc_sample: %p, %d, %d\n", + s->event_log[tail].eip, s->event_log[tail].mode, + s->event_log[tail].event);*/ + oprofile_samples++; + tail++; + } + + s->event_tail = tail; + s->losing_samples = 0; + + return IRQ_HANDLED; +} + +extern int virq_to_phys(int virq); + +static int pmc_setup(void) +{ + int ret; + + if ((ret = request_irq(bind_virq_to_irq(VIRQ_PMC_OVF), + pmc_ovf_interrupt, SA_INTERRUPT, "pmc_ovf", NULL))) + goto release_irq; + + if (is_primary) { + ret = HYPERVISOR_pmc_op(PMC_RESERVE_COUNTERS, (u64)NULL, (u64)NULL); + //printk(KERN_INFO "pmc_setup: reserve_counters: ret %d\n", ret); + + ret = HYPERVISOR_pmc_op(PMC_SETUP_EVENTS, (u64)&counter_config, (u64)num_events); + //printk(KERN_INFO "pmc_setup: setup_events: ret %d\n", ret); + } + + ret = HYPERVISOR_pmc_op(PMC_ENABLE_VIRQ, (u64)NULL, (u64)NULL); + //printk(KERN_INFO "pmc_setup: enable_virq: ret %d\n", ret); + + pmc_enabled = 1; + return 0; + +release_irq: + free_irq(virq_to_phys(VIRQ_PMC_OVF), NULL); + unbind_virq_from_irq(VIRQ_PMC_OVF); + + return ret; +} + +static void pmc_shutdown(void) +{ + int ret; + pmc_enabled = 0; + + ret = HYPERVISOR_pmc_op(PMC_DISABLE_VIRQ, (u64)NULL, (u64)NULL); + //printk(KERN_INFO "pmc_shutdown: disable_virq: ret %d\n", ret); + + if (is_primary) { + ret = HYPERVISOR_pmc_op(PMC_RELEASE_COUNTERS, (u64)NULL, (u64)NULL); + //printk(KERN_INFO "pmc_shutdown: release_counters: ret %d\n", ret); + } + + free_irq(virq_to_phys(VIRQ_PMC_OVF), NULL); + unbind_virq_from_irq(VIRQ_PMC_OVF); +} + +static int pmc_start(void) +{ + int ret = 0; + if (is_primary) + ret = HYPERVISOR_pmc_op(PMC_START, (u64)NULL, (u64)NULL); + //printk(KERN_INFO "pmc_start: ret %d\n", ret); + return ret; +} + +static void pmc_stop(void) +{ + int ret = 0; + if (is_primary) + ret = HYPERVISOR_pmc_op(PMC_STOP, (u64)NULL, (u64)NULL); + //printk(KERN_INFO "pmc_stop: ret %d\n", ret); + printk(KERN_INFO "pmc: oprofile samples %llu, active %llu, passive %llu, other %llu, buffering losses %llu, NMI restarted %d\n", + oprofile_samples, HYPERVISOR_shared_info->active_samples, HYPERVISOR_shared_info->passive_samples, + HYPERVISOR_shared_info->other_samples, HYPERVISOR_shared_info->samples_lost, HYPERVISOR_shared_info->nmi_restarts); +} + +static int pmc_set_active(int *active_domains, unsigned int adomains) +{ + int ret = 0; + if (is_primary) + ret = HYPERVISOR_pmc_op(PMC_SET_ACTIVE, + (u64)active_domains, (u64)adomains); + return ret; +} + +static int pmc_set_passive(int *passive_domains, unsigned int pdomains) +{ + int ret = 0; + if (is_primary) + ret = HYPERVISOR_pmc_op(PMC_SET_PASSIVE, + (u64)passive_domains, (u64)pdomains); + return ret; +} + +struct op_counter_config counter_config[OP_MAX_COUNTER]; + +static int pmc_create_files(struct super_block * sb, struct dentry * root) +{ + unsigned int i; + + for (i = 0; i < num_events; ++i) { + struct dentry * dir; + char buf[2]; + + snprintf(buf, 2, "%d", i); + dir = oprofilefs_mkdir(sb, root, buf); + oprofilefs_create_ulong(sb, dir, "enabled", &counter_config[i].enabled); + oprofilefs_create_ulong(sb, dir, "event", &counter_config[i].event); + oprofilefs_create_ulong(sb, dir, "count", &counter_config[i].count); + oprofilefs_create_ulong(sb, dir, "unit_mask", &counter_config[i].unit_mask); + oprofilefs_create_ulong(sb, dir, "kernel", &counter_config[i].kernel); + oprofilefs_create_ulong(sb, dir, "user", &counter_config[i].user); + } + + //printk(KERN_INFO "pmc_create_files\n"); + return 0; +} + + +struct oprofile_operations pmc_ops = { + .create_files = pmc_create_files, + .set_active = pmc_set_active, + .set_passive = pmc_set_passive, + .setup = pmc_setup, + .shutdown = pmc_shutdown, + .start = pmc_start, + .stop = pmc_stop +}; + + +static void __init p4_init(void) +{ + __u8 cpu_model = current_cpu_data.x86_model; + + if (cpu_model > 3) + pmc_ops.cpu_type = "type_unknown"; + + /* We always use a non-HT system because that goves us more events */ + pmc_ops.cpu_type = "i386/p4"; +} + + +static void __init ppro_init(void) +{ + __u8 cpu_model = current_cpu_data.x86_model; + + if (cpu_model > 0xd) + pmc_ops.cpu_type = "type_unknown"; + + if (cpu_model == 9) { + pmc_ops.cpu_type = "i386/p6_mobile"; + } else if (cpu_model > 5) { + pmc_ops.cpu_type = "i386/piii"; + } else if (cpu_model > 2) { + pmc_ops.cpu_type = "i386/pii"; + } else { + pmc_ops.cpu_type = "i386/ppro"; + } +} + +/* in order to get driverfs right */ +static int using_pmc; + +int __init oprofile_arch_init(struct oprofile_operations * ops) +{ + printk (KERN_INFO "oprofile_arch_init"); + int ret = HYPERVISOR_pmc_op(PMC_INIT, (u64)&num_events, (u64)&is_primary); + + if (!ret) { + __u8 vendor = current_cpu_data.x86_vendor; + __u8 family = current_cpu_data.x86; + + if (vendor == X86_VENDOR_INTEL) { + switch (family) { + /* Pentium IV */ + case 0xf: + p4_init(); + break; + /* A P6-class processor */ + case 6: + ppro_init(); + break; + default: + pmc_ops.cpu_type = "type_unknown"; + } + } else if (vendor == X86_VENDOR_AMD) { + switch (family) { + case 6: + pmc_ops.cpu_type = "i386/athlon"; + break; + case 0xf: + pmc_ops.cpu_type = "x86-64/hammer"; + break; + default: + pmc_ops.cpu_type = "type_unknown"; + } + } else pmc_ops.cpu_type = "type_unknown"; + + init_driverfs(); + using_pmc = 1; + *ops = pmc_ops; + } + printk (KERN_INFO "oprofile_arch_init: ret %d, events %d, is_primary %d\n", ret, num_events, is_primary); + return ret; +} + + +void __exit oprofile_arch_exit(void) +{ + if (using_pmc) + exit_driverfs(); + + if (is_primary) + HYPERVISOR_pmc_op(PMC_SHUTDOWN, (u64)NULL, (u64)NULL); + +} diff -Naurp xen-unstable.hg-20050823-nooprofile/linux-2.6.12-xen0/arch/xen/x86_64/Makefile xen-unstable.hg-20050823/linux-2.6.12-xen0/arch/xen/x86_64/Makefile --- xen-unstable.hg-20050823-nooprofile/linux-2.6.12-xen0/arch/xen/x86_64/Makefile 2005-08-24 07:44:51.000000000 -0500 +++ xen-unstable.hg-20050823/linux-2.6.12-xen0/arch/xen/x86_64/Makefile 2005-08-23 07:05:17.000000000 -0500 @@ -69,7 +69,6 @@ libs-y += arch/x86_64/lib/ core-y += arch/xen/x86_64/kernel/ arch/xen/x86_64/mm/ core-$(CONFIG_IA32_EMULATION) += arch/xen/x86_64/ia32/ drivers-$(CONFIG_PCI) += arch/xen/x86_64/pci/ -drivers-$(CONFIG_OPROFILE) += arch/x86_64/oprofile/ # for clean obj- += kernel/ mm/ pci/ diff -Naurp xen-unstable.hg-20050823-nooprofile/linux-2.6.12-xen0/drivers/oprofile/buffer_sync.c xen-unstable.hg-20050823/linux-2.6.12-xen0/drivers/oprofile/buffer_sync.c --- xen-unstable.hg-20050823-nooprofile/linux-2.6.12-xen0/drivers/oprofile/buffer_sync.c 2005-06-17 14:48:29.000000000 -0500 +++ xen-unstable.hg-20050823/linux-2.6.12-xen0/drivers/oprofile/buffer_sync.c 2005-08-23 07:05:17.000000000 -0500 @@ -6,6 +6,10 @@ * * @author John Levon * + * Modified by Aravind Menon for Xen + * These modifications are: + * Copyright (C) 2005 Hewlett-Packard Co. + * * This is the core of the buffer management. Each * CPU buffer is processed and entered into the * global event buffer. Such processing is necessary @@ -265,13 +269,30 @@ static void add_cpu_switch(int i) last_cookie = ~0UL; } -static void add_kernel_ctx_switch(unsigned int in_kernel) +static void add_cpu_mode_switch(unsigned int cpu_mode) { add_event_entry(ESCAPE_CODE); - if (in_kernel) - add_event_entry(KERNEL_ENTER_SWITCH_CODE); - else - add_event_entry(KERNEL_EXIT_SWITCH_CODE); + switch (cpu_mode) + { + case CPU_MODE_USER: + add_event_entry(USER_ENTER_SWITCH_CODE); + break; + case CPU_MODE_KERNEL: + add_event_entry(KERNEL_ENTER_SWITCH_CODE); + break; + case CPU_MODE_XEN: + add_event_entry(XEN_ENTER_SWITCH_CODE); + break; + default: + break; + } +} + +static void add_dom_switch(int domain_id) +{ + add_event_entry(ESCAPE_CODE); + add_event_entry(DOMAIN_SWITCH_CODE); + add_event_entry(domain_id); } static void @@ -337,10 +358,9 @@ static int add_us_sample(struct mm_struc * sample is converted into a persistent dentry/offset pair * for later lookup from userspace. */ -static int -add_sample(struct mm_struct * mm, struct op_sample * s, int in_kernel) +static int add_sample(struct mm_struct * mm, struct op_sample * s, int cpu_mode) { - if (in_kernel) { + if (cpu_mode >= CPU_MODE_KERNEL) { add_sample_entry(s->eip, s->event); return 1; } else if (mm) { @@ -374,6 +394,11 @@ static inline int is_code(unsigned long { return val == ESCAPE_CODE; } + +static inline int is_dom_switch(unsigned long val) +{ + return val == DOMAIN_SWITCH_ESCAPE_CODE; +} /* "acquire" as many cpu buffer slots as we can */ @@ -489,10 +514,11 @@ void sync_buffer(int cpu) struct mm_struct *mm = NULL; struct task_struct * new; unsigned long cookie = 0; - int in_kernel = 1; + int cpu_mode = 1; unsigned int i; sync_buffer_state state = sb_buffer_start; unsigned long available; + int domain_switch = 0; down(&buffer_sem); @@ -506,12 +532,12 @@ void sync_buffer(int cpu) struct op_sample * s = &cpu_buf->buffer[cpu_buf->tail_pos]; if (is_code(s->eip)) { - if (s->event <= CPU_IS_KERNEL) { + if (s->event <= CPU_MODE_MAX) { /* kernel/userspace switch */ - in_kernel = s->event; + cpu_mode = s->event; if (state == sb_buffer_start) state = sb_sample_start; - add_kernel_ctx_switch(s->event); + add_cpu_mode_switch(s->event); } else if (s->event == CPU_TRACE_BEGIN) { state = sb_bt_start; add_trace_begin(); @@ -528,11 +554,23 @@ void sync_buffer(int cpu) add_user_ctx_switch(new, cookie); } } else { - if (state >= sb_bt_start && - !add_sample(mm, s, in_kernel)) { - if (state == sb_bt_start) { - state = sb_bt_ignore; - atomic_inc(&oprofile_stats.bt_lost_no_mapping); + if (is_dom_switch(s->eip)) { + add_dom_switch((int)(s->event)); + domain_switch = 1; + } + else { + if (domain_switch) { + add_sample_entry (s->eip, s->event); + domain_switch = 0; + } + else { + if (state >= sb_bt_start && + !add_sample(mm, s, cpu_mode)) { + if (state == sb_bt_start) { + state = sb_bt_ignore; + atomic_inc(&oprofile_stats.bt_lost_no_mapping); + } + } } } } diff -Naurp xen-unstable.hg-20050823-nooprofile/linux-2.6.12-xen0/drivers/oprofile/cpu_buffer.c xen-unstable.hg-20050823/linux-2.6.12-xen0/drivers/oprofile/cpu_buffer.c --- xen-unstable.hg-20050823-nooprofile/linux-2.6.12-xen0/drivers/oprofile/cpu_buffer.c 2005-06-17 14:48:29.000000000 -0500 +++ xen-unstable.hg-20050823/linux-2.6.12-xen0/drivers/oprofile/cpu_buffer.c 2005-08-23 07:05:17.000000000 -0500 @@ -6,6 +6,10 @@ * * @author John Levon * + * Modified by Aravind Menon for Xen + * These modifications are: + * Copyright (C) 2005 Hewlett-Packard Co. + * * Each CPU has a local buffer that stores PC value/event * pairs. We also log context switches when we notice them. * Eventually each CPU's buffer is processed into the global @@ -58,7 +62,7 @@ int alloc_cpu_buffers(void) goto fail; b->last_task = NULL; - b->last_is_kernel = -1; + b->last_cpu_mode = -1; b->tracing = 0; b->buffer_size = buffer_size; b->tail_pos = 0; @@ -117,7 +121,7 @@ void cpu_buffer_reset(struct oprofile_cp * collected will populate the buffer with proper * values to initialize the buffer */ - cpu_buf->last_is_kernel = -1; + cpu_buf->last_cpu_mode = -1; cpu_buf->last_task = NULL; } @@ -180,7 +184,7 @@ add_code(struct oprofile_cpu_buffer * bu * events whenever is_kernel changes */ static int log_sample(struct oprofile_cpu_buffer * cpu_buf, unsigned long pc, - int is_kernel, unsigned long event) + int cpu_mode, unsigned long event) { struct task_struct * task; @@ -191,24 +195,39 @@ static int log_sample(struct oprofile_cp return 0; } - is_kernel = !!is_kernel; + // Ensure a valid cpu mode + if (cpu_mode > CPU_MODE_XEN) + return 0; task = current; - /* notice a switch from user->kernel or vice versa */ - if (cpu_buf->last_is_kernel != is_kernel) { - cpu_buf->last_is_kernel = is_kernel; - add_code(cpu_buf, is_kernel); - } - /* notice a task switch */ - if (cpu_buf->last_task != task) { - cpu_buf->last_task = task; - add_code(cpu_buf, (unsigned long)task); + /* We treat samples from other domains in a special manner: + each sample is preceded by a record with eip equal to ~1UL. + This record is non-sticky i.e. it holds only for the following + sample. The event field of this record stores the domain id.*/ + if (pc == DOMAIN_SWITCH_ESCAPE_CODE) { + add_sample(cpu_buf, pc, event); + return 1; + } else { + /* notice a switch from user->kernel or vice versa */ + if (cpu_buf->last_cpu_mode != cpu_mode) { + cpu_buf->last_cpu_mode = cpu_mode; + add_code(cpu_buf, cpu_mode); + } + + /* notice a task switch */ + if (cpu_buf->last_task != task) { + cpu_buf->last_task = task; + add_code(cpu_buf, (unsigned long)task); + } + + /* Note: at this point, we lose the cpu_mode of a sample + if it is from another domain */ + + add_sample(cpu_buf, pc, event); + return 1; } - - add_sample(cpu_buf, pc, event); - return 1; } static int oprofile_begin_trace(struct oprofile_cpu_buffer * cpu_buf) @@ -229,6 +248,14 @@ static void oprofile_end_trace(struct op cpu_buf->tracing = 0; } +void oprofile_add_sample_xen(unsigned long eip, unsigned int cpu_mode, + unsigned long event) +{ + struct oprofile_cpu_buffer * cpu_buf = &cpu_buffer[smp_processor_id()]; + log_sample(cpu_buf, eip, cpu_mode, event); + + +} void oprofile_add_sample(struct pt_regs * const regs, unsigned long event) { diff -Naurp xen-unstable.hg-20050823-nooprofile/linux-2.6.12-xen0/drivers/oprofile/cpu_buffer.h xen-unstable.hg-20050823/linux-2.6.12-xen0/drivers/oprofile/cpu_buffer.h --- xen-unstable.hg-20050823-nooprofile/linux-2.6.12-xen0/drivers/oprofile/cpu_buffer.h 2005-06-17 14:48:29.000000000 -0500 +++ xen-unstable.hg-20050823/linux-2.6.12-xen0/drivers/oprofile/cpu_buffer.h 2005-08-23 07:05:17.000000000 -0500 @@ -36,7 +36,7 @@ struct oprofile_cpu_buffer { volatile unsigned long tail_pos; unsigned long buffer_size; struct task_struct * last_task; - int last_is_kernel; + int last_cpu_mode; int tracing; struct op_sample * buffer; unsigned long sample_received; @@ -51,7 +51,14 @@ extern struct oprofile_cpu_buffer cpu_bu void cpu_buffer_reset(struct oprofile_cpu_buffer * cpu_buf); /* transient events for the CPU buffer -> event buffer */ -#define CPU_IS_KERNEL 1 -#define CPU_TRACE_BEGIN 2 +#define CPU_MODE_USER 0 +#define CPU_MODE_KERNEL 1 +#define CPU_MODE_XEN 2 +#define CPU_MODE_MAX 2 +#define CPU_TRACE_BEGIN 3 +/* special escape code for indicating next sample in the CPU */ +/* buffer is from another Xen domain */ +#define DOMAIN_SWITCH_ESCAPE_CODE ~1UL + #endif /* OPROFILE_CPU_BUFFER_H */ diff -Naurp xen-unstable.hg-20050823-nooprofile/linux-2.6.12-xen0/drivers/oprofile/event_buffer.c xen-unstable.hg-20050823/linux-2.6.12-xen0/drivers/oprofile/event_buffer.c --- xen-unstable.hg-20050823-nooprofile/linux-2.6.12-xen0/drivers/oprofile/event_buffer.c 2005-06-17 14:48:29.000000000 -0500 +++ xen-unstable.hg-20050823/linux-2.6.12-xen0/drivers/oprofile/event_buffer.c 2005-08-23 07:05:17.000000000 -0500 @@ -56,6 +56,7 @@ void add_event_entry(unsigned long value /* Wake up the waiting process if any. This happens * on "echo 0 >/dev/oprofile/enable" so the daemon * processes the data remaining in the event buffer. + * also called on echo 1 > /dev/oprofile/dump */ void wake_up_buffer_waiter(void) { diff -Naurp xen-unstable.hg-20050823-nooprofile/linux-2.6.12-xen0/drivers/oprofile/event_buffer.h xen-unstable.hg-20050823/linux-2.6.12-xen0/drivers/oprofile/event_buffer.h --- xen-unstable.hg-20050823-nooprofile/linux-2.6.12-xen0/drivers/oprofile/event_buffer.h 2005-06-17 14:48:29.000000000 -0500 +++ xen-unstable.hg-20050823/linux-2.6.12-xen0/drivers/oprofile/event_buffer.h 2005-08-23 07:05:17.000000000 -0500 @@ -5,6 +5,10 @@ * @remark Read the file COPYING * * @author John Levon + * + * Modified by Aravind Menon for Xen + * These modifications are: + * Copyright (C) 2005 Hewlett-Packard Co. */ #ifndef EVENT_BUFFER_H @@ -29,11 +33,13 @@ void wake_up_buffer_waiter(void); #define CPU_SWITCH_CODE 2 #define COOKIE_SWITCH_CODE 3 #define KERNEL_ENTER_SWITCH_CODE 4 -#define KERNEL_EXIT_SWITCH_CODE 5 +#define USER_ENTER_SWITCH_CODE 5 #define MODULE_LOADED_CODE 6 #define CTX_TGID_CODE 7 #define TRACE_BEGIN_CODE 8 #define TRACE_END_CODE 9 +#define XEN_ENTER_SWITCH_CODE 10 +#define DOMAIN_SWITCH_CODE 11 /* add data to the event buffer */ void add_event_entry(unsigned long data); diff -Naurp xen-unstable.hg-20050823-nooprofile/linux-2.6.12-xen0/drivers/oprofile/oprof.c xen-unstable.hg-20050823/linux-2.6.12-xen0/drivers/oprofile/oprof.c --- xen-unstable.hg-20050823-nooprofile/linux-2.6.12-xen0/drivers/oprofile/oprof.c 2005-06-17 14:48:29.000000000 -0500 +++ xen-unstable.hg-20050823/linux-2.6.12-xen0/drivers/oprofile/oprof.c 2005-08-23 07:05:17.000000000 -0500 @@ -5,6 +5,10 @@ * @remark Read the file COPYING * * @author John Levon + * + * Modified by Aravind Menon for Xen + * These modifications are: + * Copyright (C) 2005 Hewlett-Packard Co. */ #include @@ -33,6 +37,25 @@ static DECLARE_MUTEX(start_sem); */ static int timer = 0; +extern unsigned int adomains, pdomains; +extern int active_domains[MAX_OPROF_DOMAINS], passive_domains[MAX_OPROF_DOMAINS]; + +int oprofile_set_active(void) +{ + if (oprofile_ops.set_active) + return oprofile_ops.set_active(active_domains, adomains); + + return -EINVAL; +} + +int oprofile_set_passive(void) +{ + if (oprofile_ops.set_passive) + return oprofile_ops.set_passive(passive_domains, pdomains); + + return -EINVAL; +} + int oprofile_setup(void) { int err; diff -Naurp xen-unstable.hg-20050823-nooprofile/linux-2.6.12-xen0/drivers/oprofile/oprofile_files.c xen-unstable.hg-20050823/linux-2.6.12-xen0/drivers/oprofile/oprofile_files.c --- xen-unstable.hg-20050823-nooprofile/linux-2.6.12-xen0/drivers/oprofile/oprofile_files.c 2005-06-17 14:48:29.000000000 -0500 +++ xen-unstable.hg-20050823/linux-2.6.12-xen0/drivers/oprofile/oprofile_files.c 2005-08-23 07:05:17.000000000 -0500 @@ -5,10 +5,16 @@ * @remark Read the file COPYING * * @author John Levon + * + * Modified by Aravind Menon for Xen + * These modifications are: + * Copyright (C) 2005 Hewlett-Packard Co. */ #include #include +#include +#include #include "event_buffer.h" #include "oprofile_stats.h" @@ -117,11 +123,140 @@ static ssize_t dump_write(struct file * static struct file_operations dump_fops = { .write = dump_write, }; - + +#define TMPBUFSIZE 50 + +unsigned int adomains = 0; +long active_domains[MAX_OPROF_DOMAINS]; + +extern int oprofile_set_active(void); + +static ssize_t adomain_write(struct file *file, char const __user *buf, size_t count, loff_t * offset) +{ + char tmpbuf[TMPBUFSIZE]; + char *startp = tmpbuf; + char *endp = tmpbuf; + int i; + unsigned long val; + + if (*offset) + return -EINVAL; + if (!count) + return 0; + if (count > TMPBUFSIZE - 1) + return -EINVAL; + + memset(tmpbuf, 0x0, TMPBUFSIZE); + + if (copy_from_user(tmpbuf, buf, count)) + return -EFAULT; + + for (i = 0; i < MAX_OPROF_DOMAINS; i++) + active_domains[i] = -1; + adomains = 0; + + while (1) { + val = simple_strtol(startp, &endp, 0); + if (endp == startp) + break; + while (ispunct(*endp)) + endp++; + active_domains[adomains++] = val; + if (adomains >= MAX_OPROF_DOMAINS) + break; + startp = endp; + } + if (oprofile_set_active()) + return -EINVAL; + return count; +} + +static ssize_t adomain_read(struct file *file, char __user * buf, size_t count, loff_t * offset) +{ + char tmpbuf[TMPBUFSIZE]; + size_t len = 0; + int i; + /* This is all screwed up if we run out of space */ + for (i = 0; i < adomains; i++) + len += snprintf(tmpbuf + len, TMPBUFSIZE - len, "%u ", (unsigned int)active_domains[i]); + len += snprintf(tmpbuf + len, TMPBUFSIZE - len, "\n"); + return simple_read_from_buffer((void __user *)buf, count, offset, tmpbuf, len); +} + + +static struct file_operations active_domain_ops = { + .read = adomain_read, + .write = adomain_write, +}; + +unsigned int pdomains = 0; +long passive_domains[MAX_OPROF_DOMAINS]; + +extern int oprofile_set_passive(void); + +static ssize_t pdomain_write(struct file *file, char const __user *buf, size_t count, loff_t * offset) +{ + char tmpbuf[TMPBUFSIZE]; + char *startp = tmpbuf; + char *endp = tmpbuf; + int i; + unsigned long val; + + if (*offset) + return -EINVAL; + if (!count) + return 0; + if (count > TMPBUFSIZE - 1) + return -EINVAL; + + memset(tmpbuf, 0x0, TMPBUFSIZE); + + if (copy_from_user(tmpbuf, buf, count)) + return -EFAULT; + + for (i = 0; i < MAX_OPROF_DOMAINS; i++) + passive_domains[i] = -1; + pdomains = 0; + + while (1) { + val = simple_strtol(startp, &endp, 0); + if (endp == startp) + break; + while (ispunct(*endp)) + endp++; + passive_domains[pdomains++] = val; + if (pdomains >= MAX_OPROF_DOMAINS) + break; + startp = endp; + } + if (oprofile_set_passive()) + return -EINVAL; + return count; +} + +static ssize_t pdomain_read(struct file *file, char __user * buf, size_t count, loff_t * offset) +{ + char tmpbuf[TMPBUFSIZE]; + size_t len = 0; + int i; + /* This is all screwed up if we run out of space */ + for (i = 0; i < pdomains; i++) + len += snprintf(tmpbuf + len, TMPBUFSIZE - len, "%u ", (unsigned int)passive_domains[i]); + len += snprintf (tmpbuf + len, TMPBUFSIZE - len, "\n"); + return simple_read_from_buffer((void __user *)buf, count, offset, tmpbuf, len); +} + +static struct file_operations passive_domain_ops = { + .read = pdomain_read, + .write = pdomain_write, +}; + void oprofile_create_files(struct super_block * sb, struct dentry * root) { oprofilefs_create_file(sb, root, "enable", &enable_fops); oprofilefs_create_file_perm(sb, root, "dump", &dump_fops, 0666); + oprofilefs_create_file(sb, root, "active_domains", &active_domain_ops); + oprofilefs_create_file(sb, root, "passive_domains", &passive_domain_ops); oprofilefs_create_file(sb, root, "buffer", &event_buffer_fops); oprofilefs_create_ulong(sb, root, "buffer_size", &fs_buffer_size); oprofilefs_create_ulong(sb, root, "buffer_watershed", &fs_buffer_watershed); diff -Naurp xen-unstable.hg-20050823-nooprofile/linux-2.6.12-xen0/include/asm-x86_64/offset.h xen-unstable.hg-20050823/linux-2.6.12-xen0/include/asm-x86_64/offset.h --- xen-unstable.hg-20050823-nooprofile/linux-2.6.12-xen0/include/asm-x86_64/offset.h 1969-12-31 18:00:00.000000000 -0600 +++ xen-unstable.hg-20050823/linux-2.6.12-xen0/include/asm-x86_64/offset.h 2005-08-23 07:06:19.000000000 -0500 @@ -0,0 +1,43 @@ +#ifndef __ASM_OFFSETS_H__ +#define __ASM_OFFSETS_H__ +/* + * DO NOT MODIFY. + * + * This file was generated by arch/xen/Makefile + * + */ + +#define tsk_state 0 /* offsetof(struct task_struct, state) */ +#define tsk_flags 24 /* offsetof(struct task_struct, flags) */ +#define tsk_thread 752 /* offsetof(struct task_struct, thread) */ +#define tsk_pid 252 /* offsetof(struct task_struct, pid) */ + +#define threadinfo_flags 16 /* offsetof(struct thread_info, flags) */ +#define threadinfo_addr_limit 32 /* offsetof(struct thread_info, addr_limit) */ +#define threadinfo_preempt_count 28 /* offsetof(struct thread_info, preempt_count) */ + +#define pda_kernelstack 24 /* offsetof(struct x8664_pda, kernelstack) */ +#define pda_oldrsp 32 /* offsetof(struct x8664_pda, oldrsp) */ +#define pda_pcurrent 0 /* offsetof(struct x8664_pda, pcurrent) */ +#define pda_irqrsp 40 /* offsetof(struct x8664_pda, irqrsp) */ +#define pda_irqcount 48 /* offsetof(struct x8664_pda, irqcount) */ +#define pda_cpunumber 52 /* offsetof(struct x8664_pda, cpunumber) */ +#define pda_irqstackptr 56 /* offsetof(struct x8664_pda, irqstackptr) */ + +#define IA32_SIGCONTEXT_eax 44 /* offsetof(struct sigcontext_ia32, eax) */ +#define IA32_SIGCONTEXT_ebx 32 /* offsetof(struct sigcontext_ia32, ebx) */ +#define IA32_SIGCONTEXT_ecx 40 /* offsetof(struct sigcontext_ia32, ecx) */ +#define IA32_SIGCONTEXT_edx 36 /* offsetof(struct sigcontext_ia32, edx) */ +#define IA32_SIGCONTEXT_esi 20 /* offsetof(struct sigcontext_ia32, esi) */ +#define IA32_SIGCONTEXT_edi 16 /* offsetof(struct sigcontext_ia32, edi) */ +#define IA32_SIGCONTEXT_ebp 24 /* offsetof(struct sigcontext_ia32, ebp) */ +#define IA32_SIGCONTEXT_esp 28 /* offsetof(struct sigcontext_ia32, esp) */ +#define IA32_SIGCONTEXT_eip 56 /* offsetof(struct sigcontext_ia32, eip) */ + +#define IA32_RT_SIGFRAME_sigcontext 164 /* offsetof (struct rt_sigframe32, uc.uc_mcontext) */ + +#define pbe_address 0 /* offsetof(struct pbe, address) */ +#define pbe_orig_address 8 /* offsetof(struct pbe, orig_address) */ +#define pbe_next 24 /* offsetof(struct pbe, next) */ + +#endif diff -Naurp xen-unstable.hg-20050823-nooprofile/linux-2.6.12-xen0/include/asm-xen/asm-i386/hypercall.h xen-unstable.hg-20050823/linux-2.6.12-xen0/include/asm-xen/asm-i386/hypercall.h --- xen-unstable.hg-20050823-nooprofile/linux-2.6.12-xen0/include/asm-xen/asm-i386/hypercall.h 2005-08-24 07:44:51.000000000 -0500 +++ xen-unstable.hg-20050823/linux-2.6.12-xen0/include/asm-xen/asm-i386/hypercall.h 2005-08-23 07:05:17.000000000 -0500 @@ -576,4 +576,21 @@ HYPERVISOR_vcpu_pickle( return ret; } + +static inline int +HYPERVISOR_pmc_op( + int op, unsigned int arg1, unsigned int arg2) +{ + int ret; + unsigned long ign1, ign2, ign3; + + __asm__ __volatile__ ( + TRAP_INSTR + : "=a"(ret), "=b"(ign1), "=c"(ign2), "=d"(ign3) + : "0"(__HYPERVISOR_pmc_op), "1"(op), "2"(arg1), "3"(arg2) + : "memory" ); + + return ret; +} + #endif /* __HYPERCALL_H__ */ diff -Naurp xen-unstable.hg-20050823-nooprofile/linux-2.6.12-xen0/include/asm-xen/asm-x86_64/hypercall.h xen-unstable.hg-20050823/linux-2.6.12-xen0/include/asm-xen/asm-x86_64/hypercall.h --- xen-unstable.hg-20050823-nooprofile/linux-2.6.12-xen0/include/asm-xen/asm-x86_64/hypercall.h 2005-08-24 07:44:51.000000000 -0500 +++ xen-unstable.hg-20050823/linux-2.6.12-xen0/include/asm-xen/asm-x86_64/hypercall.h 2005-08-23 07:05:17.000000000 -0500 @@ -519,4 +519,19 @@ HYPERVISOR_vcpu_pickle( return ret; } +static inline int +HYPERVISOR_pmc_op( + int op, u64 arg1, u64 arg2) +{ + int ret; + + __asm__ __volatile__ ( + TRAP_INSTR + : "=a"(ret) + : "0"(__HYPERVISOR_pmc_op), "D"(op), "S"(arg1), "d"(arg2) + : __syscall_clobber ); + + return ret; +} + #endif /* __HYPERCALL_H__ */ diff -Naurp xen-unstable.hg-20050823-nooprofile/linux-2.6.12-xen0/include/linux/oprofile.h xen-unstable.hg-20050823/linux-2.6.12-xen0/include/linux/oprofile.h --- xen-unstable.hg-20050823-nooprofile/linux-2.6.12-xen0/include/linux/oprofile.h 2005-06-17 14:48:29.000000000 -0500 +++ xen-unstable.hg-20050823/linux-2.6.12-xen0/include/linux/oprofile.h 2005-08-23 07:05:17.000000000 -0500 @@ -8,6 +8,10 @@ * @remark Read the file COPYING * * @author John Levon + * + * Modified by Aravind Menon for Xen + * These modifications are: + * Copyright (C) 2005 Hewlett-Packard Co. */ #ifndef OPROFILE_H @@ -27,6 +31,10 @@ struct oprofile_operations { /* create any necessary configuration files in the oprofile fs. * Optional. */ int (*create_files)(struct super_block * sb, struct dentry * root); + /* setup active domains with Xen */ + int (*set_active)(int *active_domains, unsigned int adomains); + /* setup passive domains with Xen */ + int (*set_passive)(int *passive_domains, unsigned int pdomains); /* Do any necessary interrupt setup. Optional. */ int (*setup)(void); /* Do any necessary interrupt shutdown. Optional. */ @@ -61,6 +69,15 @@ void oprofile_arch_exit(void); */ void oprofile_add_sample(struct pt_regs * const regs, unsigned long event); +/** + * alternative function to Add a sample for Xen. + * It would be better to combine both functions into only one but this would + * require getting parameter cpu_mode(old is_kernel) back to + * oprofile_add_sample() m(Xen is the best location to determine cpu_mode) + */ +extern void oprofile_add_sample_xen(unsigned long eip, unsigned int cpu_mode, + unsigned long event); + /* Use this instead when the PC value is not from the regs. Doesn't * backtrace. */ void oprofile_add_pc(unsigned long pc, int is_kernel, unsigned long event);