Loading arch/Kconfig +2 −0 Original line number Diff line number Diff line Loading @@ -6,6 +6,8 @@ config OPROFILE tristate "OProfile system profiling (EXPERIMENTAL)" depends on PROFILING depends on HAVE_OPROFILE select TRACING select RING_BUFFER help OProfile is a profiling system capable of profiling the whole system, include the kernel, kernel modules, libraries, Loading arch/x86/oprofile/op_model_amd.c +91 −133 Original line number Diff line number Diff line Loading @@ -2,7 +2,7 @@ * @file op_model_amd.c * athlon / K7 / K8 / Family 10h model-specific MSR operations * * @remark Copyright 2002-2008 OProfile authors * @remark Copyright 2002-2009 OProfile authors * @remark Read the file COPYING * * @author John Levon Loading Loading @@ -60,56 +60,10 @@ static unsigned long reset_value[NUM_COUNTERS]; #define IBS_OP_LOW_VALID_BIT (1ULL<<18) /* bit 18 */ #define IBS_OP_LOW_ENABLE (1ULL<<17) /* bit 17 */ /* Codes used in cpu_buffer.c */ /* This produces duplicate code, need to be fixed */ #define IBS_FETCH_BEGIN 3 #define IBS_OP_BEGIN 4 /* The function interface needs to be fixed, something like add data. Should then be added to linux/oprofile.h. */ extern void oprofile_add_ibs_sample(struct pt_regs *const regs, unsigned int *const ibs_sample, int ibs_code); struct ibs_fetch_sample { /* MSRC001_1031 IBS Fetch Linear Address Register */ unsigned int ibs_fetch_lin_addr_low; unsigned int ibs_fetch_lin_addr_high; /* MSRC001_1030 IBS Fetch Control Register */ unsigned int ibs_fetch_ctl_low; unsigned int ibs_fetch_ctl_high; /* MSRC001_1032 IBS Fetch Physical Address Register */ unsigned int ibs_fetch_phys_addr_low; unsigned int ibs_fetch_phys_addr_high; }; #define IBS_FETCH_SIZE 6 #define IBS_OP_SIZE 12 struct ibs_op_sample { /* MSRC001_1034 IBS Op Logical Address Register (IbsRIP) */ unsigned int ibs_op_rip_low; unsigned int ibs_op_rip_high; /* MSRC001_1035 IBS Op Data Register */ unsigned int ibs_op_data1_low; unsigned int ibs_op_data1_high; /* MSRC001_1036 IBS Op Data 2 Register */ unsigned int ibs_op_data2_low; unsigned int ibs_op_data2_high; /* MSRC001_1037 IBS Op Data 3 Register */ unsigned int ibs_op_data3_low; unsigned int ibs_op_data3_high; /* MSRC001_1038 IBS DC Linear Address Register (IbsDcLinAd) */ unsigned int ibs_dc_linear_low; unsigned int ibs_dc_linear_high; /* MSRC001_1039 IBS DC Physical Address Register (IbsDcPhysAd) */ unsigned int ibs_dc_phys_low; unsigned int ibs_dc_phys_high; }; /* * unitialize the APIC for the IBS interrupts if needed on AMD Family10h+ */ static void clear_ibs_nmi(void); static int ibs_allowed; /* AMD Family10h and later */ static int has_ibs; /* AMD Family10h and later */ struct op_ibs_config { unsigned long op_enabled; Loading Loading @@ -200,31 +154,29 @@ static inline int op_amd_handle_ibs(struct pt_regs * const regs, struct op_msrs const * const msrs) { unsigned int low, high; struct ibs_fetch_sample ibs_fetch; struct ibs_op_sample ibs_op; u32 low, high; u64 msr; struct op_entry entry; if (!ibs_allowed) if (!has_ibs) return 1; if (ibs_config.fetch_enabled) { rdmsr(MSR_AMD64_IBSFETCHCTL, low, high); if (high & IBS_FETCH_HIGH_VALID_BIT) { ibs_fetch.ibs_fetch_ctl_high = high; ibs_fetch.ibs_fetch_ctl_low = low; rdmsr(MSR_AMD64_IBSFETCHLINAD, low, high); ibs_fetch.ibs_fetch_lin_addr_high = high; ibs_fetch.ibs_fetch_lin_addr_low = low; rdmsr(MSR_AMD64_IBSFETCHPHYSAD, low, high); ibs_fetch.ibs_fetch_phys_addr_high = high; ibs_fetch.ibs_fetch_phys_addr_low = low; oprofile_add_ibs_sample(regs, (unsigned int *)&ibs_fetch, IBS_FETCH_BEGIN); rdmsrl(MSR_AMD64_IBSFETCHLINAD, msr); oprofile_write_reserve(&entry, regs, msr, IBS_FETCH_CODE, IBS_FETCH_SIZE); oprofile_add_data(&entry, (u32)msr); oprofile_add_data(&entry, (u32)(msr >> 32)); oprofile_add_data(&entry, low); oprofile_add_data(&entry, high); rdmsrl(MSR_AMD64_IBSFETCHPHYSAD, msr); oprofile_add_data(&entry, (u32)msr); oprofile_add_data(&entry, (u32)(msr >> 32)); oprofile_write_commit(&entry); /* reenable the IRQ */ rdmsr(MSR_AMD64_IBSFETCHCTL, low, high); high &= ~IBS_FETCH_HIGH_VALID_BIT; high |= IBS_FETCH_HIGH_ENABLE; low &= IBS_FETCH_LOW_MAX_CNT_MASK; Loading @@ -235,30 +187,29 @@ op_amd_handle_ibs(struct pt_regs * const regs, if (ibs_config.op_enabled) { rdmsr(MSR_AMD64_IBSOPCTL, low, high); if (low & IBS_OP_LOW_VALID_BIT) { rdmsr(MSR_AMD64_IBSOPRIP, low, high); ibs_op.ibs_op_rip_low = low; ibs_op.ibs_op_rip_high = high; rdmsr(MSR_AMD64_IBSOPDATA, low, high); ibs_op.ibs_op_data1_low = low; ibs_op.ibs_op_data1_high = high; rdmsr(MSR_AMD64_IBSOPDATA2, low, high); ibs_op.ibs_op_data2_low = low; ibs_op.ibs_op_data2_high = high; rdmsr(MSR_AMD64_IBSOPDATA3, low, high); ibs_op.ibs_op_data3_low = low; ibs_op.ibs_op_data3_high = high; rdmsr(MSR_AMD64_IBSDCLINAD, low, high); ibs_op.ibs_dc_linear_low = low; ibs_op.ibs_dc_linear_high = high; rdmsr(MSR_AMD64_IBSDCPHYSAD, low, high); ibs_op.ibs_dc_phys_low = low; ibs_op.ibs_dc_phys_high = high; rdmsrl(MSR_AMD64_IBSOPRIP, msr); oprofile_write_reserve(&entry, regs, msr, IBS_OP_CODE, IBS_OP_SIZE); oprofile_add_data(&entry, (u32)msr); oprofile_add_data(&entry, (u32)(msr >> 32)); rdmsrl(MSR_AMD64_IBSOPDATA, msr); oprofile_add_data(&entry, (u32)msr); oprofile_add_data(&entry, (u32)(msr >> 32)); rdmsrl(MSR_AMD64_IBSOPDATA2, msr); oprofile_add_data(&entry, (u32)msr); oprofile_add_data(&entry, (u32)(msr >> 32)); rdmsrl(MSR_AMD64_IBSOPDATA3, msr); oprofile_add_data(&entry, (u32)msr); oprofile_add_data(&entry, (u32)(msr >> 32)); rdmsrl(MSR_AMD64_IBSDCLINAD, msr); oprofile_add_data(&entry, (u32)msr); oprofile_add_data(&entry, (u32)(msr >> 32)); rdmsrl(MSR_AMD64_IBSDCPHYSAD, msr); oprofile_add_data(&entry, (u32)msr); oprofile_add_data(&entry, (u32)(msr >> 32)); oprofile_write_commit(&entry); /* reenable the IRQ */ oprofile_add_ibs_sample(regs, (unsigned int *)&ibs_op, IBS_OP_BEGIN); rdmsr(MSR_AMD64_IBSOPCTL, low, high); high = 0; low &= ~IBS_OP_LOW_VALID_BIT; low |= IBS_OP_LOW_ENABLE; Loading Loading @@ -308,14 +259,14 @@ static void op_amd_start(struct op_msrs const * const msrs) } #ifdef CONFIG_OPROFILE_IBS if (ibs_allowed && ibs_config.fetch_enabled) { if (has_ibs && ibs_config.fetch_enabled) { low = (ibs_config.max_cnt_fetch >> 4) & 0xFFFF; high = ((ibs_config.rand_en & 0x1) << 25) /* bit 57 */ + IBS_FETCH_HIGH_ENABLE; wrmsr(MSR_AMD64_IBSFETCHCTL, low, high); } if (ibs_allowed && ibs_config.op_enabled) { if (has_ibs && ibs_config.op_enabled) { low = ((ibs_config.max_cnt_op >> 4) & 0xFFFF) + ((ibs_config.dispatched_ops & 0x1) << 19) /* bit 19 */ + IBS_OP_LOW_ENABLE; Loading @@ -331,8 +282,10 @@ static void op_amd_stop(struct op_msrs const * const msrs) unsigned int low, high; int i; /* Subtle: stop on all counters to avoid race with * setting our pm callback */ /* * Subtle: stop on all counters to avoid race with setting our * pm callback */ for (i = 0 ; i < NUM_COUNTERS ; ++i) { if (!reset_value[i]) continue; Loading @@ -342,14 +295,16 @@ static void op_amd_stop(struct op_msrs const * const msrs) } #ifdef CONFIG_OPROFILE_IBS if (ibs_allowed && ibs_config.fetch_enabled) { low = 0; /* clear max count and enable */ if (has_ibs && ibs_config.fetch_enabled) { /* clear max count and enable */ low = 0; high = 0; wrmsr(MSR_AMD64_IBSFETCHCTL, low, high); } if (ibs_allowed && ibs_config.op_enabled) { low = 0; /* clear max count and enable */ if (has_ibs && ibs_config.op_enabled) { /* clear max count and enable */ low = 0; high = 0; wrmsr(MSR_AMD64_IBSOPCTL, low, high); } Loading @@ -370,18 +325,7 @@ static void op_amd_shutdown(struct op_msrs const * const msrs) } } #ifndef CONFIG_OPROFILE_IBS /* no IBS support */ static int op_amd_init(struct oprofile_operations *ops) { return 0; } static void op_amd_exit(void) {} #else #ifdef CONFIG_OPROFILE_IBS static u8 ibs_eilvt_off; Loading @@ -395,7 +339,7 @@ static inline void apic_clear_ibs_nmi_per_cpu(void *arg) setup_APIC_eilvt_ibs(0, APIC_EILVT_MSG_FIX, 1); } static int pfm_amd64_setup_eilvt(void) static int init_ibs_nmi(void) { #define IBSCTL_LVTOFFSETVAL (1 << 8) #define IBSCTL 0x1cc Loading @@ -419,6 +363,7 @@ static int pfm_amd64_setup_eilvt(void) | IBSCTL_LVTOFFSETVAL); pci_read_config_dword(cpu_cfg, IBSCTL, &value); if (value != (ibs_eilvt_off | IBSCTL_LVTOFFSETVAL)) { pci_dev_put(cpu_cfg); printk(KERN_DEBUG "Failed to setup IBS LVT offset, " "IBSCTL = 0x%08x", value); return 1; Loading @@ -443,33 +388,35 @@ static int pfm_amd64_setup_eilvt(void) return 0; } /* * initialize the APIC for the IBS interrupts * if available (AMD Family10h rev B0 and later) */ static void setup_ibs(void) /* uninitialize the APIC for the IBS interrupts if needed */ static void clear_ibs_nmi(void) { ibs_allowed = boot_cpu_has(X86_FEATURE_IBS); if (has_ibs) on_each_cpu(apic_clear_ibs_nmi_per_cpu, NULL, 1); } if (!ibs_allowed) /* initialize the APIC for the IBS interrupts if available */ static void ibs_init(void) { has_ibs = boot_cpu_has(X86_FEATURE_IBS); if (!has_ibs) return; if (pfm_amd64_setup_eilvt()) { ibs_allowed = 0; if (init_ibs_nmi()) { has_ibs = 0; return; } printk(KERN_INFO "oprofile: AMD IBS detected\n"); } /* * unitialize the APIC for the IBS interrupts if needed on AMD Family10h * rev B0 and later */ static void clear_ibs_nmi(void) static void ibs_exit(void) { if (ibs_allowed) on_each_cpu(apic_clear_ibs_nmi_per_cpu, NULL, 1); if (!has_ibs) return; clear_ibs_nmi(); } static int (*create_arch_files)(struct super_block *sb, struct dentry *root); Loading @@ -486,7 +433,7 @@ static int setup_ibs_files(struct super_block *sb, struct dentry *root) if (ret) return ret; if (!ibs_allowed) if (!has_ibs) return ret; /* model specific files */ Loading Loading @@ -519,7 +466,7 @@ static int setup_ibs_files(struct super_block *sb, struct dentry *root) static int op_amd_init(struct oprofile_operations *ops) { setup_ibs(); ibs_init(); create_arch_files = ops->create_files; ops->create_files = setup_ibs_files; return 0; Loading @@ -527,10 +474,21 @@ static int op_amd_init(struct oprofile_operations *ops) static void op_amd_exit(void) { clear_ibs_nmi(); ibs_exit(); } #endif #else /* no IBS support */ static int op_amd_init(struct oprofile_operations *ops) { return 0; } static void op_amd_exit(void) {} #endif /* CONFIG_OPROFILE_IBS */ struct op_x86_model_spec const op_amd_spec = { .init = op_amd_init, Loading drivers/oprofile/buffer_sync.c +81 −148 Original line number Diff line number Diff line /** * @file buffer_sync.c * * @remark Copyright 2002 OProfile authors * @remark Copyright 2002-2009 OProfile authors * @remark Read the file COPYING * * @author John Levon <levon@movementarian.org> * @author Barry Kasindorf * @author Robert Richter <robert.richter@amd.com> * * This is the core of the buffer management. Each * CPU buffer is processed and entered into the Loading Loading @@ -268,18 +269,6 @@ lookup_dcookie(struct mm_struct *mm, unsigned long addr, off_t *offset) return cookie; } static void increment_tail(struct oprofile_cpu_buffer *b) { unsigned long new_tail = b->tail_pos + 1; rmb(); /* be sure fifo pointers are synchromized */ if (new_tail < b->buffer_size) b->tail_pos = new_tail; else b->tail_pos = 0; } static unsigned long last_cookie = INVALID_COOKIE; static void add_cpu_switch(int i) Loading Loading @@ -327,84 +316,73 @@ static void add_trace_begin(void) add_event_entry(TRACE_BEGIN_CODE); } #ifdef CONFIG_OPROFILE_IBS #define IBS_FETCH_CODE_SIZE 2 #define IBS_OP_CODE_SIZE 5 #define IBS_EIP(offset) \ (((struct op_sample *)&cpu_buf->buffer[(offset)])->eip) #define IBS_EVENT(offset) \ (((struct op_sample *)&cpu_buf->buffer[(offset)])->event) /* * Add IBS fetch and op entries to event buffer */ static void add_ibs_begin(struct oprofile_cpu_buffer *cpu_buf, int code, struct mm_struct *mm) static void add_data(struct op_entry *entry, struct mm_struct *mm) { unsigned long rip; int i, count; unsigned long ibs_cookie = 0; unsigned long code, pc, val; unsigned long cookie; off_t offset; increment_tail(cpu_buf); /* move to RIP entry */ rip = IBS_EIP(cpu_buf->tail_pos); #ifdef __LP64__ rip += IBS_EVENT(cpu_buf->tail_pos) << 32; #endif if (!op_cpu_buffer_get_data(entry, &code)) return; if (!op_cpu_buffer_get_data(entry, &pc)) return; if (!op_cpu_buffer_get_size(entry)) return; if (mm) { ibs_cookie = lookup_dcookie(mm, rip, &offset); cookie = lookup_dcookie(mm, pc, &offset); if (ibs_cookie == NO_COOKIE) offset = rip; if (ibs_cookie == INVALID_COOKIE) { if (cookie == NO_COOKIE) offset = pc; if (cookie == INVALID_COOKIE) { atomic_inc(&oprofile_stats.sample_lost_no_mapping); offset = rip; offset = pc; } if (ibs_cookie != last_cookie) { add_cookie_switch(ibs_cookie); last_cookie = ibs_cookie; if (cookie != last_cookie) { add_cookie_switch(cookie); last_cookie = cookie; } } else offset = rip; offset = pc; add_event_entry(ESCAPE_CODE); add_event_entry(code); add_event_entry(offset); /* Offset from Dcookie */ /* we send the Dcookie offset, but send the raw Linear Add also*/ add_event_entry(IBS_EIP(cpu_buf->tail_pos)); add_event_entry(IBS_EVENT(cpu_buf->tail_pos)); if (code == IBS_FETCH_CODE) count = IBS_FETCH_CODE_SIZE; /*IBS FETCH is 2 int64s*/ else count = IBS_OP_CODE_SIZE; /*IBS OP is 5 int64s*/ for (i = 0; i < count; i++) { increment_tail(cpu_buf); add_event_entry(IBS_EIP(cpu_buf->tail_pos)); add_event_entry(IBS_EVENT(cpu_buf->tail_pos)); } while (op_cpu_buffer_get_data(entry, &val)) add_event_entry(val); } #endif static void add_sample_entry(unsigned long offset, unsigned long event) static inline void add_sample_entry(unsigned long offset, unsigned long event) { add_event_entry(offset); add_event_entry(event); } static int add_us_sample(struct mm_struct *mm, struct op_sample *s) /* * Add a sample to the global event buffer. If possible the * sample is converted into a persistent dentry/offset pair * for later lookup from userspace. Return 0 on failure. */ static int add_sample(struct mm_struct *mm, struct op_sample *s, int in_kernel) { unsigned long cookie; off_t offset; if (in_kernel) { add_sample_entry(s->eip, s->event); return 1; } /* add userspace sample */ if (!mm) { atomic_inc(&oprofile_stats.sample_lost_no_mm); return 0; } cookie = lookup_dcookie(mm, s->eip, &offset); if (cookie == INVALID_COOKIE) { Loading @@ -423,25 +401,6 @@ static int add_us_sample(struct mm_struct *mm, struct op_sample *s) } /* Add a sample to the global event buffer. If possible the * 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) { if (in_kernel) { add_sample_entry(s->eip, s->event); return 1; } else if (mm) { return add_us_sample(mm, s); } else { atomic_inc(&oprofile_stats.sample_lost_no_mm); } return 0; } static void release_mm(struct mm_struct *mm) { if (!mm) Loading @@ -466,33 +425,6 @@ static inline int is_code(unsigned long val) } /* "acquire" as many cpu buffer slots as we can */ static unsigned long get_slots(struct oprofile_cpu_buffer *b) { unsigned long head = b->head_pos; unsigned long tail = b->tail_pos; /* * Subtle. This resets the persistent last_task * and in_kernel values used for switching notes. * BUT, there is a small window between reading * head_pos, and this call, that means samples * can appear at the new head position, but not * be prefixed with the notes for switching * kernel mode or a task switch. This small hole * can lead to mis-attribution or samples where * we don't know if it's in the kernel or not, * at the start of an event buffer. */ cpu_buffer_reset(b); if (head >= tail) return head - tail; return head + (b->buffer_size - tail); } /* Move tasks along towards death. Any tasks on dead_tasks * will definitely have no remaining references in any * CPU buffers at this point, because we use two lists, Loading Loading @@ -559,72 +491,73 @@ typedef enum { */ void sync_buffer(int cpu) { struct oprofile_cpu_buffer *cpu_buf = &per_cpu(cpu_buffer, cpu); struct mm_struct *mm = NULL; struct mm_struct *oldmm; unsigned long val; struct task_struct *new; unsigned long cookie = 0; int in_kernel = 1; sync_buffer_state state = sb_buffer_start; #ifndef CONFIG_OPROFILE_IBS unsigned int i; unsigned long available; #endif unsigned long flags; struct op_entry entry; struct op_sample *sample; mutex_lock(&buffer_mutex); add_cpu_switch(cpu); /* Remember, only we can modify tail_pos */ #ifndef CONFIG_OPROFILE_IBS available = get_slots(cpu_buf); op_cpu_buffer_reset(cpu); available = op_cpu_buffer_entries(cpu); for (i = 0; i < available; ++i) { #else while (get_slots(cpu_buf)) { #endif struct op_sample *s = &cpu_buf->buffer[cpu_buf->tail_pos]; sample = op_cpu_buffer_read_entry(&entry, cpu); if (!sample) break; if (is_code(s->eip)) { if (s->event <= CPU_IS_KERNEL) { if (is_code(sample->eip)) { flags = sample->event; if (flags & TRACE_BEGIN) { state = sb_bt_start; add_trace_begin(); } if (flags & KERNEL_CTX_SWITCH) { /* kernel/userspace switch */ in_kernel = s->event; in_kernel = flags & IS_KERNEL; if (state == sb_buffer_start) state = sb_sample_start; add_kernel_ctx_switch(s->event); } else if (s->event == CPU_TRACE_BEGIN) { state = sb_bt_start; add_trace_begin(); #ifdef CONFIG_OPROFILE_IBS } else if (s->event == IBS_FETCH_BEGIN) { state = sb_bt_start; add_ibs_begin(cpu_buf, IBS_FETCH_CODE, mm); } else if (s->event == IBS_OP_BEGIN) { state = sb_bt_start; add_ibs_begin(cpu_buf, IBS_OP_CODE, mm); #endif } else { struct mm_struct *oldmm = mm; add_kernel_ctx_switch(flags & IS_KERNEL); } if (flags & USER_CTX_SWITCH && op_cpu_buffer_get_data(&entry, &val)) { /* userspace context switch */ new = (struct task_struct *)s->event; new = (struct task_struct *)val; oldmm = mm; release_mm(oldmm); mm = take_tasks_mm(new); if (mm != oldmm) cookie = get_exec_dcookie(mm); add_user_ctx_switch(new, cookie); } } else if (state >= sb_bt_start && !add_sample(mm, s, in_kernel)) { if (op_cpu_buffer_get_size(&entry)) add_data(&entry, mm); continue; } if (state < sb_bt_start) /* ignore sample */ continue; if (add_sample(mm, sample, in_kernel)) continue; /* ignore backtraces if failed to add a sample */ if (state == sb_bt_start) { state = sb_bt_ignore; atomic_inc(&oprofile_stats.bt_lost_no_mapping); } } increment_tail(cpu_buf); } release_mm(mm); mark_done(cpu); Loading drivers/oprofile/cpu_buffer.c +240 −153 File changed.Preview size limit exceeded, changes collapsed. Show changes drivers/oprofile/cpu_buffer.h +62 −10 Original line number Diff line number Diff line /** * @file cpu_buffer.h * * @remark Copyright 2002 OProfile authors * @remark Copyright 2002-2009 OProfile authors * @remark Read the file COPYING * * @author John Levon <levon@movementarian.org> * @author Robert Richter <robert.richter@amd.com> */ #ifndef OPROFILE_CPU_BUFFER_H Loading @@ -15,6 +16,7 @@ #include <linux/workqueue.h> #include <linux/cache.h> #include <linux/sched.h> #include <linux/ring_buffer.h> struct task_struct; Loading @@ -30,16 +32,16 @@ void end_cpu_work(void); struct op_sample { unsigned long eip; unsigned long event; unsigned long data[0]; }; struct op_entry; struct oprofile_cpu_buffer { volatile unsigned long head_pos; volatile unsigned long tail_pos; unsigned long buffer_size; struct task_struct *last_task; int last_is_kernel; int tracing; struct op_sample *buffer; unsigned long sample_received; unsigned long sample_lost_overflow; unsigned long backtrace_aborted; Loading @@ -50,12 +52,62 @@ struct oprofile_cpu_buffer { DECLARE_PER_CPU(struct oprofile_cpu_buffer, cpu_buffer); void cpu_buffer_reset(struct oprofile_cpu_buffer *cpu_buf); /* * Resets the cpu buffer to a sane state. * * reset these to invalid values; the next sample collected will * populate the buffer with proper values to initialize the buffer */ static inline void op_cpu_buffer_reset(int cpu) { struct oprofile_cpu_buffer *cpu_buf = &per_cpu(cpu_buffer, cpu); cpu_buf->last_is_kernel = -1; cpu_buf->last_task = NULL; } struct op_sample *op_cpu_buffer_write_reserve(struct op_entry *entry, unsigned long size); int op_cpu_buffer_write_commit(struct op_entry *entry); struct op_sample *op_cpu_buffer_read_entry(struct op_entry *entry, int cpu); unsigned long op_cpu_buffer_entries(int cpu); /* returns the remaining free size of data in the entry */ static inline int op_cpu_buffer_add_data(struct op_entry *entry, unsigned long val) { if (!entry->size) return 0; *entry->data = val; entry->size--; entry->data++; return entry->size; } /* returns the size of data in the entry */ static inline int op_cpu_buffer_get_size(struct op_entry *entry) { return entry->size; } /* returns 0 if empty or the size of data including the current value */ static inline int op_cpu_buffer_get_data(struct op_entry *entry, unsigned long *val) { int size = entry->size; if (!size) return 0; *val = *entry->data; entry->size--; entry->data++; return size; } /* transient events for the CPU buffer -> event buffer */ #define CPU_IS_KERNEL 1 #define CPU_TRACE_BEGIN 2 #define IBS_FETCH_BEGIN 3 #define IBS_OP_BEGIN 4 /* extra data flags */ #define KERNEL_CTX_SWITCH (1UL << 0) #define IS_KERNEL (1UL << 1) #define TRACE_BEGIN (1UL << 2) #define USER_CTX_SWITCH (1UL << 3) #endif /* OPROFILE_CPU_BUFFER_H */ Loading
arch/Kconfig +2 −0 Original line number Diff line number Diff line Loading @@ -6,6 +6,8 @@ config OPROFILE tristate "OProfile system profiling (EXPERIMENTAL)" depends on PROFILING depends on HAVE_OPROFILE select TRACING select RING_BUFFER help OProfile is a profiling system capable of profiling the whole system, include the kernel, kernel modules, libraries, Loading
arch/x86/oprofile/op_model_amd.c +91 −133 Original line number Diff line number Diff line Loading @@ -2,7 +2,7 @@ * @file op_model_amd.c * athlon / K7 / K8 / Family 10h model-specific MSR operations * * @remark Copyright 2002-2008 OProfile authors * @remark Copyright 2002-2009 OProfile authors * @remark Read the file COPYING * * @author John Levon Loading Loading @@ -60,56 +60,10 @@ static unsigned long reset_value[NUM_COUNTERS]; #define IBS_OP_LOW_VALID_BIT (1ULL<<18) /* bit 18 */ #define IBS_OP_LOW_ENABLE (1ULL<<17) /* bit 17 */ /* Codes used in cpu_buffer.c */ /* This produces duplicate code, need to be fixed */ #define IBS_FETCH_BEGIN 3 #define IBS_OP_BEGIN 4 /* The function interface needs to be fixed, something like add data. Should then be added to linux/oprofile.h. */ extern void oprofile_add_ibs_sample(struct pt_regs *const regs, unsigned int *const ibs_sample, int ibs_code); struct ibs_fetch_sample { /* MSRC001_1031 IBS Fetch Linear Address Register */ unsigned int ibs_fetch_lin_addr_low; unsigned int ibs_fetch_lin_addr_high; /* MSRC001_1030 IBS Fetch Control Register */ unsigned int ibs_fetch_ctl_low; unsigned int ibs_fetch_ctl_high; /* MSRC001_1032 IBS Fetch Physical Address Register */ unsigned int ibs_fetch_phys_addr_low; unsigned int ibs_fetch_phys_addr_high; }; #define IBS_FETCH_SIZE 6 #define IBS_OP_SIZE 12 struct ibs_op_sample { /* MSRC001_1034 IBS Op Logical Address Register (IbsRIP) */ unsigned int ibs_op_rip_low; unsigned int ibs_op_rip_high; /* MSRC001_1035 IBS Op Data Register */ unsigned int ibs_op_data1_low; unsigned int ibs_op_data1_high; /* MSRC001_1036 IBS Op Data 2 Register */ unsigned int ibs_op_data2_low; unsigned int ibs_op_data2_high; /* MSRC001_1037 IBS Op Data 3 Register */ unsigned int ibs_op_data3_low; unsigned int ibs_op_data3_high; /* MSRC001_1038 IBS DC Linear Address Register (IbsDcLinAd) */ unsigned int ibs_dc_linear_low; unsigned int ibs_dc_linear_high; /* MSRC001_1039 IBS DC Physical Address Register (IbsDcPhysAd) */ unsigned int ibs_dc_phys_low; unsigned int ibs_dc_phys_high; }; /* * unitialize the APIC for the IBS interrupts if needed on AMD Family10h+ */ static void clear_ibs_nmi(void); static int ibs_allowed; /* AMD Family10h and later */ static int has_ibs; /* AMD Family10h and later */ struct op_ibs_config { unsigned long op_enabled; Loading Loading @@ -200,31 +154,29 @@ static inline int op_amd_handle_ibs(struct pt_regs * const regs, struct op_msrs const * const msrs) { unsigned int low, high; struct ibs_fetch_sample ibs_fetch; struct ibs_op_sample ibs_op; u32 low, high; u64 msr; struct op_entry entry; if (!ibs_allowed) if (!has_ibs) return 1; if (ibs_config.fetch_enabled) { rdmsr(MSR_AMD64_IBSFETCHCTL, low, high); if (high & IBS_FETCH_HIGH_VALID_BIT) { ibs_fetch.ibs_fetch_ctl_high = high; ibs_fetch.ibs_fetch_ctl_low = low; rdmsr(MSR_AMD64_IBSFETCHLINAD, low, high); ibs_fetch.ibs_fetch_lin_addr_high = high; ibs_fetch.ibs_fetch_lin_addr_low = low; rdmsr(MSR_AMD64_IBSFETCHPHYSAD, low, high); ibs_fetch.ibs_fetch_phys_addr_high = high; ibs_fetch.ibs_fetch_phys_addr_low = low; oprofile_add_ibs_sample(regs, (unsigned int *)&ibs_fetch, IBS_FETCH_BEGIN); rdmsrl(MSR_AMD64_IBSFETCHLINAD, msr); oprofile_write_reserve(&entry, regs, msr, IBS_FETCH_CODE, IBS_FETCH_SIZE); oprofile_add_data(&entry, (u32)msr); oprofile_add_data(&entry, (u32)(msr >> 32)); oprofile_add_data(&entry, low); oprofile_add_data(&entry, high); rdmsrl(MSR_AMD64_IBSFETCHPHYSAD, msr); oprofile_add_data(&entry, (u32)msr); oprofile_add_data(&entry, (u32)(msr >> 32)); oprofile_write_commit(&entry); /* reenable the IRQ */ rdmsr(MSR_AMD64_IBSFETCHCTL, low, high); high &= ~IBS_FETCH_HIGH_VALID_BIT; high |= IBS_FETCH_HIGH_ENABLE; low &= IBS_FETCH_LOW_MAX_CNT_MASK; Loading @@ -235,30 +187,29 @@ op_amd_handle_ibs(struct pt_regs * const regs, if (ibs_config.op_enabled) { rdmsr(MSR_AMD64_IBSOPCTL, low, high); if (low & IBS_OP_LOW_VALID_BIT) { rdmsr(MSR_AMD64_IBSOPRIP, low, high); ibs_op.ibs_op_rip_low = low; ibs_op.ibs_op_rip_high = high; rdmsr(MSR_AMD64_IBSOPDATA, low, high); ibs_op.ibs_op_data1_low = low; ibs_op.ibs_op_data1_high = high; rdmsr(MSR_AMD64_IBSOPDATA2, low, high); ibs_op.ibs_op_data2_low = low; ibs_op.ibs_op_data2_high = high; rdmsr(MSR_AMD64_IBSOPDATA3, low, high); ibs_op.ibs_op_data3_low = low; ibs_op.ibs_op_data3_high = high; rdmsr(MSR_AMD64_IBSDCLINAD, low, high); ibs_op.ibs_dc_linear_low = low; ibs_op.ibs_dc_linear_high = high; rdmsr(MSR_AMD64_IBSDCPHYSAD, low, high); ibs_op.ibs_dc_phys_low = low; ibs_op.ibs_dc_phys_high = high; rdmsrl(MSR_AMD64_IBSOPRIP, msr); oprofile_write_reserve(&entry, regs, msr, IBS_OP_CODE, IBS_OP_SIZE); oprofile_add_data(&entry, (u32)msr); oprofile_add_data(&entry, (u32)(msr >> 32)); rdmsrl(MSR_AMD64_IBSOPDATA, msr); oprofile_add_data(&entry, (u32)msr); oprofile_add_data(&entry, (u32)(msr >> 32)); rdmsrl(MSR_AMD64_IBSOPDATA2, msr); oprofile_add_data(&entry, (u32)msr); oprofile_add_data(&entry, (u32)(msr >> 32)); rdmsrl(MSR_AMD64_IBSOPDATA3, msr); oprofile_add_data(&entry, (u32)msr); oprofile_add_data(&entry, (u32)(msr >> 32)); rdmsrl(MSR_AMD64_IBSDCLINAD, msr); oprofile_add_data(&entry, (u32)msr); oprofile_add_data(&entry, (u32)(msr >> 32)); rdmsrl(MSR_AMD64_IBSDCPHYSAD, msr); oprofile_add_data(&entry, (u32)msr); oprofile_add_data(&entry, (u32)(msr >> 32)); oprofile_write_commit(&entry); /* reenable the IRQ */ oprofile_add_ibs_sample(regs, (unsigned int *)&ibs_op, IBS_OP_BEGIN); rdmsr(MSR_AMD64_IBSOPCTL, low, high); high = 0; low &= ~IBS_OP_LOW_VALID_BIT; low |= IBS_OP_LOW_ENABLE; Loading Loading @@ -308,14 +259,14 @@ static void op_amd_start(struct op_msrs const * const msrs) } #ifdef CONFIG_OPROFILE_IBS if (ibs_allowed && ibs_config.fetch_enabled) { if (has_ibs && ibs_config.fetch_enabled) { low = (ibs_config.max_cnt_fetch >> 4) & 0xFFFF; high = ((ibs_config.rand_en & 0x1) << 25) /* bit 57 */ + IBS_FETCH_HIGH_ENABLE; wrmsr(MSR_AMD64_IBSFETCHCTL, low, high); } if (ibs_allowed && ibs_config.op_enabled) { if (has_ibs && ibs_config.op_enabled) { low = ((ibs_config.max_cnt_op >> 4) & 0xFFFF) + ((ibs_config.dispatched_ops & 0x1) << 19) /* bit 19 */ + IBS_OP_LOW_ENABLE; Loading @@ -331,8 +282,10 @@ static void op_amd_stop(struct op_msrs const * const msrs) unsigned int low, high; int i; /* Subtle: stop on all counters to avoid race with * setting our pm callback */ /* * Subtle: stop on all counters to avoid race with setting our * pm callback */ for (i = 0 ; i < NUM_COUNTERS ; ++i) { if (!reset_value[i]) continue; Loading @@ -342,14 +295,16 @@ static void op_amd_stop(struct op_msrs const * const msrs) } #ifdef CONFIG_OPROFILE_IBS if (ibs_allowed && ibs_config.fetch_enabled) { low = 0; /* clear max count and enable */ if (has_ibs && ibs_config.fetch_enabled) { /* clear max count and enable */ low = 0; high = 0; wrmsr(MSR_AMD64_IBSFETCHCTL, low, high); } if (ibs_allowed && ibs_config.op_enabled) { low = 0; /* clear max count and enable */ if (has_ibs && ibs_config.op_enabled) { /* clear max count and enable */ low = 0; high = 0; wrmsr(MSR_AMD64_IBSOPCTL, low, high); } Loading @@ -370,18 +325,7 @@ static void op_amd_shutdown(struct op_msrs const * const msrs) } } #ifndef CONFIG_OPROFILE_IBS /* no IBS support */ static int op_amd_init(struct oprofile_operations *ops) { return 0; } static void op_amd_exit(void) {} #else #ifdef CONFIG_OPROFILE_IBS static u8 ibs_eilvt_off; Loading @@ -395,7 +339,7 @@ static inline void apic_clear_ibs_nmi_per_cpu(void *arg) setup_APIC_eilvt_ibs(0, APIC_EILVT_MSG_FIX, 1); } static int pfm_amd64_setup_eilvt(void) static int init_ibs_nmi(void) { #define IBSCTL_LVTOFFSETVAL (1 << 8) #define IBSCTL 0x1cc Loading @@ -419,6 +363,7 @@ static int pfm_amd64_setup_eilvt(void) | IBSCTL_LVTOFFSETVAL); pci_read_config_dword(cpu_cfg, IBSCTL, &value); if (value != (ibs_eilvt_off | IBSCTL_LVTOFFSETVAL)) { pci_dev_put(cpu_cfg); printk(KERN_DEBUG "Failed to setup IBS LVT offset, " "IBSCTL = 0x%08x", value); return 1; Loading @@ -443,33 +388,35 @@ static int pfm_amd64_setup_eilvt(void) return 0; } /* * initialize the APIC for the IBS interrupts * if available (AMD Family10h rev B0 and later) */ static void setup_ibs(void) /* uninitialize the APIC for the IBS interrupts if needed */ static void clear_ibs_nmi(void) { ibs_allowed = boot_cpu_has(X86_FEATURE_IBS); if (has_ibs) on_each_cpu(apic_clear_ibs_nmi_per_cpu, NULL, 1); } if (!ibs_allowed) /* initialize the APIC for the IBS interrupts if available */ static void ibs_init(void) { has_ibs = boot_cpu_has(X86_FEATURE_IBS); if (!has_ibs) return; if (pfm_amd64_setup_eilvt()) { ibs_allowed = 0; if (init_ibs_nmi()) { has_ibs = 0; return; } printk(KERN_INFO "oprofile: AMD IBS detected\n"); } /* * unitialize the APIC for the IBS interrupts if needed on AMD Family10h * rev B0 and later */ static void clear_ibs_nmi(void) static void ibs_exit(void) { if (ibs_allowed) on_each_cpu(apic_clear_ibs_nmi_per_cpu, NULL, 1); if (!has_ibs) return; clear_ibs_nmi(); } static int (*create_arch_files)(struct super_block *sb, struct dentry *root); Loading @@ -486,7 +433,7 @@ static int setup_ibs_files(struct super_block *sb, struct dentry *root) if (ret) return ret; if (!ibs_allowed) if (!has_ibs) return ret; /* model specific files */ Loading Loading @@ -519,7 +466,7 @@ static int setup_ibs_files(struct super_block *sb, struct dentry *root) static int op_amd_init(struct oprofile_operations *ops) { setup_ibs(); ibs_init(); create_arch_files = ops->create_files; ops->create_files = setup_ibs_files; return 0; Loading @@ -527,10 +474,21 @@ static int op_amd_init(struct oprofile_operations *ops) static void op_amd_exit(void) { clear_ibs_nmi(); ibs_exit(); } #endif #else /* no IBS support */ static int op_amd_init(struct oprofile_operations *ops) { return 0; } static void op_amd_exit(void) {} #endif /* CONFIG_OPROFILE_IBS */ struct op_x86_model_spec const op_amd_spec = { .init = op_amd_init, Loading
drivers/oprofile/buffer_sync.c +81 −148 Original line number Diff line number Diff line /** * @file buffer_sync.c * * @remark Copyright 2002 OProfile authors * @remark Copyright 2002-2009 OProfile authors * @remark Read the file COPYING * * @author John Levon <levon@movementarian.org> * @author Barry Kasindorf * @author Robert Richter <robert.richter@amd.com> * * This is the core of the buffer management. Each * CPU buffer is processed and entered into the Loading Loading @@ -268,18 +269,6 @@ lookup_dcookie(struct mm_struct *mm, unsigned long addr, off_t *offset) return cookie; } static void increment_tail(struct oprofile_cpu_buffer *b) { unsigned long new_tail = b->tail_pos + 1; rmb(); /* be sure fifo pointers are synchromized */ if (new_tail < b->buffer_size) b->tail_pos = new_tail; else b->tail_pos = 0; } static unsigned long last_cookie = INVALID_COOKIE; static void add_cpu_switch(int i) Loading Loading @@ -327,84 +316,73 @@ static void add_trace_begin(void) add_event_entry(TRACE_BEGIN_CODE); } #ifdef CONFIG_OPROFILE_IBS #define IBS_FETCH_CODE_SIZE 2 #define IBS_OP_CODE_SIZE 5 #define IBS_EIP(offset) \ (((struct op_sample *)&cpu_buf->buffer[(offset)])->eip) #define IBS_EVENT(offset) \ (((struct op_sample *)&cpu_buf->buffer[(offset)])->event) /* * Add IBS fetch and op entries to event buffer */ static void add_ibs_begin(struct oprofile_cpu_buffer *cpu_buf, int code, struct mm_struct *mm) static void add_data(struct op_entry *entry, struct mm_struct *mm) { unsigned long rip; int i, count; unsigned long ibs_cookie = 0; unsigned long code, pc, val; unsigned long cookie; off_t offset; increment_tail(cpu_buf); /* move to RIP entry */ rip = IBS_EIP(cpu_buf->tail_pos); #ifdef __LP64__ rip += IBS_EVENT(cpu_buf->tail_pos) << 32; #endif if (!op_cpu_buffer_get_data(entry, &code)) return; if (!op_cpu_buffer_get_data(entry, &pc)) return; if (!op_cpu_buffer_get_size(entry)) return; if (mm) { ibs_cookie = lookup_dcookie(mm, rip, &offset); cookie = lookup_dcookie(mm, pc, &offset); if (ibs_cookie == NO_COOKIE) offset = rip; if (ibs_cookie == INVALID_COOKIE) { if (cookie == NO_COOKIE) offset = pc; if (cookie == INVALID_COOKIE) { atomic_inc(&oprofile_stats.sample_lost_no_mapping); offset = rip; offset = pc; } if (ibs_cookie != last_cookie) { add_cookie_switch(ibs_cookie); last_cookie = ibs_cookie; if (cookie != last_cookie) { add_cookie_switch(cookie); last_cookie = cookie; } } else offset = rip; offset = pc; add_event_entry(ESCAPE_CODE); add_event_entry(code); add_event_entry(offset); /* Offset from Dcookie */ /* we send the Dcookie offset, but send the raw Linear Add also*/ add_event_entry(IBS_EIP(cpu_buf->tail_pos)); add_event_entry(IBS_EVENT(cpu_buf->tail_pos)); if (code == IBS_FETCH_CODE) count = IBS_FETCH_CODE_SIZE; /*IBS FETCH is 2 int64s*/ else count = IBS_OP_CODE_SIZE; /*IBS OP is 5 int64s*/ for (i = 0; i < count; i++) { increment_tail(cpu_buf); add_event_entry(IBS_EIP(cpu_buf->tail_pos)); add_event_entry(IBS_EVENT(cpu_buf->tail_pos)); } while (op_cpu_buffer_get_data(entry, &val)) add_event_entry(val); } #endif static void add_sample_entry(unsigned long offset, unsigned long event) static inline void add_sample_entry(unsigned long offset, unsigned long event) { add_event_entry(offset); add_event_entry(event); } static int add_us_sample(struct mm_struct *mm, struct op_sample *s) /* * Add a sample to the global event buffer. If possible the * sample is converted into a persistent dentry/offset pair * for later lookup from userspace. Return 0 on failure. */ static int add_sample(struct mm_struct *mm, struct op_sample *s, int in_kernel) { unsigned long cookie; off_t offset; if (in_kernel) { add_sample_entry(s->eip, s->event); return 1; } /* add userspace sample */ if (!mm) { atomic_inc(&oprofile_stats.sample_lost_no_mm); return 0; } cookie = lookup_dcookie(mm, s->eip, &offset); if (cookie == INVALID_COOKIE) { Loading @@ -423,25 +401,6 @@ static int add_us_sample(struct mm_struct *mm, struct op_sample *s) } /* Add a sample to the global event buffer. If possible the * 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) { if (in_kernel) { add_sample_entry(s->eip, s->event); return 1; } else if (mm) { return add_us_sample(mm, s); } else { atomic_inc(&oprofile_stats.sample_lost_no_mm); } return 0; } static void release_mm(struct mm_struct *mm) { if (!mm) Loading @@ -466,33 +425,6 @@ static inline int is_code(unsigned long val) } /* "acquire" as many cpu buffer slots as we can */ static unsigned long get_slots(struct oprofile_cpu_buffer *b) { unsigned long head = b->head_pos; unsigned long tail = b->tail_pos; /* * Subtle. This resets the persistent last_task * and in_kernel values used for switching notes. * BUT, there is a small window between reading * head_pos, and this call, that means samples * can appear at the new head position, but not * be prefixed with the notes for switching * kernel mode or a task switch. This small hole * can lead to mis-attribution or samples where * we don't know if it's in the kernel or not, * at the start of an event buffer. */ cpu_buffer_reset(b); if (head >= tail) return head - tail; return head + (b->buffer_size - tail); } /* Move tasks along towards death. Any tasks on dead_tasks * will definitely have no remaining references in any * CPU buffers at this point, because we use two lists, Loading Loading @@ -559,72 +491,73 @@ typedef enum { */ void sync_buffer(int cpu) { struct oprofile_cpu_buffer *cpu_buf = &per_cpu(cpu_buffer, cpu); struct mm_struct *mm = NULL; struct mm_struct *oldmm; unsigned long val; struct task_struct *new; unsigned long cookie = 0; int in_kernel = 1; sync_buffer_state state = sb_buffer_start; #ifndef CONFIG_OPROFILE_IBS unsigned int i; unsigned long available; #endif unsigned long flags; struct op_entry entry; struct op_sample *sample; mutex_lock(&buffer_mutex); add_cpu_switch(cpu); /* Remember, only we can modify tail_pos */ #ifndef CONFIG_OPROFILE_IBS available = get_slots(cpu_buf); op_cpu_buffer_reset(cpu); available = op_cpu_buffer_entries(cpu); for (i = 0; i < available; ++i) { #else while (get_slots(cpu_buf)) { #endif struct op_sample *s = &cpu_buf->buffer[cpu_buf->tail_pos]; sample = op_cpu_buffer_read_entry(&entry, cpu); if (!sample) break; if (is_code(s->eip)) { if (s->event <= CPU_IS_KERNEL) { if (is_code(sample->eip)) { flags = sample->event; if (flags & TRACE_BEGIN) { state = sb_bt_start; add_trace_begin(); } if (flags & KERNEL_CTX_SWITCH) { /* kernel/userspace switch */ in_kernel = s->event; in_kernel = flags & IS_KERNEL; if (state == sb_buffer_start) state = sb_sample_start; add_kernel_ctx_switch(s->event); } else if (s->event == CPU_TRACE_BEGIN) { state = sb_bt_start; add_trace_begin(); #ifdef CONFIG_OPROFILE_IBS } else if (s->event == IBS_FETCH_BEGIN) { state = sb_bt_start; add_ibs_begin(cpu_buf, IBS_FETCH_CODE, mm); } else if (s->event == IBS_OP_BEGIN) { state = sb_bt_start; add_ibs_begin(cpu_buf, IBS_OP_CODE, mm); #endif } else { struct mm_struct *oldmm = mm; add_kernel_ctx_switch(flags & IS_KERNEL); } if (flags & USER_CTX_SWITCH && op_cpu_buffer_get_data(&entry, &val)) { /* userspace context switch */ new = (struct task_struct *)s->event; new = (struct task_struct *)val; oldmm = mm; release_mm(oldmm); mm = take_tasks_mm(new); if (mm != oldmm) cookie = get_exec_dcookie(mm); add_user_ctx_switch(new, cookie); } } else if (state >= sb_bt_start && !add_sample(mm, s, in_kernel)) { if (op_cpu_buffer_get_size(&entry)) add_data(&entry, mm); continue; } if (state < sb_bt_start) /* ignore sample */ continue; if (add_sample(mm, sample, in_kernel)) continue; /* ignore backtraces if failed to add a sample */ if (state == sb_bt_start) { state = sb_bt_ignore; atomic_inc(&oprofile_stats.bt_lost_no_mapping); } } increment_tail(cpu_buf); } release_mm(mm); mark_done(cpu); Loading
drivers/oprofile/cpu_buffer.c +240 −153 File changed.Preview size limit exceeded, changes collapsed. Show changes
drivers/oprofile/cpu_buffer.h +62 −10 Original line number Diff line number Diff line /** * @file cpu_buffer.h * * @remark Copyright 2002 OProfile authors * @remark Copyright 2002-2009 OProfile authors * @remark Read the file COPYING * * @author John Levon <levon@movementarian.org> * @author Robert Richter <robert.richter@amd.com> */ #ifndef OPROFILE_CPU_BUFFER_H Loading @@ -15,6 +16,7 @@ #include <linux/workqueue.h> #include <linux/cache.h> #include <linux/sched.h> #include <linux/ring_buffer.h> struct task_struct; Loading @@ -30,16 +32,16 @@ void end_cpu_work(void); struct op_sample { unsigned long eip; unsigned long event; unsigned long data[0]; }; struct op_entry; struct oprofile_cpu_buffer { volatile unsigned long head_pos; volatile unsigned long tail_pos; unsigned long buffer_size; struct task_struct *last_task; int last_is_kernel; int tracing; struct op_sample *buffer; unsigned long sample_received; unsigned long sample_lost_overflow; unsigned long backtrace_aborted; Loading @@ -50,12 +52,62 @@ struct oprofile_cpu_buffer { DECLARE_PER_CPU(struct oprofile_cpu_buffer, cpu_buffer); void cpu_buffer_reset(struct oprofile_cpu_buffer *cpu_buf); /* * Resets the cpu buffer to a sane state. * * reset these to invalid values; the next sample collected will * populate the buffer with proper values to initialize the buffer */ static inline void op_cpu_buffer_reset(int cpu) { struct oprofile_cpu_buffer *cpu_buf = &per_cpu(cpu_buffer, cpu); cpu_buf->last_is_kernel = -1; cpu_buf->last_task = NULL; } struct op_sample *op_cpu_buffer_write_reserve(struct op_entry *entry, unsigned long size); int op_cpu_buffer_write_commit(struct op_entry *entry); struct op_sample *op_cpu_buffer_read_entry(struct op_entry *entry, int cpu); unsigned long op_cpu_buffer_entries(int cpu); /* returns the remaining free size of data in the entry */ static inline int op_cpu_buffer_add_data(struct op_entry *entry, unsigned long val) { if (!entry->size) return 0; *entry->data = val; entry->size--; entry->data++; return entry->size; } /* returns the size of data in the entry */ static inline int op_cpu_buffer_get_size(struct op_entry *entry) { return entry->size; } /* returns 0 if empty or the size of data including the current value */ static inline int op_cpu_buffer_get_data(struct op_entry *entry, unsigned long *val) { int size = entry->size; if (!size) return 0; *val = *entry->data; entry->size--; entry->data++; return size; } /* transient events for the CPU buffer -> event buffer */ #define CPU_IS_KERNEL 1 #define CPU_TRACE_BEGIN 2 #define IBS_FETCH_BEGIN 3 #define IBS_OP_BEGIN 4 /* extra data flags */ #define KERNEL_CTX_SWITCH (1UL << 0) #define IS_KERNEL (1UL << 1) #define TRACE_BEGIN (1UL << 2) #define USER_CTX_SWITCH (1UL << 3) #endif /* OPROFILE_CPU_BUFFER_H */