staticint __init boot_alloc_snapshot(char *str)
{ char *slot = boot_snapshot_info + boot_snapshot_index; int left = sizeof(boot_snapshot_info) - boot_snapshot_index; int ret;
if (str[0] == '=') {
str++; if (strlen(str) >= left) return -1;
ret = snprintf(slot, left, "%s\t", str);
boot_snapshot_index += ret;
} else {
allocate_snapshot = true; /* We also need the main ring buffer expanded */
trace_set_ring_buffer_expanded(NULL);
} return1;
}
__setup("alloc_snapshot", boot_alloc_snapshot);
/* trace_options that are only supported by global_trace */ #define TOP_LEVEL_TRACE_FLAGS (TRACE_ITER_PRINTK | \
TRACE_ITER_PRINTK_MSGONLY | TRACE_ITER_RECORD_CMD)
/* trace_flags that are default zero for instances */ #define ZEROED_TRACE_FLAGS \
(TRACE_ITER_EVENT_FORK | TRACE_ITER_FUNC_FORK | TRACE_ITER_TRACE_PRINTK | \
TRACE_ITER_COPY_MARKER)
/* Returns true if the status of tr changed */ staticbool update_marker_trace(struct trace_array *tr, int enabled)
{
lockdep_assert_held(&event_mutex);
if (enabled) { if (!list_empty(&tr->marker_list)) returnfalse;
if (trace_pid_list_first(pid_list, &first) < 0) return NULL;
pid = first;
/* Return pid + 1 so that zero can be the exit value */ for (pid++; pid && l < *pos;
pid = (unsignedlong)trace_pid_next(pid_list, (void *)pid, &l))
; return (void *)pid;
}
if (filtered_pids) { /* copy the current bits to the new max */
ret = trace_pid_list_first(filtered_pids, &pid); while (!ret) {
ret = trace_pid_list_set(pid_list, pid); if (ret < 0) goto out;
ret = trace_pid_list_next(filtered_pids, pid + 1, &pid);
nr_pids++;
}
}
ret = 0; while (cnt > 0) {
pos = 0;
ret = trace_get_user(&parser, ubuf, cnt, &pos); if (ret < 0) break;
read += ret;
ubuf += ret;
cnt -= ret;
if (!trace_parser_loaded(&parser)) break;
ret = -EINVAL; if (kstrtoul(parser.buffer, 0, &val)) break;
pid = (pid_t)val;
if (trace_pid_list_set(pid_list, pid) < 0) {
ret = -1; break;
}
nr_pids++;
trace_parser_clear(&parser);
ret = 0;
}
out:
trace_parser_put(&parser);
if (ret < 0) {
trace_pid_list_free(pid_list); return ret;
}
if (!nr_pids) { /* Cleared the list of pids */
trace_pid_list_free(pid_list);
pid_list = NULL;
}
*new_pid_list = pid_list;
return read;
}
static u64 buffer_ftrace_now(struct array_buffer *buf, int cpu)
{
u64 ts;
/* Early boot up does not have a buffer yet */ if (!buf->buffer) return trace_clock_local();
staticinlinevoid trace_access_lock(int cpu)
{ if (cpu == RING_BUFFER_ALL_CPUS) { /* gain it for accessing the whole ring buffer. */
down_write(&all_cpu_access_lock);
} else { /* gain it for accessing a cpu ring buffer. */
/* Firstly block other trace_access_lock(RING_BUFFER_ALL_CPUS). */
down_read(&all_cpu_access_lock);
/* Secondly block other access to this @cpu ring buffer. */
mutex_lock(&per_cpu(cpu_access_lock, cpu));
}
}
/* If this is the temp buffer, we need to commit fully */ if (this_cpu_read(trace_buffered_event) == event) { /* Length is in event->array[0] */
ring_buffer_write(buffer, event->array[0], &event->array[1]); /* Release the temp buffer */
this_cpu_dec(trace_buffered_event_cnt); /* ring_buffer_unlock_commit() enables preemption */
preempt_enable_notrace();
} else
ring_buffer_unlock_commit(buffer);
}
int __trace_array_puts(struct trace_array *tr, unsignedlong ip, constchar *str, int size)
{ struct ring_buffer_event *event; struct trace_buffer *buffer; struct print_entry *entry; unsignedint trace_ctx; int alloc;
if (!(tr->trace_flags & TRACE_ITER_PRINTK)) return0;
if (unlikely(tracing_selftest_running && tr == &global_trace)) return0;
if (in_nmi()) {
trace_array_puts(tr, "*** SNAPSHOT CALLED FROM NMI CONTEXT ***\n");
trace_array_puts(tr, "*** snapshot is being ignored ***\n"); return;
}
if (!tr->allocated_snapshot) {
trace_array_puts(tr, "*** SNAPSHOT NOT ALLOCATED ***\n");
trace_array_puts(tr, "*** stopping trace here! ***\n");
tracer_tracing_off(tr); return;
}
/* Note, snapshot can not be used when the tracer uses it */ if (tracer->use_max_tr) {
trace_array_puts(tr, "*** LATENCY TRACER ACTIVE ***\n");
trace_array_puts(tr, "*** Can not use snapshot (sorry) ***\n"); return;
}
if (tr->mapped) {
trace_array_puts(tr, "*** BUFFER MEMORY MAPPED ***\n");
trace_array_puts(tr, "*** Can not use snapshot (sorry) ***\n"); return;
}
int tracing_alloc_snapshot_instance(struct trace_array *tr)
{ int order; int ret;
if (!tr->allocated_snapshot) {
/* Make the snapshot buffer have the same order as main buffer */
order = ring_buffer_subbuf_order_get(tr->array_buffer.buffer);
ret = ring_buffer_subbuf_order_set(tr->max_buffer.buffer, order); if (ret < 0) return ret;
/* allocate spare buffer */
ret = resize_buffer_duplicate_size(&tr->max_buffer,
&tr->array_buffer, RING_BUFFER_ALL_CPUS); if (ret < 0) return ret;
ret = tracing_alloc_snapshot_instance(tr); if (ret) {
spin_lock(&tr->snapshot_trigger_lock);
tr->snapshot--;
spin_unlock(&tr->snapshot_trigger_lock);
}
return ret;
}
int tracing_arm_snapshot(struct trace_array *tr)
{
guard(mutex)(&trace_types_lock); return tracing_arm_snapshot_locked(tr);
}
/* *TRACE_FLAGSisdefinedasatuplematchingbitmaskswithstrings. *ItusesC(a,b)where'a'istheeval(enum)nameand'b'isthestringthat *matchesit.Bydefining"C(a,b)b",TRACE_FLAGSbecomesalist *ofstringsintheorderthattheevals(enum)weredefined.
*/ #undef C #define C(a, b) b
/* These must match the bit positions in trace_iterator_flags */ staticconstchar *trace_options[] = {
TRACE_FLAGS
NULL
};
/* *Theparserisnotfinishedwiththelastwrite, *continuereadingtheuserinputwithoutskippingspaces.
*/ if (!parser->cont) { /* skip white space */ while (cnt && isspace(ch)) {
ret = get_user(ch, ubuf++); if (ret) goto fail;
read++;
cnt--;
}
parser->idx = 0;
/* only spaces were written */ if (isspace(ch) || !ch) {
*ppos += read; return read;
}
}
/* read the non-space input */ while (cnt && !isspace(ch) && ch) { if (parser->idx < parser->size - 1)
parser->buffer[parser->idx++] = ch; else {
ret = -EINVAL; goto fail;
}
ret = get_user(ch, ubuf++); if (ret) goto fail;
read++;
cnt--;
}
/* We either got finished input or we have to wait for another call. */ if (isspace(ch) || !ch) {
parser->buffer[parser->idx] = 0;
parser->cont = false;
} elseif (parser->idx < parser->size - 1) {
parser->cont = true;
parser->buffer[parser->idx++] = ch; /* Make sure the parsed string always terminates with '\0'. */
parser->buffer[parser->idx] = 0;
} else {
ret = -EINVAL; goto fail;
}
if (!tr->allocated_snapshot) { /* Only the nop tracer should hit this when disabling */
WARN_ON_ONCE(tr->current_trace != &nop_trace); return;
}
arch_spin_lock(&tr->max_lock);
/* Inherit the recordable setting from array_buffer */ if (ring_buffer_record_is_set_on(tr->array_buffer.buffer))
ring_buffer_record_on(tr->max_buffer.buffer); else
ring_buffer_record_off(tr->max_buffer.buffer);
/* Any waiters on the old snapshot buffer need to wake up */
ring_buffer_wake_waiters(tr->array_buffer.buffer, RING_BUFFER_ALL_CPUS);
}
/** *update_max_tr_single-onlycopyonetraceover,andresettherest *@tr:tracer *@tsk:taskwiththelatency *@cpu:thecpuofthebuffertocopy. * *FlipthetraceofasingleCPUbufferbetweenthe@trandthemax_tr.
*/ void
update_max_tr_single(struct trace_array *tr, struct task_struct *tsk, int cpu)
{ int ret;
if (tr->stop_count) return;
WARN_ON_ONCE(!irqs_disabled()); if (!tr->allocated_snapshot) { /* Only the nop tracer should hit this when disabling */
WARN_ON_ONCE(tr->current_trace != &nop_trace); return;
}
arch_spin_lock(&tr->max_lock);
ret = ring_buffer_swap_cpu(tr->max_buffer.buffer, tr->array_buffer.buffer, cpu);
if (ret == -EBUSY) { /* *Wefailedtoswapthebufferduetoacommittaking *placeonthisCPU.Wefailtorecord,butwereset *themaxtracebuffer(noonewritesdirectlytoit) *andflagthatitfailed. *Anotherreasonisresizeisinprogress.
*/
trace_array_printk_buf(tr->max_buffer.buffer, _THIS_IP_, "Failed to swap buffers due to commit or resize in progress\n");
}
WARN_ON_ONCE(ret && ret != -EAGAIN && ret != -EBUSY);
#ifdef CONFIG_TRACER_MAX_TRACE if (type->use_max_tr) { /* If we expanded the buffers, make sure the max is expanded too */ if (tr->ring_buffer_expanded)
ring_buffer_resize(tr->max_buffer.buffer, trace_buf_size,
RING_BUFFER_ALL_CPUS);
tr->allocated_snapshot = true;
} #endif
/* the test is responsible for initializing and enabling */
pr_info("Testing tracer %s: ", type->name);
ret = type->selftest(type, tr); /* the test is responsible for resetting too */
tr->current_trace = saved_tracer; if (ret) {
printk(KERN_CONT "FAILED!\n"); /* Add the warning after printing 'FAILED' */
WARN_ON(1); return -1;
} /* Only reset on passing, to avoid touching corrupted buffers */
tracing_reset_online_cpus(&tr->array_buffer);
#ifdef CONFIG_TRACER_MAX_TRACE if (type->use_max_tr) {
tr->allocated_snapshot = false;
/* Shrink the max buffer again */ if (tr->ring_buffer_expanded)
ring_buffer_resize(tr->max_buffer.buffer, 1,
RING_BUFFER_ALL_CPUS);
} #endif
printk(KERN_CONT "PASSED\n"); return0;
}
staticint do_run_tracer_selftest(struct tracer *type)
{ int ret;
tracing_selftest_running = true;
ret = run_tracer_selftest(type);
tracing_selftest_running = false;
return ret;
}
static __init int init_trace_selftests(void)
{ struct trace_selftests *p, *n; struct tracer *t, **last; int ret;
selftests_can_run = true;
guard(mutex)(&trace_types_lock);
if (list_empty(&postponed_selftests)) return0;
pr_info("Running postponed tracer tests:\n");
tracing_selftest_running = true;
list_for_each_entry_safe(p, n, &postponed_selftests, list) { /* This loop can take minutes when sanitizers are enabled, so *letsmakesureweallowRCUprocessing.
*/
cond_resched();
ret = run_tracer_selftest(p->type); /* If the test fails, then warn and remove from available_tracers */ if (ret < 0) {
WARN(1, "tracer: %s failed selftest, disabling\n",
p->type->name);
last = &trace_types; for (t = trace_types; t; t = t->next) { if (t == p->type) {
*last = t->next; break;
}
last = &t->next;
}
}
list_del(&p->list);
kfree(p);
}
tracing_selftest_running = false;
if (strncmp(default_bootup_tracer, type->name, MAX_TRACER_SIZE)) return0;
printk(KERN_INFO "Starting tracer '%s'\n", type->name); /* Do we want this tracer to start on bootup? */
tracing_set_tracer(&global_trace, type->name);
default_bootup_tracer = NULL;
apply_trace_boot_options();
/* disable other selftests, since this will break it. */
disable_tracing_selftest("running a tracer");
for_each_tracing_cpu(cpu) {
page = alloc_pages_node(cpu_to_node(cpu),
GFP_KERNEL | __GFP_NORETRY, 0); /* This is just an optimization and can handle failures */ if (!page) {
pr_err("Failed to allocate event buffer\n"); break;
}
#ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API if (fregs) { for (int i = 0; i < FTRACE_REGS_MAX_ARGS; i++)
entry->args[i] = ftrace_regs_get_argument(fregs, i);
} #endif
if (static_branch_unlikely(&trace_function_exports_enabled))
ftrace_exports(event, TRACE_EXPORT_FUNCTION);
__buffer_unlock_commit(buffer, event);
}
buffers = alloc_percpu(struct trace_buffer_struct); if (MEM_FAIL(!buffers, "Could not allocate percpu trace_printk buffer")) return -ENOMEM;
trace_percpu_buffer = buffers; return0;
}
staticint buffers_allocated;
void trace_printk_init_buffers(void)
{ if (buffers_allocated) return;
if (alloc_percpu_trace_buffer()) return;
/* trace_printk() is for debug use only. Don't use it in production. */
pr_warn("\n");
pr_warn("**********************************************************\n");
pr_warn("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **\n");
pr_warn("** **\n");
pr_warn("** trace_printk() being used. Allocating extra memory. **\n");
pr_warn("** **\n");
pr_warn("** This means that this is a DEBUG kernel and it is **\n");
pr_warn("** unsafe for production use. **\n");
pr_warn("** **\n");
pr_warn("** If you see this message and you are not debugging **\n");
pr_warn("** the kernel, report this immediately to your vendor! **\n");
pr_warn("** **\n");
pr_warn("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **\n");
pr_warn("**********************************************************\n");
/* Expand the buffers to set size */
tracing_update_buffers(&global_trace);
void trace_printk_start_comm(void)
{ /* Start tracing comms if trace printk is set */ if (!buffers_allocated) return;
tracing_start_cmdline_record();
}
staticvoid trace_printk_start_stop_comm(int enabled)
{ if (!buffers_allocated) return;
if (enabled)
tracing_start_cmdline_record(); else
tracing_stop_cmdline_record();
}
/* Returns true if the string is safe to dereference from an event */ staticbool trace_safe_str(struct trace_iterator *iter, constchar *str)
{ unsignedlong addr = (unsignedlong)str; struct trace_event *trace_event; struct trace_event_call *event;
/* OK if part of the event data */ if ((addr >= (unsignedlong)iter->ent) &&
(addr < (unsignedlong)iter->ent + iter->ent_size)) returntrue;
/* OK if part of the temp seq buffer */ if ((addr >= (unsignedlong)iter->tmp_seq.buffer) &&
(addr < (unsignedlong)iter->tmp_seq.buffer + TRACE_SEQ_BUFFER_SIZE)) returntrue;
/* Core rodata can not be freed */ if (is_kernel_rodata(addr)) returntrue;
if (trace_is_tracepoint_string(str)) returntrue;
/* *Nowthiscouldbeamoduleevent,referencingcoremodule *data,whichisOK.
*/ if (!iter->ent) returnfalse;
trace_event = ftrace_find_event(iter->ent->type); if (!trace_event) returnfalse;
/* Find the next real entry, without updating the iterator itself */ struct trace_entry *trace_find_next_entry(struct trace_iterator *iter, int *ent_cpu, u64 *ent_ts)
{ /* __find_next_entry will reset ent_size */ int ent_size = iter->ent_size; struct trace_entry *entry;
/* *The__find_next_entry()maycallpeek_next_entry(),whichmay *callring_buffer_peek()thatmaymakethecontentsofiter->ent *undefined.Needtocopyiter->entnow.
*/ if (iter->ent && iter->ent != iter->temp) { if ((!iter->temp || iter->temp_size < iter->ent_size) &&
!WARN_ON_ONCE(iter->temp == static_temp_buf)) { void *temp;
temp = kmalloc(iter->ent_size, GFP_KERNEL); if (!temp) return NULL;
kfree(iter->temp);
iter->temp = temp;
iter->temp_size = iter->ent_size;
}
memcpy(iter->temp, iter->ent, iter->ent_size);
iter->ent = iter->temp;
}
entry = __find_next_entry(iter, ent_cpu, NULL, ent_ts); /* Put back the original ent_size */
iter->ent_size = ent_size;
return entry;
}
/* Find the next real entry, and increment the iterator to the next entry */ void *trace_find_next_entry_inc(struct trace_iterator *iter)
{
iter->ent = __find_next_entry(iter, &iter->cpu,
&iter->lost_events, &iter->ts);
buf_iter = trace_buffer_iter(iter, cpu); if (!buf_iter) return;
ring_buffer_iter_reset(buf_iter);
/* *Wecouldhavethecasewiththemaxlatencytracers *thataresetnevertookplaceonacpu.Thisisevident *bythetimestampbeingbeforethestartofthebuffer.
*/ while (ring_buffer_iter_peek(buf_iter, &ts)) { if (ts >= iter->array_buffer->time_start) break;
entries++;
ring_buffer_iter_advance(buf_iter); /* This could be a big loop */
cond_resched();
}
mutex_lock(&trace_types_lock); if (unlikely(tr->current_trace != iter->trace)) { /* Close iter->trace before switching to the new current tracer */ if (iter->trace->close)
iter->trace->close(iter);
iter->trace = tr->current_trace; /* Reopen the new current tracer */ if (iter->trace->open)
iter->trace->open(iter);
}
mutex_unlock(&trace_types_lock);
#ifdef CONFIG_TRACER_MAX_TRACE if (iter->snapshot && iter->trace->use_max_tr) return ERR_PTR(-EBUSY); #endif
if (!(tr->trace_flags & TRACE_ITER_ANNOTATE)) return;
if (!(iter->iter_flags & TRACE_FILE_ANNOTATE)) return;
if (cpumask_available(iter->started) &&
cpumask_test_cpu(iter->cpu, iter->started)) return;
if (per_cpu_ptr(iter->array_buffer->data, iter->cpu)->skipped_entries) return;
if (cpumask_available(iter->started))
cpumask_set_cpu(iter->cpu, iter->started);
/* Don't print started cpu buffer for the first entry of the trace */ if (iter->idx > 1)
trace_seq_printf(s, "##### CPU %u buffer started ####\n",
iter->cpu);
}
int trace_empty(struct trace_iterator *iter)
{ struct ring_buffer_iter *buf_iter; int cpu;
/* If we are looking at one CPU buffer, only check that one */ if (iter->cpu_file != RING_BUFFER_ALL_CPUS) {
cpu = iter->cpu_file;
buf_iter = trace_buffer_iter(iter, cpu); if (buf_iter) { if (!ring_buffer_iter_empty(buf_iter)) return0;
} else { if (!ring_buffer_empty_cpu(iter->array_buffer->buffer, cpu)) return0;
} return1;
}
for_each_tracing_cpu(cpu) {
buf_iter = trace_buffer_iter(iter, cpu); if (buf_iter) { if (!ring_buffer_iter_empty(buf_iter)) return0;
} else { if (!ring_buffer_empty_cpu(iter->array_buffer->buffer, cpu)) return0;
}
}
if (!(trace_flags & TRACE_ITER_CONTEXT_INFO)) return;
if (iter->iter_flags & TRACE_FILE_LAT_FMT) { /* print nothing if the buffers are empty */ if (trace_empty(iter)) return;
print_trace_header(m, iter); if (!(trace_flags & TRACE_ITER_VERBOSE))
print_lat_help_header(m);
} else { if (!(trace_flags & TRACE_ITER_VERBOSE)) { if (trace_flags & TRACE_ITER_IRQ_INFO)
print_func_help_header_irq(iter->array_buffer,
m, trace_flags); else
print_func_help_header(iter->array_buffer, m,
trace_flags);
}
}
}
staticvoid test_ftrace_alive(struct seq_file *m)
{ if (!ftrace_is_dead()) return;
seq_puts(m, "# WARNING: FUNCTION TRACING IS CORRUPTED\n" "# MAY BE MISSING FUNCTION EVENTS\n");
}
#ifdef CONFIG_TRACER_MAX_TRACE staticvoid show_snapshot_main_help(struct seq_file *m)
{
seq_puts(m, "# echo 0 > snapshot : Clears and frees snapshot buffer\n" "# echo 1 > snapshot : Allocates snapshot buffer, if not already allocated.\n" "# Takes a snapshot of the main buffer.\n" "# echo 2 > snapshot : Clears snapshot buffer (but does not allocate or free)\n" "# (Doesn't have to be '2' works with any number that\n" "# is not a '0' or '1')\n");
}
staticvoid show_snapshot_percpu_help(struct seq_file *m)
{
seq_puts(m, "# echo 0 > snapshot : Invalid for per_cpu snapshot file.\n"); #ifdef CONFIG_RING_BUFFER_ALLOW_SWAP
seq_puts(m, "# echo 1 > snapshot : Allocates snapshot buffer, if not already allocated.\n" "# Takes a snapshot of the main buffer for this cpu.\n"); #else
seq_puts(m, "# echo 1 > snapshot : Not supported with this kernel.\n" "# Must use main snapshot file to allocate.\n"); #endif
seq_puts(m, "# echo 2 > snapshot : Clears this cpu's snapshot buffer (but does not allocate)\n" "# (Doesn't have to be '2' works with any number that\n" "# is not a '0' or '1')\n");
}
staticvoid print_snapshot_help(struct seq_file *m, struct trace_iterator *iter)
{ if (iter->tr->allocated_snapshot)
seq_puts(m, "#\n# * Snapshot is allocated *\n#\n"); else
seq_puts(m, "#\n# * Snapshot is freed *\n#\n");
seq_puts(m, "# Snapshot commands:\n"); if (iter->cpu_file == RING_BUFFER_ALL_CPUS)
show_snapshot_main_help(m); else
show_snapshot_percpu_help(m);
} #else /* Should never be called */ staticinlinevoid print_snapshot_help(struct seq_file *m, struct trace_iterator *iter) { } #endif
/* *Note,asiteritselfcanbeallocatedandfreedindifferent *ways,thisfunctionisonlyusedtofreeitscontent,andnot *theiteratoritself.Theonlyrequirementtoalltheallocations *isthatitmustzeroallfields(kzalloc),asfreeingworkswith *ethierallocatedcontentorNULL.
*/ staticvoid free_trace_iter_content(struct trace_iterator *iter)
{ /* The fmt is either NULL, allocated or points to static_fmt_buf */ if (iter->fmt != static_fmt_buf)
kfree(iter->fmt);
if (!zalloc_cpumask_var(&iter->started, GFP_KERNEL)) goto fail;
iter->tr = tr;
#ifdef CONFIG_TRACER_MAX_TRACE /* Currently only the top directory has a snapshot */ if (tr->current_trace->print_max || snapshot)
iter->array_buffer = &tr->max_buffer; else #endif
iter->array_buffer = &tr->array_buffer;
iter->snapshot = snapshot;
iter->pos = -1;
iter->cpu_file = tracing_get_cpu(inode);
mutex_init(&iter->mutex);
/* Notify the tracer early; before we stop tracing. */ if (iter->trace->open)
iter->trace->open(iter);
/* Annotate start of buffers if we had overruns */ if (ring_buffer_overruns(iter->array_buffer->buffer))
iter->iter_flags |= TRACE_FILE_ANNOTATE;
/* Output in nanoseconds only if we are using a clock in nanoseconds. */ if (trace_clocks[tr->clock_id].in_ns)
iter->iter_flags |= TRACE_FILE_TIME_IN_NS;
/* *Openandupdatetrace_arrayrefcount. *Musthavethecurrenttrace_arraypassedtoit.
*/ int tracing_open_generic_tr(struct inode *inode, struct file *filp)
{ struct trace_array *tr = inode->i_private; int ret;
ret = tracing_check_open_get_tr(tr); if (ret) return ret;
filp->private_data = inode->i_private;
return0;
}
/* *Theprivatepointeroftheinodeisthetrace_event_file. *Updatethetrrefcountassociatedtoit.
*/ int tracing_open_file_tr(struct inode *inode, struct file *filp)
{ struct trace_event_file *file = inode->i_private; int ret;
ret = tracing_check_open_get_tr(file->tr); if (ret) return ret;
guard(mutex)(&event_mutex);
/* Fail if the file is marked for removal */ if (file->flags & EVENT_FILE_FL_FREED) {
trace_array_put(file->tr); return -ENODEV;
} else {
event_file_get(file);
}
ret = tracing_check_open_get_tr(tr); if (ret) return ret;
/* If this file was open for write, then erase contents */ if ((file->f_mode & FMODE_WRITE) && (file->f_flags & O_TRUNC)) { int cpu = tracing_get_cpu(inode); struct array_buffer *trace_buf = &tr->array_buffer;
#ifdef CONFIG_TRACER_MAX_TRACE if (tr->current_trace->print_max)
trace_buf = &tr->max_buffer; #endif
if (cpu == RING_BUFFER_ALL_CPUS)
tracing_reset_online_cpus(trace_buf); else
tracing_reset_cpu(trace_buf, cpu);
}
if (file->f_mode & FMODE_READ) {
iter = __tracing_open(inode, file, false); if (IS_ERR(iter))
ret = PTR_ERR(iter); elseif (tr->trace_flags & TRACE_ITER_LATENCY_FMT)
iter->iter_flags |= TRACE_FILE_LAT_FMT;
}
if (ret < 0)
trace_array_put(tr);
return ret;
}
/* *Sometracersarenotsuitableforinstancebuffers. *Atracerisalwaysavailablefortheglobalarray(toplevel) *orifitexplicitlystatesthatitis.
*/ staticbool
trace_ok_for_array(struct tracer *t, struct trace_array *tr)
{ #ifdef CONFIG_TRACER_SNAPSHOT /* arrays with mapped buffer range do not have snapshots */ if (tr->range_addr_start && t->use_max_tr) returnfalse; #endif return (tr->flags & TRACE_ARRAY_FL_GLOBAL) || t->allow_instances;
}
/* Find the next tracer that this trace array may use */ staticstruct tracer *
get_tracer_for_array(struct trace_array *tr, struct tracer *t)
{ while (t && !trace_ok_for_array(t, tr))
t = t->next;
/* Some tracers require overwrite to stay enabled */ int trace_keep_overwrite(struct tracer *tracer, u32 mask, int set)
{ if (tracer->enabled && (mask & TRACE_ITER_OVERWRITE) && !set) return -1;
return0;
}
int set_tracer_flag(struct trace_array *tr, unsignedint mask, int enabled)
{ if ((mask == TRACE_ITER_RECORD_TGID) ||
(mask == TRACE_ITER_RECORD_CMD) ||
(mask == TRACE_ITER_TRACE_PRINTK) ||
(mask == TRACE_ITER_COPY_MARKER))
lockdep_assert_held(&event_mutex);
/* do nothing if flag is already set */ if (!!(tr->trace_flags & mask) == !!enabled) return0;
/* Give the tracer a chance to approve the change */ if (tr->current_trace->flag_changed) if (tr->current_trace->flag_changed(tr, mask, !!enabled)) return -EINVAL;
if (mask == TRACE_ITER_TRACE_PRINTK) { if (enabled) {
update_printk_trace(tr);
} else { /* *Theglobal_tracecannotclearthis. *It'sflagonlygetsclearedifanotherinstancesetsit.
*/ if (printk_trace == &global_trace) return -EINVAL; /* *Aninstancemustalwayshaveitset. *bydefault,that'stheglobal_traceinstane.
*/ if (printk_trace == tr)
update_printk_trace(&global_trace);
}
}
if (mask == TRACE_ITER_COPY_MARKER)
update_marker_trace(tr, enabled);
if (enabled)
tr->trace_flags |= mask; else
tr->trace_flags &= ~mask;
if (mask == TRACE_ITER_RECORD_CMD)
trace_event_enable_cmd_record(enabled);
ret = match_string(trace_options, -1, cmp); /* If no option could be set, test the specific tracer options */ if (ret < 0)
ret = set_tracer_option(tr, cmp, neg); else
ret = set_tracer_flag(tr, 1 << ret, !neg);
staticconstchar readme_msg[] = "tracing mini-HOWTO:\n\n" "By default tracefs removes all OTH file permission bits.\n" "When mounting tracefs an optional group id can be specified\n" "which adds the group to every directory and file in tracefs:\n\n" "\t e.g. mount -t tracefs [-o [gid=<gid>]] nodev /sys/kernel/tracing\n\n" "# echo 0 > tracing_on : quick way to disable tracing\n" "# echo 1 > tracing_on : quick way to re-enable tracing\n\n" " Important files:\n" " trace\t\t\t- The static contents of the buffer\n" "\t\t\t To clear the buffer write into this file: echo > trace\n" " trace_pipe\t\t- A consuming read to see the contents of the buffer\n" " current_tracer\t- function and latency tracers\n" " available_tracers\t- list of configured tracers for current_tracer\n" " error_log\t- error log for failed commands (that support it)\n" " buffer_size_kb\t- view and modify size of per cpu buffer\n" " buffer_total_size_kb - view total size of all cpu buffers\n\n" " trace_clock\t\t- change the clock used to order events\n" " local: Per cpu clock but may not be synced across CPUs\n" " global: Synced across CPUs but slows tracing down.\n" " counter: Not a clock, but just an increment\n" " uptime: Jiffy counter from time of boot\n" " perf: Same clock that perf events use\n" #ifdef CONFIG_X86_64 " x86-tsc: TSC cycle counter\n" #endif "\n timestamp_mode\t- view the mode used to timestamp events\n" " delta: Delta difference against a buffer-wide timestamp\n" " absolute: Absolute (standalone) timestamp\n" "\n trace_marker\t\t- Writes into this file writes into the kernel buffer\n" "\n trace_marker_raw\t\t- Writes into this file writes binary data into the kernel buffer\n" " tracing_cpumask\t- Limit which CPUs to trace\n" " instances\t\t- Make sub-buffers with: mkdir instances/foo\n" "\t\t\t Remove sub-buffer with rmdir\n" " trace_options\t\t- Set format or modify how tracing happens\n" "\t\t\t Disable an option by prefixing 'no' to the\n" "\t\t\t option name\n" " saved_cmdlines_size\t- echo command number in here to store comm-pid list\n" #ifdef CONFIG_DYNAMIC_FTRACE "\n available_filter_functions - list of functions that can be filtered on\n" " set_ftrace_filter\t- echo function name in here to only trace these\n" "\t\t\t functions\n" "\t accepts: func_full_name or glob-matching-pattern\n" "\t modules: Can select a group via module\n" "\t Format: :mod:<module-name>\n" "\t example: echo :mod:ext3 > set_ftrace_filter\n" "\t triggers: a command to perform when function is hit\n" "\t Format: <function>:<trigger>[:count]\n" "\t trigger: traceon, traceoff\n" "\t\t enable_event:<system>:<event>\n" "\t\t disable_event:<system>:<event>\n" #ifdef CONFIG_STACKTRACE "\t\t stacktrace\n" #endif #ifdef CONFIG_TRACER_SNAPSHOT "\t\t snapshot\n" #endif "\t\t dump\n" "\t\t cpudump\n" "\t example: echo do_fault:traceoff > set_ftrace_filter\n" "\t echo do_trap:traceoff:3 > set_ftrace_filter\n" "\t The first one will disable tracing every time do_fault is hit\n" "\t The second will disable tracing at most 3 times when do_trap is hit\n" "\t The first time do trap is hit and it disables tracing, the\n" "\t counter will decrement to 2. If tracing is already disabled,\n" "\t the counter will not decrement. It only decrements when the\n" "\t trigger did work\n" "\t To remove trigger without count:\n" "\t echo '!<function>:<trigger> > set_ftrace_filter\n" "\t To remove trigger with a count:\n" "\t echo '!<function>:<trigger>:0 > set_ftrace_filter\n" " set_ftrace_notrace\t- echo function name in here to never trace.\n" "\t accepts: func_full_name, *func_end, func_begin*, *func_middle*\n" "\t modules: Can select a group via module command :mod:\n" "\t Does not accept triggers\n" #endif/* CONFIG_DYNAMIC_FTRACE */ #ifdef CONFIG_FUNCTION_TRACER " set_ftrace_pid\t- Write pid(s) to only function trace those pids\n" "\t\t (function)\n" " set_ftrace_notrace_pid\t- Write pid(s) to not function trace those pids\n" "\t\t (function)\n" #endif #ifdef CONFIG_FUNCTION_GRAPH_TRACER " set_graph_function\t- Trace the nested calls of a function (function_graph)\n" " set_graph_notrace\t- Do not trace the nested calls of a function (function_graph)\n" " max_graph_depth\t- Trace a limited depth of nested calls (0 is unlimited)\n" #endif #ifdef CONFIG_TRACER_SNAPSHOT "\n snapshot\t\t- Like 'trace' but shows the content of the static\n" "\t\t\t snapshot buffer. Read the contents for more\n" "\t\t\t information\n" #endif #ifdef CONFIG_STACK_TRACER " stack_trace\t\t- Shows the max stack trace when active\n" " stack_max_size\t- Shows current max stack size that was traced\n" "\t\t\t Write into this file to reset the max size (trigger a\n" "\t\t\t new trace)\n" #ifdef CONFIG_DYNAMIC_FTRACE " stack_trace_filter\t- Like set_ftrace_filter but limits what stack_trace\n" "\t\t\t traces\n" #endif #endif/* CONFIG_STACK_TRACER */ #ifdef CONFIG_DYNAMIC_EVENTS " dynamic_events\t\t- Create/append/remove/show the generic dynamic events\n" "\t\t\t Write into this file to define/undefine new trace events.\n" #endif #ifdef CONFIG_KPROBE_EVENTS " kprobe_events\t\t- Create/append/remove/show the kernel dynamic events\n" "\t\t\t Write into this file to define/undefine new trace events.\n" #endif #ifdef CONFIG_UPROBE_EVENTS " uprobe_events\t\t- Create/append/remove/show the userspace dynamic events\n" "\t\t\t Write into this file to define/undefine new trace events.\n" #endif #ifdefined(CONFIG_KPROBE_EVENTS) || defined(CONFIG_UPROBE_EVENTS) || \ defined(CONFIG_FPROBE_EVENTS) "\t accepts: event-definitions (one definition per line)\n" #ifdefined(CONFIG_KPROBE_EVENTS) || defined(CONFIG_UPROBE_EVENTS) "\t Format: p[:[<group>/][<event>]] <place> [<args>]\n" "\t r[maxactive][:[<group>/][<event>]] <place> [<args>]\n" #endif #ifdef CONFIG_FPROBE_EVENTS "\t f[:[<group>/][<event>]] <func-name>[%return] [<args>]\n" "\t t[:[<group>/][<event>]] <tracepoint> [<args>]\n" #endif #ifdef CONFIG_HIST_TRIGGERS "\t s:[synthetic/]<event> <field> [<field>]\n" #endif "\t e[:[<group>/][<event>]] <attached-group>.<attached-event> [<args>] [if <filter>]\n" "\t -:[<group>/][<event>]\n" #ifdef CONFIG_KPROBE_EVENTS "\t place: [<module>:]<symbol>[+<offset>]|<memaddr>\n" "place (kretprobe): [<module>:]<symbol>[+<offset>]%return|<memaddr>\n" #endif #ifdef CONFIG_UPROBE_EVENTS " place (uprobe): <path>:<offset>[%return][(ref_ctr_offset)]\n" #endif "\t args: <name>=fetcharg[:type]\n" "\t fetcharg: (%<register>|$<efield>), @<address>, @<symbol>[+|-<offset>],\n" #ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API "\t $stack<index>, $stack, $retval, $comm, $arg<N>,\n" #ifdef CONFIG_PROBE_EVENTS_BTF_ARGS "\t <argname>[->field[->field|.field...]],\n" #endif #else "\t $stack<index>, $stack, $retval, $comm,\n" #endif "\t +|-[u]<offset>(<fetcharg>), \\imm-value, \\\"imm-string\"\n" "\t kernel return probes support: $retval, $arg<N>, $comm\n" "\t type: s8/16/32/64, u8/16/32/64, x8/16/32/64, char, string, symbol,\n" "\t b<bit-width>@<bit-offset>/<container-size>, ustring,\n" "\t symstr, %pd/%pD, <type>\\[<array-size>\\]\n" #ifdef CONFIG_HIST_TRIGGERS "\t field: <stype> <name>;\n" "\t stype: u8/u16/u32/u64, s8/s16/s32/s64, pid_t,\n" "\t [unsigned] char/int/long\n" #endif "\t efield: For event probes ('e' types), the field is on of the fields\n" "\t of the <attached-group>/<attached-event>.\n" #endif " set_event\t\t- Enables events by name written into it\n" "\t\t\t Can enable module events via: :mod:<module>\n" " events/\t\t- Directory containing all trace event subsystems:\n" " enable\t\t- Write 0/1 to enable/disable tracing of all events\n" " events/<system>/\t- Directory containing all trace events for <system>:\n" " enable\t\t- Write 0/1 to enable/disable tracing of all <system>\n" "\t\t\t events\n" " filter\t\t- If set, only events passing filter are traced\n" " events/<system>/<event>/\t- Directory containing control files for\n" "\t\t\t <event>:\n" " enable\t\t- Write 0/1 to enable/disable tracing of <event>\n" " filter\t\t- If set, only events passing filter are traced\n" " trigger\t\t- If set, a command to perform when event is hit\n" "\t Format: <trigger>[:count][if <filter>]\n" "\t trigger: traceon, traceoff\n" "\t enable_event:<system>:<event>\n" "\t disable_event:<system>:<event>\n" #ifdef CONFIG_HIST_TRIGGERS "\t enable_hist:<system>:<event>\n" "\t disable_hist:<system>:<event>\n" #endif #ifdef CONFIG_STACKTRACE "\t\t stacktrace\n" #endif #ifdef CONFIG_TRACER_SNAPSHOT "\t\t snapshot\n" #endif #ifdef CONFIG_HIST_TRIGGERS "\t\t hist (see below)\n" #endif "\t example: echo traceoff > events/block/block_unplug/trigger\n" "\t echo traceoff:3 > events/block/block_unplug/trigger\n" "\t echo 'enable_event:kmem:kmalloc:3 if nr_rq > 1' > \\\n" "\t events/block/block_unplug/trigger\n" "\t The first disables tracing every time block_unplug is hit.\n" "\t The second disables tracing the first 3 times block_unplug is hit.\n" "\t The third enables the kmalloc event the first 3 times block_unplug\n" "\t is hit and has value of greater than 1 for the 'nr_rq' event field.\n" "\t Like function triggers, the counter is only decremented if it\n" "\t enabled or disabled tracing.\n" "\t To remove a trigger without a count:\n" "\t echo '!<trigger> > <system>/<event>/trigger\n" "\t To remove a trigger with a count:\n" "\t echo '!<trigger>:0 > <system>/<event>/trigger\n" "\t Filters can be ignored when removing a trigger.\n" #ifdef CONFIG_HIST_TRIGGERS " hist trigger\t- If set, event hits are aggregated into a hash table\n" "\t Format: hist:keys=<field1[,field2,...]>\n" "\t [:<var1>=<field|var_ref|numeric_literal>[,<var2>=...]]\n" "\t [:values=<field1[,field2,...]>]\n" "\t [:sort=<field1[,field2,...]>]\n" "\t [:size=#entries]\n" "\t [:pause][:continue][:clear]\n" "\t [:name=histname1]\n" "\t [:nohitcount]\n" "\t [:<handler>.<action>]\n" "\t [if <filter>]\n\n" "\t Note, special fields can be used as well:\n" "\t common_timestamp - to record current timestamp\n" "\t common_cpu - to record the CPU the event happened on\n" "\n" "\t A hist trigger variable can be:\n" "\t - a reference to a field e.g. x=current_timestamp,\n" "\t - a reference to another variable e.g. y=$x,\n" "\t - a numeric literal: e.g. ms_per_sec=1000,\n" "\t - an arithmetic expression: e.g. time_secs=current_timestamp/1000\n" "\n" "\t hist trigger arithmetic expressions support addition(+), subtraction(-),\n" "\t multiplication(*) and division(/) operators. An operand can be either a\n" "\t variable reference, field or numeric literal.\n" "\n" "\t When a matching event is hit, an entry is added to a hash\n" "\t table using the key(s) and value(s) named, and the value of a\n" "\t sum called 'hitcount' is incremented. Keys and values\n" "\t correspond to fields in the event's format description. Keys\n" "\t can be any field, or the special string 'common_stacktrace'.\n" "\t Compound keys consisting of up to two fields can be specified\n" "\t by the 'keys' keyword. Values must correspond to numeric\n" "\t fields. Sort keys consisting of up to two fields can be\n" "\t specified using the 'sort' keyword. The sort direction can\n" "\t be modified by appending '.descending' or '.ascending' to a\n" "\t sort field. The 'size' parameter can be used to specify more\n" "\t or fewer than the default 2048 entries for the hashtable size.\n" "\t If a hist trigger is given a name using the 'name' parameter,\n" "\t its histogram data will be shared with other triggers of the\n" "\t same name, and trigger hits will update this common data.\n\n" "\t Reading the 'hist' file for the event will dump the hash\n" "\t table in its entirety to stdout. If there are multiple hist\n" "\t triggers attached to an event, there will be a table for each\n" "\t trigger in the output. The table displayed for a named\n" "\t trigger will be the same as any other instance having the\n" "\t same name. The default format used to display a given field\n" "\t can be modified by appending any of the following modifiers\n" "\t to the field name, as applicable:\n\n" "\t .hex display a number as a hex value\n" "\t .sym display an address as a symbol\n" "\t .sym-offset display an address as a symbol and offset\n" "\t .execname display a common_pid as a program name\n" "\t .syscall display a syscall id as a syscall name\n" "\t .log2 display log2 value rather than raw number\n" "\t .buckets=size display values in groups of size rather than raw number\n" "\t .usecs display a common_timestamp in microseconds\n" "\t .percent display a number of percentage value\n" "\t .graph display a bar-graph of a value\n\n" "\t The 'pause' parameter can be used to pause an existing hist\n" "\t trigger or to start a hist trigger but not log any events\n" "\t until told to do so. 'continue' can be used to start or\n" "\t restart a paused hist trigger.\n\n" "\t The 'clear' parameter will clear the contents of a running\n" "\t hist trigger and leave its current paused/active state\n" "\t unchanged.\n\n" "\t The 'nohitcount' (or NOHC) parameter will suppress display of\n" "\t raw hitcount in the histogram.\n\n" "\t The enable_hist and disable_hist triggers can be used to\n" "\t have one event conditionally start and stop another event's\n" "\t already-attached hist trigger. The syntax is analogous to\n" "\t the enable_event and disable_event triggers.\n\n" "\t Hist trigger handlers and actions are executed whenever a\n" "\t a histogram entry is added or updated. They take the form:\n\n" "\t <handler>.<action>\n\n" "\t The available handlers are:\n\n" "\t onmatch(matching.event) - invoke on addition or update\n" "\t onmax(var) - invoke if var exceeds current max\n" "\t onchange(var) - invoke action if var changes\n\n" "\t The available actions are:\n\n" "\t trace(<synthetic_event>,param list) - generate synthetic event\n" "\t save(field,...) - save current event fields\n" #ifdef CONFIG_TRACER_SNAPSHOT "\t snapshot() - snapshot the trace buffer\n\n" #endif #ifdef CONFIG_SYNTH_EVENTS " events/synthetic_events\t- Create/append/remove/show synthetic events\n" "\t Write into this file to define/undefine new synthetic events.\n" "\t example: echo 'myevent u64 lat; char name[]; long[] stack' >> synthetic_events\n" #endif #endif
;
/* Always run sanitizer only if btf_type_tag attr exists. */ if (len <= 0) { if (!(IS_ENABLED(CONFIG_DEBUG_INFO_BTF) &&
IS_ENABLED(CONFIG_PAHOLE_HAS_BTF_TAG) &&
__has_attribute(btf_type_tag))) return;
}
#ifdef CONFIG_TRACER_MAX_TRACE /* resize @tr's buffer to the size of @size_tr's entries */ staticint resize_buffer_duplicate_size(struct array_buffer *trace_buf, struct array_buffer *size_buf, int cpu_id)
{ int cpu, ret = 0;
if (cpu_id == RING_BUFFER_ALL_CPUS) {
for_each_tracing_cpu(cpu) {
ret = ring_buffer_resize(trace_buf->buffer,
per_cpu_ptr(size_buf->data, cpu)->entries, cpu); if (ret < 0) break;
per_cpu_ptr(trace_buf->data, cpu)->entries =
per_cpu_ptr(size_buf->data, cpu)->entries;
}
} else {
ret = ring_buffer_resize(trace_buf->buffer,
per_cpu_ptr(size_buf->data, cpu_id)->entries, cpu_id); if (ret == 0)
per_cpu_ptr(trace_buf->data, cpu_id)->entries =
per_cpu_ptr(size_buf->data, cpu_id)->entries;
}
return ret;
} #endif/* CONFIG_TRACER_MAX_TRACE */
staticint __tracing_resize_ring_buffer(struct trace_array *tr, unsignedlong size, int cpu)
{ int ret;
ssize_t tracing_resize_ring_buffer(struct trace_array *tr, unsignedlong size, int cpu_id)
{
guard(mutex)(&trace_types_lock);
if (cpu_id != RING_BUFFER_ALL_CPUS) { /* make sure, this cpu is enabled in the mask */ if (!cpumask_test_cpu(cpu_id, tracing_buffer_mask)) return -EINVAL;
}
if (tr->current_trace->reset)
tr->current_trace->reset(tr);
tr->current_trace = &nop_trace;
}
staticbool tracer_options_updated;
staticvoid add_tracer_options(struct trace_array *tr, struct tracer *t)
{ /* Only enable if the directory has been created already. */ if (!tr->dir && !(tr->flags & TRACE_ARRAY_FL_GLOBAL)) return;
/* Only create trace option files after update_tracer_options finish */ if (!tracer_options_updated) return;
create_trace_option_files(tr, t);
}
int tracing_set_tracer(struct trace_array *tr, constchar *buf)
{ struct tracer *t; #ifdef CONFIG_TRACER_MAX_TRACE bool had_max_tr; #endif int ret;
guard(mutex)(&trace_types_lock);
update_last_data(tr);
if (!tr->ring_buffer_expanded) {
ret = __tracing_resize_ring_buffer(tr, trace_buf_size,
RING_BUFFER_ALL_CPUS); if (ret < 0) return ret;
ret = 0;
}
for (t = trace_types; t; t = t->next) { if (strcmp(t->name, buf) == 0) break;
} if (!t) return -EINVAL;
if (t == tr->current_trace) return0;
#ifdef CONFIG_TRACER_SNAPSHOT if (t->use_max_tr) {
local_irq_disable();
arch_spin_lock(&tr->max_lock);
ret = tr->cond_snapshot ? -EBUSY : 0;
arch_spin_unlock(&tr->max_lock);
local_irq_enable(); if (ret) return ret;
} #endif /* Some tracers won't work on kernel command line */ if (system_state < SYSTEM_RUNNING && t->noboot) {
pr_warn("Tracer '%s' is not allowed on command line, ignored\n",
t->name); return -EINVAL;
}
/* Some tracers are only allowed for the top level buffer */ if (!trace_ok_for_array(t, tr)) return -EINVAL;
/* If trace pipe files are being read, we can't change the tracer */ if (tr->trace_ref) return -EBUSY;
trace_branch_disable();
tr->current_trace->enabled--;
if (tr->current_trace->reset)
tr->current_trace->reset(tr);
if (!had_max_tr && t->use_max_tr) {
ret = tracing_arm_snapshot_locked(tr); if (ret) return ret;
} #else
tr->current_trace = &nop_trace; #endif
if (t->init) {
ret = tracer_init(t, tr); if (ret) { #ifdef CONFIG_TRACER_MAX_TRACE if (t->use_max_tr)
tracing_disarm_snapshot(tr); #endif return ret;
}
}
staticint tracing_open_pipe(struct inode *inode, struct file *filp)
{ struct trace_array *tr = inode->i_private; struct trace_iterator *iter; int cpu; int ret;
ret = tracing_check_open_get_tr(tr); if (ret) return ret;
guard(mutex)(&trace_types_lock);
cpu = tracing_get_cpu(inode);
ret = open_pipe_on_cpu(tr, cpu); if (ret) goto fail_pipe_on_cpu;
/* create a buffer to store the information to pass to userspace */
iter = kzalloc(sizeof(*iter), GFP_KERNEL); if (!iter) {
ret = -ENOMEM; goto fail_alloc_iter;
}
if (!alloc_cpumask_var(&iter->started, GFP_KERNEL)) {
ret = -ENOMEM; goto fail;
}
/* trace pipe does not show start of buffer */
cpumask_setall(iter->started);
if (tr->trace_flags & TRACE_ITER_LATENCY_FMT)
iter->iter_flags |= TRACE_FILE_LAT_FMT;
/* Output in nanoseconds only if we are using a clock in nanoseconds. */ if (trace_clocks[tr->clock_id].in_ns)
iter->iter_flags |= TRACE_FILE_TIME_IN_NS;
/* Must be called with iter->mutex held. */ staticint tracing_wait_pipe(struct file *filp)
{ struct trace_iterator *iter = filp->private_data; int ret;
while (trace_empty(iter)) {
if ((filp->f_flags & O_NONBLOCK)) { return -EAGAIN;
}
/* return any leftover data */
sret = trace_seq_to_user(&iter->seq, ubuf, cnt); if (sret != -EBUSY) return sret;
trace_seq_init(&iter->seq);
if (iter->trace->read) {
sret = iter->trace->read(iter, filp, ubuf, cnt, ppos); if (sret) return sret;
}
waitagain: if (update_last_data_if_empty(iter->tr)) return0;
sret = tracing_wait_pipe(filp); if (sret <= 0) return sret;
/* stop when tracing is finished */ if (trace_empty(iter)) return0;
if (cnt >= TRACE_SEQ_BUFFER_SIZE)
cnt = TRACE_SEQ_BUFFER_SIZE - 1;
/* reset all but tr, trace, and overruns */
trace_iterator_reset(iter);
cpumask_clear(iter->started);
trace_seq_init(&iter->seq);
trace_event_read_lock();
trace_access_lock(iter->cpu_file); while (trace_find_next_entry_inc(iter) != NULL) { enum print_line_t ret; int save_len = iter->seq.seq.len;
ret = print_trace_line(iter); if (ret == TRACE_TYPE_PARTIAL_LINE) { /* *Ifoneprint_trace_line()fillsentiretrace_seqinoneshot, *trace_seq_to_user()willreturns-EBUSYbecausesave_len==0, *Inthiscase,weneedtoconsumeit,otherwise,loopwillpeek *thiseventnexttime,resultinginaninfiniteloop.
*/ if (save_len == 0) {
iter->seq.full = 0;
trace_seq_puts(&iter->seq, "[LINE TOO BIG]\n");
trace_consume(iter); break;
}
/* In other cases, don't print partial lines */
iter->seq.seq.len = save_len; break;
} if (ret != TRACE_TYPE_NO_CONSUME)
trace_consume(iter);
if (trace_seq_used(&iter->seq) >= cnt) break;
/* *Settingthefullflagmeanswereachedthetrace_seqbuffer *sizeandweshouldleavebypartialoutputconditionabove. *Oneofthetrace_seq_*functionsisnotusedproperly.
*/
WARN_ONCE(iter->seq.full, "full flag set for trace type %d",
iter->ent->type);
}
trace_access_unlock(iter->cpu_file);
trace_event_read_unlock();
/* Now copy what we have to the user */
sret = trace_seq_to_user(&iter->seq, ubuf, cnt); if (iter->seq.readpos >= trace_seq_used(&iter->seq))
trace_seq_init(&iter->seq);
/* *Iftherewasnothingtosendtouser,inspiteofconsumingtrace *entries,gobacktowaitformoreentries.
*/ if (sret == -EBUSY) goto waitagain;
/* Fill as many pages as possible. */ for (i = 0, rem = len; i < spd.nr_pages_max && rem; i++) {
spd.pages[i] = alloc_page(GFP_KERNEL); if (!spd.pages[i]) break;
rem = tracing_fill_pipe_page(rem, iter);
/* Copy the data into the page, so we can start over. */
ret = trace_seq_to_buffer(&iter->seq,
page_address(spd.pages[i]),
min((size_t)trace_seq_used(&iter->seq),
(size_t)PAGE_SIZE)); if (ret < 0) {
__free_page(spd.pages[i]); break;
}
spd.partial[i].offset = 0;
spd.partial[i].len = ret;
if (i)
ret = splice_to_pipe(pipe, &spd); else
ret = 0;
out:
splice_shrink_spd(&spd); return ret;
out_err:
mutex_unlock(&iter->mutex); goto out;
}
static ssize_t
tracing_entries_read(struct file *filp, char __user *ubuf,
size_t cnt, loff_t *ppos)
{ struct inode *inode = file_inode(filp); struct trace_array *tr = inode->i_private; int cpu = tracing_get_cpu(inode); char buf[64]; int r = 0;
ssize_t ret;
mutex_lock(&trace_types_lock);
if (cpu == RING_BUFFER_ALL_CPUS) { int cpu, buf_size_same; unsignedlong size;
size = 0;
buf_size_same = 1; /* check if all cpu sizes are same */
for_each_tracing_cpu(cpu) { /* fill in the size from first enabled cpu */ if (size == 0)
size = per_cpu_ptr(tr->array_buffer.data, cpu)->entries; if (size != per_cpu_ptr(tr->array_buffer.data, cpu)->entries) {
buf_size_same = 0; break;
}
}
if (buf_size_same) { if (!tr->ring_buffer_expanded)
r = sprintf(buf, "%lu (expanded: %lu)\n",
size >> 10,
trace_buf_size >> 10); else
r = sprintf(buf, "%lu\n", size >> 10);
} else
r = sprintf(buf, "X\n");
} else
r = sprintf(buf, "%lu\n", per_cpu_ptr(tr->array_buffer.data, cpu)->entries >> 10);
mutex_unlock(&trace_types_lock);
ret = simple_read_from_buffer(ubuf, cnt, ppos, buf, r); return ret;
}
/* Must be called with preemption disabled */ staticchar *trace_user_fault_read(struct trace_user_buf_info *tinfo, constchar __user *ptr, size_t size,
size_t *read_size)
{ int cpu = smp_processor_id(); char *buffer = per_cpu_ptr(tinfo->tbuf, cpu)->buf; unsignedint cnt; int trys = 0; int ret;
if (size > TRACE_MARKER_MAX_SIZE)
size = TRACE_MARKER_MAX_SIZE;
*read_size = 0;
do { /* *Ifforsomereason,copy_from_user()alwayscausesacontext *switch,thiswouldthencauseaninfiniteloop. *Ifthistaskispreemptedbyanotheruserspacetask,it *willcausethistasktotryagain.Butjustincasesomething *changeswherethecopyingfromuserspacecausesanothertask *torun,preventthisfromgoingintoaninfiniteloop. *100triesshouldbeplenty.
*/ if (WARN_ONCE(trys++ > 100, "Error: Too many tries to read user space")) return NULL;
/* Read the current CPU context switch counter */
cnt = nr_context_switches_cpu(cpu);
if (!(tr->trace_flags & TRACE_ITER_MARKERS)) return -EINVAL;
if ((ssize_t)cnt < 0) return -EINVAL;
if (cnt > TRACE_MARKER_MAX_SIZE)
cnt = TRACE_MARKER_MAX_SIZE;
/* Must have preemption disabled while having access to the buffer */
guard(preempt_notrace)();
buf = trace_user_fault_read(trace_user_buffer, ubuf, cnt, &size); if (!buf) return -EFAULT;
if (cnt > size)
cnt = size;
/* The selftests expect this function to be the IP address */
ip = _THIS_IP_;
/* The global trace_marker can go to multiple instances */ if (tr == &global_trace) {
guard(rcu)();
list_for_each_entry_rcu(tr, &marker_copies, marker_list) {
written = write_marker_to_buffer(tr, buf, cnt, ip); if (written < 0) break;
}
} else {
written = write_marker_to_buffer(tr, buf, cnt, ip);
}
/* cnt includes both the entry->id and the data behind it. */
size = struct_size(entry, buf, cnt - sizeof(entry->id));
buffer = tr->array_buffer.buffer;
if (size > ring_buffer_max_event_size(buffer)) return -EINVAL;
event = __trace_buffer_lock_reserve(buffer, TRACE_RAW_DATA, size,
tracing_gen_ctx()); if (!event) /* Ring buffer disabled, return as if not open for write */ return -EBADF;
entry = ring_buffer_event_data(event);
unsafe_memcpy(&entry->id, buf, cnt, "id and content already reserved on ring buffer" "'buf' includes the 'id' and the data." "'entry' was allocated with cnt from 'id'.");
written = cnt;
if (!(tr->trace_flags & TRACE_ITER_MARKERS)) return -EINVAL;
/* The marker must at least have a tag id */ if (cnt < sizeof(unsignedint)) return -EINVAL;
/* Must have preemption disabled while having access to the buffer */
guard(preempt_notrace)();
buf = trace_user_fault_read(trace_user_buffer, ubuf, cnt, &size); if (!buf) return -EFAULT;
/* raw write is all or nothing */ if (cnt > size) return -EINVAL;
/* The global trace_marker_raw can go to multiple instances */ if (tr == &global_trace) {
guard(rcu)();
list_for_each_entry_rcu(tr, &marker_copies, marker_list) {
written = write_raw_marker_to_buffer(tr, buf, cnt); if (written < 0) break;
}
} else {
written = write_raw_marker_to_buffer(tr, buf, cnt);
}
return written;
}
staticint tracing_mark_open(struct inode *inode, struct file *filp)
{ int ret;
ret = trace_user_fault_buffer_enable(); if (ret < 0) return ret;
stream_open(inode, filp);
ret = tracing_open_generic_tr(inode, filp); if (ret < 0)
trace_user_fault_buffer_disable(); return ret;
}
for (i = 0; i < ARRAY_SIZE(trace_clocks); i++)
seq_printf(m, "%s%s%s%s", i ? " " : "",
i == tr->clock_id ? "[" : "", trace_clocks[i].name,
i == tr->clock_id ? "]" : "");
seq_putc(m, '\n');
return 0;
}
int tracing_set_clock(struct trace_array *tr, const char *clockstr)
{
int i;
for (i = 0; i < ARRAY_SIZE(trace_clocks); i++) {
if (strcmp(trace_clocks[i].name, clockstr) == 0)
break;
}
if (i == ARRAY_SIZE(trace_clocks))
return -EINVAL;
/*
* New clock may not be consistent with the previous clock.
* Reset the buffer so that it doesn't have incomparable timestamps.
*/
tracing_reset_online_cpus(&tr->array_buffer);
#ifdef CONFIG_TRACER_MAX_TRACE
if (tr->max_buffer.buffer)
ring_buffer_set_clock(tr->max_buffer.buffer, trace_clocks[i].func);
tracing_reset_online_cpus(&tr->max_buffer);
#endif
/*
* Set or disable using the per CPU trace_buffer_event when possible.
*/
int tracing_set_filter_buffering(struct trace_array *tr, bool set)
{
guard(mutex)(&trace_types_lock);
if (set && tr->no_filter_buffering_ref++)
return 0;
if (!set) {
if (WARN_ON_ONCE(!tr->no_filter_buffering_ref))
return -EINVAL;
--tr->no_filter_buffering_ref;
}
return 0;
}
struct ftrace_buffer_info {
struct trace_iterator iter;
void *spare;
unsigned int spare_cpu;
unsigned int spare_size;
unsigned int read;
};
ret = tracing_check_open_get_tr(tr);
if (ret)
return ret;
if (file->f_mode & FMODE_READ) {
iter = __tracing_open(inode, file, true);
if (IS_ERR(iter))
ret = PTR_ERR(iter);
} else {
/* Writes still need the seq_file to hold the private data */
ret = -ENOMEM;
m = kzalloc(sizeof(*m), GFP_KERNEL);
if (!m)
goto out;
iter = kzalloc(sizeof(*iter), GFP_KERNEL);
if (!iter) {
kfree(m);
goto out;
}
ret = 0;
ret = tracing_update_buffers(tr);
if (ret < 0)
return ret;
ret = kstrtoul_from_user(ubuf, cnt, 10, &val);
if (ret)
return ret;
guard(mutex)(&trace_types_lock);
if (tr->current_trace->use_max_tr)
return -EBUSY;
local_irq_disable();
arch_spin_lock(&tr->max_lock);
if (tr->cond_snapshot)
ret = -EBUSY;
arch_spin_unlock(&tr->max_lock);
local_irq_enable();
if (ret)
return ret;
switch (val) {
case 0:
if (iter->cpu_file != RING_BUFFER_ALL_CPUS)
return -EINVAL;
if (tr->allocated_snapshot)
free_snapshot(tr);
break;
case 1:
/* Only allow per-cpu swap if the ring buffer supports it */
#ifndef CONFIG_RING_BUFFER_ALLOW_SWAP
if (iter->cpu_file != RING_BUFFER_ALL_CPUS)
return -EINVAL;
#endif
if (tr->allocated_snapshot)
ret = resize_buffer_duplicate_size(&tr->max_buffer,
&tr->array_buffer, iter->cpu_file);
ret = tracing_arm_snapshot_locked(tr);
if (ret)
return ret;
/*
* trace_min_max_write - Write a u64 value to a trace_min_max_param struct
* @filp: The active open file structure
* @ubuf: The userspace provided buffer to read value into
* @cnt: The maximum number of bytes to read
* @ppos: The current "file" position
*
* This function implements the write interface for a struct trace_min_max_param.
* The filp->private_data must point to a trace_min_max_param structure that
* defines where to write the value, the min and the max acceptable values,
* and a lock to protect the write.
*/
static ssize_t
trace_min_max_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos)
{
struct trace_min_max_param *param = filp->private_data;
u64 val;
int err;
if (!param)
return -EFAULT;
err = kstrtoull_from_user(ubuf, cnt, 10, &val);
if (err)
return err;
if (param->lock)
mutex_lock(param->lock);
if (param->min && val < *param->min)
err = -EINVAL;
if (param->max && val > *param->max)
err = -EINVAL;
if (!err)
*param->val = val;
if (param->lock)
mutex_unlock(param->lock);
if (err)
return err;
return cnt;
}
/*
* trace_min_max_read - Read a u64 value from a trace_min_max_param struct
* @filp: The active open file structure
* @ubuf: The userspace provided buffer to read value into
* @cnt: The maximum number of bytes to read
* @ppos: The current "file" position
*
* This function implements the read interface for a struct trace_min_max_param.
* The filp->private_data must point to a trace_min_max_param struct with valid
* data.
*/
static ssize_t
trace_min_max_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos)
{
struct trace_min_max_param *param = filp->private_data;
char buf[U64_STR_SIZE];
int len;
u64 val;
/**
* err_pos - find the position of a string within a command for error careting
* @cmd: The tracing command that caused the error
* @str: The string to position the caret at within @cmd
*
* Finds the position of the first occurrence of @str within @cmd. The
* return value can be passed to tracing_log_err() for caret placement
* within @cmd.
*
* Returns the index within @cmd of the first occurrence of @str or 0
* if @str was not found.
*/
unsigned int err_pos(char *cmd, const char *str)
{
char *found;
if (WARN_ON(!strlen(cmd)))
return 0;
found = strstr(cmd, str);
if (found)
return found - cmd;
return 0;
}
/**
* tracing_log_err - write an error to the tracing error log
* @tr: The associated trace array for the error (NULL for top level array)
* @loc: A string describing where the error occurred
* @cmd: The tracing command that caused the error
* @errs: The array of loc-specific static error strings
* @type: The index into errs[], which produces the specific static err string
* @pos: The position the caret should be placed in the cmd
*
* Writes an error into tracing/error_log of the form:
*
* <loc>: error: <text>
* Command: <cmd>
* ^
*
* tracing/error_log is a small log file containing the last
* TRACING_LOG_ERRS_MAX errors (8). Memory for errors isn't allocated
* unless there has been a tracing error, and the error log can be
* cleared and have its memory freed by writing the empty string in
* truncation mode to it i.e. echo > tracing/error_log.
*
* NOTE: the @errs array along with the @type param are used to
* produce a static error string - this string is not copied and saved
* when the error is logged - only a pointer to it is saved. See
* existing callers for examples of how static strings are typically
* defined for use with tracing_log_err().
*/
void tracing_log_err(struct trace_array *tr,
const char *loc, const char *cmd,
const char **errs, u8 type, u16 pos)
{
struct tracing_log_err *err;
int len = 0;
/* Make sure the spare matches the current sub buffer size */
if (info->spare) {
if (page_size != info->spare_size) {
ring_buffer_free_read_page(iter->array_buffer->buffer,
info->spare_cpu, info->spare);
info->spare = NULL;
}
}
if (!info->spare) {
info->spare = ring_buffer_alloc_read_page(iter->array_buffer->buffer,
iter->cpu_file);
if (IS_ERR(info->spare)) {
ret = PTR_ERR(info->spare);
info->spare = NULL;
} else {
info->spare_cpu = iter->cpu_file;
info->spare_size = page_size;
}
}
if (!info->spare)
return ret;
/* Do we have previous read data to read? */
if (info->read < page_size)
goto read;
again:
trace_access_lock(iter->cpu_file);
ret = ring_buffer_read_page(iter->array_buffer->buffer,
info->spare,
count,
iter->cpu_file, 0);
trace_access_unlock(iter->cpu_file);
if (ret < 0) {
if (trace_empty(iter) && !iter->closed) {
if (update_last_data_if_empty(iter->tr))
return 0;
/*
* Callback from splice_to_pipe(), if we need to release some pages
* at the end of the spd in case we error'ed out in filling the pipe.
*/
static void buffer_spd_release(struct splice_pipe_desc *spd, unsigned int i)
{
struct buffer_ref *ref =
(struct buffer_ref *)spd->partial[i].private;
#ifdef CONFIG_TRACER_MAX_TRACE
static int get_snapshot_map(struct trace_array *tr)
{
int err = 0;
/*
* Called with mmap_lock held. lockdep would be unhappy if we would now
* take trace_types_lock. Instead use the specific
* snapshot_trigger_lock.
*/
spin_lock(&tr->snapshot_trigger_lock);
if (trace_clocks[tr->clock_id].in_ns) {
/* local or global for trace_clock */
t = ns2usecs(ring_buffer_oldest_event_ts(trace_buf->buffer, cpu));
usec_rem = do_div(t, USEC_PER_SEC);
trace_seq_printf(s, "oldest event ts: %5llu.%06lu\n",
t, usec_rem);
/*
* In order to pass in both the trace_array descriptor as well as the index
* to the flag that the trace option file represents, the trace_array
* has a character array of trace_flags_index[], which holds the index
* of the bit for the flag it represents. index[0] == 0, index[1] == 1, etc.
* The address of this character array is passed to the flag option file
* read/write callbacks.
*
* In order to extract both the index and the trace_array descriptor,
* get_tr_index() uses the following algorithm.
*
* idx = *ptr;
*
* As the pointer itself contains the address of the index (remember
* index[1] == 1).
*
* Then to get the trace_array descriptor, by subtracting that index
* from the ptr, we get to the start of the index itself.
*
* ptr - idx == &index[0]
*
* Then a simple container_of() from that pointer gets us to the
* trace_array descriptor.
*/
static void get_tr_index(void *data, struct trace_array **ptr,
unsigned int *pindex)
{
*pindex = *(unsigned char *)data;
/*
* If this is an instance, only create flags for tracers
* the instance may have.
*/
if (!trace_ok_for_array(tracer, tr))
return;
for (i = 0; i < tr->nr_topts; i++) {
/* Make sure there's no duplicate flags. */
if (WARN_ON_ONCE(tr->topts[i].tracer->flags == tracer->flags))
return;
}
opts = flags->opts;
for (cnt = 0; opts[cnt].name; cnt++)
;
topts = kcalloc(cnt + 1, sizeof(*topts), GFP_KERNEL);
if (!topts)
return;
ret = kstrtoul_from_user(ubuf, cnt, 10, &val);
if (ret)
return ret;
if (buffer) {
guard(mutex)(&trace_types_lock);
if (!!val == tracer_tracing_is_on(tr)) {
val = 0; /* do nothing */
} else if (val) {
tracer_tracing_on(tr);
if (tr->current_trace->start)
tr->current_trace->start(tr);
} else {
tracer_tracing_off(tr);
if (tr->current_trace->stop)
tr->current_trace->stop(tr);
/* Wake up any waiters */
ring_buffer_wake_waiters(buffer, RING_BUFFER_ALL_CPUS);
}
}
static ssize_t
buffer_subbuf_size_write(struct file *filp, const char __user *ubuf,
size_t cnt, loff_t *ppos)
{
struct trace_array *tr = filp->private_data;
unsigned long val;
int old_order;
int order;
int pages;
int ret;
ret = kstrtoul_from_user(ubuf, cnt, 10, &val);
if (ret)
return ret;
val *= 1024; /* value passed in is in KB */
pages = DIV_ROUND_UP(val, PAGE_SIZE);
order = fls(pages - 1);
/* limit between 1 and 128 system pages */
if (order < 0 || order > 7)
return -EINVAL;
/* Do not allow tracing while changing the order of the ring buffer */
tracing_stop_tr(tr);
old_order = ring_buffer_subbuf_order_get(tr->array_buffer.buffer);
if (old_order == order)
goto out;
ret = ring_buffer_subbuf_order_set(tr->array_buffer.buffer, order);
if (ret)
goto out;
#ifdef CONFIG_TRACER_MAX_TRACE
if (!tr->allocated_snapshot)
goto out_max;
ret = ring_buffer_subbuf_order_set(tr->max_buffer.buffer, order);
if (ret) {
/* Put back the old order */
cnt = ring_buffer_subbuf_order_set(tr->array_buffer.buffer, old_order);
if (WARN_ON_ONCE(cnt)) {
/*
* AARGH! We are left with different orders!
* The max buffer is our "snapshot" buffer.
* When a tracer needs a snapshot (one of the
* latency tracers), it swaps the max buffer
* with the saved snap shot. We succeeded to
* update the order of the main buffer, but failed to
* update the order of the max buffer. But when we tried
* to reset the main buffer to the original size, we
* failed there too. This is very unlikely to
* happen, but if it does, warn and kill all
* tracing.
*/
tracing_disabled = 1;
}
goto out;
}
out_max:
#endif
(*ppos)++;
out:
if (ret)
cnt = ret;
tracing_start_tr(tr);
return cnt;
}
static void setup_trace_scratch(struct trace_array *tr,
struct trace_scratch *tscratch, unsigned int size)
{
struct trace_module_delta *module_delta;
struct trace_mod_entry *entry;
int i, nr_entries;
if (!tscratch)
return;
tr->scratch = tscratch;
tr->scratch_size = size;
if (tscratch->text_addr)
tr->text_delta = (unsigned long)_text - tscratch->text_addr;
if (struct_size(tscratch, entries, tscratch->nr_entries) > size)
goto reset;
/* Check if each module name is a valid string */
for (i = 0; i < tscratch->nr_entries; i++) {
int n;
entry = &tscratch->entries[i];
for (n = 0; n < MODULE_NAME_LEN; n++) {
if (entry->mod_name[n] == '\0')
break;
if (!isprint(entry->mod_name[n]))
goto reset;
}
if (n == MODULE_NAME_LEN)
goto reset;
}
/* Sort the entries so that we can find appropriate module from address. */
nr_entries = tscratch->nr_entries;
sort_r(tscratch->entries, nr_entries, sizeof(struct trace_mod_entry),
mod_addr_comp, NULL, NULL);
if (IS_ENABLED(CONFIG_MODULES)) {
module_delta = kzalloc(struct_size(module_delta, delta, nr_entries), GFP_KERNEL);
if (!module_delta) {
pr_info("module_delta allocation failed. Not able to decode module address.");
goto reset;
}
init_rcu_head(&module_delta->rcu);
} else
module_delta = NULL;
WRITE_ONCE(tr->module_delta, module_delta);
/* Scan modules to make text delta for modules. */
module_for_each_mod(make_mod_delta, tr);
/* Set trace_clock as the same of the previous boot. */
if (tscratch->clock_id != tr->clock_id) {
if (tscratch->clock_id >= ARRAY_SIZE(trace_clocks) ||
tracing_set_clock(tr, trace_clocks[tscratch->clock_id].name) < 0) {
pr_info("the previous trace_clock info is not valid.");
goto reset;
}
}
return;
reset:
/* Invalid trace modules */
memset(tscratch, 0, size);
}
static int
allocate_trace_buffer(struct trace_array *tr, struct array_buffer *buf, int size)
{
enum ring_buffer_flags rb_flags;
struct trace_scratch *tscratch;
unsigned int scratch_size = 0;
/*
* This is basically the same as a mapped buffer,
* with the same restrictions.
*/
tr->mapped++;
} else {
buf->buffer = ring_buffer_alloc(size, rb_flags);
}
if (!buf->buffer)
return -ENOMEM;
static int __remove_instance(struct trace_array *tr)
{
int i;
/* Reference counter for a newly created trace array = 1. */ if (tr->ref > 1 || (tr->current_trace && tr->trace_ref)) return -EBUSY;
list_del(&tr->list);
/* Disable all the flags that were enabled coming in */
for (i = 0; i < TRACE_FLAGS_MAX_SIZE; i++) { if ((1 << i) & ZEROED_TRACE_FLAGS)
set_tracer_flag(tr, 1 << i, 0);
}
if (printk_trace == tr)
update_printk_trace(&global_trace);
if (update_marker_trace(tr, 0))
synchronize_rcu();
static void __init eval_map_work_func(struct work_struct *work)
{
int len;
len = __stop_ftrace_eval_maps - __start_ftrace_eval_maps;
trace_event_update_with_eval_map(NULL, __start_ftrace_eval_maps, len);
}
static int __init trace_eval_init(void)
{
INIT_WORK(&eval_map_work, eval_map_work_func);
eval_map_wq = alloc_workqueue("eval_map_wq", WQ_UNBOUND, 0); if (!eval_map_wq) {
pr_err("Unable to allocate eval_map_wq\n"); /* Do work here */
eval_map_work_func(&eval_map_work); return -ENOMEM;
}
static int __init trace_eval_sync(void)
{ /* Make sure the eval map updates are finished */ if (eval_map_wq)
destroy_workqueue(eval_map_wq); return0;
}
late_initcall_sync(trace_eval_sync);
#ifdef CONFIG_MODULES
bool module_exists(const char *module)
{ /* All modules have the symbol __this_module */ staticconst char this_mod[] = "__this_module";
char modname[MODULE_NAME_LEN + sizeof(this_mod) + 2];
unsigned long val;
int n;
n = snprintf(modname, sizeof(modname), "%s:%s", module, this_mod);
if (n > sizeof(modname) - 1) returnfalse;
val = module_kallsyms_lookup_name(modname); return val != 0;
}
if (iter->trace && iter->trace->open)
iter->trace->open(iter);
/* Annotate start of buffers if we had overruns */ if (ring_buffer_overruns(iter->array_buffer->buffer))
iter->iter_flags |= TRACE_FILE_ANNOTATE;
/* Output in nanoseconds only if we are using a clock in nanoseconds. */ if (trace_clocks[iter->tr->clock_id].in_ns)
iter->iter_flags |= TRACE_FILE_TIME_IN_NS;
/* Can not use kmalloc for iter.temp and iter.fmt */
iter->temp = static_temp_buf;
iter->temp_size = STATIC_TEMP_BUF_SIZE;
iter->fmt = static_fmt_buf;
iter->fmt_size = STATIC_FMT_BUF_SIZE;
}
static void ftrace_dump_one(struct trace_array *tr, enum ftrace_dump_mode dump_mode)
{ /* use static because iter can be a bit big for the stack */ staticstruct trace_iterator iter;
unsigned int old_userobj;
unsigned long flags;
int cnt = 0;
/* Did function tracer already get disabled? */ if (ftrace_is_dead()) {
printk("# WARNING: FUNCTION TRACING IS CORRUPTED\n");
printk("# MAY BE MISSING FUNCTION EVENTS\n");
}
tok = addr_delim; if (tok && isdigit(*tok)) {
start = memparse(tok, &tok); if (!start) {
pr_warn("Tracing: Invalid boot instance address for %s\n",
name); continue;
} if (*tok != ':') {
pr_warn("Tracing: No size specified for instance %s\n", name); continue;
}
tok++;
size = memparse(tok, &tok); if (!size) {
pr_warn("Tracing: Invalid boot instance size for %s\n",
name); continue;
}
memmap_area = true;
} elseif (tok) { if (!reserve_mem_find_by_name(tok, &start, &size)) {
start = 0;
pr_warn("Failed to map boot instance %s to %s\n", name, tok); continue;
}
rname = kstrdup(tok, GFP_KERNEL);
}
if (start) { /* Start and size must be page aligned */ if (start & ~PAGE_MASK) {
pr_warn("Tracing: mapping start addr %pa is not page aligned\n", &start); continue;
} if (size & ~PAGE_MASK) {
pr_warn("Tracing: mapping size %pa is not page aligned\n", &size); continue;
}
if (memmap_area)
addr = map_pages(start, size); else
addr = (unsigned long)phys_to_virt(start); if (addr) {
pr_info("Tracing: mapped boot instance %s at physical memory %pa of size 0x%lx\n",
name, &start, (unsigned long)size);
} else {
pr_warn("Tracing: Failed to map boot instance %s\n", name); continue;
}
} else { /* Only non mapped buffers have snapshot buffers */ if (IS_ENABLED(CONFIG_TRACER_MAX_TRACE))
do_allocate_snapshot(name);
}
tr = trace_array_create_systems(name, NULL, addr, size); if (IS_ERR(tr)) {
pr_warn("Tracing: Failed to create instance buffer %s\n", curr_str); continue;
}
if (traceoff)
tracer_tracing_off(tr);
if (traceprintk)
update_printk_trace(tr);
/*
* memmap'd buffers can not be freed.
*/
if (memmap_area) {
tr->flags |= TRACE_ARRAY_FL_MEMMAP;
tr->ref++;
}
__init static int tracer_alloc_buffers(void)
{
int ring_buf_size;
int ret = -ENOMEM;
if (security_locked_down(LOCKDOWN_TRACEFS)) {
pr_warn("Tracing disabled due to lockdown\n");
return -EPERM;
}
/*
* Make sure we don't accidentally add more trace options
* than we have bits for.
*/
BUILD_BUG_ON(TRACE_ITER_LAST_BIT > TRACE_FLAGS_MAX_SIZE);
if (!alloc_cpumask_var(&tracing_buffer_mask, GFP_KERNEL))
return -ENOMEM;
if (!alloc_cpumask_var(&global_trace.tracing_cpumask, GFP_KERNEL))
goto out_free_buffer_mask;
/* Only allocate trace_printk buffers if a trace_printk exists */
if (&__stop___trace_bprintk_fmt != &__start___trace_bprintk_fmt)
/* Must be called before global_trace.buffer is allocated */
trace_printk_init_buffers();
/* To save memory, keep the ring buffer size to its minimum */
if (global_trace.ring_buffer_expanded)
ring_buf_size = trace_buf_size;
else
ring_buf_size = 1;
/*
* The prepare callbacks allocates some memory for the ring buffer. We
* don't free the buffer if the CPU goes down. If we were to free
* the buffer, then the user would lose any trace that was in the
* buffer. The memory will be removed once the "instance" is removed.
*/
ret = cpuhp_setup_state_multi(CPUHP_TRACE_RB_PREPARE,
"trace/RB:prepare", trace_rb_cpu_prepare,
NULL);
if (ret < 0)
goto out_free_cpumask;
/* Used for event triggers */
ret = -ENOMEM;
temp_buffer = ring_buffer_alloc(PAGE_SIZE, RB_FL_OVERWRITE);
if (!temp_buffer)
goto out_rm_hp_state;
if (trace_create_savedcmd() < 0)
goto out_free_temp_buffer;
if (!zalloc_cpumask_var(&global_trace.pipe_cpumask, GFP_KERNEL))
goto out_free_savedcmd;
/* TODO: make the number of buffers hot pluggable with CPUS */
if (allocate_trace_buffers(&global_trace, ring_buf_size) < 0) {
MEM_FAIL(1, "tracer: failed to allocate ring buffer!\n");
goto out_free_pipe_cpumask;
}
if (global_trace.buffer_disabled)
tracing_off();
if (trace_boot_clock) {
ret = tracing_set_clock(&global_trace, trace_boot_clock);
if (ret < 0)
pr_warn("Trace clock %s not defined, going back to default\n",
trace_boot_clock);
}
/*
* register_tracer() might reference current_trace, so it
* needs to be set before we register anything. This is
* just a bootstrap of current_trace anyway.
*/
global_trace.current_trace = &nop_trace;
#ifdef CONFIG_FUNCTION_TRACER
/* Used to set module cached ftrace filtering at boot up */
__init struct trace_array *trace_get_global_array(void)
{
return &global_trace;
}
#endif
__init static void clear_boot_tracer(void)
{
/*
* The default tracer at boot buffer is an init section.
* This function is called in lateinit. If we did not
* find the boot tracer, then clear it out, to prevent
* later registration from accessing the buffer that is
* about to be freed.
*/
if (!default_bootup_tracer)
return;
#ifdef CONFIG_HAVE_UNSTABLE_SCHED_CLOCK
__init static void tracing_set_default_clock(void)
{
/* sched_clock_stable() is determined in late_initcall */
if (!trace_boot_clock && !sched_clock_stable()) {
if (security_locked_down(LOCKDOWN_TRACEFS)) {
pr_warn("Can not set tracing clock due to lockdown\n");
return;
}
printk(KERN_WARNING
"Unstable clock detected, switching default tracing clock to \"global\"\n"
"If you want to keep using the local clock, then add:\n"
" \"trace_clock=local\"\n"
"on the kernel command line\n");
tracing_set_clock(&global_trace, "global");
}
}
#else
static inline void tracing_set_default_clock(void) { }
#endif
__init static int late_trace_init(void)
{
if (tracepoint_printk && tracepoint_printk_stop_on_boot) {
static_key_disable(&tracepoint_printk_key.key);
tracepoint_printk = 0;
}
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.575Bemerkung:
(vorverarbeitet am 2026-10-11)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.