Skip to contentSkip to conversation

dynamic debug - core infrastructure

PATCH In review Jason Baron 4 participants 7 messages Jun 13, 2008 – Jun 21, 2008

Subsystems (7)
lore
Share & tools
lore
#0 lore raw

PATCH 2/8 dynamic debug - core infrastructure

This is the core patch that implements a new dynamic debug infrastructure.

Each kernel sub-system seems to have its own way of dealing with debugging statements. Some of these methods include 'dprintk', 'pr_debug', 'dev_debug', 'DEBUGP'. There are also a myriad of ways of enabling these statements. Some require CONFIG_ parameters to be set, some provide /proc interfaces, while others require 'DEBUG' to be set in header files. Most of these methods also require kernel re-compiles in order to set the appropriate knobs.

I've attempted to standardize how how the kernel deals with debugging statements, both from the user as well as the kernel perspective. Why aren't these debug statements made into printk(KERN_DEBUG), and be done with it? The problem here is that 'dprintk', 'pr_debug', etc. were introduced b/c they are often provide too verbose a level of output, could potentially adversly affect system performance if they were always enabled, and they clutter up the system logs.

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 not affect when CONFIG_DYNAMIC_PRINTK_DEBUG is not set. The rest of the discussion deals with the case where CONFIG_DYNAMIC_PRINTK_DEBUG is set.

---

 Documentation/kernel-parameters.txt |    5 
 include/asm-generic/vmlinux.lds.h   |   10 +
 include/linux/device.h              |   16 +
 include/linux/dynamic_printk.h      |   49 +++
 include/linux/kernel.h              |   78 +++++-
 include/linux/module.h              |    5 
 kernel/module.c                     |   24 ++
 lib/Kconfig.debug                   |   67 +++++
 lib/Makefile                        |    2 
 lib/dynamic_printk.c                |  492 +++++++++++++++++++++++++++++++++++
 net/netfilter/nf_conntrack_pptp.c   |    2 
 11 files changed, 744 insertions(+), 6 deletions(-)
 create mode 100644 include/linux/dynamic_printk.h
 create mode 100644 lib/dynamic_printk.c
Documentation/kernel-parameters.txt +5 -0
index bf6303e..d9a1245 100644
@@ -1580,6 +1580,11 @@ and is between 256 and 4096 characters. It is defined in the file
 			autoconfiguration.
 			Ranges are in pairs (memory base and size).
 
+	dynamic_printk
+			Enables pr_debug()/dev_dbg() calls if 
+			CONFIG_PRINK_DYNAMIC has been enabled. These can also 
+			be switched on/off via <debugfs>/dynamic_printk/modules
+
 	print-fatal-signals=
 			[KNL] debug: print fatal signals
 			print-fatal-signals=1: print segfault info to
include/asm-generic/vmlinux.lds.h +8 -2
index f054778..799a8b5 100644
@@ -167,7 +167,6 @@
         __ksymtab_strings : AT(ADDR(__ksymtab_strings) - LOAD_OFFSET) {	\
 		*(__ksymtab_strings)					\
 	}								\
-									\
 	/* __*init sections */						\
 	__init_rodata : AT(ADDR(__init_rodata) - LOAD_OFFSET) {		\
 		*(.ref.rodata)						\
@@ -249,7 +248,14 @@
 	CPU_DISCARD(init.data)						\
 	CPU_DISCARD(init.rodata)					\
 	MEM_DISCARD(init.data)						\
-	MEM_DISCARD(init.rodata)
+	MEM_DISCARD(init.rodata)					\
+	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							\
 	*(.init.text)							\
include/linux/device.h +16 -0
index 1a06026..9521f14 100644
@@ -595,6 +595,22 @@ extern const char *dev_driver_string(struct device *dev);
 #ifdef DEBUG
 #define dev_dbg(dev, format, arg...)		\
 	dev_printk(KERN_DEBUG , dev , format , ## arg)
+#elif defined(CONFIG_DYNAMIC_PRINTK_DEBUG)
+#define dev_dbg(dev, format, ...) do {				    	     \
+	static char mod_name[]						     \
+	__attribute__((section("__verbose_strings")))			     \
+	 = KBUILD_MODNAME;						     \
+	static struct mod_debug foobar					     \
+	__used								     \
+	__attribute__((section("__verbose"), aligned(8))) =		     \
+	{ mod_name, mod_name, "0", "0", NULL };				     \
+	int hash = dynamic_name_hash(KBUILD_MODNAME);			     \
+	if (dynamic_printk_enabled[raw_smp_processor_id()] & (1 << hash))    \
+		dynamic_printk(KBUILD_MODNAME,				     \
+				KERN_DEBUG KBUILD_MODNAME ": %s %s: " format,\
+				dev_driver_string(dev), (dev)->bus_id,	     \
+				##__VA_ARGS__);				     \
+	} while (0)
 #else
 #define dev_dbg(dev, format, arg...)		\
 	({ if (0) dev_printk(KERN_DEBUG, dev, format, ##arg); 0; })
include/linux/dynamic_printk.h new file +49 -0

49 lines not on this page · raw message

include/linux/kernel.h +75 -3

96 lines not on this page · raw message

include/linux/module.h +5 -0
index 819c4e8..267dba5 100644
@@ -16,6 +16,7 @@
 #include <linux/kobject.h>
 #include <linux/moduleparam.h>
 #include <linux/marker.h>
+#include <linux/dynamic_printk.h>
 #include <asm/local.h>
 
 #include <asm/module.h>
@@ -359,6 +360,10 @@ struct module
 	struct marker *markers;
 	unsigned int num_markers;
 #endif
+#ifdef CONFIG_DYNAMIC_PRINTK_DEBUG
+	struct mod_debug *start_verbose;
+	unsigned int num_verbose;
+#endif
 };
 #ifndef MODULE_ARCH_INIT
 #define MODULE_ARCH_INIT {}
kernel/module.c +24 -0
index 8d6cccc..b8a19bb 100644
@@ -42,6 +42,7 @@
 #include <linux/string.h>
 #include <linux/mutex.h>
 #include <linux/unwind.h>
+#include <linux/dynamic_printk.h>
 #include <asm/uaccess.h>
 #include <asm/cacheflush.h>
 #include <linux/license.h>
@@ -744,6 +745,7 @@ sys_delete_module(const char __user *name_user, unsigned int flags)
 	}
 	/* Store the name of the last unloaded module for diagnostic purposes */
 	strlcpy(last_unloaded_module, mod->name, sizeof(last_unloaded_module));
+	unregister_debug_module(mod->name);
 	free_module(mod);
 
  out:
@@ -1717,6 +1719,9 @@ static struct module *load_module(void __user *umod,
 	unsigned int unusedgplcrcindex;
 	unsigned int markersindex;
 	unsigned int markersstringsindex;
+	unsigned int verboseindex;
+	struct mod_debug *iter;
+	unsigned long value;
 	struct module *mod;
 	long err = 0;
 	void *percpu = NULL, *ptr = NULL; /* Stops spurious gcc warning */
@@ -1993,6 +1998,7 @@ static struct module *load_module(void __user *umod,
 	markersindex = find_sec(hdr, sechdrs, secstrings, "__markers");
  	markersstringsindex = find_sec(hdr, sechdrs, secstrings,
 					"__markers_strings");
+	verboseindex = find_sec(hdr, sechdrs, secstrings, "__verbose");
 
 	/* Now do relocations. */
 	for (i = 1; i < hdr->e_shnum; i++) {
@@ -2020,6 +2026,11 @@ static struct module *load_module(void __user *umod,
 	mod->num_markers =
 		sechdrs[markersindex].sh_size / sizeof(*mod->markers);
 #endif
+#ifdef CONFIG_DYNAMIC_PRINTK_DEBUG
+	mod->start_verbose = (void *)sechdrs[verboseindex].sh_addr;
+	mod->num_verbose = sechdrs[verboseindex].sh_size /
+				sizeof(*mod->start_verbose);
+#endif
 
         /* Find duplicate symbols */
 	err = verify_export_symbols(mod);
@@ -2043,6 +2054,19 @@ static struct module *load_module(void __user *umod,
 		marker_update_probe_range(mod->markers,
 			mod->markers + mod->num_markers);
 #endif
+#ifdef CONFIG_DYNAMIC_PRINTK_DEBUG
+	for (value = (unsigned long)mod->start_verbose;
+		value < (unsigned long)mod->start_verbose +
+		(unsigned long)(mod->num_verbose * sizeof(struct mod_debug));
+		value += sizeof(struct mod_debug)) {
+			iter = (struct mod_debug *)value;
+			register_debug_module(iter->modname,
+				simple_strtoul(iter->type, NULL, 10),
+				iter->logical_modname,
+				simple_strtoul(iter->num_flags, NULL, 10),
+				iter->flag_names);
+	}
+#endif
 	err = module_finalize(hdr, sechdrs, mod);
 	if (err < 0)
 		goto cleanup;
lib/Kconfig.debug +67 -0

73 lines not on this page · raw message

lib/Makefile +2 -0
index bf8000f..78ab656 100644
@@ -70,6 +70,8 @@ lib-$(CONFIG_GENERIC_BUG) += bug.o
 
 obj-$(CONFIG_HAVE_LMB) += lmb.o
 
+obj-$(CONFIG_DYNAMIC_PRINTK_DEBUG) += dynamic_printk.o
+
 hostprogs-y	:= gen_crc32table
 clean-files	:= crc32table.h
 
lib/dynamic_printk.c new file +492 -0

492 lines not on this page · raw message

net/netfilter/nf_conntrack_pptp.c +1 -1
index 97e54b0..7fea304 100644
@@ -65,7 +65,7 @@ void
 			     struct nf_conntrack_expect *exp) __read_mostly;
 EXPORT_SYMBOL_GPL(nf_nat_pptp_hook_expectfn);
 
-#ifdef DEBUG
+#if defined(DEBUG) || defined(CONFIG_DYNAMIC_PRINTK_DEBUG)
 /* PptpControlMessageType names */
 const char *const pptp_msg_name[] = {
 	"UNKNOWN_MESSAGE",
#1 lore raw
Quoted 2 lines · Jason Baron

This is the core patch that implements a new dynamic debug infrastructure.

Some general and specific comments.

Quoted 1 line

+int dynamic_printk_enabled[NR_CPUS];

I don't understand why you use NR_CPUS.

Also, I think the major use cases are 1 or 2 modules enabled, no modules enabled, or all modules enabled, so the hashing of module names is particularly not useful.

Any pr_debug or dev_dbg is in a slow path.

I think a list and a direct comparison to KBUILD_MODNAME would be simpler and as fast as necessary.

lib/Kconfig.debug config FIREWIRE_OHCI_REMOTE_DMA · line 690

Quoted 4 lines
lib/Kconfig.debug +4 -0
+         To set the level or flag value for type 'level' or 'flag': 
+
+               $echo "set level=<#> <module_name>" > dynamic_printk/modules
+

I think that set level=<#> should also work for "all"

Perhaps a simpler interface would be to use
enable <module> <level>
where <level> not specified is 0.

lib/dynamic_printk.c line 138

Quoted 13 lines
lib/dynamic_printk.c new file +13 -0
+int unregister_debug_module(char *mod_name)
+{
+	struct debug_name *element;
+	struct debug_name *parent = NULL;
+	int ret = 0;
+	int i;
+
+	down(&debug_list_mutex);
+	element = find_debug_module(mod_name);
+	if (!element) {
+		ret = -EINVAL;
+		goto out;
+	}

[]

lib/dynamic_printk.c line 170

Quoted 4 lines
lib/dynamic_printk.c new file +4 -0
+out:
+	up(&debug_list_mutex);
+	return 0;
+}

Because the return values aren't used,
the functions might as well be declared void

You should probably add a sprinkling of "const"
to the argument lists.

+extern void dynamic_printk(char *, char *, ...);

+ dynamic_printk(KBUILD_MODNAME, \
+ KERN_DEBUG KBUILD_MODNAME ": " fmt,\

Instead of prefixing every fmt with KBUILD_MODNAME ": ", why not change this to something like:

extern void dynamic_printk(const char *level, const char *module,

			   const char *fmt, ...);

and just do the printk in 2 parts?

	if (module_enabled) {
		printk("%s%s: ", level, module);
		vprintk(fmt, args);
	}

If not that, why should dynamic_printk prefix a KBUILD_MODNAME at all? Why not just check if the module is enabled, then output what the code specifies?

trivial: The dynamic versions of the dev_dbg and pr_debug macros don't return the number of chars output, but always return 0.

#2 lore raw
Quoted 10 lines · Joe Perches
Quoted 2 lines · Jason Baron

This is the core patch that implements a new dynamic debug infrastructure.

Some general and specific comments.

Quoted 1 line

+int dynamic_printk_enabled[NR_CPUS];

I don't understand why you use NR_CPUS.

Quoted 9 lines

Also, I think the major use cases are 1 or 2 modules enabled, no modules enabled, or all modules enabled, so the hashing of module names is particularly not useful.

Any pr_debug or dev_dbg is in a slow path.

I think a list and a direct comparison to KBUILD_MODNAME would be simpler and as fast as necessary.

NR_CPUS is used to set up a per-cpu int variable where bit 'n' is set if any enabled module's name hashes into bucket 'n'. Having a single linked list of the enabled modules would work, but i like just checking a bit in the common case. Like you pointed out, most modules are likely disabled which means in the common case, i'm just checking a bit, whereas in a linked list I'd be walking a small number of entries.

However, we can better optimize the case of no modules enabled or all modules enabled, by simply checking globals that indicate this state. I think that would be improvment. So dynamic_dbg_enabled() looks something like:

if (alloff)

	return 0;
elseif (allon)
	return 1;
elseif (if hash bucket of KBUILD_MODNAME is disabled)
	return 0;
else
	lookup KBUILD_MODNAME in hash table
	if (level or flag comparisons pass and enabled)
		return 1;
	else
		return 0;
Quoted 7 lines

lib/Kconfig.debug config FIREWIRE_OHCI_REMOTE_DMA · line 690

Quoted 4 lines
lib/Kconfig.debug +4 -0
+         To set the level or flag value for type 'level' or 'flag': 
+
+               $echo "set level=<#> <module_name>" > dynamic_printk/modules
+

I think that set level=<#> should also work for "all"

good idea.

Quoted 4 lines

Perhaps a simpler interface would be to use
enable <module> <level>
where <level> not specified is 0.

hmmm, there are also 'flags' that can be set for certain modules. Right now flags and level are set the same way, via 'set level=". However, i want flags to be able to be set via their name - "set FLAG_FOOBAR=1" or
"set FLAG_FOOBAR=0". I also like having this independent of whether the module is enabled or disabled.

Quoted 16 lines

lib/dynamic_printk.c line 138

Quoted 13 lines
lib/dynamic_printk.c new file +13 -0
+int unregister_debug_module(char *mod_name)
+{
+	struct debug_name *element;
+	struct debug_name *parent = NULL;
+	int ret = 0;
+	int i;
+
+	down(&debug_list_mutex);
+	element = find_debug_module(mod_name);
+	if (!element) {
+		ret = -EINVAL;
+		goto out;
+	}

[]

Quoted 8 lines

lib/dynamic_printk.c line 170

Quoted 4 lines
lib/dynamic_printk.c new file +4 -0
+out:
+	up(&debug_list_mutex);
+	return 0;
+}

Because the return values aren't used,
the functions might as well be declared void

ok

Quoted 3 lines

You should probably add a sprinkling of "const"
to the argument lists.

ok

Quoted 18 lines
include/linux/kernel.h +3 -0
+extern void dynamic_printk(char *, char *, ...);
 
+	dynamic_printk(KBUILD_MODNAME,                     \
+			KERN_DEBUG KBUILD_MODNAME ": " fmt,\

Instead of prefixing every fmt with KBUILD_MODNAME ": ", why not change this to something like:

extern void dynamic_printk(const char *level, const char *module,

			   const char *fmt, ...);

and just do the printk in 2 parts?

	if (module_enabled) {
		printk("%s%s: ", level, module);
		vprintk(fmt, args);
	}
Quoted 8 lines

If not that, why should dynamic_printk prefix a KBUILD_MODNAME at all? Why not just check if the module is enabled, then output what the code specifies?

trivial: The dynamic versions of the dev_dbg and pr_debug macros don't return the number of chars output, but always return 0.

good idea - i think pr_debug and dev_dbg should simply be implemented on top of dynamic_dbg_enabled(). I'll add that in the next spin.

thanks,

-Jason

#3 lore raw
In reply to Jason Baron
Quoted 5 lines · Jason Baron
include/linux/device.h +0 -0
@@ -595,6 +595,22 @@ extern const char *dev_driver_string(struct device *dev);
 #ifdef DEBUG
 #define dev_dbg(dev, format, arg...)		\

include/linux/device.h extern const char *dev_driver_string(struct device *dev); · line 606

Quoted 10 lines
include/linux/device.h +9 -0
 	dev_printk(KERN_DEBUG , dev , format , ## arg)
+#elif defined(CONFIG_DYNAMIC_PRINTK_DEBUG)
+#define dev_dbg(dev, format, ...) do {				    	     \
+	static char mod_name[]						     \
+	__attribute__((section("__verbose_strings")))			     \
+	 = KBUILD_MODNAME;						     \
+	static struct mod_debug foobar					     \
+	__used								     \
+	__attribute__((section("__verbose"), aligned(8))) =		     \
+	{ mod_name, mod_name, "0", "0", NULL };				     \

Does the compiler merge all of these variables together into one within the same file?

If not, should we have a new macro, one per file, for this information?

Quoted 11 lines
include/linux/kernel.h +1 -0
@@ -14,6 +14,7 @@
 #include <linux/compiler.h>
 #include <linux/bitops.h>
 #include <linux/log2.h>
+#include <linux/dynamic_printk.h>
 #include <asm/byteorder.h>
 #include <asm/bug.h>
 

include/linux/kernel.h extern void print_hex_dump_bytes(const char *prefix_str, int prefix_type, · line 292

Quoted 5 lines
include/linux/kernel.h +0 -2
@@ -288,15 +289,86 @@ extern void print_hex_dump_bytes(const char *prefix_str, int prefix_type,
 #define pr_info(fmt, arg...) \
 	printk(KERN_INFO fmt, ##arg)
 
-#ifdef DEBUG
-/* If you are writing a driver, please use dev_dbg instead */

Hm, this comment is still correct, please don't delete it :)

Other than those minor nits, and the fact that your patch has a lot of trailing whitespace, it's looking very good so far.

thanks,

greg k-h

#4 lore raw
Quoted 11 lines · Greg KH
Quoted 10 lines · Jason Baron
include/linux/device.h +4 -0
@@ -595,6 +595,22 @@ extern const char *dev_driver_string(struct device *dev);
 #ifdef DEBUG
 #define dev_dbg(dev, format, arg...)		\
 	dev_printk(KERN_DEBUG , dev , format , ## arg)
+#elif defined(CONFIG_DYNAMIC_PRINTK_DEBUG)
+#define dev_dbg(dev, format, ...) do {				    	     \
+	static char mod_name[]						     \
+	__attribute__((section("__verbose_strings")))			     \
Quoted 9 lines

include/linux/device.h extern const char *dev_driver_string(struct device *dev); · line 606

Quoted 5 lines
include/linux/device.h +5 -0
+	 = KBUILD_MODNAME;						     \
+	static struct mod_debug foobar					     \
+	__used								     \
+	__attribute__((section("__verbose"), aligned(8))) =		     \
+	{ mod_name, mod_name, "0", "0", NULL };				     \

Does the compiler merge all of these variables together into one within the same file?

no.

Quoted 2 lines

If not, should we have a new macro, one per file, for this information?

With this feature enabled, the above 'meta-data' added ~32K to the .init.data section. This memory is freed when the system boots because it is placed in the .init.data section. The same is true for modules, the 'meta-data' is freed when the module loads.

We could reduce the vmlinux by having 'register()' type calls for this infrastructure, or a new marco per file as you suggest, and i had something like that in an earlier iteration, but I think the memory savings is very small when compared to the increased complexity. The simplicity to just put a 'pr_debug()" anywhere and just have it do the right thing is nice.

Also, it should be noted that the total increase in the size of the 'vmlinux' was 3%, or 2M, this is due to the above 'meta-data', the new code, and probably most significantly, the strings, for the additinal printks. Thus, compressing the above meta-data, or eliminating it by some other means would only decrease the size of this infrastructure by 2% or so.

thanks,

-Jason

#5 lore raw
Quoted 38 lines · Jason Baron
Quoted 11 lines · Greg KH
Quoted 10 lines · Jason Baron
include/linux/device.h +4 -0
@@ -595,6 +595,22 @@ extern const char *dev_driver_string(struct device *dev);
 #ifdef DEBUG
 #define dev_dbg(dev, format, arg...)		\
 	dev_printk(KERN_DEBUG , dev , format , ## arg)
+#elif defined(CONFIG_DYNAMIC_PRINTK_DEBUG)
+#define dev_dbg(dev, format, ...) do {				    	     \
+	static char mod_name[]						     \
+	__attribute__((section("__verbose_strings")))			     \
Quoted 9 lines

include/linux/device.h extern const char *dev_driver_string(struct device *dev); · line 606

Quoted 5 lines
include/linux/device.h +5 -0
+	 = KBUILD_MODNAME;						     \
+	static struct mod_debug foobar					     \
+	__used								     \
+	__attribute__((section("__verbose"), aligned(8))) =		     \
+	{ mod_name, mod_name, "0", "0", NULL };				     \

Does the compiler merge all of these variables together into one within the same file?

no.

Quoted 2 lines

If not, should we have a new macro, one per file, for this information?

With this feature enabled, the above 'meta-data' added ~32K to the .init.data section. This memory is freed when the system boots because it is placed in the .init.data section. The same is true for modules, the 'meta-data' is freed when the module loads.

We could reduce the vmlinux by having 'register()' type calls for this infrastructure, or a new marco per file as you suggest, and i had something like that in an earlier iteration, but I think the memory savings is very small when compared to the increased complexity. The simplicity to just put a 'pr_debug()" anywhere and just have it do the right thing is nice.

Quoted 5 lines

Also, it should be noted that the total increase in the size of the 'vmlinux' was 3%, or 2M, this is due to the above 'meta-data', the new code, and probably most significantly, the strings, for the additinal printks. Thus, compressing the above meta-data, or eliminating it by some other means would only decrease the size of this infrastructure by 2% or so.

Ok, thanks for the good explaination. As long as there is a way to turn this off for the EMBEDDED people who do care about 2% at times, this should be ok.

greg k-h

#6 lore raw
In reply to Jason Baron

Hi!

Quoted 9 lines

This is the core patch that implements a new dynamic debug infrastructure.

Each kernel sub-system seems to have its own way of dealing with debugging statements. Some of these methods include 'dprintk', 'pr_debug', 'dev_debug', 'DEBUGP'. There are also a myriad of ways of enabling these statements. Some require CONFIG_ parameters to be set, some provide /proc interfaces, while others require 'DEBUG' to be set in header files. Most of these methods also require kernel re-compiles in order to set the appropriate knobs.

Quoted 7 lines

I've attempted to standardize how how the kernel deals with debugging statements, both from the user as well as the kernel perspective. Why aren't these debug statements made into printk(KERN_DEBUG), and be done with it? The problem here is that 'dprintk', 'pr_debug', etc. were introduced b/c they are often provide too verbose a level of output, could potentially adversly affect system performance if they were always enabled, and they clutter up the system logs.

Its good that someone is finallu cleaning this up. Thanks!

Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html