diff options
author | Glenn Elliott <gelliott@cs.unc.edu> | 2012-03-04 19:47:13 -0500 |
---|---|---|
committer | Glenn Elliott <gelliott@cs.unc.edu> | 2012-03-04 19:47:13 -0500 |
commit | c71c03bda1e86c9d5198c5d83f712e695c4f2a1e (patch) | |
tree | ecb166cb3e2b7e2adb3b5e292245fefd23381ac8 /lib/dynamic_debug.c | |
parent | ea53c912f8a86a8567697115b6a0d8152beee5c8 (diff) | |
parent | 6a00f206debf8a5c8899055726ad127dbeeed098 (diff) |
Merge branch 'mpi-master' into wip-k-fmlpwip-k-fmlp
Conflicts:
litmus/sched_cedf.c
Diffstat (limited to 'lib/dynamic_debug.c')
-rw-r--r-- | lib/dynamic_debug.c | 206 |
1 files changed, 130 insertions, 76 deletions
diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c index 02afc2533728..75ca78f3a8c9 100644 --- a/lib/dynamic_debug.c +++ b/lib/dynamic_debug.c | |||
@@ -7,6 +7,7 @@ | |||
7 | * Copyright (C) 2008 Jason Baron <jbaron@redhat.com> | 7 | * Copyright (C) 2008 Jason Baron <jbaron@redhat.com> |
8 | * By Greg Banks <gnb@melbourne.sgi.com> | 8 | * By Greg Banks <gnb@melbourne.sgi.com> |
9 | * Copyright (c) 2008 Silicon Graphics Inc. All Rights Reserved. | 9 | * Copyright (c) 2008 Silicon Graphics Inc. All Rights Reserved. |
10 | * Copyright (C) 2011 Bart Van Assche. All Rights Reserved. | ||
10 | */ | 11 | */ |
11 | 12 | ||
12 | #include <linux/kernel.h> | 13 | #include <linux/kernel.h> |
@@ -26,19 +27,13 @@ | |||
26 | #include <linux/dynamic_debug.h> | 27 | #include <linux/dynamic_debug.h> |
27 | #include <linux/debugfs.h> | 28 | #include <linux/debugfs.h> |
28 | #include <linux/slab.h> | 29 | #include <linux/slab.h> |
30 | #include <linux/jump_label.h> | ||
31 | #include <linux/hardirq.h> | ||
32 | #include <linux/sched.h> | ||
29 | 33 | ||
30 | extern struct _ddebug __start___verbose[]; | 34 | extern struct _ddebug __start___verbose[]; |
31 | extern struct _ddebug __stop___verbose[]; | 35 | extern struct _ddebug __stop___verbose[]; |
32 | 36 | ||
33 | /* dynamic_debug_enabled, and dynamic_debug_enabled2 are bitmasks in which | ||
34 | * bit n is set to 1 if any modname hashes into the bucket n, 0 otherwise. They | ||
35 | * use independent hash functions, to reduce the chance of false positives. | ||
36 | */ | ||
37 | long long dynamic_debug_enabled; | ||
38 | EXPORT_SYMBOL_GPL(dynamic_debug_enabled); | ||
39 | long long dynamic_debug_enabled2; | ||
40 | EXPORT_SYMBOL_GPL(dynamic_debug_enabled2); | ||
41 | |||
42 | struct ddebug_table { | 37 | struct ddebug_table { |
43 | struct list_head link; | 38 | struct list_head link; |
44 | char *mod_name; | 39 | char *mod_name; |
@@ -71,15 +66,25 @@ static inline const char *basename(const char *path) | |||
71 | return tail ? tail+1 : path; | 66 | return tail ? tail+1 : path; |
72 | } | 67 | } |
73 | 68 | ||
69 | static struct { unsigned flag:8; char opt_char; } opt_array[] = { | ||
70 | { _DPRINTK_FLAGS_PRINT, 'p' }, | ||
71 | { _DPRINTK_FLAGS_INCL_MODNAME, 'm' }, | ||
72 | { _DPRINTK_FLAGS_INCL_FUNCNAME, 'f' }, | ||
73 | { _DPRINTK_FLAGS_INCL_LINENO, 'l' }, | ||
74 | { _DPRINTK_FLAGS_INCL_TID, 't' }, | ||
75 | }; | ||
76 | |||
74 | /* format a string into buf[] which describes the _ddebug's flags */ | 77 | /* format a string into buf[] which describes the _ddebug's flags */ |
75 | static char *ddebug_describe_flags(struct _ddebug *dp, char *buf, | 78 | static char *ddebug_describe_flags(struct _ddebug *dp, char *buf, |
76 | size_t maxlen) | 79 | size_t maxlen) |
77 | { | 80 | { |
78 | char *p = buf; | 81 | char *p = buf; |
82 | int i; | ||
79 | 83 | ||
80 | BUG_ON(maxlen < 4); | 84 | BUG_ON(maxlen < 4); |
81 | if (dp->flags & _DPRINTK_FLAGS_PRINT) | 85 | for (i = 0; i < ARRAY_SIZE(opt_array); ++i) |
82 | *p++ = 'p'; | 86 | if (dp->flags & opt_array[i].flag) |
87 | *p++ = opt_array[i].opt_char; | ||
83 | if (p == buf) | 88 | if (p == buf) |
84 | *p++ = '-'; | 89 | *p++ = '-'; |
85 | *p = '\0'; | 90 | *p = '\0'; |
@@ -88,26 +93,6 @@ static char *ddebug_describe_flags(struct _ddebug *dp, char *buf, | |||
88 | } | 93 | } |
89 | 94 | ||
90 | /* | 95 | /* |
91 | * must be called with ddebug_lock held | ||
92 | */ | ||
93 | |||
94 | static int disabled_hash(char hash, bool first_table) | ||
95 | { | ||
96 | struct ddebug_table *dt; | ||
97 | char table_hash_value; | ||
98 | |||
99 | list_for_each_entry(dt, &ddebug_tables, link) { | ||
100 | if (first_table) | ||
101 | table_hash_value = dt->ddebugs->primary_hash; | ||
102 | else | ||
103 | table_hash_value = dt->ddebugs->secondary_hash; | ||
104 | if (dt->num_enabled && (hash == table_hash_value)) | ||
105 | return 0; | ||
106 | } | ||
107 | return 1; | ||
108 | } | ||
109 | |||
110 | /* | ||
111 | * Search the tables for _ddebug's which match the given | 96 | * Search the tables for _ddebug's which match the given |
112 | * `query' and apply the `flags' and `mask' to them. Tells | 97 | * `query' and apply the `flags' and `mask' to them. Tells |
113 | * the user which ddebug's were changed, or whether none | 98 | * the user which ddebug's were changed, or whether none |
@@ -169,19 +154,10 @@ static void ddebug_change(const struct ddebug_query *query, | |||
169 | else if (!dp->flags) | 154 | else if (!dp->flags) |
170 | dt->num_enabled++; | 155 | dt->num_enabled++; |
171 | dp->flags = newflags; | 156 | dp->flags = newflags; |
172 | if (newflags) { | 157 | if (newflags) |
173 | dynamic_debug_enabled |= | 158 | dp->enabled = 1; |
174 | (1LL << dp->primary_hash); | 159 | else |
175 | dynamic_debug_enabled2 |= | 160 | dp->enabled = 0; |
176 | (1LL << dp->secondary_hash); | ||
177 | } else { | ||
178 | if (disabled_hash(dp->primary_hash, true)) | ||
179 | dynamic_debug_enabled &= | ||
180 | ~(1LL << dp->primary_hash); | ||
181 | if (disabled_hash(dp->secondary_hash, false)) | ||
182 | dynamic_debug_enabled2 &= | ||
183 | ~(1LL << dp->secondary_hash); | ||
184 | } | ||
185 | if (verbose) | 161 | if (verbose) |
186 | printk(KERN_INFO | 162 | printk(KERN_INFO |
187 | "ddebug: changed %s:%d [%s]%s %s\n", | 163 | "ddebug: changed %s:%d [%s]%s %s\n", |
@@ -380,7 +356,7 @@ static int ddebug_parse_flags(const char *str, unsigned int *flagsp, | |||
380 | unsigned int *maskp) | 356 | unsigned int *maskp) |
381 | { | 357 | { |
382 | unsigned flags = 0; | 358 | unsigned flags = 0; |
383 | int op = '='; | 359 | int op = '=', i; |
384 | 360 | ||
385 | switch (*str) { | 361 | switch (*str) { |
386 | case '+': | 362 | case '+': |
@@ -395,13 +371,14 @@ static int ddebug_parse_flags(const char *str, unsigned int *flagsp, | |||
395 | printk(KERN_INFO "%s: op='%c'\n", __func__, op); | 371 | printk(KERN_INFO "%s: op='%c'\n", __func__, op); |
396 | 372 | ||
397 | for ( ; *str ; ++str) { | 373 | for ( ; *str ; ++str) { |
398 | switch (*str) { | 374 | for (i = ARRAY_SIZE(opt_array) - 1; i >= 0; i--) { |
399 | case 'p': | 375 | if (*str == opt_array[i].opt_char) { |
400 | flags |= _DPRINTK_FLAGS_PRINT; | 376 | flags |= opt_array[i].flag; |
401 | break; | 377 | break; |
402 | default: | 378 | } |
403 | return -EINVAL; | ||
404 | } | 379 | } |
380 | if (i < 0) | ||
381 | return -EINVAL; | ||
405 | } | 382 | } |
406 | if (flags == 0) | 383 | if (flags == 0) |
407 | return -EINVAL; | 384 | return -EINVAL; |
@@ -429,6 +406,69 @@ static int ddebug_parse_flags(const char *str, unsigned int *flagsp, | |||
429 | return 0; | 406 | return 0; |
430 | } | 407 | } |
431 | 408 | ||
409 | static int ddebug_exec_query(char *query_string) | ||
410 | { | ||
411 | unsigned int flags = 0, mask = 0; | ||
412 | struct ddebug_query query; | ||
413 | #define MAXWORDS 9 | ||
414 | int nwords; | ||
415 | char *words[MAXWORDS]; | ||
416 | |||
417 | nwords = ddebug_tokenize(query_string, words, MAXWORDS); | ||
418 | if (nwords <= 0) | ||
419 | return -EINVAL; | ||
420 | if (ddebug_parse_query(words, nwords-1, &query)) | ||
421 | return -EINVAL; | ||
422 | if (ddebug_parse_flags(words[nwords-1], &flags, &mask)) | ||
423 | return -EINVAL; | ||
424 | |||
425 | /* actually go and implement the change */ | ||
426 | ddebug_change(&query, flags, mask); | ||
427 | return 0; | ||
428 | } | ||
429 | |||
430 | int __dynamic_pr_debug(struct _ddebug *descriptor, const char *fmt, ...) | ||
431 | { | ||
432 | va_list args; | ||
433 | int res; | ||
434 | |||
435 | BUG_ON(!descriptor); | ||
436 | BUG_ON(!fmt); | ||
437 | |||
438 | va_start(args, fmt); | ||
439 | res = printk(KERN_DEBUG); | ||
440 | if (descriptor->flags & _DPRINTK_FLAGS_INCL_TID) { | ||
441 | if (in_interrupt()) | ||
442 | res += printk(KERN_CONT "<intr> "); | ||
443 | else | ||
444 | res += printk(KERN_CONT "[%d] ", task_pid_vnr(current)); | ||
445 | } | ||
446 | if (descriptor->flags & _DPRINTK_FLAGS_INCL_MODNAME) | ||
447 | res += printk(KERN_CONT "%s:", descriptor->modname); | ||
448 | if (descriptor->flags & _DPRINTK_FLAGS_INCL_FUNCNAME) | ||
449 | res += printk(KERN_CONT "%s:", descriptor->function); | ||
450 | if (descriptor->flags & _DPRINTK_FLAGS_INCL_LINENO) | ||
451 | res += printk(KERN_CONT "%d ", descriptor->lineno); | ||
452 | res += vprintk(fmt, args); | ||
453 | va_end(args); | ||
454 | |||
455 | return res; | ||
456 | } | ||
457 | EXPORT_SYMBOL(__dynamic_pr_debug); | ||
458 | |||
459 | static __initdata char ddebug_setup_string[1024]; | ||
460 | static __init int ddebug_setup_query(char *str) | ||
461 | { | ||
462 | if (strlen(str) >= 1024) { | ||
463 | pr_warning("ddebug boot param string too large\n"); | ||
464 | return 0; | ||
465 | } | ||
466 | strcpy(ddebug_setup_string, str); | ||
467 | return 1; | ||
468 | } | ||
469 | |||
470 | __setup("ddebug_query=", ddebug_setup_query); | ||
471 | |||
432 | /* | 472 | /* |
433 | * File_ops->write method for <debugfs>/dynamic_debug/conrol. Gathers the | 473 | * File_ops->write method for <debugfs>/dynamic_debug/conrol. Gathers the |
434 | * command text from userspace, parses and executes it. | 474 | * command text from userspace, parses and executes it. |
@@ -436,12 +476,8 @@ static int ddebug_parse_flags(const char *str, unsigned int *flagsp, | |||
436 | static ssize_t ddebug_proc_write(struct file *file, const char __user *ubuf, | 476 | static ssize_t ddebug_proc_write(struct file *file, const char __user *ubuf, |
437 | size_t len, loff_t *offp) | 477 | size_t len, loff_t *offp) |
438 | { | 478 | { |
439 | unsigned int flags = 0, mask = 0; | ||
440 | struct ddebug_query query; | ||
441 | #define MAXWORDS 9 | ||
442 | int nwords; | ||
443 | char *words[MAXWORDS]; | ||
444 | char tmpbuf[256]; | 479 | char tmpbuf[256]; |
480 | int ret; | ||
445 | 481 | ||
446 | if (len == 0) | 482 | if (len == 0) |
447 | return 0; | 483 | return 0; |
@@ -455,16 +491,9 @@ static ssize_t ddebug_proc_write(struct file *file, const char __user *ubuf, | |||
455 | printk(KERN_INFO "%s: read %d bytes from userspace\n", | 491 | printk(KERN_INFO "%s: read %d bytes from userspace\n", |
456 | __func__, (int)len); | 492 | __func__, (int)len); |
457 | 493 | ||
458 | nwords = ddebug_tokenize(tmpbuf, words, MAXWORDS); | 494 | ret = ddebug_exec_query(tmpbuf); |
459 | if (nwords <= 0) | 495 | if (ret) |
460 | return -EINVAL; | 496 | return ret; |
461 | if (ddebug_parse_query(words, nwords-1, &query)) | ||
462 | return -EINVAL; | ||
463 | if (ddebug_parse_flags(words[nwords-1], &flags, &mask)) | ||
464 | return -EINVAL; | ||
465 | |||
466 | /* actually go and implement the change */ | ||
467 | ddebug_change(&query, flags, mask); | ||
468 | 497 | ||
469 | *offp += len; | 498 | *offp += len; |
470 | return len; | 499 | return len; |
@@ -725,13 +754,14 @@ static void ddebug_remove_all_tables(void) | |||
725 | mutex_unlock(&ddebug_lock); | 754 | mutex_unlock(&ddebug_lock); |
726 | } | 755 | } |
727 | 756 | ||
728 | static int __init dynamic_debug_init(void) | 757 | static __initdata int ddebug_init_success; |
758 | |||
759 | static int __init dynamic_debug_init_debugfs(void) | ||
729 | { | 760 | { |
730 | struct dentry *dir, *file; | 761 | struct dentry *dir, *file; |
731 | struct _ddebug *iter, *iter_start; | 762 | |
732 | const char *modname = NULL; | 763 | if (!ddebug_init_success) |
733 | int ret = 0; | 764 | return -ENODEV; |
734 | int n = 0; | ||
735 | 765 | ||
736 | dir = debugfs_create_dir("dynamic_debug", NULL); | 766 | dir = debugfs_create_dir("dynamic_debug", NULL); |
737 | if (!dir) | 767 | if (!dir) |
@@ -742,6 +772,16 @@ static int __init dynamic_debug_init(void) | |||
742 | debugfs_remove(dir); | 772 | debugfs_remove(dir); |
743 | return -ENOMEM; | 773 | return -ENOMEM; |
744 | } | 774 | } |
775 | return 0; | ||
776 | } | ||
777 | |||
778 | static int __init dynamic_debug_init(void) | ||
779 | { | ||
780 | struct _ddebug *iter, *iter_start; | ||
781 | const char *modname = NULL; | ||
782 | int ret = 0; | ||
783 | int n = 0; | ||
784 | |||
745 | if (__start___verbose != __stop___verbose) { | 785 | if (__start___verbose != __stop___verbose) { |
746 | iter = __start___verbose; | 786 | iter = __start___verbose; |
747 | modname = iter->modname; | 787 | modname = iter->modname; |
@@ -759,12 +799,26 @@ static int __init dynamic_debug_init(void) | |||
759 | } | 799 | } |
760 | ret = ddebug_add_module(iter_start, n, modname); | 800 | ret = ddebug_add_module(iter_start, n, modname); |
761 | } | 801 | } |
802 | |||
803 | /* ddebug_query boot param got passed -> set it up */ | ||
804 | if (ddebug_setup_string[0] != '\0') { | ||
805 | ret = ddebug_exec_query(ddebug_setup_string); | ||
806 | if (ret) | ||
807 | pr_warning("Invalid ddebug boot param %s", | ||
808 | ddebug_setup_string); | ||
809 | else | ||
810 | pr_info("ddebug initialized with string %s", | ||
811 | ddebug_setup_string); | ||
812 | } | ||
813 | |||
762 | out_free: | 814 | out_free: |
763 | if (ret) { | 815 | if (ret) |
764 | ddebug_remove_all_tables(); | 816 | ddebug_remove_all_tables(); |
765 | debugfs_remove(dir); | 817 | else |
766 | debugfs_remove(file); | 818 | ddebug_init_success = 1; |
767 | } | ||
768 | return 0; | 819 | return 0; |
769 | } | 820 | } |
770 | module_init(dynamic_debug_init); | 821 | /* Allow early initialization for boot messages via boot param */ |
822 | arch_initcall(dynamic_debug_init); | ||
823 | /* Debugfs setup must be done later */ | ||
824 | module_init(dynamic_debug_init_debugfs); | ||