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

From: Daniel Bristot de Oliveira
Date: Fri Sep 10 2021 - 12:54:19 EST


On 8/10/21 10:48 PM, Steven Rostedt wrote:
> From: "Steven Rostedt (VMware)" <rostedt@xxxxxxxxxxx>
>
> 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@xxxxxxxxxxx>
> ---
> 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@xxxxxxxxxxx>
> + *
> + * Updates:
> + * Copyright (C) 2021, VMware, Tzvetomir Stoyanov <tz.stoyanov@xxxxxxxxx>
> + *
> + */
> +#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;
> +}
>