cregit-Linux how code gets into the kernel

Release 4.15 kernel/trace/trace_events_hist.c

Directory: kernel/trace
/*
 * 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);


#define HIST_FIELD_OPERANDS_MAX	2


struct hist_field {
	
struct ftrace_event_field	*field;
	
unsigned long			flags;
	
hist_field_fn_t			fn;
	
unsigned int			size;
	
unsigned int			offset;
	
unsigned int                    is_signed;
	
struct hist_field		*operands[HIST_FIELD_OPERANDS_MAX];
};


static u64 hist_field_none(struct hist_field *field, void *event) { return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Tom Zanussi18100.00%1100.00%
Total18100.00%1100.00%


static u64 hist_field_counter(struct hist_field *field, void *event) { return 1; }

Contributors

PersonTokensPropCommitsCommitProp
Tom Zanussi18100.00%1100.00%
Total18100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Tom Zanussi43100.00%1100.00%
Total43100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Namhyung Kim64100.00%1100.00%
Total64100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Namhyung Kim46100.00%1100.00%
Total46100.00%1100.00%


static u64 hist_field_log2(struct hist_field *hist_field, void *event) { struct hist_field *operand = hist_field->operands[0]; u64 val = operand->fn(operand, event); return (u64) ilog2(roundup_pow_of_two(val)); }

Contributors

PersonTokensPropCommitsCommitProp
Namhyung Kim3262.75%150.00%
Tom Zanussi1937.25%150.00%
Total51100.00%2100.00%

#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 << 0, HIST_FIELD_FL_KEY = 1 << 1, HIST_FIELD_FL_STRING = 1 << 2, HIST_FIELD_FL_HEX = 1 << 3, HIST_FIELD_FL_SYM = 1 << 4, HIST_FIELD_FL_SYM_OFFSET = 1 << 5, HIST_FIELD_FL_EXECNAME = 1 << 6, HIST_FIELD_FL_SYSCALL = 1 << 7, HIST_FIELD_FL_STACKTRACE = 1 << 8, HIST_FIELD_FL_LOG2 = 1 << 9, }; 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 const char *hist_field_name(struct hist_field *field, unsigned int level) { const char *field_name = ""; if (level > 1) return field_name; if (field->field) field_name = field->field->name; else if (field->flags & HIST_FIELD_FL_LOG2) field_name = hist_field_name(field->operands[0], ++level); if (field_name == NULL) field_name = ""; return field_name; }

Contributors

PersonTokensPropCommitsCommitProp
Tom Zanussi84100.00%2100.00%
Total84100.00%2100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Tom Zanussi94100.00%1100.00%
Total94100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Tom Zanussi96100.00%1100.00%
Total96100.00%1100.00%


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); }

Contributors

PersonTokensPropCommitsCommitProp
Tom Zanussi50100.00%4100.00%
Total50100.00%4100.00%


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); }

Contributors

PersonTokensPropCommitsCommitProp
Tom Zanussi387100.00%6100.00%
Total387100.00%6100.00%


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); }

Contributors

PersonTokensPropCommitsCommitProp
Tom Zanussi65100.00%1100.00%
Total65100.00%1100.00%


static void hist_trigger_elt_comm_free(struct tracing_map_elt *elt) { kfree((char *)elt->private_data); }

Contributors

PersonTokensPropCommitsCommitProp
Tom Zanussi22100.00%1100.00%
Total22100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Tom Zanussi92100.00%1100.00%
Total92100.00%1100.00%


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); }

Contributors

PersonTokensPropCommitsCommitProp
Tom Zanussi47100.00%1100.00%
Total47100.00%1100.00%


static void hist_trigger_elt_comm_init(struct tracing_map_elt *elt) { char *comm = elt->private_data; if (comm) save_comm(comm, current); }

Contributors

PersonTokensPropCommitsCommitProp
Tom Zanussi30100.00%1100.00%
Total30100.00%1100.00%

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, unsigned int level) { unsigned int i; if (level > 2) return; if (!hist_field) return; for (i = 0; i < HIST_FIELD_OPERANDS_MAX; i++) destroy_hist_field(hist_field->operands[i], level + 1); kfree(hist_field); }

Contributors

PersonTokensPropCommitsCommitProp
Tom Zanussi64100.00%2100.00%
Total64100.00%2100.00%


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) { unsigned long fl = flags & ~HIST_FIELD_FL_LOG2; hist_field->fn = hist_field_log2; hist_field->operands[0] = create_hist_field(field, fl); hist_field->size = hist_field->operands[0]->size; 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, 0); return NULL; } } out: hist_field->field = field; hist_field->flags = flags; return hist_field; }

Contributors

PersonTokensPropCommitsCommitProp
Tom Zanussi20981.64%466.67%
Namhyung Kim4718.36%233.33%
Total256100.00%6100.00%


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], 0); hist_data->fields[i] = NULL; } } }

Contributors

PersonTokensPropCommitsCommitProp
Tom Zanussi62100.00%2100.00%
Total62100.00%2100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Tom Zanussi62100.00%1100.00%
Total62100.00%1100.00%


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 || !field->size) { 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; }

Contributors

PersonTokensPropCommitsCommitProp
Tom Zanussi192100.00%3100.00%
Total192100.00%3100.00%


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; } if (fields_str && (strcmp(fields_str, "hitcount") != 0)) ret = -EINVAL; out: return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Tom Zanussi175100.00%2100.00%
Total175100.00%2100.00%


static int create_key_field(struct hist_trigger_data *hist_data, unsigned int key_idx, unsigned int key_offset, struct trace_event_file *file, char *field_str) { struct ftrace_event_field *field = NULL; unsigned long flags = 0; unsigned int key_size; int ret = 0; if (WARN_ON(key_idx >= TRACING_MAP_FIELDS_MAX)) return -EINVAL; flags |= HIST_FIELD_FL_KEY; if (strcmp(field_str, "stacktrace") == 0) { flags |= HIST_FIELD_FL_STACKTRACE; key_size = sizeof(unsigned long) * HIST_STACKTRACE_DEPTH; } else { char *field_name = strsep(&field_str, "."); if (field_str) { if (strcmp(field_str, "hex") == 0) flags |= HIST_FIELD_FL_HEX; else if (strcmp(field_str, "sym") == 0) flags |= HIST_FIELD_FL_SYM; else if (strcmp(field_str, "sym-offset") == 0) flags |= HIST_FIELD_FL_SYM_OFFSET; else if ((strcmp(field_str, "execname") == 0) && (strcmp(field_name, "common_pid") == 0)) flags |= HIST_FIELD_FL_EXECNAME; else if (strcmp(field_str, "syscall") == 0) flags |= HIST_FIELD_FL_SYSCALL; else if (strcmp(field_str, "log2") == 0) flags |= HIST_FIELD_FL_LOG2; else { ret = -EINVAL; goto out; } } field = trace_find_event_field(file->event_call, field_name); if (!field || !field->size) { ret = -EINVAL; goto out; } if (is_string_field(field)) key_size = MAX_FILTER_STR_VAL; else key_size = field->size; } hist_data->fields[key_idx] = create_hist_field(field, flags); if (!hist_data->fields[key_idx]) { ret = -ENOMEM; goto out; } key_size = ALIGN(key_size, sizeof(u64)); hist_data->fields[key_idx]->size = key_size; hist_data->fields[key_idx]->offset = key_offset; hist_data->key_size += key_size; if (hist_data->key_size > HIST_KEY_SIZE_MAX) { ret = -EINVAL; goto out; } hist_data->n_keys++; if (WARN_ON(hist_data->n_keys > TRACING_MAP_KEYS_MAX)) return -EINVAL; ret = key_size; out: return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Tom Zanussi37793.32%981.82%
Namhyung Kim276.68%218.18%
Total404100.00%11100.00%


static int create_key_fields(struct hist_trigger_data *hist_data, struct trace_event_file *file) {