Received: by 2002:ac0:946b:0:0:0:0:0 with SMTP id j40csp188526imj; Thu, 14 Feb 2019 18:16:46 -0800 (PST) X-Google-Smtp-Source: AHgI3Ia7/EdHBniX27EmgVkcFyxdRTyGPoGsRbIW6azKNAVfM8STbKP1xEWAmaUgjJWqyezLrDFm X-Received: by 2002:a63:d444:: with SMTP id i4mr6858027pgj.237.1550197005955; Thu, 14 Feb 2019 18:16:45 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1550197005; cv=none; d=google.com; s=arc-20160816; b=aCrOZLuMKqlnl3XUIL8WeNpSiMKK2BIOpCX3YHS94WZUt7V47UuiZeUl4cHE93rPl9 MtVdM/F0oSit35NqmLaPOlQDOeGSJOCDwHHDuXa0utChy4JcYa+18XDNKAbim57WMTRi +ebShG++h5ITYJsaSAGzuOk85z0CzrDpCJN902tT4MgUFW30s9sXyHL14tloIfq/iFwV /Zf5MYeeOIPaBXEy43ofOPc2jnktw04Tg48iCQ6hfeZuZjT9/O+9UJBqdZecaNgdottB YaZ6Q5o7bDjaPwIpq09Q+CaMuiezzYeX2Xy9u0NmqhwKe6GNdyOYyOqnWvx9GRJPT6Hk 0KAg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:cc:to:from:subject:references :mime-version:message-id:in-reply-to:date:dkim-signature; bh=iSAngcIaWP81iz096aRbzzNpQNwIWf0zdQBcJK8kOLU=; b=z2Vfe6g1SZHW33KXEMh9WRwoU0IzABeIXilDwXj0CUpG5XMs0+byKCa7ijPgTqOf6u NtYYk5nXJi0jbNYurDdLTtSE96Loh5UqSmokPLCmzz1cAmIzl2zP0bWyf3fNQqQL2EQU v/pTYREOjgqn22xj0MPv1PDpoMEtNcLNlNGzOViuVw1eiga3+1rmeBfL4p46UB3BWCHr P6htT+miqrSiBoXmxcVxxZu1SOnTlwpCqokOQxcL4tqUQ6pgwnY4dniT2PYmBCERGcds vER9AWLKJi1s0xH7TIpMoQeBL+lRa9zlyE9sGaTeVCF7WTDXY4uqqlkZ9xmoWxtIBohm gifg== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@google.com header.s=20161025 header.b=unyX3vp5; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id c10si4107444pls.271.2019.02.14.18.16.29; Thu, 14 Feb 2019 18:16:45 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@google.com header.s=20161025 header.b=unyX3vp5; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2438068AbfBNVii (ORCPT + 99 others); Thu, 14 Feb 2019 16:38:38 -0500 Received: from mail-it1-f202.google.com ([209.85.166.202]:54229 "EHLO mail-it1-f202.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2406057AbfBNVih (ORCPT ); Thu, 14 Feb 2019 16:38:37 -0500 Received: by mail-it1-f202.google.com with SMTP id i65so11687968ite.3 for ; Thu, 14 Feb 2019 13:38:36 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=iSAngcIaWP81iz096aRbzzNpQNwIWf0zdQBcJK8kOLU=; b=unyX3vp5W4a7b7+lOGI3B+B9v/eL3j7geJQKvlIjiBix/xEpqvZdIycDSczhbZ3tdJ PZ/BTKBfYmqBocAk7EMmC36owS+xpvM0psah9nflpf6tWtCC5T2aojz6bSqEhZMa2wXF a62POdTzkDp9adb2YVs+1hoRA1DdwEk861n6Zrbg33QrAkB8Yf8xaFi6xax02IrezVX9 JSUs8eeNbDYOjCPTctLi0qIeaERR5ESUKnnHKYY63XCGzhIjpuk0CmrXUsv5jPKVDoHa j5Pibr5dzDJMHtCdWgoPHwNjbRnMPl1N/Zqs0aVWwdGN72yVtBHVMToeCcweqzIyTdPj u0/w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=iSAngcIaWP81iz096aRbzzNpQNwIWf0zdQBcJK8kOLU=; b=hQIq+YGro4B3Jt3mZvbTygl6RKbF9p0nuOR71tJqQqjDCFZu+R7mwLOj7Xinn8aqaZ HTtXrO380PZFs8HpKhvyr6kq/vXzb8wIUo2mX4g/UlmUD7886ESZ7fRCcFyiMouQptmA LMaG/rdAsPXveZPRqLkvXVkyN3fAdlHwvSB5ArAMkF6RXCUCkXPq3UjDDvumwLFySfyG kxA66U4K4PTfeaKhQpUhjEn4XHHAUqxm0uVU53XFF75zgfsbhhPIWWrnSZ0R2XvDyUAO JPlRW3leJCOdUL3GpZfzQVyCRKLDCLifPtpDPqYKJ7sbo8/EP9BOS6j89zdVQ/oEsM0E vmOQ== X-Gm-Message-State: AHQUAuYBf76S1NBbjtVmsoj99Bn1x8qmOTEY2XqbMRHs9a/kObIkwIUm GO1S+568sb5xgSunJqNu9r8GWwnVOgVlkn7H6p7aDg== X-Received: by 2002:a24:2782:: with SMTP id g124mr3818359ita.26.1550180315694; Thu, 14 Feb 2019 13:38:35 -0800 (PST) Date: Thu, 14 Feb 2019 13:37:15 -0800 In-Reply-To: <20190214213729.21702-1-brendanhiggins@google.com> Message-Id: <20190214213729.21702-4-brendanhiggins@google.com> Mime-Version: 1.0 References: <20190214213729.21702-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.21.0.rc0.258.g878e2cd30e-goog Subject: [RFC v4 03/17] kunit: test: add string_stream a std::stream like string builder From: Brendan Higgins To: keescook@google.com, mcgrof@kernel.org, shuah@kernel.org, robh@kernel.org, kieran.bingham@ideasonboard.com, frowand.list@gmail.com Cc: gregkh@linuxfoundation.org, joel@jms.id.au, mpe@ellerman.id.au, joe@perches.com, brakmo@fb.com, rostedt@goodmis.org, Tim.Bird@sony.com, khilman@baylibre.com, julia.lawall@lip6.fr, linux-kselftest@vger.kernel.org, kunit-dev@googlegroups.com, linux-kernel@vger.kernel.org, jdike@addtoit.com, richard@nod.at, linux-um@lists.infradead.org, daniel@ffwll.ch, dri-devel@lists.freedesktop.org, dan.j.williams@intel.com, linux-nvdimm@lists.01.org, knut.omang@oracle.com, devicetree@vger.kernel.org, pmladek@suse.com, Alexander.Levin@microsoft.com, amir73il@gmail.com, dan.carpenter@oracle.com, wfg@linux.intel.com, Brendan Higgins Content-Type: text/plain; charset="UTF-8" Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org A number of test features need to do pretty complicated string printing where it may not be possible to rely on a single preallocated string with parameters. So provide a library for constructing the string as you go similar to C++'s std::string. Signed-off-by: Brendan Higgins --- Changes Since Last Version - None. There was some discussion about maybe trying to generalize this or replace it with something existing, but it didn't seem feasible to generalize this, and there wasn't really anything that is a great replacement. --- include/kunit/string-stream.h | 44 ++++++++++ kunit/Makefile | 3 +- kunit/string-stream.c | 149 ++++++++++++++++++++++++++++++++++ 3 files changed, 195 insertions(+), 1 deletion(-) create mode 100644 include/kunit/string-stream.h create mode 100644 kunit/string-stream.c diff --git a/include/kunit/string-stream.h b/include/kunit/string-stream.h new file mode 100644 index 0000000000000..280ee67559588 --- /dev/null +++ b/include/kunit/string-stream.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * C++ stream style string builder used in KUnit for building messages. + * + * Copyright (C) 2019, Google LLC. + * Author: Brendan Higgins + */ + +#ifndef _KUNIT_STRING_STREAM_H +#define _KUNIT_STRING_STREAM_H + +#include +#include +#include +#include + +struct string_stream_fragment { + struct list_head node; + char *fragment; +}; + +struct string_stream { + size_t length; + struct list_head fragments; + + /* length and fragments are protected by this lock */ + spinlock_t lock; + struct kref refcount; + int (*add)(struct string_stream *this, const char *fmt, ...); + int (*vadd)(struct string_stream *this, const char *fmt, va_list args); + char *(*get_string)(struct string_stream *this); + void (*clear)(struct string_stream *this); + bool (*is_empty)(struct string_stream *this); +}; + +struct string_stream *new_string_stream(void); + +void destroy_string_stream(struct string_stream *stream); + +void string_stream_get(struct string_stream *stream); + +int string_stream_put(struct string_stream *stream); + +#endif /* _KUNIT_STRING_STREAM_H */ diff --git a/kunit/Makefile b/kunit/Makefile index 5efdc4dea2c08..275b565a0e81f 100644 --- a/kunit/Makefile +++ b/kunit/Makefile @@ -1 +1,2 @@ -obj-$(CONFIG_KUNIT) += test.o +obj-$(CONFIG_KUNIT) += test.o \ + string-stream.o diff --git a/kunit/string-stream.c b/kunit/string-stream.c new file mode 100644 index 0000000000000..e90fb595a5607 --- /dev/null +++ b/kunit/string-stream.c @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * C++ stream style string builder used in KUnit for building messages. + * + * Copyright (C) 2019, Google LLC. + * Author: Brendan Higgins + */ + +#include +#include +#include + +static int string_stream_vadd(struct string_stream *this, + const char *fmt, + va_list args) +{ + struct string_stream_fragment *fragment; + int len; + va_list args_for_counting; + unsigned long flags; + + /* Make a copy because `vsnprintf` could change it */ + va_copy(args_for_counting, args); + + /* Need space for null byte. */ + len = vsnprintf(NULL, 0, fmt, args_for_counting) + 1; + + va_end(args_for_counting); + + fragment = kmalloc(sizeof(*fragment), GFP_KERNEL); + if (!fragment) + return -ENOMEM; + + fragment->fragment = kmalloc(len, GFP_KERNEL); + if (!fragment->fragment) { + kfree(fragment); + return -ENOMEM; + } + + len = vsnprintf(fragment->fragment, len, fmt, args); + spin_lock_irqsave(&this->lock, flags); + this->length += len; + list_add_tail(&fragment->node, &this->fragments); + spin_unlock_irqrestore(&this->lock, flags); + return 0; +} + +static int string_stream_add(struct string_stream *this, const char *fmt, ...) +{ + va_list args; + int result; + + va_start(args, fmt); + result = string_stream_vadd(this, fmt, args); + va_end(args); + return result; +} + +static void string_stream_clear(struct string_stream *this) +{ + struct string_stream_fragment *fragment, *fragment_safe; + unsigned long flags; + + spin_lock_irqsave(&this->lock, flags); + list_for_each_entry_safe(fragment, + fragment_safe, + &this->fragments, + node) { + list_del(&fragment->node); + kfree(fragment->fragment); + kfree(fragment); + } + this->length = 0; + spin_unlock_irqrestore(&this->lock, flags); +} + +static char *string_stream_get_string(struct string_stream *this) +{ + struct string_stream_fragment *fragment; + size_t buf_len = this->length + 1; /* +1 for null byte. */ + char *buf; + unsigned long flags; + + buf = kzalloc(buf_len, GFP_KERNEL); + if (!buf) + return NULL; + + spin_lock_irqsave(&this->lock, flags); + list_for_each_entry(fragment, &this->fragments, node) + strlcat(buf, fragment->fragment, buf_len); + spin_unlock_irqrestore(&this->lock, flags); + + return buf; +} + +static bool string_stream_is_empty(struct string_stream *this) +{ + bool is_empty; + unsigned long flags; + + spin_lock_irqsave(&this->lock, flags); + is_empty = list_empty(&this->fragments); + spin_unlock_irqrestore(&this->lock, flags); + + return is_empty; +} + +void destroy_string_stream(struct string_stream *stream) +{ + stream->clear(stream); + kfree(stream); +} + +static void string_stream_destroy(struct kref *kref) +{ + struct string_stream *stream = container_of(kref, + struct string_stream, + refcount); + destroy_string_stream(stream); +} + +struct string_stream *new_string_stream(void) +{ + struct string_stream *stream = kzalloc(sizeof(*stream), GFP_KERNEL); + + if (!stream) + return NULL; + + INIT_LIST_HEAD(&stream->fragments); + spin_lock_init(&stream->lock); + kref_init(&stream->refcount); + stream->add = string_stream_add; + stream->vadd = string_stream_vadd; + stream->get_string = string_stream_get_string; + stream->clear = string_stream_clear; + stream->is_empty = string_stream_is_empty; + return stream; +} + +void string_stream_get(struct string_stream *stream) +{ + kref_get(&stream->refcount); +} + +int string_stream_put(struct string_stream *stream) +{ + return kref_put(&stream->refcount, &string_stream_destroy); +} + -- 2.21.0.rc0.258.g878e2cd30e-goog