Build, Test & Profile from the Shell
Run builds and tests from the shell, capture profiles with go tool pprof, and line up failures with journalctl timestamps on Linux hosts.
Search across all documentation pages
Run builds and tests from the shell, capture profiles with go tool pprof, and line up failures with journalctl timestamps on Linux hosts.
The Go toolchain is CLI-native: go build, go test, and go tool pprof are designed for scripts, CI, and SSH sessions.
Pair them with shell redirection, tee, and journal queries so a failing test on your laptop uses the same commands you will run during a production incident.
Quick-reference recipe card - copy-paste ready.
# Build and test from module root
go build -o bin/api ./cmd/api
go test ./... -count=1 -race 2>&1 | tee test.log
# CPU profile a benchmark locally
go test -bench=. -benchmem -cpuprofile=cpu.prof -run=^$ ./...
go tool pprof -http=:0 cpu.prof
# Fetch profile from a running server (when pprof endpoint is enabled)
curl -s "http://localhost:6060/debug/pprof/profile?seconds=30" -o live.prof
go tool pprof live.prof
# Correlate with systemd logs
sudo journalctl -u api.service --since "10 min ago" --no-pagerWhen to reach for this:
go test output with journal timestamps.#!/usr/bin/env bash
set -euo pipefail
cd "$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
# Build
mkdir -p bin
go build -ldflags="-s -w" -o bin/api ./cmd/api
# Test with race detector; keep log artifact
go test ./... -count=1 -race -timeout=5m 2>&1 | tee artifacts/test-$(date +%Y%m%d%H%M).log
# Micro-benchmark + CPU profile for one package
PKG=./internal/handler
go test "$PKG" -bench=BenchmarkHealth -benchmem \
-cpuprofile=artifacts/cpu.prof -run=^$ -count=1
go tool pprof -top artifacts/cpu.prof | head -20
# If api.service runs on this host, pull recent journal lines
if systemctl is-active --quiet api.service 2>/dev/null; then
journalctl -u api.service --since "15 min ago" -n 200 --no-pager \
| tee artifacts/api.journal.snippet
fiWhat this demonstrates:
go test ./... suitable for CI parity.journalctl snippet captured beside test output for incident notebooks.go build compiles packages; -o names the output binary and -ldflags injects version metadata or strips debug symbols.go test executes _test.go files; flags like -race, -count=1, and -timeout map directly to CI YAML.go test -cpuprofile and -memprofile attach runtime/pprof sampling during tests or benchmarks.go tool pprof reads profile files or URLs and can open a local web UI (-http) for flame graphs./debug/pprof/* when you import net/http/pprof and listen on a debug port (guard in production).journalctl reads the systemd journal; filtering by unit and time range places Go stack traces next to OOM or restart events.| Flag | Purpose |
|---|---|
./... | All packages under module root |
-race | Race detector (CI for pure Go packages) |
-count=1 | Disable test cache |
-run=^$ | Skip tests; benchmarks only |
-bench=. | Run benchmarks matching regex |
-cpuprofile=file | Write CPU profile |
-memprofile=file | Write heap profile |
-json | Machine-readable test events (Go 1.20+) |
| Source | Command |
|---|---|
| Test/bench | go test -cpuprofile=cpu.prof |
| Local server | curl localhost:6060/debug/pprof/profile?seconds=30 |
| K8s pod | kubectl port-forward pod/api 6060:6060 then curl |
| File analysis | go tool pprof -http=:0 cpu.prof |
// cmd/api/main.go - optional debug listener (staging only)
import _ "net/http/pprof"
go func() {
log.Println(http.ListenAndServe("127.0.0.1:6060", nil))
}()Bind to loopback, protect with auth or network policy before exposing beyond localhost.
hey, k6, or integration tests while profiling.:6060 public - Attackers scan for open pprof ports. Fix: bind 127.0.0.1, use port-forward, or gate with auth.go test -count=1 in CI and when debugging flakes.-tags consistently.go test ./... logs - Failures scroll off screen in tmux. Fix: always tee to a file and rg FAIL afterward.journalctl uses local time by default; CI UTC differs. Fix: pass --utc or note offsets in incident docs.| Alternative | Use When | Don't Use When |
|---|---|---|
| IDE test runner | Fast inner loop while editing | You need the exact CI command transcript |
make test wrapper | Team-standard flags and artifacts | It hides flags new hires never learn |
| Continuous profiling (Parca, Pyroscope) | Always-on production history | You only need a one-off local benchmark |
perf / bpftrace | Kernel-level or syscall issues | Go CPU hotspots are usually enough in user space |
| Cloud vendor profiler | Managed GCP/AWS integration | Air-gapped or minimal dependency policy |
Per-package while iterating; full ./... before push and in CI.
Use -run TestName to narrow further.
Relative to the package directory under test unless you pass an absolute path.
Collect them into artifacts/ in scripts to avoid dirtying git trees.
15-30 seconds is typical for HTTP services under steady load.
Shorter samples work for tight benchmarks.
Yes.
Use -memprofile, or curl .../debug/pprof/heap and go tool pprof -top on the heap profile.
Kernel messages, systemd restart reasons, and unit metadata on the same timeline as your Go logs when stdout goes to the journal.
Strong default for services without heavy CGO.
Budget extra time and skip only with documented exceptions.
go tool pprof -base=old.prof new.prof highlights deltas after an optimization branch.
Yes.
Tools like gotestsum and custom jq filters consume JSON events for CI annotations.
Smaller binaries for container images.
Keep unstripped builds for internal crash symbolication if you lack separate debug info.
Set GODEBUG=gctrace=1 or use test benchmarks wrapping the hot path; CLIs often profile better in unit tests than via HTTP pprof.
The process likely had no samples during the window.
Increase load or extend seconds= query parameter.
journalctl -u api.service -g panic searches message text.
Export a window to a file and rg for stack frames as a backup.
Stack versions: This page was written for Go 1.26.x (Green Tea GC default, go fix modernizers - verify patch at build), chi (latest - verify at build), gin (latest - verify at build), echo (latest - verify at build), google.golang.org/grpc (latest - verify at build), sigs.k8s.io/controller-runtime (latest - verify at build), kubebuilder (latest - verify at build), tinygo (latest - verify board targets at build), wazero (latest - verify at build), and golangci-lint (latest - verify linter set at build).
Reviewed by Chris St. John·Last updated Jul 18, 2026