diff options
Diffstat (limited to 'lib/dynamic_debug.c')
| -rw-r--r-- | lib/dynamic_debug.c | 269 | 
1 files changed, 150 insertions, 119 deletions
| diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c index 321437bbf87d..1d012e597cc3 100644 --- a/lib/dynamic_debug.c +++ b/lib/dynamic_debug.c @@ -11,7 +11,7 @@   * Copyright (C) 2013 Du, Changbin <changbin.du@gmail.com>   */ -#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__ +#define pr_fmt(fmt) "dyndbg: " fmt  #include <linux/kernel.h>  #include <linux/module.h> @@ -39,8 +39,8 @@  #include <rdma/ib_verbs.h> -extern struct _ddebug __start___verbose[]; -extern struct _ddebug __stop___verbose[]; +extern struct _ddebug __start___dyndbg[]; +extern struct _ddebug __stop___dyndbg[];  struct ddebug_table {  	struct list_head link; @@ -62,6 +62,11 @@ struct ddebug_iter {  	unsigned int idx;  }; +struct flag_settings { +	unsigned int flags; +	unsigned int mask; +}; +  static DEFINE_MUTEX(ddebug_lock);  static LIST_HEAD(ddebug_tables);  static int verbose; @@ -87,30 +92,33 @@ static struct { unsigned flag:8; char opt_char; } opt_array[] = {  	{ _DPRINTK_FLAGS_NONE, '_' },  }; +struct flagsbuf { char buf[ARRAY_SIZE(opt_array)+1]; }; +  /* format a string into buf[] which describes the _ddebug's flags */ -static char *ddebug_describe_flags(struct _ddebug *dp, char *buf, -				    size_t maxlen) +static char *ddebug_describe_flags(unsigned int flags, struct flagsbuf *fb)  { -	char *p = buf; +	char *p = fb->buf;  	int i; -	BUG_ON(maxlen < 6);  	for (i = 0; i < ARRAY_SIZE(opt_array); ++i) -		if (dp->flags & opt_array[i].flag) +		if (flags & opt_array[i].flag)  			*p++ = opt_array[i].opt_char; -	if (p == buf) +	if (p == fb->buf)  		*p++ = '_';  	*p = '\0'; -	return buf; +	return fb->buf;  } -#define vpr_info(fmt, ...)					\ +#define vnpr_info(lvl, fmt, ...)				\  do {								\ -	if (verbose)						\ +	if (verbose >= lvl)					\  		pr_info(fmt, ##__VA_ARGS__);			\  } while (0) +#define vpr_info(fmt, ...)	vnpr_info(1, fmt, ##__VA_ARGS__) +#define v2pr_info(fmt, ...)	vnpr_info(2, fmt, ##__VA_ARGS__) +  static void vpr_info_dq(const struct ddebug_query *query, const char *msg)  {  	/* trim any trailing newlines */ @@ -124,10 +132,10 @@ static void vpr_info_dq(const struct ddebug_query *query, const char *msg)  	vpr_info("%s: func=\"%s\" file=\"%s\" module=\"%s\" format=\"%.*s\" lineno=%u-%u\n",  		 msg, -		 query->function ? query->function : "", -		 query->filename ? query->filename : "", -		 query->module ? query->module : "", -		 fmtlen, query->format ? query->format : "", +		 query->function ?: "", +		 query->filename ?: "", +		 query->module ?: "", +		 fmtlen, query->format ?: "",  		 query->first_lineno, query->last_lineno);  } @@ -138,13 +146,13 @@ static void vpr_info_dq(const struct ddebug_query *query, const char *msg)   * logs the changes.  Takes ddebug_lock.   */  static int ddebug_change(const struct ddebug_query *query, -			unsigned int flags, unsigned int mask) +			 struct flag_settings *modifiers)  {  	int i;  	struct ddebug_table *dt;  	unsigned int newflags;  	unsigned int nfound = 0; -	char flagbuf[10]; +	struct flagsbuf fbuf;  	/* search for matching ddebugs */  	mutex_lock(&ddebug_lock); @@ -173,9 +181,16 @@ static int ddebug_change(const struct ddebug_query *query,  				continue;  			/* match against the format */ -			if (query->format && -			    !strstr(dp->format, query->format)) -				continue; +			if (query->format) { +				if (*query->format == '^') { +					char *p; +					/* anchored search. match must be at beginning */ +					p = strstr(dp->format, query->format+1); +					if (p != dp->format) +						continue; +				} else if (!strstr(dp->format, query->format)) +					continue; +			}  			/* match against the line number range */  			if (query->first_lineno && @@ -187,22 +202,21 @@ static int ddebug_change(const struct ddebug_query *query,  			nfound++; -			newflags = (dp->flags & mask) | flags; +			newflags = (dp->flags & modifiers->mask) | modifiers->flags;  			if (newflags == dp->flags)  				continue;  #ifdef CONFIG_JUMP_LABEL  			if (dp->flags & _DPRINTK_FLAGS_PRINT) { -				if (!(flags & _DPRINTK_FLAGS_PRINT)) +				if (!(modifiers->flags & _DPRINTK_FLAGS_PRINT))  					static_branch_disable(&dp->key.dd_key_true); -			} else if (flags & _DPRINTK_FLAGS_PRINT) +			} else if (modifiers->flags & _DPRINTK_FLAGS_PRINT)  				static_branch_enable(&dp->key.dd_key_true);  #endif  			dp->flags = newflags; -			vpr_info("changed %s:%d [%s]%s =%s\n", +			v2pr_info("changed %s:%d [%s]%s =%s\n",  				 trim_prefix(dp->filename), dp->lineno,  				 dt->mod_name, dp->function, -				 ddebug_describe_flags(dp, flagbuf, -						       sizeof(flagbuf))); +				 ddebug_describe_flags(dp->flags, &fbuf));  		}  	}  	mutex_unlock(&ddebug_lock); @@ -289,6 +303,41 @@ static inline int parse_lineno(const char *str, unsigned int *val)  	return 0;  } +static int parse_linerange(struct ddebug_query *query, const char *first) +{ +	char *last = strchr(first, '-'); + +	if (query->first_lineno || query->last_lineno) { +		pr_err("match-spec: line used 2x\n"); +		return -EINVAL; +	} +	if (last) +		*last++ = '\0'; +	if (parse_lineno(first, &query->first_lineno) < 0) +		return -EINVAL; +	if (last) { +		/* range <first>-<last> */ +		if (parse_lineno(last, &query->last_lineno) < 0) +			return -EINVAL; + +		/* special case for last lineno not specified */ +		if (query->last_lineno == 0) +			query->last_lineno = UINT_MAX; + +		if (query->last_lineno < query->first_lineno) { +			pr_err("last-line:%d < 1st-line:%d\n", +			       query->last_lineno, +			       query->first_lineno); +			return -EINVAL; +		} +	} else { +		query->last_lineno = query->first_lineno; +	} +	vpr_info("parsed line %d-%d\n", query->first_lineno, +		 query->last_lineno); +	return 0; +} +  static int check_set(const char **dest, char *src, char *name)  {  	int rc = 0; @@ -304,7 +353,8 @@ static int check_set(const char **dest, char *src, char *name)  /*   * Parse words[] as a ddebug query specification, which is a series - * of (keyword, value) pairs chosen from these possibilities: + * of (keyword, value) pairs or combined keyword=value terms, + * chosen from these possibilities:   *   * func <function-name>   * file <full-pathname> @@ -322,61 +372,62 @@ static int ddebug_parse_query(char *words[], int nwords,  {  	unsigned int i;  	int rc = 0; - -	/* check we have an even number of words */ -	if (nwords % 2 != 0) { -		pr_err("expecting pairs of match-spec <value>\n"); -		return -EINVAL; -	} -	memset(query, 0, sizeof(*query)); +	char *fline; +	char *keyword, *arg;  	if (modname)  		/* support $modname.dyndbg=<multiple queries> */  		query->module = modname; -	for (i = 0; i < nwords; i += 2) { -		if (!strcmp(words[i], "func")) { -			rc = check_set(&query->function, words[i+1], "func"); -		} else if (!strcmp(words[i], "file")) { -			rc = check_set(&query->filename, words[i+1], "file"); -		} else if (!strcmp(words[i], "module")) { -			rc = check_set(&query->module, words[i+1], "module"); -		} else if (!strcmp(words[i], "format")) { -			string_unescape_inplace(words[i+1], UNESCAPE_SPACE | -							    UNESCAPE_OCTAL | -							    UNESCAPE_SPECIAL); -			rc = check_set(&query->format, words[i+1], "format"); -		} else if (!strcmp(words[i], "line")) { -			char *first = words[i+1]; -			char *last = strchr(first, '-'); -			if (query->first_lineno || query->last_lineno) { -				pr_err("match-spec: line used 2x\n"); +	for (i = 0; i < nwords; i++) { +		/* accept keyword=arg */ +		vpr_info("%d w:%s\n", i, words[i]); + +		keyword = words[i]; +		arg = strchr(keyword, '='); +		if (arg) { +			*arg++ = '\0'; +		} else { +			i++; /* next word is arg */ +			if (!(i < nwords)) { +				pr_err("missing arg to keyword: %s\n", keyword);  				return -EINVAL;  			} -			if (last) -				*last++ = '\0'; -			if (parse_lineno(first, &query->first_lineno) < 0) -				return -EINVAL; -			if (last) { -				/* range <first>-<last> */ -				if (parse_lineno(last, &query->last_lineno) < 0) -					return -EINVAL; +			arg = words[i]; +		} +		vpr_info("%d key:%s arg:%s\n", i, keyword, arg); -				/* special case for last lineno not specified */ -				if (query->last_lineno == 0) -					query->last_lineno = UINT_MAX; +		if (!strcmp(keyword, "func")) { +			rc = check_set(&query->function, arg, "func"); +		} else if (!strcmp(keyword, "file")) { +			if (check_set(&query->filename, arg, "file")) +				return -EINVAL; -				if (query->last_lineno < query->first_lineno) { -					pr_err("last-line:%d < 1st-line:%d\n", -						query->last_lineno, -						query->first_lineno); +			/* tail :$info is function or line-range */ +			fline = strchr(query->filename, ':'); +			if (!fline) +				break; +			*fline++ = '\0'; +			if (isalpha(*fline) || *fline == '*' || *fline == '?') { +				/* take as function name */ +				if (check_set(&query->function, fline, "func"))  					return -EINVAL; -				}  			} else { -				query->last_lineno = query->first_lineno; +				if (parse_linerange(query, fline)) +					return -EINVAL;  			} +		} else if (!strcmp(keyword, "module")) { +			rc = check_set(&query->module, arg, "module"); +		} else if (!strcmp(keyword, "format")) { +			string_unescape_inplace(arg, UNESCAPE_SPACE | +							    UNESCAPE_OCTAL | +							    UNESCAPE_SPECIAL); +			rc = check_set(&query->format, arg, "format"); +		} else if (!strcmp(keyword, "line")) { +			if (parse_linerange(query, arg)) +				return -EINVAL;  		} else { -			pr_err("unknown keyword \"%s\"\n", words[i]); +			pr_err("unknown keyword \"%s\"\n", keyword);  			return -EINVAL;  		}  		if (rc) @@ -392,11 +443,9 @@ static int ddebug_parse_query(char *words[], int nwords,   * flags fields of matched _ddebug's.  Returns 0 on success   * or <0 on error.   */ -static int ddebug_parse_flags(const char *str, unsigned int *flagsp, -			       unsigned int *maskp) +static int ddebug_parse_flags(const char *str, struct flag_settings *modifiers)  { -	unsigned flags = 0; -	int op = '=', i; +	int op, i;  	switch (*str) {  	case '+': @@ -413,40 +462,40 @@ static int ddebug_parse_flags(const char *str, unsigned int *flagsp,  	for (; *str ; ++str) {  		for (i = ARRAY_SIZE(opt_array) - 1; i >= 0; i--) {  			if (*str == opt_array[i].opt_char) { -				flags |= opt_array[i].flag; +				modifiers->flags |= opt_array[i].flag;  				break;  			}  		}  		if (i < 0) { -			pr_err("unknown flag '%c' in \"%s\"\n", *str, str); +			pr_err("unknown flag '%c'\n", *str);  			return -EINVAL;  		}  	} -	vpr_info("flags=0x%x\n", flags); +	vpr_info("flags=0x%x\n", modifiers->flags); -	/* calculate final *flagsp, *maskp according to mask and op */ +	/* calculate final flags, mask based upon op */  	switch (op) {  	case '=': -		*maskp = 0; -		*flagsp = flags; +		/* modifiers->flags already set */ +		modifiers->mask = 0;  		break;  	case '+': -		*maskp = ~0U; -		*flagsp = flags; +		modifiers->mask = ~0U;  		break;  	case '-': -		*maskp = ~flags; -		*flagsp = 0; +		modifiers->mask = ~modifiers->flags; +		modifiers->flags = 0;  		break;  	} -	vpr_info("*flagsp=0x%x *maskp=0x%x\n", *flagsp, *maskp); +	vpr_info("*flagsp=0x%x *maskp=0x%x\n", modifiers->flags, modifiers->mask); +  	return 0;  }  static int ddebug_exec_query(char *query_string, const char *modname)  { -	unsigned int flags = 0, mask = 0; -	struct ddebug_query query; +	struct flag_settings modifiers = {}; +	struct ddebug_query query = {};  #define MAXWORDS 9  	int nwords, nfound;  	char *words[MAXWORDS]; @@ -457,7 +506,7 @@ static int ddebug_exec_query(char *query_string, const char *modname)  		return -EINVAL;  	}  	/* check flags 1st (last arg) so query is pairs of spec,val */ -	if (ddebug_parse_flags(words[nwords-1], &flags, &mask)) { +	if (ddebug_parse_flags(words[nwords-1], &modifiers)) {  		pr_err("flags parse failed\n");  		return -EINVAL;  	} @@ -466,7 +515,7 @@ static int ddebug_exec_query(char *query_string, const char *modname)  		return -EINVAL;  	}  	/* actually go and implement the change */ -	nfound = ddebug_change(&query, flags, mask); +	nfound = ddebug_change(&query, &modifiers);  	vpr_info_dq(&query, nfound ? "applied" : "no-match");  	return nfound; @@ -476,7 +525,7 @@ static int ddebug_exec_query(char *query_string, const char *modname)     last error or number of matching callsites.  Module name is either     in param (for boot arg) or perhaps in query string.  */ -static int ddebug_exec_queries(char *query, const char *modname) +int ddebug_exec_queries(char *query, const char *modname)  {  	char *split;  	int i, errs = 0, exitcode = 0, rc, nfound = 0; @@ -508,6 +557,7 @@ static int ddebug_exec_queries(char *query, const char *modname)  		return exitcode;  	return nfound;  } +EXPORT_SYMBOL_GPL(ddebug_exec_queries);  #define PREFIX_SIZE 64 @@ -771,8 +821,6 @@ static void *ddebug_proc_start(struct seq_file *m, loff_t *pos)  	struct _ddebug *dp;  	int n = *pos; -	vpr_info("called m=%p *pos=%lld\n", m, (unsigned long long)*pos); -  	mutex_lock(&ddebug_lock);  	if (!n) @@ -795,9 +843,6 @@ static void *ddebug_proc_next(struct seq_file *m, void *p, loff_t *pos)  	struct ddebug_iter *iter = m->private;  	struct _ddebug *dp; -	vpr_info("called m=%p p=%p *pos=%lld\n", -		 m, p, (unsigned long long)*pos); -  	if (p == SEQ_START_TOKEN)  		dp = ddebug_iter_first(iter);  	else @@ -816,9 +861,7 @@ static int ddebug_proc_show(struct seq_file *m, void *p)  {  	struct ddebug_iter *iter = m->private;  	struct _ddebug *dp = p; -	char flagsbuf[10]; - -	vpr_info("called m=%p p=%p\n", m, p); +	struct flagsbuf flags;  	if (p == SEQ_START_TOKEN) {  		seq_puts(m, @@ -829,7 +872,7 @@ static int ddebug_proc_show(struct seq_file *m, void *p)  	seq_printf(m, "%s:%u [%s]%s =%s \"",  		   trim_prefix(dp->filename), dp->lineno,  		   iter->table->mod_name, dp->function, -		   ddebug_describe_flags(dp, flagsbuf, sizeof(flagsbuf))); +		   ddebug_describe_flags(dp->flags, &flags));  	seq_escape(m, dp->format, "\t\r\n\"");  	seq_puts(m, "\"\n"); @@ -842,7 +885,6 @@ static int ddebug_proc_show(struct seq_file *m, void *p)   */  static void ddebug_proc_stop(struct seq_file *m, void *p)  { -	vpr_info("called m=%p p=%p\n", m, p);  	mutex_unlock(&ddebug_lock);  } @@ -853,13 +895,6 @@ static const struct seq_operations ddebug_proc_seqops = {  	.stop = ddebug_proc_stop  }; -/* - * File_ops->open method for <debugfs>/dynamic_debug/control.  Does - * the seq_file setup dance, and also creates an iterator to walk the - * _ddebugs.  Note that we create a seq_file always, even for O_WRONLY - * files where it's not needed, as doing so simplifies the ->release - * method. - */  static int ddebug_proc_open(struct inode *inode, struct file *file)  {  	vpr_info("called\n"); @@ -909,10 +944,10 @@ int ddebug_add_module(struct _ddebug *tab, unsigned int n,  	dt->ddebugs = tab;  	mutex_lock(&ddebug_lock); -	list_add_tail(&dt->link, &ddebug_tables); +	list_add(&dt->link, &ddebug_tables);  	mutex_unlock(&ddebug_lock); -	vpr_info("%u debug prints in module %s\n", n, dt->mod_name); +	v2pr_info("%u debug prints in module %s\n", n, dt->mod_name);  	return 0;  } @@ -971,7 +1006,7 @@ int ddebug_remove_module(const char *mod_name)  	struct ddebug_table *dt, *nextdt;  	int ret = -ENOENT; -	vpr_info("removing module \"%s\"\n", mod_name); +	v2pr_info("removing module \"%s\"\n", mod_name);  	mutex_lock(&ddebug_lock);  	list_for_each_entry_safe(dt, nextdt, &ddebug_tables, link) { @@ -1029,9 +1064,8 @@ static int __init dynamic_debug_init(void)  	char *cmdline;  	int ret = 0;  	int n = 0, entries = 0, modct = 0; -	int verbose_bytes = 0; -	if (&__start___verbose == &__stop___verbose) { +	if (&__start___dyndbg == &__stop___dyndbg) {  		if (IS_ENABLED(CONFIG_DYNAMIC_DEBUG)) {  			pr_warn("_ddebug table is empty in a CONFIG_DYNAMIC_DEBUG build\n");  			return 1; @@ -1040,14 +1074,11 @@ static int __init dynamic_debug_init(void)  		ddebug_init_success = 1;  		return 0;  	} -	iter = __start___verbose; +	iter = __start___dyndbg;  	modname = iter->modname;  	iter_start = iter; -	for (; iter < __stop___verbose; iter++) { +	for (; iter < __stop___dyndbg; iter++) {  		entries++; -		verbose_bytes += strlen(iter->modname) + strlen(iter->function) -			+ strlen(iter->filename) + strlen(iter->format); -  		if (strcmp(modname, iter->modname)) {  			modct++;  			ret = ddebug_add_module(iter_start, n, modname); @@ -1064,9 +1095,9 @@ static int __init dynamic_debug_init(void)  		goto out_err;  	ddebug_init_success = 1; -	vpr_info("%d modules, %d entries and %d bytes in ddebug tables, %d bytes in (readonly) verbose section\n", +	vpr_info("%d modules, %d entries and %d bytes in ddebug tables, %d bytes in __dyndbg section\n",  		 modct, entries, (int)(modct * sizeof(struct ddebug_table)), -		 verbose_bytes + (int)(__stop___verbose - __start___verbose)); +		 (int)(entries * sizeof(struct _ddebug)));  	/* apply ddebug_query boot param, dont unload tables on err */  	if (ddebug_setup_string[0] != '\0') { | 
