Skip to content

Commit

Permalink
ftrace: Add multiple fgraph storage selftest
Browse files Browse the repository at this point in the history
Add a selftest for multiple function graph tracer with storage on a same
function. In this case, the shadow stack entry will be shared among those
fgraph with different data storage. So this will ensure the fgraph will
not mixed those storage data.

Link: https://lore.kernel.org/linux-trace-kernel/171509111465.162236.3795819216426570800.stgit@devnote2
Link: https://lore.kernel.org/linux-trace-kernel/20240603190824.284049716@goodmis.org

Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Alexei Starovoitov <alexei.starovoitov@gmail.com>
Cc: Florent Revest <revest@chromium.org>
Cc: Martin KaFai Lau <martin.lau@linux.dev>
Cc: bpf <bpf@vger.kernel.org>
Cc: Sven Schnelle <svens@linux.ibm.com>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: Alan Maguire <alan.maguire@oracle.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Guo Ren <guoren@kernel.org>
Reviewed-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Suggested-by: Steven Rostedt (Google) <rostedt@goodmis.org>
Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
  • Loading branch information
Masami Hiramatsu (Google) authored and Steven Rostedt (Google) committed Jun 4, 2024
1 parent 47c3c70 commit dd120af
Showing 1 changed file with 126 additions and 45 deletions.
171 changes: 126 additions & 45 deletions kernel/trace/trace_selftest.c
Original file line number Diff line number Diff line change
Expand Up @@ -762,28 +762,32 @@ trace_selftest_startup_function(struct tracer *trace, struct trace_array *tr)
#define SHORT_NUMBER 12345
#define WORD_NUMBER 1234567890
#define LONG_NUMBER 1234567890123456789LL

static int fgraph_store_size __initdata;
static const char *fgraph_store_type_name __initdata;
static char *fgraph_error_str __initdata;
static char fgraph_error_str_buf[128] __initdata;
#define ERRSTR_BUFLEN 128

struct fgraph_fixture {
struct fgraph_ops gops;
int store_size;
const char *store_type_name;
char error_str_buf[ERRSTR_BUFLEN];
char *error_str;
};

static __init int store_entry(struct ftrace_graph_ent *trace,
struct fgraph_ops *gops)
{
const char *type = fgraph_store_type_name;
int size = fgraph_store_size;
struct fgraph_fixture *fixture = container_of(gops, struct fgraph_fixture, gops);
const char *type = fixture->store_type_name;
int size = fixture->store_size;
void *p;

p = fgraph_reserve_data(gops->idx, size);
if (!p) {
snprintf(fgraph_error_str_buf, sizeof(fgraph_error_str_buf),
snprintf(fixture->error_str_buf, ERRSTR_BUFLEN,
"Failed to reserve %s\n", type);
fgraph_error_str = fgraph_error_str_buf;
return 0;
}

switch (fgraph_store_size) {
switch (size) {
case 1:
*(char *)p = BYTE_NUMBER;
break;
Expand All @@ -804,28 +808,27 @@ static __init int store_entry(struct ftrace_graph_ent *trace,
static __init void store_return(struct ftrace_graph_ret *trace,
struct fgraph_ops *gops)
{
const char *type = fgraph_store_type_name;
struct fgraph_fixture *fixture = container_of(gops, struct fgraph_fixture, gops);
const char *type = fixture->store_type_name;
long long expect = 0;
long long found = -1;
int size;
char *p;

p = fgraph_retrieve_data(gops->idx, &size);
if (!p) {
snprintf(fgraph_error_str_buf, sizeof(fgraph_error_str_buf),
snprintf(fixture->error_str_buf, ERRSTR_BUFLEN,
"Failed to retrieve %s\n", type);
fgraph_error_str = fgraph_error_str_buf;
return;
}
if (fgraph_store_size > size) {
snprintf(fgraph_error_str_buf, sizeof(fgraph_error_str_buf),
if (fixture->store_size > size) {
snprintf(fixture->error_str_buf, ERRSTR_BUFLEN,
"Retrieved size %d is smaller than expected %d\n",
size, (int)fgraph_store_size);
fgraph_error_str = fgraph_error_str_buf;
size, (int)fixture->store_size);
return;
}

switch (fgraph_store_size) {
switch (fixture->store_size) {
case 1:
expect = BYTE_NUMBER;
found = *(char *)p;
Expand All @@ -845,76 +848,154 @@ static __init void store_return(struct ftrace_graph_ret *trace,
}

if (found != expect) {
snprintf(fgraph_error_str_buf, sizeof(fgraph_error_str_buf),
snprintf(fixture->error_str_buf, ERRSTR_BUFLEN,
"%s returned not %lld but %lld\n", type, expect, found);
fgraph_error_str = fgraph_error_str_buf;
return;
}
fgraph_error_str = NULL;
fixture->error_str = NULL;
}

static struct fgraph_ops store_bytes __initdata = {
.entryfunc = store_entry,
.retfunc = store_return,
};

static int __init test_graph_storage_type(const char *name, int size)
static int __init init_fgraph_fixture(struct fgraph_fixture *fixture)
{
char *func_name;
int len;
int ret;

fgraph_store_type_name = name;
fgraph_store_size = size;
snprintf(fixture->error_str_buf, ERRSTR_BUFLEN,
"Failed to execute storage %s\n", fixture->store_type_name);
fixture->error_str = fixture->error_str_buf;

snprintf(fgraph_error_str_buf, sizeof(fgraph_error_str_buf),
"Failed to execute storage %s\n", name);
fgraph_error_str = fgraph_error_str_buf;
func_name = "*" __stringify(DYN_FTRACE_TEST_NAME);
len = strlen(func_name);

return ftrace_set_filter(&fixture->gops.ops, func_name, len, 1);
}

/* Test fgraph storage for each size */
static int __init test_graph_storage_single(struct fgraph_fixture *fixture)
{
int size = fixture->store_size;
int ret;

pr_cont("PASSED\n");
pr_info("Testing fgraph storage of %d byte%s: ", size, size > 1 ? "s" : "");

func_name = "*" __stringify(DYN_FTRACE_TEST_NAME);
len = strlen(func_name);

ret = ftrace_set_filter(&store_bytes.ops, func_name, len, 1);
ret = init_fgraph_fixture(fixture);
if (ret && ret != -ENODEV) {
pr_cont("*Could not set filter* ");
return -1;
}

ret = register_ftrace_graph(&store_bytes);
ret = register_ftrace_graph(&fixture->gops);
if (ret) {
pr_warn("Failed to init store_bytes fgraph tracing\n");
return -1;
}

DYN_FTRACE_TEST_NAME();

unregister_ftrace_graph(&store_bytes);
unregister_ftrace_graph(&fixture->gops);

if (fgraph_error_str) {
pr_cont("*** %s ***", fgraph_error_str);
if (fixture->error_str) {
pr_cont("*** %s ***", fixture->error_str);
return -1;
}

return 0;
}

static struct fgraph_fixture store_bytes[4] __initdata = {
[0] = {
.gops = {
.entryfunc = store_entry,
.retfunc = store_return,
},
.store_size = 1,
.store_type_name = "byte",
},
[1] = {
.gops = {
.entryfunc = store_entry,
.retfunc = store_return,
},
.store_size = 2,
.store_type_name = "short",
},
[2] = {
.gops = {
.entryfunc = store_entry,
.retfunc = store_return,
},
.store_size = 4,
.store_type_name = "word",
},
[3] = {
.gops = {
.entryfunc = store_entry,
.retfunc = store_return,
},
.store_size = 8,
.store_type_name = "long long",
},
};

static __init int test_graph_storage_multi(void)
{
struct fgraph_fixture *fixture;
bool printed = false;
int i, ret;

pr_cont("PASSED\n");
pr_info("Testing multiple fgraph storage on a function: ");

for (i = 0; i < ARRAY_SIZE(store_bytes); i++) {
fixture = &store_bytes[i];
ret = init_fgraph_fixture(fixture);
if (ret && ret != -ENODEV) {
pr_cont("*Could not set filter* ");
printed = true;
goto out;
}

ret = register_ftrace_graph(&fixture->gops);
if (ret) {
pr_warn("Failed to init store_bytes fgraph tracing\n");
printed = true;
goto out;
}
}

DYN_FTRACE_TEST_NAME();
out:
while (--i >= 0) {
fixture = &store_bytes[i];
unregister_ftrace_graph(&fixture->gops);

if (fixture->error_str && !printed) {
pr_cont("*** %s ***", fixture->error_str);
printed = true;
}
}
return printed ? -1 : 0;
}

/* Test the storage passed across function_graph entry and return */
static __init int test_graph_storage(void)
{
int ret;

ret = test_graph_storage_type("byte", 1);
ret = test_graph_storage_single(&store_bytes[0]);
if (ret)
return ret;
ret = test_graph_storage_single(&store_bytes[1]);
if (ret)
return ret;
ret = test_graph_storage_type("short", 2);
ret = test_graph_storage_single(&store_bytes[2]);
if (ret)
return ret;
ret = test_graph_storage_type("word", 4);
ret = test_graph_storage_single(&store_bytes[3]);
if (ret)
return ret;
ret = test_graph_storage_type("long long", 8);
ret = test_graph_storage_multi();
if (ret)
return ret;
return 0;
Expand Down

0 comments on commit dd120af

Please sign in to comment.