]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph-client.git/commitdiff
ceph: bookkeeper for detecting memory leaks
authorYehuda Sadeh <yehuda@hq.newdream.net>
Wed, 2 Dec 2009 00:22:30 +0000 (16:22 -0800)
committerYehuda Sadeh <yehuda@hq.newdream.net>
Mon, 14 Dec 2009 19:41:55 +0000 (11:41 -0800)
fs/ceph/Kconfig
fs/ceph/Makefile
fs/ceph/bookkeeper.c [new file with mode: 0644]
fs/ceph/bookkeeper.h [new file with mode: 0644]
fs/ceph/crush/crush.c
fs/ceph/debugfs.c
fs/ceph/super.c
fs/ceph/super.h

index bc1fbd95618722dca4b7486498dd1253c7c7d027..129a0725fa302989d154fbbddae1cfcc5b6a88d9 100644 (file)
@@ -24,3 +24,9 @@ config CEPH_FS_PRETTYDEBUG
 
          If unsure, say N.
 
+config CEPH_BOOKKEEPER
+       bool "Ceph leaks detection tool"
+       depends on CEPH_FS
+       help
+         Leaks detection tool for the Ceph fs.
+
index 827629c85768b67506ceadf04c68e2e574145ee0..d43dd2354a1cc8436675e10c9f37ef55c094461c 100644 (file)
@@ -14,7 +14,7 @@ ceph-objs := super.o inode.o dir.o file.o addr.o ioctl.o \
        osd_client.o osdmap.o crush/crush.o crush/mapper.o crush/hash.o \
        debugfs.o \
        auth.o auth_none.o \
-       ceph_fs.o ceph_strings.o ceph_hash.o ceph_frag.o
+       ceph_fs.o ceph_strings.o ceph_hash.o ceph_frag.o bookkeeper.o
 
 else
 #Otherwise we were called directly from the command
diff --git a/fs/ceph/bookkeeper.c b/fs/ceph/bookkeeper.c
new file mode 100644 (file)
index 0000000..af03a09
--- /dev/null
@@ -0,0 +1,164 @@
+#include "ceph_debug.h"
+
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/kallsyms.h>
+
+#define CEPH_OVERRIDE_BOOKKEEPER /* avoid kmalloc/kfree recursion */
+
+#define CEPH_BK_MAGIC 0x140985AC
+
+int ceph_debug_tools __read_mostly = -1;
+#define DOUT_VAR ceph_debug_tools
+#define DOUT_MASK DOUT_MASK_TOOLS
+#include "super.h"
+
+static struct list_head _bk_allocs;
+
+static DEFINE_SPINLOCK(_bk_lock);
+
+static size_t _total_alloc;
+static size_t _total_free;
+
+struct alloc_data {
+       u32 prefix_magic;
+       struct list_head node;
+       size_t size;
+       char *fname;
+       int line;
+       u32 suffix_magic;
+};
+
+void *ceph_kmalloc(char *fname, int line, size_t size, gfp_t flags)
+{
+       struct alloc_data *p = kmalloc(size+sizeof(struct alloc_data), flags);
+
+       if (!p)
+               return NULL;
+
+       p->prefix_magic = CEPH_BK_MAGIC;
+       p->size = size;
+       p->fname = fname;
+       p->line = line;
+       p->suffix_magic = CEPH_BK_MAGIC;
+
+       spin_lock(&_bk_lock);
+       _total_alloc += size;
+
+       list_add_tail(&p->node, &_bk_allocs);
+       spin_unlock(&_bk_lock);
+
+       return ((void *)p)+sizeof(struct alloc_data);
+}
+
+void ceph_kfree(const void *ptr)
+{
+       struct alloc_data *p = (struct alloc_data *)(ptr -
+                                                    sizeof(struct alloc_data));
+       int overrun = 0;
+
+       if (!ptr)
+               return;
+
+       if (p->prefix_magic != CEPH_BK_MAGIC) {
+               printk(KERN_ERR "ERROR: memory overrun (under)!\n");
+               overrun = 1;
+       }
+
+       if (p->suffix_magic != CEPH_BK_MAGIC) {
+               printk(KERN_ERR "ERROR: Memory overrun (over)!\n");
+               overrun = 1;
+       }
+
+       if (overrun) {
+               printk(KERN_ERR "Memory allocated at %s(%d): p=%p (%zu bytes)\n",
+                    p->fname, p->line, ((void *)p)+sizeof(struct alloc_data),
+                    p->size);
+       }
+
+       BUG_ON(overrun);
+
+       spin_lock(&_bk_lock);
+       _total_free += p->size;
+       list_del(&p->node);
+       spin_unlock(&_bk_lock);
+
+       kfree(p);
+
+       return;
+}
+
+
+void ceph_bookkeeper_dump(void)
+{
+       struct list_head *p;
+       struct alloc_data *entry;
+
+       printk(KERN_ERR "bookkeeper: total bytes alloc: %zu\n", _total_alloc);
+       printk(KERN_ERR "bookkeeper: total bytes free: %zu\n", _total_free);
+
+       if (_total_alloc != _total_free) {
+               list_for_each(p, &_bk_allocs) {
+                       entry = list_entry(p, struct alloc_data, node);
+                       printk(KERN_ERR "%s(%d): p=%p (%zu bytes)\n", entry->fname,
+                            entry->line,
+                            ((void *)entry)+sizeof(struct alloc_data),
+                            entry->size);
+               }
+       } else {
+               dout("No leaks found! Yay!\n");
+       }
+}
+
+char *ceph_kstrdup(char *fname, int line, const char *src, gfp_t flags)
+{
+       int len;
+       char *dst;
+
+       if (!src)
+               return NULL;
+
+       len = strlen(src);
+       dst = ceph_kmalloc(fname, line, len + 1, flags);
+       if (!dst)
+               return NULL;
+
+       memcpy(dst, src, len);
+       dst[len] = '\0';
+
+       return dst;
+}
+
+char *ceph_kstrndup(char *fname, int line, const char *src, int n, gfp_t flags)
+{
+       int len;
+       char *dst;
+
+       if (!src)
+               return NULL;
+
+       len = strlen(src);
+       if (len > n)
+               len = n;
+
+       dst = ceph_kmalloc(fname, line, len + 1, flags);
+       if (!dst)
+               return NULL;
+
+       memcpy(dst, src, len);
+       dst[len] = '\0';
+
+       return dst;
+}
+
+void ceph_bookkeeper_init(void)
+{
+       printk(KERN_ERR "bookkeeper: start\n");
+       dout("bookkeeper: start\n");
+       INIT_LIST_HEAD(&_bk_allocs);
+}
+
+void ceph_bookkeeper_finalize(void)
+{
+       ceph_bookkeeper_dump();
+}
diff --git a/fs/ceph/bookkeeper.h b/fs/ceph/bookkeeper.h
new file mode 100644 (file)
index 0000000..598ae8d
--- /dev/null
@@ -0,0 +1,46 @@
+#ifdef CONFIG_CEPH_BOOKKEEPER
+
+#ifndef _FS_CEPH_BOOKKEEPER_H
+#define _FS_CEPH_BOOKKEEPER_H
+
+#include <linux/module.h>
+
+extern void ceph_bookkeeper_dump(void);
+extern void ceph_bookkeeper_init(void);
+extern void ceph_bookkeeper_finalize(void);
+
+extern void *ceph_kmalloc(char *fname, int line, size_t size, gfp_t flags);
+extern char *ceph_kstrdup(char *fname, int line, const char *src, gfp_t flags);
+extern char *ceph_kstrndup(char *fname, int line, const char *src, int n, gfp_t flags);
+
+extern void ceph_kfree(const void *ptr);
+
+#endif
+
+
+#ifndef CEPH_OVERRIDE_BOOKKEEPER
+#define CEPH_BOOKKEEPER_DEFINED
+#define kmalloc(size, flags)   ceph_kmalloc(__FILE__, __LINE__, size, flags)
+#define kzalloc(size, flags)   ceph_kmalloc(__FILE__, __LINE__, size, \
+                                            flags | __GFP_ZERO)
+#define kcalloc(n, size, flags)        ceph_kmalloc(__FILE__, __LINE__, n * size, \
+                                            flags | __GFP_ZERO)
+#define kstrdup(src, flags)    ceph_kstrdup(__FILE__, __LINE__, \
+                                                     src, flags)
+#define kstrndup(src, n, flags)        ceph_kstrndup(__FILE__, __LINE__, \
+                                                     src, n, flags)
+#define kfree  ceph_kfree
+#endif
+
+#ifdef CEPH_DISABLE_BOOKKEEPER
+#ifdef CEPH_BOOKKEEPER_DEFINED
+#undef kmalloc
+#undef kzalloc
+#undef kcalloc
+#undef kstrdup
+#undef kstrndup
+#undef kfree
+#endif
+#endif
+
+#endif
index fabd302e5779c0188b55916be81d7913dce84105..055f84a67fdcd641c3b82b95030b149b2f8b60a2 100644 (file)
@@ -1,6 +1,7 @@
 
 #ifdef __KERNEL__
 # include <linux/slab.h>
+# include "../bookkeeper.h"
 #else
 # include <stdlib.h>
 # include <assert.h>
index b90fc3e1ff709f2ec82321a937b541933337caf4..21a8b42121050b230212ae06e45f1fe9162a8f94 100644 (file)
@@ -27,6 +27,9 @@
  */
 
 static struct dentry *ceph_debugfs_dir;
+#ifdef CONFIG_CEPH_BOOKKEEPER
+static struct dentry *ceph_debugfs_bookkeeper;
+#endif
 
 static int monmap_show(struct seq_file *s, void *p)
 {
@@ -318,6 +321,24 @@ DEFINE_SHOW_FUNC(osdc_show)
 DEFINE_SHOW_FUNC(dentry_lru_show)
 DEFINE_SHOW_FUNC(caps_show)
 
+#ifdef CONFIG_CEPH_BOOKKEEPER
+static int debugfs_bookkeeper_set(void *data, u64 val)
+{
+       if (val)
+               ceph_bookkeeper_dump();
+       return 0;
+}
+
+static int debugfs_bookkeeper_get(void *data, u64 *val)
+{
+       *val = 0;
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(bookkeeper_fops, debugfs_bookkeeper_get,
+                       debugfs_bookkeeper_set, "%llu\n");
+#endif
+
 int __init ceph_debugfs_init(void)
 {
        ceph_debugfs_dir = debugfs_create_dir("ceph", NULL);
@@ -407,6 +428,15 @@ int ceph_debugfs_client_init(struct ceph_client *client)
        if (!client->debugfs_caps)
                goto out;
 
+#ifdef CONFIG_CEPH_BOOKKEEPER
+       ceph_debugfs_bookkeeper = debugfs_create_file("show_bookkeeper",
+                                       0600,
+                                       ceph_debugfs_dir,
+                                       NULL,
+                                       &bookkeeper_fops);
+       if (!ceph_debugfs_bookkeeper)
+               goto out;
+#endif
        return 0;
 
 out:
@@ -424,6 +454,9 @@ void ceph_debugfs_client_cleanup(struct ceph_client *client)
        debugfs_remove(client->osdc.debugfs_file);
        debugfs_remove(client->mdsc.debugfs_file);
        debugfs_remove(client->monc.debugfs_file);
+#ifdef CONFIG_CEPH_BOOKKEEPER
+       debugfs_remove(ceph_debugfs_bookkeeper);
+#endif
        debugfs_remove(client->debugfs_dir);
 }
 
index a828943296c50b4820889de52543a18b91d30a59..aa3e42ee139a1385111a385ca908bf113f203411 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/version.h>
 #include <linux/vmalloc.h>
 
+#include "bookkeeper.h"
 #include "decode.h"
 #include "super.h"
 #include "mon_client.h"
@@ -931,6 +932,9 @@ static int __init init_ceph(void)
 {
        int ret = 0;
 
+#ifdef CONFIG_CEPH_BOOKKEEPER
+       ceph_bookkeeper_init();
+#endif
        ret = ceph_debugfs_init();
        if (ret < 0)
                goto out;
@@ -972,6 +976,9 @@ static void __exit exit_ceph(void)
        destroy_caches();
        ceph_msgr_exit();
        ceph_debugfs_cleanup();
+#ifdef CONFIG_CEPH_BOOKKEEPER
+       ceph_bookkeeper_finalize();
+#endif
 }
 
 module_init(init_ceph);
index de5e324149784aab20363a0dfeb41e579286582d..6fe65dbadec809ba0e7564e50841fdf35083ba80 100644 (file)
@@ -3,6 +3,9 @@
 
 #include "ceph_debug.h"
 
+#define CEPH_DISABLE_BOOKKEEPER
+#include "bookkeeper.h"
+
 #include <asm/unaligned.h>
 #include <linux/backing-dev.h>
 #include <linux/completion.h>
@@ -12,6 +15,9 @@
 #include <linux/pagemap.h>
 #include <linux/wait.h>
 
+#undef CEPH_DISABLE_BOOKKEEPER
+#include "bookkeeper.h"
+
 #include "types.h"
 #include "messenger.h"
 #include "msgpool.h"
@@ -19,6 +25,7 @@
 #include "mds_client.h"
 #include "osd_client.h"
 #include "ceph_fs.h"
+#include "bookkeeper.h"
 
 /* f_type in struct statfs */
 #define CEPH_SUPER_MAGIC 0x00c36400