5910 lines · 135.7 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* * trace_events_hist - trace event hist triggers * * 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 <linux/tracefs.h> #include "tracing_map.h"#include "trace.h"#include "trace_dynevent.h" #define SYNTH_SYSTEM		"synthetic"#define SYNTH_FIELDS_MAX	16 #define STR_VAR_LEN_MAX		32 /* must be multiple of sizeof(u64) */ struct hist_field; typedef u64 (*hist_field_fn_t) (struct hist_field *field,				struct tracing_map_elt *elt,				struct ring_buffer_event *rbe,				void *event); #define HIST_FIELD_OPERANDS_MAX	2#define HIST_FIELDS_MAX		(TRACING_MAP_FIELDS_MAX + TRACING_MAP_VARS_MAX)#define HIST_ACTIONS_MAX	8 enum field_op_id {	FIELD_OP_NONE,	FIELD_OP_PLUS,	FIELD_OP_MINUS,	FIELD_OP_UNARY_MINUS,}; /* * A hist_var (histogram variable) contains variable information for * hist_fields having the HIST_FIELD_FL_VAR or HIST_FIELD_FL_VAR_REF * flag set.  A hist_var has a variable name e.g. ts0, and is * associated with a given histogram trigger, as specified by * hist_data.  The hist_var idx is the unique index assigned to the * variable by the hist trigger's tracing_map.  The idx is what is * used to set a variable's value and, by a variable reference, to * retrieve it. */struct hist_var {	char				*name;	struct hist_trigger_data	*hist_data;	unsigned int			idx;}; 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;	const char			*type;	struct hist_field		*operands[HIST_FIELD_OPERANDS_MAX];	struct hist_trigger_data	*hist_data; 	/*	 * Variable fields contain variable-specific info in var.	 */	struct hist_var			var;	enum field_op_id		operator;	char				*system;	char				*event_name; 	/*	 * The name field is used for EXPR and VAR_REF fields.  VAR	 * fields contain the variable name in var.name.	 */	char				*name; 	/*	 * When a histogram trigger is hit, if it has any references	 * to variables, the values of those variables are collected	 * into a var_ref_vals array by resolve_var_refs().  The	 * current value of each variable is read from the tracing_map	 * using the hist field's hist_var.idx and entered into the	 * var_ref_idx entry i.e. var_ref_vals[var_ref_idx].	 */	unsigned int			var_ref_idx;	bool                            read_once;}; static u64 hist_field_none(struct hist_field *field,			   struct tracing_map_elt *elt,			   struct ring_buffer_event *rbe,			   void *event){	return 0;} static u64 hist_field_counter(struct hist_field *field,			      struct tracing_map_elt *elt,			      struct ring_buffer_event *rbe,			      void *event){	return 1;} static u64 hist_field_string(struct hist_field *hist_field,			     struct tracing_map_elt *elt,			     struct ring_buffer_event *rbe,			     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,				struct tracing_map_elt *elt,				struct ring_buffer_event *rbe,				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,			      struct tracing_map_elt *elt,			      struct ring_buffer_event *rbe,			      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,			   struct tracing_map_elt *elt,			   struct ring_buffer_event *rbe,			   void *event){	struct hist_field *operand = hist_field->operands[0]; 	u64 val = operand->fn(operand, elt, rbe, event); 	return (u64) ilog2(roundup_pow_of_two(val));} static u64 hist_field_plus(struct hist_field *hist_field,			   struct tracing_map_elt *elt,			   struct ring_buffer_event *rbe,			   void *event){	struct hist_field *operand1 = hist_field->operands[0];	struct hist_field *operand2 = hist_field->operands[1]; 	u64 val1 = operand1->fn(operand1, elt, rbe, event);	u64 val2 = operand2->fn(operand2, elt, rbe, event); 	return val1 + val2;} static u64 hist_field_minus(struct hist_field *hist_field,			    struct tracing_map_elt *elt,			    struct ring_buffer_event *rbe,			    void *event){	struct hist_field *operand1 = hist_field->operands[0];	struct hist_field *operand2 = hist_field->operands[1]; 	u64 val1 = operand1->fn(operand1, elt, rbe, event);	u64 val2 = operand2->fn(operand2, elt, rbe, event); 	return val1 - val2;} static u64 hist_field_unary_minus(struct hist_field *hist_field,				  struct tracing_map_elt *elt,				  struct ring_buffer_event *rbe,				  void *event){	struct hist_field *operand = hist_field->operands[0]; 	s64 sval = (s64)operand->fn(operand, elt, rbe, event);	u64 val = (u64)-sval; 	return val;} #define DEFINE_HIST_FIELD_FN(type)					\	static u64 hist_field_##type(struct hist_field *hist_field,	\				     struct tracing_map_elt *elt,	\				     struct ring_buffer_event *rbe,	\				     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,	HIST_FIELD_FL_TIMESTAMP		= 1 << 10,	HIST_FIELD_FL_TIMESTAMP_USECS	= 1 << 11,	HIST_FIELD_FL_VAR		= 1 << 12,	HIST_FIELD_FL_EXPR		= 1 << 13,	HIST_FIELD_FL_VAR_REF		= 1 << 14,	HIST_FIELD_FL_CPU		= 1 << 15,	HIST_FIELD_FL_ALIAS		= 1 << 16,}; struct var_defs {	unsigned int	n_vars;	char		*name[TRACING_MAP_VARS_MAX];	char		*expr[TRACING_MAP_VARS_MAX];}; struct hist_trigger_attrs {	char		*keys_str;	char		*vals_str;	char		*sort_key_str;	char		*name;	char		*clock;	bool		pause;	bool		cont;	bool		clear;	bool		ts_in_usecs;	unsigned int	map_bits; 	char		*assignment_str[TRACING_MAP_VARS_MAX];	unsigned int	n_assignments; 	char		*action_str[HIST_ACTIONS_MAX];	unsigned int	n_actions; 	struct var_defs	var_defs;}; struct field_var {	struct hist_field	*var;	struct hist_field	*val;}; struct field_var_hist {	struct hist_trigger_data	*hist_data;	char				*cmd;}; struct hist_trigger_data {	struct hist_field               *fields[HIST_FIELDS_MAX];	unsigned int			n_vals;	unsigned int			n_keys;	unsigned int			n_fields;	unsigned int			n_vars;	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;	bool				enable_timestamps;	bool				remove;	struct hist_field               *var_refs[TRACING_MAP_VARS_MAX];	unsigned int			n_var_refs; 	struct action_data		*actions[HIST_ACTIONS_MAX];	unsigned int			n_actions; 	struct field_var		*field_vars[SYNTH_FIELDS_MAX];	unsigned int			n_field_vars;	unsigned int			n_field_var_str;	struct field_var_hist		*field_var_hists[SYNTH_FIELDS_MAX];	unsigned int			n_field_var_hists; 	struct field_var		*max_vars[SYNTH_FIELDS_MAX];	unsigned int			n_max_vars;	unsigned int			n_max_var_str;}; static int synth_event_create(int argc, const char **argv);static int synth_event_show(struct seq_file *m, struct dyn_event *ev);static int synth_event_release(struct dyn_event *ev);static bool synth_event_is_busy(struct dyn_event *ev);static bool synth_event_match(const char *system, const char *event,			      struct dyn_event *ev); static struct dyn_event_operations synth_event_ops = {	.create = synth_event_create,	.show = synth_event_show,	.is_busy = synth_event_is_busy,	.free = synth_event_release,	.match = synth_event_match,}; struct synth_field {	char *type;	char *name;	size_t size;	bool is_signed;	bool is_string;}; struct synth_event {	struct dyn_event			devent;	int					ref;	char					*name;	struct synth_field			**fields;	unsigned int				n_fields;	unsigned int				n_u64;	struct trace_event_class		class;	struct trace_event_call			call;	struct tracepoint			*tp;}; static bool is_synth_event(struct dyn_event *ev){	return ev->ops == &synth_event_ops;} static struct synth_event *to_synth_event(struct dyn_event *ev){	return container_of(ev, struct synth_event, devent);} static bool synth_event_is_busy(struct dyn_event *ev){	struct synth_event *event = to_synth_event(ev); 	return event->ref != 0;} static bool synth_event_match(const char *system, const char *event,			      struct dyn_event *ev){	struct synth_event *sev = to_synth_event(ev); 	return strcmp(sev->name, event) == 0 &&		(!system || strcmp(system, SYNTH_SYSTEM) == 0);} struct action_data; typedef void (*action_fn_t) (struct hist_trigger_data *hist_data,			     struct tracing_map_elt *elt, void *rec,			     struct ring_buffer_event *rbe,			     struct action_data *data, u64 *var_ref_vals); struct action_data {	action_fn_t		fn;	unsigned int		n_params;	char			*params[SYNTH_FIELDS_MAX]; 	union {		struct {			/*			 * When a histogram trigger is hit, the values of any			 * references to variables, including variables being passed			 * as parameters to synthetic events, are collected into a			 * var_ref_vals array.  This var_ref_idx is the index of the			 * first param in the array to be passed to the synthetic			 * event invocation.			 */			unsigned int		var_ref_idx;			char			*match_event;			char			*match_event_system;			char			*synth_event_name;			struct synth_event	*synth_event;		} onmatch; 		struct {			char			*var_str;			char			*fn_name;			unsigned int		max_var_ref_idx;			struct hist_field	*max_var;			struct hist_field	*var;		} onmax;	};};  static char last_hist_cmd[MAX_FILTER_STR_VAL];static char hist_err_str[MAX_FILTER_STR_VAL]; static void last_cmd_set(char *str){	if (!str)		return; 	strncpy(last_hist_cmd, str, MAX_FILTER_STR_VAL - 1);} static void hist_err(char *str, char *var){	int maxlen = MAX_FILTER_STR_VAL - 1; 	if (!str)		return; 	if (strlen(hist_err_str))		return; 	if (!var)		var = ""; 	if (strlen(hist_err_str) + strlen(str) + strlen(var) > maxlen)		return; 	strcat(hist_err_str, str);	strcat(hist_err_str, var);} static void hist_err_event(char *str, char *system, char *event, char *var){	char err[MAX_FILTER_STR_VAL]; 	if (system && var)		snprintf(err, MAX_FILTER_STR_VAL, "%s.%s.%s", system, event, var);	else if (system)		snprintf(err, MAX_FILTER_STR_VAL, "%s.%s", system, event);	else		strscpy(err, var, MAX_FILTER_STR_VAL); 	hist_err(str, err);} static void hist_err_clear(void){	hist_err_str[0] = '\0';} static bool have_hist_err(void){	if (strlen(hist_err_str))		return true; 	return false;} struct synth_trace_event {	struct trace_entry	ent;	u64			fields[];}; static int synth_event_define_fields(struct trace_event_call *call){	struct synth_trace_event trace;	int offset = offsetof(typeof(trace), fields);	struct synth_event *event = call->data;	unsigned int i, size, n_u64;	char *name, *type;	bool is_signed;	int ret = 0; 	for (i = 0, n_u64 = 0; i < event->n_fields; i++) {		size = event->fields[i]->size;		is_signed = event->fields[i]->is_signed;		type = event->fields[i]->type;		name = event->fields[i]->name;		ret = trace_define_field(call, type, name, offset, size,

Lines 1–500 of 5910.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t