Kernel memory leak checking tool --- kmemleak

kmemleak is a tool used by the Linux kernel to detect memory leaks. The basic principle of implementation is to track memory allocation interfaces such as kmalloc(), vmalloc(), kmem_cache_alloc(), etc. The information is stored in the kmemleak data structure.

Configure kmemleak

The kmemleak function needs to enable relevant configuration items in the kernel before it can be used. The main configuration items are as follows:

CONFIG_DEBUG_KMEMLEAK
CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE
CONFIG_DEBUG_KMEMLEAK_TEST

Some other dependency configurations are as follows:

CONFIG_DEBUG_KERNEL
CONFIG_DEBUG_FS
CONFIG_STACKTRACE
CONFIG_KALLSYMS
CONFIG_CRC32

After kmemleak is configured, a kernel thread will scan the memory every 10 minutes and print the number of leaks found.

Check kmemleak

Check whether the kmemleak function takes effect through the /sys/kernel/debug/kmemleak node.

root@Linux:/# mount -t debugfs nodev /sys/kernel/debug/
root@Linux:/# cat /sys/kernel/debug/kmemleak

During the startup phase, because the default CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE configuration is too small, the early log overflows during the system startup phase, which will cause kmemleak to be automatically disabled. The log is as follows:

[    0.000000] kmemleak: Kernel memory leak detector disabled
[    0.000000] clk r_dsp_cache0 not found in of_sunxi_periph_cpus_clk_setup
[    0.000000] clk r_dsp_cache1 not found in of_sunxi_periph_cpus_clk_setup
[    0.000000] clocksource: timer: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 79635851949 ns
[    0.000000] arm_arch_timer: Architected cp15 timer(s) running at 24.00MHz (virt).
[    0.000000] clocksource: arch_sys_counter: mask: 0xffffffffffffff max_cycles: 0x588fe9dc0, max_idle_ns: 440795202592 ns
[    0.000005] sched_clock: 56 bits at 24MHz, resolution 41ns, wraps every 4398046511097ns
[    0.008203] Console: colour dummy device 80x25
[    0.012434] kmemleak: Early log buffer exceeded (1438), please increase DEBUG_KMEMLEAK_EARLY_LOG_SIZE

In this case, you need to reconfigure the kernel CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE parameter, and set this parameter according to the recommended value of the log or set the maximum value.

Memory leak scan

After booting, if there is a memory leak in the system, there will be a memory leak scanning log (after starting, wait for a while, about tens of seconds, if there is a leak, there will be a scan result), as shown below:

[   65.088222] kmemleak: 3 new suspected memory leaks (see /sys/kernel/debug/kmemleak)

By looking at the /sys/kernel/debug/kmemleak node, you can check where the suspected memory leak is. The log is as follows:

root@Linux:/# cat /sys/kernel/debug/kmemleak
unreferenced object 0xffffffc008efcc00 (size 512):
  comm "swapper/0", pid 1, jiffies 4294892779 (age 162.728s)
  hex dump (first 32 bytes):
    00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  backtrace:
    [<ffffff8008156eac>] __save_stack_trace+0x24/0x30
    [<ffffff80081574ac>] create_object+0x100/0x23c
    [<ffffff80085e7d28>] kmemleak_alloc+0x30/0x5c
    [<ffffff8008153cc4>] kmem_cache_alloc+0xd0/0x184
    [<ffffff800833a008>] hub_probe+0x144/0x804
    [<ffffff8008342190>] usb_probe_interface+0x1d4/0x1fc
    [<ffffff80082d18cc>] driver_probe_device+0x1b4/0x26c
    [<ffffff80082d1b04>] __device_attach_driver+0x9c/0xb0
    [<ffffff80082cff08>] bus_for_each_drv+0x84/0x94
    [<ffffff80082d1690>] __device_attach+0xa8/0x100
    [<ffffff80082d1c58>] device_initial_probe+0x10/0x18
    [<ffffff80082d0c84>] bus_probe_device+0x2c/0x8c
    [<ffffff80082cf094>] device_add+0x45c/0x51c
    [<ffffff800834063c>] usb_set_configuration+0x604/0x648
    [<ffffff800834aee0>] generic_probe+0x58/0x80
    [<ffffff8008341fa0>] usb_probe_device+0x28/0x44
unreferenced object 0xffffffc008efd000 (size 512):
  comm "swapper/0", pid 1, jiffies 4294892825 (age 162.544s)
  hex dump (first 32 bytes):
    00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  backtrace:
    [<ffffff8008156eac>] __save_stack_trace+0x24/0x30
    [<ffffff80081574ac>] create_object+0x100/0x23c
    [<ffffff80085e7d28>] kmemleak_alloc+0x30/0x5c
    [<ffffff8008153cc4>] kmem_cache_alloc+0xd0/0x184
    [<ffffff8008339ff8>] hub_probe+0x134/0x804
    [<ffffff8008342190>] usb_probe_interface+0x1d4/0x1fc
    [<ffffff80082d18cc>] driver_probe_device+0x1b4/0x26c
    [<ffffff80082d1b04>] __device_attach_driver+0x9c/0xb0
    [<ffffff80082cff08>] bus_for_each_drv+0x84/0x94
    [<ffffff80082d1690>] __device_attach+0xa8/0x100
    [<ffffff80082d1c58>] device_initial_probe+0x10/0x18
    [<ffffff80082d0c84>] bus_probe_device+0x2c/0x8c
    [<ffffff80082cf094>] device_add+0x45c/0x51c
    [<ffffff800834063c>] usb_set_configuration+0x604/0x648
    [<ffffff800834aee0>] generic_probe+0x58/0x80
    [<ffffff8008341fa0>] usb_probe_device+0x28/0x44
unreferenced object 0xffffffc008efd200 (size 512):
  comm "swapper/0", pid 1, jiffies 4294892825 (age 162.544s)
  hex dump (first 32 bytes):
    00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  backtrace:
    [<ffffff8008156eac>] __save_stack_trace+0x24/0x30
    [<ffffff80081574ac>] create_object+0x100/0x23c
    [<ffffff80085e7d28>] kmemleak_alloc+0x30/0x5c
    [<ffffff8008153cc4>] kmem_cache_alloc+0xd0/0x184
    [<ffffff800833a008>] hub_probe+0x144/0x804
    [<ffffff8008342190>] usb_probe_interface+0x1d4/0x1fc
    [<ffffff80082d18cc>] driver_probe_device+0x1b4/0x26c
    [<ffffff80082d1b04>] __device_attach_driver+0x9c/0xb0
    [<ffffff80082cff08>] bus_for_each_drv+0x84/0x94
    [<ffffff80082d1690>] __device_attach+0xa8/0x100
    [<ffffff80082d1c58>] device_initial_probe+0x10/0x18
    [<ffffff80082d0c84>] bus_probe_device+0x2c/0x8c
    [<ffffff80082cf094>] device_add+0x45c/0x51c
    [<ffffff800834063c>] usb_set_configuration+0x604/0x648
    [<ffffff800834aee0>] generic_probe+0x58/0x80
    [<ffffff8008341fa0>] usb_probe_device+0x28/0x44

Modifications to the actual code:

diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 46c99ef6bb82..04dc4a7ada46 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -1784,6 +1784,11 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
        /* We found a hub */
        dev_info(&intf->dev, "USB hub found\n");

+       {
+               struct usb_hub *test;
+               test = kcalloc(1, sizeof(*test), GFP_KERNEL);
+               test = kzalloc(sizeof(*test), GFP_KERNEL);
+       }
        hub = kzalloc(sizeof(*hub), GFP_KERNEL);
        if (!hub)
                return -ENOMEM;

kmemleak can only help solve some very simple memory leak problems. For more complex leak paths, kmemleak cannot handle it, and kmemleak also has the possibility of false detection.

Operations supported by kmemleak

  • echo scan > /sys/kernel/debug/kmemleak: trigger a scan
  • echo clear > /sys/kernel/debug/kmemleak: Clear the leaked information recorded by the current kmemleak
  • echo off > /sys/kernel/debug/kmemleak: Turn off kmemleak (irreversible)
  • echo stack=off > /sys/kernel/debug/kmemleak: Turn off task stack scanning
  • echo stack=on > /sys/kernel/debug/kmemleak: enable task stack scanning
  • echo scan=on > /sys/kernel/debug/kmemleak: Start automatic memory scanning thread
  • echo scan=off > /sys/kernel/debug/kmemleak: Stop automatic memory scanning thread
  • echo scan=<secs> > /sys/kernel/debug/kmemleak: Set the automatic scanning thread scanning interval. The default is 600. Setting 0 will stop scanning.
  • echo dump=<addr> > /sys/kernel/debug/kmemleak: dump the memory block information of a certain address, such as the above echo dump=0xffffffc008efd200 > /sys/kernel/debug/kmemleak to view detailed information

kmemleak=offIn addition, kmemleak can be turned off by passing through the kernel command line , kmemleak=onand kmemleak can be turned on by passing it. This premise is that kmemleak has been configured when compiling the kernel.

Scan algorithm steps

  1. Mark all objects as white (remaining white objects will be treated as orphans later);
  2. Scan the memory starting from the data section and stack, checking the values ​​against the addresses stored in the rbtree. If a pointer to a white object is found, the object is added to the gray list;
  3. Scan gray objects for matching addresses (some white objects will turn gray and be added to the end of the gray list) until the end of the gray set;
  4. Remaining white objects are considered orphans and are reported via /sys/kernel/debug/kmemleak;

As for more content on kmemleak, you can view Documentation/dev-tools/kmemleak.rstthe content.

Supongo que te gusta

Origin blog.csdn.net/weixin_41944449/article/details/123441820
Recomendado
Clasificación