mirror of https://github.com/torvalds/linux.git
mm: kmemleak: add support for dumping physical and __percpu object info
Patch series "mm: kmemleak: Usability improvements".
Following a recent false positive tracking that led to commit 488b5b9eca
("mm: kmemleak: fix upper boundary check for physical address objects"), I
needed kmemleak to give me more debug information about the objects it is
tracking. This lead to the first patch of this series. The second patch
changes the kmemleak-test module to show the raw pointers for debugging
purposes.
This patch (of 2):
Currently, echo dump=... > /sys/kernel/debug/kmemleak only looks up the
main virtual address object tree. However, for debugging, it's useful to
dump information about physical address and __percpu objects.
Search all three object trees for the dump= command and also print the
type of the object if not virtual: "(phys)" or "(percpu)". In addition,
allow search by alias (pointer within the object).
Link: https://lkml.kernel.org/r/20250206114537.2597764-1-catalin.marinas@arm.com
Link: https://lkml.kernel.org/r/20250206114537.2597764-2-catalin.marinas@arm.com
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
This commit is contained in:
parent
9a5b183941
commit
7ddeb91f5b
|
|
@ -352,6 +352,15 @@ static bool unreferenced_object(struct kmemleak_object *object)
|
||||||
jiffies_last_scan);
|
jiffies_last_scan);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *__object_type_str(struct kmemleak_object *object)
|
||||||
|
{
|
||||||
|
if (object->flags & OBJECT_PHYS)
|
||||||
|
return " (phys)";
|
||||||
|
if (object->flags & OBJECT_PERCPU)
|
||||||
|
return " (percpu)";
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Printing of the unreferenced objects information to the seq file. The
|
* Printing of the unreferenced objects information to the seq file. The
|
||||||
* print_unreferenced function must be called with the object->lock held.
|
* print_unreferenced function must be called with the object->lock held.
|
||||||
|
|
@ -364,8 +373,9 @@ static void print_unreferenced(struct seq_file *seq,
|
||||||
unsigned int nr_entries;
|
unsigned int nr_entries;
|
||||||
|
|
||||||
nr_entries = stack_depot_fetch(object->trace_handle, &entries);
|
nr_entries = stack_depot_fetch(object->trace_handle, &entries);
|
||||||
warn_or_seq_printf(seq, "unreferenced object 0x%08lx (size %zu):\n",
|
warn_or_seq_printf(seq, "unreferenced object%s 0x%08lx (size %zu):\n",
|
||||||
object->pointer, object->size);
|
__object_type_str(object),
|
||||||
|
object->pointer, object->size);
|
||||||
warn_or_seq_printf(seq, " comm \"%s\", pid %d, jiffies %lu\n",
|
warn_or_seq_printf(seq, " comm \"%s\", pid %d, jiffies %lu\n",
|
||||||
object->comm, object->pid, object->jiffies);
|
object->comm, object->pid, object->jiffies);
|
||||||
hex_dump_object(seq, object);
|
hex_dump_object(seq, object);
|
||||||
|
|
@ -384,10 +394,10 @@ static void print_unreferenced(struct seq_file *seq,
|
||||||
*/
|
*/
|
||||||
static void dump_object_info(struct kmemleak_object *object)
|
static void dump_object_info(struct kmemleak_object *object)
|
||||||
{
|
{
|
||||||
pr_notice("Object 0x%08lx (size %zu):\n",
|
pr_notice("Object%s 0x%08lx (size %zu):\n",
|
||||||
object->pointer, object->size);
|
__object_type_str(object), object->pointer, object->size);
|
||||||
pr_notice(" comm \"%s\", pid %d, jiffies %lu\n",
|
pr_notice(" comm \"%s\", pid %d, jiffies %lu\n",
|
||||||
object->comm, object->pid, object->jiffies);
|
object->comm, object->pid, object->jiffies);
|
||||||
pr_notice(" min_count = %d\n", object->min_count);
|
pr_notice(" min_count = %d\n", object->min_count);
|
||||||
pr_notice(" count = %d\n", object->count);
|
pr_notice(" count = %d\n", object->count);
|
||||||
pr_notice(" flags = 0x%x\n", object->flags);
|
pr_notice(" flags = 0x%x\n", object->flags);
|
||||||
|
|
@ -1998,25 +2008,41 @@ static int kmemleak_open(struct inode *inode, struct file *file)
|
||||||
return seq_open(file, &kmemleak_seq_ops);
|
return seq_open(file, &kmemleak_seq_ops);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dump_str_object_info(const char *str)
|
static bool __dump_str_object_info(unsigned long addr, unsigned int objflags)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
struct kmemleak_object *object;
|
struct kmemleak_object *object;
|
||||||
unsigned long addr;
|
|
||||||
|
|
||||||
if (kstrtoul(str, 0, &addr))
|
object = __find_and_get_object(addr, 1, objflags);
|
||||||
return -EINVAL;
|
if (!object)
|
||||||
object = find_and_get_object(addr, 0);
|
return false;
|
||||||
if (!object) {
|
|
||||||
pr_info("Unknown object at 0x%08lx\n", addr);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
raw_spin_lock_irqsave(&object->lock, flags);
|
raw_spin_lock_irqsave(&object->lock, flags);
|
||||||
dump_object_info(object);
|
dump_object_info(object);
|
||||||
raw_spin_unlock_irqrestore(&object->lock, flags);
|
raw_spin_unlock_irqrestore(&object->lock, flags);
|
||||||
|
|
||||||
put_object(object);
|
put_object(object);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dump_str_object_info(const char *str)
|
||||||
|
{
|
||||||
|
unsigned long addr;
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
|
if (kstrtoul(str, 0, &addr))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
found |= __dump_str_object_info(addr, 0);
|
||||||
|
found |= __dump_str_object_info(addr, OBJECT_PHYS);
|
||||||
|
found |= __dump_str_object_info(addr, OBJECT_PERCPU);
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
pr_info("Unknown object at 0x%08lx\n", addr);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue