mirror of
				https://github.com/torvalds/linux.git
				synced 2025-10-31 16:48:26 +02:00 
			
		
		
		
	tracing: Allow triggers to filter for CPU ids and process names
By extending the filter rules by more generic fields we can write triggers filters like echo 'stacktrace if cpu == 1' > \ /sys/kernel/debug/tracing/events/raw_syscalls/sys_enter/trigger or echo 'stacktrace if comm == sshd' > \ /sys/kernel/debug/tracing/events/raw_syscalls/sys_enter/trigger CPU and COMM are not part of struct trace_entry. We could add the two new fields to ftrace_common_field list and fix up all depending sides. But that looks pretty ugly. Another thing I would like to avoid that the 'format' file contents changes. All this can be avoided by introducing another list which contains non field members of struct trace_entry. Link: http://lkml.kernel.org/r/1439210146-24707-1-git-send-email-daniel.wagner@bmw-carit.de Signed-off-by: Daniel Wagner <daniel.wagner@bmw-carit.de> Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
This commit is contained in:
		
							parent
							
								
									c93bf928fe
								
							
						
					
					
						commit
						9f61668073
					
				
					 2 changed files with 77 additions and 2 deletions
				
			
		|  | @ -30,6 +30,7 @@ | ||||||
| DEFINE_MUTEX(event_mutex); | DEFINE_MUTEX(event_mutex); | ||||||
| 
 | 
 | ||||||
| LIST_HEAD(ftrace_events); | LIST_HEAD(ftrace_events); | ||||||
|  | static LIST_HEAD(ftrace_generic_fields); | ||||||
| static LIST_HEAD(ftrace_common_fields); | static LIST_HEAD(ftrace_common_fields); | ||||||
| 
 | 
 | ||||||
| #define GFP_TRACE (GFP_KERNEL | __GFP_ZERO) | #define GFP_TRACE (GFP_KERNEL | __GFP_ZERO) | ||||||
|  | @ -94,6 +95,10 @@ trace_find_event_field(struct trace_event_call *call, char *name) | ||||||
| 	struct ftrace_event_field *field; | 	struct ftrace_event_field *field; | ||||||
| 	struct list_head *head; | 	struct list_head *head; | ||||||
| 
 | 
 | ||||||
|  | 	field = __find_event_field(&ftrace_generic_fields, name); | ||||||
|  | 	if (field) | ||||||
|  | 		return field; | ||||||
|  | 
 | ||||||
| 	field = __find_event_field(&ftrace_common_fields, name); | 	field = __find_event_field(&ftrace_common_fields, name); | ||||||
| 	if (field) | 	if (field) | ||||||
| 		return field; | 		return field; | ||||||
|  | @ -144,6 +149,13 @@ int trace_define_field(struct trace_event_call *call, const char *type, | ||||||
| } | } | ||||||
| EXPORT_SYMBOL_GPL(trace_define_field); | EXPORT_SYMBOL_GPL(trace_define_field); | ||||||
| 
 | 
 | ||||||
|  | #define __generic_field(type, item, filter_type)			\ | ||||||
|  | 	ret = __trace_define_field(&ftrace_generic_fields, #type,	\ | ||||||
|  | 				   #item, 0, 0, is_signed_type(type),	\ | ||||||
|  | 				   filter_type);			\ | ||||||
|  | 	if (ret)							\ | ||||||
|  | 		return ret; | ||||||
|  | 
 | ||||||
| #define __common_field(type, item)					\ | #define __common_field(type, item)					\ | ||||||
| 	ret = __trace_define_field(&ftrace_common_fields, #type,	\ | 	ret = __trace_define_field(&ftrace_common_fields, #type,	\ | ||||||
| 				   "common_" #item,			\ | 				   "common_" #item,			\ | ||||||
|  | @ -153,6 +165,16 @@ EXPORT_SYMBOL_GPL(trace_define_field); | ||||||
| 	if (ret)							\ | 	if (ret)							\ | ||||||
| 		return ret; | 		return ret; | ||||||
| 
 | 
 | ||||||
|  | static int trace_define_generic_fields(void) | ||||||
|  | { | ||||||
|  | 	int ret; | ||||||
|  | 
 | ||||||
|  | 	__generic_field(int, cpu, FILTER_OTHER); | ||||||
|  | 	__generic_field(char *, comm, FILTER_PTR_STRING); | ||||||
|  | 
 | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static int trace_define_common_fields(void) | static int trace_define_common_fields(void) | ||||||
| { | { | ||||||
| 	int ret; | 	int ret; | ||||||
|  | @ -2671,6 +2693,9 @@ static __init int event_trace_init(void) | ||||||
| 	if (!entry) | 	if (!entry) | ||||||
| 		pr_warn("Could not create tracefs 'available_events' entry\n"); | 		pr_warn("Could not create tracefs 'available_events' entry\n"); | ||||||
| 
 | 
 | ||||||
|  | 	if (trace_define_generic_fields()) | ||||||
|  | 		pr_warn("tracing: Failed to allocated generic fields"); | ||||||
|  | 
 | ||||||
| 	if (trace_define_common_fields()) | 	if (trace_define_common_fields()) | ||||||
| 		pr_warn("tracing: Failed to allocate common fields"); | 		pr_warn("tracing: Failed to allocate common fields"); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -252,6 +252,50 @@ static int filter_pred_strloc(struct filter_pred *pred, void *event) | ||||||
| 	return match; | 	return match; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /* Filter predicate for CPUs. */ | ||||||
|  | static int filter_pred_cpu(struct filter_pred *pred, void *event) | ||||||
|  | { | ||||||
|  | 	int cpu, cmp; | ||||||
|  | 	int match = 0; | ||||||
|  | 
 | ||||||
|  | 	cpu = raw_smp_processor_id(); | ||||||
|  | 	cmp = pred->val; | ||||||
|  | 
 | ||||||
|  | 	switch (pred->op) { | ||||||
|  | 	case OP_EQ: | ||||||
|  | 		match = cpu == cmp; | ||||||
|  | 		break; | ||||||
|  | 	case OP_LT: | ||||||
|  | 		match = cpu < cmp; | ||||||
|  | 		break; | ||||||
|  | 	case OP_LE: | ||||||
|  | 		match = cpu <= cmp; | ||||||
|  | 		break; | ||||||
|  | 	case OP_GT: | ||||||
|  | 		match = cpu > cmp; | ||||||
|  | 		break; | ||||||
|  | 	case OP_GE: | ||||||
|  | 		match = cpu >= cmp; | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 		break; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return !!match == !pred->not; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Filter predicate for COMM. */ | ||||||
|  | static int filter_pred_comm(struct filter_pred *pred, void *event) | ||||||
|  | { | ||||||
|  | 	int cmp, match; | ||||||
|  | 
 | ||||||
|  | 	cmp = pred->regex.match(current->comm, &pred->regex, | ||||||
|  | 				pred->regex.field_len); | ||||||
|  | 	match = cmp ^ pred->not; | ||||||
|  | 
 | ||||||
|  | 	return match; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static int filter_pred_none(struct filter_pred *pred, void *event) | static int filter_pred_none(struct filter_pred *pred, void *event) | ||||||
| { | { | ||||||
| 	return 0; | 	return 0; | ||||||
|  | @ -1002,7 +1046,10 @@ static int init_pred(struct filter_parse_state *ps, | ||||||
| 	if (is_string_field(field)) { | 	if (is_string_field(field)) { | ||||||
| 		filter_build_regex(pred); | 		filter_build_regex(pred); | ||||||
| 
 | 
 | ||||||
| 		if (field->filter_type == FILTER_STATIC_STRING) { | 		if (!strcmp(field->name, "comm")) { | ||||||
|  | 			fn = filter_pred_comm; | ||||||
|  | 			pred->regex.field_len = TASK_COMM_LEN; | ||||||
|  | 		} else if (field->filter_type == FILTER_STATIC_STRING) { | ||||||
| 			fn = filter_pred_string; | 			fn = filter_pred_string; | ||||||
| 			pred->regex.field_len = field->size; | 			pred->regex.field_len = field->size; | ||||||
| 		} else if (field->filter_type == FILTER_DYN_STRING) | 		} else if (field->filter_type == FILTER_DYN_STRING) | ||||||
|  | @ -1025,7 +1072,10 @@ static int init_pred(struct filter_parse_state *ps, | ||||||
| 		} | 		} | ||||||
| 		pred->val = val; | 		pred->val = val; | ||||||
| 
 | 
 | ||||||
| 		fn = select_comparison_fn(pred->op, field->size, | 		if (!strcmp(field->name, "cpu")) | ||||||
|  | 			fn = filter_pred_cpu; | ||||||
|  | 		else | ||||||
|  | 			fn = select_comparison_fn(pred->op, field->size, | ||||||
| 					  field->is_signed); | 					  field->is_signed); | ||||||
| 		if (!fn) { | 		if (!fn) { | ||||||
| 			parse_error(ps, FILT_ERR_INVALID_OP, 0); | 			parse_error(ps, FILT_ERR_INVALID_OP, 0); | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Daniel Wagner
						Daniel Wagner