|
@@ -32,12 +32,6 @@
|
|
#include <asm/ds.h>
|
|
#include <asm/ds.h>
|
|
|
|
|
|
|
|
|
|
-/*
|
|
|
|
- * 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.
|
|
* does not yet catch signals sent when the child dies.
|
|
* in exit.c or in signal.c.
|
|
* in exit.c or in signal.c.
|
|
@@ -466,17 +460,12 @@ static int ptrace_set_debugreg(struct task_struct *child,
|
|
return 0;
|
|
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)
|
|
if (!child->thread.ds_area_msr)
|
|
return -ENXIO;
|
|
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,
|
|
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;
|
|
struct bts_struct ret;
|
|
int retval;
|
|
int retval;
|
|
- int bts_size;
|
|
|
|
|
|
+ int bts_end;
|
|
int bts_index;
|
|
int bts_index;
|
|
|
|
|
|
if (!child->thread.ds_area_msr)
|
|
if (!child->thread.ds_area_msr)
|
|
@@ -494,15 +483,15 @@ static int ptrace_bts_read_record(struct task_struct *child,
|
|
if (index < 0)
|
|
if (index < 0)
|
|
return -EINVAL;
|
|
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;
|
|
return -EINVAL;
|
|
|
|
|
|
/* translate the ptrace bts index into the ds bts index */
|
|
/* translate the ptrace bts index into the ds bts index */
|
|
bts_index = ds_get_bts_index((void *)child->thread.ds_area_msr);
|
|
bts_index = ds_get_bts_index((void *)child->thread.ds_area_msr);
|
|
bts_index -= (index + 1);
|
|
bts_index -= (index + 1);
|
|
if (bts_index < 0)
|
|
if (bts_index < 0)
|
|
- bts_index += bts_size;
|
|
|
|
|
|
+ bts_index += bts_end;
|
|
|
|
|
|
retval = ds_read_bts((void *)child->thread.ds_area_msr,
|
|
retval = ds_read_bts((void *)child->thread.ds_area_msr,
|
|
bts_index, &ret);
|
|
bts_index, &ret);
|
|
@@ -530,19 +519,97 @@ static int ptrace_bts_write_record(struct task_struct *child,
|
|
return sizeof(*in);
|
|
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;
|
|
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;
|
|
child->thread.debugctlmsr |= debugctl_mask;
|
|
set_tsk_thread_flag(child, TIF_DEBUGCTLMSR);
|
|
set_tsk_thread_flag(child, TIF_DEBUGCTLMSR);
|
|
} else {
|
|
} else {
|
|
@@ -555,7 +622,7 @@ static int ptrace_bts_config(struct task_struct *child,
|
|
clear_tsk_thread_flag(child, TIF_DEBUGCTLMSR);
|
|
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);
|
|
set_tsk_thread_flag(child, TIF_BTS_TRACE_TS);
|
|
else
|
|
else
|
|
clear_tsk_thread_flag(child, TIF_BTS_TRACE_TS);
|
|
clear_tsk_thread_flag(child, TIF_BTS_TRACE_TS);
|
|
@@ -563,59 +630,32 @@ static int ptrace_bts_config(struct task_struct *child,
|
|
return 0;
|
|
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,
|
|
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
|
|
.variant.jiffies = jiffies
|
|
};
|
|
};
|
|
|
|
|
|
- if (ptrace_bts_get_buffer_size(tsk) <= 0)
|
|
|
|
- return;
|
|
|
|
-
|
|
|
|
ptrace_bts_write_record(tsk, &rec);
|
|
ptrace_bts_write_record(tsk, &rec);
|
|
}
|
|
}
|
|
|
|
|
|
@@ -808,30 +845,32 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
|
|
break;
|
|
break;
|
|
#endif
|
|
#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;
|
|
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;
|
|
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;
|
|
break;
|
|
|
|
|
|
- case PTRACE_BTS_READ_RECORD:
|
|
|
|
|
|
+ case PTRACE_BTS_GET:
|
|
ret = ptrace_bts_read_record
|
|
ret = ptrace_bts_read_record
|
|
- (child, data,
|
|
|
|
- (struct bts_struct __user *) addr);
|
|
|
|
|
|
+ (child, data, (struct bts_struct __user *) addr);
|
|
break;
|
|
break;
|
|
|
|
|
|
- case PTRACE_BTS_CONFIG:
|
|
|
|
- ret = ptrace_bts_config(child, data);
|
|
|
|
|
|
+ case PTRACE_BTS_CLEAR:
|
|
|
|
+ ret = ptrace_bts_clear(child);
|
|
break;
|
|
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;
|
|
break;
|
|
|
|
|
|
default:
|
|
default:
|
|
@@ -1017,12 +1056,12 @@ asmlinkage long sys32_ptrace(long request, u32 pid, u32 addr, u32 data)
|
|
case PTRACE_SETOPTIONS:
|
|
case PTRACE_SETOPTIONS:
|
|
case PTRACE_SET_THREAD_AREA:
|
|
case PTRACE_SET_THREAD_AREA:
|
|
case PTRACE_GET_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_CONFIG:
|
|
case PTRACE_BTS_STATUS:
|
|
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);
|
|
return sys_ptrace(request, pid, addr, data);
|
|
|
|
|
|
default:
|
|
default:
|