linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/9] libtracefs: APIs to read a trace event hist file
@ 2021-08-10 20:48 Steven Rostedt
  2021-08-10 20:48 ` [PATCH 1/9] tracefs: Add API tracefs_hist_data_parse() Steven Rostedt
                   ` (8 more replies)
  0 siblings, 9 replies; 11+ messages in thread
From: Steven Rostedt @ 2021-08-10 20:48 UTC (permalink / raw)
  To: linux-trace-devel
  Cc: linux-kernel, Tom Zanussi, Daniel Bristot de Oliveira,
	Masami Hiramatsu, Namhyung Kim, linux-rt-users, Clark Williams,
	Steven Rostedt (VMware)

From: "Steven Rostedt (VMware)" <rostedt@goodmis.org>

The hist trigger for trace events will create a histogram that can be read
in the trace event's hist file. The file is human readable ASCII format, but
that makes it difficult to process in programs. The tracefs_hist_data*()
functions convert the histogram ASCII format into structures that can be
processed and converted into tables.

This patch series creates an API to read and parse this data into machine
code readable structures that can then be processed. There's a working
program in the man page.

I found the parsing of the hist file to be somewhat trivial where I only
needed to implement flex to do most of the work and was able to avoid using
bison. That said, there are still some histograms that can fail to parse.
Namely, if any key has a comma (,) or a colon (:) in it. This includes exec
names if there's a program name with a comma or colon. This can be fixed with
a bit more clever tricks with the lexer, but I'll add those if this becomes
an issue.

This series is also in my personal github account here:

  https://github.com/rostedt/libtracefs/tree/read-hist

Steven Rostedt (VMware) (9):
  tracefs: Add API tracefs_hist_data_parse()
  libtracefs: Parse comment for hist data information
  libtracefs: Change hist_data_key type to flags
  libtracefs: Add API tracefs_hist_data_read()
  libtracefs: Add API tracefs_list_dup()
  libtracefs: Add APIs tracefs_hist_data_keys/value_names()
  libtracefs: Add API tracefs_hist_data_keys/values() and next_bucket()
  libtracefs: Have tracefs_hist_bucket_key flags save the type
  libtracefs: Add man pages for tracefs_hist_data functions

 Documentation/libtracefs-hist-data-2.txt |  346 +++++++
 Documentation/libtracefs-hist-data.txt   |  294 ++++++
 include/tracefs.h                        |   54 +
 src/Makefile                             |    7 +
 src/tracefs-hist-data.c                  | 1175 ++++++++++++++++++++++
 src/tracefs-utils.c                      |   26 +
 6 files changed, 1902 insertions(+)
 create mode 100644 Documentation/libtracefs-hist-data-2.txt
 create mode 100644 Documentation/libtracefs-hist-data.txt
 create mode 100644 src/tracefs-hist-data.c

-- 
2.30.2


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

* [PATCH 1/9] tracefs: Add API tracefs_hist_data_parse()
  2021-08-10 20:48 [PATCH 0/9] libtracefs: APIs to read a trace event hist file Steven Rostedt
@ 2021-08-10 20:48 ` Steven Rostedt
  2021-09-10 16:54   ` Daniel Bristot de Oliveira
  2021-08-10 20:48 ` [PATCH 2/9] libtracefs: Parse comment for hist data information Steven Rostedt
                   ` (7 subsequent siblings)
  8 siblings, 1 reply; 11+ messages in thread
From: Steven Rostedt @ 2021-08-10 20:48 UTC (permalink / raw)
  To: linux-trace-devel
  Cc: linux-kernel, Tom Zanussi, Daniel Bristot de Oliveira,
	Masami Hiramatsu, Namhyung Kim, linux-rt-users, Clark Williams,
	Steven Rostedt (VMware)

From: "Steven Rostedt (VMware)" <rostedt@goodmis.org>

Add a function tracefs_hist_data_parse() that will take the content of a
trace event's hist data file, and parse it into a "tracefs_hist_data"
descriptor that can be used to read the raw data from the file.

Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
---
 include/tracefs.h       |   7 +
 src/Makefile            |   7 +
 src/tracefs-hist-data.c | 861 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 875 insertions(+)
 create mode 100644 src/tracefs-hist-data.c

diff --git a/include/tracefs.h b/include/tracefs.h
index 17020de0108a..6bd40d72cb25 100644
--- a/include/tracefs.h
+++ b/include/tracefs.h
@@ -413,6 +413,13 @@ static inline int tracefs_hist_destroy(struct tracefs_instance *instance,
 	return tracefs_hist_command(instance, hist, TRACEFS_HIST_CMD_DESTROY);
 }
 
+struct tracefs_hist_data;
+
+struct tracefs_hist_data *tracefs_hist_data_parse(const char *buffer,
+						  const char **next_buffer,
+						  char **err);
+void tracefs_hist_data_free(struct tracefs_hist_data *hdata);
+
 struct tracefs_synth;
 
 /*
diff --git a/src/Makefile b/src/Makefile
index 9248efc5c7fd..1ab181416b82 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -17,6 +17,10 @@ OBJS += sqlhist-lex.o
 OBJS += sqlhist.tab.o
 OBJS += tracefs-sqlhist.o
 
+# Order matters for the the two below
+OBJS += hist-lex.o
+OBJS += tracefs-hist-data.o
+
 OBJS := $(OBJS:%.o=$(bdir)/%.o)
 DEPS := $(OBJS:$(bdir)/%.o=$(bdir)/.%.d)
 
@@ -45,6 +49,9 @@ sqlhist.tab.c: sqlhist.y sqlhist.tab.h
 sqlhist-lex.c: sqlhist.l sqlhist.tab.c
 	flex -o $@ $<
 
+hist-lex.c: hist.l
+	flex -P hist_ -o $@ $<
+
 $(bdir)/%.o: %.c
 	$(Q)$(call do_fpic_compile)
 
diff --git a/src/tracefs-hist-data.c b/src/tracefs-hist-data.c
new file mode 100644
index 000000000000..497ab9ce97b4
--- /dev/null
+++ b/src/tracefs-hist-data.c
@@ -0,0 +1,861 @@
+// SPDX-License-Identifier: LGPL-2.1
+/*
+ * Copyright (C) 2021 VMware Inc, Steven Rostedt <rostedt@goodmis.org>
+ *
+ * Updates:
+ * Copyright (C) 2021, VMware, Tzvetomir Stoyanov <tz.stoyanov@gmail.com>
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+
+#include "tracefs.h"
+#include "tracefs-local.h"
+
+#define HIST_FILE "hist"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <tracefs.h>
+
+#include "hist.h"
+
+#define offset_of(type, field)	((unsigned long )(&((type *)0)->field))
+#define container_of(p, type, field) ((type *)((void *)(p) - offset_of(type, field)));
+
+extern int hist_lex_init_extra(void *data, void* ptr_yy_globals);
+extern int hist_lex_destroy(void *scanner);
+
+int hist_yyinput(void *extra, char *buf, int max)
+{
+	struct hist_data *data = extra;
+
+	if (!data || !data->buffer)
+		return -1;
+
+	if (data->buffer_idx + max > data->buffer_size)
+		max = data->buffer_size - data->buffer_idx;
+
+	if (max)
+		memcpy(buf, data->buffer + data->buffer_idx, max);
+
+	data->buffer_idx += max;
+
+	return max;
+}
+
+extern int hist_yylex(void *data, void *scanner);
+
+static char *name_token(enum yytokentype type)
+{
+	switch (type) {
+	case YYEMPTY:
+		return "YYEMPTY";
+	case YYEOF:
+		return "YYEOF";
+	case YYerror:
+		return "YYerror";
+	case YYUNDEF:
+		return "YYUNDEF";
+	case NUMBER:
+		return "NUMBER";
+	case HEX:
+		return "HEX";
+	case NEWLINE:
+		return "NEWLINE";
+	case STRING:
+		return "STRING";
+	case KEY_TYPE:
+		return "KEY_TYPE";
+	case KEY_VAL:
+		return "KEY_VAL";
+	case START_RANGE:
+		return "START_RANGE";
+	case RANGE_LINEAR:
+		return "RANGE_LINEAR";
+	case RANGE_EXPONENT:
+		return "RANGE_EXPONENT";
+	case RAW_VAL:
+		return "RAW_VAL";
+	case STACKTRACE:
+		return "STACKTRACE";
+	case STACK_ITEM:
+		return "STACK_ITEM";
+	case STACK_MOD:
+		return "STACK_MOD";
+	case VALUE:
+		return "VALUE";
+	case TOTALS:
+		return "TOTALS";
+	case HITS:
+		return "HITS";
+	case ENTRIES:
+		return "ENTRIES";
+	case DROPPED:
+		return "DROPPED";
+	case COMMENT:
+		return "COMMENT";
+	case COLON:
+		return "COLON";
+	case COMMA:
+		return "COMMA";
+	}
+	return NULL;
+}
+
+enum tracefs_bucket_key_type {
+	TRACEFS_BUCKET_KEY_UNDEF,
+	TRACEFS_BUCKET_KEY_SINGLE,
+	TRACEFS_BUCKET_KEY_RANGE,
+};
+
+struct tracefs_hist_bucket_key_single {
+	long long		val;
+	char			*sym;
+};
+
+struct tracefs_hist_bucket_key_range {
+	long long		start;
+	long long		end;
+};
+
+struct tracefs_hist_bucket_key {
+	struct tracefs_hist_bucket_key	*next;
+	enum tracefs_bucket_key_type	type;
+	union {
+		struct tracefs_hist_bucket_key_single	single;
+		struct tracefs_hist_bucket_key_range	range;
+	};
+};
+
+struct tracefs_hist_bucket_val {
+	struct tracefs_hist_bucket_val		*next;
+	long long				val;
+};
+
+struct tracefs_hist_bucket {
+	struct tracefs_hist_bucket		*next;
+	struct tracefs_hist_bucket_key		*keys;
+	struct tracefs_hist_bucket_key		**next_key;
+	struct tracefs_hist_bucket_val		*vals;
+	struct tracefs_hist_bucket_val		**next_val;
+};
+
+struct tracefs_hist_data {
+	char				**key_names;
+	char				**value_names;
+	struct tracefs_hist_bucket	*buckets;
+	struct tracefs_hist_bucket	**next_bucket;
+	unsigned long long		hits;
+	unsigned long long		entries;
+	unsigned long long		dropped;
+};
+
+static int do_comment(struct tracefs_hist_data *hdata, const char *comment)
+{
+	return 0;
+}
+
+static int do_key_type(struct tracefs_hist_data *hdata, const char *key)
+{
+	char **tmp;
+
+	tmp = tracefs_list_add(hdata->key_names, key);
+	if (!tmp)
+		return -1;
+	hdata->key_names = tmp;
+
+	return 0;
+}
+
+static int do_value_type(struct tracefs_hist_data *hdata, const char *key)
+{
+	char **tmp;
+
+	tmp = tracefs_list_add(hdata->value_names, key);
+	if (!tmp)
+		return -1;
+	hdata->value_names = tmp;
+
+	return 0;
+}
+
+static int start_new_row(struct tracefs_hist_data *hdata)
+{
+	struct tracefs_hist_bucket *bucket;
+	struct tracefs_hist_bucket_key *key;
+
+	bucket = calloc(1, sizeof(*bucket));
+	if (!bucket)
+		return -1;
+
+	key = calloc(1, sizeof(*key));
+	if (!key) {
+		free(bucket);
+		return -1;
+	}
+
+	bucket->keys = key;
+	bucket->next_key = &key->next;
+
+	bucket->next_val = &bucket->vals;
+
+	*hdata->next_bucket = bucket;
+	hdata->next_bucket = &bucket->next;
+	return 0;
+}
+
+static int start_new_key(struct tracefs_hist_data *hdata)
+{
+	struct tracefs_hist_bucket *bucket;
+	struct tracefs_hist_bucket_key *key;
+
+	bucket = container_of(hdata->next_bucket, struct tracefs_hist_bucket, next);
+
+	key = calloc(1, sizeof(*key));
+	if (!key) {
+		free(bucket);
+		return -1;
+	}
+
+	*bucket->next_key = key;
+	bucket->next_key = &key->next;
+
+	return 0;
+}
+
+static char *chomp(char *text)
+{
+	char *p;
+	int len;
+
+	while (isspace(*text))
+		text++;
+
+	len = strlen(text);
+	p = text + len - 1;
+	while (p >= text && isspace(*p))
+		p--;
+
+	p[1] = '\0';
+
+	return text;
+}
+
+static int __do_key_val(struct tracefs_hist_data *hdata,
+			char *text, const char *delim, const char *end)
+{
+	struct tracefs_hist_bucket *bucket;
+	struct tracefs_hist_bucket_key *key;
+	struct tracefs_hist_bucket_key_single *k;
+	char *val;
+	int len;
+
+	text = chomp(text);
+
+	bucket = container_of(hdata->next_bucket, struct tracefs_hist_bucket, next);
+
+	key = container_of(bucket->next_key, struct tracefs_hist_bucket_key, next);
+	if (!key->type)
+		key->type = TRACEFS_BUCKET_KEY_SINGLE;
+
+	if (key->type != TRACEFS_BUCKET_KEY_SINGLE)
+		return -1;
+
+	k = &key->single;
+
+	len = strlen(text);
+	len += k->sym ? strlen(k->sym) + strlen(delim) : 0;
+	if (end)
+		len += strlen(end);
+
+	val = realloc(k->sym, len + 1);
+	if (!val)
+		return -1;
+
+	if (k->sym)
+		strcat(val, delim);
+	else
+		val[0] = '\0';
+
+	strcat(val, text);
+	if (end)
+		strcat(val, end);
+
+	k->sym = val;
+
+	return 0;
+}
+
+static int do_key_val(struct tracefs_hist_data *hdata, char *text)
+{
+	return __do_key_val(hdata, text, " ", NULL);
+}
+
+static int do_key_stack(struct tracefs_hist_data *hdata, char *text)
+{
+	return __do_key_val(hdata, text, "\n", NULL);
+}
+
+static int do_key_stack_mod(struct tracefs_hist_data *hdata, char *text)
+{
+	return __do_key_val(hdata, text, " [", "]");
+}
+
+static int do_key_raw(struct tracefs_hist_data *hdata, char *text)
+{
+	struct tracefs_hist_bucket *bucket;
+	struct tracefs_hist_bucket_key *key;
+	struct tracefs_hist_bucket_key_single *k;
+
+	text = chomp(text);
+
+	bucket = container_of(hdata->next_bucket, struct tracefs_hist_bucket, next);
+
+	key = container_of(bucket->next_key, struct tracefs_hist_bucket_key, next);
+	if (key->type != TRACEFS_BUCKET_KEY_SINGLE)
+		return -1;
+
+	k = &key->single;
+
+	if (k->val)
+		return -1;
+
+	k->val = strtoll(text, NULL, 0);
+
+	return 0;
+}
+
+static int do_key_range(struct tracefs_hist_data *hdata, long long start,
+			long long end)
+{
+	struct tracefs_hist_bucket *bucket;
+	struct tracefs_hist_bucket_key *key;
+	struct tracefs_hist_bucket_key_range *k;
+
+	bucket = container_of(hdata->next_bucket, struct tracefs_hist_bucket, next);
+
+	key = container_of(bucket->next_key, struct tracefs_hist_bucket_key, next);
+
+	if (!key->type)
+		key->type = TRACEFS_BUCKET_KEY_RANGE;
+
+	if (key->type != TRACEFS_BUCKET_KEY_RANGE)
+		return -1;
+
+	k = &key->range;
+
+	k->start = start;
+	k->end = end;
+
+	return 0;
+}
+
+static int do_value_num(struct tracefs_hist_data *hdata, long long num)
+{
+	struct tracefs_hist_bucket *bucket;
+	struct tracefs_hist_bucket_val *val;
+
+	bucket = container_of(hdata->next_bucket, struct tracefs_hist_bucket, next);
+	val = calloc(1, sizeof(*val));
+	if (!val)
+		return -1;
+
+	val->val = num;
+
+	*bucket->next_val = val;
+	bucket->next_val = &val->next;
+
+	return 0;
+}
+
+static long long expo(unsigned int e, long long exp)
+{
+	long long ret;
+
+	if (exp < 0)
+		exp = 0;
+
+	if (e == 2)
+		return 1LL << exp;
+
+	ret = 1;
+	for (; exp > 0; exp--)
+		ret *= e;
+	return e;
+}
+
+enum hist_state {
+	HIST_START,
+	HIST_KEYS_START,
+	HIST_KEYS,
+	HIST_KEY_VALS,
+	HIST_RANGE,
+	HIST_VALUES,
+	HIST_NEXT_KEY,
+	HIST_STACK,
+	HIST_ENTRIES,
+	HIST_DROPPED,
+	HIST_END,
+};
+
+static const char *find_buffer_line(const char *buffer, int line_no)
+{
+	int line = 0;
+	int i;
+
+	for (i = 0; buffer[i]; i++) {
+		if (buffer[i] == '\n') {
+			line++;
+			if (line >= line_no) {
+				i++;
+				break;
+			}
+		}
+	}
+	return buffer + i;
+}
+
+static void print_line(struct trace_seq *seq, struct hist_data *data)
+{
+	const char *buffer = data->buffer;
+	int i;
+
+	buffer = find_buffer_line(buffer, data->line_no);
+
+	for (i = 0; buffer[i]; i++) {
+		if (buffer[i] == '\n')
+			break;
+	}
+
+	trace_seq_printf(seq, "%.*s (line:%d idx:%d)\n", i, buffer,
+			 data->line_no, data->line_idx);
+	trace_seq_printf(seq, "%*s\n", data->line_idx, "^");
+}
+
+static void print_error(struct hist_data *data, char **err,
+			enum hist_state state, enum yytokentype type)
+{
+	struct trace_seq seq;
+	char *tname;
+
+	if (!err)
+		return;
+
+	trace_seq_init(&seq);
+
+	print_line(&seq, data);
+
+	trace_seq_printf(&seq, "Error in ");
+	switch (state) {
+	case HIST_START:
+		trace_seq_printf(&seq, "HIST_START");
+		break;
+	case HIST_KEYS_START:
+		trace_seq_printf(&seq, "HIST_KEYS_START");
+		break;
+	case HIST_KEYS:
+		trace_seq_printf(&seq, "HIST_KEYS");
+		break;
+	case HIST_KEY_VALS:
+		trace_seq_printf(&seq, "HIST_KEY_VALS");
+		break;
+	case HIST_RANGE:
+		trace_seq_printf(&seq, "HIST_RANGE");
+		break;
+	case HIST_VALUES:
+		trace_seq_printf(&seq, "HIST_VALUES");
+		break;
+	case HIST_NEXT_KEY:
+		trace_seq_printf(&seq, "HIST_NEXT_KEY");
+	case HIST_STACK:
+		trace_seq_printf(&seq, "HIST_STACK");
+		break;
+	case HIST_ENTRIES:
+		trace_seq_printf(&seq, "HIST_ENTRIES");
+		break;
+	case HIST_DROPPED:
+		trace_seq_printf(&seq, "HIST_DROPPED");
+		break;
+	case HIST_END:
+		trace_seq_printf(&seq, "HIST_END");
+		break;
+	}
+	trace_seq_printf(&seq, " with token ");
+	tname = name_token(type);
+	if (tname)
+		trace_seq_printf(&seq, "%s", tname);
+	else
+		trace_seq_printf(&seq, "(unknown %d)", type);
+
+	trace_seq_printf(&seq, " last token %s\n", data->text);
+	trace_seq_terminate(&seq);
+	if (seq.buffer)
+		*err = seq.buffer;
+	seq.buffer = NULL;
+	trace_seq_destroy(&seq);
+}
+
+static void update_next(const char **next_buffer, struct hist_data *data)
+{
+	if (!next_buffer)
+		return;
+
+	*next_buffer = find_buffer_line(data->buffer, data->line_no - 1);
+}
+
+/**
+ * tracefs_hist_data_free - free a created hist data descriptor
+ * @hdata: The tracefs_hist_data descriptor to free.
+ *
+ * Frees the data allocated by tracefs_hist_data_parse().
+ */
+void tracefs_hist_data_free(struct tracefs_hist_data *hdata)
+{
+	struct tracefs_hist_bucket *bucket;
+	struct tracefs_hist_bucket_key *key;
+	struct tracefs_hist_bucket_val *val;
+
+	if (!hdata)
+		return;
+
+	tracefs_list_free(hdata->key_names);
+	tracefs_list_free(hdata->value_names);
+
+	while ((bucket = hdata->buckets)) {
+		hdata->buckets = bucket->next;
+		while ((key = bucket->keys)) {
+			bucket->keys = key->next;
+			switch (key->type) {
+			case TRACEFS_BUCKET_KEY_SINGLE:
+				free(key->single.sym);
+				break;
+			default:
+				break;
+			}
+			free(key);
+		}
+		while ((val = bucket->vals)) {
+			bucket->vals = val->next;
+			free(val);
+		}
+		free(bucket);
+	}
+
+	free(hdata);
+}
+
+/* Used for debugging in gdb */
+static void breakpoint(char *text)
+{
+}
+
+/**
+ * tracefs_hist_data_parse - parse a hist file of a trace event
+ * @buffer: The buffer containing the hist file content
+ * @next_buffer: If not NULL will point to the next hist in the buffer
+ * @err: If not NULL, will load the error message on error
+ *
+ * Reads and parses the content of a "hist" file of a trace event.
+ * It will return a descriptor that can be used to read the content and
+ * create a histogram table.
+ *
+ * Because "hist" files may contain more than one histogram, and this
+ * function will only parse one of the histograms, if there are more
+ * than one histogram in the buffer, and @next_buffer is not NULL, then
+ * it will return the location of the next histogram in @next_buffer.
+ *
+ * If there's an error in the parsing, then @err will contain an error
+ * message about what went wrong.
+ *
+ * Returns a desrciptor of a histogram representing the hist file content.
+ * NULL on error.
+ * The descriptor must be freed with tracefs_hist_data_free().
+ */
+struct tracefs_hist_data *
+tracefs_hist_data_parse(const char *buffer, const char **next_buffer, char **err)
+{
+	struct tracefs_hist_data *hdata;
+	struct hist_data data;
+	enum hist_state state = 0;
+	long long start_range, end_range;
+	bool first = false;
+	unsigned int e;
+	int buffer_size;
+	bool done = false;
+	char *text;
+	enum yytokentype type;
+	int ret;
+
+	if (!buffer)
+		return NULL;
+
+	hdata = calloc(1, sizeof(*hdata));
+	if (!hdata)
+		return NULL;
+
+	hdata->next_bucket = &hdata->buckets;
+
+	memset(&data, 0, sizeof(data));
+
+	buffer_size = strlen(buffer);
+	data.buffer = buffer;
+	data.buffer_size = buffer_size;
+	data.text = malloc(buffer_size);
+	if (!data.text) {
+		free(hdata);
+		perror("text");
+		exit(-1);
+	}
+
+	ret = hist_lex_init_extra(&data, &data.scanner);
+	if (ret < 0) {
+		perror("ylex_init");
+		return NULL;
+	}
+	while (!done) {
+		type = hist_yylex(&data, data.scanner);
+		if (type < 0)
+			break;
+		text = data.text;
+		breakpoint(text);
+		switch (state) {
+		case HIST_START:
+			switch (type) {
+			case COMMENT:
+				first = true;
+				ret = do_comment(hdata, text);
+				if (ret < 0)
+					goto error;
+				break;
+			case KEY_TYPE:
+				goto key_type;
+			case STACKTRACE:
+				goto stacktrace;
+			default:
+				goto error;
+			}
+			break;
+		case HIST_KEYS_START:
+			switch (type) {
+			case KEY_TYPE:
+ key_type:
+				if (first) {
+					ret = do_key_type(hdata, text);
+					if (ret < 0)
+						goto error;
+				}
+				ret = start_new_row(hdata);
+				state = HIST_KEY_VALS;
+				break;
+			case STACKTRACE:
+ stacktrace:
+				if (first) {
+					ret = do_key_type(hdata, "stacktrace");
+					if (ret < 0)
+						goto error;
+				}
+				ret = start_new_row(hdata);
+				state = HIST_STACK;
+				break;
+			case HITS:
+				hdata->hits = strtoll(text, NULL, 0);
+				state = HIST_ENTRIES;
+				break;
+			default:
+				goto error;
+			}
+			break;
+		case HIST_KEYS:
+			switch (type) {
+			case KEY_TYPE:
+				if (first) {
+					ret = do_key_type(hdata, text);
+					if (ret < 0)
+						goto error;
+				}
+				ret = start_new_key(hdata);
+				state = HIST_KEY_VALS;
+				break;
+			case STACKTRACE:
+				if (first) {
+					ret = do_key_type(hdata, "stacktrace");
+					if (ret < 0)
+						goto error;
+				}
+				ret = start_new_key(hdata);
+				state = HIST_STACK;
+				break;
+			case NEWLINE:
+				break;
+			case COLON:
+				state = HIST_VALUES;
+				break;
+			default:
+				goto error;
+			}
+			break;
+		case HIST_NEXT_KEY:
+			switch (type) {
+			case COLON:
+				state = HIST_VALUES;
+				break;
+			case COMMA:
+				state = HIST_KEYS;
+				break;
+			default:
+				goto error;
+			}
+			break;
+		case HIST_KEY_VALS:
+			switch (type) {
+			case NEWLINE:
+				continue;
+			case START_RANGE:
+				start_range = strtoll(text, NULL, 0);
+				state = HIST_RANGE;
+				break;
+			case KEY_VAL:
+				ret = do_key_val(hdata, text);
+				if (ret < 0)
+					goto error;
+				break;
+			case RAW_VAL:
+				ret = do_key_raw(hdata, text);
+				if (ret < 0)
+					goto error;
+				state = HIST_NEXT_KEY;
+				break;
+			case COLON:
+				state = HIST_VALUES;
+				break;
+			case COMMA:
+				state = HIST_KEYS;
+				break;
+			default:
+				goto error;
+			}
+			break;
+		case HIST_STACK:
+			switch (type) {
+			case NEWLINE:
+				break;
+			case STACK_ITEM:
+				ret = do_key_stack(hdata, text);
+				if (ret < 0)
+					goto error;
+				break;
+			case STACK_MOD:
+				ret = do_key_stack_mod(hdata, text);
+				if (ret < 0)
+					goto error;
+				break;
+			case COLON:
+				state = HIST_VALUES;
+				break;
+			case COMMA:
+				state = HIST_KEYS;
+				break;
+			default:
+				goto error;
+			}
+			break;
+		case HIST_RANGE:
+			switch (type) {
+			case RANGE_LINEAR:
+				do_key_range(hdata, start_range,
+					     strtoll(text, NULL, 0));
+				break;
+			case RANGE_EXPONENT:
+				end_range = strtoll(text, NULL, 0);
+				e = (unsigned int)start_range;
+				start_range = expo(e, end_range - 1);
+				end_range = expo(e, end_range);
+				do_key_range(hdata, start_range, end_range);
+				break;
+			default:
+				goto error;
+			}
+			state = HIST_KEYS;
+			break;
+		case HIST_VALUES:
+			switch (type) {
+			case VALUE:
+				if (first) {
+					ret = do_value_type(hdata, text);
+					if (ret < 0)
+						goto error;
+				}
+				break;
+			case NUMBER:
+				ret = do_value_num(hdata, strtoll(text, NULL, 0));
+				if (ret < 0)
+					goto error;
+				break;
+			case NEWLINE:
+				state = HIST_KEYS_START;
+				first = false;
+				break;
+			default:
+				goto error;
+			}
+			break;
+		case HIST_ENTRIES:
+			switch (type) {
+			case ENTRIES:
+				hdata->entries = strtoll(text, NULL, 0);
+				state = HIST_DROPPED;
+				break;
+			default:
+				goto error;
+			}
+			break;
+		case HIST_DROPPED:
+			switch (type) {
+			case DROPPED:
+				hdata->dropped = strtoll(text, NULL, 0);
+				state = HIST_END;
+				break;
+			default:
+				goto error;
+			}
+			break;
+		case HIST_END:
+			done = true;
+			switch (type) {
+			case COMMENT:
+				update_next(next_buffer, &data);
+				break;
+			case YYEOF:
+				/* Fall through */
+			default:
+				/* Do at end, as next_buffer may point to buffer*/
+				if (next_buffer)
+					*next_buffer = NULL;
+				break;
+			}
+			break;
+		}
+	}
+
+	hist_lex_destroy(data.scanner);
+	free(data.text);
+
+	return hdata;
+ error:
+	print_error(&data, err, state, type);
+	hist_lex_destroy(data.scanner);
+	free(data.text);
+	tracefs_hist_data_free(hdata);
+	return NULL;
+}
-- 
2.30.2


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

* [PATCH 2/9] libtracefs: Parse comment for hist data information
  2021-08-10 20:48 [PATCH 0/9] libtracefs: APIs to read a trace event hist file Steven Rostedt
  2021-08-10 20:48 ` [PATCH 1/9] tracefs: Add API tracefs_hist_data_parse() Steven Rostedt
@ 2021-08-10 20:48 ` Steven Rostedt
  2021-08-10 20:48 ` [PATCH 3/9] libtracefs: Change hist_data_key type to flags Steven Rostedt
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 11+ messages in thread
From: Steven Rostedt @ 2021-08-10 20:48 UTC (permalink / raw)
  To: linux-trace-devel
  Cc: linux-kernel, Tom Zanussi, Daniel Bristot de Oliveira,
	Masami Hiramatsu, Namhyung Kim, linux-rt-users, Clark Williams,
	Steven Rostedt (VMware)

From: "Steven Rostedt (VMware)" <rostedt@goodmis.org>

The comments at the top of a hist file for a trace event includes the
string used to create the hist file. Parse it for the key names as well as
for the key types.

Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
---
 src/tracefs-hist-data.c | 211 +++++++++++++++++++++++++++++++++++-----
 1 file changed, 185 insertions(+), 26 deletions(-)

diff --git a/src/tracefs-hist-data.c b/src/tracefs-hist-data.c
index 497ab9ce97b4..6ec262e8b180 100644
--- a/src/tracefs-hist-data.c
+++ b/src/tracefs-hist-data.c
@@ -151,21 +151,26 @@ struct tracefs_hist_bucket {
 	struct tracefs_hist_bucket_val		**next_val;
 };
 
+struct key_types {
+	struct key_types		*next;
+	enum tracefs_hist_key_type	type;
+};
+
 struct tracefs_hist_data {
+	char				*hist_str;
 	char				**key_names;
 	char				**value_names;
+	struct key_types		*key_types;
+	struct key_types		**next_key_type;
+	struct key_types		*current_key_type;
 	struct tracefs_hist_bucket	*buckets;
 	struct tracefs_hist_bucket	**next_bucket;
 	unsigned long long		hits;
 	unsigned long long		entries;
 	unsigned long long		dropped;
+	int				current_key;
 };
 
-static int do_comment(struct tracefs_hist_data *hdata, const char *comment)
-{
-	return 0;
-}
-
 static int do_key_type(struct tracefs_hist_data *hdata, const char *key)
 {
 	char **tmp;
@@ -234,6 +239,23 @@ static int start_new_key(struct tracefs_hist_data *hdata)
 	return 0;
 }
 
+static int do_add_key_type(struct tracefs_hist_data *hdata,
+			   enum tracefs_hist_key_type type)
+{
+	struct key_types *key_type;
+
+	key_type = calloc(1, sizeof(*key_type));
+	if (!key_type)
+		return -1;
+
+	key_type->type = type;
+
+	*hdata->next_key_type = key_type;
+	hdata->next_key_type = &key_type->next;
+
+	return 0;
+}
+
 static char *chomp(char *text)
 {
 	char *p;
@@ -252,6 +274,100 @@ static char *chomp(char *text)
 	return text;
 }
 
+static int do_comment(struct tracefs_hist_data *hdata, char *comment)
+{
+	enum tracefs_hist_key_type key_type;
+	const char trigger_info[] = "trigger info: ";
+	const char hist[] = "hist:";
+	const char keys[] = "keys=";
+	char *name;
+	int ret;
+
+	comment = chomp(comment);
+	if (!comment)
+		return -1;
+
+	if (!strcmp(comment, "event histogram"))
+		return 0;
+
+	if (strncmp(comment, trigger_info, strlen(trigger_info)) != 0)
+		return 0;
+
+	comment += strlen(trigger_info);
+	comment = chomp(comment);
+
+	if (strncmp(comment, hist, strlen(hist)) != 0)
+		return -1;
+
+	hdata->hist_str = strdup(comment);
+	if (!hdata->hist_str)
+		return -1;
+
+	comment += strlen(hist);
+
+	if (strncmp(comment, keys, strlen(keys)) != 0)
+		return -1;
+	comment += strlen(keys);
+
+	name = comment;
+
+	while (*comment) {
+		bool comma = false;
+
+		if (*comment == ':')
+			break;
+		switch (*comment) {
+		case ',':
+			comma = true;
+		case '.':
+			*comment = '\0';
+			do_key_type(hdata, name);
+			comment++;
+			if (comma) {
+				name = comment;
+				ret = do_add_key_type(hdata, 0);
+				if (ret < 0)
+					return -1;
+				continue;
+			}
+			if (!strncmp(comment, "hex", 3)) {
+				key_type = TRACEFS_HIST_KEY_HEX;
+			} else if (!strncmp(comment, "sym-offset", 10)) {
+				key_type = TRACEFS_HIST_KEY_SYM_OFFSET;
+			} else if (!strncmp(comment, "sym", 3)) {
+				key_type = TRACEFS_HIST_KEY_SYM;
+			} else if (!strncmp(comment, "syscall", 3)) {
+				key_type = TRACEFS_HIST_KEY_SYSCALL;
+			} else if (!strncmp(comment, "execname", 3)) {
+				key_type = TRACEFS_HIST_KEY_EXECNAME;
+			} else if (!strncmp(comment, "log2", 3)) {
+				key_type = TRACEFS_HIST_KEY_LOG;
+			} else if (!strncmp(comment, "usecs", 3)) {
+				key_type = TRACEFS_HIST_KEY_USECS;
+			} else {
+				key_type = 0;
+			}
+
+			ret = do_add_key_type(hdata, key_type);
+			if (ret < 0)
+				return -1;
+			while (*comment) {
+				if (*comment == ',') {
+					comment++;
+					name = comment;
+					break;
+				}
+				if (*comment == ':')
+					break;
+				comment++;
+			}
+			continue;
+		}
+		comment++;
+	}
+	return 0;
+}
+
 static int __do_key_val(struct tracefs_hist_data *hdata,
 			char *text, const char *delim, const char *end)
 {
@@ -261,6 +377,9 @@ static int __do_key_val(struct tracefs_hist_data *hdata,
 	char *val;
 	int len;
 
+	if (!hdata->current_key_type)
+		return -1;
+
 	text = chomp(text);
 
 	bucket = container_of(hdata->next_bucket, struct tracefs_hist_bucket, next);
@@ -294,6 +413,16 @@ static int __do_key_val(struct tracefs_hist_data *hdata,
 
 	k->sym = val;
 
+	switch (hdata->current_key_type->type) {
+	case TRACEFS_HIST_KEY_HEX:
+		k->val = strtoll(k->sym, NULL, 16);
+		break;
+	case TRACEFS_HIST_KEY_USECS:
+		k->val = strtoll(k->sym, NULL, 0);
+	default:
+		break;
+	}
+
 	return 0;
 }
 
@@ -318,6 +447,9 @@ static int do_key_raw(struct tracefs_hist_data *hdata, char *text)
 	struct tracefs_hist_bucket_key *key;
 	struct tracefs_hist_bucket_key_single *k;
 
+	if (!hdata->current_key_type)
+		return -1;
+
 	text = chomp(text);
 
 	bucket = container_of(hdata->next_bucket, struct tracefs_hist_bucket, next);
@@ -331,7 +463,15 @@ static int do_key_raw(struct tracefs_hist_data *hdata, char *text)
 	if (k->val)
 		return -1;
 
-	k->val = strtoll(text, NULL, 0);
+	switch (hdata->current_key_type->type) {
+	case TRACEFS_HIST_KEY_SYM:
+	case TRACEFS_HIST_KEY_SYM_OFFSET:
+		k->val = strtoll(text, NULL, 16);
+		break;
+	default:
+		k->val = strtoll(text, NULL, 0);
+		break;
+	}
 
 	return 0;
 }
@@ -514,6 +654,27 @@ static void update_next(const char **next_buffer, struct hist_data *data)
 	*next_buffer = find_buffer_line(data->buffer, data->line_no - 1);
 }
 
+static int test_key(struct tracefs_hist_data *hdata, const char *key)
+{
+	if (tracefs_list_size(hdata->key_names) <= hdata->current_key)
+		return -1;
+
+	return strcmp(key, hdata->key_names[hdata->current_key]) == 0 ? 0 : -1;
+}
+
+static void reset_key_test(struct tracefs_hist_data *hdata)
+{
+	hdata->current_key = 0;
+	hdata->current_key_type = hdata->key_types;
+}
+
+static void inc_key_test(struct tracefs_hist_data *hdata)
+{
+	hdata->current_key++;
+	if (hdata->current_key_type)
+		hdata->current_key_type = hdata->current_key_type->next;
+}
+
 /**
  * tracefs_hist_data_free - free a created hist data descriptor
  * @hdata: The tracefs_hist_data descriptor to free.
@@ -529,6 +690,7 @@ void tracefs_hist_data_free(struct tracefs_hist_data *hdata)
 	if (!hdata)
 		return;
 
+	free(hdata->hist_str);
 	tracefs_list_free(hdata->key_names);
 	tracefs_list_free(hdata->value_names);
 
@@ -605,6 +767,7 @@ tracefs_hist_data_parse(const char *buffer, const char **next_buffer, char **err
 		return NULL;
 
 	hdata->next_bucket = &hdata->buckets;
+	hdata->next_key_type = &hdata->key_types;
 
 	memset(&data, 0, sizeof(data));
 
@@ -650,21 +813,19 @@ tracefs_hist_data_parse(const char *buffer, const char **next_buffer, char **err
 			switch (type) {
 			case KEY_TYPE:
  key_type:
-				if (first) {
-					ret = do_key_type(hdata, text);
-					if (ret < 0)
-						goto error;
-				}
+				reset_key_test(hdata);
+				ret = test_key(hdata, text);
+				if (ret < 0)
+					goto error;
 				ret = start_new_row(hdata);
 				state = HIST_KEY_VALS;
 				break;
 			case STACKTRACE:
  stacktrace:
-				if (first) {
-					ret = do_key_type(hdata, "stacktrace");
-					if (ret < 0)
-						goto error;
-				}
+				reset_key_test(hdata);
+				ret = test_key(hdata, "stacktrace");
+				if (ret < 0)
+					goto error;
 				ret = start_new_row(hdata);
 				state = HIST_STACK;
 				break;
@@ -679,20 +840,18 @@ tracefs_hist_data_parse(const char *buffer, const char **next_buffer, char **err
 		case HIST_KEYS:
 			switch (type) {
 			case KEY_TYPE:
-				if (first) {
-					ret = do_key_type(hdata, text);
-					if (ret < 0)
-						goto error;
-				}
+				inc_key_test(hdata);
+				ret = test_key(hdata, text);
+				if (ret < 0)
+					goto error;
 				ret = start_new_key(hdata);
 				state = HIST_KEY_VALS;
 				break;
 			case STACKTRACE:
-				if (first) {
-					ret = do_key_type(hdata, "stacktrace");
-					if (ret < 0)
-						goto error;
-				}
+				inc_key_test(hdata);
+				ret = test_key(hdata, "stacktrace");
+				if (ret < 0)
+					goto error;
 				ret = start_new_key(hdata);
 				state = HIST_STACK;
 				break;
-- 
2.30.2


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

* [PATCH 3/9] libtracefs: Change hist_data_key type to flags
  2021-08-10 20:48 [PATCH 0/9] libtracefs: APIs to read a trace event hist file Steven Rostedt
  2021-08-10 20:48 ` [PATCH 1/9] tracefs: Add API tracefs_hist_data_parse() Steven Rostedt
  2021-08-10 20:48 ` [PATCH 2/9] libtracefs: Parse comment for hist data information Steven Rostedt
@ 2021-08-10 20:48 ` Steven Rostedt
  2021-08-10 20:48 ` [PATCH 4/9] libtracefs: Add API tracefs_hist_data_read() Steven Rostedt
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 11+ messages in thread
From: Steven Rostedt @ 2021-08-10 20:48 UTC (permalink / raw)
  To: linux-trace-devel
  Cc: linux-kernel, Tom Zanussi, Daniel Bristot de Oliveira,
	Masami Hiramatsu, Namhyung Kim, linux-rt-users, Clark Williams,
	Steven Rostedt (VMware)

From: "Steven Rostedt (VMware)" <rostedt@goodmis.org>

As the tracefs_hist_bucket_key will be exposed to users of the library,
have the type be flags, where it can be modified in the future, and not
break backward compatibility.

Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
---
 src/tracefs-hist-data.c | 41 +++++++++++++++++++++--------------------
 1 file changed, 21 insertions(+), 20 deletions(-)

diff --git a/src/tracefs-hist-data.c b/src/tracefs-hist-data.c
index 6ec262e8b180..c7e110559ee8 100644
--- a/src/tracefs-hist-data.c
+++ b/src/tracefs-hist-data.c
@@ -113,10 +113,10 @@ static char *name_token(enum yytokentype type)
 	return NULL;
 }
 
-enum tracefs_bucket_key_type {
-	TRACEFS_BUCKET_KEY_UNDEF,
-	TRACEFS_BUCKET_KEY_SINGLE,
-	TRACEFS_BUCKET_KEY_RANGE,
+enum tracefs_bucket_key_flags {
+	TRACEFS_BUCKET_KEY_FL_UNDEF	= (1 << 29),
+	TRACEFS_BUCKET_KEY_FL_SINGLE	= (1 << 30),
+	TRACEFS_BUCKET_KEY_FL_RANGE	= (1 << 31),
 };
 
 struct tracefs_hist_bucket_key_single {
@@ -131,7 +131,7 @@ struct tracefs_hist_bucket_key_range {
 
 struct tracefs_hist_bucket_key {
 	struct tracefs_hist_bucket_key	*next;
-	enum tracefs_bucket_key_type	type;
+	unsigned int			flags;
 	union {
 		struct tracefs_hist_bucket_key_single	single;
 		struct tracefs_hist_bucket_key_range	range;
@@ -210,6 +210,8 @@ static int start_new_row(struct tracefs_hist_data *hdata)
 		return -1;
 	}
 
+	key->flags = TRACEFS_BUCKET_KEY_FL_UNDEF;
+
 	bucket->keys = key;
 	bucket->next_key = &key->next;
 
@@ -233,6 +235,8 @@ static int start_new_key(struct tracefs_hist_data *hdata)
 		return -1;
 	}
 
+	key->flags = TRACEFS_BUCKET_KEY_FL_UNDEF;
+
 	*bucket->next_key = key;
 	bucket->next_key = &key->next;
 
@@ -385,12 +389,13 @@ static int __do_key_val(struct tracefs_hist_data *hdata,
 	bucket = container_of(hdata->next_bucket, struct tracefs_hist_bucket, next);
 
 	key = container_of(bucket->next_key, struct tracefs_hist_bucket_key, next);
-	if (!key->type)
-		key->type = TRACEFS_BUCKET_KEY_SINGLE;
-
-	if (key->type != TRACEFS_BUCKET_KEY_SINGLE)
+	if (!(key->flags &
+	      (TRACEFS_BUCKET_KEY_FL_UNDEF | TRACEFS_BUCKET_KEY_FL_SINGLE)))
 		return -1;
 
+	key->flags &= ~TRACEFS_BUCKET_KEY_FL_UNDEF;
+	key->flags |= TRACEFS_BUCKET_KEY_FL_SINGLE;
+
 	k = &key->single;
 
 	len = strlen(text);
@@ -455,7 +460,7 @@ static int do_key_raw(struct tracefs_hist_data *hdata, char *text)
 	bucket = container_of(hdata->next_bucket, struct tracefs_hist_bucket, next);
 
 	key = container_of(bucket->next_key, struct tracefs_hist_bucket_key, next);
-	if (key->type != TRACEFS_BUCKET_KEY_SINGLE)
+	if (!(key->flags & TRACEFS_BUCKET_KEY_FL_SINGLE))
 		return -1;
 
 	k = &key->single;
@@ -487,12 +492,13 @@ static int do_key_range(struct tracefs_hist_data *hdata, long long start,
 
 	key = container_of(bucket->next_key, struct tracefs_hist_bucket_key, next);
 
-	if (!key->type)
-		key->type = TRACEFS_BUCKET_KEY_RANGE;
-
-	if (key->type != TRACEFS_BUCKET_KEY_RANGE)
+	if (!(key->flags &
+	      (TRACEFS_BUCKET_KEY_FL_UNDEF | TRACEFS_BUCKET_KEY_FL_RANGE)))
 		return -1;
 
+	key->flags &= ~TRACEFS_BUCKET_KEY_FL_UNDEF;
+	key->flags |= TRACEFS_BUCKET_KEY_FL_RANGE;
+
 	k = &key->range;
 
 	k->start = start;
@@ -698,13 +704,8 @@ void tracefs_hist_data_free(struct tracefs_hist_data *hdata)
 		hdata->buckets = bucket->next;
 		while ((key = bucket->keys)) {
 			bucket->keys = key->next;
-			switch (key->type) {
-			case TRACEFS_BUCKET_KEY_SINGLE:
+			if (key->flags & TRACEFS_BUCKET_KEY_FL_SINGLE)
 				free(key->single.sym);
-				break;
-			default:
-				break;
-			}
 			free(key);
 		}
 		while ((val = bucket->vals)) {
-- 
2.30.2


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

* [PATCH 4/9] libtracefs: Add API tracefs_hist_data_read()
  2021-08-10 20:48 [PATCH 0/9] libtracefs: APIs to read a trace event hist file Steven Rostedt
                   ` (2 preceding siblings ...)
  2021-08-10 20:48 ` [PATCH 3/9] libtracefs: Change hist_data_key type to flags Steven Rostedt
@ 2021-08-10 20:48 ` Steven Rostedt
  2021-08-10 20:48 ` [PATCH 5/9] libtracefs: Add API tracefs_list_dup() Steven Rostedt
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 11+ messages in thread
From: Steven Rostedt @ 2021-08-10 20:48 UTC (permalink / raw)
  To: linux-trace-devel
  Cc: linux-kernel, Tom Zanussi, Daniel Bristot de Oliveira,
	Masami Hiramatsu, Namhyung Kim, linux-rt-users, Clark Williams,
	Steven Rostedt (VMware)

From: "Steven Rostedt (VMware)" <rostedt@goodmis.org>

Adds an API to read a "hist" file of a trace event and create a list of
tracefs_hist_data descriptors for every histogram that exists in the
"hist" file.

Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
---
 include/tracefs.h       |  7 ++++
 src/tracefs-hist-data.c | 74 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 81 insertions(+)

diff --git a/include/tracefs.h b/include/tracefs.h
index 6bd40d72cb25..f1e4ffa0d65f 100644
--- a/include/tracefs.h
+++ b/include/tracefs.h
@@ -418,7 +418,14 @@ struct tracefs_hist_data;
 struct tracefs_hist_data *tracefs_hist_data_parse(const char *buffer,
 						  const char **next_buffer,
 						  char **err);
+
+struct tracefs_hist_data **tracefs_hist_data_read(struct tracefs_instance *instance,
+						  const char *system,
+						  const char *event,
+						  char **err);
+
 void tracefs_hist_data_free(struct tracefs_hist_data *hdata);
+void tracefs_hist_data_free_list(struct tracefs_hist_data **hdata_list);
 
 struct tracefs_synth;
 
diff --git a/src/tracefs-hist-data.c b/src/tracefs-hist-data.c
index c7e110559ee8..ab1ae824f59b 100644
--- a/src/tracefs-hist-data.c
+++ b/src/tracefs-hist-data.c
@@ -718,6 +718,24 @@ void tracefs_hist_data_free(struct tracefs_hist_data *hdata)
 	free(hdata);
 }
 
+/**
+ * tracefs_hist_data_free_list - frees a list of created hist data descriptors
+ * @hdata_list: The tracefs_hist_data descriptor list to free.
+ *
+ * Frees the data allocated by tracefs_hist_data_read().
+ */
+void tracefs_hist_data_free_list(struct tracefs_hist_data **hdata_list)
+{
+	int i;
+
+	if (!hdata_list)
+		return;
+
+	for (i = 0; hdata_list[i]; i++)
+		tracefs_hist_data_free(hdata_list[i]);
+	free(hdata_list);
+}
+
 /* Used for debugging in gdb */
 static void breakpoint(char *text)
 {
@@ -1019,3 +1037,59 @@ tracefs_hist_data_parse(const char *buffer, const char **next_buffer, char **err
 	tracefs_hist_data_free(hdata);
 	return NULL;
 }
+
+/**
+ * tracefs_hist_data_read - Reads and parses the trace event "hist" file
+ * @instance: The instance the trace event is in (NULL for top level)
+ * @system: The system of the @event (NULL to pick first event)
+ * @event: The trace event name to read the hist file from
+ * @err: On parsing errors, @err will be set to a message explaining what failed.
+ *
+ * Reads the content of a trace @event hist file and parses it.
+ *
+ * Returns an array of tracefs_hist_data descriptors, as a hist file
+ * may contain more than one histogram. Must be freed with
+ * tracefs_hist_data_free_list().
+ *
+ * Returns NULL on error, and if there was a parsing error, @err will contain
+ * a message explaining what failed.
+ */
+struct tracefs_hist_data **
+tracefs_hist_data_read(struct tracefs_instance *instance,
+		       const char *system, const char *event, char **err)
+{
+	struct tracefs_hist_data **tmp, **hdata_list = NULL;
+	const char *buffer;
+	char *content;
+	int cnt = 0;
+
+	if (err)
+		*err = NULL;
+
+	content = tracefs_event_file_read(instance, system, event, "hist", NULL);
+	if (!content)
+		return NULL;
+
+	buffer = content;
+	do {
+		tmp = realloc(hdata_list, sizeof(*tmp) * (cnt + 2));
+		if (!tmp)
+			goto error;
+		tmp[cnt + 1] = NULL;
+		tmp[cnt] = tracefs_hist_data_parse(buffer, &buffer, err);
+		if (!tmp[cnt])
+			goto error;
+		hdata_list = tmp;
+		if (buffer)
+			cnt++;
+	} while (buffer);
+
+	free(content);
+	return hdata_list;
+
+ error:
+	free(content);
+	tracefs_hist_data_free_list(hdata_list);
+	return NULL;
+}
+
-- 
2.30.2


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

* [PATCH 5/9] libtracefs: Add API tracefs_list_dup()
  2021-08-10 20:48 [PATCH 0/9] libtracefs: APIs to read a trace event hist file Steven Rostedt
                   ` (3 preceding siblings ...)
  2021-08-10 20:48 ` [PATCH 4/9] libtracefs: Add API tracefs_hist_data_read() Steven Rostedt
@ 2021-08-10 20:48 ` Steven Rostedt
  2021-08-10 20:48 ` [PATCH 6/9] libtracefs: Add APIs tracefs_hist_data_keys/value_names() Steven Rostedt
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 11+ messages in thread
From: Steven Rostedt @ 2021-08-10 20:48 UTC (permalink / raw)
  To: linux-trace-devel
  Cc: linux-kernel, Tom Zanussi, Daniel Bristot de Oliveira,
	Masami Hiramatsu, Namhyung Kim, linux-rt-users, Clark Williams,
	Steven Rostedt (VMware)

From: "Steven Rostedt (VMware)" <rostedt@goodmis.org>

Add an API tracefs_list_dup() that will duplicate an existing list.

Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
---
 include/tracefs.h   |  1 +
 src/tracefs-utils.c | 26 ++++++++++++++++++++++++++
 2 files changed, 27 insertions(+)

diff --git a/include/tracefs.h b/include/tracefs.h
index f1e4ffa0d65f..6fb66c44afc7 100644
--- a/include/tracefs.h
+++ b/include/tracefs.h
@@ -67,6 +67,7 @@ void tracefs_list_free(char **list);
 char **tracefs_list_add(char **list, const char *string);
 int tracefs_list_size(char **list);
 int tracefs_list_pop(char **list);
+char **tracefs_list_dup(char **list);
 
 /**
  * tracefs_trace_on_get_fd - Get a file descriptor of "tracing_on" in given instance
diff --git a/src/tracefs-utils.c b/src/tracefs-utils.c
index 63bb413298fe..8c92d6c54ab1 100644
--- a/src/tracefs-utils.c
+++ b/src/tracefs-utils.c
@@ -489,3 +489,29 @@ int tracefs_list_size(char **list)
 	list--;
 	return (int)*(unsigned long *)list;
 }
+
+/**
+ * tracefs_list_dup - create a duplicate list
+ * @list: The list to duplicate
+ *
+ * Allocates a new list that can be modified and freed separately.
+ *
+ * Returns a new allocated list that must be freed with
+ * tracefs_list_free(), or NULL on error.
+ */
+char **tracefs_list_dup(char **list)
+{
+	char **new = NULL;
+	char **tmp;
+	int i;
+
+	for (i = 0; list[i]; i++) {
+		tmp = tracefs_list_add(new, list[i]);
+		if (!tmp) {
+			free(new);
+			return NULL;
+		}
+		new = tmp;
+	}
+	return new;
+}
-- 
2.30.2


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

* [PATCH 6/9] libtracefs: Add APIs tracefs_hist_data_keys/value_names()
  2021-08-10 20:48 [PATCH 0/9] libtracefs: APIs to read a trace event hist file Steven Rostedt
                   ` (4 preceding siblings ...)
  2021-08-10 20:48 ` [PATCH 5/9] libtracefs: Add API tracefs_list_dup() Steven Rostedt
@ 2021-08-10 20:48 ` Steven Rostedt
  2021-08-10 20:48 ` [PATCH 7/9] libtracefs: Add API tracefs_hist_data_keys/values() and next_bucket() Steven Rostedt
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 11+ messages in thread
From: Steven Rostedt @ 2021-08-10 20:48 UTC (permalink / raw)
  To: linux-trace-devel
  Cc: linux-kernel, Tom Zanussi, Daniel Bristot de Oliveira,
	Masami Hiramatsu, Namhyung Kim, linux-rt-users, Clark Williams,
	Steven Rostedt (VMware)

From: "Steven Rostedt (VMware)" <rostedt@goodmis.org>

Add the APIs

  tracefs_hist_data_key_names()
  tracefs_hist_data_value_names()

To get the names of the keys and values respectively.

Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
---
 include/tracefs.h       |  3 +++
 src/tracefs-hist-data.c | 30 ++++++++++++++++++++++++++++++
 2 files changed, 33 insertions(+)

diff --git a/include/tracefs.h b/include/tracefs.h
index 6fb66c44afc7..7aa6a3e5673a 100644
--- a/include/tracefs.h
+++ b/include/tracefs.h
@@ -428,6 +428,9 @@ struct tracefs_hist_data **tracefs_hist_data_read(struct tracefs_instance *insta
 void tracefs_hist_data_free(struct tracefs_hist_data *hdata);
 void tracefs_hist_data_free_list(struct tracefs_hist_data **hdata_list);
 
+char **tracefs_hist_data_key_names(struct tracefs_hist_data *hdata);
+char **tracefs_hist_data_value_names(struct tracefs_hist_data *hdata);
+
 struct tracefs_synth;
 
 /*
diff --git a/src/tracefs-hist-data.c b/src/tracefs-hist-data.c
index ab1ae824f59b..c93c27453255 100644
--- a/src/tracefs-hist-data.c
+++ b/src/tracefs-hist-data.c
@@ -1093,3 +1093,33 @@ tracefs_hist_data_read(struct tracefs_instance *instance,
 	return NULL;
 }
 
+/**
+ * tracefs_hist_data_key_names - return key names
+ * @hdata: The hist data descriptor to get the names from
+ *
+ * Returns a copy of the key names of the keys. The list of keys
+ * will be in the same order as the keys are listed.
+ * Returns NULL on error.
+ *
+ * Must be freed with tracefs_list_free();
+ */
+char **tracefs_hist_data_key_names(struct tracefs_hist_data *hdata)
+{
+	return tracefs_list_dup(hdata->key_names);
+}
+
+/**
+ * tracefs_hist_data_value_names - return value names
+ * @hdata: The hist data descriptor to get the names from
+ *
+ * Returns a copy of the value names of the keys. The list of keys
+ * will be in the same order as the values are listed.
+ * Returns NULL on error.
+ *
+ * Must be freed with tracefs_list_free();
+ */
+char **tracefs_hist_data_value_names(struct tracefs_hist_data *hdata)
+{
+	return tracefs_list_dup(hdata->value_names);
+}
+
-- 
2.30.2


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

* [PATCH 7/9] libtracefs: Add API tracefs_hist_data_keys/values() and next_bucket()
  2021-08-10 20:48 [PATCH 0/9] libtracefs: APIs to read a trace event hist file Steven Rostedt
                   ` (5 preceding siblings ...)
  2021-08-10 20:48 ` [PATCH 6/9] libtracefs: Add APIs tracefs_hist_data_keys/value_names() Steven Rostedt
@ 2021-08-10 20:48 ` Steven Rostedt
  2021-08-10 20:48 ` [PATCH 8/9] libtracefs: Have tracefs_hist_bucket_key flags save the type Steven Rostedt
  2021-08-10 20:48 ` [PATCH 9/9] libtracefs: Add man pages for tracefs_hist_data functions Steven Rostedt
  8 siblings, 0 replies; 11+ messages in thread
From: Steven Rostedt @ 2021-08-10 20:48 UTC (permalink / raw)
  To: linux-trace-devel
  Cc: linux-kernel, Tom Zanussi, Daniel Bristot de Oliveira,
	Masami Hiramatsu, Namhyung Kim, linux-rt-users, Clark Williams,
	Steven Rostedt (VMware)

From: "Steven Rostedt (VMware)" <rostedt@goodmis.org>

Add APIs:

	tracefs_hist_data_keys()
	tracefs_hist_data_values()
	tracefs_hist_data_next_bucket()
	tracefs_hist_data_first_bucket()

Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
---
 include/tracefs.h       |  36 ++++++++++++++
 src/tracefs-hist-data.c | 107 +++++++++++++++++++++++++++++-----------
 2 files changed, 113 insertions(+), 30 deletions(-)

diff --git a/include/tracefs.h b/include/tracefs.h
index 7aa6a3e5673a..db0a1d73f091 100644
--- a/include/tracefs.h
+++ b/include/tracefs.h
@@ -431,6 +431,42 @@ void tracefs_hist_data_free_list(struct tracefs_hist_data **hdata_list);
 char **tracefs_hist_data_key_names(struct tracefs_hist_data *hdata);
 char **tracefs_hist_data_value_names(struct tracefs_hist_data *hdata);
 
+enum tracefs_bucket_key_flags {
+	TRACEFS_BUCKET_KEY_FL_UNDEF	= (1 << 29),
+	TRACEFS_BUCKET_KEY_FL_SINGLE	= (1 << 30),
+	TRACEFS_BUCKET_KEY_FL_RANGE	= (1 << 31),
+};
+
+struct tracefs_hist_bucket_key_single {
+	long long		val;
+	char			*sym;
+};
+
+struct tracefs_hist_bucket_key_range {
+	long long		start;
+	long long		end;
+};
+
+struct tracefs_hist_bucket_key {
+	struct tracefs_hist_bucket_key	*next;
+	unsigned int			flags;
+	union {
+		struct tracefs_hist_bucket_key_single	single;
+		struct tracefs_hist_bucket_key_range	range;
+	};
+};
+
+struct tracefs_hist_bucket_val {
+	struct tracefs_hist_bucket_val		*next;
+	long long				val;
+};
+
+const struct tracefs_hist_bucket_key *tracefs_hist_data_keys(struct tracefs_hist_data *hdata);
+const struct tracefs_hist_bucket_val *tracefs_hist_data_values(struct tracefs_hist_data *hdata);
+
+int tracefs_hist_data_next_bucket(struct tracefs_hist_data *hdata);
+int tracefs_hist_data_first_bucket(struct tracefs_hist_data *hdata);
+
 struct tracefs_synth;
 
 /*
diff --git a/src/tracefs-hist-data.c b/src/tracefs-hist-data.c
index c93c27453255..0f811d6e3154 100644
--- a/src/tracefs-hist-data.c
+++ b/src/tracefs-hist-data.c
@@ -113,36 +113,6 @@ static char *name_token(enum yytokentype type)
 	return NULL;
 }
 
-enum tracefs_bucket_key_flags {
-	TRACEFS_BUCKET_KEY_FL_UNDEF	= (1 << 29),
-	TRACEFS_BUCKET_KEY_FL_SINGLE	= (1 << 30),
-	TRACEFS_BUCKET_KEY_FL_RANGE	= (1 << 31),
-};
-
-struct tracefs_hist_bucket_key_single {
-	long long		val;
-	char			*sym;
-};
-
-struct tracefs_hist_bucket_key_range {
-	long long		start;
-	long long		end;
-};
-
-struct tracefs_hist_bucket_key {
-	struct tracefs_hist_bucket_key	*next;
-	unsigned int			flags;
-	union {
-		struct tracefs_hist_bucket_key_single	single;
-		struct tracefs_hist_bucket_key_range	range;
-	};
-};
-
-struct tracefs_hist_bucket_val {
-	struct tracefs_hist_bucket_val		*next;
-	long long				val;
-};
-
 struct tracefs_hist_bucket {
 	struct tracefs_hist_bucket		*next;
 	struct tracefs_hist_bucket_key		*keys;
@@ -165,6 +135,7 @@ struct tracefs_hist_data {
 	struct key_types		*current_key_type;
 	struct tracefs_hist_bucket	*buckets;
 	struct tracefs_hist_bucket	**next_bucket;
+	struct tracefs_hist_bucket	*current_bucket;
 	unsigned long long		hits;
 	unsigned long long		entries;
 	unsigned long long		dropped;
@@ -1029,6 +1000,9 @@ tracefs_hist_data_parse(const char *buffer, const char **next_buffer, char **err
 	hist_lex_destroy(data.scanner);
 	free(data.text);
 
+	/* Set to read the first bucket after creation */
+	tracefs_hist_data_first_bucket(hdata);
+
 	return hdata;
  error:
 	print_error(&data, err, state, type);
@@ -1123,3 +1097,76 @@ char **tracefs_hist_data_value_names(struct tracefs_hist_data *hdata)
 	return tracefs_list_dup(hdata->value_names);
 }
 
+/**
+ * tracefs_hist_data_keys - Return the content of the keys
+ * @hdata: The hist data descriptor of the keys
+ *
+ * Returns the actual pointer to the key data list in the @hdata descriptor.
+ * It must not be modified or freed.
+ */
+const struct tracefs_hist_bucket_key *
+tracefs_hist_data_keys(struct tracefs_hist_data *hdata)
+{
+	if (!hdata || !hdata->current_bucket)
+		return NULL;
+
+	return hdata->current_bucket->keys;
+}
+
+/**
+ * tracefs_hist_data_values - Return the content of the values
+ * @hdata: The hist data descriptor of the values
+ *
+ * Returns the actual pointer to the value data list in the @hdata descriptor.
+ * It must not be modified or freed.
+ */
+const struct tracefs_hist_bucket_val *
+tracefs_hist_data_values(struct tracefs_hist_data *hdata)
+{
+	if (!hdata || !hdata->current_bucket)
+		return NULL;
+
+	return hdata->current_bucket->vals;
+}
+
+/**
+ * tracefs_hist_data_next_bucket - Move to the next bucket with content
+ * @hdata: The hist data desrciptor
+ *
+ * Move the "cursor" of the bucket that tracefs_hist_data_keys()
+ * and tracefs_hist_data_values() will return their data from.
+ *
+ * Returns -1 if @hdata is NULL or already hit the last bucket.
+ * Returns 0 if there's still data after going to the next bucket
+ * Returns 1 if there's no more data left.
+ */
+int tracefs_hist_data_next_bucket(struct tracefs_hist_data *hdata)
+{
+	if (!hdata || !hdata->current_bucket)
+		return -1;
+
+	hdata->current_bucket = hdata->current_bucket->next;
+
+	return !hdata->current_bucket;
+}
+
+/**
+ * tracefs_hist_data_first_bucket - Reset to the first bucket
+ * @hdata: The hist data desrciptor
+ *
+ * Move the "cursor" of the bucket that tracefs_hist_data_keys()
+ * and tracefs_hist_data_values() will return their data from
+ * to the first bucket in the @hlist.
+ *
+ * Returns -1 if @hdata is NULL or already hit the last bucket.
+ * Returns 0 if there's still data after going to the next bucket
+ * Returns 1 if there's no more data left.
+ */
+int tracefs_hist_data_first_bucket(struct tracefs_hist_data *hdata)
+{
+	if (!hdata || !hdata->buckets)
+		return -1;
+
+	hdata->current_bucket = hdata->buckets;
+	return !hdata->current_bucket;
+}
-- 
2.30.2


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

* [PATCH 8/9] libtracefs: Have tracefs_hist_bucket_key flags save the type
  2021-08-10 20:48 [PATCH 0/9] libtracefs: APIs to read a trace event hist file Steven Rostedt
                   ` (6 preceding siblings ...)
  2021-08-10 20:48 ` [PATCH 7/9] libtracefs: Add API tracefs_hist_data_keys/values() and next_bucket() Steven Rostedt
@ 2021-08-10 20:48 ` Steven Rostedt
  2021-08-10 20:48 ` [PATCH 9/9] libtracefs: Add man pages for tracefs_hist_data functions Steven Rostedt
  8 siblings, 0 replies; 11+ messages in thread
From: Steven Rostedt @ 2021-08-10 20:48 UTC (permalink / raw)
  To: linux-trace-devel
  Cc: linux-kernel, Tom Zanussi, Daniel Bristot de Oliveira,
	Masami Hiramatsu, Namhyung Kim, linux-rt-users, Clark Williams,
	Steven Rostedt (VMware)

From: "Steven Rostedt (VMware)" <rostedt@goodmis.org>

Add the key_type enum as a bit flag in the key->flags for
tracefs_hist_bucket_key such that a user could know how to handle the
type.

Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
---
 src/tracefs-hist-data.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/tracefs-hist-data.c b/src/tracefs-hist-data.c
index 0f811d6e3154..90c9cb2c7df8 100644
--- a/src/tracefs-hist-data.c
+++ b/src/tracefs-hist-data.c
@@ -399,6 +399,9 @@ static int __do_key_val(struct tracefs_hist_data *hdata,
 		break;
 	}
 
+	if (hdata->current_key_type->type < TRACEFS_HIST_KEY_MAX)
+		key->flags |= (1 << hdata->current_key_type->type);
+
 	return 0;
 }
 
-- 
2.30.2


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

* [PATCH 9/9] libtracefs: Add man pages for tracefs_hist_data functions
  2021-08-10 20:48 [PATCH 0/9] libtracefs: APIs to read a trace event hist file Steven Rostedt
                   ` (7 preceding siblings ...)
  2021-08-10 20:48 ` [PATCH 8/9] libtracefs: Have tracefs_hist_bucket_key flags save the type Steven Rostedt
@ 2021-08-10 20:48 ` Steven Rostedt
  8 siblings, 0 replies; 11+ messages in thread
From: Steven Rostedt @ 2021-08-10 20:48 UTC (permalink / raw)
  To: linux-trace-devel
  Cc: linux-kernel, Tom Zanussi, Daniel Bristot de Oliveira,
	Masami Hiramatsu, Namhyung Kim, linux-rt-users, Clark Williams,
	Steven Rostedt (VMware)

From: "Steven Rostedt (VMware)" <rostedt@goodmis.org>

Add man pages for:

  tracefs_hist_data_parse()
  tracefs_hist_data_read()
  tracefs_hist_data_free()
  tracefs_hist_data_free_list()
  tracefs_hist_data_key_names()
  tracefs_hist_data_value_names()
  tracefs_hist_data_keys()
  tracefs_hist_data_values()
  tracefs_hist_data_next_bucket()
  tracefs_hist_data_first_bucket()

Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
---
 Documentation/libtracefs-hist-data-2.txt | 346 +++++++++++++++++++++++
 Documentation/libtracefs-hist-data.txt   | 294 +++++++++++++++++++
 2 files changed, 640 insertions(+)
 create mode 100644 Documentation/libtracefs-hist-data-2.txt
 create mode 100644 Documentation/libtracefs-hist-data.txt

diff --git a/Documentation/libtracefs-hist-data-2.txt b/Documentation/libtracefs-hist-data-2.txt
new file mode 100644
index 000000000000..c5b11aaa650a
--- /dev/null
+++ b/Documentation/libtracefs-hist-data-2.txt
@@ -0,0 +1,346 @@
+libtracefs(3)
+=============
+
+NAME
+----
+tracefs_hist_data_key_names, tracefs_hist_data_key_values, tracefs_hist_data_keys, tracefs_hist_data_values,
+tracefs_hist_data_next_bucket, tracefs_hist_data_first_bucket - Read an allocated tracefs_hist_data descriptor
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <tracefs.h>*
+
+
+char pass:[**]tracefs_hist_data_key_names(struct tracefs_hist_data pass:[*]hdata);
+char pass:[**]tracefs_hist_data_value_names(struct tracefs_hist_data pass:[*]hdata);
+
+const struct tracefs_hist_bucket_key pass:[*]tracefs_hist_data_keys(struct tracefs_hist_data pass:[*]hdata);
+const struct tracefs_hist_bucket_val pass:[*]tracefs_hist_data_values(struct tracefs_hist_data pass:[*]hdata);
+
+int tracefs_hist_data_next_bucket(struct tracefs_hist_data pass:[*]hdata);
+int tracefs_hist_data_first_bucket(struct tracefs_hist_data pass:[*]hdata);
+--
+
+DESCRIPTION
+-----------
+The _hist_ trigger for trace events will create a histogram that can be read in
+the trace event's _hist_ file. The file is human readable ASCII format, but that
+makes it difficult to process in programs. The *tracefs_hist_data_*pass:[*]()
+functions convert the histogram ASCII format into structures that can be processed
+and converted into tables.
+
+*tracefs_hist_data_key_names*() Returns an allocated list of strings containing the
+names of the keys in the order that they are saved in the list returned by
+*tracefs_hist_data_keys*(3). The _hdata_ is a _tracefs_hist_data_ descriptor that
+was created by either *tracefs_hist_data_parse*(3) or tracefs_hist_data_read*(3).
+
+*tracefs_hist_data_value_names*() Returns an allocated list of strings containing the
+names of the values in the order that they are saved in the list returned by
+*tracefs_hist_data_values*(3). The _hdata_ is a _tracefs_hist_data_ descriptor that
+was created by either *tracefs_hist_data_parse*(3) or *tracefs_hist_data_read*(3).
+
+*tracefs_hist_data_keys*() returns a link list of _tracefs_hist_data_bucket_key_
+descriptors that contain the content of the keys of the current bucket. Each key
+has its own descriptor, and the _key_->next will link to the next descriptor. The
+returned link list is an actual internal construct of the _tracefs_hist_data_
+and must not be modified or freed.
+
+*tracefs_hist_data_values*() returns a link list of _tracefs_hist_data_bucket_val_
+descriptors that contain the content of the values of the current bucket. Each value
+has its own descriptor, and the _value_->next will link to the next descriptor. The
+returned link list is an actual internal construct of the _tracefs_hist_data_
+and must not be modified or freed.
+
+After the _tracefs_hist_data_ has been created, it will keep track of the internal
+bucket, and this is not reentrant, so care must be taken when using with threads.
+The *tracefs_hist_data_keys*() or *tracefs_hist_data_values*() will return the list
+of keys or values respectively for the current bucket.
+
+*tracefs_hist_data_next_bucket*() will move the internal cursor of the _tracefs_hist_data_
+to the next bucket, where following this call, *tracefs_hist_data_keys*() and
+*tracefs_hist_data_values*() will return the keys and values from the new bucket.
+
+*tracefs_hist_data_first_bucket*() will reset the index such that following calls
+to *tracefs_hist_data_keys*() or tracefs_hist_data_values*() will return the link
+list of keys or values for the first bucket, and the list can be traversed again
+with *tracefs_hist_data_next_bucket*().
+
+RETURN VALUE
+------------
+*tracefs_hist_data_key_names*() on success, returns an allocated list of the names of the
+keys in _hdata_ and must be freed with *tracefs_list_free*(3). Returns NULL
+on failure.
+
+*tracefs_hist_data_value_names*() on success, returns an allocated list of the names of the
+values in _hdata_ and must be freed with *tracefs_list_free*(3). Returns NULL
+on failure.
+
+*tracefs_hist_data_keys*() on success, returns a link list of _tracefs_hist_bucket_key_ descriptors.
+Returns NULL on error.
+
+struct tracefs_hist_bucket_key {
+	struct tracefs_hist_bucket_key	*next;
+	unsigned int			flags;
+	union {
+		struct tracefs_hist_bucket_key_single	single;
+		struct tracefs_hist_bucket_key_range	range;
+	};
+};
+
+To traverse to the next key in the list, use the _key_->next, where the last key will have
+its _next_ pointer be NULL.
+
+If the flag TRACEFS_BUCKET_KEY_FL_SINGLE is set, then the "single" union structure should
+be used, otherwise the TRACEFS_BUCKET_KEY_FL_RANGE bit should be set.
+
+The other lower bits map to the type of the key, defined by the _tracefs_hist_key_type_ enum.
+
+struct tracefs_hist_bucket_key_single {
+	long long		val;
+	char			*sym;
+};
+
+Depending on the flags set, _val_ may be the integer representation of the _sym_.
+See the EXAMPLE below.
+
+struct tracefs_hist_bucket_key_range {
+	long long		start;
+	long long		end;
+};
+
+If the key is a range, then the tracefs_hist_bucket_key_range should be used
+to get the _start_ and _end_ values of that range inclusive.
+
+*tracefs_hist_data_next_bucket*() Returns -1 on error, 0 on succes and the
+cursor is pointing to the next bucket to read from, or 1 on success but there
+are on more buckets to read.
+
+*tracefs_hist_data_first_bucket*() Returns -1 on error, 0 on succes and the
+cursor is pointing to the next bucket to read from, or 1 on success but there
+are on more buckets to read.
+
+EXAMPLE
+-------
+[source,c]
+--
+#include <stdlib.h>
+#include <unistd.h>
+#include <tracefs.h>
+
+int main (int argc, char **argv)
+{
+	struct tracefs_hist_data *hdata;
+	const struct tracefs_hist_bucket_key *key;
+	const struct tracefs_hist_bucket_val *val;
+	char buf[BUFSIZ];
+	const char *buffer;
+	char *content = NULL;
+	char *file;
+	char *err;
+	char **key_names;
+	char **value_names;
+	int key_idx;
+	int val_idx;
+	FILE *fp;
+	size_t r;
+	bool done = false;
+	int buffer_size = 0;
+	int c;
+	int i;
+
+	for (;;) {
+		c = getopt(argc, argv, "hf:");
+		if (c == -1)
+			break;
+
+		switch(c) {
+		case 'h':
+			fprintf(stderr, "usage: %s [-f file]\n", argv[0]);
+			exit(0);
+		case 'f':
+			file = optarg;
+			break;
+		}
+	}
+
+	if (file) {
+		if (!strcmp(file, "-"))
+			fp = stdin;
+		else
+			fp = fopen(file, "r");
+		if (!fp) {
+			perror(file);
+			exit(-1);
+		}
+		while ((r = fread(buf, 1, BUFSIZ, fp)) > 0) {
+			content = realloc(content, buffer_size + r + 1);
+			strncpy(content + buffer_size, buf, r);
+			buffer_size += r;
+		}
+		fclose(fp);
+		if (buffer_size)
+			content[buffer_size] = '\0';
+	} else if (argc == optind) {
+		fprintf(stderr, "usage: %s [-f file]\n", argv[0]);
+		exit(-1);
+	} else {
+		for (i = optind; i < argc; i++) {
+			r = strlen(argv[i]);
+			content = realloc(content, buffer_size + r + 2);
+			if (i != optind)
+				content[buffer_size++] = ' ';
+			strcpy(content + buffer_size, argv[i]);
+			buffer_size += r;
+		}
+	}
+
+	buffer = content;
+	hdata = tracefs_hist_data_parse(buffer, &buffer, &err);
+	printf("hdata = %p\n", hdata);
+	if (!hdata && err) {
+		printf("%s\n", err);
+		exit(-1);
+	}
+
+	key_names = tracefs_hist_data_key_names(hdata);
+	if (!key_names) {
+		perror("key_names");
+		exit(-1);
+	}
+
+	value_names = tracefs_hist_data_value_names(hdata);
+	if (!value_names) {
+		perror("value_names");
+		exit(-1);
+	}
+
+	do {
+		bool first = true;
+		key = tracefs_hist_data_keys(hdata);
+		val = tracefs_hist_data_values(hdata);
+		key_idx = 0;
+		val_idx = 0;
+
+		if (!key || !val) {
+			perror("keys or vals");
+			exit(-1);
+		}
+
+		while (key) {
+			if (!first)
+				printf(",");
+			first = false;
+			if (key_names[key_idx])
+				printf("%s:", key_names[key_idx++]);
+			else
+				printf("(\?\?):");
+			if (key->flags & TRACEFS_BUCKET_KEY_FL_UNDEF) {
+				fprintf(stderr, "bad key type?");
+				exit (-1);
+			} else if (key->flags & TRACEFS_BUCKET_KEY_FL_SINGLE) {
+
+				if (key->flags &
+				    ((1 << TRACEFS_HIST_KEY_SYM) |
+				     (1 << TRACEFS_HIST_KEY_SYM_OFFSET) |
+				     (1 << TRACEFS_HIST_KEY_SYSCALL) |
+				     (1 << TRACEFS_HIST_KEY_EXECNAME)))
+					printf("%s [ %lld ]",
+					       key->single.sym,
+					       key->single.val);
+				else
+					printf("%s", key->single.sym);
+
+			} else if  (key->flags & TRACEFS_BUCKET_KEY_FL_RANGE)
+				printf("%lld - %lld",
+				       key->range.start,
+				       key->range.end);
+			key = key->next;
+		}
+		printf(":");
+		first = true;
+		while (val) {
+			if (!first)
+				printf(",");
+			first = false;
+			if (value_names[val_idx])
+				printf("%s:", value_names[val_idx++]);
+			else
+				printf("(\?\?):");
+			printf("%lld", val->val);
+			val = val->next;
+		}
+		printf("\n");
+		if (tracefs_hist_data_next_bucket(hdata))
+			done = true;
+	} while (!done);
+
+	tracefs_list_free(key_names);
+	tracefs_list_free(value_names);
+	tracefs_hist_data_free(hdata);
+
+	return 0;
+}
+--
+
+BUGS
+----
+There are some known values that the histograms can produce that will break
+the parsing. Those are any string value that contains a comma (,) or a
+colon (:) may cause the parse to misinterpret the parsing and fail to parse.
+This may be fixed in the future.
+
+FILES
+-----
+[verse]
+--
+*tracefs.h*
+	Header file to include in order to have access to the library APIs.
+*-ltracefs*
+	Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+_libtracefs(3)_,
+_libtraceevent(3)_,
+_trace-cmd(1)_,
+_tracefs_hist_data_parse(3)_
+_tracefs_hist_data_read(3)_
+_tracefs_hist_data_free(3)_
+_tracefs_hist_data_free_list(3)_
+_tracefs_hist_alloc(3)_,
+_tracefs_hist_free(3)_,
+_tracefs_hist_add_key(3)_,
+_tracefs_hist_add_value(3)_,
+_tracefs_hist_add_name(3)_,
+_tracefs_hist_start(3)_,
+_tracefs_hist_destory(3)_,
+_tracefs_hist_add_sort_key(3)_,
+_tracefs_hist_sort_key_direction(3)_
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>
+*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>
+*sameeruddin shaik* <sameeruddin.shaik8@gmail.com>
+--
+REPORTING BUGS
+--------------
+Report bugs to  <linux-trace-devel@vger.kernel.org>
+
+LICENSE
+-------
+libtracefs is Free Software licensed under the GNU LGPL 2.1
+
+RESOURCES
+---------
+https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/
+
+COPYING
+-------
+Copyright \(C) 2020 VMware, Inc. Free use of this software is granted under
+the terms of the GNU Public License (GPL).
diff --git a/Documentation/libtracefs-hist-data.txt b/Documentation/libtracefs-hist-data.txt
new file mode 100644
index 000000000000..1890eabb7cd1
--- /dev/null
+++ b/Documentation/libtracefs-hist-data.txt
@@ -0,0 +1,294 @@
+libtracefs(3)
+=============
+
+NAME
+----
+tracefs_hist_data_parse, tracefs_hist_data_read, tracefs_hist_data_free, tracefs_hist_data_free_list - Read and parse hist format files
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <tracefs.h>*
+
+struct tracefs_hist_data pass:[*]tracefs_hist_data_parse(const char pass:[*]buffer,
+						  const char pass:[**]next_buffer,
+						  char pass:[**]err);
+
+struct tracefs_hist_data pass:[**]tracefs_hist_data_read(struct tracefs_instance pass:[*]instance,
+						  const char pass:[*]system,
+						  const char pass:[*]event,
+						  char pass:[**]err);
+
+void tracefs_hist_data_free(struct tracefs_hist_data pass:[*]hdata);
+void tracefs_hist_data_free_list(struct tracefs_hist_data pass:[**]hdata_list);
+--
+
+DESCRIPTION
+-----------
+The _hist_ trigger for trace events will create a histogram that can be read in
+the trace event's _hist_ file. The file is human readable ASCII format, but that
+makes it difficult to process in programs. The *tracefs_hist_data_*pass:[*]()
+functions convert the histogram ASCII format into structures that can be processed
+and converted into tables.
+
+*tracefs_hist_data_parse*() will read a string buffer that contains the contents
+of a trace_event hist file in _buffer_, and allocate and create a _tracefs_hist_data_
+descriptor. If there are more than one histograms in _buffer_, and _next_buffer_ is
+not NULL, it will then be set to the location of _buffer_ that contains the next
+histogram. _next_buffer_ may be a pointer to _buffer_ as it will not be updated
+until after the histogram is fully parsed. The _tracefs_trace_data_ returned must
+be freed with *tracefs_hist_data_free*().
+
+*tracefs_hist_data_read*() will read the "hist" file of the given trace event
+that is located in the _instance_ directory or the top level directory if _instance_ is NULL.
+The trace event is found with the _system_ and _event_ names, where _system_ can be
+NULL, and the first trace event with _event_ as its name will be used.
+It returns an array of _tracefs_hist_data_ structures or NULL on error. The
+array ends with a pointer to NULL. The reason for the array is because _hist_ files
+may contain more than one histogram, and this will return an array that has all the
+histograms in the _hist_ file parsed. The array can be freed with *tracefs_hist_data_free_list*()
+or each individual _tracefs_hist_data_ may be freed with *tracefs_hist_data_free*() and the
+array itself freed with *free*().
+
+*tracefs_hist_data_free*() frees a _tracefs_hist_data_ descriptor that was created
+with *tracefs_hist_data_parse*().
+
+*tracefs_hist_data_free_list*() frees an array of _tracefs_hist_data_ descriptors that was created
+with *tracefs_hist_data_read*().
+
+RETURN VALUE
+------------
+*tracefs_hist_data_parse*() returns an allocated _tracefs_hist_data_ descriptor. Or
+NULL on error, and if it was a parsing error and _err_ is not NULL, it will be set to
+an allocated string describing the error. _err_ must be freed with *free*().
+
+*tracefs_hist_data_read*() returns an allocated array of allocated _tracefs_hist_data_
+descriptors. Or NULL on error, and if it was a parsing error and _err_ is not NULL,
+it will be set to an allocated string describing the error. _err_ must be freed with *free*().
+
+EXAMPLE
+-------
+[source,c]
+--
+#include <stdlib.h>
+#include <unistd.h>
+#include <tracefs.h>
+
+int main (int argc, char **argv)
+{
+	struct tracefs_hist_data *hdata;
+	const struct tracefs_hist_bucket_key *key;
+	const struct tracefs_hist_bucket_val *val;
+	char buf[BUFSIZ];
+	const char *buffer;
+	char *content = NULL;
+	char *file;
+	char *err;
+	char **key_names;
+	char **value_names;
+	int key_idx;
+	int val_idx;
+	FILE *fp;
+	size_t r;
+	bool done = false;
+	int buffer_size = 0;
+	int c;
+	int i;
+
+	for (;;) {
+		c = getopt(argc, argv, "hf:");
+		if (c == -1)
+			break;
+
+		switch(c) {
+		case 'h':
+			fprintf(stderr, "usage: %s [-f file]\n", argv[0]);
+			exit(0);
+		case 'f':
+			file = optarg;
+			break;
+		}
+	}
+
+	if (file) {
+		if (!strcmp(file, "-"))
+			fp = stdin;
+		else
+			fp = fopen(file, "r");
+		if (!fp) {
+			perror(file);
+			exit(-1);
+		}
+		while ((r = fread(buf, 1, BUFSIZ, fp)) > 0) {
+			content = realloc(content, buffer_size + r + 1);
+			strncpy(content + buffer_size, buf, r);
+			buffer_size += r;
+		}
+		fclose(fp);
+		if (buffer_size)
+			content[buffer_size] = '\0';
+	} else if (argc == optind) {
+		fprintf(stderr, "usage: %s [-f file]\n", argv[0]);
+		exit(-1);
+	} else {
+		for (i = optind; i < argc; i++) {
+			r = strlen(argv[i]);
+			content = realloc(content, buffer_size + r + 2);
+			if (i != optind)
+				content[buffer_size++] = ' ';
+			strcpy(content + buffer_size, argv[i]);
+			buffer_size += r;
+		}
+	}
+
+	buffer = content;
+	hdata = tracefs_hist_data_parse(buffer, &buffer, &err);
+	printf("hdata = %p\n", hdata);
+	if (!hdata && err) {
+		printf("%s\n", err);
+		exit(-1);
+	}
+
+	key_names = tracefs_hist_data_key_names(hdata);
+	if (!key_names) {
+		perror("key_names");
+		exit(-1);
+	}
+
+	value_names = tracefs_hist_data_value_names(hdata);
+	if (!value_names) {
+		perror("value_names");
+		exit(-1);
+	}
+
+	do {
+		bool first = true;
+		key = tracefs_hist_data_keys(hdata);
+		val = tracefs_hist_data_values(hdata);
+		key_idx = 0;
+		val_idx = 0;
+
+		if (!key || !val) {
+			perror("keys or vals");
+			exit(-1);
+		}
+
+		while (key) {
+			if (!first)
+				printf(",");
+			first = false;
+			if (key_names[key_idx])
+				printf("%s:", key_names[key_idx++]);
+			else
+				printf("(\?\?):");
+			if (key->flags & TRACEFS_BUCKET_KEY_FL_UNDEF) {
+				fprintf(stderr, "bad key type?");
+				exit (-1);
+			} else if (key->flags & TRACEFS_BUCKET_KEY_FL_SINGLE) {
+
+				if (key->flags &
+				    ((1 << TRACEFS_HIST_KEY_SYM) |
+				     (1 << TRACEFS_HIST_KEY_SYM_OFFSET) |
+				     (1 << TRACEFS_HIST_KEY_SYSCALL) |
+				     (1 << TRACEFS_HIST_KEY_EXECNAME)))
+					printf("%s [ %lld ]",
+					       key->single.sym,
+					       key->single.val);
+				else
+					printf("%s", key->single.sym);
+
+			} else if  (key->flags & TRACEFS_BUCKET_KEY_FL_RANGE)
+				printf("%lld - %lld",
+				       key->range.start,
+				       key->range.end);
+			key = key->next;
+		}
+		printf(":");
+		first = true;
+		while (val) {
+			if (!first)
+				printf(",");
+			first = false;
+			if (value_names[val_idx])
+				printf("%s:", value_names[val_idx++]);
+			else
+				printf("(\?\?):");
+			printf("%lld", val->val);
+			val = val->next;
+		}
+		printf("\n");
+		if (tracefs_hist_data_next_bucket(hdata))
+			done = true;
+	} while (!done);
+
+	tracefs_list_free(key_names);
+	tracefs_list_free(value_names);
+	tracefs_hist_data_free(hdata);
+
+	return 0;
+}
+--
+
+BUGS
+----
+There are some known values that the histograms can produce that will break
+the parsing. Those are any string value that contains a comma (,) or a
+colon (:) may cause the parse to misinterpret the parsing and fail to parse.
+This may be fixed in the future.
+
+FILES
+-----
+[verse]
+--
+*tracefs.h*
+	Header file to include in order to have access to the library APIs.
+*-ltracefs*
+	Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+_libtracefs(3)_,
+_libtraceevent(3)_,
+_trace-cmd(1)_,
+_tracefs_hist_data_key_names(3)_
+_tracefs_hist_data_value_names(3)_
+_tracefs_hist_data_keys(3)_
+_tracefs_hist_data_values(3)_
+_tracefs_hist_data_next_bucket(3)_
+_tracefs_hist_data_first_bucket(3)_
+_tracefs_hist_alloc(3)_,
+_tracefs_hist_free(3)_,
+_tracefs_hist_add_key(3)_,
+_tracefs_hist_add_value(3)_,
+_tracefs_hist_add_name(3)_,
+_tracefs_hist_start(3)_,
+_tracefs_hist_destory(3)_,
+_tracefs_hist_add_sort_key(3)_,
+_tracefs_hist_sort_key_direction(3)_
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>
+*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>
+*sameeruddin shaik* <sameeruddin.shaik8@gmail.com>
+--
+REPORTING BUGS
+--------------
+Report bugs to  <linux-trace-devel@vger.kernel.org>
+
+LICENSE
+-------
+libtracefs is Free Software licensed under the GNU LGPL 2.1
+
+RESOURCES
+---------
+https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/
+
+COPYING
+-------
+Copyright \(C) 2020 VMware, Inc. Free use of this software is granted under
+the terms of the GNU Public License (GPL).
-- 
2.30.2


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

* Re: [PATCH 1/9] tracefs: Add API tracefs_hist_data_parse()
  2021-08-10 20:48 ` [PATCH 1/9] tracefs: Add API tracefs_hist_data_parse() Steven Rostedt
@ 2021-09-10 16:54   ` Daniel Bristot de Oliveira
  0 siblings, 0 replies; 11+ messages in thread
From: Daniel Bristot de Oliveira @ 2021-09-10 16:54 UTC (permalink / raw)
  To: Steven Rostedt, linux-trace-devel
  Cc: linux-kernel, Tom Zanussi, Masami Hiramatsu, Namhyung Kim,
	linux-rt-users, Clark Williams

On 8/10/21 10:48 PM, Steven Rostedt wrote:
> From: "Steven Rostedt (VMware)" <rostedt@goodmis.org>
> 
> Add a function tracefs_hist_data_parse() that will take the content of a
> trace event's hist data file, and parse it into a "tracefs_hist_data"
> descriptor that can be used to read the raw data from the file.

Steve,

Is this the latest version?

I am getting this when trying it (the patch 1/9):

[root@f34 libtracefs]# make
  COMPILE FPIC       tracefs-utils.o
  COMPILE FPIC       tracefs-instance.o
  COMPILE FPIC       tracefs-events.o
  COMPILE FPIC       tracefs-tools.o
  COMPILE FPIC       tracefs-marker.o
  COMPILE FPIC       tracefs-kprobes.o
  COMPILE FPIC       tracefs-hist.o
  COMPILE FPIC       tracefs-filter.o
  COMPILE FPIC       sqlhist-lex.o
  COMPILE FPIC       sqlhist.tab.o
  COMPILE FPIC       tracefs-sqlhist.o
make[1]: *** No rule to make target 'hist.l', needed by 'hist-lex.c'.  Stop.
make: *** [Makefile:365: /root/libtracefs/lib/tracefs/libtracefs.so.1.3.dev] Error 2

-- Daniel

> 
> Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
> ---
>  include/tracefs.h       |   7 +
>  src/Makefile            |   7 +
>  src/tracefs-hist-data.c | 861 ++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 875 insertions(+)
>  create mode 100644 src/tracefs-hist-data.c
> 
> diff --git a/include/tracefs.h b/include/tracefs.h
> index 17020de0108a..6bd40d72cb25 100644
> --- a/include/tracefs.h
> +++ b/include/tracefs.h
> @@ -413,6 +413,13 @@ static inline int tracefs_hist_destroy(struct tracefs_instance *instance,
>  	return tracefs_hist_command(instance, hist, TRACEFS_HIST_CMD_DESTROY);
>  }
>  
> +struct tracefs_hist_data;
> +
> +struct tracefs_hist_data *tracefs_hist_data_parse(const char *buffer,
> +						  const char **next_buffer,
> +						  char **err);
> +void tracefs_hist_data_free(struct tracefs_hist_data *hdata);
> +
>  struct tracefs_synth;
>  
>  /*
> diff --git a/src/Makefile b/src/Makefile
> index 9248efc5c7fd..1ab181416b82 100644
> --- a/src/Makefile
> +++ b/src/Makefile
> @@ -17,6 +17,10 @@ OBJS += sqlhist-lex.o
>  OBJS += sqlhist.tab.o
>  OBJS += tracefs-sqlhist.o
>  
> +# Order matters for the the two below
> +OBJS += hist-lex.o
> +OBJS += tracefs-hist-data.o
> +
>  OBJS := $(OBJS:%.o=$(bdir)/%.o)
>  DEPS := $(OBJS:$(bdir)/%.o=$(bdir)/.%.d)
>  
> @@ -45,6 +49,9 @@ sqlhist.tab.c: sqlhist.y sqlhist.tab.h
>  sqlhist-lex.c: sqlhist.l sqlhist.tab.c
>  	flex -o $@ $<
>  
> +hist-lex.c: hist.l
> +	flex -P hist_ -o $@ $<
> +
>  $(bdir)/%.o: %.c
>  	$(Q)$(call do_fpic_compile)
>  
> diff --git a/src/tracefs-hist-data.c b/src/tracefs-hist-data.c
> new file mode 100644
> index 000000000000..497ab9ce97b4
> --- /dev/null
> +++ b/src/tracefs-hist-data.c
> @@ -0,0 +1,861 @@
> +// SPDX-License-Identifier: LGPL-2.1
> +/*
> + * Copyright (C) 2021 VMware Inc, Steven Rostedt <rostedt@goodmis.org>
> + *
> + * Updates:
> + * Copyright (C) 2021, VMware, Tzvetomir Stoyanov <tz.stoyanov@gmail.com>
> + *
> + */
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <dirent.h>
> +#include <unistd.h>
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <limits.h>
> +
> +#include "tracefs.h"
> +#include "tracefs-local.h"
> +
> +#define HIST_FILE "hist"
> +
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <stdarg.h>
> +#include <errno.h>
> +#include <ctype.h>
> +#include <unistd.h>
> +#include <tracefs.h>
> +
> +#include "hist.h"
> +
> +#define offset_of(type, field)	((unsigned long )(&((type *)0)->field))
> +#define container_of(p, type, field) ((type *)((void *)(p) - offset_of(type, field)));
> +
> +extern int hist_lex_init_extra(void *data, void* ptr_yy_globals);
> +extern int hist_lex_destroy(void *scanner);
> +
> +int hist_yyinput(void *extra, char *buf, int max)
> +{
> +	struct hist_data *data = extra;
> +
> +	if (!data || !data->buffer)
> +		return -1;
> +
> +	if (data->buffer_idx + max > data->buffer_size)
> +		max = data->buffer_size - data->buffer_idx;
> +
> +	if (max)
> +		memcpy(buf, data->buffer + data->buffer_idx, max);
> +
> +	data->buffer_idx += max;
> +
> +	return max;
> +}
> +
> +extern int hist_yylex(void *data, void *scanner);
> +
> +static char *name_token(enum yytokentype type)
> +{
> +	switch (type) {
> +	case YYEMPTY:
> +		return "YYEMPTY";
> +	case YYEOF:
> +		return "YYEOF";
> +	case YYerror:
> +		return "YYerror";
> +	case YYUNDEF:
> +		return "YYUNDEF";
> +	case NUMBER:
> +		return "NUMBER";
> +	case HEX:
> +		return "HEX";
> +	case NEWLINE:
> +		return "NEWLINE";
> +	case STRING:
> +		return "STRING";
> +	case KEY_TYPE:
> +		return "KEY_TYPE";
> +	case KEY_VAL:
> +		return "KEY_VAL";
> +	case START_RANGE:
> +		return "START_RANGE";
> +	case RANGE_LINEAR:
> +		return "RANGE_LINEAR";
> +	case RANGE_EXPONENT:
> +		return "RANGE_EXPONENT";
> +	case RAW_VAL:
> +		return "RAW_VAL";
> +	case STACKTRACE:
> +		return "STACKTRACE";
> +	case STACK_ITEM:
> +		return "STACK_ITEM";
> +	case STACK_MOD:
> +		return "STACK_MOD";
> +	case VALUE:
> +		return "VALUE";
> +	case TOTALS:
> +		return "TOTALS";
> +	case HITS:
> +		return "HITS";
> +	case ENTRIES:
> +		return "ENTRIES";
> +	case DROPPED:
> +		return "DROPPED";
> +	case COMMENT:
> +		return "COMMENT";
> +	case COLON:
> +		return "COLON";
> +	case COMMA:
> +		return "COMMA";
> +	}
> +	return NULL;
> +}
> +
> +enum tracefs_bucket_key_type {
> +	TRACEFS_BUCKET_KEY_UNDEF,
> +	TRACEFS_BUCKET_KEY_SINGLE,
> +	TRACEFS_BUCKET_KEY_RANGE,
> +};
> +
> +struct tracefs_hist_bucket_key_single {
> +	long long		val;
> +	char			*sym;
> +};
> +
> +struct tracefs_hist_bucket_key_range {
> +	long long		start;
> +	long long		end;
> +};
> +
> +struct tracefs_hist_bucket_key {
> +	struct tracefs_hist_bucket_key	*next;
> +	enum tracefs_bucket_key_type	type;
> +	union {
> +		struct tracefs_hist_bucket_key_single	single;
> +		struct tracefs_hist_bucket_key_range	range;
> +	};
> +};
> +
> +struct tracefs_hist_bucket_val {
> +	struct tracefs_hist_bucket_val		*next;
> +	long long				val;
> +};
> +
> +struct tracefs_hist_bucket {
> +	struct tracefs_hist_bucket		*next;
> +	struct tracefs_hist_bucket_key		*keys;
> +	struct tracefs_hist_bucket_key		**next_key;
> +	struct tracefs_hist_bucket_val		*vals;
> +	struct tracefs_hist_bucket_val		**next_val;
> +};
> +
> +struct tracefs_hist_data {
> +	char				**key_names;
> +	char				**value_names;
> +	struct tracefs_hist_bucket	*buckets;
> +	struct tracefs_hist_bucket	**next_bucket;
> +	unsigned long long		hits;
> +	unsigned long long		entries;
> +	unsigned long long		dropped;
> +};
> +
> +static int do_comment(struct tracefs_hist_data *hdata, const char *comment)
> +{
> +	return 0;
> +}
> +
> +static int do_key_type(struct tracefs_hist_data *hdata, const char *key)
> +{
> +	char **tmp;
> +
> +	tmp = tracefs_list_add(hdata->key_names, key);
> +	if (!tmp)
> +		return -1;
> +	hdata->key_names = tmp;
> +
> +	return 0;
> +}
> +
> +static int do_value_type(struct tracefs_hist_data *hdata, const char *key)
> +{
> +	char **tmp;
> +
> +	tmp = tracefs_list_add(hdata->value_names, key);
> +	if (!tmp)
> +		return -1;
> +	hdata->value_names = tmp;
> +
> +	return 0;
> +}
> +
> +static int start_new_row(struct tracefs_hist_data *hdata)
> +{
> +	struct tracefs_hist_bucket *bucket;
> +	struct tracefs_hist_bucket_key *key;
> +
> +	bucket = calloc(1, sizeof(*bucket));
> +	if (!bucket)
> +		return -1;
> +
> +	key = calloc(1, sizeof(*key));
> +	if (!key) {
> +		free(bucket);
> +		return -1;
> +	}
> +
> +	bucket->keys = key;
> +	bucket->next_key = &key->next;
> +
> +	bucket->next_val = &bucket->vals;
> +
> +	*hdata->next_bucket = bucket;
> +	hdata->next_bucket = &bucket->next;
> +	return 0;
> +}
> +
> +static int start_new_key(struct tracefs_hist_data *hdata)
> +{
> +	struct tracefs_hist_bucket *bucket;
> +	struct tracefs_hist_bucket_key *key;
> +
> +	bucket = container_of(hdata->next_bucket, struct tracefs_hist_bucket, next);
> +
> +	key = calloc(1, sizeof(*key));
> +	if (!key) {
> +		free(bucket);
> +		return -1;
> +	}
> +
> +	*bucket->next_key = key;
> +	bucket->next_key = &key->next;
> +
> +	return 0;
> +}
> +
> +static char *chomp(char *text)
> +{
> +	char *p;
> +	int len;
> +
> +	while (isspace(*text))
> +		text++;
> +
> +	len = strlen(text);
> +	p = text + len - 1;
> +	while (p >= text && isspace(*p))
> +		p--;
> +
> +	p[1] = '\0';
> +
> +	return text;
> +}
> +
> +static int __do_key_val(struct tracefs_hist_data *hdata,
> +			char *text, const char *delim, const char *end)
> +{
> +	struct tracefs_hist_bucket *bucket;
> +	struct tracefs_hist_bucket_key *key;
> +	struct tracefs_hist_bucket_key_single *k;
> +	char *val;
> +	int len;
> +
> +	text = chomp(text);
> +
> +	bucket = container_of(hdata->next_bucket, struct tracefs_hist_bucket, next);
> +
> +	key = container_of(bucket->next_key, struct tracefs_hist_bucket_key, next);
> +	if (!key->type)
> +		key->type = TRACEFS_BUCKET_KEY_SINGLE;
> +
> +	if (key->type != TRACEFS_BUCKET_KEY_SINGLE)
> +		return -1;
> +
> +	k = &key->single;
> +
> +	len = strlen(text);
> +	len += k->sym ? strlen(k->sym) + strlen(delim) : 0;
> +	if (end)
> +		len += strlen(end);
> +
> +	val = realloc(k->sym, len + 1);
> +	if (!val)
> +		return -1;
> +
> +	if (k->sym)
> +		strcat(val, delim);
> +	else
> +		val[0] = '\0';
> +
> +	strcat(val, text);
> +	if (end)
> +		strcat(val, end);
> +
> +	k->sym = val;
> +
> +	return 0;
> +}
> +
> +static int do_key_val(struct tracefs_hist_data *hdata, char *text)
> +{
> +	return __do_key_val(hdata, text, " ", NULL);
> +}
> +
> +static int do_key_stack(struct tracefs_hist_data *hdata, char *text)
> +{
> +	return __do_key_val(hdata, text, "\n", NULL);
> +}
> +
> +static int do_key_stack_mod(struct tracefs_hist_data *hdata, char *text)
> +{
> +	return __do_key_val(hdata, text, " [", "]");
> +}
> +
> +static int do_key_raw(struct tracefs_hist_data *hdata, char *text)
> +{
> +	struct tracefs_hist_bucket *bucket;
> +	struct tracefs_hist_bucket_key *key;
> +	struct tracefs_hist_bucket_key_single *k;
> +
> +	text = chomp(text);
> +
> +	bucket = container_of(hdata->next_bucket, struct tracefs_hist_bucket, next);
> +
> +	key = container_of(bucket->next_key, struct tracefs_hist_bucket_key, next);
> +	if (key->type != TRACEFS_BUCKET_KEY_SINGLE)
> +		return -1;
> +
> +	k = &key->single;
> +
> +	if (k->val)
> +		return -1;
> +
> +	k->val = strtoll(text, NULL, 0);
> +
> +	return 0;
> +}
> +
> +static int do_key_range(struct tracefs_hist_data *hdata, long long start,
> +			long long end)
> +{
> +	struct tracefs_hist_bucket *bucket;
> +	struct tracefs_hist_bucket_key *key;
> +	struct tracefs_hist_bucket_key_range *k;
> +
> +	bucket = container_of(hdata->next_bucket, struct tracefs_hist_bucket, next);
> +
> +	key = container_of(bucket->next_key, struct tracefs_hist_bucket_key, next);
> +
> +	if (!key->type)
> +		key->type = TRACEFS_BUCKET_KEY_RANGE;
> +
> +	if (key->type != TRACEFS_BUCKET_KEY_RANGE)
> +		return -1;
> +
> +	k = &key->range;
> +
> +	k->start = start;
> +	k->end = end;
> +
> +	return 0;
> +}
> +
> +static int do_value_num(struct tracefs_hist_data *hdata, long long num)
> +{
> +	struct tracefs_hist_bucket *bucket;
> +	struct tracefs_hist_bucket_val *val;
> +
> +	bucket = container_of(hdata->next_bucket, struct tracefs_hist_bucket, next);
> +	val = calloc(1, sizeof(*val));
> +	if (!val)
> +		return -1;
> +
> +	val->val = num;
> +
> +	*bucket->next_val = val;
> +	bucket->next_val = &val->next;
> +
> +	return 0;
> +}
> +
> +static long long expo(unsigned int e, long long exp)
> +{
> +	long long ret;
> +
> +	if (exp < 0)
> +		exp = 0;
> +
> +	if (e == 2)
> +		return 1LL << exp;
> +
> +	ret = 1;
> +	for (; exp > 0; exp--)
> +		ret *= e;
> +	return e;
> +}
> +
> +enum hist_state {
> +	HIST_START,
> +	HIST_KEYS_START,
> +	HIST_KEYS,
> +	HIST_KEY_VALS,
> +	HIST_RANGE,
> +	HIST_VALUES,
> +	HIST_NEXT_KEY,
> +	HIST_STACK,
> +	HIST_ENTRIES,
> +	HIST_DROPPED,
> +	HIST_END,
> +};
> +
> +static const char *find_buffer_line(const char *buffer, int line_no)
> +{
> +	int line = 0;
> +	int i;
> +
> +	for (i = 0; buffer[i]; i++) {
> +		if (buffer[i] == '\n') {
> +			line++;
> +			if (line >= line_no) {
> +				i++;
> +				break;
> +			}
> +		}
> +	}
> +	return buffer + i;
> +}
> +
> +static void print_line(struct trace_seq *seq, struct hist_data *data)
> +{
> +	const char *buffer = data->buffer;
> +	int i;
> +
> +	buffer = find_buffer_line(buffer, data->line_no);
> +
> +	for (i = 0; buffer[i]; i++) {
> +		if (buffer[i] == '\n')
> +			break;
> +	}
> +
> +	trace_seq_printf(seq, "%.*s (line:%d idx:%d)\n", i, buffer,
> +			 data->line_no, data->line_idx);
> +	trace_seq_printf(seq, "%*s\n", data->line_idx, "^");
> +}
> +
> +static void print_error(struct hist_data *data, char **err,
> +			enum hist_state state, enum yytokentype type)
> +{
> +	struct trace_seq seq;
> +	char *tname;
> +
> +	if (!err)
> +		return;
> +
> +	trace_seq_init(&seq);
> +
> +	print_line(&seq, data);
> +
> +	trace_seq_printf(&seq, "Error in ");
> +	switch (state) {
> +	case HIST_START:
> +		trace_seq_printf(&seq, "HIST_START");
> +		break;
> +	case HIST_KEYS_START:
> +		trace_seq_printf(&seq, "HIST_KEYS_START");
> +		break;
> +	case HIST_KEYS:
> +		trace_seq_printf(&seq, "HIST_KEYS");
> +		break;
> +	case HIST_KEY_VALS:
> +		trace_seq_printf(&seq, "HIST_KEY_VALS");
> +		break;
> +	case HIST_RANGE:
> +		trace_seq_printf(&seq, "HIST_RANGE");
> +		break;
> +	case HIST_VALUES:
> +		trace_seq_printf(&seq, "HIST_VALUES");
> +		break;
> +	case HIST_NEXT_KEY:
> +		trace_seq_printf(&seq, "HIST_NEXT_KEY");
> +	case HIST_STACK:
> +		trace_seq_printf(&seq, "HIST_STACK");
> +		break;
> +	case HIST_ENTRIES:
> +		trace_seq_printf(&seq, "HIST_ENTRIES");
> +		break;
> +	case HIST_DROPPED:
> +		trace_seq_printf(&seq, "HIST_DROPPED");
> +		break;
> +	case HIST_END:
> +		trace_seq_printf(&seq, "HIST_END");
> +		break;
> +	}
> +	trace_seq_printf(&seq, " with token ");
> +	tname = name_token(type);
> +	if (tname)
> +		trace_seq_printf(&seq, "%s", tname);
> +	else
> +		trace_seq_printf(&seq, "(unknown %d)", type);
> +
> +	trace_seq_printf(&seq, " last token %s\n", data->text);
> +	trace_seq_terminate(&seq);
> +	if (seq.buffer)
> +		*err = seq.buffer;
> +	seq.buffer = NULL;
> +	trace_seq_destroy(&seq);
> +}
> +
> +static void update_next(const char **next_buffer, struct hist_data *data)
> +{
> +	if (!next_buffer)
> +		return;
> +
> +	*next_buffer = find_buffer_line(data->buffer, data->line_no - 1);
> +}
> +
> +/**
> + * tracefs_hist_data_free - free a created hist data descriptor
> + * @hdata: The tracefs_hist_data descriptor to free.
> + *
> + * Frees the data allocated by tracefs_hist_data_parse().
> + */
> +void tracefs_hist_data_free(struct tracefs_hist_data *hdata)
> +{
> +	struct tracefs_hist_bucket *bucket;
> +	struct tracefs_hist_bucket_key *key;
> +	struct tracefs_hist_bucket_val *val;
> +
> +	if (!hdata)
> +		return;
> +
> +	tracefs_list_free(hdata->key_names);
> +	tracefs_list_free(hdata->value_names);
> +
> +	while ((bucket = hdata->buckets)) {
> +		hdata->buckets = bucket->next;
> +		while ((key = bucket->keys)) {
> +			bucket->keys = key->next;
> +			switch (key->type) {
> +			case TRACEFS_BUCKET_KEY_SINGLE:
> +				free(key->single.sym);
> +				break;
> +			default:
> +				break;
> +			}
> +			free(key);
> +		}
> +		while ((val = bucket->vals)) {
> +			bucket->vals = val->next;
> +			free(val);
> +		}
> +		free(bucket);
> +	}
> +
> +	free(hdata);
> +}
> +
> +/* Used for debugging in gdb */
> +static void breakpoint(char *text)
> +{
> +}
> +
> +/**
> + * tracefs_hist_data_parse - parse a hist file of a trace event
> + * @buffer: The buffer containing the hist file content
> + * @next_buffer: If not NULL will point to the next hist in the buffer
> + * @err: If not NULL, will load the error message on error
> + *
> + * Reads and parses the content of a "hist" file of a trace event.
> + * It will return a descriptor that can be used to read the content and
> + * create a histogram table.
> + *
> + * Because "hist" files may contain more than one histogram, and this
> + * function will only parse one of the histograms, if there are more
> + * than one histogram in the buffer, and @next_buffer is not NULL, then
> + * it will return the location of the next histogram in @next_buffer.
> + *
> + * If there's an error in the parsing, then @err will contain an error
> + * message about what went wrong.
> + *
> + * Returns a desrciptor of a histogram representing the hist file content.
> + * NULL on error.
> + * The descriptor must be freed with tracefs_hist_data_free().
> + */
> +struct tracefs_hist_data *
> +tracefs_hist_data_parse(const char *buffer, const char **next_buffer, char **err)
> +{
> +	struct tracefs_hist_data *hdata;
> +	struct hist_data data;
> +	enum hist_state state = 0;
> +	long long start_range, end_range;
> +	bool first = false;
> +	unsigned int e;
> +	int buffer_size;
> +	bool done = false;
> +	char *text;
> +	enum yytokentype type;
> +	int ret;
> +
> +	if (!buffer)
> +		return NULL;
> +
> +	hdata = calloc(1, sizeof(*hdata));
> +	if (!hdata)
> +		return NULL;
> +
> +	hdata->next_bucket = &hdata->buckets;
> +
> +	memset(&data, 0, sizeof(data));
> +
> +	buffer_size = strlen(buffer);
> +	data.buffer = buffer;
> +	data.buffer_size = buffer_size;
> +	data.text = malloc(buffer_size);
> +	if (!data.text) {
> +		free(hdata);
> +		perror("text");
> +		exit(-1);
> +	}
> +
> +	ret = hist_lex_init_extra(&data, &data.scanner);
> +	if (ret < 0) {
> +		perror("ylex_init");
> +		return NULL;
> +	}
> +	while (!done) {
> +		type = hist_yylex(&data, data.scanner);
> +		if (type < 0)
> +			break;
> +		text = data.text;
> +		breakpoint(text);
> +		switch (state) {
> +		case HIST_START:
> +			switch (type) {
> +			case COMMENT:
> +				first = true;
> +				ret = do_comment(hdata, text);
> +				if (ret < 0)
> +					goto error;
> +				break;
> +			case KEY_TYPE:
> +				goto key_type;
> +			case STACKTRACE:
> +				goto stacktrace;
> +			default:
> +				goto error;
> +			}
> +			break;
> +		case HIST_KEYS_START:
> +			switch (type) {
> +			case KEY_TYPE:
> + key_type:
> +				if (first) {
> +					ret = do_key_type(hdata, text);
> +					if (ret < 0)
> +						goto error;
> +				}
> +				ret = start_new_row(hdata);
> +				state = HIST_KEY_VALS;
> +				break;
> +			case STACKTRACE:
> + stacktrace:
> +				if (first) {
> +					ret = do_key_type(hdata, "stacktrace");
> +					if (ret < 0)
> +						goto error;
> +				}
> +				ret = start_new_row(hdata);
> +				state = HIST_STACK;
> +				break;
> +			case HITS:
> +				hdata->hits = strtoll(text, NULL, 0);
> +				state = HIST_ENTRIES;
> +				break;
> +			default:
> +				goto error;
> +			}
> +			break;
> +		case HIST_KEYS:
> +			switch (type) {
> +			case KEY_TYPE:
> +				if (first) {
> +					ret = do_key_type(hdata, text);
> +					if (ret < 0)
> +						goto error;
> +				}
> +				ret = start_new_key(hdata);
> +				state = HIST_KEY_VALS;
> +				break;
> +			case STACKTRACE:
> +				if (first) {
> +					ret = do_key_type(hdata, "stacktrace");
> +					if (ret < 0)
> +						goto error;
> +				}
> +				ret = start_new_key(hdata);
> +				state = HIST_STACK;
> +				break;
> +			case NEWLINE:
> +				break;
> +			case COLON:
> +				state = HIST_VALUES;
> +				break;
> +			default:
> +				goto error;
> +			}
> +			break;
> +		case HIST_NEXT_KEY:
> +			switch (type) {
> +			case COLON:
> +				state = HIST_VALUES;
> +				break;
> +			case COMMA:
> +				state = HIST_KEYS;
> +				break;
> +			default:
> +				goto error;
> +			}
> +			break;
> +		case HIST_KEY_VALS:
> +			switch (type) {
> +			case NEWLINE:
> +				continue;
> +			case START_RANGE:
> +				start_range = strtoll(text, NULL, 0);
> +				state = HIST_RANGE;
> +				break;
> +			case KEY_VAL:
> +				ret = do_key_val(hdata, text);
> +				if (ret < 0)
> +					goto error;
> +				break;
> +			case RAW_VAL:
> +				ret = do_key_raw(hdata, text);
> +				if (ret < 0)
> +					goto error;
> +				state = HIST_NEXT_KEY;
> +				break;
> +			case COLON:
> +				state = HIST_VALUES;
> +				break;
> +			case COMMA:
> +				state = HIST_KEYS;
> +				break;
> +			default:
> +				goto error;
> +			}
> +			break;
> +		case HIST_STACK:
> +			switch (type) {
> +			case NEWLINE:
> +				break;
> +			case STACK_ITEM:
> +				ret = do_key_stack(hdata, text);
> +				if (ret < 0)
> +					goto error;
> +				break;
> +			case STACK_MOD:
> +				ret = do_key_stack_mod(hdata, text);
> +				if (ret < 0)
> +					goto error;
> +				break;
> +			case COLON:
> +				state = HIST_VALUES;
> +				break;
> +			case COMMA:
> +				state = HIST_KEYS;
> +				break;
> +			default:
> +				goto error;
> +			}
> +			break;
> +		case HIST_RANGE:
> +			switch (type) {
> +			case RANGE_LINEAR:
> +				do_key_range(hdata, start_range,
> +					     strtoll(text, NULL, 0));
> +				break;
> +			case RANGE_EXPONENT:
> +				end_range = strtoll(text, NULL, 0);
> +				e = (unsigned int)start_range;
> +				start_range = expo(e, end_range - 1);
> +				end_range = expo(e, end_range);
> +				do_key_range(hdata, start_range, end_range);
> +				break;
> +			default:
> +				goto error;
> +			}
> +			state = HIST_KEYS;
> +			break;
> +		case HIST_VALUES:
> +			switch (type) {
> +			case VALUE:
> +				if (first) {
> +					ret = do_value_type(hdata, text);
> +					if (ret < 0)
> +						goto error;
> +				}
> +				break;
> +			case NUMBER:
> +				ret = do_value_num(hdata, strtoll(text, NULL, 0));
> +				if (ret < 0)
> +					goto error;
> +				break;
> +			case NEWLINE:
> +				state = HIST_KEYS_START;
> +				first = false;
> +				break;
> +			default:
> +				goto error;
> +			}
> +			break;
> +		case HIST_ENTRIES:
> +			switch (type) {
> +			case ENTRIES:
> +				hdata->entries = strtoll(text, NULL, 0);
> +				state = HIST_DROPPED;
> +				break;
> +			default:
> +				goto error;
> +			}
> +			break;
> +		case HIST_DROPPED:
> +			switch (type) {
> +			case DROPPED:
> +				hdata->dropped = strtoll(text, NULL, 0);
> +				state = HIST_END;
> +				break;
> +			default:
> +				goto error;
> +			}
> +			break;
> +		case HIST_END:
> +			done = true;
> +			switch (type) {
> +			case COMMENT:
> +				update_next(next_buffer, &data);
> +				break;
> +			case YYEOF:
> +				/* Fall through */
> +			default:
> +				/* Do at end, as next_buffer may point to buffer*/
> +				if (next_buffer)
> +					*next_buffer = NULL;
> +				break;
> +			}
> +			break;
> +		}
> +	}
> +
> +	hist_lex_destroy(data.scanner);
> +	free(data.text);
> +
> +	return hdata;
> + error:
> +	print_error(&data, err, state, type);
> +	hist_lex_destroy(data.scanner);
> +	free(data.text);
> +	tracefs_hist_data_free(hdata);
> +	return NULL;
> +}
> 


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

end of thread, other threads:[~2021-09-10 16:54 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-08-10 20:48 [PATCH 0/9] libtracefs: APIs to read a trace event hist file Steven Rostedt
2021-08-10 20:48 ` [PATCH 1/9] tracefs: Add API tracefs_hist_data_parse() Steven Rostedt
2021-09-10 16:54   ` Daniel Bristot de Oliveira
2021-08-10 20:48 ` [PATCH 2/9] libtracefs: Parse comment for hist data information Steven Rostedt
2021-08-10 20:48 ` [PATCH 3/9] libtracefs: Change hist_data_key type to flags Steven Rostedt
2021-08-10 20:48 ` [PATCH 4/9] libtracefs: Add API tracefs_hist_data_read() Steven Rostedt
2021-08-10 20:48 ` [PATCH 5/9] libtracefs: Add API tracefs_list_dup() Steven Rostedt
2021-08-10 20:48 ` [PATCH 6/9] libtracefs: Add APIs tracefs_hist_data_keys/value_names() Steven Rostedt
2021-08-10 20:48 ` [PATCH 7/9] libtracefs: Add API tracefs_hist_data_keys/values() and next_bucket() Steven Rostedt
2021-08-10 20:48 ` [PATCH 8/9] libtracefs: Have tracefs_hist_bucket_key flags save the type Steven Rostedt
2021-08-10 20:48 ` [PATCH 9/9] libtracefs: Add man pages for tracefs_hist_data functions Steven Rostedt

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).