aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/linux/ftrace.h19
-rw-r--r--kernel/trace/trace.h4
-rw-r--r--kernel/trace/trace_boot.c101
3 files changed, 124 insertions, 0 deletions
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
index 5de9903645d..91954eb6460 100644
--- a/include/linux/ftrace.h
+++ b/include/linux/ftrace.h
@@ -5,6 +5,8 @@
#include <linux/linkage.h>
#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/types.h>
extern int ftrace_enabled;
extern int
@@ -209,4 +211,21 @@ ftrace_init_module(unsigned long *start, unsigned long *end) { }
#endif
+struct boot_trace {
+ pid_t caller;
+ initcall_t func;
+ int result;
+ unsigned long long duration;
+};
+
+#ifdef CONFIG_BOOT_TRACER
+extern void trace_boot(struct boot_trace *it);
+extern void start_boot_trace(void);
+#else
+static inline void trace_boot(struct boot_trace *it) { }
+static inline void start_boot_trace(void) { }
+#endif
+
+
+
#endif /* _LINUX_FTRACE_H */
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index cb2c3fb7dd5..b28bf8812ef 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -6,6 +6,7 @@
#include <linux/sched.h>
#include <linux/clocksource.h>
#include <linux/mmiotrace.h>
+#include <linux/ftrace.h>
enum trace_type {
__TRACE_FIRST_TYPE = 0,
@@ -19,6 +20,7 @@ enum trace_type {
TRACE_SPECIAL,
TRACE_MMIO_RW,
TRACE_MMIO_MAP,
+ TRACE_BOOT,
__TRACE_LAST_TYPE
};
@@ -30,6 +32,7 @@ struct ftrace_entry {
unsigned long ip;
unsigned long parent_ip;
};
+extern struct tracer boot_tracer;
/*
* Context switch trace entry - which task (and prio) we switched from/to:
@@ -108,6 +111,7 @@ struct trace_field {
struct print_entry print;
struct mmiotrace_rw mmiorw;
struct mmiotrace_map mmiomap;
+ struct boot_trace initcall;
};
};
diff --git a/kernel/trace/trace_boot.c b/kernel/trace/trace_boot.c
new file mode 100644
index 00000000000..c65ef8ffd6b
--- /dev/null
+++ b/kernel/trace/trace_boot.c
@@ -0,0 +1,101 @@
+/*
+ * ring buffer based initcalls tracer
+ *
+ * Copyright (C) 2008 Frederic Weisbecker <fweisbec@gmail.com>
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/debugfs.h>
+#include <linux/ftrace.h>
+
+#include "trace.h"
+
+static struct trace_array *boot_trace;
+static int trace_boot_enabled;
+
+
+/* Should be started after do_pre_smp_initcalls() in init/main.c */
+void start_boot_trace(void)
+{
+ trace_boot_enabled = 1;
+}
+
+void stop_boot_trace(struct trace_array *tr)
+{
+ trace_boot_enabled = 0;
+}
+
+static void boot_trace_init(struct trace_array *tr)
+{
+ int cpu;
+ boot_trace = tr;
+
+ trace_boot_enabled = 0;
+
+ for_each_cpu_mask(cpu, cpu_possible_map)
+ tracing_reset(tr->data[cpu]);
+}
+
+static void boot_trace_ctrl_update(struct trace_array *tr)
+{
+ if (tr->ctrl)
+ start_boot_trace();
+ else
+ stop_boot_trace(tr);
+}
+
+static int initcall_print_line(struct trace_iterator *iter)
+{
+ int ret = 1;
+ struct trace_entry *entry = iter->ent;
+ struct boot_trace *it = &entry->field.initcall;
+ struct trace_seq *s = &iter->seq;
+
+ if (iter->ent->type == TRACE_BOOT)
+ ret = trace_seq_printf(s, "%pF called from %i "
+ "returned %d after %lld msecs\n",
+ it->func, it->caller, it->result,
+ it->duration);
+ if (ret)
+ return 1;
+ return 0;
+}
+
+struct tracer boot_tracer __read_mostly =
+{
+ .name = "initcall",
+ .init = boot_trace_init,
+ .reset = stop_boot_trace,
+ .ctrl_update = boot_trace_ctrl_update,
+ .print_line = initcall_print_line,
+};
+
+
+void trace_boot(struct boot_trace *it)
+{
+ struct trace_entry *entry;
+ struct trace_array_cpu *data;
+ unsigned long irq_flags;
+ struct trace_array *tr = boot_trace;
+
+ if (!trace_boot_enabled)
+ return;
+
+ preempt_disable();
+ data = tr->data[smp_processor_id()];
+
+ raw_local_irq_save(irq_flags);
+ __raw_spin_lock(&data->lock);
+
+ entry = tracing_get_trace_entry(tr, data);
+ tracing_generic_entry_update(entry, 0);
+ entry->type = TRACE_BOOT;
+ entry->field.initcall = *it;
+
+ __raw_spin_unlock(&data->lock);
+ raw_local_irq_restore(irq_flags);
+ trace_wake_up();
+
+ preempt_enable();
+}