diff options
author | Markus Metzger | 2008-01-30 13:31:20 +0100 |
---|---|---|
committer | Ingo Molnar | 2008-01-30 13:31:20 +0100 |
commit | a95d67f87e1a5f1b4429be3ba3bf7b4051657908 (patch) | |
tree | 4a67994b901c5dfbf3c2ee752efd53dbce938c36 /arch | |
parent | e4811f2568c55e595a7bf15a3b9aba863b31fb94 (diff) |
x86, ptrace: new ptrace BTS API
Here's the new ptrace BTS API that supports two different overflow handling mechanisms (wrap-around and buffer-full-signal) to support two different use cases (debugging and profiling).
It further combines buffer allocation and configuration.
Opens:
- memory rlimit
- overflow signal
What would be the right signal to use?
Signed-off-by: Markus Metzger <markus.t.metzger@intel.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/x86/kernel/ds.c | 56 | ||||
-rw-r--r-- | arch/x86/kernel/ptrace.c | 221 |
2 files changed, 181 insertions, 96 deletions
diff --git a/arch/x86/kernel/ds.c b/arch/x86/kernel/ds.c index e7855def97c3..6eb5d49a36bb 100644 --- a/arch/x86/kernel/ds.c +++ b/arch/x86/kernel/ds.c @@ -177,18 +177,20 @@ static inline void set_info_data(char *base, unsigned long value) } -int ds_allocate(void **dsp, size_t bts_size_in_records) +int ds_allocate(void **dsp, size_t bts_size_in_bytes) { - size_t bts_size_in_bytes = 0; - void *bts = 0; - void *ds = 0; + size_t bts_size_in_records; + void *bts; + void *ds; if (!ds_cfg.sizeof_ds || !ds_cfg.sizeof_bts) return -EOPNOTSUPP; - if (bts_size_in_records < 0) + if (bts_size_in_bytes < 0) return -EINVAL; + bts_size_in_records = + bts_size_in_bytes / ds_cfg.sizeof_bts; bts_size_in_bytes = bts_size_in_records * ds_cfg.sizeof_bts; @@ -233,9 +235,21 @@ int ds_get_bts_size(void *ds) if (!ds_cfg.sizeof_ds || !ds_cfg.sizeof_bts) return -EOPNOTSUPP; + if (!ds) + return 0; + size_in_bytes = get_bts_absolute_maximum(ds) - get_bts_buffer_base(ds); + return size_in_bytes; +} + +int ds_get_bts_end(void *ds) +{ + size_t size_in_bytes = ds_get_bts_size(ds); + + if (size_in_bytes <= 0) + return size_in_bytes; return size_in_bytes / ds_cfg.sizeof_bts; } @@ -254,6 +268,38 @@ int ds_get_bts_index(void *ds) return index_offset_in_bytes / ds_cfg.sizeof_bts; } +int ds_set_overflow(void *ds, int method) +{ + switch (method) { + case DS_O_SIGNAL: + return -EOPNOTSUPP; + case DS_O_WRAP: + return 0; + default: + return -EINVAL; + } +} + +int ds_get_overflow(void *ds) +{ + return DS_O_WRAP; +} + +int ds_clear(void *ds) +{ + int bts_size = ds_get_bts_size(ds); + void *bts_base; + + if (bts_size <= 0) + return bts_size; + + bts_base = get_bts_buffer_base(ds); + memset(bts_base, 0, bts_size); + + set_bts_index(ds, bts_base); + return 0; +} + int ds_read_bts(void *ds, size_t index, struct bts_struct *out) { void *bts; diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c index 3e78c124e2d2..18972a305890 100644 --- a/arch/x86/kernel/ptrace.c +++ b/arch/x86/kernel/ptrace.c @@ -33,12 +33,6 @@ /* - * The maximal size of a BTS buffer per traced task in number of BTS - * records. - */ -#define PTRACE_BTS_BUFFER_MAX 4000 - -/* * does not yet catch signals sent when the child dies. * in exit.c or in signal.c. */ @@ -466,17 +460,12 @@ static int ptrace_set_debugreg(struct task_struct *child, return 0; } -static int ptrace_bts_max_buffer_size(void) -{ - return PTRACE_BTS_BUFFER_MAX; -} - -static int ptrace_bts_get_buffer_size(struct task_struct *child) +static int ptrace_bts_get_size(struct task_struct *child) { if (!child->thread.ds_area_msr) return -ENXIO; - return ds_get_bts_size((void *)child->thread.ds_area_msr); + return ds_get_bts_index((void *)child->thread.ds_area_msr); } static int ptrace_bts_read_record(struct task_struct *child, @@ -485,7 +474,7 @@ static int ptrace_bts_read_record(struct task_struct *child, { struct bts_struct ret; int retval; - int bts_size; + int bts_end; int bts_index; if (!child->thread.ds_area_msr) @@ -494,15 +483,15 @@ static int ptrace_bts_read_record(struct task_struct *child, if (index < 0) return -EINVAL; - bts_size = ds_get_bts_size((void *)child->thread.ds_area_msr); - if (bts_size <= index) + bts_end = ds_get_bts_end((void *)child->thread.ds_area_msr); + if (bts_end <= index) return -EINVAL; /* translate the ptrace bts index into the ds bts index */ bts_index = ds_get_bts_index((void *)child->thread.ds_area_msr); bts_index -= (index + 1); if (bts_index < 0) - bts_index += bts_size; + bts_index += bts_end; retval = ds_read_bts((void *)child->thread.ds_area_msr, bts_index, &ret); @@ -530,19 +519,97 @@ static int ptrace_bts_write_record(struct task_struct *child, return sizeof(*in); } -static int ptrace_bts_config(struct task_struct *child, - unsigned long options) +static int ptrace_bts_clear(struct task_struct *child) { - unsigned long debugctl_mask = ds_debugctl_mask(); - int retval; + if (!child->thread.ds_area_msr) + return -ENXIO; - retval = ptrace_bts_get_buffer_size(child); - if (retval < 0) - return retval; - if (retval == 0) + return ds_clear((void *)child->thread.ds_area_msr); +} + +static int ptrace_bts_drain(struct task_struct *child, + struct bts_struct __user *out) +{ + int end, i; + void *ds = (void *)child->thread.ds_area_msr; + + if (!ds) return -ENXIO; - if (options & PTRACE_BTS_O_TRACE_TASK) { + end = ds_get_bts_index(ds); + if (end <= 0) + return end; + + for (i = 0; i < end; i++, out++) { + struct bts_struct ret; + int retval; + + retval = ds_read_bts(ds, i, &ret); + if (retval < 0) + return retval; + + if (copy_to_user(out, &ret, sizeof(ret))) + return -EFAULT; + } + + ds_clear(ds); + + return i; +} + +static int ptrace_bts_config(struct task_struct *child, + const struct ptrace_bts_config __user *ucfg) +{ + struct ptrace_bts_config cfg; + unsigned long debugctl_mask; + int bts_size, ret; + void *ds; + + if (copy_from_user(&cfg, ucfg, sizeof(cfg))) + return -EFAULT; + + bts_size = 0; + ds = (void *)child->thread.ds_area_msr; + if (ds) { + bts_size = ds_get_bts_size(ds); + if (bts_size < 0) + return bts_size; + } + + if (bts_size != cfg.size) { + ret = ds_free((void **)&child->thread.ds_area_msr); + if (ret < 0) + return ret; + + if (cfg.size > 0) + ret = ds_allocate((void **)&child->thread.ds_area_msr, + cfg.size); + ds = (void *)child->thread.ds_area_msr; + if (ds) + set_tsk_thread_flag(child, TIF_DS_AREA_MSR); + else + clear_tsk_thread_flag(child, TIF_DS_AREA_MSR); + + if (ret < 0) + return ret; + + bts_size = ds_get_bts_size(ds); + if (bts_size <= 0) + return bts_size; + } + + if (ds) { + if (cfg.flags & PTRACE_BTS_O_SIGNAL) { + ret = ds_set_overflow(ds, DS_O_SIGNAL); + } else { + ret = ds_set_overflow(ds, DS_O_WRAP); + } + if (ret < 0) + return ret; + } + + debugctl_mask = ds_debugctl_mask(); + if (ds && (cfg.flags & PTRACE_BTS_O_TRACE)) { child->thread.debugctlmsr |= debugctl_mask; set_tsk_thread_flag(child, TIF_DEBUGCTLMSR); } else { @@ -555,7 +622,7 @@ static int ptrace_bts_config(struct task_struct *child, clear_tsk_thread_flag(child, TIF_DEBUGCTLMSR); } - if (options & PTRACE_BTS_O_TIMESTAMPS) + if (ds && (cfg.flags & PTRACE_BTS_O_SCHED)) set_tsk_thread_flag(child, TIF_BTS_TRACE_TS); else clear_tsk_thread_flag(child, TIF_BTS_TRACE_TS); @@ -563,59 +630,32 @@ static int ptrace_bts_config(struct task_struct *child, return 0; } -static int ptrace_bts_status(struct task_struct *child) +static int ptrace_bts_status(struct task_struct *child, + struct ptrace_bts_config __user *ucfg) { - unsigned long debugctl_mask = ds_debugctl_mask(); - int retval, status = 0; - - retval = ptrace_bts_get_buffer_size(child); - if (retval < 0) - return retval; - if (retval == 0) - return -ENXIO; - - if (ptrace_bts_get_buffer_size(child) <= 0) - return -ENXIO; + void *ds = (void *)child->thread.ds_area_msr; + struct ptrace_bts_config cfg; - if (test_tsk_thread_flag(child, TIF_DEBUGCTLMSR) && - child->thread.debugctlmsr & debugctl_mask) - status |= PTRACE_BTS_O_TRACE_TASK; - if (test_tsk_thread_flag(child, TIF_BTS_TRACE_TS)) - status |= PTRACE_BTS_O_TIMESTAMPS; + memset(&cfg, 0, sizeof(cfg)); - return status; -} + if (ds) { + cfg.size = ds_get_bts_size(ds); -static int ptrace_bts_allocate_bts(struct task_struct *child, - int size_in_records) -{ - int retval = 0; - void *ds; + if (ds_get_overflow(ds) == DS_O_SIGNAL) + cfg.flags |= PTRACE_BTS_O_SIGNAL; - if (size_in_records < 0) - return -EINVAL; + if (test_tsk_thread_flag(child, TIF_DEBUGCTLMSR) && + child->thread.debugctlmsr & ds_debugctl_mask()) + cfg.flags |= PTRACE_BTS_O_TRACE; - if (size_in_records > ptrace_bts_max_buffer_size()) - return -EINVAL; - - if (size_in_records == 0) { - ptrace_bts_config(child, /* options = */ 0); - } else { - retval = ds_allocate(&ds, size_in_records); - if (retval) - return retval; + if (test_tsk_thread_flag(child, TIF_BTS_TRACE_TS)) + cfg.flags |= PTRACE_BTS_O_SCHED; } - if (child->thread.ds_area_msr) - ds_free((void **)&child->thread.ds_area_msr); - - child->thread.ds_area_msr = (unsigned long)ds; - if (child->thread.ds_area_msr) - set_tsk_thread_flag(child, TIF_DS_AREA_MSR); - else - clear_tsk_thread_flag(child, TIF_DS_AREA_MSR); + if (copy_to_user(ucfg, &cfg, sizeof(cfg))) + return -EFAULT; - return retval; + return sizeof(cfg); } void ptrace_bts_take_timestamp(struct task_struct *tsk, @@ -626,9 +666,6 @@ void ptrace_bts_take_timestamp(struct task_struct *tsk, .variant.jiffies = jiffies }; - if (ptrace_bts_get_buffer_size(tsk) <= 0) - return; - ptrace_bts_write_record(tsk, &rec); } @@ -808,30 +845,32 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) break; #endif - case PTRACE_BTS_MAX_BUFFER_SIZE: - ret = ptrace_bts_max_buffer_size(); + case PTRACE_BTS_CONFIG: + ret = ptrace_bts_config + (child, (struct ptrace_bts_config __user *)addr); break; - case PTRACE_BTS_ALLOCATE_BUFFER: - ret = ptrace_bts_allocate_bts(child, data); + case PTRACE_BTS_STATUS: + ret = ptrace_bts_status + (child, (struct ptrace_bts_config __user *)addr); break; - case PTRACE_BTS_GET_BUFFER_SIZE: - ret = ptrace_bts_get_buffer_size(child); + case PTRACE_BTS_SIZE: + ret = ptrace_bts_get_size(child); break; - case PTRACE_BTS_READ_RECORD: + case PTRACE_BTS_GET: ret = ptrace_bts_read_record - (child, data, - (struct bts_struct __user *) addr); + (child, data, (struct bts_struct __user *) addr); break; - case PTRACE_BTS_CONFIG: - ret = ptrace_bts_config(child, data); + case PTRACE_BTS_CLEAR: + ret = ptrace_bts_clear(child); break; - case PTRACE_BTS_STATUS: - ret = ptrace_bts_status(child); + case PTRACE_BTS_DRAIN: + ret = ptrace_bts_drain + (child, (struct bts_struct __user *) addr); break; default: @@ -1017,12 +1056,12 @@ asmlinkage long sys32_ptrace(long request, u32 pid, u32 addr, u32 data) case PTRACE_SETOPTIONS: case PTRACE_SET_THREAD_AREA: case PTRACE_GET_THREAD_AREA: - case PTRACE_BTS_MAX_BUFFER_SIZE: - case PTRACE_BTS_ALLOCATE_BUFFER: - case PTRACE_BTS_GET_BUFFER_SIZE: - case PTRACE_BTS_READ_RECORD: case PTRACE_BTS_CONFIG: case PTRACE_BTS_STATUS: + case PTRACE_BTS_SIZE: + case PTRACE_BTS_GET: + case PTRACE_BTS_CLEAR: + case PTRACE_BTS_DRAIN: return sys_ptrace(request, pid, addr, data); default: |