- Events
Meet Our Amazing Pathwire Mavericks!
We use Go for a lot of our server development here at Mailgun, and it’s great. Coming from Python, though, there is one thing I really missed:
1import pdb; 23pdb.set_trace()
I could insert this little line whenever I was confused about what was happening in the code and see exactly what was going on. But in Go? Not so much. When I started this project in January, gdb failed on every program I tried it on. delve didn’t work on OS X, and print-statement-debugging was too slow and limited. What’s a developer to do?
Make my own debugger, of course.
All that stands in the way [of a good Go debugger] is the writing of a lot of non-portable low-level code talking to buggy undocumented interfaces.
– Rob Pike
godebug is a different kind of debugger. Traditional debuggers for compiled languages use low-level system calls and read binary files for debugging symbols. They’re hard to get right and they’re hard to port.
godebug takes a different approach: take the source code of a target program, insert debugging code between every line, then compile and run that instead. The result is a fully-functional debugger that is extremely portable. In fact, thanks to gopherjs, you can run it right here in your browser!
You can edit the program and relaunch it with the “DEBUG IT!” button as many times as you like.
Here’s a quick diagram of the above demo:
The original code gets transformed twice. First, godebug inserted debugging instrumentation. Then gopherjs compiled the result to javascript.
Let’s take a look at the instrumentation step. (To learn more about the gopherjs part (which is awesome) checkout its website.) Here are some of the calls that godebug inserts:
godebug.EnterFunc
lets the godebug runtime library know that we are entering a function. Since “next” doesn’t stop inside function calls, the runtime library takes note of these calls so it knows when to skip over lines.
godebug.ExitFunc
lets the runtime library know we are leaving a function. Omitted in main
.
godebug.Line
causes the program to pause and wait for input if and only if a user command or a breakpoint told it to. When it pauses, it prompts the user for input and responds to any commands.
godebug.Declare
records the mapping of variable names to their values. This mapping is used by the print command.
This is an abridged overview. There are other functions that godebug inserts and many details of the above functions have been omitted. But these are the basic pieces of how godebug works.
All of the above (minus the server & javascript stuff) is packaged into the godebug
command line tool. Here’s how to use it:
$ go get -u github.com/mailgun/godebug
Add this marker anywhere you want a breakpoint:
_ = "breakpoint"
This statement becomes a breakpoint when running under godebug and is a no-op otherwise.
Since breakpoints are part of the source code, you can wrap your own logic around them. Let’s say you are running a table driven test with dozens of cases, and one of the test cases fails: the one that tested the input "weird string"
. You can add this breakpoint to your test:
for _, tt := range myTestCases {
if tt.in == "weird string" {
_ = "breakpoint"
}
...
}
godebug test
will pause the program at the marker statement, which is conveniently positioned just before the failing test case runs.
Use the godebug run command:
godebug run <gofiles...>
Or, for tests, use the godebug test command:
godebug test
By default, godebug will only add debugging instrumentation to package main (for godebug run) or the package under test (for godebug test). It will not instrument any imported packages. This is to decrease the overhead of the debugger — any packages you are not interested in will run as normal, at full speed.
This means that by default you can not step into function calls from imported packages. But sometimes you will need to do that! To debug other packages, pass the -instrument
flag to godebug run
or godebug test
:
godebug test -instrument=pkgA,pkgB,pkgC pkg/under/test
The above command will instrument (and thus allow you to step into) pkgA
, pkgB
, pkgC
, and pkg/under/test
. It will then build and run the tests for pkg/under/test
. Similar semantics apply for godebug run
.
Next time you want to understand what is happening in a Go program, try godebug. Keep in mind that it is still a new tool that needs some polish. In particular, some known limitations are:
performance overhead
may cause read conflicts if your program reads from stdin
can’t attach to a running process
must know the packages you want to debug before starting the session
That said, I’m excited about this tool and hope it can provide a lot of value to the Go community. Try it out and let me know what you think! If you have any problems I would be happy to hear about them. File an issue at https://github.com/mailgun/godebug/issues or send an email to: dev@godebug.rocks
Hope you enjoy it!
https://github.com/mailgun/godebug
Want to learn more about godebug? Here is a talk I gave at GoSF, including more depth on code generation and concurrency management:
Last updated on August 28, 2020
Meet Our Amazing Pathwire Mavericks!
International Women’s Day: How Pathwire’s Female Leaders Choose To Challenge
Mailpets: For The Love Of Animals
The Mailgun Maverick Program Is Here!
Force for Change: It's Time to Speak Out
HTTP Crash Course
Golang’s Superior Cache Solution to Memcached and Redis
HTTP/2 Cleartext (H2C) Client Example in Go
How we built a Lucene-inspired parser in Go
Delivering HTML Emails With Mailgun-Go
InboxReady x Salesforce: The Key to a Stronger Email Deliverability
Become an Email Pro With Our Templates API
Google Postmaster Tools: Understanding Sender Reputation
Navigating Your Career as a Woman in Tech
Implementing Dmarc – A Step-by-Step Guide
Email Bounces: What To Do About Them
Announcing InboxReady: The deliverability suite you need to hit the inbox
Black History Month in Tech: 7 Visionaries Who Shaped The Future
How To Create a Successful Triggered Email Program
Designing HTML Email Templates For Transactional Emails
InboxReady x Salesforce: The Key to a Stronger Email Deliverability
Implementing Dmarc – A Step-by-Step Guide
Announcing InboxReady: The deliverability suite you need to hit the inbox
Designing HTML Email Templates For Transactional Emails
Email Security Best Practices: How To Keep Your Email Program Safe
Mailgun’s Active Defense Against Log4j
Email Blasts: The Dos And Many Don’ts Of Mass Email Sending
Email's Best of 2021
5 Ideas For Better Developer-Designer Collaboration
Mailgun Joins Sinch: The Future of Customer Communications Is Here
Always be in the know and grab free email resources!
By sending this form, I agree that Mailgun may contact me and process my data in accordance with its Privacy Policy.