lkml.org 
[lkml]   [2005]   [Feb]   [20]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Date
    From
    Subject[PATCH] /proc/kmalloc
    I've been sitting on this for over a year now, kicking it out in the
    hopes that someone finds it useful. kernel.org was down when I was
    tidying this up so it's against 2.6.10 which is what I had handy.

    /proc/kmalloc allocation tracing

    This quick hack adds accounting for kmalloc/kfree callers. This can
    aid in tracking down memory leaks and large dynamic memory users. The
    stock version use ~280k of memory for hash tables and can track 32k
    active allocations.

    Here's some sample output from my laptop:

    total bytes allocated: 47118848
    slack bytes allocated: 8717262
    net bytes allocated: 2825920
    number of allocs: 132796
    number of frees: 122629
    number of callers: 325
    lost callers: 0
    lost allocs: 0
    unknown frees: 0

    total slack net alloc/free caller
    24576 0 0 3/3 copy_thread+0x1ad
    192 56 192 1/0 fbcon_startup+0xc9
    32768 0 32768 1/0 fbcon_startup+0x267
    18720 9360 32 585/584 bit_cursor+0x1a1
    8192 0 8192 1/0 sys_ioperm+0x6c
    8192 0 8192 1/0 register_framebuffer+0x10b
    512 0 512 1/0 fb_alloc_cmap+0x42
    512 0 512 1/0 fb_alloc_cmap+0x55
    8192 3192 8192 1/0 framebuffer_alloc+0x36
    512 0 512 1/0 fb_alloc_cmap+0x68
    64 0 64 1/0 fb_add_videomode+0x52
    10334976 4521552 0 80742/80742 soft_cursor+0x67
    32 0 32 1/0 mtrr_file_add+0x8c
    128 36 128 1/0 create_driver+0x20
    16842752 0 212992 4112/4060 dup_task_struct+0x3d
    1536 0 0 12/12 radeon_do_probe_i2c_edid+0x5f
    672 336 672 21/0 acpi_os_create_semaphore+0x17
    32 32 32 1/0 acpi_os_create_lock+0xd
    448 28 0 7/7 acpi_os_queue_for_execution+0x2a
    2016 252 1184 63/26 __request_region+0x20
    768 480 768 24/0 register_sysctl_table+0x24
    32 8 32 1/0 radeon_agp_alloc+0x6b
    128 52 128 1/0 radeon_agp_init+0x3f
    224 56 224 7/0 radeon_addmap+0x2b
    224 140 224 7/0 radeon_addmap+0x11f
    4096 1792 4096 1/0 radeon_addbufs_agp+0x197
    1024 896 1024 32/0 radeon_addbufs_agp+0x273
    2880 720 1728 15/6 groups_alloc+0x35
    32 28 0 1/1 radeon_ctxbitmap_next+0xfd
    4096 0 4096 1/0 radeon_ctxbitmap_init+0x26
    2048 732 2048 1/0 radeon_dma_setup+0x1a
    32 16 32 1/0 radeon_addctx+0x77
    768 0 768 6/0 __create_workqueue+0x3b
    32 20 32 1/0 radeon_setup+0x97
    32 16 32 1/0 radeon_setup+0xd7
    64 24 64 2/0 inter_module_register+0x1f
    128 32 0 1/1 sock_kmalloc+0x3d
    64 16 64 1/0 radeon_open_helper+0x51
    5347584 2076992 1048576 3520/3264 alloc_skb+0x34
    4160 1392 4160 28/0 param_sysfs_setup+0x4c
    514304 214784 0 1008/1008 pskb_expand_head+0x4c
    32 22 32 1/0 radeon_setunique+0x73
    32 15 32 1/0 radeon_setunique+0xca
    160 24 160 2/0 radeon_realloc+0x21
    576 360 192 18/12 radeon_vm_open+0x32
    192 0 192 1/0 radeon_stub_getminor+0xa4
    19200 5160 0 112/112 acpi_ut_initialize_buffer+0x24
    269344 111231 195008 5588/1890 acpi_ut_callocate+0x37
    24416 17461 0 761/761 acpi_ut_allocate+0x38
    512 248 512 1/0 radeon_do_init_cp+0x2b
    320 266 320 10/0 net_sysctl_strdup+0x2a
    64 40 64 2/0 use_module+0x8c
    ...



    Index: km/init/Kconfig
    ===================================================================
    --- km.orig/init/Kconfig 2004-12-24 13:35:24.000000000 -0800
    +++ km/init/Kconfig 2005-02-20 10:51:49.000000000 -0800
    @@ -287,6 +287,13 @@ config KALLSYMS_EXTRA_PASS
    reported. KALLSYMS_EXTRA_PASS is only a temporary workaround while
    you wait for kallsyms to be fixed.

    +config KMALLOC_ACCOUNTING
    + default n
    + bool "Enabled accounting of kmalloc/kfree allocations" if EMBEDDED
    + help
    + This option records kmalloc and kfree activity and reports it via
    + /proc/kmalloc.
    +
    config FUTEX
    bool "Enable futex support" if EMBEDDED
    default y
    Index: km/mm/slab.c
    ===================================================================
    --- km.orig/mm/slab.c 2004-12-24 13:35:59.000000000 -0800
    +++ km/mm/slab.c 2005-02-20 10:50:15.000000000 -0800
    @@ -2427,6 +2427,7 @@ EXPORT_SYMBOL(kmem_cache_alloc_node);
    void * __kmalloc (size_t size, int flags)
    {
    struct cache_sizes *csizep = malloc_sizes;
    + void *a;

    for (; csizep->cs_size; csizep++) {
    if (size > csizep->cs_size)
    @@ -2438,8 +2439,10 @@ void * __kmalloc (size_t size, int flags
    */
    BUG_ON(csizep->cs_cachep == NULL);
    #endif
    - return __cache_alloc(flags & GFP_DMA ?
    + a = __cache_alloc(flags & GFP_DMA ?
    csizep->cs_dmacachep : csizep->cs_cachep, flags);
    + kmalloc_account(a, csizep->cs_size, size);
    + return a;
    }
    return NULL;
    }
    @@ -2543,6 +2546,8 @@ void kfree (const void *objp)
    kmem_cache_t *c;
    unsigned long flags;

    + kfree_account(objp, ksize(objp));
    +
    if (!objp)
    return;
    local_irq_save(flags);
    Index: km/mm/kmallocacct.c
    ===================================================================
    --- /dev/null 1970-01-01 00:00:00.000000000 +0000
    +++ km/mm/kmallocacct.c 2005-02-20 12:25:59.000000000 -0800
    @@ -0,0 +1,182 @@
    +#include <linux/config.h>
    +#include <linux/seq_file.h>
    +#include <linux/kallsyms.h>
    +
    +struct kma_caller {
    + const void *caller;
    + int total, net, slack, allocs, frees;
    +};
    +
    +struct kma_list {
    + int callerhash;
    + const void *address;
    +};
    +
    +#define MAX_CALLER_TABLE 1024
    +#define MAX_ALLOC_TRACK 32768
    +
    +#define kma_hash(address, size) (((u32)address / (u32)size) % size)
    +
    +static struct kma_list kma_alloc[MAX_ALLOC_TRACK];
    +static struct kma_caller kma_caller[MAX_CALLER_TABLE];
    +
    +static int kma_callers;
    +static int kma_lost_callers, kma_lost_allocs, kma_unknown_frees;
    +static int kma_total, kma_net, kma_slack, kma_allocs, kma_frees;
    +static spinlock_t kma_lock = SPIN_LOCK_UNLOCKED;
    +
    +void __kmalloc_account(const void *caller, const void *addr, int size, int req)
    +{
    + int i, hasha, hashc;
    + unsigned long flags;
    +
    + spin_lock_irqsave(&kma_lock, flags);
    + if(req >= 0) /* kmalloc */
    + {
    + /* find callers slot */
    + hashc = kma_hash(caller, MAX_CALLER_TABLE);
    + for (i = 0; i < MAX_CALLER_TABLE; i++) {
    + if (!kma_caller[hashc].caller ||
    + kma_caller[hashc].caller == caller)
    + break;
    + hashc = (hashc + 1) % MAX_CALLER_TABLE;
    + }
    +
    + if (!kma_caller[hashc].caller)
    + kma_callers++;
    +
    + if (i < MAX_CALLER_TABLE) {
    + /* update callers stats */
    + kma_caller[hashc].caller = caller;
    + kma_caller[hashc].total += size;
    + kma_caller[hashc].net += size;
    + kma_caller[hashc].slack += size - req;
    + kma_caller[hashc].allocs++;
    +
    + /* add malloc to list */
    + hasha = kma_hash(addr, MAX_ALLOC_TRACK);
    + for (i = 0; i < MAX_ALLOC_TRACK; i++) {
    + if (!kma_alloc[hasha].callerhash)
    + break;
    + hasha = (hasha + 1) % MAX_ALLOC_TRACK;
    + }
    +
    + if(i < MAX_ALLOC_TRACK) {
    + kma_alloc[hasha].callerhash = hashc;
    + kma_alloc[hasha].address = addr;
    + }
    + else
    + kma_lost_allocs++;
    + }
    + else {
    + kma_lost_callers++;
    + kma_lost_allocs++;
    + }
    +
    + kma_total += size;
    + kma_net += size;
    + kma_slack += size - req;
    + kma_allocs++;
    + }
    + else { /* kfree */
    + hasha = kma_hash(addr, MAX_ALLOC_TRACK);
    + for (i = 0; i < MAX_ALLOC_TRACK ; i++) {
    + if (kma_alloc[hasha].address == addr)
    + break;
    + hasha = (hasha + 1) % MAX_ALLOC_TRACK;
    + }
    +
    + if (i < MAX_ALLOC_TRACK) {
    + hashc = kma_alloc[hasha].callerhash;
    + kma_alloc[hasha].callerhash = 0;
    + kma_caller[hashc].net -= size;
    + kma_caller[hashc].frees++;
    + }
    + else
    + kma_unknown_frees++;
    +
    + kma_net -= size;
    + kma_frees++;
    + }
    + spin_unlock_irqrestore(&kma_lock, flags);
    +}
    +
    +static void *as_start(struct seq_file *m, loff_t *pos)
    +{
    + int i;
    + loff_t n = *pos;
    +
    + if (!n) {
    + seq_printf(m, "total bytes allocated: %8d\n", kma_total);
    + seq_printf(m, "slack bytes allocated: %8d\n", kma_slack);
    + seq_printf(m, "net bytes allocated: %8d\n", kma_net);
    + seq_printf(m, "number of allocs: %8d\n", kma_allocs);
    + seq_printf(m, "number of frees: %8d\n", kma_frees);
    + seq_printf(m, "number of callers: %8d\n", kma_callers);
    + seq_printf(m, "lost callers: %8d\n",
    + kma_lost_callers);
    + seq_printf(m, "lost allocs: %8d\n",
    + kma_lost_allocs);
    + seq_printf(m, "unknown frees: %8d\n",
    + kma_unknown_frees);
    + seq_puts(m, "\n total slack net alloc/free caller\n");
    + }
    +
    + for (i = 0; i < MAX_CALLER_TABLE; i++) {
    + if(kma_caller[i].caller)
    + n--;
    + if(n < 0)
    + return (void *)(i+1);
    + }
    +
    + return 0;
    +}
    +
    +static void *as_next(struct seq_file *m, void *p, loff_t *pos)
    +{
    + int n = (int)p-1, i;
    + ++*pos;
    +
    + for (i = n + 1; i < MAX_CALLER_TABLE; i++)
    + if(kma_caller[i].caller)
    + return (void *)(i+1);
    +
    + return 0;
    +}
    +
    +static void as_stop(struct seq_file *m, void *p)
    +{
    +}
    +
    +static int as_show(struct seq_file *m, void *p)
    +{
    + int n = (int)p-1;
    + struct kma_caller *c;
    +#ifdef CONFIG_KALLSYMS
    + char *modname;
    + const char *name;
    + unsigned long offset = 0, size;
    + char namebuf[128];
    +
    + c = &kma_caller[n];
    + name = kallsyms_lookup((int)c->caller, &size, &offset, &modname,
    + namebuf);
    + seq_printf(m, "%8d %8d %8d %5d/%-5d %s+0x%lx\n",
    + c->total, c->slack, c->net, c->allocs, c->frees,
    + name, offset);
    +#else
    + c = &kma_caller[n];
    + seq_printf(m, "%8d %8d %8d %5d/%-5d %p\n",
    + c->total, c->slack, c->net, c->allocs, c->frees, c->caller);
    +#endif
    +
    + return 0;
    +}
    +
    +struct seq_operations kmalloc_account_op = {
    + .start = as_start,
    + .next = as_next,
    + .stop = as_stop,
    + .show = as_show,
    +};
    +
    Index: km/mm/Makefile
    ===================================================================
    --- km.orig/mm/Makefile 2004-12-24 13:35:00.000000000 -0800
    +++ km/mm/Makefile 2005-02-20 10:50:15.000000000 -0800
    @@ -12,6 +12,7 @@ obj-y := bootmem.o filemap.o mempool.o
    readahead.o slab.o swap.o truncate.o vmscan.o \
    $(mmu-y)

    +obj-$(CONFIG_KMALLOC_ACCOUNTING) += kmallocacct.o
    obj-$(CONFIG_SWAP) += page_io.o swap_state.o swapfile.o thrash.o
    obj-$(CONFIG_HUGETLBFS) += hugetlb.o
    obj-$(CONFIG_NUMA) += mempolicy.o
    Index: km/include/linux/slab.h
    ===================================================================
    --- km.orig/include/linux/slab.h 2004-12-24 13:35:40.000000000 -0800
    +++ km/include/linux/slab.h 2005-02-20 10:50:15.000000000 -0800
    @@ -53,6 +53,23 @@ typedef struct kmem_cache_s kmem_cache_t
    #define SLAB_CTOR_ATOMIC 0x002UL /* tell constructor it can't sleep */
    #define SLAB_CTOR_VERIFY 0x004UL /* tell constructor it's a verify call */

    +#ifdef CONFIG_KMALLOC_ACCOUNTING
    +void __kmalloc_account(const void *, const void *, int, int);
    +
    +static void inline kmalloc_account(const void *addr, int size, int req)
    +{
    + __kmalloc_account(__builtin_return_address(0), addr, size, req);
    +}
    +
    +static void inline kfree_account(const void *addr, int size)
    +{
    + __kmalloc_account(__builtin_return_address(0), addr, size, -1);
    +}
    +#else
    +#define kmalloc_account(a, b, c)
    +#define kfree_account(a, b)
    +#endif
    +
    /* prototypes */
    extern void __init kmem_cache_init(void);

    @@ -84,6 +101,7 @@ extern void *__kmalloc(size_t, int);

    static inline void *kmalloc(size_t size, int flags)
    {
    +#ifndef CONFIG_KMALLOC_ACCOUNTING
    if (__builtin_constant_p(size)) {
    int i = 0;
    #define CACHE(x) \
    @@ -102,6 +120,7 @@ found:
    malloc_sizes[i].cs_dmacachep :
    malloc_sizes[i].cs_cachep, flags);
    }
    +#endif
    return __kmalloc(size, flags);
    }

    Index: km/fs/proc/proc_misc.c
    ===================================================================
    --- km.orig/fs/proc/proc_misc.c 2004-12-24 13:34:00.000000000 -0800
    +++ km/fs/proc/proc_misc.c 2005-02-20 10:50:15.000000000 -0800
    @@ -358,6 +358,24 @@ static struct file_operations proc_slabi
    .release = seq_release,
    };

    +#ifdef CONFIG_KMALLOC_ACCOUNTING
    +
    +extern struct seq_operations kmalloc_account_op;
    +
    +static int kmalloc_account_open(struct inode *inode, struct file *file)
    +{
    + return seq_open(file, &kmalloc_account_op);
    +}
    +
    +static struct file_operations proc_kmalloc_account_operations = {
    + .open = kmalloc_account_open,
    + .read = seq_read,
    + .llseek = seq_lseek,
    + .release = seq_release,
    +};
    +
    +#endif
    +
    int show_stat(struct seq_file *p, void *v)
    {
    int i;
    @@ -617,6 +635,9 @@ void __init proc_misc_init(void)
    create_seq_entry("stat", 0, &proc_stat_operations);
    create_seq_entry("interrupts", 0, &proc_interrupts_operations);
    create_seq_entry("slabinfo",S_IWUSR|S_IRUGO,&proc_slabinfo_operations);
    +#ifdef CONFIG_KMALLOC_ACCOUNTING
    + create_seq_entry("kmalloc",S_IRUGO,&proc_kmalloc_account_operations);
    +#endif
    create_seq_entry("buddyinfo",S_IRUGO, &fragmentation_file_operations);
    create_seq_entry("vmstat",S_IRUGO, &proc_vmstat_file_operations);
    create_seq_entry("diskstats", 0, &proc_diskstats_operations);


    --
    Mathematics is the supreme nostalgia of our time.
    -
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

    \
     
     \ /
      Last update: 2005-03-22 14:10    [W:4.374 / U:0.008 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site