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 #include <linux/time.h> 13 14 #include "trace.h" 15 #include "trace_output.h" 16 17 static struct trace_array *boot_trace; 18 static bool pre_initcalls_finished; 19 20 /* Tells the boot tracer that the pre_smp_initcalls are finished. 21 * So we are ready . 22 * It doesn't enable sched events tracing however. 23 * You have to call enable_boot_trace to do so. 24 */ 25 void start_boot_trace(void) 26 { 27 pre_initcalls_finished = true; 28 } 29 30 void enable_boot_trace(void) 31 { 32 if (boot_trace && pre_initcalls_finished) 33 tracing_start_sched_switch_record(); 34 } 35 36 void disable_boot_trace(void) 37 { 38 if (boot_trace && pre_initcalls_finished) 39 tracing_stop_sched_switch_record(); 40 } 41 42 static int boot_trace_init(struct trace_array *tr) 43 { 44 boot_trace = tr; 45 46 if (!tr) 47 return 0; 48 49 tracing_reset_online_cpus(tr); 50 51 tracing_sched_switch_assign_trace(tr); 52 return 0; 53 } 54 55 static enum print_line_t 56 initcall_call_print_line(struct trace_iterator *iter) 57 { 58 struct trace_entry *entry = iter->ent; 59 struct trace_seq *s = &iter->seq; 60 struct trace_boot_call *field; 61 struct boot_trace_call *call; 62 u64 ts; 63 unsigned long nsec_rem; 64 int ret; 65 66 trace_assign_type(field, entry); 67 call = &field->boot_call; 68 ts = iter->ts; 69 nsec_rem = do_div(ts, NSEC_PER_SEC); 70 71 ret = trace_seq_printf(s, "[%5ld.%09ld] calling %s @ %i\n", 72 (unsigned long)ts, nsec_rem, call->func, call->caller); 73 74 if (!ret) 75 return TRACE_TYPE_PARTIAL_LINE; 76 else 77 return TRACE_TYPE_HANDLED; 78 } 79 80 static enum print_line_t 81 initcall_ret_print_line(struct trace_iterator *iter) 82 { 83 struct trace_entry *entry = iter->ent; 84 struct trace_seq *s = &iter->seq; 85 struct trace_boot_ret *field; 86 struct boot_trace_ret *init_ret; 87 u64 ts; 88 unsigned long nsec_rem; 89 int ret; 90 91 trace_assign_type(field, entry); 92 init_ret = &field->boot_ret; 93 ts = iter->ts; 94 nsec_rem = do_div(ts, NSEC_PER_SEC); 95 96 ret = trace_seq_printf(s, "[%5ld.%09ld] initcall %s " 97 "returned %d after %llu msecs\n", 98 (unsigned long) ts, 99 nsec_rem, 100 init_ret->func, init_ret->result, init_ret->duration); 101 102 if (!ret) 103 return TRACE_TYPE_PARTIAL_LINE; 104 else 105 return TRACE_TYPE_HANDLED; 106 } 107 108 static enum print_line_t initcall_print_line(struct trace_iterator *iter) 109 { 110 struct trace_entry *entry = iter->ent; 111 112 switch (entry->type) { 113 case TRACE_BOOT_CALL: 114 return initcall_call_print_line(iter); 115 case TRACE_BOOT_RET: 116 return initcall_ret_print_line(iter); 117 default: 118 return TRACE_TYPE_UNHANDLED; 119 } 120 } 121 122 struct tracer boot_tracer __read_mostly = 123 { 124 .name = "initcall", 125 .init = boot_trace_init, 126 .reset = tracing_reset_online_cpus, 127 .print_line = initcall_print_line, 128 }; 129 130 void trace_boot_call(struct boot_trace_call *bt, initcall_t fn) 131 { 132 struct ring_buffer_event *event; 133 struct ring_buffer *buffer; 134 struct trace_boot_call *entry; 135 struct trace_array *tr = boot_trace; 136 137 if (!tr || !pre_initcalls_finished) 138 return; 139 140 /* Get its name now since this function could 141 * disappear because it is in the .init section. 142 */ 143 sprint_symbol(bt->func, (unsigned long)fn); 144 preempt_disable(); 145 146 buffer = tr->buffer; 147 event = trace_buffer_lock_reserve(buffer, TRACE_BOOT_CALL, 148 sizeof(*entry), 0, 0); 149 if (!event) 150 goto out; 151 entry = ring_buffer_event_data(event); 152 entry->boot_call = *bt; 153 trace_buffer_unlock_commit(buffer, event, 0, 0); 154 out: 155 preempt_enable(); 156 } 157 158 void trace_boot_ret(struct boot_trace_ret *bt, initcall_t fn) 159 { 160 struct ring_buffer_event *event; 161 struct ring_buffer *buffer; 162 struct trace_boot_ret *entry; 163 struct trace_array *tr = boot_trace; 164 165 if (!tr || !pre_initcalls_finished) 166 return; 167 168 sprint_symbol(bt->func, (unsigned long)fn); 169 preempt_disable(); 170 171 buffer = tr->buffer; 172 event = trace_buffer_lock_reserve(buffer, TRACE_BOOT_RET, 173 sizeof(*entry), 0, 0); 174 if (!event) 175 goto out; 176 entry = ring_buffer_event_data(event); 177 entry->boot_ret = *bt; 178 trace_buffer_unlock_commit(buffer, event, 0, 0); 179 out: 180 preempt_enable(); 181 } 182