Merge tag 'pm-4.20-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael...
[linux-2.6-block.git] / mm / kmemleak.c
index 17dd883198aeab0dccebe762a6b432dad2f14c9d..4f7e4b5a2f082fafc7b354bf98242d6ad59588e0 100644 (file)
@@ -86,6 +86,7 @@
 #include <linux/seq_file.h>
 #include <linux/cpumask.h>
 #include <linux/spinlock.h>
+#include <linux/module.h>
 #include <linux/mutex.h>
 #include <linux/rcupdate.h>
 #include <linux/stacktrace.h>
@@ -181,6 +182,7 @@ struct kmemleak_object {
 /* flag set to not scan the object */
 #define OBJECT_NO_SCAN         (1 << 2)
 
+#define HEX_PREFIX             "    "
 /* number of bytes to print per line; must be 16 or 32 */
 #define HEX_ROW_SIZE           16
 /* number of bytes to print at a time (1, 2, 4, 8) */
@@ -235,6 +237,9 @@ static int kmemleak_skip_disable;
 /* If there are leaks that can be reported */
 static bool kmemleak_found_leaks;
 
+static bool kmemleak_verbose;
+module_param_named(verbose, kmemleak_verbose, bool, 0600);
+
 /*
  * Early object allocation/freeing logging. Kmemleak is initialized after the
  * kernel allocator. However, both the kernel allocator and kmemleak may
@@ -299,6 +304,25 @@ static void kmemleak_disable(void);
        kmemleak_disable();             \
 } while (0)
 
+#define warn_or_seq_printf(seq, fmt, ...)      do {    \
+       if (seq)                                        \
+               seq_printf(seq, fmt, ##__VA_ARGS__);    \
+       else                                            \
+               pr_warn(fmt, ##__VA_ARGS__);            \
+} while (0)
+
+static void warn_or_seq_hex_dump(struct seq_file *seq, int prefix_type,
+                                int rowsize, int groupsize, const void *buf,
+                                size_t len, bool ascii)
+{
+       if (seq)
+               seq_hex_dump(seq, HEX_PREFIX, prefix_type, rowsize, groupsize,
+                            buf, len, ascii);
+       else
+               print_hex_dump(KERN_WARNING, pr_fmt(HEX_PREFIX), prefix_type,
+                              rowsize, groupsize, buf, len, ascii);
+}
+
 /*
  * Printing of the objects hex dump to the seq file. The number of lines to be
  * printed is limited to HEX_MAX_LINES to prevent seq file spamming. The
@@ -314,10 +338,10 @@ static void hex_dump_object(struct seq_file *seq,
        /* limit the number of lines to HEX_MAX_LINES */
        len = min_t(size_t, object->size, HEX_MAX_LINES * HEX_ROW_SIZE);
 
-       seq_printf(seq, "  hex dump (first %zu bytes):\n", len);
+       warn_or_seq_printf(seq, "  hex dump (first %zu bytes):\n", len);
        kasan_disable_current();
-       seq_hex_dump(seq, "    ", DUMP_PREFIX_NONE, HEX_ROW_SIZE,
-                    HEX_GROUP_SIZE, ptr, len, HEX_ASCII);
+       warn_or_seq_hex_dump(seq, DUMP_PREFIX_NONE, HEX_ROW_SIZE,
+                            HEX_GROUP_SIZE, ptr, len, HEX_ASCII);
        kasan_enable_current();
 }
 
@@ -365,17 +389,17 @@ static void print_unreferenced(struct seq_file *seq,
        int i;
        unsigned int msecs_age = jiffies_to_msecs(jiffies - object->jiffies);
 
-       seq_printf(seq, "unreferenced object 0x%08lx (size %zu):\n",
+       warn_or_seq_printf(seq, "unreferenced object 0x%08lx (size %zu):\n",
                   object->pointer, object->size);
-       seq_printf(seq, "  comm \"%s\", pid %d, jiffies %lu (age %d.%03ds)\n",
+       warn_or_seq_printf(seq, "  comm \"%s\", pid %d, jiffies %lu (age %d.%03ds)\n",
                   object->comm, object->pid, object->jiffies,
                   msecs_age / 1000, msecs_age % 1000);
        hex_dump_object(seq, object);
-       seq_printf(seq, "  backtrace:\n");
+       warn_or_seq_printf(seq, "  backtrace:\n");
 
        for (i = 0; i < object->trace_len; i++) {
                void *ptr = (void *)object->trace[i];
-               seq_printf(seq, "    [<%p>] %pS\n", ptr, ptr);
+               warn_or_seq_printf(seq, "    [<%p>] %pS\n", ptr, ptr);
        }
 }
 
@@ -1598,6 +1622,10 @@ static void kmemleak_scan(void)
                if (unreferenced_object(object) &&
                    !(object->flags & OBJECT_REPORTED)) {
                        object->flags |= OBJECT_REPORTED;
+
+                       if (kmemleak_verbose)
+                               print_unreferenced(NULL, object);
+
                        new_leaks++;
                }
                spin_unlock_irqrestore(&object->lock, flags);