Resource leak detection

399 views
Skip to first unread message

Dmitry Vyukov

unread,
Aug 8, 2011, 9:46:39 AM8/8/11
to golan...@googlegroups.com
Hi,

If a resource requires explicit closing but finalization is not an option due to performance/limited number of resources/ideology (os.File), it still can help with resource leak detection in debug mode.
The following program:

package main

import "co"
import "runtime"
import "time"

func foo() {
rc := co.MakeResourceCache(1, 2, 0, nil, nil)
...
// forget to call rc.Close()
}

func main() {
foo()
runtime.GC()
time.Sleep(1e8)
}

when run with GODEBUG=1 outputs:

$ ./test
co.ResourceCache leak at:

main.foo+0x45 /home/dvyukov/go_xadd/src/pkg/co/test.go:8
main.foo()
main.main+0x27 /home/dvyukov/go_xadd/src/pkg/co/test.go:14
main.main()
runtime.mainstart+0xf /home/dvyukov/go_xadd/src/pkg/runtime/amd64/asm.s:79
runtime.mainstart()
runtime.goexit+0x0 /home/dvyukov/go_xadd/src/pkg/runtime/proc.c:244
runtime.goexit()

The trick is as follows:

type ResourceCache struct {
  ...
}

var debug = os.Getenv("GODEBUG") != ""

func leakDetectOn(cache *ResourceCache) {
if debug == false {
return
}
callstack := make([]uintptr, 64)
cnt := runtime.Callers(3, callstack)
callstack = callstack[:cnt]
runtime.SetFinalizer(cache, func(cache *ResourceCache) {
print("co.ResourceCache leak at:\n\n")
for i := 0; i != len(callstack); i++ {
pc := callstack[i]
f := runtime.FuncForPC(pc)
if f == nil {
print(unsafe.Pointer(pc), "\n")
continue
}
file, line := f.FileLine(pc)
off := pc - f.Entry()
print(f.Name(), "+", unsafe.Pointer(off), " ", file, ":", line, "\n\t", f.Name(), "()\n")
}
print("\n")
})
}

func leakDetectOff(res interface{}) {
if debug == false {
return
}
runtime.SetFinalizer(res, nil)
}

func MakeResourceCache(low, high, global uint32, ctor func() interface{}, dtor func(interface{})) *ResourceCache {
cache := new(ResourceCache)
...
leakDetectOn(cache)
return cache
}

func (cache *ResourceCache) Close() {
leakDetectOff(cache)
...
}

John Asmuth

unread,
Aug 8, 2011, 9:51:57 AM8/8/11
to golan...@googlegroups.com
Good trick. Might be worth adding to http://code.google.com/p/go-wiki/.

Dmitry Vyukov

unread,
Aug 8, 2011, 10:54:43 AM8/8/11
to golan...@googlegroups.com
понедельник, 8 августа 2011 г. 17:51:57 UTC+4 пользователь John Asmuth написал:
Good trick. Might be worth adding to http://code.google.com/p/go-wiki/.

What would wiki owners say?

Btw, I tried to make the functionality reusable, but failed because finalizer function must know real type of the object. Perhaps it would be reasonable if finalizer function accepts either *T (exact real type) or interface{}.

John Asmuth

unread,
Aug 8, 2011, 11:01:36 AM8/8/11
to golan...@googlegroups.com
If you have something useful to add, just email one of the owners and ask to be made a committer (unless I misunderstood your question)

Brad Fitzpatrick

unread,
Aug 8, 2011, 11:13:06 AM8/8/11
to golan...@googlegroups.com
On Mon, Aug 8, 2011 at 7:54 AM, Dmitry Vyukov <dvy...@google.com> wrote:
понедельник, 8 августа 2011 г. 17:51:57 UTC+4 пользователь John Asmuth написал:
Good trick. Might be worth adding to http://code.google.com/p/go-wiki/.

What would wiki owners say?

Go for it.  You have access.

Reply all
Reply to author
Forward
0 new messages