Commit e2219db2 authored by Alan Maguire's avatar Alan Maguire Committed by Shuah Khan
Browse files

kunit: add debugfs /sys/kernel/debug/kunit/<suite>/results display



add debugfs support for displaying kunit test suite results; this is
especially useful for module-loaded tests to allow disentangling of
test result display from other dmesg events.  debugfs support is
provided if CONFIG_KUNIT_DEBUGFS=y.

As well as printk()ing messages, we append them to a per-test log.

Signed-off-by: default avatarAlan Maguire <alan.maguire@oracle.com>
Reviewed-by: default avatarBrendan Higgins <brendanhiggins@google.com>
Reviewed-by: default avatarFrank Rowand <frank.rowand@sony.com>
Signed-off-by: default avatarShuah Khan <skhan@linuxfoundation.org>
parent 0d5792c9
Loading
Loading
Loading
Loading
+46 −8
Original line number Diff line number Diff line
@@ -81,6 +81,9 @@ struct kunit_resource {

struct kunit;

/* Size of log associated with test. */
#define KUNIT_LOG_SIZE	512

/**
 * struct kunit_case - represents an individual test case.
 *
@@ -123,8 +126,14 @@ struct kunit_case {

	/* private: internal use only. */
	bool success;
	char *log;
};

static inline char *kunit_status_to_string(bool status)
{
	return status ? "ok" : "not ok";
}

/**
 * KUNIT_CASE - A helper for creating a &struct kunit_case
 *
@@ -157,6 +166,10 @@ struct kunit_suite {
	int (*init)(struct kunit *test);
	void (*exit)(struct kunit *test);
	struct kunit_case *test_cases;

	/* private - internal use only */
	struct dentry *debugfs;
	char *log;
};

/**
@@ -175,6 +188,7 @@ struct kunit {

	/* private: internal use only. */
	const char *name; /* Read only after initialization! */
	char *log; /* Points at case log after initialization */
	struct kunit_try_catch try_catch;
	/*
	 * success starts as true, and may only be set to false during a
@@ -193,10 +207,19 @@ struct kunit {
	struct list_head resources; /* Protected by lock. */
};

void kunit_init_test(struct kunit *test, const char *name);
void kunit_init_test(struct kunit *test, const char *name, char *log);

int kunit_run_tests(struct kunit_suite *suite);

size_t kunit_suite_num_test_cases(struct kunit_suite *suite);

unsigned int kunit_test_case_num(struct kunit_suite *suite,
				 struct kunit_case *test_case);

int __kunit_test_suites_init(struct kunit_suite **suites);

void __kunit_test_suites_exit(struct kunit_suite **suites);

/**
 * kunit_test_suites() - used to register one or more &struct kunit_suite
 *			 with KUnit.
@@ -226,20 +249,22 @@ int kunit_run_tests(struct kunit_suite *suite);
	static struct kunit_suite *suites[] = { __VA_ARGS__, NULL};	\
	static int kunit_test_suites_init(void)				\
	{								\
		unsigned int i;						\
		for (i = 0; suites[i] != NULL; i++)			\
			kunit_run_tests(suites[i]);			\
		return 0;						\
		return __kunit_test_suites_init(suites);		\
	}								\
	late_initcall(kunit_test_suites_init);				\
	static void __exit kunit_test_suites_exit(void)			\
	{								\
		return;							\
		return __kunit_test_suites_exit(suites);		\
	}								\
	module_exit(kunit_test_suites_exit)

#define kunit_test_suite(suite)	kunit_test_suites(&suite)

#define kunit_suite_for_each_test_case(suite, test_case)		\
	for (test_case = suite->test_cases; test_case->run_case; test_case++)

bool kunit_suite_has_succeeded(struct kunit_suite *suite);

/*
 * Like kunit_alloc_resource() below, but returns the struct kunit_resource
 * object that contains the allocation. This is mostly for testing purposes.
@@ -356,8 +381,21 @@ static inline void *kunit_kzalloc(struct kunit *test, size_t size, gfp_t gfp)

void kunit_cleanup(struct kunit *test);

void kunit_log_append(char *log, const char *fmt, ...);

/*
 * printk and log to per-test or per-suite log buffer.  Logging only done
 * if CONFIG_KUNIT_DEBUGFS is 'y'; if it is 'n', no log is allocated/used.
 */
#define kunit_log(lvl, test_or_suite, fmt, ...)				\
	do {								\
		printk(lvl fmt, ##__VA_ARGS__);				\
		kunit_log_append((test_or_suite)->log,	fmt "\n",	\
				 ##__VA_ARGS__);			\
	} while (0)

#define kunit_printk(lvl, test, fmt, ...)				\
	printk(lvl "\t# %s: " fmt, (test)->name, ##__VA_ARGS__)
	kunit_log(lvl, test, "\t# %s: " fmt, (test)->name, ##__VA_ARGS__)

/**
 * kunit_info() - Prints an INFO level message associated with @test.
+8 −0
Original line number Diff line number Diff line
@@ -14,6 +14,14 @@ menuconfig KUNIT

if KUNIT

config KUNIT_DEBUGFS
	bool "KUnit - Enable /sys/kernel/debug/kunit debugfs representation"
	help
	  Enable debugfs representation for kunit.  Currently this consists
	  of /sys/kernel/debug/kunit/<test_suite>/results files for each
	  test suite, which allow users to see results of the last test suite
	  run that occurred.

config KUNIT_TEST
	tristate "KUnit test for KUnit"
	help
+4 −0
Original line number Diff line number Diff line
@@ -5,6 +5,10 @@ kunit-objs += test.o \
					assert.o \
					try-catch.o

ifeq ($(CONFIG_KUNIT_DEBUGFS),y)
kunit-objs +=				debugfs.o
endif

obj-$(CONFIG_KUNIT_TEST) +=		kunit-test.o

# string-stream-test compiles built-in only.

lib/kunit/debugfs.c

0 → 100644
+116 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (c) 2020, Oracle and/or its affiliates.
 *    Author: Alan Maguire <alan.maguire@oracle.com>
 */

#include <linux/debugfs.h>
#include <linux/module.h>

#include <kunit/test.h>

#include "string-stream.h"

#define KUNIT_DEBUGFS_ROOT             "kunit"
#define KUNIT_DEBUGFS_RESULTS          "results"

/*
 * Create a debugfs representation of test suites:
 *
 * Path						Semantics
 * /sys/kernel/debug/kunit/<testsuite>/results	Show results of last run for
 *						testsuite
 *
 */

static struct dentry *debugfs_rootdir;

void kunit_debugfs_cleanup(void)
{
	debugfs_remove_recursive(debugfs_rootdir);
}

void kunit_debugfs_init(void)
{
	if (!debugfs_rootdir)
		debugfs_rootdir = debugfs_create_dir(KUNIT_DEBUGFS_ROOT, NULL);
}

static void debugfs_print_result(struct seq_file *seq,
				 struct kunit_suite *suite,
				 struct kunit_case *test_case)
{
	if (!test_case || !test_case->log)
		return;

	seq_printf(seq, "%s", test_case->log);
}

/*
 * /sys/kernel/debug/kunit/<testsuite>/results shows all results for testsuite.
 */
static int debugfs_print_results(struct seq_file *seq, void *v)
{
	struct kunit_suite *suite = (struct kunit_suite *)seq->private;
	bool success = kunit_suite_has_succeeded(suite);
	struct kunit_case *test_case;

	if (!suite || !suite->log)
		return 0;

	seq_printf(seq, "%s", suite->log);

	kunit_suite_for_each_test_case(suite, test_case)
		debugfs_print_result(seq, suite, test_case);

	seq_printf(seq, "%s %d - %s\n",
		   kunit_status_to_string(success), 1, suite->name);
	return 0;
}

static int debugfs_release(struct inode *inode, struct file *file)
{
	return single_release(inode, file);
}

static int debugfs_results_open(struct inode *inode, struct file *file)
{
	struct kunit_suite *suite;

	suite = (struct kunit_suite *)inode->i_private;

	return single_open(file, debugfs_print_results, suite);
}

static const struct file_operations debugfs_results_fops = {
	.open = debugfs_results_open,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = debugfs_release,
};

void kunit_debugfs_create_suite(struct kunit_suite *suite)
{
	struct kunit_case *test_case;

	/* Allocate logs before creating debugfs representation. */
	suite->log = kzalloc(KUNIT_LOG_SIZE, GFP_KERNEL);
	kunit_suite_for_each_test_case(suite, test_case)
		test_case->log = kzalloc(KUNIT_LOG_SIZE, GFP_KERNEL);

	suite->debugfs = debugfs_create_dir(suite->name, debugfs_rootdir);

	debugfs_create_file(KUNIT_DEBUGFS_RESULTS, S_IFREG | 0444,
			    suite->debugfs,
			    suite, &debugfs_results_fops);
}

void kunit_debugfs_destroy_suite(struct kunit_suite *suite)
{
	struct kunit_case *test_case;

	debugfs_remove_recursive(suite->debugfs);
	kfree(suite->log);
	kunit_suite_for_each_test_case(suite, test_case)
		kfree(test_case->log);
}

lib/kunit/debugfs.h

0 → 100644
+30 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * Copyright (C) 2020, Oracle and/or its affiliates.
 */

#ifndef _KUNIT_DEBUGFS_H
#define _KUNIT_DEBUGFS_H

#include <kunit/test.h>

#ifdef CONFIG_KUNIT_DEBUGFS

void kunit_debugfs_create_suite(struct kunit_suite *suite);
void kunit_debugfs_destroy_suite(struct kunit_suite *suite);
void kunit_debugfs_init(void);
void kunit_debugfs_cleanup(void);

#else

static inline void kunit_debugfs_create_suite(struct kunit_suite *suite) { }

static inline void kunit_debugfs_destroy_suite(struct kunit_suite *suite) { }

static inline void kunit_debugfs_init(void) { }

static inline void kunit_debugfs_cleanup(void) { }

#endif /* CONFIG_KUNIT_DEBUGFS */

#endif /* _KUNIT_DEBUGFS_H */
Loading