- 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.
190 lines
4.8 KiB
Go
190 lines
4.8 KiB
Go
// 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}
|
||
}
|