diff options
Diffstat (limited to 'kernel/trace/trace_selftest.c')
| -rw-r--r-- | kernel/trace/trace_selftest.c | 259 | 
1 files changed, 255 insertions, 4 deletions
| diff --git a/kernel/trace/trace_selftest.c b/kernel/trace/trace_selftest.c index e9c5058a8efd..97f1e4bc47dc 100644 --- a/kernel/trace/trace_selftest.c +++ b/kernel/trace/trace_selftest.c @@ -756,13 +756,262 @@ trace_selftest_startup_function(struct tracer *trace, struct trace_array *tr)  #ifdef CONFIG_FUNCTION_GRAPH_TRACER +#ifdef CONFIG_DYNAMIC_FTRACE + +#define CHAR_NUMBER 123 +#define SHORT_NUMBER 12345 +#define WORD_NUMBER 1234567890 +#define LONG_NUMBER 1234567890123456789LL +#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) +{ +	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(fixture->error_str_buf, ERRSTR_BUFLEN, +			 "Failed to reserve %s\n", type); +		return 0; +	} + +	switch (size) { +	case 1: +		*(char *)p = CHAR_NUMBER; +		break; +	case 2: +		*(short *)p = SHORT_NUMBER; +		break; +	case 4: +		*(int *)p = WORD_NUMBER; +		break; +	case 8: +		*(long long *)p = LONG_NUMBER; +		break; +	} + +	return 1; +} + +static __init void store_return(struct ftrace_graph_ret *trace, +				struct fgraph_ops *gops) +{ +	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(fixture->error_str_buf, ERRSTR_BUFLEN, +			 "Failed to retrieve %s\n", type); +		return; +	} +	if (fixture->store_size > size) { +		snprintf(fixture->error_str_buf, ERRSTR_BUFLEN, +			 "Retrieved size %d is smaller than expected %d\n", +			 size, (int)fixture->store_size); +		return; +	} + +	switch (fixture->store_size) { +	case 1: +		expect = CHAR_NUMBER; +		found = *(char *)p; +		break; +	case 2: +		expect = SHORT_NUMBER; +		found = *(short *)p; +		break; +	case 4: +		expect = WORD_NUMBER; +		found = *(int *)p; +		break; +	case 8: +		expect = LONG_NUMBER; +		found = *(long long *)p; +		break; +	} + +	if (found != expect) { +		snprintf(fixture->error_str_buf, ERRSTR_BUFLEN, +			 "%s returned not %lld but %lld\n", type, expect, found); +		return; +	} +	fixture->error_str = NULL; +} + +static int __init init_fgraph_fixture(struct fgraph_fixture *fixture) +{ +	char *func_name; +	int len; + +	snprintf(fixture->error_str_buf, ERRSTR_BUFLEN, +		 "Failed to execute storage %s\n", fixture->store_type_name); +	fixture->error_str = fixture->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, str_plural(size)); + +	ret = init_fgraph_fixture(fixture); +	if (ret && ret != -ENODEV) { +		pr_cont("*Could not set filter* "); +		return -1; +	} + +	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(&fixture->gops); + +	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_single(&store_bytes[0]); +	if (ret) +		return ret; +	ret = test_graph_storage_single(&store_bytes[1]); +	if (ret) +		return ret; +	ret = test_graph_storage_single(&store_bytes[2]); +	if (ret) +		return ret; +	ret = test_graph_storage_single(&store_bytes[3]); +	if (ret) +		return ret; +	ret = test_graph_storage_multi(); +	if (ret) +		return ret; +	return 0; +} +#else +static inline int test_graph_storage(void) { return 0; } +#endif /* CONFIG_DYNAMIC_FTRACE */ +  /* Maximum number of functions to trace before diagnosing a hang */  #define GRAPH_MAX_FUNC_TEST	100000000  static unsigned int graph_hang_thresh;  /* Wrap the real function entry probe to avoid possible hanging */ -static int trace_graph_entry_watchdog(struct ftrace_graph_ent *trace) +static int trace_graph_entry_watchdog(struct ftrace_graph_ent *trace, +				      struct fgraph_ops *gops)  {  	/* This is harmlessly racy, we want to approximately detect a hang */  	if (unlikely(++graph_hang_thresh > GRAPH_MAX_FUNC_TEST)) { @@ -776,7 +1025,7 @@ static int trace_graph_entry_watchdog(struct ftrace_graph_ent *trace)  		return 0;  	} -	return trace_graph_entry(trace); +	return trace_graph_entry(trace, gops);  }  static struct fgraph_ops fgraph_ops __initdata  = { @@ -812,7 +1061,7 @@ trace_selftest_startup_function_graph(struct tracer *trace,  	 * to detect and recover from possible hangs  	 */  	tracing_reset_online_cpus(&tr->array_buffer); -	set_graph_array(tr); +	fgraph_ops.private = tr;  	ret = register_ftrace_graph(&fgraph_ops);  	if (ret) {  		warn_failed_init_tracer(trace, ret); @@ -855,7 +1104,7 @@ trace_selftest_startup_function_graph(struct tracer *trace,  	cond_resched();  	tracing_reset_online_cpus(&tr->array_buffer); -	set_graph_array(tr); +	fgraph_ops.private = tr;  	/*  	 * Some archs *cough*PowerPC*cough* add characters to the @@ -912,6 +1161,8 @@ trace_selftest_startup_function_graph(struct tracer *trace,  	ftrace_set_global_filter(NULL, 0, 1);  #endif +	ret = test_graph_storage(); +  	/* Don't test dynamic tracing, the function tracer already did */  out:  	/* Stop it if we failed */ |