diff --git a/cmd/sing-box/debug.go b/cmd/sing-box/debug.go deleted file mode 100644 index bffa721f..00000000 --- a/cmd/sing-box/debug.go +++ /dev/null @@ -1,44 +0,0 @@ -//go:build debug - -package main - -import ( - "encoding/json" - "net/http" - _ "net/http/pprof" - "runtime" - "runtime/debug" - - "github.com/sagernet/sing-box/common/badjson" - "github.com/sagernet/sing-box/log" - - "github.com/dustin/go-humanize" -) - -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.IBytes(memStats.HeapInuse)) - memObject.Put("stack", humanize.IBytes(memStats.StackInuse)) - memObject.Put("idle", humanize.IBytes(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 { - log.Debug(err) - } - }() -} diff --git a/debug_go118.go b/debug_go118.go index 88fdc05e..4fe97325 100644 --- a/debug_go118.go +++ b/debug_go118.go @@ -10,6 +10,7 @@ import ( ) func applyDebugOptions(options option.DebugOptions) { + applyDebugListenOption(options) if options.GCPercent != nil { debug.SetGCPercent(*options.GCPercent) } diff --git a/debug.go b/debug_go119.go similarity index 96% rename from debug.go rename to debug_go119.go index b6197420..ef90da6d 100644 --- a/debug.go +++ b/debug_go119.go @@ -10,6 +10,7 @@ import ( ) func applyDebugOptions(options option.DebugOptions) { + applyDebugListenOption(options) if options.GCPercent != nil { debug.SetGCPercent(*options.GCPercent) } diff --git a/debug_http.go b/debug_http.go new file mode 100644 index 00000000..30134592 --- /dev/null +++ b/debug_http.go @@ -0,0 +1,67 @@ +package box + +import ( + "net/http" + "net/http/pprof" + "runtime" + "runtime/debug" + + "github.com/sagernet/sing-box/common/badjson" + "github.com/sagernet/sing-box/common/json" + "github.com/sagernet/sing-box/log" + "github.com/sagernet/sing-box/option" + E "github.com/sagernet/sing/common/exceptions" + + "github.com/dustin/go-humanize" + "github.com/go-chi/chi/v5" +) + +var debugHTTPServer *http.Server + +func applyDebugListenOption(options option.DebugOptions) { + if debugHTTPServer != nil { + debugHTTPServer.Close() + debugHTTPServer = nil + } + if options.Listen == "" { + return + } + r := chi.NewMux() + r.Route("/debug", func(r chi.Router) { + r.Get("/gc", func(writer http.ResponseWriter, request *http.Request) { + writer.WriteHeader(http.StatusNoContent) + go debug.FreeOSMemory() + }) + r.Get("/memory", func(writer http.ResponseWriter, request *http.Request) { + var memStats runtime.MemStats + runtime.ReadMemStats(&memStats) + + var memObject badjson.JSONObject + memObject.Put("heap", humanize.IBytes(memStats.HeapInuse)) + memObject.Put("stack", humanize.IBytes(memStats.StackInuse)) + memObject.Put("idle", humanize.IBytes(memStats.HeapIdle-memStats.HeapReleased)) + memObject.Put("goroutines", runtime.NumGoroutine()) + memObject.Put("rss", rusageMaxRSS()) + + encoder := json.NewEncoder(writer) + encoder.SetIndent("", " ") + encoder.Encode(memObject) + }) + r.HandleFunc("/pprof", pprof.Index) + r.HandleFunc("/pprof/*", pprof.Index) + r.HandleFunc("/pprof/cmdline", pprof.Cmdline) + r.HandleFunc("/pprof/profile", pprof.Profile) + r.HandleFunc("/pprof/symbol", pprof.Symbol) + r.HandleFunc("/pprof/trace", pprof.Trace) + }) + debugHTTPServer = &http.Server{ + Addr: options.Listen, + Handler: r, + } + go func() { + err := debugHTTPServer.ListenAndServe() + if err != nil && !E.IsClosed(err) { + log.Error(E.Cause(err, "serve debug HTTP server")) + } + }() +} diff --git a/cmd/sing-box/debug_linux.go b/debug_linux.go similarity index 92% rename from cmd/sing-box/debug_linux.go rename to debug_linux.go index ecee3295..3296bdec 100644 --- a/cmd/sing-box/debug_linux.go +++ b/debug_linux.go @@ -1,6 +1,4 @@ -//go:build debug - -package main +package box import ( "runtime" diff --git a/cmd/sing-box/debug_stub.go b/debug_stub.go similarity index 52% rename from cmd/sing-box/debug_stub.go rename to debug_stub.go index 5b16a0d4..ea7e2c0b 100644 --- a/cmd/sing-box/debug_stub.go +++ b/debug_stub.go @@ -1,6 +1,6 @@ -//go:build debug && !linux +//go:build !linux -package main +package box func rusageMaxRSS() float64 { return -1 diff --git a/option/debug.go b/option/debug.go index 78318c23..68b6f79b 100644 --- a/option/debug.go +++ b/option/debug.go @@ -7,6 +7,7 @@ import ( ) type DebugOptions struct { + Listen string `json:"listen,omitempty"` GCPercent *int `json:"gc_percent,omitempty"` MaxStack *int `json:"max_stack,omitempty"` MaxThreads *int `json:"max_threads,omitempty"`