Skip to content

Commit d139c10

Browse files
authored
Merge pull request #376 from zenhack/leak-stacks
Report full stack traces when detecting leaked clients.
2 parents 63918f6 + 7772e28 commit d139c10

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)