diff options
author | Jim Cromie <jim.cromie@gmail.com> | 2011-12-19 17:13:21 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2012-01-24 15:50:36 -0500 |
commit | 85f7f6c0edb8414053d788229c97d5ecff21efab (patch) | |
tree | 214ed1f1ed2d026280ca46728948f59c0cb5f528 | |
parent | 574b3725e327531c70361d1a10b8dc8dd2b93590 (diff) |
dynamic_debug: process multiple debug-queries on a line
Insert ddebug_exec_queries() in place of ddebug_exec_query(). It
splits the query string on [;\n], and calls ddebug_exec_query() on
each. All queries are processed independent of errors, allowing a
query to fail, for example when a module is not installed. Empty
lines and comments are skipped. Errors are counted, and the last
error seen (negative) or the number of callsites found (0 or positive)
is returned. Return code checks are altered accordingly.
With this, multiple queries can be given in ddebug_query, allowing
more selective enabling of callsites. As a side effect, a set of
commands can be batched in:
cat cmd-file > $DBGMT/dynamic_debug/control
We dont want a ddebug_query syntax error to kill the dynamic debug
facility, so dynamic_debug_init() zeros ddebug_exec_queries()'s return
code after logging the appropriate message, so that ddebug tables are
preserved and $DBGMT/dynamic_debug/control file is created. This
would be appropriate even without accepting multiple queries.
This patch also alters ddebug_change() to return number of callsites
matched (which typically is the same as number of callsites changed).
ddebug_exec_query() also returns the number found, or a negative value
if theres a parse error on the query.
Splitting on [;\n] prevents their use in format-specs, but selecting
callsites on punctuation is brittle anyway, meaningful and selective
substrings are more typical.
Note: splitting queries on ';' before handling trailing #comments
means that a ';' also terminates a comment, and text after the ';' is
treated as another query. This trailing query will almost certainly
result in a parse error and thus have no effect other than the error
message. The double corner case with unexpected results is:
ddebug_query="func foo +p # enable foo ; +p"
Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
Signed-off-by: Jason Baron <jbaron@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r-- | Documentation/dynamic-debug-howto.txt | 23 | ||||
-rw-r--r-- | lib/dynamic_debug.c | 73 |
2 files changed, 66 insertions, 30 deletions
diff --git a/Documentation/dynamic-debug-howto.txt b/Documentation/dynamic-debug-howto.txt index 378b5d1bf436..74e6c7782678 100644 --- a/Documentation/dynamic-debug-howto.txt +++ b/Documentation/dynamic-debug-howto.txt | |||
@@ -12,7 +12,7 @@ dynamically enabled per-callsite. | |||
12 | Dynamic debug has even more useful features: | 12 | Dynamic debug has even more useful features: |
13 | 13 | ||
14 | * Simple query language allows turning on and off debugging statements by | 14 | * Simple query language allows turning on and off debugging statements by |
15 | matching any combination of: | 15 | matching any combination of 0 or 1 of: |
16 | 16 | ||
17 | - source filename | 17 | - source filename |
18 | - function name | 18 | - function name |
@@ -79,31 +79,24 @@ Command Language Reference | |||
79 | ========================== | 79 | ========================== |
80 | 80 | ||
81 | At the lexical level, a command comprises a sequence of words separated | 81 | At the lexical level, a command comprises a sequence of words separated |
82 | by whitespace characters. Note that newlines are treated as word | 82 | by spaces or tabs. So these are all equivalent: |
83 | separators and do *not* end a command or allow multiple commands to | ||
84 | be done together. So these are all equivalent: | ||
85 | 83 | ||
86 | nullarbor:~ # echo -c 'file svcsock.c line 1603 +p' > | 84 | nullarbor:~ # echo -c 'file svcsock.c line 1603 +p' > |
87 | <debugfs>/dynamic_debug/control | 85 | <debugfs>/dynamic_debug/control |
88 | nullarbor:~ # echo -c ' file svcsock.c line 1603 +p ' > | 86 | nullarbor:~ # echo -c ' file svcsock.c line 1603 +p ' > |
89 | <debugfs>/dynamic_debug/control | 87 | <debugfs>/dynamic_debug/control |
90 | nullarbor:~ # echo -c 'file svcsock.c\nline 1603 +p' > | ||
91 | <debugfs>/dynamic_debug/control | ||
92 | nullarbor:~ # echo -n 'file svcsock.c line 1603 +p' > | 88 | nullarbor:~ # echo -n 'file svcsock.c line 1603 +p' > |
93 | <debugfs>/dynamic_debug/control | 89 | <debugfs>/dynamic_debug/control |
94 | 90 | ||
95 | Commands are bounded by a write() system call. If you want to do | 91 | Command submissions are bounded by a write() system call. |
96 | multiple commands you need to do a separate "echo" for each, like: | 92 | Multiple commands can be written together, separated by ';' or '\n'. |
97 | 93 | ||
98 | nullarbor:~ # echo 'file svcsock.c line 1603 +p' > /proc/dprintk ;\ | 94 | ~# echo "func pnpacpi_get_resources +p; func pnp_assign_mem +p" \ |
99 | > echo 'file svcsock.c line 1563 +p' > /proc/dprintk | 95 | > <debugfs>/dynamic_debug/control |
100 | 96 | ||
101 | or even like: | 97 | If your query set is big, you can batch them too: |
102 | 98 | ||
103 | nullarbor:~ # ( | 99 | ~# cat query-batch-file > <debugfs>/dynamic_debug/control |
104 | > echo 'file svcsock.c line 1603 +p' ;\ | ||
105 | > echo 'file svcsock.c line 1563 +p' ;\ | ||
106 | > ) > /proc/dprintk | ||
107 | 100 | ||
108 | At the syntactical level, a command comprises a sequence of match | 101 | At the syntactical level, a command comprises a sequence of match |
109 | specifications, followed by a flags change specification. | 102 | specifications, followed by a flags change specification. |
diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c index 93fc5d500cd5..310c753cf83e 100644 --- a/lib/dynamic_debug.c +++ b/lib/dynamic_debug.c | |||
@@ -124,13 +124,13 @@ do { \ | |||
124 | } while (0) | 124 | } while (0) |
125 | 125 | ||
126 | /* | 126 | /* |
127 | * Search the tables for _ddebug's which match the given | 127 | * Search the tables for _ddebug's which match the given `query' and |
128 | * `query' and apply the `flags' and `mask' to them. Tells | 128 | * apply the `flags' and `mask' to them. Returns number of matching |
129 | * the user which ddebug's were changed, or whether none | 129 | * callsites, normally the same as number of changes. If verbose, |
130 | * were matched. | 130 | * logs the changes. Takes ddebug_lock. |
131 | */ | 131 | */ |
132 | static void ddebug_change(const struct ddebug_query *query, | 132 | static int ddebug_change(const struct ddebug_query *query, |
133 | unsigned int flags, unsigned int mask) | 133 | unsigned int flags, unsigned int mask) |
134 | { | 134 | { |
135 | int i; | 135 | int i; |
136 | struct ddebug_table *dt; | 136 | struct ddebug_table *dt; |
@@ -192,6 +192,8 @@ static void ddebug_change(const struct ddebug_query *query, | |||
192 | 192 | ||
193 | if (!nfound && verbose) | 193 | if (!nfound && verbose) |
194 | pr_info("no matches for query\n"); | 194 | pr_info("no matches for query\n"); |
195 | |||
196 | return nfound; | ||
195 | } | 197 | } |
196 | 198 | ||
197 | /* | 199 | /* |
@@ -449,7 +451,7 @@ static int ddebug_exec_query(char *query_string) | |||
449 | unsigned int flags = 0, mask = 0; | 451 | unsigned int flags = 0, mask = 0; |
450 | struct ddebug_query query; | 452 | struct ddebug_query query; |
451 | #define MAXWORDS 9 | 453 | #define MAXWORDS 9 |
452 | int nwords; | 454 | int nwords, nfound; |
453 | char *words[MAXWORDS]; | 455 | char *words[MAXWORDS]; |
454 | 456 | ||
455 | nwords = ddebug_tokenize(query_string, words, MAXWORDS); | 457 | nwords = ddebug_tokenize(query_string, words, MAXWORDS); |
@@ -461,8 +463,47 @@ static int ddebug_exec_query(char *query_string) | |||
461 | return -EINVAL; | 463 | return -EINVAL; |
462 | 464 | ||
463 | /* actually go and implement the change */ | 465 | /* actually go and implement the change */ |
464 | ddebug_change(&query, flags, mask); | 466 | nfound = ddebug_change(&query, flags, mask); |
465 | return 0; | 467 | vpr_info_dq((&query), (nfound) ? "applied" : "no-match"); |
468 | |||
469 | return nfound; | ||
470 | } | ||
471 | |||
472 | /* handle multiple queries in query string, continue on error, return | ||
473 | last error or number of matching callsites. Module name is either | ||
474 | in param (for boot arg) or perhaps in query string. | ||
475 | */ | ||
476 | static int ddebug_exec_queries(char *query) | ||
477 | { | ||
478 | char *split; | ||
479 | int i, errs = 0, exitcode = 0, rc, nfound = 0; | ||
480 | |||
481 | for (i = 0; query; query = split) { | ||
482 | split = strpbrk(query, ";\n"); | ||
483 | if (split) | ||
484 | *split++ = '\0'; | ||
485 | |||
486 | query = skip_spaces(query); | ||
487 | if (!query || !*query || *query == '#') | ||
488 | continue; | ||
489 | |||
490 | if (verbose) | ||
491 | pr_info("query %d: \"%s\"\n", i, query); | ||
492 | |||
493 | rc = ddebug_exec_query(query); | ||
494 | if (rc < 0) { | ||
495 | errs++; | ||
496 | exitcode = rc; | ||
497 | } else | ||
498 | nfound += rc; | ||
499 | i++; | ||
500 | } | ||
501 | pr_info("processed %d queries, with %d matches, %d errs\n", | ||
502 | i, nfound, errs); | ||
503 | |||
504 | if (exitcode) | ||
505 | return exitcode; | ||
506 | return nfound; | ||
466 | } | 507 | } |
467 | 508 | ||
468 | #define PREFIX_SIZE 64 | 509 | #define PREFIX_SIZE 64 |
@@ -615,9 +656,9 @@ static ssize_t ddebug_proc_write(struct file *file, const char __user *ubuf, | |||
615 | if (verbose) | 656 | if (verbose) |
616 | pr_info("read %d bytes from userspace\n", (int)len); | 657 | pr_info("read %d bytes from userspace\n", (int)len); |
617 | 658 | ||
618 | ret = ddebug_exec_query(tmpbuf); | 659 | ret = ddebug_exec_queries(tmpbuf); |
619 | kfree(tmpbuf); | 660 | kfree(tmpbuf); |
620 | if (ret) | 661 | if (ret < 0) |
621 | return ret; | 662 | return ret; |
622 | 663 | ||
623 | *offp += len; | 664 | *offp += len; |
@@ -927,13 +968,15 @@ static int __init dynamic_debug_init(void) | |||
927 | 968 | ||
928 | /* ddebug_query boot param got passed -> set it up */ | 969 | /* ddebug_query boot param got passed -> set it up */ |
929 | if (ddebug_setup_string[0] != '\0') { | 970 | if (ddebug_setup_string[0] != '\0') { |
930 | ret = ddebug_exec_query(ddebug_setup_string); | 971 | ret = ddebug_exec_queries(ddebug_setup_string); |
931 | if (ret) | 972 | if (ret < 0) |
932 | pr_warn("Invalid ddebug boot param %s", | 973 | pr_warn("Invalid ddebug boot param %s", |
933 | ddebug_setup_string); | 974 | ddebug_setup_string); |
934 | else | 975 | else |
935 | pr_info("ddebug initialized with string %s", | 976 | pr_info("%d changes by ddebug_query\n", ret); |
936 | ddebug_setup_string); | 977 | |
978 | /* keep tables even on ddebug_query parse error */ | ||
979 | ret = 0; | ||
937 | } | 980 | } |
938 | 981 | ||
939 | out_free: | 982 | out_free: |