mirror of
https://github.com/torvalds/linux.git
synced 2026-11-06 21:58:01 +01:00
tracing updates for v7.4:
- Add "within" to filter logic Add a way to trace events by what function they fall in. echo 'within == "vfs_read" > events/sched/sched_switch/filter Will only trigger sched_switch events that happen within the vfs_read function. It uses a stacktrace at the event and checks if any entry is within the specified function and if so, it prints the event. - Remove a duplicate definition of ftrace_set_clr_event() from the header - Do not have ftrace_set_clr_event() modify its input buffer Have the buffer passed into ftrace_set_clr_event() to be const char * and not allow modifications to it that might cause side effects. - Add kunit tests for ftrace_parse_event_string() - Add filter argument to __ftrace_set_clr_event() Make it possible to set a filter on an event at the same time it is enabled. - Add event filtering logic to the kernel command line To allow filtering events on boot, allow filtering to be specified on the kernel command line when enabling events. - Rename "ring_buffer_size()" to "ring_buffer_capacity()" "size" refers to how much is allocated whereas the actual return value is the amount of available data in the ring buffer minus its meta data. - Rename "trace_array_entries" to "trace_array_capacity" There's several functions that use "entries" and returns the amount of data that that is usable. "entries" should be only for the number of events, not the amount of data. - Print the histogram "bucket" size as unsigned. If a bucket size is LONG_MAX, then it shows up as a negative number. - Show bucket size in output of histograms The output of what the histogram is shown doesn't show the size of the buckets when buckets are used. The output should be the same as the input and since specifying buckets without a size is an invalid command, the output to display what was entered should also show the size. - Fix stacktrace modifier output Only show the stacktrace modifier when it was specified in creation. - Unregister filed variable histograms when the command fails If one histogram references a variable of another histogram it registers that other histogram. But if the first histogram fails in setup after it registered the other histogram it doesn't unregister it. Have the first histogram add the reference to the other histogram so the failure clean up will unregister the second histogram too. - Fix 64-bit signed histogram fields on 32-bit kernels A fix to convert char * to u64 with added casting also incorrectly added an intermediate cast to convert a long value to u64 which ended up truncating the upper 32 bits for 32-bit kernels. Remove the intermediate typecast. - Print signed histogram keys as signed Histogram keys were being printed as unsigned and with signed keys it was giving the wrong output. - Use copy_from_user() and not __copy_from_user() in syscall tracing The syscall tracing incorrectly was using __copy_from_user() which could be an issue if the system trusts perf doing the trace. - Take trace_types_lock when recording a new module in persistent buffer When a new module is loaded, the persistent ring buffer saves it in its persistent memory. All the trace_arrays are iterated looking for any persistent buffer to add the module to. But it did not hold the trace_types_lock meaning a trace_array could be added or removed while iterating the list. - Only create tracepoint guest_halt_poll_ns when it is used The tracepoint guest_halt_poll_ns is defined in the power management code but it is only used when CONFIG_CPU_IDLE_GOV_HALTPOLL is enabled. This wastes memory when that config isn't enabled so wrap the tracepoint around that as well. - Handle empty fields for events in trace_event_get_offsets() The user events could have empty fields and this causes trace_event_get_offsets() to crash as it expects events to always have fields. Make the function handle events with no fields. - Reject incomplete event filter expressions If a filter has an incomplete expression (common_pid == 1111 || ), it will cause a out-of-bounds bug. Have the predicate_parse() only process the expression if it is complete. -----BEGIN PGP SIGNATURE----- iIoEABYKADIWIQRRSw7ePDh/lE+zeZMp5XQQmuv6qgUCatwDfRQccm9zdGVkdEBn b29kbWlzLm9yZwAKCRAp5XQQmuv6qk8TAP9buI4fQRvXGetyth9F/j8qGgTAEv19 sL5B48A/HDx2wgD+O6FcFAwwNKTjJHfHPArJUcQMp/D17rNDFSoF4T0kFQU= =F2J6 -----END PGP SIGNATURE----- Merge tag 'trace-v7.4' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace Pull tracing updates from Steven Rostedt: - Add "within" to filter logic Add a way to trace events by what function they fall in. echo 'within == "vfs_read" > events/sched/sched_switch/filter Will only trigger sched_switch events that happen within the vfs_read function. It uses a stacktrace at the event and checks if any entry is within the specified function and if so, it prints the event. - Remove a duplicate definition of ftrace_set_clr_event() from the header - Do not have ftrace_set_clr_event() modify its input buffer Have the buffer passed into ftrace_set_clr_event() to be const char * and not allow modifications to it that might cause side effects. - Add kunit tests for ftrace_parse_event_string() - Add filter argument to __ftrace_set_clr_event() Make it possible to set a filter on an event at the same time it is enabled. - Add event filtering logic to the kernel command line To allow filtering events on boot, allow filtering to be specified on the kernel command line when enabling events. - Rename "ring_buffer_size()" to "ring_buffer_capacity()" "size" refers to how much is allocated whereas the actual return value is the amount of available data in the ring buffer minus its meta data. - Rename "trace_array_entries" to "trace_array_capacity" There's several functions that use "entries" and returns the amount of data that that is usable. "entries" should be only for the number of events, not the amount of data. - Print the histogram "bucket" size as unsigned. If a bucket size is LONG_MAX, then it shows up as a negative number. - Show bucket size in output of histograms The output of what the histogram is shown doesn't show the size of the buckets when buckets are used. The output should be the same as the input and since specifying buckets without a size is an invalid command, the output to display what was entered should also show the size. - Fix stacktrace modifier output Only show the stacktrace modifier when it was specified in creation. - Unregister filed variable histograms when the command fails If one histogram references a variable of another histogram it registers that other histogram. But if the first histogram fails in setup after it registered the other histogram it doesn't unregister it. Have the first histogram add the reference to the other histogram so the failure clean up will unregister the second histogram too. - Fix 64-bit signed histogram fields on 32-bit kernels A fix to convert char * to u64 with added casting also incorrectly added an intermediate cast to convert a long value to u64 which ended up truncating the upper 32 bits for 32-bit kernels. Remove the intermediate typecast. - Print signed histogram keys as signed Histogram keys were being printed as unsigned and with signed keys it was giving the wrong output. - Use copy_from_user() and not __copy_from_user() in syscall tracing The syscall tracing incorrectly was using __copy_from_user() which could be an issue if the system trusts perf doing the trace. - Take trace_types_lock when recording a new module in persistent buffer When a new module is loaded, the persistent ring buffer saves it in its persistent memory. All the trace_arrays are iterated looking for any persistent buffer to add the module to. But it did not hold the trace_types_lock meaning a trace_array could be added or removed while iterating the list. - Only create tracepoint guest_halt_poll_ns when it is used The tracepoint guest_halt_poll_ns is defined in the power management code but it is only used when CONFIG_CPU_IDLE_GOV_HALTPOLL is enabled. This wastes memory when that config isn't enabled so wrap the tracepoint around that as well. - Handle empty fields for events in trace_event_get_offsets() The user events could have empty fields and this causes trace_event_get_offsets() to crash as it expects events to always have fields. Make the function handle events with no fields. - Reject incomplete event filter expressions If a filter has an incomplete expression (common_pid == 1111 || ), it will cause a out-of-bounds bug. Have the predicate_parse() only process the expression if it is complete. * tag 'trace-v7.4' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace: (27 commits) tracing: Reject incomplete event filter expressions tracing: Guard trace_event_get_offsets() against an empty fields list cpuidle: haltpoll: Only define guest_halt_poll_ns tracepoint when used tracing/osnoise: Add __printf attribute to osnoise_print() tracing/histograms: Verify event_call identity when updating synth_event refcount tracing: Save the event file instead of the compatible histogram tracing: Hold trace_types_lock when resetting last boot data from readers tracing: Hold trace_types_lock when updating modules in persistent instances tracing/syscalls: Use copy_from_user for the faultable syscall buffer tracing: Print a signed histogram key as signed tracing: Fix 64-bit and signed histogram fields on 32-bit kernels tracing: Clamp the printed end of a histogram bucket at U64_MAX tracing: Unregister field variable histograms when the command fails tracing: Only report the stacktrace modifier on a real field tracing: Add the bucket size to expr_field_str() tracing: Print the bucket size as unsigned tracing: Rename tracing_entries_fops to tracing_capacity_fops tracing: Rename trace_array::entries to capacity ring-buffer: Rename ring_buffer_size() to ring_buffer_capacity() tracing: Parse filter from boot event string ...
This commit is contained in:
commit
59a02493f3
|
|
@ -7748,8 +7748,13 @@ Kernel parameters
|
|||
trace_event=:mod:<module>
|
||||
|
||||
The value before :mod: will only enable specific events
|
||||
that are part of the module. See the above mentioned
|
||||
document for more information.
|
||||
that are part of the module.
|
||||
|
||||
To apply a filter, use the :filter: keyword:
|
||||
|
||||
trace_event=...:filter:<filter>
|
||||
|
||||
See the above mentioned document for more information.
|
||||
|
||||
trace_instance=[instance-info]
|
||||
[FTRACE] Create a ring buffer instance early in boot up.
|
||||
|
|
|
|||
|
|
@ -118,6 +118,11 @@ In order to facilitate early boot debugging, use boot option::
|
|||
event-list is a comma separated list of events. See section 2.1 for event
|
||||
format.
|
||||
|
||||
The boot option also supports the ``:filter:`` keyword to apply a filter.
|
||||
The ``:filter:`` keyword needs to be at the end of the event specification::
|
||||
|
||||
<system>:<event>:mod:<module>:filter:<filter>
|
||||
|
||||
3. Defining an event-enabled tracepoint
|
||||
=======================================
|
||||
|
||||
|
|
@ -243,6 +248,18 @@ the function "security_prepare_creds" and less than the end of that function.
|
|||
The ".function" postfix can only be attached to values of size long, and can only
|
||||
be compared with "==" or "!=".
|
||||
|
||||
The special field "within" can be used to filter events based on whether
|
||||
a specific function appears in the current call stack::
|
||||
|
||||
within == "function_name"
|
||||
within != "function_name"
|
||||
|
||||
For example, to only trace events where "vfs_read" is in the call stack::
|
||||
|
||||
# echo 'within == "vfs_read"' > events/sched/sched_switch/filter
|
||||
|
||||
The within field supports only the "==" and "!=" operators.
|
||||
|
||||
Cpumask fields or scalar fields that encode a CPU number can be filtered using
|
||||
a user-provided cpumask in cpulist format. The format is as follows::
|
||||
|
||||
|
|
|
|||
|
|
@ -1943,6 +1943,9 @@ the ".buckets" modifier and specify a size (in this case groups of 10)::
|
|||
Entries: 16
|
||||
Dropped: 0
|
||||
|
||||
Keys are 64-bit, so the group holding the largest values is truncated where
|
||||
that range ends and can be smaller than the size asked for.
|
||||
|
||||
To save stacktraces, create a synthetic event with a field of type "unsigned long[]"
|
||||
or even just "long[]". For example, to see how long a task is blocked in an
|
||||
uninterruptible state::
|
||||
|
|
|
|||
|
|
@ -173,7 +173,7 @@ 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);
|
||||
unsigned long ring_buffer_capacity(struct trace_buffer *buffer, int cpu);
|
||||
unsigned long ring_buffer_max_event_size(struct trace_buffer *buffer);
|
||||
|
||||
void ring_buffer_reset_cpu(struct trace_buffer *buffer, int cpu);
|
||||
|
|
|
|||
|
|
@ -872,6 +872,7 @@ enum {
|
|||
FILTER_COMM,
|
||||
FILTER_CPU,
|
||||
FILTER_STACKTRACE,
|
||||
FILTER_WITHIN,
|
||||
};
|
||||
|
||||
extern int trace_event_raw_init(struct trace_event_call *call);
|
||||
|
|
@ -882,7 +883,7 @@ extern int trace_add_event_call(struct trace_event_call *call);
|
|||
extern int trace_remove_event_call(struct trace_event_call *call);
|
||||
extern int trace_event_get_offsets(struct trace_event_call *call);
|
||||
|
||||
int ftrace_set_clr_event(struct trace_array *tr, char *buf, int set);
|
||||
int ftrace_set_clr_event(struct trace_array *tr, const char *buf, int set, bool handle_filter);
|
||||
int trace_set_clr_event(const char *system, const char *event, int set);
|
||||
int trace_array_set_clr_event(struct trace_array *tr, const char *system,
|
||||
const char *event, bool enable);
|
||||
|
|
|
|||
|
|
@ -497,6 +497,7 @@ DEFINE_EVENT(dev_pm_qos_request, dev_pm_qos_remove_request,
|
|||
TP_ARGS(name, type, new_value)
|
||||
);
|
||||
|
||||
#ifdef CONFIG_CPU_IDLE_GOV_HALTPOLL
|
||||
TRACE_EVENT(guest_halt_poll_ns,
|
||||
|
||||
TP_PROTO(bool grow, unsigned int new, unsigned int old),
|
||||
|
|
@ -525,6 +526,8 @@ TRACE_EVENT(guest_halt_poll_ns,
|
|||
trace_guest_halt_poll_ns(true, new, old)
|
||||
#define trace_guest_halt_poll_ns_shrink(new, old) \
|
||||
trace_guest_halt_poll_ns(false, new, old)
|
||||
#endif /* CONFIG_CPU_IDLE_GOV_HALTPOLL */
|
||||
|
||||
#endif /* _TRACE_POWER_H */
|
||||
|
||||
/* This part must be outside protection */
|
||||
|
|
|
|||
|
|
@ -151,6 +151,14 @@ config EVENT_TRACING
|
|||
select GLOB
|
||||
bool
|
||||
|
||||
config EVENT_TRACING_TEST
|
||||
tristate "Test for event tracing" if !KUNIT_ALL_TESTS
|
||||
depends on EVENT_TRACING
|
||||
depends on KUNIT
|
||||
default KUNIT_ALL_TESTS
|
||||
help
|
||||
KUnit test for the event tracing implementation.
|
||||
|
||||
config CONTEXT_SWITCH_TRACER
|
||||
bool
|
||||
|
||||
|
|
|
|||
|
|
@ -96,6 +96,7 @@ obj-$(CONFIG_EVENT_TRACING) += blktrace.o
|
|||
endif
|
||||
obj-$(CONFIG_EVENT_TRACING) += trace_events.o
|
||||
obj-$(CONFIG_EVENT_TRACING) += trace_export.o
|
||||
obj-$(CONFIG_EVENT_TRACING_TEST) += trace_events_test.o
|
||||
obj-$(CONFIG_FTRACE_SYSCALLS) += trace_syscalls.o
|
||||
ifeq ($(CONFIG_PERF_EVENTS),y)
|
||||
obj-$(CONFIG_EVENT_TRACING) += trace_event_perf.o
|
||||
|
|
|
|||
|
|
@ -6548,18 +6548,23 @@ void ring_buffer_iter_advance(struct ring_buffer_iter *iter)
|
|||
EXPORT_SYMBOL_GPL(ring_buffer_iter_advance);
|
||||
|
||||
/**
|
||||
* ring_buffer_size - return the size of the ring buffer (in bytes)
|
||||
* ring_buffer_capacity - return the capacity of the ring buffer (in bytes)
|
||||
* @buffer: The ring buffer.
|
||||
* @cpu: The CPU to get ring buffer size from.
|
||||
* @cpu: The CPU to get ring buffer capacity from.
|
||||
*
|
||||
* Capacity is the total memory available for recording event data in the
|
||||
* ring buffer. It excludes subbuffer headers.
|
||||
*
|
||||
* Returns the capacity in bytes, or 0 if the CPU is not set in the buffer mask.
|
||||
*/
|
||||
unsigned long ring_buffer_size(struct trace_buffer *buffer, int cpu)
|
||||
unsigned long ring_buffer_capacity(struct trace_buffer *buffer, int cpu)
|
||||
{
|
||||
if (!cpumask_test_cpu(cpu, buffer->cpumask))
|
||||
return 0;
|
||||
|
||||
return rb_subbuf_capacity(buffer) * buffer->buffers[cpu]->nr_pages;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ring_buffer_size);
|
||||
EXPORT_SYMBOL_GPL(ring_buffer_capacity);
|
||||
|
||||
/**
|
||||
* ring_buffer_max_event_size - return the max data size of an event
|
||||
|
|
|
|||
|
|
@ -3614,6 +3614,7 @@ static int tracing_single_release_tr(struct inode *inode, struct file *file)
|
|||
return single_release(inode, file);
|
||||
}
|
||||
|
||||
static bool __update_last_data_if_empty(struct trace_array *tr);
|
||||
static bool update_last_data_if_empty(struct trace_array *tr);
|
||||
|
||||
static int tracing_open(struct inode *inode, struct file *file)
|
||||
|
|
@ -4708,24 +4709,24 @@ tracing_set_trace_read(struct file *filp, char __user *ubuf,
|
|||
int tracer_init(struct tracer *t, struct trace_array *tr)
|
||||
{
|
||||
tracing_reset_online_cpus(&tr->array_buffer);
|
||||
update_last_data_if_empty(tr);
|
||||
__update_last_data_if_empty(tr);
|
||||
return t->init(tr);
|
||||
}
|
||||
|
||||
void trace_set_buffer_entries(struct array_buffer *buf, unsigned long val)
|
||||
void trace_set_buffer_capacity(struct array_buffer *buf, unsigned long val)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
for_each_tracing_cpu(cpu)
|
||||
per_cpu_ptr(buf->data, cpu)->entries = val;
|
||||
per_cpu_ptr(buf->data, cpu)->capacity = val;
|
||||
}
|
||||
|
||||
static void update_buffer_entries(struct array_buffer *buf, int cpu)
|
||||
static void update_buffer_capacity(struct array_buffer *buf, int cpu)
|
||||
{
|
||||
if (cpu == RING_BUFFER_ALL_CPUS) {
|
||||
trace_set_buffer_entries(buf, ring_buffer_size(buf->buffer, 0));
|
||||
trace_set_buffer_capacity(buf, ring_buffer_capacity(buf->buffer, 0));
|
||||
} else {
|
||||
per_cpu_ptr(buf->data, cpu)->entries = ring_buffer_size(buf->buffer, cpu);
|
||||
per_cpu_ptr(buf->data, cpu)->capacity = ring_buffer_capacity(buf->buffer, cpu);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -4781,12 +4782,12 @@ static int __tracing_resize_ring_buffer(struct trace_array *tr,
|
|||
goto out_start;
|
||||
}
|
||||
|
||||
update_buffer_entries(&tr->snapshot_buffer, cpu);
|
||||
update_buffer_capacity(&tr->snapshot_buffer, cpu);
|
||||
|
||||
out:
|
||||
#endif /* CONFIG_TRACER_SNAPSHOT */
|
||||
|
||||
update_buffer_entries(&tr->array_buffer, cpu);
|
||||
update_buffer_capacity(&tr->array_buffer, cpu);
|
||||
out_start:
|
||||
tracing_start_tr(tr);
|
||||
return ret;
|
||||
|
|
@ -5387,8 +5388,10 @@ static int tracing_wait_pipe(struct file *filp)
|
|||
return 1;
|
||||
}
|
||||
|
||||
static bool update_last_data_if_empty(struct trace_array *tr)
|
||||
static bool __update_last_data_if_empty(struct trace_array *tr)
|
||||
{
|
||||
lockdep_assert_held(&trace_types_lock);
|
||||
|
||||
if (!(tr->flags & TRACE_ARRAY_FL_LAST_BOOT))
|
||||
return false;
|
||||
|
||||
|
|
@ -5403,6 +5406,16 @@ static bool update_last_data_if_empty(struct trace_array *tr)
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool update_last_data_if_empty(struct trace_array *tr)
|
||||
{
|
||||
/* Avoid the lock on the read path unless this is a last boot buffer */
|
||||
if (!(tr->flags & TRACE_ARRAY_FL_LAST_BOOT))
|
||||
return false;
|
||||
|
||||
guard(mutex)(&trace_types_lock);
|
||||
return __update_last_data_if_empty(tr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Consumer reader.
|
||||
*/
|
||||
|
|
@ -5688,8 +5701,7 @@ tracing_syscall_buf_write(struct file *filp, const char __user *ubuf,
|
|||
}
|
||||
|
||||
static ssize_t
|
||||
tracing_entries_read(struct file *filp, char __user *ubuf,
|
||||
size_t cnt, loff_t *ppos)
|
||||
tracing_capacity_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos)
|
||||
{
|
||||
struct inode *inode = file_inode(filp);
|
||||
struct trace_array *tr = inode->i_private;
|
||||
|
|
@ -5710,8 +5722,8 @@ tracing_entries_read(struct file *filp, char __user *ubuf,
|
|||
for_each_tracing_cpu(cpu) {
|
||||
/* fill in the size from first enabled cpu */
|
||||
if (size == 0)
|
||||
size = per_cpu_ptr(tr->array_buffer.data, cpu)->entries;
|
||||
if (size != per_cpu_ptr(tr->array_buffer.data, cpu)->entries) {
|
||||
size = per_cpu_ptr(tr->array_buffer.data, cpu)->capacity;
|
||||
if (size != per_cpu_ptr(tr->array_buffer.data, cpu)->capacity) {
|
||||
buf_size_same = 0;
|
||||
break;
|
||||
}
|
||||
|
|
@ -5727,7 +5739,7 @@ tracing_entries_read(struct file *filp, char __user *ubuf,
|
|||
} else
|
||||
r = sprintf(buf, "X\n");
|
||||
} else
|
||||
r = sprintf(buf, "%lu\n", per_cpu_ptr(tr->array_buffer.data, cpu)->entries >> 10);
|
||||
r = sprintf(buf, "%lu\n", per_cpu_ptr(tr->array_buffer.data, cpu)->capacity >> 10);
|
||||
|
||||
mutex_unlock(&trace_types_lock);
|
||||
|
||||
|
|
@ -5736,8 +5748,7 @@ tracing_entries_read(struct file *filp, char __user *ubuf,
|
|||
}
|
||||
|
||||
static ssize_t
|
||||
tracing_entries_write(struct file *filp, const char __user *ubuf,
|
||||
size_t cnt, loff_t *ppos)
|
||||
tracing_capacity_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos)
|
||||
{
|
||||
struct inode *inode = file_inode(filp);
|
||||
struct trace_array *tr = inode->i_private;
|
||||
|
|
@ -5764,8 +5775,7 @@ tracing_entries_write(struct file *filp, const char __user *ubuf,
|
|||
}
|
||||
|
||||
static ssize_t
|
||||
tracing_total_entries_read(struct file *filp, char __user *ubuf,
|
||||
size_t cnt, loff_t *ppos)
|
||||
tracing_total_capacity_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos)
|
||||
{
|
||||
struct trace_array *tr = filp->private_data;
|
||||
char buf[64];
|
||||
|
|
@ -5774,7 +5784,7 @@ tracing_total_entries_read(struct file *filp, char __user *ubuf,
|
|||
|
||||
mutex_lock(&trace_types_lock);
|
||||
for_each_tracing_cpu(cpu) {
|
||||
size += per_cpu_ptr(tr->array_buffer.data, cpu)->entries >> 10;
|
||||
size += per_cpu_ptr(tr->array_buffer.data, cpu)->capacity >> 10;
|
||||
if (!tr->ring_buffer_expanded)
|
||||
expanded_size += trace_buf_size >> 10;
|
||||
}
|
||||
|
|
@ -6495,7 +6505,7 @@ int tracing_set_clock(struct trace_array *tr, const char *clockstr)
|
|||
ring_buffer_set_clock(tr->snapshot_buffer.buffer, trace_clocks[i].func);
|
||||
tracing_reset_online_cpus(&tr->snapshot_buffer);
|
||||
#endif
|
||||
update_last_data_if_empty(tr);
|
||||
__update_last_data_if_empty(tr);
|
||||
|
||||
if (tr->scratch && !(tr->flags & TRACE_ARRAY_FL_LAST_BOOT)) {
|
||||
struct trace_scratch *tscratch = tr->scratch;
|
||||
|
|
@ -6616,10 +6626,10 @@ static const struct file_operations tracing_pipe_fops = {
|
|||
.release = tracing_release_pipe,
|
||||
};
|
||||
|
||||
static const struct file_operations tracing_entries_fops = {
|
||||
static const struct file_operations tracing_capacity_fops = {
|
||||
.open = tracing_open_generic_tr,
|
||||
.read = tracing_entries_read,
|
||||
.write = tracing_entries_write,
|
||||
.read = tracing_capacity_read,
|
||||
.write = tracing_capacity_write,
|
||||
.llseek = generic_file_llseek,
|
||||
.release = tracing_release_generic_tr,
|
||||
};
|
||||
|
|
@ -6639,9 +6649,9 @@ static const struct file_operations tracing_buffer_meta_fops = {
|
|||
.release = tracing_seq_release,
|
||||
};
|
||||
|
||||
static const struct file_operations tracing_total_entries_fops = {
|
||||
static const struct file_operations tracing_total_capacity_fops = {
|
||||
.open = tracing_open_generic_tr,
|
||||
.read = tracing_total_entries_read,
|
||||
.read = tracing_total_capacity_read,
|
||||
.llseek = generic_file_llseek,
|
||||
.release = tracing_release_generic_tr,
|
||||
};
|
||||
|
|
@ -7669,7 +7679,7 @@ tracing_init_tracefs_percpu(struct trace_array *tr, long cpu)
|
|||
tr, cpu, &tracing_stats_fops);
|
||||
|
||||
trace_create_cpu_file("buffer_size_kb", TRACE_MODE_WRITE, d_cpu,
|
||||
tr, cpu, &tracing_entries_fops);
|
||||
tr, cpu, &tracing_capacity_fops);
|
||||
|
||||
if (tr->range_addr_start)
|
||||
trace_create_cpu_file("buffer_meta", TRACE_MODE_READ, d_cpu,
|
||||
|
|
@ -8535,8 +8545,8 @@ int allocate_trace_buffer(struct trace_array *tr, struct array_buffer *buf, int
|
|||
}
|
||||
|
||||
/* Allocate the first page for all buffers */
|
||||
trace_set_buffer_entries(&tr->array_buffer,
|
||||
ring_buffer_size(tr->array_buffer.buffer, 0));
|
||||
trace_set_buffer_capacity(&tr->array_buffer,
|
||||
ring_buffer_capacity(tr->array_buffer.buffer, 0));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -9014,10 +9024,10 @@ init_tracer_tracefs(struct trace_array *tr, struct dentry *d_tracer)
|
|||
tr, &tracing_pipe_fops);
|
||||
|
||||
trace_create_file("buffer_size_kb", writable_mode, d_tracer,
|
||||
tr, &tracing_entries_fops);
|
||||
tr, &tracing_capacity_fops);
|
||||
|
||||
trace_create_file("buffer_total_size_kb", TRACE_MODE_READ, d_tracer,
|
||||
tr, &tracing_total_entries_fops);
|
||||
tr, &tracing_total_capacity_fops);
|
||||
|
||||
trace_create_file("trace_clock", writable_mode, d_tracer, tr,
|
||||
&trace_clock_fops);
|
||||
|
|
@ -9263,6 +9273,8 @@ static void trace_module_record(struct module *mod, bool add)
|
|||
struct trace_array *tr;
|
||||
unsigned long flags;
|
||||
|
||||
guard(mutex)(&trace_types_lock);
|
||||
|
||||
list_for_each_entry(tr, &ftrace_trace_arrays, list) {
|
||||
flags = tr->flags & (TRACE_ARRAY_FL_BOOT | TRACE_ARRAY_FL_LAST_BOOT);
|
||||
/* Update any persistent trace array that has already been started */
|
||||
|
|
|
|||
|
|
@ -191,7 +191,7 @@ struct trace_array;
|
|||
struct trace_array_cpu {
|
||||
local_t disabled;
|
||||
|
||||
unsigned long entries;
|
||||
unsigned long capacity;
|
||||
unsigned long saved_latency;
|
||||
unsigned long critical_start;
|
||||
unsigned long critical_end;
|
||||
|
|
@ -731,7 +731,7 @@ ssize_t tracing_nsecs_read(unsigned long *ptr, char __user *ubuf,
|
|||
ssize_t tracing_nsecs_write(unsigned long *ptr, const char __user *ubuf,
|
||||
size_t cnt, loff_t *ppos);
|
||||
|
||||
void trace_set_buffer_entries(struct array_buffer *buf, unsigned long val);
|
||||
void trace_set_buffer_capacity(struct array_buffer *buf, unsigned long val);
|
||||
|
||||
/*
|
||||
* Should be used after trace_array_get(), trace_types_lock
|
||||
|
|
@ -1823,7 +1823,8 @@ static inline bool is_string_field(struct ftrace_event_field *field)
|
|||
field->filter_type == FILTER_RDYN_STRING ||
|
||||
field->filter_type == FILTER_STATIC_STRING ||
|
||||
field->filter_type == FILTER_PTR_STRING ||
|
||||
field->filter_type == FILTER_COMM;
|
||||
field->filter_type == FILTER_COMM ||
|
||||
field->filter_type == FILTER_WITHIN;
|
||||
}
|
||||
|
||||
static inline bool is_function_field(struct ftrace_event_field *field)
|
||||
|
|
@ -2286,7 +2287,6 @@ static inline const char *get_syscall_name(int syscall)
|
|||
void trace_event_init(void);
|
||||
void trace_event_update_all(struct trace_eval_map **map, int len, struct module *mod);
|
||||
/* Used from boot time tracer */
|
||||
extern int ftrace_set_clr_event(struct trace_array *tr, char *buf, int set);
|
||||
extern int trigger_process_regex(struct trace_event_file *file, char *buff);
|
||||
#else
|
||||
static inline void __init trace_event_init(void) { }
|
||||
|
|
@ -2506,3 +2506,8 @@ static inline int rv_init_interface(void)
|
|||
})
|
||||
|
||||
#endif /* _LINUX_KERNEL_TRACE_H */
|
||||
|
||||
#if IS_ENABLED(CONFIG_KUNIT)
|
||||
void ftrace_parse_event_string(char *buf, char **match, char **sub, char **event, char **mod,
|
||||
bool handle_filter, char **filter);
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ trace_boot_enable_events(struct trace_array *tr, struct xbc_node *node)
|
|||
continue;
|
||||
}
|
||||
|
||||
if (ftrace_set_clr_event(tr, buf, 1) < 0)
|
||||
if (ftrace_set_clr_event(tr, buf, 1, false) < 0)
|
||||
pr_err("Failed to enable event: %s\n", p);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@
|
|||
#include <linux/delay.h>
|
||||
#include <linux/btf.h>
|
||||
|
||||
#include <kunit/visibility.h>
|
||||
|
||||
#include <trace/events/sched.h>
|
||||
#include <trace/syscall.h>
|
||||
|
||||
|
|
@ -201,6 +203,9 @@ static int trace_define_generic_fields(void)
|
|||
__generic_field(char *, comm, FILTER_COMM);
|
||||
__generic_field(char *, stacktrace, FILTER_STACKTRACE);
|
||||
__generic_field(char *, STACKTRACE, FILTER_STACKTRACE);
|
||||
#ifdef CONFIG_STACKTRACE
|
||||
__generic_field(char *, within, FILTER_WITHIN);
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -241,6 +246,10 @@ int trace_event_get_offsets(struct trace_event_call *call)
|
|||
struct list_head *head;
|
||||
|
||||
head = trace_get_fields(call);
|
||||
|
||||
if (list_empty(head))
|
||||
return sizeof(struct trace_entry);
|
||||
|
||||
/*
|
||||
* head->next points to the last field with the largest offset,
|
||||
* since it was added last by trace_define_field()
|
||||
|
|
@ -941,6 +950,7 @@ struct event_mod_load {
|
|||
char *match;
|
||||
char *system;
|
||||
char *event;
|
||||
char *filter;
|
||||
};
|
||||
|
||||
static void free_event_mod(struct event_mod_load *event_mod)
|
||||
|
|
@ -950,6 +960,7 @@ static void free_event_mod(struct event_mod_load *event_mod)
|
|||
kfree(event_mod->match);
|
||||
kfree(event_mod->system);
|
||||
kfree(event_mod->event);
|
||||
kfree(event_mod->filter);
|
||||
kfree(event_mod);
|
||||
}
|
||||
|
||||
|
|
@ -991,7 +1002,7 @@ static int remove_cache_mod(struct trace_array *tr, const char *mod,
|
|||
}
|
||||
|
||||
static int cache_mod(struct trace_array *tr, const char *mod, int set,
|
||||
const char *match, const char *system, const char *event)
|
||||
const char *match, const char *system, const char *event, const char *filter)
|
||||
{
|
||||
struct event_mod_load *event_mod;
|
||||
|
||||
|
|
@ -1030,6 +1041,12 @@ static int cache_mod(struct trace_array *tr, const char *mod, int set,
|
|||
goto out_free;
|
||||
}
|
||||
|
||||
if (filter) {
|
||||
event_mod->filter = kstrdup(filter, GFP_KERNEL);
|
||||
if (!event_mod->filter)
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
list_add(&event_mod->list, &tr->mod_events);
|
||||
|
||||
return 0;
|
||||
|
|
@ -1042,7 +1059,7 @@ static int cache_mod(struct trace_array *tr, const char *mod, int set,
|
|||
#else /* CONFIG_MODULES */
|
||||
static inline void clear_mod_events(struct trace_array *tr) { }
|
||||
static int cache_mod(struct trace_array *tr, const char *mod, int set,
|
||||
const char *match, const char *system, const char *event)
|
||||
const char *match, const char *system, const char *event, const char *filter)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
@ -1351,7 +1368,7 @@ static void remove_event_file_dir(struct trace_event_file *file)
|
|||
static int
|
||||
__ftrace_set_clr_event_nolock(struct trace_array *tr, const char *match,
|
||||
const char *sub, const char *event, int set,
|
||||
const char *mod)
|
||||
const char *mod, char *filter)
|
||||
{
|
||||
struct trace_event_file *file;
|
||||
struct trace_event_call *call;
|
||||
|
|
@ -1401,7 +1418,11 @@ __ftrace_set_clr_event_nolock(struct trace_array *tr, const char *match,
|
|||
if (event && strcmp(event, name) != 0)
|
||||
continue;
|
||||
|
||||
ret = ftrace_event_enable_disable(file, set);
|
||||
if (filter)
|
||||
ret = apply_event_filter(file, filter);
|
||||
|
||||
if (!filter || !ret)
|
||||
ret = ftrace_event_enable_disable(file, set);
|
||||
|
||||
/*
|
||||
* Save the first error and return that. Some events
|
||||
|
|
@ -1419,14 +1440,14 @@ __ftrace_set_clr_event_nolock(struct trace_array *tr, const char *match,
|
|||
* check if the module was loaded. If it wasn't cache it.
|
||||
*/
|
||||
if (module && ret == -EINVAL && !eret)
|
||||
ret = cache_mod(tr, module, set, match, sub, event);
|
||||
ret = cache_mod(tr, module, set, match, sub, event, filter);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __ftrace_set_clr_event(struct trace_array *tr, const char *match,
|
||||
const char *sub, const char *event, int set,
|
||||
const char *mod)
|
||||
const char *mod, char *filter)
|
||||
{
|
||||
int ret;
|
||||
|
||||
|
|
@ -1434,26 +1455,35 @@ static int __ftrace_set_clr_event(struct trace_array *tr, const char *match,
|
|||
return -EACCES;
|
||||
|
||||
mutex_lock(&event_mutex);
|
||||
ret = __ftrace_set_clr_event_nolock(tr, match, sub, event, set, mod);
|
||||
ret = __ftrace_set_clr_event_nolock(tr, match, sub, event, set, mod, filter);
|
||||
mutex_unlock(&event_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ftrace_set_clr_event(struct trace_array *tr, char *buf, int set)
|
||||
VISIBLE_IF_KUNIT void
|
||||
ftrace_parse_event_string(char *buf, char **match, char **sub, char **event, char **mod,
|
||||
bool handle_filter, char **filter)
|
||||
{
|
||||
char *event = NULL, *sub = NULL, *match, *mod;
|
||||
int ret;
|
||||
*event = NULL;
|
||||
*sub = NULL;
|
||||
*filter = NULL;
|
||||
|
||||
if (!tr)
|
||||
return -ENOENT;
|
||||
/* event filters can be appended with :filter:<filter> */
|
||||
if (handle_filter) {
|
||||
*filter = strstr(buf, ":filter:");
|
||||
if (*filter) {
|
||||
**filter = '\0';
|
||||
*filter += 8;
|
||||
}
|
||||
}
|
||||
|
||||
/* Modules events can be appended with :mod:<module> */
|
||||
mod = strstr(buf, ":mod:");
|
||||
if (mod) {
|
||||
*mod = '\0';
|
||||
*mod = strstr(buf, ":mod:");
|
||||
if (*mod) {
|
||||
**mod = '\0';
|
||||
/* move to the module name */
|
||||
mod += 5;
|
||||
*mod += 5;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1468,31 +1498,38 @@ int ftrace_set_clr_event(struct trace_array *tr, char *buf, int set)
|
|||
* the name <name> or any event that matches <name>
|
||||
*/
|
||||
|
||||
match = strsep(&buf, ":");
|
||||
*match = strsep(&buf, ":");
|
||||
if (buf) {
|
||||
sub = match;
|
||||
event = buf;
|
||||
match = NULL;
|
||||
*sub = *match;
|
||||
*event = buf;
|
||||
*match = NULL;
|
||||
|
||||
if (!strlen(sub) || strcmp(sub, "*") == 0)
|
||||
sub = NULL;
|
||||
if (!strlen(event) || strcmp(event, "*") == 0)
|
||||
event = NULL;
|
||||
} else if (mod) {
|
||||
if (!strlen(*sub) || strcmp(*sub, "*") == 0)
|
||||
*sub = NULL;
|
||||
if (!strlen(*event) || strcmp(*event, "*") == 0)
|
||||
*event = NULL;
|
||||
} else if (*mod) {
|
||||
/* Allow wildcard for no length or star */
|
||||
if (!strlen(match) || strcmp(match, "*") == 0)
|
||||
match = NULL;
|
||||
if (!strlen(*match) || strcmp(*match, "*") == 0)
|
||||
*match = NULL;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_IF_KUNIT(ftrace_parse_event_string);
|
||||
|
||||
ret = __ftrace_set_clr_event(tr, match, sub, event, set, mod);
|
||||
int ftrace_set_clr_event(struct trace_array *tr, const char *arg_buf, int set, bool handle_filter)
|
||||
{
|
||||
char *event, *sub, *match, *mod, *filter;
|
||||
|
||||
/* Put back the colon to allow this to be called again */
|
||||
if (buf)
|
||||
*(buf - 1) = ':';
|
||||
if (mod)
|
||||
*(mod - 5) = ':';
|
||||
if (!tr)
|
||||
return -ENOENT;
|
||||
|
||||
return ret;
|
||||
char *dupped_buf __free(kfree) = kstrdup(arg_buf, GFP_KERNEL);
|
||||
if (!dupped_buf)
|
||||
return -ENOMEM;
|
||||
|
||||
ftrace_parse_event_string(dupped_buf, &match, &sub, &event, &mod, handle_filter, &filter);
|
||||
|
||||
return __ftrace_set_clr_event(tr, match, sub, event, set, mod, filter);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1514,7 +1551,7 @@ int trace_set_clr_event(const char *system, const char *event, int set)
|
|||
if (!tr)
|
||||
return -ENODEV;
|
||||
|
||||
return __ftrace_set_clr_event(tr, NULL, system, event, set, NULL);
|
||||
return __ftrace_set_clr_event(tr, NULL, system, event, set, NULL, NULL);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(trace_set_clr_event);
|
||||
|
||||
|
|
@ -1540,7 +1577,7 @@ int trace_array_set_clr_event(struct trace_array *tr, const char *system,
|
|||
return -ENOENT;
|
||||
|
||||
set = (enable == true) ? 1 : 0;
|
||||
return __ftrace_set_clr_event(tr, NULL, system, event, set, NULL);
|
||||
return __ftrace_set_clr_event(tr, NULL, system, event, set, NULL, NULL);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(trace_array_set_clr_event);
|
||||
|
||||
|
|
@ -1574,7 +1611,7 @@ ftrace_event_write(struct file *file, const char __user *ubuf,
|
|||
if (*parser.buffer == '!')
|
||||
set = 0;
|
||||
|
||||
ret = ftrace_set_clr_event(tr, parser.buffer + !set, set);
|
||||
ret = ftrace_set_clr_event(tr, parser.buffer + !set, set, false);
|
||||
if (ret)
|
||||
goto out_put;
|
||||
}
|
||||
|
|
@ -2060,7 +2097,7 @@ system_enable_write(struct file *filp, const char __user *ubuf, size_t cnt,
|
|||
if (system)
|
||||
name = system->name;
|
||||
|
||||
ret = __ftrace_set_clr_event(dir->tr, NULL, name, NULL, val, NULL);
|
||||
ret = __ftrace_set_clr_event(dir->tr, NULL, name, NULL, val, NULL, NULL);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
|
|
@ -4035,7 +4072,8 @@ static void update_mod_cache(struct trace_array *tr, struct module *mod)
|
|||
|
||||
__ftrace_set_clr_event_nolock(tr, event_mod->match,
|
||||
event_mod->system,
|
||||
event_mod->event, 1, mod->name);
|
||||
event_mod->event, 1, mod->name,
|
||||
event_mod->filter);
|
||||
free_event_mod(event_mod);
|
||||
}
|
||||
}
|
||||
|
|
@ -4809,7 +4847,7 @@ int event_trace_del_tracer(struct trace_array *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, NULL);
|
||||
__ftrace_set_clr_event_nolock(tr, NULL, NULL, NULL, 0, NULL, NULL);
|
||||
|
||||
/* Make sure no more events are being executed */
|
||||
tracepoint_synchronize_unregister();
|
||||
|
|
@ -4842,10 +4880,10 @@ static __init void __early_set_events(struct trace_array *tr, char *buf, bool en
|
|||
while ((token = strsep(&buf, ","))) {
|
||||
if (*token) {
|
||||
if (enable) {
|
||||
if (ftrace_set_clr_event(tr, token, 1))
|
||||
if (ftrace_set_clr_event(tr, token, 1, true))
|
||||
pr_warn("Failed to enable trace event: %s\n", token);
|
||||
} else {
|
||||
ftrace_set_clr_event(tr, token, 0);
|
||||
ftrace_set_clr_event(tr, token, 0, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -5119,7 +5157,7 @@ static __init void event_trace_self_tests(void)
|
|||
|
||||
pr_info("Testing event system %s: ", system->name);
|
||||
|
||||
ret = __ftrace_set_clr_event(tr, NULL, system->name, NULL, 1, NULL);
|
||||
ret = __ftrace_set_clr_event(tr, NULL, system->name, NULL, 1, NULL, NULL);
|
||||
if (WARN_ON_ONCE(ret)) {
|
||||
pr_warn("error enabling system %s\n",
|
||||
system->name);
|
||||
|
|
@ -5128,7 +5166,7 @@ static __init void event_trace_self_tests(void)
|
|||
|
||||
event_test_stuff();
|
||||
|
||||
ret = __ftrace_set_clr_event(tr, NULL, system->name, NULL, 0, NULL);
|
||||
ret = __ftrace_set_clr_event(tr, NULL, system->name, NULL, 0, NULL, NULL);
|
||||
if (WARN_ON_ONCE(ret)) {
|
||||
pr_warn("error disabling system %s\n",
|
||||
system->name);
|
||||
|
|
@ -5143,7 +5181,7 @@ static __init void event_trace_self_tests(void)
|
|||
pr_info("Running tests on all trace events:\n");
|
||||
pr_info("Testing all events: ");
|
||||
|
||||
ret = __ftrace_set_clr_event(tr, NULL, NULL, NULL, 1, NULL);
|
||||
ret = __ftrace_set_clr_event(tr, NULL, NULL, NULL, 1, NULL, NULL);
|
||||
if (WARN_ON_ONCE(ret)) {
|
||||
pr_warn("error enabling all events\n");
|
||||
return;
|
||||
|
|
@ -5152,7 +5190,7 @@ static __init void event_trace_self_tests(void)
|
|||
event_test_stuff();
|
||||
|
||||
/* reset sysname */
|
||||
ret = __ftrace_set_clr_event(tr, NULL, NULL, NULL, 0, NULL);
|
||||
ret = __ftrace_set_clr_event(tr, NULL, NULL, NULL, 0, NULL, NULL);
|
||||
if (WARN_ON_ONCE(ret)) {
|
||||
pr_warn("error disabling all events\n");
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@ enum filter_pred_fn {
|
|||
FILTER_PRED_FN_CPUMASK,
|
||||
FILTER_PRED_FN_CPUMASK_CPU,
|
||||
FILTER_PRED_FN_FUNCTION,
|
||||
FILTER_PRED_FN_WITHIN,
|
||||
FILTER_PRED_FN_,
|
||||
FILTER_PRED_TEST_VISITED,
|
||||
};
|
||||
|
|
@ -117,6 +118,7 @@ struct filter_pred {
|
|||
C(IP_FIELD_ONLY, "Only 'ip' field is supported for function trace"), \
|
||||
C(INVALID_VALUE, "Invalid value (did you forget quotes)?"), \
|
||||
C(NO_FUNCTION, "Function not found"), \
|
||||
C(GLOB_WITHIN, "'within' filter cannot take glob expressions"), \
|
||||
C(ERRNO, "Error"), \
|
||||
C(NO_FILTER, "No filter found")
|
||||
|
||||
|
|
@ -595,6 +597,12 @@ predicate_parse(const char *str, int nr_parens, int nr_preds,
|
|||
top--; /* #12 */
|
||||
}
|
||||
}
|
||||
/* Only the inner loop can complete a non-empty expression. */
|
||||
if (N) {
|
||||
ret = -EINVAL;
|
||||
parse_error(pe, FILT_ERR_INVALID_FILTER, ptr - str);
|
||||
goto out_free;
|
||||
}
|
||||
out:
|
||||
if (top != op_stack) {
|
||||
/* Too many '(' */
|
||||
|
|
@ -1008,6 +1016,22 @@ static int filter_pred_function(struct filter_pred *pred, void *event)
|
|||
return pred->op == OP_EQ ? ret : !ret;
|
||||
}
|
||||
|
||||
/* Filter predicate for within. */
|
||||
static int filter_pred_within(struct filter_pred *pred, void *event)
|
||||
{
|
||||
#ifdef CONFIG_STACKTRACE
|
||||
unsigned long entries[16];
|
||||
unsigned int nr_entries;
|
||||
int i;
|
||||
|
||||
nr_entries = stack_trace_save(entries, ARRAY_SIZE(entries), 0);
|
||||
for (i = 0; i < nr_entries; i++)
|
||||
if (pred->val <= entries[i] && entries[i] < pred->val2)
|
||||
return !pred->not;
|
||||
#endif
|
||||
return pred->not;
|
||||
}
|
||||
|
||||
/*
|
||||
* regex_match_foo - Basic regex callbacks
|
||||
*
|
||||
|
|
@ -1617,6 +1641,8 @@ static int filter_pred_fn_call(struct filter_pred *pred, void *event)
|
|||
return filter_pred_cpumask_cpu(pred, event);
|
||||
case FILTER_PRED_FN_FUNCTION:
|
||||
return filter_pred_function(pred, event);
|
||||
case FILTER_PRED_FN_WITHIN:
|
||||
return filter_pred_within(pred, event);
|
||||
case FILTER_PRED_TEST_VISITED:
|
||||
return test_pred_visited_fn(pred, event);
|
||||
default:
|
||||
|
|
@ -2002,10 +2028,30 @@ static int parse_pred(const char *str, void *data,
|
|||
|
||||
} else if (field->filter_type == FILTER_DYN_STRING) {
|
||||
pred->fn_num = FILTER_PRED_FN_STRLOC;
|
||||
} else if (field->filter_type == FILTER_RDYN_STRING)
|
||||
} else if (field->filter_type == FILTER_RDYN_STRING) {
|
||||
pred->fn_num = FILTER_PRED_FN_STRRELLOC;
|
||||
else {
|
||||
} else if (field->filter_type == FILTER_WITHIN) {
|
||||
unsigned long func;
|
||||
|
||||
if (op == OP_GLOB) {
|
||||
parse_error(pe, FILT_ERR_GLOB_WITHIN, pos + i);
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
pred->fn_num = FILTER_PRED_FN_WITHIN;
|
||||
func = kallsyms_lookup_name(pred->regex->pattern);
|
||||
if (!func) {
|
||||
parse_error(pe, FILT_ERR_NO_FUNCTION, pos + i);
|
||||
goto err_free;
|
||||
}
|
||||
/* Now find the function start and end address */
|
||||
if (!kallsyms_lookup_size_offset(func, &size, &offset)) {
|
||||
parse_error(pe, FILT_ERR_NO_FUNCTION, pos + i);
|
||||
goto err_free;
|
||||
}
|
||||
pred->val = func - offset;
|
||||
pred->val2 = pred->val + size;
|
||||
} else {
|
||||
if (!ustring_per_cpu) {
|
||||
/* Once allocated, keep it around for good */
|
||||
ustring_per_cpu = alloc_percpu(struct ustring_buffer);
|
||||
|
|
|
|||
|
|
@ -279,6 +279,14 @@ static u64 hist_field_pstring(struct hist_field *hist_field,
|
|||
return (u64)(unsigned long)*addr;
|
||||
}
|
||||
|
||||
static bool hist_field_is_signed(struct hist_field *hist_field)
|
||||
{
|
||||
if (hist_field->field)
|
||||
return hist_field->field->is_signed;
|
||||
|
||||
return hist_field->is_signed;
|
||||
}
|
||||
|
||||
static u64 hist_field_log2(struct hist_field *hist_field,
|
||||
struct tracing_map_elt *elt,
|
||||
struct trace_buffer *buffer,
|
||||
|
|
@ -466,7 +474,7 @@ static u64 hist_field_unary_minus(struct hist_field *hist_field,
|
|||
{ \
|
||||
type *addr = (type *)(event + hist_field->field->offset); \
|
||||
\
|
||||
return (u64)(unsigned long)*addr; \
|
||||
return (u64)*addr; \
|
||||
}
|
||||
|
||||
DEFINE_HIST_FIELD_FN(s64);
|
||||
|
|
@ -549,7 +557,7 @@ struct field_var {
|
|||
};
|
||||
|
||||
struct field_var_hist {
|
||||
struct hist_trigger_data *hist_data;
|
||||
struct trace_event_file *file;
|
||||
char *cmd;
|
||||
};
|
||||
|
||||
|
|
@ -1725,7 +1733,7 @@ static const char *get_hist_field_flags(struct hist_field *hist_field)
|
|||
flags_str = "percent";
|
||||
else if (hist_field->flags & HIST_FIELD_FL_GRAPH)
|
||||
flags_str = "graph";
|
||||
else if (hist_field->flags & HIST_FIELD_FL_STACKTRACE)
|
||||
else if (hist_field->flags & HIST_FIELD_FL_STACKTRACE && hist_field->field)
|
||||
flags_str = "stacktrace";
|
||||
|
||||
return flags_str;
|
||||
|
|
@ -1754,6 +1762,9 @@ static bool expr_field_str(struct hist_field *field, struct seq_buf *s)
|
|||
seq_buf_printf(s, ".%s", flags_str);
|
||||
}
|
||||
|
||||
if (field->buckets)
|
||||
seq_buf_printf(s, "=%lu", field->buckets);
|
||||
|
||||
return !seq_buf_has_overflowed(s);
|
||||
}
|
||||
|
||||
|
|
@ -3117,8 +3128,8 @@ create_field_var_hist(struct hist_trigger_data *target_hist_data,
|
|||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
/* Save the compatible histogram information */
|
||||
var_hist->hist_data = hist_data;
|
||||
/* Needed to remove the histogram when the target goes away */
|
||||
var_hist->file = file;
|
||||
|
||||
/* Create the new histogram with our variable */
|
||||
ret = event_hist_trigger_parse(&trigger_hist_cmd, file,
|
||||
|
|
@ -3133,20 +3144,18 @@ create_field_var_hist(struct hist_trigger_data *target_hist_data,
|
|||
|
||||
kfree(cmd);
|
||||
|
||||
n = target_hist_data->n_field_var_hists;
|
||||
target_hist_data->field_var_hists[n] = var_hist;
|
||||
target_hist_data->n_field_var_hists++;
|
||||
|
||||
/* If we can't find the variable, something went wrong */
|
||||
event_var = find_synthetic_field_var(target_hist_data, subsys_name,
|
||||
event_name, field_name);
|
||||
if (IS_ERR_OR_NULL(event_var)) {
|
||||
kfree(var_hist->cmd);
|
||||
kfree(var_hist);
|
||||
hist_err(tr, HIST_ERR_SYNTH_VAR_NOT_FOUND, errpos(field_name));
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
n = target_hist_data->n_field_var_hists;
|
||||
target_hist_data->field_var_hists[n] = var_hist;
|
||||
target_hist_data->n_field_var_hists++;
|
||||
|
||||
return event_var;
|
||||
}
|
||||
|
||||
|
|
@ -5565,15 +5574,24 @@ static void hist_trigger_print_key(struct seq_file *m,
|
|||
*(u64 *)(key + key_field->offset));
|
||||
} else if (key_field->flags & HIST_FIELD_FL_BUCKET) {
|
||||
unsigned long buckets = key_field->buckets;
|
||||
u64 end;
|
||||
|
||||
uval = *(u64 *)(key + key_field->offset);
|
||||
end = uval + buckets - 1;
|
||||
if (end < uval)
|
||||
end = U64_MAX;
|
||||
seq_printf(m, "%s: ~ %llu-%llu", field_name,
|
||||
uval, uval + buckets -1);
|
||||
uval, end);
|
||||
} else if (key_field->flags & HIST_FIELD_FL_STRING) {
|
||||
seq_printf(m, "%s: %-50s", field_name,
|
||||
(char *)(key + key_field->offset));
|
||||
} else {
|
||||
uval = *(u64 *)(key + key_field->offset);
|
||||
seq_printf(m, "%s: %10llu", field_name, uval);
|
||||
if (hist_field_is_signed(key_field))
|
||||
seq_printf(m, "%s: %10lld", field_name,
|
||||
(s64)uval);
|
||||
else
|
||||
seq_printf(m, "%s: %10llu", field_name, uval);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -6205,7 +6223,7 @@ static void hist_field_print(struct seq_file *m, struct hist_field *hist_field)
|
|||
}
|
||||
}
|
||||
if (hist_field->buckets)
|
||||
seq_printf(m, "=%ld", hist_field->buckets);
|
||||
seq_printf(m, "=%lu", hist_field->buckets);
|
||||
}
|
||||
|
||||
static int event_hist_trigger_print(struct seq_file *m,
|
||||
|
|
@ -6349,7 +6367,7 @@ static void unregister_field_var_hists(struct hist_trigger_data *hist_data)
|
|||
int ret;
|
||||
|
||||
for (i = 0; i < hist_data->n_field_var_hists; i++) {
|
||||
file = hist_data->field_var_hists[i]->hist_data->event_file;
|
||||
file = hist_data->field_var_hists[i]->file;
|
||||
cmd = hist_data->field_var_hists[i]->cmd;
|
||||
ret = event_hist_trigger_parse(&trigger_hist_cmd, file,
|
||||
"!hist", "hist", cmd);
|
||||
|
|
@ -6799,7 +6817,6 @@ static void hist_unreg_all(struct trace_event_file *file)
|
|||
struct event_trigger_data *test, *n;
|
||||
struct hist_trigger_data *hist_data;
|
||||
struct synth_event *se;
|
||||
const char *se_name;
|
||||
|
||||
lockdep_assert_held(&event_mutex);
|
||||
|
||||
|
|
@ -6812,8 +6829,7 @@ static void hist_unreg_all(struct trace_event_file *file)
|
|||
list_del_rcu(&test->list);
|
||||
trace_event_trigger_enable_disable(file, 0);
|
||||
|
||||
se_name = trace_event_name(file->event_call);
|
||||
se = find_synth_event(se_name);
|
||||
se = find_synth_event_call(file->event_call);
|
||||
if (se)
|
||||
se->ref--;
|
||||
|
||||
|
|
@ -6837,7 +6853,6 @@ static int event_hist_trigger_parse(struct event_command *cmd_ops,
|
|||
struct hist_trigger_data *hist_data;
|
||||
char *param, *filter, *p, *start;
|
||||
struct synth_event *se;
|
||||
const char *se_name;
|
||||
bool remove;
|
||||
int ret = 0;
|
||||
|
||||
|
|
@ -6931,8 +6946,7 @@ static int event_hist_trigger_parse(struct event_command *cmd_ops,
|
|||
}
|
||||
|
||||
event_trigger_unregister(cmd_ops, file, glob+1, trigger_data);
|
||||
se_name = trace_event_name(file->event_call);
|
||||
se = find_synth_event(se_name);
|
||||
se = find_synth_event_call(file->event_call);
|
||||
if (se)
|
||||
se->ref--;
|
||||
ret = 0;
|
||||
|
|
@ -6967,8 +6981,7 @@ static int event_hist_trigger_parse(struct event_command *cmd_ops,
|
|||
if (ret)
|
||||
goto out_unreg;
|
||||
|
||||
se_name = trace_event_name(file->event_call);
|
||||
se = find_synth_event(se_name);
|
||||
se = find_synth_event_call(file->event_call);
|
||||
if (se)
|
||||
se->ref++;
|
||||
out:
|
||||
|
|
@ -6985,6 +6998,8 @@ static int event_hist_trigger_parse(struct event_command *cmd_ops,
|
|||
|
||||
trigger_data_free(trigger_data);
|
||||
|
||||
unregister_field_var_hists(hist_data);
|
||||
|
||||
destroy_hist_data(hist_data);
|
||||
goto out;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -914,6 +914,16 @@ struct synth_event *find_synth_event(const char *name)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
struct synth_event *find_synth_event_call(struct trace_event_call *call)
|
||||
{
|
||||
struct synth_event *se;
|
||||
const char *name;
|
||||
|
||||
name = trace_event_name(call);
|
||||
se = find_synth_event(name);
|
||||
return se && &se->call == call ? se : NULL;
|
||||
}
|
||||
|
||||
static struct trace_event_fields synth_event_fields_array[] = {
|
||||
{ .type = TRACE_FUNCTION_TYPE,
|
||||
.define_fields = synth_event_define_fields },
|
||||
|
|
|
|||
122
kernel/trace/trace_events_test.c
Normal file
122
kernel/trace/trace_events_test.c
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <kunit/test.h>
|
||||
|
||||
#include "trace.h"
|
||||
|
||||
struct parse_event_string_test_case {
|
||||
const char *input;
|
||||
|
||||
const char *match, *sub, *event, *mod, *filter;
|
||||
};
|
||||
|
||||
static const struct parse_event_string_test_case parse_event_string_test_cases[] = {
|
||||
{
|
||||
"",
|
||||
.match = "",
|
||||
},
|
||||
{
|
||||
"*:event",
|
||||
.event = "event",
|
||||
},
|
||||
{
|
||||
":event",
|
||||
.event = "event",
|
||||
},
|
||||
{
|
||||
"sub:*",
|
||||
.sub = "sub",
|
||||
},
|
||||
{
|
||||
"sub:",
|
||||
.sub = "sub",
|
||||
},
|
||||
{
|
||||
"sub:event",
|
||||
.sub = "sub",
|
||||
.event = "event",
|
||||
},
|
||||
{
|
||||
"sub:event:filter:filter",
|
||||
.sub = "sub",
|
||||
.event = "event",
|
||||
.filter = "filter",
|
||||
},
|
||||
{
|
||||
"match",
|
||||
.match = "match",
|
||||
},
|
||||
{
|
||||
":mod:module",
|
||||
.mod = "module",
|
||||
},
|
||||
{
|
||||
"sub:event:mod:module",
|
||||
.sub = "sub",
|
||||
.event = "event",
|
||||
.mod = "module",
|
||||
},
|
||||
{
|
||||
"sub:event:mod:module:filter:filter",
|
||||
.sub = "sub",
|
||||
.event = "event",
|
||||
.mod = "module",
|
||||
.filter = "filter",
|
||||
},
|
||||
{
|
||||
"sub:event:mod:module:filter:unexpected-filter",
|
||||
.sub = "sub",
|
||||
.event = "event",
|
||||
.mod = "module:filter:unexpected-filter",
|
||||
},
|
||||
};
|
||||
|
||||
static void
|
||||
parse_event_string_test_desc(const struct parse_event_string_test_case *params, char *desc)
|
||||
{
|
||||
snprintf(desc, KUNIT_PARAM_DESC_SIZE, "'%s'", params->input);
|
||||
}
|
||||
|
||||
KUNIT_ARRAY_PARAM(parse_event_string, parse_event_string_test_cases, parse_event_string_test_desc);
|
||||
|
||||
#define EXPECT_NULL_OR_STR_EQ(test, left, right) \
|
||||
({ \
|
||||
if ((left) != NULL) \
|
||||
KUNIT_EXPECT_STREQ(test, (left), (right)); \
|
||||
else \
|
||||
KUNIT_EXPECT_NULL(test, (right)); \
|
||||
})
|
||||
|
||||
static void parse_event_string(struct kunit *test)
|
||||
{
|
||||
const struct parse_event_string_test_case *params = test->param_value;
|
||||
char *input, *match, *sub, *event, *mod, *filter;
|
||||
|
||||
input = kunit_kstrdup(test, params->input, GFP_KERNEL);
|
||||
if (!input)
|
||||
kunit_skip(test, "ENOMEM");
|
||||
|
||||
ftrace_parse_event_string(input, &match, &sub, &event, &mod, params->filter, &filter);
|
||||
|
||||
EXPECT_NULL_OR_STR_EQ(test, match, params->match);
|
||||
EXPECT_NULL_OR_STR_EQ(test, sub, params->sub);
|
||||
EXPECT_NULL_OR_STR_EQ(test, event, params->event);
|
||||
EXPECT_NULL_OR_STR_EQ(test, mod, params->mod);
|
||||
EXPECT_NULL_OR_STR_EQ(test, filter, params->filter);
|
||||
}
|
||||
|
||||
static struct kunit_case trace_events_test_cases[] = {
|
||||
KUNIT_CASE_PARAM(parse_event_string, parse_event_string_gen_params),
|
||||
{}
|
||||
};
|
||||
|
||||
static struct kunit_suite trace_events_test_suite = {
|
||||
.name = "trace-events",
|
||||
.test_cases = trace_events_test_cases,
|
||||
};
|
||||
kunit_test_suite(trace_events_test_suite);
|
||||
|
||||
MODULE_DESCRIPTION("KUnit test for the event tracing implementation");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
|
||||
|
|
@ -83,6 +83,7 @@ struct osnoise_instance {
|
|||
|
||||
static struct list_head osnoise_instances;
|
||||
|
||||
__printf(1, 2)
|
||||
static void osnoise_print(const char *fmt, ...)
|
||||
{
|
||||
struct osnoise_instance *inst;
|
||||
|
|
|
|||
|
|
@ -142,18 +142,18 @@ int resize_buffer_duplicate_size(struct array_buffer *trace_buf,
|
|||
if (cpu_id == RING_BUFFER_ALL_CPUS) {
|
||||
for_each_tracing_cpu(cpu) {
|
||||
ret = ring_buffer_resize(trace_buf->buffer,
|
||||
per_cpu_ptr(size_buf->data, cpu)->entries, cpu);
|
||||
per_cpu_ptr(size_buf->data, cpu)->capacity, cpu);
|
||||
if (ret < 0)
|
||||
break;
|
||||
per_cpu_ptr(trace_buf->data, cpu)->entries =
|
||||
per_cpu_ptr(size_buf->data, cpu)->entries;
|
||||
per_cpu_ptr(trace_buf->data, cpu)->capacity =
|
||||
per_cpu_ptr(size_buf->data, cpu)->capacity;
|
||||
}
|
||||
} else {
|
||||
ret = ring_buffer_resize(trace_buf->buffer,
|
||||
per_cpu_ptr(size_buf->data, cpu_id)->entries, cpu_id);
|
||||
per_cpu_ptr(size_buf->data, cpu_id)->capacity, cpu_id);
|
||||
if (ret == 0)
|
||||
per_cpu_ptr(trace_buf->data, cpu_id)->entries =
|
||||
per_cpu_ptr(size_buf->data, cpu_id)->entries;
|
||||
per_cpu_ptr(trace_buf->data, cpu_id)->capacity =
|
||||
per_cpu_ptr(size_buf->data, cpu_id)->capacity;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
|
@ -193,7 +193,7 @@ void free_snapshot(struct trace_array *tr)
|
|||
*/
|
||||
ring_buffer_subbuf_order_set(tr->snapshot_buffer.buffer, 0);
|
||||
ring_buffer_resize(tr->snapshot_buffer.buffer, 1, RING_BUFFER_ALL_CPUS);
|
||||
trace_set_buffer_entries(&tr->snapshot_buffer, 1);
|
||||
trace_set_buffer_capacity(&tr->snapshot_buffer, 1);
|
||||
tracing_reset_online_cpus(&tr->snapshot_buffer);
|
||||
tr->allocated_snapshot = false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,5 +37,6 @@ struct synth_event {
|
|||
};
|
||||
|
||||
extern struct synth_event *find_synth_event(const char *name);
|
||||
extern struct synth_event *find_synth_event_call(struct trace_event_call *call);
|
||||
|
||||
#endif /* __TRACE_SYNTH_H */
|
||||
|
|
|
|||
|
|
@ -683,7 +683,7 @@ static int syscall_copy_user_array(char *buf, const char __user *ptr,
|
|||
|
||||
for (int i = 0; i < args->uargs; i++, buf += SYSCALL_FAULT_ARG_SZ) {
|
||||
ptr = (char __user *)args->ptr_array[i];
|
||||
ret = __copy_from_user(buf, ptr, size);
|
||||
ret = copy_from_user(buf, ptr, size);
|
||||
args->read[i] = ret ? -1 : size;
|
||||
}
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
#!/bin/sh
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
# description: event filter - reject incomplete logical expressions
|
||||
# requires: events/sched/sched_switch/filter
|
||||
# flags: instance
|
||||
|
||||
filter=events/sched/sched_switch/filter
|
||||
|
||||
for base in \
|
||||
'common_pid == 1' \
|
||||
'common_pid == 1 || common_pid == 2' \
|
||||
'common_pid == 1 && common_pid == 2' \
|
||||
'!(common_pid == 1 || common_pid == 2)'
|
||||
do
|
||||
printf '%s' "$base" > "$filter"
|
||||
for tail in '&&' '||' '&& !' '|| !' '&& !! ' '|| !! '; do
|
||||
if printf '%s' "$base $tail" > "$filter" 2>/dev/null; then
|
||||
echo "Accepted incomplete filter: $base $tail"
|
||||
exit_fail
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
echo 0 > "$filter"
|
||||
Loading…
Reference in New Issue
Block a user