2021-08-10 20:50:15

by Steven Rostedt

[permalink] [raw]
Subject: [PATCH 0/9] libtracefs: APIs to read a trace event hist file

From: "Steven Rostedt (VMware)" <[email protected]>

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


2021-08-10 20:50:25

by Steven Rostedt

[permalink] [raw]
Subject: [PATCH 4/9] libtracefs: Add API tracefs_hist_data_read()

From: "Steven Rostedt (VMware)" <[email protected]>

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) <[email protected]>
---
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

2021-08-10 20:50:36

by Steven Rostedt

[permalink] [raw]
Subject: [PATCH 7/9] libtracefs: Add API tracefs_hist_data_keys/values() and next_bucket()

From: "Steven Rostedt (VMware)" <[email protected]>

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) <[email protected]>
---
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

2021-08-10 20:51:30

by Steven Rostedt

[permalink] [raw]
Subject: [PATCH 6/9] libtracefs: Add APIs tracefs_hist_data_keys/value_names()

From: "Steven Rostedt (VMware)" <[email protected]>

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) <[email protected]>
---
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

2021-08-10 20:51:35

by Steven Rostedt

[permalink] [raw]
Subject: [PATCH 1/9] tracefs: Add API tracefs_hist_data_parse()

From: "Steven Rostedt (VMware)" <[email protected]>

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) <[email protected]>
---
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 <[email protected]>
+ *
+ * Updates:
+ * Copyright (C) 2021, VMware, Tzvetomir Stoyanov <[email protected]>
+ *
+ */
+#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

2021-08-10 20:51:39

by Steven Rostedt

[permalink] [raw]
Subject: [PATCH 9/9] libtracefs: Add man pages for tracefs_hist_data functions

From: "Steven Rostedt (VMware)" <[email protected]>

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) <[email protected]>
---
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* <[email protected]>
+*Tzvetomir Stoyanov* <[email protected]>
+*sameeruddin shaik* <[email protected]>
+--
+REPORTING BUGS
+--------------
+Report bugs to <[email protected]>
+
+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* <[email protected]>
+*Tzvetomir Stoyanov* <[email protected]>
+*sameeruddin shaik* <[email protected]>
+--
+REPORTING BUGS
+--------------
+Report bugs to <[email protected]>
+
+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

Subject: Re: [PATCH 1/9] tracefs: Add API tracefs_hist_data_parse()

On 8/10/21 10:48 PM, Steven Rostedt wrote:
> From: "Steven Rostedt (VMware)" <[email protected]>
>
> 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) <[email protected]>
> ---
> 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 <[email protected]>
> + *
> + * Updates:
> + * Copyright (C) 2021, VMware, Tzvetomir Stoyanov <[email protected]>
> + *
> + */
> +#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;
> +}
>