Loading kernel/trace/Makefile +2 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,8 @@ obj-$(CONFIG_FUNCTION_TRACER) += libftrace.o obj-$(CONFIG_RING_BUFFER) += ring_buffer.o obj-$(CONFIG_TRACING) += trace.o obj-$(CONFIG_TRACING) += trace_output.o obj-$(CONFIG_TRACING) += trace_stat.o obj-$(CONFIG_CONTEXT_SWITCH_TRACER) += trace_sched_switch.o obj-$(CONFIG_SYSPROF_TRACER) += trace_sysprof.o obj-$(CONFIG_FUNCTION_TRACER) += trace_functions.o Loading kernel/trace/trace.c +41 −700 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ #include <linux/irqflags.h> #include "trace.h" #include "trace_output.h" #define TRACE_BUFFER_FLAGS (RB_FL_OVERWRITE) Loading Loading @@ -329,132 +330,6 @@ __update_max_tr(struct trace_array *tr, struct task_struct *tsk, int cpu) tracing_record_cmdline(current); } /** * trace_seq_printf - sequence printing of trace information * @s: trace sequence descriptor * @fmt: printf format string * * The tracer may use either sequence operations or its own * copy to user routines. To simplify formating of a trace * trace_seq_printf is used to store strings into a special * buffer (@s). Then the output may be either used by * the sequencer or pulled into another buffer. */ int trace_seq_printf(struct trace_seq *s, const char *fmt, ...) { int len = (PAGE_SIZE - 1) - s->len; va_list ap; int ret; if (!len) return 0; va_start(ap, fmt); ret = vsnprintf(s->buffer + s->len, len, fmt, ap); va_end(ap); /* If we can't write it all, don't bother writing anything */ if (ret >= len) return 0; s->len += ret; return len; } /** * trace_seq_puts - trace sequence printing of simple string * @s: trace sequence descriptor * @str: simple string to record * * The tracer may use either the sequence operations or its own * copy to user routines. This function records a simple string * into a special buffer (@s) for later retrieval by a sequencer * or other mechanism. */ static int trace_seq_puts(struct trace_seq *s, const char *str) { int len = strlen(str); if (len > ((PAGE_SIZE - 1) - s->len)) return 0; memcpy(s->buffer + s->len, str, len); s->len += len; return len; } static int trace_seq_putc(struct trace_seq *s, unsigned char c) { if (s->len >= (PAGE_SIZE - 1)) return 0; s->buffer[s->len++] = c; return 1; } static int trace_seq_putmem(struct trace_seq *s, void *mem, size_t len) { if (len > ((PAGE_SIZE - 1) - s->len)) return 0; memcpy(s->buffer + s->len, mem, len); s->len += len; return len; } #define MAX_MEMHEX_BYTES 8 #define HEX_CHARS (MAX_MEMHEX_BYTES*2 + 1) static int trace_seq_putmem_hex(struct trace_seq *s, void *mem, size_t len) { unsigned char hex[HEX_CHARS]; unsigned char *data = mem; int i, j; #ifdef __BIG_ENDIAN for (i = 0, j = 0; i < len; i++) { #else for (i = len-1, j = 0; i >= 0; i--) { #endif hex[j++] = hex_asc_hi(data[i]); hex[j++] = hex_asc_lo(data[i]); } hex[j++] = ' '; return trace_seq_putmem(s, hex, j); } static int trace_seq_path(struct trace_seq *s, struct path *path) { unsigned char *p; if (s->len >= (PAGE_SIZE - 1)) return 0; p = d_path(path, s->buffer + s->len, PAGE_SIZE - s->len); if (!IS_ERR(p)) { p = mangle_path(s->buffer + s->len, p, "\n"); if (p) { s->len = p - s->buffer; return 1; } } else { s->buffer[s->len++] = '?'; return 1; } return 0; } static void trace_seq_reset(struct trace_seq *s) { Loading Loading @@ -1472,154 +1347,6 @@ static void s_stop(struct seq_file *m, void *p) mutex_unlock(&trace_types_lock); } #ifdef CONFIG_KRETPROBES static inline const char *kretprobed(const char *name) { static const char tramp_name[] = "kretprobe_trampoline"; int size = sizeof(tramp_name); if (strncmp(tramp_name, name, size) == 0) return "[unknown/kretprobe'd]"; return name; } #else static inline const char *kretprobed(const char *name) { return name; } #endif /* CONFIG_KRETPROBES */ static int seq_print_sym_short(struct trace_seq *s, const char *fmt, unsigned long address) { #ifdef CONFIG_KALLSYMS char str[KSYM_SYMBOL_LEN]; const char *name; kallsyms_lookup(address, NULL, NULL, NULL, str); name = kretprobed(str); return trace_seq_printf(s, fmt, name); #endif return 1; } static int seq_print_sym_offset(struct trace_seq *s, const char *fmt, unsigned long address) { #ifdef CONFIG_KALLSYMS char str[KSYM_SYMBOL_LEN]; const char *name; sprint_symbol(str, address); name = kretprobed(str); return trace_seq_printf(s, fmt, name); #endif return 1; } #ifndef CONFIG_64BIT # define IP_FMT "%08lx" #else # define IP_FMT "%016lx" #endif int seq_print_ip_sym(struct trace_seq *s, unsigned long ip, unsigned long sym_flags) { int ret; if (!ip) return trace_seq_printf(s, "0"); if (sym_flags & TRACE_ITER_SYM_OFFSET) ret = seq_print_sym_offset(s, "%s", ip); else ret = seq_print_sym_short(s, "%s", ip); if (!ret) return 0; if (sym_flags & TRACE_ITER_SYM_ADDR) ret = trace_seq_printf(s, " <" IP_FMT ">", ip); return ret; } static inline int seq_print_user_ip(struct trace_seq *s, struct mm_struct *mm, unsigned long ip, unsigned long sym_flags) { struct file *file = NULL; unsigned long vmstart = 0; int ret = 1; if (mm) { const struct vm_area_struct *vma; down_read(&mm->mmap_sem); vma = find_vma(mm, ip); if (vma) { file = vma->vm_file; vmstart = vma->vm_start; } if (file) { ret = trace_seq_path(s, &file->f_path); if (ret) ret = trace_seq_printf(s, "[+0x%lx]", ip - vmstart); } up_read(&mm->mmap_sem); } if (ret && ((sym_flags & TRACE_ITER_SYM_ADDR) || !file)) ret = trace_seq_printf(s, " <" IP_FMT ">", ip); return ret; } static int seq_print_userip_objs(const struct userstack_entry *entry, struct trace_seq *s, unsigned long sym_flags) { struct mm_struct *mm = NULL; int ret = 1; unsigned int i; if (trace_flags & TRACE_ITER_SYM_USEROBJ) { struct task_struct *task; /* * we do the lookup on the thread group leader, * since individual threads might have already quit! */ rcu_read_lock(); task = find_task_by_vpid(entry->ent.tgid); if (task) mm = get_task_mm(task); rcu_read_unlock(); } for (i = 0; i < FTRACE_STACK_ENTRIES; i++) { unsigned long ip = entry->caller[i]; if (ip == ULONG_MAX || !ret) break; if (i && ret) ret = trace_seq_puts(s, " <- "); if (!ip) { if (ret) ret = trace_seq_puts(s, "??"); continue; } if (!ret) break; if (ret) ret = seq_print_user_ip(s, mm, ip, sym_flags); } if (mm) mmput(mm); return ret; } static void print_lat_help_header(struct seq_file *m) { seq_puts(m, "# _------=> CPU# \n"); Loading Loading @@ -1755,52 +1482,6 @@ lat_print_timestamp(struct trace_seq *s, u64 abs_usecs, trace_seq_puts(s, " : "); } static const char state_to_char[] = TASK_STATE_TO_CHAR_STR; static int task_state_char(unsigned long state) { int bit = state ? __ffs(state) + 1 : 0; return bit < sizeof(state_to_char) - 1 ? state_to_char[bit] : '?'; } /* * The message is supposed to contain an ending newline. * If the printing stops prematurely, try to add a newline of our own. */ void trace_seq_print_cont(struct trace_seq *s, struct trace_iterator *iter) { struct trace_entry *ent; struct trace_field_cont *cont; bool ok = true; ent = peek_next_entry(iter, iter->cpu, NULL); if (!ent || ent->type != TRACE_CONT) { trace_seq_putc(s, '\n'); return; } do { cont = (struct trace_field_cont *)ent; if (ok) ok = (trace_seq_printf(s, "%s", cont->buf) > 0); ftrace_disable_cpu(); if (iter->buffer_iter[iter->cpu]) ring_buffer_read(iter->buffer_iter[iter->cpu], NULL); else ring_buffer_consume(iter->tr->buffer, iter->cpu, NULL); ftrace_enable_cpu(); ent = peek_next_entry(iter, iter->cpu, NULL); } while (ent && ent->type == TRACE_CONT); if (!ok) trace_seq_putc(s, '\n'); } static void test_cpu_buff_start(struct trace_iterator *iter) { struct trace_seq *s = &iter->seq; Loading @@ -1824,17 +1505,14 @@ print_lat_fmt(struct trace_iterator *iter, unsigned int trace_idx, int cpu) struct trace_seq *s = &iter->seq; unsigned long sym_flags = (trace_flags & TRACE_ITER_SYM_MASK); struct trace_entry *next_entry; struct trace_event *event; unsigned long verbose = (trace_flags & TRACE_ITER_VERBOSE); struct trace_entry *entry = iter->ent; unsigned long abs_usecs; unsigned long rel_usecs; u64 next_ts; char *comm; int S, T; int i; if (entry->type == TRACE_CONT) return TRACE_TYPE_HANDLED; int ret; test_cpu_buff_start(iter); Loading @@ -1859,96 +1537,16 @@ print_lat_fmt(struct trace_iterator *iter, unsigned int trace_idx, int cpu) lat_print_generic(s, entry, cpu); lat_print_timestamp(s, abs_usecs, rel_usecs); } switch (entry->type) { case TRACE_FN: { struct ftrace_entry *field; trace_assign_type(field, entry); seq_print_ip_sym(s, field->ip, sym_flags); trace_seq_puts(s, " ("); seq_print_ip_sym(s, field->parent_ip, sym_flags); trace_seq_puts(s, ")\n"); break; } case TRACE_CTX: case TRACE_WAKE: { struct ctx_switch_entry *field; trace_assign_type(field, entry); T = task_state_char(field->next_state); S = task_state_char(field->prev_state); comm = trace_find_cmdline(field->next_pid); trace_seq_printf(s, " %5d:%3d:%c %s [%03d] %5d:%3d:%c %s\n", field->prev_pid, field->prev_prio, S, entry->type == TRACE_CTX ? "==>" : " +", field->next_cpu, field->next_pid, field->next_prio, T, comm); break; } case TRACE_SPECIAL: { struct special_entry *field; trace_assign_type(field, entry); trace_seq_printf(s, "# %ld %ld %ld\n", field->arg1, field->arg2, field->arg3); break; } case TRACE_STACK: { struct stack_entry *field; trace_assign_type(field, entry); for (i = 0; i < FTRACE_STACK_ENTRIES; i++) { if (i) trace_seq_puts(s, " <= "); seq_print_ip_sym(s, field->caller[i], sym_flags); } trace_seq_puts(s, "\n"); break; } case TRACE_PRINT: { struct print_entry *field; trace_assign_type(field, entry); seq_print_ip_sym(s, field->ip, sym_flags); trace_seq_printf(s, ": %s", field->buf); if (entry->flags & TRACE_FLAG_CONT) trace_seq_print_cont(s, iter); break; } case TRACE_BRANCH: { struct trace_branch *field; trace_assign_type(field, entry); trace_seq_printf(s, "[%s] %s:%s:%d\n", field->correct ? " ok " : " MISS ", field->func, field->file, field->line); break; event = ftrace_find_event(entry->type); if (event && event->latency_trace) { ret = event->latency_trace(s, entry, sym_flags); if (ret) return ret; return TRACE_TYPE_HANDLED; } case TRACE_USER_STACK: { struct userstack_entry *field; trace_assign_type(field, entry); seq_print_userip_objs(field, s, sym_flags); trace_seq_putc(s, '\n'); break; } default: trace_seq_printf(s, "Unknown type %d\n", entry->type); } return TRACE_TYPE_HANDLED; } Loading @@ -1957,19 +1555,15 @@ static enum print_line_t print_trace_fmt(struct trace_iterator *iter) struct trace_seq *s = &iter->seq; unsigned long sym_flags = (trace_flags & TRACE_ITER_SYM_MASK); struct trace_entry *entry; struct trace_event *event; unsigned long usec_rem; unsigned long long t; unsigned long secs; char *comm; int ret; int S, T; int i; entry = iter->ent; if (entry->type == TRACE_CONT) return TRACE_TYPE_HANDLED; test_cpu_buff_start(iter); comm = trace_find_cmdline(iter->ent->pid); Loading @@ -1988,129 +1582,17 @@ static enum print_line_t print_trace_fmt(struct trace_iterator *iter) if (!ret) return TRACE_TYPE_PARTIAL_LINE; switch (entry->type) { case TRACE_FN: { struct ftrace_entry *field; trace_assign_type(field, entry); ret = seq_print_ip_sym(s, field->ip, sym_flags); if (!ret) return TRACE_TYPE_PARTIAL_LINE; if ((sym_flags & TRACE_ITER_PRINT_PARENT) && field->parent_ip) { ret = trace_seq_printf(s, " <-"); if (!ret) return TRACE_TYPE_PARTIAL_LINE; ret = seq_print_ip_sym(s, field->parent_ip, sym_flags); if (!ret) return TRACE_TYPE_PARTIAL_LINE; } ret = trace_seq_printf(s, "\n"); if (!ret) return TRACE_TYPE_PARTIAL_LINE; break; } case TRACE_CTX: case TRACE_WAKE: { struct ctx_switch_entry *field; trace_assign_type(field, entry); T = task_state_char(field->next_state); S = task_state_char(field->prev_state); ret = trace_seq_printf(s, " %5d:%3d:%c %s [%03d] %5d:%3d:%c\n", field->prev_pid, field->prev_prio, S, entry->type == TRACE_CTX ? "==>" : " +", field->next_cpu, field->next_pid, field->next_prio, T); if (!ret) return TRACE_TYPE_PARTIAL_LINE; break; } case TRACE_SPECIAL: { struct special_entry *field; trace_assign_type(field, entry); ret = trace_seq_printf(s, "# %ld %ld %ld\n", field->arg1, field->arg2, field->arg3); if (!ret) return TRACE_TYPE_PARTIAL_LINE; break; } case TRACE_STACK: { struct stack_entry *field; trace_assign_type(field, entry); for (i = 0; i < FTRACE_STACK_ENTRIES; i++) { if (i) { ret = trace_seq_puts(s, " <= "); if (!ret) return TRACE_TYPE_PARTIAL_LINE; } ret = seq_print_ip_sym(s, field->caller[i], sym_flags); if (!ret) return TRACE_TYPE_PARTIAL_LINE; event = ftrace_find_event(entry->type); if (event && event->trace) { ret = event->trace(s, entry, sym_flags); if (ret) return ret; return TRACE_TYPE_HANDLED; } ret = trace_seq_puts(s, "\n"); ret = trace_seq_printf(s, "Unknown type %d\n", entry->type); if (!ret) return TRACE_TYPE_PARTIAL_LINE; break; } case TRACE_PRINT: { struct print_entry *field; trace_assign_type(field, entry); seq_print_ip_sym(s, field->ip, sym_flags); trace_seq_printf(s, ": %s", field->buf); if (entry->flags & TRACE_FLAG_CONT) trace_seq_print_cont(s, iter); break; } case TRACE_GRAPH_RET: { return print_graph_function(iter); } case TRACE_GRAPH_ENT: { return print_graph_function(iter); } case TRACE_BRANCH: { struct trace_branch *field; trace_assign_type(field, entry); trace_seq_printf(s, "[%s] %s:%s:%d\n", field->correct ? " ok " : " MISS ", field->func, field->file, field->line); break; } case TRACE_USER_STACK: { struct userstack_entry *field; trace_assign_type(field, entry); ret = seq_print_userip_objs(field, s, sym_flags); if (!ret) return TRACE_TYPE_PARTIAL_LINE; ret = trace_seq_putc(s, '\n'); if (!ret) return TRACE_TYPE_PARTIAL_LINE; break; } } return TRACE_TYPE_HANDLED; } Loading @@ -2118,152 +1600,47 @@ static enum print_line_t print_raw_fmt(struct trace_iterator *iter) { struct trace_seq *s = &iter->seq; struct trace_entry *entry; struct trace_event *event; int ret; int S, T; entry = iter->ent; if (entry->type == TRACE_CONT) return TRACE_TYPE_HANDLED; ret = trace_seq_printf(s, "%d %d %llu ", entry->pid, iter->cpu, iter->ts); if (!ret) return TRACE_TYPE_PARTIAL_LINE; switch (entry->type) { case TRACE_FN: { struct ftrace_entry *field; trace_assign_type(field, entry); ret = trace_seq_printf(s, "%x %x\n", field->ip, field->parent_ip); if (!ret) return TRACE_TYPE_PARTIAL_LINE; break; } case TRACE_CTX: case TRACE_WAKE: { struct ctx_switch_entry *field; trace_assign_type(field, entry); T = task_state_char(field->next_state); S = entry->type == TRACE_WAKE ? '+' : task_state_char(field->prev_state); ret = trace_seq_printf(s, "%d %d %c %d %d %d %c\n", field->prev_pid, field->prev_prio, S, field->next_cpu, field->next_pid, field->next_prio, T); if (!ret) return TRACE_TYPE_PARTIAL_LINE; break; event = ftrace_find_event(entry->type); if (event && event->raw) { ret = event->raw(s, entry, 0); if (ret) return ret; return TRACE_TYPE_HANDLED; } case TRACE_SPECIAL: case TRACE_USER_STACK: case TRACE_STACK: { struct special_entry *field; trace_assign_type(field, entry); ret = trace_seq_printf(s, "# %ld %ld %ld\n", field->arg1, field->arg2, field->arg3); ret = trace_seq_printf(s, "%d ?\n", entry->type); if (!ret) return TRACE_TYPE_PARTIAL_LINE; break; } case TRACE_PRINT: { struct print_entry *field; trace_assign_type(field, entry); trace_seq_printf(s, "# %lx %s", field->ip, field->buf); if (entry->flags & TRACE_FLAG_CONT) trace_seq_print_cont(s, iter); break; } } return TRACE_TYPE_HANDLED; } #define SEQ_PUT_FIELD_RET(s, x) \ do { \ if (!trace_seq_putmem(s, &(x), sizeof(x))) \ return 0; \ } while (0) #define SEQ_PUT_HEX_FIELD_RET(s, x) \ do { \ BUILD_BUG_ON(sizeof(x) > MAX_MEMHEX_BYTES); \ if (!trace_seq_putmem_hex(s, &(x), sizeof(x))) \ return 0; \ } while (0) static enum print_line_t print_hex_fmt(struct trace_iterator *iter) { struct trace_seq *s = &iter->seq; unsigned char newline = '\n'; struct trace_entry *entry; int S, T; struct trace_event *event; entry = iter->ent; if (entry->type == TRACE_CONT) return TRACE_TYPE_HANDLED; SEQ_PUT_HEX_FIELD_RET(s, entry->pid); SEQ_PUT_HEX_FIELD_RET(s, iter->cpu); SEQ_PUT_HEX_FIELD_RET(s, iter->ts); switch (entry->type) { case TRACE_FN: { struct ftrace_entry *field; event = ftrace_find_event(entry->type); if (event && event->hex) event->hex(s, entry, 0); trace_assign_type(field, entry); SEQ_PUT_HEX_FIELD_RET(s, field->ip); SEQ_PUT_HEX_FIELD_RET(s, field->parent_ip); break; } case TRACE_CTX: case TRACE_WAKE: { struct ctx_switch_entry *field; trace_assign_type(field, entry); T = task_state_char(field->next_state); S = entry->type == TRACE_WAKE ? '+' : task_state_char(field->prev_state); SEQ_PUT_HEX_FIELD_RET(s, field->prev_pid); SEQ_PUT_HEX_FIELD_RET(s, field->prev_prio); SEQ_PUT_HEX_FIELD_RET(s, S); SEQ_PUT_HEX_FIELD_RET(s, field->next_cpu); SEQ_PUT_HEX_FIELD_RET(s, field->next_pid); SEQ_PUT_HEX_FIELD_RET(s, field->next_prio); SEQ_PUT_HEX_FIELD_RET(s, T); break; } case TRACE_SPECIAL: case TRACE_USER_STACK: case TRACE_STACK: { struct special_entry *field; trace_assign_type(field, entry); SEQ_PUT_HEX_FIELD_RET(s, field->arg1); SEQ_PUT_HEX_FIELD_RET(s, field->arg2); SEQ_PUT_HEX_FIELD_RET(s, field->arg3); break; } } SEQ_PUT_FIELD_RET(s, newline); return TRACE_TYPE_HANDLED; Loading @@ -2282,9 +1659,6 @@ static enum print_line_t print_printk_msg_only(struct trace_iterator *iter) if (!ret) return TRACE_TYPE_PARTIAL_LINE; if (entry->flags & TRACE_FLAG_CONT) trace_seq_print_cont(s, iter); return TRACE_TYPE_HANDLED; } Loading @@ -2292,53 +1666,19 @@ static enum print_line_t print_bin_fmt(struct trace_iterator *iter) { struct trace_seq *s = &iter->seq; struct trace_entry *entry; struct trace_event *event; entry = iter->ent; if (entry->type == TRACE_CONT) return TRACE_TYPE_HANDLED; SEQ_PUT_FIELD_RET(s, entry->pid); SEQ_PUT_FIELD_RET(s, entry->cpu); SEQ_PUT_FIELD_RET(s, iter->ts); switch (entry->type) { case TRACE_FN: { struct ftrace_entry *field; trace_assign_type(field, entry); SEQ_PUT_FIELD_RET(s, field->ip); SEQ_PUT_FIELD_RET(s, field->parent_ip); break; } case TRACE_CTX: { struct ctx_switch_entry *field; trace_assign_type(field, entry); SEQ_PUT_FIELD_RET(s, field->prev_pid); SEQ_PUT_FIELD_RET(s, field->prev_prio); SEQ_PUT_FIELD_RET(s, field->prev_state); SEQ_PUT_FIELD_RET(s, field->next_pid); SEQ_PUT_FIELD_RET(s, field->next_prio); SEQ_PUT_FIELD_RET(s, field->next_state); break; } case TRACE_SPECIAL: case TRACE_USER_STACK: case TRACE_STACK: { struct special_entry *field; event = ftrace_find_event(entry->type); if (event && event->binary) event->binary(s, entry, 0); trace_assign_type(field, entry); SEQ_PUT_FIELD_RET(s, field->arg1); SEQ_PUT_FIELD_RET(s, field->arg2); SEQ_PUT_FIELD_RET(s, field->arg3); break; } } return 1; return TRACE_TYPE_HANDLED; } static int trace_empty(struct trace_iterator *iter) Loading Loading @@ -3013,6 +2353,7 @@ static int tracing_set_tracer(char *buf) if (ret) goto out; } init_tracer_stat(t); trace_branch_enable(tr); out: Loading Loading @@ -3877,7 +3218,7 @@ __init static int tracer_alloc_buffers(void) #else current_trace = &nop_trace; #endif init_tracer_stat(current_trace); /* All seems OK, enable tracing */ tracing_disabled = 0; Loading kernel/trace/trace.h +20 −16 Original line number Diff line number Diff line Loading @@ -17,7 +17,6 @@ enum trace_type { TRACE_FN, TRACE_CTX, TRACE_WAKE, TRACE_CONT, TRACE_STACK, TRACE_PRINT, TRACE_SPECIAL, Loading @@ -34,7 +33,7 @@ enum trace_type { TRACE_KMEM_FREE, TRACE_POWER, __TRACE_LAST_TYPE __TRACE_LAST_TYPE, }; /* Loading Loading @@ -199,7 +198,6 @@ struct kmemtrace_free_entry { * NEED_RESCED - reschedule is requested * HARDIRQ - inside an interrupt handler * SOFTIRQ - inside a softirq handler * CONT - multiple entries hold the trace item */ enum trace_flag_type { TRACE_FLAG_IRQS_OFF = 0x01, Loading @@ -207,7 +205,6 @@ enum trace_flag_type { TRACE_FLAG_NEED_RESCHED = 0x04, TRACE_FLAG_HARDIRQ = 0x08, TRACE_FLAG_SOFTIRQ = 0x10, TRACE_FLAG_CONT = 0x20, }; #define TRACE_BUF_SIZE 1024 Loading Loading @@ -283,7 +280,6 @@ extern void __ftrace_bad_type(void); do { \ IF_ASSIGN(var, ent, struct ftrace_entry, TRACE_FN); \ IF_ASSIGN(var, ent, struct ctx_switch_entry, 0); \ IF_ASSIGN(var, ent, struct trace_field_cont, TRACE_CONT); \ IF_ASSIGN(var, ent, struct stack_entry, TRACE_STACK); \ IF_ASSIGN(var, ent, struct userstack_entry, TRACE_USER_STACK);\ IF_ASSIGN(var, ent, struct print_entry, TRACE_PRINT); \ Loading Loading @@ -365,6 +361,21 @@ struct tracer { struct tracer *next; int print_max; struct tracer_flags *flags; /* * If you change one of the following on tracing runtime, recall * init_tracer_stat() */ /* Iteration over statistic entries */ void *(*stat_start)(void); void *(*stat_next)(void *prev, int idx); /* Compare two entries for sorting (optional) for stats */ int (*stat_cmp)(void *p1, void *p2); /* Print a stat entry */ int (*stat_show)(struct seq_file *s, void *p); /* Print the headers of your stat entries */ int (*stat_headers)(struct seq_file *s); }; struct trace_seq { Loading Loading @@ -450,6 +461,8 @@ void tracing_start_sched_switch_record(void); int register_tracer(struct tracer *type); void unregister_tracer(struct tracer *type); void init_tracer_stat(struct tracer *trace); extern unsigned long nsecs_to_usecs(unsigned long nsecs); extern unsigned long tracing_max_latency; Loading Loading @@ -481,10 +494,10 @@ struct tracer_switch_ops { void *private; struct tracer_switch_ops *next; }; char *trace_find_cmdline(int pid); #endif /* CONFIG_CONTEXT_SWITCH_TRACER */ extern char *trace_find_cmdline(int pid); #ifdef CONFIG_DYNAMIC_FTRACE extern unsigned long ftrace_update_tot_cnt; #define DYN_FTRACE_TEST_NAME trace_selftest_dynamic_test_func Loading Loading @@ -513,15 +526,6 @@ extern int trace_selftest_startup_branch(struct tracer *trace, #endif /* CONFIG_FTRACE_STARTUP_TEST */ extern void *head_page(struct trace_array_cpu *data); extern int trace_seq_printf(struct trace_seq *s, const char *fmt, ...); extern void trace_seq_print_cont(struct trace_seq *s, struct trace_iterator *iter); extern int seq_print_ip_sym(struct trace_seq *s, unsigned long ip, unsigned long sym_flags); extern ssize_t trace_seq_to_user(struct trace_seq *s, char __user *ubuf, size_t cnt); extern long ns2usecs(cycle_t nsec); extern int trace_vprintk(unsigned long ip, int depth, const char *fmt, va_list args); Loading kernel/trace/trace_boot.c +1 −0 Original line number Diff line number Diff line Loading @@ -11,6 +11,7 @@ #include <linux/kallsyms.h> #include "trace.h" #include "trace_output.h" static struct trace_array *boot_trace; static bool pre_initcalls_finished; Loading Loading
kernel/trace/Makefile +2 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,8 @@ obj-$(CONFIG_FUNCTION_TRACER) += libftrace.o obj-$(CONFIG_RING_BUFFER) += ring_buffer.o obj-$(CONFIG_TRACING) += trace.o obj-$(CONFIG_TRACING) += trace_output.o obj-$(CONFIG_TRACING) += trace_stat.o obj-$(CONFIG_CONTEXT_SWITCH_TRACER) += trace_sched_switch.o obj-$(CONFIG_SYSPROF_TRACER) += trace_sysprof.o obj-$(CONFIG_FUNCTION_TRACER) += trace_functions.o Loading
kernel/trace/trace.c +41 −700 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ #include <linux/irqflags.h> #include "trace.h" #include "trace_output.h" #define TRACE_BUFFER_FLAGS (RB_FL_OVERWRITE) Loading Loading @@ -329,132 +330,6 @@ __update_max_tr(struct trace_array *tr, struct task_struct *tsk, int cpu) tracing_record_cmdline(current); } /** * trace_seq_printf - sequence printing of trace information * @s: trace sequence descriptor * @fmt: printf format string * * The tracer may use either sequence operations or its own * copy to user routines. To simplify formating of a trace * trace_seq_printf is used to store strings into a special * buffer (@s). Then the output may be either used by * the sequencer or pulled into another buffer. */ int trace_seq_printf(struct trace_seq *s, const char *fmt, ...) { int len = (PAGE_SIZE - 1) - s->len; va_list ap; int ret; if (!len) return 0; va_start(ap, fmt); ret = vsnprintf(s->buffer + s->len, len, fmt, ap); va_end(ap); /* If we can't write it all, don't bother writing anything */ if (ret >= len) return 0; s->len += ret; return len; } /** * trace_seq_puts - trace sequence printing of simple string * @s: trace sequence descriptor * @str: simple string to record * * The tracer may use either the sequence operations or its own * copy to user routines. This function records a simple string * into a special buffer (@s) for later retrieval by a sequencer * or other mechanism. */ static int trace_seq_puts(struct trace_seq *s, const char *str) { int len = strlen(str); if (len > ((PAGE_SIZE - 1) - s->len)) return 0; memcpy(s->buffer + s->len, str, len); s->len += len; return len; } static int trace_seq_putc(struct trace_seq *s, unsigned char c) { if (s->len >= (PAGE_SIZE - 1)) return 0; s->buffer[s->len++] = c; return 1; } static int trace_seq_putmem(struct trace_seq *s, void *mem, size_t len) { if (len > ((PAGE_SIZE - 1) - s->len)) return 0; memcpy(s->buffer + s->len, mem, len); s->len += len; return len; } #define MAX_MEMHEX_BYTES 8 #define HEX_CHARS (MAX_MEMHEX_BYTES*2 + 1) static int trace_seq_putmem_hex(struct trace_seq *s, void *mem, size_t len) { unsigned char hex[HEX_CHARS]; unsigned char *data = mem; int i, j; #ifdef __BIG_ENDIAN for (i = 0, j = 0; i < len; i++) { #else for (i = len-1, j = 0; i >= 0; i--) { #endif hex[j++] = hex_asc_hi(data[i]); hex[j++] = hex_asc_lo(data[i]); } hex[j++] = ' '; return trace_seq_putmem(s, hex, j); } static int trace_seq_path(struct trace_seq *s, struct path *path) { unsigned char *p; if (s->len >= (PAGE_SIZE - 1)) return 0; p = d_path(path, s->buffer + s->len, PAGE_SIZE - s->len); if (!IS_ERR(p)) { p = mangle_path(s->buffer + s->len, p, "\n"); if (p) { s->len = p - s->buffer; return 1; } } else { s->buffer[s->len++] = '?'; return 1; } return 0; } static void trace_seq_reset(struct trace_seq *s) { Loading Loading @@ -1472,154 +1347,6 @@ static void s_stop(struct seq_file *m, void *p) mutex_unlock(&trace_types_lock); } #ifdef CONFIG_KRETPROBES static inline const char *kretprobed(const char *name) { static const char tramp_name[] = "kretprobe_trampoline"; int size = sizeof(tramp_name); if (strncmp(tramp_name, name, size) == 0) return "[unknown/kretprobe'd]"; return name; } #else static inline const char *kretprobed(const char *name) { return name; } #endif /* CONFIG_KRETPROBES */ static int seq_print_sym_short(struct trace_seq *s, const char *fmt, unsigned long address) { #ifdef CONFIG_KALLSYMS char str[KSYM_SYMBOL_LEN]; const char *name; kallsyms_lookup(address, NULL, NULL, NULL, str); name = kretprobed(str); return trace_seq_printf(s, fmt, name); #endif return 1; } static int seq_print_sym_offset(struct trace_seq *s, const char *fmt, unsigned long address) { #ifdef CONFIG_KALLSYMS char str[KSYM_SYMBOL_LEN]; const char *name; sprint_symbol(str, address); name = kretprobed(str); return trace_seq_printf(s, fmt, name); #endif return 1; } #ifndef CONFIG_64BIT # define IP_FMT "%08lx" #else # define IP_FMT "%016lx" #endif int seq_print_ip_sym(struct trace_seq *s, unsigned long ip, unsigned long sym_flags) { int ret; if (!ip) return trace_seq_printf(s, "0"); if (sym_flags & TRACE_ITER_SYM_OFFSET) ret = seq_print_sym_offset(s, "%s", ip); else ret = seq_print_sym_short(s, "%s", ip); if (!ret) return 0; if (sym_flags & TRACE_ITER_SYM_ADDR) ret = trace_seq_printf(s, " <" IP_FMT ">", ip); return ret; } static inline int seq_print_user_ip(struct trace_seq *s, struct mm_struct *mm, unsigned long ip, unsigned long sym_flags) { struct file *file = NULL; unsigned long vmstart = 0; int ret = 1; if (mm) { const struct vm_area_struct *vma; down_read(&mm->mmap_sem); vma = find_vma(mm, ip); if (vma) { file = vma->vm_file; vmstart = vma->vm_start; } if (file) { ret = trace_seq_path(s, &file->f_path); if (ret) ret = trace_seq_printf(s, "[+0x%lx]", ip - vmstart); } up_read(&mm->mmap_sem); } if (ret && ((sym_flags & TRACE_ITER_SYM_ADDR) || !file)) ret = trace_seq_printf(s, " <" IP_FMT ">", ip); return ret; } static int seq_print_userip_objs(const struct userstack_entry *entry, struct trace_seq *s, unsigned long sym_flags) { struct mm_struct *mm = NULL; int ret = 1; unsigned int i; if (trace_flags & TRACE_ITER_SYM_USEROBJ) { struct task_struct *task; /* * we do the lookup on the thread group leader, * since individual threads might have already quit! */ rcu_read_lock(); task = find_task_by_vpid(entry->ent.tgid); if (task) mm = get_task_mm(task); rcu_read_unlock(); } for (i = 0; i < FTRACE_STACK_ENTRIES; i++) { unsigned long ip = entry->caller[i]; if (ip == ULONG_MAX || !ret) break; if (i && ret) ret = trace_seq_puts(s, " <- "); if (!ip) { if (ret) ret = trace_seq_puts(s, "??"); continue; } if (!ret) break; if (ret) ret = seq_print_user_ip(s, mm, ip, sym_flags); } if (mm) mmput(mm); return ret; } static void print_lat_help_header(struct seq_file *m) { seq_puts(m, "# _------=> CPU# \n"); Loading Loading @@ -1755,52 +1482,6 @@ lat_print_timestamp(struct trace_seq *s, u64 abs_usecs, trace_seq_puts(s, " : "); } static const char state_to_char[] = TASK_STATE_TO_CHAR_STR; static int task_state_char(unsigned long state) { int bit = state ? __ffs(state) + 1 : 0; return bit < sizeof(state_to_char) - 1 ? state_to_char[bit] : '?'; } /* * The message is supposed to contain an ending newline. * If the printing stops prematurely, try to add a newline of our own. */ void trace_seq_print_cont(struct trace_seq *s, struct trace_iterator *iter) { struct trace_entry *ent; struct trace_field_cont *cont; bool ok = true; ent = peek_next_entry(iter, iter->cpu, NULL); if (!ent || ent->type != TRACE_CONT) { trace_seq_putc(s, '\n'); return; } do { cont = (struct trace_field_cont *)ent; if (ok) ok = (trace_seq_printf(s, "%s", cont->buf) > 0); ftrace_disable_cpu(); if (iter->buffer_iter[iter->cpu]) ring_buffer_read(iter->buffer_iter[iter->cpu], NULL); else ring_buffer_consume(iter->tr->buffer, iter->cpu, NULL); ftrace_enable_cpu(); ent = peek_next_entry(iter, iter->cpu, NULL); } while (ent && ent->type == TRACE_CONT); if (!ok) trace_seq_putc(s, '\n'); } static void test_cpu_buff_start(struct trace_iterator *iter) { struct trace_seq *s = &iter->seq; Loading @@ -1824,17 +1505,14 @@ print_lat_fmt(struct trace_iterator *iter, unsigned int trace_idx, int cpu) struct trace_seq *s = &iter->seq; unsigned long sym_flags = (trace_flags & TRACE_ITER_SYM_MASK); struct trace_entry *next_entry; struct trace_event *event; unsigned long verbose = (trace_flags & TRACE_ITER_VERBOSE); struct trace_entry *entry = iter->ent; unsigned long abs_usecs; unsigned long rel_usecs; u64 next_ts; char *comm; int S, T; int i; if (entry->type == TRACE_CONT) return TRACE_TYPE_HANDLED; int ret; test_cpu_buff_start(iter); Loading @@ -1859,96 +1537,16 @@ print_lat_fmt(struct trace_iterator *iter, unsigned int trace_idx, int cpu) lat_print_generic(s, entry, cpu); lat_print_timestamp(s, abs_usecs, rel_usecs); } switch (entry->type) { case TRACE_FN: { struct ftrace_entry *field; trace_assign_type(field, entry); seq_print_ip_sym(s, field->ip, sym_flags); trace_seq_puts(s, " ("); seq_print_ip_sym(s, field->parent_ip, sym_flags); trace_seq_puts(s, ")\n"); break; } case TRACE_CTX: case TRACE_WAKE: { struct ctx_switch_entry *field; trace_assign_type(field, entry); T = task_state_char(field->next_state); S = task_state_char(field->prev_state); comm = trace_find_cmdline(field->next_pid); trace_seq_printf(s, " %5d:%3d:%c %s [%03d] %5d:%3d:%c %s\n", field->prev_pid, field->prev_prio, S, entry->type == TRACE_CTX ? "==>" : " +", field->next_cpu, field->next_pid, field->next_prio, T, comm); break; } case TRACE_SPECIAL: { struct special_entry *field; trace_assign_type(field, entry); trace_seq_printf(s, "# %ld %ld %ld\n", field->arg1, field->arg2, field->arg3); break; } case TRACE_STACK: { struct stack_entry *field; trace_assign_type(field, entry); for (i = 0; i < FTRACE_STACK_ENTRIES; i++) { if (i) trace_seq_puts(s, " <= "); seq_print_ip_sym(s, field->caller[i], sym_flags); } trace_seq_puts(s, "\n"); break; } case TRACE_PRINT: { struct print_entry *field; trace_assign_type(field, entry); seq_print_ip_sym(s, field->ip, sym_flags); trace_seq_printf(s, ": %s", field->buf); if (entry->flags & TRACE_FLAG_CONT) trace_seq_print_cont(s, iter); break; } case TRACE_BRANCH: { struct trace_branch *field; trace_assign_type(field, entry); trace_seq_printf(s, "[%s] %s:%s:%d\n", field->correct ? " ok " : " MISS ", field->func, field->file, field->line); break; event = ftrace_find_event(entry->type); if (event && event->latency_trace) { ret = event->latency_trace(s, entry, sym_flags); if (ret) return ret; return TRACE_TYPE_HANDLED; } case TRACE_USER_STACK: { struct userstack_entry *field; trace_assign_type(field, entry); seq_print_userip_objs(field, s, sym_flags); trace_seq_putc(s, '\n'); break; } default: trace_seq_printf(s, "Unknown type %d\n", entry->type); } return TRACE_TYPE_HANDLED; } Loading @@ -1957,19 +1555,15 @@ static enum print_line_t print_trace_fmt(struct trace_iterator *iter) struct trace_seq *s = &iter->seq; unsigned long sym_flags = (trace_flags & TRACE_ITER_SYM_MASK); struct trace_entry *entry; struct trace_event *event; unsigned long usec_rem; unsigned long long t; unsigned long secs; char *comm; int ret; int S, T; int i; entry = iter->ent; if (entry->type == TRACE_CONT) return TRACE_TYPE_HANDLED; test_cpu_buff_start(iter); comm = trace_find_cmdline(iter->ent->pid); Loading @@ -1988,129 +1582,17 @@ static enum print_line_t print_trace_fmt(struct trace_iterator *iter) if (!ret) return TRACE_TYPE_PARTIAL_LINE; switch (entry->type) { case TRACE_FN: { struct ftrace_entry *field; trace_assign_type(field, entry); ret = seq_print_ip_sym(s, field->ip, sym_flags); if (!ret) return TRACE_TYPE_PARTIAL_LINE; if ((sym_flags & TRACE_ITER_PRINT_PARENT) && field->parent_ip) { ret = trace_seq_printf(s, " <-"); if (!ret) return TRACE_TYPE_PARTIAL_LINE; ret = seq_print_ip_sym(s, field->parent_ip, sym_flags); if (!ret) return TRACE_TYPE_PARTIAL_LINE; } ret = trace_seq_printf(s, "\n"); if (!ret) return TRACE_TYPE_PARTIAL_LINE; break; } case TRACE_CTX: case TRACE_WAKE: { struct ctx_switch_entry *field; trace_assign_type(field, entry); T = task_state_char(field->next_state); S = task_state_char(field->prev_state); ret = trace_seq_printf(s, " %5d:%3d:%c %s [%03d] %5d:%3d:%c\n", field->prev_pid, field->prev_prio, S, entry->type == TRACE_CTX ? "==>" : " +", field->next_cpu, field->next_pid, field->next_prio, T); if (!ret) return TRACE_TYPE_PARTIAL_LINE; break; } case TRACE_SPECIAL: { struct special_entry *field; trace_assign_type(field, entry); ret = trace_seq_printf(s, "# %ld %ld %ld\n", field->arg1, field->arg2, field->arg3); if (!ret) return TRACE_TYPE_PARTIAL_LINE; break; } case TRACE_STACK: { struct stack_entry *field; trace_assign_type(field, entry); for (i = 0; i < FTRACE_STACK_ENTRIES; i++) { if (i) { ret = trace_seq_puts(s, " <= "); if (!ret) return TRACE_TYPE_PARTIAL_LINE; } ret = seq_print_ip_sym(s, field->caller[i], sym_flags); if (!ret) return TRACE_TYPE_PARTIAL_LINE; event = ftrace_find_event(entry->type); if (event && event->trace) { ret = event->trace(s, entry, sym_flags); if (ret) return ret; return TRACE_TYPE_HANDLED; } ret = trace_seq_puts(s, "\n"); ret = trace_seq_printf(s, "Unknown type %d\n", entry->type); if (!ret) return TRACE_TYPE_PARTIAL_LINE; break; } case TRACE_PRINT: { struct print_entry *field; trace_assign_type(field, entry); seq_print_ip_sym(s, field->ip, sym_flags); trace_seq_printf(s, ": %s", field->buf); if (entry->flags & TRACE_FLAG_CONT) trace_seq_print_cont(s, iter); break; } case TRACE_GRAPH_RET: { return print_graph_function(iter); } case TRACE_GRAPH_ENT: { return print_graph_function(iter); } case TRACE_BRANCH: { struct trace_branch *field; trace_assign_type(field, entry); trace_seq_printf(s, "[%s] %s:%s:%d\n", field->correct ? " ok " : " MISS ", field->func, field->file, field->line); break; } case TRACE_USER_STACK: { struct userstack_entry *field; trace_assign_type(field, entry); ret = seq_print_userip_objs(field, s, sym_flags); if (!ret) return TRACE_TYPE_PARTIAL_LINE; ret = trace_seq_putc(s, '\n'); if (!ret) return TRACE_TYPE_PARTIAL_LINE; break; } } return TRACE_TYPE_HANDLED; } Loading @@ -2118,152 +1600,47 @@ static enum print_line_t print_raw_fmt(struct trace_iterator *iter) { struct trace_seq *s = &iter->seq; struct trace_entry *entry; struct trace_event *event; int ret; int S, T; entry = iter->ent; if (entry->type == TRACE_CONT) return TRACE_TYPE_HANDLED; ret = trace_seq_printf(s, "%d %d %llu ", entry->pid, iter->cpu, iter->ts); if (!ret) return TRACE_TYPE_PARTIAL_LINE; switch (entry->type) { case TRACE_FN: { struct ftrace_entry *field; trace_assign_type(field, entry); ret = trace_seq_printf(s, "%x %x\n", field->ip, field->parent_ip); if (!ret) return TRACE_TYPE_PARTIAL_LINE; break; } case TRACE_CTX: case TRACE_WAKE: { struct ctx_switch_entry *field; trace_assign_type(field, entry); T = task_state_char(field->next_state); S = entry->type == TRACE_WAKE ? '+' : task_state_char(field->prev_state); ret = trace_seq_printf(s, "%d %d %c %d %d %d %c\n", field->prev_pid, field->prev_prio, S, field->next_cpu, field->next_pid, field->next_prio, T); if (!ret) return TRACE_TYPE_PARTIAL_LINE; break; event = ftrace_find_event(entry->type); if (event && event->raw) { ret = event->raw(s, entry, 0); if (ret) return ret; return TRACE_TYPE_HANDLED; } case TRACE_SPECIAL: case TRACE_USER_STACK: case TRACE_STACK: { struct special_entry *field; trace_assign_type(field, entry); ret = trace_seq_printf(s, "# %ld %ld %ld\n", field->arg1, field->arg2, field->arg3); ret = trace_seq_printf(s, "%d ?\n", entry->type); if (!ret) return TRACE_TYPE_PARTIAL_LINE; break; } case TRACE_PRINT: { struct print_entry *field; trace_assign_type(field, entry); trace_seq_printf(s, "# %lx %s", field->ip, field->buf); if (entry->flags & TRACE_FLAG_CONT) trace_seq_print_cont(s, iter); break; } } return TRACE_TYPE_HANDLED; } #define SEQ_PUT_FIELD_RET(s, x) \ do { \ if (!trace_seq_putmem(s, &(x), sizeof(x))) \ return 0; \ } while (0) #define SEQ_PUT_HEX_FIELD_RET(s, x) \ do { \ BUILD_BUG_ON(sizeof(x) > MAX_MEMHEX_BYTES); \ if (!trace_seq_putmem_hex(s, &(x), sizeof(x))) \ return 0; \ } while (0) static enum print_line_t print_hex_fmt(struct trace_iterator *iter) { struct trace_seq *s = &iter->seq; unsigned char newline = '\n'; struct trace_entry *entry; int S, T; struct trace_event *event; entry = iter->ent; if (entry->type == TRACE_CONT) return TRACE_TYPE_HANDLED; SEQ_PUT_HEX_FIELD_RET(s, entry->pid); SEQ_PUT_HEX_FIELD_RET(s, iter->cpu); SEQ_PUT_HEX_FIELD_RET(s, iter->ts); switch (entry->type) { case TRACE_FN: { struct ftrace_entry *field; event = ftrace_find_event(entry->type); if (event && event->hex) event->hex(s, entry, 0); trace_assign_type(field, entry); SEQ_PUT_HEX_FIELD_RET(s, field->ip); SEQ_PUT_HEX_FIELD_RET(s, field->parent_ip); break; } case TRACE_CTX: case TRACE_WAKE: { struct ctx_switch_entry *field; trace_assign_type(field, entry); T = task_state_char(field->next_state); S = entry->type == TRACE_WAKE ? '+' : task_state_char(field->prev_state); SEQ_PUT_HEX_FIELD_RET(s, field->prev_pid); SEQ_PUT_HEX_FIELD_RET(s, field->prev_prio); SEQ_PUT_HEX_FIELD_RET(s, S); SEQ_PUT_HEX_FIELD_RET(s, field->next_cpu); SEQ_PUT_HEX_FIELD_RET(s, field->next_pid); SEQ_PUT_HEX_FIELD_RET(s, field->next_prio); SEQ_PUT_HEX_FIELD_RET(s, T); break; } case TRACE_SPECIAL: case TRACE_USER_STACK: case TRACE_STACK: { struct special_entry *field; trace_assign_type(field, entry); SEQ_PUT_HEX_FIELD_RET(s, field->arg1); SEQ_PUT_HEX_FIELD_RET(s, field->arg2); SEQ_PUT_HEX_FIELD_RET(s, field->arg3); break; } } SEQ_PUT_FIELD_RET(s, newline); return TRACE_TYPE_HANDLED; Loading @@ -2282,9 +1659,6 @@ static enum print_line_t print_printk_msg_only(struct trace_iterator *iter) if (!ret) return TRACE_TYPE_PARTIAL_LINE; if (entry->flags & TRACE_FLAG_CONT) trace_seq_print_cont(s, iter); return TRACE_TYPE_HANDLED; } Loading @@ -2292,53 +1666,19 @@ static enum print_line_t print_bin_fmt(struct trace_iterator *iter) { struct trace_seq *s = &iter->seq; struct trace_entry *entry; struct trace_event *event; entry = iter->ent; if (entry->type == TRACE_CONT) return TRACE_TYPE_HANDLED; SEQ_PUT_FIELD_RET(s, entry->pid); SEQ_PUT_FIELD_RET(s, entry->cpu); SEQ_PUT_FIELD_RET(s, iter->ts); switch (entry->type) { case TRACE_FN: { struct ftrace_entry *field; trace_assign_type(field, entry); SEQ_PUT_FIELD_RET(s, field->ip); SEQ_PUT_FIELD_RET(s, field->parent_ip); break; } case TRACE_CTX: { struct ctx_switch_entry *field; trace_assign_type(field, entry); SEQ_PUT_FIELD_RET(s, field->prev_pid); SEQ_PUT_FIELD_RET(s, field->prev_prio); SEQ_PUT_FIELD_RET(s, field->prev_state); SEQ_PUT_FIELD_RET(s, field->next_pid); SEQ_PUT_FIELD_RET(s, field->next_prio); SEQ_PUT_FIELD_RET(s, field->next_state); break; } case TRACE_SPECIAL: case TRACE_USER_STACK: case TRACE_STACK: { struct special_entry *field; event = ftrace_find_event(entry->type); if (event && event->binary) event->binary(s, entry, 0); trace_assign_type(field, entry); SEQ_PUT_FIELD_RET(s, field->arg1); SEQ_PUT_FIELD_RET(s, field->arg2); SEQ_PUT_FIELD_RET(s, field->arg3); break; } } return 1; return TRACE_TYPE_HANDLED; } static int trace_empty(struct trace_iterator *iter) Loading Loading @@ -3013,6 +2353,7 @@ static int tracing_set_tracer(char *buf) if (ret) goto out; } init_tracer_stat(t); trace_branch_enable(tr); out: Loading Loading @@ -3877,7 +3218,7 @@ __init static int tracer_alloc_buffers(void) #else current_trace = &nop_trace; #endif init_tracer_stat(current_trace); /* All seems OK, enable tracing */ tracing_disabled = 0; Loading
kernel/trace/trace.h +20 −16 Original line number Diff line number Diff line Loading @@ -17,7 +17,6 @@ enum trace_type { TRACE_FN, TRACE_CTX, TRACE_WAKE, TRACE_CONT, TRACE_STACK, TRACE_PRINT, TRACE_SPECIAL, Loading @@ -34,7 +33,7 @@ enum trace_type { TRACE_KMEM_FREE, TRACE_POWER, __TRACE_LAST_TYPE __TRACE_LAST_TYPE, }; /* Loading Loading @@ -199,7 +198,6 @@ struct kmemtrace_free_entry { * NEED_RESCED - reschedule is requested * HARDIRQ - inside an interrupt handler * SOFTIRQ - inside a softirq handler * CONT - multiple entries hold the trace item */ enum trace_flag_type { TRACE_FLAG_IRQS_OFF = 0x01, Loading @@ -207,7 +205,6 @@ enum trace_flag_type { TRACE_FLAG_NEED_RESCHED = 0x04, TRACE_FLAG_HARDIRQ = 0x08, TRACE_FLAG_SOFTIRQ = 0x10, TRACE_FLAG_CONT = 0x20, }; #define TRACE_BUF_SIZE 1024 Loading Loading @@ -283,7 +280,6 @@ extern void __ftrace_bad_type(void); do { \ IF_ASSIGN(var, ent, struct ftrace_entry, TRACE_FN); \ IF_ASSIGN(var, ent, struct ctx_switch_entry, 0); \ IF_ASSIGN(var, ent, struct trace_field_cont, TRACE_CONT); \ IF_ASSIGN(var, ent, struct stack_entry, TRACE_STACK); \ IF_ASSIGN(var, ent, struct userstack_entry, TRACE_USER_STACK);\ IF_ASSIGN(var, ent, struct print_entry, TRACE_PRINT); \ Loading Loading @@ -365,6 +361,21 @@ struct tracer { struct tracer *next; int print_max; struct tracer_flags *flags; /* * If you change one of the following on tracing runtime, recall * init_tracer_stat() */ /* Iteration over statistic entries */ void *(*stat_start)(void); void *(*stat_next)(void *prev, int idx); /* Compare two entries for sorting (optional) for stats */ int (*stat_cmp)(void *p1, void *p2); /* Print a stat entry */ int (*stat_show)(struct seq_file *s, void *p); /* Print the headers of your stat entries */ int (*stat_headers)(struct seq_file *s); }; struct trace_seq { Loading Loading @@ -450,6 +461,8 @@ void tracing_start_sched_switch_record(void); int register_tracer(struct tracer *type); void unregister_tracer(struct tracer *type); void init_tracer_stat(struct tracer *trace); extern unsigned long nsecs_to_usecs(unsigned long nsecs); extern unsigned long tracing_max_latency; Loading Loading @@ -481,10 +494,10 @@ struct tracer_switch_ops { void *private; struct tracer_switch_ops *next; }; char *trace_find_cmdline(int pid); #endif /* CONFIG_CONTEXT_SWITCH_TRACER */ extern char *trace_find_cmdline(int pid); #ifdef CONFIG_DYNAMIC_FTRACE extern unsigned long ftrace_update_tot_cnt; #define DYN_FTRACE_TEST_NAME trace_selftest_dynamic_test_func Loading Loading @@ -513,15 +526,6 @@ extern int trace_selftest_startup_branch(struct tracer *trace, #endif /* CONFIG_FTRACE_STARTUP_TEST */ extern void *head_page(struct trace_array_cpu *data); extern int trace_seq_printf(struct trace_seq *s, const char *fmt, ...); extern void trace_seq_print_cont(struct trace_seq *s, struct trace_iterator *iter); extern int seq_print_ip_sym(struct trace_seq *s, unsigned long ip, unsigned long sym_flags); extern ssize_t trace_seq_to_user(struct trace_seq *s, char __user *ubuf, size_t cnt); extern long ns2usecs(cycle_t nsec); extern int trace_vprintk(unsigned long ip, int depth, const char *fmt, va_list args); Loading
kernel/trace/trace_boot.c +1 −0 Original line number Diff line number Diff line Loading @@ -11,6 +11,7 @@ #include <linux/kallsyms.h> #include "trace.h" #include "trace_output.h" static struct trace_array *boot_trace; static bool pre_initcalls_finished; Loading