/* * trace_events_hist - trace event hist triggers * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * Copyright (C) 2015 Tom Zanussi <tom.zanussi@linux.intel.com> */ #include <linux/module.h>#include <linux/kallsyms.h>#include <linux/mutex.h>#include <linux/slab.h>#include <linux/stacktrace.h>#include <linux/rculist.h> #include "tracing_map.h"#include "trace.h" struct hist_field; typedef u64 (*hist_field_fn_t) (struct hist_field *field, void *event); struct hist_field { struct ftrace_event_field *field; unsigned long flags; hist_field_fn_t fn; unsigned int size; unsigned int offset;}; static u64 hist_field_none(struct hist_field *field, void *event){ return 0;} static u64 hist_field_counter(struct hist_field *field, void *event){ return 1;} static u64 hist_field_string(struct hist_field *hist_field, void *event){ char *addr = (char *)(event + hist_field->field->offset); return (u64)(unsigned long)addr;} static u64 hist_field_dynstring(struct hist_field *hist_field, void *event){ u32 str_item = *(u32 *)(event + hist_field->field->offset); int str_loc = str_item & 0xffff; char *addr = (char *)(event + str_loc); return (u64)(unsigned long)addr;} static u64 hist_field_pstring(struct hist_field *hist_field, void *event){ char **addr = (char **)(event + hist_field->field->offset); return (u64)(unsigned long)*addr;} static u64 hist_field_log2(struct hist_field *hist_field, void *event){ u64 val = *(u64 *)(event + hist_field->field->offset); return (u64) ilog2(roundup_pow_of_two(val));} #define DEFINE_HIST_FIELD_FN(type) \static u64 hist_field_##type(struct hist_field *hist_field, void *event)\{ \ type *addr = (type *)(event + hist_field->field->offset); \ \ return (u64)(unsigned long)*addr; \} DEFINE_HIST_FIELD_FN(s64);DEFINE_HIST_FIELD_FN(u64);DEFINE_HIST_FIELD_FN(s32);DEFINE_HIST_FIELD_FN(u32);DEFINE_HIST_FIELD_FN(s16);DEFINE_HIST_FIELD_FN(u16);DEFINE_HIST_FIELD_FN(s8);DEFINE_HIST_FIELD_FN(u8); #define for_each_hist_field(i, hist_data) \ for ((i) = 0; (i) < (hist_data)->n_fields; (i)++) #define for_each_hist_val_field(i, hist_data) \ for ((i) = 0; (i) < (hist_data)->n_vals; (i)++) #define for_each_hist_key_field(i, hist_data) \ for ((i) = (hist_data)->n_vals; (i) < (hist_data)->n_fields; (i)++) #define HIST_STACKTRACE_DEPTH 16#define HIST_STACKTRACE_SIZE (HIST_STACKTRACE_DEPTH * sizeof(unsigned long))#define HIST_STACKTRACE_SKIP 5 #define HITCOUNT_IDX 0#define HIST_KEY_SIZE_MAX (MAX_FILTER_STR_VAL + HIST_STACKTRACE_SIZE) enum hist_field_flags { HIST_FIELD_FL_HITCOUNT = 1, HIST_FIELD_FL_KEY = 2, HIST_FIELD_FL_STRING = 4, HIST_FIELD_FL_HEX = 8, HIST_FIELD_FL_SYM = 16, HIST_FIELD_FL_SYM_OFFSET = 32, HIST_FIELD_FL_EXECNAME = 64, HIST_FIELD_FL_SYSCALL = 128, HIST_FIELD_FL_STACKTRACE = 256, HIST_FIELD_FL_LOG2 = 512,}; struct hist_trigger_attrs { char *keys_str; char *vals_str; char *sort_key_str; char *name; bool pause; bool cont; bool clear; unsigned int map_bits;}; struct hist_trigger_data { struct hist_field *fields[TRACING_MAP_FIELDS_MAX]; unsigned int n_vals; unsigned int n_keys; unsigned int n_fields; unsigned int key_size; struct tracing_map_sort_key sort_keys[TRACING_MAP_SORT_KEYS_MAX]; unsigned int n_sort_keys; struct trace_event_file *event_file; struct hist_trigger_attrs *attrs; struct tracing_map *map;}; static hist_field_fn_t select_value_fn(int field_size, int field_is_signed){ hist_field_fn_t fn = NULL; switch (field_size) { case 8: if (field_is_signed) fn = hist_field_s64; else fn = hist_field_u64; break; case 4: if (field_is_signed) fn = hist_field_s32; else fn = hist_field_u32; break; case 2: if (field_is_signed) fn = hist_field_s16; else fn = hist_field_u16; break; case 1: if (field_is_signed) fn = hist_field_s8; else fn = hist_field_u8; break; } return fn;} static int parse_map_size(char *str){ unsigned long size, map_bits; int ret; strsep(&str, "="); if (!str) { ret = -EINVAL; goto out; } ret = kstrtoul(str, 0, &size); if (ret) goto out; map_bits = ilog2(roundup_pow_of_two(size)); if (map_bits < TRACING_MAP_BITS_MIN || map_bits > TRACING_MAP_BITS_MAX) ret = -EINVAL; else ret = map_bits; out: return ret;} static void destroy_hist_trigger_attrs(struct hist_trigger_attrs *attrs){ if (!attrs) return; kfree(attrs->name); kfree(attrs->sort_key_str); kfree(attrs->keys_str); kfree(attrs->vals_str); kfree(attrs);} static struct hist_trigger_attrs *parse_hist_trigger_attrs(char *trigger_str){ struct hist_trigger_attrs *attrs; int ret = 0; attrs = kzalloc(sizeof(*attrs), GFP_KERNEL); if (!attrs) return ERR_PTR(-ENOMEM); while (trigger_str) { char *str = strsep(&trigger_str, ":"); if ((strncmp(str, "key=", strlen("key=")) == 0) || (strncmp(str, "keys=", strlen("keys=")) == 0)) attrs->keys_str = kstrdup(str, GFP_KERNEL); else if ((strncmp(str, "val=", strlen("val=")) == 0) || (strncmp(str, "vals=", strlen("vals=")) == 0) || (strncmp(str, "values=", strlen("values=")) == 0)) attrs->vals_str = kstrdup(str, GFP_KERNEL); else if (strncmp(str, "sort=", strlen("sort=")) == 0) attrs->sort_key_str = kstrdup(str, GFP_KERNEL); else if (strncmp(str, "name=", strlen("name=")) == 0) attrs->name = kstrdup(str, GFP_KERNEL); else if (strcmp(str, "pause") == 0) attrs->pause = true; else if ((strcmp(str, "cont") == 0) || (strcmp(str, "continue") == 0)) attrs->cont = true; else if (strcmp(str, "clear") == 0) attrs->clear = true; else if (strncmp(str, "size=", strlen("size=")) == 0) { int map_bits = parse_map_size(str); if (map_bits < 0) { ret = map_bits; goto free; } attrs->map_bits = map_bits; } else { ret = -EINVAL; goto free; } } if (!attrs->keys_str) { ret = -EINVAL; goto free; } return attrs; free: destroy_hist_trigger_attrs(attrs); return ERR_PTR(ret);} static inline void save_comm(char *comm, struct task_struct *task){ if (!task->pid) { strcpy(comm, "<idle>"); return; } if (WARN_ON_ONCE(task->pid < 0)) { strcpy(comm, "<XXX>"); return; } memcpy(comm, task->comm, TASK_COMM_LEN);} static void hist_trigger_elt_comm_free(struct tracing_map_elt *elt){ kfree((char *)elt->private_data);} static int hist_trigger_elt_comm_alloc(struct tracing_map_elt *elt){ struct hist_trigger_data *hist_data = elt->map->private_data; struct hist_field *key_field; unsigned int i; for_each_hist_key_field(i, hist_data) { key_field = hist_data->fields[i]; if (key_field->flags & HIST_FIELD_FL_EXECNAME) { unsigned int size = TASK_COMM_LEN + 1; elt->private_data = kzalloc(size, GFP_KERNEL); if (!elt->private_data) return -ENOMEM; break; } } return 0;} static void hist_trigger_elt_comm_copy(struct tracing_map_elt *to, struct tracing_map_elt *from){ char *comm_from = from->private_data; char *comm_to = to->private_data; if (comm_from) memcpy(comm_to, comm_from, TASK_COMM_LEN + 1);} static void hist_trigger_elt_comm_init(struct tracing_map_elt *elt){ char *comm = elt->private_data; if (comm) save_comm(comm, current);} static const struct tracing_map_ops hist_trigger_elt_comm_ops = { .elt_alloc = hist_trigger_elt_comm_alloc, .elt_copy = hist_trigger_elt_comm_copy, .elt_free = hist_trigger_elt_comm_free, .elt_init = hist_trigger_elt_comm_init,}; static void destroy_hist_field(struct hist_field *hist_field){ kfree(hist_field);} static struct hist_field *create_hist_field(struct ftrace_event_field *field, unsigned long flags){ struct hist_field *hist_field; if (field && is_function_field(field)) return NULL; hist_field = kzalloc(sizeof(struct hist_field), GFP_KERNEL); if (!hist_field) return NULL; if (flags & HIST_FIELD_FL_HITCOUNT) { hist_field->fn = hist_field_counter; goto out; } if (flags & HIST_FIELD_FL_STACKTRACE) { hist_field->fn = hist_field_none; goto out; } if (flags & HIST_FIELD_FL_LOG2) { hist_field->fn = hist_field_log2; goto out; } if (WARN_ON_ONCE(!field)) goto out; if (is_string_field(field)) { flags |= HIST_FIELD_FL_STRING; if (field->filter_type == FILTER_STATIC_STRING) hist_field->fn = hist_field_string; else if (field->filter_type == FILTER_DYN_STRING) hist_field->fn = hist_field_dynstring; else hist_field->fn = hist_field_pstring; } else { hist_field->fn = select_value_fn(field->size, field->is_signed); if (!hist_field->fn) { destroy_hist_field(hist_field); return NULL; } } out: hist_field->field = field; hist_field->flags = flags; return hist_field;} static void destroy_hist_fields(struct hist_trigger_data *hist_data){ unsigned int i; for (i = 0; i < TRACING_MAP_FIELDS_MAX; i++) { if (hist_data->fields[i]) { destroy_hist_field(hist_data->fields[i]); hist_data->fields[i] = NULL; } }} static int create_hitcount_val(struct hist_trigger_data *hist_data){ hist_data->fields[HITCOUNT_IDX] = create_hist_field(NULL, HIST_FIELD_FL_HITCOUNT); if (!hist_data->fields[HITCOUNT_IDX]) return -ENOMEM; hist_data->n_vals++; if (WARN_ON(hist_data->n_vals > TRACING_MAP_VALS_MAX)) return -EINVAL; return 0;} static int create_val_field(struct hist_trigger_data *hist_data, unsigned int val_idx, struct trace_event_file *file, char *field_str){ struct ftrace_event_field *field = NULL; unsigned long flags = 0; char *field_name; int ret = 0; if (WARN_ON(val_idx >= TRACING_MAP_VALS_MAX)) return -EINVAL; field_name = strsep(&field_str, "."); if (field_str) { if (strcmp(field_str, "hex") == 0) flags |= HIST_FIELD_FL_HEX; else { ret = -EINVAL; goto out; } } field = trace_find_event_field(file->event_call, field_name); if (!field) { ret = -EINVAL; goto out; } hist_data->fields[val_idx] = create_hist_field(field, flags); if (!hist_data->fields[val_idx]) { ret = -ENOMEM; goto out; } ++hist_data->n_vals; if (WARN_ON(hist_data->n_vals > TRACING_MAP_VALS_MAX)) ret = -EINVAL; out: return ret;} static int create_val_fields(struct hist_trigger_data *hist_data, struct trace_event_file *file){ char *fields_str, *field_str; unsigned int i, j; int ret; ret = create_hitcount_val(hist_data); if (ret) goto out; fields_str = hist_data->attrs->vals_str; if (!fields_str) goto out; strsep(&fields_str, "="); if (!fields_str) goto out; for (i = 0, j = 1; i < TRACING_MAP_VALS_MAX && j < TRACING_MAP_VALS_MAX; i++) { field_str = strsep(&fields_str, ","); if (!field_str) break; if (strcmp(field_str, "hitcount") == 0) continue; ret = create_val_field(hist_data, j++, file, field_str); if (ret) goto out;Lines 1–500 of 1756. Load more
/* * trace_events_hist - trace event hist triggers * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * Copyright (C) 2015 Tom Zanussi <tom.zanussi@linux.intel.com> */ #include <linux/module.h> #include <linux/kallsyms.h> #include <linux/mutex.h> #include <linux/slab.h> #include <linux/stacktrace.h> #include <linux/rculist.h> #include "tracing_map.h" #include "trace.h" struct hist_field; typedef u64 (*hist_field_fn_t) (struct hist_field *field, void *event); struct hist_field { struct ftrace_event_field *field; unsigned long flags; hist_field_fn_t fn; unsigned int size; unsigned int offset; }; static u64 hist_field_none(struct hist_field *field, void *event) { return 0; } static u64 hist_field_counter(struct hist_field *field, void *event) { return 1; } static u64 hist_field_string(struct hist_field *hist_field, void *event) { char *addr = (char *)(event + hist_field->field->offset); return (u64)(unsigned long)addr; } static u64 hist_field_dynstring(struct hist_field *hist_field, void *event) { u32 str_item = *(u32 *)(event + hist_field->field->offset); int str_loc = str_item & 0xffff; char *addr = (char *)(event + str_loc); return (u64)(unsigned long)addr; } static u64 hist_field_pstring(struct hist_field *hist_field, void *event) { char **addr = (char **)(event + hist_field->field->offset); return (u64)(unsigned long)*addr; } static u64 hist_field_log2(struct hist_field *hist_field, void *event) { u64 val = *(u64 *)(event + hist_field->field->offset); return (u64) ilog2(roundup_pow_of_two(val)); } #define DEFINE_HIST_FIELD_FN(type) \ static u64 hist_field_##type(struct hist_field *hist_field, void *event)\ { \ type *addr = (type *)(event + hist_field->field->offset); \ \ return (u64)(unsigned long)*addr; \ } DEFINE_HIST_FIELD_FN(s64); DEFINE_HIST_FIELD_FN(u64); DEFINE_HIST_FIELD_FN(s32); DEFINE_HIST_FIELD_FN(u32); DEFINE_HIST_FIELD_FN(s16); DEFINE_HIST_FIELD_FN(u16); DEFINE_HIST_FIELD_FN(s8); DEFINE_HIST_FIELD_FN(u8); #define for_each_hist_field(i, hist_data) \ for ((i) = 0; (i) < (hist_data)->n_fields; (i)++) #define for_each_hist_val_field(i, hist_data) \ for ((i) = 0; (i) < (hist_data)->n_vals; (i)++) #define for_each_hist_key_field(i, hist_data) \ for ((i) = (hist_data)->n_vals; (i) < (hist_data)->n_fields; (i)++) #define HIST_STACKTRACE_DEPTH 16 #define HIST_STACKTRACE_SIZE (HIST_STACKTRACE_DEPTH * sizeof(unsigned long)) #define HIST_STACKTRACE_SKIP 5 #define HITCOUNT_IDX 0 #define HIST_KEY_SIZE_MAX (MAX_FILTER_STR_VAL + HIST_STACKTRACE_SIZE) enum hist_field_flags { HIST_FIELD_FL_HITCOUNT = 1, HIST_FIELD_FL_KEY = 2, HIST_FIELD_FL_STRING = 4, HIST_FIELD_FL_HEX = 8, HIST_FIELD_FL_SYM = 16, HIST_FIELD_FL_SYM_OFFSET = 32, HIST_FIELD_FL_EXECNAME = 64, HIST_FIELD_FL_SYSCALL = 128, HIST_FIELD_FL_STACKTRACE = 256, HIST_FIELD_FL_LOG2 = 512, }; struct hist_trigger_attrs { char *keys_str; char *vals_str; char *sort_key_str; char *name; bool pause; bool cont; bool clear; unsigned int map_bits; }; struct hist_trigger_data { struct hist_field *fields[TRACING_MAP_FIELDS_MAX]; unsigned int n_vals; unsigned int n_keys; unsigned int n_fields; unsigned int key_size; struct tracing_map_sort_key sort_keys[TRACING_MAP_SORT_KEYS_MAX]; unsigned int n_sort_keys; struct trace_event_file *event_file; struct hist_trigger_attrs *attrs; struct tracing_map *map; }; static hist_field_fn_t select_value_fn(int field_size, int field_is_signed) { hist_field_fn_t fn = NULL; switch (field_size) { case 8: if (field_is_signed) fn = hist_field_s64; else fn = hist_field_u64; break; case 4: if (field_is_signed) fn = hist_field_s32; else fn = hist_field_u32; break; case 2: if (field_is_signed) fn = hist_field_s16; else fn = hist_field_u16; break; case 1: if (field_is_signed) fn = hist_field_s8; else fn = hist_field_u8; break; } return fn; } static int parse_map_size(char *str) { unsigned long size, map_bits; int ret; strsep(&str, "="); if (!str) { ret = -EINVAL; goto out; } ret = kstrtoul(str, 0, &size); if (ret) goto out; map_bits = ilog2(roundup_pow_of_two(size)); if (map_bits < TRACING_MAP_BITS_MIN || map_bits > TRACING_MAP_BITS_MAX) ret = -EINVAL; else ret = map_bits; out: return ret; } static void destroy_hist_trigger_attrs(struct hist_trigger_attrs *attrs) { if (!attrs) return; kfree(attrs->name); kfree(attrs->sort_key_str); kfree(attrs->keys_str); kfree(attrs->vals_str); kfree(attrs); } static struct hist_trigger_attrs *parse_hist_trigger_attrs(char *trigger_str) { struct hist_trigger_attrs *attrs; int ret = 0; attrs = kzalloc(sizeof(*attrs), GFP_KERNEL); if (!attrs) return ERR_PTR(-ENOMEM); while (trigger_str) { char *str = strsep(&trigger_str, ":"); if ((strncmp(str, "key=", strlen("key=")) == 0) || (strncmp(str, "keys=", strlen("keys=")) == 0)) attrs->keys_str = kstrdup(str, GFP_KERNEL); else if ((strncmp(str, "val=", strlen("val=")) == 0) || (strncmp(str, "vals=", strlen("vals=")) == 0) || (strncmp(str, "values=", strlen("values=")) == 0)) attrs->vals_str = kstrdup(str, GFP_KERNEL); else if (strncmp(str, "sort=", strlen("sort=")) == 0) attrs->sort_key_str = kstrdup(str, GFP_KERNEL); else if (strncmp(str, "name=", strlen("name=")) == 0) attrs->name = kstrdup(str, GFP_KERNEL); else if (strcmp(str, "pause") == 0) attrs->pause = true; else if ((strcmp(str, "cont") == 0) || (strcmp(str, "continue") == 0)) attrs->cont = true; else if (strcmp(str, "clear") == 0) attrs->clear = true; else if (strncmp(str, "size=", strlen("size=")) == 0) { int map_bits = parse_map_size(str); if (map_bits < 0) { ret = map_bits; goto free; } attrs->map_bits = map_bits; } else { ret = -EINVAL; goto free; } } if (!attrs->keys_str) { ret = -EINVAL; goto free; } return attrs; free: destroy_hist_trigger_attrs(attrs); return ERR_PTR(ret); } static inline void save_comm(char *comm, struct task_struct *task) { if (!task->pid) { strcpy(comm, "<idle>"); return; } if (WARN_ON_ONCE(task->pid < 0)) { strcpy(comm, "<XXX>"); return; } memcpy(comm, task->comm, TASK_COMM_LEN); } static void hist_trigger_elt_comm_free(struct tracing_map_elt *elt) { kfree((char *)elt->private_data); } static int hist_trigger_elt_comm_alloc(struct tracing_map_elt *elt) { struct hist_trigger_data *hist_data = elt->map->private_data; struct hist_field *key_field; unsigned int i; for_each_hist_key_field(i, hist_data) { key_field = hist_data->fields[i]; if (key_field->flags & HIST_FIELD_FL_EXECNAME) { unsigned int size = TASK_COMM_LEN + 1; elt->private_data = kzalloc(size, GFP_KERNEL); if (!elt->private_data) return -ENOMEM; break; } } return 0; } static void hist_trigger_elt_comm_copy(struct tracing_map_elt *to, struct tracing_map_elt *from) { char *comm_from = from->private_data; char *comm_to = to->private_data; if (comm_from) memcpy(comm_to, comm_from, TASK_COMM_LEN + 1); } static void hist_trigger_elt_comm_init(struct tracing_map_elt *elt) { char *comm = elt->private_data; if (comm) save_comm(comm, current); } static const struct tracing_map_ops hist_trigger_elt_comm_ops = { .elt_alloc = hist_trigger_elt_comm_alloc, .elt_copy = hist_trigger_elt_comm_copy, .elt_free = hist_trigger_elt_comm_free, .elt_init = hist_trigger_elt_comm_init, }; static void destroy_hist_field(struct hist_field *hist_field) { kfree(hist_field); } static struct hist_field *create_hist_field(struct ftrace_event_field *field, unsigned long flags) { struct hist_field *hist_field; if (field && is_function_field(field)) return NULL; hist_field = kzalloc(sizeof(struct hist_field), GFP_KERNEL); if (!hist_field) return NULL; if (flags & HIST_FIELD_FL_HITCOUNT) { hist_field->fn = hist_field_counter; goto out; } if (flags & HIST_FIELD_FL_STACKTRACE) { hist_field->fn = hist_field_none; goto out; } if (flags & HIST_FIELD_FL_LOG2) { hist_field->fn = hist_field_log2; goto out; } if (WARN_ON_ONCE(!field)) goto out; if (is_string_field(field)) { flags |= HIST_FIELD_FL_STRING; if (field->filter_type == FILTER_STATIC_STRING) hist_field->fn = hist_field_string; else if (field->filter_type == FILTER_DYN_STRING) hist_field->fn = hist_field_dynstring; else hist_field->fn = hist_field_pstring; } else { hist_field->fn = select_value_fn(field->size, field->is_signed); if (!hist_field->fn) { destroy_hist_field(hist_field); return NULL; } } out: hist_field->field = field; hist_field->flags = flags; return hist_field; } static void destroy_hist_fields(struct hist_trigger_data *hist_data) { unsigned int i; for (i = 0; i < TRACING_MAP_FIELDS_MAX; i++) { if (hist_data->fields[i]) { destroy_hist_field(hist_data->fields[i]); hist_data->fields[i] = NULL; } } } static int create_hitcount_val(struct hist_trigger_data *hist_data) { hist_data->fields[HITCOUNT_IDX] = create_hist_field(NULL, HIST_FIELD_FL_HITCOUNT); if (!hist_data->fields[HITCOUNT_IDX]) return -ENOMEM; hist_data->n_vals++; if (WARN_ON(hist_data->n_vals > TRACING_MAP_VALS_MAX)) return -EINVAL; return 0; } static int create_val_field(struct hist_trigger_data *hist_data, unsigned int val_idx, struct trace_event_file *file, char *field_str) { struct ftrace_event_field *field = NULL; unsigned long flags = 0; char *field_name; int ret = 0; if (WARN_ON(val_idx >= TRACING_MAP_VALS_MAX)) return -EINVAL; field_name = strsep(&field_str, "."); if (field_str) { if (strcmp(field_str, "hex") == 0) flags |= HIST_FIELD_FL_HEX; else { ret = -EINVAL; goto out; } } field = trace_find_event_field(file->event_call, field_name); if (!field) { ret = -EINVAL; goto out; } hist_data->fields[val_idx] = create_hist_field(field, flags); if (!hist_data->fields[val_idx]) { ret = -ENOMEM; goto out; } ++hist_data->n_vals; if (WARN_ON(hist_data->n_vals > TRACING_MAP_VALS_MAX)) ret = -EINVAL; out: return ret; } static int create_val_fields(struct hist_trigger_data *hist_data, struct trace_event_file *file) { char *fields_str, *field_str; unsigned int i, j; int ret; ret = create_hitcount_val(hist_data); if (ret) goto out; fields_str = hist_data->attrs->vals_str; if (!fields_str) goto out; strsep(&fields_str, "="); if (!fields_str) goto out; for (i = 0, j = 1; i < TRACING_MAP_VALS_MAX && j < TRACING_MAP_VALS_MAX; i++) { field_str = strsep(&fields_str, ","); if (!field_str) break; if (strcmp(field_str, "hitcount") == 0) continue; ret = create_val_field(hist_data, j++, file, field_str); if (ret) goto out;