Skip to content

Commit 83768a6

Browse files
committed
Report full stack traces when detecting leaked clients.
This patch adjusts the logic for detecting client leaks to include full stack traces for where they were created, rather than just the source location.
1 parent 2924ffa commit 83768a6

File tree

1 file changed

+24
-18
lines changed

1 file changed

+24
-18
lines changed

capability.go

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"strconv"
88
"sync"
99

10+
"capnproto.org/go/capnp/v3/exp/bufferpool"
1011
"capnproto.org/go/capnp/v3/flowcontrol"
1112
"capnproto.org/go/capnp/v3/internal/syncutil"
1213
)
@@ -114,9 +115,10 @@ type ClientKind = struct {
114115
}
115116

116117
type client struct {
117-
creatorFunc int
118-
creatorFile string
119-
creatorLine int
118+
creatorFunc int
119+
creatorFile string
120+
creatorStack string
121+
creatorLine int
120122

121123
mu sync.Mutex // protects the struct
122124
limiter flowcontrol.FlowLimiter
@@ -147,6 +149,19 @@ type clientHook struct {
147149
resolvedHook *clientHook // valid only if resolved is closed
148150
}
149151

152+
func (c Client) setupLeakReporting(creatorFunc int) {
153+
if clientLeakFunc == nil {
154+
return
155+
}
156+
c.creatorFunc = creatorFunc
157+
_, c.creatorFile, c.creatorLine, _ = runtime.Caller(2)
158+
buf := bufferpool.Default.Get(1e6)
159+
n := runtime.Stack(buf, false)
160+
c.creatorStack = string(buf[:n])
161+
bufferpool.Default.Put(buf)
162+
c.setFinalizer()
163+
}
164+
150165
// NewClient creates the first reference to a capability.
151166
// If hook is nil, then NewClient returns nil.
152167
//
@@ -165,11 +180,7 @@ func NewClient(hook ClientHook) Client {
165180
}
166181
h.resolvedHook = h
167182
c := Client{client: &client{h: h}}
168-
if clientLeakFunc != nil {
169-
c.creatorFunc = 1
170-
_, c.creatorFile, c.creatorLine, _ = runtime.Caller(1)
171-
c.setFinalizer()
172-
}
183+
c.setupLeakReporting(1)
173184
return c
174185
}
175186

@@ -192,11 +203,7 @@ func NewPromisedClient(hook ClientHook) (Client, *ClientPromise) {
192203
metadata: *NewMetadata(),
193204
}
194205
c := Client{client: &client{h: h}}
195-
if clientLeakFunc != nil {
196-
c.creatorFunc = 2
197-
_, c.creatorFile, c.creatorLine, _ = runtime.Caller(1)
198-
c.setFinalizer()
199-
}
206+
c.setupLeakReporting(2)
200207
return c, &ClientPromise{h: h}
201208
}
202209

@@ -460,11 +467,7 @@ func (c Client) AddRef() Client {
460467
c.h.refs++
461468
c.h.mu.Unlock()
462469
d := Client{client: &client{h: c.h}}
463-
if clientLeakFunc != nil {
464-
d.creatorFunc = 3
465-
_, d.creatorFile, d.creatorLine, _ = runtime.Caller(1)
466-
d.setFinalizer()
467-
}
470+
d.setupLeakReporting(3)
468471
return d
469472
}
470473

@@ -653,6 +656,9 @@ func finalizeClient(c *client) {
653656
} else {
654657
msg = fmt.Sprintf("leaked client created by %s on %s:%d", fname, c.creatorFile, c.creatorLine)
655658
}
659+
if c.creatorStack != "" {
660+
msg += "\nCreation stack trace:\n" + c.creatorStack + "\n"
661+
}
656662

657663
// finalizeClient will only be called if clientLeakFunc != nil.
658664
go clientLeakFunc(msg)

0 commit comments

Comments
 (0)