kmemleak: Simplify the reports logged by the scanning thread
[safe/jmp/linux-2.6] / mm / kmemleak.c
index ec759b6..4130a48 100644 (file)
@@ -61,6 +61,8 @@
  * structure.
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/list.h>
@@ -192,7 +194,7 @@ static unsigned long jiffies_min_age;
 /* delay between automatic memory scannings */
 static signed long jiffies_scan_wait;
 /* enables or disables the task stacks scanning */
-static int kmemleak_stack_scan;
+static int kmemleak_stack_scan = 1;
 /* mutex protecting the memory scanning */
 static DEFINE_MUTEX(scan_mutex);
 /* mutex protecting the access to the /sys/kernel/debug/kmemleak file */
@@ -233,7 +235,7 @@ struct early_log {
 };
 
 /* early logging buffer and current position */
-static struct early_log early_log[200];
+static struct early_log early_log[CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE];
 static int crt_early_log;
 
 static void kmemleak_disable(void);
@@ -277,15 +279,6 @@ static int color_gray(const struct kmemleak_object *object)
 }
 
 /*
- * Objects are considered referenced if their color is gray and they have not
- * been deleted.
- */
-static int referenced_object(struct kmemleak_object *object)
-{
-       return (object->flags & OBJECT_ALLOCATED) && color_gray(object);
-}
-
-/*
  * Objects are considered unreferenced only if their color is white, they have
  * not be deleted and have a minimum age to avoid false positives caused by
  * pointers temporarily stored in CPU registers.
@@ -297,38 +290,23 @@ static int unreferenced_object(struct kmemleak_object *object)
 }
 
 /*
- * Printing of the (un)referenced objects information, either to the seq file
- * or to the kernel log. The print_referenced/print_unreferenced functions
- * must be called with the object->lock held.
+ * Printing of the unreferenced objects information to the seq file. The
+ * print_unreferenced function must be called with the object->lock held.
  */
-#define print_helper(seq, x...)        do {    \
-       struct seq_file *s = (seq);     \
-       if (s)                          \
-               seq_printf(s, x);       \
-       else                            \
-               pr_info(x);             \
-} while (0)
-
-static void print_referenced(struct kmemleak_object *object)
-{
-       pr_info("kmemleak: referenced object 0x%08lx (size %zu)\n",
-               object->pointer, object->size);
-}
-
 static void print_unreferenced(struct seq_file *seq,
                               struct kmemleak_object *object)
 {
        int i;
 
-       print_helper(seq, "kmemleak: unreferenced object 0x%08lx (size %zu):\n",
-                    object->pointer, object->size);
-       print_helper(seq, "  comm \"%s\", pid %d, jiffies %lu\n",
-                    object->comm, object->pid, object->jiffies);
-       print_helper(seq, "  backtrace:\n");
+       seq_printf(seq, "unreferenced object 0x%08lx (size %zu):\n",
+                  object->pointer, object->size);
+       seq_printf(seq, "  comm \"%s\", pid %d, jiffies %lu\n",
+                  object->comm, object->pid, object->jiffies);
+       seq_printf(seq, "  backtrace:\n");
 
        for (i = 0; i < object->trace_len; i++) {
                void *ptr = (void *)object->trace[i];
-               print_helper(seq, "    [<%p>] %pS\n", ptr, ptr);
+               seq_printf(seq, "    [<%p>] %pS\n", ptr, ptr);
        }
 }
 
@@ -344,7 +322,7 @@ static void dump_object_info(struct kmemleak_object *object)
        trace.nr_entries = object->trace_len;
        trace.entries = object->trace;
 
-       pr_notice("kmemleak: Object 0x%08lx (size %zu):\n",
+       pr_notice("Object 0x%08lx (size %zu):\n",
                  object->tree_node.start, object->size);
        pr_notice("  comm \"%s\", pid %d, jiffies %lu\n",
                  object->comm, object->pid, object->jiffies);
@@ -372,7 +350,7 @@ static struct kmemleak_object *lookup_object(unsigned long ptr, int alias)
                object = prio_tree_entry(node, struct kmemleak_object,
                                         tree_node);
                if (!alias && object->pointer != ptr) {
-                       kmemleak_warn("kmemleak: Found object by alias");
+                       kmemleak_warn("Found object by alias");
                        object = NULL;
                }
        } else
@@ -467,8 +445,7 @@ static void create_object(unsigned long ptr, size_t size, int min_count,
 
        object = kmem_cache_alloc(object_cache, gfp & GFP_KMEMLEAK_MASK);
        if (!object) {
-               kmemleak_stop("kmemleak: Cannot allocate a kmemleak_object "
-                             "structure\n");
+               kmemleak_stop("Cannot allocate a kmemleak_object structure\n");
                return;
        }
 
@@ -527,8 +504,8 @@ static void create_object(unsigned long ptr, size_t size, int min_count,
        if (node != &object->tree_node) {
                unsigned long flags;
 
-               kmemleak_stop("kmemleak: Cannot insert 0x%lx into the object "
-                             "search tree (already existing)\n", ptr);
+               kmemleak_stop("Cannot insert 0x%lx into the object search tree "
+                             "(already existing)\n", ptr);
                object = lookup_object(ptr, 1);
                spin_lock_irqsave(&object->lock, flags);
                dump_object_info(object);
@@ -553,7 +530,7 @@ static void delete_object(unsigned long ptr)
        write_lock_irqsave(&kmemleak_lock, flags);
        object = lookup_object(ptr, 0);
        if (!object) {
-               kmemleak_warn("kmemleak: Freeing unknown object at 0x%08lx\n",
+               kmemleak_warn("Freeing unknown object at 0x%08lx\n",
                              ptr);
                write_unlock_irqrestore(&kmemleak_lock, flags);
                return;
@@ -570,8 +547,6 @@ static void delete_object(unsigned long ptr)
         * cannot be freed when it is being scanned.
         */
        spin_lock_irqsave(&object->lock, flags);
-       if (object->flags & OBJECT_REPORTED)
-               print_referenced(object);
        object->flags &= ~OBJECT_ALLOCATED;
        spin_unlock_irqrestore(&object->lock, flags);
        put_object(object);
@@ -588,8 +563,7 @@ static void make_gray_object(unsigned long ptr)
 
        object = find_and_get_object(ptr, 0);
        if (!object) {
-               kmemleak_warn("kmemleak: Graying unknown object at 0x%08lx\n",
-                             ptr);
+               kmemleak_warn("Graying unknown object at 0x%08lx\n", ptr);
                return;
        }
 
@@ -610,8 +584,7 @@ static void make_black_object(unsigned long ptr)
 
        object = find_and_get_object(ptr, 0);
        if (!object) {
-               kmemleak_warn("kmemleak: Blacking unknown object at 0x%08lx\n",
-                             ptr);
+               kmemleak_warn("Blacking unknown object at 0x%08lx\n", ptr);
                return;
        }
 
@@ -634,21 +607,20 @@ static void add_scan_area(unsigned long ptr, unsigned long offset,
 
        object = find_and_get_object(ptr, 0);
        if (!object) {
-               kmemleak_warn("kmemleak: Adding scan area to unknown "
-                             "object at 0x%08lx\n", ptr);
+               kmemleak_warn("Adding scan area to unknown object at 0x%08lx\n",
+                             ptr);
                return;
        }
 
        area = kmem_cache_alloc(scan_area_cache, gfp & GFP_KMEMLEAK_MASK);
        if (!area) {
-               kmemleak_warn("kmemleak: Cannot allocate a scan area\n");
+               kmemleak_warn("Cannot allocate a scan area\n");
                goto out;
        }
 
        spin_lock_irqsave(&object->lock, flags);
        if (offset + length > object->size) {
-               kmemleak_warn("kmemleak: Scan area larger than object "
-                             "0x%08lx\n", ptr);
+               kmemleak_warn("Scan area larger than object 0x%08lx\n", ptr);
                dump_object_info(object);
                kmem_cache_free(scan_area_cache, area);
                goto out_unlock;
@@ -677,8 +649,7 @@ static void object_no_scan(unsigned long ptr)
 
        object = find_and_get_object(ptr, 0);
        if (!object) {
-               kmemleak_warn("kmemleak: Not scanning unknown object at "
-                             "0x%08lx\n", ptr);
+               kmemleak_warn("Not scanning unknown object at 0x%08lx\n", ptr);
                return;
        }
 
@@ -699,7 +670,8 @@ static void log_early(int op_type, const void *ptr, size_t size,
        struct early_log *log;
 
        if (crt_early_log >= ARRAY_SIZE(early_log)) {
-               kmemleak_stop("kmemleak: Early log buffer exceeded\n");
+               pr_warning("Early log buffer exceeded\n");
+               kmemleak_disable();
                return;
        }
 
@@ -966,7 +938,7 @@ static void kmemleak_scan(void)
                 * 1 reference to any object at this point.
                 */
                if (atomic_read(&object->use_count) > 1) {
-                       pr_debug("kmemleak: object->use_count = %d\n",
+                       pr_debug("object->use_count = %d\n",
                                 atomic_read(&object->use_count));
                        dump_object_info(object);
                }
@@ -1062,7 +1034,7 @@ static int kmemleak_scan_thread(void *arg)
 {
        static int first_run = 1;
 
-       pr_info("kmemleak: Automatic memory scanning thread started\n");
+       pr_info("Automatic memory scanning thread started\n");
 
        /*
         * Wait before the first scan to allow the system to fully initialize.
@@ -1075,40 +1047,37 @@ static int kmemleak_scan_thread(void *arg)
        while (!kthread_should_stop()) {
                struct kmemleak_object *object;
                signed long timeout = jiffies_scan_wait;
+               int new_leaks = 0;
 
                mutex_lock(&scan_mutex);
 
                kmemleak_scan();
-               reported_leaks = 0;
 
                rcu_read_lock();
                list_for_each_entry_rcu(object, &object_list, object_list) {
                        unsigned long flags;
 
-                       if (reported_leaks >= REPORTS_NR)
-                               break;
                        spin_lock_irqsave(&object->lock, flags);
-                       if (!(object->flags & OBJECT_REPORTED) &&
-                           unreferenced_object(object)) {
-                               print_unreferenced(NULL, object);
+                       if (unreferenced_object(object) &&
+                           !(object->flags & OBJECT_REPORTED)) {
                                object->flags |= OBJECT_REPORTED;
-                               reported_leaks++;
-                       } else if ((object->flags & OBJECT_REPORTED) &&
-                                  referenced_object(object)) {
-                               print_referenced(object);
-                               object->flags &= ~OBJECT_REPORTED;
+                               new_leaks++;
                        }
                        spin_unlock_irqrestore(&object->lock, flags);
                }
                rcu_read_unlock();
 
+               if (new_leaks)
+                       pr_info("%d new suspected memory leaks (see "
+                               "/sys/kernel/debug/kmemleak)\n", new_leaks);
+
                mutex_unlock(&scan_mutex);
                /* wait before the next scan */
                while (timeout && !kthread_should_stop())
                        timeout = schedule_timeout_interruptible(timeout);
        }
 
-       pr_info("kmemleak: Automatic memory scanning thread ended\n");
+       pr_info("Automatic memory scanning thread ended\n");
 
        return 0;
 }
@@ -1123,7 +1092,7 @@ void start_scan_thread(void)
                return;
        scan_thread = kthread_run(kmemleak_scan_thread, NULL, "kmemleak");
        if (IS_ERR(scan_thread)) {
-               pr_warning("kmemleak: Failed to create the scan thread\n");
+               pr_warning("Failed to create the scan thread\n");
                scan_thread = NULL;
        }
 }
@@ -1367,7 +1336,7 @@ static void kmemleak_cleanup(void)
        cleanup_thread = kthread_run(kmemleak_cleanup_thread, NULL,
                                     "kmemleak-clean");
        if (IS_ERR(cleanup_thread))
-               pr_warning("kmemleak: Failed to create the clean-up thread\n");
+               pr_warning("Failed to create the clean-up thread\n");
 }
 
 /*
@@ -1488,8 +1457,7 @@ static int __init kmemleak_late_init(void)
        dentry = debugfs_create_file("kmemleak", S_IRUGO, NULL, NULL,
                                     &kmemleak_fops);
        if (!dentry)
-               pr_warning("kmemleak: Failed to create the debugfs kmemleak "
-                          "file\n");
+               pr_warning("Failed to create the debugfs kmemleak file\n");
        mutex_lock(&kmemleak_mutex);
        start_scan_thread();
        mutex_unlock(&kmemleak_mutex);