Hi,

I would like to use btrace to debug refernce counting.  The idea
is to a a tracepoint for every type of refcnt we have.  When it
changes, print the actual object, the current counter and the change
value.

#!/usr/sbin/btrace
tracepoint:refcnt:inpcb{
        printf("%s %x %u %+d\n", probe, arg0, arg1, arg2)
}

It should look like this:

tracepoint:refcnt:inpcb fffffd8078e31840 0 +1
tracepoint:refcnt:inpcb fffffd8078e31840 1 +1
tracepoint:refcnt:inpcb fffffd8078e31840 2 +1
tracepoint:refcnt:inpcb fffffd8078e31840 3 -1
tracepoint:refcnt:inpcb fffffd8078e31840 2 +1
tracepoint:refcnt:inpcb fffffd8078e31840 3 -1
tracepoint:refcnt:inpcb fffffd8078e31840 2 -1
tracepoint:refcnt:inpcb fffffd8078e31840 1 -1

Unfortunately btrace cannot deal with negative numbers right now.
So it looks like this, but that can be fixed independently.

tracepoint:refcnt:inpcb fffffd8078e31840 0 +1
tracepoint:refcnt:inpcb fffffd8078e31840 1 +1
tracepoint:refcnt:inpcb fffffd8078e31840 2 +1
tracepoint:refcnt:inpcb fffffd8078e31840 3 +4294967295
tracepoint:refcnt:inpcb fffffd8078e31840 2 +1
tracepoint:refcnt:inpcb fffffd8078e31840 3 +4294967295
tracepoint:refcnt:inpcb fffffd8078e31840 2 +4294967295
tracepoint:refcnt:inpcb fffffd8078e31840 1 +4294967295

To debug leaks, btrace can also print kernel stack trace.

tracepoint:refcnt:inpcb fffffd8078e31840 0 +1
in_pcballoc+0x92
tcp_attach+0xd1
sonewconn+0x23d
syn_cache_get+0x1bf
tcp_input+0x885
ip_deliver+0xd3
ip6_input_if+0x762
ipv6_input+0x39
ether_input+0x3a2
if_input_process+0x6f
ifiq_process+0x69
taskq_thread+0xdc
proc_trampoline+0x17
kernel
tracepoint:refcnt:inpcb fffffd8078e31840 1 +1
in_pcbref+0x29
pf_inp_link+0x4e
tcp_input+0x8d2
ip_deliver+0xd3
ip6_input_if+0x762
ipv6_input+0x39
ether_input+0x3a2
if_input_process+0x6f
ifiq_process+0x69
taskq_thread+0xdc
proc_trampoline+0x17
kernel
tracepoint:refcnt:inpcb fffffd8078e31840 2 +1
in_pcbref+0x29
pf_mbuf_link_inpcb+0x27
tcp_output+0x1455
tcp_usrreq+0x386
sosend+0x37c
dofilewritev+0x14d
sys_write+0x51
syscall+0x314
Xsyscall+0x128
kernel

I register the tracepoint when initializing the refcnt.  There
exists a global array of possible refcnt types.  I implemented it
only for inpcb and tdb as a prove of concept.

Do we want that feature?

bluhm

Index: dev/dt/dt_prov_static.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/dev/dt/dt_prov_static.c,v
retrieving revision 1.12
diff -u -p -r1.12 dt_prov_static.c
--- dev/dt/dt_prov_static.c     26 Jan 2022 06:31:31 -0000      1.12
+++ dev/dt/dt_prov_static.c     16 Mar 2022 23:22:34 -0000
@@ -2,6 +2,7 @@
 
 /*
  * Copyright (c) 2019 Martin Pieuchot <m...@openbsd.org>
+ * Copyright (c) 2022 Alexander Bluhm <bl...@openbsd.org>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -87,6 +88,12 @@ DT_STATIC_PROBE1(smr, barrier_exit, "int
 DT_STATIC_PROBE0(smr, wakeup);
 DT_STATIC_PROBE2(smr, thread, "uint64_t", "uint64_t");
 
+/*
+ * reference counting
+ */
+DT_STATIC_PROBE0(refcnt, none);
+DT_STATIC_PROBE3(refcnt, inpcb, "void *", "int", "int");
+DT_STATIC_PROBE3(refcnt, tdb, "void *", "int", "int");
 
 /*
  * List of all static probes
@@ -127,15 +134,24 @@ struct dt_probe *dtps_static[] = {
        &_DT_STATIC_P(smr, barrier_exit),
        &_DT_STATIC_P(smr, wakeup),
        &_DT_STATIC_P(smr, thread),
+       /* refcnt */
+       &_DT_STATIC_P(refcnt, none),
+       &_DT_STATIC_P(refcnt, inpcb),
+       &_DT_STATIC_P(refcnt, tdb),
 };
 
+struct dt_probe **dtps_index_refcnt;
+
 int
 dt_prov_static_init(void)
 {
        int i;
 
-       for (i = 0; i < nitems(dtps_static); i++)
+       for (i = 0; i < nitems(dtps_static); i++) {
+               if (dtps_static[i] == &_DT_STATIC_P(refcnt, none))
+                       dtps_index_refcnt = &dtps_static[i];
                dt_dev_register_probe(dtps_static[i]);
+       }
 
        return i;
 }
Index: dev/dt/dtvar.h
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/dev/dt/dtvar.h,v
retrieving revision 1.13
diff -u -p -r1.13 dtvar.h
--- dev/dt/dtvar.h      27 Feb 2022 10:14:01 -0000      1.13
+++ dev/dt/dtvar.h      16 Mar 2022 23:22:34 -0000
@@ -2,6 +2,7 @@
 
 /*
  * Copyright (c) 2019 Martin Pieuchot <m...@openbsd.org>
+ * Copyright (c) 2022 Alexander Bluhm <bl...@openbsd.org>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -313,11 +314,29 @@ extern volatile uint32_t  dt_tracing;     /* 
 #define        DT_STATIC_ENTER(func, name, args...) do {                       
\
        extern struct dt_probe _DT_STATIC_P(func, name);                \
        struct dt_probe *dtp = &_DT_STATIC_P(func, name);               \
-       struct dt_provider *dtpv = dtp->dtp_prov;                       \
                                                                        \
        if (__predict_false(dt_tracing) &&                              \
            __predict_false(dtp->dtp_recording)) {                      \
+               struct dt_provider *dtpv = dtp->dtp_prov;               \
+                                                                       \
                dtpv->dtpv_enter(dtpv, dtp, args);                      \
+       }                                                               \
+} while (0)
+
+#define _DT_INDEX_P(func)              (dtps_index_##func)
+
+#define DT_INDEX_ENTER(func, index, args...) do {                      \
+       extern struct dt_probe **_DT_INDEX_P(func);                     \
+                                                                       \
+       if (__predict_false(dt_tracing) &&                              \
+           _DT_INDEX_P(func) != NULL && index > 0) {                   \
+               struct dt_probe *dtp = _DT_INDEX_P(func)[index];        \
+                                                                       \
+                   if(__predict_false(dtp->dtp_recording)) {           \
+                       struct dt_provider *dtpv = dtp->dtp_prov;       \
+                                                                       \
+                       dtpv->dtpv_enter(dtpv, dtp, args);              \
+               }                                                       \
        }                                                               \
 } while (0)
 
Index: kern/kern_synch.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/kern/kern_synch.c,v
retrieving revision 1.184
diff -u -p -r1.184 kern_synch.c
--- kern/kern_synch.c   16 Mar 2022 14:13:01 -0000      1.184
+++ kern/kern_synch.c   16 Mar 2022 23:25:13 -0000
@@ -804,31 +804,37 @@ sys___thrwakeup(struct proc *p, void *v,
 void
 refcnt_init(struct refcnt *r)
 {
+       refcnt_init_trace(r, 0);
+}
+
+void
+refcnt_init_trace(struct refcnt *r, int id)
+{
+       r->r_id = id;
        atomic_store_int(&r->r_refs, 1);
+       TRACEINDEX(refcnt, r->r_id, r, 0, +1);
 }
 
 void
 refcnt_take(struct refcnt *r)
 {
-#ifdef DIAGNOSTIC
-       u_int refcnt;
+       u_int refs;
 
-       refcnt = atomic_inc_int_nv(&r->r_refs);
-       KASSERT(refcnt != 0);
-#else
-       atomic_inc_int(&r->r_refs);
-#endif
+       refs = atomic_inc_int_nv(&r->r_refs);
+       KASSERT(refs != 0);
+       TRACEINDEX(refcnt, r->r_id, r, refs - 1, +1);
 }
 
 int
 refcnt_rele(struct refcnt *r)
 {
-       u_int refcnt;
+       u_int refs;
 
-       refcnt = atomic_dec_int_nv(&r->r_refs);
-       KASSERT(refcnt != ~0);
+       refs = atomic_dec_int_nv(&r->r_refs);
+       KASSERT(refs != ~0);
+       TRACEINDEX(refcnt, r->r_id, r, refs + 1, -1);
 
-       return (refcnt == 0);
+       return (refs == 0);
 }
 
 void
@@ -842,26 +848,37 @@ void
 refcnt_finalize(struct refcnt *r, const char *wmesg)
 {
        struct sleep_state sls;
-       u_int refcnt;
+       u_int refs;
 
-       refcnt = atomic_dec_int_nv(&r->r_refs);
-       while (refcnt) {
+       refs = atomic_dec_int_nv(&r->r_refs);
+       KASSERT(refs != ~0);
+       TRACEINDEX(refcnt, r->r_id, r, refs + 1, -1);
+       while (refs) {
                sleep_setup(&sls, r, PWAIT, wmesg, 0);
-               refcnt = atomic_load_int(&r->r_refs);
-               sleep_finish(&sls, refcnt);
+               refs = atomic_load_int(&r->r_refs);
+               sleep_finish(&sls, refs);
        }
+       TRACEINDEX(refcnt, r->r_id, r, refs, 0);
 }
 
 int
 refcnt_shared(struct refcnt *r)
 {
-       return (atomic_load_int(&r->r_refs) > 1);
+       u_int refs;
+
+       refs = atomic_load_int(&r->r_refs);
+       TRACEINDEX(refcnt, r->r_id, r, refs, 0);
+       return (refs > 1);
 }
 
 unsigned int
 refcnt_read(struct refcnt *r)
 {
-       return (atomic_load_int(&r->r_refs));
+       u_int refs;
+
+       refs = atomic_load_int(&r->r_refs);
+       TRACEINDEX(refcnt, r->r_id, r, refs, 0);
+       return (refs);
 }
 
 void
Index: netinet/in_pcb.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/netinet/in_pcb.c,v
retrieving revision 1.261
diff -u -p -r1.261 in_pcb.c
--- netinet/in_pcb.c    14 Mar 2022 22:38:43 -0000      1.261
+++ netinet/in_pcb.c    16 Mar 2022 23:22:34 -0000
@@ -234,7 +234,7 @@ in_pcballoc(struct socket *so, struct in
                return (ENOBUFS);
        inp->inp_table = table;
        inp->inp_socket = so;
-       refcnt_init(&inp->inp_refcnt);
+       refcnt_init_trace(&inp->inp_refcnt, DT_REFCNT_ID_INPCB);
        inp->inp_seclevel[SL_AUTH] = IPSEC_AUTH_LEVEL_DEFAULT;
        inp->inp_seclevel[SL_ESP_TRANS] = IPSEC_ESP_TRANS_LEVEL_DEFAULT;
        inp->inp_seclevel[SL_ESP_NETWORK] = IPSEC_ESP_NETWORK_LEVEL_DEFAULT;
Index: netinet/ip_ipsp.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/netinet/ip_ipsp.c,v
retrieving revision 1.269
diff -u -p -r1.269 ip_ipsp.c
--- netinet/ip_ipsp.c   10 Mar 2022 15:21:08 -0000      1.269
+++ netinet/ip_ipsp.c   16 Mar 2022 23:22:34 -0000
@@ -1048,7 +1048,7 @@ tdb_alloc(u_int rdomain)
 
        tdbp = pool_get(&tdb_pool, PR_WAITOK | PR_ZERO);
 
-       refcnt_init(&tdbp->tdb_refcnt);
+       refcnt_init_trace(&tdbp->tdb_refcnt, DT_REFCNT_ID_TDB);
        mtx_init(&tdbp->tdb_mtx, IPL_SOFTNET);
        TAILQ_INIT(&tdbp->tdb_policy_head);
 
Index: sys/refcnt.h
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/sys/refcnt.h,v
retrieving revision 1.6
diff -u -p -r1.6 refcnt.h
--- sys/refcnt.h        16 Mar 2022 14:13:01 -0000      1.6
+++ sys/refcnt.h        16 Mar 2022 23:22:34 -0000
@@ -21,24 +21,30 @@
 
 /*
  * Locks used to protect struct members in this file:
+ *     I       immutable after creation
  *     a       atomic operations
  */
 
 struct refcnt {
+       int             r_id;           /* [I] index for dt(4) tracing  */
        unsigned int    r_refs;         /* [a] reference counter */
 };
 
-#define REFCNT_INITIALIZER()           { .r_refs = 1 }
+#define REFCNT_INITIALIZER()           { .r_id = 0, .r_refs = 1 }
 
 #ifdef _KERNEL
 
 void   refcnt_init(struct refcnt *);
+void   refcnt_init_trace(struct refcnt *, int id);
 void   refcnt_take(struct refcnt *);
 int    refcnt_rele(struct refcnt *);
 void   refcnt_rele_wake(struct refcnt *);
 void   refcnt_finalize(struct refcnt *, const char *);
 int    refcnt_shared(struct refcnt *);
 unsigned int   refcnt_read(struct refcnt *);
+
+#define DT_REFCNT_ID_INPCB     1
+#define DT_REFCNT_ID_TDB       2
 
 #endif /* _KERNEL */
 
Index: sys/tracepoint.h
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/sys/tracepoint.h,v
retrieving revision 1.1
diff -u -p -r1.1 tracepoint.h
--- sys/tracepoint.h    21 Jan 2020 16:16:23 -0000      1.1
+++ sys/tracepoint.h    16 Mar 2022 23:22:34 -0000
@@ -25,11 +25,13 @@
 #if NDT > 0
 #include <dev/dt/dtvar.h>
 
-#define        TRACEPOINT(func, name, args...) DT_STATIC_ENTER(func, name, 
args)
+#define TRACEPOINT(func, name, args...)        DT_STATIC_ENTER(func, name, 
args)
+#define TRACEINDEX(func, index, args...) DT_INDEX_ENTER(func, index, args)
 
 #else /* NDT > 0 */
 
-#define        TRACEPOINT(func, name, args...)
+#define TRACEPOINT(func, name, args...)
+#define TRACEINDEX(func, index, args...)
 
 #endif /* NDT > 0 */
 #endif /* _KERNEL */

Reply via email to