diff options
-rw-r--r-- | include/linux/ftrace.h | 10 | ||||
-rw-r--r-- | kernel/trace/trace.c | 273 | ||||
-rw-r--r-- | kernel/trace/trace.h | 11 | ||||
-rw-r--r-- | kernel/trace/trace_selftest.c | 11 |
4 files changed, 272 insertions, 33 deletions
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 6b232a2460c0..f53b975e32fa 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h | |||
@@ -157,9 +157,18 @@ static inline void __ftrace_enabled_restore(int enabled) | |||
157 | #ifdef CONFIG_TRACING | 157 | #ifdef CONFIG_TRACING |
158 | extern void | 158 | extern void |
159 | ftrace_special(unsigned long arg1, unsigned long arg2, unsigned long arg3); | 159 | ftrace_special(unsigned long arg1, unsigned long arg2, unsigned long arg3); |
160 | # define ftrace_printk(x...) __ftrace_printk(_THIS_IP_, x) | ||
161 | extern int | ||
162 | __ftrace_printk(unsigned long ip, const char *fmt, ...) | ||
163 | __attribute__ ((format (printf, 2, 3))); | ||
160 | #else | 164 | #else |
161 | static inline void | 165 | static inline void |
162 | ftrace_special(unsigned long arg1, unsigned long arg2, unsigned long arg3) { } | 166 | ftrace_special(unsigned long arg1, unsigned long arg2, unsigned long arg3) { } |
167 | static inline int | ||
168 | ftrace_printk(const char *fmt, ...) __attribute__ ((format (printf, 1, 0))) | ||
169 | { | ||
170 | return 0; | ||
171 | } | ||
163 | #endif | 172 | #endif |
164 | 173 | ||
165 | #ifdef CONFIG_FTRACE_MCOUNT_RECORD | 174 | #ifdef CONFIG_FTRACE_MCOUNT_RECORD |
@@ -173,4 +182,5 @@ ftrace_init_module(unsigned long *start, unsigned long *end) { } | |||
173 | static inline void ftrace_release(void *start, unsigned long size) { } | 182 | static inline void ftrace_release(void *start, unsigned long size) { } |
174 | #endif | 183 | #endif |
175 | 184 | ||
185 | |||
176 | #endif /* _LINUX_FTRACE_H */ | 186 | #endif /* _LINUX_FTRACE_H */ |
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 76dfe6d2466c..a917bea82715 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c | |||
@@ -197,12 +197,14 @@ unsigned long nsecs_to_usecs(unsigned long nsecs) | |||
197 | * NEED_RESCED - reschedule is requested | 197 | * NEED_RESCED - reschedule is requested |
198 | * HARDIRQ - inside an interrupt handler | 198 | * HARDIRQ - inside an interrupt handler |
199 | * SOFTIRQ - inside a softirq handler | 199 | * SOFTIRQ - inside a softirq handler |
200 | * CONT - multiple entries hold the trace item | ||
200 | */ | 201 | */ |
201 | enum trace_flag_type { | 202 | enum trace_flag_type { |
202 | TRACE_FLAG_IRQS_OFF = 0x01, | 203 | TRACE_FLAG_IRQS_OFF = 0x01, |
203 | TRACE_FLAG_NEED_RESCHED = 0x02, | 204 | TRACE_FLAG_NEED_RESCHED = 0x02, |
204 | TRACE_FLAG_HARDIRQ = 0x04, | 205 | TRACE_FLAG_HARDIRQ = 0x04, |
205 | TRACE_FLAG_SOFTIRQ = 0x08, | 206 | TRACE_FLAG_SOFTIRQ = 0x08, |
207 | TRACE_FLAG_CONT = 0x10, | ||
206 | }; | 208 | }; |
207 | 209 | ||
208 | /* | 210 | /* |
@@ -1074,6 +1076,7 @@ enum trace_file_type { | |||
1074 | TRACE_FILE_LAT_FMT = 1, | 1076 | TRACE_FILE_LAT_FMT = 1, |
1075 | }; | 1077 | }; |
1076 | 1078 | ||
1079 | /* Return the current entry. */ | ||
1077 | static struct trace_entry * | 1080 | static struct trace_entry * |
1078 | trace_entry_idx(struct trace_array *tr, struct trace_array_cpu *data, | 1081 | trace_entry_idx(struct trace_array *tr, struct trace_array_cpu *data, |
1079 | struct trace_iterator *iter, int cpu) | 1082 | struct trace_iterator *iter, int cpu) |
@@ -1104,8 +1107,58 @@ trace_entry_idx(struct trace_array *tr, struct trace_array_cpu *data, | |||
1104 | return &array[iter->next_page_idx[cpu]]; | 1107 | return &array[iter->next_page_idx[cpu]]; |
1105 | } | 1108 | } |
1106 | 1109 | ||
1110 | /* Increment the index counter of an iterator by one */ | ||
1111 | static void trace_iterator_increment(struct trace_iterator *iter, int cpu) | ||
1112 | { | ||
1113 | iter->idx++; | ||
1114 | iter->next_idx[cpu]++; | ||
1115 | iter->next_page_idx[cpu]++; | ||
1116 | |||
1117 | if (iter->next_page_idx[cpu] >= ENTRIES_PER_PAGE) { | ||
1118 | struct trace_array_cpu *data = iter->tr->data[cpu]; | ||
1119 | |||
1120 | iter->next_page_idx[cpu] = 0; | ||
1121 | iter->next_page[cpu] = | ||
1122 | trace_next_list(data, iter->next_page[cpu]); | ||
1123 | } | ||
1124 | } | ||
1125 | |||
1107 | static struct trace_entry * | 1126 | static struct trace_entry * |
1108 | find_next_entry(struct trace_iterator *iter, int *ent_cpu) | 1127 | trace_entry_next(struct trace_array *tr, struct trace_array_cpu *data, |
1128 | struct trace_iterator *iter, int cpu) | ||
1129 | { | ||
1130 | struct list_head *next_page; | ||
1131 | struct trace_entry *ent; | ||
1132 | int idx, next_idx, next_page_idx; | ||
1133 | |||
1134 | ent = trace_entry_idx(tr, tr->data[cpu], iter, cpu); | ||
1135 | |||
1136 | if (likely(!ent || ent->type != TRACE_CONT)) | ||
1137 | return ent; | ||
1138 | |||
1139 | /* save the iterator details */ | ||
1140 | idx = iter->idx; | ||
1141 | next_idx = iter->next_idx[cpu]; | ||
1142 | next_page_idx = iter->next_page_idx[cpu]; | ||
1143 | next_page = iter->next_page[cpu]; | ||
1144 | |||
1145 | /* find a real entry */ | ||
1146 | do { | ||
1147 | trace_iterator_increment(iter, cpu); | ||
1148 | ent = trace_entry_idx(tr, tr->data[cpu], iter, cpu); | ||
1149 | } while (ent && ent->type != TRACE_CONT); | ||
1150 | |||
1151 | /* reset the iterator */ | ||
1152 | iter->idx = idx; | ||
1153 | iter->next_idx[cpu] = next_idx; | ||
1154 | iter->next_page_idx[cpu] = next_page_idx; | ||
1155 | iter->next_page[cpu] = next_page; | ||
1156 | |||
1157 | return ent; | ||
1158 | } | ||
1159 | |||
1160 | static struct trace_entry * | ||
1161 | __find_next_entry(struct trace_iterator *iter, int *ent_cpu, int inc) | ||
1109 | { | 1162 | { |
1110 | struct trace_array *tr = iter->tr; | 1163 | struct trace_array *tr = iter->tr; |
1111 | struct trace_entry *ent, *next = NULL; | 1164 | struct trace_entry *ent, *next = NULL; |
@@ -1115,7 +1168,23 @@ find_next_entry(struct trace_iterator *iter, int *ent_cpu) | |||
1115 | for_each_tracing_cpu(cpu) { | 1168 | for_each_tracing_cpu(cpu) { |
1116 | if (!head_page(tr->data[cpu])) | 1169 | if (!head_page(tr->data[cpu])) |
1117 | continue; | 1170 | continue; |
1171 | |||
1118 | ent = trace_entry_idx(tr, tr->data[cpu], iter, cpu); | 1172 | ent = trace_entry_idx(tr, tr->data[cpu], iter, cpu); |
1173 | |||
1174 | if (ent && ent->type == TRACE_CONT) { | ||
1175 | struct trace_array_cpu *data = tr->data[cpu]; | ||
1176 | |||
1177 | if (!inc) | ||
1178 | ent = trace_entry_next(tr, data, iter, cpu); | ||
1179 | else { | ||
1180 | while (ent && ent->type == TRACE_CONT) { | ||
1181 | trace_iterator_increment(iter, cpu); | ||
1182 | ent = trace_entry_idx(tr, tr->data[cpu], | ||
1183 | iter, cpu); | ||
1184 | } | ||
1185 | } | ||
1186 | } | ||
1187 | |||
1119 | /* | 1188 | /* |
1120 | * Pick the entry with the smallest timestamp: | 1189 | * Pick the entry with the smallest timestamp: |
1121 | */ | 1190 | */ |
@@ -1131,25 +1200,39 @@ find_next_entry(struct trace_iterator *iter, int *ent_cpu) | |||
1131 | return next; | 1200 | return next; |
1132 | } | 1201 | } |
1133 | 1202 | ||
1134 | static void trace_iterator_increment(struct trace_iterator *iter) | 1203 | /* Find the next real entry, without updating the iterator itself */ |
1204 | static struct trace_entry * | ||
1205 | find_next_entry(struct trace_iterator *iter, int *ent_cpu) | ||
1135 | { | 1206 | { |
1136 | iter->idx++; | 1207 | return __find_next_entry(iter, ent_cpu, 0); |
1137 | iter->next_idx[iter->cpu]++; | 1208 | } |
1138 | iter->next_page_idx[iter->cpu]++; | 1209 | |
1210 | /* Find the next real entry, and increment the iterator to the next entry */ | ||
1211 | static void *find_next_entry_inc(struct trace_iterator *iter) | ||
1212 | { | ||
1213 | struct trace_entry *next; | ||
1214 | int next_cpu = -1; | ||
1139 | 1215 | ||
1140 | if (iter->next_page_idx[iter->cpu] >= ENTRIES_PER_PAGE) { | 1216 | next = __find_next_entry(iter, &next_cpu, 1); |
1141 | struct trace_array_cpu *data = iter->tr->data[iter->cpu]; | ||
1142 | 1217 | ||
1143 | iter->next_page_idx[iter->cpu] = 0; | 1218 | iter->prev_ent = iter->ent; |
1144 | iter->next_page[iter->cpu] = | 1219 | iter->prev_cpu = iter->cpu; |
1145 | trace_next_list(data, iter->next_page[iter->cpu]); | 1220 | |
1146 | } | 1221 | iter->ent = next; |
1222 | iter->cpu = next_cpu; | ||
1223 | |||
1224 | if (next) | ||
1225 | trace_iterator_increment(iter, iter->cpu); | ||
1226 | |||
1227 | return next ? iter : NULL; | ||
1147 | } | 1228 | } |
1148 | 1229 | ||
1149 | static void trace_consume(struct trace_iterator *iter) | 1230 | static void trace_consume(struct trace_iterator *iter) |
1150 | { | 1231 | { |
1151 | struct trace_array_cpu *data = iter->tr->data[iter->cpu]; | 1232 | struct trace_array_cpu *data = iter->tr->data[iter->cpu]; |
1233 | struct trace_entry *ent; | ||
1152 | 1234 | ||
1235 | again: | ||
1153 | data->trace_tail_idx++; | 1236 | data->trace_tail_idx++; |
1154 | if (data->trace_tail_idx >= ENTRIES_PER_PAGE) { | 1237 | if (data->trace_tail_idx >= ENTRIES_PER_PAGE) { |
1155 | data->trace_tail = trace_next_page(data, data->trace_tail); | 1238 | data->trace_tail = trace_next_page(data, data->trace_tail); |
@@ -1160,25 +1243,11 @@ static void trace_consume(struct trace_iterator *iter) | |||
1160 | if (data->trace_head == data->trace_tail && | 1243 | if (data->trace_head == data->trace_tail && |
1161 | data->trace_head_idx == data->trace_tail_idx) | 1244 | data->trace_head_idx == data->trace_tail_idx) |
1162 | data->trace_idx = 0; | 1245 | data->trace_idx = 0; |
1163 | } | ||
1164 | |||
1165 | static void *find_next_entry_inc(struct trace_iterator *iter) | ||
1166 | { | ||
1167 | struct trace_entry *next; | ||
1168 | int next_cpu = -1; | ||
1169 | |||
1170 | next = find_next_entry(iter, &next_cpu); | ||
1171 | |||
1172 | iter->prev_ent = iter->ent; | ||
1173 | iter->prev_cpu = iter->cpu; | ||
1174 | |||
1175 | iter->ent = next; | ||
1176 | iter->cpu = next_cpu; | ||
1177 | |||
1178 | if (next) | ||
1179 | trace_iterator_increment(iter); | ||
1180 | 1246 | ||
1181 | return next ? iter : NULL; | 1247 | ent = trace_entry_idx(iter->tr, iter->tr->data[iter->cpu], |
1248 | iter, iter->cpu); | ||
1249 | if (ent && ent->type == TRACE_CONT) | ||
1250 | goto again; | ||
1182 | } | 1251 | } |
1183 | 1252 | ||
1184 | static void *s_next(struct seq_file *m, void *v, loff_t *pos) | 1253 | static void *s_next(struct seq_file *m, void *v, loff_t *pos) |
@@ -1473,6 +1542,26 @@ lat_print_timestamp(struct trace_seq *s, unsigned long long abs_usecs, | |||
1473 | 1542 | ||
1474 | static const char state_to_char[] = TASK_STATE_TO_CHAR_STR; | 1543 | static const char state_to_char[] = TASK_STATE_TO_CHAR_STR; |
1475 | 1544 | ||
1545 | static void | ||
1546 | trace_seq_print_cont(struct trace_seq *s, struct trace_iterator *iter) | ||
1547 | { | ||
1548 | struct trace_array *tr = iter->tr; | ||
1549 | struct trace_array_cpu *data = tr->data[iter->cpu]; | ||
1550 | struct trace_entry *ent; | ||
1551 | |||
1552 | ent = trace_entry_idx(tr, data, iter, iter->cpu); | ||
1553 | if (!ent || ent->type != TRACE_CONT) { | ||
1554 | trace_seq_putc(s, '\n'); | ||
1555 | return; | ||
1556 | } | ||
1557 | |||
1558 | do { | ||
1559 | trace_seq_printf(s, "%s", ent->cont.buf); | ||
1560 | trace_iterator_increment(iter, iter->cpu); | ||
1561 | ent = trace_entry_idx(tr, data, iter, iter->cpu); | ||
1562 | } while (ent && ent->type == TRACE_CONT); | ||
1563 | } | ||
1564 | |||
1476 | static int | 1565 | static int |
1477 | print_lat_fmt(struct trace_iterator *iter, unsigned int trace_idx, int cpu) | 1566 | print_lat_fmt(struct trace_iterator *iter, unsigned int trace_idx, int cpu) |
1478 | { | 1567 | { |
@@ -1491,6 +1580,10 @@ print_lat_fmt(struct trace_iterator *iter, unsigned int trace_idx, int cpu) | |||
1491 | 1580 | ||
1492 | if (!next_entry) | 1581 | if (!next_entry) |
1493 | next_entry = entry; | 1582 | next_entry = entry; |
1583 | |||
1584 | if (entry->type == TRACE_CONT) | ||
1585 | return 1; | ||
1586 | |||
1494 | rel_usecs = ns2usecs(next_entry->field.t - entry->field.t); | 1587 | rel_usecs = ns2usecs(next_entry->field.t - entry->field.t); |
1495 | abs_usecs = ns2usecs(entry->field.t - iter->tr->time_start); | 1588 | abs_usecs = ns2usecs(entry->field.t - iter->tr->time_start); |
1496 | 1589 | ||
@@ -1550,6 +1643,12 @@ print_lat_fmt(struct trace_iterator *iter, unsigned int trace_idx, int cpu) | |||
1550 | } | 1643 | } |
1551 | trace_seq_puts(s, "\n"); | 1644 | trace_seq_puts(s, "\n"); |
1552 | break; | 1645 | break; |
1646 | case TRACE_PRINT: | ||
1647 | seq_print_ip_sym(s, field->print.ip, sym_flags); | ||
1648 | trace_seq_printf(s, ": %s", field->print.buf); | ||
1649 | if (field->flags && TRACE_FLAG_CONT) | ||
1650 | trace_seq_print_cont(s, iter); | ||
1651 | break; | ||
1553 | default: | 1652 | default: |
1554 | trace_seq_printf(s, "Unknown type %d\n", entry->type); | 1653 | trace_seq_printf(s, "Unknown type %d\n", entry->type); |
1555 | } | 1654 | } |
@@ -1571,6 +1670,10 @@ static int print_trace_fmt(struct trace_iterator *iter) | |||
1571 | int i; | 1670 | int i; |
1572 | 1671 | ||
1573 | entry = iter->ent; | 1672 | entry = iter->ent; |
1673 | |||
1674 | if (entry->type == TRACE_CONT) | ||
1675 | return 1; | ||
1676 | |||
1574 | field = &entry->field; | 1677 | field = &entry->field; |
1575 | 1678 | ||
1576 | comm = trace_find_cmdline(iter->ent->field.pid); | 1679 | comm = trace_find_cmdline(iter->ent->field.pid); |
@@ -1653,6 +1756,12 @@ static int print_trace_fmt(struct trace_iterator *iter) | |||
1653 | if (!ret) | 1756 | if (!ret) |
1654 | return 0; | 1757 | return 0; |
1655 | break; | 1758 | break; |
1759 | case TRACE_PRINT: | ||
1760 | seq_print_ip_sym(s, field->print.ip, sym_flags); | ||
1761 | trace_seq_printf(s, ": %s", field->print.buf); | ||
1762 | if (field->flags && TRACE_FLAG_CONT) | ||
1763 | trace_seq_print_cont(s, iter); | ||
1764 | break; | ||
1656 | } | 1765 | } |
1657 | return 1; | 1766 | return 1; |
1658 | } | 1767 | } |
@@ -1666,6 +1775,10 @@ static int print_raw_fmt(struct trace_iterator *iter) | |||
1666 | int S, T; | 1775 | int S, T; |
1667 | 1776 | ||
1668 | entry = iter->ent; | 1777 | entry = iter->ent; |
1778 | |||
1779 | if (entry->type == TRACE_CONT) | ||
1780 | return 1; | ||
1781 | |||
1669 | field = &entry->field; | 1782 | field = &entry->field; |
1670 | 1783 | ||
1671 | ret = trace_seq_printf(s, "%d %d %llu ", | 1784 | ret = trace_seq_printf(s, "%d %d %llu ", |
@@ -1708,6 +1821,12 @@ static int print_raw_fmt(struct trace_iterator *iter) | |||
1708 | if (!ret) | 1821 | if (!ret) |
1709 | return 0; | 1822 | return 0; |
1710 | break; | 1823 | break; |
1824 | case TRACE_PRINT: | ||
1825 | trace_seq_printf(s, "# %lx %s", | ||
1826 | field->print.ip, field->print.buf); | ||
1827 | if (field->flags && TRACE_FLAG_CONT) | ||
1828 | trace_seq_print_cont(s, iter); | ||
1829 | break; | ||
1711 | } | 1830 | } |
1712 | return 1; | 1831 | return 1; |
1713 | } | 1832 | } |
@@ -1733,6 +1852,10 @@ static int print_hex_fmt(struct trace_iterator *iter) | |||
1733 | int S, T; | 1852 | int S, T; |
1734 | 1853 | ||
1735 | entry = iter->ent; | 1854 | entry = iter->ent; |
1855 | |||
1856 | if (entry->type == TRACE_CONT) | ||
1857 | return 1; | ||
1858 | |||
1736 | field = &entry->field; | 1859 | field = &entry->field; |
1737 | 1860 | ||
1738 | SEQ_PUT_HEX_FIELD_RET(s, field->pid); | 1861 | SEQ_PUT_HEX_FIELD_RET(s, field->pid); |
@@ -1778,6 +1901,10 @@ static int print_bin_fmt(struct trace_iterator *iter) | |||
1778 | struct trace_field *field; | 1901 | struct trace_field *field; |
1779 | 1902 | ||
1780 | entry = iter->ent; | 1903 | entry = iter->ent; |
1904 | |||
1905 | if (entry->type == TRACE_CONT) | ||
1906 | return 1; | ||
1907 | |||
1781 | field = &entry->field; | 1908 | field = &entry->field; |
1782 | 1909 | ||
1783 | SEQ_PUT_FIELD_RET(s, field->pid); | 1910 | SEQ_PUT_FIELD_RET(s, field->pid); |
@@ -2943,6 +3070,94 @@ static __init void tracer_init_debugfs(void) | |||
2943 | #endif | 3070 | #endif |
2944 | } | 3071 | } |
2945 | 3072 | ||
3073 | #define TRACE_BUF_SIZE 1024 | ||
3074 | #define TRACE_PRINT_BUF_SIZE \ | ||
3075 | (sizeof(struct trace_field) - offsetof(struct trace_field, print.buf)) | ||
3076 | #define TRACE_CONT_BUF_SIZE sizeof(struct trace_field) | ||
3077 | |||
3078 | /** | ||
3079 | * ftrace_printk - printf formatting in the ftrace buffer | ||
3080 | * @fmt - the printf format for printing. | ||
3081 | * | ||
3082 | * Note: __ftrace_printk is an internal function for ftrace_printk and | ||
3083 | * the @ip is passed in via the ftrace_printk macro. | ||
3084 | * | ||
3085 | * This function allows a kernel developer to debug fast path sections | ||
3086 | * that printk is not appropriate for. By scattering in various | ||
3087 | * printk like tracing in the code, a developer can quickly see | ||
3088 | * where problems are occurring. | ||
3089 | * | ||
3090 | * This is intended as a debugging tool for the developer only. | ||
3091 | * Please reframe from leaving ftrace_printks scattered around in | ||
3092 | * your code. | ||
3093 | */ | ||
3094 | int __ftrace_printk(unsigned long ip, const char *fmt, ...) | ||
3095 | { | ||
3096 | struct trace_array *tr = &global_trace; | ||
3097 | static DEFINE_SPINLOCK(trace_buf_lock); | ||
3098 | static char trace_buf[TRACE_BUF_SIZE]; | ||
3099 | struct trace_array_cpu *data; | ||
3100 | struct trace_entry *entry; | ||
3101 | unsigned long flags; | ||
3102 | long disabled; | ||
3103 | va_list ap; | ||
3104 | int cpu, len = 0, write, written = 0; | ||
3105 | |||
3106 | if (likely(!ftrace_function_enabled)) | ||
3107 | return 0; | ||
3108 | |||
3109 | local_irq_save(flags); | ||
3110 | cpu = raw_smp_processor_id(); | ||
3111 | data = tr->data[cpu]; | ||
3112 | disabled = atomic_inc_return(&data->disabled); | ||
3113 | |||
3114 | if (unlikely(disabled != 1 || !ftrace_function_enabled)) | ||
3115 | goto out; | ||
3116 | |||
3117 | spin_lock(&trace_buf_lock); | ||
3118 | va_start(ap, fmt); | ||
3119 | len = vsnprintf(trace_buf, TRACE_BUF_SIZE, fmt, ap); | ||
3120 | va_end(ap); | ||
3121 | |||
3122 | len = min(len, TRACE_BUF_SIZE-1); | ||
3123 | trace_buf[len] = 0; | ||
3124 | |||
3125 | __raw_spin_lock(&data->lock); | ||
3126 | entry = tracing_get_trace_entry(tr, data); | ||
3127 | tracing_generic_entry_update(entry, flags); | ||
3128 | entry->type = TRACE_PRINT; | ||
3129 | entry->field.print.ip = ip; | ||
3130 | |||
3131 | write = min(len, (int)(TRACE_PRINT_BUF_SIZE-1)); | ||
3132 | |||
3133 | memcpy(&entry->field.print.buf, trace_buf, write); | ||
3134 | entry->field.print.buf[write] = 0; | ||
3135 | written = write; | ||
3136 | |||
3137 | if (written != len) | ||
3138 | entry->field.flags |= TRACE_FLAG_CONT; | ||
3139 | |||
3140 | while (written != len) { | ||
3141 | entry = tracing_get_trace_entry(tr, data); | ||
3142 | |||
3143 | entry->type = TRACE_CONT; | ||
3144 | write = min(len - written, (int)(TRACE_CONT_BUF_SIZE-1)); | ||
3145 | memcpy(&entry->cont.buf, trace_buf+written, write); | ||
3146 | entry->cont.buf[write] = 0; | ||
3147 | written += write; | ||
3148 | } | ||
3149 | __raw_spin_unlock(&data->lock); | ||
3150 | |||
3151 | spin_unlock(&trace_buf_lock); | ||
3152 | |||
3153 | out: | ||
3154 | atomic_dec(&data->disabled); | ||
3155 | local_irq_restore(flags); | ||
3156 | |||
3157 | return len; | ||
3158 | } | ||
3159 | EXPORT_SYMBOL_GPL(__ftrace_printk); | ||
3160 | |||
2946 | static int trace_alloc_page(void) | 3161 | static int trace_alloc_page(void) |
2947 | { | 3162 | { |
2948 | struct trace_array_cpu *data; | 3163 | struct trace_array_cpu *data; |
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 6ddd6a6556cf..50b6d7a6f01a 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h | |||
@@ -13,7 +13,9 @@ enum trace_type { | |||
13 | TRACE_FN, | 13 | TRACE_FN, |
14 | TRACE_CTX, | 14 | TRACE_CTX, |
15 | TRACE_WAKE, | 15 | TRACE_WAKE, |
16 | TRACE_CONT, | ||
16 | TRACE_STACK, | 17 | TRACE_STACK, |
18 | TRACE_PRINT, | ||
17 | TRACE_SPECIAL, | 19 | TRACE_SPECIAL, |
18 | TRACE_MMIO_RW, | 20 | TRACE_MMIO_RW, |
19 | TRACE_MMIO_MAP, | 21 | TRACE_MMIO_MAP, |
@@ -61,6 +63,14 @@ struct stack_entry { | |||
61 | }; | 63 | }; |
62 | 64 | ||
63 | /* | 65 | /* |
66 | * ftrace_printk entry: | ||
67 | */ | ||
68 | struct print_entry { | ||
69 | unsigned long ip; | ||
70 | char buf[]; | ||
71 | }; | ||
72 | |||
73 | /* | ||
64 | * The trace field - the most basic unit of tracing. This is what | 74 | * The trace field - the most basic unit of tracing. This is what |
65 | * is printed in the end as a single line in the trace output, such as: | 75 | * is printed in the end as a single line in the trace output, such as: |
66 | * | 76 | * |
@@ -77,6 +87,7 @@ struct trace_field { | |||
77 | struct ctx_switch_entry ctx; | 87 | struct ctx_switch_entry ctx; |
78 | struct special_entry special; | 88 | struct special_entry special; |
79 | struct stack_entry stack; | 89 | struct stack_entry stack; |
90 | struct print_entry print; | ||
80 | struct mmiotrace_rw mmiorw; | 91 | struct mmiotrace_rw mmiorw; |
81 | struct mmiotrace_map mmiomap; | 92 | struct mmiotrace_map mmiomap; |
82 | }; | 93 | }; |
diff --git a/kernel/trace/trace_selftest.c b/kernel/trace/trace_selftest.c index 0911b7e073bf..630715bbd572 100644 --- a/kernel/trace/trace_selftest.c +++ b/kernel/trace/trace_selftest.c | |||
@@ -9,7 +9,9 @@ static inline int trace_valid_entry(struct trace_entry *entry) | |||
9 | case TRACE_FN: | 9 | case TRACE_FN: |
10 | case TRACE_CTX: | 10 | case TRACE_CTX: |
11 | case TRACE_WAKE: | 11 | case TRACE_WAKE: |
12 | case TRACE_CONT: | ||
12 | case TRACE_STACK: | 13 | case TRACE_STACK: |
14 | case TRACE_PRINT: | ||
13 | case TRACE_SPECIAL: | 15 | case TRACE_SPECIAL: |
14 | return 1; | 16 | return 1; |
15 | } | 17 | } |
@@ -120,11 +122,11 @@ int trace_selftest_startup_dynamic_tracing(struct tracer *trace, | |||
120 | struct trace_array *tr, | 122 | struct trace_array *tr, |
121 | int (*func)(void)) | 123 | int (*func)(void)) |
122 | { | 124 | { |
123 | unsigned long count; | ||
124 | int ret; | ||
125 | int save_ftrace_enabled = ftrace_enabled; | 125 | int save_ftrace_enabled = ftrace_enabled; |
126 | int save_tracer_enabled = tracer_enabled; | 126 | int save_tracer_enabled = tracer_enabled; |
127 | unsigned long count; | ||
127 | char *func_name; | 128 | char *func_name; |
129 | int ret; | ||
128 | 130 | ||
129 | /* The ftrace test PASSED */ | 131 | /* The ftrace test PASSED */ |
130 | printk(KERN_CONT "PASSED\n"); | 132 | printk(KERN_CONT "PASSED\n"); |
@@ -157,6 +159,7 @@ int trace_selftest_startup_dynamic_tracing(struct tracer *trace, | |||
157 | /* enable tracing */ | 159 | /* enable tracing */ |
158 | tr->ctrl = 1; | 160 | tr->ctrl = 1; |
159 | trace->init(tr); | 161 | trace->init(tr); |
162 | |||
160 | /* Sleep for a 1/10 of a second */ | 163 | /* Sleep for a 1/10 of a second */ |
161 | msleep(100); | 164 | msleep(100); |
162 | 165 | ||
@@ -212,10 +215,10 @@ int trace_selftest_startup_dynamic_tracing(struct tracer *trace, | |||
212 | int | 215 | int |
213 | trace_selftest_startup_function(struct tracer *trace, struct trace_array *tr) | 216 | trace_selftest_startup_function(struct tracer *trace, struct trace_array *tr) |
214 | { | 217 | { |
215 | unsigned long count; | ||
216 | int ret; | ||
217 | int save_ftrace_enabled = ftrace_enabled; | 218 | int save_ftrace_enabled = ftrace_enabled; |
218 | int save_tracer_enabled = tracer_enabled; | 219 | int save_tracer_enabled = tracer_enabled; |
220 | unsigned long count; | ||
221 | int ret; | ||
219 | 222 | ||
220 | /* make sure msleep has been recorded */ | 223 | /* make sure msleep has been recorded */ |
221 | msleep(1); | 224 | msleep(1); |