From f6df7126b6a39e70f847d5be8786519fdfe5d904 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Sat, 4 Apr 2026 10:05:19 -0700 Subject: [PATCH] feat(admin): add profiling options for debugging high memory/CPU usage (#8923) * feat(admin): add profiling options for debugging high memory/CPU usage Add -debug, -debug.port, -cpuprofile, and -memprofile flags to the admin command, matching the profiling support already available in master, volume, and other server commands. This enables investigation of resource usage issues like #8919. * refactor(admin): move profiling flags into AdminOptions struct Move cpuprofile and memprofile flags from global variables into the AdminOptions struct and init() function for consistency with other flags. * fix(debug): bind pprof server to localhost only and document profiling flags StartDebugServer was binding to all interfaces (0.0.0.0), exposing runtime profiling data to the network. Restrict to 127.0.0.1 since this is a development/debugging tool. Also add a "Debugging and Profiling" section to the admin command's help text documenting the new flags. --- weed/command/admin.go | 25 +++++++++++++++++++++++++ weed/util/grace/pprof.go | 4 ++-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/weed/command/admin.go b/weed/command/admin.go index 6d6dc7198..fca5524e1 100644 --- a/weed/command/admin.go +++ b/weed/command/admin.go @@ -30,6 +30,7 @@ import ( "github.com/seaweedfs/seaweedfs/weed/pb" "github.com/seaweedfs/seaweedfs/weed/security" "github.com/seaweedfs/seaweedfs/weed/util" + "github.com/seaweedfs/seaweedfs/weed/util/grace" ) var ( @@ -48,6 +49,10 @@ type AdminOptions struct { dataDir *string icebergPort *int urlPrefix *string + debug *bool + debugPort *int + cpuProfile *string + memProfile *string } func init() { @@ -64,6 +69,10 @@ func init() { a.readOnlyPassword = cmdAdmin.Flag.String("readOnlyPassword", "", "read-only user password (optional, for view-only access; requires adminPassword to be set)") a.icebergPort = cmdAdmin.Flag.Int("iceberg.port", 8181, "Iceberg REST Catalog port (0 to hide in UI)") a.urlPrefix = cmdAdmin.Flag.String("urlPrefix", "", "URL path prefix when running behind a reverse proxy under a subdirectory (e.g. /seaweedfs)") + a.debug = cmdAdmin.Flag.Bool("debug", false, "serves runtime profiling data via pprof on the port specified by -debug.port") + a.debugPort = cmdAdmin.Flag.Int("debug.port", 6060, "http port for debugging") + a.cpuProfile = cmdAdmin.Flag.String("cpuprofile", "", "cpu profile output file") + a.memProfile = cmdAdmin.Flag.String("memprofile", "", "memory profile output file") } var cmdAdmin = &Command{ @@ -140,6 +149,16 @@ var cmdAdmin = &Command{ - All static assets, API endpoints, and navigation links will use the prefix - Session cookies are scoped to the prefix path + Debugging and Profiling: + - Use -debug to start a pprof HTTP server for live profiling (localhost only) + - Set -debug.port to choose the pprof port (default 6060) + - Profiles are accessible at http://127.0.0.1:/debug/pprof/ + - Use -cpuprofile and -memprofile to write profiles to files on shutdown + - WARNING: -debug exposes runtime internals; use only in trusted environments + - Examples: + weed admin -debug -debug.port=6060 -master="localhost:9333" + weed admin -cpuprofile=cpu.prof -memprofile=mem.prof -master="localhost:9333" + Configuration File: - The security.toml file is read from ".", "$HOME/.seaweedfs/", "/usr/local/etc/seaweedfs/", or "/etc/seaweedfs/", in that order @@ -149,6 +168,12 @@ var cmdAdmin = &Command{ } func runAdmin(cmd *Command, args []string) bool { + if *a.debug { + grace.StartDebugServer(*a.debugPort) + } + + grace.SetupProfiling(*a.cpuProfile, *a.memProfile) + // Load security configuration util.LoadSecurityConfiguration() diff --git a/weed/util/grace/pprof.go b/weed/util/grace/pprof.go index 5f5617c16..ad1b1de39 100644 --- a/weed/util/grace/pprof.go +++ b/weed/util/grace/pprof.go @@ -15,8 +15,8 @@ import ( // The server runs in a goroutine and serves pprof endpoints at /debug/pprof/*. func StartDebugServer(debugPort int) { go func() { - addr := fmt.Sprintf(":%d", debugPort) - glog.V(0).Infof("Starting debug server for pprof at http://localhost%s/debug/pprof/", addr) + addr := fmt.Sprintf("127.0.0.1:%d", debugPort) + glog.V(0).Infof("Starting debug server for pprof at http://%s/debug/pprof/", addr) if err := http.ListenAndServe(addr, nil); err != nil && err != http.ErrServerClosed { glog.Errorf("Failed to start debug server on %s: %v", addr, err) }