Using golang's pprof over a Unix Socket

Python Manhole

When working with a complex python daemon, I really appreciated the python-manhole library. This tool allows you to listen on a Unix domain socket, and serve a REPL, get stack traces, and more.

From the README, it is as simple as this:

Install it:

pip install manhole

Add it to your python code somewhere in the start sequence:

import manhole
...
def main():
    manhole.install() # this will start the daemon thread
    ...
    # and now you start your app, eg: server.serve_forever()

And then connect to it:

$ nc -U /tmp/manhole-1234

Python 2.7.3 (default, Apr 10 2013, 06:20:15)
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> dir()
['__builtins__', 'dump_stacktraces', 'os', 'socket', 'sys', 'traceback']
>>> print 'foobar'
foobar

On a live system this can be a godsend. It doesn’t require running a webserver, and it can be as secure as normal unix file permissions on the server.

Is there anything like this for Golang?

Golang Cannula

No. Fundamentally golang is a compiled language, you won’t be able to exactly get a REPL.

Using the delve debugger, you can debug a golang process in real time, but I would not recommend this for production systems.

Golang’s pprof tool is the standard way to get stack traces, flame graphs, heap dumps, and more from a running golang process. But it requires a webserver (instructions). What if we don’t want a webserver?

Cannulla is a library, sorta like python-manhole, which exposes a pprof server as a Unix socket.

Getting Started With Cannula

You can load Cannula like this:

package main

import (
    github.com/retailnext/cannula
)

func main() {
    path := "/tmp/pprof-cannula.sock"
    go canulla.Start(path)
    ...
}

The Cannula library itself is small enough to be able to vendor yourself and customize to your needs. For example you could add the PID into the path to make it unique for each process, making it easier to pick the right socket.

Once started, you can use curl to access it directly:

curl --unix-socket /tmp/pprof-cannula.sock 'http:/debug/pprof/goroutine?debug=1'

But it would be also nice to use the native go tool pprof command. To that, just start socat to proxy to the socket:

socat TCP-LISTEN:1337,reuseaddr,fork UNIX-CONNECT:/tmp/pprof-cannula.sock
go tool pprof  http://127.0.0.1:1337/debug/pprof/heap

Because we are only exposing a socket, the permissions of that .sock file are all we need to worry about. There is no risk of us exposing /debug to the world.

Conclusion

Having pprof enabled on a production golang process can be extremly valuable. Just like python-manhole or Java JMX, you never know when you are going to need to attach to a live process and see what is using up all the ram.

But I don’t really like running webservers to host this data, so I’ll be adding cannula to all my golang projects to get the benefits of pprof without having to exposing a port or dealing with the default golang http Mux.

Comment via email