debug_http.go 2.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. package box
  2. import (
  3. "net/http"
  4. "net/http/pprof"
  5. "runtime"
  6. "runtime/debug"
  7. "strings"
  8. "github.com/sagernet/sing-box/log"
  9. "github.com/sagernet/sing-box/option"
  10. "github.com/sagernet/sing/common/byteformats"
  11. E "github.com/sagernet/sing/common/exceptions"
  12. "github.com/sagernet/sing/common/json"
  13. "github.com/sagernet/sing/common/json/badjson"
  14. "github.com/go-chi/chi/v5"
  15. )
  16. var debugHTTPServer *http.Server
  17. func applyDebugListenOption(options option.DebugOptions) {
  18. if debugHTTPServer != nil {
  19. debugHTTPServer.Close()
  20. debugHTTPServer = nil
  21. }
  22. if options.Listen == "" {
  23. return
  24. }
  25. r := chi.NewMux()
  26. r.Route("/debug", func(r chi.Router) {
  27. r.Get("/gc", func(writer http.ResponseWriter, request *http.Request) {
  28. writer.WriteHeader(http.StatusNoContent)
  29. go debug.FreeOSMemory()
  30. })
  31. r.Get("/memory", func(writer http.ResponseWriter, request *http.Request) {
  32. var memStats runtime.MemStats
  33. runtime.ReadMemStats(&memStats)
  34. var memObject badjson.JSONObject
  35. memObject.Put("heap", byteformats.FormatMemoryBytes(memStats.HeapInuse))
  36. memObject.Put("stack", byteformats.FormatMemoryBytes(memStats.StackInuse))
  37. memObject.Put("idle", byteformats.FormatMemoryBytes(memStats.HeapIdle-memStats.HeapReleased))
  38. memObject.Put("goroutines", runtime.NumGoroutine())
  39. memObject.Put("rss", rusageMaxRSS())
  40. encoder := json.NewEncoder(writer)
  41. encoder.SetIndent("", " ")
  42. encoder.Encode(&memObject)
  43. })
  44. r.Route("/pprof", func(r chi.Router) {
  45. r.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
  46. if !strings.HasSuffix(request.URL.Path, "/") {
  47. http.Redirect(writer, request, request.URL.Path+"/", http.StatusMovedPermanently)
  48. } else {
  49. pprof.Index(writer, request)
  50. }
  51. })
  52. r.HandleFunc("/*", pprof.Index)
  53. r.HandleFunc("/cmdline", pprof.Cmdline)
  54. r.HandleFunc("/profile", pprof.Profile)
  55. r.HandleFunc("/symbol", pprof.Symbol)
  56. r.HandleFunc("/trace", pprof.Trace)
  57. })
  58. })
  59. debugHTTPServer = &http.Server{
  60. Addr: options.Listen,
  61. Handler: r,
  62. }
  63. go func() {
  64. err := debugHTTPServer.ListenAndServe()
  65. if err != nil && !E.IsClosed(err) {
  66. log.Error(E.Cause(err, "serve debug HTTP server"))
  67. }
  68. }()
  69. }