forked from Minki/linux
Tracing updates for 5.18:
- 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). -----BEGIN PGP SIGNATURE----- iIoEABYIADIWIQRRSw7ePDh/lE+zeZMp5XQQmuv6qgUCYjiO3RQccm9zdGVkdEBn b29kbWlzLm9yZwAKCRAp5XQQmuv6qhQzAQDtek5p80p/zkMGymm14wSH6qq0NdgN Kv7fTBwEewUa0gD/UCOVLw4Oj+JtHQhCa3sCGZopmRv0BT1+4UQANqosKQY= =Au08 -----END PGP SIGNATURE----- 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 ...
This commit is contained in:
commit
1bc191051d
@ -1465,6 +1465,14 @@
|
||||
as early as possible in order to facilitate early
|
||||
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] will dump the trace buffers on oops.
|
||||
If no parameter is passed, ftrace will dump
|
||||
|
@ -30,3 +30,4 @@ Linux Tracing Technologies
|
||||
stm
|
||||
sys-t
|
||||
coresight/index
|
||||
user_events
|
||||
|
216
Documentation/trace/user_events.rst
Normal file
216
Documentation/trace/user_events.rst
Normal file
@ -0,0 +1,216 @@
|
||||
=========================================
|
||||
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 @@
|
||||
#define ARCH_SUPPORTS_FTRACE_OPS 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_TRACING
|
||||
extern void ftrace_boot_snapshot(void);
|
||||
#else
|
||||
static inline void ftrace_boot_snapshot(void) { }
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_FUNCTION_TRACER
|
||||
struct ftrace_ops;
|
||||
struct ftrace_regs;
|
||||
@ -215,7 +221,10 @@ struct ftrace_ops_hash {
|
||||
void ftrace_free_init_mem(void);
|
||||
void ftrace_free_mem(struct module *mod, void *start, void *end);
|
||||
#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) { }
|
||||
#endif
|
||||
|
||||
|
@ -315,6 +315,7 @@ enum {
|
||||
TRACE_EVENT_FL_KPROBE_BIT,
|
||||
TRACE_EVENT_FL_UPROBE_BIT,
|
||||
TRACE_EVENT_FL_EPROBE_BIT,
|
||||
TRACE_EVENT_FL_CUSTOM_BIT,
|
||||
};
|
||||
|
||||
/*
|
||||
@ -328,6 +329,9 @@ enum {
|
||||
* KPROBE - Event is a kprobe
|
||||
* UPROBE - Event is a uprobe
|
||||
* 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 {
|
||||
TRACE_EVENT_FL_FILTERED = (1 << TRACE_EVENT_FL_FILTERED_BIT),
|
||||
@ -339,6 +343,7 @@ enum {
|
||||
TRACE_EVENT_FL_KPROBE = (1 << TRACE_EVENT_FL_KPROBE_BIT),
|
||||
TRACE_EVENT_FL_UPROBE = (1 << TRACE_EVENT_FL_UPROBE_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)
|
||||
@ -440,7 +445,9 @@ static inline bool bpf_prog_array_valid(struct trace_event_call *call)
|
||||
static inline const char *
|
||||
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;
|
||||
else
|
||||
return call->name;
|
||||
@ -903,3 +910,18 @@ perf_trace_buf_submit(void *raw_data, int size, int rctx, u16 type,
|
||||
#endif
|
||||
|
||||
#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) */
|
||||
|
77
include/trace/define_custom_trace.h
Normal file
77
include/trace/define_custom_trace.h
Normal file
@ -0,0 +1,77 @@
|
||||
/* 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 */
|
37
include/trace/stages/init.h
Normal file
37
include/trace/stages/init.h
Normal file
@ -0,0 +1,37 @@
|
||||
|
||||
#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
|
51
include/trace/stages/stage1_defines.h
Normal file
51
include/trace/stages/stage1_defines.h
Normal file
@ -0,0 +1,51 @@
|
||||
/* 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
|
54
include/trace/stages/stage2_defines.h
Normal file
54
include/trace/stages/stage2_defines.h
Normal file
@ -0,0 +1,54 @@
|
||||
/* 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)
|
135
include/trace/stages/stage3_defines.h
Normal file
135
include/trace/stages/stage3_defines.h
Normal file
@ -0,0 +1,135 @@
|
||||
/* 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); \
|
||||
})
|
63
include/trace/stages/stage4_defines.h
Normal file
63
include/trace/stages/stage4_defines.h
Normal file
@ -0,0 +1,63 @@
|
||||
/* 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)
|
89
include/trace/stages/stage5_defines.h
Normal file
89
include/trace/stages/stage5_defines.h
Normal file
@ -0,0 +1,89 @@
|
||||
/* 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)
|
106
include/trace/stages/stage6_defines.h
Normal file
106
include/trace/stages/stage6_defines.h
Normal file
@ -0,0 +1,106 @@
|
||||
/* 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)
|
36
include/trace/stages/stage7_defines.h
Normal file
36
include/trace/stages/stage7_defines.h
Normal file
@ -0,0 +1,36 @@
|
||||
/* 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)
|
221
include/trace/trace_custom_events.h
Normal file
221
include/trace/trace_custom_events.h
Normal file
@ -0,0 +1,221 @@
|
||||
/* 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 @@
|
||||
#define TRACE_SYSTEM_VAR TRACE_SYSTEM
|
||||
#endif
|
||||
|
||||
#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
|
||||
#include "stages/init.h"
|
||||
|
||||
/*
|
||||
* DECLARE_EVENT_CLASS can be used to add a generic function
|
||||
@ -80,54 +45,7 @@ TRACE_MAKE_SYSTEM_STR();
|
||||
PARAMS(print)); \
|
||||
DEFINE_EVENT(name, name, PARAMS(proto), PARAMS(args));
|
||||
|
||||
|
||||
#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
|
||||
#include "stages/stage1_defines.h"
|
||||
|
||||
#undef DECLARE_EVENT_CLASS
|
||||
#define DECLARE_EVENT_CLASS(name, proto, args, tstruct, assign, print) \
|
||||
@ -191,56 +109,7 @@ TRACE_MAKE_SYSTEM_STR();
|
||||
* The size of an array is also encoded, in the higher 16 bits of <item>.
|
||||
*/
|
||||
|
||||
#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)
|
||||
#include "stages/stage2_defines.h"
|
||||
|
||||
#undef DECLARE_EVENT_CLASS
|
||||
#define DECLARE_EVENT_CLASS(call, proto, args, tstruct, assign, print) \
|
||||
@ -312,137 +181,7 @@ TRACE_MAKE_SYSTEM_STR();
|
||||
* in binary.
|
||||
*/
|
||||
|
||||
#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); \
|
||||
})
|
||||
#include "stages/stage3_defines.h"
|
||||
|
||||
#undef DECLARE_EVENT_CLASS
|
||||
#define DECLARE_EVENT_CLASS(call, proto, args, tstruct, assign, print) \
|
||||
@ -497,65 +236,7 @@ static struct trace_event_functions trace_event_type_funcs_##call = { \
|
||||
|
||||
#include TRACE_INCLUDE(TRACE_INCLUDE_FILE)
|
||||
|
||||
#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)
|
||||
#include "stages/stage4_defines.h"
|
||||
|
||||
#undef DECLARE_EVENT_CLASS
|
||||
#define DECLARE_EVENT_CLASS(call, proto, args, tstruct, func, print) \
|
||||
@ -568,91 +249,7 @@ static struct trace_event_fields trace_event_fields_##call[] = { \
|
||||
|
||||
#include TRACE_INCLUDE(TRACE_INCLUDE_FILE)
|
||||
|
||||
/*
|
||||
* 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)
|
||||
#include "stages/stage5_defines.h"
|
||||
|
||||
#undef DECLARE_EVENT_CLASS
|
||||
#define DECLARE_EVENT_CLASS(call, proto, args, tstruct, assign, print) \
|
||||
@ -775,107 +372,7 @@ static inline notrace int trace_event_get_offsets_##call( \
|
||||
#define _TRACE_PERF_INIT(call)
|
||||
#endif /* CONFIG_PERF_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))
|
||||
|
||||
#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)
|
||||
#include "stages/stage6_defines.h"
|
||||
|
||||
#undef DECLARE_EVENT_CLASS
|
||||
#define DECLARE_EVENT_CLASS(call, proto, args, tstruct, assign, print) \
|
||||
@ -921,38 +418,7 @@ static inline void ftrace_test_probe_##call(void) \
|
||||
|
||||
#include TRACE_INCLUDE(TRACE_INCLUDE_FILE)
|
||||
|
||||
#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)
|
||||
#include "stages/stage7_defines.h"
|
||||
|
||||
#undef DECLARE_EVENT_CLASS
|
||||
#define DECLARE_EVENT_CLASS(call, proto, args, tstruct, assign, print) \
|
||||
|
116
include/uapi/linux/user_events.h
Normal file
116
include/uapi/linux/user_events.h
Normal file
@ -0,0 +1,116 @@
|
||||
/* 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
|
||||
|
||||
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
|
||||
bool "Histogram triggers"
|
||||
depends on ARCH_HAVE_NMI_SAFE_CMPXCHG
|
||||
|
@ -82,6 +82,7 @@ obj-$(CONFIG_PROBE_EVENTS) += trace_eprobe.o
|
||||
obj-$(CONFIG_TRACE_EVENT_INJECT) += trace_events_inject.o
|
||||
obj-$(CONFIG_SYNTH_EVENTS) += trace_events_synth.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_KPROBE_EVENTS) += trace_kprobe.o
|
||||
obj-$(CONFIG_TRACEPOINTS) += error_report-traces.o
|
||||
|
@ -7096,6 +7096,8 @@ void __init ftrace_free_init_mem(void)
|
||||
void *start = (void *)(&__init_begin);
|
||||
void *end = (void *)(&__init_end);
|
||||
|
||||
ftrace_boot_snapshot();
|
||||
|
||||
ftrace_free_mem(NULL, start, end);
|
||||
}
|
||||
|
||||
|
@ -185,6 +185,7 @@ static char bootup_tracer_buf[MAX_TRACER_SIZE] __initdata;
|
||||
static char *default_bootup_tracer;
|
||||
|
||||
static bool allocate_snapshot;
|
||||
static bool snapshot_at_boot;
|
||||
|
||||
static int __init set_cmdline_ftrace(char *str)
|
||||
{
|
||||
@ -230,6 +231,15 @@ static int __init boot_alloc_snapshot(char *str)
|
||||
__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 int __init set_trace_boot_options(char *str)
|
||||
@ -7725,7 +7735,7 @@ const struct file_operations trace_min_max_fops = {
|
||||
struct err_info {
|
||||
const char **errs; /* ptr to loc-specific array of err strings */
|
||||
u8 type; /* index into errs -> specific err string */
|
||||
u8 pos; /* MAX_FILTER_STR_VAL = 256 */
|
||||
u16 pos; /* caret position */
|
||||
u64 ts;
|
||||
};
|
||||
|
||||
@ -7733,26 +7743,52 @@ struct tracing_log_err {
|
||||
struct list_head list;
|
||||
struct err_info info;
|
||||
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 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;
|
||||
|
||||
if (tr->n_err_log_entries < TRACING_LOG_ERRS_MAX) {
|
||||
err = kzalloc(sizeof(*err), GFP_KERNEL);
|
||||
if (!err)
|
||||
err = ERR_PTR(-ENOMEM);
|
||||
else
|
||||
err = alloc_tracing_log_err(len);
|
||||
if (PTR_ERR(err) != -ENOMEM)
|
||||
tr->n_err_log_entries++;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return err;
|
||||
@ -7813,22 +7849,25 @@ unsigned int err_pos(char *cmd, const char *str)
|
||||
*/
|
||||
void tracing_log_err(struct trace_array *tr,
|
||||
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;
|
||||
int len = 0;
|
||||
|
||||
if (!tr)
|
||||
tr = &global_trace;
|
||||
|
||||
len += sizeof(CMD_PREFIX) + 2 * sizeof("\n") + strlen(cmd) + 1;
|
||||
|
||||
mutex_lock(&tracing_err_log_lock);
|
||||
err = get_tracing_log_err(tr);
|
||||
err = get_tracing_log_err(tr, len);
|
||||
if (PTR_ERR(err) == -ENOMEM) {
|
||||
mutex_unlock(&tracing_err_log_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
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.type = type;
|
||||
@ -7846,7 +7885,7 @@ static void clear_tracing_err_log(struct trace_array *tr)
|
||||
mutex_lock(&tracing_err_log_lock);
|
||||
list_for_each_entry_safe(err, next, &tr->err_log, list) {
|
||||
list_del(&err->list);
|
||||
kfree(err);
|
||||
free_tracing_log_err(err);
|
||||
}
|
||||
|
||||
tr->n_err_log_entries = 0;
|
||||
@ -7874,9 +7913,9 @@ static void tracing_err_log_seq_stop(struct seq_file *m, void *v)
|
||||
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++)
|
||||
seq_putc(m, ' ');
|
||||
@ -10122,6 +10161,14 @@ out:
|
||||
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)
|
||||
{
|
||||
if (tracepoint_printk) {
|
||||
|
@ -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 void tracing_log_err(struct trace_array *tr,
|
||||
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
|
||||
|
@ -40,6 +40,14 @@ static LIST_HEAD(ftrace_generic_fields);
|
||||
static LIST_HEAD(ftrace_common_fields);
|
||||
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)
|
||||
|
||||
static struct kmem_cache *field_cachep;
|
||||
@ -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)
|
||||
{
|
||||
struct trace_event_call *call, *p;
|
||||
@ -2678,6 +2756,7 @@ void trace_event_eval_update(struct trace_eval_map **map, int len)
|
||||
first = false;
|
||||
}
|
||||
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)
|
||||
mutex_unlock(&trace_types_lock);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(trace_add_event_call);
|
||||
|
||||
/*
|
||||
* 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)
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(trace_remove_event_call);
|
||||
|
||||
#define for_each_event(event, start, end) \
|
||||
for (event = start; \
|
||||
@ -2863,6 +2944,7 @@ static void trace_module_add_events(struct module *mod)
|
||||
static void trace_module_remove_events(struct module *mod)
|
||||
{
|
||||
struct trace_event_call *call, *p;
|
||||
struct module_string *modstr, *m;
|
||||
|
||||
down_write(&trace_event_sem);
|
||||
list_for_each_entry_safe(call, p, &ftrace_events, list) {
|
||||
@ -2871,6 +2953,14 @@ static void trace_module_remove_events(struct module *mod)
|
||||
if (call->module == mod)
|
||||
__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);
|
||||
|
||||
/*
|
||||
|
@ -727,11 +727,16 @@ static struct track_data *track_data_alloc(unsigned int key_len,
|
||||
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 int errpos(char *str)
|
||||
{
|
||||
if (!str || !last_cmd)
|
||||
return 0;
|
||||
|
||||
return err_pos(last_cmd, str);
|
||||
}
|
||||
|
||||
@ -739,12 +744,22 @@ static void last_cmd_set(struct trace_event_file *file, char *str)
|
||||
{
|
||||
const char *system = NULL, *name = NULL;
|
||||
struct trace_event_call *call;
|
||||
int len;
|
||||
|
||||
if (!str)
|
||||
return;
|
||||
|
||||
strcpy(last_cmd, "hist:");
|
||||
strncat(last_cmd, str, MAX_FILTER_STR_VAL - 1 - sizeof("hist:"));
|
||||
/* sizeof() contains the nul byte */
|
||||
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) {
|
||||
call = file->event_call;
|
||||
@ -757,18 +772,22 @@ static void last_cmd_set(struct trace_event_file *file, char *str)
|
||||
}
|
||||
|
||||
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,
|
||||
err_type, err_pos);
|
||||
}
|
||||
|
||||
static void hist_err_clear(void)
|
||||
{
|
||||
last_cmd[0] = '\0';
|
||||
if (last_cmd)
|
||||
last_cmd[0] = '\0';
|
||||
last_cmd_loc[0] = '\0';
|
||||
}
|
||||
|
||||
@ -5610,7 +5629,7 @@ static int event_hist_trigger_print(struct seq_file *m,
|
||||
bool have_var = false;
|
||||
unsigned int i;
|
||||
|
||||
seq_puts(m, "hist:");
|
||||
seq_puts(m, HIST_PREFIX);
|
||||
|
||||
if (data->name)
|
||||
seq_printf(m, "%s:", data->name);
|
||||
|
@ -42,10 +42,13 @@ enum { 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)
|
||||
{
|
||||
if (!str || !last_cmd)
|
||||
return 0;
|
||||
|
||||
return err_pos(last_cmd, str);
|
||||
}
|
||||
|
||||
@ -54,11 +57,16 @@ static void last_cmd_set(const char *str)
|
||||
if (!str)
|
||||
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,
|
||||
err_type, err_pos);
|
||||
}
|
||||
|
1690
kernel/trace/trace_events_user.c
Normal file
1690
kernel/trace/trace_events_user.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -45,7 +45,7 @@ probe_sched_wakeup(void *ignore, struct task_struct *wakee)
|
||||
|
||||
if (!flags)
|
||||
return;
|
||||
tracing_record_taskinfo(current, flags);
|
||||
tracing_record_taskinfo_sched_switch(current, wakee, flags);
|
||||
}
|
||||
|
||||
static int tracing_sched_register(void)
|
||||
|
@ -14,7 +14,13 @@ config SAMPLE_TRACE_EVENTS
|
||||
tristate "Build trace_events examples -- loadable modules only"
|
||||
depends on EVENT_TRACING && m
|
||||
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
|
||||
tristate "Build trace_printk module - tests various trace_printk formats"
|
||||
|
@ -20,6 +20,7 @@ obj-$(CONFIG_SAMPLE_RPMSG_CLIENT) += rpmsg/
|
||||
subdir-$(CONFIG_SAMPLE_SECCOMP) += seccomp
|
||||
subdir-$(CONFIG_SAMPLE_TIMER) += timers
|
||||
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_FTRACE_DIRECT) += ftrace/
|
||||
obj-$(CONFIG_SAMPLE_FTRACE_DIRECT_MULTI) += ftrace/
|
||||
|
@ -11,5 +11,7 @@
|
||||
# Here trace-events-sample.c does the CREATE_TRACE_POINTS.
|
||||
#
|
||||
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_CUSTOM_EVENTS) += trace_custom_sched.o
|
||||
|
60
samples/trace_events/trace_custom_sched.c
Normal file
60
samples/trace_events/trace_custom_sched.c
Normal file
@ -0,0 +1,60 @@
|
||||
// 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");
|
96
samples/trace_events/trace_custom_sched.h
Normal file
96
samples/trace_events/trace_custom_sched.h
Normal file
@ -0,0 +1,96 @@
|
||||
/* 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>
|
5
samples/user_events/Makefile
Normal file
5
samples/user_events/Makefile
Normal file
@ -0,0 +1,5 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
CFLAGS += -Wl,-no-as-needed -Wall -I../../usr/include
|
||||
|
||||
example: example.o
|
||||
example.o: example.c
|
91
samples/user_events/example.c
Normal file
91
samples/user_events/example.c
Normal file
@ -0,0 +1,91 @@
|
||||
// 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, ®) == -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;
|
||||
}
|
9
tools/testing/selftests/user_events/Makefile
Normal file
9
tools/testing/selftests/user_events/Makefile
Normal file
@ -0,0 +1,9 @@
|
||||
# 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
|
130
tools/testing/selftests/user_events/dyn_test.c
Normal file
130
tools/testing/selftests/user_events/dyn_test.c
Normal file
@ -0,0 +1,130 @@
|
||||
// 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);
|
||||
}
|
452
tools/testing/selftests/user_events/ftrace_test.c
Normal file
452
tools/testing/selftests/user_events/ftrace_test.c
Normal file
@ -0,0 +1,452 @@
|
||||
// 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, ®);
|
||||
|
||||
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, ®));
|
||||
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, ®));
|
||||
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 = ®.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, ®));
|
||||
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 = ®.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 = ®.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, ®));
|
||||
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, ®));
|
||||
ASSERT_EQ(0, reg.write_index);
|
||||
ASSERT_NE(0, reg.status_index);
|
||||
|
||||
io[0].iov_base = ®.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);
|
||||
}
|
168
tools/testing/selftests/user_events/perf_test.c
Normal file
168
tools/testing/selftests/user_events/perf_test.c
Normal file
@ -0,0 +1,168 @@
|
||||
// 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, ®));
|
||||
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);
|
||||
}
|
1
tools/testing/selftests/user_events/settings
Normal file
1
tools/testing/selftests/user_events/settings
Normal file
@ -0,0 +1 @@
|
||||
timeout=90
|
Loading…
Reference in New Issue
Block a user