lolly/internal/server/pprof_impl.go
xfy 58e095a35b feat(pprof): add /debug/pprof/allocs endpoint for allocation profiling
- Add writeAllocsProfile() helper in pprof_impl.go
- Register /allocs route in PprofHandler.ServeHTTP
- Add handleAllocs() method with proper streaming response
- Update index page to list the new allocs profile link

This aligns lolly's pprof endpoints with net/http/pprof and enables
allocation hotspot analysis during performance benchmarking.
2026-06-11 13:47:47 +08:00

190 lines
4.8 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Package server 提供 pprof 性能分析的底层实现。
//
// 该文件封装 runtime/pprof 的调用,为 fasthttp 提供流式输出支持。
// 包含以下核心功能:
// - CPU profile 的启动和停止
// - 内存分配 profile 的写入
// - Goroutine stack traces 的写入
// - 阻塞和锁竞争 profile 的写入
//
// 主要用途:
//
// 作为 pprof.go 的底层实现层,处理与 runtime/pprof 的直接交互。
//
// 注意事项:
// - CPU profile 使用互斥锁保护,确保并发安全
// - 所有写入操作均使用 io.Writer 接口,支持流式输出
//
// 作者xfy
package server
import (
"bufio"
"io"
"runtime"
"runtime/pprof"
"sync"
)
var (
// cpuProfileMu 保护 cpuProfileActive 状态的互斥锁。
//
// 用于确保 CPU profiling 启动和停止操作的线程安全性,
// 防止并发调用 startCPUProfile/stopCPUProfile 导致的状态不一致。
cpuProfileMu sync.Mutex
// cpuProfileActive 标记当前 CPU profile 是否处于采集状态。
//
// 值为 true 表示正在采集false 表示未采集。
// 该变量由 cpuProfileMu 保护,不应直接访问。
cpuProfileActive bool
)
// startCPUProfile 启动 CPU profile 采集。
//
// 参数:
// - w: 输出 writer
//
// 返回值:
// - error: 启动失败时的错误
func startCPUProfile(w io.Writer) error {
cpuProfileMu.Lock()
defer cpuProfileMu.Unlock()
if cpuProfileActive {
return nil // 已在采集,忽略
}
if err := pprof.StartCPUProfile(w); err != nil {
return err
}
cpuProfileActive = true
return nil
}
// stopCPUProfile 停止 CPU profile 采集。
//
// 终止正在进行的 CPU profile 采集,并将缓冲数据刷新到输出。
// 若当前无活跃采集,则忽略该调用。
//
// 注意事项:
// - 该函数由 cpuProfileMu 保护,调用前无需额外加锁
func stopCPUProfile() {
cpuProfileMu.Lock()
defer cpuProfileMu.Unlock()
if cpuProfileActive {
pprof.StopCPUProfile()
cpuProfileActive = false
}
}
// writeHeapProfile 写入内存分配 profile。
//
// 执行 GC 后采集内存分配数据并写入输出流。
// GC 可确保获取更准确的内存使用数据。
//
// 参数:
// - w: 输出 writer用于写入 profile 数据
func writeHeapProfile(w io.Writer) {
runtime.GC() // 先执行 GC获取更准确的数据
_ = pprof.WriteHeapProfile(w)
}
// writeGoroutineProfile 写入 Goroutine stack traces。
//
// 采集当前所有 Goroutine 的栈追踪信息并写入输出流。
//
// 参数:
// - w: 输出 writer用于写入 profile 数据
func writeGoroutineProfile(w io.Writer) {
p := pprof.Lookup("goroutine")
if p != nil {
_ = p.WriteTo(w, 0)
}
}
// writeBlockProfile 写入阻塞 profile。
//
// 采集阻塞操作的 profile 数据并写入输出流。
//
// 参数:
// - w: 输出 writer用于写入 profile 数据
func writeBlockProfile(w io.Writer) {
p := pprof.Lookup("block")
if p != nil {
_ = p.WriteTo(w, 0)
}
}
// writeMutexProfile 写入锁竞争 profile。
//
// 采集互斥锁竞争的 profile 数据并写入输出流。
//
// 参数:
// - w: 输出 writer用于写入 profile 数据
func writeMutexProfile(w io.Writer) {
p := pprof.Lookup("mutex")
if p != nil {
_ = p.WriteTo(w, 0)
}
}
// writeAllocsProfile 写入分配 profile。
//
// 与 heap profile 使用同一份数据但默认以分配视角展示Samples=alloc_*)。
// 等价于 net/http/pprof 的 /debug/pprof/allocs。
//
// 参数:
// - w: 输出 writer用于写入 profile 数据
func writeAllocsProfile(w io.Writer) {
p := pprof.Lookup("allocs")
if p != nil {
_ = p.WriteTo(w, 0)
return
}
// 如果 allocs profile 未注册,回退到 heap profile
runtime.GC()
_ = pprof.WriteHeapProfile(w)
}
// bufioWriterAdapter 将 bufio.Writer 包装为 io.Writer自动 Flush。
//
// 该结构体实现 io.Writer 接口,在每次写入后自动调用 Flush
// 确保数据立即发送到客户端,适用于流式响应场景。
type bufioWriterAdapter struct {
// w 被包装的 bufio.Writer
w *bufio.Writer
}
// Write 实现 io.Writer 接口,写入数据并自动 Flush。
//
// 参数:
// - p: 待写入的字节切片
//
// 返回值:
// - n: 实际写入的字节数
// - err: 写入过程中遇到的错误
func (a *bufioWriterAdapter) Write(p []byte) (n int, err error) {
n, err = a.w.Write(p)
if err == nil {
_ = a.w.Flush()
}
return n, err
}
// wrapBufioWriter 将 bufio.Writer 包装为 io.Writer。
//
// 创建一个 bufioWriterAdapter 实例,使 bufio.Writer 能够
// 在每次写入后自动 Flush满足流式输出的需求。
//
// 参数:
// - w: 待包装的 bufio.Writer
//
// 返回值:
// - io.Writer: 包装后的 io.Writer 接口实例
func wrapBufioWriter(w *bufio.Writer) io.Writer {
return &bufioWriterAdapter{w: w}
}