mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2024-12-29 17:22:07 +00:00
New tracing features:
- The ring buffer is no longer disabled when reading the trace file. The trace_pipe file was made to be used for live tracing and reading as it acted like the normal producer/consumer. As the trace file would not consume the data, the easy way of handling it was to just disable writes to the ring buffer. This came to a surprise to the BPF folks who complained about lost events due to reading. This is no longer an issue. If someone wants to keep the old disabling there's a new option "pause-on-trace" that can be set. - New set_ftrace_notrace_pid file. PIDs in this file will not be traced by the function tracer. Similar to set_ftrace_pid, which makes the function tracer only trace those tasks with PIDs in the file, the set_ftrace_notrace_pid does the reverse. - New set_event_notrace_pid file. PIDs in this file will cause events not to be traced if triggered by a task with a matching PID. Similar to the set_event_pid file but will not be traced. Note, sched_waking and sched_switch events may still be trace if one of the tasks referenced by those events contains a PID that is allowed to be traced. Tracing related features: - New bootconfig option, that is attached to the initrd file. If bootconfig is on the command line, then the initrd file is searched looking for a bootconfig appended at the end. - New GPU tracepoint infrastructure to help the gfx drivers to get off debugfs (acked by Greg Kroah-Hartman) Other minor updates and fixes. -----BEGIN PGP SIGNATURE----- iIoEABYIADIWIQRRSw7ePDh/lE+zeZMp5XQQmuv6qgUCXokgWRQccm9zdGVkdEBn b29kbWlzLm9yZwAKCRAp5XQQmuv6qgrHAP0UkKs/52JY4oWa3OIh/OqK+vnCrIwz zGvDFOYM0fKbwgD9FZWgzlcaYK5m2Cxlhp4VoraZveHMLJUhnEHtdX6X0wk= =Rebj -----END PGP SIGNATURE----- Merge tag 'trace-v5.7' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace Pull tracing updates from Steven Rostedt: "New tracing features: - The ring buffer is no longer disabled when reading the trace file. The trace_pipe file was made to be used for live tracing and reading as it acted like the normal producer/consumer. As the trace file would not consume the data, the easy way of handling it was to just disable writes to the ring buffer. This came to a surprise to the BPF folks who complained about lost events due to reading. This is no longer an issue. If someone wants to keep the old disabling there's a new option "pause-on-trace" that can be set. - New set_ftrace_notrace_pid file. PIDs in this file will not be traced by the function tracer. Similar to set_ftrace_pid, which makes the function tracer only trace those tasks with PIDs in the file, the set_ftrace_notrace_pid does the reverse. - New set_event_notrace_pid file. PIDs in this file will cause events not to be traced if triggered by a task with a matching PID. Similar to the set_event_pid file but will not be traced. Note, sched_waking and sched_switch events may still be traced if one of the tasks referenced by those events contains a PID that is allowed to be traced. Tracing related features: - New bootconfig option, that is attached to the initrd file. If bootconfig is on the command line, then the initrd file is searched looking for a bootconfig appended at the end. - New GPU tracepoint infrastructure to help the gfx drivers to get off debugfs (acked by Greg Kroah-Hartman) And other minor updates and fixes" * tag 'trace-v5.7' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace: (27 commits) tracing: Do not allocate buffer in trace_find_next_entry() in atomic tracing: Add documentation on set_ftrace_notrace_pid and set_event_notrace_pid selftests/ftrace: Add test to test new set_event_notrace_pid file selftests/ftrace: Add test to test new set_ftrace_notrace_pid file tracing: Create set_event_notrace_pid to not trace tasks ftrace: Create set_ftrace_notrace_pid to not trace tasks ftrace: Make function trace pid filtering a bit more exact ftrace/kprobe: Show the maxactive number on kprobe_events tracing: Have the document reflect that the trace file keeps tracing enabled ring-buffer/tracing: Have iterator acknowledge dropped events tracing: Do not disable tracing when reading the trace file ring-buffer: Do not disable recording when there is an iterator ring-buffer: Make resize disable per cpu buffer instead of total buffer ring-buffer: Optimize rb_iter_head_event() ring-buffer: Do not die if rb_iter_peek() fails more than thrice ring-buffer: Have rb_iter_head_event() handle concurrent writer ring-buffer: Add page_stamp to iterator for synchronization ring-buffer: Rename ring_buffer_read() to read_buffer_iter_advance() ring-buffer: Have ring_buffer_empty() not depend on tracing stopped tracing: Save off entry when peeking at next entry ...
This commit is contained in:
commit
aa1a8ce533
@ -125,10 +125,13 @@ of ftrace. Here is a list of some of the key files:
|
||||
trace:
|
||||
|
||||
This file holds the output of the trace in a human
|
||||
readable format (described below). Note, tracing is temporarily
|
||||
disabled when the file is open for reading. Once all readers
|
||||
are closed, tracing is re-enabled. Opening this file for
|
||||
readable format (described below). Opening this file for
|
||||
writing with the O_TRUNC flag clears the ring buffer content.
|
||||
Note, this file is not a consumer. If tracing is off
|
||||
(no tracer running, or tracing_on is zero), it will produce
|
||||
the same output each time it is read. When tracing is on,
|
||||
it may produce inconsistent results as it tries to read
|
||||
the entire buffer without consuming it.
|
||||
|
||||
trace_pipe:
|
||||
|
||||
@ -142,9 +145,7 @@ of ftrace. Here is a list of some of the key files:
|
||||
will not be read again with a sequential read. The
|
||||
"trace" file is static, and if the tracer is not
|
||||
adding more data, it will display the same
|
||||
information every time it is read. Unlike the
|
||||
"trace" file, opening this file for reading will not
|
||||
temporarily disable tracing.
|
||||
information every time it is read.
|
||||
|
||||
trace_options:
|
||||
|
||||
@ -262,6 +263,20 @@ of ftrace. Here is a list of some of the key files:
|
||||
traced by the function tracer as well. This option will also
|
||||
cause PIDs of tasks that exit to be removed from the file.
|
||||
|
||||
set_ftrace_notrace_pid:
|
||||
|
||||
Have the function tracer ignore threads whose PID are listed in
|
||||
this file.
|
||||
|
||||
If the "function-fork" option is set, then when a task whose
|
||||
PID is listed in this file forks, the child's PID will
|
||||
automatically be added to this file, and the child will not be
|
||||
traced by the function tracer as well. This option will also
|
||||
cause PIDs of tasks that exit to be removed from the file.
|
||||
|
||||
If a PID is in both this file and "set_ftrace_pid", then this
|
||||
file takes precedence, and the thread will not be traced.
|
||||
|
||||
set_event_pid:
|
||||
|
||||
Have the events only trace a task with a PID listed in this file.
|
||||
@ -273,6 +288,19 @@ of ftrace. Here is a list of some of the key files:
|
||||
cause the PIDs of tasks to be removed from this file when the task
|
||||
exits.
|
||||
|
||||
set_event_notrace_pid:
|
||||
|
||||
Have the events not trace a task with a PID listed in this file.
|
||||
Note, sched_switch and sched_wakeup will trace threads not listed
|
||||
in this file, even if a thread's PID is in the file if the
|
||||
sched_switch or sched_wakeup events also trace a thread that should
|
||||
be traced.
|
||||
|
||||
To have the PIDs of children of tasks with their PID in this file
|
||||
added on fork, enable the "event-fork" option. That option will also
|
||||
cause the PIDs of tasks to be removed from this file when the task
|
||||
exits.
|
||||
|
||||
set_graph_function:
|
||||
|
||||
Functions listed in this file will cause the function graph
|
||||
@ -1125,6 +1153,12 @@ Here are the available options:
|
||||
the trace displays additional information about the
|
||||
latency, as described in "Latency trace format".
|
||||
|
||||
pause-on-trace
|
||||
When set, opening the trace file for read, will pause
|
||||
writing to the ring buffer (as if tracing_on was set to zero).
|
||||
This simulates the original behavior of the trace file.
|
||||
When the file is closed, tracing will be enabled again.
|
||||
|
||||
record-cmd
|
||||
When any event or tracer is enabled, a hook is enabled
|
||||
in the sched_switch trace point to fill comm cache
|
||||
@ -1176,6 +1210,8 @@ Here are the available options:
|
||||
tasks fork. Also, when tasks with PIDs in set_event_pid exit,
|
||||
their PIDs will be removed from the file.
|
||||
|
||||
This affects PIDs listed in set_event_notrace_pid as well.
|
||||
|
||||
function-trace
|
||||
The latency tracers will enable function tracing
|
||||
if this option is enabled (default it is). When
|
||||
@ -1190,6 +1226,8 @@ Here are the available options:
|
||||
set_ftrace_pid exit, their PIDs will be removed from the
|
||||
file.
|
||||
|
||||
This affects PIDs in set_ftrace_notrace_pid as well.
|
||||
|
||||
display-graph
|
||||
When set, the latency tracers (irqsoff, wakeup, etc) will
|
||||
use function graph tracing instead of function tracing.
|
||||
@ -2126,6 +2164,8 @@ periodically make a CPU constantly busy with interrupts disabled.
|
||||
# cat trace
|
||||
# tracer: hwlat
|
||||
#
|
||||
# entries-in-buffer/entries-written: 13/13 #P:8
|
||||
#
|
||||
# _-----=> irqs-off
|
||||
# / _----=> need-resched
|
||||
# | / _---=> hardirq/softirq
|
||||
@ -2133,12 +2173,18 @@ periodically make a CPU constantly busy with interrupts disabled.
|
||||
# ||| / delay
|
||||
# TASK-PID CPU# |||| TIMESTAMP FUNCTION
|
||||
# | | | |||| | |
|
||||
<...>-3638 [001] d... 19452.055471: #1 inner/outer(us): 12/14 ts:1499801089.066141940
|
||||
<...>-3638 [003] d... 19454.071354: #2 inner/outer(us): 11/9 ts:1499801091.082164365
|
||||
<...>-3638 [002] dn.. 19461.126852: #3 inner/outer(us): 12/9 ts:1499801098.138150062
|
||||
<...>-3638 [001] d... 19488.340960: #4 inner/outer(us): 8/12 ts:1499801125.354139633
|
||||
<...>-3638 [003] d... 19494.388553: #5 inner/outer(us): 8/12 ts:1499801131.402150961
|
||||
<...>-3638 [003] d... 19501.283419: #6 inner/outer(us): 0/12 ts:1499801138.297435289 nmi-total:4 nmi-count:1
|
||||
<...>-1729 [001] d... 678.473449: #1 inner/outer(us): 11/12 ts:1581527483.343962693 count:6
|
||||
<...>-1729 [004] d... 689.556542: #2 inner/outer(us): 16/9 ts:1581527494.889008092 count:1
|
||||
<...>-1729 [005] d... 714.756290: #3 inner/outer(us): 16/16 ts:1581527519.678961629 count:5
|
||||
<...>-1729 [001] d... 718.788247: #4 inner/outer(us): 9/17 ts:1581527523.889012713 count:1
|
||||
<...>-1729 [002] d... 719.796341: #5 inner/outer(us): 13/9 ts:1581527524.912872606 count:1
|
||||
<...>-1729 [006] d... 844.787091: #6 inner/outer(us): 9/12 ts:1581527649.889048502 count:2
|
||||
<...>-1729 [003] d... 849.827033: #7 inner/outer(us): 18/9 ts:1581527654.889013793 count:1
|
||||
<...>-1729 [007] d... 853.859002: #8 inner/outer(us): 9/12 ts:1581527658.889065736 count:1
|
||||
<...>-1729 [001] d... 855.874978: #9 inner/outer(us): 9/11 ts:1581527660.861991877 count:1
|
||||
<...>-1729 [001] d... 863.938932: #10 inner/outer(us): 9/11 ts:1581527668.970010500 count:1 nmi-total:7 nmi-count:1
|
||||
<...>-1729 [007] d... 878.050780: #11 inner/outer(us): 9/12 ts:1581527683.385002600 count:1 nmi-total:5 nmi-count:1
|
||||
<...>-1729 [007] d... 886.114702: #12 inner/outer(us): 9/12 ts:1581527691.385001600 count:1
|
||||
|
||||
|
||||
The above output is somewhat the same in the header. All events will have
|
||||
@ -2148,7 +2194,7 @@ interrupts disabled 'd'. Under the FUNCTION title there is:
|
||||
This is the count of events recorded that were greater than the
|
||||
tracing_threshold (See below).
|
||||
|
||||
inner/outer(us): 12/14
|
||||
inner/outer(us): 11/11
|
||||
|
||||
This shows two numbers as "inner latency" and "outer latency". The test
|
||||
runs in a loop checking a timestamp twice. The latency detected within
|
||||
@ -2156,11 +2202,15 @@ interrupts disabled 'd'. Under the FUNCTION title there is:
|
||||
after the previous timestamp and the next timestamp in the loop is
|
||||
the "outer latency".
|
||||
|
||||
ts:1499801089.066141940
|
||||
ts:1581527483.343962693
|
||||
|
||||
The absolute timestamp that the event happened.
|
||||
The absolute timestamp that the first latency was recorded in the window.
|
||||
|
||||
nmi-total:4 nmi-count:1
|
||||
count:6
|
||||
|
||||
The number of times a latency was detected during the window.
|
||||
|
||||
nmi-total:7 nmi-count:1
|
||||
|
||||
On architectures that support it, if an NMI comes in during the
|
||||
test, the time spent in NMI is reported in "nmi-total" (in
|
||||
|
@ -200,6 +200,8 @@ source "drivers/thunderbolt/Kconfig"
|
||||
|
||||
source "drivers/android/Kconfig"
|
||||
|
||||
source "drivers/gpu/trace/Kconfig"
|
||||
|
||||
source "drivers/nvdimm/Kconfig"
|
||||
|
||||
source "drivers/dax/Kconfig"
|
||||
|
@ -5,3 +5,4 @@
|
||||
obj-$(CONFIG_TEGRA_HOST1X) += host1x/
|
||||
obj-y += drm/ vga/
|
||||
obj-$(CONFIG_IMX_IPUV3_CORE) += ipu-v3/
|
||||
obj-$(CONFIG_TRACE_GPU_MEM) += trace/
|
||||
|
4
drivers/gpu/trace/Kconfig
Normal file
4
drivers/gpu/trace/Kconfig
Normal file
@ -0,0 +1,4 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
config TRACE_GPU_MEM
|
||||
bool
|
3
drivers/gpu/trace/Makefile
Normal file
3
drivers/gpu/trace/Makefile
Normal file
@ -0,0 +1,3 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
obj-$(CONFIG_TRACE_GPU_MEM) += trace_gpu_mem.o
|
13
drivers/gpu/trace/trace_gpu_mem.c
Normal file
13
drivers/gpu/trace/trace_gpu_mem.c
Normal file
@ -0,0 +1,13 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* GPU memory trace points
|
||||
*
|
||||
* Copyright (C) 2020 Google, Inc.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include <trace/events/gpu_mem.h>
|
||||
|
||||
EXPORT_TRACEPOINT_SYMBOL(gpu_mem_total);
|
@ -216,7 +216,8 @@ static inline int __init xbc_node_compose_key(struct xbc_node *node,
|
||||
}
|
||||
|
||||
/* XBC node initializer */
|
||||
int __init xbc_init(char *buf);
|
||||
int __init xbc_init(char *buf, const char **emsg, int *epos);
|
||||
|
||||
|
||||
/* XBC cleanup data structures */
|
||||
void __init xbc_destroy_all(void);
|
||||
|
@ -135,10 +135,10 @@ void ring_buffer_read_finish(struct ring_buffer_iter *iter);
|
||||
|
||||
struct ring_buffer_event *
|
||||
ring_buffer_iter_peek(struct ring_buffer_iter *iter, u64 *ts);
|
||||
struct ring_buffer_event *
|
||||
ring_buffer_read(struct ring_buffer_iter *iter, u64 *ts);
|
||||
void ring_buffer_iter_advance(struct ring_buffer_iter *iter);
|
||||
void ring_buffer_iter_reset(struct ring_buffer_iter *iter);
|
||||
int ring_buffer_iter_empty(struct ring_buffer_iter *iter);
|
||||
bool ring_buffer_iter_dropped(struct ring_buffer_iter *iter);
|
||||
|
||||
unsigned long ring_buffer_size(struct trace_buffer *buffer, int cpu);
|
||||
|
||||
|
@ -85,6 +85,8 @@ struct trace_iterator {
|
||||
struct mutex mutex;
|
||||
struct ring_buffer_iter **buffer_iter;
|
||||
unsigned long iter_flags;
|
||||
void *temp; /* temp holder */
|
||||
unsigned int temp_size;
|
||||
|
||||
/* trace_seq for __print_flags() and __print_symbolic() etc. */
|
||||
struct trace_seq tmp_seq;
|
||||
|
57
include/trace/events/gpu_mem.h
Normal file
57
include/trace/events/gpu_mem.h
Normal file
@ -0,0 +1,57 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* GPU memory trace points
|
||||
*
|
||||
* Copyright (C) 2020 Google, Inc.
|
||||
*/
|
||||
|
||||
#undef TRACE_SYSTEM
|
||||
#define TRACE_SYSTEM gpu_mem
|
||||
|
||||
#if !defined(_TRACE_GPU_MEM_H) || defined(TRACE_HEADER_MULTI_READ)
|
||||
#define _TRACE_GPU_MEM_H
|
||||
|
||||
#include <linux/tracepoint.h>
|
||||
|
||||
/*
|
||||
* The gpu_memory_total event indicates that there's an update to either the
|
||||
* global or process total gpu memory counters.
|
||||
*
|
||||
* This event should be emitted whenever the kernel device driver allocates,
|
||||
* frees, imports, unimports memory in the GPU addressable space.
|
||||
*
|
||||
* @gpu_id: This is the gpu id.
|
||||
*
|
||||
* @pid: Put 0 for global total, while positive pid for process total.
|
||||
*
|
||||
* @size: Virtual size of the allocation in bytes.
|
||||
*
|
||||
*/
|
||||
TRACE_EVENT(gpu_mem_total,
|
||||
|
||||
TP_PROTO(uint32_t gpu_id, uint32_t pid, uint64_t size),
|
||||
|
||||
TP_ARGS(gpu_id, pid, size),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(uint32_t, gpu_id)
|
||||
__field(uint32_t, pid)
|
||||
__field(uint64_t, size)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->gpu_id = gpu_id;
|
||||
__entry->pid = pid;
|
||||
__entry->size = size;
|
||||
),
|
||||
|
||||
TP_printk("gpu_id=%u pid=%u size=%llu",
|
||||
__entry->gpu_id,
|
||||
__entry->pid,
|
||||
__entry->size)
|
||||
);
|
||||
|
||||
#endif /* _TRACE_GPU_MEM_H */
|
||||
|
||||
/* This part must be outside protection */
|
||||
#include <trace/define_trace.h>
|
14
init/main.c
14
init/main.c
@ -353,6 +353,8 @@ static int __init bootconfig_params(char *param, char *val,
|
||||
static void __init setup_boot_config(const char *cmdline)
|
||||
{
|
||||
static char tmp_cmdline[COMMAND_LINE_SIZE] __initdata;
|
||||
const char *msg;
|
||||
int pos;
|
||||
u32 size, csum;
|
||||
char *data, *copy;
|
||||
u32 *hdr;
|
||||
@ -400,10 +402,14 @@ static void __init setup_boot_config(const char *cmdline)
|
||||
memcpy(copy, data, size);
|
||||
copy[size] = '\0';
|
||||
|
||||
ret = xbc_init(copy);
|
||||
if (ret < 0)
|
||||
pr_err("Failed to parse bootconfig\n");
|
||||
else {
|
||||
ret = xbc_init(copy, &msg, &pos);
|
||||
if (ret < 0) {
|
||||
if (pos < 0)
|
||||
pr_err("Failed to init bootconfig: %s.\n", msg);
|
||||
else
|
||||
pr_err("Failed to parse bootconfig: %s at %d.\n",
|
||||
msg, pos);
|
||||
} else {
|
||||
pr_info("Load bootconfig: %d bytes %d nodes\n", size, ret);
|
||||
/* keys starting with "kernel." are passed via cmdline */
|
||||
extra_command_line = xbc_make_cmdline("kernel");
|
||||
|
@ -102,7 +102,7 @@ static bool ftrace_pids_enabled(struct ftrace_ops *ops)
|
||||
|
||||
tr = ops->private;
|
||||
|
||||
return tr->function_pids != NULL;
|
||||
return tr->function_pids != NULL || tr->function_no_pids != NULL;
|
||||
}
|
||||
|
||||
static void ftrace_update_trampoline(struct ftrace_ops *ops);
|
||||
@ -139,13 +139,23 @@ static inline void ftrace_ops_init(struct ftrace_ops *ops)
|
||||
#endif
|
||||
}
|
||||
|
||||
#define FTRACE_PID_IGNORE -1
|
||||
#define FTRACE_PID_TRACE -2
|
||||
|
||||
static void ftrace_pid_func(unsigned long ip, unsigned long parent_ip,
|
||||
struct ftrace_ops *op, struct pt_regs *regs)
|
||||
{
|
||||
struct trace_array *tr = op->private;
|
||||
int pid;
|
||||
|
||||
if (tr && this_cpu_read(tr->array_buffer.data->ftrace_ignore_pid))
|
||||
return;
|
||||
if (tr) {
|
||||
pid = this_cpu_read(tr->array_buffer.data->ftrace_ignore_pid);
|
||||
if (pid == FTRACE_PID_IGNORE)
|
||||
return;
|
||||
if (pid != FTRACE_PID_TRACE &&
|
||||
pid != current->pid)
|
||||
return;
|
||||
}
|
||||
|
||||
op->saved_func(ip, parent_ip, op, regs);
|
||||
}
|
||||
@ -6923,11 +6933,17 @@ ftrace_filter_pid_sched_switch_probe(void *data, bool preempt,
|
||||
{
|
||||
struct trace_array *tr = data;
|
||||
struct trace_pid_list *pid_list;
|
||||
struct trace_pid_list *no_pid_list;
|
||||
|
||||
pid_list = rcu_dereference_sched(tr->function_pids);
|
||||
no_pid_list = rcu_dereference_sched(tr->function_no_pids);
|
||||
|
||||
this_cpu_write(tr->array_buffer.data->ftrace_ignore_pid,
|
||||
trace_ignore_this_task(pid_list, next));
|
||||
if (trace_ignore_this_task(pid_list, no_pid_list, next))
|
||||
this_cpu_write(tr->array_buffer.data->ftrace_ignore_pid,
|
||||
FTRACE_PID_IGNORE);
|
||||
else
|
||||
this_cpu_write(tr->array_buffer.data->ftrace_ignore_pid,
|
||||
next->pid);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -6940,6 +6956,9 @@ ftrace_pid_follow_sched_process_fork(void *data,
|
||||
|
||||
pid_list = rcu_dereference_sched(tr->function_pids);
|
||||
trace_filter_add_remove_task(pid_list, self, task);
|
||||
|
||||
pid_list = rcu_dereference_sched(tr->function_no_pids);
|
||||
trace_filter_add_remove_task(pid_list, self, task);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -6950,6 +6969,9 @@ ftrace_pid_follow_sched_process_exit(void *data, struct task_struct *task)
|
||||
|
||||
pid_list = rcu_dereference_sched(tr->function_pids);
|
||||
trace_filter_add_remove_task(pid_list, NULL, task);
|
||||
|
||||
pid_list = rcu_dereference_sched(tr->function_no_pids);
|
||||
trace_filter_add_remove_task(pid_list, NULL, task);
|
||||
}
|
||||
|
||||
void ftrace_pid_follow_fork(struct trace_array *tr, bool enable)
|
||||
@ -6967,42 +6989,57 @@ void ftrace_pid_follow_fork(struct trace_array *tr, bool enable)
|
||||
}
|
||||
}
|
||||
|
||||
static void clear_ftrace_pids(struct trace_array *tr)
|
||||
static void clear_ftrace_pids(struct trace_array *tr, int type)
|
||||
{
|
||||
struct trace_pid_list *pid_list;
|
||||
struct trace_pid_list *no_pid_list;
|
||||
int cpu;
|
||||
|
||||
pid_list = rcu_dereference_protected(tr->function_pids,
|
||||
lockdep_is_held(&ftrace_lock));
|
||||
if (!pid_list)
|
||||
no_pid_list = rcu_dereference_protected(tr->function_no_pids,
|
||||
lockdep_is_held(&ftrace_lock));
|
||||
|
||||
/* Make sure there's something to do */
|
||||
if (!pid_type_enabled(type, pid_list, no_pid_list))
|
||||
return;
|
||||
|
||||
unregister_trace_sched_switch(ftrace_filter_pid_sched_switch_probe, tr);
|
||||
/* See if the pids still need to be checked after this */
|
||||
if (!still_need_pid_events(type, pid_list, no_pid_list)) {
|
||||
unregister_trace_sched_switch(ftrace_filter_pid_sched_switch_probe, tr);
|
||||
for_each_possible_cpu(cpu)
|
||||
per_cpu_ptr(tr->array_buffer.data, cpu)->ftrace_ignore_pid = FTRACE_PID_TRACE;
|
||||
}
|
||||
|
||||
for_each_possible_cpu(cpu)
|
||||
per_cpu_ptr(tr->array_buffer.data, cpu)->ftrace_ignore_pid = false;
|
||||
if (type & TRACE_PIDS)
|
||||
rcu_assign_pointer(tr->function_pids, NULL);
|
||||
|
||||
rcu_assign_pointer(tr->function_pids, NULL);
|
||||
if (type & TRACE_NO_PIDS)
|
||||
rcu_assign_pointer(tr->function_no_pids, NULL);
|
||||
|
||||
/* Wait till all users are no longer using pid filtering */
|
||||
synchronize_rcu();
|
||||
|
||||
trace_free_pid_list(pid_list);
|
||||
if ((type & TRACE_PIDS) && pid_list)
|
||||
trace_free_pid_list(pid_list);
|
||||
|
||||
if ((type & TRACE_NO_PIDS) && no_pid_list)
|
||||
trace_free_pid_list(no_pid_list);
|
||||
}
|
||||
|
||||
void ftrace_clear_pids(struct trace_array *tr)
|
||||
{
|
||||
mutex_lock(&ftrace_lock);
|
||||
|
||||
clear_ftrace_pids(tr);
|
||||
clear_ftrace_pids(tr, TRACE_PIDS | TRACE_NO_PIDS);
|
||||
|
||||
mutex_unlock(&ftrace_lock);
|
||||
}
|
||||
|
||||
static void ftrace_pid_reset(struct trace_array *tr)
|
||||
static void ftrace_pid_reset(struct trace_array *tr, int type)
|
||||
{
|
||||
mutex_lock(&ftrace_lock);
|
||||
clear_ftrace_pids(tr);
|
||||
clear_ftrace_pids(tr, type);
|
||||
|
||||
ftrace_update_pid_func();
|
||||
ftrace_startup_all(0);
|
||||
@ -7066,9 +7103,45 @@ static const struct seq_operations ftrace_pid_sops = {
|
||||
.show = fpid_show,
|
||||
};
|
||||
|
||||
static int
|
||||
ftrace_pid_open(struct inode *inode, struct file *file)
|
||||
static void *fnpid_start(struct seq_file *m, loff_t *pos)
|
||||
__acquires(RCU)
|
||||
{
|
||||
struct trace_pid_list *pid_list;
|
||||
struct trace_array *tr = m->private;
|
||||
|
||||
mutex_lock(&ftrace_lock);
|
||||
rcu_read_lock_sched();
|
||||
|
||||
pid_list = rcu_dereference_sched(tr->function_no_pids);
|
||||
|
||||
if (!pid_list)
|
||||
return !(*pos) ? FTRACE_NO_PIDS : NULL;
|
||||
|
||||
return trace_pid_start(pid_list, pos);
|
||||
}
|
||||
|
||||
static void *fnpid_next(struct seq_file *m, void *v, loff_t *pos)
|
||||
{
|
||||
struct trace_array *tr = m->private;
|
||||
struct trace_pid_list *pid_list = rcu_dereference_sched(tr->function_no_pids);
|
||||
|
||||
if (v == FTRACE_NO_PIDS) {
|
||||
(*pos)++;
|
||||
return NULL;
|
||||
}
|
||||
return trace_pid_next(pid_list, v, pos);
|
||||
}
|
||||
|
||||
static const struct seq_operations ftrace_no_pid_sops = {
|
||||
.start = fnpid_start,
|
||||
.next = fnpid_next,
|
||||
.stop = fpid_stop,
|
||||
.show = fpid_show,
|
||||
};
|
||||
|
||||
static int pid_open(struct inode *inode, struct file *file, int type)
|
||||
{
|
||||
const struct seq_operations *seq_ops;
|
||||
struct trace_array *tr = inode->i_private;
|
||||
struct seq_file *m;
|
||||
int ret = 0;
|
||||
@ -7079,9 +7152,18 @@ ftrace_pid_open(struct inode *inode, struct file *file)
|
||||
|
||||
if ((file->f_mode & FMODE_WRITE) &&
|
||||
(file->f_flags & O_TRUNC))
|
||||
ftrace_pid_reset(tr);
|
||||
ftrace_pid_reset(tr, type);
|
||||
|
||||
ret = seq_open(file, &ftrace_pid_sops);
|
||||
switch (type) {
|
||||
case TRACE_PIDS:
|
||||
seq_ops = &ftrace_pid_sops;
|
||||
break;
|
||||
case TRACE_NO_PIDS:
|
||||
seq_ops = &ftrace_no_pid_sops;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = seq_open(file, seq_ops);
|
||||
if (ret < 0) {
|
||||
trace_array_put(tr);
|
||||
} else {
|
||||
@ -7093,10 +7175,23 @@ ftrace_pid_open(struct inode *inode, struct file *file)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
ftrace_pid_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return pid_open(inode, file, TRACE_PIDS);
|
||||
}
|
||||
|
||||
static int
|
||||
ftrace_no_pid_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return pid_open(inode, file, TRACE_NO_PIDS);
|
||||
}
|
||||
|
||||
static void ignore_task_cpu(void *data)
|
||||
{
|
||||
struct trace_array *tr = data;
|
||||
struct trace_pid_list *pid_list;
|
||||
struct trace_pid_list *no_pid_list;
|
||||
|
||||
/*
|
||||
* This function is called by on_each_cpu() while the
|
||||
@ -7104,18 +7199,25 @@ static void ignore_task_cpu(void *data)
|
||||
*/
|
||||
pid_list = rcu_dereference_protected(tr->function_pids,
|
||||
mutex_is_locked(&ftrace_lock));
|
||||
no_pid_list = rcu_dereference_protected(tr->function_no_pids,
|
||||
mutex_is_locked(&ftrace_lock));
|
||||
|
||||
this_cpu_write(tr->array_buffer.data->ftrace_ignore_pid,
|
||||
trace_ignore_this_task(pid_list, current));
|
||||
if (trace_ignore_this_task(pid_list, no_pid_list, current))
|
||||
this_cpu_write(tr->array_buffer.data->ftrace_ignore_pid,
|
||||
FTRACE_PID_IGNORE);
|
||||
else
|
||||
this_cpu_write(tr->array_buffer.data->ftrace_ignore_pid,
|
||||
current->pid);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
ftrace_pid_write(struct file *filp, const char __user *ubuf,
|
||||
size_t cnt, loff_t *ppos)
|
||||
pid_write(struct file *filp, const char __user *ubuf,
|
||||
size_t cnt, loff_t *ppos, int type)
|
||||
{
|
||||
struct seq_file *m = filp->private_data;
|
||||
struct trace_array *tr = m->private;
|
||||
struct trace_pid_list *filtered_pids = NULL;
|
||||
struct trace_pid_list *filtered_pids;
|
||||
struct trace_pid_list *other_pids;
|
||||
struct trace_pid_list *pid_list;
|
||||
ssize_t ret;
|
||||
|
||||
@ -7124,19 +7226,39 @@ ftrace_pid_write(struct file *filp, const char __user *ubuf,
|
||||
|
||||
mutex_lock(&ftrace_lock);
|
||||
|
||||
filtered_pids = rcu_dereference_protected(tr->function_pids,
|
||||
switch (type) {
|
||||
case TRACE_PIDS:
|
||||
filtered_pids = rcu_dereference_protected(tr->function_pids,
|
||||
lockdep_is_held(&ftrace_lock));
|
||||
other_pids = rcu_dereference_protected(tr->function_no_pids,
|
||||
lockdep_is_held(&ftrace_lock));
|
||||
break;
|
||||
case TRACE_NO_PIDS:
|
||||
filtered_pids = rcu_dereference_protected(tr->function_no_pids,
|
||||
lockdep_is_held(&ftrace_lock));
|
||||
other_pids = rcu_dereference_protected(tr->function_pids,
|
||||
lockdep_is_held(&ftrace_lock));
|
||||
break;
|
||||
}
|
||||
|
||||
ret = trace_pid_write(filtered_pids, &pid_list, ubuf, cnt);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
rcu_assign_pointer(tr->function_pids, pid_list);
|
||||
switch (type) {
|
||||
case TRACE_PIDS:
|
||||
rcu_assign_pointer(tr->function_pids, pid_list);
|
||||
break;
|
||||
case TRACE_NO_PIDS:
|
||||
rcu_assign_pointer(tr->function_no_pids, pid_list);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (filtered_pids) {
|
||||
synchronize_rcu();
|
||||
trace_free_pid_list(filtered_pids);
|
||||
} else if (pid_list) {
|
||||
} else if (pid_list && !other_pids) {
|
||||
/* Register a probe to set whether to ignore the tracing of a task */
|
||||
register_trace_sched_switch(ftrace_filter_pid_sched_switch_probe, tr);
|
||||
}
|
||||
@ -7159,6 +7281,20 @@ ftrace_pid_write(struct file *filp, const char __user *ubuf,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
ftrace_pid_write(struct file *filp, const char __user *ubuf,
|
||||
size_t cnt, loff_t *ppos)
|
||||
{
|
||||
return pid_write(filp, ubuf, cnt, ppos, TRACE_PIDS);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
ftrace_no_pid_write(struct file *filp, const char __user *ubuf,
|
||||
size_t cnt, loff_t *ppos)
|
||||
{
|
||||
return pid_write(filp, ubuf, cnt, ppos, TRACE_NO_PIDS);
|
||||
}
|
||||
|
||||
static int
|
||||
ftrace_pid_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
@ -7177,10 +7313,20 @@ static const struct file_operations ftrace_pid_fops = {
|
||||
.release = ftrace_pid_release,
|
||||
};
|
||||
|
||||
static const struct file_operations ftrace_no_pid_fops = {
|
||||
.open = ftrace_no_pid_open,
|
||||
.write = ftrace_no_pid_write,
|
||||
.read = seq_read,
|
||||
.llseek = tracing_lseek,
|
||||
.release = ftrace_pid_release,
|
||||
};
|
||||
|
||||
void ftrace_init_tracefs(struct trace_array *tr, struct dentry *d_tracer)
|
||||
{
|
||||
trace_create_file("set_ftrace_pid", 0644, d_tracer,
|
||||
tr, &ftrace_pid_fops);
|
||||
trace_create_file("set_ftrace_notrace_pid", 0644, d_tracer,
|
||||
tr, &ftrace_no_pid_fops);
|
||||
}
|
||||
|
||||
void __init ftrace_init_tracefs_toplevel(struct trace_array *tr,
|
||||
|
@ -441,6 +441,7 @@ enum {
|
||||
struct ring_buffer_per_cpu {
|
||||
int cpu;
|
||||
atomic_t record_disabled;
|
||||
atomic_t resize_disabled;
|
||||
struct trace_buffer *buffer;
|
||||
raw_spinlock_t reader_lock; /* serialize readers */
|
||||
arch_spinlock_t lock;
|
||||
@ -484,7 +485,6 @@ struct trace_buffer {
|
||||
unsigned flags;
|
||||
int cpus;
|
||||
atomic_t record_disabled;
|
||||
atomic_t resize_disabled;
|
||||
cpumask_var_t cpumask;
|
||||
|
||||
struct lock_class_key *reader_lock_key;
|
||||
@ -503,10 +503,14 @@ struct trace_buffer {
|
||||
struct ring_buffer_iter {
|
||||
struct ring_buffer_per_cpu *cpu_buffer;
|
||||
unsigned long head;
|
||||
unsigned long next_event;
|
||||
struct buffer_page *head_page;
|
||||
struct buffer_page *cache_reader_page;
|
||||
unsigned long cache_read;
|
||||
u64 read_stamp;
|
||||
u64 page_stamp;
|
||||
struct ring_buffer_event *event;
|
||||
int missed_events;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -1737,18 +1741,24 @@ int ring_buffer_resize(struct trace_buffer *buffer, unsigned long size,
|
||||
|
||||
size = nr_pages * BUF_PAGE_SIZE;
|
||||
|
||||
/*
|
||||
* Don't succeed if resizing is disabled, as a reader might be
|
||||
* manipulating the ring buffer and is expecting a sane state while
|
||||
* this is true.
|
||||
*/
|
||||
if (atomic_read(&buffer->resize_disabled))
|
||||
return -EBUSY;
|
||||
|
||||
/* prevent another thread from changing buffer sizes */
|
||||
mutex_lock(&buffer->mutex);
|
||||
|
||||
|
||||
if (cpu_id == RING_BUFFER_ALL_CPUS) {
|
||||
/*
|
||||
* Don't succeed if resizing is disabled, as a reader might be
|
||||
* manipulating the ring buffer and is expecting a sane state while
|
||||
* this is true.
|
||||
*/
|
||||
for_each_buffer_cpu(buffer, cpu) {
|
||||
cpu_buffer = buffer->buffers[cpu];
|
||||
if (atomic_read(&cpu_buffer->resize_disabled)) {
|
||||
err = -EBUSY;
|
||||
goto out_err_unlock;
|
||||
}
|
||||
}
|
||||
|
||||
/* calculate the pages to update */
|
||||
for_each_buffer_cpu(buffer, cpu) {
|
||||
cpu_buffer = buffer->buffers[cpu];
|
||||
@ -1816,6 +1826,16 @@ int ring_buffer_resize(struct trace_buffer *buffer, unsigned long size,
|
||||
if (nr_pages == cpu_buffer->nr_pages)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Don't succeed if resizing is disabled, as a reader might be
|
||||
* manipulating the ring buffer and is expecting a sane state while
|
||||
* this is true.
|
||||
*/
|
||||
if (atomic_read(&cpu_buffer->resize_disabled)) {
|
||||
err = -EBUSY;
|
||||
goto out_err_unlock;
|
||||
}
|
||||
|
||||
cpu_buffer->nr_pages_to_update = nr_pages -
|
||||
cpu_buffer->nr_pages;
|
||||
|
||||
@ -1885,6 +1905,7 @@ int ring_buffer_resize(struct trace_buffer *buffer, unsigned long size,
|
||||
free_buffer_page(bpage);
|
||||
}
|
||||
}
|
||||
out_err_unlock:
|
||||
mutex_unlock(&buffer->mutex);
|
||||
return err;
|
||||
}
|
||||
@ -1913,17 +1934,65 @@ rb_reader_event(struct ring_buffer_per_cpu *cpu_buffer)
|
||||
cpu_buffer->reader_page->read);
|
||||
}
|
||||
|
||||
static __always_inline struct ring_buffer_event *
|
||||
rb_iter_head_event(struct ring_buffer_iter *iter)
|
||||
{
|
||||
return __rb_page_index(iter->head_page, iter->head);
|
||||
}
|
||||
|
||||
static __always_inline unsigned rb_page_commit(struct buffer_page *bpage)
|
||||
{
|
||||
return local_read(&bpage->page->commit);
|
||||
}
|
||||
|
||||
static struct ring_buffer_event *
|
||||
rb_iter_head_event(struct ring_buffer_iter *iter)
|
||||
{
|
||||
struct ring_buffer_event *event;
|
||||
struct buffer_page *iter_head_page = iter->head_page;
|
||||
unsigned long commit;
|
||||
unsigned length;
|
||||
|
||||
if (iter->head != iter->next_event)
|
||||
return iter->event;
|
||||
|
||||
/*
|
||||
* When the writer goes across pages, it issues a cmpxchg which
|
||||
* is a mb(), which will synchronize with the rmb here.
|
||||
* (see rb_tail_page_update() and __rb_reserve_next())
|
||||
*/
|
||||
commit = rb_page_commit(iter_head_page);
|
||||
smp_rmb();
|
||||
event = __rb_page_index(iter_head_page, iter->head);
|
||||
length = rb_event_length(event);
|
||||
|
||||
/*
|
||||
* READ_ONCE() doesn't work on functions and we don't want the
|
||||
* compiler doing any crazy optimizations with length.
|
||||
*/
|
||||
barrier();
|
||||
|
||||
if ((iter->head + length) > commit || length > BUF_MAX_DATA_SIZE)
|
||||
/* Writer corrupted the read? */
|
||||
goto reset;
|
||||
|
||||
memcpy(iter->event, event, length);
|
||||
/*
|
||||
* If the page stamp is still the same after this rmb() then the
|
||||
* event was safely copied without the writer entering the page.
|
||||
*/
|
||||
smp_rmb();
|
||||
|
||||
/* Make sure the page didn't change since we read this */
|
||||
if (iter->page_stamp != iter_head_page->page->time_stamp ||
|
||||
commit > rb_page_commit(iter_head_page))
|
||||
goto reset;
|
||||
|
||||
iter->next_event = iter->head + length;
|
||||
return iter->event;
|
||||
reset:
|
||||
/* Reset to the beginning */
|
||||
iter->page_stamp = iter->read_stamp = iter->head_page->page->time_stamp;
|
||||
iter->head = 0;
|
||||
iter->next_event = 0;
|
||||
iter->missed_events = 1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Size is determined by what has been committed */
|
||||
static __always_inline unsigned rb_page_size(struct buffer_page *bpage)
|
||||
{
|
||||
@ -1959,8 +2028,9 @@ static void rb_inc_iter(struct ring_buffer_iter *iter)
|
||||
else
|
||||
rb_inc_page(cpu_buffer, &iter->head_page);
|
||||
|
||||
iter->read_stamp = iter->head_page->page->time_stamp;
|
||||
iter->page_stamp = iter->read_stamp = iter->head_page->page->time_stamp;
|
||||
iter->head = 0;
|
||||
iter->next_event = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -3547,14 +3617,18 @@ static void rb_iter_reset(struct ring_buffer_iter *iter)
|
||||
/* Iterator usage is expected to have record disabled */
|
||||
iter->head_page = cpu_buffer->reader_page;
|
||||
iter->head = cpu_buffer->reader_page->read;
|
||||
iter->next_event = iter->head;
|
||||
|
||||
iter->cache_reader_page = iter->head_page;
|
||||
iter->cache_read = cpu_buffer->read;
|
||||
|
||||
if (iter->head)
|
||||
if (iter->head) {
|
||||
iter->read_stamp = cpu_buffer->read_stamp;
|
||||
else
|
||||
iter->page_stamp = cpu_buffer->reader_page->page->time_stamp;
|
||||
} else {
|
||||
iter->read_stamp = iter->head_page->page->time_stamp;
|
||||
iter->page_stamp = iter->read_stamp;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -3590,17 +3664,38 @@ int ring_buffer_iter_empty(struct ring_buffer_iter *iter)
|
||||
struct buffer_page *reader;
|
||||
struct buffer_page *head_page;
|
||||
struct buffer_page *commit_page;
|
||||
struct buffer_page *curr_commit_page;
|
||||
unsigned commit;
|
||||
u64 curr_commit_ts;
|
||||
u64 commit_ts;
|
||||
|
||||
cpu_buffer = iter->cpu_buffer;
|
||||
|
||||
/* Remember, trace recording is off when iterator is in use */
|
||||
reader = cpu_buffer->reader_page;
|
||||
head_page = cpu_buffer->head_page;
|
||||
commit_page = cpu_buffer->commit_page;
|
||||
commit = rb_page_commit(commit_page);
|
||||
commit_ts = commit_page->page->time_stamp;
|
||||
|
||||
return ((iter->head_page == commit_page && iter->head == commit) ||
|
||||
/*
|
||||
* When the writer goes across pages, it issues a cmpxchg which
|
||||
* is a mb(), which will synchronize with the rmb here.
|
||||
* (see rb_tail_page_update())
|
||||
*/
|
||||
smp_rmb();
|
||||
commit = rb_page_commit(commit_page);
|
||||
/* We want to make sure that the commit page doesn't change */
|
||||
smp_rmb();
|
||||
|
||||
/* Make sure commit page didn't change */
|
||||
curr_commit_page = READ_ONCE(cpu_buffer->commit_page);
|
||||
curr_commit_ts = READ_ONCE(curr_commit_page->page->time_stamp);
|
||||
|
||||
/* If the commit page changed, then there's more data */
|
||||
if (curr_commit_page != commit_page ||
|
||||
curr_commit_ts != commit_ts)
|
||||
return 0;
|
||||
|
||||
/* Still racy, as it may return a false positive, but that's OK */
|
||||
return ((iter->head_page == commit_page && iter->head >= commit) ||
|
||||
(iter->head_page == reader && commit_page == head_page &&
|
||||
head_page->read == commit &&
|
||||
iter->head == rb_page_commit(cpu_buffer->reader_page)));
|
||||
@ -3828,15 +3923,22 @@ static void rb_advance_reader(struct ring_buffer_per_cpu *cpu_buffer)
|
||||
static void rb_advance_iter(struct ring_buffer_iter *iter)
|
||||
{
|
||||
struct ring_buffer_per_cpu *cpu_buffer;
|
||||
struct ring_buffer_event *event;
|
||||
unsigned length;
|
||||
|
||||
cpu_buffer = iter->cpu_buffer;
|
||||
|
||||
/* If head == next_event then we need to jump to the next event */
|
||||
if (iter->head == iter->next_event) {
|
||||
/* If the event gets overwritten again, there's nothing to do */
|
||||
if (rb_iter_head_event(iter) == NULL)
|
||||
return;
|
||||
}
|
||||
|
||||
iter->head = iter->next_event;
|
||||
|
||||
/*
|
||||
* Check if we are at the end of the buffer.
|
||||
*/
|
||||
if (iter->head >= rb_page_size(iter->head_page)) {
|
||||
if (iter->next_event >= rb_page_size(iter->head_page)) {
|
||||
/* discarded commits can make the page empty */
|
||||
if (iter->head_page == cpu_buffer->commit_page)
|
||||
return;
|
||||
@ -3844,27 +3946,7 @@ static void rb_advance_iter(struct ring_buffer_iter *iter)
|
||||
return;
|
||||
}
|
||||
|
||||
event = rb_iter_head_event(iter);
|
||||
|
||||
length = rb_event_length(event);
|
||||
|
||||
/*
|
||||
* This should not be called to advance the header if we are
|
||||
* at the tail of the buffer.
|
||||
*/
|
||||
if (RB_WARN_ON(cpu_buffer,
|
||||
(iter->head_page == cpu_buffer->commit_page) &&
|
||||
(iter->head + length > rb_commit_index(cpu_buffer))))
|
||||
return;
|
||||
|
||||
rb_update_iter_read_stamp(iter, event);
|
||||
|
||||
iter->head += length;
|
||||
|
||||
/* check for end of page padding */
|
||||
if ((iter->head >= rb_page_size(iter->head_page)) &&
|
||||
(iter->head_page != cpu_buffer->commit_page))
|
||||
rb_inc_iter(iter);
|
||||
rb_update_iter_read_stamp(iter, iter->event);
|
||||
}
|
||||
|
||||
static int rb_lost_events(struct ring_buffer_per_cpu *cpu_buffer)
|
||||
@ -3952,6 +4034,7 @@ rb_iter_peek(struct ring_buffer_iter *iter, u64 *ts)
|
||||
struct ring_buffer_per_cpu *cpu_buffer;
|
||||
struct ring_buffer_event *event;
|
||||
int nr_loops = 0;
|
||||
bool failed = false;
|
||||
|
||||
if (ts)
|
||||
*ts = 0;
|
||||
@ -3978,10 +4061,14 @@ rb_iter_peek(struct ring_buffer_iter *iter, u64 *ts)
|
||||
* to a data event, we should never loop more than three times.
|
||||
* Once for going to next page, once on time extend, and
|
||||
* finally once to get the event.
|
||||
* (We never hit the following condition more than thrice).
|
||||
* We should never hit the following condition more than thrice,
|
||||
* unless the buffer is very small, and there's a writer
|
||||
* that is causing the reader to fail getting an event.
|
||||
*/
|
||||
if (RB_WARN_ON(cpu_buffer, ++nr_loops > 3))
|
||||
if (++nr_loops > 3) {
|
||||
RB_WARN_ON(cpu_buffer, !failed);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (rb_per_cpu_empty(cpu_buffer))
|
||||
return NULL;
|
||||
@ -3992,6 +4079,10 @@ rb_iter_peek(struct ring_buffer_iter *iter, u64 *ts)
|
||||
}
|
||||
|
||||
event = rb_iter_head_event(iter);
|
||||
if (!event) {
|
||||
failed = true;
|
||||
goto again;
|
||||
}
|
||||
|
||||
switch (event->type_len) {
|
||||
case RINGBUF_TYPE_PADDING:
|
||||
@ -4102,6 +4193,20 @@ ring_buffer_peek(struct trace_buffer *buffer, int cpu, u64 *ts,
|
||||
return event;
|
||||
}
|
||||
|
||||
/** ring_buffer_iter_dropped - report if there are dropped events
|
||||
* @iter: The ring buffer iterator
|
||||
*
|
||||
* Returns true if there was dropped events since the last peek.
|
||||
*/
|
||||
bool ring_buffer_iter_dropped(struct ring_buffer_iter *iter)
|
||||
{
|
||||
bool ret = iter->missed_events != 0;
|
||||
|
||||
iter->missed_events = 0;
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ring_buffer_iter_dropped);
|
||||
|
||||
/**
|
||||
* ring_buffer_iter_peek - peek at the next event to be read
|
||||
* @iter: The ring buffer iterator
|
||||
@ -4208,16 +4313,21 @@ ring_buffer_read_prepare(struct trace_buffer *buffer, int cpu, gfp_t flags)
|
||||
if (!cpumask_test_cpu(cpu, buffer->cpumask))
|
||||
return NULL;
|
||||
|
||||
iter = kmalloc(sizeof(*iter), flags);
|
||||
iter = kzalloc(sizeof(*iter), flags);
|
||||
if (!iter)
|
||||
return NULL;
|
||||
|
||||
iter->event = kmalloc(BUF_MAX_DATA_SIZE, flags);
|
||||
if (!iter->event) {
|
||||
kfree(iter);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cpu_buffer = buffer->buffers[cpu];
|
||||
|
||||
iter->cpu_buffer = cpu_buffer;
|
||||
|
||||
atomic_inc(&buffer->resize_disabled);
|
||||
atomic_inc(&cpu_buffer->record_disabled);
|
||||
atomic_inc(&cpu_buffer->resize_disabled);
|
||||
|
||||
return iter;
|
||||
}
|
||||
@ -4290,42 +4400,31 @@ ring_buffer_read_finish(struct ring_buffer_iter *iter)
|
||||
rb_check_pages(cpu_buffer);
|
||||
raw_spin_unlock_irqrestore(&cpu_buffer->reader_lock, flags);
|
||||
|
||||
atomic_dec(&cpu_buffer->record_disabled);
|
||||
atomic_dec(&cpu_buffer->buffer->resize_disabled);
|
||||
atomic_dec(&cpu_buffer->resize_disabled);
|
||||
kfree(iter->event);
|
||||
kfree(iter);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ring_buffer_read_finish);
|
||||
|
||||
/**
|
||||
* ring_buffer_read - read the next item in the ring buffer by the iterator
|
||||
* ring_buffer_iter_advance - advance the iterator to the next location
|
||||
* @iter: The ring buffer iterator
|
||||
* @ts: The time stamp of the event read.
|
||||
*
|
||||
* This reads the next event in the ring buffer and increments the iterator.
|
||||
* Move the location of the iterator such that the next read will
|
||||
* be the next location of the iterator.
|
||||
*/
|
||||
struct ring_buffer_event *
|
||||
ring_buffer_read(struct ring_buffer_iter *iter, u64 *ts)
|
||||
void ring_buffer_iter_advance(struct ring_buffer_iter *iter)
|
||||
{
|
||||
struct ring_buffer_event *event;
|
||||
struct ring_buffer_per_cpu *cpu_buffer = iter->cpu_buffer;
|
||||
unsigned long flags;
|
||||
|
||||
raw_spin_lock_irqsave(&cpu_buffer->reader_lock, flags);
|
||||
again:
|
||||
event = rb_iter_peek(iter, ts);
|
||||
if (!event)
|
||||
goto out;
|
||||
|
||||
if (event->type_len == RINGBUF_TYPE_PADDING)
|
||||
goto again;
|
||||
|
||||
rb_advance_iter(iter);
|
||||
out:
|
||||
raw_spin_unlock_irqrestore(&cpu_buffer->reader_lock, flags);
|
||||
|
||||
return event;
|
||||
raw_spin_unlock_irqrestore(&cpu_buffer->reader_lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ring_buffer_read);
|
||||
EXPORT_SYMBOL_GPL(ring_buffer_iter_advance);
|
||||
|
||||
/**
|
||||
* ring_buffer_size - return the size of the ring buffer (in bytes)
|
||||
@ -4406,7 +4505,7 @@ void ring_buffer_reset_cpu(struct trace_buffer *buffer, int cpu)
|
||||
if (!cpumask_test_cpu(cpu, buffer->cpumask))
|
||||
return;
|
||||
|
||||
atomic_inc(&buffer->resize_disabled);
|
||||
atomic_inc(&cpu_buffer->resize_disabled);
|
||||
atomic_inc(&cpu_buffer->record_disabled);
|
||||
|
||||
/* Make sure all commits have finished */
|
||||
@ -4427,7 +4526,7 @@ void ring_buffer_reset_cpu(struct trace_buffer *buffer, int cpu)
|
||||
raw_spin_unlock_irqrestore(&cpu_buffer->reader_lock, flags);
|
||||
|
||||
atomic_dec(&cpu_buffer->record_disabled);
|
||||
atomic_dec(&buffer->resize_disabled);
|
||||
atomic_dec(&cpu_buffer->resize_disabled);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ring_buffer_reset_cpu);
|
||||
|
||||
|
@ -386,16 +386,22 @@ trace_find_filtered_pid(struct trace_pid_list *filtered_pids, pid_t search_pid)
|
||||
* Returns false if @task should be traced.
|
||||
*/
|
||||
bool
|
||||
trace_ignore_this_task(struct trace_pid_list *filtered_pids, struct task_struct *task)
|
||||
trace_ignore_this_task(struct trace_pid_list *filtered_pids,
|
||||
struct trace_pid_list *filtered_no_pids,
|
||||
struct task_struct *task)
|
||||
{
|
||||
/*
|
||||
* Return false, because if filtered_pids does not exist,
|
||||
* all pids are good to trace.
|
||||
* If filterd_no_pids is not empty, and the task's pid is listed
|
||||
* in filtered_no_pids, then return true.
|
||||
* Otherwise, if filtered_pids is empty, that means we can
|
||||
* trace all tasks. If it has content, then only trace pids
|
||||
* within filtered_pids.
|
||||
*/
|
||||
if (!filtered_pids)
|
||||
return false;
|
||||
|
||||
return !trace_find_filtered_pid(filtered_pids, task->pid);
|
||||
return (filtered_pids &&
|
||||
!trace_find_filtered_pid(filtered_pids, task->pid)) ||
|
||||
(filtered_no_pids &&
|
||||
trace_find_filtered_pid(filtered_no_pids, task->pid));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -3378,7 +3384,7 @@ static void trace_iterator_increment(struct trace_iterator *iter)
|
||||
|
||||
iter->idx++;
|
||||
if (buf_iter)
|
||||
ring_buffer_read(buf_iter, NULL);
|
||||
ring_buffer_iter_advance(buf_iter);
|
||||
}
|
||||
|
||||
static struct trace_entry *
|
||||
@ -3388,11 +3394,15 @@ peek_next_entry(struct trace_iterator *iter, int cpu, u64 *ts,
|
||||
struct ring_buffer_event *event;
|
||||
struct ring_buffer_iter *buf_iter = trace_buffer_iter(iter, cpu);
|
||||
|
||||
if (buf_iter)
|
||||
if (buf_iter) {
|
||||
event = ring_buffer_iter_peek(buf_iter, ts);
|
||||
else
|
||||
if (lost_events)
|
||||
*lost_events = ring_buffer_iter_dropped(buf_iter) ?
|
||||
(unsigned long)-1 : 0;
|
||||
} else {
|
||||
event = ring_buffer_peek(iter->array_buffer->buffer, cpu, ts,
|
||||
lost_events);
|
||||
}
|
||||
|
||||
if (event) {
|
||||
iter->ent_size = ring_buffer_event_length(event);
|
||||
@ -3462,11 +3472,51 @@ __find_next_entry(struct trace_iterator *iter, int *ent_cpu,
|
||||
return next;
|
||||
}
|
||||
|
||||
#define STATIC_TEMP_BUF_SIZE 128
|
||||
static char static_temp_buf[STATIC_TEMP_BUF_SIZE];
|
||||
|
||||
/* 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)
|
||||
{
|
||||
return __find_next_entry(iter, ent_cpu, NULL, ent_ts);
|
||||
/* __find_next_entry will reset ent_size */
|
||||
int ent_size = iter->ent_size;
|
||||
struct trace_entry *entry;
|
||||
|
||||
/*
|
||||
* If called from ftrace_dump(), then the iter->temp buffer
|
||||
* will be the static_temp_buf and not created from kmalloc.
|
||||
* If the entry size is greater than the buffer, we can
|
||||
* not save it. Just return NULL in that case. This is only
|
||||
* used to add markers when two consecutive events' time
|
||||
* stamps have a large delta. See trace_print_lat_context()
|
||||
*/
|
||||
if (iter->temp == static_temp_buf &&
|
||||
STATIC_TEMP_BUF_SIZE < ent_size)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* The __find_next_entry() may call peek_next_entry(), which may
|
||||
* call ring_buffer_peek() that may make the contents of iter->ent
|
||||
* undefined. Need to copy iter->ent now.
|
||||
*/
|
||||
if (iter->ent && iter->ent != iter->temp) {
|
||||
if ((!iter->temp || iter->temp_size < iter->ent_size) &&
|
||||
!WARN_ON_ONCE(iter->temp == static_temp_buf)) {
|
||||
kfree(iter->temp);
|
||||
iter->temp = kmalloc(iter->ent_size, GFP_KERNEL);
|
||||
if (!iter->temp)
|
||||
return NULL;
|
||||
}
|
||||
memcpy(iter->temp, iter->ent, iter->ent_size);
|
||||
iter->temp_size = 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 */
|
||||
@ -3538,7 +3588,7 @@ void tracing_iter_reset(struct trace_iterator *iter, int cpu)
|
||||
if (ts >= iter->array_buffer->time_start)
|
||||
break;
|
||||
entries++;
|
||||
ring_buffer_read(buf_iter, NULL);
|
||||
ring_buffer_iter_advance(buf_iter);
|
||||
}
|
||||
|
||||
per_cpu_ptr(iter->array_buffer->data, cpu)->skipped_entries = entries;
|
||||
@ -3981,8 +4031,12 @@ enum print_line_t print_trace_line(struct trace_iterator *iter)
|
||||
enum print_line_t ret;
|
||||
|
||||
if (iter->lost_events) {
|
||||
trace_seq_printf(&iter->seq, "CPU:%d [LOST %lu EVENTS]\n",
|
||||
iter->cpu, iter->lost_events);
|
||||
if (iter->lost_events == (unsigned long)-1)
|
||||
trace_seq_printf(&iter->seq, "CPU:%d [LOST EVENTS]\n",
|
||||
iter->cpu);
|
||||
else
|
||||
trace_seq_printf(&iter->seq, "CPU:%d [LOST %lu EVENTS]\n",
|
||||
iter->cpu, iter->lost_events);
|
||||
if (trace_seq_has_overflowed(&iter->seq))
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
}
|
||||
@ -4197,6 +4251,18 @@ __tracing_open(struct inode *inode, struct file *file, bool snapshot)
|
||||
if (!iter->buffer_iter)
|
||||
goto release;
|
||||
|
||||
/*
|
||||
* trace_find_next_entry() may need to save off iter->ent.
|
||||
* It will place it into the iter->temp buffer. As most
|
||||
* events are less than 128, allocate a buffer of that size.
|
||||
* If one is greater, then trace_find_next_entry() will
|
||||
* allocate a new buffer to adjust for the bigger iter->ent.
|
||||
* It's not critical if it fails to get allocated here.
|
||||
*/
|
||||
iter->temp = kmalloc(128, GFP_KERNEL);
|
||||
if (iter->temp)
|
||||
iter->temp_size = 128;
|
||||
|
||||
/*
|
||||
* We make a copy of the current tracer to avoid concurrent
|
||||
* changes on it while we are reading.
|
||||
@ -4237,8 +4303,11 @@ __tracing_open(struct inode *inode, struct file *file, bool snapshot)
|
||||
if (trace_clocks[tr->clock_id].in_ns)
|
||||
iter->iter_flags |= TRACE_FILE_TIME_IN_NS;
|
||||
|
||||
/* stop the trace while dumping if we are not opening "snapshot" */
|
||||
if (!iter->snapshot)
|
||||
/*
|
||||
* If pause-on-trace is enabled, then stop the trace while
|
||||
* dumping, unless this is the "snapshot" file
|
||||
*/
|
||||
if (!iter->snapshot && (tr->trace_flags & TRACE_ITER_PAUSE_ON_TRACE))
|
||||
tracing_stop_tr(tr);
|
||||
|
||||
if (iter->cpu_file == RING_BUFFER_ALL_CPUS) {
|
||||
@ -4269,6 +4338,7 @@ __tracing_open(struct inode *inode, struct file *file, bool snapshot)
|
||||
fail:
|
||||
mutex_unlock(&trace_types_lock);
|
||||
kfree(iter->trace);
|
||||
kfree(iter->temp);
|
||||
kfree(iter->buffer_iter);
|
||||
release:
|
||||
seq_release_private(inode, file);
|
||||
@ -4334,7 +4404,7 @@ static int tracing_release(struct inode *inode, struct file *file)
|
||||
if (iter->trace && iter->trace->close)
|
||||
iter->trace->close(iter);
|
||||
|
||||
if (!iter->snapshot)
|
||||
if (!iter->snapshot && tr->stop_count)
|
||||
/* reenable tracing if it was previously enabled */
|
||||
tracing_start_tr(tr);
|
||||
|
||||
@ -4344,6 +4414,7 @@ static int tracing_release(struct inode *inode, struct file *file)
|
||||
|
||||
mutex_destroy(&iter->mutex);
|
||||
free_cpumask_var(iter->started);
|
||||
kfree(iter->temp);
|
||||
kfree(iter->trace);
|
||||
kfree(iter->buffer_iter);
|
||||
seq_release_private(inode, file);
|
||||
@ -4964,6 +5035,8 @@ static const char readme_msg[] =
|
||||
#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"
|
||||
@ -9146,6 +9219,9 @@ void ftrace_dump(enum ftrace_dump_mode oops_dump_mode)
|
||||
|
||||
/* Simulate the iterator */
|
||||
trace_init_global_iter(&iter);
|
||||
/* Can not use kmalloc for iter.temp */
|
||||
iter.temp = static_temp_buf;
|
||||
iter.temp_size = STATIC_TEMP_BUF_SIZE;
|
||||
|
||||
for_each_tracing_cpu(cpu) {
|
||||
atomic_inc(&per_cpu_ptr(iter.array_buffer->data, cpu)->disabled);
|
||||
@ -9334,7 +9410,7 @@ __init static int tracer_alloc_buffers(void)
|
||||
goto out_free_buffer_mask;
|
||||
|
||||
/* Only allocate trace_printk buffers if a trace_printk exists */
|
||||
if (__stop___trace_bprintk_fmt != __start___trace_bprintk_fmt)
|
||||
if (&__stop___trace_bprintk_fmt != &__start___trace_bprintk_fmt)
|
||||
/* Must be called before global_trace.buffer is allocated */
|
||||
trace_printk_init_buffers();
|
||||
|
||||
|
@ -178,10 +178,10 @@ struct trace_array_cpu {
|
||||
kuid_t uid;
|
||||
char comm[TASK_COMM_LEN];
|
||||
|
||||
bool ignore_pid;
|
||||
#ifdef CONFIG_FUNCTION_TRACER
|
||||
bool ftrace_ignore_pid;
|
||||
int ftrace_ignore_pid;
|
||||
#endif
|
||||
bool ignore_pid;
|
||||
};
|
||||
|
||||
struct tracer;
|
||||
@ -207,6 +207,30 @@ struct trace_pid_list {
|
||||
unsigned long *pids;
|
||||
};
|
||||
|
||||
enum {
|
||||
TRACE_PIDS = BIT(0),
|
||||
TRACE_NO_PIDS = BIT(1),
|
||||
};
|
||||
|
||||
static inline bool pid_type_enabled(int type, struct trace_pid_list *pid_list,
|
||||
struct trace_pid_list *no_pid_list)
|
||||
{
|
||||
/* Return true if the pid list in type has pids */
|
||||
return ((type & TRACE_PIDS) && pid_list) ||
|
||||
((type & TRACE_NO_PIDS) && no_pid_list);
|
||||
}
|
||||
|
||||
static inline bool still_need_pid_events(int type, struct trace_pid_list *pid_list,
|
||||
struct trace_pid_list *no_pid_list)
|
||||
{
|
||||
/*
|
||||
* Turning off what is in @type, return true if the "other"
|
||||
* pid list, still has pids in it.
|
||||
*/
|
||||
return (!(type & TRACE_PIDS) && pid_list) ||
|
||||
(!(type & TRACE_NO_PIDS) && no_pid_list);
|
||||
}
|
||||
|
||||
typedef bool (*cond_update_fn_t)(struct trace_array *tr, void *cond_data);
|
||||
|
||||
/**
|
||||
@ -285,6 +309,7 @@ struct trace_array {
|
||||
#endif
|
||||
#endif
|
||||
struct trace_pid_list __rcu *filtered_pids;
|
||||
struct trace_pid_list __rcu *filtered_no_pids;
|
||||
/*
|
||||
* max_lock is used to protect the swapping of buffers
|
||||
* when taking a max snapshot. The buffers themselves are
|
||||
@ -331,6 +356,7 @@ struct trace_array {
|
||||
#ifdef CONFIG_FUNCTION_TRACER
|
||||
struct ftrace_ops *ops;
|
||||
struct trace_pid_list __rcu *function_pids;
|
||||
struct trace_pid_list __rcu *function_no_pids;
|
||||
#ifdef CONFIG_DYNAMIC_FTRACE
|
||||
/* All of these are protected by the ftrace_lock */
|
||||
struct list_head func_probes;
|
||||
@ -557,12 +583,7 @@ struct tracer {
|
||||
* caller, and we can skip the current check.
|
||||
*/
|
||||
enum {
|
||||
TRACE_BUFFER_BIT,
|
||||
TRACE_BUFFER_NMI_BIT,
|
||||
TRACE_BUFFER_IRQ_BIT,
|
||||
TRACE_BUFFER_SIRQ_BIT,
|
||||
|
||||
/* Start of function recursion bits */
|
||||
/* Function recursion bits */
|
||||
TRACE_FTRACE_BIT,
|
||||
TRACE_FTRACE_NMI_BIT,
|
||||
TRACE_FTRACE_IRQ_BIT,
|
||||
@ -787,6 +808,7 @@ extern int pid_max;
|
||||
bool trace_find_filtered_pid(struct trace_pid_list *filtered_pids,
|
||||
pid_t search_pid);
|
||||
bool trace_ignore_this_task(struct trace_pid_list *filtered_pids,
|
||||
struct trace_pid_list *filtered_no_pids,
|
||||
struct task_struct *task);
|
||||
void trace_filter_add_remove_task(struct trace_pid_list *pid_list,
|
||||
struct task_struct *self,
|
||||
@ -1307,6 +1329,7 @@ extern int trace_get_user(struct trace_parser *parser, const char __user *ubuf,
|
||||
C(IRQ_INFO, "irq-info"), \
|
||||
C(MARKERS, "markers"), \
|
||||
C(EVENT_FORK, "event-fork"), \
|
||||
C(PAUSE_ON_TRACE, "pause-on-trace"), \
|
||||
FUNCTION_FLAGS \
|
||||
FGRAPH_FLAGS \
|
||||
STACK_FLAGS \
|
||||
|
@ -325,14 +325,16 @@ FTRACE_ENTRY(hwlat, hwlat_entry,
|
||||
__field_desc( long, timestamp, tv_nsec )
|
||||
__field( unsigned int, nmi_count )
|
||||
__field( unsigned int, seqnum )
|
||||
__field( unsigned int, count )
|
||||
),
|
||||
|
||||
F_printk("cnt:%u\tts:%010llu.%010lu\tinner:%llu\touter:%llu\tnmi-ts:%llu\tnmi-count:%u\n",
|
||||
F_printk("cnt:%u\tts:%010llu.%010lu\tinner:%llu\touter:%llu\tcount:%d\tnmi-ts:%llu\tnmi-count:%u\n",
|
||||
__entry->seqnum,
|
||||
__entry->tv_sec,
|
||||
__entry->tv_nsec,
|
||||
__entry->duration,
|
||||
__entry->outer_duration,
|
||||
__entry->count,
|
||||
__entry->nmi_total_ts,
|
||||
__entry->nmi_count)
|
||||
);
|
||||
|
@ -232,10 +232,13 @@ bool trace_event_ignore_this_pid(struct trace_event_file *trace_file)
|
||||
{
|
||||
struct trace_array *tr = trace_file->tr;
|
||||
struct trace_array_cpu *data;
|
||||
struct trace_pid_list *no_pid_list;
|
||||
struct trace_pid_list *pid_list;
|
||||
|
||||
pid_list = rcu_dereference_raw(tr->filtered_pids);
|
||||
if (!pid_list)
|
||||
no_pid_list = rcu_dereference_raw(tr->filtered_no_pids);
|
||||
|
||||
if (!pid_list && !no_pid_list)
|
||||
return false;
|
||||
|
||||
data = this_cpu_ptr(tr->array_buffer.data);
|
||||
@ -510,6 +513,9 @@ event_filter_pid_sched_process_exit(void *data, struct task_struct *task)
|
||||
|
||||
pid_list = rcu_dereference_raw(tr->filtered_pids);
|
||||
trace_filter_add_remove_task(pid_list, NULL, task);
|
||||
|
||||
pid_list = rcu_dereference_raw(tr->filtered_no_pids);
|
||||
trace_filter_add_remove_task(pid_list, NULL, task);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -522,6 +528,9 @@ event_filter_pid_sched_process_fork(void *data,
|
||||
|
||||
pid_list = rcu_dereference_sched(tr->filtered_pids);
|
||||
trace_filter_add_remove_task(pid_list, self, task);
|
||||
|
||||
pid_list = rcu_dereference_sched(tr->filtered_no_pids);
|
||||
trace_filter_add_remove_task(pid_list, self, task);
|
||||
}
|
||||
|
||||
void trace_event_follow_fork(struct trace_array *tr, bool enable)
|
||||
@ -544,13 +553,23 @@ event_filter_pid_sched_switch_probe_pre(void *data, bool preempt,
|
||||
struct task_struct *prev, struct task_struct *next)
|
||||
{
|
||||
struct trace_array *tr = data;
|
||||
struct trace_pid_list *no_pid_list;
|
||||
struct trace_pid_list *pid_list;
|
||||
bool ret;
|
||||
|
||||
pid_list = rcu_dereference_sched(tr->filtered_pids);
|
||||
no_pid_list = rcu_dereference_sched(tr->filtered_no_pids);
|
||||
|
||||
this_cpu_write(tr->array_buffer.data->ignore_pid,
|
||||
trace_ignore_this_task(pid_list, prev) &&
|
||||
trace_ignore_this_task(pid_list, next));
|
||||
/*
|
||||
* Sched switch is funny, as we only want to ignore it
|
||||
* in the notrace case if both prev and next should be ignored.
|
||||
*/
|
||||
ret = trace_ignore_this_task(NULL, no_pid_list, prev) &&
|
||||
trace_ignore_this_task(NULL, no_pid_list, next);
|
||||
|
||||
this_cpu_write(tr->array_buffer.data->ignore_pid, ret ||
|
||||
(trace_ignore_this_task(pid_list, NULL, prev) &&
|
||||
trace_ignore_this_task(pid_list, NULL, next)));
|
||||
}
|
||||
|
||||
static void
|
||||
@ -558,18 +577,21 @@ event_filter_pid_sched_switch_probe_post(void *data, bool preempt,
|
||||
struct task_struct *prev, struct task_struct *next)
|
||||
{
|
||||
struct trace_array *tr = data;
|
||||
struct trace_pid_list *no_pid_list;
|
||||
struct trace_pid_list *pid_list;
|
||||
|
||||
pid_list = rcu_dereference_sched(tr->filtered_pids);
|
||||
no_pid_list = rcu_dereference_sched(tr->filtered_no_pids);
|
||||
|
||||
this_cpu_write(tr->array_buffer.data->ignore_pid,
|
||||
trace_ignore_this_task(pid_list, next));
|
||||
trace_ignore_this_task(pid_list, no_pid_list, next));
|
||||
}
|
||||
|
||||
static void
|
||||
event_filter_pid_sched_wakeup_probe_pre(void *data, struct task_struct *task)
|
||||
{
|
||||
struct trace_array *tr = data;
|
||||
struct trace_pid_list *no_pid_list;
|
||||
struct trace_pid_list *pid_list;
|
||||
|
||||
/* Nothing to do if we are already tracing */
|
||||
@ -577,15 +599,17 @@ event_filter_pid_sched_wakeup_probe_pre(void *data, struct task_struct *task)
|
||||
return;
|
||||
|
||||
pid_list = rcu_dereference_sched(tr->filtered_pids);
|
||||
no_pid_list = rcu_dereference_sched(tr->filtered_no_pids);
|
||||
|
||||
this_cpu_write(tr->array_buffer.data->ignore_pid,
|
||||
trace_ignore_this_task(pid_list, task));
|
||||
trace_ignore_this_task(pid_list, no_pid_list, task));
|
||||
}
|
||||
|
||||
static void
|
||||
event_filter_pid_sched_wakeup_probe_post(void *data, struct task_struct *task)
|
||||
{
|
||||
struct trace_array *tr = data;
|
||||
struct trace_pid_list *no_pid_list;
|
||||
struct trace_pid_list *pid_list;
|
||||
|
||||
/* Nothing to do if we are not tracing */
|
||||
@ -593,23 +617,15 @@ event_filter_pid_sched_wakeup_probe_post(void *data, struct task_struct *task)
|
||||
return;
|
||||
|
||||
pid_list = rcu_dereference_sched(tr->filtered_pids);
|
||||
no_pid_list = rcu_dereference_sched(tr->filtered_no_pids);
|
||||
|
||||
/* Set tracing if current is enabled */
|
||||
this_cpu_write(tr->array_buffer.data->ignore_pid,
|
||||
trace_ignore_this_task(pid_list, current));
|
||||
trace_ignore_this_task(pid_list, no_pid_list, current));
|
||||
}
|
||||
|
||||
static void __ftrace_clear_event_pids(struct trace_array *tr)
|
||||
static void unregister_pid_events(struct trace_array *tr)
|
||||
{
|
||||
struct trace_pid_list *pid_list;
|
||||
struct trace_event_file *file;
|
||||
int cpu;
|
||||
|
||||
pid_list = rcu_dereference_protected(tr->filtered_pids,
|
||||
lockdep_is_held(&event_mutex));
|
||||
if (!pid_list)
|
||||
return;
|
||||
|
||||
unregister_trace_sched_switch(event_filter_pid_sched_switch_probe_pre, tr);
|
||||
unregister_trace_sched_switch(event_filter_pid_sched_switch_probe_post, tr);
|
||||
|
||||
@ -621,26 +637,55 @@ static void __ftrace_clear_event_pids(struct trace_array *tr)
|
||||
|
||||
unregister_trace_sched_waking(event_filter_pid_sched_wakeup_probe_pre, tr);
|
||||
unregister_trace_sched_waking(event_filter_pid_sched_wakeup_probe_post, tr);
|
||||
}
|
||||
|
||||
list_for_each_entry(file, &tr->events, list) {
|
||||
clear_bit(EVENT_FILE_FL_PID_FILTER_BIT, &file->flags);
|
||||
static void __ftrace_clear_event_pids(struct trace_array *tr, int type)
|
||||
{
|
||||
struct trace_pid_list *pid_list;
|
||||
struct trace_pid_list *no_pid_list;
|
||||
struct trace_event_file *file;
|
||||
int cpu;
|
||||
|
||||
pid_list = rcu_dereference_protected(tr->filtered_pids,
|
||||
lockdep_is_held(&event_mutex));
|
||||
no_pid_list = rcu_dereference_protected(tr->filtered_no_pids,
|
||||
lockdep_is_held(&event_mutex));
|
||||
|
||||
/* Make sure there's something to do */
|
||||
if (!pid_type_enabled(type, pid_list, no_pid_list))
|
||||
return;
|
||||
|
||||
if (!still_need_pid_events(type, pid_list, no_pid_list)) {
|
||||
unregister_pid_events(tr);
|
||||
|
||||
list_for_each_entry(file, &tr->events, list) {
|
||||
clear_bit(EVENT_FILE_FL_PID_FILTER_BIT, &file->flags);
|
||||
}
|
||||
|
||||
for_each_possible_cpu(cpu)
|
||||
per_cpu_ptr(tr->array_buffer.data, cpu)->ignore_pid = false;
|
||||
}
|
||||
|
||||
for_each_possible_cpu(cpu)
|
||||
per_cpu_ptr(tr->array_buffer.data, cpu)->ignore_pid = false;
|
||||
if (type & TRACE_PIDS)
|
||||
rcu_assign_pointer(tr->filtered_pids, NULL);
|
||||
|
||||
rcu_assign_pointer(tr->filtered_pids, NULL);
|
||||
if (type & TRACE_NO_PIDS)
|
||||
rcu_assign_pointer(tr->filtered_no_pids, NULL);
|
||||
|
||||
/* Wait till all users are no longer using pid filtering */
|
||||
tracepoint_synchronize_unregister();
|
||||
|
||||
trace_free_pid_list(pid_list);
|
||||
if ((type & TRACE_PIDS) && pid_list)
|
||||
trace_free_pid_list(pid_list);
|
||||
|
||||
if ((type & TRACE_NO_PIDS) && no_pid_list)
|
||||
trace_free_pid_list(no_pid_list);
|
||||
}
|
||||
|
||||
static void ftrace_clear_event_pids(struct trace_array *tr)
|
||||
static void ftrace_clear_event_pids(struct trace_array *tr, int type)
|
||||
{
|
||||
mutex_lock(&event_mutex);
|
||||
__ftrace_clear_event_pids(tr);
|
||||
__ftrace_clear_event_pids(tr, type);
|
||||
mutex_unlock(&event_mutex);
|
||||
}
|
||||
|
||||
@ -1013,15 +1058,32 @@ static void t_stop(struct seq_file *m, void *p)
|
||||
}
|
||||
|
||||
static void *
|
||||
p_next(struct seq_file *m, void *v, loff_t *pos)
|
||||
__next(struct seq_file *m, void *v, loff_t *pos, int type)
|
||||
{
|
||||
struct trace_array *tr = m->private;
|
||||
struct trace_pid_list *pid_list = rcu_dereference_sched(tr->filtered_pids);
|
||||
struct trace_pid_list *pid_list;
|
||||
|
||||
if (type == TRACE_PIDS)
|
||||
pid_list = rcu_dereference_sched(tr->filtered_pids);
|
||||
else
|
||||
pid_list = rcu_dereference_sched(tr->filtered_no_pids);
|
||||
|
||||
return trace_pid_next(pid_list, v, pos);
|
||||
}
|
||||
|
||||
static void *p_start(struct seq_file *m, loff_t *pos)
|
||||
static void *
|
||||
p_next(struct seq_file *m, void *v, loff_t *pos)
|
||||
{
|
||||
return __next(m, v, pos, TRACE_PIDS);
|
||||
}
|
||||
|
||||
static void *
|
||||
np_next(struct seq_file *m, void *v, loff_t *pos)
|
||||
{
|
||||
return __next(m, v, pos, TRACE_NO_PIDS);
|
||||
}
|
||||
|
||||
static void *__start(struct seq_file *m, loff_t *pos, int type)
|
||||
__acquires(RCU)
|
||||
{
|
||||
struct trace_pid_list *pid_list;
|
||||
@ -1036,7 +1098,10 @@ static void *p_start(struct seq_file *m, loff_t *pos)
|
||||
mutex_lock(&event_mutex);
|
||||
rcu_read_lock_sched();
|
||||
|
||||
pid_list = rcu_dereference_sched(tr->filtered_pids);
|
||||
if (type == TRACE_PIDS)
|
||||
pid_list = rcu_dereference_sched(tr->filtered_pids);
|
||||
else
|
||||
pid_list = rcu_dereference_sched(tr->filtered_no_pids);
|
||||
|
||||
if (!pid_list)
|
||||
return NULL;
|
||||
@ -1044,6 +1109,18 @@ static void *p_start(struct seq_file *m, loff_t *pos)
|
||||
return trace_pid_start(pid_list, pos);
|
||||
}
|
||||
|
||||
static void *p_start(struct seq_file *m, loff_t *pos)
|
||||
__acquires(RCU)
|
||||
{
|
||||
return __start(m, pos, TRACE_PIDS);
|
||||
}
|
||||
|
||||
static void *np_start(struct seq_file *m, loff_t *pos)
|
||||
__acquires(RCU)
|
||||
{
|
||||
return __start(m, pos, TRACE_NO_PIDS);
|
||||
}
|
||||
|
||||
static void p_stop(struct seq_file *m, void *p)
|
||||
__releases(RCU)
|
||||
{
|
||||
@ -1588,6 +1665,7 @@ static void ignore_task_cpu(void *data)
|
||||
{
|
||||
struct trace_array *tr = data;
|
||||
struct trace_pid_list *pid_list;
|
||||
struct trace_pid_list *no_pid_list;
|
||||
|
||||
/*
|
||||
* This function is called by on_each_cpu() while the
|
||||
@ -1595,18 +1673,50 @@ static void ignore_task_cpu(void *data)
|
||||
*/
|
||||
pid_list = rcu_dereference_protected(tr->filtered_pids,
|
||||
mutex_is_locked(&event_mutex));
|
||||
no_pid_list = rcu_dereference_protected(tr->filtered_no_pids,
|
||||
mutex_is_locked(&event_mutex));
|
||||
|
||||
this_cpu_write(tr->array_buffer.data->ignore_pid,
|
||||
trace_ignore_this_task(pid_list, current));
|
||||
trace_ignore_this_task(pid_list, no_pid_list, current));
|
||||
}
|
||||
|
||||
static void register_pid_events(struct trace_array *tr)
|
||||
{
|
||||
/*
|
||||
* Register a probe that is called before all other probes
|
||||
* to set ignore_pid if next or prev do not match.
|
||||
* Register a probe this is called after all other probes
|
||||
* to only keep ignore_pid set if next pid matches.
|
||||
*/
|
||||
register_trace_prio_sched_switch(event_filter_pid_sched_switch_probe_pre,
|
||||
tr, INT_MAX);
|
||||
register_trace_prio_sched_switch(event_filter_pid_sched_switch_probe_post,
|
||||
tr, 0);
|
||||
|
||||
register_trace_prio_sched_wakeup(event_filter_pid_sched_wakeup_probe_pre,
|
||||
tr, INT_MAX);
|
||||
register_trace_prio_sched_wakeup(event_filter_pid_sched_wakeup_probe_post,
|
||||
tr, 0);
|
||||
|
||||
register_trace_prio_sched_wakeup_new(event_filter_pid_sched_wakeup_probe_pre,
|
||||
tr, INT_MAX);
|
||||
register_trace_prio_sched_wakeup_new(event_filter_pid_sched_wakeup_probe_post,
|
||||
tr, 0);
|
||||
|
||||
register_trace_prio_sched_waking(event_filter_pid_sched_wakeup_probe_pre,
|
||||
tr, INT_MAX);
|
||||
register_trace_prio_sched_waking(event_filter_pid_sched_wakeup_probe_post,
|
||||
tr, 0);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
ftrace_event_pid_write(struct file *filp, const char __user *ubuf,
|
||||
size_t cnt, loff_t *ppos)
|
||||
event_pid_write(struct file *filp, const char __user *ubuf,
|
||||
size_t cnt, loff_t *ppos, int type)
|
||||
{
|
||||
struct seq_file *m = filp->private_data;
|
||||
struct trace_array *tr = m->private;
|
||||
struct trace_pid_list *filtered_pids = NULL;
|
||||
struct trace_pid_list *other_pids = NULL;
|
||||
struct trace_pid_list *pid_list;
|
||||
struct trace_event_file *file;
|
||||
ssize_t ret;
|
||||
@ -1620,14 +1730,26 @@ ftrace_event_pid_write(struct file *filp, const char __user *ubuf,
|
||||
|
||||
mutex_lock(&event_mutex);
|
||||
|
||||
filtered_pids = rcu_dereference_protected(tr->filtered_pids,
|
||||
lockdep_is_held(&event_mutex));
|
||||
if (type == TRACE_PIDS) {
|
||||
filtered_pids = rcu_dereference_protected(tr->filtered_pids,
|
||||
lockdep_is_held(&event_mutex));
|
||||
other_pids = rcu_dereference_protected(tr->filtered_no_pids,
|
||||
lockdep_is_held(&event_mutex));
|
||||
} else {
|
||||
filtered_pids = rcu_dereference_protected(tr->filtered_no_pids,
|
||||
lockdep_is_held(&event_mutex));
|
||||
other_pids = rcu_dereference_protected(tr->filtered_pids,
|
||||
lockdep_is_held(&event_mutex));
|
||||
}
|
||||
|
||||
ret = trace_pid_write(filtered_pids, &pid_list, ubuf, cnt);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
rcu_assign_pointer(tr->filtered_pids, pid_list);
|
||||
if (type == TRACE_PIDS)
|
||||
rcu_assign_pointer(tr->filtered_pids, pid_list);
|
||||
else
|
||||
rcu_assign_pointer(tr->filtered_no_pids, pid_list);
|
||||
|
||||
list_for_each_entry(file, &tr->events, list) {
|
||||
set_bit(EVENT_FILE_FL_PID_FILTER_BIT, &file->flags);
|
||||
@ -1636,32 +1758,8 @@ ftrace_event_pid_write(struct file *filp, const char __user *ubuf,
|
||||
if (filtered_pids) {
|
||||
tracepoint_synchronize_unregister();
|
||||
trace_free_pid_list(filtered_pids);
|
||||
} else if (pid_list) {
|
||||
/*
|
||||
* Register a probe that is called before all other probes
|
||||
* to set ignore_pid if next or prev do not match.
|
||||
* Register a probe this is called after all other probes
|
||||
* to only keep ignore_pid set if next pid matches.
|
||||
*/
|
||||
register_trace_prio_sched_switch(event_filter_pid_sched_switch_probe_pre,
|
||||
tr, INT_MAX);
|
||||
register_trace_prio_sched_switch(event_filter_pid_sched_switch_probe_post,
|
||||
tr, 0);
|
||||
|
||||
register_trace_prio_sched_wakeup(event_filter_pid_sched_wakeup_probe_pre,
|
||||
tr, INT_MAX);
|
||||
register_trace_prio_sched_wakeup(event_filter_pid_sched_wakeup_probe_post,
|
||||
tr, 0);
|
||||
|
||||
register_trace_prio_sched_wakeup_new(event_filter_pid_sched_wakeup_probe_pre,
|
||||
tr, INT_MAX);
|
||||
register_trace_prio_sched_wakeup_new(event_filter_pid_sched_wakeup_probe_post,
|
||||
tr, 0);
|
||||
|
||||
register_trace_prio_sched_waking(event_filter_pid_sched_wakeup_probe_pre,
|
||||
tr, INT_MAX);
|
||||
register_trace_prio_sched_waking(event_filter_pid_sched_wakeup_probe_post,
|
||||
tr, 0);
|
||||
} else if (pid_list && !other_pids) {
|
||||
register_pid_events(tr);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1680,9 +1778,24 @@ ftrace_event_pid_write(struct file *filp, const char __user *ubuf,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
ftrace_event_pid_write(struct file *filp, const char __user *ubuf,
|
||||
size_t cnt, loff_t *ppos)
|
||||
{
|
||||
return event_pid_write(filp, ubuf, cnt, ppos, TRACE_PIDS);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
ftrace_event_npid_write(struct file *filp, const char __user *ubuf,
|
||||
size_t cnt, loff_t *ppos)
|
||||
{
|
||||
return event_pid_write(filp, ubuf, cnt, ppos, TRACE_NO_PIDS);
|
||||
}
|
||||
|
||||
static int ftrace_event_avail_open(struct inode *inode, struct file *file);
|
||||
static int ftrace_event_set_open(struct inode *inode, struct file *file);
|
||||
static int ftrace_event_set_pid_open(struct inode *inode, struct file *file);
|
||||
static int ftrace_event_set_npid_open(struct inode *inode, struct file *file);
|
||||
static int ftrace_event_release(struct inode *inode, struct file *file);
|
||||
|
||||
static const struct seq_operations show_event_seq_ops = {
|
||||
@ -1706,6 +1819,13 @@ static const struct seq_operations show_set_pid_seq_ops = {
|
||||
.stop = p_stop,
|
||||
};
|
||||
|
||||
static const struct seq_operations show_set_no_pid_seq_ops = {
|
||||
.start = np_start,
|
||||
.next = np_next,
|
||||
.show = trace_pid_show,
|
||||
.stop = p_stop,
|
||||
};
|
||||
|
||||
static const struct file_operations ftrace_avail_fops = {
|
||||
.open = ftrace_event_avail_open,
|
||||
.read = seq_read,
|
||||
@ -1729,6 +1849,14 @@ static const struct file_operations ftrace_set_event_pid_fops = {
|
||||
.release = ftrace_event_release,
|
||||
};
|
||||
|
||||
static const struct file_operations ftrace_set_event_notrace_pid_fops = {
|
||||
.open = ftrace_event_set_npid_open,
|
||||
.read = seq_read,
|
||||
.write = ftrace_event_npid_write,
|
||||
.llseek = seq_lseek,
|
||||
.release = ftrace_event_release,
|
||||
};
|
||||
|
||||
static const struct file_operations ftrace_enable_fops = {
|
||||
.open = tracing_open_generic,
|
||||
.read = event_enable_read,
|
||||
@ -1858,7 +1986,28 @@ ftrace_event_set_pid_open(struct inode *inode, struct file *file)
|
||||
|
||||
if ((file->f_mode & FMODE_WRITE) &&
|
||||
(file->f_flags & O_TRUNC))
|
||||
ftrace_clear_event_pids(tr);
|
||||
ftrace_clear_event_pids(tr, TRACE_PIDS);
|
||||
|
||||
ret = ftrace_event_open(inode, file, seq_ops);
|
||||
if (ret < 0)
|
||||
trace_array_put(tr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
ftrace_event_set_npid_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
const struct seq_operations *seq_ops = &show_set_no_pid_seq_ops;
|
||||
struct trace_array *tr = inode->i_private;
|
||||
int ret;
|
||||
|
||||
ret = tracing_check_open_get_tr(tr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if ((file->f_mode & FMODE_WRITE) &&
|
||||
(file->f_flags & O_TRUNC))
|
||||
ftrace_clear_event_pids(tr, TRACE_NO_PIDS);
|
||||
|
||||
ret = ftrace_event_open(inode, file, seq_ops);
|
||||
if (ret < 0)
|
||||
@ -3075,6 +3224,11 @@ create_event_toplevel_files(struct dentry *parent, struct trace_array *tr)
|
||||
if (!entry)
|
||||
pr_warn("Could not create tracefs 'set_event_pid' entry\n");
|
||||
|
||||
entry = tracefs_create_file("set_event_notrace_pid", 0644, parent,
|
||||
tr, &ftrace_set_event_notrace_pid_fops);
|
||||
if (!entry)
|
||||
pr_warn("Could not create tracefs 'set_event_notrace_pid' entry\n");
|
||||
|
||||
/* ring buffer internal formats */
|
||||
entry = trace_create_file("header_page", 0444, d_events,
|
||||
ring_buffer_print_page_header,
|
||||
@ -3158,7 +3312,7 @@ int event_trace_del_tracer(struct trace_array *tr)
|
||||
clear_event_triggers(tr);
|
||||
|
||||
/* Clear the pid list */
|
||||
__ftrace_clear_event_pids(tr);
|
||||
__ftrace_clear_event_pids(tr, TRACE_PIDS | TRACE_NO_PIDS);
|
||||
|
||||
/* Disable any running events */
|
||||
__ftrace_set_clr_event_nolock(tr, NULL, NULL, NULL, 0);
|
||||
|
@ -482,7 +482,7 @@ get_return_for_leaf(struct trace_iterator *iter,
|
||||
|
||||
/* this is a leaf, now advance the iterator */
|
||||
if (ring_iter)
|
||||
ring_buffer_read(ring_iter, NULL);
|
||||
ring_buffer_iter_advance(ring_iter);
|
||||
|
||||
return next;
|
||||
}
|
||||
|
@ -83,6 +83,7 @@ struct hwlat_sample {
|
||||
u64 nmi_total_ts; /* Total time spent in NMIs */
|
||||
struct timespec64 timestamp; /* wall time */
|
||||
int nmi_count; /* # NMIs during this sample */
|
||||
int count; /* # of iteratons over threash */
|
||||
};
|
||||
|
||||
/* keep the global state somewhere. */
|
||||
@ -124,6 +125,7 @@ static void trace_hwlat_sample(struct hwlat_sample *sample)
|
||||
entry->timestamp = sample->timestamp;
|
||||
entry->nmi_total_ts = sample->nmi_total_ts;
|
||||
entry->nmi_count = sample->nmi_count;
|
||||
entry->count = sample->count;
|
||||
|
||||
if (!call_filter_check_discard(call, entry, buffer, event))
|
||||
trace_buffer_unlock_commit_nostack(buffer, event);
|
||||
@ -167,12 +169,14 @@ void trace_hwlat_callback(bool enter)
|
||||
static int get_sample(void)
|
||||
{
|
||||
struct trace_array *tr = hwlat_trace;
|
||||
struct hwlat_sample s;
|
||||
time_type start, t1, t2, last_t2;
|
||||
s64 diff, total, last_total = 0;
|
||||
s64 diff, outer_diff, total, last_total = 0;
|
||||
u64 sample = 0;
|
||||
u64 thresh = tracing_thresh;
|
||||
u64 outer_sample = 0;
|
||||
int ret = -1;
|
||||
unsigned int count = 0;
|
||||
|
||||
do_div(thresh, NSEC_PER_USEC); /* modifies interval value */
|
||||
|
||||
@ -186,6 +190,7 @@ static int get_sample(void)
|
||||
|
||||
init_time(last_t2, 0);
|
||||
start = time_get(); /* start timestamp */
|
||||
outer_diff = 0;
|
||||
|
||||
do {
|
||||
|
||||
@ -194,14 +199,14 @@ static int get_sample(void)
|
||||
|
||||
if (time_u64(last_t2)) {
|
||||
/* Check the delta from outer loop (t2 to next t1) */
|
||||
diff = time_to_us(time_sub(t1, last_t2));
|
||||
outer_diff = time_to_us(time_sub(t1, last_t2));
|
||||
/* This shouldn't happen */
|
||||
if (diff < 0) {
|
||||
if (outer_diff < 0) {
|
||||
pr_err(BANNER "time running backwards\n");
|
||||
goto out;
|
||||
}
|
||||
if (diff > outer_sample)
|
||||
outer_sample = diff;
|
||||
if (outer_diff > outer_sample)
|
||||
outer_sample = outer_diff;
|
||||
}
|
||||
last_t2 = t2;
|
||||
|
||||
@ -217,6 +222,12 @@ static int get_sample(void)
|
||||
/* This checks the inner loop (t1 to t2) */
|
||||
diff = time_to_us(time_sub(t2, t1)); /* current diff */
|
||||
|
||||
if (diff > thresh || outer_diff > thresh) {
|
||||
if (!count)
|
||||
ktime_get_real_ts64(&s.timestamp);
|
||||
count++;
|
||||
}
|
||||
|
||||
/* This shouldn't happen */
|
||||
if (diff < 0) {
|
||||
pr_err(BANNER "time running backwards\n");
|
||||
@ -236,7 +247,6 @@ static int get_sample(void)
|
||||
|
||||
/* If we exceed the threshold value, we have found a hardware latency */
|
||||
if (sample > thresh || outer_sample > thresh) {
|
||||
struct hwlat_sample s;
|
||||
u64 latency;
|
||||
|
||||
ret = 1;
|
||||
@ -249,9 +259,9 @@ static int get_sample(void)
|
||||
s.seqnum = hwlat_data.count;
|
||||
s.duration = sample;
|
||||
s.outer_duration = outer_sample;
|
||||
ktime_get_real_ts64(&s.timestamp);
|
||||
s.nmi_total_ts = nmi_total_ts;
|
||||
s.nmi_count = nmi_count;
|
||||
s.count = count;
|
||||
trace_hwlat_sample(&s);
|
||||
|
||||
latency = max(sample, outer_sample);
|
||||
|
@ -1078,6 +1078,8 @@ static int trace_kprobe_show(struct seq_file *m, struct dyn_event *ev)
|
||||
int i;
|
||||
|
||||
seq_putc(m, trace_kprobe_is_return(tk) ? 'r' : 'p');
|
||||
if (trace_kprobe_is_return(tk) && tk->rp.maxactive)
|
||||
seq_printf(m, "%d", tk->rp.maxactive);
|
||||
seq_printf(m, ":%s/%s", trace_probe_group_name(&tk->tp),
|
||||
trace_probe_name(&tk->tp));
|
||||
|
||||
|
@ -617,22 +617,19 @@ int trace_print_context(struct trace_iterator *iter)
|
||||
|
||||
int trace_print_lat_context(struct trace_iterator *iter)
|
||||
{
|
||||
struct trace_entry *entry, *next_entry;
|
||||
struct trace_array *tr = iter->tr;
|
||||
/* trace_find_next_entry will reset ent_size */
|
||||
int ent_size = iter->ent_size;
|
||||
struct trace_seq *s = &iter->seq;
|
||||
u64 next_ts;
|
||||
struct trace_entry *entry = iter->ent,
|
||||
*next_entry = trace_find_next_entry(iter, NULL,
|
||||
&next_ts);
|
||||
unsigned long verbose = (tr->trace_flags & TRACE_ITER_VERBOSE);
|
||||
u64 next_ts;
|
||||
|
||||
/* Restore the original ent_size */
|
||||
iter->ent_size = ent_size;
|
||||
|
||||
next_entry = trace_find_next_entry(iter, NULL, &next_ts);
|
||||
if (!next_entry)
|
||||
next_ts = iter->ts;
|
||||
|
||||
/* trace_find_next_entry() may change iter->ent */
|
||||
entry = iter->ent;
|
||||
|
||||
if (verbose) {
|
||||
char comm[TASK_COMM_LEN];
|
||||
|
||||
@ -1158,12 +1155,12 @@ trace_hwlat_print(struct trace_iterator *iter, int flags,
|
||||
|
||||
trace_assign_type(field, entry);
|
||||
|
||||
trace_seq_printf(s, "#%-5u inner/outer(us): %4llu/%-5llu ts:%lld.%09ld",
|
||||
trace_seq_printf(s, "#%-5u inner/outer(us): %4llu/%-5llu ts:%lld.%09ld count:%d",
|
||||
field->seqnum,
|
||||
field->duration,
|
||||
field->outer_duration,
|
||||
(long long)field->timestamp.tv_sec,
|
||||
field->timestamp.tv_nsec);
|
||||
field->timestamp.tv_nsec, field->count);
|
||||
|
||||
if (field->nmi_count) {
|
||||
/*
|
||||
|
@ -29,12 +29,14 @@ static int xbc_node_num __initdata;
|
||||
static char *xbc_data __initdata;
|
||||
static size_t xbc_data_size __initdata;
|
||||
static struct xbc_node *last_parent __initdata;
|
||||
static const char *xbc_err_msg __initdata;
|
||||
static int xbc_err_pos __initdata;
|
||||
|
||||
static int __init xbc_parse_error(const char *msg, const char *p)
|
||||
{
|
||||
int pos = p - xbc_data;
|
||||
xbc_err_msg = msg;
|
||||
xbc_err_pos = (int)(p - xbc_data);
|
||||
|
||||
pr_err("Parse error at pos %d: %s\n", pos, msg);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -738,33 +740,44 @@ void __init xbc_destroy_all(void)
|
||||
/**
|
||||
* xbc_init() - Parse given XBC file and build XBC internal tree
|
||||
* @buf: boot config text
|
||||
* @emsg: A pointer of const char * to store the error message
|
||||
* @epos: A pointer of int to store the error position
|
||||
*
|
||||
* This parses the boot config text in @buf. @buf must be a
|
||||
* null terminated string and smaller than XBC_DATA_MAX.
|
||||
* Return the number of stored nodes (>0) if succeeded, or -errno
|
||||
* if there is any error.
|
||||
* In error cases, @emsg will be updated with an error message and
|
||||
* @epos will be updated with the error position which is the byte offset
|
||||
* of @buf. If the error is not a parser error, @epos will be -1.
|
||||
*/
|
||||
int __init xbc_init(char *buf)
|
||||
int __init xbc_init(char *buf, const char **emsg, int *epos)
|
||||
{
|
||||
char *p, *q;
|
||||
int ret, c;
|
||||
|
||||
if (epos)
|
||||
*epos = -1;
|
||||
|
||||
if (xbc_data) {
|
||||
pr_err("Error: bootconfig is already initialized.\n");
|
||||
if (emsg)
|
||||
*emsg = "Bootconfig is already initialized";
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
ret = strlen(buf);
|
||||
if (ret > XBC_DATA_MAX - 1 || ret == 0) {
|
||||
pr_err("Error: Config data is %s.\n",
|
||||
ret ? "too big" : "empty");
|
||||
if (emsg)
|
||||
*emsg = ret ? "Config data is too big" :
|
||||
"Config data is empty";
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
xbc_nodes = memblock_alloc(sizeof(struct xbc_node) * XBC_NODE_MAX,
|
||||
SMP_CACHE_BYTES);
|
||||
if (!xbc_nodes) {
|
||||
pr_err("Failed to allocate memory for bootconfig nodes.\n");
|
||||
if (emsg)
|
||||
*emsg = "Failed to allocate bootconfig nodes";
|
||||
return -ENOMEM;
|
||||
}
|
||||
memset(xbc_nodes, 0, sizeof(struct xbc_node) * XBC_NODE_MAX);
|
||||
@ -814,9 +827,13 @@ int __init xbc_init(char *buf)
|
||||
if (!ret)
|
||||
ret = xbc_verify_tree();
|
||||
|
||||
if (ret < 0)
|
||||
if (ret < 0) {
|
||||
if (epos)
|
||||
*epos = xbc_err_pos;
|
||||
if (emsg)
|
||||
*emsg = xbc_err_msg;
|
||||
xbc_destroy_all();
|
||||
else
|
||||
} else
|
||||
ret = xbc_node_num;
|
||||
|
||||
return ret;
|
||||
|
@ -1,23 +1,30 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
# Makefile for bootconfig command
|
||||
include ../scripts/Makefile.include
|
||||
|
||||
bindir ?= /usr/bin
|
||||
|
||||
HEADER = include/linux/bootconfig.h
|
||||
CFLAGS = -Wall -g -I./include
|
||||
ifeq ($(srctree),)
|
||||
srctree := $(patsubst %/,%,$(dir $(CURDIR)))
|
||||
srctree := $(patsubst %/,%,$(dir $(srctree)))
|
||||
endif
|
||||
|
||||
PROGS = bootconfig
|
||||
LIBSRC = $(srctree)/lib/bootconfig.c $(srctree)/include/linux/bootconfig.h
|
||||
CFLAGS = -Wall -g -I$(CURDIR)/include
|
||||
|
||||
all: $(PROGS)
|
||||
ALL_TARGETS := bootconfig
|
||||
ALL_PROGRAMS := $(patsubst %,$(OUTPUT)%,$(ALL_TARGETS))
|
||||
|
||||
bootconfig: ../../lib/bootconfig.c main.c $(HEADER)
|
||||
all: $(ALL_PROGRAMS)
|
||||
|
||||
$(OUTPUT)bootconfig: main.c $(LIBSRC)
|
||||
$(CC) $(filter %.c,$^) $(CFLAGS) -o $@
|
||||
|
||||
install: $(PROGS)
|
||||
install bootconfig $(DESTDIR)$(bindir)
|
||||
test: $(ALL_PROGRAMS) test-bootconfig.sh
|
||||
./test-bootconfig.sh $(OUTPUT)
|
||||
|
||||
test: bootconfig
|
||||
./test-bootconfig.sh
|
||||
install: $(ALL_PROGRAMS)
|
||||
install $(OUTPUT)bootconfig $(DESTDIR)$(bindir)
|
||||
|
||||
clean:
|
||||
$(RM) -f *.o bootconfig
|
||||
$(RM) -f $(OUTPUT)*.o $(ALL_PROGRAMS)
|
||||
|
@ -130,6 +130,7 @@ int load_xbc_from_initrd(int fd, char **buf)
|
||||
int ret;
|
||||
u32 size = 0, csum = 0, rcsum;
|
||||
char magic[BOOTCONFIG_MAGIC_LEN];
|
||||
const char *msg;
|
||||
|
||||
ret = fstat(fd, &stat);
|
||||
if (ret < 0)
|
||||
@ -182,10 +183,12 @@ int load_xbc_from_initrd(int fd, char **buf)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = xbc_init(*buf);
|
||||
ret = xbc_init(*buf, &msg, NULL);
|
||||
/* Wrong data */
|
||||
if (ret < 0)
|
||||
if (ret < 0) {
|
||||
pr_err("parse error: %s.\n", msg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
@ -244,11 +247,34 @@ int delete_xbc(const char *path)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void show_xbc_error(const char *data, const char *msg, int pos)
|
||||
{
|
||||
int lin = 1, col, i;
|
||||
|
||||
if (pos < 0) {
|
||||
pr_err("Error: %s.\n", msg);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Note that pos starts from 0 but lin and col should start from 1. */
|
||||
col = pos + 1;
|
||||
for (i = 0; i < pos; i++) {
|
||||
if (data[i] == '\n') {
|
||||
lin++;
|
||||
col = pos - i;
|
||||
}
|
||||
}
|
||||
pr_err("Parse Error: %s at %d:%d\n", msg, lin, col);
|
||||
|
||||
}
|
||||
|
||||
int apply_xbc(const char *path, const char *xbc_path)
|
||||
{
|
||||
u32 size, csum;
|
||||
char *buf, *data;
|
||||
int ret, fd;
|
||||
const char *msg;
|
||||
int pos;
|
||||
|
||||
ret = load_xbc_file(xbc_path, &buf);
|
||||
if (ret < 0) {
|
||||
@ -267,11 +293,12 @@ int apply_xbc(const char *path, const char *xbc_path)
|
||||
*(u32 *)(data + size + 4) = csum;
|
||||
|
||||
/* Check the data format */
|
||||
ret = xbc_init(buf);
|
||||
ret = xbc_init(buf, &msg, &pos);
|
||||
if (ret < 0) {
|
||||
pr_err("Failed to parse %s: %d\n", xbc_path, ret);
|
||||
show_xbc_error(data, msg, pos);
|
||||
free(data);
|
||||
free(buf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
printf("Apply %s to %s\n", xbc_path, path);
|
||||
|
@ -3,9 +3,16 @@
|
||||
|
||||
echo "Boot config test script"
|
||||
|
||||
BOOTCONF=./bootconfig
|
||||
INITRD=`mktemp initrd-XXXX`
|
||||
TEMPCONF=`mktemp temp-XXXX.bconf`
|
||||
if [ -d "$1" ]; then
|
||||
TESTDIR=$1
|
||||
else
|
||||
TESTDIR=.
|
||||
fi
|
||||
BOOTCONF=${TESTDIR}/bootconfig
|
||||
|
||||
INITRD=`mktemp ${TESTDIR}/initrd-XXXX`
|
||||
TEMPCONF=`mktemp ${TESTDIR}/temp-XXXX.bconf`
|
||||
OUTFILE=`mktemp ${TESTDIR}/tempout-XXXX`
|
||||
NG=0
|
||||
|
||||
cleanup() {
|
||||
@ -65,7 +72,6 @@ new_size=$(stat -c %s $INITRD)
|
||||
xpass test $new_size -eq $initrd_size
|
||||
|
||||
echo "No error messge while applying"
|
||||
OUTFILE=`mktemp tempout-XXXX`
|
||||
dd if=/dev/zero of=$INITRD bs=4096 count=1
|
||||
printf " \0\0\0 \0\0\0" >> $INITRD
|
||||
$BOOTCONF -a $TEMPCONF $INITRD > $OUTFILE 2>&1
|
||||
|
125
tools/testing/selftests/ftrace/test.d/event/event-no-pid.tc
Normal file
125
tools/testing/selftests/ftrace/test.d/event/event-no-pid.tc
Normal file
@ -0,0 +1,125 @@
|
||||
#!/bin/sh
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
# description: event tracing - restricts events based on pid notrace filtering
|
||||
# flags: instance
|
||||
|
||||
do_reset() {
|
||||
echo > set_event
|
||||
echo > set_event_pid
|
||||
echo > set_event_notrace_pid
|
||||
echo 0 > options/event-fork
|
||||
echo 0 > events/enable
|
||||
clear_trace
|
||||
echo 1 > tracing_on
|
||||
}
|
||||
|
||||
fail() { #msg
|
||||
cat trace
|
||||
do_reset
|
||||
echo $1
|
||||
exit_fail
|
||||
}
|
||||
|
||||
count_pid() {
|
||||
pid=$@
|
||||
cat trace | grep -v '^#' | sed -e 's/[^-]*-\([0-9]*\).*/\1/' | grep $pid | wc -l
|
||||
}
|
||||
|
||||
count_no_pid() {
|
||||
pid=$1
|
||||
cat trace | grep -v '^#' | sed -e 's/[^-]*-\([0-9]*\).*/\1/' | grep -v $pid | wc -l
|
||||
}
|
||||
|
||||
enable_system() {
|
||||
system=$1
|
||||
|
||||
if [ -d events/$system ]; then
|
||||
echo 1 > events/$system/enable
|
||||
fi
|
||||
}
|
||||
|
||||
enable_events() {
|
||||
echo 0 > tracing_on
|
||||
# Enable common groups of events, as all events can allow for
|
||||
# events to be traced via scheduling that we don't care to test.
|
||||
enable_system syscalls
|
||||
enable_system rcu
|
||||
enable_system block
|
||||
enable_system exceptions
|
||||
enable_system irq
|
||||
enable_system net
|
||||
enable_system power
|
||||
enable_system signal
|
||||
enable_system sock
|
||||
enable_system timer
|
||||
enable_system thermal
|
||||
echo 1 > tracing_on
|
||||
}
|
||||
|
||||
if [ ! -f set_event -o ! -d events/sched ]; then
|
||||
echo "event tracing is not supported"
|
||||
exit_unsupported
|
||||
fi
|
||||
|
||||
if [ ! -f set_event_pid -o ! -f set_event_notrace_pid ]; then
|
||||
echo "event pid notrace filtering is not supported"
|
||||
exit_unsupported
|
||||
fi
|
||||
|
||||
echo 0 > options/event-fork
|
||||
|
||||
do_reset
|
||||
|
||||
read mypid rest < /proc/self/stat
|
||||
|
||||
echo $mypid > set_event_notrace_pid
|
||||
grep -q $mypid set_event_notrace_pid
|
||||
|
||||
enable_events
|
||||
|
||||
yield
|
||||
|
||||
echo 0 > tracing_on
|
||||
|
||||
cnt=`count_pid $mypid`
|
||||
if [ $cnt -ne 0 ]; then
|
||||
fail "Filtered out task has events"
|
||||
fi
|
||||
|
||||
cnt=`count_no_pid $mypid`
|
||||
if [ $cnt -eq 0 ]; then
|
||||
fail "No other events were recorded"
|
||||
fi
|
||||
|
||||
do_reset
|
||||
|
||||
echo $mypid > set_event_notrace_pid
|
||||
echo 1 > options/event-fork
|
||||
|
||||
enable_events
|
||||
|
||||
yield &
|
||||
child=$!
|
||||
echo "child = $child"
|
||||
wait $child
|
||||
|
||||
echo 0 > tracing_on
|
||||
|
||||
cnt=`count_pid $mypid`
|
||||
if [ $cnt -ne 0 ]; then
|
||||
fail "Filtered out task has events"
|
||||
fi
|
||||
|
||||
cnt=`count_pid $child`
|
||||
if [ $cnt -ne 0 ]; then
|
||||
fail "Child of filtered out taskhas events"
|
||||
fi
|
||||
|
||||
cnt=`count_no_pid $mypid`
|
||||
if [ $cnt -eq 0 ]; then
|
||||
fail "No other events were recorded"
|
||||
fi
|
||||
|
||||
do_reset
|
||||
|
||||
exit 0
|
@ -0,0 +1,108 @@
|
||||
#!/bin/sh
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
# description: ftrace - function pid notrace filters
|
||||
# flags: instance
|
||||
|
||||
# Make sure that function pid matching filter with notrace works.
|
||||
|
||||
if ! grep -q function available_tracers; then
|
||||
echo "no function tracer configured"
|
||||
exit_unsupported
|
||||
fi
|
||||
|
||||
if [ ! -f set_ftrace_notrace_pid ]; then
|
||||
echo "set_ftrace_notrace_pid not found? Is function tracer not set?"
|
||||
exit_unsupported
|
||||
fi
|
||||
|
||||
if [ ! -f set_ftrace_filter ]; then
|
||||
echo "set_ftrace_filter not found? Is function tracer not set?"
|
||||
exit_unsupported
|
||||
fi
|
||||
|
||||
do_function_fork=1
|
||||
|
||||
if [ ! -f options/function-fork ]; then
|
||||
do_function_fork=0
|
||||
echo "no option for function-fork found. Option will not be tested."
|
||||
fi
|
||||
|
||||
read PID _ < /proc/self/stat
|
||||
|
||||
if [ $do_function_fork -eq 1 ]; then
|
||||
# default value of function-fork option
|
||||
orig_value=`grep function-fork trace_options`
|
||||
fi
|
||||
|
||||
do_reset() {
|
||||
if [ $do_function_fork -eq 0 ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
echo > set_ftrace_notrace_pid
|
||||
echo $orig_value > trace_options
|
||||
}
|
||||
|
||||
fail() { # msg
|
||||
do_reset
|
||||
echo $1
|
||||
exit_fail
|
||||
}
|
||||
|
||||
do_test() {
|
||||
disable_tracing
|
||||
|
||||
echo do_execve* > set_ftrace_filter
|
||||
echo *do_fork >> set_ftrace_filter
|
||||
|
||||
echo $PID > set_ftrace_notrace_pid
|
||||
echo function > current_tracer
|
||||
|
||||
if [ $do_function_fork -eq 1 ]; then
|
||||
# don't allow children to be traced
|
||||
echo nofunction-fork > trace_options
|
||||
fi
|
||||
|
||||
enable_tracing
|
||||
yield
|
||||
|
||||
count_pid=`cat trace | grep -v ^# | grep $PID | wc -l`
|
||||
count_other=`cat trace | grep -v ^# | grep -v $PID | wc -l`
|
||||
|
||||
# count_pid should be 0
|
||||
if [ $count_pid -ne 0 -o $count_other -eq 0 ]; then
|
||||
fail "PID filtering not working? traced task = $count_pid; other tasks = $count_other "
|
||||
fi
|
||||
|
||||
disable_tracing
|
||||
clear_trace
|
||||
|
||||
if [ $do_function_fork -eq 0 ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
# allow children to be traced
|
||||
echo function-fork > trace_options
|
||||
|
||||
# With pid in both set_ftrace_pid and set_ftrace_notrace_pid
|
||||
# there should not be any tasks traced.
|
||||
|
||||
echo $PID > set_ftrace_pid
|
||||
|
||||
enable_tracing
|
||||
yield
|
||||
|
||||
count_pid=`cat trace | grep -v ^# | grep $PID | wc -l`
|
||||
count_other=`cat trace | grep -v ^# | grep -v $PID | wc -l`
|
||||
|
||||
# both should be zero
|
||||
if [ $count_pid -ne 0 -o $count_other -ne 0 ]; then
|
||||
fail "PID filtering not following fork? traced task = $count_pid; other tasks = $count_other "
|
||||
fi
|
||||
}
|
||||
|
||||
do_test
|
||||
|
||||
do_reset
|
||||
|
||||
exit 0
|
@ -41,7 +41,7 @@ fi
|
||||
|
||||
echo '** ENABLE EVENTS'
|
||||
|
||||
echo 1 > events/enable
|
||||
echo 1 > events/sched/enable
|
||||
|
||||
echo '** ENABLE TRACING'
|
||||
enable_tracing
|
||||
|
Loading…
Reference in New Issue
Block a user