Frontend Labs
⋯

Day 28 — Profiling

Learning Objectives

  • Generate and read CPU and heap profiles
  • Identify hot functions in a flame graph
  • Know what to look for when engineers say "the WAF is slow"

Key Concepts

# Add pprof endpoints (one import)
import _ "net/http/pprof"

# Collect CPU profile (30 seconds)
curl -s http://localhost:8080/debug/pprof/profile?seconds=30 > cpu.prof

# Analyze
go tool pprof cpu.prof
(pprof) top10        # top 10 functions by CPU
(pprof) web          # open flame graph in browser (requires graphviz)
(pprof) list regexp  # source code for regexp package

# Heap profile
curl -s http://localhost:8080/debug/pprof/heap > heap.prof
go tool pprof heap.prof
(pprof) top          # top allocators
(pprof) allocs       # allocation profile

Exercise: Profile the WAF Pattern Matcher

package main

import (
    "fmt"
    "net/http"
    _ "net/http/pprof"
    "regexp"
    "time"
)

// Simulate WAF pattern matching under load
var wafPatterns = []*regexp.Regexp{
    regexp.MustCompile(`(?i)\b(UNION|SELECT|INSERT|DELETE|DROP)\b`),
    regexp.MustCompile(`(?i)<script[\s>]`),
    regexp.MustCompile(`(?i)javascript:`),
    regexp.MustCompile(`\.\./`),
    regexp.MustCompile(`(?i)(cmd|exec|system|eval)\s*\(`),
}

func checkPayload(payload string) bool {
    for _, re := range wafPatterns {
        if re.MatchString(payload) {
            return true // blocked
        }
    }
    return false
}

func handler(w http.ResponseWriter, r *http.Request) {
    payload := r.URL.Query().Get("q")
    blocked := checkPayload(payload)
    if blocked {
        http.Error(w, "forbidden", 403)
        return
    }
    fmt.Fprint(w, "ok")
}

func main() {
    http.HandleFunc("/", handler)

    // Load generator: 500 goroutines hammering for 30s
    go func() {
        time.Sleep(1 * time.Second)
        fmt.Println("starting load test...")
        payloads := []string{"normal search", "product name", "UNION SELECT", "<script>"}
        var wg sync.WaitGroup
        for i := 0; i < 500; i++ {
            wg.Add(1)
            go func(id int) {
                defer wg.Done()
                for j := 0; j < 100; j++ {
                    payload := payloads[j%len(payloads)]
                    http.Get(fmt.Sprintf("http://localhost:8080/?q=%s", url.QueryEscape(payload)))
                }
            }(i)
        }
        wg.Wait()
        fmt.Println("load test complete. check: go tool pprof http://localhost:8080/debug/pprof/profile?seconds=30")
    }()

    fmt.Println("profiling server on :8080")
    http.ListenAndServe(":8080", nil)
}

Profile it:

go run . &
sleep 2
go tool pprof -http=:9090 http://localhost:8080/debug/pprof/profile?seconds=10
# Open http://localhost:9090 for flame graph

Real-World Context

When your WAF adds more latency than expected, profiling will show you whether it's regex, JSON parsing, or mutex contention. A flame graph makes the answer obvious in 30 seconds — no guessing. This is the skill that separates engineers who fix performance issues from ones who guess.


Generated from week4.md · 8 Oct 2026 16:36