1789 lines · 43.3 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* * trace_events_synth - synthetic trace events * * Copyright (C) 2015, 2020 Tom Zanussi <tom.zanussi@linux.intel.com> */ #include <linux/module.h>#include <linux/kallsyms.h>#include <linux/security.h>#include <linux/mutex.h>#include <linux/slab.h>#include <linux/stacktrace.h>#include <linux/rculist.h>#include <linux/tracefs.h> /* for gfp flag names */#include <linux/trace_events.h>#include <trace/events/mmflags.h> #include "trace_synth.h" static int create_synth_event(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,			int argc, const char **argv, struct dyn_event *ev); static struct dyn_event_operations synth_event_ops = {	.create = create_synth_event,	.show = synth_event_show,	.is_busy = synth_event_is_busy,	.free = synth_event_release,	.match = synth_event_match,}; 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,			int argc, const char **argv, 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 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,					 is_signed, FILTER_OTHER);		if (ret)			break; 		event->fields[i]->offset = n_u64; 		if (event->fields[i]->is_string) {			offset += STR_VAR_LEN_MAX;			n_u64 += STR_VAR_LEN_MAX / sizeof(u64);		} else {			offset += sizeof(u64);			n_u64++;		}	} 	event->n_u64 = n_u64; 	return ret;} static bool synth_field_signed(char *type){	if (str_has_prefix(type, "u"))		return false;	if (strcmp(type, "gfp_t") == 0)		return false; 	return true;} static int synth_field_is_string(char *type){	if (strstr(type, "char[") != NULL)		return true; 	return false;} static int synth_field_string_size(char *type){	char buf[4], *end, *start;	unsigned int len;	int size, err; 	start = strstr(type, "char[");	if (start == NULL)		return -EINVAL;	start += sizeof("char[") - 1; 	end = strchr(type, ']');	if (!end || end < start)		return -EINVAL; 	len = end - start;	if (len > 3)		return -EINVAL; 	strncpy(buf, start, len);	buf[len] = '\0'; 	err = kstrtouint(buf, 0, &size);	if (err)		return err; 	if (size > STR_VAR_LEN_MAX)		return -EINVAL; 	return size;} static int synth_field_size(char *type){	int size = 0; 	if (strcmp(type, "s64") == 0)		size = sizeof(s64);	else if (strcmp(type, "u64") == 0)		size = sizeof(u64);	else if (strcmp(type, "s32") == 0)		size = sizeof(s32);	else if (strcmp(type, "u32") == 0)		size = sizeof(u32);	else if (strcmp(type, "s16") == 0)		size = sizeof(s16);	else if (strcmp(type, "u16") == 0)		size = sizeof(u16);	else if (strcmp(type, "s8") == 0)		size = sizeof(s8);	else if (strcmp(type, "u8") == 0)		size = sizeof(u8);	else if (strcmp(type, "char") == 0)		size = sizeof(char);	else if (strcmp(type, "unsigned char") == 0)		size = sizeof(unsigned char);	else if (strcmp(type, "int") == 0)		size = sizeof(int);	else if (strcmp(type, "unsigned int") == 0)		size = sizeof(unsigned int);	else if (strcmp(type, "long") == 0)		size = sizeof(long);	else if (strcmp(type, "unsigned long") == 0)		size = sizeof(unsigned long);	else if (strcmp(type, "pid_t") == 0)		size = sizeof(pid_t);	else if (strcmp(type, "gfp_t") == 0)		size = sizeof(gfp_t);	else if (synth_field_is_string(type))		size = synth_field_string_size(type); 	return size;} static const char *synth_field_fmt(char *type){	const char *fmt = "%llu"; 	if (strcmp(type, "s64") == 0)		fmt = "%lld";	else if (strcmp(type, "u64") == 0)		fmt = "%llu";	else if (strcmp(type, "s32") == 0)		fmt = "%d";	else if (strcmp(type, "u32") == 0)		fmt = "%u";	else if (strcmp(type, "s16") == 0)		fmt = "%d";	else if (strcmp(type, "u16") == 0)		fmt = "%u";	else if (strcmp(type, "s8") == 0)		fmt = "%d";	else if (strcmp(type, "u8") == 0)		fmt = "%u";	else if (strcmp(type, "char") == 0)		fmt = "%d";	else if (strcmp(type, "unsigned char") == 0)		fmt = "%u";	else if (strcmp(type, "int") == 0)		fmt = "%d";	else if (strcmp(type, "unsigned int") == 0)		fmt = "%u";	else if (strcmp(type, "long") == 0)		fmt = "%ld";	else if (strcmp(type, "unsigned long") == 0)		fmt = "%lu";	else if (strcmp(type, "pid_t") == 0)		fmt = "%d";	else if (strcmp(type, "gfp_t") == 0)		fmt = "%x";	else if (synth_field_is_string(type))		fmt = "%s"; 	return fmt;} static void print_synth_event_num_val(struct trace_seq *s,				      char *print_fmt, char *name,				      int size, u64 val, char *space){	switch (size) {	case 1:		trace_seq_printf(s, print_fmt, name, (u8)val, space);		break; 	case 2:		trace_seq_printf(s, print_fmt, name, (u16)val, space);		break; 	case 4:		trace_seq_printf(s, print_fmt, name, (u32)val, space);		break; 	default:		trace_seq_printf(s, print_fmt, name, val, space);		break;	}} static enum print_line_t print_synth_event(struct trace_iterator *iter,					   int flags,					   struct trace_event *event){	struct trace_array *tr = iter->tr;	struct trace_seq *s = &iter->seq;	struct synth_trace_event *entry;	struct synth_event *se;	unsigned int i, n_u64;	char print_fmt[32];	const char *fmt; 	entry = (struct synth_trace_event *)iter->ent;	se = container_of(event, struct synth_event, call.event); 	trace_seq_printf(s, "%s: ", se->name); 	for (i = 0, n_u64 = 0; i < se->n_fields; i++) {		if (trace_seq_has_overflowed(s))			goto end; 		fmt = synth_field_fmt(se->fields[i]->type); 		/* parameter types */		if (tr && tr->trace_flags & TRACE_ITER_VERBOSE)			trace_seq_printf(s, "%s ", fmt); 		snprintf(print_fmt, sizeof(print_fmt), "%%s=%s%%s", fmt); 		/* parameter values */		if (se->fields[i]->is_string) {			trace_seq_printf(s, print_fmt, se->fields[i]->name,					 (char *)&entry->fields[n_u64],					 i == se->n_fields - 1 ? "" : " ");			n_u64 += STR_VAR_LEN_MAX / sizeof(u64);		} else {			struct trace_print_flags __flags[] = {			    __def_gfpflag_names, {-1, NULL} };			char *space = (i == se->n_fields - 1 ? "" : " "); 			print_synth_event_num_val(s, print_fmt,						  se->fields[i]->name,						  se->fields[i]->size,						  entry->fields[n_u64],						  space); 			if (strcmp(se->fields[i]->type, "gfp_t") == 0) {				trace_seq_puts(s, " (");				trace_print_flags_seq(s, "|",						      entry->fields[n_u64],						      __flags);				trace_seq_putc(s, ')');			}			n_u64++;		}	}end:	trace_seq_putc(s, '\n'); 	return trace_handle_return(s);} static struct trace_event_functions synth_event_funcs = {	.trace		= print_synth_event}; static notrace void trace_event_raw_event_synth(void *__data,						u64 *var_ref_vals,						unsigned int *var_ref_idx){	struct trace_event_file *trace_file = __data;	struct synth_trace_event *entry;	struct trace_event_buffer fbuffer;	struct trace_buffer *buffer;	struct synth_event *event;	unsigned int i, n_u64, val_idx;	int fields_size = 0; 	event = trace_file->event_call->data; 	if (trace_trigger_soft_disabled(trace_file))		return; 	fields_size = event->n_u64 * sizeof(u64); 	/*	 * Avoid ring buffer recursion detection, as this event	 * is being performed within another event.	 */	buffer = trace_file->tr->array_buffer.buffer;	ring_buffer_nest_start(buffer); 	entry = trace_event_buffer_reserve(&fbuffer, trace_file,					   sizeof(*entry) + fields_size);	if (!entry)		goto out; 	for (i = 0, n_u64 = 0; i < event->n_fields; i++) {		val_idx = var_ref_idx[i];		if (event->fields[i]->is_string) {			char *str_val = (char *)(long)var_ref_vals[val_idx];			char *str_field = (char *)&entry->fields[n_u64]; 			strscpy(str_field, str_val, STR_VAR_LEN_MAX);			n_u64 += STR_VAR_LEN_MAX / sizeof(u64);		} else {			struct synth_field *field = event->fields[i];			u64 val = var_ref_vals[val_idx]; 			switch (field->size) {			case 1:				*(u8 *)&entry->fields[n_u64] = (u8)val;				break; 			case 2:				*(u16 *)&entry->fields[n_u64] = (u16)val;				break; 			case 4:				*(u32 *)&entry->fields[n_u64] = (u32)val;				break; 			default:				entry->fields[n_u64] = val;				break;			}			n_u64++;		}	} 	trace_event_buffer_commit(&fbuffer);out:	ring_buffer_nest_end(buffer);} static void free_synth_event_print_fmt(struct trace_event_call *call){	if (call) {		kfree(call->print_fmt);		call->print_fmt = NULL;	}} static int __set_synth_event_print_fmt(struct synth_event *event,				       char *buf, int len){	const char *fmt;	int pos = 0;	int i; 	/* When len=0, we just calculate the needed length */#define LEN_OR_ZERO (len ? len - pos : 0) 	pos += snprintf(buf + pos, LEN_OR_ZERO, "\"");	for (i = 0; i < event->n_fields; i++) {		fmt = synth_field_fmt(event->fields[i]->type);		pos += snprintf(buf + pos, LEN_OR_ZERO, "%s=%s%s",				event->fields[i]->name, fmt,				i == event->n_fields - 1 ? "" : ", ");	}	pos += snprintf(buf + pos, LEN_OR_ZERO, "\""); 	for (i = 0; i < event->n_fields; i++) {		pos += snprintf(buf + pos, LEN_OR_ZERO,				", REC->%s", event->fields[i]->name);	} #undef LEN_OR_ZERO 	/* return the length of print_fmt */	return pos;} static int set_synth_event_print_fmt(struct trace_event_call *call){	struct synth_event *event = call->data;	char *print_fmt;	int len; 	/* First: called with 0 length to calculate the needed length */	len = __set_synth_event_print_fmt(event, NULL, 0); 	print_fmt = kmalloc(len + 1, GFP_KERNEL);	if (!print_fmt)		return -ENOMEM; 	/* Second: actually write the @print_fmt */	__set_synth_event_print_fmt(event, print_fmt, len + 1);	call->print_fmt = print_fmt; 	return 0;} static void free_synth_field(struct synth_field *field){	kfree(field->type);	kfree(field->name);	kfree(field);} static struct synth_field *parse_synth_field(int argc, const char **argv,					     int *consumed){	struct synth_field *field;	const char *prefix = NULL, *field_type = argv[0], *field_name, *array;	int len, ret = 0; 	if (field_type[0] == ';')		field_type++; 	if (!strcmp(field_type, "unsigned")) {		if (argc < 3)			return ERR_PTR(-EINVAL);		prefix = "unsigned ";		field_type = argv[1];		field_name = argv[2];		*consumed = 3;	} else {		field_name = argv[1];		*consumed = 2;	} 	field = kzalloc(sizeof(*field), GFP_KERNEL);	if (!field)		return ERR_PTR(-ENOMEM); 	len = strlen(field_name);	array = strchr(field_name, '[');	if (array)		len -= strlen(array);	else if (field_name[len - 1] == ';')		len--; 	field->name = kmemdup_nul(field_name, len, GFP_KERNEL);	if (!field->name) {		ret = -ENOMEM;		goto free;	} 

Lines 1–500 of 1789.