Skip to content

Commit 40b53b7

Browse files
mhiramatrostedt
authored andcommitted
tracing: probeevent: Add array type support
Add array type support for probe events. This allows user to get arraied types from memory address. The array type syntax is TYPE[N] Where TYPE is one of types (u8/16/32/64,s8/16/32/64, x8/16/32/64, symbol, string) and N is a fixed value less than 64. The string array type is a bit different from other types. For other base types, <base-type>[1] is equal to <base-type> (e.g. +0(%di):x32[1] is same as +0(%di):x32.) But string[1] is not equal to string. The string type itself represents "char array", but string array type represents "char * array". So, for example, +0(%di):string[1] is equal to +0(+0(%di)):string. Link: http://lkml.kernel.org/r/152465891533.26224.6150658225601339931.stgit@devbox Signed-off-by: Masami Hiramatsu <[email protected]> Signed-off-by: Steven Rostedt (VMware) <[email protected]>
1 parent 60c2e0c commit 40b53b7

File tree

5 files changed

+181
-40
lines changed

5 files changed

+181
-40
lines changed

Documentation/trace/kprobetrace.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,20 @@ respectively. 'x' prefix implies it is unsigned. Traced arguments are shown
6464
in decimal ('s' and 'u') or hexadecimal ('x'). Without type casting, 'x32'
6565
or 'x64' is used depends on the architecture (e.g. x86-32 uses x32, and
6666
x86-64 uses x64).
67+
These value types can be an array. To record array data, you can add '[N]'
68+
(where N is a fixed number, less than 64) to the base type.
69+
E.g. 'x16[4]' means an array of x16 (2bytes hex) with 4 elements.
70+
Note that the array can be applied to memory type fetchargs, you can not
71+
apply it to registers/stack-entries etc. (for example, '$stack1:x8[8]' is
72+
wrong, but '+8($stack):x8[8]' is OK.)
6773
String type is a special type, which fetches a "null-terminated" string from
6874
kernel space. This means it will fail and store NULL if the string container
6975
has been paged out.
76+
The string array type is a bit different from other types. For other base
77+
types, <base-type>[1] is equal to <base-type> (e.g. +0(%di):x32[1] is same
78+
as +0(%di):x32.) But string[1] is not equal to string. The string type itself
79+
represents "char array", but string array type represents "char * array".
80+
So, for example, +0(%di):string[1] is equal to +0(+0(%di)):string.
7081
Bitfield is another special type, which takes 3 parameters, bit-width, bit-
7182
offset, and container-size (usually 32). The syntax is::
7283

kernel/trace/trace.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4627,7 +4627,8 @@ static const char readme_msg[] =
46274627
"\t fetcharg: %<register>, @<address>, @<symbol>[+|-<offset>],\n"
46284628
"\t $stack<index>, $stack, $retval, $comm\n"
46294629
"\t type: s8/16/32/64, u8/16/32/64, x8/16/32/64, string, symbol,\n"
4630-
"\t b<bit-width>@<bit-offset>/<container-size>\n"
4630+
"\t b<bit-width>@<bit-offset>/<container-size>,\n"
4631+
"\t <type>\\[<array-size>\\]\n"
46314632
#endif
46324633
" events/\t\t- Directory containing all trace event subsystems:\n"
46334634
" enable\t\t- Write 0/1 to enable/disable tracing of all events\n"

kernel/trace/trace_probe.c

Lines changed: 99 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -341,9 +341,9 @@ static int __parse_bitfield_probe_arg(const char *bf,
341341
int traceprobe_parse_probe_arg(char *arg, ssize_t *size,
342342
struct probe_arg *parg, bool is_return, bool is_kprobe)
343343
{
344-
struct fetch_insn *code, *tmp = NULL;
345-
const char *t;
346-
int ret;
344+
struct fetch_insn *code, *scode, *tmp = NULL;
345+
char *t, *t2;
346+
int ret, len;
347347

348348
if (strlen(arg) > MAX_ARGSTR_LEN) {
349349
pr_info("Argument is too long.: %s\n", arg);
@@ -354,24 +354,42 @@ int traceprobe_parse_probe_arg(char *arg, ssize_t *size,
354354
pr_info("Failed to allocate memory for command '%s'.\n", arg);
355355
return -ENOMEM;
356356
}
357-
t = strchr(parg->comm, ':');
357+
t = strchr(arg, ':');
358358
if (t) {
359-
arg[t - parg->comm] = '\0';
360-
t++;
359+
*t = '\0';
360+
t2 = strchr(++t, '[');
361+
if (t2) {
362+
*t2 = '\0';
363+
parg->count = simple_strtoul(t2 + 1, &t2, 0);
364+
if (strcmp(t2, "]") || parg->count == 0)
365+
return -EINVAL;
366+
if (parg->count > MAX_ARRAY_LEN)
367+
return -E2BIG;
368+
}
361369
}
362370
/*
363371
* The default type of $comm should be "string", and it can't be
364372
* dereferenced.
365373
*/
366374
if (!t && strcmp(arg, "$comm") == 0)
367-
t = "string";
368-
parg->type = find_fetch_type(t);
375+
parg->type = find_fetch_type("string");
376+
else
377+
parg->type = find_fetch_type(t);
369378
if (!parg->type) {
370379
pr_info("Unsupported type: %s\n", t);
371380
return -EINVAL;
372381
}
373382
parg->offset = *size;
374-
*size += parg->type->size;
383+
*size += parg->type->size * (parg->count ?: 1);
384+
385+
if (parg->count) {
386+
len = strlen(parg->type->fmttype) + 6;
387+
parg->fmt = kmalloc(len, GFP_KERNEL);
388+
if (!parg->fmt)
389+
return -ENOMEM;
390+
snprintf(parg->fmt, len, "%s[%d]", parg->type->fmttype,
391+
parg->count);
392+
}
375393

376394
code = tmp = kzalloc(sizeof(*code) * FETCH_INSN_MAX, GFP_KERNEL);
377395
if (!code)
@@ -391,10 +409,20 @@ int traceprobe_parse_probe_arg(char *arg, ssize_t *size,
391409
ret = -EINVAL;
392410
goto fail;
393411
}
394-
/* Since IMM or COMM must be the 1st insn, this is safe */
395-
if (code->op == FETCH_OP_IMM || code->op == FETCH_OP_COMM)
412+
if (code->op != FETCH_OP_DEREF || parg->count) {
413+
/*
414+
* IMM and COMM is pointing actual address, those must
415+
* be kept, and if parg->count != 0, this is an array
416+
* of string pointers instead of string address itself.
417+
*/
396418
code++;
419+
if (code->op != FETCH_OP_NOP) {
420+
ret = -E2BIG;
421+
goto fail;
422+
}
423+
}
397424
code->op = FETCH_OP_ST_STRING; /* In DEREF case, replace it */
425+
code->size = parg->type->size;
398426
parg->dynamic = true;
399427
} else if (code->op == FETCH_OP_DEREF) {
400428
code->op = FETCH_OP_ST_MEM;
@@ -408,12 +436,29 @@ int traceprobe_parse_probe_arg(char *arg, ssize_t *size,
408436
code->op = FETCH_OP_ST_RAW;
409437
code->size = parg->type->size;
410438
}
439+
scode = code;
411440
/* Modify operation */
412441
if (t != NULL) {
413442
ret = __parse_bitfield_probe_arg(t, parg->type, &code);
414443
if (ret)
415444
goto fail;
416445
}
446+
/* Loop(Array) operation */
447+
if (parg->count) {
448+
if (scode->op != FETCH_OP_ST_MEM &&
449+
scode->op != FETCH_OP_ST_STRING) {
450+
pr_info("array only accepts memory or address\n");
451+
ret = -EINVAL;
452+
goto fail;
453+
}
454+
code++;
455+
if (code->op != FETCH_OP_NOP) {
456+
ret = -E2BIG;
457+
goto fail;
458+
}
459+
code->op = FETCH_OP_LP_ARRAY;
460+
code->param = parg->count;
461+
}
417462
code++;
418463
code->op = FETCH_OP_END;
419464

@@ -452,14 +497,17 @@ void traceprobe_free_probe_arg(struct probe_arg *arg)
452497
kfree(arg->code);
453498
kfree(arg->name);
454499
kfree(arg->comm);
500+
kfree(arg->fmt);
455501
}
456502

503+
/* When len=0, we just calculate the needed length */
504+
#define LEN_OR_ZERO (len ? len - pos : 0)
457505
static int __set_print_fmt(struct trace_probe *tp, char *buf, int len,
458506
bool is_return)
459507
{
460-
int i;
508+
struct probe_arg *parg;
509+
int i, j;
461510
int pos = 0;
462-
463511
const char *fmt, *arg;
464512

465513
if (!is_return) {
@@ -470,33 +518,49 @@ static int __set_print_fmt(struct trace_probe *tp, char *buf, int len,
470518
arg = "REC->" FIELD_STRING_FUNC ", REC->" FIELD_STRING_RETIP;
471519
}
472520

473-
/* When len=0, we just calculate the needed length */
474-
#define LEN_OR_ZERO (len ? len - pos : 0)
475-
476521
pos += snprintf(buf + pos, LEN_OR_ZERO, "\"%s", fmt);
477522

478523
for (i = 0; i < tp->nr_args; i++) {
479-
pos += snprintf(buf + pos, LEN_OR_ZERO, " %s=%s",
480-
tp->args[i].name, tp->args[i].type->fmt);
524+
parg = tp->args + i;
525+
pos += snprintf(buf + pos, LEN_OR_ZERO, " %s=", parg->name);
526+
if (parg->count) {
527+
pos += snprintf(buf + pos, LEN_OR_ZERO, "{%s",
528+
parg->type->fmt);
529+
for (j = 1; j < parg->count; j++)
530+
pos += snprintf(buf + pos, LEN_OR_ZERO, ",%s",
531+
parg->type->fmt);
532+
pos += snprintf(buf + pos, LEN_OR_ZERO, "}");
533+
} else
534+
pos += snprintf(buf + pos, LEN_OR_ZERO, "%s",
535+
parg->type->fmt);
481536
}
482537

483538
pos += snprintf(buf + pos, LEN_OR_ZERO, "\", %s", arg);
484539

485540
for (i = 0; i < tp->nr_args; i++) {
486-
if (strcmp(tp->args[i].type->name, "string") == 0)
541+
parg = tp->args + i;
542+
if (parg->count) {
543+
if (strcmp(parg->type->name, "string") == 0)
544+
fmt = ", __get_str(%s[%d])";
545+
else
546+
fmt = ", REC->%s[%d]";
547+
for (j = 0; j < parg->count; j++)
548+
pos += snprintf(buf + pos, LEN_OR_ZERO,
549+
fmt, parg->name, j);
550+
} else {
551+
if (strcmp(parg->type->name, "string") == 0)
552+
fmt = ", __get_str(%s)";
553+
else
554+
fmt = ", REC->%s";
487555
pos += snprintf(buf + pos, LEN_OR_ZERO,
488-
", __get_str(%s)",
489-
tp->args[i].name);
490-
else
491-
pos += snprintf(buf + pos, LEN_OR_ZERO, ", REC->%s",
492-
tp->args[i].name);
556+
fmt, parg->name);
557+
}
493558
}
494559

495-
#undef LEN_OR_ZERO
496-
497560
/* return the length of print_fmt */
498561
return pos;
499562
}
563+
#undef LEN_OR_ZERO
500564

501565
int traceprobe_set_print_fmt(struct trace_probe *tp, bool is_return)
502566
{
@@ -524,11 +588,15 @@ int traceprobe_define_arg_fields(struct trace_event_call *event_call,
524588
/* Set argument names as fields */
525589
for (i = 0; i < tp->nr_args; i++) {
526590
struct probe_arg *parg = &tp->args[i];
527-
528-
ret = trace_define_field(event_call, parg->type->fmttype,
529-
parg->name,
530-
offset + parg->offset,
531-
parg->type->size,
591+
const char *fmt = parg->type->fmttype;
592+
int size = parg->type->size;
593+
594+
if (parg->fmt)
595+
fmt = parg->fmt;
596+
if (parg->count)
597+
size *= parg->count;
598+
ret = trace_define_field(event_call, fmt, parg->name,
599+
offset + parg->offset, size,
532600
parg->type->is_signed,
533601
FILTER_OTHER);
534602
if (ret)

kernel/trace/trace_probe.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030

3131
#define MAX_TRACE_ARGS 128
3232
#define MAX_ARGSTR_LEN 63
33+
#define MAX_ARRAY_LEN 64
3334
#define MAX_STRING_SIZE PATH_MAX
3435

3536
/* Reserved field names */
@@ -65,6 +66,14 @@ static nokprobe_inline void *get_loc_data(u32 *dl, void *ent)
6566
return (u8 *)ent + get_loc_offs(*dl);
6667
}
6768

69+
static nokprobe_inline u32 update_data_loc(u32 loc, int consumed)
70+
{
71+
u32 maxlen = get_loc_len(loc);
72+
u32 offset = get_loc_offs(loc);
73+
74+
return make_data_loc(maxlen - consumed, offset + consumed);
75+
}
76+
6877
/* Printing function type */
6978
typedef int (*print_type_func_t)(struct trace_seq *, void *, void *);
7079

@@ -86,6 +95,8 @@ enum fetch_op {
8695
FETCH_OP_ST_STRING, /* String: .offset, .size */
8796
// Stage 4 (modify) op
8897
FETCH_OP_MOD_BF, /* Bitfield: .basesize, .lshift, .rshift */
98+
// Stage 5 (loop) op
99+
FETCH_OP_LP_ARRAY, /* Array: .param = loop count */
89100
FETCH_OP_END,
90101
};
91102

@@ -175,6 +186,7 @@ DECLARE_BASIC_PRINT_TYPE_FUNC(symbol);
175186
_ASSIGN_FETCH_TYPE(#ptype, ptype, ftype, sizeof(ftype), sign, atype)
176187

177188
#define ASSIGN_FETCH_TYPE_END {}
189+
#define MAX_ARRAY_LEN 64
178190

179191
#ifdef CONFIG_KPROBE_EVENTS
180192
bool trace_kprobe_on_func_entry(struct trace_event_call *call);
@@ -195,8 +207,10 @@ struct probe_arg {
195207
struct fetch_insn *code;
196208
bool dynamic;/* Dynamic array (string) is used */
197209
unsigned int offset; /* Offset from argument entry */
210+
unsigned int count; /* Array count */
198211
const char *name; /* Name of this argument */
199212
const char *comm; /* Command of this argument */
213+
char *fmt; /* Format string if needed */
200214
const struct fetch_type *type; /* Type of this argument */
201215
};
202216

0 commit comments

Comments
 (0)