kmemleak suggestion (long message)

From: Sergey Senozhatsky
Date: Thu Jun 25 2009 - 18:17:08 EST


Hello.

Currently kmemleak prints info about all objects. I guess sometimes kmemleak gives you more than you actually need.
syslog:
...
kmemleak: unreferenced object 0xf702fad0 (size 152):
kmemleak: comm "swapper", pid 0, jiffies 4294877296
kmemleak: backtrace:
kmemleak: [<c10e963b>] kmemleak_alloc+0x11b/0x2b0
kmemleak: [<c10e4b91>] kmem_cache_alloc+0x111/0x1c0
kmemleak: [<c123b0c4>] idr_pre_get+0x64/0x90
kmemleak: [<c123b115>] ida_pre_get+0x25/0xe0
kmemleak: [<c10edf3f>] set_anon_super+0x2f/0xf0
kmemleak: [<c10eea50>] sget+0x2d0/0x390
kmemleak: [<c10ef0f1>] get_sb_single+0x41/0xd0
kmemleak: [<c114a3f8>] sysfs_get_sb+0x28/0x40
kmemleak: [<c10eeea1>] vfs_kern_mount+0x71/0x150
kmemleak: [<c10eefa1>] kern_mount_data+0x21/0x40
kmemleak: [<c15cb2f5>] sysfs_init+0x67/0xcc
kmemleak: [<c15ca0f0>] mnt_init+0x9d/0x182
kmemleak: [<c15c9ca6>] vfs_caches_init+0x10d/0x130
kmemleak: [<c15aca26>] start_kernel+0x2f4/0x343
kmemleak: [<c15ac088>] __init_begin+0x88/0xa1
kmemleak: [<ffffffff>] 0xffffffff
kmemleak: unreferenced object 0xf702fa20 (size 152):
kmemleak: comm "swapper", pid 0, jiffies 4294877296
kmemleak: backtrace:
kmemleak: [<c10e963b>] kmemleak_alloc+0x11b/0x2b0
kmemleak: [<c10e4b91>] kmem_cache_alloc+0x111/0x1c0
kmemleak: [<c123b0c4>] idr_pre_get+0x64/0x90
kmemleak: [<c123b115>] ida_pre_get+0x25/0xe0
kmemleak: [<c10edf3f>] set_anon_super+0x2f/0xf0
kmemleak: [<c10eea50>] sget+0x2d0/0x390
kmemleak: [<c10ef0f1>] get_sb_single+0x41/0xd0
kmemleak: [<c114a3f8>] sysfs_get_sb+0x28/0x40
kmemleak: [<c10eeea1>] vfs_kern_mount+0x71/0x150
kmemleak: [<c10eefa1>] kern_mount_data+0x21/0x40
kmemleak: [<c15cb2f5>] sysfs_init+0x67/0xcc
kmemleak: [<c15ca0f0>] mnt_init+0x9d/0x182
kmemleak: [<c15c9ca6>] vfs_caches_init+0x10d/0x130
kmemleak: [<c15aca26>] start_kernel+0x2f4/0x343
kmemleak: [<c15ac088>] __init_begin+0x88/0xa1
kmemleak: [<ffffffff>] 0xffffffff
...
kmemleak: unreferenced object 0xf7028140 (size 1024):
kmemleak: comm "swapper", pid 0, jiffies 4294877296
kmemleak: backtrace:
kmemleak: [<c10e963b>] kmemleak_alloc+0x11b/0x2b0
kmemleak: [<c10e584d>] __kmalloc+0x16d/0x210
kmemleak: [<c10e5bcd>] alloc_arraycache+0x2d/0x70
kmemleak: [<c10e5e46>] do_tune_cpucache+0x236/0x3e0
kmemleak: [<c10e6192>] enable_cpucache+0x42/0x110
kmemleak: [<c13ec2a3>] setup_cpu_cache+0x183/0x2a0
kmemleak: [<c10e65f5>] kmem_cache_create+0x395/0x5a0
kmemleak: [<c15cf0d1>] radix_tree_init+0x33/0x87
kmemleak: [<c15aca2b>] start_kernel+0x2f9/0x343
kmemleak: [<c15ac088>] __init_begin+0x88/0xa1
kmemleak: [<ffffffff>] 0xffffffff
kmemleak: unreferenced object 0xf70057b0 (size 128):
kmemleak: comm "swapper", pid 0, jiffies 4294877296
kmemleak: backtrace:
kmemleak: [<c10e963b>] kmemleak_alloc+0x11b/0x2b0
kmemleak: [<c10e584d>] __kmalloc+0x16d/0x210
kmemleak: [<c113b729>] __proc_create+0x99/0x120
kmemleak: [<c113c143>] proc_symlink+0x33/0xb0
kmemleak: [<c15cac41>] proc_root_init+0x60/0xbe
kmemleak: [<c15aca3a>] start_kernel+0x308/0x343
kmemleak: [<c15ac088>] __init_begin+0x88/0xa1
kmemleak: [<ffffffff>] 0xffffffff
....
kmemleak: unreferenced object 0xf702fce0 (size 152):
kmemleak: comm "swapper", pid 0, jiffies 4294877296
kmemleak: backtrace:
kmemleak: [<c10e963b>] kmemleak_alloc+0x11b/0x2b0
kmemleak: [<c10e4b91>] kmem_cache_alloc+0x111/0x1c0
kmemleak: [<c123b0c4>] idr_pre_get+0x64/0x90
kmemleak: [<c123b115>] ida_pre_get+0x25/0xe0
kmemleak: [<c113bcaf>] proc_register+0x2f/0x1e0
kmemleak: [<c113c17e>] proc_symlink+0x6e/0xb0
kmemleak: [<c15cac41>] proc_root_init+0x60/0xbe
kmemleak: [<c15aca3a>] start_kernel+0x308/0x343
kmemleak: [<c15ac088>] __init_begin+0x88/0xa1
kmemleak: [<ffffffff>] 0xffffffff
....x10

Suppose, I'm monitoring application which never calls "[<c123b0c4>] idr_pre_get" or
"[<c12cf49b>] tty_ldisc_try_get". It would be nice to "temporarily turn-off unimportant" output.
I suggest to give ability to "blacklist" function(s). I did it via printed by kmemleak function address %p
(like [<c10e963b>]) since it's impossible to assume what objects will be reported in next YYY seconds
("unreferenced object 0xf702fce0"). (It's possible to "blacklist" according to function name
(like idr_pre_get) but I think %p is quite enough).

For example, to avoid some notifications I just copy-paste address from printed stack:
echo "block=c123b0c4" > /sys/kernel/debug/kmemleak

//debug output
kmemleak: Added to blacklist: <c123b0c4>

syslog:
kmemleak: unreferenced object 0xf7049a10 (size 1024):
kmemleak: comm "swapper", pid 0, jiffies 4294877296
kmemleak: backtrace:
kmemleak: [<c10e963b>] kmemleak_alloc+0x11b/0x2b0
kmemleak: [<c10e584d>] __kmalloc+0x16d/0x210
kmemleak: [<c10e5bcd>] alloc_arraycache+0x2d/0x70
kmemleak: [<c10e5e46>] do_tune_cpucache+0x236/0x3e0
kmemleak: [<c10e6192>] enable_cpucache+0x42/0x110
kmemleak: [<c13ec2a3>] setup_cpu_cache+0x183/0x2a0
kmemleak: [<c10e65f5>] kmem_cache_create+0x395/0x5a0
kmemleak: [<c15c2a6b>] signals_init+0x34/0x4c
kmemleak: [<c15aca30>] start_kernel+0x2fe/0x343
kmemleak: [<c15ac088>] __init_begin+0x88/0xa1
kmemleak: [<ffffffff>] 0xffffffff
kmemleak: unreferenced object 0xf70495f8 (size 1024):
kmemleak: comm "swapper", pid 0, jiffies 4294877296
kmemleak: backtrace:
kmemleak: [<c10e963b>] kmemleak_alloc+0x11b/0x2b0
kmemleak: [<c10e584d>] __kmalloc+0x16d/0x210
kmemleak: [<c10e5bcd>] alloc_arraycache+0x2d/0x70
kmemleak: [<c10e5e46>] do_tune_cpucache+0x236/0x3e0
kmemleak: [<c10e6192>] enable_cpucache+0x42/0x110
kmemleak: [<c13ec2a3>] setup_cpu_cache+0x183/0x2a0
kmemleak: [<c10e65f5>] kmem_cache_create+0x395/0x5a0
kmemleak: [<c15cabc9>] proc_init_inodecache+0x31/0x49
kmemleak: [<c15cabf7>] proc_root_init+0x16/0xbe
kmemleak: [<c15aca3a>] start_kernel+0x308/0x343
kmemleak: [<c15ac088>] __init_begin+0x88/0xa1
kmemleak: [<ffffffff>] 0xffffffff
kmemleak: unreferenced object 0xf70057b0 (size 128):
kmemleak: comm "swapper", pid 0, jiffies 4294877296
kmemleak: backtrace:
kmemleak: [<c10e963b>] kmemleak_alloc+0x11b/0x2b0
kmemleak: [<c10e584d>] __kmalloc+0x16d/0x210
kmemleak: [<c113b729>] __proc_create+0x99/0x120
kmemleak: [<c113c143>] proc_symlink+0x33/0xb0
kmemleak: [<c15cac41>] proc_root_init+0x60/0xbe
kmemleak: [<c15aca3a>] start_kernel+0x308/0x343
kmemleak: [<c15ac088>] __init_begin+0x88/0xa1
kmemleak: [<ffffffff>] 0xffffffff
...
//Debug output showing that we have blocked objects.
kmemleak: Function blacklisted <c123b0c4>
kmemleak: Function blacklisted <c123b0c4>
kmemleak: Function blacklisted <c123b0c4>
kmemleak: Function blacklisted <c123b0c4>
kmemleak: Function blacklisted <c123b0c4>
kmemleak: Function blacklisted <c123b0c4>
kmemleak: Function blacklisted <c123b0c4>
kmemleak: Function blacklisted <c123b0c4>
kmemleak: Function blacklisted <c123b0c4>
kmemleak: Function blacklisted <c123b0c4>
kmemleak: Function blacklisted <c123b0c4>
...
kmemleak: unreferenced object 0xf7005680 (size 128):
kmemleak: comm "swapper", pid 0, jiffies 4294877296
kmemleak: backtrace:
kmemleak: [<c10e963b>] kmemleak_alloc+0x11b/0x2b0
kmemleak: [<c10e584d>] __kmalloc+0x16d/0x210
kmemleak: [<c113b729>] __proc_create+0x99/0x120
kmemleak: [<c113c143>] proc_symlink+0x33/0xb0
kmemleak: [<c15cb03a>] proc_net_init+0x22/0x3f
kmemleak: [<c15cac46>] proc_root_init+0x65/0xbe
kmemleak: [<c15aca3a>] start_kernel+0x308/0x343
kmemleak: [<c15ac088>] __init_begin+0x88/0xa1
kmemleak: [<ffffffff>] 0xffffffff

To unblock:
echo "unblock=c123b0c4" > /sys/kernel/debug/kmemleak

//Debug output
kmemleak: Removed from blacklist <c123b0c4>

syslog:
...
kmemleak: comm "swapper", pid 0, jiffies 4294877296
kmemleak: backtrace:
kmemleak: [<c10e963b>] kmemleak_alloc+0x11b/0x2b0
kmemleak: [<c10e4b91>] kmem_cache_alloc+0x111/0x1c0
kmemleak: [<c123b0c4>] idr_pre_get+0x64/0x90
kmemleak: [<c123b115>] ida_pre_get+0x25/0xe0
kmemleak: [<c113bcaf>] proc_register+0x2f/0x1e0
kmemleak: [<c113c17e>] proc_symlink+0x6e/0xb0
kmemleak: [<c15cac41>] proc_root_init+0x60/0xbe
kmemleak: [<c15aca3a>] start_kernel+0x308/0x343
kmemleak: [<c15ac088>] __init_begin+0x88/0xa1
kmemleak: [<ffffffff>] 0xffffffff
kmemleak: unreferenced object 0xf7037810 (size 152):
kmemleak: comm "swapper", pid 0, jiffies 4294877296
kmemleak: backtrace:
kmemleak: [<c10e963b>] kmemleak_alloc+0x11b/0x2b0
kmemleak: [<c10e4b91>] kmem_cache_alloc+0x111/0x1c0
kmemleak: [<c123b0c4>] idr_pre_get+0x64/0x90
kmemleak: [<c123b115>] ida_pre_get+0x25/0xe0
kmemleak: [<c113bcaf>] proc_register+0x2f/0x1e0
kmemleak: [<c113c17e>] proc_symlink+0x6e/0xb0
kmemleak: [<c15cac41>] proc_root_init+0x60/0xbe
kmemleak: [<c15aca3a>] start_kernel+0x308/0x343
kmemleak: [<c15ac088>] __init_begin+0x88/0xa1
kmemleak: [<ffffffff>] 0xffffffff
kmemleak: unreferenced object 0xf7037760 (size 152):
kmemleak: comm "swapper", pid 0, jiffies 4294877296
kmemleak: backtrace:
kmemleak: [<c10e963b>] kmemleak_alloc+0x11b/0x2b0
kmemleak: [<c10e4b91>] kmem_cache_alloc+0x111/0x1c0
kmemleak: [<c123b0c4>] idr_pre_get+0x64/0x90
kmemleak: [<c123b115>] ida_pre_get+0x25/0xe0
kmemleak: [<c113bcaf>] proc_register+0x2f/0x1e0
kmemleak: [<c113c17e>] proc_symlink+0x6e/0xb0
kmemleak: [<c15cac41>] proc_root_init+0x60/0xbe
kmemleak: [<c15aca3a>] start_kernel+0x308/0x343
kmemleak: [<c15ac088>] __init_begin+0x88/0xa1
kmemleak: [<ffffffff>] 0xffffffff
...

As you can see, I'm not blocking monitoring of any object. I just suppress output of objects with "unwanted"
addresses in stack.

Other useful feature (to my mind) - block according to pid. (Or block "if pid != <the_one_I_want>").

"block=XXXX"/"unblock=XXXX" is _very_ general. I should use block-address/block-function/filter-by-address, etc.
Here is code I wrote today (it's just a concept. Not 'beta' or something like this.).
It's against kmemleak.c without CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE support.

If you like the basic idea - I'll continue my work.
Any comments are highly appreciable.

---
diff --git a/mm/kmemleak.c b/mm/kmemleak.c
index c96f2c8..7a20898 100644
--- a/mm/kmemleak.c
+++ b/mm/kmemleak.c
@@ -153,6 +153,14 @@ struct kmemleak_object {
char comm[TASK_COMM_LEN]; /* executable name */
};

+/*
+ * Structure holding the data for each unwanted address (blacklisted function).
+ */
+struct kmemleak_function {
+ unsigned int pointer;
+ struct list_head function_list;
+};
+
/* flag representing the memory block allocation status */
#define OBJECT_ALLOCATED (1 << 0)
/* flag set after the first reporting of an unreference object */
@@ -164,10 +172,14 @@ struct kmemleak_object {
static LIST_HEAD(object_list);
/* the list of gray-colored objects (see color_gray comment below) */
static LIST_HEAD(gray_list);
+/* the list of blacklisted functions */
+static LIST_HEAD(function_list);
/* prio search tree for object boundaries */
static struct prio_tree_root object_tree_root;
/* rw_lock protecting the access to object_list and prio_tree_root */
static DEFINE_RWLOCK(kmemleak_lock);
+/* spinlock protecting the access to function_list*/
+static DEFINE_RWLOCK(function_list_lock);

/* allocation caches for kmemleak internal data */
static struct kmem_cache *object_cache;
@@ -258,6 +270,102 @@ static void kmemleak_disable(void);
kmemleak_disable(); \
} while (0)

+
+static int lookup_function(unsigned long pointer) {
+ struct kmemleak_function *function;
+ unsigned long flags;
+
+ /*Find out if given address (pointer) is in blacklist.
+ *
+ */
+ read_lock_irqsave(&function_list_lock, flags);
+ list_for_each_entry(function, &function_list, function_list) {
+ if( function->pointer == pointer) {
+ pr_info("Function already blacklisted <%p>\n", (void*)pointer);
+ return 1;
+ }
+ }
+ read_unlock_irqrestore(&function_list_lock, flags);
+
+ return 0;
+}
+
+
+static void create_function(unsigned long pointer) {
+ unsigned long flags;
+ struct kmemleak_function *function;
+
+ /*prevent multi-blacklisted*/
+ if( lookup_function(pointer) )
+ return;
+
+ /*Is it ok? Should I allocate in cache (like objects)?*/
+ function = kmalloc(sizeof(struct kmemleak_function), GFP_KERNEL & GFP_KMEMLEAK_MASK);
+ if( !function ) {
+ kmemleak_warn("Cannot allocate a kmemleak_function structure\n");
+ return;
+ }
+
+ function->pointer = pointer;
+ INIT_LIST_HEAD(&function->function_list);
+
+ write_lock_irqsave(&function_list_lock, flags);
+ list_add(&function->function_list, &function_list);
+ write_unlock_irqrestore(&function_list_lock, flags);
+
+ pr_info("Added to blacklist: <%p>\n", (void*)pointer);
+}
+
+
+static void delete_function(unsigned long pointer) {
+ unsigned long flags;
+ struct kmemleak_function *function;
+
+ write_lock_irqsave(&function_list_lock, flags);
+
+ list_for_each_entry(function, &function_list, function_list) {
+ if( function->pointer == pointer ) {
+ list_del(&function->function_list);
+ /*Again. Is it ok?*/
+ kfree(function);
+ write_unlock_irqrestore(&function_list_lock, flags);
+
+ pr_info("Removed from blacklist <%p>\n", (void*)pointer);
+ return;
+ }
+ }
+
+ write_unlock_irqrestore(&function_list_lock, flags);
+}
+
+
+static int match_function(const unsigned long *trace , int trace_len) {
+ int i;
+ unsigned long flags;
+ struct kmemleak_function *function;
+
+ read_lock_irqsave(&function_list_lock, flags);
+ /*Most objects usually have more than 10 pointers in stack.
+ *I don't think that blacklisted count normally will be more that 10 functions.
+ *So it's better to inner-loop on blacklist.
+ */
+ list_for_each_entry(function, &function_list, function_list) {
+ for(i = 0; i < trace_len; i++) {
+ if( function->pointer == trace[i]) {
+ read_unlock_irqrestore(&function_list_lock, flags);
+ pr_info("Function blacklisted <%p>\n", (void*)trace[i]);
+ return 1;
+ }
+ }
+ }
+
+ read_unlock_irqrestore(&function_list_lock, flags);
+
+ return 0;
+}
+
+
+
/*
* Object colors, encoded with count and min_count:
* - white - orphan object, not enough references to it (count < min_count)
@@ -321,6 +429,15 @@ static void print_unreferenced(struct seq_file *seq,
struct kmemleak_object *object)
{
int i;
+ void *ptr;
+
+ /*The basic idea is to stop printing object's stack with blacklisted
+ *function(s). As soon as we whitelist (unblock) function, we'll get this info
+ *again.
+ */
+ if( match_function(object->trace, object->trace_len) )
+ return;
+

print_helper(seq, "unreferenced object 0x%08lx (size %zu):\n",
object->pointer, object->size);
@@ -329,7 +446,7 @@ static void print_unreferenced(struct seq_file *seq,
print_helper(seq, " backtrace:\n");

for (i = 0; i < object->trace_len; i++) {
- void *ptr = (void *)object->trace[i];
+ ptr = (void *)object->trace[i];
print_helper(seq, " [<%p>] %pS\n", ptr, ptr);
}
}
@@ -1303,6 +1420,33 @@ static ssize_t kmemleak_write(struct file *file, const char __user *user_buf,
start_scan_thread();
else if (strncmp(buf, "scan=off", 8) == 0)
stop_scan_thread();
+ else if (strncmp(buf, "block=", 6) == 0) {
+ unsigned long pointer;
+ int err;
+
+ /*or we can force user to pass xADDRESS (base = 0).
+ *basically, address will be given as simple copy-paste
+ *from print_XXX stack <%p> (I think so).
+ */
+ err = strict_strtoul(buf + 6, 16, &pointer);
+ if (err < 0)
+ return err;
+
+ create_function(pointer);
+ }
+ else if (strncmp(buf, "unblock=", 8) ==0 ) {
+ unsigned long pointer;
+ int err;
+
+ /*
+ *Read comment for block=XXX.
+ */
+ err = strict_strtoul(buf + 8, 16, &pointer);
+ if (err < 0)
+ return err;
+
+ delete_function(pointer);
+ }
else if (strncmp(buf, "scan=", 5) == 0) {
unsigned long secs;
int err;
@@ -1338,8 +1482,11 @@ static const struct file_operations kmemleak_fops = {
*/
static int kmemleak_cleanup_thread(void *arg)
{
+ unsigned long flags;
struct kmemleak_object *object;
+ struct kmemleak_function *function;

+
mutex_lock(&kmemleak_mutex);
stop_scan_thread();
mutex_unlock(&kmemleak_mutex);
@@ -1349,6 +1496,15 @@ static int kmemleak_cleanup_thread(void *arg)
list_for_each_entry_rcu(object, &object_list, object_list)
delete_object(object->pointer);
rcu_read_unlock();
+
+ write_lock_irqsave(&function_list_lock, flags);
+ list_for_each_entry(function, &function_list, function_list) {
+ list_del(&function->function_list);
+ kfree(function);
+ }
+
+ write_unlock_irqrestore(&function_list_lock, flags);
+
mutex_unlock(&scan_mutex);

return 0;


Thanks,
Sergey
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/