The output of the test executable should be grouped together with the
regular KUnit output and also be available in debugfs.
Install a custom miscdevice as stdout and stderr which forwards the
written data to the KUnit log.

Signed-off-by: Thomas Weißschuh <[email protected]>
---
 lib/kunit/kunit-uapi.c | 155 ++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 154 insertions(+), 1 deletion(-)

diff --git a/lib/kunit/kunit-uapi.c b/lib/kunit/kunit-uapi.c
index 485b79fd193d..7f0309a827a5 100644
--- a/lib/kunit/kunit-uapi.c
+++ b/lib/kunit/kunit-uapi.c
@@ -11,6 +11,7 @@
 #include <linux/file.h>
 #include <linux/fs.h>
 #include <linux/fs_struct.h>
+#include <linux/miscdevice.h>
 #include <linux/pid.h>
 #include <linux/pipe_fs_i.h>
 #include <linux/sched/task.h>
@@ -22,6 +23,8 @@
 #include <kunit/test.h>
 #include <kunit/uapi.h>
 
+#define KUNIT_LOG_DEVICE "kunit-log"
+
 enum {
        KSFT_PASS       = 0,
        KSFT_FAIL       = 1,
@@ -94,10 +97,48 @@ static int kunit_uapi_get_cwd(struct vfsmount *mnt)
        return take_fd(fd);
 }
 
+static int kunit_uapi_open_standard_streams(void)
+{
+       struct vfsmount *devtmpfs __free(kern_unmount) = 
kunit_uapi_mount_fs("devtmpfs");
+       if (IS_ERR(devtmpfs))
+               return PTR_ERR(devtmpfs);
+
+       CLASS(get_unused_fd, stdin_fd)(O_RDONLY);
+       if (stdin_fd < 0)
+               return stdin_fd;
+
+       CLASS(get_unused_fd, stdout_fd)(O_WRONLY);
+       if (stdout_fd < 0)
+               return stdout_fd;
+
+       CLASS(get_unused_fd, stderr_fd)(O_WRONLY);
+       if (stderr_fd < 0)
+               return stderr_fd;
+
+       struct file *logfile __free(fput) = file_open_root_mnt(devtmpfs, 
KUNIT_LOG_DEVICE,
+                                                              O_RDWR, 0);
+       if (IS_ERR(logfile))
+               return PTR_ERR(logfile);
+
+       fd_install(stdin_fd, no_free_ptr(logfile));
+       fd_install(stdout_fd, fget(stdin_fd));
+       fd_install(stderr_fd, fget(stdin_fd));
+
+       take_fd(stdin_fd);
+       take_fd(stdout_fd);
+       take_fd(stderr_fd);
+
+       return 0;
+}
+
 static int kunit_uapi_usermodehelper_init(struct subprocess_info *info, struct 
cred *new)
 {
        struct kunit_uapi_usermodehelper_ctx *ctx = info->data;
-       int dirfd;
+       int ret, dirfd;
+
+       ret = kunit_uapi_open_standard_streams();
+       if (ret)
+               return ret;
 
        dirfd = kunit_uapi_get_cwd(ctx->mnt);
        if (dirfd < 0)
@@ -188,6 +229,118 @@ void kunit_uapi_run_kselftest(struct kunit *test, const 
struct kunit_uapi_blob *
 }
 EXPORT_SYMBOL_GPL(kunit_uapi_run_kselftest);
 
+struct kunit_uapi_log_private {
+       struct mutex mutex;
+       struct seq_buf buf;
+       char data[4096];
+};
+
+static int kunit_uapi_log_open(struct inode *ino, struct file *file)
+{
+       struct kunit_uapi_log_private *priv;
+
+       priv = kmalloc_obj(*priv);
+       if (!priv)
+               return -ENOMEM;
+
+       mutex_init(&priv->mutex);
+       seq_buf_init(&priv->buf, priv->data, sizeof(priv->data));
+
+       file->private_data = priv;
+
+       return 0;
+}
+
+static void kunit_uapi_log_str(struct kunit *test, const char *str, size_t len)
+{
+       kunit_log(KERN_INFO, test, KUNIT_SUBSUBTEST_INDENT "%.*s", (int)len, 
str);
+}
+
+static void kunit_uapi_print_buf_to_log(struct kunit *test, struct seq_buf *s)
+{
+       const char *start, *lf;
+
+       if (s->size == 0 || s->len == 0)
+               return;
+
+       start = seq_buf_str(s);
+       while ((lf = strchr(start, '\n'))) {
+               kunit_uapi_log_str(test, start, lf - start + 1);
+               start = ++lf;
+       }
+
+       /* Remove printed data from buffer */
+       memmove(s->buffer, start, start - s->buffer);
+       s->len -= start - s->buffer;
+}
+
+static ssize_t kunit_uapi_log_write(struct file *file, const char __user 
*ubuf, size_t count,
+                                   loff_t *off)
+{
+       struct kunit_uapi_log_private *priv = file->private_data;
+       struct seq_buf *buf = &priv->buf;
+       struct kunit *test;
+
+       test = kunit_get_current_test();
+       if (!test)
+               return -ENODEV;
+
+       guard(mutex)(&priv->mutex);
+
+       if (seq_buf_has_overflowed(buf))
+               return -E2BIG;
+
+       if (buf->size < buf->len + count) {
+               seq_buf_set_overflow(buf);
+               kunit_warn(test, "KUnit UAPI line buffer has overflowed\n");
+               return -E2BIG;
+       }
+
+       if (copy_from_user(buf->buffer + buf->len, ubuf, count))
+               return -EFAULT;
+
+       buf->len += count;
+
+       kunit_uapi_print_buf_to_log(test, &priv->buf);
+
+       return count;
+}
+
+static int kunit_uapi_log_release(struct inode *ino, struct file *file)
+{
+       struct kunit_uapi_log_private *priv = file->private_data;
+       struct kunit *test;
+
+       mutex_destroy(&priv->mutex);
+
+       test = kunit_get_current_test();
+       if (!test) {
+               kfree(priv);
+               return -ENODEV;
+       }
+
+       /* Flush last partial line */
+       kunit_uapi_log_str(test, priv->buf.buffer, priv->buf.len);
+       kunit_uapi_log_str(test, "\n", 1);
+
+       kfree(priv);
+       return 0;
+}
+
+static const struct file_operations kunit_uapi_log_fops = {
+       .owner          = THIS_MODULE,
+       .open           = kunit_uapi_log_open,
+       .release        = kunit_uapi_log_release,
+       .write          = kunit_uapi_log_write,
+};
+
+static struct miscdevice kunit_uapi_log = {
+       .minor  = MISC_DYNAMIC_MINOR,
+       .name   = KUNIT_LOG_DEVICE,
+       .fops   = &kunit_uapi_log_fops,
+};
+module_misc_device(kunit_uapi_log);
+
 MODULE_DESCRIPTION("KUnit UAPI testing framework");
 MODULE_AUTHOR("Thomas Weißschuh <[email protected]");
 MODULE_LICENSE("GPL");

-- 
2.53.0


Reply via email to