diff --git a/cmd/sing-box/debug.go b/cmd/sing-box/debug.go index 5134ff4c..a27fbbcf 100644 --- a/cmd/sing-box/debug.go +++ b/cmd/sing-box/debug.go @@ -5,11 +5,36 @@ package main import ( "net/http" _ "net/http/pprof" + "runtime" "github.com/sagernet/sing-box/log" + + "github.com/dustin/go-humanize" + "runtime/debug" + "encoding/json" + "github.com/sagernet/sing-box/common/badjson" ) func init() { + http.HandleFunc("/debug/gc", func(writer http.ResponseWriter, request *http.Request) { + writer.WriteHeader(http.StatusNoContent) + go debug.FreeOSMemory() + }) + http.HandleFunc("/debug/memory", func(writer http.ResponseWriter, request *http.Request) { + var memStats runtime.MemStats + runtime.ReadMemStats(&memStats) + + var memObject badjson.JSONObject + memObject.Put("heap", humanize.Bytes(memStats.HeapInuse)) + memObject.Put("stack", humanize.Bytes(memStats.StackInuse)) + memObject.Put("idle", humanize.Bytes(memStats.HeapIdle-memStats.HeapReleased)) + memObject.Put("goroutines", runtime.NumGoroutine()) + memObject.Put("rss", rusageMaxRSS()) + + encoder := json.NewEncoder(writer) + encoder.SetIndent("", " ") + encoder.Encode(memObject) + }) go func() { err := http.ListenAndServe("0.0.0.0:8964", nil) if err != nil { diff --git a/cmd/sing-box/debug_linux.go b/cmd/sing-box/debug_linux.go new file mode 100644 index 00000000..ecee3295 --- /dev/null +++ b/cmd/sing-box/debug_linux.go @@ -0,0 +1,25 @@ +//go:build debug + +package main + +import ( + "runtime" + "syscall" +) + +func rusageMaxRSS() float64 { + ru := syscall.Rusage{} + err := syscall.Getrusage(syscall.RUSAGE_SELF, &ru) + if err != nil { + return 0 + } + + rss := float64(ru.Maxrss) + if runtime.GOOS == "darwin" || runtime.GOOS == "ios" { + rss /= 1 << 20 // ru_maxrss is bytes on darwin + } else { + // ru_maxrss is kilobytes elsewhere (linux, openbsd, etc) + rss /= 1 << 10 + } + return rss +} diff --git a/cmd/sing-box/debug_stub.go b/cmd/sing-box/debug_stub.go new file mode 100644 index 00000000..5b16a0d4 --- /dev/null +++ b/cmd/sing-box/debug_stub.go @@ -0,0 +1,7 @@ +//go:build debug && !linux + +package main + +func rusageMaxRSS() float64 { + return -1 +} diff --git a/common/badjson/object.go b/common/badjson/object.go index ca555971..d9c2a36e 100644 --- a/common/badjson/object.go +++ b/common/badjson/object.go @@ -13,7 +13,7 @@ type JSONObject struct { linkedhashmap.Map[string, any] } -func (m *JSONObject) MarshalJSON() ([]byte, error) { +func (m JSONObject) MarshalJSON() ([]byte, error) { buffer := new(bytes.Buffer) buffer.WriteString("{") items := m.Entries() diff --git a/go.mod b/go.mod index d7002e8b..bb4ea302 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.18 require ( github.com/database64128/tfo-go v1.1.0 + github.com/dustin/go-humanize v1.0.0 github.com/fsnotify/fsnotify v1.5.4 github.com/go-chi/chi/v5 v5.0.7 github.com/go-chi/cors v1.2.1 diff --git a/go.sum b/go.sum index 1fa83389..0cb2b80c 100644 --- a/go.sum +++ b/go.sum @@ -24,6 +24,7 @@ github.com/database64128/tfo-go v1.1.0/go.mod h1:95pOT8bnV3P2Lmu9upHNWFHz6dYGJ9c github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/eycorsican/go-tun2socks v1.16.11 h1:+hJDNgisrYaGEqoSxhdikMgMJ4Ilfwm/IZDrWRrbaH8= github.com/eycorsican/go-tun2socks v1.16.11/go.mod h1:wgB2BFT8ZaPKyKOQ/5dljMG/YIow+AIXyq4KBwJ5sGQ=