diff options
Diffstat (limited to 'kernel/trace/trace_output.c')
-rw-r--r-- | kernel/trace/trace_output.c | 916 |
1 files changed, 916 insertions, 0 deletions
diff --git a/kernel/trace/trace_output.c b/kernel/trace/trace_output.c new file mode 100644 index 000000000000..491832af9ba1 --- /dev/null +++ b/kernel/trace/trace_output.c | |||
@@ -0,0 +1,916 @@ | |||
1 | /* | ||
2 | * trace_output.c | ||
3 | * | ||
4 | * Copyright (C) 2008 Red Hat Inc, Steven Rostedt <srostedt@redhat.com> | ||
5 | * | ||
6 | */ | ||
7 | |||
8 | #include <linux/module.h> | ||
9 | #include <linux/mutex.h> | ||
10 | #include <linux/ftrace.h> | ||
11 | |||
12 | #include "trace_output.h" | ||
13 | |||
14 | /* must be a power of 2 */ | ||
15 | #define EVENT_HASHSIZE 128 | ||
16 | |||
17 | static DEFINE_MUTEX(trace_event_mutex); | ||
18 | static struct hlist_head event_hash[EVENT_HASHSIZE] __read_mostly; | ||
19 | |||
20 | static int next_event_type = __TRACE_LAST_TYPE + 1; | ||
21 | |||
22 | /** | ||
23 | * trace_seq_printf - sequence printing of trace information | ||
24 | * @s: trace sequence descriptor | ||
25 | * @fmt: printf format string | ||
26 | * | ||
27 | * The tracer may use either sequence operations or its own | ||
28 | * copy to user routines. To simplify formating of a trace | ||
29 | * trace_seq_printf is used to store strings into a special | ||
30 | * buffer (@s). Then the output may be either used by | ||
31 | * the sequencer or pulled into another buffer. | ||
32 | */ | ||
33 | int | ||
34 | trace_seq_printf(struct trace_seq *s, const char *fmt, ...) | ||
35 | { | ||
36 | int len = (PAGE_SIZE - 1) - s->len; | ||
37 | va_list ap; | ||
38 | int ret; | ||
39 | |||
40 | if (!len) | ||
41 | return 0; | ||
42 | |||
43 | va_start(ap, fmt); | ||
44 | ret = vsnprintf(s->buffer + s->len, len, fmt, ap); | ||
45 | va_end(ap); | ||
46 | |||
47 | /* If we can't write it all, don't bother writing anything */ | ||
48 | if (ret >= len) | ||
49 | return 0; | ||
50 | |||
51 | s->len += ret; | ||
52 | |||
53 | return len; | ||
54 | } | ||
55 | |||
56 | int trace_seq_bprintf(struct trace_seq *s, const char *fmt, const u32 *binary) | ||
57 | { | ||
58 | int len = (PAGE_SIZE - 1) - s->len; | ||
59 | int ret; | ||
60 | |||
61 | if (!len) | ||
62 | return 0; | ||
63 | |||
64 | ret = bstr_printf(s->buffer + s->len, len, fmt, binary); | ||
65 | |||
66 | /* If we can't write it all, don't bother writing anything */ | ||
67 | if (ret >= len) | ||
68 | return 0; | ||
69 | |||
70 | s->len += ret; | ||
71 | |||
72 | return len; | ||
73 | } | ||
74 | |||
75 | /** | ||
76 | * trace_seq_puts - trace sequence printing of simple string | ||
77 | * @s: trace sequence descriptor | ||
78 | * @str: simple string to record | ||
79 | * | ||
80 | * The tracer may use either the sequence operations or its own | ||
81 | * copy to user routines. This function records a simple string | ||
82 | * into a special buffer (@s) for later retrieval by a sequencer | ||
83 | * or other mechanism. | ||
84 | */ | ||
85 | int trace_seq_puts(struct trace_seq *s, const char *str) | ||
86 | { | ||
87 | int len = strlen(str); | ||
88 | |||
89 | if (len > ((PAGE_SIZE - 1) - s->len)) | ||
90 | return 0; | ||
91 | |||
92 | memcpy(s->buffer + s->len, str, len); | ||
93 | s->len += len; | ||
94 | |||
95 | return len; | ||
96 | } | ||
97 | |||
98 | int trace_seq_putc(struct trace_seq *s, unsigned char c) | ||
99 | { | ||
100 | if (s->len >= (PAGE_SIZE - 1)) | ||
101 | return 0; | ||
102 | |||
103 | s->buffer[s->len++] = c; | ||
104 | |||
105 | return 1; | ||
106 | } | ||
107 | |||
108 | int trace_seq_putmem(struct trace_seq *s, void *mem, size_t len) | ||
109 | { | ||
110 | if (len > ((PAGE_SIZE - 1) - s->len)) | ||
111 | return 0; | ||
112 | |||
113 | memcpy(s->buffer + s->len, mem, len); | ||
114 | s->len += len; | ||
115 | |||
116 | return len; | ||
117 | } | ||
118 | |||
119 | int trace_seq_putmem_hex(struct trace_seq *s, void *mem, size_t len) | ||
120 | { | ||
121 | unsigned char hex[HEX_CHARS]; | ||
122 | unsigned char *data = mem; | ||
123 | int i, j; | ||
124 | |||
125 | #ifdef __BIG_ENDIAN | ||
126 | for (i = 0, j = 0; i < len; i++) { | ||
127 | #else | ||
128 | for (i = len-1, j = 0; i >= 0; i--) { | ||
129 | #endif | ||
130 | hex[j++] = hex_asc_hi(data[i]); | ||
131 | hex[j++] = hex_asc_lo(data[i]); | ||
132 | } | ||
133 | hex[j++] = ' '; | ||
134 | |||
135 | return trace_seq_putmem(s, hex, j); | ||
136 | } | ||
137 | |||
138 | int trace_seq_path(struct trace_seq *s, struct path *path) | ||
139 | { | ||
140 | unsigned char *p; | ||
141 | |||
142 | if (s->len >= (PAGE_SIZE - 1)) | ||
143 | return 0; | ||
144 | p = d_path(path, s->buffer + s->len, PAGE_SIZE - s->len); | ||
145 | if (!IS_ERR(p)) { | ||
146 | p = mangle_path(s->buffer + s->len, p, "\n"); | ||
147 | if (p) { | ||
148 | s->len = p - s->buffer; | ||
149 | return 1; | ||
150 | } | ||
151 | } else { | ||
152 | s->buffer[s->len++] = '?'; | ||
153 | return 1; | ||
154 | } | ||
155 | |||
156 | return 0; | ||
157 | } | ||
158 | |||
159 | #ifdef CONFIG_KRETPROBES | ||
160 | static inline const char *kretprobed(const char *name) | ||
161 | { | ||
162 | static const char tramp_name[] = "kretprobe_trampoline"; | ||
163 | int size = sizeof(tramp_name); | ||
164 | |||
165 | if (strncmp(tramp_name, name, size) == 0) | ||
166 | return "[unknown/kretprobe'd]"; | ||
167 | return name; | ||
168 | } | ||
169 | #else | ||
170 | static inline const char *kretprobed(const char *name) | ||
171 | { | ||
172 | return name; | ||
173 | } | ||
174 | #endif /* CONFIG_KRETPROBES */ | ||
175 | |||
176 | static int | ||
177 | seq_print_sym_short(struct trace_seq *s, const char *fmt, unsigned long address) | ||
178 | { | ||
179 | #ifdef CONFIG_KALLSYMS | ||
180 | char str[KSYM_SYMBOL_LEN]; | ||
181 | const char *name; | ||
182 | |||
183 | kallsyms_lookup(address, NULL, NULL, NULL, str); | ||
184 | |||
185 | name = kretprobed(str); | ||
186 | |||
187 | return trace_seq_printf(s, fmt, name); | ||
188 | #endif | ||
189 | return 1; | ||
190 | } | ||
191 | |||
192 | static int | ||
193 | seq_print_sym_offset(struct trace_seq *s, const char *fmt, | ||
194 | unsigned long address) | ||
195 | { | ||
196 | #ifdef CONFIG_KALLSYMS | ||
197 | char str[KSYM_SYMBOL_LEN]; | ||
198 | const char *name; | ||
199 | |||
200 | sprint_symbol(str, address); | ||
201 | name = kretprobed(str); | ||
202 | |||
203 | return trace_seq_printf(s, fmt, name); | ||
204 | #endif | ||
205 | return 1; | ||
206 | } | ||
207 | |||
208 | #ifndef CONFIG_64BIT | ||
209 | # define IP_FMT "%08lx" | ||
210 | #else | ||
211 | # define IP_FMT "%016lx" | ||
212 | #endif | ||
213 | |||
214 | int seq_print_user_ip(struct trace_seq *s, struct mm_struct *mm, | ||
215 | unsigned long ip, unsigned long sym_flags) | ||
216 | { | ||
217 | struct file *file = NULL; | ||
218 | unsigned long vmstart = 0; | ||
219 | int ret = 1; | ||
220 | |||
221 | if (mm) { | ||
222 | const struct vm_area_struct *vma; | ||
223 | |||
224 | down_read(&mm->mmap_sem); | ||
225 | vma = find_vma(mm, ip); | ||
226 | if (vma) { | ||
227 | file = vma->vm_file; | ||
228 | vmstart = vma->vm_start; | ||
229 | } | ||
230 | if (file) { | ||
231 | ret = trace_seq_path(s, &file->f_path); | ||
232 | if (ret) | ||
233 | ret = trace_seq_printf(s, "[+0x%lx]", | ||
234 | ip - vmstart); | ||
235 | } | ||
236 | up_read(&mm->mmap_sem); | ||
237 | } | ||
238 | if (ret && ((sym_flags & TRACE_ITER_SYM_ADDR) || !file)) | ||
239 | ret = trace_seq_printf(s, " <" IP_FMT ">", ip); | ||
240 | return ret; | ||
241 | } | ||
242 | |||
243 | int | ||
244 | seq_print_userip_objs(const struct userstack_entry *entry, struct trace_seq *s, | ||
245 | unsigned long sym_flags) | ||
246 | { | ||
247 | struct mm_struct *mm = NULL; | ||
248 | int ret = 1; | ||
249 | unsigned int i; | ||
250 | |||
251 | if (trace_flags & TRACE_ITER_SYM_USEROBJ) { | ||
252 | struct task_struct *task; | ||
253 | /* | ||
254 | * we do the lookup on the thread group leader, | ||
255 | * since individual threads might have already quit! | ||
256 | */ | ||
257 | rcu_read_lock(); | ||
258 | task = find_task_by_vpid(entry->ent.tgid); | ||
259 | if (task) | ||
260 | mm = get_task_mm(task); | ||
261 | rcu_read_unlock(); | ||
262 | } | ||
263 | |||
264 | for (i = 0; i < FTRACE_STACK_ENTRIES; i++) { | ||
265 | unsigned long ip = entry->caller[i]; | ||
266 | |||
267 | if (ip == ULONG_MAX || !ret) | ||
268 | break; | ||
269 | if (i && ret) | ||
270 | ret = trace_seq_puts(s, " <- "); | ||
271 | if (!ip) { | ||
272 | if (ret) | ||
273 | ret = trace_seq_puts(s, "??"); | ||
274 | continue; | ||
275 | } | ||
276 | if (!ret) | ||
277 | break; | ||
278 | if (ret) | ||
279 | ret = seq_print_user_ip(s, mm, ip, sym_flags); | ||
280 | } | ||
281 | |||
282 | if (mm) | ||
283 | mmput(mm); | ||
284 | return ret; | ||
285 | } | ||
286 | |||
287 | int | ||
288 | seq_print_ip_sym(struct trace_seq *s, unsigned long ip, unsigned long sym_flags) | ||
289 | { | ||
290 | int ret; | ||
291 | |||
292 | if (!ip) | ||
293 | return trace_seq_printf(s, "0"); | ||
294 | |||
295 | if (sym_flags & TRACE_ITER_SYM_OFFSET) | ||
296 | ret = seq_print_sym_offset(s, "%s", ip); | ||
297 | else | ||
298 | ret = seq_print_sym_short(s, "%s", ip); | ||
299 | |||
300 | if (!ret) | ||
301 | return 0; | ||
302 | |||
303 | if (sym_flags & TRACE_ITER_SYM_ADDR) | ||
304 | ret = trace_seq_printf(s, " <" IP_FMT ">", ip); | ||
305 | return ret; | ||
306 | } | ||
307 | |||
308 | static int | ||
309 | lat_print_generic(struct trace_seq *s, struct trace_entry *entry, int cpu) | ||
310 | { | ||
311 | int hardirq, softirq; | ||
312 | char *comm; | ||
313 | |||
314 | comm = trace_find_cmdline(entry->pid); | ||
315 | hardirq = entry->flags & TRACE_FLAG_HARDIRQ; | ||
316 | softirq = entry->flags & TRACE_FLAG_SOFTIRQ; | ||
317 | |||
318 | if (!trace_seq_printf(s, "%8.8s-%-5d %3d%c%c%c", | ||
319 | comm, entry->pid, cpu, | ||
320 | (entry->flags & TRACE_FLAG_IRQS_OFF) ? 'd' : | ||
321 | (entry->flags & TRACE_FLAG_IRQS_NOSUPPORT) ? | ||
322 | 'X' : '.', | ||
323 | (entry->flags & TRACE_FLAG_NEED_RESCHED) ? | ||
324 | 'N' : '.', | ||
325 | (hardirq && softirq) ? 'H' : | ||
326 | hardirq ? 'h' : softirq ? 's' : '.')) | ||
327 | return 0; | ||
328 | |||
329 | if (entry->preempt_count) | ||
330 | return trace_seq_printf(s, "%x", entry->preempt_count); | ||
331 | return trace_seq_puts(s, "."); | ||
332 | } | ||
333 | |||
334 | static unsigned long preempt_mark_thresh = 100; | ||
335 | |||
336 | static int | ||
337 | lat_print_timestamp(struct trace_seq *s, u64 abs_usecs, | ||
338 | unsigned long rel_usecs) | ||
339 | { | ||
340 | return trace_seq_printf(s, " %4lldus%c: ", abs_usecs, | ||
341 | rel_usecs > preempt_mark_thresh ? '!' : | ||
342 | rel_usecs > 1 ? '+' : ' '); | ||
343 | } | ||
344 | |||
345 | int trace_print_context(struct trace_iterator *iter) | ||
346 | { | ||
347 | struct trace_seq *s = &iter->seq; | ||
348 | struct trace_entry *entry = iter->ent; | ||
349 | char *comm = trace_find_cmdline(entry->pid); | ||
350 | unsigned long long t = ns2usecs(iter->ts); | ||
351 | unsigned long usec_rem = do_div(t, USEC_PER_SEC); | ||
352 | unsigned long secs = (unsigned long)t; | ||
353 | |||
354 | return trace_seq_printf(s, "%16s-%-5d [%03d] %5lu.%06lu: ", | ||
355 | comm, entry->pid, iter->cpu, secs, usec_rem); | ||
356 | } | ||
357 | |||
358 | int trace_print_lat_context(struct trace_iterator *iter) | ||
359 | { | ||
360 | u64 next_ts; | ||
361 | int ret; | ||
362 | struct trace_seq *s = &iter->seq; | ||
363 | struct trace_entry *entry = iter->ent, | ||
364 | *next_entry = trace_find_next_entry(iter, NULL, | ||
365 | &next_ts); | ||
366 | unsigned long verbose = (trace_flags & TRACE_ITER_VERBOSE); | ||
367 | unsigned long abs_usecs = ns2usecs(iter->ts - iter->tr->time_start); | ||
368 | unsigned long rel_usecs; | ||
369 | |||
370 | if (!next_entry) | ||
371 | next_ts = iter->ts; | ||
372 | rel_usecs = ns2usecs(next_ts - iter->ts); | ||
373 | |||
374 | if (verbose) { | ||
375 | char *comm = trace_find_cmdline(entry->pid); | ||
376 | ret = trace_seq_printf(s, "%16s %5d %3d %d %08x %08lx [%08lx]" | ||
377 | " %ld.%03ldms (+%ld.%03ldms): ", comm, | ||
378 | entry->pid, iter->cpu, entry->flags, | ||
379 | entry->preempt_count, iter->idx, | ||
380 | ns2usecs(iter->ts), | ||
381 | abs_usecs / USEC_PER_MSEC, | ||
382 | abs_usecs % USEC_PER_MSEC, | ||
383 | rel_usecs / USEC_PER_MSEC, | ||
384 | rel_usecs % USEC_PER_MSEC); | ||
385 | } else { | ||
386 | ret = lat_print_generic(s, entry, iter->cpu); | ||
387 | if (ret) | ||
388 | ret = lat_print_timestamp(s, abs_usecs, rel_usecs); | ||
389 | } | ||
390 | |||
391 | return ret; | ||
392 | } | ||
393 | |||
394 | static const char state_to_char[] = TASK_STATE_TO_CHAR_STR; | ||
395 | |||
396 | static int task_state_char(unsigned long state) | ||
397 | { | ||
398 | int bit = state ? __ffs(state) + 1 : 0; | ||
399 | |||
400 | return bit < sizeof(state_to_char) - 1 ? state_to_char[bit] : '?'; | ||
401 | } | ||
402 | |||
403 | /** | ||
404 | * ftrace_find_event - find a registered event | ||
405 | * @type: the type of event to look for | ||
406 | * | ||
407 | * Returns an event of type @type otherwise NULL | ||
408 | */ | ||
409 | struct trace_event *ftrace_find_event(int type) | ||
410 | { | ||
411 | struct trace_event *event; | ||
412 | struct hlist_node *n; | ||
413 | unsigned key; | ||
414 | |||
415 | key = type & (EVENT_HASHSIZE - 1); | ||
416 | |||
417 | hlist_for_each_entry_rcu(event, n, &event_hash[key], node) { | ||
418 | if (event->type == type) | ||
419 | return event; | ||
420 | } | ||
421 | |||
422 | return NULL; | ||
423 | } | ||
424 | |||
425 | /** | ||
426 | * register_ftrace_event - register output for an event type | ||
427 | * @event: the event type to register | ||
428 | * | ||
429 | * Event types are stored in a hash and this hash is used to | ||
430 | * find a way to print an event. If the @event->type is set | ||
431 | * then it will use that type, otherwise it will assign a | ||
432 | * type to use. | ||
433 | * | ||
434 | * If you assign your own type, please make sure it is added | ||
435 | * to the trace_type enum in trace.h, to avoid collisions | ||
436 | * with the dynamic types. | ||
437 | * | ||
438 | * Returns the event type number or zero on error. | ||
439 | */ | ||
440 | int register_ftrace_event(struct trace_event *event) | ||
441 | { | ||
442 | unsigned key; | ||
443 | int ret = 0; | ||
444 | |||
445 | mutex_lock(&trace_event_mutex); | ||
446 | |||
447 | if (!event->type) | ||
448 | event->type = next_event_type++; | ||
449 | else if (event->type > __TRACE_LAST_TYPE) { | ||
450 | printk(KERN_WARNING "Need to add type to trace.h\n"); | ||
451 | WARN_ON(1); | ||
452 | } | ||
453 | |||
454 | if (ftrace_find_event(event->type)) | ||
455 | goto out; | ||
456 | |||
457 | if (event->trace == NULL) | ||
458 | event->trace = trace_nop_print; | ||
459 | if (event->raw == NULL) | ||
460 | event->raw = trace_nop_print; | ||
461 | if (event->hex == NULL) | ||
462 | event->hex = trace_nop_print; | ||
463 | if (event->binary == NULL) | ||
464 | event->binary = trace_nop_print; | ||
465 | |||
466 | key = event->type & (EVENT_HASHSIZE - 1); | ||
467 | |||
468 | hlist_add_head_rcu(&event->node, &event_hash[key]); | ||
469 | |||
470 | ret = event->type; | ||
471 | out: | ||
472 | mutex_unlock(&trace_event_mutex); | ||
473 | |||
474 | return ret; | ||
475 | } | ||
476 | |||
477 | /** | ||
478 | * unregister_ftrace_event - remove a no longer used event | ||
479 | * @event: the event to remove | ||
480 | */ | ||
481 | int unregister_ftrace_event(struct trace_event *event) | ||
482 | { | ||
483 | mutex_lock(&trace_event_mutex); | ||
484 | hlist_del(&event->node); | ||
485 | mutex_unlock(&trace_event_mutex); | ||
486 | |||
487 | return 0; | ||
488 | } | ||
489 | |||
490 | /* | ||
491 | * Standard events | ||
492 | */ | ||
493 | |||
494 | enum print_line_t trace_nop_print(struct trace_iterator *iter, int flags) | ||
495 | { | ||
496 | return TRACE_TYPE_HANDLED; | ||
497 | } | ||
498 | |||
499 | /* TRACE_FN */ | ||
500 | static enum print_line_t trace_fn_trace(struct trace_iterator *iter, int flags) | ||
501 | { | ||
502 | struct ftrace_entry *field; | ||
503 | struct trace_seq *s = &iter->seq; | ||
504 | |||
505 | trace_assign_type(field, iter->ent); | ||
506 | |||
507 | if (!seq_print_ip_sym(s, field->ip, flags)) | ||
508 | goto partial; | ||
509 | |||
510 | if ((flags & TRACE_ITER_PRINT_PARENT) && field->parent_ip) { | ||
511 | if (!trace_seq_printf(s, " <-")) | ||
512 | goto partial; | ||
513 | if (!seq_print_ip_sym(s, | ||
514 | field->parent_ip, | ||
515 | flags)) | ||
516 | goto partial; | ||
517 | } | ||
518 | if (!trace_seq_printf(s, "\n")) | ||
519 | goto partial; | ||
520 | |||
521 | return TRACE_TYPE_HANDLED; | ||
522 | |||
523 | partial: | ||
524 | return TRACE_TYPE_PARTIAL_LINE; | ||
525 | } | ||
526 | |||
527 | static enum print_line_t trace_fn_raw(struct trace_iterator *iter, int flags) | ||
528 | { | ||
529 | struct ftrace_entry *field; | ||
530 | |||
531 | trace_assign_type(field, iter->ent); | ||
532 | |||
533 | if (!trace_seq_printf(&iter->seq, "%lx %lx\n", | ||
534 | field->ip, | ||
535 | field->parent_ip)) | ||
536 | return TRACE_TYPE_PARTIAL_LINE; | ||
537 | |||
538 | return TRACE_TYPE_HANDLED; | ||
539 | } | ||
540 | |||
541 | static enum print_line_t trace_fn_hex(struct trace_iterator *iter, int flags) | ||
542 | { | ||
543 | struct ftrace_entry *field; | ||
544 | struct trace_seq *s = &iter->seq; | ||
545 | |||
546 | trace_assign_type(field, iter->ent); | ||
547 | |||
548 | SEQ_PUT_HEX_FIELD_RET(s, field->ip); | ||
549 | SEQ_PUT_HEX_FIELD_RET(s, field->parent_ip); | ||
550 | |||
551 | return TRACE_TYPE_HANDLED; | ||
552 | } | ||
553 | |||
554 | static enum print_line_t trace_fn_bin(struct trace_iterator *iter, int flags) | ||
555 | { | ||
556 | struct ftrace_entry *field; | ||
557 | struct trace_seq *s = &iter->seq; | ||
558 | |||
559 | trace_assign_type(field, iter->ent); | ||
560 | |||
561 | SEQ_PUT_FIELD_RET(s, field->ip); | ||
562 | SEQ_PUT_FIELD_RET(s, field->parent_ip); | ||
563 | |||
564 | return TRACE_TYPE_HANDLED; | ||
565 | } | ||
566 | |||
567 | static struct trace_event trace_fn_event = { | ||
568 | .type = TRACE_FN, | ||
569 | .trace = trace_fn_trace, | ||
570 | .raw = trace_fn_raw, | ||
571 | .hex = trace_fn_hex, | ||
572 | .binary = trace_fn_bin, | ||
573 | }; | ||
574 | |||
575 | /* TRACE_CTX an TRACE_WAKE */ | ||
576 | static enum print_line_t trace_ctxwake_print(struct trace_iterator *iter, | ||
577 | char *delim) | ||
578 | { | ||
579 | struct ctx_switch_entry *field; | ||
580 | char *comm; | ||
581 | int S, T; | ||
582 | |||
583 | trace_assign_type(field, iter->ent); | ||
584 | |||
585 | T = task_state_char(field->next_state); | ||
586 | S = task_state_char(field->prev_state); | ||
587 | comm = trace_find_cmdline(field->next_pid); | ||
588 | if (!trace_seq_printf(&iter->seq, | ||
589 | " %5d:%3d:%c %s [%03d] %5d:%3d:%c %s\n", | ||
590 | field->prev_pid, | ||
591 | field->prev_prio, | ||
592 | S, delim, | ||
593 | field->next_cpu, | ||
594 | field->next_pid, | ||
595 | field->next_prio, | ||
596 | T, comm)) | ||
597 | return TRACE_TYPE_PARTIAL_LINE; | ||
598 | |||
599 | return TRACE_TYPE_HANDLED; | ||
600 | } | ||
601 | |||
602 | static enum print_line_t trace_ctx_print(struct trace_iterator *iter, int flags) | ||
603 | { | ||
604 | return trace_ctxwake_print(iter, "==>"); | ||
605 | } | ||
606 | |||
607 | static enum print_line_t trace_wake_print(struct trace_iterator *iter, | ||
608 | int flags) | ||
609 | { | ||
610 | return trace_ctxwake_print(iter, " +"); | ||
611 | } | ||
612 | |||
613 | static int trace_ctxwake_raw(struct trace_iterator *iter, char S) | ||
614 | { | ||
615 | struct ctx_switch_entry *field; | ||
616 | int T; | ||
617 | |||
618 | trace_assign_type(field, iter->ent); | ||
619 | |||
620 | if (!S) | ||
621 | task_state_char(field->prev_state); | ||
622 | T = task_state_char(field->next_state); | ||
623 | if (!trace_seq_printf(&iter->seq, "%d %d %c %d %d %d %c\n", | ||
624 | field->prev_pid, | ||
625 | field->prev_prio, | ||
626 | S, | ||
627 | field->next_cpu, | ||
628 | field->next_pid, | ||
629 | field->next_prio, | ||
630 | T)) | ||
631 | return TRACE_TYPE_PARTIAL_LINE; | ||
632 | |||
633 | return TRACE_TYPE_HANDLED; | ||
634 | } | ||
635 | |||
636 | static enum print_line_t trace_ctx_raw(struct trace_iterator *iter, int flags) | ||
637 | { | ||
638 | return trace_ctxwake_raw(iter, 0); | ||
639 | } | ||
640 | |||
641 | static enum print_line_t trace_wake_raw(struct trace_iterator *iter, int flags) | ||
642 | { | ||
643 | return trace_ctxwake_raw(iter, '+'); | ||
644 | } | ||
645 | |||
646 | |||
647 | static int trace_ctxwake_hex(struct trace_iterator *iter, char S) | ||
648 | { | ||
649 | struct ctx_switch_entry *field; | ||
650 | struct trace_seq *s = &iter->seq; | ||
651 | int T; | ||
652 | |||
653 | trace_assign_type(field, iter->ent); | ||
654 | |||
655 | if (!S) | ||
656 | task_state_char(field->prev_state); | ||
657 | T = task_state_char(field->next_state); | ||
658 | |||
659 | SEQ_PUT_HEX_FIELD_RET(s, field->prev_pid); | ||
660 | SEQ_PUT_HEX_FIELD_RET(s, field->prev_prio); | ||
661 | SEQ_PUT_HEX_FIELD_RET(s, S); | ||
662 | SEQ_PUT_HEX_FIELD_RET(s, field->next_cpu); | ||
663 | SEQ_PUT_HEX_FIELD_RET(s, field->next_pid); | ||
664 | SEQ_PUT_HEX_FIELD_RET(s, field->next_prio); | ||
665 | SEQ_PUT_HEX_FIELD_RET(s, T); | ||
666 | |||
667 | return TRACE_TYPE_HANDLED; | ||
668 | } | ||
669 | |||
670 | static enum print_line_t trace_ctx_hex(struct trace_iterator *iter, int flags) | ||
671 | { | ||
672 | return trace_ctxwake_hex(iter, 0); | ||
673 | } | ||
674 | |||
675 | static enum print_line_t trace_wake_hex(struct trace_iterator *iter, int flags) | ||
676 | { | ||
677 | return trace_ctxwake_hex(iter, '+'); | ||
678 | } | ||
679 | |||
680 | static enum print_line_t trace_ctxwake_bin(struct trace_iterator *iter, | ||
681 | int flags) | ||
682 | { | ||
683 | struct ctx_switch_entry *field; | ||
684 | struct trace_seq *s = &iter->seq; | ||
685 | |||
686 | trace_assign_type(field, iter->ent); | ||
687 | |||
688 | SEQ_PUT_FIELD_RET(s, field->prev_pid); | ||
689 | SEQ_PUT_FIELD_RET(s, field->prev_prio); | ||
690 | SEQ_PUT_FIELD_RET(s, field->prev_state); | ||
691 | SEQ_PUT_FIELD_RET(s, field->next_pid); | ||
692 | SEQ_PUT_FIELD_RET(s, field->next_prio); | ||
693 | SEQ_PUT_FIELD_RET(s, field->next_state); | ||
694 | |||
695 | return TRACE_TYPE_HANDLED; | ||
696 | } | ||
697 | |||
698 | static struct trace_event trace_ctx_event = { | ||
699 | .type = TRACE_CTX, | ||
700 | .trace = trace_ctx_print, | ||
701 | .raw = trace_ctx_raw, | ||
702 | .hex = trace_ctx_hex, | ||
703 | .binary = trace_ctxwake_bin, | ||
704 | }; | ||
705 | |||
706 | static struct trace_event trace_wake_event = { | ||
707 | .type = TRACE_WAKE, | ||
708 | .trace = trace_wake_print, | ||
709 | .raw = trace_wake_raw, | ||
710 | .hex = trace_wake_hex, | ||
711 | .binary = trace_ctxwake_bin, | ||
712 | }; | ||
713 | |||
714 | /* TRACE_SPECIAL */ | ||
715 | static enum print_line_t trace_special_print(struct trace_iterator *iter, | ||
716 | int flags) | ||
717 | { | ||
718 | struct special_entry *field; | ||
719 | |||
720 | trace_assign_type(field, iter->ent); | ||
721 | |||
722 | if (!trace_seq_printf(&iter->seq, "# %ld %ld %ld\n", | ||
723 | field->arg1, | ||
724 | field->arg2, | ||
725 | field->arg3)) | ||
726 | return TRACE_TYPE_PARTIAL_LINE; | ||
727 | |||
728 | return TRACE_TYPE_HANDLED; | ||
729 | } | ||
730 | |||
731 | static enum print_line_t trace_special_hex(struct trace_iterator *iter, | ||
732 | int flags) | ||
733 | { | ||
734 | struct special_entry *field; | ||
735 | struct trace_seq *s = &iter->seq; | ||
736 | |||
737 | trace_assign_type(field, iter->ent); | ||
738 | |||
739 | SEQ_PUT_HEX_FIELD_RET(s, field->arg1); | ||
740 | SEQ_PUT_HEX_FIELD_RET(s, field->arg2); | ||
741 | SEQ_PUT_HEX_FIELD_RET(s, field->arg3); | ||
742 | |||
743 | return TRACE_TYPE_HANDLED; | ||
744 | } | ||
745 | |||
746 | static enum print_line_t trace_special_bin(struct trace_iterator *iter, | ||
747 | int flags) | ||
748 | { | ||
749 | struct special_entry *field; | ||
750 | struct trace_seq *s = &iter->seq; | ||
751 | |||
752 | trace_assign_type(field, iter->ent); | ||
753 | |||
754 | SEQ_PUT_FIELD_RET(s, field->arg1); | ||
755 | SEQ_PUT_FIELD_RET(s, field->arg2); | ||
756 | SEQ_PUT_FIELD_RET(s, field->arg3); | ||
757 | |||
758 | return TRACE_TYPE_HANDLED; | ||
759 | } | ||
760 | |||
761 | static struct trace_event trace_special_event = { | ||
762 | .type = TRACE_SPECIAL, | ||
763 | .trace = trace_special_print, | ||
764 | .raw = trace_special_print, | ||
765 | .hex = trace_special_hex, | ||
766 | .binary = trace_special_bin, | ||
767 | }; | ||
768 | |||
769 | /* TRACE_STACK */ | ||
770 | |||
771 | static enum print_line_t trace_stack_print(struct trace_iterator *iter, | ||
772 | int flags) | ||
773 | { | ||
774 | struct stack_entry *field; | ||
775 | struct trace_seq *s = &iter->seq; | ||
776 | int i; | ||
777 | |||
778 | trace_assign_type(field, iter->ent); | ||
779 | |||
780 | for (i = 0; i < FTRACE_STACK_ENTRIES; i++) { | ||
781 | if (i) { | ||
782 | if (!trace_seq_puts(s, " <= ")) | ||
783 | goto partial; | ||
784 | |||
785 | if (!seq_print_ip_sym(s, field->caller[i], flags)) | ||
786 | goto partial; | ||
787 | } | ||
788 | if (!trace_seq_puts(s, "\n")) | ||
789 | goto partial; | ||
790 | } | ||
791 | |||
792 | return TRACE_TYPE_HANDLED; | ||
793 | |||
794 | partial: | ||
795 | return TRACE_TYPE_PARTIAL_LINE; | ||
796 | } | ||
797 | |||
798 | static struct trace_event trace_stack_event = { | ||
799 | .type = TRACE_STACK, | ||
800 | .trace = trace_stack_print, | ||
801 | .raw = trace_special_print, | ||
802 | .hex = trace_special_hex, | ||
803 | .binary = trace_special_bin, | ||
804 | }; | ||
805 | |||
806 | /* TRACE_USER_STACK */ | ||
807 | static enum print_line_t trace_user_stack_print(struct trace_iterator *iter, | ||
808 | int flags) | ||
809 | { | ||
810 | struct userstack_entry *field; | ||
811 | struct trace_seq *s = &iter->seq; | ||
812 | |||
813 | trace_assign_type(field, iter->ent); | ||
814 | |||
815 | if (!seq_print_userip_objs(field, s, flags)) | ||
816 | goto partial; | ||
817 | |||
818 | if (!trace_seq_putc(s, '\n')) | ||
819 | goto partial; | ||
820 | |||
821 | return TRACE_TYPE_HANDLED; | ||
822 | |||
823 | partial: | ||
824 | return TRACE_TYPE_PARTIAL_LINE; | ||
825 | } | ||
826 | |||
827 | static struct trace_event trace_user_stack_event = { | ||
828 | .type = TRACE_USER_STACK, | ||
829 | .trace = trace_user_stack_print, | ||
830 | .raw = trace_special_print, | ||
831 | .hex = trace_special_hex, | ||
832 | .binary = trace_special_bin, | ||
833 | }; | ||
834 | |||
835 | /* TRACE_PRINT */ | ||
836 | static enum print_line_t | ||
837 | trace_print_print(struct trace_iterator *iter, int flags) | ||
838 | { | ||
839 | struct trace_entry *entry = iter->ent; | ||
840 | struct trace_seq *s = &iter->seq; | ||
841 | struct print_entry *field; | ||
842 | |||
843 | trace_assign_type(field, entry); | ||
844 | |||
845 | if (!seq_print_ip_sym(s, field->ip, flags)) | ||
846 | goto partial; | ||
847 | |||
848 | if (!trace_seq_puts(s, ": ")) | ||
849 | goto partial; | ||
850 | |||
851 | if (!trace_seq_bprintf(s, field->fmt, field->buf)) | ||
852 | goto partial; | ||
853 | |||
854 | return TRACE_TYPE_HANDLED; | ||
855 | |||
856 | partial: | ||
857 | return TRACE_TYPE_PARTIAL_LINE; | ||
858 | } | ||
859 | |||
860 | |||
861 | static enum print_line_t trace_print_raw(struct trace_iterator *iter, int flags) | ||
862 | { | ||
863 | struct print_entry *field; | ||
864 | struct trace_seq *s = &iter->seq; | ||
865 | |||
866 | trace_assign_type(field, iter->ent); | ||
867 | |||
868 | if (!trace_seq_printf(s, ": %lx : ", field->ip)) | ||
869 | goto partial; | ||
870 | |||
871 | if (!trace_seq_bprintf(s, field->fmt, field->buf)) | ||
872 | goto partial; | ||
873 | |||
874 | return TRACE_TYPE_HANDLED; | ||
875 | |||
876 | partial: | ||
877 | return TRACE_TYPE_PARTIAL_LINE; | ||
878 | } | ||
879 | |||
880 | |||
881 | static struct trace_event trace_print_event = { | ||
882 | .type = TRACE_PRINT, | ||
883 | .trace = trace_print_print, | ||
884 | .raw = trace_print_raw, | ||
885 | }; | ||
886 | |||
887 | static struct trace_event *events[] __initdata = { | ||
888 | &trace_fn_event, | ||
889 | &trace_ctx_event, | ||
890 | &trace_wake_event, | ||
891 | &trace_special_event, | ||
892 | &trace_stack_event, | ||
893 | &trace_user_stack_event, | ||
894 | &trace_print_event, | ||
895 | NULL | ||
896 | }; | ||
897 | |||
898 | __init static int init_events(void) | ||
899 | { | ||
900 | struct trace_event *event; | ||
901 | int i, ret; | ||
902 | |||
903 | for (i = 0; events[i]; i++) { | ||
904 | event = events[i]; | ||
905 | |||
906 | ret = register_ftrace_event(event); | ||
907 | if (!ret) { | ||
908 | printk(KERN_WARNING "event %d failed to register\n", | ||
909 | event->type); | ||
910 | WARN_ON_ONCE(1); | ||
911 | } | ||
912 | } | ||
913 | |||
914 | return 0; | ||
915 | } | ||
916 | device_initcall(init_events); | ||