1 /* 2 * ring buffer based initcalls tracer 3 * 4 * Copyright (C) 2008 Frederic Weisbecker <fweisbec@gmail.com> 5 * 6 */ 7 8 #include <linux/init.h> 9 #include <linux/debugfs.h> 10 #include <linux/ftrace.h> 11 #include <linux/kallsyms.h> 12 13 #include "trace.h" 14 15 static struct trace_array *boot_trace; 16 static int trace_boot_enabled; 17 18 19 /* Should be started after do_pre_smp_initcalls() in init/main.c */ 20 void start_boot_trace(void) 21 { 22 trace_boot_enabled = 1; 23 } 24 25 void stop_boot_trace(void) 26 { 27 trace_boot_enabled = 0; 28 } 29 30 void reset_boot_trace(struct trace_array *tr) 31 { 32 stop_boot_trace(); 33 } 34 35 static void boot_trace_init(struct trace_array *tr) 36 { 37 int cpu; 38 boot_trace = tr; 39 40 trace_boot_enabled = 0; 41 42 for_each_cpu_mask(cpu, cpu_possible_map) 43 tracing_reset(tr, cpu); 44 } 45 46 static void boot_trace_ctrl_update(struct trace_array *tr) 47 { 48 if (tr->ctrl) 49 start_boot_trace(); 50 else 51 stop_boot_trace(); 52 } 53 54 static enum print_line_t initcall_print_line(struct trace_iterator *iter) 55 { 56 int ret; 57 struct trace_entry *entry = iter->ent; 58 struct trace_boot *field = (struct trace_boot *)entry; 59 struct boot_trace *it = &field->initcall; 60 struct trace_seq *s = &iter->seq; 61 struct timespec calltime = ktime_to_timespec(it->calltime); 62 struct timespec rettime = ktime_to_timespec(it->rettime); 63 64 if (entry->type == TRACE_BOOT) { 65 ret = trace_seq_printf(s, "[%5ld.%09ld] calling %s @ %i\n", 66 calltime.tv_sec, 67 calltime.tv_nsec, 68 it->func, it->caller); 69 if (!ret) 70 return TRACE_TYPE_PARTIAL_LINE; 71 72 ret = trace_seq_printf(s, "[%5ld.%09ld] initcall %s " 73 "returned %d after %lld msecs\n", 74 rettime.tv_sec, 75 rettime.tv_nsec, 76 it->func, it->result, it->duration); 77 78 if (!ret) 79 return TRACE_TYPE_PARTIAL_LINE; 80 return TRACE_TYPE_HANDLED; 81 } 82 return TRACE_TYPE_UNHANDLED; 83 } 84 85 struct tracer boot_tracer __read_mostly = 86 { 87 .name = "initcall", 88 .init = boot_trace_init, 89 .reset = reset_boot_trace, 90 .ctrl_update = boot_trace_ctrl_update, 91 .print_line = initcall_print_line, 92 }; 93 94 void trace_boot(struct boot_trace *it, initcall_t fn) 95 { 96 struct ring_buffer_event *event; 97 struct trace_boot *entry; 98 struct trace_array_cpu *data; 99 unsigned long irq_flags; 100 struct trace_array *tr = boot_trace; 101 102 if (!trace_boot_enabled) 103 return; 104 105 /* Get its name now since this function could 106 * disappear because it is in the .init section. 107 */ 108 sprint_symbol(it->func, (unsigned long)fn); 109 preempt_disable(); 110 data = tr->data[smp_processor_id()]; 111 112 event = ring_buffer_lock_reserve(tr->buffer, sizeof(*entry), 113 &irq_flags); 114 if (!event) 115 goto out; 116 entry = ring_buffer_event_data(event); 117 tracing_generic_entry_update(&entry->ent, 0, 0); 118 entry->ent.type = TRACE_BOOT; 119 entry->initcall = *it; 120 ring_buffer_unlock_commit(tr->buffer, event, irq_flags); 121 122 trace_wake_up(); 123 124 out: 125 preempt_enable(); 126 } 127