driver core: basic infrastructure for per-module dynamic debug messages

Base infrastructure to enable per-module debug messages.

I've introduced CONFIG_DYNAMIC_PRINTK_DEBUG, which when enabled centralizes
control of debugging statements on a per-module basis in one /proc file,
currently, <debugfs>/dynamic_printk/modules. When, CONFIG_DYNAMIC_PRINTK_DEBUG,
is not set, debugging statements can still be enabled as before, often by
defining 'DEBUG' for the proper compilation unit. Thus, this patch set has no
affect when CONFIG_DYNAMIC_PRINTK_DEBUG is not set.

The infrastructure currently ties into all pr_debug() and dev_dbg() calls. That
is, if CONFIG_DYNAMIC_PRINTK_DEBUG is set, all pr_debug() and dev_dbg() calls
can be dynamically enabled/disabled on a per-module basis.

Future plans include extending this functionality to subsystems, that define 
their own debug levels and flags.

Usage:

Dynamic debugging is controlled by the debugfs file, 
<debugfs>/dynamic_printk/modules. This file contains a list of the modules that
can be enabled. The format of the file is as follows:

	<module_name> <enabled=0/1>
		.
		.
		.

	<module_name> : Name of the module in which the debug call resides
	<enabled=0/1> : whether the messages are enabled or not

For example:

	snd_hda_intel enabled=0
	fixup enabled=1
	driver enabled=0

Enable a module:

	$echo "set enabled=1 <module_name>" > dynamic_printk/modules

Disable a module:

	$echo "set enabled=0 <module_name>" > dynamic_printk/modules

Enable all modules:

	$echo "set enabled=1 all" > dynamic_printk/modules

Disable all modules:

	$echo "set enabled=0 all" > dynamic_printk/modules

Finally, passing "dynamic_printk" at the command line enables
debugging for all modules. This mode can be turned off via the above
disable command.

[gkh: minor cleanups and tweaks to make the build work quietly]

Signed-off-by: Jason Baron <jbaron@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
Jason Baron 2008-08-12 16:46:19 -04:00 committed by Greg Kroah-Hartman
parent 33376c1c04
commit 346e15beb5
14 changed files with 700 additions and 7 deletions

View file

@ -1713,6 +1713,11 @@ and is between 256 and 4096 characters. It is defined in the file
autoconfiguration. autoconfiguration.
Ranges are in pairs (memory base and size). Ranges are in pairs (memory base and size).
dynamic_printk
Enables pr_debug()/dev_dbg() calls if
CONFIG_DYNAMIC_PRINTK_DEBUG has been enabled. These can also
be switched on/off via <debugfs>/dynamic_printk/modules
print-fatal-signals= print-fatal-signals=
[KNL] debug: print fatal signals [KNL] debug: print fatal signals
print-fatal-signals=1: print segfault info to print-fatal-signals=1: print segfault info to

View file

@ -268,7 +268,15 @@
CPU_DISCARD(init.data) \ CPU_DISCARD(init.data) \
CPU_DISCARD(init.rodata) \ CPU_DISCARD(init.rodata) \
MEM_DISCARD(init.data) \ MEM_DISCARD(init.data) \
MEM_DISCARD(init.rodata) MEM_DISCARD(init.rodata) \
/* implement dynamic printk debug */ \
VMLINUX_SYMBOL(__start___verbose_strings) = .; \
*(__verbose_strings) \
VMLINUX_SYMBOL(__stop___verbose_strings) = .; \
. = ALIGN(8); \
VMLINUX_SYMBOL(__start___verbose) = .; \
*(__verbose) \
VMLINUX_SYMBOL(__stop___verbose) = .;
#define INIT_TEXT \ #define INIT_TEXT \
*(.init.text) \ *(.init.text) \

View file

@ -550,7 +550,11 @@ extern const char *dev_driver_string(const struct device *dev);
#define dev_info(dev, format, arg...) \ #define dev_info(dev, format, arg...) \
dev_printk(KERN_INFO , dev , format , ## arg) dev_printk(KERN_INFO , dev , format , ## arg)
#ifdef DEBUG #if defined(CONFIG_DYNAMIC_PRINTK_DEBUG)
#define dev_dbg(dev, format, ...) do { \
dynamic_dev_dbg(dev, format, ##__VA_ARGS__); \
} while (0)
#elif defined(DEBUG)
#define dev_dbg(dev, format, arg...) \ #define dev_dbg(dev, format, arg...) \
dev_printk(KERN_DEBUG , dev , format , ## arg) dev_printk(KERN_DEBUG , dev , format , ## arg)
#else #else

View file

@ -0,0 +1,93 @@
#ifndef _DYNAMIC_PRINTK_H
#define _DYNAMIC_PRINTK_H
#define DYNAMIC_DEBUG_HASH_BITS 6
#define DEBUG_HASH_TABLE_SIZE (1 << DYNAMIC_DEBUG_HASH_BITS)
#define TYPE_BOOLEAN 1
#define DYNAMIC_ENABLED_ALL 0
#define DYNAMIC_ENABLED_NONE 1
#define DYNAMIC_ENABLED_SOME 2
extern int dynamic_enabled;
/* dynamic_printk_enabled, and dynamic_printk_enabled2 are bitmasks in which
* bit n is set to 1 if any modname hashes into the bucket n, 0 otherwise. They
* use independent hash functions, to reduce the chance of false positives.
*/
extern long long dynamic_printk_enabled;
extern long long dynamic_printk_enabled2;
struct mod_debug {
char *modname;
char *logical_modname;
char *flag_names;
int type;
int hash;
int hash2;
} __attribute__((aligned(8)));
int register_dynamic_debug_module(char *mod_name, int type, char *share_name,
char *flags, int hash, int hash2);
#if defined(CONFIG_DYNAMIC_PRINTK_DEBUG)
extern int unregister_dynamic_debug_module(char *mod_name);
extern int __dynamic_dbg_enabled_helper(char *modname, int type,
int value, int hash);
#define __dynamic_dbg_enabled(module, type, value, level, hash) ({ \
int __ret = 0; \
if (unlikely((dynamic_printk_enabled & (1LL << DEBUG_HASH)) && \
(dynamic_printk_enabled2 & (1LL << DEBUG_HASH2)))) \
__ret = __dynamic_dbg_enabled_helper(module, type, \
value, hash);\
__ret; })
#define dynamic_pr_debug(fmt, ...) do { \
static char mod_name[] \
__attribute__((section("__verbose_strings"))) \
= KBUILD_MODNAME; \
static struct mod_debug descriptor \
__used \
__attribute__((section("__verbose"), aligned(8))) = \
{ mod_name, mod_name, NULL, TYPE_BOOLEAN, DEBUG_HASH, DEBUG_HASH2 };\
if (__dynamic_dbg_enabled(KBUILD_MODNAME, TYPE_BOOLEAN, \
0, 0, DEBUG_HASH)) \
printk(KERN_DEBUG KBUILD_MODNAME ":" fmt, \
##__VA_ARGS__); \
} while (0)
#define dynamic_dev_dbg(dev, format, ...) do { \
static char mod_name[] \
__attribute__((section("__verbose_strings"))) \
= KBUILD_MODNAME; \
static struct mod_debug descriptor \
__used \
__attribute__((section("__verbose"), aligned(8))) = \
{ mod_name, mod_name, NULL, TYPE_BOOLEAN, DEBUG_HASH, DEBUG_HASH2 };\
if (__dynamic_dbg_enabled(KBUILD_MODNAME, TYPE_BOOLEAN, \
0, 0, DEBUG_HASH)) \
dev_printk(KERN_DEBUG, dev, \
KBUILD_MODNAME ": " format, \
##__VA_ARGS__); \
} while (0)
#else
static inline int unregister_dynamic_debug_module(const char *mod_name)
{
return 0;
}
static inline int __dynamic_dbg_enabled_helper(char *modname, int type,
int value, int hash)
{
return 0;
}
#define __dynamic_dbg_enabled(module, type, value, level, hash) ({ 0; })
#define dynamic_pr_debug(fmt, ...) do { } while (0)
#define dynamic_dev_dbg(dev, format, ...) do { } while (0)
#endif
#endif

View file

@ -16,6 +16,7 @@
#include <linux/log2.h> #include <linux/log2.h>
#include <linux/typecheck.h> #include <linux/typecheck.h>
#include <linux/ratelimit.h> #include <linux/ratelimit.h>
#include <linux/dynamic_printk.h>
#include <asm/byteorder.h> #include <asm/byteorder.h>
#include <asm/bug.h> #include <asm/bug.h>
@ -303,8 +304,12 @@ static inline char *pack_hex_byte(char *buf, u8 byte)
#define pr_info(fmt, arg...) \ #define pr_info(fmt, arg...) \
printk(KERN_INFO fmt, ##arg) printk(KERN_INFO fmt, ##arg)
#ifdef DEBUG
/* If you are writing a driver, please use dev_dbg instead */ /* If you are writing a driver, please use dev_dbg instead */
#if defined(CONFIG_DYNAMIC_PRINTK_DEBUG)
#define pr_debug(fmt, ...) do { \
dynamic_pr_debug(fmt, ##__VA_ARGS__); \
} while (0)
#elif defined(DEBUG)
#define pr_debug(fmt, arg...) \ #define pr_debug(fmt, arg...) \
printk(KERN_DEBUG fmt, ##arg) printk(KERN_DEBUG fmt, ##arg)
#else #else

View file

@ -345,7 +345,6 @@ struct module
/* Reference counts */ /* Reference counts */
struct module_ref ref[NR_CPUS]; struct module_ref ref[NR_CPUS];
#endif #endif
}; };
#ifndef MODULE_ARCH_INIT #ifndef MODULE_ARCH_INIT
#define MODULE_ARCH_INIT {} #define MODULE_ARCH_INIT {}

View file

@ -784,6 +784,7 @@ sys_delete_module(const char __user *name_user, unsigned int flags)
mutex_lock(&module_mutex); mutex_lock(&module_mutex);
/* Store the name of the last unloaded module for diagnostic purposes */ /* Store the name of the last unloaded module for diagnostic purposes */
strlcpy(last_unloaded_module, mod->name, sizeof(last_unloaded_module)); strlcpy(last_unloaded_module, mod->name, sizeof(last_unloaded_module));
unregister_dynamic_debug_module(mod->name);
free_module(mod); free_module(mod);
out: out:
@ -1783,6 +1784,33 @@ static inline void add_kallsyms(struct module *mod,
} }
#endif /* CONFIG_KALLSYMS */ #endif /* CONFIG_KALLSYMS */
#ifdef CONFIG_DYNAMIC_PRINTK_DEBUG
static void dynamic_printk_setup(Elf_Shdr *sechdrs, unsigned int verboseindex)
{
struct mod_debug *debug_info;
unsigned long pos, end;
unsigned int num_verbose;
pos = sechdrs[verboseindex].sh_addr;
num_verbose = sechdrs[verboseindex].sh_size /
sizeof(struct mod_debug);
end = pos + (num_verbose * sizeof(struct mod_debug));
for (; pos < end; pos += sizeof(struct mod_debug)) {
debug_info = (struct mod_debug *)pos;
register_dynamic_debug_module(debug_info->modname,
debug_info->type, debug_info->logical_modname,
debug_info->flag_names, debug_info->hash,
debug_info->hash2);
}
}
#else
static inline void dynamic_printk_setup(Elf_Shdr *sechdrs,
unsigned int verboseindex)
{
}
#endif /* CONFIG_DYNAMIC_PRINTK_DEBUG */
static void *module_alloc_update_bounds(unsigned long size) static void *module_alloc_update_bounds(unsigned long size)
{ {
void *ret = module_alloc(size); void *ret = module_alloc(size);
@ -1831,6 +1859,7 @@ static noinline struct module *load_module(void __user *umod,
#endif #endif
unsigned int markersindex; unsigned int markersindex;
unsigned int markersstringsindex; unsigned int markersstringsindex;
unsigned int verboseindex;
struct module *mod; struct module *mod;
long err = 0; long err = 0;
void *percpu = NULL, *ptr = NULL; /* Stops spurious gcc warning */ void *percpu = NULL, *ptr = NULL; /* Stops spurious gcc warning */
@ -2117,6 +2146,7 @@ static noinline struct module *load_module(void __user *umod,
markersindex = find_sec(hdr, sechdrs, secstrings, "__markers"); markersindex = find_sec(hdr, sechdrs, secstrings, "__markers");
markersstringsindex = find_sec(hdr, sechdrs, secstrings, markersstringsindex = find_sec(hdr, sechdrs, secstrings,
"__markers_strings"); "__markers_strings");
verboseindex = find_sec(hdr, sechdrs, secstrings, "__verbose");
/* Now do relocations. */ /* Now do relocations. */
for (i = 1; i < hdr->e_shnum; i++) { for (i = 1; i < hdr->e_shnum; i++) {
@ -2167,6 +2197,7 @@ static noinline struct module *load_module(void __user *umod,
marker_update_probe_range(mod->markers, marker_update_probe_range(mod->markers,
mod->markers + mod->num_markers); mod->markers + mod->num_markers);
#endif #endif
dynamic_printk_setup(sechdrs, verboseindex);
err = module_finalize(hdr, sechdrs, mod); err = module_finalize(hdr, sechdrs, mod);
if (err < 0) if (err < 0)
goto cleanup; goto cleanup;

View file

@ -807,6 +807,61 @@ menuconfig BUILD_DOCSRC
Say N if you are unsure. Say N if you are unsure.
config DYNAMIC_PRINTK_DEBUG
bool "Enable dynamic printk() call support"
default n
depends on PRINTK
select PRINTK_DEBUG
help
Compiles debug level messages into the kernel, which would not
otherwise be available at runtime. These messages can then be
enabled/disabled on a per module basis. This mechanism implicitly
enables all pr_debug() and dev_dbg() calls. The impact of this
compile option is a larger kernel text size of about 2%.
Usage:
Dynamic debugging is controlled by the debugfs file,
dynamic_printk/modules. This file contains a list of the modules that
can be enabled. The format of the file is the module name, followed
by a set of flags that can be enabled. The first flag is always the
'enabled' flag. For example:
<module_name> <enabled=0/1>
.
.
.
<module_name> : Name of the module in which the debug call resides
<enabled=0/1> : whether the messages are enabled or not
From a live system:
snd_hda_intel enabled=0
fixup enabled=0
driver enabled=0
Enable a module:
$echo "set enabled=1 <module_name>" > dynamic_printk/modules
Disable a module:
$echo "set enabled=0 <module_name>" > dynamic_printk/modules
Enable all modules:
$echo "set enabled=1 all" > dynamic_printk/modules
Disable all modules:
$echo "set enabled=0 all" > dynamic_printk/modules
Finally, passing "dynamic_printk" at the command line enables
debugging for all modules. This mode can be turned off via the above
disable command.
source "samples/Kconfig" source "samples/Kconfig"
source "lib/Kconfig.kgdb" source "lib/Kconfig.kgdb"

View file

@ -81,6 +81,8 @@ obj-$(CONFIG_HAVE_LMB) += lmb.o
obj-$(CONFIG_HAVE_ARCH_TRACEHOOK) += syscall.o obj-$(CONFIG_HAVE_ARCH_TRACEHOOK) += syscall.o
obj-$(CONFIG_DYNAMIC_PRINTK_DEBUG) += dynamic_printk.o
hostprogs-y := gen_crc32table hostprogs-y := gen_crc32table
clean-files := crc32table.h clean-files := crc32table.h

418
lib/dynamic_printk.c Normal file
View file

@ -0,0 +1,418 @@
/*
* lib/dynamic_printk.c
*
* make pr_debug()/dev_dbg() calls runtime configurable based upon their
* their source module.
*
* Copyright (C) 2008 Red Hat, Inc., Jason Baron <jbaron@redhat.com>
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/uaccess.h>
#include <linux/seq_file.h>
#include <linux/debugfs.h>
#include <linux/fs.h>
extern struct mod_debug __start___verbose[];
extern struct mod_debug __stop___verbose[];
struct debug_name {
struct hlist_node hlist;
struct hlist_node hlist2;
int hash1;
int hash2;
char *name;
int enable;
int type;
};
static int nr_entries;
static int num_enabled;
int dynamic_enabled = DYNAMIC_ENABLED_NONE;
static struct hlist_head module_table[DEBUG_HASH_TABLE_SIZE] =
{ [0 ... DEBUG_HASH_TABLE_SIZE-1] = HLIST_HEAD_INIT };
static struct hlist_head module_table2[DEBUG_HASH_TABLE_SIZE] =
{ [0 ... DEBUG_HASH_TABLE_SIZE-1] = HLIST_HEAD_INIT };
static DECLARE_MUTEX(debug_list_mutex);
/* dynamic_printk_enabled, and dynamic_printk_enabled2 are bitmasks in which
* bit n is set to 1 if any modname hashes into the bucket n, 0 otherwise. They
* use independent hash functions, to reduce the chance of false positives.
*/
long long dynamic_printk_enabled;
EXPORT_SYMBOL_GPL(dynamic_printk_enabled);
long long dynamic_printk_enabled2;
EXPORT_SYMBOL_GPL(dynamic_printk_enabled2);
/* returns the debug module pointer. */
static struct debug_name *find_debug_module(char *module_name)
{
int i;
struct hlist_head *head;
struct hlist_node *node;
struct debug_name *element;
element = NULL;
for (i = 0; i < DEBUG_HASH_TABLE_SIZE; i++) {
head = &module_table[i];
hlist_for_each_entry_rcu(element, node, head, hlist)
if (!strcmp(element->name, module_name))
return element;
}
return NULL;
}
/* returns the debug module pointer. */
static struct debug_name *find_debug_module_hash(char *module_name, int hash)
{
struct hlist_head *head;
struct hlist_node *node;
struct debug_name *element;
element = NULL;
head = &module_table[hash];
hlist_for_each_entry_rcu(element, node, head, hlist)
if (!strcmp(element->name, module_name))
return element;
return NULL;
}
/* caller must hold mutex*/
static int __add_debug_module(char *mod_name, int hash, int hash2)
{
struct debug_name *new;
char *module_name;
int ret = 0;
if (find_debug_module(mod_name)) {
ret = -EINVAL;
goto out;
}
module_name = kmalloc(strlen(mod_name) + 1, GFP_KERNEL);
if (!module_name) {
ret = -ENOMEM;
goto out;
}
module_name = strcpy(module_name, mod_name);
module_name[strlen(mod_name)] = '\0';
new = kzalloc(sizeof(struct debug_name), GFP_KERNEL);
if (!new) {
kfree(module_name);
ret = -ENOMEM;
goto out;
}
INIT_HLIST_NODE(&new->hlist);
INIT_HLIST_NODE(&new->hlist2);
new->name = module_name;
new->hash1 = hash;
new->hash2 = hash2;
hlist_add_head_rcu(&new->hlist, &module_table[hash]);
hlist_add_head_rcu(&new->hlist2, &module_table2[hash2]);
nr_entries++;
out:
return ret;
}
int unregister_dynamic_debug_module(char *mod_name)
{
struct debug_name *element;
int ret = 0;
down(&debug_list_mutex);
element = find_debug_module(mod_name);
if (!element) {
ret = -EINVAL;
goto out;
}
hlist_del_rcu(&element->hlist);
hlist_del_rcu(&element->hlist2);
synchronize_rcu();
kfree(element->name);
if (element->enable)
num_enabled--;
kfree(element);
nr_entries--;
out:
up(&debug_list_mutex);
return 0;
}
EXPORT_SYMBOL_GPL(unregister_dynamic_debug_module);
int register_dynamic_debug_module(char *mod_name, int type, char *share_name,
char *flags, int hash, int hash2)
{
struct debug_name *elem;
int ret = 0;
down(&debug_list_mutex);
elem = find_debug_module(mod_name);
if (!elem) {
if (__add_debug_module(mod_name, hash, hash2))
goto out;
elem = find_debug_module(mod_name);
if (dynamic_enabled == DYNAMIC_ENABLED_ALL &&
!strcmp(mod_name, share_name)) {
elem->enable = true;
num_enabled++;
}
}
elem->type |= type;
out:
up(&debug_list_mutex);
return ret;
}
EXPORT_SYMBOL_GPL(register_dynamic_debug_module);
int __dynamic_dbg_enabled_helper(char *mod_name, int type, int value, int hash)
{
struct debug_name *elem;
int ret = 0;
if (dynamic_enabled == DYNAMIC_ENABLED_ALL)
return 1;
rcu_read_lock();
elem = find_debug_module_hash(mod_name, hash);
if (elem && elem->enable)
ret = 1;
rcu_read_unlock();
return ret;
}
EXPORT_SYMBOL_GPL(__dynamic_dbg_enabled_helper);
static void set_all(bool enable)
{
struct debug_name *e;
struct hlist_node *node;
int i;
long long enable_mask;
for (i = 0; i < DEBUG_HASH_TABLE_SIZE; i++) {
if (module_table[i].first != NULL) {
hlist_for_each_entry(e, node, &module_table[i], hlist) {
e->enable = enable;
}
}
}
if (enable)
enable_mask = ULLONG_MAX;
else
enable_mask = 0;
dynamic_printk_enabled = enable_mask;
dynamic_printk_enabled2 = enable_mask;
}
static int disabled_hash(int i, bool first_table)
{
struct debug_name *e;
struct hlist_node *node;
if (first_table) {
hlist_for_each_entry(e, node, &module_table[i], hlist) {
if (e->enable)
return 0;
}
} else {
hlist_for_each_entry(e, node, &module_table2[i], hlist2) {
if (e->enable)
return 0;
}
}
return 1;
}
static ssize_t pr_debug_write(struct file *file, const char __user *buf,
size_t length, loff_t *ppos)
{
char *buffer, *s, *value_str, *setting_str;
int err, value;
struct debug_name *elem = NULL;
int all = 0;
if (length > PAGE_SIZE || length < 0)
return -EINVAL;
buffer = (char *)__get_free_page(GFP_KERNEL);
if (!buffer)
return -ENOMEM;
err = -EFAULT;
if (copy_from_user(buffer, buf, length))
goto out;
err = -EINVAL;
if (length < PAGE_SIZE)
buffer[length] = '\0';
else if (buffer[PAGE_SIZE-1])
goto out;
err = -EINVAL;
down(&debug_list_mutex);
if (strncmp("set", buffer, 3))
goto out_up;
s = buffer + 3;
setting_str = strsep(&s, "=");
if (s == NULL)
goto out_up;
setting_str = strstrip(setting_str);
value_str = strsep(&s, " ");
if (s == NULL)
goto out_up;
s = strstrip(s);
if (!strncmp(s, "all", 3))
all = 1;
else
elem = find_debug_module(s);
if (!strncmp(setting_str, "enable", 6)) {
value = !!simple_strtol(value_str, NULL, 10);
if (all) {
if (value) {
set_all(true);
num_enabled = nr_entries;
dynamic_enabled = DYNAMIC_ENABLED_ALL;
} else {
set_all(false);
num_enabled = 0;
dynamic_enabled = DYNAMIC_ENABLED_NONE;
}
err = 0;
} else {
if (elem) {
if (value && (elem->enable == 0)) {
dynamic_printk_enabled |=
(1LL << elem->hash1);
dynamic_printk_enabled2 |=
(1LL << elem->hash2);
elem->enable = 1;
num_enabled++;
dynamic_enabled = DYNAMIC_ENABLED_SOME;
err = 0;
printk(KERN_DEBUG
"debugging enabled for module %s",
elem->name);
} else if (!value && (elem->enable == 1)) {
elem->enable = 0;
num_enabled--;
if (disabled_hash(elem->hash1, true))
dynamic_printk_enabled &=
~(1LL << elem->hash1);
if (disabled_hash(elem->hash2, false))
dynamic_printk_enabled2 &=
~(1LL << elem->hash2);
if (num_enabled)
dynamic_enabled =
DYNAMIC_ENABLED_SOME;
else
dynamic_enabled =
DYNAMIC_ENABLED_NONE;
err = 0;
printk(KERN_DEBUG
"debugging disabled for module "
"%s", elem->name);
}
}
}
}
if (!err)
err = length;
out_up:
up(&debug_list_mutex);
out:
free_page((unsigned long)buffer);
return err;
}
static void *pr_debug_seq_start(struct seq_file *f, loff_t *pos)
{
return (*pos < DEBUG_HASH_TABLE_SIZE) ? pos : NULL;
}
static void *pr_debug_seq_next(struct seq_file *s, void *v, loff_t *pos)
{
(*pos)++;
if (*pos >= DEBUG_HASH_TABLE_SIZE)
return NULL;
return pos;
}
static void pr_debug_seq_stop(struct seq_file *s, void *v)
{
/* Nothing to do */
}
static int pr_debug_seq_show(struct seq_file *s, void *v)
{
struct hlist_head *head;
struct hlist_node *node;
struct debug_name *elem;
unsigned int i = *(loff_t *) v;
rcu_read_lock();
head = &module_table[i];
hlist_for_each_entry_rcu(elem, node, head, hlist) {
seq_printf(s, "%s enabled=%d", elem->name, elem->enable);
seq_printf(s, "\n");
}
rcu_read_unlock();
return 0;
}
static struct seq_operations pr_debug_seq_ops = {
.start = pr_debug_seq_start,
.next = pr_debug_seq_next,
.stop = pr_debug_seq_stop,
.show = pr_debug_seq_show
};
static int pr_debug_open(struct inode *inode, struct file *filp)
{
return seq_open(filp, &pr_debug_seq_ops);
}
static const struct file_operations pr_debug_operations = {
.open = pr_debug_open,
.read = seq_read,
.write = pr_debug_write,
.llseek = seq_lseek,
.release = seq_release,
};
static int __init dynamic_printk_init(void)
{
struct dentry *dir, *file;
struct mod_debug *iter;
unsigned long value;
dir = debugfs_create_dir("dynamic_printk", NULL);
if (!dir)
return -ENOMEM;
file = debugfs_create_file("modules", 0644, dir, NULL,
&pr_debug_operations);
if (!file) {
debugfs_remove(dir);
return -ENOMEM;
}
for (value = (unsigned long)__start___verbose;
value < (unsigned long)__stop___verbose;
value += sizeof(struct mod_debug)) {
iter = (struct mod_debug *)value;
register_dynamic_debug_module(iter->modname,
iter->type,
iter->logical_modname,
iter->flag_names, iter->hash, iter->hash2);
}
return 0;
}
module_init(dynamic_printk_init);
/* may want to move this earlier so we can get traces as early as possible */
static int __init dynamic_printk_setup(char *str)
{
if (str)
return -ENOENT;
set_all(true);
return 0;
}
/* Use early_param(), so we can get debug output as early as possible */
early_param("dynamic_printk", dynamic_printk_setup);

View file

@ -65,7 +65,7 @@ void
struct nf_conntrack_expect *exp) __read_mostly; struct nf_conntrack_expect *exp) __read_mostly;
EXPORT_SYMBOL_GPL(nf_nat_pptp_hook_expectfn); EXPORT_SYMBOL_GPL(nf_nat_pptp_hook_expectfn);
#ifdef DEBUG #if defined(DEBUG) || defined(CONFIG_DYNAMIC_PRINTK_DEBUG)
/* PptpControlMessageType names */ /* PptpControlMessageType names */
const char *const pptp_msg_name[] = { const char *const pptp_msg_name[] = {
"UNKNOWN_MESSAGE", "UNKNOWN_MESSAGE",

View file

@ -96,6 +96,14 @@ basename_flags = -D"KBUILD_BASENAME=KBUILD_STR($(call name-fix,$(basetarget)))"
modname_flags = $(if $(filter 1,$(words $(modname))),\ modname_flags = $(if $(filter 1,$(words $(modname))),\
-D"KBUILD_MODNAME=KBUILD_STR($(call name-fix,$(modname)))") -D"KBUILD_MODNAME=KBUILD_STR($(call name-fix,$(modname)))")
#hash values
ifdef CONFIG_DYNAMIC_PRINTK_DEBUG
debug_flags = -D"DEBUG_HASH=$(shell ./scripts/basic/hash djb2 $(@D)$(modname))"\
-D"DEBUG_HASH2=$(shell ./scripts/basic/hash r5 $(@D)$(modname))"
else
debug_flags =
endif
orig_c_flags = $(KBUILD_CFLAGS) $(ccflags-y) $(CFLAGS_$(basetarget).o) orig_c_flags = $(KBUILD_CFLAGS) $(ccflags-y) $(CFLAGS_$(basetarget).o)
_c_flags = $(filter-out $(CFLAGS_REMOVE_$(basetarget).o), $(orig_c_flags)) _c_flags = $(filter-out $(CFLAGS_REMOVE_$(basetarget).o), $(orig_c_flags))
_a_flags = $(KBUILD_AFLAGS) $(asflags-y) $(AFLAGS_$(basetarget).o) _a_flags = $(KBUILD_AFLAGS) $(asflags-y) $(AFLAGS_$(basetarget).o)
@ -121,7 +129,8 @@ endif
c_flags = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(KBUILD_CPPFLAGS) \ c_flags = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(KBUILD_CPPFLAGS) \
$(__c_flags) $(modkern_cflags) \ $(__c_flags) $(modkern_cflags) \
-D"KBUILD_STR(s)=\#s" $(basename_flags) $(modname_flags) -D"KBUILD_STR(s)=\#s" $(basename_flags) $(modname_flags) \
$(debug_flags)
a_flags = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(KBUILD_CPPFLAGS) \ a_flags = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(KBUILD_CPPFLAGS) \
$(__a_flags) $(modkern_aflags) $(__a_flags) $(modkern_aflags)

View file

@ -9,7 +9,7 @@
# fixdep: Used to generate dependency information during build process # fixdep: Used to generate dependency information during build process
# docproc: Used in Documentation/DocBook # docproc: Used in Documentation/DocBook
hostprogs-y := fixdep docproc hostprogs-y := fixdep docproc hash
always := $(hostprogs-y) always := $(hostprogs-y)
# fixdep is needed to compile other host programs # fixdep is needed to compile other host programs

64
scripts/basic/hash.c Normal file
View file

@ -0,0 +1,64 @@
/*
* Copyright (C) 2008 Red Hat, Inc., Jason Baron <jbaron@redhat.com>
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define DYNAMIC_DEBUG_HASH_BITS 6
static const char *program;
static void usage(void)
{
printf("Usage: %s <djb2|r5> <modname>\n", program);
exit(1);
}
/* djb2 hashing algorithm by Dan Bernstein. From:
* http://www.cse.yorku.ca/~oz/hash.html
*/
unsigned int djb2_hash(char *str)
{
unsigned long hash = 5381;
int c;
c = *str;
while (c) {
hash = ((hash << 5) + hash) + c;
c = *++str;
}
return (unsigned int)(hash & ((1 << DYNAMIC_DEBUG_HASH_BITS) - 1));
}
unsigned int r5_hash(char *str)
{
unsigned long hash = 0;
int c;
c = *str;
while (c) {
hash = (hash + (c << 4) + (c >> 4)) * 11;
c = *++str;
}
return (unsigned int)(hash & ((1 << DYNAMIC_DEBUG_HASH_BITS) - 1));
}
int main(int argc, char *argv[])
{
program = argv[0];
if (argc != 3)
usage();
if (!strcmp(argv[1], "djb2"))
printf("%d\n", djb2_hash(argv[2]));
else if (!strcmp(argv[1], "r5"))
printf("%d\n", r5_hash(argv[2]));
else
usage();
exit(0);
}