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;
/* 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]);
strcpy(p, trace_probe_log.argv[i]);
p[len] = ' ';
p += len + 1;
}
*(p - 1) = '\0';
/* 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)) { case8: return"s8"; case16: return"s16"; case32: return"s32"; case64: return"s64";
}
} else { /* unsigned */ switch (BTF_INT_BITS(intdata)) { case8: return"u8"; case16: return"u16"; case32: return"u32"; case64: 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 */
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++;
}
}
/* Return 1 if the field separater is arrow operator ('->') */ staticint split_next_field(char *varname, char **next_field, struct traceprobe_parse_context *ctx)
{ char *field; int ret = 0;
field = strpbrk(varname, ".-"); if (field) { if (field[0] == '-' && field[1] == '>') {
field[0] = '\0';
field += 2;
ret = 1;
} elseif (field[0] == '.') {
field[0] = '\0';
field += 1;
} else {
trace_probe_log_err(ctx->offset + field - varname, BAD_HYPHEN); return -EINVAL;
}
*next_field = field;
}
do { /* Outer loop for solving arrow operator ('->') */ if (BTF_INFO_KIND(type->info) != BTF_KIND_PTR) {
trace_probe_log_err(ctx->offset, NO_PTR_STRCT); return -EINVAL;
} /* Convert a struct pointer type to a struct type */
type = btf_type_skip_modifiers(ctx->btf, type->type, &tid); if (!type) {
trace_probe_log_err(ctx->offset, BAD_BTF_TID); return -EINVAL;
}
bitoffs = 0; do { /* Inner loop for solving dot operator ('.') */
next = NULL;
is_ptr = split_next_field(fieldname, &next, ctx); if (is_ptr < 0) return is_ptr;
is_ptr = split_next_field(varname, &field, ctx); if (is_ptr < 0) return is_ptr; if (!is_ptr && field) { /* dot-connected field on an argument is not supported. */
trace_probe_log_err(ctx->offset + field - varname,
NOSUP_DAT_ARG); return -EOPNOTSUPP;
}
if (ctx->flags & TPARG_FL_RETURN && !strcmp(varname, "$retval")) {
code->op = FETCH_OP_RETVAL; /* Check whether the function return type is not void */ if (query_btf_context(ctx) == 0) { if (ctx->proto->type == 0) {
trace_probe_log_err(ctx->offset, NO_RETVAL); return -ENOENT;
}
tid = ctx->proto->type; goto found;
} if (field) {
trace_probe_log_err(ctx->offset + field - varname,
NO_BTF_ENTRY); return -ENOENT;
} return0;
}
if (!ctx->btf) {
ret = query_btf_context(ctx); if (ret < 0 || ctx->nr_params == 0) {
trace_probe_log_err(ctx->offset, NO_BTF_ENTRY); return -ENOENT;
}
}
params = ctx->params;
for (i = 0; i < ctx->nr_params; i++) { constchar *name = btf_name_by_offset(ctx->btf, params[i].name_off);
if (name && !strcmp(name, varname)) { if (tparg_is_function_entry(ctx->flags)) {
code->op = FETCH_OP_ARG; if (ctx->flags & TPARG_FL_TPOINT)
code->param = i + 1; else
code->param = i;
} elseif (tparg_is_function_return(ctx->flags)) {
code->op = FETCH_OP_EDATA;
ret = __store_entry_arg(ctx->tp, i); if (ret < 0) { /* internal error */ return ret;
}
code->offset = ret;
}
tid = params[i].type; goto found;
}
}
trace_probe_log_err(ctx->offset, NO_BTFARG); return -ENOENT;
found:
type = btf_type_skip_modifiers(ctx->btf, tid, &tid); if (!type) {
trace_probe_log_err(ctx->offset, BAD_BTF_TID); return -EINVAL;
} /* Initialize the last type information */
ctx->last_type = type;
ctx->last_bitoffs = 0;
ctx->last_bitsize = 0; if (field) {
ctx->offset += field - varname; return parse_btf_field(field, type, pcode, end, ctx);
} return0;
}
/* Search the offset for the sprcified argnum. */ for (i = 0; i < earg->size - 1 && earg->code[i].op != FETCH_OP_END; i += 2) { if (WARN_ON_ONCE(earg->code[i].op != FETCH_OP_ARG)) return -EINVAL;
if (earg->code[i].param != argnum) continue;
if (WARN_ON_ONCE(earg->code[i + 1].op != FETCH_OP_ST_EDATA)) return -EINVAL;
return earg->code[i + 1].offset;
} /* Not found, append new entry if possible. */ if (i >= earg->size - 1) return -ENOSPC;
/* The last entry must have the largest offset. */ if (i != 0) { if (WARN_ON_ONCE(earg->code[i - 1].op != FETCH_OP_ST_EDATA)) return -EINVAL;
last_offset = earg->code[i - 1].offset;
}
#ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API
len = str_has_prefix(arg, "arg"); if (len) {
ret = kstrtoul(arg + len, 10, ¶m); if (ret) goto inval;
if (!param || param > PARAM_MAX_STACK) {
err = TP_ERR_BAD_ARG_NUM; goto inval;
}
param--; /* argN starts from 1, but internal arg[N] starts from 0 */
if (tparg_is_function_entry(ctx->flags)) {
code->op = FETCH_OP_ARG;
code->param = (unsignedint)param; /* *Thetracepointprobewillprobeastubfunction,andthe *firstparameterofthestubisadummyandshouldbeignored.
*/ if (ctx->flags & TPARG_FL_TPOINT)
code->param++;
} elseif (tparg_is_function_return(ctx->flags)) { /* function entry argument access from return probe */
ret = __store_entry_arg(ctx->tp, param); if (ret < 0) /* This error should be an internal error */ return ret;
/* Split type part from @arg and return it. */ staticchar *parse_probe_arg_type(char *arg, struct probe_arg *parg, struct traceprobe_parse_context *ctx)
{ char *t = NULL, *t2, *t3; int offs;
t = strchr(arg, ':'); if (t) {
*t++ = '\0';
t2 = strchr(t, '['); if (t2) {
*t2++ = '\0';
t3 = strchr(t2, ']'); if (!t3) {
offs = t2 + strlen(t2) - arg;
/* *Since$commandimmediatestringcannotbedereferenced, *wecanfindthosebystrcmp.Butignoreforeprobes.
*/ if (!(ctx->flags & TPARG_FL_TEVENT) &&
(strcmp(arg, "$comm") == 0 || strcmp(arg, "$COMM") == 0 ||
strncmp(arg, "\\\"", 2) == 0)) { /* The type of $comm must be "string", and not an array type. */ if (parg->count || (t && strcmp(t, "string"))) {
trace_probe_log_err(ctx->offset + offs, NEED_STRING_TYPE); return ERR_PTR(-EINVAL);
}
parg->type = find_fetch_type("string", ctx->flags);
} else
parg->type = find_fetch_type(t, ctx->flags);
if (!parg->type) {
trace_probe_log_err(ctx->offset + offs, BAD_TYPE); return ERR_PTR(-EINVAL);
}
return t;
}
/* After parsing, adjust the fetch_insn according to the probe_arg */ staticint finalize_fetch_insn(struct fetch_insn *code, struct probe_arg *parg, char *type, int type_offset, struct traceprobe_parse_context *ctx)
{ struct fetch_insn *scode; int ret;
/* Store operation */ if (parg->type->is_string) { /* Check bad combination of the type and the last fetch_insn. */ if (!strcmp(parg->type->name, "symstr")) { if (code->op != FETCH_OP_REG && code->op != FETCH_OP_STACK &&
code->op != FETCH_OP_RETVAL && code->op != FETCH_OP_ARG &&
code->op != FETCH_OP_DEREF && code->op != FETCH_OP_TP_ARG) {
trace_probe_log_err(ctx->offset + type_offset,
BAD_SYMSTRING); return -EINVAL;
}
} else { if (code->op != FETCH_OP_DEREF && code->op != FETCH_OP_UDEREF &&
code->op != FETCH_OP_IMM && code->op != FETCH_OP_COMM &&
code->op != FETCH_OP_DATA && code->op != FETCH_OP_TP_ARG) {
trace_probe_log_err(ctx->offset + type_offset,
BAD_STRING); return -EINVAL;
}
}
ctx->last_type = NULL;
ret = parse_probe_arg(arg, parg->type, &code, &code[FETCH_INSN_MAX - 1],
ctx); if (ret < 0) goto fail;
/* Update storing type if BTF is available */ if (IS_ENABLED(CONFIG_PROBE_EVENTS_BTF_ARGS) &&
ctx->last_type) { if (!type) {
parg->type = find_fetch_type_from_btf_type(ctx);
} elseif (strstr(type, "string")) {
ret = check_prepare_btf_string_fetch(type, &code, ctx); if (ret) goto fail;
}
}
parg->offset = *size;
*size += parg->type->size * (parg->count ?: 1);
if (parg->count) {
len = strlen(parg->type->fmttype) + 6;
parg->fmt = kmalloc(len, GFP_KERNEL); if (!parg->fmt) {
ret = -ENOMEM; goto fail;
}
snprintf(parg->fmt, len, "%s[%d]", parg->type->fmttype,
parg->count);
}
ret = finalize_fetch_insn(code, parg, type, type ? type - arg : 0, ctx); if (ret < 0) goto fail;
for (; code < tmp + FETCH_INSN_MAX; code++) if (code->op == FETCH_OP_END) break; /* Shrink down the code buffer */
parg->code = kcalloc(code - tmp + 1, sizeof(*code), GFP_KERNEL); if (!parg->code)
ret = -ENOMEM; else
memcpy(parg->code, tmp, sizeof(*code) * (code - tmp + 1));
fail: if (ret < 0) { for (code = tmp; code < tmp + FETCH_INSN_MAX; code++) if (code->op == FETCH_NOP_SYMBOL ||
code->op == FETCH_OP_DATA)
kfree(code->data);
}
kfree(tmp);
return ret;
}
/* Return 1 if name is reserved or already used by another argument */ staticint traceprobe_conflict_field_name(constchar *name, struct probe_arg *args, int narg)
{ int i;
for (i = 0; i < ARRAY_SIZE(reserved_field_names); i++) if (strcmp(reserved_field_names[i], name) == 0) return1;
for (i = 0; i < narg; i++) if (strcmp(args[i].name, name) == 0) return1;
/* *Ifargumentnameisomitted,tryargasaname(BTFvariable) *or"argN".
*/ if (IS_ENABLED(CONFIG_PROBE_EVENTS_BTF_ARGS)) {
end = strchr(arg, ':'); if (!end)
end = arg + strlen(arg);
name = kmemdup_nul(arg, end - arg, GFP_KERNEL); if (!name || !is_good_name(name)) {
kfree(name);
name = NULL;
}
}
if (!name)
name = kasprintf(GFP_KERNEL, "arg%d", idx + 1);
return name;
}
int traceprobe_parse_probe_arg(struct trace_probe *tp, int i, constchar *arg, struct traceprobe_parse_context *ctx)
{ struct probe_arg *parg = &tp->args[i]; constchar *body;
ctx->tp = tp;
body = strchr(arg, '='); if (body) { if (body - arg > MAX_ARG_NAME_LEN) {
trace_probe_log_err(0, ARG_NAME_TOO_LONG); return -EINVAL;
} elseif (body == arg) {
trace_probe_log_err(0, NO_ARG_NAME); return -EINVAL;
}
parg->name = kmemdup_nul(arg, body - arg, GFP_KERNEL);
body++;
} else {
parg->name = generate_probe_arg_name(arg, i);
body = arg;
} if (!parg->name) return -ENOMEM;
staticint sprint_nth_btf_arg(int idx, constchar *type, char *buf, int bufsize, struct traceprobe_parse_context *ctx)
{ constchar *name; int ret;
if (idx >= ctx->nr_params) {
trace_probe_log_err(0, NO_BTFARG); return -ENOENT;
}
name = btf_name_by_offset(ctx->btf, ctx->params[idx].name_off); if (!name) {
trace_probe_log_err(0, NO_BTF_ENTRY); return -ENOENT;
}
ret = snprintf(buf, bufsize, "%s%s", name, type); if (ret >= bufsize) {
trace_probe_log_err(0, ARGS_2LONG); return -E2BIG;
} return ret;
}
/* Return new_argv which must be freed after use */ constchar **traceprobe_expand_meta_args(int argc, constchar *argv[], int *new_argc, char *buf, int bufsize, struct traceprobe_parse_context *ctx)
{ conststruct btf_param *params = NULL; int i, j, n, used, ret, args_idx = -1; constchar **new_argv __free(kfree) = NULL;
ret = argv_has_var_arg(argc, argv, &args_idx, ctx); if (ret < 0) return ERR_PTR(ret);
if (!ret) {
*new_argc = argc; return NULL;
}
ret = query_btf_context(ctx); if (ret < 0 || ctx->nr_params == 0) { if (args_idx != -1) { /* $arg* requires BTF info */
trace_probe_log_err(0, NOSUP_BTFARG); return (constchar **)params;
}
*new_argc = argc; return NULL;
}
new_argv = kcalloc(*new_argc, sizeof(char *), GFP_KERNEL); if (!new_argv) return ERR_PTR(-ENOMEM);
used = 0; for (i = 0, j = 0; i < argc; i++) {
trace_probe_log_set_index(i + 2); if (i == args_idx) { for (n = 0; n < ctx->nr_params; n++) {
ret = sprint_nth_btf_arg(n, "", buf + used,
bufsize - used, ctx); if (ret < 0) return ERR_PTR(ret);
new_argv[j++] = buf + used;
used += ret + 1;
} continue;
}
if (str_has_prefix(argv[i], "$arg")) { char *type = NULL;
n = simple_strtoul(argv[i] + 4, &type, 10); if (type && !(*type == ':' || *type == '\0')) {
trace_probe_log_err(0, BAD_VAR); return ERR_PTR(-ENOENT);
} /* Note: $argN starts from $arg1 */
ret = sprint_nth_btf_arg(n - 1, type, buf + used,
bufsize - used, ctx); if (ret < 0) return ERR_PTR(ret);
new_argv[j++] = buf + used;
used += ret + 1;
} else
new_argv[j++] = argv[i];
}
return_ptr(new_argv);
}
/* @buf: *buf must be equal to NULL. Caller must to free *buf */ int traceprobe_expand_dentry_args(int argc, constchar *argv[], char **buf)
{ int i, used, ret; constint bufsize = MAX_DENTRY_ARGS_LEN; char *tmpbuf __free(kfree) = NULL;
if (*buf) return -EINVAL;
used = 0; for (i = 0; i < argc; i++) { char *tmp __free(kfree) = NULL; char *equal;
size_t arg_len;
if (!glob_match("*:%p[dD]", argv[i])) continue;
if (!tmpbuf) {
tmpbuf = kmalloc(bufsize, GFP_KERNEL); if (!tmpbuf) return -ENOMEM;
}
tmp = kstrdup(argv[i], GFP_KERNEL); if (!tmp) return -ENOMEM;
int traceprobe_update_arg(struct probe_arg *arg)
{ struct fetch_insn *code = arg->code; long offset; char *tmp; char c; int ret = 0;
while (code && code->op != FETCH_OP_END) { if (code->op == FETCH_NOP_SYMBOL) { if (code[1].op != FETCH_OP_IMM) return -EINVAL;
tmp = strpbrk(code->data, "+-"); if (tmp)
c = *tmp;
ret = traceprobe_split_symbol_offset(code->data,
&offset); if (ret) return ret;
code[1].immediate =
(unsignedlong)kallsyms_lookup_name(code->data); if (tmp)
*tmp = c; if (!code[1].immediate) return -ENOENT;
code[1].immediate += offset;
}
code++;
} return0;
}
/* When len=0, we just calculate the needed length */ #define LEN_OR_ZERO (len ? len - pos : 0) staticint __set_print_fmt(struct trace_probe *tp, char *buf, int len, enum probe_print_type ptype)
{ struct probe_arg *parg; int i, j; int pos = 0; constchar *fmt, *arg;
for (i = 0; i < tp->nr_args; i++) {
parg = tp->args + i; if (parg->count) { if (parg->type->is_string)
fmt = ", __get_str(%s[%d])"; else
fmt = ", REC->%s[%d]"; for (j = 0; j < parg->count; j++)
pos += snprintf(buf + pos, LEN_OR_ZERO,
fmt, parg->name, j);
} else { if (parg->type->is_string)
fmt = ", __get_str(%s)"; else
fmt = ", REC->%s";
pos += snprintf(buf + pos, LEN_OR_ZERO,
fmt, parg->name);
}
}
/* return the length of print_fmt */ return pos;
} #undef LEN_OR_ZERO
int traceprobe_set_print_fmt(struct trace_probe *tp, enum probe_print_type ptype)
{ struct trace_event_call *call = trace_probe_event_call(tp); int len; char *print_fmt;
/* First: called with 0 length to calculate the needed length */
len = __set_print_fmt(tp, NULL, 0, ptype);
print_fmt = kmalloc(len + 1, GFP_KERNEL); if (!print_fmt) return -ENOMEM;
/* Second: actually write the @print_fmt */
__set_print_fmt(tp, print_fmt, len + 1, ptype);
call->print_fmt = print_fmt;
return0;
}
int traceprobe_define_arg_fields(struct trace_event_call *event_call,
size_t offset, struct trace_probe *tp)
{ int ret, i;
/* Set argument names as fields */ for (i = 0; i < tp->nr_args; i++) { struct probe_arg *parg = &tp->args[i]; constchar *fmt = parg->type->fmttype; int size = parg->type->size;
if (parg->fmt)
fmt = parg->fmt; if (parg->count)
size *= parg->count;
ret = trace_define_field(event_call, fmt, parg->name,
offset + parg->offset, size,
parg->type->is_signed,
FILTER_OTHER); if (ret) return ret;
} return0;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.