diff -Naur xen-unstable/linux-2.6.12-xen0/drivers/oprofile/buffer_sync.c xen-unstable-prof/linux-2.6.12-xen0/drivers/oprofile/buffer_sync.c --- xen-unstable/linux-2.6.12-xen0/drivers/oprofile/buffer_sync.c 2005-06-17 12:48:29.000000000 -0700 +++ xen-unstable-prof/linux-2.6.12-xen0/drivers/oprofile/buffer_sync.c 2005-09-22 18:56:56.000000000 -0700 @@ -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 @@ 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 @@ * 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 @@ { 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 @@ 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 @@ 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 @@ 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 -Naur xen-unstable/linux-2.6.12-xen0/drivers/oprofile/cpu_buffer.c xen-unstable-prof/linux-2.6.12-xen0/drivers/oprofile/cpu_buffer.c --- xen-unstable/linux-2.6.12-xen0/drivers/oprofile/cpu_buffer.c 2005-06-17 12:48:29.000000000 -0700 +++ xen-unstable-prof/linux-2.6.12-xen0/drivers/oprofile/cpu_buffer.c 2005-10-07 16:55:03.000000000 -0700 @@ -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 @@ 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 @@ * 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 @@ * 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 @@ 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 @@ 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 -Naur xen-unstable/linux-2.6.12-xen0/drivers/oprofile/cpu_buffer.h xen-unstable-prof/linux-2.6.12-xen0/drivers/oprofile/cpu_buffer.h --- xen-unstable/linux-2.6.12-xen0/drivers/oprofile/cpu_buffer.h 2005-06-17 12:48:29.000000000 -0700 +++ xen-unstable-prof/linux-2.6.12-xen0/drivers/oprofile/cpu_buffer.h 2005-09-22 18:56:56.000000000 -0700 @@ -36,7 +36,7 @@ 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 @@ 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 -Naur xen-unstable/linux-2.6.12-xen0/drivers/oprofile/event_buffer.c xen-unstable-prof/linux-2.6.12-xen0/drivers/oprofile/event_buffer.c --- xen-unstable/linux-2.6.12-xen0/drivers/oprofile/event_buffer.c 2005-06-17 12:48:29.000000000 -0700 +++ xen-unstable-prof/linux-2.6.12-xen0/drivers/oprofile/event_buffer.c 2005-09-22 18:56:56.000000000 -0700 @@ -56,6 +56,7 @@ /* 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 -Naur xen-unstable/linux-2.6.12-xen0/drivers/oprofile/event_buffer.h xen-unstable-prof/linux-2.6.12-xen0/drivers/oprofile/event_buffer.h --- xen-unstable/linux-2.6.12-xen0/drivers/oprofile/event_buffer.h 2005-06-17 12:48:29.000000000 -0700 +++ xen-unstable-prof/linux-2.6.12-xen0/drivers/oprofile/event_buffer.h 2005-09-22 18:56:56.000000000 -0700 @@ -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 @@ #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 -Naur xen-unstable/linux-2.6.12-xen0/drivers/oprofile/oprof.c xen-unstable-prof/linux-2.6.12-xen0/drivers/oprofile/oprof.c --- xen-unstable/linux-2.6.12-xen0/drivers/oprofile/oprof.c 2005-06-17 12:48:29.000000000 -0700 +++ xen-unstable-prof/linux-2.6.12-xen0/drivers/oprofile/oprof.c 2005-09-22 18:56:56.000000000 -0700 @@ -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 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 -Naur xen-unstable/linux-2.6.12-xen0/drivers/oprofile/oprofile_files.c xen-unstable-prof/linux-2.6.12-xen0/drivers/oprofile/oprofile_files.c --- xen-unstable/linux-2.6.12-xen0/drivers/oprofile/oprofile_files.c 2005-06-17 12:48:29.000000000 -0700 +++ xen-unstable-prof/linux-2.6.12-xen0/drivers/oprofile/oprofile_files.c 2005-09-22 18:56:56.000000000 -0700 @@ -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 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 -Naur xen-unstable/linux-2.6.12-xen0/include/linux/oprofile.h xen-unstable-prof/linux-2.6.12-xen0/include/linux/oprofile.h --- xen-unstable/linux-2.6.12-xen0/include/linux/oprofile.h 2005-06-17 12:48:29.000000000 -0700 +++ xen-unstable-prof/linux-2.6.12-xen0/include/linux/oprofile.h 2005-09-22 18:56:56.000000000 -0700 @@ -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 @@ /* 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_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);