* [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 [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
* [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 [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 [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 [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 [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 [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 [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 [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 [flat|nested] 11+ messages in thread