aboutsummaryrefslogtreecommitdiffstats
path: root/lib/dynamic_debug.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dynamic_debug.c')
-rw-r--r--lib/dynamic_debug.c206
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
30extern struct _ddebug __start___verbose[]; 34extern struct _ddebug __start___verbose[];
31extern struct _ddebug __stop___verbose[]; 35extern 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 */
37long long dynamic_debug_enabled;
38EXPORT_SYMBOL_GPL(dynamic_debug_enabled);
39long long dynamic_debug_enabled2;
40EXPORT_SYMBOL_GPL(dynamic_debug_enabled2);
41
42struct ddebug_table { 37struct 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
69static 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 */
75static char *ddebug_describe_flags(struct _ddebug *dp, char *buf, 78static 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
94static 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
409static 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
430int __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}
457EXPORT_SYMBOL(__dynamic_pr_debug);
458
459static __initdata char ddebug_setup_string[1024];
460static __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,
436static ssize_t ddebug_proc_write(struct file *file, const char __user *ubuf, 476static 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
728static int __init dynamic_debug_init(void) 757static __initdata int ddebug_init_success;
758
759static 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
778static 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
762out_free: 814out_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}
770module_init(dynamic_debug_init); 821/* Allow early initialization for boot messages via boot param */
822arch_initcall(dynamic_debug_init);
823/* Debugfs setup must be done later */
824module_init(dynamic_debug_init_debugfs);