forked from mirrors/linux
		
	ftrace: Allow enabling of filters via index of available_filter_functions
Enabling of large number of functions by echoing in a large subset of the functions in available_filter_functions can take a very long time. The process requires testing all functions registered by the function tracer (which is in the 10s of thousands), and doing a kallsyms lookup to convert the ip address into a name, then comparing that name with the string passed in. When a function causes the function tracer to crash the system, a binary bisect of the available_filter_functions can be done to find the culprit. But this requires passing in half of the functions in available_filter_functions over and over again, which makes it basically a O(n^2) operation. With 40,000 functions, that ends up bing 1,600,000,000 opertions! And enabling this can take over 20 minutes. As a quick speed up, if a number is passed into one of the filter files, instead of doing a search, it just enables the function at the corresponding line of the available_filter_functions file. That is: # echo 50 > set_ftrace_filter # cat set_ftrace_filter x86_pmu_commit_txn # head -50 available_filter_functions | tail -1 x86_pmu_commit_txn This allows setting of half the available_filter_functions to take place in less than a second! # time seq 20000 > set_ftrace_filter real 0m0.042s user 0m0.005s sys 0m0.015s # wc -l set_ftrace_filter 20000 set_ftrace_filter Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
This commit is contained in:
		
							parent
							
								
									85acbb21b9
								
							
						
					
					
						commit
						f79b3f3385
					
				
					 4 changed files with 74 additions and 0 deletions
				
			
		|  | @ -233,6 +233,12 @@ of ftrace. Here is a list of some of the key files: | ||||||
| 	This interface also allows for commands to be used. See the | 	This interface also allows for commands to be used. See the | ||||||
| 	"Filter commands" section for more details. | 	"Filter commands" section for more details. | ||||||
| 
 | 
 | ||||||
|  | 	As a speed up, since processing strings can't be quite expensive | ||||||
|  | 	and requires a check of all functions registered to tracing, instead | ||||||
|  | 	an index can be written into this file. A number (starting with "1") | ||||||
|  | 	written will instead select the same corresponding at the line position | ||||||
|  | 	of the "available_filter_functions" file. | ||||||
|  | 
 | ||||||
|   set_ftrace_notrace: |   set_ftrace_notrace: | ||||||
| 
 | 
 | ||||||
| 	This has an effect opposite to that of | 	This has an effect opposite to that of | ||||||
|  | @ -2835,6 +2841,38 @@ Produces:: | ||||||
| 
 | 
 | ||||||
| We can see that there's no more lock or preempt tracing. | We can see that there's no more lock or preempt tracing. | ||||||
| 
 | 
 | ||||||
|  | Selecting function filters via index | ||||||
|  | ------------------------------------ | ||||||
|  | 
 | ||||||
|  | Because processing of strings is expensive (the address of the function | ||||||
|  | needs to be looked up before comparing to the string being passed in), | ||||||
|  | an index can be used as well to enable functions. This is useful in the | ||||||
|  | case of setting thousands of specific functions at a time. By passing | ||||||
|  | in a list of numbers, no string processing will occur. Instead, the function | ||||||
|  | at the specific location in the internal array (which corresponds to the | ||||||
|  | functions in the "available_filter_functions" file), is selected. | ||||||
|  | 
 | ||||||
|  | :: | ||||||
|  | 
 | ||||||
|  |   # echo 1 > set_ftrace_filter | ||||||
|  | 
 | ||||||
|  | Will select the first function listed in "available_filter_functions" | ||||||
|  | 
 | ||||||
|  | :: | ||||||
|  | 
 | ||||||
|  |   # head -1 available_filter_functions | ||||||
|  |   trace_initcall_finish_cb | ||||||
|  | 
 | ||||||
|  |   # cat set_ftrace_filter | ||||||
|  |   trace_initcall_finish_cb | ||||||
|  | 
 | ||||||
|  |   # head -50 available_filter_functions | tail -1 | ||||||
|  |   x86_pmu_commit_txn | ||||||
|  | 
 | ||||||
|  |   # echo 1 50 > set_ftrace_filter | ||||||
|  |   # cat set_ftrace_filter | ||||||
|  |   trace_initcall_finish_cb | ||||||
|  |   x86_pmu_commit_txn | ||||||
| 
 | 
 | ||||||
| Dynamic ftrace with the function graph tracer | Dynamic ftrace with the function graph tracer | ||||||
| --------------------------------------------- | --------------------------------------------- | ||||||
|  |  | ||||||
|  | @ -3701,6 +3701,31 @@ enter_record(struct ftrace_hash *hash, struct dyn_ftrace *rec, int clear_filter) | ||||||
| 	return ret; | 	return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static int | ||||||
|  | add_rec_by_index(struct ftrace_hash *hash, struct ftrace_glob *func_g, | ||||||
|  | 		 int clear_filter) | ||||||
|  | { | ||||||
|  | 	long index = simple_strtoul(func_g->search, NULL, 0); | ||||||
|  | 	struct ftrace_page *pg; | ||||||
|  | 	struct dyn_ftrace *rec; | ||||||
|  | 
 | ||||||
|  | 	/* The index starts at 1 */ | ||||||
|  | 	if (--index < 0) | ||||||
|  | 		return 0; | ||||||
|  | 
 | ||||||
|  | 	do_for_each_ftrace_rec(pg, rec) { | ||||||
|  | 		if (pg->index <= index) { | ||||||
|  | 			index -= pg->index; | ||||||
|  | 			/* this is a double loop, break goes to the next page */ | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 		rec = &pg->records[index]; | ||||||
|  | 		enter_record(hash, rec, clear_filter); | ||||||
|  | 		return 1; | ||||||
|  | 	} while_for_each_ftrace_rec(); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static int | static int | ||||||
| ftrace_match_record(struct dyn_ftrace *rec, struct ftrace_glob *func_g, | ftrace_match_record(struct dyn_ftrace *rec, struct ftrace_glob *func_g, | ||||||
| 		struct ftrace_glob *mod_g, int exclude_mod) | 		struct ftrace_glob *mod_g, int exclude_mod) | ||||||
|  | @ -3769,6 +3794,11 @@ match_records(struct ftrace_hash *hash, char *func, int len, char *mod) | ||||||
| 	if (unlikely(ftrace_disabled)) | 	if (unlikely(ftrace_disabled)) | ||||||
| 		goto out_unlock; | 		goto out_unlock; | ||||||
| 
 | 
 | ||||||
|  | 	if (func_g.type == MATCH_INDEX) { | ||||||
|  | 		found = add_rec_by_index(hash, &func_g, clear_filter); | ||||||
|  | 		goto out_unlock; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	do_for_each_ftrace_rec(pg, rec) { | 	do_for_each_ftrace_rec(pg, rec) { | ||||||
| 
 | 
 | ||||||
| 		if (rec->flags & FTRACE_FL_DISABLED) | 		if (rec->flags & FTRACE_FL_DISABLED) | ||||||
|  |  | ||||||
|  | @ -1459,6 +1459,7 @@ enum regex_type { | ||||||
| 	MATCH_MIDDLE_ONLY, | 	MATCH_MIDDLE_ONLY, | ||||||
| 	MATCH_END_ONLY, | 	MATCH_END_ONLY, | ||||||
| 	MATCH_GLOB, | 	MATCH_GLOB, | ||||||
|  | 	MATCH_INDEX, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct regex { | struct regex { | ||||||
|  |  | ||||||
|  | @ -825,6 +825,9 @@ enum regex_type filter_parse_regex(char *buff, int len, char **search, int *not) | ||||||
| 
 | 
 | ||||||
| 	*search = buff; | 	*search = buff; | ||||||
| 
 | 
 | ||||||
|  | 	if (isdigit(buff[0])) | ||||||
|  | 		return MATCH_INDEX; | ||||||
|  | 
 | ||||||
| 	for (i = 0; i < len; i++) { | 	for (i = 0; i < len; i++) { | ||||||
| 		if (buff[i] == '*') { | 		if (buff[i] == '*') { | ||||||
| 			if (!i) { | 			if (!i) { | ||||||
|  | @ -862,6 +865,8 @@ static void filter_build_regex(struct filter_pred *pred) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	switch (type) { | 	switch (type) { | ||||||
|  | 	/* MATCH_INDEX should not happen, but if it does, match full */ | ||||||
|  | 	case MATCH_INDEX: | ||||||
| 	case MATCH_FULL: | 	case MATCH_FULL: | ||||||
| 		r->match = regex_match_full; | 		r->match = regex_match_full; | ||||||
| 		break; | 		break; | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Steven Rostedt (VMware)
						Steven Rostedt (VMware)