// SPDX-License-Identifier: GPL-2.0/* * Common code for probe-based Dynamic events. * * This code was copied from kernel/trace/trace_kprobe.c written by * Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> * * Updates to make this generic: * Copyright (C) IBM Corporation, 2010-2011 * Author: Srikar Dronamraju */#define pr_fmt(fmt) "trace_probe: " fmt #include <linux/bpf.h>#include <linux/fs.h> #include "trace_btf.h"#include "trace_probe.h" #undef C#define C(a, b) b static const char *trace_probe_err_text[] = { ERRORS }; static const char *reserved_field_names[] = { "common_type", "common_flags", "common_preempt_count", "common_pid", "common_tgid", FIELD_STRING_IP, FIELD_STRING_RETIP, FIELD_STRING_FUNC,}; /* Printing in basic type function template */#define DEFINE_BASIC_PRINT_TYPE_FUNC(tname, type, fmt) \int PRINT_TYPE_FUNC_NAME(tname)(struct trace_seq *s, void *data, void *ent)\{ \ trace_seq_printf(s, fmt, *(type *)data); \ return !trace_seq_has_overflowed(s); \} \const char PRINT_TYPE_FMT_NAME(tname)[] = fmt; DEFINE_BASIC_PRINT_TYPE_FUNC(u8, u8, "%u")DEFINE_BASIC_PRINT_TYPE_FUNC(u16, u16, "%u")DEFINE_BASIC_PRINT_TYPE_FUNC(u32, u32, "%u")DEFINE_BASIC_PRINT_TYPE_FUNC(u64, u64, "%Lu")DEFINE_BASIC_PRINT_TYPE_FUNC(s8, s8, "%d")DEFINE_BASIC_PRINT_TYPE_FUNC(s16, s16, "%d")DEFINE_BASIC_PRINT_TYPE_FUNC(s32, s32, "%d")DEFINE_BASIC_PRINT_TYPE_FUNC(s64, s64, "%Ld")DEFINE_BASIC_PRINT_TYPE_FUNC(x8, u8, "0x%x")DEFINE_BASIC_PRINT_TYPE_FUNC(x16, u16, "0x%x")DEFINE_BASIC_PRINT_TYPE_FUNC(x32, u32, "0x%x")DEFINE_BASIC_PRINT_TYPE_FUNC(x64, u64, "0x%Lx")DEFINE_BASIC_PRINT_TYPE_FUNC(char, u8, "'%c'") int PRINT_TYPE_FUNC_NAME(symbol)(struct trace_seq *s, void *data, void *ent){ trace_seq_printf(s, "%pS", (void *)*(unsigned long *)data); return !trace_seq_has_overflowed(s);}const char PRINT_TYPE_FMT_NAME(symbol)[] = "%pS"; /* Print type function for string type */int PRINT_TYPE_FUNC_NAME(string)(struct trace_seq *s, void *data, void *ent){ int len = *(u32 *)data >> 16; if (!len) trace_seq_puts(s, FAULT_STRING); else trace_seq_printf(s, "\"%s\"", (const char *)get_loc_data(data, ent)); return !trace_seq_has_overflowed(s);} const char PRINT_TYPE_FMT_NAME(string)[] = "\\\"%s\\\""; /* Fetch type information table */static const struct fetch_type probe_fetch_types[] = { /* Special types */ __ASSIGN_FETCH_TYPE("string", string, string, sizeof(u32), 1, 1, "__data_loc char[]"), __ASSIGN_FETCH_TYPE("ustring", string, string, sizeof(u32), 1, 1, "__data_loc char[]"), __ASSIGN_FETCH_TYPE("symstr", string, string, sizeof(u32), 1, 1, "__data_loc char[]"), /* Basic types */ ASSIGN_FETCH_TYPE(u8, u8, 0), ASSIGN_FETCH_TYPE(u16, u16, 0), ASSIGN_FETCH_TYPE(u32, u32, 0), ASSIGN_FETCH_TYPE(u64, u64, 0), ASSIGN_FETCH_TYPE(s8, u8, 1), ASSIGN_FETCH_TYPE(s16, u16, 1), ASSIGN_FETCH_TYPE(s32, u32, 1), ASSIGN_FETCH_TYPE(s64, u64, 1), ASSIGN_FETCH_TYPE_ALIAS(x8, u8, u8, 0), ASSIGN_FETCH_TYPE_ALIAS(x16, u16, u16, 0), ASSIGN_FETCH_TYPE_ALIAS(x32, u32, u32, 0), ASSIGN_FETCH_TYPE_ALIAS(x64, u64, u64, 0), ASSIGN_FETCH_TYPE_ALIAS(char, u8, u8, 0), ASSIGN_FETCH_TYPE_ALIAS(symbol, ADDR_FETCH_TYPE, ADDR_FETCH_TYPE, 0), ASSIGN_FETCH_TYPE_END}; static const struct fetch_type *find_fetch_type(const char *type, unsigned long flags){ int i; /* Reject the symbol/symstr for uprobes */ if (type && (flags & TPARG_FL_USER) && (!strcmp(type, "symbol") || !strcmp(type, "symstr"))) return NULL; if (!type) type = DEFAULT_FETCH_TYPE_STR; /* Special case: bitfield */ if (*type == 'b') { unsigned long bs; type = strchr(type, '/'); if (!type) goto fail; type++; if (kstrtoul(type, 0, &bs)) goto fail; switch (bs) { case 8: return find_fetch_type("u8", flags); case 16: return find_fetch_type("u16", flags); case 32: return find_fetch_type("u32", flags); case 64: return find_fetch_type("u64", flags); default: goto fail; } } for (i = 0; probe_fetch_types[i].name; i++) { if (strcmp(type, probe_fetch_types[i].name) == 0) return &probe_fetch_types[i]; } fail: return NULL;} static struct trace_probe_log trace_probe_log;extern struct mutex dyn_event_ops_mutex; const char *trace_probe_log_init(const char *subsystem, int argc, const char **argv){ lockdep_assert_held(&dyn_event_ops_mutex); trace_probe_log.subsystem = subsystem; trace_probe_log.argc = argc; trace_probe_log.argv = argv; trace_probe_log.index = 0; return subsystem;} void trace_probe_log_clear(void){ lockdep_assert_held(&dyn_event_ops_mutex); memset(&trace_probe_log, 0, sizeof(trace_probe_log));} void trace_probe_log_set_index(int index){ lockdep_assert_held(&dyn_event_ops_mutex); trace_probe_log.index = index;} void __trace_probe_log_err(int offset, int err_type){ char *command, *p; int i, len = 0, pos = 0; lockdep_assert_held(&dyn_event_ops_mutex); if (!trace_probe_log.argv) return; /* Recalculate the length and allocate buffer */ for (i = 0; i < trace_probe_log.argc; i++) { if (i == trace_probe_log.index) pos = len; len += strlen(trace_probe_log.argv[i]) + 1; } command = kzalloc(len, GFP_KERNEL); if (!command) return; if (trace_probe_log.index >= trace_probe_log.argc) { /** * Set the error position is next to the last arg + space. * Note that len includes the terminal null and the cursor * appears at pos + 1. */ pos = len; offset = 0; } /* And make a command string from argv array */ p = command; for (i = 0; i < trace_probe_log.argc; i++) { len = strlen(trace_probe_log.argv[i]); memcpy(p, trace_probe_log.argv[i], len); p[len] = ' '; p += len + 1; } *(p - 1) = '\0'; tracing_log_err(NULL, trace_probe_log.subsystem, command, trace_probe_err_text, err_type, pos + offset); kfree(command);} /* Split symbol and offset. */int traceprobe_split_symbol_offset(char *symbol, long *offset){ char *tmp; int ret; if (!offset) return -EINVAL; tmp = strpbrk(symbol, "+-"); if (tmp) { ret = kstrtol(tmp, 0, offset); if (ret) return ret; *tmp = '\0'; } else *offset = 0; return 0;} /** * traceprobe_parse_event_name() - Parse a string into group and event names * @pevent: A pointer to the string to be parsed. * @pgroup: A pointer to the group name. * @buf: A buffer to store the parsed group name. * @offset: The offset of the string in the original user command, for logging. * * This parses a string with the format `[GROUP/][EVENT]` or `[GROUP.][EVENT]` * (either GROUP or EVENT or both must be specified). * Since the parsed group name is stored in @buf, the caller must ensure @buf * is at least MAX_EVENT_NAME_LEN bytes. * * Return: 0 on success, or -EINVAL on failure. * * If success, *@pevent is updated to point to the event name part of the * original string, or NULL if there is no event name. * Also, *@pgroup is updated to point to the parsed group which is stored * in @buf, or NULL if there is no group name. */int traceprobe_parse_event_name(const char **pevent, const char **pgroup, char *buf, int offset){ const char *slash, *event = *pevent; int len; slash = strchr(event, '/'); if (!slash) slash = strchr(event, '.'); if (slash) { if (slash == event) { trace_probe_log_err(offset, NO_GROUP_NAME); return -EINVAL; } if (slash - event + 1 > MAX_EVENT_NAME_LEN) { trace_probe_log_err(offset, GROUP_TOO_LONG); return -EINVAL; } strscpy(buf, event, slash - event + 1); if (!is_good_system_name(buf)) { trace_probe_log_err(offset, BAD_GROUP_NAME); return -EINVAL; } *pgroup = buf; *pevent = slash + 1; offset += slash - event + 1; event = *pevent; } len = strlen(event); if (len == 0) { if (slash) { *pevent = NULL; return 0; } trace_probe_log_err(offset, NO_EVENT_NAME); return -EINVAL; } else if (len >= MAX_EVENT_NAME_LEN) { trace_probe_log_err(offset, EVENT_TOO_LONG); return -EINVAL; } if (!is_good_name(event)) { trace_probe_log_err(offset, BAD_EVENT_NAME); return -EINVAL; } return 0;} static int parse_trace_event_arg(char *arg, struct fetch_insn *code, struct traceprobe_parse_context *ctx){ struct ftrace_event_field *field; struct list_head *head; head = trace_get_fields(ctx->event); list_for_each_entry(field, head, link) { if (!strcmp(arg, field->name)) { code->op = FETCH_OP_TP_ARG; code->data = field; return 0; } } return -ENOENT;} #ifdef CONFIG_PROBE_EVENTS_BTF_ARGS static u32 btf_type_int(const struct btf_type *t){ return *(u32 *)(t + 1);} static bool btf_type_is_char_ptr(struct btf *btf, const struct btf_type *type){ const struct btf_type *real_type; u32 intdata; s32 tid; real_type = btf_type_skip_modifiers(btf, type->type, &tid); if (!real_type) return false; if (BTF_INFO_KIND(real_type->info) != BTF_KIND_INT) return false; intdata = btf_type_int(real_type); return !(BTF_INT_ENCODING(intdata) & BTF_INT_SIGNED) && BTF_INT_BITS(intdata) == 8;} static bool btf_type_is_char_array(struct btf *btf, const struct btf_type *type){ const struct btf_type *real_type; const struct btf_array *array; u32 intdata; s32 tid; if (BTF_INFO_KIND(type->info) != BTF_KIND_ARRAY) return false; array = (const struct btf_array *)(type + 1); real_type = btf_type_skip_modifiers(btf, array->type, &tid); intdata = btf_type_int(real_type); return !(BTF_INT_ENCODING(intdata) & BTF_INT_SIGNED) && BTF_INT_BITS(intdata) == 8;} static int check_prepare_btf_string_fetch(char *typename, struct fetch_insn **pcode, struct traceprobe_parse_context *ctx){ struct btf *btf = ctx->btf; if (!btf || !ctx->last_type) return 0; /* char [] does not need any change. */ if (btf_type_is_char_array(btf, ctx->last_type)) return 0; /* char * requires dereference the pointer. */ if (btf_type_is_char_ptr(btf, ctx->last_type)) { struct fetch_insn *code = *pcode + 1; if (code->op == FETCH_OP_END) { trace_probe_log_err(ctx->offset, TOO_MANY_OPS); return -E2BIG; } if (typename[0] == 'u') code->op = FETCH_OP_UDEREF; else code->op = FETCH_OP_DEREF; code->offset = 0; *pcode = code; return 0; } /* Other types are not available for string */ trace_probe_log_err(ctx->offset, BAD_TYPE4STR); return -EINVAL;} static const char *fetch_type_from_btf_type(struct btf *btf, const struct btf_type *type, struct traceprobe_parse_context *ctx){ u32 intdata; /* TODO: const char * could be converted as a string */ switch (BTF_INFO_KIND(type->info)) { case BTF_KIND_ENUM: /* enum is "int", so convert to "s32" */ return "s32"; case BTF_KIND_ENUM64: return "s64"; case BTF_KIND_PTR: /* pointer will be converted to "x??" */ if (IS_ENABLED(CONFIG_64BIT)) return "x64"; else return "x32"; case BTF_KIND_INT: intdata = btf_type_int(type); if (BTF_INT_ENCODING(intdata) & BTF_INT_SIGNED) { switch (BTF_INT_BITS(intdata)) { case 8: return "s8"; case 16: return "s16"; case 32: return "s32"; case 64: return "s64"; } } else { /* unsigned */ switch (BTF_INT_BITS(intdata)) { case 8: return "u8"; case 16: return "u16"; case 32: return "u32"; case 64: return "u64"; } /* bitfield, size is encoded in the type */ ctx->last_bitsize = BTF_INT_BITS(intdata); ctx->last_bitoffs += BTF_INT_OFFSET(intdata); return "u64"; } } /* TODO: support other types */ return NULL;} static int query_btf_context(struct traceprobe_parse_context *ctx){ const struct btf_param *param; const struct btf_type *type; struct btf *btf; s32 nr; if (ctx->btf) return 0; if (!ctx->funcname) return -EINVAL; type = btf_find_func_proto(ctx->funcname, &btf); if (!type) return -ENOENT; ctx->btf = btf; ctx->proto = type; /* ctx->params is optional, since func(void) will not have params. */ nr = 0; param = btf_get_func_param(type, &nr); if (!IS_ERR_OR_NULL(param)) { /* Hide the first 'data' argument of tracepoint */ if (ctx->flags & TPARG_FL_TPOINT) { nr--; param++; } } if (nr > 0) { ctx->nr_params = nr; ctx->params = param;Lines 1–500 of 2279. Load more
// SPDX-License-Identifier: GPL-2.0 /* * Common code for probe-based Dynamic events. * * This code was copied from kernel/trace/trace_kprobe.c written by * Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> * * Updates to make this generic: * Copyright (C) IBM Corporation, 2010-2011 * Author: Srikar Dronamraju */ #define pr_fmt(fmt) "trace_probe: " fmt #include <linux/bpf.h> #include <linux/fs.h> #include "trace_btf.h" #include "trace_probe.h" #undef C #define C(a, b) b static const char *trace_probe_err_text[] = { ERRORS }; static const char *reserved_field_names[] = { "common_type", "common_flags", "common_preempt_count", "common_pid", "common_tgid", FIELD_STRING_IP, FIELD_STRING_RETIP, FIELD_STRING_FUNC, }; /* Printing in basic type function template */ #define DEFINE_BASIC_PRINT_TYPE_FUNC(tname, type, fmt) \ int PRINT_TYPE_FUNC_NAME(tname)(struct trace_seq *s, void *data, void *ent)\ { \ trace_seq_printf(s, fmt, *(type *)data); \ return !trace_seq_has_overflowed(s); \ } \ const char PRINT_TYPE_FMT_NAME(tname)[] = fmt; DEFINE_BASIC_PRINT_TYPE_FUNC(u8, u8, "%u") DEFINE_BASIC_PRINT_TYPE_FUNC(u16, u16, "%u") DEFINE_BASIC_PRINT_TYPE_FUNC(u32, u32, "%u") DEFINE_BASIC_PRINT_TYPE_FUNC(u64, u64, "%Lu") DEFINE_BASIC_PRINT_TYPE_FUNC(s8, s8, "%d") DEFINE_BASIC_PRINT_TYPE_FUNC(s16, s16, "%d") DEFINE_BASIC_PRINT_TYPE_FUNC(s32, s32, "%d") DEFINE_BASIC_PRINT_TYPE_FUNC(s64, s64, "%Ld") DEFINE_BASIC_PRINT_TYPE_FUNC(x8, u8, "0x%x") DEFINE_BASIC_PRINT_TYPE_FUNC(x16, u16, "0x%x") DEFINE_BASIC_PRINT_TYPE_FUNC(x32, u32, "0x%x") DEFINE_BASIC_PRINT_TYPE_FUNC(x64, u64, "0x%Lx") DEFINE_BASIC_PRINT_TYPE_FUNC(char, u8, "'%c'") int PRINT_TYPE_FUNC_NAME(symbol)(struct trace_seq *s, void *data, void *ent) { trace_seq_printf(s, "%pS", (void *)*(unsigned long *)data); return !trace_seq_has_overflowed(s); } const char PRINT_TYPE_FMT_NAME(symbol)[] = "%pS"; /* Print type function for string type */ int PRINT_TYPE_FUNC_NAME(string)(struct trace_seq *s, void *data, void *ent) { int len = *(u32 *)data >> 16; if (!len) trace_seq_puts(s, FAULT_STRING); else trace_seq_printf(s, "\"%s\"", (const char *)get_loc_data(data, ent)); return !trace_seq_has_overflowed(s); } const char PRINT_TYPE_FMT_NAME(string)[] = "\\\"%s\\\""; /* Fetch type information table */ static const struct fetch_type probe_fetch_types[] = { /* Special types */ __ASSIGN_FETCH_TYPE("string", string, string, sizeof(u32), 1, 1, "__data_loc char[]"), __ASSIGN_FETCH_TYPE("ustring", string, string, sizeof(u32), 1, 1, "__data_loc char[]"), __ASSIGN_FETCH_TYPE("symstr", string, string, sizeof(u32), 1, 1, "__data_loc char[]"), /* Basic types */ ASSIGN_FETCH_TYPE(u8, u8, 0), ASSIGN_FETCH_TYPE(u16, u16, 0), ASSIGN_FETCH_TYPE(u32, u32, 0), ASSIGN_FETCH_TYPE(u64, u64, 0), ASSIGN_FETCH_TYPE(s8, u8, 1), ASSIGN_FETCH_TYPE(s16, u16, 1), ASSIGN_FETCH_TYPE(s32, u32, 1), ASSIGN_FETCH_TYPE(s64, u64, 1), ASSIGN_FETCH_TYPE_ALIAS(x8, u8, u8, 0), ASSIGN_FETCH_TYPE_ALIAS(x16, u16, u16, 0), ASSIGN_FETCH_TYPE_ALIAS(x32, u32, u32, 0), ASSIGN_FETCH_TYPE_ALIAS(x64, u64, u64, 0), ASSIGN_FETCH_TYPE_ALIAS(char, u8, u8, 0), ASSIGN_FETCH_TYPE_ALIAS(symbol, ADDR_FETCH_TYPE, ADDR_FETCH_TYPE, 0), ASSIGN_FETCH_TYPE_END }; static const struct fetch_type *find_fetch_type(const char *type, unsigned long flags) { int i; /* Reject the symbol/symstr for uprobes */ if (type && (flags & TPARG_FL_USER) && (!strcmp(type, "symbol") || !strcmp(type, "symstr"))) return NULL; if (!type) type = DEFAULT_FETCH_TYPE_STR; /* Special case: bitfield */ if (*type == 'b') { unsigned long bs; type = strchr(type, '/'); if (!type) goto fail; type++; if (kstrtoul(type, 0, &bs)) goto fail; switch (bs) { case 8: return find_fetch_type("u8", flags); case 16: return find_fetch_type("u16", flags); case 32: return find_fetch_type("u32", flags); case 64: return find_fetch_type("u64", flags); default: goto fail; } } for (i = 0; probe_fetch_types[i].name; i++) { if (strcmp(type, probe_fetch_types[i].name) == 0) return &probe_fetch_types[i]; } fail: return NULL; } static struct trace_probe_log trace_probe_log; extern struct mutex dyn_event_ops_mutex; const char *trace_probe_log_init(const char *subsystem, int argc, const char **argv) { lockdep_assert_held(&dyn_event_ops_mutex); trace_probe_log.subsystem = subsystem; trace_probe_log.argc = argc; trace_probe_log.argv = argv; trace_probe_log.index = 0; return subsystem; } void trace_probe_log_clear(void) { lockdep_assert_held(&dyn_event_ops_mutex); memset(&trace_probe_log, 0, sizeof(trace_probe_log)); } void trace_probe_log_set_index(int index) { lockdep_assert_held(&dyn_event_ops_mutex); trace_probe_log.index = index; } void __trace_probe_log_err(int offset, int err_type) { char *command, *p; int i, len = 0, pos = 0; lockdep_assert_held(&dyn_event_ops_mutex); if (!trace_probe_log.argv) return; /* Recalculate the length and allocate buffer */ for (i = 0; i < trace_probe_log.argc; i++) { if (i == trace_probe_log.index) pos = len; len += strlen(trace_probe_log.argv[i]) + 1; } command = kzalloc(len, GFP_KERNEL); if (!command) return; if (trace_probe_log.index >= trace_probe_log.argc) { /** * Set the error position is next to the last arg + space. * Note that len includes the terminal null and the cursor * appears at pos + 1. */ pos = len; offset = 0; } /* And make a command string from argv array */ p = command; for (i = 0; i < trace_probe_log.argc; i++) { len = strlen(trace_probe_log.argv[i]); memcpy(p, trace_probe_log.argv[i], len); p[len] = ' '; p += len + 1; } *(p - 1) = '\0'; tracing_log_err(NULL, trace_probe_log.subsystem, command, trace_probe_err_text, err_type, pos + offset); kfree(command); } /* Split symbol and offset. */ int traceprobe_split_symbol_offset(char *symbol, long *offset) { char *tmp; int ret; if (!offset) return -EINVAL; tmp = strpbrk(symbol, "+-"); if (tmp) { ret = kstrtol(tmp, 0, offset); if (ret) return ret; *tmp = '\0'; } else *offset = 0; return 0; } /** * traceprobe_parse_event_name() - Parse a string into group and event names * @pevent: A pointer to the string to be parsed. * @pgroup: A pointer to the group name. * @buf: A buffer to store the parsed group name. * @offset: The offset of the string in the original user command, for logging. * * This parses a string with the format `[GROUP/][EVENT]` or `[GROUP.][EVENT]` * (either GROUP or EVENT or both must be specified). * Since the parsed group name is stored in @buf, the caller must ensure @buf * is at least MAX_EVENT_NAME_LEN bytes. * * Return: 0 on success, or -EINVAL on failure. * * If success, *@pevent is updated to point to the event name part of the * original string, or NULL if there is no event name. * Also, *@pgroup is updated to point to the parsed group which is stored * in @buf, or NULL if there is no group name. */ int traceprobe_parse_event_name(const char **pevent, const char **pgroup, char *buf, int offset) { const char *slash, *event = *pevent; int len; slash = strchr(event, '/'); if (!slash) slash = strchr(event, '.'); if (slash) { if (slash == event) { trace_probe_log_err(offset, NO_GROUP_NAME); return -EINVAL; } if (slash - event + 1 > MAX_EVENT_NAME_LEN) { trace_probe_log_err(offset, GROUP_TOO_LONG); return -EINVAL; } strscpy(buf, event, slash - event + 1); if (!is_good_system_name(buf)) { trace_probe_log_err(offset, BAD_GROUP_NAME); return -EINVAL; } *pgroup = buf; *pevent = slash + 1; offset += slash - event + 1; event = *pevent; } len = strlen(event); if (len == 0) { if (slash) { *pevent = NULL; return 0; } trace_probe_log_err(offset, NO_EVENT_NAME); return -EINVAL; } else if (len >= MAX_EVENT_NAME_LEN) { trace_probe_log_err(offset, EVENT_TOO_LONG); return -EINVAL; } if (!is_good_name(event)) { trace_probe_log_err(offset, BAD_EVENT_NAME); return -EINVAL; } return 0; } static int parse_trace_event_arg(char *arg, struct fetch_insn *code, struct traceprobe_parse_context *ctx) { struct ftrace_event_field *field; struct list_head *head; head = trace_get_fields(ctx->event); list_for_each_entry(field, head, link) { if (!strcmp(arg, field->name)) { code->op = FETCH_OP_TP_ARG; code->data = field; return 0; } } return -ENOENT; } #ifdef CONFIG_PROBE_EVENTS_BTF_ARGS static u32 btf_type_int(const struct btf_type *t) { return *(u32 *)(t + 1); } static bool btf_type_is_char_ptr(struct btf *btf, const struct btf_type *type) { const struct btf_type *real_type; u32 intdata; s32 tid; real_type = btf_type_skip_modifiers(btf, type->type, &tid); if (!real_type) return false; if (BTF_INFO_KIND(real_type->info) != BTF_KIND_INT) return false; intdata = btf_type_int(real_type); return !(BTF_INT_ENCODING(intdata) & BTF_INT_SIGNED) && BTF_INT_BITS(intdata) == 8; } static bool btf_type_is_char_array(struct btf *btf, const struct btf_type *type) { const struct btf_type *real_type; const struct btf_array *array; u32 intdata; s32 tid; if (BTF_INFO_KIND(type->info) != BTF_KIND_ARRAY) return false; array = (const struct btf_array *)(type + 1); real_type = btf_type_skip_modifiers(btf, array->type, &tid); intdata = btf_type_int(real_type); return !(BTF_INT_ENCODING(intdata) & BTF_INT_SIGNED) && BTF_INT_BITS(intdata) == 8; } static int check_prepare_btf_string_fetch(char *typename, struct fetch_insn **pcode, struct traceprobe_parse_context *ctx) { struct btf *btf = ctx->btf; if (!btf || !ctx->last_type) return 0; /* char [] does not need any change. */ if (btf_type_is_char_array(btf, ctx->last_type)) return 0; /* char * requires dereference the pointer. */ if (btf_type_is_char_ptr(btf, ctx->last_type)) { struct fetch_insn *code = *pcode + 1; if (code->op == FETCH_OP_END) { trace_probe_log_err(ctx->offset, TOO_MANY_OPS); return -E2BIG; } if (typename[0] == 'u') code->op = FETCH_OP_UDEREF; else code->op = FETCH_OP_DEREF; code->offset = 0; *pcode = code; return 0; } /* Other types are not available for string */ trace_probe_log_err(ctx->offset, BAD_TYPE4STR); return -EINVAL; } static const char *fetch_type_from_btf_type(struct btf *btf, const struct btf_type *type, struct traceprobe_parse_context *ctx) { u32 intdata; /* TODO: const char * could be converted as a string */ switch (BTF_INFO_KIND(type->info)) { case BTF_KIND_ENUM: /* enum is "int", so convert to "s32" */ return "s32"; case BTF_KIND_ENUM64: return "s64"; case BTF_KIND_PTR: /* pointer will be converted to "x??" */ if (IS_ENABLED(CONFIG_64BIT)) return "x64"; else return "x32"; case BTF_KIND_INT: intdata = btf_type_int(type); if (BTF_INT_ENCODING(intdata) & BTF_INT_SIGNED) { switch (BTF_INT_BITS(intdata)) { case 8: return "s8"; case 16: return "s16"; case 32: return "s32"; case 64: return "s64"; } } else { /* unsigned */ switch (BTF_INT_BITS(intdata)) { case 8: return "u8"; case 16: return "u16"; case 32: return "u32"; case 64: return "u64"; } /* bitfield, size is encoded in the type */ ctx->last_bitsize = BTF_INT_BITS(intdata); ctx->last_bitoffs += BTF_INT_OFFSET(intdata); return "u64"; } } /* TODO: support other types */ return NULL; } static int query_btf_context(struct traceprobe_parse_context *ctx) { const struct btf_param *param; const struct btf_type *type; struct btf *btf; s32 nr; if (ctx->btf) return 0; if (!ctx->funcname) return -EINVAL; type = btf_find_func_proto(ctx->funcname, &btf); if (!type) return -ENOENT; ctx->btf = btf; ctx->proto = type; /* ctx->params is optional, since func(void) will not have params. */ nr = 0; param = btf_get_func_param(type, &nr); if (!IS_ERR_OR_NULL(param)) { /* Hide the first 'data' argument of tracepoint */ if (ctx->flags & TPARG_FL_TPOINT) { nr--; param++; } } if (nr > 0) { ctx->nr_params = nr; ctx->params = param;