Files
laptops-kernel/kernel/trace/trace_probe.h
Masami Hiramatsu 1a0ffe10bf tracing/probes: Eliminate recursion in parse_probe_arg()
To avoid potential stack overflows on limited kernel stacks, convert
parse_probe_arg() from a recursive function into a loop-based
implementation with a simple local state stack.

Since recursion is eliminated using a loop with a fixed-size
stack in the context, this restricts the dereference nesting
depth. The maximum nesting depth of dereferences is now restricted
to the same limit as typecasts (TRACEPROBE_MAX_NESTED_LEVEL, which
is 8) and reports the same TOO_MANY_NESTED error. Update ftrace
selftests to reflect this restriction and simplify the checks.

Note that this change slightly alters the behavior of nested
dereferencing in fetcharg. Previously, dereferencing without BTF
allowed for up to 14 levels of nesting, whereas dereferencing
with BTF was limited to 3 levels. With this change, the nesting
depth is now limited to 8 levels in both cases.

Link: https://lore.kernel.org/all/178399141396.27810.5390060618628718661.stgit@devnote2/

Assisted-by: Antigravity:gemini-3.5-flash
Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
2026-07-14 22:43:40 +09:00

662 lines
22 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Common header file for probe-based Dynamic events.
*
* This code was copied from kernel/trace/trace_kprobe.h written by
* Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
*
* Updates to make this generic:
* Copyright (C) IBM Corporation, 2010-2011
* Author: Srikar Dronamraju
*/
#include <linux/bitops.h>
#include <linux/btf.h>
#include <linux/cleanup.h>
#include <linux/kprobes.h>
#include <linux/limits.h>
#include <linux/perf_event.h>
#include <linux/ptrace.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/smp.h>
#include <linux/string.h>
#include <linux/stringify.h>
#include <linux/tracefs.h>
#include <linux/types.h>
#include <linux/uaccess.h>
#include <asm/bitsperlong.h>
#include "trace.h"
#include "trace_output.h"
#define MAX_TRACE_ARGS 128
#define MAX_ARGSTR_LEN 255
#define MAX_COMMON_HEAD_LEN 63
#define MAX_ARRAY_LEN 64
#define MAX_ARG_NAME_LEN 32
#define MAX_BTF_ARGS_LEN 128
#define MAX_DENTRY_ARGS_LEN 256
#define MAX_STRING_SIZE PATH_MAX
#define MAX_PROBE_EVENT_SIZE 3072
/* Reserved field names */
#define FIELD_STRING_IP "__probe_ip"
#define FIELD_STRING_RETIP "__probe_ret_ip"
#define FIELD_STRING_FUNC "__probe_func"
#undef DEFINE_FIELD
#define DEFINE_FIELD(type, item, name, is_signed) \
do { \
ret = trace_define_field(event_call, #type, name, \
offsetof(typeof(field), item), \
sizeof(field.item), is_signed, \
FILTER_OTHER); \
if (ret) \
return ret; \
} while (0)
/* Flags for trace_probe */
#define TP_FLAG_TRACE 1
#define TP_FLAG_PROFILE 2
/* data_loc: data location, compatible with u32 */
#define make_data_loc(len, offs) \
(((u32)(len) << 16) | ((u32)(offs) & 0xffff))
#define get_loc_len(dl) ((u32)(dl) >> 16)
#define get_loc_offs(dl) ((u32)(dl) & 0xffff)
static nokprobe_inline void *get_loc_data(u32 *dl, void *ent)
{
return (u8 *)ent + get_loc_offs(*dl);
}
static nokprobe_inline u32 update_data_loc(u32 loc, int consumed)
{
u32 maxlen = get_loc_len(loc);
u32 offset = get_loc_offs(loc);
return make_data_loc(maxlen - consumed, offset + consumed);
}
/* Printing function type */
typedef int (*print_type_func_t)(struct trace_seq *, void *, void *);
#define FETCH_OP_LIST { \
/* Stage 1 (load) ops */ \
FETCH_OP(NOP, none), /* NOP */ \
FETCH_OP(REG, param), /* Register: .param = offset */ \
FETCH_OP(STACK, param), /* Stack: .param = index */ \
FETCH_OP(STACKP, none), /* Stack pointer */ \
FETCH_OP(RETVAL, none), /* Return value */ \
FETCH_OP(IMM, imm), /* Immediate: .immediate */ \
FETCH_OP(COMM, none), /* Current comm */ \
FETCH_OP(CURRENT, none), /* Current task_struct address */\
FETCH_OP(ARG, param), /* Argument: .param = index */ \
FETCH_OP(FOFFS, imm), /* File offset: .immediate */ \
FETCH_OP(IMMSTR, string), /* Allocated string: .data */ \
FETCH_OP(EDATA, offset), /* Entry data: .offset */ \
FETCH_OP(TP_ARG, tp_arg), /* Tracepoint argument: .data */\
/* Stage 2 (dereference) ops */ \
FETCH_OP(DEREF, offset), /* Dereference: .offset */ \
FETCH_OP(UDEREF, offset), /* User-space dereference: .offset */\
FETCH_OP(CPU_PTR, none), /* Per-CPU pointer: .offset */ \
/* Stage 3 (store) ops */ \
FETCH_OP(ST_RAW, store), /* Raw value: .size */ \
FETCH_OP(ST_MEM, store), /* Memory: .offset, .size */ \
FETCH_OP(ST_UMEM, store), /* User memory: .offset, .size */\
FETCH_OP(ST_STRING, store), /* String: .offset, .size */ \
FETCH_OP(ST_USTRING, store), /* User string: .offset, .size */\
FETCH_OP(ST_SYMSTR, store), /* Symbol name: .offset, .size */\
FETCH_OP(ST_EDATA, offset), /* Entry data: .offset */ \
/* Stage 4 (modify) op */ \
FETCH_OP(MOD_BF, bf), /* Bitfield: .basesize, .lshift, .rshift*/\
/* Stage 5 (loop) op */ \
FETCH_OP(LP_ARRAY, param), /* Loop array: .param = count */\
/* End */ \
FETCH_OP(END, none), \
/* Unresolved Symbol holder */ \
FETCH_OP(NOP_SYMBOL, symbol), /* Non loaded symbol: .data = symbol name */\
}
#define FETCH_OP(opname, decode_fn) FETCH_OP_##opname
enum fetch_op FETCH_OP_LIST;
#undef FETCH_OP
#define FETCH_NOP_SYMBOL FETCH_OP_NOP_SYMBOL
struct fetch_insn {
enum fetch_op op;
union {
unsigned int param;
struct {
unsigned int size;
int offset;
};
struct {
unsigned char basesize;
unsigned char lshift;
unsigned char rshift;
};
unsigned long immediate;
void *data;
};
};
/* fetch + deref*N + store + mod + end <= 16, this allows N=12, enough */
#define FETCH_INSN_MAX 16
#define FETCH_TOKEN_COMM (-ECOMM)
/* Fetch type information table */
struct fetch_type {
const char *name; /* Name of type */
size_t size; /* Byte size of type */
bool is_signed; /* Signed flag */
bool is_string; /* String flag */
print_type_func_t print; /* Print functions */
const char *fmt; /* Format string */
const char *fmttype; /* Name in format file */
};
/* For defining macros, define string/string_size types */
typedef u32 string;
typedef u32 string_size;
#define PRINT_TYPE_FUNC_NAME(type) print_type_##type
#define PRINT_TYPE_FMT_NAME(type) print_type_format_##type
/* Printing in basic type function template */
#define DECLARE_BASIC_PRINT_TYPE_FUNC(type) \
int PRINT_TYPE_FUNC_NAME(type)(struct trace_seq *s, void *data, void *ent);\
extern const char PRINT_TYPE_FMT_NAME(type)[]
DECLARE_BASIC_PRINT_TYPE_FUNC(u8);
DECLARE_BASIC_PRINT_TYPE_FUNC(u16);
DECLARE_BASIC_PRINT_TYPE_FUNC(u32);
DECLARE_BASIC_PRINT_TYPE_FUNC(u64);
DECLARE_BASIC_PRINT_TYPE_FUNC(s8);
DECLARE_BASIC_PRINT_TYPE_FUNC(s16);
DECLARE_BASIC_PRINT_TYPE_FUNC(s32);
DECLARE_BASIC_PRINT_TYPE_FUNC(s64);
DECLARE_BASIC_PRINT_TYPE_FUNC(x8);
DECLARE_BASIC_PRINT_TYPE_FUNC(x16);
DECLARE_BASIC_PRINT_TYPE_FUNC(x32);
DECLARE_BASIC_PRINT_TYPE_FUNC(x64);
DECLARE_BASIC_PRINT_TYPE_FUNC(char);
DECLARE_BASIC_PRINT_TYPE_FUNC(string);
DECLARE_BASIC_PRINT_TYPE_FUNC(symbol);
/* Default (unsigned long) fetch type */
#define __DEFAULT_FETCH_TYPE(t) x##t
#define _DEFAULT_FETCH_TYPE(t) __DEFAULT_FETCH_TYPE(t)
#define DEFAULT_FETCH_TYPE _DEFAULT_FETCH_TYPE(BITS_PER_LONG)
#define DEFAULT_FETCH_TYPE_STR __stringify(DEFAULT_FETCH_TYPE)
#define __ADDR_FETCH_TYPE(t) u##t
#define _ADDR_FETCH_TYPE(t) __ADDR_FETCH_TYPE(t)
#define ADDR_FETCH_TYPE _ADDR_FETCH_TYPE(BITS_PER_LONG)
#define __ASSIGN_FETCH_TYPE(_name, ptype, ftype, _size, sign, str, _fmttype) \
{.name = _name, \
.size = _size, \
.is_signed = (bool)sign, \
.is_string = (bool)str, \
.print = PRINT_TYPE_FUNC_NAME(ptype), \
.fmt = PRINT_TYPE_FMT_NAME(ptype), \
.fmttype = _fmttype, \
}
/* Non string types can use these macros */
#define _ASSIGN_FETCH_TYPE(_name, ptype, ftype, _size, sign, _fmttype) \
__ASSIGN_FETCH_TYPE(_name, ptype, ftype, _size, sign, 0, #_fmttype)
#define ASSIGN_FETCH_TYPE(ptype, ftype, sign) \
_ASSIGN_FETCH_TYPE(#ptype, ptype, ftype, sizeof(ftype), sign, ptype)
/* If ptype is an alias of atype, use this macro (show atype in format) */
#define ASSIGN_FETCH_TYPE_ALIAS(ptype, atype, ftype, sign) \
_ASSIGN_FETCH_TYPE(#ptype, ptype, ftype, sizeof(ftype), sign, atype)
#define ASSIGN_FETCH_TYPE_END {}
#define MAX_ARRAY_LEN 64
#ifdef CONFIG_KPROBE_EVENTS
bool trace_kprobe_on_func_entry(struct trace_event_call *call);
bool trace_kprobe_error_injectable(struct trace_event_call *call);
#else
static inline bool trace_kprobe_on_func_entry(struct trace_event_call *call)
{
return false;
}
static inline bool trace_kprobe_error_injectable(struct trace_event_call *call)
{
return false;
}
#endif /* CONFIG_KPROBE_EVENTS */
struct probe_arg {
struct fetch_insn *code;
bool dynamic;/* Dynamic array (string) is used */
unsigned int offset; /* Offset from argument entry */
unsigned int count; /* Array count */
const char *name; /* Name of this argument */
const char *comm; /* Command of this argument */
char *fmt; /* Format string if needed */
const struct fetch_type *type; /* Type of this argument */
};
struct probe_entry_arg {
unsigned int size; /* The entry data size */
struct fetch_insn code[] __counted_by(size);
};
struct trace_uprobe_filter {
rwlock_t rwlock;
int nr_systemwide;
struct list_head perf_events;
};
/* Event call and class holder */
struct trace_probe_event {
unsigned int flags; /* For TP_FLAG_* */
struct trace_event_class class;
struct trace_event_call call;
struct list_head files;
struct list_head probes;
struct trace_uprobe_filter filter[];
};
struct trace_probe {
struct list_head list;
struct trace_probe_event *event;
ssize_t size; /* trace entry size */
unsigned int nr_args;
struct probe_entry_arg *entry_arg; /* This is only for return probe */
struct probe_arg args[];
};
struct event_file_link {
struct trace_event_file *file;
struct list_head list;
};
static inline unsigned int trace_probe_load_flag(struct trace_probe *tp)
{
return smp_load_acquire(&tp->event->flags);
}
static inline bool trace_probe_test_flag(struct trace_probe *tp,
unsigned int flag)
{
return !!(trace_probe_load_flag(tp) & flag);
}
static inline void trace_probe_set_flag(struct trace_probe *tp,
unsigned int flag)
{
smp_store_release(&tp->event->flags, tp->event->flags | flag);
}
static inline void trace_probe_clear_flag(struct trace_probe *tp,
unsigned int flag)
{
tp->event->flags &= ~flag;
}
static inline bool trace_probe_is_enabled(struct trace_probe *tp)
{
return trace_probe_test_flag(tp, TP_FLAG_TRACE | TP_FLAG_PROFILE);
}
static inline const char *trace_probe_name(struct trace_probe *tp)
{
return trace_event_name(&tp->event->call);
}
static inline const char *trace_probe_group_name(struct trace_probe *tp)
{
return tp->event->call.class->system;
}
static inline struct trace_event_call *
trace_probe_event_call(struct trace_probe *tp)
{
return &tp->event->call;
}
static inline struct trace_probe_event *
trace_probe_event_from_call(struct trace_event_call *event_call)
{
return container_of(event_call, struct trace_probe_event, call);
}
static inline struct trace_probe *
trace_probe_primary_from_call(struct trace_event_call *call)
{
struct trace_probe_event *tpe = trace_probe_event_from_call(call);
return list_first_entry_or_null(&tpe->probes, struct trace_probe, list);
}
static inline struct list_head *trace_probe_probe_list(struct trace_probe *tp)
{
return &tp->event->probes;
}
static inline bool trace_probe_has_sibling(struct trace_probe *tp)
{
struct list_head *list = trace_probe_probe_list(tp);
return !list_empty(list) && !list_is_singular(list);
}
static inline int trace_probe_unregister_event_call(struct trace_probe *tp)
{
/* tp->event is unregistered in trace_remove_event_call() */
return trace_remove_event_call(&tp->event->call);
}
static inline bool trace_probe_has_single_file(struct trace_probe *tp)
{
return !!list_is_singular(&tp->event->files);
}
int trace_probe_init(struct trace_probe *tp, const char *event,
const char *group, bool alloc_filter, int nargs);
void trace_probe_cleanup(struct trace_probe *tp);
int trace_probe_append(struct trace_probe *tp, struct trace_probe *to);
void trace_probe_unlink(struct trace_probe *tp);
int trace_probe_register_event_call(struct trace_probe *tp);
int trace_probe_add_file(struct trace_probe *tp, struct trace_event_file *file);
int trace_probe_remove_file(struct trace_probe *tp,
struct trace_event_file *file);
struct event_file_link *trace_probe_get_file_link(struct trace_probe *tp,
struct trace_event_file *file);
int trace_probe_compare_arg_type(struct trace_probe *a, struct trace_probe *b);
bool trace_probe_match_command_args(struct trace_probe *tp,
int argc, const char **argv);
int trace_probe_create(const char *raw_command, int (*createfn)(int, const char **));
int trace_probe_print_args(struct trace_seq *s, struct probe_arg *args, int nr_args,
u8 *data, void *field);
#ifdef CONFIG_PROBE_EVENTS_DUMP_FETCHARG
void trace_probe_dump_args(struct seq_file *m, struct trace_probe *tp);
#else
static inline void trace_probe_dump_args(struct seq_file *m, struct trace_probe *tp)
{
}
#endif
#ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API
int traceprobe_get_entry_data_size(struct trace_probe *tp);
/* This is a runtime function to store entry data */
void store_trace_entry_data(void *edata, struct trace_probe *tp, struct pt_regs *regs);
#else /* !CONFIG_HAVE_FUNCTION_ARG_ACCESS_API */
static inline int traceprobe_get_entry_data_size(struct trace_probe *tp)
{
return 0;
}
#define store_trace_entry_data(edata, tp, regs) do { } while (0)
#endif
#define trace_probe_for_each_link(pos, tp) \
list_for_each_entry(pos, &(tp)->event->files, list)
#define trace_probe_for_each_link_rcu(pos, tp) \
list_for_each_entry_rcu(pos, &(tp)->event->files, list)
/*
* The flags used for parsing trace_probe arguments.
* TPARG_FL_RETURN, TPARG_FL_FENTRY and TPARG_FL_TEVENT are mutually exclusive.
* TPARG_FL_KERNEL and TPARG_FL_USER are also mutually exclusive.
* TPARG_FL_FPROBE and TPARG_FL_TPOINT are optional but it should be with
* TPARG_FL_KERNEL.
*/
#define TPARG_FL_RETURN BIT(0)
#define TPARG_FL_KERNEL BIT(1)
#define TPARG_FL_FENTRY BIT(2)
#define TPARG_FL_TEVENT BIT(3)
#define TPARG_FL_USER BIT(4)
#define TPARG_FL_FPROBE BIT(5)
#define TPARG_FL_TPOINT BIT(6)
#define TPARG_FL_LOC_MASK GENMASK(4, 0)
static inline bool tparg_is_function_entry(unsigned int flags)
{
return (flags & TPARG_FL_LOC_MASK) == (TPARG_FL_KERNEL | TPARG_FL_FENTRY);
}
static inline bool tparg_is_function_return(unsigned int flags)
{
return (flags & TPARG_FL_LOC_MASK) == (TPARG_FL_KERNEL | TPARG_FL_RETURN);
}
static inline bool tparg_is_event_probe(unsigned int flags)
{
return !!(flags & TPARG_FL_TEVENT);
}
/* Each typecast consumes nested level. So the max number of typecast is 8. */
#define TRACEPROBE_MAX_NESTED_LEVEL 8
enum parse_state_type {
STATE_DEREF,
STATE_TYPECAST,
};
struct parse_state {
int type;
union {
struct {
int deref;
long offset;
int cur_offs;
char *inner_arg;
bool is_cpu_read;
} deref;
struct {
char *casttype;
char *fieldname;
int orig_offset;
int field_offset_diff;
char *inner_arg;
} typecast;
};
};
struct traceprobe_parse_context {
struct trace_event_call *event;
/* BTF related parameters */
const char *funcname; /* Function name in BTF */
const struct btf_type *proto; /* Prototype of the function */
const struct btf_param *params; /* Parameter of the function */
s32 nr_params; /* The number of the parameters */
struct btf *btf; /* The BTF to be used */
struct btf *struct_btf; /* The BTF to be used for structs */
const struct btf_type *last_type; /* Saved type */
const struct btf_type *last_struct; /* Saved structure */
u32 last_bitoffs; /* Saved bitoffs */
u32 last_bitsize; /* Saved bitsize */
struct trace_probe *tp;
unsigned int flags;
int offset;
int prefix_byteoffs; /* The byte offset of the prefix field of typecast */
struct parse_state stack[TRACEPROBE_MAX_NESTED_LEVEL + 1];
int depth;
};
extern int traceprobe_parse_probe_arg(struct trace_probe *tp, int i,
const char *argv,
struct traceprobe_parse_context *ctx);
const char **traceprobe_expand_meta_args(int argc, const char *argv[],
int *new_argc, char *buf, int bufsize,
struct traceprobe_parse_context *ctx);
extern int traceprobe_expand_dentry_args(int argc, const char *argv[], char **buf);
extern int traceprobe_update_arg(struct probe_arg *arg);
extern void traceprobe_free_probe_arg(struct probe_arg *arg);
/*
* If either traceprobe_parse_probe_arg() or traceprobe_expand_meta_args() is called,
* this MUST be called for clean up the context and return a resource.
*/
void traceprobe_finish_parse(struct traceprobe_parse_context *ctx);
static inline void traceprobe_free_parse_ctx(struct traceprobe_parse_context *ctx)
{
traceprobe_finish_parse(ctx);
kfree(ctx);
}
DEFINE_FREE(traceprobe_parse_context, struct traceprobe_parse_context *,
if (_T) traceprobe_free_parse_ctx(_T))
extern int traceprobe_split_symbol_offset(char *symbol, long *offset);
int traceprobe_parse_event_name(const char **pevent, const char **pgroup,
char *buf, int offset);
enum probe_print_type {
PROBE_PRINT_NORMAL,
PROBE_PRINT_RETURN,
PROBE_PRINT_EVENT,
};
extern int traceprobe_set_print_fmt(struct trace_probe *tp, enum probe_print_type ptype);
#ifdef CONFIG_PERF_EVENTS
extern struct trace_event_call *
create_local_trace_kprobe(char *func, void *addr, unsigned long offs,
bool is_return);
extern void destroy_local_trace_kprobe(struct trace_event_call *event_call);
extern struct trace_event_call *
create_local_trace_uprobe(char *name, unsigned long offs,
unsigned long ref_ctr_offset, bool is_return);
extern void destroy_local_trace_uprobe(struct trace_event_call *event_call);
#endif
extern int traceprobe_define_arg_fields(struct trace_event_call *event_call,
size_t offset, struct trace_probe *tp);
#undef ERRORS
#define ERRORS \
C(ARGIDX_2BIG, "$argN index is too big"), \
C(ARGS_2LONG, "$arg* failed because the argument list is too long"), \
C(ARG_NAME_TOO_LONG, "Argument name is too long"), \
C(ARG_TOO_LONG, "Argument expression is too long"), \
C(ARRAY_NO_CLOSE, "Array is not closed"), \
C(ARRAY_TOO_BIG, "Array number is too big"), \
C(BAD_ADDR_SUFFIX, "Invalid probed address suffix"), \
C(BAD_ARG_NAME, "Argument name must follow the same rules as C identifiers"), \
C(BAD_ARG_NUM, "Invalid argument number"), \
C(BAD_ARRAY_NUM, "Invalid array size"), \
C(BAD_ARRAY_SUFFIX, "Array has wrong suffix"), \
C(BAD_ATTACH_ARG, "Attached event does not have this field"), \
C(BAD_ATTACH_EVENT, "Attached event does not exist"), \
C(BAD_BITFIELD, "Invalid bitfield"), \
C(BAD_BTF_TID, "Failed to get BTF type info."), \
C(BAD_DEREF_OFFS, "Invalid dereference offset"), \
C(BAD_EVENT_NAME, "Event name must follow the same rules as C identifiers"), \
C(BAD_FETCH_ARG, "Invalid fetch argument"), \
C(BAD_FILE_OFFS, "Invalid file offset value"), \
C(BAD_GROUP_NAME, "Group name must follow the same rules as C identifiers"), \
C(BAD_HYPHEN, "Failed to parse single hyphen. Forgot '>'?"), \
C(BAD_IMM, "Invalid immediate value"), \
C(BAD_INSN_BNDRY, "Probe point is not an instruction boundary"), \
C(BAD_MAXACT, "Invalid maxactive number"), \
C(BAD_MAXACT_TYPE, "Maxactive is only for function exit"), \
C(BAD_MEM_ADDR, "Invalid memory address"), \
C(BAD_PROBE_ADDR, "Invalid probed address or symbol"), \
C(BAD_REFCNT, "Invalid reference counter offset"), \
C(BAD_REFCNT_SUFFIX, "Reference counter has wrong suffix"), \
C(BAD_REG_NAME, "Invalid register name"), \
C(BAD_RETPROBE, "Retprobe address must be an function entry"), \
C(BAD_STACK_NUM, "Invalid stack number"), \
C(BAD_STRING, "String accepts only memory argument"), \
C(BAD_SYMSTRING, "Symbol String doesn't accept data/userdata"), \
C(BAD_TP_NAME, "Invalid character in tracepoint name"), \
C(BAD_TYPE, "Unknown type is specified"), \
C(BAD_TYPE4STR, "This type does not fit for string."), \
C(BAD_UPROBE_OFFS, "Invalid uprobe offset"), \
C(BAD_VAR, "Invalid $-variable specified"), \
C(BAD_VAR_ARGS, "$arg* must be an independent parameter without name etc."), \
C(COMM_CANT_DEREF, "$comm can not be dereferenced"), \
C(DEREF_NEED_BRACE, "Dereference needs a brace"), \
C(DEREF_OPEN_BRACE, "Dereference brace is not closed"), \
C(DIFF_ARG_TYPE, "Argument type or name is different from existing probe"), \
C(DIFF_PROBE_TYPE, "Probe type is different from existing probe"), \
C(DOUBLE_ARGS, "$arg* can be used only once in the parameters"), \
C(EVENT_EXIST, "Given group/event name is already used by another event"), \
C(EVENT_TOO_BIG, "Event too big (too many fields?)"), \
C(EVENT_TOO_LONG, "Event name is too long"), \
C(FAIL_REG_PROBE, "Failed to register probe event"), \
C(FILE_NOT_FOUND, "Failed to find the given file"), \
C(FILE_ON_KPROBE, "File offset is not available for kernel probes"), \
C(GROUP_TOO_LONG, "Group name is too long"), \
C(IMMSTR_NO_CLOSE, "String is not closed with '\"'"), \
C(MAXACT_TOO_BIG, "Maxactive is too big"), \
C(NEED_STRING_TYPE, "$comm and immediate-string only accepts string type"), \
C(NOFENTRY_ARGS, "$arg* can be used only on function entry or exit"), \
C(NON_UNIQ_SYMBOL, "The symbol is not unique"), \
C(NOSUP_BTFARG, "BTF is not available or not supported"), \
C(NOSUP_DAT_ARG, "Non pointer structure/union argument is not supported."), \
C(NOSUP_PERCPU, "Per-cpu variable access is only for kernel probes"), \
C(NO_ARG_BODY, "No argument expression"), \
C(NO_ARG_NAME, "Argument name is not specified"), \
C(NO_BTFARG, "This variable is not found at this probe point"), \
C(NO_BTF_ENTRY, "No BTF entry for this probe point"), \
C(NO_BTF_FIELD, "This field is not found."), \
C(NO_EP_FILTER, "No filter rule after 'if'"), \
C(NO_EVENT_FIELD, "This event field is not found."), \
C(NO_EVENT_INFO, "This requires both group and event name to attach"), \
C(NO_EVENT_NAME, "Event name is not specified"), \
C(NO_GROUP_NAME, "Group name is not specified"), \
C(NO_PTR_STRCT, "This is not a pointer to union/structure."), \
C(NO_REGULAR_FILE, "Not a regular file"), \
C(NO_RETVAL, "This function returns 'void' type"), \
C(NO_TRACEPOINT, "Tracepoint is not found"), \
C(REFCNT_OPEN_BRACE, "Reference counter brace is not closed"), \
C(RETVAL_ON_PROBE, "$retval is not available on probe"), \
C(SAME_PROBE, "There is already the exact same probe event"), \
C(SYM_ON_UPROBE, "Symbol is not available with uprobe"), \
C(TOO_MANY_ARGS, "Too many arguments are specified"), \
C(TOO_MANY_EARGS, "Too many entry arguments specified"), \
C(TOO_MANY_NESTED, "Too many nested typecasts/dereferences"), \
C(TOO_MANY_OPS, "Dereference is too much nested"), \
C(TYPECAST_BAD_ARROW, "Typecast field option does not support -> operator"), \
C(TYPECAST_NOT_ALIGNED, "Typecast field option is not byte-aligned"), \
C(TYPECAST_NOT_EVENT, "Typecasts are only for eprobe fields"), \
C(TYPECAST_REQ_FIELD, "Typecast requires a field access"), \
C(TYPECAST_SYM_OFFSET, "@SYM+/-OFFSET with typecast needs parentheses"), \
C(USED_ARG_NAME, "This argument name is already used"),
#undef C
#define C(a, b) TP_ERR_##a
/* Define TP_ERR_ */
enum { ERRORS };
/* Error text is defined in trace_probe.c */
struct trace_probe_log {
const char *subsystem;
const char **argv;
int argc;
int index;
};
const char *trace_probe_log_init(const char *subsystem, int argc, const char **argv);
void trace_probe_log_set_index(int index);
void trace_probe_log_clear(void);
void __trace_probe_log_err(int offset, int err);
DEFINE_FREE(trace_probe_log_clear, const char *, if (_T) trace_probe_log_clear())
#define trace_probe_log_err(offs, err) \
__trace_probe_log_err(offs, TP_ERR_##err)
struct uprobe_dispatch_data {
struct trace_uprobe *tu;
unsigned long bp_addr;
};