linux-trace-devel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v3 0/4] libtraceevent: Handling cpumask event fields
@ 2022-12-13 16:56 Valentin Schneider
  2022-12-13 16:56 ` [PATCH v3 1/4] libtraceevent: Ensure print_field_raw() terminates with '\0' Valentin Schneider
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Valentin Schneider @ 2022-12-13 16:56 UTC (permalink / raw)
  To: linux-trace-devel
  Cc: Steven Rostedt, Daniel Bristot de Oliveira, Clark Williams,
	Douglas RAILLARD

Hi folks,

While discussing around [1], Steve had the idea [2] of creating a "proper" type
for cpumask event fields so that userspace tooling doesn't have to guess whether
an unsigned long [] is meant to be a cpumask or not.

o Patch 1 is a collateral fix found by running the new units tests.
o Patch 2 introduces a small amount of boiler plate to get cpumask fields on the same level 
  as bitmask fields
o Patch 3 adds pretty-print output for cpumasks
o Patch 4 is unit tests for the above

Cheers,
Valentin

Revisions
=========

v2 -> v3
++++++++
o Rebased on top of latest libtraceevent
o Added unit tests (Steven)
o Moved TEP_PRINT_CPUMASK definition to the tail of tep_print_arg_type (Steven)

v1 -> v2
++++++++
o Rebased on top of latest libtraceevent
o Fixed s/__get_cpumask/__get_rel_cpumask/ typo

[1]: http://lore.kernel.org/r/xhsmhilkqfi7z.mognet@vschneid.remote.csb
[2]: http://lore.kernel.org/r/20221014080456.1d32b989@rorschach.local.home

Valentin Schneider (4):
  libtraceevent: Ensure print_field_raw() terminates with '\0'
  libtraceevent: Add boiler-plate code for cpumask types
  libtraceevent: Pretty-print cpumask fields as a cpulist
  libtraceevent: Add unit tests for cpumask processing

 include/traceevent/event-parse.h |   1 +
 src/event-parse.c                | 191 +++++++++++++++++++++++++++++++
 utest/traceevent-utest.c         | 161 ++++++++++++++++++++++++++
 3 files changed, 353 insertions(+)

--
2.31.1


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH v3 1/4] libtraceevent: Ensure print_field_raw() terminates with '\0'
  2022-12-13 16:56 [PATCH v3 0/4] libtraceevent: Handling cpumask event fields Valentin Schneider
@ 2022-12-13 16:56 ` Valentin Schneider
  2022-12-13 16:56 ` [PATCH v3 2/4] libtraceevent: Add boiler-plate code for cpumask types Valentin Schneider
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: Valentin Schneider @ 2022-12-13 16:56 UTC (permalink / raw)
  To: linux-trace-devel
  Cc: Steven Rostedt, Daniel Bristot de Oliveira, Clark Williams,
	Douglas RAILLARD

Testing printing cpumasks reveals an issue in print_field_raw()'s handling
of arrays: its final operation is trace_seq_putc(']'), which omits a final
'\0'.

The other cases in the function invoke trace_seq_printf() which does the
right thing, only the TEP_FIELD_IS_ARRAY case has that issue. Still, to
prevent any future surprises, add a call to trace_seq_terminate() at the
end of print_field_raw().

Signed-off-by: Valentin Schneider <vschneid@redhat.com>
---
 src/event-parse.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/event-parse.c b/src/event-parse.c
index a6e9e93..093b345 100644
--- a/src/event-parse.c
+++ b/src/event-parse.c
@@ -5681,6 +5681,7 @@ static void print_field_raw(struct trace_seq *s, void *data, int size,
 				trace_seq_printf(s, "%llu", val);
 		}
 	}
+	trace_seq_terminate(s);
 }
 
 static int print_parse_data(struct tep_print_parse *parse, struct trace_seq *s,
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH v3 2/4] libtraceevent: Add boiler-plate code for cpumask types
  2022-12-13 16:56 [PATCH v3 0/4] libtraceevent: Handling cpumask event fields Valentin Schneider
  2022-12-13 16:56 ` [PATCH v3 1/4] libtraceevent: Ensure print_field_raw() terminates with '\0' Valentin Schneider
@ 2022-12-13 16:56 ` Valentin Schneider
  2022-12-13 16:56 ` [PATCH v3 3/4] libtraceevent: Pretty-print cpumask fields as a cpulist Valentin Schneider
  2022-12-13 16:56 ` [PATCH v3 4/4] libtraceevent: Add unit tests for cpumask processing Valentin Schneider
  3 siblings, 0 replies; 5+ messages in thread
From: Valentin Schneider @ 2022-12-13 16:56 UTC (permalink / raw)
  To: linux-trace-devel
  Cc: Steven Rostedt, Daniel Bristot de Oliveira, Clark Williams,
	Douglas RAILLARD

A cpumask event field type was recently added to Linux, which helps
distinguish any odd bitmask from a cpumask. Right now this field type is
not recognized by libtraceevent:

  [ipi:ipi_send_cpumask] function __get_cpumask not defined
  CPU 0 is empty
  CPU 1 is empty
  CPU 3 is empty
  cpus=4
	    echo-173   [002]    11.859745: ipi_send_cpumask:     [FAILED TO PARSE] cpumask=ARRAY[02,
00, 00, 00, 00, 00, 00, 00] callsite=0xffffffff81121013

Since a cpumask is still a bitmask, define the boiler plate code for this
new field type and wire it all to bitmask handling.

Signed-off-by: Valentin Schneider <vschneid@redhat.com>
---
 include/traceevent/event-parse.h |  1 +
 src/event-parse.c                | 24 ++++++++++++++++++++++++
 2 files changed, 25 insertions(+)

diff --git a/include/traceevent/event-parse.h b/include/traceevent/event-parse.h
index 3be98b0..2171ad7 100644
--- a/include/traceevent/event-parse.h
+++ b/include/traceevent/event-parse.h
@@ -244,6 +244,7 @@ enum tep_print_arg_type {
 	TEP_PRINT_BITMASK,
 	TEP_PRINT_DYNAMIC_ARRAY_LEN,
 	TEP_PRINT_HEX_STR,
+	TEP_PRINT_CPUMASK,
 };
 
 struct tep_print_arg {
diff --git a/src/event-parse.c b/src/event-parse.c
index 093b345..ba8e727 100644
--- a/src/event-parse.c
+++ b/src/event-parse.c
@@ -1120,6 +1120,7 @@ static void free_arg(struct tep_print_arg *arg)
 		free(arg->string.string);
 		break;
 	case TEP_PRINT_BITMASK:
+	case TEP_PRINT_CPUMASK:
 		free(arg->bitmask.bitmask);
 		break;
 	case TEP_PRINT_DYNAMIC_ARRAY:
@@ -2853,6 +2854,7 @@ static int arg_num_eval(struct tep_print_arg *arg, long long *val)
 	case TEP_PRINT_STRING:
 	case TEP_PRINT_BSTRING:
 	case TEP_PRINT_BITMASK:
+	case TEP_PRINT_CPUMASK:
 	default:
 		do_warning("invalid eval type %d", arg->type);
 		ret = 0;
@@ -2882,6 +2884,7 @@ static char *arg_eval (struct tep_print_arg *arg)
 	case TEP_PRINT_STRING:
 	case TEP_PRINT_BSTRING:
 	case TEP_PRINT_BITMASK:
+	case TEP_PRINT_CPUMASK:
 	default:
 		do_warning("invalid eval type %d", arg->type);
 		break;
@@ -3362,6 +3365,17 @@ process_bitmask(struct tep_event *event __maybe_unused, struct tep_print_arg *ar
 	return TEP_EVENT_ERROR;
 }
 
+static enum tep_event_type
+process_cpumask(struct tep_event *event __maybe_unused, struct tep_print_arg *arg,
+		char **tok)
+{
+	enum tep_event_type type = process_bitmask(event, arg, tok);
+	if (type != TEP_EVENT_ERROR)
+		arg->type = TEP_PRINT_CPUMASK;
+
+	return type;
+}
+
 static struct tep_function_handler *
 find_func_handler(struct tep_handle *tep, char *func_name)
 {
@@ -3521,6 +3535,11 @@ process_function(struct tep_event *event, struct tep_print_arg *arg,
 		free_token(token);
 		return process_bitmask(event, arg, tok);
 	}
+	if (strcmp(token, "__get_cpumask") == 0 ||
+	    strcmp(token, "__get_rel_cpumask") == 0) {
+		free_token(token);
+		return process_cpumask(event, arg, tok);
+	}
 	if (strcmp(token, "__get_dynamic_array") == 0 ||
 	    strcmp(token, "__get_rel_dynamic_array") == 0 ||
 	    strcmp(token, "__get_sockaddr") == 0 ||
@@ -4181,6 +4200,7 @@ eval_num_arg(void *data, int size, struct tep_event *event, struct tep_print_arg
 	case TEP_PRINT_STRING:
 	case TEP_PRINT_BSTRING:
 	case TEP_PRINT_BITMASK:
+	case TEP_PRINT_CPUMASK:
 		return 0;
 	case TEP_PRINT_FUNC: {
 		struct trace_seq s;
@@ -4674,6 +4694,7 @@ static void print_str_arg(struct trace_seq *s, void *data, int size,
 	case TEP_PRINT_BSTRING:
 		print_str_to_seq(s, format, len_arg, arg->string.string);
 		break;
+	case TEP_PRINT_CPUMASK:
 	case TEP_PRINT_BITMASK: {
 		if (!arg->bitmask.field) {
 			arg->bitmask.field = tep_find_any_field(event, arg->bitmask.bitmask);
@@ -7196,6 +7217,9 @@ static void print_args(struct tep_print_arg *args)
 	case TEP_PRINT_BITMASK:
 		printf("__get_bitmask(%s)", args->bitmask.bitmask);
 		break;
+	case TEP_PRINT_CPUMASK:
+		printf("__get_cpumask(%s)", args->bitmask.bitmask);
+		break;
 	case TEP_PRINT_TYPE:
 		printf("(%s)", args->typecast.type);
 		print_args(args->typecast.item);
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH v3 3/4] libtraceevent: Pretty-print cpumask fields as a cpulist
  2022-12-13 16:56 [PATCH v3 0/4] libtraceevent: Handling cpumask event fields Valentin Schneider
  2022-12-13 16:56 ` [PATCH v3 1/4] libtraceevent: Ensure print_field_raw() terminates with '\0' Valentin Schneider
  2022-12-13 16:56 ` [PATCH v3 2/4] libtraceevent: Add boiler-plate code for cpumask types Valentin Schneider
@ 2022-12-13 16:56 ` Valentin Schneider
  2022-12-13 16:56 ` [PATCH v3 4/4] libtraceevent: Add unit tests for cpumask processing Valentin Schneider
  3 siblings, 0 replies; 5+ messages in thread
From: Valentin Schneider @ 2022-12-13 16:56 UTC (permalink / raw)
  To: linux-trace-devel
  Cc: Steven Rostedt, Daniel Bristot de Oliveira, Clark Williams,
	Douglas RAILLARD

Now that we can denote which bitmasks are cpumasks, it makes sense to
pretty-print them to a more user-friendly format: a cpulist.

There's two hurdles to that:
1) Estimating the required string buffer size.

   I've tried to condense it down to an estimator function that is
   computationally simple enough, though it overestimates by ~1/3.

   For reference, this estimates:
   180 bytes for NR_CPUS=64  (x86 defconfig)
   911 bytes for NR_CPUS=256 (arm64 defconfig)

2) Iterating through the bits and bytes.

   The kernel has a collection of carefully crafted bitmask iterators which
   make this relatively simple (cf. bitmap_list_string()), but I didn't
   feel justified in importing half a dozen helpers just for one function.
   I've implemented a "homegrown" byte-parsing logic which isn't the
   fastest, but is at least condensed to a single function.

Signed-off-by: Valentin Schneider <vschneid@redhat.com>
---
 src/event-parse.c | 168 +++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 167 insertions(+), 1 deletion(-)

diff --git a/src/event-parse.c b/src/event-parse.c
index ba8e727..d1363a5 100644
--- a/src/event-parse.c
+++ b/src/event-parse.c
@@ -4491,6 +4491,161 @@ static void print_bitmask_to_seq(struct tep_handle *tep,
 	free(str);
 }
 
+#define log10(n)               \
+(			       \
+	n < 10UL ? 0 :	       \
+	n < 100UL ? 1 :	       \
+	n < 1000UL ? 2 :       \
+	n < 10000UL ? 3 :      \
+	n < 100000UL ? 4 :     \
+	n < 1000000UL ? 5 :    \
+	n < 10000000UL ? 6 :   \
+	n < 100000000UL ? 7 :  \
+	n < 1000000000UL ? 8 : \
+	9		       \
+)
+
+/* ilog10(0) should be 1 but the 0 simplifies below math */
+#define ilog10(n)    \
+(                     \
+	n == 0 ? 0UL : \
+	n == 1 ? 10UL : \
+	n == 2 ? 100UL : \
+	n == 3 ? 1000UL : \
+	n == 4 ? 10000UL : \
+	n == 5 ? 100000UL : \
+	n == 6 ? 1000000UL : \
+	n == 7 ? 10000000UL : \
+	n == 8 ? 100000000UL : \
+		 1000000000UL   \
+)
+
+static unsigned int cpumask_worst_size(unsigned int nr_bits)
+{
+	/*
+	 * Printing all the CPUs separated by a comma is a decent bound for the
+	 * maximum memory required to print a cpumask (a slightly better bound
+	 * is chunks of 2 bits set, i.e. 0-1,3-4,6-7...).
+	 *
+	 * e.g. for nr_bits=132:
+	 * - 131 commas
+	 * - 10 * 1 chars for CPUS [0, 9]
+	 * - 90 * 2 chars for CPUS [10-99]
+	 * - 32 * 3 chars for CPUS [100-131]
+	 */
+	unsigned int last_cpu = nr_bits - 1;
+	unsigned int nr_chars = nr_bits - 1;
+	int last_lvl = log10(last_cpu);
+
+	/* All log10 levels before the last one have all values used */
+	for (int lvl = 0; lvl < last_lvl; lvl++) {
+		int nr_values = ilog10(lvl + 1) - ilog10(lvl);
+
+		nr_chars += nr_values * (lvl + 1);
+	}
+	/* Last level is incomplete */
+	nr_chars += (nr_bits - ilog10(last_lvl)) * (last_lvl + 1);
+
+	return nr_chars;
+}
+
+static void print_cpumask_to_seq(struct tep_handle *tep,
+				 struct trace_seq *s, const char *format,
+				 int len_arg, const void *data, int size)
+{
+	int firstone = -1, firstzero = -1;
+	int nr_bits = size * 8;
+	bool first = true;
+	int str_size = 0;
+	char buf[12]; /* '-' + log10(2^32) + 1 digits + '\0' */
+	char *str;
+	int index;
+	int i;
+
+	str = malloc(cpumask_worst_size(nr_bits) + 1);
+	if (!str) {
+		do_warning("%s: not enough memory!", __func__);
+		return;
+	}
+
+	for (i = 0; i < size; i++) {
+		unsigned char byte;
+		int fmtsize;
+
+		if (tep->file_bigendian)
+			index = size - (i + 1);
+		else
+			index = i;
+
+		/* Byte by byte scan, not the best... */
+		byte = *(((unsigned char *)data) + index);
+more:
+		/* First find a bit set to one...*/
+		if (firstone < 0 && byte) {
+			/*
+			 * Set all lower bits, so a later ffz on this same byte
+			 * is guaranteed to find a later bit.
+			 */
+			firstone = ffs(byte) - 1;
+			byte |= (1 << firstone) - 1;
+			firstone += i * 8;
+		}
+
+		if (firstone < 0)
+			continue;
+
+		/* ...Then find a bit set to zero */
+		if ((~byte) & 0xFF) {
+			/*
+			 * Clear all lower bits, so a later ffs on this same
+			 * byte is guaranteed to find a later bit.
+			 */
+			firstzero = ffs(~byte) - 1;
+			byte &= ~((1 << (firstzero)) - 1);
+			firstzero += i * 8;
+		} else if (i == size - 1) { /* ...Or reach the end of the mask */
+			firstzero = nr_bits;
+			byte = 0;
+		} else {
+			continue;
+		}
+
+		/* We've found a bit set to one, and a later bit set to zero. */
+		if (!first) {
+			str[str_size] = ',';
+			str_size++;
+		}
+		first = false;
+
+		/* It takes {log10(number) + 1} chars to format a number */
+		fmtsize = log10(firstone) + 1;
+		snprintf(buf, fmtsize + 1, "%d", firstone);
+		memcpy(str + str_size, buf, fmtsize);
+		str_size += fmtsize;
+
+		if (firstzero > firstone + 1) {
+			fmtsize = log10(firstzero - 1) + 2;
+			snprintf(buf, fmtsize + 1, "-%d", firstzero - 1);
+			memcpy(str + str_size, buf, fmtsize);
+			str_size += fmtsize;
+		}
+
+		firstzero = firstone = -1;
+		if (byte)
+			goto more;
+	}
+
+	str[str_size] = 0;
+	str_size++;
+
+	if (len_arg >= 0)
+		trace_seq_printf(s, format, len_arg, str);
+	else
+		trace_seq_printf(s, format, str);
+
+	free(str);
+}
+
 static void print_str_arg(struct trace_seq *s, void *data, int size,
 			  struct tep_event *event, const char *format,
 			  int len_arg, struct tep_print_arg *arg)
@@ -4694,7 +4849,6 @@ static void print_str_arg(struct trace_seq *s, void *data, int size,
 	case TEP_PRINT_BSTRING:
 		print_str_to_seq(s, format, len_arg, arg->string.string);
 		break;
-	case TEP_PRINT_CPUMASK:
 	case TEP_PRINT_BITMASK: {
 		if (!arg->bitmask.field) {
 			arg->bitmask.field = tep_find_any_field(event, arg->bitmask.bitmask);
@@ -4707,6 +4861,18 @@ static void print_str_arg(struct trace_seq *s, void *data, int size,
 				     data + offset, len);
 		break;
 	}
+	case TEP_PRINT_CPUMASK: {
+		if (!arg->bitmask.field) {
+			arg->bitmask.field = tep_find_any_field(event, arg->bitmask.bitmask);
+			arg->bitmask.offset = arg->bitmask.field->offset;
+		}
+		if (!arg->bitmask.field)
+			break;
+		dynamic_offset_field(tep, arg->bitmask.field, data, size, &offset, &len);
+		print_cpumask_to_seq(tep, s, format, len_arg,
+				     data + offset, len);
+		break;
+	}
 	case TEP_PRINT_OP:
 		/*
 		 * The only op for string should be ? :
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH v3 4/4] libtraceevent: Add unit tests for cpumask processing
  2022-12-13 16:56 [PATCH v3 0/4] libtraceevent: Handling cpumask event fields Valentin Schneider
                   ` (2 preceding siblings ...)
  2022-12-13 16:56 ` [PATCH v3 3/4] libtraceevent: Pretty-print cpumask fields as a cpulist Valentin Schneider
@ 2022-12-13 16:56 ` Valentin Schneider
  3 siblings, 0 replies; 5+ messages in thread
From: Valentin Schneider @ 2022-12-13 16:56 UTC (permalink / raw)
  To: linux-trace-devel
  Cc: Steven Rostedt, Daniel Bristot de Oliveira, Clark Williams,
	Douglas RAILLARD

Add units tests that cover the TEP_PRINT_CPUMASK cases.

Sicne print_cpumask_to_seq() is based on a per-byte scan, add some tests
that cross byte boundaries when looking for the next non-zero byte.

Signed-off-by: Valentin Schneider <vschneid@redhat.com>
---
 utest/traceevent-utest.c | 161 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 161 insertions(+)

diff --git a/utest/traceevent-utest.c b/utest/traceevent-utest.c
index 99900de..4fc09e5 100644
--- a/utest/traceevent-utest.c
+++ b/utest/traceevent-utest.c
@@ -100,6 +100,86 @@ static char dyn_str_old_data[] = {
 };
 static void *dyn_str_old_event_data = (void *)dyn_str_old_data;
 
+#define CPUMASK_EVENT_SYSTEM "ipi"
+#define CPUMASK_EVENT_FIELD  "cpumask"
+static const char cpumask_event_format[] =
+	"name: ipi_send_cpumask\n"
+	"ID: 3\n"
+	"format:\n"
+	"\tfield:unsigned short common_type;\toffset:0;\tsize:2;\n"
+	"\tfield:unsigned char common_flags;\toffset:2;\tsize:1;\n"
+	"\tfield:unsigned char common_preempt_count;\toffset:3;\tsize:1;\n"
+	"\tfield:int common_pid;\toffset:4;\tsize:4;\n"
+	"\n"
+	"\tfield:__data_loc cpumask_t *[] cpumask;\toffset:8;\tsize:4;\tsigned:0;\n"
+	"\n"
+	"print fmt: \"cpumask=%s\", __get_cpumask(cpumask)\n";
+
+/* Mind the endianness! */
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+#define DECL_CPUMASK_EVENT_DATA(name, args...)			\
+	static char cpumask_##name##_event_data[] = {		\
+	/* common type */		3, 0x00,                \
+	/* common flags */		0x00,                   \
+	/* common_preempt_count */	0x00,                   \
+	/* common_pid */		0x00, 0x00, 0x00, 0x00, \
+	/* [offset, size] */            16, 0x00, 8, 0x00,      \
+	/* padding */			0x00, 0x00, 0x00, 0x00, \
+	/* cpumask */                   args,                   \
+}
+#else
+#define DECL_CPUMASK_EVENT_DATA(name, args...)			\
+static char cpumask_##name##_event_data[] = {                       \
+	/* common type */		0x00, 3,                \
+	/* common flags */		0x00,                   \
+	/* common_preempt_count */	0x00,                   \
+	/* common_pid */		0x00, 0x00, 0x00, 0x00, \
+	/* [offset, size] */            0x00, 8, 0x00, 16,      \
+	/* padding */			0x00, 0x00, 0x00, 0x00, \
+	/* cpumask */                   args,                   \
+}
+#endif
+
+DECL_CPUMASK_EVENT_DATA(full, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff);
+#define CPUMASK_FULL     "ARRAY[ff, ff, ff, ff, ff, ff, ff, ff]"
+#define CPUMASK_FULL_FMT "cpumask=0-63"
+
+DECL_CPUMASK_EVENT_DATA(empty, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
+#define CPUMASK_EMPTY     "ARRAY[00, 00, 00, 00, 00, 00, 00, 00]"
+#define CPUMASK_EMPTY_FMT "cpumask="
+
+DECL_CPUMASK_EVENT_DATA(half, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55);
+#define CPUMASK_HALF     "ARRAY[55, 55, 55, 55, 55, 55, 55, 55]"
+#define CPUMASK_HALF_FMT "cpumask=0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62"
+
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+DECL_CPUMASK_EVENT_DATA(bytep1, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
+#define CPUMASK_BYTEP1     "ARRAY[01, 80, 00, 00, 00, 00, 00, 00]"
+#define CPUMASK_BYTEP1_FMT "cpumask=0,15"
+
+DECL_CPUMASK_EVENT_DATA(bytep2, 0x01, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00);
+#define CPUMASK_BYTEP2     "ARRAY[01, 00, 80, 00, 00, 00, 00, 00]"
+#define CPUMASK_BYTEP2_FMT "cpumask=0,23"
+
+DECL_CPUMASK_EVENT_DATA(bytepn, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80);
+#define CPUMASK_BYTEPN     "ARRAY[01, 00, 00, 00, 00, 00, 00, 80]"
+#define CPUMASK_BYTEPN_FMT "cpumask=0,63"
+
+#else
+
+DECL_CPUMASK_EVENT_DATA(bytep1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01);
+#define CPUMASK_BYTEP1     "ARRAY[00, 00, 00, 00, 00, 00, 80, 01]"
+#define CPUMASK_BYTEP1_FMT "cpumask=0,15"
+
+DECL_CPUMASK_EVENT_DATA(bytep2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x01);
+#define CPUMASK_BYTEP2     "ARRAY[00, 00, 00, 00, 00, 80, 00, 01]"
+#define CPUMASK_BYTEP2_FMT "cpumask=0,23"
+
+DECL_CPUMASK_EVENT_DATA(bytepn, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01);
+#define CPUMASK_BYTEPN     "ARRAY[80, 00, 00, 00, 00, 00, 80, 01]"
+#define CPUMASK_BYTEPN_FMT "cpumask=0,63"
+#endif
+
 static struct tep_handle *test_tep;
 static struct trace_seq *test_seq;
 static struct trace_seq seq_storage;
@@ -139,6 +219,75 @@ static void test_parse_dyn_str_old_event(void)
 	parse_dyn_str(dyn_str_old_event, dyn_str_old_event_data, sizeof(dyn_str_old_data));
 }
 
+static void parse_cpumask(const char *format, void *data, int size,
+			  const char* expected_raw, const char* expected)
+{
+	struct tep_format_field *field;
+	struct tep_event *event;
+	struct tep_record record;
+
+	record.data = data;
+	record.size = size;
+
+	CU_TEST(tep_parse_format(test_tep, &event,
+				 format, strlen(format),
+				 CPUMASK_EVENT_SYSTEM) == TEP_ERRNO__SUCCESS);
+
+	field = tep_find_any_field(event, CPUMASK_EVENT_FIELD);
+	CU_TEST(field != NULL);
+
+	trace_seq_reset(test_seq);
+	tep_print_field_content(test_seq, data, size, field);
+	CU_TEST(strcmp(test_seq->buffer, expected_raw) == 0);
+
+	trace_seq_reset(test_seq);
+	tep_print_event(test_tep, test_seq, &record, "%s", TEP_PRINT_INFO);
+	trace_seq_do_printf(test_seq);
+	CU_TEST(strcmp(test_seq->buffer, expected) == 0);
+}
+
+static void test_parse_cpumask_full(void)
+{
+	parse_cpumask(cpumask_event_format,
+		      cpumask_full_event_data, sizeof(cpumask_full_event_data),
+		      CPUMASK_FULL, CPUMASK_FULL_FMT);
+}
+
+static void test_parse_cpumask_empty(void)
+{
+	parse_cpumask(cpumask_event_format,
+		      cpumask_empty_event_data, sizeof(cpumask_empty_event_data),
+		      CPUMASK_EMPTY, CPUMASK_EMPTY_FMT);
+}
+
+static void test_parse_cpumask_half(void)
+{
+	parse_cpumask(cpumask_event_format,
+		      cpumask_half_event_data, sizeof(cpumask_half_event_data),
+		      CPUMASK_HALF, CPUMASK_HALF_FMT);
+}
+
+static void test_parse_cpumask_bytep1(void)
+{
+	parse_cpumask(cpumask_event_format,
+		      cpumask_bytep1_event_data, sizeof(cpumask_bytep1_event_data),
+		      CPUMASK_BYTEP1, CPUMASK_BYTEP1_FMT);
+}
+
+static void test_parse_cpumask_bytep2(void)
+{
+	parse_cpumask(cpumask_event_format,
+		      cpumask_bytep2_event_data, sizeof(cpumask_bytep2_event_data),
+		      CPUMASK_BYTEP2, CPUMASK_BYTEP2_FMT);
+}
+
+static void test_parse_cpumask_bytepn(void)
+{
+	parse_cpumask(cpumask_event_format,
+		      cpumask_bytepn_event_data, sizeof(cpumask_bytepn_event_data),
+		      CPUMASK_BYTEPN, CPUMASK_BYTEPN_FMT);
+}
+
 static int test_suite_destroy(void)
 {
 	tep_free(test_tep);
@@ -169,4 +318,16 @@ void test_traceevent_lib(void)
 		    test_parse_dyn_str_event);
 	CU_add_test(suite, "parse old dynamic string event",
 		    test_parse_dyn_str_old_event);
+	CU_add_test(suite, "parse full cpumask",
+		    test_parse_cpumask_full);
+	CU_add_test(suite, "parse empty cpumask",
+		    test_parse_cpumask_empty);
+	CU_add_test(suite, "parse half-filled cpumask",
+		    test_parse_cpumask_half);
+	CU_add_test(suite, "parse cpumask spanning 2 bytes",
+		    test_parse_cpumask_bytep1);
+	CU_add_test(suite, "parse cpumask spanning 3 bytes",
+		    test_parse_cpumask_bytep2);
+	CU_add_test(suite, "parse cpumask spanning all bytes",
+		    test_parse_cpumask_bytepn);
 }
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2022-12-13 17:04 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2022-12-13 16:56 [PATCH v3 0/4] libtraceevent: Handling cpumask event fields Valentin Schneider
2022-12-13 16:56 ` [PATCH v3 1/4] libtraceevent: Ensure print_field_raw() terminates with '\0' Valentin Schneider
2022-12-13 16:56 ` [PATCH v3 2/4] libtraceevent: Add boiler-plate code for cpumask types Valentin Schneider
2022-12-13 16:56 ` [PATCH v3 3/4] libtraceevent: Pretty-print cpumask fields as a cpulist Valentin Schneider
2022-12-13 16:56 ` [PATCH v3 4/4] libtraceevent: Add unit tests for cpumask processing Valentin Schneider

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).