Commit 1bc19105 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'trace-v5.18' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace

Pull tracing updates from Steven Rostedt:

 - New user_events interface. User space can register an event with the
   kernel describing the format of the event. Then it will receive a
   byte in a page mapping that it can check against. A privileged task
   can then enable that event like any other event, which will change
   the mapped byte to true, telling the user space application to start
   writing the event to the tracing buffer.

 - Add new "ftrace_boot_snapshot" kernel command line parameter. When
   set, the tracing buffer will be saved in the snapshot buffer at boot
   up when the kernel hands things over to user space. This will keep
   the traces that happened at boot up available even if user space boot
   up has tracing as well.

 - Have TRACE_EVENT_ENUM() also update trace event field type
   descriptions. Thus if a static array defines its size with an enum,
   the user space trace event parsers can still know how to parse that
   array.

 - Add new TRACE_CUSTOM_EVENT() macro. This acts the same as the
   TRACE_EVENT() macro, but will attach to an existing tracepoint. This
   will make one tracepoint be able to trace different content and not
   be stuck at only what the original TRACE_EVENT() macro exports.

 - Fixes to tracing error logging.

 - Better saving of cmdlines to PIDs when tracing (use the wakeup events
   for mapping).

* tag 'trace-v5.18' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace: (30 commits)
  tracing: Have type enum modifications copy the strings
  user_events: Add trace event call as root for low permission cases
  tracing/user_events: Use alloc_pages instead of kzalloc() for register pages
  tracing: Add snapshot at end of kernel boot up
  tracing: Have TRACE_DEFINE_ENUM affect trace event types as well
  tracing: Fix strncpy warning in trace_events_synth.c
  user_events: Prevent dyn_event delete racing with ioctl add/delete
  tracing: Add TRACE_CUSTOM_EVENT() macro
  tracing: Move the defines to create TRACE_EVENTS into their own files
  tracing: Add sample code for custom trace events
  tracing: Allow custom events to be added to the tracefs directory
  tracing: Fix last_cmd_set() string management in histogram code
  user_events: Fix potential uninitialized pointer while parsing field
  tracing: Fix allocation of last_cmd in last_cmd_set()
  user_events: Add documentation file
  user_events: Add sample code for typical usage
  user_events: Add self-test for validator boundaries
  user_events: Add self-test for perf_event integration
  user_events: Add self-test for dynamic_events integration
  user_events: Add self-test for ftrace integration
  ...
parents 20f463fb 795301d3
...@@ -1465,6 +1465,14 @@ ...@@ -1465,6 +1465,14 @@
as early as possible in order to facilitate early as early as possible in order to facilitate early
boot debugging. boot debugging.
ftrace_boot_snapshot
[FTRACE] On boot up, a snapshot will be taken of the
ftrace ring buffer that can be read at:
/sys/kernel/tracing/snapshot.
This is useful if you need tracing information from kernel
boot up that is likely to be overridden by user space
start up functionality.
ftrace_dump_on_oops[=orig_cpu] ftrace_dump_on_oops[=orig_cpu]
[FTRACE] will dump the trace buffers on oops. [FTRACE] will dump the trace buffers on oops.
If no parameter is passed, ftrace will dump If no parameter is passed, ftrace will dump
......
...@@ -30,3 +30,4 @@ Linux Tracing Technologies ...@@ -30,3 +30,4 @@ Linux Tracing Technologies
stm stm
sys-t sys-t
coresight/index coresight/index
user_events
=========================================
user_events: User-based Event Tracing
=========================================
:Author: Beau Belgrave
Overview
--------
User based trace events allow user processes to create events and trace data
that can be viewed via existing tools, such as ftrace, perf and eBPF.
To enable this feature, build your kernel with CONFIG_USER_EVENTS=y.
Programs can view status of the events via
/sys/kernel/debug/tracing/user_events_status and can both register and write
data out via /sys/kernel/debug/tracing/user_events_data.
Programs can also use /sys/kernel/debug/tracing/dynamic_events to register and
delete user based events via the u: prefix. The format of the command to
dynamic_events is the same as the ioctl with the u: prefix applied.
Typically programs will register a set of events that they wish to expose to
tools that can read trace_events (such as ftrace and perf). The registration
process gives back two ints to the program for each event. The first int is the
status index. This index describes which byte in the
/sys/kernel/debug/tracing/user_events_status file represents this event. The
second int is the write index. This index describes the data when a write() or
writev() is called on the /sys/kernel/debug/tracing/user_events_data file.
The structures referenced in this document are contained with the
/include/uap/linux/user_events.h file in the source tree.
**NOTE:** *Both user_events_status and user_events_data are under the tracefs
filesystem and may be mounted at different paths than above.*
Registering
-----------
Registering within a user process is done via ioctl() out to the
/sys/kernel/debug/tracing/user_events_data file. The command to issue is
DIAG_IOCSREG.
This command takes a struct user_reg as an argument::
struct user_reg {
u32 size;
u64 name_args;
u32 status_index;
u32 write_index;
};
The struct user_reg requires two inputs, the first is the size of the structure
to ensure forward and backward compatibility. The second is the command string
to issue for registering. Upon success two outputs are set, the status index
and the write index.
User based events show up under tracefs like any other event under the
subsystem named "user_events". This means tools that wish to attach to the
events need to use /sys/kernel/debug/tracing/events/user_events/[name]/enable
or perf record -e user_events:[name] when attaching/recording.
**NOTE:** *The write_index returned is only valid for the FD that was used*
Command Format
^^^^^^^^^^^^^^
The command string format is as follows::
name[:FLAG1[,FLAG2...]] [Field1[;Field2...]]
Supported Flags
^^^^^^^^^^^^^^^
**BPF_ITER** - EBPF programs attached to this event will get the raw iovec
struct instead of any data copies for max performance.
Field Format
^^^^^^^^^^^^
::
type name [size]
Basic types are supported (__data_loc, u32, u64, int, char, char[20], etc).
User programs are encouraged to use clearly sized types like u32.
**NOTE:** *Long is not supported since size can vary between user and kernel.*
The size is only valid for types that start with a struct prefix.
This allows user programs to describe custom structs out to tools, if required.
For example, a struct in C that looks like this::
struct mytype {
char data[20];
};
Would be represented by the following field::
struct mytype myname 20
Deleting
-----------
Deleting an event from within a user process is done via ioctl() out to the
/sys/kernel/debug/tracing/user_events_data file. The command to issue is
DIAG_IOCSDEL.
This command only requires a single string specifying the event to delete by
its name. Delete will only succeed if there are no references left to the
event (in both user and kernel space). User programs should use a separate file
to request deletes than the one used for registration due to this.
Status
------
When tools attach/record user based events the status of the event is updated
in realtime. This allows user programs to only incur the cost of the write() or
writev() calls when something is actively attached to the event.
User programs call mmap() on /sys/kernel/debug/tracing/user_events_status to
check the status for each event that is registered. The byte to check in the
file is given back after the register ioctl() via user_reg.status_index.
Currently the size of user_events_status is a single page, however, custom
kernel configurations can change this size to allow more user based events. In
all cases the size of the file is a multiple of a page size.
For example, if the register ioctl() gives back a status_index of 3 you would
check byte 3 of the returned mmap data to see if anything is attached to that
event.
Administrators can easily check the status of all registered events by reading
the user_events_status file directly via a terminal. The output is as follows::
Byte:Name [# Comments]
...
Active: ActiveCount
Busy: BusyCount
Max: MaxCount
For example, on a system that has a single event the output looks like this::
1:test
Active: 1
Busy: 0
Max: 4096
If a user enables the user event via ftrace, the output would change to this::
1:test # Used by ftrace
Active: 1
Busy: 1
Max: 4096
**NOTE:** *A status index of 0 will never be returned. This allows user
programs to have an index that can be used on error cases.*
Status Bits
^^^^^^^^^^^
The byte being checked will be non-zero if anything is attached. Programs can
check specific bits in the byte to see what mechanism has been attached.
The following values are defined to aid in checking what has been attached:
**EVENT_STATUS_FTRACE** - Bit set if ftrace has been attached (Bit 0).
**EVENT_STATUS_PERF** - Bit set if perf/eBPF has been attached (Bit 1).
Writing Data
------------
After registering an event the same fd that was used to register can be used
to write an entry for that event. The write_index returned must be at the start
of the data, then the remaining data is treated as the payload of the event.
For example, if write_index returned was 1 and I wanted to write out an int
payload of the event. Then the data would have to be 8 bytes (2 ints) in size,
with the first 4 bytes being equal to 1 and the last 4 bytes being equal to the
value I want as the payload.
In memory this would look like this::
int index;
int payload;
User programs might have well known structs that they wish to use to emit out
as payloads. In those cases writev() can be used, with the first vector being
the index and the following vector(s) being the actual event payload.
For example, if I have a struct like this::
struct payload {
int src;
int dst;
int flags;
};
It's advised for user programs to do the following::
struct iovec io[2];
struct payload e;
io[0].iov_base = &write_index;
io[0].iov_len = sizeof(write_index);
io[1].iov_base = &e;
io[1].iov_len = sizeof(e);
writev(fd, (const struct iovec*)io, 2);
**NOTE:** *The write_index is not emitted out into the trace being recorded.*
EBPF
----
EBPF programs that attach to a user-based event tracepoint are given a pointer
to a struct user_bpf_context. The bpf context contains the data type (which can
be a user or kernel buffer, or can be a pointer to the iovec) and the data
length that was emitted (minus the write_index).
Example Code
------------
See sample code in samples/user_events.
...@@ -30,6 +30,12 @@ ...@@ -30,6 +30,12 @@
#define ARCH_SUPPORTS_FTRACE_OPS 0 #define ARCH_SUPPORTS_FTRACE_OPS 0
#endif #endif
#ifdef CONFIG_TRACING
extern void ftrace_boot_snapshot(void);
#else
static inline void ftrace_boot_snapshot(void) { }
#endif
#ifdef CONFIG_FUNCTION_TRACER #ifdef CONFIG_FUNCTION_TRACER
struct ftrace_ops; struct ftrace_ops;
struct ftrace_regs; struct ftrace_regs;
...@@ -215,7 +221,10 @@ struct ftrace_ops_hash { ...@@ -215,7 +221,10 @@ struct ftrace_ops_hash {
void ftrace_free_init_mem(void); void ftrace_free_init_mem(void);
void ftrace_free_mem(struct module *mod, void *start, void *end); void ftrace_free_mem(struct module *mod, void *start, void *end);
#else #else
static inline void ftrace_free_init_mem(void) { } static inline void ftrace_free_init_mem(void)
{
ftrace_boot_snapshot();
}
static inline void ftrace_free_mem(struct module *mod, void *start, void *end) { } static inline void ftrace_free_mem(struct module *mod, void *start, void *end) { }
#endif #endif
......
...@@ -315,6 +315,7 @@ enum { ...@@ -315,6 +315,7 @@ enum {
TRACE_EVENT_FL_KPROBE_BIT, TRACE_EVENT_FL_KPROBE_BIT,
TRACE_EVENT_FL_UPROBE_BIT, TRACE_EVENT_FL_UPROBE_BIT,
TRACE_EVENT_FL_EPROBE_BIT, TRACE_EVENT_FL_EPROBE_BIT,
TRACE_EVENT_FL_CUSTOM_BIT,
}; };
/* /*
...@@ -328,6 +329,9 @@ enum { ...@@ -328,6 +329,9 @@ enum {
* KPROBE - Event is a kprobe * KPROBE - Event is a kprobe
* UPROBE - Event is a uprobe * UPROBE - Event is a uprobe
* EPROBE - Event is an event probe * EPROBE - Event is an event probe
* CUSTOM - Event is a custom event (to be attached to an exsiting tracepoint)
* This is set when the custom event has not been attached
* to a tracepoint yet, then it is cleared when it is.
*/ */
enum { enum {
TRACE_EVENT_FL_FILTERED = (1 << TRACE_EVENT_FL_FILTERED_BIT), TRACE_EVENT_FL_FILTERED = (1 << TRACE_EVENT_FL_FILTERED_BIT),
...@@ -339,6 +343,7 @@ enum { ...@@ -339,6 +343,7 @@ enum {
TRACE_EVENT_FL_KPROBE = (1 << TRACE_EVENT_FL_KPROBE_BIT), TRACE_EVENT_FL_KPROBE = (1 << TRACE_EVENT_FL_KPROBE_BIT),
TRACE_EVENT_FL_UPROBE = (1 << TRACE_EVENT_FL_UPROBE_BIT), TRACE_EVENT_FL_UPROBE = (1 << TRACE_EVENT_FL_UPROBE_BIT),
TRACE_EVENT_FL_EPROBE = (1 << TRACE_EVENT_FL_EPROBE_BIT), TRACE_EVENT_FL_EPROBE = (1 << TRACE_EVENT_FL_EPROBE_BIT),
TRACE_EVENT_FL_CUSTOM = (1 << TRACE_EVENT_FL_CUSTOM_BIT),
}; };
#define TRACE_EVENT_FL_UKPROBE (TRACE_EVENT_FL_KPROBE | TRACE_EVENT_FL_UPROBE) #define TRACE_EVENT_FL_UKPROBE (TRACE_EVENT_FL_KPROBE | TRACE_EVENT_FL_UPROBE)
...@@ -440,7 +445,9 @@ static inline bool bpf_prog_array_valid(struct trace_event_call *call) ...@@ -440,7 +445,9 @@ static inline bool bpf_prog_array_valid(struct trace_event_call *call)
static inline const char * static inline const char *
trace_event_name(struct trace_event_call *call) trace_event_name(struct trace_event_call *call)
{ {
if (call->flags & TRACE_EVENT_FL_TRACEPOINT) if (call->flags & TRACE_EVENT_FL_CUSTOM)
return call->name;
else if (call->flags & TRACE_EVENT_FL_TRACEPOINT)
return call->tp ? call->tp->name : NULL; return call->tp ? call->tp->name : NULL;
else else
return call->name; return call->name;
...@@ -903,3 +910,18 @@ perf_trace_buf_submit(void *raw_data, int size, int rctx, u16 type, ...@@ -903,3 +910,18 @@ perf_trace_buf_submit(void *raw_data, int size, int rctx, u16 type,
#endif #endif
#endif /* _LINUX_TRACE_EVENT_H */ #endif /* _LINUX_TRACE_EVENT_H */
/*
* Note: we keep the TRACE_CUSTOM_EVENT outside the include file ifdef protection.
* This is due to the way trace custom events work. If a file includes two
* trace event headers under one "CREATE_CUSTOM_TRACE_EVENTS" the first include
* will override the TRACE_CUSTOM_EVENT and break the second include.
*/
#ifndef TRACE_CUSTOM_EVENT
#define DECLARE_CUSTOM_EVENT_CLASS(name, proto, args, tstruct, assign, print)
#define DEFINE_CUSTOM_EVENT(template, name, proto, args)
#define TRACE_CUSTOM_EVENT(name, proto, args, struct, assign, print)
#endif /* ifdef TRACE_CUSTOM_EVENT (see note above) */
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Trace files that want to automate creation of all tracepoints defined
* in their file should include this file. The following are macros that the
* trace file may define:
*
* TRACE_SYSTEM defines the system the tracepoint is for
*
* TRACE_INCLUDE_FILE if the file name is something other than TRACE_SYSTEM.h
* This macro may be defined to tell define_trace.h what file to include.
* Note, leave off the ".h".
*
* TRACE_INCLUDE_PATH if the path is something other than core kernel include/trace
* then this macro can define the path to use. Note, the path is relative to
* define_trace.h, not the file including it. Full path names for out of tree
* modules must be used.
*/
#ifdef CREATE_CUSTOM_TRACE_EVENTS
/* Prevent recursion */
#undef CREATE_CUSTOM_TRACE_EVENTS
#include <linux/stringify.h>
#undef TRACE_CUSTOM_EVENT
#define TRACE_CUSTOM_EVENT(name, proto, args, tstruct, assign, print)
#undef DEFINE_CUSTOM_EVENT
#define DEFINE_CUSTOM_EVENT(template, name, proto, args)
#undef TRACE_INCLUDE
#undef __TRACE_INCLUDE
#ifndef TRACE_INCLUDE_FILE
# define TRACE_INCLUDE_FILE TRACE_SYSTEM
# define UNDEF_TRACE_INCLUDE_FILE
#endif
#ifndef TRACE_INCLUDE_PATH
# define __TRACE_INCLUDE(system) <trace/events/system.h>
# define UNDEF_TRACE_INCLUDE_PATH
#else
# define __TRACE_INCLUDE(system) __stringify(TRACE_INCLUDE_PATH/system.h)
#endif
# define TRACE_INCLUDE(system) __TRACE_INCLUDE(system)
/* Let the trace headers be reread */
#define TRACE_CUSTOM_MULTI_READ
#include TRACE_INCLUDE(TRACE_INCLUDE_FILE)
#ifdef TRACEPOINTS_ENABLED
#include <trace/trace_custom_events.h>
#endif
#undef TRACE_CUSTOM_EVENT
#undef DECLARE_CUSTOM_EVENT_CLASS
#undef DEFINE_CUSTOM_EVENT
#undef TRACE_CUSTOM_MULTI_READ
/* Only undef what we defined in this file */
#ifdef UNDEF_TRACE_INCLUDE_FILE
# undef TRACE_INCLUDE_FILE
# undef UNDEF_TRACE_INCLUDE_FILE
#endif
#ifdef UNDEF_TRACE_INCLUDE_PATH
# undef TRACE_INCLUDE_PATH
# undef UNDEF_TRACE_INCLUDE_PATH
#endif
/* We may be processing more files */
#define CREATE_CUSTOM_TRACE_POINTS
#endif /* CREATE_CUSTOM_TRACE_POINTS */
#define __app__(x, y) str__##x##y
#define __app(x, y) __app__(x, y)
#define TRACE_SYSTEM_STRING __app(TRACE_SYSTEM_VAR,__trace_system_name)
#define TRACE_MAKE_SYSTEM_STR() \
static const char TRACE_SYSTEM_STRING[] = \
__stringify(TRACE_SYSTEM)
TRACE_MAKE_SYSTEM_STR();
#undef TRACE_DEFINE_ENUM
#define TRACE_DEFINE_ENUM(a) \
static struct trace_eval_map __used __initdata \
__##TRACE_SYSTEM##_##a = \
{ \
.system = TRACE_SYSTEM_STRING, \
.eval_string = #a, \
.eval_value = a \
}; \
static struct trace_eval_map __used \
__section("_ftrace_eval_map") \
*TRACE_SYSTEM##_##a = &__##TRACE_SYSTEM##_##a
#undef TRACE_DEFINE_SIZEOF
#define TRACE_DEFINE_SIZEOF(a) \
static struct trace_eval_map __used __initdata \
__##TRACE_SYSTEM##_##a = \
{ \
.system = TRACE_SYSTEM_STRING, \
.eval_string = "sizeof(" #a ")", \
.eval_value = sizeof(a) \
}; \
static struct trace_eval_map __used \
__section("_ftrace_eval_map") \
*TRACE_SYSTEM##_##a = &__##TRACE_SYSTEM##_##a
/* SPDX-License-Identifier: GPL-2.0 */
/* Stage 1 definitions for creating trace events */
#undef __field
#define __field(type, item) type item;
#undef __field_ext
#define __field_ext(type, item, filter_type) type item;
#undef __field_struct
#define __field_struct(type, item) type item;
#undef __field_struct_ext
#define __field_struct_ext(type, item, filter_type) type item;
#undef __array
#define __array(type, item, len) type item[len];
#undef __dynamic_array
#define __dynamic_array(type, item, len) u32 __data_loc_##item;
#undef __string
#define __string(item, src) __dynamic_array(char, item, -1)
#undef __string_len
#define __string_len(item, src, len) __dynamic_array(char, item, -1)
#undef __bitmask
#define __bitmask(item, nr_bits) __dynamic_array(char, item, -1)
#undef __sockaddr
#define __sockaddr(field, len) __dynamic_array(u8, field, len)
#undef __rel_dynamic_array
#define __rel_dynamic_array(type, item, len) u32 __rel_loc_##item;
#undef __rel_string
#define __rel_string(item, src) __rel_dynamic_array(char, item, -1)
#undef __rel_string_len
#define __rel_string_len(item, src, len) __rel_dynamic_array(char, item, -1)
#undef __rel_bitmask
#define __rel_bitmask(item, nr_bits) __rel_dynamic_array(char, item, -1)
#undef __rel_sockaddr
#define __rel_sockaddr(field, len) __rel_dynamic_array(u8, field, len)
#undef TP_STRUCT__entry
#define TP_STRUCT__entry(args...) args
/* SPDX-License-Identifier: GPL-2.0 */
/* Stage 2 definitions for creating trace events */
#undef TRACE_DEFINE_ENUM
#define TRACE_DEFINE_ENUM(a)
#undef TRACE_DEFINE_SIZEOF
#define TRACE_DEFINE_SIZEOF(a)
#undef __field
#define __field(type, item)
#undef __field_ext
#define __field_ext(type, item, filter_type)
#undef __field_struct
#define __field_struct(type, item)
#undef __field_struct_ext
#define __field_struct_ext(type, item, filter_type)
#undef __array
#define __array(type, item, len)
#undef __dynamic_array
#define __dynamic_array(type, item, len) u32 item;
#undef __string
#define __string(item, src) __dynamic_array(char, item, -1)
#undef __string_len
#define __string_len(item, src, len) __dynamic_array(char, item, -1)
#undef __bitmask
#define __bitmask(item, nr_bits) __dynamic_array(unsigned long, item, -1)
#undef __sockaddr
#define __sockaddr(field, len) __dynamic_array(u8, field, len)
#undef __rel_dynamic_array
#define __rel_dynamic_array(type, item, len) u32 item;
#undef __rel_string
#define __rel_string(item, src) __rel_dynamic_array(char, item, -1)
#undef __rel_string_len
#define __rel_string_len(item, src, len) __rel_dynamic_array(char, item, -1)
#undef __rel_bitmask
#define __rel_bitmask(item, nr_bits) __rel_dynamic_array(unsigned long, item, -1)
#undef __rel_sockaddr
#define __rel_sockaddr(field, len) __rel_dynamic_array(u8, field, len)
/* SPDX-License-Identifier: GPL-2.0 */
/* Stage 3 definitions for creating trace events */
#undef __entry
#define __entry field
#undef TP_printk
#define TP_printk(fmt, args...) fmt "\n", args
#undef __get_dynamic_array
#define __get_dynamic_array(field) \
((void *)__entry + (__entry->__data_loc_##field & 0xffff))
#undef __get_dynamic_array_len
#define __get_dynamic_array_len(field) \
((__entry->__data_loc_##field >> 16) & 0xffff)
#undef __get_str
#define __get_str(field) ((char *)__get_dynamic_array(field))
#undef __get_rel_dynamic_array
#define __get_rel_dynamic_array(field) \
((void *)__entry + \
offsetof(typeof(*__entry), __rel_loc_##field) + \
sizeof(__entry->__rel_loc_##field) + \
(__entry->__rel_loc_##field & 0xffff))
#undef __get_rel_dynamic_array_len
#define __get_rel_dynamic_array_len(field) \
((__entry->__rel_loc_##field >> 16) & 0xffff)
#undef __get_rel_str
#define __get_rel_str(field) ((char *)__get_rel_dynamic_array(field))
#undef __get_bitmask
#define __get_bitmask(field) \
({ \
void *__bitmask = __get_dynamic_array(field); \
unsigned int __bitmask_size; \
__bitmask_size = __get_dynamic_array_len(field); \
trace_print_bitmask_seq(p, __bitmask, __bitmask_size); \
})
#undef __get_rel_bitmask
#define __get_rel_bitmask(field) \
({ \
void *__bitmask = __get_rel_dynamic_array(field); \
unsigned int __bitmask_size; \
__bitmask_size = __get_rel_dynamic_array_len(field); \
trace_print_bitmask_seq(p, __bitmask, __bitmask_size); \
})
#undef __get_sockaddr
#define __get_sockaddr(field) ((struct sockaddr *)__get_dynamic_array(field))
#undef __get_rel_sockaddr
#define __get_rel_sockaddr(field) ((struct sockaddr *)__get_rel_dynamic_array(field))
#undef __print_flags
#define __print_flags(flag, delim, flag_array...) \
({ \
static const struct trace_print_flags __flags[] = \
{ flag_array, { -1, NULL }}; \
trace_print_flags_seq(p, delim, flag, __flags); \
})
#undef __print_symbolic
#define __print_symbolic(value, symbol_array...) \
({ \
static const struct trace_print_flags symbols[] = \
{ symbol_array, { -1, NULL }}; \
trace_print_symbols_seq(p, value, symbols); \
})
#undef __print_flags_u64
#undef __print_symbolic_u64
#if BITS_PER_LONG == 32
#define __print_flags_u64(flag, delim, flag_array...) \
({ \
static const struct trace_print_flags_u64 __flags[] = \
{ flag_array, { -1, NULL } }; \
trace_print_flags_seq_u64(p, delim, flag, __flags); \
})
#define __print_symbolic_u64(value, symbol_array...) \
({ \
static const struct trace_print_flags_u64 symbols[] = \
{ symbol_array, { -1, NULL } }; \
trace_print_symbols_seq_u64(p, value, symbols); \
})
#else
#define __print_flags_u64(flag, delim, flag_array...) \
__print_flags(flag, delim, flag_array)
#define __print_symbolic_u64(value, symbol_array...) \
__print_symbolic(value, symbol_array)
#endif
#undef __print_hex
#define __print_hex(buf, buf_len) \
trace_print_hex_seq(p, buf, buf_len, false)
#undef __print_hex_str
#define __print_hex_str(buf, buf_len) \
trace_print_hex_seq(p, buf, buf_len, true)
#undef __print_array
#define __print_array(array, count, el_size) \
({ \
BUILD_BUG_ON(el_size != 1 && el_size != 2 && \
el_size != 4 && el_size != 8); \
trace_print_array_seq(p, array, count, el_size); \
})
#undef __print_hex_dump
#define __print_hex_dump(prefix_str, prefix_type, \
rowsize, groupsize, buf, len, ascii) \
trace_print_hex_dump_seq(p, prefix_str, prefix_type, \
rowsize, groupsize, buf, len, ascii)
#undef __print_ns_to_secs
#define __print_ns_to_secs(value) \
({ \
u64 ____val = (u64)(value); \
do_div(____val, NSEC_PER_SEC); \
____val; \
})
#undef __print_ns_without_secs
#define __print_ns_without_secs(value) \
({ \
u64 ____val = (u64)(value); \
(u32) do_div(____val, NSEC_PER_SEC); \
})
/* SPDX-License-Identifier: GPL-2.0 */
/* Stage 4 definitions for creating trace events */
#undef __field_ext
#define __field_ext(_type, _item, _filter_type) { \
.type = #_type, .name = #_item, \
.size = sizeof(_type), .align = __alignof__(_type), \
.is_signed = is_signed_type(_type), .filter_type = _filter_type },
#undef __field_struct_ext
#define __field_struct_ext(_type, _item, _filter_type) { \
.type = #_type, .name = #_item, \
.size = sizeof(_type), .align = __alignof__(_type), \
0, .filter_type = _filter_type },
#undef __field
#define __field(type, item) __field_ext(type, item, FILTER_OTHER)
#undef __field_struct
#define __field_struct(type, item) __field_struct_ext(type, item, FILTER_OTHER)
#undef __array
#define __array(_type, _item, _len) { \
.type = #_type"["__stringify(_len)"]", .name = #_item, \
.size = sizeof(_type[_len]), .align = __alignof__(_type), \
.is_signed = is_signed_type(_type), .filter_type = FILTER_OTHER },
#undef __dynamic_array
#define __dynamic_array(_type, _item, _len) { \
.type = "__data_loc " #_type "[]", .name = #_item, \
.size = 4, .align = 4, \
.is_signed = is_signed_type(_type), .filter_type = FILTER_OTHER },
#undef __string
#define __string(item, src) __dynamic_array(char, item, -1)
#undef __string_len
#define __string_len(item, src, len) __dynamic_array(char, item, -1)
#undef __bitmask
#define __bitmask(item, nr_bits) __dynamic_array(unsigned long, item, -1)
#undef __sockaddr
#define __sockaddr(field, len) __dynamic_array(u8, field, len)
#undef __rel_dynamic_array
#define __rel_dynamic_array(_type, _item, _len) { \
.type = "__rel_loc " #_type "[]", .name = #_item, \
.size = 4, .align = 4, \
.is_signed = is_signed_type(_type), .filter_type = FILTER_OTHER },
#undef __rel_string
#define __rel_string(item, src) __rel_dynamic_array(char, item, -1)
#undef __rel_string_len
#define __rel_string_len(item, src, len) __rel_dynamic_array(char, item, -1)
#undef __rel_bitmask
#define __rel_bitmask(item, nr_bits) __rel_dynamic_array(unsigned long, item, -1)
#undef __rel_sockaddr
#define __rel_sockaddr(field, len) __rel_dynamic_array(u8, field, len)
/* SPDX-License-Identifier: GPL-2.0 */
/* Stage 5 definitions for creating trace events */
/*
* remember the offset of each array from the beginning of the event.
*/
#undef __entry
#define __entry entry
#undef __field
#define __field(type, item)
#undef __field_ext
#define __field_ext(type, item, filter_type)
#undef __field_struct
#define __field_struct(type, item)
#undef __field_struct_ext
#define __field_struct_ext(type, item, filter_type)
#undef __array
#define __array(type, item, len)
#undef __dynamic_array
#define __dynamic_array(type, item, len) \
__item_length = (len) * sizeof(type); \
__data_offsets->item = __data_size + \
offsetof(typeof(*entry), __data); \
__data_offsets->item |= __item_length << 16; \
__data_size += __item_length;
#undef __string
#define __string(item, src) __dynamic_array(char, item, \
strlen((src) ? (const char *)(src) : "(null)") + 1)
#undef __string_len
#define __string_len(item, src, len) __dynamic_array(char, item, (len) + 1)
#undef __rel_dynamic_array
#define __rel_dynamic_array(type, item, len) \
__item_length = (len) * sizeof(type); \
__data_offsets->item = __data_size + \
offsetof(typeof(*entry), __data) - \
offsetof(typeof(*entry), __rel_loc_##item) - \
sizeof(u32); \
__data_offsets->item |= __item_length << 16; \
__data_size += __item_length;
#undef __rel_string
#define __rel_string(item, src) __rel_dynamic_array(char, item, \
strlen((src) ? (const char *)(src) : "(null)") + 1)
#undef __rel_string_len
#define __rel_string_len(item, src, len) __rel_dynamic_array(char, item, (len) + 1)
/*
* __bitmask_size_in_bytes_raw is the number of bytes needed to hold
* num_possible_cpus().
*/
#define __bitmask_size_in_bytes_raw(nr_bits) \
(((nr_bits) + 7) / 8)
#define __bitmask_size_in_longs(nr_bits) \
((__bitmask_size_in_bytes_raw(nr_bits) + \
((BITS_PER_LONG / 8) - 1)) / (BITS_PER_LONG / 8))
/*
* __bitmask_size_in_bytes is the number of bytes needed to hold
* num_possible_cpus() padded out to the nearest long. This is what
* is saved in the buffer, just to be consistent.
*/
#define __bitmask_size_in_bytes(nr_bits) \
(__bitmask_size_in_longs(nr_bits) * (BITS_PER_LONG / 8))
#undef __bitmask
#define __bitmask(item, nr_bits) __dynamic_array(unsigned long, item, \
__bitmask_size_in_longs(nr_bits))
#undef __rel_bitmask
#define __rel_bitmask(item, nr_bits) __rel_dynamic_array(unsigned long, item, \
__bitmask_size_in_longs(nr_bits))
#undef __sockaddr
#define __sockaddr(field, len) __dynamic_array(u8, field, len)
#undef __rel_sockaddr
#define __rel_sockaddr(field, len) __rel_dynamic_array(u8, field, len)
/* SPDX-License-Identifier: GPL-2.0 */
/* Stage 6 definitions for creating trace events */
#undef __entry
#define __entry entry
#undef __field
#define __field(type, item)
#undef __field_struct
#define __field_struct(type, item)
#undef __array
#define __array(type, item, len)
#undef __dynamic_array
#define __dynamic_array(type, item, len) \
__entry->__data_loc_##item = __data_offsets.item;
#undef __string
#define __string(item, src) __dynamic_array(char, item, -1)
#undef __string_len
#define __string_len(item, src, len) __dynamic_array(char, item, -1)
#undef __assign_str
#define __assign_str(dst, src) \
strcpy(__get_str(dst), (src) ? (const char *)(src) : "(null)");
#undef __assign_str_len
#define __assign_str_len(dst, src, len) \
do { \
memcpy(__get_str(dst), (src), (len)); \
__get_str(dst)[len] = '\0'; \
} while(0)
#undef __bitmask
#define __bitmask(item, nr_bits) __dynamic_array(unsigned long, item, -1)
#undef __get_bitmask
#define __get_bitmask(field) (char *)__get_dynamic_array(field)
#undef __assign_bitmask
#define __assign_bitmask(dst, src, nr_bits) \
memcpy(__get_bitmask(dst), (src), __bitmask_size_in_bytes(nr_bits))
#undef __sockaddr
#define __sockaddr(field, len) __dynamic_array(u8, field, len)
#undef __get_sockaddr
#define __get_sockaddr(field) ((struct sockaddr *)__get_dynamic_array(field))
#undef __assign_sockaddr
#define __assign_sockaddr(dest, src, len) \
memcpy(__get_dynamic_array(dest), src, len)
#undef __rel_dynamic_array
#define __rel_dynamic_array(type, item, len) \
__entry->__rel_loc_##item = __data_offsets.item;
#undef __rel_string
#define __rel_string(item, src) __rel_dynamic_array(char, item, -1)
#undef __rel_string_len
#define __rel_string_len(item, src, len) __rel_dynamic_array(char, item, -1)
#undef __assign_rel_str
#define __assign_rel_str(dst, src) \
strcpy(__get_rel_str(dst), (src) ? (const char *)(src) : "(null)");
#undef __assign_rel_str_len
#define __assign_rel_str_len(dst, src, len) \
do { \
memcpy(__get_rel_str(dst), (src), (len)); \
__get_rel_str(dst)[len] = '\0'; \
} while (0)
#undef __rel_bitmask
#define __rel_bitmask(item, nr_bits) __rel_dynamic_array(unsigned long, item, -1)
#undef __get_rel_bitmask
#define __get_rel_bitmask(field) (char *)__get_rel_dynamic_array(field)
#undef __assign_rel_bitmask
#define __assign_rel_bitmask(dst, src, nr_bits) \
memcpy(__get_rel_bitmask(dst), (src), __bitmask_size_in_bytes(nr_bits))
#undef __rel_sockaddr
#define __rel_sockaddr(field, len) __rel_dynamic_array(u8, field, len)
#undef __get_rel_sockaddr
#define __get_rel_sockaddr(field) ((struct sockaddr *)__get_rel_dynamic_array(field))
#undef __assign_rel_sockaddr
#define __assign_rel_sockaddr(dest, src, len) \
memcpy(__get_rel_dynamic_array(dest), src, len)
#undef TP_fast_assign
#define TP_fast_assign(args...) args
#undef __perf_count
#define __perf_count(c) (c)
#undef __perf_task
#define __perf_task(t) (t)
/* SPDX-License-Identifier: GPL-2.0 */
/* Stage 7 definitions for creating trace events */
#undef __entry
#define __entry REC
#undef __print_flags
#undef __print_symbolic
#undef __print_hex
#undef __print_hex_str
#undef __get_dynamic_array
#undef __get_dynamic_array_len
#undef __get_str
#undef __get_bitmask
#undef __get_sockaddr
#undef __get_rel_dynamic_array
#undef __get_rel_dynamic_array_len
#undef __get_rel_str
#undef __get_rel_bitmask
#undef __get_rel_sockaddr
#undef __print_array
#undef __print_hex_dump
/*
* The below is not executed in the kernel. It is only what is
* displayed in the print format for userspace to parse.
*/
#undef __print_ns_to_secs
#define __print_ns_to_secs(val) (val) / 1000000000UL
#undef __print_ns_without_secs
#define __print_ns_without_secs(val) (val) % 1000000000UL
#undef TP_printk
#define TP_printk(fmt, args...) "\"" fmt "\", " __stringify(args)
/* SPDX-License-Identifier: GPL-2.0 */
/*
* This is similar to the trace_events.h file, but is to only
* create custom trace events to be attached to existing tracepoints.
* Where as the TRACE_EVENT() macro (from trace_events.h) will create
* both the trace event and the tracepoint it will attach the event to,
* TRACE_CUSTOM_EVENT() is to create only a custom version of an existing
* trace event (created by TRACE_EVENT() or DEFINE_EVENT()), and will
* be placed in the "custom" system.
*/
#include <linux/trace_events.h>
/* All custom events are placed in the custom group */
#undef TRACE_SYSTEM
#define TRACE_SYSTEM custom
#ifndef TRACE_SYSTEM_VAR
#define TRACE_SYSTEM_VAR TRACE_SYSTEM
#endif
/* The init stage creates the system string and enum mappings */
#include "stages/init.h"
#undef TRACE_CUSTOM_EVENT
#define TRACE_CUSTOM_EVENT(name, proto, args, tstruct, assign, print) \
DECLARE_CUSTOM_EVENT_CLASS(name, \
PARAMS(proto), \
PARAMS(args), \
PARAMS(tstruct), \
PARAMS(assign), \
PARAMS(print)); \
DEFINE_CUSTOM_EVENT(name, name, PARAMS(proto), PARAMS(args));
/* Stage 1 creates the structure of the recorded event layout */
#include "stages/stage1_defines.h"
#undef DECLARE_CUSTOM_EVENT_CLASS
#define DECLARE_CUSTOM_EVENT_CLASS(name, proto, args, tstruct, assign, print) \
struct trace_custom_event_raw_##name { \
struct trace_entry ent; \
tstruct \
char __data[]; \
}; \
\
static struct trace_event_class custom_event_class_##name;
#undef DEFINE_CUSTOM_EVENT
#define DEFINE_CUSTOM_EVENT(template, name, proto, args) \
static struct trace_event_call __used \
__attribute__((__aligned__(4))) custom_event_##name
#include TRACE_INCLUDE(TRACE_INCLUDE_FILE)
/* Stage 2 creates the custom class */
#include "stages/stage2_defines.h"
#undef DECLARE_CUSTOM_EVENT_CLASS
#define DECLARE_CUSTOM_EVENT_CLASS(call, proto, args, tstruct, assign, print) \
struct trace_custom_event_data_offsets_##call { \
tstruct; \
};
#undef DEFINE_CUSTOM_EVENT
#define DEFINE_CUSTOM_EVENT(template, name, proto, args)
#include TRACE_INCLUDE(TRACE_INCLUDE_FILE)
/* Stage 3 create the way to print the custom event */
#include "stages/stage3_defines.h"
#undef DECLARE_CUSTOM_EVENT_CLASS
#define DECLARE_CUSTOM_EVENT_CLASS(call, proto, args, tstruct, assign, print) \
static notrace enum print_line_t \
trace_custom_raw_output_##call(struct trace_iterator *iter, int flags, \
struct trace_event *trace_event) \
{ \
struct trace_seq *s = &iter->seq; \
struct trace_seq __maybe_unused *p = &iter->tmp_seq; \
struct trace_custom_event_raw_##call *field; \
int ret; \
\
field = (typeof(field))iter->ent; \
\
ret = trace_raw_output_prep(iter, trace_event); \
if (ret != TRACE_TYPE_HANDLED) \
return ret; \
\
trace_event_printf(iter, print); \
\
return trace_handle_return(s); \
} \
static struct trace_event_functions trace_custom_event_type_funcs_##call = { \
.trace = trace_custom_raw_output_##call, \
};
#include TRACE_INCLUDE(TRACE_INCLUDE_FILE)
/* Stage 4 creates the offset layout for the fields */
#include "stages/stage4_defines.h"
#undef DECLARE_CUSTOM_EVENT_CLASS
#define DECLARE_CUSTOM_EVENT_CLASS(call, proto, args, tstruct, func, print) \
static struct trace_event_fields trace_custom_event_fields_##call[] = { \
tstruct \
{} };
#include TRACE_INCLUDE(TRACE_INCLUDE_FILE)
/* Stage 5 creates the helper function for dynamic fields */
#include "stages/stage5_defines.h"
#undef DECLARE_CUSTOM_EVENT_CLASS
#define DECLARE_CUSTOM_EVENT_CLASS(call, proto, args, tstruct, assign, print) \
static inline notrace int trace_custom_event_get_offsets_##call( \
struct trace_custom_event_data_offsets_##call *__data_offsets, proto) \
{ \
int __data_size = 0; \
int __maybe_unused __item_length; \
struct trace_custom_event_raw_##call __maybe_unused *entry; \
\
tstruct; \
\
return __data_size; \
}
#include TRACE_INCLUDE(TRACE_INCLUDE_FILE)
/* Stage 6 creates the probe function that records the event */
#include "stages/stage6_defines.h"
#undef DECLARE_CUSTOM_EVENT_CLASS
#define DECLARE_CUSTOM_EVENT_CLASS(call, proto, args, tstruct, assign, print) \
\
static notrace void \
trace_custom_event_raw_event_##call(void *__data, proto) \
{ \
struct trace_event_file *trace_file = __data; \
struct trace_custom_event_data_offsets_##call __maybe_unused __data_offsets; \
struct trace_event_buffer fbuffer; \
struct trace_custom_event_raw_##call *entry; \
int __data_size; \
\
if (trace_trigger_soft_disabled(trace_file)) \
return; \
\
__data_size = trace_custom_event_get_offsets_##call(&__data_offsets, args); \
\
entry = trace_event_buffer_reserve(&fbuffer, trace_file, \
sizeof(*entry) + __data_size); \
\
if (!entry) \
return; \
\
tstruct \
\
{ assign; } \
\
trace_event_buffer_commit(&fbuffer); \
}
/*
* The ftrace_test_custom_probe is compiled out, it is only here as a build time check
* to make sure that if the tracepoint handling changes, the ftrace probe will
* fail to compile unless it too is updated.
*/
#undef DEFINE_CUSTOM_EVENT
#define DEFINE_CUSTOM_EVENT(template, call, proto, args) \
static inline void ftrace_test_custom_probe_##call(void) \
{ \
check_trace_callback_type_##call(trace_custom_event_raw_event_##template); \
}
#include TRACE_INCLUDE(TRACE_INCLUDE_FILE)
/* Stage 7 creates the actual class and event structure for the custom event */
#include "stages/stage7_defines.h"
#undef DECLARE_CUSTOM_EVENT_CLASS
#define DECLARE_CUSTOM_EVENT_CLASS(call, proto, args, tstruct, assign, print) \
static char custom_print_fmt_##call[] = print; \
static struct trace_event_class __used __refdata custom_event_class_##call = { \
.system = TRACE_SYSTEM_STRING, \
.fields_array = trace_custom_event_fields_##call, \
.fields = LIST_HEAD_INIT(custom_event_class_##call.fields),\
.raw_init = trace_event_raw_init, \
.probe = trace_custom_event_raw_event_##call, \
.reg = trace_event_reg, \
};
#undef DEFINE_CUSTOM_EVENT
#define DEFINE_CUSTOM_EVENT(template, call, proto, args) \
\
static struct trace_event_call __used custom_event_##call = { \
.name = #call, \
.class = &custom_event_class_##template, \
.event.funcs = &trace_custom_event_type_funcs_##template, \
.print_fmt = custom_print_fmt_##template, \
.flags = TRACE_EVENT_FL_CUSTOM, \
}; \
static inline int trace_custom_event_##call##_update(struct tracepoint *tp) \
{ \
if (tp->name && strcmp(tp->name, #call) == 0) { \
custom_event_##call.tp = tp; \
custom_event_##call.flags = TRACE_EVENT_FL_TRACEPOINT; \
return 1; \
} \
return 0; \
} \
static struct trace_event_call __used \
__section("_ftrace_events") *__custom_event_##call = &custom_event_##call
#include TRACE_INCLUDE(TRACE_INCLUDE_FILE)
...@@ -24,42 +24,7 @@ ...@@ -24,42 +24,7 @@
#define TRACE_SYSTEM_VAR TRACE_SYSTEM #define TRACE_SYSTEM_VAR TRACE_SYSTEM
#endif #endif
#define __app__(x, y) str__##x##y #include "stages/init.h"
#define __app(x, y) __app__(x, y)
#define TRACE_SYSTEM_STRING __app(TRACE_SYSTEM_VAR,__trace_system_name)
#define TRACE_MAKE_SYSTEM_STR() \
static const char TRACE_SYSTEM_STRING[] = \
__stringify(TRACE_SYSTEM)
TRACE_MAKE_SYSTEM_STR();
#undef TRACE_DEFINE_ENUM
#define TRACE_DEFINE_ENUM(a) \
static struct trace_eval_map __used __initdata \
__##TRACE_SYSTEM##_##a = \
{ \
.system = TRACE_SYSTEM_STRING, \
.eval_string = #a, \
.eval_value = a \
}; \
static struct trace_eval_map __used \
__section("_ftrace_eval_map") \
*TRACE_SYSTEM##_##a = &__##TRACE_SYSTEM##_##a
#undef TRACE_DEFINE_SIZEOF
#define TRACE_DEFINE_SIZEOF(a) \
static struct trace_eval_map __used __initdata \
__##TRACE_SYSTEM##_##a = \
{ \
.system = TRACE_SYSTEM_STRING, \
.eval_string = "sizeof(" #a ")", \
.eval_value = sizeof(a) \
}; \
static struct trace_eval_map __used \
__section("_ftrace_eval_map") \
*TRACE_SYSTEM##_##a = &__##TRACE_SYSTEM##_##a
/* /*
* DECLARE_EVENT_CLASS can be used to add a generic function * DECLARE_EVENT_CLASS can be used to add a generic function
...@@ -80,54 +45,7 @@ TRACE_MAKE_SYSTEM_STR(); ...@@ -80,54 +45,7 @@ TRACE_MAKE_SYSTEM_STR();
PARAMS(print)); \ PARAMS(print)); \
DEFINE_EVENT(name, name, PARAMS(proto), PARAMS(args)); DEFINE_EVENT(name, name, PARAMS(proto), PARAMS(args));
#include "stages/stage1_defines.h"
#undef __field
#define __field(type, item) type item;
#undef __field_ext
#define __field_ext(type, item, filter_type) type item;
#undef __field_struct
#define __field_struct(type, item) type item;
#undef __field_struct_ext
#define __field_struct_ext(type, item, filter_type) type item;
#undef __array
#define __array(type, item, len) type item[len];
#undef __dynamic_array
#define __dynamic_array(type, item, len) u32 __data_loc_##item;
#undef __string
#define __string(item, src) __dynamic_array(char, item, -1)
#undef __string_len
#define __string_len(item, src, len) __dynamic_array(char, item, -1)
#undef __bitmask
#define __bitmask(item, nr_bits) __dynamic_array(char, item, -1)
#undef __sockaddr
#define __sockaddr(field, len) __dynamic_array(u8, field, len)
#undef __rel_dynamic_array
#define __rel_dynamic_array(type, item, len) u32 __rel_loc_##item;
#undef __rel_string
#define __rel_string(item, src) __rel_dynamic_array(char, item, -1)
#undef __rel_string_len
#define __rel_string_len(item, src, len) __rel_dynamic_array(char, item, -1)
#undef __rel_bitmask
#define __rel_bitmask(item, nr_bits) __rel_dynamic_array(char, item, -1)
#undef __rel_sockaddr
#define __rel_sockaddr(field, len) __rel_dynamic_array(u8, field, len)
#undef TP_STRUCT__entry
#define TP_STRUCT__entry(args...) args
#undef DECLARE_EVENT_CLASS #undef DECLARE_EVENT_CLASS
#define DECLARE_EVENT_CLASS(name, proto, args, tstruct, assign, print) \ #define DECLARE_EVENT_CLASS(name, proto, args, tstruct, assign, print) \
...@@ -191,56 +109,7 @@ TRACE_MAKE_SYSTEM_STR(); ...@@ -191,56 +109,7 @@ TRACE_MAKE_SYSTEM_STR();
* The size of an array is also encoded, in the higher 16 bits of <item>. * The size of an array is also encoded, in the higher 16 bits of <item>.
*/ */
#undef TRACE_DEFINE_ENUM #include "stages/stage2_defines.h"
#define TRACE_DEFINE_ENUM(a)
#undef TRACE_DEFINE_SIZEOF
#define TRACE_DEFINE_SIZEOF(a)
#undef __field
#define __field(type, item)
#undef __field_ext
#define __field_ext(type, item, filter_type)
#undef __field_struct
#define __field_struct(type, item)
#undef __field_struct_ext
#define __field_struct_ext(type, item, filter_type)
#undef __array
#define __array(type, item, len)
#undef __dynamic_array
#define __dynamic_array(type, item, len) u32 item;
#undef __string
#define __string(item, src) __dynamic_array(char, item, -1)
#undef __string_len
#define __string_len(item, src, len) __dynamic_array(char, item, -1)
#undef __bitmask
#define __bitmask(item, nr_bits) __dynamic_array(unsigned long, item, -1)
#undef __sockaddr
#define __sockaddr(field, len) __dynamic_array(u8, field, len)
#undef __rel_dynamic_array
#define __rel_dynamic_array(type, item, len) u32 item;
#undef __rel_string
#define __rel_string(item, src) __rel_dynamic_array(char, item, -1)
#undef __rel_string_len
#define __rel_string_len(item, src, len) __rel_dynamic_array(char, item, -1)
#undef __rel_bitmask
#define __rel_bitmask(item, nr_bits) __rel_dynamic_array(unsigned long, item, -1)
#undef __rel_sockaddr
#define __rel_sockaddr(field, len) __rel_dynamic_array(u8, field, len)
#undef DECLARE_EVENT_CLASS #undef DECLARE_EVENT_CLASS
#define DECLARE_EVENT_CLASS(call, proto, args, tstruct, assign, print) \ #define DECLARE_EVENT_CLASS(call, proto, args, tstruct, assign, print) \
...@@ -312,137 +181,7 @@ TRACE_MAKE_SYSTEM_STR(); ...@@ -312,137 +181,7 @@ TRACE_MAKE_SYSTEM_STR();
* in binary. * in binary.
*/ */
#undef __entry #include "stages/stage3_defines.h"
#define __entry field
#undef TP_printk
#define TP_printk(fmt, args...) fmt "\n", args
#undef __get_dynamic_array
#define __get_dynamic_array(field) \
((void *)__entry + (__entry->__data_loc_##field & 0xffff))
#undef __get_dynamic_array_len
#define __get_dynamic_array_len(field) \
((__entry->__data_loc_##field >> 16) & 0xffff)
#undef __get_str
#define __get_str(field) ((char *)__get_dynamic_array(field))
#undef __get_rel_dynamic_array
#define __get_rel_dynamic_array(field) \
((void *)__entry + \
offsetof(typeof(*__entry), __rel_loc_##field) + \
sizeof(__entry->__rel_loc_##field) + \
(__entry->__rel_loc_##field & 0xffff))
#undef __get_rel_dynamic_array_len
#define __get_rel_dynamic_array_len(field) \
((__entry->__rel_loc_##field >> 16) & 0xffff)
#undef __get_rel_str
#define __get_rel_str(field) ((char *)__get_rel_dynamic_array(field))
#undef __get_bitmask
#define __get_bitmask(field) \
({ \
void *__bitmask = __get_dynamic_array(field); \
unsigned int __bitmask_size; \
__bitmask_size = __get_dynamic_array_len(field); \
trace_print_bitmask_seq(p, __bitmask, __bitmask_size); \
})
#undef __get_rel_bitmask
#define __get_rel_bitmask(field) \
({ \
void *__bitmask = __get_rel_dynamic_array(field); \
unsigned int __bitmask_size; \
__bitmask_size = __get_rel_dynamic_array_len(field); \
trace_print_bitmask_seq(p, __bitmask, __bitmask_size); \
})
#undef __get_sockaddr
#define __get_sockaddr(field) ((struct sockaddr *)__get_dynamic_array(field))
#undef __get_rel_sockaddr
#define __get_rel_sockaddr(field) ((struct sockaddr *)__get_rel_dynamic_array(field))
#undef __print_flags
#define __print_flags(flag, delim, flag_array...) \
({ \
static const struct trace_print_flags __flags[] = \
{ flag_array, { -1, NULL }}; \
trace_print_flags_seq(p, delim, flag, __flags); \
})
#undef __print_symbolic
#define __print_symbolic(value, symbol_array...) \
({ \
static const struct trace_print_flags symbols[] = \
{ symbol_array, { -1, NULL }}; \
trace_print_symbols_seq(p, value, symbols); \
})
#undef __print_flags_u64
#undef __print_symbolic_u64
#if BITS_PER_LONG == 32
#define __print_flags_u64(flag, delim, flag_array...) \
({ \
static const struct trace_print_flags_u64 __flags[] = \
{ flag_array, { -1, NULL } }; \
trace_print_flags_seq_u64(p, delim, flag, __flags); \
})
#define __print_symbolic_u64(value, symbol_array...) \
({ \
static const struct trace_print_flags_u64 symbols[] = \
{ symbol_array, { -1, NULL } }; \
trace_print_symbols_seq_u64(p, value, symbols); \
})
#else
#define __print_flags_u64(flag, delim, flag_array...) \
__print_flags(flag, delim, flag_array)
#define __print_symbolic_u64(value, symbol_array...) \
__print_symbolic(value, symbol_array)
#endif
#undef __print_hex
#define __print_hex(buf, buf_len) \
trace_print_hex_seq(p, buf, buf_len, false)
#undef __print_hex_str
#define __print_hex_str(buf, buf_len) \
trace_print_hex_seq(p, buf, buf_len, true)
#undef __print_array
#define __print_array(array, count, el_size) \
({ \
BUILD_BUG_ON(el_size != 1 && el_size != 2 && \
el_size != 4 && el_size != 8); \
trace_print_array_seq(p, array, count, el_size); \
})
#undef __print_hex_dump
#define __print_hex_dump(prefix_str, prefix_type, \
rowsize, groupsize, buf, len, ascii) \
trace_print_hex_dump_seq(p, prefix_str, prefix_type, \
rowsize, groupsize, buf, len, ascii)
#undef __print_ns_to_secs
#define __print_ns_to_secs(value) \
({ \
u64 ____val = (u64)(value); \
do_div(____val, NSEC_PER_SEC); \
____val; \
})
#undef __print_ns_without_secs
#define __print_ns_without_secs(value) \
({ \
u64 ____val = (u64)(value); \
(u32) do_div(____val, NSEC_PER_SEC); \
})
#undef DECLARE_EVENT_CLASS #undef DECLARE_EVENT_CLASS
#define DECLARE_EVENT_CLASS(call, proto, args, tstruct, assign, print) \ #define DECLARE_EVENT_CLASS(call, proto, args, tstruct, assign, print) \
...@@ -497,65 +236,7 @@ static struct trace_event_functions trace_event_type_funcs_##call = { \ ...@@ -497,65 +236,7 @@ static struct trace_event_functions trace_event_type_funcs_##call = { \
#include TRACE_INCLUDE(TRACE_INCLUDE_FILE) #include TRACE_INCLUDE(TRACE_INCLUDE_FILE)
#undef __field_ext #include "stages/stage4_defines.h"
#define __field_ext(_type, _item, _filter_type) { \
.type = #_type, .name = #_item, \
.size = sizeof(_type), .align = __alignof__(_type), \
.is_signed = is_signed_type(_type), .filter_type = _filter_type },
#undef __field_struct_ext
#define __field_struct_ext(_type, _item, _filter_type) { \
.type = #_type, .name = #_item, \
.size = sizeof(_type), .align = __alignof__(_type), \
0, .filter_type = _filter_type },
#undef __field
#define __field(type, item) __field_ext(type, item, FILTER_OTHER)
#undef __field_struct
#define __field_struct(type, item) __field_struct_ext(type, item, FILTER_OTHER)
#undef __array
#define __array(_type, _item, _len) { \
.type = #_type"["__stringify(_len)"]", .name = #_item, \
.size = sizeof(_type[_len]), .align = __alignof__(_type), \
.is_signed = is_signed_type(_type), .filter_type = FILTER_OTHER },
#undef __dynamic_array
#define __dynamic_array(_type, _item, _len) { \
.type = "__data_loc " #_type "[]", .name = #_item, \
.size = 4, .align = 4, \
.is_signed = is_signed_type(_type), .filter_type = FILTER_OTHER },
#undef __string
#define __string(item, src) __dynamic_array(char, item, -1)
#undef __string_len
#define __string_len(item, src, len) __dynamic_array(char, item, -1)
#undef __bitmask
#define __bitmask(item, nr_bits) __dynamic_array(unsigned long, item, -1)
#undef __sockaddr
#define __sockaddr(field, len) __dynamic_array(u8, field, len)
#undef __rel_dynamic_array
#define __rel_dynamic_array(_type, _item, _len) { \
.type = "__rel_loc " #_type "[]", .name = #_item, \
.size = 4, .align = 4, \
.is_signed = is_signed_type(_type), .filter_type = FILTER_OTHER },
#undef __rel_string
#define __rel_string(item, src) __rel_dynamic_array(char, item, -1)
#undef __rel_string_len
#define __rel_string_len(item, src, len) __rel_dynamic_array(char, item, -1)
#undef __rel_bitmask
#define __rel_bitmask(item, nr_bits) __rel_dynamic_array(unsigned long, item, -1)
#undef __rel_sockaddr
#define __rel_sockaddr(field, len) __rel_dynamic_array(u8, field, len)
#undef DECLARE_EVENT_CLASS #undef DECLARE_EVENT_CLASS
#define DECLARE_EVENT_CLASS(call, proto, args, tstruct, func, print) \ #define DECLARE_EVENT_CLASS(call, proto, args, tstruct, func, print) \
...@@ -568,91 +249,7 @@ static struct trace_event_fields trace_event_fields_##call[] = { \ ...@@ -568,91 +249,7 @@ static struct trace_event_fields trace_event_fields_##call[] = { \
#include TRACE_INCLUDE(TRACE_INCLUDE_FILE) #include TRACE_INCLUDE(TRACE_INCLUDE_FILE)
/* #include "stages/stage5_defines.h"
* remember the offset of each array from the beginning of the event.
*/
#undef __entry
#define __entry entry
#undef __field
#define __field(type, item)
#undef __field_ext
#define __field_ext(type, item, filter_type)
#undef __field_struct
#define __field_struct(type, item)
#undef __field_struct_ext
#define __field_struct_ext(type, item, filter_type)
#undef __array
#define __array(type, item, len)
#undef __dynamic_array
#define __dynamic_array(type, item, len) \
__item_length = (len) * sizeof(type); \
__data_offsets->item = __data_size + \
offsetof(typeof(*entry), __data); \
__data_offsets->item |= __item_length << 16; \
__data_size += __item_length;
#undef __string
#define __string(item, src) __dynamic_array(char, item, \
strlen((src) ? (const char *)(src) : "(null)") + 1)
#undef __string_len
#define __string_len(item, src, len) __dynamic_array(char, item, (len) + 1)
#undef __rel_dynamic_array
#define __rel_dynamic_array(type, item, len) \
__item_length = (len) * sizeof(type); \
__data_offsets->item = __data_size + \
offsetof(typeof(*entry), __data) - \
offsetof(typeof(*entry), __rel_loc_##item) - \
sizeof(u32); \
__data_offsets->item |= __item_length << 16; \
__data_size += __item_length;
#undef __rel_string
#define __rel_string(item, src) __rel_dynamic_array(char, item, \
strlen((src) ? (const char *)(src) : "(null)") + 1)
#undef __rel_string_len
#define __rel_string_len(item, src, len) __rel_dynamic_array(char, item, (len) + 1)
/*
* __bitmask_size_in_bytes_raw is the number of bytes needed to hold
* num_possible_cpus().
*/
#define __bitmask_size_in_bytes_raw(nr_bits) \
(((nr_bits) + 7) / 8)
#define __bitmask_size_in_longs(nr_bits) \
((__bitmask_size_in_bytes_raw(nr_bits) + \
((BITS_PER_LONG / 8) - 1)) / (BITS_PER_LONG / 8))
/*
* __bitmask_size_in_bytes is the number of bytes needed to hold
* num_possible_cpus() padded out to the nearest long. This is what
* is saved in the buffer, just to be consistent.
*/
#define __bitmask_size_in_bytes(nr_bits) \
(__bitmask_size_in_longs(nr_bits) * (BITS_PER_LONG / 8))
#undef __bitmask
#define __bitmask(item, nr_bits) __dynamic_array(unsigned long, item, \
__bitmask_size_in_longs(nr_bits))
#undef __rel_bitmask
#define __rel_bitmask(item, nr_bits) __rel_dynamic_array(unsigned long, item, \
__bitmask_size_in_longs(nr_bits))
#undef __sockaddr
#define __sockaddr(field, len) __dynamic_array(u8, field, len)
#undef __rel_sockaddr
#define __rel_sockaddr(field, len) __rel_dynamic_array(u8, field, len)
#undef DECLARE_EVENT_CLASS #undef DECLARE_EVENT_CLASS
#define DECLARE_EVENT_CLASS(call, proto, args, tstruct, assign, print) \ #define DECLARE_EVENT_CLASS(call, proto, args, tstruct, assign, print) \
...@@ -775,107 +372,7 @@ static inline notrace int trace_event_get_offsets_##call( \ ...@@ -775,107 +372,7 @@ static inline notrace int trace_event_get_offsets_##call( \
#define _TRACE_PERF_INIT(call) #define _TRACE_PERF_INIT(call)
#endif /* CONFIG_PERF_EVENTS */ #endif /* CONFIG_PERF_EVENTS */
#undef __entry #include "stages/stage6_defines.h"
#define __entry entry
#undef __field
#define __field(type, item)
#undef __field_struct
#define __field_struct(type, item)
#undef __array
#define __array(type, item, len)
#undef __dynamic_array
#define __dynamic_array(type, item, len) \
__entry->__data_loc_##item = __data_offsets.item;
#undef __string
#define __string(item, src) __dynamic_array(char, item, -1)
#undef __string_len
#define __string_len(item, src, len) __dynamic_array(char, item, -1)
#undef __assign_str
#define __assign_str(dst, src) \
strcpy(__get_str(dst), (src) ? (const char *)(src) : "(null)");
#undef __assign_str_len
#define __assign_str_len(dst, src, len) \
do { \
memcpy(__get_str(dst), (src), (len)); \
__get_str(dst)[len] = '\0'; \
} while(0)
#undef __bitmask
#define __bitmask(item, nr_bits) __dynamic_array(unsigned long, item, -1)
#undef __get_bitmask
#define __get_bitmask(field) (char *)__get_dynamic_array(field)
#undef __assign_bitmask
#define __assign_bitmask(dst, src, nr_bits) \
memcpy(__get_bitmask(dst), (src), __bitmask_size_in_bytes(nr_bits))
#undef __sockaddr
#define __sockaddr(field, len) __dynamic_array(u8, field, len)
#undef __get_sockaddr
#define __get_sockaddr(field) ((struct sockaddr *)__get_dynamic_array(field))
#define __assign_sockaddr(dest, src, len) \
memcpy(__get_dynamic_array(dest), src, len)
#undef __rel_dynamic_array
#define __rel_dynamic_array(type, item, len) \
__entry->__rel_loc_##item = __data_offsets.item;
#undef __rel_string
#define __rel_string(item, src) __rel_dynamic_array(char, item, -1)
#undef __rel_string_len
#define __rel_string_len(item, src, len) __rel_dynamic_array(char, item, -1)
#undef __assign_rel_str
#define __assign_rel_str(dst, src) \
strcpy(__get_rel_str(dst), (src) ? (const char *)(src) : "(null)");
#undef __assign_rel_str_len
#define __assign_rel_str_len(dst, src, len) \
do { \
memcpy(__get_rel_str(dst), (src), (len)); \
__get_rel_str(dst)[len] = '\0'; \
} while (0)
#undef __rel_bitmask
#define __rel_bitmask(item, nr_bits) __rel_dynamic_array(unsigned long, item, -1)
#undef __get_rel_bitmask
#define __get_rel_bitmask(field) (char *)__get_rel_dynamic_array(field)
#undef __assign_rel_bitmask
#define __assign_rel_bitmask(dst, src, nr_bits) \
memcpy(__get_rel_bitmask(dst), (src), __bitmask_size_in_bytes(nr_bits))
#undef __rel_sockaddr
#define __rel_sockaddr(field, len) __rel_dynamic_array(u8, field, len)
#undef __get_rel_sockaddr
#define __get_rel_sockaddr(field) ((struct sockaddr *)__get_rel_dynamic_array(field))
#define __assign_rel_sockaddr(dest, src, len) \
memcpy(__get_rel_dynamic_array(dest), src, len)
#undef TP_fast_assign
#define TP_fast_assign(args...) args
#undef __perf_count
#define __perf_count(c) (c)
#undef __perf_task
#define __perf_task(t) (t)
#undef DECLARE_EVENT_CLASS #undef DECLARE_EVENT_CLASS
#define DECLARE_EVENT_CLASS(call, proto, args, tstruct, assign, print) \ #define DECLARE_EVENT_CLASS(call, proto, args, tstruct, assign, print) \
...@@ -921,38 +418,7 @@ static inline void ftrace_test_probe_##call(void) \ ...@@ -921,38 +418,7 @@ static inline void ftrace_test_probe_##call(void) \
#include TRACE_INCLUDE(TRACE_INCLUDE_FILE) #include TRACE_INCLUDE(TRACE_INCLUDE_FILE)
#undef __entry #include "stages/stage7_defines.h"
#define __entry REC
#undef __print_flags
#undef __print_symbolic
#undef __print_hex
#undef __print_hex_str
#undef __get_dynamic_array
#undef __get_dynamic_array_len
#undef __get_str
#undef __get_bitmask
#undef __get_sockaddr
#undef __get_rel_dynamic_array
#undef __get_rel_dynamic_array_len
#undef __get_rel_str
#undef __get_rel_bitmask
#undef __get_rel_sockaddr
#undef __print_array
#undef __print_hex_dump
/*
* The below is not executed in the kernel. It is only what is
* displayed in the print format for userspace to parse.
*/
#undef __print_ns_to_secs
#define __print_ns_to_secs(val) (val) / 1000000000UL
#undef __print_ns_without_secs
#define __print_ns_without_secs(val) (val) % 1000000000UL
#undef TP_printk
#define TP_printk(fmt, args...) "\"" fmt "\", " __stringify(args)
#undef DECLARE_EVENT_CLASS #undef DECLARE_EVENT_CLASS
#define DECLARE_EVENT_CLASS(call, proto, args, tstruct, assign, print) \ #define DECLARE_EVENT_CLASS(call, proto, args, tstruct, assign, print) \
......
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
/*
* Copyright (c) 2021, Microsoft Corporation.
*
* Authors:
* Beau Belgrave <beaub@linux.microsoft.com>
*/
#ifndef _UAPI_LINUX_USER_EVENTS_H
#define _UAPI_LINUX_USER_EVENTS_H
#include <linux/types.h>
#include <linux/ioctl.h>
#ifdef __KERNEL__
#include <linux/uio.h>
#else
#include <sys/uio.h>
#endif
#define USER_EVENTS_SYSTEM "user_events"
#define USER_EVENTS_PREFIX "u:"
/* Bits 0-6 are for known probe types, Bit 7 is for unknown probes */
#define EVENT_BIT_FTRACE 0
#define EVENT_BIT_PERF 1
#define EVENT_BIT_OTHER 7
#define EVENT_STATUS_FTRACE (1 << EVENT_BIT_FTRACE)
#define EVENT_STATUS_PERF (1 << EVENT_BIT_PERF)
#define EVENT_STATUS_OTHER (1 << EVENT_BIT_OTHER)
/* Create dynamic location entry within a 32-bit value */
#define DYN_LOC(offset, size) ((size) << 16 | (offset))
/* Use raw iterator for attached BPF program(s), no affect on ftrace/perf */
#define FLAG_BPF_ITER (1 << 0)
/*
* Describes an event registration and stores the results of the registration.
* This structure is passed to the DIAG_IOCSREG ioctl, callers at a minimum
* must set the size and name_args before invocation.
*/
struct user_reg {
/* Input: Size of the user_reg structure being used */
__u32 size;
/* Input: Pointer to string with event name, description and flags */
__u64 name_args;
/* Output: Byte index of the event within the status page */
__u32 status_index;
/* Output: Index of the event to use when writing data */
__u32 write_index;
};
#define DIAG_IOC_MAGIC '*'
/* Requests to register a user_event */
#define DIAG_IOCSREG _IOWR(DIAG_IOC_MAGIC, 0, struct user_reg*)
/* Requests to delete a user_event */
#define DIAG_IOCSDEL _IOW(DIAG_IOC_MAGIC, 1, char*)
/* Data type that was passed to the BPF program */
enum {
/* Data resides in kernel space */
USER_BPF_DATA_KERNEL,
/* Data resides in user space */
USER_BPF_DATA_USER,
/* Data is a pointer to a user_bpf_iter structure */
USER_BPF_DATA_ITER,
};
/*
* Describes an iovec iterator that BPF programs can use to access data for
* a given user_event write() / writev() call.
*/
struct user_bpf_iter {
/* Offset of the data within the first iovec */
__u32 iov_offset;
/* Number of iovec structures */
__u32 nr_segs;
/* Pointer to iovec structures */
const struct iovec *iov;
};
/* Context that BPF programs receive when attached to a user_event */
struct user_bpf_context {
/* Data type being passed (see union below) */
__u32 data_type;
/* Length of the data */
__u32 data_len;
/* Pointer to data, varies by data type */
union {
/* Kernel data (data_type == USER_BPF_DATA_KERNEL) */
void *kdata;
/* User data (data_type == USER_BPF_DATA_USER) */
void *udata;
/* Direct iovec (data_type == USER_BPF_DATA_ITER) */
struct user_bpf_iter *iter;
};
};
#endif /* _UAPI_LINUX_USER_EVENTS_H */
...@@ -737,6 +737,20 @@ config SYNTH_EVENTS ...@@ -737,6 +737,20 @@ config SYNTH_EVENTS
If in doubt, say N. If in doubt, say N.
config USER_EVENTS
bool "User trace events"
select TRACING
select DYNAMIC_EVENTS
help
User trace events are user-defined trace events that
can be used like an existing kernel trace event. User trace
events are generated by writing to a tracefs file. User
processes can determine if their tracing events should be
generated by memory mapping a tracefs file and checking for
an associated byte being non-zero.
If in doubt, say N.
config HIST_TRIGGERS config HIST_TRIGGERS
bool "Histogram triggers" bool "Histogram triggers"
depends on ARCH_HAVE_NMI_SAFE_CMPXCHG depends on ARCH_HAVE_NMI_SAFE_CMPXCHG
......
...@@ -82,6 +82,7 @@ obj-$(CONFIG_PROBE_EVENTS) += trace_eprobe.o ...@@ -82,6 +82,7 @@ obj-$(CONFIG_PROBE_EVENTS) += trace_eprobe.o
obj-$(CONFIG_TRACE_EVENT_INJECT) += trace_events_inject.o obj-$(CONFIG_TRACE_EVENT_INJECT) += trace_events_inject.o
obj-$(CONFIG_SYNTH_EVENTS) += trace_events_synth.o obj-$(CONFIG_SYNTH_EVENTS) += trace_events_synth.o
obj-$(CONFIG_HIST_TRIGGERS) += trace_events_hist.o obj-$(CONFIG_HIST_TRIGGERS) += trace_events_hist.o
obj-$(CONFIG_USER_EVENTS) += trace_events_user.o
obj-$(CONFIG_BPF_EVENTS) += bpf_trace.o obj-$(CONFIG_BPF_EVENTS) += bpf_trace.o
obj-$(CONFIG_KPROBE_EVENTS) += trace_kprobe.o obj-$(CONFIG_KPROBE_EVENTS) += trace_kprobe.o
obj-$(CONFIG_TRACEPOINTS) += error_report-traces.o obj-$(CONFIG_TRACEPOINTS) += error_report-traces.o
......
...@@ -7096,6 +7096,8 @@ void __init ftrace_free_init_mem(void) ...@@ -7096,6 +7096,8 @@ void __init ftrace_free_init_mem(void)
void *start = (void *)(&__init_begin); void *start = (void *)(&__init_begin);
void *end = (void *)(&__init_end); void *end = (void *)(&__init_end);
ftrace_boot_snapshot();
ftrace_free_mem(NULL, start, end); ftrace_free_mem(NULL, start, end);
} }
......
...@@ -185,6 +185,7 @@ static char bootup_tracer_buf[MAX_TRACER_SIZE] __initdata; ...@@ -185,6 +185,7 @@ static char bootup_tracer_buf[MAX_TRACER_SIZE] __initdata;
static char *default_bootup_tracer; static char *default_bootup_tracer;
static bool allocate_snapshot; static bool allocate_snapshot;
static bool snapshot_at_boot;
static int __init set_cmdline_ftrace(char *str) static int __init set_cmdline_ftrace(char *str)
{ {
...@@ -230,6 +231,15 @@ static int __init boot_alloc_snapshot(char *str) ...@@ -230,6 +231,15 @@ static int __init boot_alloc_snapshot(char *str)
__setup("alloc_snapshot", boot_alloc_snapshot); __setup("alloc_snapshot", boot_alloc_snapshot);
static int __init boot_snapshot(char *str)
{
snapshot_at_boot = true;
boot_alloc_snapshot(str);
return 1;
}
__setup("ftrace_boot_snapshot", boot_snapshot);
static char trace_boot_options_buf[MAX_TRACER_SIZE] __initdata; static char trace_boot_options_buf[MAX_TRACER_SIZE] __initdata;
static int __init set_trace_boot_options(char *str) static int __init set_trace_boot_options(char *str)
...@@ -7725,7 +7735,7 @@ const struct file_operations trace_min_max_fops = { ...@@ -7725,7 +7735,7 @@ const struct file_operations trace_min_max_fops = {
struct err_info { struct err_info {
const char **errs; /* ptr to loc-specific array of err strings */ const char **errs; /* ptr to loc-specific array of err strings */
u8 type; /* index into errs -> specific err string */ u8 type; /* index into errs -> specific err string */
u8 pos; /* MAX_FILTER_STR_VAL = 256 */ u16 pos; /* caret position */
u64 ts; u64 ts;
}; };
...@@ -7733,26 +7743,52 @@ struct tracing_log_err { ...@@ -7733,26 +7743,52 @@ struct tracing_log_err {
struct list_head list; struct list_head list;
struct err_info info; struct err_info info;
char loc[TRACING_LOG_LOC_MAX]; /* err location */ char loc[TRACING_LOG_LOC_MAX]; /* err location */
char cmd[MAX_FILTER_STR_VAL]; /* what caused err */ char *cmd; /* what caused err */
}; };
static DEFINE_MUTEX(tracing_err_log_lock); static DEFINE_MUTEX(tracing_err_log_lock);
static struct tracing_log_err *get_tracing_log_err(struct trace_array *tr) static struct tracing_log_err *alloc_tracing_log_err(int len)
{
struct tracing_log_err *err;
err = kzalloc(sizeof(*err), GFP_KERNEL);
if (!err)
return ERR_PTR(-ENOMEM);
err->cmd = kzalloc(len, GFP_KERNEL);
if (!err->cmd) {
kfree(err);
return ERR_PTR(-ENOMEM);
}
return err;
}
static void free_tracing_log_err(struct tracing_log_err *err)
{
kfree(err->cmd);
kfree(err);
}
static struct tracing_log_err *get_tracing_log_err(struct trace_array *tr,
int len)
{ {
struct tracing_log_err *err; struct tracing_log_err *err;
if (tr->n_err_log_entries < TRACING_LOG_ERRS_MAX) { if (tr->n_err_log_entries < TRACING_LOG_ERRS_MAX) {
err = kzalloc(sizeof(*err), GFP_KERNEL); err = alloc_tracing_log_err(len);
if (!err) if (PTR_ERR(err) != -ENOMEM)
err = ERR_PTR(-ENOMEM);
else
tr->n_err_log_entries++; tr->n_err_log_entries++;
return err; return err;
} }
err = list_first_entry(&tr->err_log, struct tracing_log_err, list); err = list_first_entry(&tr->err_log, struct tracing_log_err, list);
kfree(err->cmd);
err->cmd = kzalloc(len, GFP_KERNEL);
if (!err->cmd)
return ERR_PTR(-ENOMEM);
list_del(&err->list); list_del(&err->list);
return err; return err;
...@@ -7813,22 +7849,25 @@ unsigned int err_pos(char *cmd, const char *str) ...@@ -7813,22 +7849,25 @@ unsigned int err_pos(char *cmd, const char *str)
*/ */
void tracing_log_err(struct trace_array *tr, void tracing_log_err(struct trace_array *tr,
const char *loc, const char *cmd, const char *loc, const char *cmd,
const char **errs, u8 type, u8 pos) const char **errs, u8 type, u16 pos)
{ {
struct tracing_log_err *err; struct tracing_log_err *err;
int len = 0;
if (!tr) if (!tr)
tr = &global_trace; tr = &global_trace;
len += sizeof(CMD_PREFIX) + 2 * sizeof("\n") + strlen(cmd) + 1;
mutex_lock(&tracing_err_log_lock); mutex_lock(&tracing_err_log_lock);
err = get_tracing_log_err(tr); err = get_tracing_log_err(tr, len);
if (PTR_ERR(err) == -ENOMEM) { if (PTR_ERR(err) == -ENOMEM) {
mutex_unlock(&tracing_err_log_lock); mutex_unlock(&tracing_err_log_lock);
return; return;
} }
snprintf(err->loc, TRACING_LOG_LOC_MAX, "%s: error: ", loc); snprintf(err->loc, TRACING_LOG_LOC_MAX, "%s: error: ", loc);
snprintf(err->cmd, MAX_FILTER_STR_VAL,"\n" CMD_PREFIX "%s\n", cmd); snprintf(err->cmd, len, "\n" CMD_PREFIX "%s\n", cmd);
err->info.errs = errs; err->info.errs = errs;
err->info.type = type; err->info.type = type;
...@@ -7846,7 +7885,7 @@ static void clear_tracing_err_log(struct trace_array *tr) ...@@ -7846,7 +7885,7 @@ static void clear_tracing_err_log(struct trace_array *tr)
mutex_lock(&tracing_err_log_lock); mutex_lock(&tracing_err_log_lock);
list_for_each_entry_safe(err, next, &tr->err_log, list) { list_for_each_entry_safe(err, next, &tr->err_log, list) {
list_del(&err->list); list_del(&err->list);
kfree(err); free_tracing_log_err(err);
} }
tr->n_err_log_entries = 0; tr->n_err_log_entries = 0;
...@@ -7874,9 +7913,9 @@ static void tracing_err_log_seq_stop(struct seq_file *m, void *v) ...@@ -7874,9 +7913,9 @@ static void tracing_err_log_seq_stop(struct seq_file *m, void *v)
mutex_unlock(&tracing_err_log_lock); mutex_unlock(&tracing_err_log_lock);
} }
static void tracing_err_log_show_pos(struct seq_file *m, u8 pos) static void tracing_err_log_show_pos(struct seq_file *m, u16 pos)
{ {
u8 i; u16 i;
for (i = 0; i < sizeof(CMD_PREFIX) - 1; i++) for (i = 0; i < sizeof(CMD_PREFIX) - 1; i++)
seq_putc(m, ' '); seq_putc(m, ' ');
...@@ -10122,6 +10161,14 @@ __init static int tracer_alloc_buffers(void) ...@@ -10122,6 +10161,14 @@ __init static int tracer_alloc_buffers(void)
return ret; return ret;
} }
void __init ftrace_boot_snapshot(void)
{
if (snapshot_at_boot) {
tracing_snapshot();
internal_trace_puts("** Boot snapshot taken **\n");
}
}
void __init early_trace_init(void) void __init early_trace_init(void)
{ {
if (tracepoint_printk) { if (tracepoint_printk) {
......
...@@ -1877,7 +1877,7 @@ extern ssize_t trace_parse_run_command(struct file *file, ...@@ -1877,7 +1877,7 @@ extern ssize_t trace_parse_run_command(struct file *file,
extern unsigned int err_pos(char *cmd, const char *str); extern unsigned int err_pos(char *cmd, const char *str);
extern void tracing_log_err(struct trace_array *tr, extern void tracing_log_err(struct trace_array *tr,
const char *loc, const char *cmd, const char *loc, const char *cmd,
const char **errs, u8 type, u8 pos); const char **errs, u8 type, u16 pos);
/* /*
* Normal trace_printk() and friends allocates special buffers * Normal trace_printk() and friends allocates special buffers
......
...@@ -40,6 +40,14 @@ static LIST_HEAD(ftrace_generic_fields); ...@@ -40,6 +40,14 @@ static LIST_HEAD(ftrace_generic_fields);
static LIST_HEAD(ftrace_common_fields); static LIST_HEAD(ftrace_common_fields);
static bool eventdir_initialized; static bool eventdir_initialized;
static LIST_HEAD(module_strings);
struct module_string {
struct list_head next;
struct module *module;
char *str;
};
#define GFP_TRACE (GFP_KERNEL | __GFP_ZERO) #define GFP_TRACE (GFP_KERNEL | __GFP_ZERO)
static struct kmem_cache *field_cachep; static struct kmem_cache *field_cachep;
...@@ -2643,6 +2651,76 @@ static void update_event_printk(struct trace_event_call *call, ...@@ -2643,6 +2651,76 @@ static void update_event_printk(struct trace_event_call *call,
} }
} }
static void add_str_to_module(struct module *module, char *str)
{
struct module_string *modstr;
modstr = kmalloc(sizeof(*modstr), GFP_KERNEL);
/*
* If we failed to allocate memory here, then we'll just
* let the str memory leak when the module is removed.
* If this fails to allocate, there's worse problems than
* a leaked string on module removal.
*/
if (WARN_ON_ONCE(!modstr))
return;
modstr->module = module;
modstr->str = str;
list_add(&modstr->next, &module_strings);
}
static void update_event_fields(struct trace_event_call *call,
struct trace_eval_map *map)
{
struct ftrace_event_field *field;
struct list_head *head;
char *ptr;
char *str;
int len = strlen(map->eval_string);
/* Dynamic events should never have field maps */
if (WARN_ON_ONCE(call->flags & TRACE_EVENT_FL_DYNAMIC))
return;
head = trace_get_fields(call);
list_for_each_entry(field, head, link) {
ptr = strchr(field->type, '[');
if (!ptr)
continue;
ptr++;
if (!isalpha(*ptr) && *ptr != '_')
continue;
if (strncmp(map->eval_string, ptr, len) != 0)
continue;
str = kstrdup(field->type, GFP_KERNEL);
if (WARN_ON_ONCE(!str))
return;
ptr = str + (ptr - field->type);
ptr = eval_replace(ptr, map, len);
/* enum/sizeof string smaller than value */
if (WARN_ON_ONCE(!ptr)) {
kfree(str);
continue;
}
/*
* If the event is part of a module, then we need to free the string
* when the module is removed. Otherwise, it will stay allocated
* until a reboot.
*/
if (call->module)
add_str_to_module(call->module, str);
field->type = str;
}
}
void trace_event_eval_update(struct trace_eval_map **map, int len) void trace_event_eval_update(struct trace_eval_map **map, int len)
{ {
struct trace_event_call *call, *p; struct trace_event_call *call, *p;
...@@ -2678,6 +2756,7 @@ void trace_event_eval_update(struct trace_eval_map **map, int len) ...@@ -2678,6 +2756,7 @@ void trace_event_eval_update(struct trace_eval_map **map, int len)
first = false; first = false;
} }
update_event_printk(call, map[i]); update_event_printk(call, map[i]);
update_event_fields(call, map[i]);
} }
} }
} }
...@@ -2768,6 +2847,7 @@ int trace_add_event_call(struct trace_event_call *call) ...@@ -2768,6 +2847,7 @@ int trace_add_event_call(struct trace_event_call *call)
mutex_unlock(&trace_types_lock); mutex_unlock(&trace_types_lock);
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(trace_add_event_call);
/* /*
* Must be called under locking of trace_types_lock, event_mutex and * Must be called under locking of trace_types_lock, event_mutex and
...@@ -2829,6 +2909,7 @@ int trace_remove_event_call(struct trace_event_call *call) ...@@ -2829,6 +2909,7 @@ int trace_remove_event_call(struct trace_event_call *call)
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(trace_remove_event_call);
#define for_each_event(event, start, end) \ #define for_each_event(event, start, end) \
for (event = start; \ for (event = start; \
...@@ -2863,6 +2944,7 @@ static void trace_module_add_events(struct module *mod) ...@@ -2863,6 +2944,7 @@ static void trace_module_add_events(struct module *mod)
static void trace_module_remove_events(struct module *mod) static void trace_module_remove_events(struct module *mod)
{ {
struct trace_event_call *call, *p; struct trace_event_call *call, *p;
struct module_string *modstr, *m;
down_write(&trace_event_sem); down_write(&trace_event_sem);
list_for_each_entry_safe(call, p, &ftrace_events, list) { list_for_each_entry_safe(call, p, &ftrace_events, list) {
...@@ -2871,6 +2953,14 @@ static void trace_module_remove_events(struct module *mod) ...@@ -2871,6 +2953,14 @@ static void trace_module_remove_events(struct module *mod)
if (call->module == mod) if (call->module == mod)
__trace_remove_event_call(call); __trace_remove_event_call(call);
} }
/* Check for any strings allocade for this module */
list_for_each_entry_safe(modstr, m, &module_strings, next) {
if (modstr->module != mod)
continue;
list_del(&modstr->next);
kfree(modstr->str);
kfree(modstr);
}
up_write(&trace_event_sem); up_write(&trace_event_sem);
/* /*
......
...@@ -727,11 +727,16 @@ static struct track_data *track_data_alloc(unsigned int key_len, ...@@ -727,11 +727,16 @@ static struct track_data *track_data_alloc(unsigned int key_len,
return data; return data;
} }
static char last_cmd[MAX_FILTER_STR_VAL]; #define HIST_PREFIX "hist:"
static char *last_cmd;
static char last_cmd_loc[MAX_FILTER_STR_VAL]; static char last_cmd_loc[MAX_FILTER_STR_VAL];
static int errpos(char *str) static int errpos(char *str)
{ {
if (!str || !last_cmd)
return 0;
return err_pos(last_cmd, str); return err_pos(last_cmd, str);
} }
...@@ -739,12 +744,22 @@ static void last_cmd_set(struct trace_event_file *file, char *str) ...@@ -739,12 +744,22 @@ static void last_cmd_set(struct trace_event_file *file, char *str)
{ {
const char *system = NULL, *name = NULL; const char *system = NULL, *name = NULL;
struct trace_event_call *call; struct trace_event_call *call;
int len;
if (!str) if (!str)
return; return;
strcpy(last_cmd, "hist:"); /* sizeof() contains the nul byte */
strncat(last_cmd, str, MAX_FILTER_STR_VAL - 1 - sizeof("hist:")); len = sizeof(HIST_PREFIX) + strlen(str);
kfree(last_cmd);
last_cmd = kzalloc(len, GFP_KERNEL);
if (!last_cmd)
return;
strcpy(last_cmd, HIST_PREFIX);
/* Again, sizeof() contains the nul byte */
len -= sizeof(HIST_PREFIX);
strncat(last_cmd, str, len);
if (file) { if (file) {
call = file->event_call; call = file->event_call;
...@@ -757,18 +772,22 @@ static void last_cmd_set(struct trace_event_file *file, char *str) ...@@ -757,18 +772,22 @@ static void last_cmd_set(struct trace_event_file *file, char *str)
} }
if (system) if (system)
snprintf(last_cmd_loc, MAX_FILTER_STR_VAL, "hist:%s:%s", system, name); snprintf(last_cmd_loc, MAX_FILTER_STR_VAL, HIST_PREFIX "%s:%s", system, name);
} }
static void hist_err(struct trace_array *tr, u8 err_type, u8 err_pos) static void hist_err(struct trace_array *tr, u8 err_type, u16 err_pos)
{ {
if (!last_cmd)
return;
tracing_log_err(tr, last_cmd_loc, last_cmd, err_text, tracing_log_err(tr, last_cmd_loc, last_cmd, err_text,
err_type, err_pos); err_type, err_pos);
} }
static void hist_err_clear(void) static void hist_err_clear(void)
{ {
last_cmd[0] = '\0'; if (last_cmd)
last_cmd[0] = '\0';
last_cmd_loc[0] = '\0'; last_cmd_loc[0] = '\0';
} }
...@@ -5610,7 +5629,7 @@ static int event_hist_trigger_print(struct seq_file *m, ...@@ -5610,7 +5629,7 @@ static int event_hist_trigger_print(struct seq_file *m,
bool have_var = false; bool have_var = false;
unsigned int i; unsigned int i;
seq_puts(m, "hist:"); seq_puts(m, HIST_PREFIX);
if (data->name) if (data->name)
seq_printf(m, "%s:", data->name); seq_printf(m, "%s:", data->name);
......
...@@ -42,10 +42,13 @@ enum { ERRORS }; ...@@ -42,10 +42,13 @@ enum { ERRORS };
static const char *err_text[] = { ERRORS }; static const char *err_text[] = { ERRORS };
static char last_cmd[MAX_FILTER_STR_VAL]; static char *last_cmd;
static int errpos(const char *str) static int errpos(const char *str)
{ {
if (!str || !last_cmd)
return 0;
return err_pos(last_cmd, str); return err_pos(last_cmd, str);
} }
...@@ -54,11 +57,16 @@ static void last_cmd_set(const char *str) ...@@ -54,11 +57,16 @@ static void last_cmd_set(const char *str)
if (!str) if (!str)
return; return;
strncpy(last_cmd, str, MAX_FILTER_STR_VAL - 1); kfree(last_cmd);
last_cmd = kstrdup(str, GFP_KERNEL);
} }
static void synth_err(u8 err_type, u8 err_pos) static void synth_err(u8 err_type, u16 err_pos)
{ {
if (!last_cmd)
return;
tracing_log_err(NULL, "synthetic_events", last_cmd, err_text, tracing_log_err(NULL, "synthetic_events", last_cmd, err_text,
err_type, err_pos); err_type, err_pos);
} }
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2021, Microsoft Corporation.
*
* Authors:
* Beau Belgrave <beaub@linux.microsoft.com>
*/
#include <linux/bitmap.h>
#include <linux/cdev.h>
#include <linux/hashtable.h>
#include <linux/list.h>
#include <linux/io.h>
#include <linux/uio.h>
#include <linux/ioctl.h>
#include <linux/jhash.h>
#include <linux/trace_events.h>
#include <linux/tracefs.h>
#include <linux/types.h>
#include <linux/uaccess.h>
#include <uapi/linux/user_events.h>
#include "trace.h"
#include "trace_dynevent.h"
#define USER_EVENTS_PREFIX_LEN (sizeof(USER_EVENTS_PREFIX)-1)
#define FIELD_DEPTH_TYPE 0
#define FIELD_DEPTH_NAME 1
#define FIELD_DEPTH_SIZE 2
/*
* Limits how many trace_event calls user processes can create:
* Must be a power of two of PAGE_SIZE.
*/
#define MAX_PAGE_ORDER 0
#define MAX_PAGES (1 << MAX_PAGE_ORDER)
#define MAX_EVENTS (MAX_PAGES * PAGE_SIZE)
/* Limit how long of an event name plus args within the subsystem. */
#define MAX_EVENT_DESC 512
#define EVENT_NAME(user_event) ((user_event)->tracepoint.name)
#define MAX_FIELD_ARRAY_SIZE 1024
#define MAX_FIELD_ARG_NAME 256
#define MAX_BPF_COPY_SIZE PAGE_SIZE
#define MAX_STACK_BPF_DATA 512
static char *register_page_data;
static DEFINE_MUTEX(reg_mutex);
static DEFINE_HASHTABLE(register_table, 4);
static DECLARE_BITMAP(page_bitmap, MAX_EVENTS);
/*
* Stores per-event properties, as users register events
* within a file a user_event might be created if it does not
* already exist. These are globally used and their lifetime
* is tied to the refcnt member. These cannot go away until the
* refcnt reaches zero.
*/
struct user_event {
struct tracepoint tracepoint;
struct trace_event_call call;
struct trace_event_class class;
struct dyn_event devent;
struct hlist_node node;
struct list_head fields;
struct list_head validators;
atomic_t refcnt;
int index;
int flags;
int min_size;
};
/*
* Stores per-file events references, as users register events
* within a file this structure is modified and freed via RCU.
* The lifetime of this struct is tied to the lifetime of the file.
* These are not shared and only accessible by the file that created it.
*/
struct user_event_refs {
struct rcu_head rcu;
int count;
struct user_event *events[];
};
#define VALIDATOR_ENSURE_NULL (1 << 0)
#define VALIDATOR_REL (1 << 1)
struct user_event_validator {
struct list_head link;
int offset;
int flags;
};
typedef void (*user_event_func_t) (struct user_event *user, struct iov_iter *i,
void *tpdata, bool *faulted);
static int user_event_parse(char *name, char *args, char *flags,
struct user_event **newuser);
static u32 user_event_key(char *name)
{
return jhash(name, strlen(name), 0);
}
static __always_inline __must_check
size_t copy_nofault(void *addr, size_t bytes, struct iov_iter *i)
{
size_t ret;
pagefault_disable();
ret = copy_from_iter_nocache(addr, bytes, i);
pagefault_enable();
return ret;
}
static struct list_head *user_event_get_fields(struct trace_event_call *call)
{
struct user_event *user = (struct user_event *)call->data;
return &user->fields;
}
/*
* Parses a register command for user_events
* Format: event_name[:FLAG1[,FLAG2...]] [field1[;field2...]]
*
* Example event named 'test' with a 20 char 'msg' field with an unsigned int
* 'id' field after:
* test char[20] msg;unsigned int id
*
* NOTE: Offsets are from the user data perspective, they are not from the
* trace_entry/buffer perspective. We automatically add the common properties
* sizes to the offset for the user.
*
* Upon success user_event has its ref count increased by 1.
*/
static int user_event_parse_cmd(char *raw_command, struct user_event **newuser)
{
char *name = raw_command;
char *args = strpbrk(name, " ");
char *flags;
if (args)
*args++ = '\0';
flags = strpbrk(name, ":");
if (flags)
*flags++ = '\0';
return user_event_parse(name, args, flags, newuser);
}
static int user_field_array_size(const char *type)
{
const char *start = strchr(type, '[');
char val[8];
char *bracket;
int size = 0;
if (start == NULL)
return -EINVAL;
if (strscpy(val, start + 1, sizeof(val)) <= 0)
return -EINVAL;
bracket = strchr(val, ']');
if (!bracket)
return -EINVAL;
*bracket = '\0';
if (kstrtouint(val, 0, &size))
return -EINVAL;
if (size > MAX_FIELD_ARRAY_SIZE)
return -EINVAL;
return size;
}
static int user_field_size(const char *type)
{
/* long is not allowed from a user, since it's ambigious in size */
if (strcmp(type, "s64") == 0)
return sizeof(s64);
if (strcmp(type, "u64") == 0)
return sizeof(u64);
if (strcmp(type, "s32") == 0)
return sizeof(s32);
if (strcmp(type, "u32") == 0)
return sizeof(u32);
if (strcmp(type, "int") == 0)
return sizeof(int);
if (strcmp(type, "unsigned int") == 0)
return sizeof(unsigned int);
if (strcmp(type, "s16") == 0)
return sizeof(s16);
if (strcmp(type, "u16") == 0)
return sizeof(u16);
if (strcmp(type, "short") == 0)
return sizeof(short);
if (strcmp(type, "unsigned short") == 0)
return sizeof(unsigned short);
if (strcmp(type, "s8") == 0)
return sizeof(s8);
if (strcmp(type, "u8") == 0)
return sizeof(u8);
if (strcmp(type, "char") == 0)
return sizeof(char);
if (strcmp(type, "unsigned char") == 0)
return sizeof(unsigned char);
if (str_has_prefix(type, "char["))
return user_field_array_size(type);
if (str_has_prefix(type, "unsigned char["))
return user_field_array_size(type);
if (str_has_prefix(type, "__data_loc "))
return sizeof(u32);
if (str_has_prefix(type, "__rel_loc "))
return sizeof(u32);
/* Uknown basic type, error */
return -EINVAL;
}
static void user_event_destroy_validators(struct user_event *user)
{
struct user_event_validator *validator, *next;
struct list_head *head = &user->validators;
list_for_each_entry_safe(validator, next, head, link) {
list_del(&validator->link);
kfree(validator);
}
}
static void user_event_destroy_fields(struct user_event *user)
{
struct ftrace_event_field *field, *next;
struct list_head *head = &user->fields;
list_for_each_entry_safe(field, next, head, link) {
list_del(&field->link);
kfree(field);
}
}
static int user_event_add_field(struct user_event *user, const char *type,
const char *name, int offset, int size,
int is_signed, int filter_type)
{
struct user_event_validator *validator;
struct ftrace_event_field *field;
int validator_flags = 0;
field = kmalloc(sizeof(*field), GFP_KERNEL);
if (!field)
return -ENOMEM;
if (str_has_prefix(type, "__data_loc "))
goto add_validator;
if (str_has_prefix(type, "__rel_loc ")) {
validator_flags |= VALIDATOR_REL;
goto add_validator;
}
goto add_field;
add_validator:
if (strstr(type, "char") != 0)
validator_flags |= VALIDATOR_ENSURE_NULL;
validator = kmalloc(sizeof(*validator), GFP_KERNEL);
if (!validator) {
kfree(field);
return -ENOMEM;
}
validator->flags = validator_flags;
validator->offset = offset;
/* Want sequential access when validating */
list_add_tail(&validator->link, &user->validators);
add_field:
field->type = type;
field->name = name;
field->offset = offset;
field->size = size;
field->is_signed = is_signed;
field->filter_type = filter_type;
list_add(&field->link, &user->fields);
/*
* Min size from user writes that are required, this does not include
* the size of trace_entry (common fields).
*/
user->min_size = (offset + size) - sizeof(struct trace_entry);
return 0;
}
/*
* Parses the values of a field within the description
* Format: type name [size]
*/
static int user_event_parse_field(char *field, struct user_event *user,
u32 *offset)
{
char *part, *type, *name;
u32 depth = 0, saved_offset = *offset;
int len, size = -EINVAL;
bool is_struct = false;
field = skip_spaces(field);
if (*field == '\0')
return 0;
/* Handle types that have a space within */
len = str_has_prefix(field, "unsigned ");
if (len)
goto skip_next;
len = str_has_prefix(field, "struct ");
if (len) {
is_struct = true;
goto skip_next;
}
len = str_has_prefix(field, "__data_loc unsigned ");
if (len)
goto skip_next;
len = str_has_prefix(field, "__data_loc ");
if (len)
goto skip_next;
len = str_has_prefix(field, "__rel_loc unsigned ");
if (len)
goto skip_next;
len = str_has_prefix(field, "__rel_loc ");
if (len)
goto skip_next;
goto parse;
skip_next:
type = field;
field = strpbrk(field + len, " ");
if (field == NULL)
return -EINVAL;
*field++ = '\0';
depth++;
parse:
name = NULL;
while ((part = strsep(&field, " ")) != NULL) {
switch (depth++) {
case FIELD_DEPTH_TYPE:
type = part;
break;
case FIELD_DEPTH_NAME:
name = part;
break;
case FIELD_DEPTH_SIZE:
if (!is_struct)
return -EINVAL;
if (kstrtou32(part, 10, &size))
return -EINVAL;
break;
default:
return -EINVAL;
}
}
if (depth < FIELD_DEPTH_SIZE || !name)
return -EINVAL;
if (depth == FIELD_DEPTH_SIZE)
size = user_field_size(type);
if (size == 0)
return -EINVAL;
if (size < 0)
return size;
*offset = saved_offset + size;
return user_event_add_field(user, type, name, saved_offset, size,
type[0] != 'u', FILTER_OTHER);
}
static void user_event_parse_flags(struct user_event *user, char *flags)
{
char *flag;
if (flags == NULL)
return;
while ((flag = strsep(&flags, ",")) != NULL) {
if (strcmp(flag, "BPF_ITER") == 0)
user->flags |= FLAG_BPF_ITER;
}
}
static int user_event_parse_fields(struct user_event *user, char *args)
{
char *field;
u32 offset = sizeof(struct trace_entry);
int ret = -EINVAL;
if (args == NULL)
return 0;
while ((field = strsep(&args, ";")) != NULL) {
ret = user_event_parse_field(field, user, &offset);
if (ret)
break;
}
return ret;
}
static struct trace_event_fields user_event_fields_array[1];
static const char *user_field_format(const char *type)
{
if (strcmp(type, "s64") == 0)
return "%lld";
if (strcmp(type, "u64") == 0)
return "%llu";
if (strcmp(type, "s32") == 0)
return "%d";
if (strcmp(type, "u32") == 0)
return "%u";
if (strcmp(type, "int") == 0)
return "%d";
if (strcmp(type, "unsigned int") == 0)
return "%u";
if (strcmp(type, "s16") == 0)
return "%d";
if (strcmp(type, "u16") == 0)
return "%u";
if (strcmp(type, "short") == 0)
return "%d";
if (strcmp(type, "unsigned short") == 0)
return "%u";
if (strcmp(type, "s8") == 0)
return "%d";
if (strcmp(type, "u8") == 0)
return "%u";
if (strcmp(type, "char") == 0)
return "%d";
if (strcmp(type, "unsigned char") == 0)
return "%u";
if (strstr(type, "char[") != 0)
return "%s";
/* Unknown, likely struct, allowed treat as 64-bit */
return "%llu";
}
static bool user_field_is_dyn_string(const char *type, const char **str_func)
{
if (str_has_prefix(type, "__data_loc ")) {
*str_func = "__get_str";
goto check;
}
if (str_has_prefix(type, "__rel_loc ")) {
*str_func = "__get_rel_str";
goto check;
}
return false;
check:
return strstr(type, "char") != 0;
}
#define LEN_OR_ZERO (len ? len - pos : 0)
static int user_event_set_print_fmt(struct user_event *user, char *buf, int len)
{
struct ftrace_event_field *field, *next;
struct list_head *head = &user->fields;
int pos = 0, depth = 0;
const char *str_func;
pos += snprintf(buf + pos, LEN_OR_ZERO, "\"");
list_for_each_entry_safe_reverse(field, next, head, link) {
if (depth != 0)
pos += snprintf(buf + pos, LEN_OR_ZERO, " ");
pos += snprintf(buf + pos, LEN_OR_ZERO, "%s=%s",
field->name, user_field_format(field->type));
depth++;
}
pos += snprintf(buf + pos, LEN_OR_ZERO, "\"");
list_for_each_entry_safe_reverse(field, next, head, link) {
if (user_field_is_dyn_string(field->type, &str_func))
pos += snprintf(buf + pos, LEN_OR_ZERO,
", %s(%s)", str_func, field->name);
else
pos += snprintf(buf + pos, LEN_OR_ZERO,
", REC->%s", field->name);
}
return pos + 1;
}
#undef LEN_OR_ZERO
static int user_event_create_print_fmt(struct user_event *user)
{
char *print_fmt;
int len;
len = user_event_set_print_fmt(user, NULL, 0);
print_fmt = kmalloc(len, GFP_KERNEL);
if (!print_fmt)
return -ENOMEM;
user_event_set_print_fmt(user, print_fmt, len);
user->call.print_fmt = print_fmt;
return 0;
}
static enum print_line_t user_event_print_trace(struct trace_iterator *iter,
int flags,
struct trace_event *event)
{
/* Unsafe to try to decode user provided print_fmt, use hex */
trace_print_hex_dump_seq(&iter->seq, "", DUMP_PREFIX_OFFSET, 16,
1, iter->ent, iter->ent_size, true);
return trace_handle_return(&iter->seq);
}
static struct trace_event_functions user_event_funcs = {
.trace = user_event_print_trace,
};
static int user_event_set_call_visible(struct user_event *user, bool visible)
{
int ret;
const struct cred *old_cred;
struct cred *cred;
cred = prepare_creds();
if (!cred)
return -ENOMEM;
/*
* While by default tracefs is locked down, systems can be configured
* to allow user_event files to be less locked down. The extreme case
* being "other" has read/write access to user_events_data/status.
*
* When not locked down, processes may not have have permissions to
* add/remove calls themselves to tracefs. We need to temporarily
* switch to root file permission to allow for this scenario.
*/
cred->fsuid = GLOBAL_ROOT_UID;
old_cred = override_creds(cred);
if (visible)
ret = trace_add_event_call(&user->call);
else
ret = trace_remove_event_call(&user->call);
revert_creds(old_cred);
put_cred(cred);
return ret;
}
static int destroy_user_event(struct user_event *user)
{
int ret = 0;
/* Must destroy fields before call removal */
user_event_destroy_fields(user);
ret = user_event_set_call_visible(user, false);
if (ret)
return ret;
dyn_event_remove(&user->devent);
register_page_data[user->index] = 0;
clear_bit(user->index, page_bitmap);
hash_del(&user->node);
user_event_destroy_validators(user);
kfree(user->call.print_fmt);
kfree(EVENT_NAME(user));
kfree(user);
return ret;
}
static struct user_event *find_user_event(char *name, u32 *outkey)
{
struct user_event *user;
u32 key = user_event_key(name);
*outkey = key;
hash_for_each_possible(register_table, user, node, key)
if (!strcmp(EVENT_NAME(user), name)) {
atomic_inc(&user->refcnt);
return user;
}
return NULL;
}
static int user_event_validate(struct user_event *user, void *data, int len)
{
struct list_head *head = &user->validators;
struct user_event_validator *validator;
void *pos, *end = data + len;
u32 loc, offset, size;
list_for_each_entry(validator, head, link) {
pos = data + validator->offset;
/* Already done min_size check, no bounds check here */
loc = *(u32 *)pos;
offset = loc & 0xffff;
size = loc >> 16;
if (likely(validator->flags & VALIDATOR_REL))
pos += offset + sizeof(loc);
else
pos = data + offset;
pos += size;
if (unlikely(pos > end))
return -EFAULT;
if (likely(validator->flags & VALIDATOR_ENSURE_NULL))
if (unlikely(*(char *)(pos - 1) != '\0'))
return -EFAULT;
}
return 0;
}
/*
* Writes the user supplied payload out to a trace file.
*/
static void user_event_ftrace(struct user_event *user, struct iov_iter *i,
void *tpdata, bool *faulted)
{
struct trace_event_file *file;
struct trace_entry *entry;
struct trace_event_buffer event_buffer;
size_t size = sizeof(*entry) + i->count;
file = (struct trace_event_file *)tpdata;
if (!file ||
!(file->flags & EVENT_FILE_FL_ENABLED) ||
trace_trigger_soft_disabled(file))
return;
/* Allocates and fills trace_entry, + 1 of this is data payload */
entry = trace_event_buffer_reserve(&event_buffer, file, size);
if (unlikely(!entry))
return;
if (unlikely(!copy_nofault(entry + 1, i->count, i)))
goto discard;
if (!list_empty(&user->validators) &&
unlikely(user_event_validate(user, entry, size)))
goto discard;
trace_event_buffer_commit(&event_buffer);
return;
discard:
*faulted = true;
__trace_event_discard_commit(event_buffer.buffer,
event_buffer.event);
}
#ifdef CONFIG_PERF_EVENTS
static void user_event_bpf(struct user_event *user, struct iov_iter *i)
{
struct user_bpf_context context;
struct user_bpf_iter bpf_i;
char fast_data[MAX_STACK_BPF_DATA];
void *temp = NULL;
if ((user->flags & FLAG_BPF_ITER) && iter_is_iovec(i)) {
/* Raw iterator */
context.data_type = USER_BPF_DATA_ITER;
context.data_len = i->count;
context.iter = &bpf_i;
bpf_i.iov_offset = i->iov_offset;
bpf_i.iov = i->iov;
bpf_i.nr_segs = i->nr_segs;
} else if (i->nr_segs == 1 && iter_is_iovec(i)) {
/* Single buffer from user */
context.data_type = USER_BPF_DATA_USER;
context.data_len = i->count;
context.udata = i->iov->iov_base + i->iov_offset;
} else {
/* Multi buffer from user */
struct iov_iter copy = *i;
size_t copy_size = min_t(size_t, i->count, MAX_BPF_COPY_SIZE);
context.data_type = USER_BPF_DATA_KERNEL;
context.kdata = fast_data;
if (unlikely(copy_size > sizeof(fast_data))) {
temp = kmalloc(copy_size, GFP_NOWAIT);
if (temp)
context.kdata = temp;
else
copy_size = sizeof(fast_data);
}
context.data_len = copy_nofault(context.kdata,
copy_size, &copy);
}
trace_call_bpf(&user->call, &context);
kfree(temp);
}
/*
* Writes the user supplied payload out to perf ring buffer or eBPF program.
*/
static void user_event_perf(struct user_event *user, struct iov_iter *i,
void *tpdata, bool *faulted)
{
struct hlist_head *perf_head;
if (bpf_prog_array_valid(&user->call))
user_event_bpf(user, i);
perf_head = this_cpu_ptr(user->call.perf_events);
if (perf_head && !hlist_empty(perf_head)) {
struct trace_entry *perf_entry;
struct pt_regs *regs;
size_t size = sizeof(*perf_entry) + i->count;
int context;
perf_entry = perf_trace_buf_alloc(ALIGN(size, 8),
&regs, &context);
if (unlikely(!perf_entry))
return;
perf_fetch_caller_regs(regs);
if (unlikely(!copy_nofault(perf_entry + 1, i->count, i)))
goto discard;
if (!list_empty(&user->validators) &&
unlikely(user_event_validate(user, perf_entry, size)))
goto discard;
perf_trace_buf_submit(perf_entry, size, context,
user->call.event.type, 1, regs,
perf_head, NULL);
return;
discard:
*faulted = true;
perf_swevent_put_recursion_context(context);
}
}
#endif
/*
* Update the register page that is shared between user processes.
*/
static void update_reg_page_for(struct user_event *user)
{
struct tracepoint *tp = &user->tracepoint;
char status = 0;
if (atomic_read(&tp->key.enabled) > 0) {
struct tracepoint_func *probe_func_ptr;
user_event_func_t probe_func;
rcu_read_lock_sched();
probe_func_ptr = rcu_dereference_sched(tp->funcs);
if (probe_func_ptr) {
do {
probe_func = probe_func_ptr->func;
if (probe_func == user_event_ftrace)
status |= EVENT_STATUS_FTRACE;
#ifdef CONFIG_PERF_EVENTS
else if (probe_func == user_event_perf)
status |= EVENT_STATUS_PERF;
#endif
else
status |= EVENT_STATUS_OTHER;
} while ((++probe_func_ptr)->func);
}
rcu_read_unlock_sched();
}
register_page_data[user->index] = status;
}
/*
* Register callback for our events from tracing sub-systems.
*/
static int user_event_reg(struct trace_event_call *call,
enum trace_reg type,
void *data)
{
struct user_event *user = (struct user_event *)call->data;
int ret = 0;
if (!user)
return -ENOENT;
switch (type) {
case TRACE_REG_REGISTER:
ret = tracepoint_probe_register(call->tp,
call->class->probe,
data);
if (!ret)
goto inc;
break;
case TRACE_REG_UNREGISTER:
tracepoint_probe_unregister(call->tp,
call->class->probe,
data);
goto dec;
#ifdef CONFIG_PERF_EVENTS
case TRACE_REG_PERF_REGISTER:
ret = tracepoint_probe_register(call->tp,
call->class->perf_probe,
data);
if (!ret)
goto inc;
break;
case TRACE_REG_PERF_UNREGISTER:
tracepoint_probe_unregister(call->tp,
call->class->perf_probe,
data);
goto dec;
case TRACE_REG_PERF_OPEN:
case TRACE_REG_PERF_CLOSE:
case TRACE_REG_PERF_ADD:
case TRACE_REG_PERF_DEL:
break;
#endif
}
return ret;
inc:
atomic_inc(&user->refcnt);
update_reg_page_for(user);
return 0;
dec:
update_reg_page_for(user);
atomic_dec(&user->refcnt);
return 0;
}
static int user_event_create(const char *raw_command)
{
struct user_event *user;
char *name;
int ret;
if (!str_has_prefix(raw_command, USER_EVENTS_PREFIX))
return -ECANCELED;
raw_command += USER_EVENTS_PREFIX_LEN;
raw_command = skip_spaces(raw_command);
name = kstrdup(raw_command, GFP_KERNEL);
if (!name)
return -ENOMEM;
mutex_lock(&reg_mutex);
ret = user_event_parse_cmd(name, &user);
if (!ret)
atomic_dec(&user->refcnt);
mutex_unlock(&reg_mutex);
if (ret)
kfree(name);
return ret;
}
static int user_event_show(struct seq_file *m, struct dyn_event *ev)
{
struct user_event *user = container_of(ev, struct user_event, devent);
struct ftrace_event_field *field, *next;
struct list_head *head;
int depth = 0;
seq_printf(m, "%s%s", USER_EVENTS_PREFIX, EVENT_NAME(user));
head = trace_get_fields(&user->call);
list_for_each_entry_safe_reverse(field, next, head, link) {
if (depth == 0)
seq_puts(m, " ");
else
seq_puts(m, "; ");
seq_printf(m, "%s %s", field->type, field->name);
if (str_has_prefix(field->type, "struct "))
seq_printf(m, " %d", field->size);
depth++;
}
seq_puts(m, "\n");
return 0;
}
static bool user_event_is_busy(struct dyn_event *ev)
{
struct user_event *user = container_of(ev, struct user_event, devent);
return atomic_read(&user->refcnt) != 0;
}
static int user_event_free(struct dyn_event *ev)
{
struct user_event *user = container_of(ev, struct user_event, devent);
if (atomic_read(&user->refcnt) != 0)
return -EBUSY;
return destroy_user_event(user);
}
static bool user_field_match(struct ftrace_event_field *field, int argc,
const char **argv, int *iout)
{
char *field_name, *arg_name;
int len, pos, i = *iout;
bool colon = false, match = false;
if (i >= argc)
return false;
len = MAX_FIELD_ARG_NAME;
field_name = kmalloc(len, GFP_KERNEL);
arg_name = kmalloc(len, GFP_KERNEL);
if (!arg_name || !field_name)
goto out;
pos = 0;
for (; i < argc; ++i) {
if (i != *iout)
pos += snprintf(arg_name + pos, len - pos, " ");
pos += snprintf(arg_name + pos, len - pos, argv[i]);
if (strchr(argv[i], ';')) {
++i;
colon = true;
break;
}
}
pos = 0;
pos += snprintf(field_name + pos, len - pos, field->type);
pos += snprintf(field_name + pos, len - pos, " ");
pos += snprintf(field_name + pos, len - pos, field->name);
if (colon)
pos += snprintf(field_name + pos, len - pos, ";");
*iout = i;
match = strcmp(arg_name, field_name) == 0;
out:
kfree(arg_name);
kfree(field_name);
return match;
}
static bool user_fields_match(struct user_event *user, int argc,
const char **argv)
{
struct ftrace_event_field *field, *next;
struct list_head *head = &user->fields;
int i = 0;
list_for_each_entry_safe_reverse(field, next, head, link)
if (!user_field_match(field, argc, argv, &i))
return false;
if (i != argc)
return false;
return true;
}
static bool user_event_match(const char *system, const char *event,
int argc, const char **argv, struct dyn_event *ev)
{
struct user_event *user = container_of(ev, struct user_event, devent);
bool match;
match = strcmp(EVENT_NAME(user), event) == 0 &&
(!system || strcmp(system, USER_EVENTS_SYSTEM) == 0);
if (match && argc > 0)
match = user_fields_match(user, argc, argv);
return match;
}
static struct dyn_event_operations user_event_dops = {
.create = user_event_create,
.show = user_event_show,
.is_busy = user_event_is_busy,
.free = user_event_free,
.match = user_event_match,
};
static int user_event_trace_register(struct user_event *user)
{
int ret;
ret = register_trace_event(&user->call.event);
if (!ret)
return -ENODEV;
ret = user_event_set_call_visible(user, true);
if (ret)
unregister_trace_event(&user->call.event);
return ret;
}
/*
* Parses the event name, arguments and flags then registers if successful.
* The name buffer lifetime is owned by this method for success cases only.
* Upon success the returned user_event has its ref count increased by 1.
*/
static int user_event_parse(char *name, char *args, char *flags,
struct user_event **newuser)
{
int ret;
int index;
u32 key;
struct user_event *user;
/* Prevent dyn_event from racing */
mutex_lock(&event_mutex);
user = find_user_event(name, &key);
mutex_unlock(&event_mutex);
if (user) {
*newuser = user;
/*
* Name is allocated by caller, free it since it already exists.
* Caller only worries about failure cases for freeing.
*/
kfree(name);
return 0;
}
index = find_first_zero_bit(page_bitmap, MAX_EVENTS);
if (index == MAX_EVENTS)
return -EMFILE;
user = kzalloc(sizeof(*user), GFP_KERNEL);
if (!user)
return -ENOMEM;
INIT_LIST_HEAD(&user->class.fields);
INIT_LIST_HEAD(&user->fields);
INIT_LIST_HEAD(&user->validators);
user->tracepoint.name = name;
user_event_parse_flags(user, flags);
ret = user_event_parse_fields(user, args);
if (ret)
goto put_user;
ret = user_event_create_print_fmt(user);
if (ret)
goto put_user;
user->call.data = user;
user->call.class = &user->class;
user->call.name = name;
user->call.flags = TRACE_EVENT_FL_TRACEPOINT;
user->call.tp = &user->tracepoint;
user->call.event.funcs = &user_event_funcs;
user->class.system = USER_EVENTS_SYSTEM;
user->class.fields_array = user_event_fields_array;
user->class.get_fields = user_event_get_fields;
user->class.reg = user_event_reg;
user->class.probe = user_event_ftrace;
#ifdef CONFIG_PERF_EVENTS
user->class.perf_probe = user_event_perf;
#endif
mutex_lock(&event_mutex);
ret = user_event_trace_register(user);
mutex_unlock(&event_mutex);
if (ret)
goto put_user;
user->index = index;
/* Ensure we track ref */
atomic_inc(&user->refcnt);
dyn_event_init(&user->devent, &user_event_dops);
dyn_event_add(&user->devent, &user->call);
set_bit(user->index, page_bitmap);
hash_add(register_table, &user->node, key);
*newuser = user;
return 0;
put_user:
user_event_destroy_fields(user);
user_event_destroy_validators(user);
kfree(user);
return ret;
}
/*
* Deletes a previously created event if it is no longer being used.
*/
static int delete_user_event(char *name)
{
u32 key;
int ret;
struct user_event *user = find_user_event(name, &key);
if (!user)
return -ENOENT;
/* Ensure we are the last ref */
if (atomic_read(&user->refcnt) != 1) {
ret = -EBUSY;
goto put_ref;
}
ret = destroy_user_event(user);
if (ret)
goto put_ref;
return ret;
put_ref:
/* No longer have this ref */
atomic_dec(&user->refcnt);
return ret;
}
/*
* Validates the user payload and writes via iterator.
*/
static ssize_t user_events_write_core(struct file *file, struct iov_iter *i)
{
struct user_event_refs *refs;
struct user_event *user = NULL;
struct tracepoint *tp;
ssize_t ret = i->count;
int idx;
if (unlikely(copy_from_iter(&idx, sizeof(idx), i) != sizeof(idx)))
return -EFAULT;
rcu_read_lock_sched();
refs = rcu_dereference_sched(file->private_data);
/*
* The refs->events array is protected by RCU, and new items may be
* added. But the user retrieved from indexing into the events array
* shall be immutable while the file is opened.
*/
if (likely(refs && idx < refs->count))
user = refs->events[idx];
rcu_read_unlock_sched();
if (unlikely(user == NULL))
return -ENOENT;
if (unlikely(i->count < user->min_size))
return -EINVAL;
tp = &user->tracepoint;
/*
* It's possible key.enabled disables after this check, however
* we don't mind if a few events are included in this condition.
*/
if (likely(atomic_read(&tp->key.enabled) > 0)) {
struct tracepoint_func *probe_func_ptr;
user_event_func_t probe_func;
struct iov_iter copy;
void *tpdata;
bool faulted;
if (unlikely(fault_in_iov_iter_readable(i, i->count)))
return -EFAULT;
faulted = false;
rcu_read_lock_sched();
probe_func_ptr = rcu_dereference_sched(tp->funcs);
if (probe_func_ptr) {
do {
copy = *i;
probe_func = probe_func_ptr->func;
tpdata = probe_func_ptr->data;
probe_func(user, &copy, tpdata, &faulted);
} while ((++probe_func_ptr)->func);
}
rcu_read_unlock_sched();
if (unlikely(faulted))
return -EFAULT;
}
return ret;
}
static ssize_t user_events_write(struct file *file, const char __user *ubuf,
size_t count, loff_t *ppos)
{
struct iovec iov;
struct iov_iter i;
if (unlikely(*ppos != 0))
return -EFAULT;
if (unlikely(import_single_range(READ, (char *)ubuf, count, &iov, &i)))
return -EFAULT;
return user_events_write_core(file, &i);
}
static ssize_t user_events_write_iter(struct kiocb *kp, struct iov_iter *i)
{
return user_events_write_core(kp->ki_filp, i);
}
static int user_events_ref_add(struct file *file, struct user_event *user)
{
struct user_event_refs *refs, *new_refs;
int i, size, count = 0;
refs = rcu_dereference_protected(file->private_data,
lockdep_is_held(&reg_mutex));
if (refs) {
count = refs->count;
for (i = 0; i < count; ++i)
if (refs->events[i] == user)
return i;
}
size = struct_size(refs, events, count + 1);
new_refs = kzalloc(size, GFP_KERNEL);
if (!new_refs)
return -ENOMEM;
new_refs->count = count + 1;
for (i = 0; i < count; ++i)
new_refs->events[i] = refs->events[i];
new_refs->events[i] = user;
atomic_inc(&user->refcnt);
rcu_assign_pointer(file->private_data, new_refs);
if (refs)
kfree_rcu(refs, rcu);
return i;
}
static long user_reg_get(struct user_reg __user *ureg, struct user_reg *kreg)
{
u32 size;
long ret;
ret = get_user(size, &ureg->size);
if (ret)
return ret;
if (size > PAGE_SIZE)
return -E2BIG;
return copy_struct_from_user(kreg, sizeof(*kreg), ureg, size);
}
/*
* Registers a user_event on behalf of a user process.
*/
static long user_events_ioctl_reg(struct file *file, unsigned long uarg)
{
struct user_reg __user *ureg = (struct user_reg __user *)uarg;
struct user_reg reg;
struct user_event *user;
char *name;
long ret;
ret = user_reg_get(ureg, &reg);
if (ret)
return ret;
name = strndup_user((const char __user *)(uintptr_t)reg.name_args,
MAX_EVENT_DESC);
if (IS_ERR(name)) {
ret = PTR_ERR(name);
return ret;
}
ret = user_event_parse_cmd(name, &user);
if (ret) {
kfree(name);
return ret;
}
ret = user_events_ref_add(file, user);
/* No longer need parse ref, ref_add either worked or not */
atomic_dec(&user->refcnt);
/* Positive number is index and valid */
if (ret < 0)
return ret;
put_user((u32)ret, &ureg->write_index);
put_user(user->index, &ureg->status_index);
return 0;
}
/*
* Deletes a user_event on behalf of a user process.
*/
static long user_events_ioctl_del(struct file *file, unsigned long uarg)
{
void __user *ubuf = (void __user *)uarg;
char *name;
long ret;
name = strndup_user(ubuf, MAX_EVENT_DESC);
if (IS_ERR(name))
return PTR_ERR(name);
/* event_mutex prevents dyn_event from racing */
mutex_lock(&event_mutex);
ret = delete_user_event(name);
mutex_unlock(&event_mutex);
kfree(name);
return ret;
}
/*
* Handles the ioctl from user mode to register or alter operations.
*/
static long user_events_ioctl(struct file *file, unsigned int cmd,
unsigned long uarg)
{
long ret = -ENOTTY;
switch (cmd) {
case DIAG_IOCSREG:
mutex_lock(&reg_mutex);
ret = user_events_ioctl_reg(file, uarg);
mutex_unlock(&reg_mutex);
break;
case DIAG_IOCSDEL:
mutex_lock(&reg_mutex);
ret = user_events_ioctl_del(file, uarg);
mutex_unlock(&reg_mutex);
break;
}
return ret;
}
/*
* Handles the final close of the file from user mode.
*/
static int user_events_release(struct inode *node, struct file *file)
{
struct user_event_refs *refs;
struct user_event *user;
int i;
/*
* Ensure refs cannot change under any situation by taking the
* register mutex during the final freeing of the references.
*/
mutex_lock(&reg_mutex);
refs = file->private_data;
if (!refs)
goto out;
/*
* The lifetime of refs has reached an end, it's tied to this file.
* The underlying user_events are ref counted, and cannot be freed.
* After this decrement, the user_events may be freed elsewhere.
*/
for (i = 0; i < refs->count; ++i) {
user = refs->events[i];
if (user)
atomic_dec(&user->refcnt);
}
out:
file->private_data = NULL;
mutex_unlock(&reg_mutex);
kfree(refs);
return 0;
}
static const struct file_operations user_data_fops = {
.write = user_events_write,
.write_iter = user_events_write_iter,
.unlocked_ioctl = user_events_ioctl,
.release = user_events_release,
};
/*
* Maps the shared page into the user process for checking if event is enabled.
*/
static int user_status_mmap(struct file *file, struct vm_area_struct *vma)
{
unsigned long size = vma->vm_end - vma->vm_start;
if (size != MAX_EVENTS)
return -EINVAL;
return remap_pfn_range(vma, vma->vm_start,
virt_to_phys(register_page_data) >> PAGE_SHIFT,
size, vm_get_page_prot(VM_READ));
}
static void *user_seq_start(struct seq_file *m, loff_t *pos)
{
if (*pos)
return NULL;
return (void *)1;
}
static void *user_seq_next(struct seq_file *m, void *p, loff_t *pos)
{
++*pos;
return NULL;
}
static void user_seq_stop(struct seq_file *m, void *p)
{
}
static int user_seq_show(struct seq_file *m, void *p)
{
struct user_event *user;
char status;
int i, active = 0, busy = 0, flags;
mutex_lock(&reg_mutex);
hash_for_each(register_table, i, user, node) {
status = register_page_data[user->index];
flags = user->flags;
seq_printf(m, "%d:%s", user->index, EVENT_NAME(user));
if (flags != 0 || status != 0)
seq_puts(m, " #");
if (status != 0) {
seq_puts(m, " Used by");
if (status & EVENT_STATUS_FTRACE)
seq_puts(m, " ftrace");
if (status & EVENT_STATUS_PERF)
seq_puts(m, " perf");
if (status & EVENT_STATUS_OTHER)
seq_puts(m, " other");
busy++;
}
if (flags & FLAG_BPF_ITER)
seq_puts(m, " FLAG:BPF_ITER");
seq_puts(m, "\n");
active++;
}
mutex_unlock(&reg_mutex);
seq_puts(m, "\n");
seq_printf(m, "Active: %d\n", active);
seq_printf(m, "Busy: %d\n", busy);
seq_printf(m, "Max: %ld\n", MAX_EVENTS);
return 0;
}
static const struct seq_operations user_seq_ops = {
.start = user_seq_start,
.next = user_seq_next,
.stop = user_seq_stop,
.show = user_seq_show,
};
static int user_status_open(struct inode *node, struct file *file)
{
return seq_open(file, &user_seq_ops);
}
static const struct file_operations user_status_fops = {
.open = user_status_open,
.mmap = user_status_mmap,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
/*
* Creates a set of tracefs files to allow user mode interactions.
*/
static int create_user_tracefs(void)
{
struct dentry *edata, *emmap;
edata = tracefs_create_file("user_events_data", TRACE_MODE_WRITE,
NULL, NULL, &user_data_fops);
if (!edata) {
pr_warn("Could not create tracefs 'user_events_data' entry\n");
goto err;
}
/* mmap with MAP_SHARED requires writable fd */
emmap = tracefs_create_file("user_events_status", TRACE_MODE_WRITE,
NULL, NULL, &user_status_fops);
if (!emmap) {
tracefs_remove(edata);
pr_warn("Could not create tracefs 'user_events_mmap' entry\n");
goto err;
}
return 0;
err:
return -ENODEV;
}
static void set_page_reservations(bool set)
{
int page;
for (page = 0; page < MAX_PAGES; ++page) {
void *addr = register_page_data + (PAGE_SIZE * page);
if (set)
SetPageReserved(virt_to_page(addr));
else
ClearPageReserved(virt_to_page(addr));
}
}
static int __init trace_events_user_init(void)
{
struct page *pages;
int ret;
/* Zero all bits beside 0 (which is reserved for failures) */
bitmap_zero(page_bitmap, MAX_EVENTS);
set_bit(0, page_bitmap);
pages = alloc_pages(GFP_KERNEL | __GFP_ZERO, MAX_PAGE_ORDER);
if (!pages)
return -ENOMEM;
register_page_data = page_address(pages);
set_page_reservations(true);
ret = create_user_tracefs();
if (ret) {
pr_warn("user_events could not register with tracefs\n");
set_page_reservations(false);
__free_pages(pages, MAX_PAGE_ORDER);
return ret;
}
if (dyn_event_register(&user_event_dops))
pr_warn("user_events could not register with dyn_events\n");
return 0;
}
fs_initcall(trace_events_user_init);
...@@ -45,7 +45,7 @@ probe_sched_wakeup(void *ignore, struct task_struct *wakee) ...@@ -45,7 +45,7 @@ probe_sched_wakeup(void *ignore, struct task_struct *wakee)
if (!flags) if (!flags)
return; return;
tracing_record_taskinfo(current, flags); tracing_record_taskinfo_sched_switch(current, wakee, flags);
} }
static int tracing_sched_register(void) static int tracing_sched_register(void)
......
...@@ -14,7 +14,13 @@ config SAMPLE_TRACE_EVENTS ...@@ -14,7 +14,13 @@ config SAMPLE_TRACE_EVENTS
tristate "Build trace_events examples -- loadable modules only" tristate "Build trace_events examples -- loadable modules only"
depends on EVENT_TRACING && m depends on EVENT_TRACING && m
help help
This build trace event example modules. This builds the trace event example module.
config SAMPLE_TRACE_CUSTOM_EVENTS
tristate "Build custom trace event example -- loadable modules only"
depends on EVENT_TRACING && m
help
This builds the custom trace event example module.
config SAMPLE_TRACE_PRINTK config SAMPLE_TRACE_PRINTK
tristate "Build trace_printk module - tests various trace_printk formats" tristate "Build trace_printk module - tests various trace_printk formats"
......
...@@ -20,6 +20,7 @@ obj-$(CONFIG_SAMPLE_RPMSG_CLIENT) += rpmsg/ ...@@ -20,6 +20,7 @@ obj-$(CONFIG_SAMPLE_RPMSG_CLIENT) += rpmsg/
subdir-$(CONFIG_SAMPLE_SECCOMP) += seccomp subdir-$(CONFIG_SAMPLE_SECCOMP) += seccomp
subdir-$(CONFIG_SAMPLE_TIMER) += timers subdir-$(CONFIG_SAMPLE_TIMER) += timers
obj-$(CONFIG_SAMPLE_TRACE_EVENTS) += trace_events/ obj-$(CONFIG_SAMPLE_TRACE_EVENTS) += trace_events/
obj-$(CONFIG_SAMPLE_TRACE_CUSTOM_EVENTS) += trace_events/
obj-$(CONFIG_SAMPLE_TRACE_PRINTK) += trace_printk/ obj-$(CONFIG_SAMPLE_TRACE_PRINTK) += trace_printk/
obj-$(CONFIG_SAMPLE_FTRACE_DIRECT) += ftrace/ obj-$(CONFIG_SAMPLE_FTRACE_DIRECT) += ftrace/
obj-$(CONFIG_SAMPLE_FTRACE_DIRECT_MULTI) += ftrace/ obj-$(CONFIG_SAMPLE_FTRACE_DIRECT_MULTI) += ftrace/
......
...@@ -11,5 +11,7 @@ ...@@ -11,5 +11,7 @@
# Here trace-events-sample.c does the CREATE_TRACE_POINTS. # Here trace-events-sample.c does the CREATE_TRACE_POINTS.
# #
CFLAGS_trace-events-sample.o := -I$(src) CFLAGS_trace-events-sample.o := -I$(src)
CFLAGS_trace_custom_sched.o := -I$(src)
obj-$(CONFIG_SAMPLE_TRACE_EVENTS) += trace-events-sample.o obj-$(CONFIG_SAMPLE_TRACE_EVENTS) += trace-events-sample.o
obj-$(CONFIG_SAMPLE_TRACE_CUSTOM_EVENTS) += trace_custom_sched.o
// SPDX-License-Identifier: GPL-2.0
/*
* event tracer
*
* Copyright (C) 2022 Google Inc, Steven Rostedt <rostedt@goodmis.org>
*/
#define pr_fmt(fmt) fmt
#include <linux/trace_events.h>
#include <linux/version.h>
#include <linux/module.h>
#include <linux/sched.h>
/*
* Must include the event header that the custom event will attach to,
* from the C file, and not in the custom header file.
*/
#include <trace/events/sched.h>
/* Declare CREATE_CUSTOM_TRACE_EVENTS before including custom header */
#define CREATE_CUSTOM_TRACE_EVENTS
#include "trace_custom_sched.h"
/*
* As the trace events are not exported to modules, the use of
* for_each_kernel_tracepoint() is needed to find the trace event
* to attach to. The fct() function below, is a callback that
* will be called for every event.
*
* Helper functions are created by the TRACE_CUSTOM_EVENT() macro
* update the event. Those are of the form:
*
* trace_custom_event_<event>_update()
*
* Where <event> is the event to attach.
*/
static void fct(struct tracepoint *tp, void *priv)
{
trace_custom_event_sched_switch_update(tp);
trace_custom_event_sched_waking_update(tp);
}
static int __init trace_sched_init(void)
{
for_each_kernel_tracepoint(fct, NULL);
return 0;
}
static void __exit trace_sched_exit(void)
{
}
module_init(trace_sched_init);
module_exit(trace_sched_exit);
MODULE_AUTHOR("Steven Rostedt");
MODULE_DESCRIPTION("Custom scheduling events");
MODULE_LICENSE("GPL");
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Like the headers that use TRACE_EVENT(), the TRACE_CUSTOM_EVENT()
* needs a header that allows for multiple inclusions.
*
* Test for a unique name (here we have _TRACE_CUSTOM_SCHED_H),
* also allowing to continue if TRACE_CUSTOM_MULTI_READ is defined.
*/
#if !defined(_TRACE_CUSTOM_SCHED_H) || defined(TRACE_CUSTOM_MULTI_READ)
#define _TRACE_CUSTOM_SCHED_H
/* Include linux/trace_events.h for initial defines of TRACE_CUSTOM_EVENT() */
#include <linux/trace_events.h>
/*
* TRACE_CUSTOM_EVENT() is just like TRACE_EVENT(). The first parameter
* is the event name of an existing event where the TRACE_EVENT has been included
* in the C file before including this file.
*/
TRACE_CUSTOM_EVENT(sched_switch,
/*
* The TP_PROTO() and TP_ARGS must match the trace event
* that the custom event is using.
*/
TP_PROTO(bool preempt,
unsigned int prev_state,
struct task_struct *prev,
struct task_struct *next),
TP_ARGS(preempt, prev_state, prev, next),
/*
* The next fields are where the customization happens.
* The TP_STRUCT__entry() defines what will be recorded
* in the ring buffer when the custom event triggers.
*
* The rest is just like the TRACE_EVENT() macro except that
* it uses the custom entry.
*/
TP_STRUCT__entry(
__field( unsigned short, prev_prio )
__field( unsigned short, next_prio )
__field( pid_t, next_pid )
),
TP_fast_assign(
__entry->prev_prio = prev->prio;
__entry->next_pid = next->pid;
__entry->next_prio = next->prio;
),
TP_printk("prev_prio=%d next_pid=%d next_prio=%d",
__entry->prev_prio, __entry->next_pid, __entry->next_prio)
)
TRACE_CUSTOM_EVENT(sched_waking,
TP_PROTO(struct task_struct *p),
TP_ARGS(p),
TP_STRUCT__entry(
__field( pid_t, pid )
__field( unsigned short, prio )
),
TP_fast_assign(
__entry->pid = p->pid;
__entry->prio = p->prio;
),
TP_printk("pid=%d prio=%d", __entry->pid, __entry->prio)
)
#endif
/*
* Just like the headers that create TRACE_EVENTs, the below must
* be outside the protection of the above #if block.
*/
/*
* It is required that the Makefile includes:
* CFLAGS_<c_file>.o := -I$(src)
*/
#undef TRACE_INCLUDE_PATH
#undef TRACE_INCLUDE_FILE
#define TRACE_INCLUDE_PATH .
/*
* It is requred that the TRACE_INCLUDE_FILE be the same
* as this file without the ".h".
*/
#define TRACE_INCLUDE_FILE trace_custom_sched
#include <trace/define_custom_trace.h>
# SPDX-License-Identifier: GPL-2.0
CFLAGS += -Wl,-no-as-needed -Wall -I../../usr/include
example: example.o
example.o: example.c
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2021, Microsoft Corporation.
*
* Authors:
* Beau Belgrave <beaub@linux.microsoft.com>
*/
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <linux/user_events.h>
/* Assumes debugfs is mounted */
const char *data_file = "/sys/kernel/debug/tracing/user_events_data";
const char *status_file = "/sys/kernel/debug/tracing/user_events_status";
static int event_status(char **status)
{
int fd = open(status_file, O_RDONLY);
*status = mmap(NULL, sysconf(_SC_PAGESIZE), PROT_READ,
MAP_SHARED, fd, 0);
close(fd);
if (*status == MAP_FAILED)
return -1;
return 0;
}
static int event_reg(int fd, const char *command, int *status, int *write)
{
struct user_reg reg = {0};
reg.size = sizeof(reg);
reg.name_args = (__u64)command;
if (ioctl(fd, DIAG_IOCSREG, &reg) == -1)
return -1;
*status = reg.status_index;
*write = reg.write_index;
return 0;
}
int main(int argc, char **argv)
{
int data_fd, status, write;
char *status_page;
struct iovec io[2];
__u32 count = 0;
if (event_status(&status_page) == -1)
return errno;
data_fd = open(data_file, O_RDWR);
if (event_reg(data_fd, "test u32 count", &status, &write) == -1)
return errno;
/* Setup iovec */
io[0].iov_base = &write;
io[0].iov_len = sizeof(write);
io[1].iov_base = &count;
io[1].iov_len = sizeof(count);
ask:
printf("Press enter to check status...\n");
getchar();
/* Check if anyone is listening */
if (status_page[status]) {
/* Yep, trace out our data */
writev(data_fd, (const struct iovec *)io, 2);
/* Increase the count */
count++;
printf("Something was attached, wrote data\n");
}
goto ask;
return 0;
}
# SPDX-License-Identifier: GPL-2.0
CFLAGS += -Wl,-no-as-needed -Wall -I../../../../usr/include
LDLIBS += -lrt -lpthread -lm
TEST_GEN_PROGS = ftrace_test dyn_test perf_test
TEST_FILES := settings
include ../lib.mk
// SPDX-License-Identifier: GPL-2.0
/*
* User Events Dyn Events Test Program
*
* Copyright (c) 2021 Beau Belgrave <beaub@linux.microsoft.com>
*/
#include <errno.h>
#include <linux/user_events.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <unistd.h>
#include "../kselftest_harness.h"
const char *dyn_file = "/sys/kernel/debug/tracing/dynamic_events";
const char *clear = "!u:__test_event";
static int Append(const char *value)
{
int fd = open(dyn_file, O_RDWR | O_APPEND);
int ret = write(fd, value, strlen(value));
close(fd);
return ret;
}
#define CLEAR() \
do { \
int ret = Append(clear); \
if (ret == -1) \
ASSERT_EQ(ENOENT, errno); \
} while (0)
#define TEST_PARSE(x) \
do { \
ASSERT_NE(-1, Append(x)); \
CLEAR(); \
} while (0)
#define TEST_NPARSE(x) ASSERT_EQ(-1, Append(x))
FIXTURE(user) {
};
FIXTURE_SETUP(user) {
CLEAR();
}
FIXTURE_TEARDOWN(user) {
CLEAR();
}
TEST_F(user, basic_types) {
/* All should work */
TEST_PARSE("u:__test_event u64 a");
TEST_PARSE("u:__test_event u32 a");
TEST_PARSE("u:__test_event u16 a");
TEST_PARSE("u:__test_event u8 a");
TEST_PARSE("u:__test_event char a");
TEST_PARSE("u:__test_event unsigned char a");
TEST_PARSE("u:__test_event int a");
TEST_PARSE("u:__test_event unsigned int a");
TEST_PARSE("u:__test_event short a");
TEST_PARSE("u:__test_event unsigned short a");
TEST_PARSE("u:__test_event char[20] a");
TEST_PARSE("u:__test_event unsigned char[20] a");
TEST_PARSE("u:__test_event char[0x14] a");
TEST_PARSE("u:__test_event unsigned char[0x14] a");
/* Bad size format should fail */
TEST_NPARSE("u:__test_event char[aa] a");
/* Large size should fail */
TEST_NPARSE("u:__test_event char[9999] a");
/* Long size string should fail */
TEST_NPARSE("u:__test_event char[0x0000000000001] a");
}
TEST_F(user, loc_types) {
/* All should work */
TEST_PARSE("u:__test_event __data_loc char[] a");
TEST_PARSE("u:__test_event __data_loc unsigned char[] a");
TEST_PARSE("u:__test_event __rel_loc char[] a");
TEST_PARSE("u:__test_event __rel_loc unsigned char[] a");
}
TEST_F(user, size_types) {
/* Should work */
TEST_PARSE("u:__test_event struct custom a 20");
/* Size not specified on struct should fail */
TEST_NPARSE("u:__test_event struct custom a");
/* Size specified on non-struct should fail */
TEST_NPARSE("u:__test_event char a 20");
}
TEST_F(user, flags) {
/* Should work */
TEST_PARSE("u:__test_event:BPF_ITER u32 a");
/* Forward compat */
TEST_PARSE("u:__test_event:BPF_ITER,FLAG_FUTURE u32 a");
}
TEST_F(user, matching) {
/* Register */
ASSERT_NE(-1, Append("u:__test_event struct custom a 20"));
/* Should not match */
TEST_NPARSE("!u:__test_event struct custom b");
/* Should match */
TEST_PARSE("!u:__test_event struct custom a");
/* Multi field reg */
ASSERT_NE(-1, Append("u:__test_event u32 a; u32 b"));
/* Non matching cases */
TEST_NPARSE("!u:__test_event u32 a");
TEST_NPARSE("!u:__test_event u32 b");
TEST_NPARSE("!u:__test_event u32 a; u32 ");
TEST_NPARSE("!u:__test_event u32 a; u32 a");
/* Matching case */
TEST_PARSE("!u:__test_event u32 a; u32 b");
/* Register */
ASSERT_NE(-1, Append("u:__test_event u32 a; u32 b"));
/* Ensure trailing semi-colon case */
TEST_PARSE("!u:__test_event u32 a; u32 b;");
}
int main(int argc, char **argv)
{
return test_harness_run(argc, argv);
}
// SPDX-License-Identifier: GPL-2.0
/*
* User Events FTrace Test Program
*
* Copyright (c) 2021 Beau Belgrave <beaub@linux.microsoft.com>
*/
#include <errno.h>
#include <linux/user_events.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <unistd.h>
#include "../kselftest_harness.h"
const char *data_file = "/sys/kernel/debug/tracing/user_events_data";
const char *status_file = "/sys/kernel/debug/tracing/user_events_status";
const char *enable_file = "/sys/kernel/debug/tracing/events/user_events/__test_event/enable";
const char *trace_file = "/sys/kernel/debug/tracing/trace";
const char *fmt_file = "/sys/kernel/debug/tracing/events/user_events/__test_event/format";
static int trace_bytes(void)
{
int fd = open(trace_file, O_RDONLY);
char buf[256];
int bytes = 0, got;
if (fd == -1)
return -1;
while (true) {
got = read(fd, buf, sizeof(buf));
if (got == -1)
return -1;
if (got == 0)
break;
bytes += got;
}
close(fd);
return bytes;
}
static int skip_until_empty_line(FILE *fp)
{
int c, last = 0;
while (true) {
c = getc(fp);
if (c == EOF)
break;
if (last == '\n' && c == '\n')
return 0;
last = c;
}
return -1;
}
static int get_print_fmt(char *buffer, int len)
{
FILE *fp = fopen(fmt_file, "r");
char *newline;
if (!fp)
return -1;
/* Read until empty line (Skip Common) */
if (skip_until_empty_line(fp) < 0)
goto err;
/* Read until empty line (Skip Properties) */
if (skip_until_empty_line(fp) < 0)
goto err;
/* Read in print_fmt: */
if (fgets(buffer, len, fp) == NULL)
goto err;
newline = strchr(buffer, '\n');
if (newline)
*newline = '\0';
fclose(fp);
return 0;
err:
fclose(fp);
return -1;
}
static int clear(void)
{
int fd = open(data_file, O_RDWR);
if (fd == -1)
return -1;
if (ioctl(fd, DIAG_IOCSDEL, "__test_event") == -1)
if (errno != ENOENT)
return -1;
close(fd);
return 0;
}
static int check_print_fmt(const char *event, const char *expected)
{
struct user_reg reg = {0};
char print_fmt[256];
int ret;
int fd;
/* Ensure cleared */
ret = clear();
if (ret != 0)
return ret;
fd = open(data_file, O_RDWR);
if (fd == -1)
return fd;
reg.size = sizeof(reg);
reg.name_args = (__u64)event;
/* Register should work */
ret = ioctl(fd, DIAG_IOCSREG, &reg);
close(fd);
if (ret != 0)
return ret;
/* Ensure correct print_fmt */
ret = get_print_fmt(print_fmt, sizeof(print_fmt));
if (ret != 0)
return ret;
return strcmp(print_fmt, expected);
}
FIXTURE(user) {
int status_fd;
int data_fd;
int enable_fd;
};
FIXTURE_SETUP(user) {
self->status_fd = open(status_file, O_RDONLY);
ASSERT_NE(-1, self->status_fd);
self->data_fd = open(data_file, O_RDWR);
ASSERT_NE(-1, self->data_fd);
self->enable_fd = -1;
}
FIXTURE_TEARDOWN(user) {
close(self->status_fd);
close(self->data_fd);
if (self->enable_fd != -1) {
write(self->enable_fd, "0", sizeof("0"));
close(self->enable_fd);
}
ASSERT_EQ(0, clear());
}
TEST_F(user, register_events) {
struct user_reg reg = {0};
int page_size = sysconf(_SC_PAGESIZE);
char *status_page;
reg.size = sizeof(reg);
reg.name_args = (__u64)"__test_event u32 field1; u32 field2";
status_page = mmap(NULL, page_size, PROT_READ, MAP_SHARED,
self->status_fd, 0);
/* Register should work */
ASSERT_EQ(0, ioctl(self->data_fd, DIAG_IOCSREG, &reg));
ASSERT_EQ(0, reg.write_index);
ASSERT_NE(0, reg.status_index);
/* Multiple registers should result in same index */
ASSERT_EQ(0, ioctl(self->data_fd, DIAG_IOCSREG, &reg));
ASSERT_EQ(0, reg.write_index);
ASSERT_NE(0, reg.status_index);
/* Ensure disabled */
self->enable_fd = open(enable_file, O_RDWR);
ASSERT_NE(-1, self->enable_fd);
ASSERT_NE(-1, write(self->enable_fd, "0", sizeof("0")))
/* MMAP should work and be zero'd */
ASSERT_NE(MAP_FAILED, status_page);
ASSERT_NE(NULL, status_page);
ASSERT_EQ(0, status_page[reg.status_index]);
/* Enable event and ensure bits updated in status */
ASSERT_NE(-1, write(self->enable_fd, "1", sizeof("1")))
ASSERT_EQ(EVENT_STATUS_FTRACE, status_page[reg.status_index]);
/* Disable event and ensure bits updated in status */
ASSERT_NE(-1, write(self->enable_fd, "0", sizeof("0")))
ASSERT_EQ(0, status_page[reg.status_index]);
/* File still open should return -EBUSY for delete */
ASSERT_EQ(-1, ioctl(self->data_fd, DIAG_IOCSDEL, "__test_event"));
ASSERT_EQ(EBUSY, errno);
/* Delete should work only after close */
close(self->data_fd);
self->data_fd = open(data_file, O_RDWR);
ASSERT_EQ(0, ioctl(self->data_fd, DIAG_IOCSDEL, "__test_event"));
/* Unmap should work */
ASSERT_EQ(0, munmap(status_page, page_size));
}
TEST_F(user, write_events) {
struct user_reg reg = {0};
struct iovec io[3];
__u32 field1, field2;
int before = 0, after = 0;
reg.size = sizeof(reg);
reg.name_args = (__u64)"__test_event u32 field1; u32 field2";
field1 = 1;
field2 = 2;
io[0].iov_base = &reg.write_index;
io[0].iov_len = sizeof(reg.write_index);
io[1].iov_base = &field1;
io[1].iov_len = sizeof(field1);
io[2].iov_base = &field2;
io[2].iov_len = sizeof(field2);
/* Register should work */
ASSERT_EQ(0, ioctl(self->data_fd, DIAG_IOCSREG, &reg));
ASSERT_EQ(0, reg.write_index);
ASSERT_NE(0, reg.status_index);
/* Write should fail on invalid slot with ENOENT */
io[0].iov_base = &field2;
io[0].iov_len = sizeof(field2);
ASSERT_EQ(-1, writev(self->data_fd, (const struct iovec *)io, 3));
ASSERT_EQ(ENOENT, errno);
io[0].iov_base = &reg.write_index;
io[0].iov_len = sizeof(reg.write_index);
/* Enable event */
self->enable_fd = open(enable_file, O_RDWR);
ASSERT_NE(-1, write(self->enable_fd, "1", sizeof("1")))
/* Write should make it out to ftrace buffers */
before = trace_bytes();
ASSERT_NE(-1, writev(self->data_fd, (const struct iovec *)io, 3));
after = trace_bytes();
ASSERT_GT(after, before);
}
TEST_F(user, write_fault) {
struct user_reg reg = {0};
struct iovec io[2];
int l = sizeof(__u64);
void *anon;
reg.size = sizeof(reg);
reg.name_args = (__u64)"__test_event u64 anon";
anon = mmap(NULL, l, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
ASSERT_NE(MAP_FAILED, anon);
io[0].iov_base = &reg.write_index;
io[0].iov_len = sizeof(reg.write_index);
io[1].iov_base = anon;
io[1].iov_len = l;
/* Register should work */
ASSERT_EQ(0, ioctl(self->data_fd, DIAG_IOCSREG, &reg));
ASSERT_EQ(0, reg.write_index);
ASSERT_NE(0, reg.status_index);
/* Write should work normally */
ASSERT_NE(-1, writev(self->data_fd, (const struct iovec *)io, 2));
/* Faulted data should zero fill and work */
ASSERT_EQ(0, madvise(anon, l, MADV_DONTNEED));
ASSERT_NE(-1, writev(self->data_fd, (const struct iovec *)io, 2));
ASSERT_EQ(0, munmap(anon, l));
}
TEST_F(user, write_validator) {
struct user_reg reg = {0};
struct iovec io[3];
int loc, bytes;
char data[8];
int before = 0, after = 0;
reg.size = sizeof(reg);
reg.name_args = (__u64)"__test_event __rel_loc char[] data";
/* Register should work */
ASSERT_EQ(0, ioctl(self->data_fd, DIAG_IOCSREG, &reg));
ASSERT_EQ(0, reg.write_index);
ASSERT_NE(0, reg.status_index);
io[0].iov_base = &reg.write_index;
io[0].iov_len = sizeof(reg.write_index);
io[1].iov_base = &loc;
io[1].iov_len = sizeof(loc);
io[2].iov_base = data;
bytes = snprintf(data, sizeof(data), "Test") + 1;
io[2].iov_len = bytes;
/* Undersized write should fail */
ASSERT_EQ(-1, writev(self->data_fd, (const struct iovec *)io, 1));
ASSERT_EQ(EINVAL, errno);
/* Enable event */
self->enable_fd = open(enable_file, O_RDWR);
ASSERT_NE(-1, write(self->enable_fd, "1", sizeof("1")))
/* Full in-bounds write should work */
before = trace_bytes();
loc = DYN_LOC(0, bytes);
ASSERT_NE(-1, writev(self->data_fd, (const struct iovec *)io, 3));
after = trace_bytes();
ASSERT_GT(after, before);
/* Out of bounds write should fault (offset way out) */
loc = DYN_LOC(1024, bytes);
ASSERT_EQ(-1, writev(self->data_fd, (const struct iovec *)io, 3));
ASSERT_EQ(EFAULT, errno);
/* Out of bounds write should fault (offset 1 byte out) */
loc = DYN_LOC(1, bytes);
ASSERT_EQ(-1, writev(self->data_fd, (const struct iovec *)io, 3));
ASSERT_EQ(EFAULT, errno);
/* Out of bounds write should fault (size way out) */
loc = DYN_LOC(0, bytes + 1024);
ASSERT_EQ(-1, writev(self->data_fd, (const struct iovec *)io, 3));
ASSERT_EQ(EFAULT, errno);
/* Out of bounds write should fault (size 1 byte out) */
loc = DYN_LOC(0, bytes + 1);
ASSERT_EQ(-1, writev(self->data_fd, (const struct iovec *)io, 3));
ASSERT_EQ(EFAULT, errno);
/* Non-Null should fault */
memset(data, 'A', sizeof(data));
loc = DYN_LOC(0, bytes);
ASSERT_EQ(-1, writev(self->data_fd, (const struct iovec *)io, 3));
ASSERT_EQ(EFAULT, errno);
}
TEST_F(user, print_fmt) {
int ret;
ret = check_print_fmt("__test_event __rel_loc char[] data",
"print fmt: \"data=%s\", __get_rel_str(data)");
ASSERT_EQ(0, ret);
ret = check_print_fmt("__test_event __data_loc char[] data",
"print fmt: \"data=%s\", __get_str(data)");
ASSERT_EQ(0, ret);
ret = check_print_fmt("__test_event s64 data",
"print fmt: \"data=%lld\", REC->data");
ASSERT_EQ(0, ret);
ret = check_print_fmt("__test_event u64 data",
"print fmt: \"data=%llu\", REC->data");
ASSERT_EQ(0, ret);
ret = check_print_fmt("__test_event s32 data",
"print fmt: \"data=%d\", REC->data");
ASSERT_EQ(0, ret);
ret = check_print_fmt("__test_event u32 data",
"print fmt: \"data=%u\", REC->data");
ASSERT_EQ(0, ret);
ret = check_print_fmt("__test_event int data",
"print fmt: \"data=%d\", REC->data");
ASSERT_EQ(0, ret);
ret = check_print_fmt("__test_event unsigned int data",
"print fmt: \"data=%u\", REC->data");
ASSERT_EQ(0, ret);
ret = check_print_fmt("__test_event s16 data",
"print fmt: \"data=%d\", REC->data");
ASSERT_EQ(0, ret);
ret = check_print_fmt("__test_event u16 data",
"print fmt: \"data=%u\", REC->data");
ASSERT_EQ(0, ret);
ret = check_print_fmt("__test_event short data",
"print fmt: \"data=%d\", REC->data");
ASSERT_EQ(0, ret);
ret = check_print_fmt("__test_event unsigned short data",
"print fmt: \"data=%u\", REC->data");
ASSERT_EQ(0, ret);
ret = check_print_fmt("__test_event s8 data",
"print fmt: \"data=%d\", REC->data");
ASSERT_EQ(0, ret);
ret = check_print_fmt("__test_event u8 data",
"print fmt: \"data=%u\", REC->data");
ASSERT_EQ(0, ret);
ret = check_print_fmt("__test_event char data",
"print fmt: \"data=%d\", REC->data");
ASSERT_EQ(0, ret);
ret = check_print_fmt("__test_event unsigned char data",
"print fmt: \"data=%u\", REC->data");
ASSERT_EQ(0, ret);
ret = check_print_fmt("__test_event char[4] data",
"print fmt: \"data=%s\", REC->data");
ASSERT_EQ(0, ret);
}
int main(int argc, char **argv)
{
return test_harness_run(argc, argv);
}
// SPDX-License-Identifier: GPL-2.0
/*
* User Events Perf Events Test Program
*
* Copyright (c) 2021 Beau Belgrave <beaub@linux.microsoft.com>
*/
#include <errno.h>
#include <linux/user_events.h>
#include <linux/perf_event.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <asm/unistd.h>
#include "../kselftest_harness.h"
const char *data_file = "/sys/kernel/debug/tracing/user_events_data";
const char *status_file = "/sys/kernel/debug/tracing/user_events_status";
const char *id_file = "/sys/kernel/debug/tracing/events/user_events/__test_event/id";
const char *fmt_file = "/sys/kernel/debug/tracing/events/user_events/__test_event/format";
struct event {
__u32 index;
__u32 field1;
__u32 field2;
};
static long perf_event_open(struct perf_event_attr *pe, pid_t pid,
int cpu, int group_fd, unsigned long flags)
{
return syscall(__NR_perf_event_open, pe, pid, cpu, group_fd, flags);
}
static int get_id(void)
{
FILE *fp = fopen(id_file, "r");
int ret, id = 0;
if (!fp)
return -1;
ret = fscanf(fp, "%d", &id);
fclose(fp);
if (ret != 1)
return -1;
return id;
}
static int get_offset(void)
{
FILE *fp = fopen(fmt_file, "r");
int ret, c, last = 0, offset = 0;
if (!fp)
return -1;
/* Read until empty line */
while (true) {
c = getc(fp);
if (c == EOF)
break;
if (last == '\n' && c == '\n')
break;
last = c;
}
ret = fscanf(fp, "\tfield:u32 field1;\toffset:%d;", &offset);
fclose(fp);
if (ret != 1)
return -1;
return offset;
}
FIXTURE(user) {
int status_fd;
int data_fd;
};
FIXTURE_SETUP(user) {
self->status_fd = open(status_file, O_RDONLY);
ASSERT_NE(-1, self->status_fd);
self->data_fd = open(data_file, O_RDWR);
ASSERT_NE(-1, self->data_fd);
}
FIXTURE_TEARDOWN(user) {
close(self->status_fd);
close(self->data_fd);
}
TEST_F(user, perf_write) {
struct perf_event_attr pe = {0};
struct user_reg reg = {0};
int page_size = sysconf(_SC_PAGESIZE);
char *status_page;
struct event event;
struct perf_event_mmap_page *perf_page;
int id, fd, offset;
__u32 *val;
reg.size = sizeof(reg);
reg.name_args = (__u64)"__test_event u32 field1; u32 field2";
status_page = mmap(NULL, page_size, PROT_READ, MAP_SHARED,
self->status_fd, 0);
ASSERT_NE(MAP_FAILED, status_page);
/* Register should work */
ASSERT_EQ(0, ioctl(self->data_fd, DIAG_IOCSREG, &reg));
ASSERT_EQ(0, reg.write_index);
ASSERT_NE(0, reg.status_index);
ASSERT_EQ(0, status_page[reg.status_index]);
/* Id should be there */
id = get_id();
ASSERT_NE(-1, id);
offset = get_offset();
ASSERT_NE(-1, offset);
pe.type = PERF_TYPE_TRACEPOINT;
pe.size = sizeof(pe);
pe.config = id;
pe.sample_type = PERF_SAMPLE_RAW;
pe.sample_period = 1;
pe.wakeup_events = 1;
/* Tracepoint attach should work */
fd = perf_event_open(&pe, 0, -1, -1, 0);
ASSERT_NE(-1, fd);
perf_page = mmap(NULL, page_size * 2, PROT_READ, MAP_SHARED, fd, 0);
ASSERT_NE(MAP_FAILED, perf_page);
/* Status should be updated */
ASSERT_EQ(EVENT_STATUS_PERF, status_page[reg.status_index]);
event.index = reg.write_index;
event.field1 = 0xc001;
event.field2 = 0xc01a;
/* Ensure write shows up at correct offset */
ASSERT_NE(-1, write(self->data_fd, &event, sizeof(event)));
val = (void *)(((char *)perf_page) + perf_page->data_offset);
ASSERT_EQ(PERF_RECORD_SAMPLE, *val);
/* Skip over header and size, move to offset */
val += 3;
val = (void *)((char *)val) + offset;
/* Ensure correct */
ASSERT_EQ(event.field1, *val++);
ASSERT_EQ(event.field2, *val++);
}
int main(int argc, char **argv)
{
return test_harness_run(argc, argv);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment