Files
sing-box/experimental/libbox/oom_report.go
2026-04-10 16:24:25 +08:00

142 lines
4.3 KiB
Go

//go:build darwin || linux || windows
package libbox
import (
"os"
"path/filepath"
"runtime"
"strings"
"time"
"github.com/sagernet/sing-box/experimental/libbox/internal/oomprofile"
"github.com/sagernet/sing-box/service/oomkiller"
"github.com/sagernet/sing/common/byteformats"
"github.com/sagernet/sing/common/memory"
)
func init() {
sOOMReporter = &oomReporter{}
}
var oomReportProfiles = []string{
"allocs",
"block",
"goroutine",
"heap",
"mutex",
"threadcreate",
}
type oomReportMetadata struct {
reportMetadata
RecordedAt string `json:"recordedAt"`
MemoryUsage string `json:"memoryUsage"`
AvailableMemory string `json:"availableMemory,omitempty"`
// Heap
HeapAlloc string `json:"heapAlloc,omitempty"`
HeapObjects uint64 `json:"heapObjects,omitempty,string"`
HeapInuse string `json:"heapInuse,omitempty"`
HeapIdle string `json:"heapIdle,omitempty"`
HeapReleased string `json:"heapReleased,omitempty"`
HeapSys string `json:"heapSys,omitempty"`
// Stack
StackInuse string `json:"stackInuse,omitempty"`
StackSys string `json:"stackSys,omitempty"`
// Runtime metadata
MSpanInuse string `json:"mSpanInuse,omitempty"`
MSpanSys string `json:"mSpanSys,omitempty"`
MCacheSys string `json:"mCacheSys,omitempty"`
BuckHashSys string `json:"buckHashSys,omitempty"`
GCSys string `json:"gcSys,omitempty"`
OtherSys string `json:"otherSys,omitempty"`
Sys string `json:"sys,omitempty"`
// GC & runtime
TotalAlloc string `json:"totalAlloc,omitempty"`
NumGC uint32 `json:"numGC,omitempty,string"`
NumGoroutine int `json:"numGoroutine,omitempty,string"`
NextGC string `json:"nextGC,omitempty"`
LastGC string `json:"lastGC,omitempty"`
}
type oomReporter struct{}
var _ oomkiller.OOMReporter = (*oomReporter)(nil)
func (r *oomReporter) WriteReport(memoryUsage uint64) error {
now := time.Now().UTC()
reportsDir := filepath.Join(sWorkingPath, "oom_reports")
err := os.MkdirAll(reportsDir, 0o777)
if err != nil {
return err
}
chownReport(reportsDir)
destPath, err := nextAvailableReportPath(reportsDir, now)
if err != nil {
return err
}
err = os.MkdirAll(destPath, 0o777)
if err != nil {
return err
}
chownReport(destPath)
for _, name := range oomReportProfiles {
writeOOMProfile(destPath, name)
}
writeReportFile(destPath, "cmdline", []byte(strings.Join(os.Args, "\000")))
var memStats runtime.MemStats
runtime.ReadMemStats(&memStats)
metadata := oomReportMetadata{
reportMetadata: baseReportMetadata(),
RecordedAt: now.Format(time.RFC3339),
MemoryUsage: byteformats.FormatMemoryBytes(memoryUsage),
// Heap
HeapAlloc: byteformats.FormatMemoryBytes(memStats.HeapAlloc),
HeapObjects: memStats.HeapObjects,
HeapInuse: byteformats.FormatMemoryBytes(memStats.HeapInuse),
HeapIdle: byteformats.FormatMemoryBytes(memStats.HeapIdle),
HeapReleased: byteformats.FormatMemoryBytes(memStats.HeapReleased),
HeapSys: byteformats.FormatMemoryBytes(memStats.HeapSys),
// Stack
StackInuse: byteformats.FormatMemoryBytes(memStats.StackInuse),
StackSys: byteformats.FormatMemoryBytes(memStats.StackSys),
// Runtime metadata
MSpanInuse: byteformats.FormatMemoryBytes(memStats.MSpanInuse),
MSpanSys: byteformats.FormatMemoryBytes(memStats.MSpanSys),
MCacheSys: byteformats.FormatMemoryBytes(memStats.MCacheSys),
BuckHashSys: byteformats.FormatMemoryBytes(memStats.BuckHashSys),
GCSys: byteformats.FormatMemoryBytes(memStats.GCSys),
OtherSys: byteformats.FormatMemoryBytes(memStats.OtherSys),
Sys: byteformats.FormatMemoryBytes(memStats.Sys),
// GC & runtime
TotalAlloc: byteformats.FormatMemoryBytes(memStats.TotalAlloc),
NumGC: memStats.NumGC,
NumGoroutine: runtime.NumGoroutine(),
NextGC: byteformats.FormatMemoryBytes(memStats.NextGC),
}
if memStats.LastGC > 0 {
metadata.LastGC = time.Unix(0, int64(memStats.LastGC)).UTC().Format(time.RFC3339)
}
availableMemory := memory.Available()
if availableMemory > 0 {
metadata.AvailableMemory = byteformats.FormatMemoryBytes(availableMemory)
}
writeReportMetadata(destPath, metadata)
copyConfigSnapshot(destPath)
return nil
}
func writeOOMProfile(destPath string, name string) {
filePath, err := oomprofile.WriteFile(destPath, name)
if err != nil {
return
}
chownReport(filePath)
}