β Exercise 10: Java-Style Stack Traces - Making Go Panics Look Familiar
In this exercise, you’ll modify Go’s stack trace formatting to match Java’s style! π Instead of Go’s stack traces, we’ll create Java-style traces.
π― Learning Objectives
By the end of this exercise, you will:
- β Understand how Go formats stack traces in the runtime
- β Know where panic messages are generated
- β Modify runtime output formatting
π§ Background: Stack Trace Styles
We’re transforming Go’s stracktrace format:
panic: Something went wrong
goroutine 1 [running]:
main.methodC()
/Users/dev/project/main.go:15 +0x45
main.methodB()
/Users/dev/project/main.go:11 +0x23
main.methodA()
/Users/dev/project/main.go:7 +0x12
Into this Java-style format:
Exception in thread "main" go.runtime.Panic: Something went wrong
at main.methodC(main.go:15)
at main.methodB(main.go:11)
at main.methodA(main.go:7)
π Step 1: Create a Test Program
Create a stack_trace_demo.go
file:
package main
import "fmt"
func methodC() {
panic("Something went wrong")
}
func methodB() {
methodC()
}
func methodA() {
methodB()
}
func main() {
fmt.Println("Starting the program...")
methodA()
}
Run with current Go to see the stacktrace format:
go run stack_trace_demo.go
Step 2: Navigate to Runtime Files
cd go/src/runtime
Key files we’ll modify:
- panic.go
- Panic header message
- traceback.go
- Stack frame formatting
Step 3: Modify the Panic Header
Edit panic.go
:
Find the printpanics
function around line 668. Look for:
print("panic: ")
printpanicval(p.arg)
Change to:
print("Exception in thread \"main\" go.runtime.Panic: ")
printpanicval(p.arg)
Step 4: Remove Goroutine Header
Edit traceback.go
:
Find the goroutineheader
function around line 1212. Add a return statement at the beginning:
func goroutineheader(gp *g) {
return // Add this line to skip printing goroutine info
level, _, _ := gotraceback()
// ... rest of original code below (now unreachable)
}
Step 5: Transform Stack Frame Formatting
Still in traceback.go
:
Find the traceback2
function around line 965. Comment out the gotraceback()
call:
gp := u.g.ptr()
// level, _, _ := gotraceback() // Comment this out
var cgoBuf [32]uintptr
Then find where stack frames are printed (around line 990-1005). Replace this entire section:
printFuncName(name)
print("(")
if iu.isInlined(uf) {
print("...")
} else {
argp := unsafe.Pointer(u.frame.argp)
printArgs(f, argp, u.symPC())
}
print(")\n")
print("\t", file, ":", line)
if !iu.isInlined(uf) {
if u.frame.pc > f.entry() {
print(" +", hex(u.frame.pc-f.entry()))
}
if gp.m != nil && gp.m.throwing >= throwTypeRuntime && gp == gp.m.curg || level >= 2 {
print(" fp=", hex(u.frame.fp), " sp=", hex(u.frame.sp), " pc=", hex(u.frame.pc))
}
}
print("\n")
With this Java-style format:
// Extract just the filename (not full path)
fileName := file
for i := len(file) - 1; i >= 0; i-- {
if file[i] == '/' || file[i] == '\\' {
fileName = file[i+1:]
break
}
}
print(" at ", name, "(", fileName, ":", line, ")\n")
Step 6: Rebuild Go Runtime
cd ../ # back to go/src
./make.bash
Step 7: Test Java-Style Stack Traces
../go/bin/go run stack_trace_demo.go
You should see:
Starting the program...
Exception in thread "main" go.runtime.Panic: Something went wrong
at main.methodC(stack_trace_demo.go:6)
at main.methodB(stack_trace_demo.go:10)
at main.methodA(stack_trace_demo.go:14)
at main.main(stack_trace_demo.go:19)
Understanding What We Did
- Changed Panic Header (
panic.go
line 668): Changed"panic: "
to"Exception in thread \"main\" go.runtime.Panic: "
- Removed Goroutine Info (
traceback.go
line 1212): Added earlyreturn
ingoroutineheader()
- Simplified Stack Frames (
traceback.go
line 990-1005): Replaced the go output with the java" at name(file:line)"
format - Removed Debug Info: Commented out
gotraceback()
call and eliminated hex offsets, frame pointers - Basename Only: Extract filename from full path using loop
π What We Learned
- π Runtime Formatting: How Go generates stack traces
- π Panic Handling: Where panic messages originate
- π¨ Output Control: Modifying runtime print statements
π‘ Extension Ideas
Try these additional modifications: π
- β Add color to the output (red for “Exception”)
- β Make it configurable via environment variable
- β Add Python-style formatting as another option
- β Include package path conversion (github.com/user/pkg β github.com.user.pkg)
Cleanup
To restore Go’s original stack trace format:
cd go/src/runtime
git checkout panic.go traceback.go
cd ../
./make.bash
Summary
You’ve transformed Go’s stack traces into Java-style formatting:
// Before: Technical and verbose
goroutine 1 [running]:
main.methodC()
/full/path/to/main.go:15 +0x45
// After: Clean and familiar
Exception in thread "main" go.runtime.Panic: ...
at main.methodC(main.go:15)
Congratulations on completing all workshop exercises! Return to the main workshop