oom_report.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. //go:build darwin || linux || windows
  2. package libbox
  3. import (
  4. "os"
  5. "path/filepath"
  6. "runtime"
  7. "strings"
  8. "time"
  9. "github.com/sagernet/sing-box/experimental/libbox/internal/oomprofile"
  10. "github.com/sagernet/sing-box/service/oomkiller"
  11. "github.com/sagernet/sing/common/byteformats"
  12. "github.com/sagernet/sing/common/memory"
  13. )
  14. func init() {
  15. sOOMReporter = &oomReporter{}
  16. }
  17. var oomReportProfiles = []string{
  18. "allocs",
  19. "block",
  20. "goroutine",
  21. "heap",
  22. "mutex",
  23. "threadcreate",
  24. }
  25. type oomReportMetadata struct {
  26. reportMetadata
  27. RecordedAt string `json:"recordedAt"`
  28. MemoryUsage string `json:"memoryUsage"`
  29. AvailableMemory string `json:"availableMemory,omitempty"`
  30. // Heap
  31. HeapAlloc string `json:"heapAlloc,omitempty"`
  32. HeapObjects uint64 `json:"heapObjects,omitempty,string"`
  33. HeapInuse string `json:"heapInuse,omitempty"`
  34. HeapIdle string `json:"heapIdle,omitempty"`
  35. HeapReleased string `json:"heapReleased,omitempty"`
  36. HeapSys string `json:"heapSys,omitempty"`
  37. // Stack
  38. StackInuse string `json:"stackInuse,omitempty"`
  39. StackSys string `json:"stackSys,omitempty"`
  40. // Runtime metadata
  41. MSpanInuse string `json:"mSpanInuse,omitempty"`
  42. MSpanSys string `json:"mSpanSys,omitempty"`
  43. MCacheSys string `json:"mCacheSys,omitempty"`
  44. BuckHashSys string `json:"buckHashSys,omitempty"`
  45. GCSys string `json:"gcSys,omitempty"`
  46. OtherSys string `json:"otherSys,omitempty"`
  47. Sys string `json:"sys,omitempty"`
  48. // GC & runtime
  49. TotalAlloc string `json:"totalAlloc,omitempty"`
  50. NumGC uint32 `json:"numGC,omitempty,string"`
  51. NumGoroutine int `json:"numGoroutine,omitempty,string"`
  52. NextGC string `json:"nextGC,omitempty"`
  53. LastGC string `json:"lastGC,omitempty"`
  54. }
  55. type oomReporter struct{}
  56. var _ oomkiller.OOMReporter = (*oomReporter)(nil)
  57. func (r *oomReporter) WriteReport(memoryUsage uint64) error {
  58. now := time.Now().UTC()
  59. reportsDir := filepath.Join(sWorkingPath, "oom_reports")
  60. err := os.MkdirAll(reportsDir, 0o777)
  61. if err != nil {
  62. return err
  63. }
  64. chownReport(reportsDir)
  65. destPath, err := nextAvailableReportPath(reportsDir, now)
  66. if err != nil {
  67. return err
  68. }
  69. err = os.MkdirAll(destPath, 0o777)
  70. if err != nil {
  71. return err
  72. }
  73. chownReport(destPath)
  74. for _, name := range oomReportProfiles {
  75. writeOOMProfile(destPath, name)
  76. }
  77. writeReportFile(destPath, "cmdline", []byte(strings.Join(os.Args, "\000")))
  78. var memStats runtime.MemStats
  79. runtime.ReadMemStats(&memStats)
  80. metadata := oomReportMetadata{
  81. reportMetadata: baseReportMetadata(),
  82. RecordedAt: now.Format(time.RFC3339),
  83. MemoryUsage: byteformats.FormatMemoryBytes(memoryUsage),
  84. // Heap
  85. HeapAlloc: byteformats.FormatMemoryBytes(memStats.HeapAlloc),
  86. HeapObjects: memStats.HeapObjects,
  87. HeapInuse: byteformats.FormatMemoryBytes(memStats.HeapInuse),
  88. HeapIdle: byteformats.FormatMemoryBytes(memStats.HeapIdle),
  89. HeapReleased: byteformats.FormatMemoryBytes(memStats.HeapReleased),
  90. HeapSys: byteformats.FormatMemoryBytes(memStats.HeapSys),
  91. // Stack
  92. StackInuse: byteformats.FormatMemoryBytes(memStats.StackInuse),
  93. StackSys: byteformats.FormatMemoryBytes(memStats.StackSys),
  94. // Runtime metadata
  95. MSpanInuse: byteformats.FormatMemoryBytes(memStats.MSpanInuse),
  96. MSpanSys: byteformats.FormatMemoryBytes(memStats.MSpanSys),
  97. MCacheSys: byteformats.FormatMemoryBytes(memStats.MCacheSys),
  98. BuckHashSys: byteformats.FormatMemoryBytes(memStats.BuckHashSys),
  99. GCSys: byteformats.FormatMemoryBytes(memStats.GCSys),
  100. OtherSys: byteformats.FormatMemoryBytes(memStats.OtherSys),
  101. Sys: byteformats.FormatMemoryBytes(memStats.Sys),
  102. // GC & runtime
  103. TotalAlloc: byteformats.FormatMemoryBytes(memStats.TotalAlloc),
  104. NumGC: memStats.NumGC,
  105. NumGoroutine: runtime.NumGoroutine(),
  106. NextGC: byteformats.FormatMemoryBytes(memStats.NextGC),
  107. }
  108. if memStats.LastGC > 0 {
  109. metadata.LastGC = time.Unix(0, int64(memStats.LastGC)).UTC().Format(time.RFC3339)
  110. }
  111. availableMemory := memory.Available()
  112. if availableMemory > 0 {
  113. metadata.AvailableMemory = byteformats.FormatMemoryBytes(availableMemory)
  114. }
  115. writeReportMetadata(destPath, metadata)
  116. copyConfigSnapshot(destPath)
  117. return nil
  118. }
  119. func writeOOMProfile(destPath string, name string) {
  120. filePath, err := oomprofile.WriteFile(destPath, name)
  121. if err != nil {
  122. return
  123. }
  124. chownReport(filePath)
  125. }