Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/kern kmem(9): Create dtrace sdt probes for each kmem cac...



details:   https://anonhg.NetBSD.org/src/rev/cc4f24e092bc
branches:  trunk
changeset: 366565:cc4f24e092bc
user:      riastradh <riastradh%NetBSD.org@localhost>
date:      Mon May 30 20:28:30 2022 +0000

description:
kmem(9): Create dtrace sdt probes for each kmem cache size.

The names of the probes correspond to the names shown in vmstat -m.
This should make it much easier to track down who's allocating memory
when there's a leak, e.g. by getting a histogram of stack traces for
the matching kmem cache pool:

   # vmstat -m
   Memory resource pool statistics
   Name        Size Requests Fail Releases Pgreq Pgrel Npage Hiwat Minpg Maxpg Idle
   ...
   kmem-00128   256    62242    0        0  3891     0  3891  3891     0   inf    0
   ...
   # dtrace -n 'sdt:kmem:*:kmem-00128 { @[probefunc, stack()] = count() }'
   ^C

When there's no leak, the allocs and frees (probefunc) will be roughly
matched; when there's a leak, the allocs will far outnumber the frees.

diffstat:

 sys/kern/subr_kmem.c |  184 ++++++++++++++++++++++++++++++++++++++++++--------
 1 files changed, 152 insertions(+), 32 deletions(-)

diffs (290 lines):

diff -r 68e888d4835d -r cc4f24e092bc sys/kern/subr_kmem.c
--- a/sys/kern/subr_kmem.c      Mon May 30 20:28:18 2022 +0000
+++ b/sys/kern/subr_kmem.c      Mon May 30 20:28:30 2022 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: subr_kmem.c,v 1.84 2022/03/12 22:20:34 riastradh Exp $ */
+/*     $NetBSD: subr_kmem.c,v 1.85 2022/05/30 20:28:30 riastradh Exp $ */
 
 /*
  * Copyright (c) 2009-2020 The NetBSD Foundation, Inc.
@@ -78,7 +78,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: subr_kmem.c,v 1.84 2022/03/12 22:20:34 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: subr_kmem.c,v 1.85 2022/05/30 20:28:30 riastradh Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_kmem.h"
@@ -93,6 +93,7 @@
 #include <sys/cpu.h>
 #include <sys/asan.h>
 #include <sys/msan.h>
+#include <sys/sdt.h>
 
 #include <uvm/uvm_extern.h>
 #include <uvm/uvm_map.h>
@@ -102,42 +103,97 @@
 struct kmem_cache_info {
        size_t          kc_size;
        const char *    kc_name;
+#ifdef KDTRACE_HOOKS
+       const id_t      *kc_alloc_probe_id;
+       const id_t      *kc_free_probe_id;
+#endif
 };
 
+#define        KMEM_CACHE_SIZES(F)                                                   \
+       F(8, kmem-00008, kmem__00008)                                         \
+       F(16, kmem-00016, kmem__00016)                                        \
+       F(24, kmem-00024, kmem__00024)                                        \
+       F(32, kmem-00032, kmem__00032)                                        \
+       F(40, kmem-00040, kmem__00040)                                        \
+       F(48, kmem-00048, kmem__00048)                                        \
+       F(56, kmem-00056, kmem__00056)                                        \
+       F(64, kmem-00064, kmem__00064)                                        \
+       F(80, kmem-00080, kmem__00080)                                        \
+       F(96, kmem-00096, kmem__00096)                                        \
+       F(112, kmem-00112, kmem__00112)                                       \
+       F(128, kmem-00128, kmem__00128)                                       \
+       F(160, kmem-00160, kmem__00160)                                       \
+       F(192, kmem-00192, kmem__00192)                                       \
+       F(224, kmem-00224, kmem__00224)                                       \
+       F(256, kmem-00256, kmem__00256)                                       \
+       F(320, kmem-00320, kmem__00320)                                       \
+       F(384, kmem-00384, kmem__00384)                                       \
+       F(448, kmem-00448, kmem__00448)                                       \
+       F(512, kmem-00512, kmem__00512)                                       \
+       F(768, kmem-00768, kmem__00768)                                       \
+       F(1024, kmem-01024, kmem__01024)                                      \
+       /* end of KMEM_CACHE_SIZES */
+
+#define        KMEM_CACHE_BIG_SIZES(F)                                               \
+       F(2048, kmem-02048, kmem__02048)                                      \
+       F(4096, kmem-04096, kmem__04096)                                      \
+       F(8192, kmem-08192, kmem__08192)                                      \
+       F(16384, kmem-16384, kmem__16384)                                     \
+       /* end of KMEM_CACHE_BIG_SIZES */
+
+/* sdt:kmem:alloc:kmem-* probes */
+#define        F(SZ, NAME, PROBENAME)                                                \
+       SDT_PROBE_DEFINE4(sdt, kmem, alloc, PROBENAME,                        \
+           "void *"/*ptr*/,                                                  \
+           "size_t"/*requested_size*/,                                       \
+           "size_t"/*allocated_size*/,                                       \
+           "km_flag_t"/*kmflags*/);
+KMEM_CACHE_SIZES(F);
+KMEM_CACHE_BIG_SIZES(F);
+#undef F
+
+/* sdt:kmem:free:kmem-* probes */
+#define        F(SZ, NAME, PROBENAME)                                                \
+       SDT_PROBE_DEFINE3(sdt, kmem, free, PROBENAME,                         \
+           "void *"/*ptr*/,                                                  \
+           "size_t"/*requested_size*/,                                       \
+           "size_t"/*allocated_size*/);
+KMEM_CACHE_SIZES(F);
+KMEM_CACHE_BIG_SIZES(F);
+#undef F
+
+/* sdt:kmem:alloc:large, sdt:kmem:free:large probes */
+SDT_PROBE_DEFINE4(sdt, kmem, alloc, large,
+    "void *"/*ptr*/,
+    "size_t"/*requested_size*/,
+    "size_t"/*allocated_size*/,
+    "km_flag_t"/*kmflags*/);
+SDT_PROBE_DEFINE3(sdt, kmem, free, large,
+    "void *"/*ptr*/,
+    "size_t"/*requested_size*/,
+    "size_t"/*allocated_size*/);
+
+#ifdef KDTRACE_HOOKS
+#define        F(SZ, NAME, PROBENAME)                                                \
+       { SZ, #NAME,                                                          \
+         &sdt_sdt_kmem_alloc_##PROBENAME->id,                                \
+         &sdt_sdt_kmem_free_##PROBENAME->id },
+#else
+#define        F(SZ, NAME, PROBENAME)  { SZ, #NAME },
+#endif
+
 static const struct kmem_cache_info kmem_cache_sizes[] = {
-       {  8, "kmem-00008" },
-       { 16, "kmem-00016" },
-       { 24, "kmem-00024" },
-       { 32, "kmem-00032" },
-       { 40, "kmem-00040" },
-       { 48, "kmem-00048" },
-       { 56, "kmem-00056" },
-       { 64, "kmem-00064" },
-       { 80, "kmem-00080" },
-       { 96, "kmem-00096" },
-       { 112, "kmem-00112" },
-       { 128, "kmem-00128" },
-       { 160, "kmem-00160" },
-       { 192, "kmem-00192" },
-       { 224, "kmem-00224" },
-       { 256, "kmem-00256" },
-       { 320, "kmem-00320" },
-       { 384, "kmem-00384" },
-       { 448, "kmem-00448" },
-       { 512, "kmem-00512" },
-       { 768, "kmem-00768" },
-       { 1024, "kmem-01024" },
+       KMEM_CACHE_SIZES(F)
        { 0, NULL }
 };
 
 static const struct kmem_cache_info kmem_cache_big_sizes[] = {
-       { 2048, "kmem-02048" },
-       { 4096, "kmem-04096" },
-       { 8192, "kmem-08192" },
-       { 16384, "kmem-16384" },
+       KMEM_CACHE_BIG_SIZES(F)
        { 0, NULL }
 };
 
+#undef F
+
 /*
  * KMEM_ALIGN is the smallest guaranteed alignment and also the
  * smallest allocateable quantum.
@@ -177,6 +233,49 @@
 #define        kmem_size_check(p, sz)  /* nothing */
 #endif
 
+#ifndef KDTRACE_HOOKS
+
+static const id_t **const kmem_cache_alloc_probe_id = NULL;
+static const id_t **const kmem_cache_big_alloc_probe_id = NULL;
+static const id_t **const kmem_cache_free_probe_id = NULL;
+static const id_t **const kmem_cache_big_free_probe_id = NULL;
+
+#define        KMEM_CACHE_PROBE(ARRAY, INDEX, PTR, REQSIZE, ALLOCSIZE, FLAGS)        \
+       __nothing
+
+#else
+
+static const id_t *kmem_cache_alloc_probe_id[KMEM_CACHE_COUNT];
+static const id_t *kmem_cache_big_alloc_probe_id[KMEM_CACHE_COUNT];
+static const id_t *kmem_cache_free_probe_id[KMEM_CACHE_COUNT];
+static const id_t *kmem_cache_big_free_probe_id[KMEM_CACHE_COUNT];
+
+#define        KMEM_CACHE_PROBE(ARRAY, INDEX, PTR, REQSIZE, ALLOCSIZE, FLAGS) do     \
+{                                                                            \
+       id_t id;                                                              \
+                                                                             \
+       KDASSERT((INDEX) < __arraycount(ARRAY));                              \
+       if (__predict_false((id = *(ARRAY)[INDEX]) != 0)) {                   \
+               (*sdt_probe_func)(id,                                         \
+                   (uintptr_t)(PTR),                                         \
+                   (uintptr_t)(REQSIZE),                                     \
+                   (uintptr_t)(ALLOCSIZE),                                   \
+                   (uintptr_t)(FLAGS),                                       \
+                   (uintptr_t)0);                                            \
+       }                                                                     \
+} while (0)
+
+#endif /* KDTRACE_HOOKS */
+
+#define        KMEM_CACHE_ALLOC_PROBE(I, P, RS, AS, F)                               \
+       KMEM_CACHE_PROBE(kmem_cache_alloc_probe_id, I, P, RS, AS, F)
+#define        KMEM_CACHE_BIG_ALLOC_PROBE(I, P, RS, AS, F)                           \
+       KMEM_CACHE_PROBE(kmem_cache_big_alloc_probe_id, I, P, RS, AS, F)
+#define        KMEM_CACHE_FREE_PROBE(I, P, RS, AS)                                   \
+       KMEM_CACHE_PROBE(kmem_cache_free_probe_id, I, P, RS, AS, 0)
+#define        KMEM_CACHE_BIG_FREE_PROBE(I, P, RS, AS)                               \
+       KMEM_CACHE_PROBE(kmem_cache_big_free_probe_id, I, P, RS, AS, 0)
+
 CTASSERT(KM_SLEEP == PR_WAITOK);
 CTASSERT(KM_NOSLEEP == PR_NOWAIT);
 
@@ -203,17 +302,25 @@
        size = kmem_roundup_size(requested_size);
        allocsz = size + SIZE_SIZE;
 
-       if ((index = ((allocsz -1) >> KMEM_SHIFT))
+       if ((index = ((allocsz - 1) >> KMEM_SHIFT))
            < kmem_cache_maxidx) {
                pc = kmem_cache[index];
+               p = pool_cache_get(pc, kmflags);
+               KMEM_CACHE_ALLOC_PROBE(index,
+                   p, requested_size, allocsz, kmflags);
        } else if ((index = ((allocsz - 1) >> KMEM_BIG_SHIFT))
            < kmem_cache_big_maxidx) {
                pc = kmem_cache_big[index];
+               p = pool_cache_get(pc, kmflags);
+               KMEM_CACHE_BIG_ALLOC_PROBE(index,
+                   p, requested_size, allocsz, kmflags);
        } else {
                int ret = uvm_km_kmem_alloc(kmem_va_arena,
                    (vsize_t)round_page(size),
                    ((kmflags & KM_SLEEP) ? VM_SLEEP : VM_NOSLEEP)
                     | VM_INSTANTFIT, (vmem_addr_t *)&p);
+               SDT_PROBE4(sdt, kmem, alloc, large,
+                   ret ? NULL : p, requested_size, round_page(size), kmflags);
                if (ret) {
                        return NULL;
                }
@@ -221,8 +328,6 @@
                return p;
        }
 
-       p = pool_cache_get(pc, kmflags);
-
        if (__predict_true(p != NULL)) {
                FREECHECK_OUT(&kmem_freecheck, p);
                kmem_size_set(p, requested_size);
@@ -264,14 +369,18 @@
        size = kmem_roundup_size(requested_size);
        allocsz = size + SIZE_SIZE;
 
-       if ((index = ((allocsz -1) >> KMEM_SHIFT))
+       if ((index = ((allocsz - 1) >> KMEM_SHIFT))
            < kmem_cache_maxidx) {
+               KMEM_CACHE_FREE_PROBE(index, p, requested_size, allocsz);
                pc = kmem_cache[index];
        } else if ((index = ((allocsz - 1) >> KMEM_BIG_SHIFT))
            < kmem_cache_big_maxidx) {
+               KMEM_CACHE_BIG_FREE_PROBE(index, p, requested_size, allocsz);
                pc = kmem_cache_big[index];
        } else {
                FREECHECK_IN(&kmem_freecheck, p);
+               SDT_PROBE3(sdt, kmem, free, large,
+                   p, requested_size, round_page(size));
                uvm_km_kmem_free(kmem_va_arena, (vaddr_t)p,
                    round_page(size));
                return;
@@ -339,6 +448,7 @@
 
 static size_t
 kmem_create_caches(const struct kmem_cache_info *array,
+    const id_t *alloc_probe_table[], const id_t *free_probe_table[],
     pool_cache_t alloc_table[], size_t maxsize, int shift, int ipl)
 {
        size_t maxidx = 0;
@@ -388,6 +498,14 @@
 
                while (size <= cache_size) {
                        alloc_table[(size - 1) >> shift] = pc;
+                       if (alloc_probe_table) {
+                               alloc_probe_table[(size - 1) >> shift] =
+                                   array[i].kc_alloc_probe_id;
+                       }
+                       if (free_probe_table) {
+                               free_probe_table[(size - 1) >> shift] =
+                                   array[i].kc_free_probe_id;
+                       }
                        size += table_unit;
                }
        }
@@ -398,8 +516,10 @@
 kmem_init(void)
 {
        kmem_cache_maxidx = kmem_create_caches(kmem_cache_sizes,
+           kmem_cache_alloc_probe_id, kmem_cache_free_probe_id,
            kmem_cache, KMEM_MAXSIZE, KMEM_SHIFT, IPL_VM);
        kmem_cache_big_maxidx = kmem_create_caches(kmem_cache_big_sizes,
+           kmem_cache_big_alloc_probe_id, kmem_cache_big_free_probe_id,
            kmem_cache_big, PAGE_SIZE, KMEM_BIG_SHIFT, IPL_VM);
 }
 



Home | Main Index | Thread Index | Old Index