diff options
Diffstat (limited to 'lib/dynamic_debug.c')
| -rw-r--r-- | lib/dynamic_debug.c | 249 | 
1 files changed, 145 insertions, 104 deletions
diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c index 321437bbf87d..2d4dfd44b0fa 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; @@ -322,13 +371,13 @@ static int ddebug_parse_query(char *words[], int nwords,  {  	unsigned int i;  	int rc = 0; +	char *fline;  	/* 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));  	if (modname)  		/* support $modname.dyndbg=<multiple queries> */ @@ -338,7 +387,22 @@ static int ddebug_parse_query(char *words[], int nwords,  		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"); +			if (check_set(&query->filename, words[i+1], "file")) +				return -EINVAL; + +			/* 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 { +				if (parse_linerange(query, fline)) +					return -EINVAL; +			}  		} else if (!strcmp(words[i], "module")) {  			rc = check_set(&query->module, words[i+1], "module");  		} else if (!strcmp(words[i], "format")) { @@ -347,34 +411,8 @@ static int ddebug_parse_query(char *words[], int nwords,  							    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"); -				return -EINVAL; -			} -			if (last) -				*last++ = '\0'; -			if (parse_lineno(first, &query->first_lineno) < 0) +			if (parse_linerange(query, words[i+1]))  				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; -			}  		} else {  			pr_err("unknown keyword \"%s\"\n", words[i]);  			return -EINVAL; @@ -392,11 +430,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 +449,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 +493,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 +502,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; @@ -509,6 +545,30 @@ static int ddebug_exec_queries(char *query, const char *modname)  	return nfound;  } +/** + * dynamic_debug_exec_queries - select and change dynamic-debug prints + * @query: query-string described in admin-guide/dynamic-debug-howto + * @modname: string containing module name, usually &module.mod_name + * + * This uses the >/proc/dynamic_debug/control reader, allowing module + * authors to modify their dynamic-debug callsites. The modname is + * canonically struct module.mod_name, but can also be null or a + * module-wildcard, for example: "drm*". + */ +int dynamic_debug_exec_queries(const char *query, const char *modname) +{ +	int rc; +	char *qry = kstrndup(query, PAGE_SIZE, GFP_KERNEL); + +	if (!query) +		return -ENOMEM; + +	rc = ddebug_exec_queries(qry, modname); +	kfree(qry); +	return rc; +} +EXPORT_SYMBOL_GPL(dynamic_debug_exec_queries); +  #define PREFIX_SIZE 64  static int remaining(int wrote) @@ -771,8 +831,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 +853,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 +871,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 +882,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 +895,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 +905,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 +954,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("%3u debug prints in module %s\n", n, dt->mod_name);  	return 0;  } @@ -971,7 +1016,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 +1074,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 +1084,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 +1105,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') {  | 
