/* Include in trace.c */
#include <linux/kthread.h>
+#include <linux/delay.h>
static inline int trace_valid_entry(struct trace_entry *entry)
{
switch (entry->type) {
case TRACE_FN:
case TRACE_CTX:
+ case TRACE_WAKE:
+ case TRACE_STACK:
+ case TRACE_SPECIAL:
return 1;
}
return 0;
static int
trace_test_buffer_cpu(struct trace_array *tr, struct trace_array_cpu *data)
{
- struct page *page;
struct trace_entry *entries;
+ struct page *page;
int idx = 0;
int i;
+ BUG_ON(list_empty(&data->trace_pages));
page = list_entry(data->trace_pages.next, struct page, lru);
entries = page_address(page);
- if (data->trace != entries)
+ check_pages(data);
+ if (head_page(data) != entries)
goto failed;
/*
* The starting trace buffer always has valid elements,
- * if any element exits.
+ * if any element exists.
*/
- entries = data->trace;
+ entries = head_page(data);
for (i = 0; i < tr->entries; i++) {
- if (i < data->trace_idx &&
- !trace_valid_entry(&entries[idx])) {
- printk(KERN_CONT ".. invalid entry %d ", entries[idx].type);
+ if (i < data->trace_idx && !trace_valid_entry(&entries[idx])) {
+ printk(KERN_CONT ".. invalid entry %d ",
+ entries[idx].type);
goto failed;
}
return 0;
failed:
+ /* disable tracing */
+ tracing_disabled = 1;
printk(KERN_CONT ".. corrupted trace buffer .. ");
return -1;
}
*/
static int trace_test_buffer(struct trace_array *tr, unsigned long *count)
{
- unsigned long cnt = 0;
- int cpu;
- int ret = 0;
+ unsigned long flags, cnt = 0;
+ int cpu, ret = 0;
+ /* Don't allow flipping of max traces now */
+ raw_local_irq_save(flags);
+ __raw_spin_lock(&ftrace_max_lock);
for_each_possible_cpu(cpu) {
- if (!tr->data[cpu]->trace)
+ if (!head_page(tr->data[cpu]))
continue;
cnt += tr->data[cpu]->trace_idx;
- printk("%d: count = %ld\n", cpu, cnt);
ret = trace_test_buffer_cpu(tr, tr->data[cpu]);
if (ret)
break;
}
+ __raw_spin_unlock(&ftrace_max_lock);
+ raw_local_irq_restore(flags);
if (count)
*count = cnt;
}
#ifdef CONFIG_FTRACE
+
+#ifdef CONFIG_DYNAMIC_FTRACE
+
+#define __STR(x) #x
+#define STR(x) __STR(x)
+
+/* Test dynamic code modification and ftrace filters */
+int trace_selftest_startup_dynamic_tracing(struct tracer *trace,
+ struct trace_array *tr,
+ int (*func)(void))
+{
+ unsigned long count;
+ int ret;
+ int save_ftrace_enabled = ftrace_enabled;
+ int save_tracer_enabled = tracer_enabled;
+ char *func_name;
+
+ /* The ftrace test PASSED */
+ printk(KERN_CONT "PASSED\n");
+ pr_info("Testing dynamic ftrace: ");
+
+ /* enable tracing, and record the filter function */
+ ftrace_enabled = 1;
+ tracer_enabled = 1;
+
+ /* passed in by parameter to fool gcc from optimizing */
+ func();
+
+ /* update the records */
+ ret = ftrace_force_update();
+ if (ret) {
+ printk(KERN_CONT ".. ftraced failed .. ");
+ return ret;
+ }
+
+ /*
+ * Some archs *cough*PowerPC*cough* add charachters to the
+ * start of the function names. We simply put a '*' to
+ * accomodate them.
+ */
+ func_name = "*" STR(DYN_FTRACE_TEST_NAME);
+
+ /* filter only on our function */
+ ftrace_set_filter(func_name, strlen(func_name), 1);
+
+ /* enable tracing */
+ tr->ctrl = 1;
+ trace->init(tr);
+ /* Sleep for a 1/10 of a second */
+ msleep(100);
+
+ /* we should have nothing in the buffer */
+ ret = trace_test_buffer(tr, &count);
+ if (ret)
+ goto out;
+
+ if (count) {
+ ret = -1;
+ printk(KERN_CONT ".. filter did not filter .. ");
+ goto out;
+ }
+
+ /* call our function again */
+ func();
+
+ /* sleep again */
+ msleep(100);
+
+ /* stop the tracing. */
+ tr->ctrl = 0;
+ trace->ctrl_update(tr);
+ ftrace_enabled = 0;
+
+ /* check the trace buffer */
+ ret = trace_test_buffer(tr, &count);
+ trace->reset(tr);
+
+ /* we should only have one item */
+ if (!ret && count != 1) {
+ printk(KERN_CONT ".. filter failed count=%ld ..", count);
+ ret = -1;
+ goto out;
+ }
+ out:
+ ftrace_enabled = save_ftrace_enabled;
+ tracer_enabled = save_tracer_enabled;
+
+ /* Enable tracing on all functions again */
+ ftrace_set_filter(NULL, 0, 1);
+
+ return ret;
+}
+#else
+# define trace_selftest_startup_dynamic_tracing(trace, tr, func) ({ 0; })
+#endif /* CONFIG_DYNAMIC_FTRACE */
/*
* Simple verification test of ftrace function tracer.
* Enable ftrace, sleep 1/10 second, and then read the trace
{
unsigned long count;
int ret;
+ int save_ftrace_enabled = ftrace_enabled;
+ int save_tracer_enabled = tracer_enabled;
+
+ /* make sure msleep has been recorded */
+ msleep(1);
- /* make sure functions have been recorded */
+ /* force the recorded functions to be traced */
ret = ftrace_force_update();
if (ret) {
printk(KERN_CONT ".. ftraced failed .. ");
}
/* start the tracing */
+ ftrace_enabled = 1;
+ tracer_enabled = 1;
+
tr->ctrl = 1;
trace->init(tr);
/* Sleep for a 1/10 of a second */
/* stop the tracing. */
tr->ctrl = 0;
trace->ctrl_update(tr);
+ ftrace_enabled = 0;
+
/* check the trace buffer */
ret = trace_test_buffer(tr, &count);
trace->reset(tr);
if (!ret && !count) {
printk(KERN_CONT ".. no entries found ..");
ret = -1;
+ goto out;
}
+ ret = trace_selftest_startup_dynamic_tracing(trace, tr,
+ DYN_FTRACE_TEST_NAME);
+
+ out:
+ ftrace_enabled = save_ftrace_enabled;
+ tracer_enabled = save_tracer_enabled;
+
+ /* kill ftrace totally if we failed */
+ if (ret)
+ ftrace_kill();
+
return ret;
}
#endif /* CONFIG_FTRACE */
#ifdef CONFIG_SCHED_TRACER
static int trace_wakeup_test_thread(void *data)
{
- struct completion *x = data;
-
/* Make this a RT thread, doesn't need to be too high */
+ struct sched_param param = { .sched_priority = 5 };
+ struct completion *x = data;
- rt_mutex_setprio(current, MAX_RT_PRIO - 5);
+ sched_setscheduler(current, SCHED_FIFO, ¶m);
/* Make it know we have a new prio */
complete(x);
/* create a high prio thread */
p = kthread_run(trace_wakeup_test_thread, &isrt, "ftrace-test");
- if (!IS_ERR(p)) {
+ if (IS_ERR(p)) {
printk(KERN_CONT "Failed to create ftrace wakeup test thread ");
return -1;
}
return ret;
}
#endif /* CONFIG_CONTEXT_SWITCH_TRACER */
-
-#ifdef CONFIG_DYNAMIC_FTRACE
-#endif /* CONFIG_DYNAMIC_FTRACE */